From e5b2742fff8f946e321b120dcc90ab3403fbcfc2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 29 Mar 2021 11:31:44 +0200 Subject: [PATCH] debt - provide common method to load renderer with config param --- src/vs/code/electron-main/main.ts | 23 ++- .../issue/electron-main/issueMainService.ts | 31 +--- src/vs/platform/product/common/product.ts | 3 +- .../electron-main/sharedProcess.ts | 8 +- .../platform/windows/electron-main/window.ts | 150 +++++++++--------- .../platform/windows/electron-main/windows.ts | 26 +++ 6 files changed, 118 insertions(+), 123 deletions(-) diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 19403af689d..187366c9ec5 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -80,11 +80,8 @@ class CodeMain { // default electron error dialog popping up setUnexpectedErrorHandler(err => console.error(err)); - // Resolve command line arguments - const args = this.resolveArgs(); - // Create services - const [instantiationService, instanceEnvironment, environmentService, configurationService, stateService, bufferLogService, productService] = this.createServices(args); + const [instantiationService, instanceEnvironment, environmentService, configurationService, stateService, bufferLogService, productService] = this.createServices(); try { @@ -108,7 +105,7 @@ class CodeMain { // Create the main IPC server by trying to be the server // If this throws an error it means we are not the first // instance of VS Code running and so we would quit. - const mainProcessNodeIpcServer = await this.doStartup(args, logService, environmentService, lifecycleMainService, instantiationService, productService, true); + const mainProcessNodeIpcServer = await this.doStartup(logService, environmentService, lifecycleMainService, instantiationService, productService, true); // Delay creation of spdlog for perf reasons (https://github.com/microsoft/vscode/issues/72906) bufferLogService.logger = new SpdLogLogger('main', join(environmentService.logsPath, 'main.log'), true, bufferLogService.getLevel()); @@ -126,7 +123,7 @@ class CodeMain { } } - private createServices(args: NativeParsedArgs): [IInstantiationService, IProcessEnvironment, IEnvironmentMainService, ConfigurationService, StateService, BufferLogService, IProductService] { + private createServices(): [IInstantiationService, IProcessEnvironment, IEnvironmentMainService, ConfigurationService, StateService, BufferLogService, IProductService] { const services = new ServiceCollection(); // Product @@ -134,7 +131,7 @@ class CodeMain { services.set(IProductService, productService); // Environment - const environmentMainService = new EnvironmentMainService(args, productService); + const environmentMainService = new EnvironmentMainService(this.resolveArgs(), productService); const instanceEnvironment = this.patchEnvironment(environmentMainService); // Patch `process.env` with the instance's environment services.set(IEnvironmentMainService, environmentMainService); @@ -219,7 +216,7 @@ class CodeMain { return Promise.all([environmentServiceInitialization, configurationServiceInitialization, stateServiceInitialization]); } - private async doStartup(args: NativeParsedArgs, logService: ILogService, environmentMainService: IEnvironmentMainService, lifecycleMainService: ILifecycleMainService, instantiationService: IInstantiationService, productService: IProductService, retry: boolean): Promise { + private async doStartup(logService: ILogService, environmentMainService: IEnvironmentMainService, lifecycleMainService: ILifecycleMainService, instantiationService: IInstantiationService, productService: IProductService, retry: boolean): Promise { // Try to setup a server for running. If that succeeds it means // we are the first instance to startup. Otherwise it is likely @@ -271,7 +268,7 @@ class CodeMain { throw error; } - return this.doStartup(args, logService, environmentMainService, lifecycleMainService, instantiationService, productService, false); + return this.doStartup(logService, environmentMainService, lifecycleMainService, instantiationService, productService, false); } // Tests from CLI require to be the only instance currently @@ -287,7 +284,7 @@ class CodeMain { // Skip this if we are running with --wait where it is expected that we wait for a while. // Also skip when gathering diagnostics (--status) which can take a longer time. let startupWarningDialogHandle: NodeJS.Timeout | undefined = undefined; - if (!args.wait && !args.status) { + if (!environmentMainService.args.wait && !environmentMainService.args.status) { startupWarningDialogHandle = setTimeout(() => { this.showStartupWarningDialog( localize('secondInstanceNoResponse', "Another instance of {0} is running but not responding", productService.nameShort), @@ -300,7 +297,7 @@ class CodeMain { const launchService = ProxyChannel.toService(client.getChannel('launch'), { disableMarshalling: true }); // Process Info - if (args.status) { + if (environmentMainService.args.status) { return instantiationService.invokeFunction(async () => { const diagnosticsService = new DiagnosticsService(NullTelemetryService, productService); const mainProcessInfo = await launchService.getMainProcessInfo(); @@ -319,7 +316,7 @@ class CodeMain { // Send environment over... logService.trace('Sending env to running instance...'); - await launchService.start(args, process.env as IProcessEnvironment); + await launchService.start(environmentMainService.args, process.env as IProcessEnvironment); // Cleanup client.dispose(); @@ -333,7 +330,7 @@ class CodeMain { } // Print --status usage info - if (args.status) { + if (environmentMainService.args.status) { logService.warn('Warning: The --status argument can only be used if Code is already running. Please run it again after Code has started.'); throw new ExpectedError('Terminating...'); diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index c9ed978ec34..d4fc1f30b37 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import * as os from 'os'; +import { arch, release, type } from 'os'; import { IProductService } from 'vs/platform/product/common/productService'; -import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { ICommonIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/common/issue'; import { BrowserWindow, ipcMain, screen, IpcMainEvent, Display } from 'electron'; import { ILaunchMainService } from 'vs/platform/launch/electron-main/launchMainService'; @@ -14,7 +13,7 @@ import { IDiagnosticsService, PerformanceInfo, isRemoteDiagnosticError } from 'v import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; -import { IWindowState } from 'vs/platform/windows/electron-main/windows'; +import { IWindowState, toWindowUrl } from 'vs/platform/windows/electron-main/windows'; import { listProcesses } from 'vs/base/node/ps'; import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMainService'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -280,7 +279,7 @@ export class IssueMainService implements ICommonIssueService { }; this._processExplorerWindow.loadURL( - toWindowUrl('vs/code/electron-sandbox/processExplorer/processExplorer.html', windowConfiguration)); + toWindowUrl('vs/code/electron-sandbox/processExplorer/processExplorer.html', windowConfiguration, true)); this._processExplorerWindow.on('close', () => this._processExplorerWindow = null); @@ -406,9 +405,9 @@ export class IssueMainService implements ICommonIssueService { features, disableExtensions: this.environmentMainService.disableExtensions, os: { - type: os.type(), - arch: os.arch(), - release: os.release(), + type: type(), + arch: arch(), + release: release(), }, product: { nameShort: this.productService.nameShort, @@ -420,22 +419,6 @@ export class IssueMainService implements ICommonIssueService { } }; - return toWindowUrl('vs/code/electron-sandbox/issue/issueReporter.html', windowConfiguration); + return toWindowUrl('vs/code/electron-sandbox/issue/issueReporter.html', windowConfiguration, true); } } - -function toWindowUrl(modulePathToHtml: string, windowConfiguration: T): string { - const environment = parseArgs(process.argv, OPTIONS); - const config = Object.assign(environment, windowConfiguration); - for (const keyValue of Object.keys(config)) { - const key = keyValue as keyof typeof config; - if (config[key] === undefined || config[key] === null || config[key] === '') { - delete config[key]; // only send over properties that have a true value - } - } - - return FileAccess - .asBrowserUri(modulePathToHtml, require, true) - .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }) - .toString(true); -} diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 251ed36de91..0dadbc22726 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -20,7 +20,7 @@ if (isWeb || typeof require === 'undefined' || typeof require.__$__nodeRequire ! // Running out of sources if (Object.keys(product).length === 0) { Object.assign(product, { - version: '1.55.0-dev', + version: '1.56.0-dev', nameShort: isWeb ? 'Code Web - OSS Dev' : 'Code - OSS Dev', nameLong: isWeb ? 'Code Web - OSS Dev' : 'Code - OSS Dev', applicationName: 'code-oss', @@ -33,6 +33,7 @@ if (isWeb || typeof require === 'undefined' || typeof require.__$__nodeRequire ! 'ms-vscode.vscode-js-profile-flame', 'ms-vscode.vscode-js-profile-table', 'ms-vscode.github-browser', + 'ms-vscode.github-richnav', 'ms-vscode.remotehub', 'ms-vscode.remotehub-insiders' ], diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index dcc273459ac..93463e25629 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -16,7 +16,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { connect as connectMessagePort } from 'vs/base/parts/ipc/electron-main/ipc.mp'; import { assertIsDefined } from 'vs/base/common/types'; import { Emitter, Event } from 'vs/base/common/event'; -import { WindowError } from 'vs/platform/windows/electron-main/windows'; +import { toWindowUrl, WindowError } from 'vs/platform/windows/electron-main/windows'; import { resolveShellEnv } from 'vs/platform/environment/node/shellEnv'; export class SharedProcess extends Disposable implements ISharedProcess { @@ -189,11 +189,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { }; // Load with config - this.window.loadURL(FileAccess - .asBrowserUri('vs/code/electron-browser/sharedProcess/sharedProcess.html', require) - .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }) - .toString(true) - ); + this.window.loadURL(toWindowUrl('vs/code/electron-browser/sharedProcess/sharedProcess.html', config)); } private registerWindowListeners(): void { diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 78d2e2867b6..29b9a865901 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -13,13 +13,12 @@ import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, R import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { IProductService } from 'vs/platform/product/common/productService'; import { WindowMinimumSize, IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, zoomLevelToZoomFactor, INativeWindowConfiguration } from 'vs/platform/windows/common/windows'; import { Disposable } from 'vs/base/common/lifecycle'; import { browserCodeLoadingCacheStrategy, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; -import { defaultWindowState, ICodeWindow, ILoadEvent, IWindowState, WindowError, WindowMode } from 'vs/platform/windows/electron-main/windows'; +import { defaultWindowState, ICodeWindow, ILoadEvent, IWindowState, toWindowUrl, WindowError, WindowMode } from 'vs/platform/windows/electron-main/windows'; import { ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; @@ -73,7 +72,9 @@ const enum ReadyState { export class CodeWindow extends Disposable implements ICodeWindow { - private static readonly MAX_URL_LENGTH = 2 * ByteSize.MB; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32 + private static readonly MAX_URL_LENGTH = 2 * ByteSize.MB; // https://source.chromium.org/chromium/chromium/src/+/master:url/url_constants.cc;l=37 + + //#region Events private readonly _onWillLoad = this._register(new Emitter()); readonly onWillLoad = this._onWillLoad.event; @@ -87,6 +88,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { private readonly _onDidDestroy = this._register(new Emitter()); readonly onDidDestroy = this._onDidDestroy.event; + //#endregion + private hiddenTitleBarStyle: boolean | undefined; private showTimeoutHandle: NodeJS.Timeout | undefined; private windowState: IWindowState; @@ -104,6 +107,34 @@ export class CodeWindow extends Disposable implements ICodeWindow { private currentHttpProxy: string | undefined = undefined; private currentNoProxy: string | undefined = undefined; + private _id: number; + get id(): number { return this._id; } + + private _win: BrowserWindow; + get win(): BrowserWindow | null { return this._win; } + + private _lastFocusTime = -1; + get lastFocusTime(): number { return this._lastFocusTime; } + + get backupPath(): string | undefined { return this.currentConfig?.backupPath; } + + get openedWorkspace(): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined { return this.currentConfig?.workspace; } + + get remoteAuthority(): string | undefined { return this.currentConfig?.remoteAuthority; } + + private pendingLoadConfig: INativeWindowConfiguration | undefined; + + private currentConfig: INativeWindowConfiguration | undefined; + get config(): INativeWindowConfiguration | undefined { return this.currentConfig; } + + get hasHiddenTitleBarStyle(): boolean { return !!this.hiddenTitleBarStyle; } + + get isExtensionDevelopmentHost(): boolean { return !!(this.currentConfig?.extensionDevelopmentPath); } + + get isExtensionTestHost(): boolean { return !!(this.currentConfig?.extensionTestsPath); } + + get isExtensionDevelopmentTestFromCli(): boolean { return this.isExtensionDevelopmentHost && this.isExtensionTestHost && !this.currentConfig?.debugId; } + constructor( config: IWindowCreationOptions, @ILogService private readonly logService: ILogService, @@ -132,7 +163,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // in case we are maximized or fullscreen, only show later after the call to maximize/fullscreen (see below) const isFullscreenOrMaximized = (this.windowState.mode === WindowMode.Maximized || this.windowState.mode === WindowMode.Fullscreen); - const windowConfig = this.configurationService.getValue('window'); + const windowSettings = this.configurationService.getValue('window'); const options: BrowserWindowConstructorOptions = { width: this.windowState.width, @@ -152,7 +183,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { spellcheck: false, nativeWindowOpen: true, webviewTag: true, - zoomFactor: zoomLevelToZoomFactor(windowConfig?.zoomLevel), + zoomFactor: zoomLevelToZoomFactor(windowSettings?.zoomLevel), ...this.environmentMainService.sandbox ? // Sandbox @@ -188,12 +219,12 @@ export class CodeWindow extends Disposable implements ICodeWindow { if (isMacintosh) { options.acceptFirstMouse = true; // enabled by default - if (windowConfig?.clickThroughInactive === false) { + if (windowSettings?.clickThroughInactive === false) { options.acceptFirstMouse = false; } } - const useNativeTabs = isMacintosh && windowConfig?.nativeTabs === true; + const useNativeTabs = isMacintosh && windowSettings?.nativeTabs === true; if (useNativeTabs) { options.tabbingIdentifier = this.productService.nameShort; // this opts in to sierra tabs } @@ -271,25 +302,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { this.registerListeners(); } - private pendingLoadConfig: INativeWindowConfiguration | undefined; - - private currentConfig: INativeWindowConfiguration | undefined; - get config(): INativeWindowConfiguration | undefined { return this.currentConfig; } - - private _id: number; - get id(): number { return this._id; } - - private _win: BrowserWindow; - get win(): BrowserWindow | null { return this._win; } - - get hasHiddenTitleBarStyle(): boolean { return !!this.hiddenTitleBarStyle; } - - get isExtensionDevelopmentHost(): boolean { return !!(this.currentConfig?.extensionDevelopmentPath); } - - get isExtensionTestHost(): boolean { return !!(this.currentConfig?.extensionTestsPath); } - - get isExtensionDevelopmentTestFromCli(): boolean { return this.isExtensionDevelopmentHost && this.isExtensionTestHost && !this.currentConfig?.debugId; } - setRepresentedFilename(filename: string): void { if (isMacintosh) { this._win.setRepresentedFilename(filename); @@ -345,15 +357,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._win.focus(); } - private _lastFocusTime = -1; - get lastFocusTime(): number { return this._lastFocusTime; } - - get backupPath(): string | undefined { return this.currentConfig?.backupPath; } - - get openedWorkspace(): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined { return this.currentConfig?.workspace; } - - get remoteAuthority(): string | undefined { return this.currentConfig?.remoteAuthority; } - private readyState = ReadyState.NONE; setReady(): void { @@ -680,7 +683,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } } - load(config: INativeWindowConfiguration, { isReload, disableExtensions }: { isReload?: boolean, disableExtensions?: boolean } = Object.create(null)): void { + load(configuration: INativeWindowConfiguration, { isReload, disableExtensions }: { isReload?: boolean, disableExtensions?: boolean } = Object.create(null)): void { // If this window was loaded before from the command line // (as indicated by VSCODE_CLI environment), make sure to @@ -688,15 +691,15 @@ export class CodeWindow extends Disposable implements ICodeWindow { // unless the new configuration context was also a CLI // (for https://github.com/microsoft/vscode/issues/108571) const currentUserEnv = (this.currentConfig ?? this.pendingLoadConfig)?.userEnv; - if (currentUserEnv && isLaunchedFromCli(currentUserEnv) && !isLaunchedFromCli(config.userEnv)) { - config.userEnv = { ...currentUserEnv, ...config.userEnv }; // still allow to override certain environment as passed in + if (currentUserEnv && isLaunchedFromCli(currentUserEnv) && !isLaunchedFromCli(configuration.userEnv)) { + configuration.userEnv = { ...currentUserEnv, ...configuration.userEnv }; // still allow to override certain environment as passed in } // If named pipe was instantiated for the crashpad_handler process, reuse the same // pipe for new app instances connecting to the original app instance. // Ref: https://github.com/microsoft/vscode/issues/115874 if (process.env['CHROME_CRASHPAD_PIPE_NAME']) { - Object.assign(config.userEnv, { + Object.assign(configuration.userEnv, { CHROME_CRASHPAD_PIPE_NAME: process.env['CHROME_CRASHPAD_PIPE_NAME'] }); } @@ -704,7 +707,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // If this is the first time the window is loaded, we associate the paths // directly with the window because we assume the loading will just work if (this.readyState === ReadyState.NONE) { - this.currentConfig = config; + this.currentConfig = configuration; } // Otherwise, the window is currently showing a folder and if there is an @@ -712,17 +715,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { // because the loading might be vetoed. Instead we associate it later when // the window load event has fired. else { - this.pendingLoadConfig = config; + this.pendingLoadConfig = configuration; this.readyState = ReadyState.NAVIGATING; } - // Add disable-extensions to the config, but do not preserve it on currentConfig or - // pendingLoadConfig so that it is applied only on this load - const configuration = { ...config }; - if (disableExtensions !== undefined) { - configuration['disable-extensions'] = disableExtensions; - } - // Clear Document Edited if needed if (this.isDocumentEdited()) { if (!isReload || !this.backupMainService.isHotExitEnabled()) { @@ -741,7 +737,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Load URL mark('code/willOpenNewWindow'); - this._win.loadURL(this.getUrl(configuration)); + this._win.loadURL(this.getUrl(configuration, disableExtensions)); // Make window visible if it did not open in N seconds because this indicates an error // Only do this when running out of sources and not when running tests @@ -816,64 +812,63 @@ export class CodeWindow extends Disposable implements ICodeWindow { return configuration.workspace; } - private getUrl(windowConfiguration: INativeWindowConfiguration): string { + private getUrl(baseConfig: INativeWindowConfiguration, disableExtensions: boolean | undefined): string { + + // Config is a combination of native CLI args and window configuration + const configuration: { [key: string]: unknown } = { ...this.environmentMainService.args, ...baseConfig }; + + // Add disable-extensions to the config, but do not preserve it on currentConfig or + // pendingLoadConfig so that it is applied only on this load + if (disableExtensions !== undefined) { + configuration['disable-extensions'] = disableExtensions; + } // Set window ID - windowConfiguration.windowId = this._win.id; - windowConfiguration.sessionId = `window:${this._win.id}`; - windowConfiguration.logLevel = this.logService.getLevel(); - windowConfiguration.logsPath = this.environmentMainService.logsPath; + configuration.windowId = this._win.id; + configuration.sessionId = `window:${this._win.id}`; + configuration.logLevel = this.logService.getLevel(); + configuration.logsPath = this.environmentMainService.logsPath; // Set zoomlevel const windowConfig = this.configurationService.getValue('window'); const zoomLevel = windowConfig?.zoomLevel; if (typeof zoomLevel === 'number') { - windowConfiguration.zoomLevel = zoomLevel; + configuration.zoomLevel = zoomLevel; } // Set fullscreen state - windowConfiguration.fullscreen = this.isFullScreen; + configuration.fullscreen = this.isFullScreen; // Set Accessibility Config - windowConfiguration.colorScheme = { + configuration.colorScheme = { dark: nativeTheme.shouldUseDarkColors, highContrast: nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors }; - windowConfiguration.autoDetectHighContrast = windowConfig?.autoDetectHighContrast ?? true; - windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled; + configuration.autoDetectHighContrast = windowConfig?.autoDetectHighContrast ?? true; + configuration.accessibilitySupport = app.accessibilitySupportEnabled; // Title style related - windowConfiguration.maximized = this._win.isMaximized(); + configuration.maximized = this._win.isMaximized(); // Dump Perf Counters - windowConfiguration.perfMarks = getMarks(); + configuration.perfMarks = getMarks(); // Parts splash - windowConfiguration.partsSplashPath = join(this.environmentMainService.userDataPath, 'rapid_render.json'); + configuration.partsSplashPath = join(this.environmentMainService.userDataPath, 'rapid_render.json'); // OS Info - windowConfiguration.os = { + configuration.os = { release: release() }; - // Config (combination of process.argv and window configuration) - const environment = parseArgs(process.argv, OPTIONS); - const config = Object.assign(environment, windowConfiguration) as unknown as { [key: string]: unknown }; - for (const key in config) { - const configValue = config[key]; - if (configValue === undefined || configValue === null || configValue === '' || configValue === false) { - delete config[key]; // only send over properties that have a true value - } - } - // In the unlikely event of the URL becoming larger than 2MB, remove parts of // it that are not under our control. Mainly, the user environment can be very // large depending on user configuration, so we can only remove it in that case. - let configUrl = this.doGetUrl(config); + let configUrl = this.doGetUrl(configuration); if (configUrl.length > CodeWindow.MAX_URL_LENGTH) { this.logService.warn('Application URL exceeds maximum of 2MB and was shortened.'); - configUrl = this.doGetUrl({ ...config, userEnv: undefined }); + configUrl = this.doGetUrl({ ...configuration, userEnv: undefined }); if (configUrl.length > CodeWindow.MAX_URL_LENGTH) { this.logService.error('Application URL exceeds maximum of 2MB and cannot be loaded.'); @@ -891,10 +886,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { workbench = 'vs/code/electron-browser/workbench/workbench.html'; } - return FileAccess - .asBrowserUri(workbench, require) - .with({ query: `config=${encodeURIComponent(JSON.stringify(config))}` }) - .toString(true); + return toWindowUrl(workbench, config); } serializeWindowState(): IWindowState { diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 255a84e5157..5b93768fbae 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -14,6 +14,7 @@ import { URI } from 'vs/base/common/uri'; import { Rectangle, BrowserWindow, WebContents } from 'electron'; import { IDisposable } from 'vs/base/common/lifecycle'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { FileAccess } from 'vs/base/common/network'; export const enum OpenContext { @@ -206,3 +207,28 @@ export interface IOpenConfiguration extends IBaseOpenConfiguration { } export interface IOpenEmptyConfiguration extends IBaseOpenConfiguration { } + +/** + * Utility to produce a window URL that includes the provided configuration + * as query parameter. + * + * @param modulePathToHtml path to the HTML to load + * @param config the configuration for the window + * @returns the url to use for loading + */ +export function toWindowUrl(modulePathToHtml: string, config: object, forceCodeFileUri?: boolean): string { + const configuration: { [key: string]: unknown } = { ...config }; + + // Remove values from config that are falsy to reduce the size of of the URL we create + for (const key in configuration) { + const value = configuration[key]; + if (value === undefined || value === null || value === '' || value === false) { + delete configuration[key]; + } + } + + return FileAccess + .asBrowserUri(modulePathToHtml, require, forceCodeFileUri) + .with({ query: `config=${encodeURIComponent(JSON.stringify(configuration))}` }) + .toString(true); +}