Path: blob/main/src/vs/code/electron-browser/workbench/workbench.ts
5237 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45/* eslint-disable no-restricted-globals */67(async function () {89// Add a perf entry right from the top10performance.mark('code/didStartRenderer');1112type ISandboxConfiguration = import('../../../base/parts/sandbox/common/sandboxTypes.js').ISandboxConfiguration;13type ILoadResult<M, T extends ISandboxConfiguration> = import('../../../platform/window/electron-browser/window.js').ILoadResult<M, T>;14type ILoadOptions<T extends ISandboxConfiguration> = import('../../../platform/window/electron-browser/window.js').ILoadOptions<T>;15type INativeWindowConfiguration = import('../../../platform/window/common/window.ts').INativeWindowConfiguration;16type IMainWindowSandboxGlobals = import('../../../base/parts/sandbox/electron-browser/globals.js').IMainWindowSandboxGlobals;17type IDesktopMain = import('../../../workbench/electron-browser/desktop.main.js').IDesktopMain;1819const preloadGlobals = (window as unknown as { vscode: IMainWindowSandboxGlobals }).vscode; // defined by preload.ts20const safeProcess = preloadGlobals.process;2122//#region Splash Screen Helpers2324function showSplash(configuration: INativeWindowConfiguration) {25performance.mark('code/willShowPartsSplash');26showDefaultSplash(configuration);27performance.mark('code/didShowPartsSplash');28}2930function showDefaultSplash(configuration: INativeWindowConfiguration) {31let data = configuration.partsSplash;32if (data) {33if (configuration.autoDetectHighContrast && configuration.colorScheme.highContrast) {34if ((configuration.colorScheme.dark && data.baseTheme !== 'hc-black') || (!configuration.colorScheme.dark && data.baseTheme !== 'hc-light')) {35data = undefined; // high contrast mode has been turned by the OS -> ignore stored colors and layouts36}37} else if (configuration.autoDetectColorScheme) {38if ((configuration.colorScheme.dark && data.baseTheme !== 'vs-dark') || (!configuration.colorScheme.dark && data.baseTheme !== 'vs')) {39data = undefined; // OS color scheme is tracked and has changed40}41}42}4344// developing an extension -> ignore stored layouts45if (data && configuration.extensionDevelopmentPath) {46data.layoutInfo = undefined;47}4849// minimal color configuration (works with or without persisted data)50let baseTheme;51let shellBackground;52let shellForeground;53if (data) {54baseTheme = data.baseTheme;55shellBackground = data.colorInfo.editorBackground;56shellForeground = data.colorInfo.foreground;57} else if (configuration.autoDetectHighContrast && configuration.colorScheme.highContrast) {58if (configuration.colorScheme.dark) {59baseTheme = 'hc-black';60shellBackground = '#000000';61shellForeground = '#FFFFFF';62} else {63baseTheme = 'hc-light';64shellBackground = '#FFFFFF';65shellForeground = '#000000';66}67} else if (configuration.autoDetectColorScheme) {68if (configuration.colorScheme.dark) {69baseTheme = 'vs-dark';70shellBackground = '#1E1E1E';71shellForeground = '#CCCCCC';72} else {73baseTheme = 'vs';74shellBackground = '#FFFFFF';75shellForeground = '#000000';76}77}7879const style = document.createElement('style');80style.className = 'initialShellColors';81window.document.head.appendChild(style);82style.textContent = `body { background-color: ${shellBackground}; color: ${shellForeground}; margin: 0; padding: 0; }`;8384// set zoom level as soon as possible85if (typeof data?.zoomLevel === 'number' && typeof preloadGlobals?.webFrame?.setZoomLevel === 'function') {86preloadGlobals.webFrame.setZoomLevel(data.zoomLevel);87}8889// restore parts if possible (we might not always store layout info)90if (data?.layoutInfo) {91const { layoutInfo, colorInfo } = data;9293const splash = document.createElement('div');94splash.id = 'monaco-parts-splash';95splash.className = baseTheme ?? 'vs-dark';9697if (layoutInfo.windowBorder && colorInfo.windowBorder) {98const borderElement = document.createElement('div');99borderElement.style.position = 'absolute';100borderElement.style.width = 'calc(100vw - 2px)';101borderElement.style.height = 'calc(100vh - 2px)';102borderElement.style.zIndex = '1'; // allow border above other elements103borderElement.style.border = `1px solid var(--window-border-color)`;104borderElement.style.setProperty('--window-border-color', colorInfo.windowBorder);105106if (layoutInfo.windowBorderRadius) {107borderElement.style.borderRadius = layoutInfo.windowBorderRadius;108}109110splash.appendChild(borderElement);111}112113if (layoutInfo.auxiliaryBarWidth === Number.MAX_SAFE_INTEGER) {114// if auxiliary bar is maximized, it goes as wide as the115// window width but leaving room for activity bar116layoutInfo.auxiliaryBarWidth = window.innerWidth - layoutInfo.activityBarWidth;117} else {118// otherwise adjust for other parts sizes if not maximized119layoutInfo.auxiliaryBarWidth = Math.min(layoutInfo.auxiliaryBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth + layoutInfo.sideBarWidth));120}121layoutInfo.sideBarWidth = Math.min(layoutInfo.sideBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth + layoutInfo.auxiliaryBarWidth));122123// part: title124if (layoutInfo.titleBarHeight > 0) {125const titleDiv = document.createElement('div');126titleDiv.style.position = 'absolute';127titleDiv.style.width = '100%';128titleDiv.style.height = `${layoutInfo.titleBarHeight}px`;129titleDiv.style.left = '0';130titleDiv.style.top = '0';131titleDiv.style.backgroundColor = `${colorInfo.titleBarBackground}`;132(titleDiv.style as CSSStyleDeclaration & { '-webkit-app-region': string })['-webkit-app-region'] = 'drag';133splash.appendChild(titleDiv);134135if (colorInfo.titleBarBorder) {136const titleBorder = document.createElement('div');137titleBorder.style.position = 'absolute';138titleBorder.style.width = '100%';139titleBorder.style.height = '1px';140titleBorder.style.left = '0';141titleBorder.style.bottom = '0';142titleBorder.style.borderBottom = `1px solid ${colorInfo.titleBarBorder}`;143titleDiv.appendChild(titleBorder);144}145}146147// part: activity bar148if (layoutInfo.activityBarWidth > 0) {149const activityDiv = document.createElement('div');150activityDiv.style.position = 'absolute';151activityDiv.style.width = `${layoutInfo.activityBarWidth}px`;152activityDiv.style.height = `calc(100% - ${layoutInfo.titleBarHeight + layoutInfo.statusBarHeight}px)`;153activityDiv.style.top = `${layoutInfo.titleBarHeight}px`;154if (layoutInfo.sideBarSide === 'left') {155activityDiv.style.left = '0';156} else {157activityDiv.style.right = '0';158}159activityDiv.style.backgroundColor = `${colorInfo.activityBarBackground}`;160splash.appendChild(activityDiv);161162if (colorInfo.activityBarBorder) {163const activityBorderDiv = document.createElement('div');164activityBorderDiv.style.position = 'absolute';165activityBorderDiv.style.width = '1px';166activityBorderDiv.style.height = '100%';167activityBorderDiv.style.top = '0';168if (layoutInfo.sideBarSide === 'left') {169activityBorderDiv.style.right = '0';170activityBorderDiv.style.borderRight = `1px solid ${colorInfo.activityBarBorder}`;171} else {172activityBorderDiv.style.left = '0';173activityBorderDiv.style.borderLeft = `1px solid ${colorInfo.activityBarBorder}`;174}175activityDiv.appendChild(activityBorderDiv);176}177}178179// part: side bar180if (layoutInfo.sideBarWidth > 0) {181const sideDiv = document.createElement('div');182sideDiv.style.position = 'absolute';183sideDiv.style.width = `${layoutInfo.sideBarWidth}px`;184sideDiv.style.height = `calc(100% - ${layoutInfo.titleBarHeight + layoutInfo.statusBarHeight}px)`;185sideDiv.style.top = `${layoutInfo.titleBarHeight}px`;186if (layoutInfo.sideBarSide === 'left') {187sideDiv.style.left = `${layoutInfo.activityBarWidth}px`;188} else {189sideDiv.style.right = `${layoutInfo.activityBarWidth}px`;190}191sideDiv.style.backgroundColor = `${colorInfo.sideBarBackground}`;192splash.appendChild(sideDiv);193194if (colorInfo.sideBarBorder) {195const sideBorderDiv = document.createElement('div');196sideBorderDiv.style.position = 'absolute';197sideBorderDiv.style.width = '1px';198sideBorderDiv.style.height = '100%';199sideBorderDiv.style.top = '0';200sideBorderDiv.style.right = '0';201if (layoutInfo.sideBarSide === 'left') {202sideBorderDiv.style.borderRight = `1px solid ${colorInfo.sideBarBorder}`;203} else {204sideBorderDiv.style.left = '0';205sideBorderDiv.style.borderLeft = `1px solid ${colorInfo.sideBarBorder}`;206}207sideDiv.appendChild(sideBorderDiv);208}209}210211// part: auxiliary sidebar212if (layoutInfo.auxiliaryBarWidth > 0) {213const auxSideDiv = document.createElement('div');214auxSideDiv.style.position = 'absolute';215auxSideDiv.style.width = `${layoutInfo.auxiliaryBarWidth}px`;216auxSideDiv.style.height = `calc(100% - ${layoutInfo.titleBarHeight + layoutInfo.statusBarHeight}px)`;217auxSideDiv.style.top = `${layoutInfo.titleBarHeight}px`;218if (layoutInfo.sideBarSide === 'left') {219auxSideDiv.style.right = '0';220} else {221auxSideDiv.style.left = '0';222}223auxSideDiv.style.backgroundColor = `${colorInfo.sideBarBackground}`;224splash.appendChild(auxSideDiv);225226if (colorInfo.sideBarBorder) {227const auxSideBorderDiv = document.createElement('div');228auxSideBorderDiv.style.position = 'absolute';229auxSideBorderDiv.style.width = '1px';230auxSideBorderDiv.style.height = '100%';231auxSideBorderDiv.style.top = '0';232if (layoutInfo.sideBarSide === 'left') {233auxSideBorderDiv.style.left = '0';234auxSideBorderDiv.style.borderLeft = `1px solid ${colorInfo.sideBarBorder}`;235} else {236auxSideBorderDiv.style.right = '0';237auxSideBorderDiv.style.borderRight = `1px solid ${colorInfo.sideBarBorder}`;238}239auxSideDiv.appendChild(auxSideBorderDiv);240}241}242243// part: statusbar244if (layoutInfo.statusBarHeight > 0) {245const statusDiv = document.createElement('div');246statusDiv.style.position = 'absolute';247statusDiv.style.width = '100%';248statusDiv.style.height = `${layoutInfo.statusBarHeight}px`;249statusDiv.style.bottom = '0';250statusDiv.style.left = '0';251if (configuration.workspace && colorInfo.statusBarBackground) {252statusDiv.style.backgroundColor = colorInfo.statusBarBackground;253} else if (!configuration.workspace && colorInfo.statusBarNoFolderBackground) {254statusDiv.style.backgroundColor = colorInfo.statusBarNoFolderBackground;255}256splash.appendChild(statusDiv);257258if (colorInfo.statusBarBorder) {259const statusBorderDiv = document.createElement('div');260statusBorderDiv.style.position = 'absolute';261statusBorderDiv.style.width = '100%';262statusBorderDiv.style.height = '1px';263statusBorderDiv.style.top = '0';264statusBorderDiv.style.borderTop = `1px solid ${colorInfo.statusBarBorder}`;265statusDiv.appendChild(statusBorderDiv);266}267}268269window.document.body.appendChild(splash);270}271}272273//#endregion274275//#region Window Helpers276277async function load<M, T extends ISandboxConfiguration>(options: ILoadOptions<T>): Promise<ILoadResult<M, T>> {278279// Window Configuration from Preload Script280const configuration = await resolveWindowConfiguration<T>();281282// Signal before import()283options?.beforeImport?.(configuration);284285// Developer settings286const { enableDeveloperKeybindings, removeDeveloperKeybindingsAfterLoad, developerDeveloperKeybindingsDisposable, forceDisableShowDevtoolsOnError } = setupDeveloperKeybindings(configuration, options);287288// NLS289setupNLS<T>(configuration);290291// Compute base URL and set as global292const baseUrl = new URL(`${fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out/`);293globalThis._VSCODE_FILE_ROOT = baseUrl.toString();294295// Dev only: CSS import map tricks296setupCSSImportMaps<T>(configuration, baseUrl);297298// ESM Import299try {300let workbenchUrl: string;301if (!!safeProcess.env['VSCODE_DEV'] && globalThis._VSCODE_USE_RELATIVE_IMPORTS) {302workbenchUrl = '../../../workbench/workbench.desktop.main.js'; // for dev purposes only303} else {304workbenchUrl = new URL(`vs/workbench/workbench.desktop.main.js`, baseUrl).href;305}306307const result = await import(workbenchUrl);308if (developerDeveloperKeybindingsDisposable && removeDeveloperKeybindingsAfterLoad) {309developerDeveloperKeybindingsDisposable();310}311312return { result, configuration };313} catch (error) {314onUnexpectedError(error, enableDeveloperKeybindings && !forceDisableShowDevtoolsOnError);315316throw error;317}318}319320async function resolveWindowConfiguration<T extends ISandboxConfiguration>() {321const timeout = setTimeout(() => { console.error(`[resolve window config] Could not resolve window configuration within 10 seconds, but will continue to wait...`); }, 10000);322performance.mark('code/willWaitForWindowConfig');323324const configuration = await preloadGlobals.context.resolveConfiguration() as T;325performance.mark('code/didWaitForWindowConfig');326327clearTimeout(timeout);328329return configuration;330}331332function setupDeveloperKeybindings<T extends ISandboxConfiguration>(configuration: T, options: ILoadOptions<T>) {333const {334forceEnableDeveloperKeybindings,335disallowReloadKeybinding,336removeDeveloperKeybindingsAfterLoad,337forceDisableShowDevtoolsOnError338} = typeof options?.configureDeveloperSettings === 'function' ? options.configureDeveloperSettings(configuration) : {339forceEnableDeveloperKeybindings: false,340disallowReloadKeybinding: false,341removeDeveloperKeybindingsAfterLoad: false,342forceDisableShowDevtoolsOnError: false343};344345const isDev = !!safeProcess.env['VSCODE_DEV'];346const enableDeveloperKeybindings = Boolean(isDev || forceEnableDeveloperKeybindings);347let developerDeveloperKeybindingsDisposable: Function | undefined = undefined;348if (enableDeveloperKeybindings) {349developerDeveloperKeybindingsDisposable = registerDeveloperKeybindings(disallowReloadKeybinding);350}351352return {353enableDeveloperKeybindings,354removeDeveloperKeybindingsAfterLoad,355developerDeveloperKeybindingsDisposable,356forceDisableShowDevtoolsOnError357};358}359360function registerDeveloperKeybindings(disallowReloadKeybinding: boolean | undefined): Function {361const ipcRenderer = preloadGlobals.ipcRenderer;362363const extractKey =364function (e: KeyboardEvent) {365return [366e.ctrlKey ? 'ctrl-' : '',367e.metaKey ? 'meta-' : '',368e.altKey ? 'alt-' : '',369e.shiftKey ? 'shift-' : '',370e.keyCode371].join('');372};373374// Devtools & reload support375const TOGGLE_DEV_TOOLS_KB = (safeProcess.platform === 'darwin' ? 'meta-alt-73' : 'ctrl-shift-73'); // mac: Cmd-Alt-I, rest: Ctrl-Shift-I376const TOGGLE_DEV_TOOLS_KB_ALT = '123'; // F12377const RELOAD_KB = (safeProcess.platform === 'darwin' ? 'meta-82' : 'ctrl-82'); // mac: Cmd-R, rest: Ctrl-R378379let listener: ((e: KeyboardEvent) => void) | undefined = function (e) {380const key = extractKey(e);381if (key === TOGGLE_DEV_TOOLS_KB || key === TOGGLE_DEV_TOOLS_KB_ALT) {382ipcRenderer.send('vscode:toggleDevTools');383} else if (key === RELOAD_KB && !disallowReloadKeybinding) {384ipcRenderer.send('vscode:reloadWindow');385}386};387388window.addEventListener('keydown', listener);389390return function () {391if (listener) {392window.removeEventListener('keydown', listener);393listener = undefined;394}395};396}397398function setupNLS<T extends ISandboxConfiguration>(configuration: T): void {399globalThis._VSCODE_NLS_MESSAGES = configuration.nls.messages;400globalThis._VSCODE_NLS_LANGUAGE = configuration.nls.language;401402let language = configuration.nls.language || 'en';403if (language === 'zh-tw') {404language = 'zh-Hant';405} else if (language === 'zh-cn') {406language = 'zh-Hans';407}408409window.document.documentElement.setAttribute('lang', language);410}411412function onUnexpectedError(error: string | Error, showDevtoolsOnError: boolean): void {413if (showDevtoolsOnError) {414const ipcRenderer = preloadGlobals.ipcRenderer;415ipcRenderer.send('vscode:openDevTools');416}417418console.error(`[uncaught exception]: ${error}`);419420if (error && typeof error !== 'string' && error.stack) {421console.error(error.stack);422}423}424425function fileUriFromPath(path: string, config: { isWindows?: boolean; scheme?: string; fallbackAuthority?: string }): string {426427// Since we are building a URI, we normalize any backslash428// to slashes and we ensure that the path begins with a '/'.429let pathName = path.replace(/\\/g, '/');430if (pathName.length > 0 && pathName.charAt(0) !== '/') {431pathName = `/${pathName}`;432}433434let uri: string;435436// Windows: in order to support UNC paths (which start with '//')437// that have their own authority, we do not use the provided authority438// but rather preserve it.439if (config.isWindows && pathName.startsWith('//')) {440uri = encodeURI(`${config.scheme || 'file'}:${pathName}`);441}442443// Otherwise we optionally add the provided authority if specified444else {445uri = encodeURI(`${config.scheme || 'file'}://${config.fallbackAuthority || ''}${pathName}`);446}447448return uri.replace(/#/g, '%23');449}450451function setupCSSImportMaps<T extends ISandboxConfiguration>(configuration: T, baseUrl: URL) {452453// DEV ---------------------------------------------------------------------------------------454// DEV: This is for development and enables loading CSS via import-statements via import-maps.455// DEV: For each CSS modules that we have we defined an entry in the import map that maps to456// DEV: a blob URL that loads the CSS via a dynamic @import-rule.457// DEV ---------------------------------------------------------------------------------------458459if (globalThis._VSCODE_DISABLE_CSS_IMPORT_MAP) {460return; // disabled in certain development setups461}462463if (Array.isArray(configuration.cssModules) && configuration.cssModules.length > 0) {464performance.mark('code/willAddCssLoader');465466globalThis._VSCODE_CSS_LOAD = function (url) {467const link = document.createElement('link');468link.setAttribute('rel', 'stylesheet');469link.setAttribute('type', 'text/css');470link.setAttribute('href', url);471472window.document.head.appendChild(link);473};474475const importMap: { imports: Record<string, string> } = { imports: {} };476for (const cssModule of configuration.cssModules) {477const cssUrl = new URL(cssModule, baseUrl).href;478const jsSrc = `globalThis._VSCODE_CSS_LOAD('${cssUrl}');\n`;479const blob = new Blob([jsSrc], { type: 'application/javascript' });480importMap.imports[cssUrl] = URL.createObjectURL(blob);481}482483const ttp = window.trustedTypes?.createPolicy('vscode-bootstrapImportMap', { createScript(value) { return value; }, });484const importMapSrc = JSON.stringify(importMap, undefined, 2);485const importMapScript = document.createElement('script');486importMapScript.type = 'importmap';487importMapScript.setAttribute('nonce', '0c6a828f1297');488// @ts-expect-error489importMapScript.textContent = ttp?.createScript(importMapSrc) ?? importMapSrc;490window.document.head.appendChild(importMapScript);491492performance.mark('code/didAddCssLoader');493}494}495496//#endregion497498const { result, configuration } = await load<IDesktopMain, INativeWindowConfiguration>(499{500configureDeveloperSettings: function (windowConfig) {501return {502// disable automated devtools opening on error when running extension tests503// as this can lead to nondeterministic test execution (devtools steals focus)504forceDisableShowDevtoolsOnError: typeof windowConfig.extensionTestsPath === 'string' || windowConfig['enable-smoke-test-driver'] === true,505// enable devtools keybindings in extension development window506forceEnableDeveloperKeybindings: Array.isArray(windowConfig.extensionDevelopmentPath) && windowConfig.extensionDevelopmentPath.length > 0,507removeDeveloperKeybindingsAfterLoad: true508};509},510beforeImport: function (windowConfig) {511512// Show our splash as early as possible513showSplash(windowConfig);514515// Code windows have a `vscodeWindowId` property to identify them516Object.defineProperty(window, 'vscodeWindowId', {517get: () => windowConfig.windowId518});519520// It looks like browsers only lazily enable521// the <canvas> element when needed. Since we522// leverage canvas elements in our code in many523// locations, we try to help the browser to524// initialize canvas when it is idle, right525// before we wait for the scripts to be loaded.526window.requestIdleCallback(() => {527const canvas = document.createElement('canvas');528const context = canvas.getContext('2d');529context?.clearRect(0, 0, canvas.width, canvas.height);530canvas.remove();531}, { timeout: 50 });532533// Track import() perf534performance.mark('code/willLoadWorkbenchMain');535}536}537);538539// Mark start of workbench540performance.mark('code/didLoadWorkbenchMain');541542// Load workbench543result.main(configuration);544}());545546547