mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Merge from vscode 4d91d96e5e121b38d33508cdef17868bab255eae
This commit is contained in:
committed by
AzureDataStudio
parent
a971aee5bd
commit
5e7071e466
@@ -4,14 +4,21 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer, VSBufferReadableStream } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { isUNC } from 'vs/base/common/extpath';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { sep } from 'vs/base/common/path';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
import { getWebviewContentMimeType } from 'vs/platform/webview/common/mimeTypes';
|
||||
|
||||
|
||||
export const webviewPartitionId = 'webview';
|
||||
|
||||
|
||||
export namespace WebviewResourceResponse {
|
||||
export enum Type { Success, Failed, AccessDenied }
|
||||
|
||||
@@ -63,18 +70,38 @@ export async function loadLocalResource(
|
||||
|
||||
export async function loadLocalResourceStream(
|
||||
requestUri: URI,
|
||||
options: {
|
||||
extensionLocation: URI | undefined;
|
||||
roots: ReadonlyArray<URI>;
|
||||
remoteConnectionData?: IRemoteConnectionData | null;
|
||||
},
|
||||
fileService: IFileService,
|
||||
extensionLocation: URI | undefined,
|
||||
roots: ReadonlyArray<URI>
|
||||
requestService: IRequestService,
|
||||
): Promise<WebviewResourceResponse.StreamResponse> {
|
||||
const resourceToLoad = getResourceToLoad(requestUri, extensionLocation, roots);
|
||||
const resourceToLoad = getResourceToLoad(requestUri, options.extensionLocation, options.roots);
|
||||
if (!resourceToLoad) {
|
||||
return WebviewResourceResponse.AccessDenied;
|
||||
}
|
||||
const mime = getWebviewContentMimeType(requestUri); // Use the original path for the mime
|
||||
|
||||
if (options.remoteConnectionData) {
|
||||
// Remote uris must go to the resolved server.
|
||||
if (resourceToLoad.scheme === Schemas.vscodeRemote || (options.extensionLocation?.scheme === REMOTE_HOST_SCHEME)) {
|
||||
const uri = URI.parse(`http://${options.remoteConnectionData.host}:${options.remoteConnectionData.port}`).with({
|
||||
path: '/vscode-remote-resource',
|
||||
query: `tkn=${options.remoteConnectionData.connectionToken}&path=${encodeURIComponent(resourceToLoad.path)}`,
|
||||
});
|
||||
|
||||
const response = await requestService.request({ url: uri.toString(true) }, CancellationToken.None);
|
||||
if (response.res.statusCode === 200) {
|
||||
return new WebviewResourceResponse.StreamSuccess(response.stream, mime);
|
||||
}
|
||||
return WebviewResourceResponse.Failed;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const contents = await fileService.readFileStream(resourceToLoad);
|
||||
const mime = getWebviewContentMimeType(requestUri); // Use the original path for the mime
|
||||
return new WebviewResourceResponse.StreamSuccess(contents.value, mime);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
@@ -90,20 +117,7 @@ function getResourceToLoad(
|
||||
const normalizedPath = normalizeRequestPath(requestUri);
|
||||
|
||||
for (const root of roots) {
|
||||
if (!containsResource(root, normalizedPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) {
|
||||
return URI.from({
|
||||
scheme: REMOTE_HOST_SCHEME,
|
||||
authority: extensionLocation.authority,
|
||||
path: '/vscode-resource',
|
||||
query: JSON.stringify({
|
||||
requestResourcePath: normalizedPath.path
|
||||
})
|
||||
});
|
||||
} else {
|
||||
if (containsResource(root, normalizedPath)) {
|
||||
return normalizedPath;
|
||||
}
|
||||
}
|
||||
@@ -112,20 +126,20 @@ function getResourceToLoad(
|
||||
}
|
||||
|
||||
function normalizeRequestPath(requestUri: URI) {
|
||||
if (requestUri.scheme !== Schemas.vscodeWebviewResource) {
|
||||
if (requestUri.scheme === Schemas.vscodeWebviewResource) {
|
||||
// The `vscode-webview-resource` scheme has the following format:
|
||||
//
|
||||
// vscode-webview-resource://id/scheme//authority?/path
|
||||
//
|
||||
const resourceUri = URI.parse(requestUri.path.replace(/^\/([a-z0-9\-]+)\/{1,2}/i, '$1://'));
|
||||
|
||||
return resourceUri.with({
|
||||
query: requestUri.query,
|
||||
fragment: requestUri.fragment
|
||||
});
|
||||
} else {
|
||||
return requestUri;
|
||||
}
|
||||
|
||||
// The `vscode-webview-resource` scheme has the following format:
|
||||
//
|
||||
// vscode-webview-resource://id/scheme//authority?/path
|
||||
//
|
||||
const resourceUri = URI.parse(requestUri.path.replace(/^\/([a-z0-9\-]+)\/{1,2}/i, '$1://'));
|
||||
|
||||
return resourceUri.with({
|
||||
query: requestUri.query,
|
||||
fragment: requestUri.fragment
|
||||
});
|
||||
}
|
||||
|
||||
function containsResource(root: URI, resource: URI): boolean {
|
||||
|
||||
@@ -5,15 +5,17 @@
|
||||
|
||||
import { UriComponents } from 'vs/base/common/uri';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { IWebviewPortMapping } from 'vs/platform/webview/common/webviewPortMapping';
|
||||
|
||||
export const IWebviewManagerService = createDecorator<IWebviewManagerService>('webviewManagerService');
|
||||
|
||||
export interface IWebviewManagerService {
|
||||
_serviceBrand: unknown;
|
||||
|
||||
registerWebview(id: string, metadata: RegisterWebviewMetadata): Promise<void>;
|
||||
registerWebview(id: string, webContentsId: number, metadata: RegisterWebviewMetadata): Promise<void>;
|
||||
unregisterWebview(id: string): Promise<void>;
|
||||
updateLocalResourceRoots(id: string, roots: UriComponents[]): Promise<void>;
|
||||
updateWebviewMetadata(id: string, metadataDelta: Partial<RegisterWebviewMetadata>): Promise<void>;
|
||||
|
||||
setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): Promise<void>;
|
||||
}
|
||||
@@ -21,4 +23,6 @@ export interface IWebviewManagerService {
|
||||
export interface RegisterWebviewMetadata {
|
||||
readonly extensionLocation: UriComponents | undefined;
|
||||
readonly localResourceRoots: readonly UriComponents[];
|
||||
readonly remoteConnectionData: IRemoteConnectionData | null;
|
||||
readonly portMappings: readonly IWebviewPortMapping[];
|
||||
}
|
||||
|
||||
81
src/vs/platform/webview/common/webviewPortMapping.ts
Normal file
81
src/vs/platform/webview/common/webviewPortMapping.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IAddress } from 'vs/platform/remote/common/remoteAgentConnection';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { extractLocalHostUriMetaDataForPortMapping, ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
|
||||
|
||||
export interface IWebviewPortMapping {
|
||||
webviewPort: number;
|
||||
extensionHostPort: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages port mappings for a single webview.
|
||||
*/
|
||||
export class WebviewPortMappingManager implements IDisposable {
|
||||
|
||||
private readonly _tunnels = new Map<number, Promise<RemoteTunnel>>();
|
||||
|
||||
constructor(
|
||||
private readonly _getExtensionLocation: () => URI | undefined,
|
||||
private readonly _getMappings: () => readonly IWebviewPortMapping[],
|
||||
private readonly tunnelService: ITunnelService
|
||||
) { }
|
||||
|
||||
public async getRedirect(resolveAuthority: IAddress, url: string): Promise<string | undefined> {
|
||||
const uri = URI.parse(url);
|
||||
const requestLocalHostInfo = extractLocalHostUriMetaDataForPortMapping(uri);
|
||||
if (!requestLocalHostInfo) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
for (const mapping of this._getMappings()) {
|
||||
if (mapping.webviewPort === requestLocalHostInfo.port) {
|
||||
const extensionLocation = this._getExtensionLocation();
|
||||
if (extensionLocation && extensionLocation.scheme === REMOTE_HOST_SCHEME) {
|
||||
const tunnel = await this.getOrCreateTunnel(resolveAuthority, mapping.extensionHostPort);
|
||||
if (tunnel) {
|
||||
if (tunnel.tunnelLocalPort === mapping.webviewPort) {
|
||||
return undefined;
|
||||
}
|
||||
return encodeURI(uri.with({
|
||||
authority: `127.0.0.1:${tunnel.tunnelLocalPort}`,
|
||||
}).toString(true));
|
||||
}
|
||||
}
|
||||
|
||||
if (mapping.webviewPort !== mapping.extensionHostPort) {
|
||||
return encodeURI(uri.with({
|
||||
authority: `${requestLocalHostInfo.address}:${mapping.extensionHostPort}`
|
||||
}).toString(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
for (const tunnel of this._tunnels.values()) {
|
||||
tunnel.then(tunnel => tunnel.dispose());
|
||||
}
|
||||
this._tunnels.clear();
|
||||
}
|
||||
|
||||
private getOrCreateTunnel(remoteAuthority: IAddress, remotePort: number): Promise<RemoteTunnel> | undefined {
|
||||
const existing = this._tunnels.get(remotePort);
|
||||
if (existing) {
|
||||
return existing;
|
||||
}
|
||||
const tunnel = this.tunnelService.openTunnel(remoteAuthority, undefined, remotePort);
|
||||
if (tunnel) {
|
||||
this._tunnels.set(remotePort, tunnel);
|
||||
}
|
||||
return tunnel;
|
||||
}
|
||||
}
|
||||
@@ -4,36 +4,66 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { webContents } from 'electron';
|
||||
import { IWebviewManagerService, RegisterWebviewMetadata } from 'vs/platform/webview/common/webviewManagerService';
|
||||
import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { UriComponents, URI } from 'vs/base/common/uri';
|
||||
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
import { IWebviewManagerService, RegisterWebviewMetadata } from 'vs/platform/webview/common/webviewManagerService';
|
||||
import { WebviewPortMappingProvider } from 'vs/platform/webview/electron-main/webviewPortMappingProvider';
|
||||
import { WebviewProtocolProvider } from 'vs/platform/webview/electron-main/webviewProtocolProvider';
|
||||
|
||||
export class WebviewMainService implements IWebviewManagerService {
|
||||
export class WebviewMainService extends Disposable implements IWebviewManagerService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private protocolProvider: WebviewProtocolProvider;
|
||||
private readonly protocolProvider: WebviewProtocolProvider;
|
||||
private readonly portMappingProvider: WebviewPortMappingProvider;
|
||||
|
||||
constructor(
|
||||
@IFileService fileService: IFileService,
|
||||
@IRequestService requestService: IRequestService,
|
||||
@ITunnelService tunnelService: ITunnelService,
|
||||
) {
|
||||
this.protocolProvider = new WebviewProtocolProvider(fileService);
|
||||
super();
|
||||
this.protocolProvider = this._register(new WebviewProtocolProvider(fileService, requestService));
|
||||
this.portMappingProvider = this._register(new WebviewPortMappingProvider(tunnelService));
|
||||
}
|
||||
|
||||
public async registerWebview(id: string, metadata: RegisterWebviewMetadata): Promise<void> {
|
||||
this.protocolProvider.registerWebview(id,
|
||||
metadata.extensionLocation ? URI.from(metadata.extensionLocation) : undefined,
|
||||
metadata.localResourceRoots.map((x: UriComponents) => URI.from(x))
|
||||
);
|
||||
public async registerWebview(id: string, webContentsId: number, metadata: RegisterWebviewMetadata): Promise<void> {
|
||||
const extensionLocation = metadata.extensionLocation ? URI.from(metadata.extensionLocation) : undefined;
|
||||
|
||||
this.protocolProvider.registerWebview(id, {
|
||||
...metadata,
|
||||
extensionLocation,
|
||||
localResourceRoots: metadata.localResourceRoots.map(x => URI.from(x))
|
||||
});
|
||||
|
||||
this.portMappingProvider.registerWebview(id, webContentsId, {
|
||||
extensionLocation,
|
||||
mappings: metadata.portMappings,
|
||||
resolvedAuthority: metadata.remoteConnectionData,
|
||||
});
|
||||
}
|
||||
|
||||
public async unregisterWebview(id: string): Promise<void> {
|
||||
this.protocolProvider.unreigsterWebview(id);
|
||||
this.protocolProvider.unregisterWebview(id);
|
||||
this.portMappingProvider.unregisterWebview(id);
|
||||
}
|
||||
|
||||
public async updateLocalResourceRoots(id: string, roots: UriComponents[]): Promise<void> {
|
||||
this.protocolProvider.updateLocalResourceRoots(id, roots.map((x: UriComponents) => URI.from(x)));
|
||||
public async updateWebviewMetadata(id: string, metaDataDelta: Partial<RegisterWebviewMetadata>): Promise<void> {
|
||||
const extensionLocation = metaDataDelta.extensionLocation ? URI.from(metaDataDelta.extensionLocation) : undefined;
|
||||
|
||||
this.protocolProvider.updateWebviewMetadata(id, {
|
||||
...metaDataDelta,
|
||||
extensionLocation,
|
||||
localResourceRoots: metaDataDelta.localResourceRoots?.map(x => URI.from(x)),
|
||||
});
|
||||
|
||||
this.portMappingProvider.updateWebviewMetadata(id, {
|
||||
...metaDataDelta,
|
||||
extensionLocation,
|
||||
});
|
||||
}
|
||||
|
||||
public async setIgnoreMenuShortcuts(webContentsId: number, enabled: boolean): Promise<void> {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { session } from 'electron';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IAddress } from 'vs/platform/remote/common/remoteAgentConnection';
|
||||
import { ITunnelService } from 'vs/platform/remote/common/tunnel';
|
||||
import { webviewPartitionId } from 'vs/platform/webview/common/resourceLoader';
|
||||
import { IWebviewPortMapping, WebviewPortMappingManager } from 'vs/platform/webview/common/webviewPortMapping';
|
||||
|
||||
interface PortMappingData {
|
||||
readonly extensionLocation: URI | undefined;
|
||||
readonly mappings: readonly IWebviewPortMapping[];
|
||||
readonly resolvedAuthority: IAddress | null | undefined;
|
||||
}
|
||||
|
||||
export class WebviewPortMappingProvider extends Disposable {
|
||||
|
||||
private readonly _webviewData = new Map<string, {
|
||||
readonly webContentsId: number;
|
||||
readonly manager: WebviewPortMappingManager;
|
||||
metadata: PortMappingData;
|
||||
}>();
|
||||
|
||||
private _webContentsIdsToWebviewIds = new Map<number, /* id */ string>();
|
||||
|
||||
constructor(
|
||||
@ITunnelService private readonly _tunnelService: ITunnelService,
|
||||
) {
|
||||
super();
|
||||
|
||||
const sess = session.fromPartition(webviewPartitionId);
|
||||
|
||||
sess.webRequest.onBeforeRequest({
|
||||
urls: [
|
||||
'*://localhost:*/',
|
||||
'*://127.0.0.1:*/',
|
||||
'*://0.0.0.0:*/',
|
||||
]
|
||||
}, async (details, callback) => {
|
||||
const webviewId = details.webContentsId && this._webContentsIdsToWebviewIds.get(details.webContentsId);
|
||||
if (!webviewId) {
|
||||
return callback({});
|
||||
}
|
||||
|
||||
const entry = this._webviewData.get(webviewId);
|
||||
if (!entry || !entry.metadata.resolvedAuthority) {
|
||||
return callback({});
|
||||
}
|
||||
|
||||
const redirect = await entry.manager.getRedirect(entry.metadata.resolvedAuthority, details.url);
|
||||
return callback(redirect ? { redirectURL: redirect } : {});
|
||||
});
|
||||
}
|
||||
|
||||
public async registerWebview(id: string, webContentsId: number, metadata: PortMappingData): Promise<void> {
|
||||
const manager = new WebviewPortMappingManager(
|
||||
() => this._webviewData.get(id)?.metadata.extensionLocation,
|
||||
() => this._webviewData.get(id)?.metadata.mappings || [],
|
||||
this._tunnelService);
|
||||
|
||||
this._webviewData.set(id, { webContentsId, metadata, manager });
|
||||
this._webContentsIdsToWebviewIds.set(webContentsId, id);
|
||||
}
|
||||
|
||||
public unregisterWebview(id: string): void {
|
||||
const existing = this._webviewData.get(id);
|
||||
if (existing) {
|
||||
existing.manager.dispose();
|
||||
this._webviewData.delete(id);
|
||||
this._webContentsIdsToWebviewIds.delete(existing.webContentsId);
|
||||
}
|
||||
}
|
||||
|
||||
public async updateWebviewMetadata(id: string, metadataDelta: Partial<PortMappingData>): Promise<void> {
|
||||
const entry = this._webviewData.get(id);
|
||||
if (entry) {
|
||||
this._webviewData.set(id, {
|
||||
...entry,
|
||||
...metadataDelta,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,38 +3,51 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { protocol } from 'electron';
|
||||
import { session } from 'electron';
|
||||
import { Readable } from 'stream';
|
||||
import { VSBufferReadableStream } from 'vs/base/common/buffer';
|
||||
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { streamToNodeReadable } from 'vs/base/node/stream';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { loadLocalResourceStream, WebviewResourceResponse } from 'vs/platform/webview/common/resourceLoader';
|
||||
import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
import { loadLocalResourceStream, webviewPartitionId, WebviewResourceResponse } from 'vs/platform/webview/common/resourceLoader';
|
||||
|
||||
interface WebviewMetadata {
|
||||
readonly extensionLocation: URI | undefined;
|
||||
readonly localResourceRoots: readonly URI[];
|
||||
readonly remoteConnectionData: IRemoteConnectionData | null;
|
||||
}
|
||||
|
||||
export class WebviewProtocolProvider extends Disposable {
|
||||
|
||||
private readonly webviewMetadata = new Map<string, {
|
||||
readonly extensionLocation: URI | undefined;
|
||||
readonly localResourceRoots: readonly URI[];
|
||||
}>();
|
||||
private readonly webviewMetadata = new Map<string, WebviewMetadata>();
|
||||
|
||||
constructor(
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IRequestService private readonly requestService: IRequestService,
|
||||
) {
|
||||
super();
|
||||
|
||||
protocol.registerStreamProtocol(Schemas.vscodeWebviewResource, async (request, callback): Promise<void> => {
|
||||
const sess = session.fromPartition(webviewPartitionId);
|
||||
|
||||
sess.protocol.registerStreamProtocol(Schemas.vscodeWebviewResource, async (request, callback): Promise<void> => {
|
||||
try {
|
||||
const uri = URI.parse(request.url);
|
||||
|
||||
const id = uri.authority;
|
||||
const metadata = this.webviewMetadata.get(id);
|
||||
if (metadata) {
|
||||
const result = await loadLocalResourceStream(uri, this.fileService, metadata.extensionLocation, metadata.localResourceRoots);
|
||||
const result = await loadLocalResourceStream(uri, {
|
||||
extensionLocation: metadata.extensionLocation,
|
||||
roots: metadata.localResourceRoots,
|
||||
remoteConnectionData: metadata.remoteConnectionData,
|
||||
}, this.fileService, this.requestService);
|
||||
if (result.type === WebviewResourceResponse.Type.Success) {
|
||||
return callback({
|
||||
statusCode: 200,
|
||||
data: streamToNodeReadable(result.stream),
|
||||
data: this.streamToNodeReadable(result.stream),
|
||||
headers: {
|
||||
'Content-Type': result.mimeType,
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
@@ -54,23 +67,67 @@ export class WebviewProtocolProvider extends Disposable {
|
||||
return callback({ data: null, statusCode: 404 });
|
||||
});
|
||||
|
||||
this._register(toDisposable(() => protocol.unregisterProtocol(Schemas.vscodeWebviewResource)));
|
||||
this._register(toDisposable(() => sess.protocol.unregisterProtocol(Schemas.vscodeWebviewResource)));
|
||||
}
|
||||
|
||||
public registerWebview(id: string, extensionLocation: URI | undefined, localResourceRoots: readonly URI[]): void {
|
||||
this.webviewMetadata.set(id, { extensionLocation, localResourceRoots });
|
||||
private streamToNodeReadable(stream: VSBufferReadableStream): Readable {
|
||||
return new class extends Readable {
|
||||
private listening = false;
|
||||
|
||||
_read(size?: number): void {
|
||||
if (!this.listening) {
|
||||
this.listening = true;
|
||||
|
||||
// Data
|
||||
stream.on('data', data => {
|
||||
try {
|
||||
if (!this.push(data.buffer)) {
|
||||
stream.pause(); // pause the stream if we should not push anymore
|
||||
}
|
||||
} catch (error) {
|
||||
this.emit(error);
|
||||
}
|
||||
});
|
||||
|
||||
// End
|
||||
stream.on('end', () => {
|
||||
try {
|
||||
this.push(null); // signal EOS
|
||||
} catch (error) {
|
||||
this.emit(error);
|
||||
}
|
||||
});
|
||||
|
||||
// Error
|
||||
stream.on('error', error => this.emit('error', error));
|
||||
}
|
||||
|
||||
// ensure the stream is flowing
|
||||
stream.resume();
|
||||
}
|
||||
|
||||
_destroy(error: Error | null, callback: (error: Error | null) => void): void {
|
||||
stream.destroy();
|
||||
|
||||
callback(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public unreigsterWebview(id: string): void {
|
||||
public async registerWebview(id: string, metadata: WebviewMetadata): Promise<void> {
|
||||
this.webviewMetadata.set(id, metadata);
|
||||
}
|
||||
|
||||
public unregisterWebview(id: string): void {
|
||||
this.webviewMetadata.delete(id);
|
||||
}
|
||||
|
||||
public updateLocalResourceRoots(id: string, localResourceRoots: readonly URI[]) {
|
||||
public async updateWebviewMetadata(id: string, metadataDelta: Partial<WebviewMetadata>): Promise<void> {
|
||||
const entry = this.webviewMetadata.get(id);
|
||||
if (entry) {
|
||||
this.webviewMetadata.set(id, {
|
||||
extensionLocation: entry.extensionLocation,
|
||||
localResourceRoots: localResourceRoots,
|
||||
...entry,
|
||||
...metadataDelta,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user