diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 8775dc32f2b..390e4d48637 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -202,27 +202,41 @@ class PollingURLCallbackProvider extends Disposable implements IURLCallbackProvi class WorkspaceProvider implements IWorkspaceProvider { - constructor(public readonly workspace: IWorkspace) { } + static QUERY_PARAM_EMPTY_WINDOW = 'ew'; + static QUERY_PARAM_FOLDER = 'folder'; + static QUERY_PARAM_WORKSPACE = 'workspace'; - async open(workspace: IWorkspace, options?: { reuse?: boolean }): Promise { - if (options && options.reuse && this.isSame(this.workspace, workspace)) { - return; // return early if workspace is not changing and we are reusing window + constructor( + public readonly workspace: IWorkspace, + public readonly environment: ReadonlyMap + ) { } + + async open(workspace: IWorkspace, options?: { reuse?: boolean, environment?: Map }): Promise { + if (options && options.reuse && !options.environment && this.isSame(this.workspace, workspace)) { + return; // return early if workspace and environment is not changing and we are reusing window } // Empty let targetHref: string | undefined = undefined; if (!workspace) { - targetHref = `${document.location.origin}${document.location.pathname}?ew=true`; + targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW}=true`; } // Folder else if (isFolderToOpen(workspace)) { - targetHref = `${document.location.origin}${document.location.pathname}?folder=${workspace.folderUri.path}`; + targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_FOLDER}=${workspace.folderUri.path}`; } // Workspace else if (isWorkspaceToOpen(workspace)) { - targetHref = `${document.location.origin}${document.location.pathname}?workspace=${workspace.workspaceUri.path}`; + targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_WORKSPACE}=${workspace.workspaceUri.path}`; + } + + // Environment + if (options && options.environment) { + for (const [key, value] of options.environment) { + targetHref += `&${key}=${encodeURIComponent(value)}`; + } } if (targetHref) { @@ -255,21 +269,53 @@ class WorkspaceProvider implements IWorkspaceProvider { } } -const configElement = document.getElementById('vscode-workbench-web-configuration'); -const configElementAttribute = configElement ? configElement.getAttribute('data-settings') : undefined; -if (!configElement || !configElementAttribute) { - throw new Error('Missing web configuration element'); -} +(function () { -const options: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = JSON.parse(configElementAttribute); -options.workspaceProvider = new WorkspaceProvider(options.folderUri ? { folderUri: URI.revive(options.folderUri) } : options.workspaceUri ? { workspaceUri: URI.revive(options.workspaceUri) } : undefined); -options.urlCallbackProvider = new PollingURLCallbackProvider(); -options.credentialsProvider = new LocalStorageCredentialsProvider(); + // Find config element in DOM + const configElement = document.getElementById('vscode-workbench-web-configuration'); + const configElementAttribute = configElement ? configElement.getAttribute('data-settings') : undefined; + if (!configElement || !configElementAttribute) { + throw new Error('Missing web configuration element'); + } -if (Array.isArray(options.staticExtensions)) { - options.staticExtensions.forEach(extension => { - extension.extensionLocation = URI.revive(extension.extensionLocation); - }); -} + const options: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = JSON.parse(configElementAttribute); -create(document.body, options); + // Determine workspace to open + let workspace: IWorkspace; + if (options.folderUri) { + workspace = { folderUri: URI.revive(options.folderUri) }; + } else if (options.workspaceUri) { + workspace = { workspaceUri: URI.revive(options.workspaceUri) }; + } else { + workspace = undefined; + } + + // Find environmental properties + const environment = new Map(); + if (document && document.location && document.location.search) { + const query = document.location.search.substring(1); + const vars = query.split('&'); + for (let p of vars) { + const pair = p.split('='); + if (pair.length === 2) { + const [key, value] = pair; + if (key !== WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW && key !== WorkspaceProvider.QUERY_PARAM_FOLDER && key !== WorkspaceProvider.QUERY_PARAM_WORKSPACE) { + environment.set(key, decodeURIComponent(value)); + } + } + } + } + + options.workspaceProvider = new WorkspaceProvider(workspace, environment); + options.urlCallbackProvider = new PollingURLCallbackProvider(); + options.credentialsProvider = new LocalStorageCredentialsProvider(); + + if (Array.isArray(options.staticExtensions)) { + options.staticExtensions.forEach(extension => { + extension.extensionLocation = URI.revive(extension.extensionLocation); + }); + } + + // Finally create workbench + create(document.body, options); +})(); diff --git a/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts b/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts index b1957999213..d18940a05fe 100644 --- a/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts +++ b/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts @@ -5,7 +5,6 @@ import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; import { IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug'; @@ -13,17 +12,19 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; -import { IProcessEnvironment } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IWorkspaceProvider, IWorkspace } from 'vs/workbench/services/host/browser/browserHostService'; class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient implements IExtensionHostDebugService { + private workspaceProvider: IWorkspaceProvider; + constructor( @IRemoteAgentService remoteAgentService: IRemoteAgentService, - @IEnvironmentService environmentService: IEnvironmentService + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService ) { const connection = remoteAgentService.getConnection(); - let channel: IChannel; if (connection) { channel = connection.getChannel(ExtensionHostDebugBroadcastChannel.ChannelName); @@ -35,11 +36,26 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i super(channel); + if (environmentService.options && environmentService.options.workspaceProvider) { + this.workspaceProvider = environmentService.options.workspaceProvider; + } else { + this.workspaceProvider = { open: async () => undefined, workspace: undefined }; + console.warn('Extension Host Debugging not available due to missing workspace provider.'); + } + + this.registerListeners(environmentService); + } + + private registerListeners(environmentService: IWorkbenchEnvironmentService): void { + + // Reload window on reload request this._register(this.onReload(event => { if (environmentService.isExtensionDevelopment && environmentService.debugExtensionHost.debugId === event.sessionId) { window.location.reload(); } })); + + // Close window on close request this._register(this.onClose(event => { if (environmentService.isExtensionDevelopment && environmentService.debugExtensionHost.debugId === event.sessionId) { window.close(); @@ -47,7 +63,45 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i })); } - openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment): Promise { + async openExtensionDevelopmentHostWindow(args: string[]): Promise { + if (!this.workspaceProvider.environment) { + // TODO@Ben remove me once environment is adopted + return this.openExtensionDevelopmentHostWindowLegacy(args); + } + + // Find out which workspace to open debug window on + let debugWorkspace: IWorkspace = undefined; + const folderUriArg = this.findArgument('folder-uri', args); + if (folderUriArg) { + debugWorkspace = { folderUri: URI.parse(folderUriArg) }; + } + + // Add environment parameters required for debug to work + const environment = new Map(); + + const extensionDevelopmentPath = this.findArgument('extensionDevelopmentPath', args); + if (extensionDevelopmentPath) { + environment.set('extensionDevelopmentPath', extensionDevelopmentPath); + } + + const debugId = this.findArgument('debugId', args); + if (debugId) { + environment.set('debugId', debugId); + } + + const inspectBrkExtensions = this.findArgument('inspect-brk-extensions', args); + if (inspectBrkExtensions) { + environment.set('inspect-brk-extensions', inspectBrkExtensions); + } + + // Open debug window as new window. Pass ParsedArgs over. + this.workspaceProvider.open(debugWorkspace, { + reuse: false, // debugging always requires a new window + environment // mandatory properties to enable debugging + }); + } + + private async openExtensionDevelopmentHostWindowLegacy(args: string[]): Promise { // we pass the "args" as query parameters of the URL let newAddress = `${document.location.origin}${document.location.pathname}?`; @@ -73,7 +127,7 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i const f = findArgument('folder-uri'); if (f) { - const u = URI.parse(f[0]); + const u = URI.parse(f); gotFolder = true; addQueryParameter('folder', u.path); } @@ -84,24 +138,34 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i const ep = findArgument('extensionDevelopmentPath'); if (ep) { - let u = ep[0]; - addQueryParameter('edp', u); + addQueryParameter('extensionDevelopmentPath', ep); } const di = findArgument('debugId'); if (di) { - addQueryParameter('di', di); + addQueryParameter('debugId', di); } const ibe = findArgument('inspect-brk-extensions'); if (ibe) { - addQueryParameter('ibe', ibe); + addQueryParameter('inspect-brk-extensions', ibe); } window.open(newAddress); return Promise.resolve(); } + + private findArgument(key: string, args: string[]): string | undefined { + for (const a of args) { + const k = `--${key}=`; + if (a.indexOf(k) === 0) { + return a.substr(k.length); + } + } + + return undefined; + } } registerSingleton(IExtensionHostDebugService, BrowserExtensionHostDebugService); diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index c63d334ec74..7e3051f96d1 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -100,32 +100,53 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment this.untitledWorkspacesHome = URI.from({ scheme: Schemas.untitled, path: 'Workspaces' }); - if (document && document.location && document.location.search) { - const map = new Map(); - const query = document.location.search.substring(1); - const vars = query.split('&'); - for (let p of vars) { - const pair = p.split('='); - if (pair.length >= 2) { - map.set(pair[0], decodeURIComponent(pair[1])); + // Fill in selected extra environmental properties + if (options.workspaceProvider && options.workspaceProvider.environment) { + const environment = options.workspaceProvider.environment; + for (const [key, value] of environment) { + switch (key) { + case 'extensionDevelopmentPath': + this.extensionDevelopmentLocationURI = [URI.parse(value)]; + this.isExtensionDevelopment = true; + break; + case 'debugId': + this.debugExtensionHost.debugId = value; + break; + case 'inspect-brk-extensions': + this.debugExtensionHost.port = parseInt(value); + this.debugExtensionHost.break = false; + break; } } + } else { + // TODO@Ben remove me once environment is adopted + if (document && document.location && document.location.search) { + const map = new Map(); + const query = document.location.search.substring(1); + const vars = query.split('&'); + for (let p of vars) { + const pair = p.split('='); + if (pair.length >= 2) { + map.set(pair[0], decodeURIComponent(pair[1])); + } + } - const edp = map.get('edp'); - if (edp) { - this.extensionDevelopmentLocationURI = [URI.parse(edp)]; - this.isExtensionDevelopment = true; - } + const edp = map.get('extensionDevelopmentPath'); + if (edp) { + this.extensionDevelopmentLocationURI = [URI.parse(edp)]; + this.isExtensionDevelopment = true; + } - const di = map.get('di'); - if (di) { - this.debugExtensionHost.debugId = di; - } + const di = map.get('debugId'); + if (di) { + this.debugExtensionHost.debugId = di; + } - const ibe = map.get('ibe'); - if (ibe) { - this.debugExtensionHost.port = parseInt(ibe); - this.debugExtensionHost.break = false; + const ibe = map.get('inspect-brk-extensions'); + if (ibe) { + this.debugExtensionHost.port = parseInt(ibe); + this.debugExtensionHost.break = false; + } } } } diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index f68e4e20558..a84472e399b 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -33,13 +33,22 @@ export interface IWorkspaceProvider { */ readonly workspace: IWorkspace; + /** + * Optionally a set of environmental properties to be used + * as environment for the workspace to open. + */ + readonly environment?: ReadonlyMap; + /** * Asks to open a workspace in the current or a new window. * * @param workspace the workspace to open. - * @param options wether to open inside the current window or a new window. + * @param options optional options for the workspace to open. + * - `reuse`: wether to open inside the current window or a new window + * - `environment`: a map of environmental properties that should be + * filled into the environment of the workspace to open. */ - open(workspace: IWorkspace, options?: { reuse?: boolean }): Promise; + open(workspace: IWorkspace, options?: { reuse?: boolean, environment?: Map }): Promise; } export class BrowserHostService extends Disposable implements IHostService { diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 0140a8cec27..8d5a72ae332 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -82,7 +82,6 @@ interface IWorkbenchConstructionOptions { */ logLevel?: LogLevel; - /** * Experimental: Support for update reporting. */