Path: blob/main/src/vs/code/electron-browser/workbench/workbench.ts
3294 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: IMainWindowSandboxGlobals = (window as any).vscode; // defined by preload.ts20const safeProcess = preloadGlobals.process;2122//#region Splash Screen Helpers2324function showSplash(configuration: INativeWindowConfiguration) {25performance.mark('code/willShowPartsSplash');2627let data = configuration.partsSplash;28if (data) {29if (configuration.autoDetectHighContrast && configuration.colorScheme.highContrast) {30if ((configuration.colorScheme.dark && data.baseTheme !== 'hc-black') || (!configuration.colorScheme.dark && data.baseTheme !== 'hc-light')) {31data = undefined; // high contrast mode has been turned by the OS -> ignore stored colors and layouts32}33} else if (configuration.autoDetectColorScheme) {34if ((configuration.colorScheme.dark && data.baseTheme !== 'vs-dark') || (!configuration.colorScheme.dark && data.baseTheme !== 'vs')) {35data = undefined; // OS color scheme is tracked and has changed36}37}38}3940// developing an extension -> ignore stored layouts41if (data && configuration.extensionDevelopmentPath) {42data.layoutInfo = undefined;43}4445// minimal color configuration (works with or without persisted data)46let baseTheme;47let shellBackground;48let shellForeground;49if (data) {50baseTheme = data.baseTheme;51shellBackground = data.colorInfo.editorBackground;52shellForeground = data.colorInfo.foreground;53} else if (configuration.autoDetectHighContrast && configuration.colorScheme.highContrast) {54if (configuration.colorScheme.dark) {55baseTheme = 'hc-black';56shellBackground = '#000000';57shellForeground = '#FFFFFF';58} else {59baseTheme = 'hc-light';60shellBackground = '#FFFFFF';61shellForeground = '#000000';62}63} else if (configuration.autoDetectColorScheme) {64if (configuration.colorScheme.dark) {65baseTheme = 'vs-dark';66shellBackground = '#1E1E1E';67shellForeground = '#CCCCCC';68} else {69baseTheme = 'vs';70shellBackground = '#FFFFFF';71shellForeground = '#000000';72}73}7475const style = document.createElement('style');76style.className = 'initialShellColors';77window.document.head.appendChild(style);78style.textContent = `body { background-color: ${shellBackground}; color: ${shellForeground}; margin: 0; padding: 0; }`;7980// set zoom level as soon as possible81if (typeof data?.zoomLevel === 'number' && typeof preloadGlobals?.webFrame?.setZoomLevel === 'function') {82preloadGlobals.webFrame.setZoomLevel(data.zoomLevel);83}8485// restore parts if possible (we might not always store layout info)86if (data?.layoutInfo) {87const { layoutInfo, colorInfo } = data;8889const splash = document.createElement('div');90splash.id = 'monaco-parts-splash';91splash.className = baseTheme ?? 'vs-dark';9293if (layoutInfo.windowBorder && colorInfo.windowBorder) {94const borderElement = document.createElement('div');95borderElement.style.position = 'absolute';96borderElement.style.width = 'calc(100vw - 2px)';97borderElement.style.height = 'calc(100vh - 2px)';98borderElement.style.zIndex = '1'; // allow border above other elements99borderElement.style.border = `1px solid var(--window-border-color)`;100borderElement.style.setProperty('--window-border-color', colorInfo.windowBorder);101102if (layoutInfo.windowBorderRadius) {103borderElement.style.borderRadius = layoutInfo.windowBorderRadius;104}105106splash.appendChild(borderElement);107}108109if (layoutInfo.auxiliaryBarWidth === Number.MAX_SAFE_INTEGER) {110// if auxiliary bar is maximized, it goes as wide as the111// window width but leaving room for activity bar112layoutInfo.auxiliaryBarWidth = window.innerWidth - layoutInfo.activityBarWidth;113} else {114// otherwise adjust for other parts sizes if not maximized115layoutInfo.auxiliaryBarWidth = Math.min(layoutInfo.auxiliaryBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth + layoutInfo.sideBarWidth));116}117layoutInfo.sideBarWidth = Math.min(layoutInfo.sideBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth + layoutInfo.auxiliaryBarWidth));118119// part: title120if (layoutInfo.titleBarHeight > 0) {121const titleDiv = document.createElement('div');122titleDiv.style.position = 'absolute';123titleDiv.style.width = '100%';124titleDiv.style.height = `${layoutInfo.titleBarHeight}px`;125titleDiv.style.left = '0';126titleDiv.style.top = '0';127titleDiv.style.backgroundColor = `${colorInfo.titleBarBackground}`;128(titleDiv.style as any)['-webkit-app-region'] = 'drag';129splash.appendChild(titleDiv);130131if (colorInfo.titleBarBorder) {132const titleBorder = document.createElement('div');133titleBorder.style.position = 'absolute';134titleBorder.style.width = '100%';135titleBorder.style.height = '1px';136titleBorder.style.left = '0';137titleBorder.style.bottom = '0';138titleBorder.style.borderBottom = `1px solid ${colorInfo.titleBarBorder}`;139titleDiv.appendChild(titleBorder);140}141}142143// part: activity bar144if (layoutInfo.activityBarWidth > 0) {145const activityDiv = document.createElement('div');146activityDiv.style.position = 'absolute';147activityDiv.style.width = `${layoutInfo.activityBarWidth}px`;148activityDiv.style.height = `calc(100% - ${layoutInfo.titleBarHeight + layoutInfo.statusBarHeight}px)`;149activityDiv.style.top = `${layoutInfo.titleBarHeight}px`;150if (layoutInfo.sideBarSide === 'left') {151activityDiv.style.left = '0';152} else {153activityDiv.style.right = '0';154}155activityDiv.style.backgroundColor = `${colorInfo.activityBarBackground}`;156splash.appendChild(activityDiv);157158if (colorInfo.activityBarBorder) {159const activityBorderDiv = document.createElement('div');160activityBorderDiv.style.position = 'absolute';161activityBorderDiv.style.width = '1px';162activityBorderDiv.style.height = '100%';163activityBorderDiv.style.top = '0';164if (layoutInfo.sideBarSide === 'left') {165activityBorderDiv.style.right = '0';166activityBorderDiv.style.borderRight = `1px solid ${colorInfo.activityBarBorder}`;167} else {168activityBorderDiv.style.left = '0';169activityBorderDiv.style.borderLeft = `1px solid ${colorInfo.activityBarBorder}`;170}171activityDiv.appendChild(activityBorderDiv);172}173}174175// part: side bar176if (layoutInfo.sideBarWidth > 0) {177const sideDiv = document.createElement('div');178sideDiv.style.position = 'absolute';179sideDiv.style.width = `${layoutInfo.sideBarWidth}px`;180sideDiv.style.height = `calc(100% - ${layoutInfo.titleBarHeight + layoutInfo.statusBarHeight}px)`;181sideDiv.style.top = `${layoutInfo.titleBarHeight}px`;182if (layoutInfo.sideBarSide === 'left') {183sideDiv.style.left = `${layoutInfo.activityBarWidth}px`;184} else {185sideDiv.style.right = `${layoutInfo.activityBarWidth}px`;186}187sideDiv.style.backgroundColor = `${colorInfo.sideBarBackground}`;188splash.appendChild(sideDiv);189190if (colorInfo.sideBarBorder) {191const sideBorderDiv = document.createElement('div');192sideBorderDiv.style.position = 'absolute';193sideBorderDiv.style.width = '1px';194sideBorderDiv.style.height = '100%';195sideBorderDiv.style.top = '0';196sideBorderDiv.style.right = '0';197if (layoutInfo.sideBarSide === 'left') {198sideBorderDiv.style.borderRight = `1px solid ${colorInfo.sideBarBorder}`;199} else {200sideBorderDiv.style.left = '0';201sideBorderDiv.style.borderLeft = `1px solid ${colorInfo.sideBarBorder}`;202}203sideDiv.appendChild(sideBorderDiv);204}205}206207// part: auxiliary sidebar208if (layoutInfo.auxiliaryBarWidth > 0) {209const auxSideDiv = document.createElement('div');210auxSideDiv.style.position = 'absolute';211auxSideDiv.style.width = `${layoutInfo.auxiliaryBarWidth}px`;212auxSideDiv.style.height = `calc(100% - ${layoutInfo.titleBarHeight + layoutInfo.statusBarHeight}px)`;213auxSideDiv.style.top = `${layoutInfo.titleBarHeight}px`;214if (layoutInfo.sideBarSide === 'left') {215auxSideDiv.style.right = '0';216} else {217auxSideDiv.style.left = '0';218}219auxSideDiv.style.backgroundColor = `${colorInfo.sideBarBackground}`;220splash.appendChild(auxSideDiv);221222if (colorInfo.sideBarBorder) {223const auxSideBorderDiv = document.createElement('div');224auxSideBorderDiv.style.position = 'absolute';225auxSideBorderDiv.style.width = '1px';226auxSideBorderDiv.style.height = '100%';227auxSideBorderDiv.style.top = '0';228if (layoutInfo.sideBarSide === 'left') {229auxSideBorderDiv.style.left = '0';230auxSideBorderDiv.style.borderLeft = `1px solid ${colorInfo.sideBarBorder}`;231} else {232auxSideBorderDiv.style.right = '0';233auxSideBorderDiv.style.borderRight = `1px solid ${colorInfo.sideBarBorder}`;234}235auxSideDiv.appendChild(auxSideBorderDiv);236}237}238239// part: statusbar240if (layoutInfo.statusBarHeight > 0) {241const statusDiv = document.createElement('div');242statusDiv.style.position = 'absolute';243statusDiv.style.width = '100%';244statusDiv.style.height = `${layoutInfo.statusBarHeight}px`;245statusDiv.style.bottom = '0';246statusDiv.style.left = '0';247if (configuration.workspace && colorInfo.statusBarBackground) {248statusDiv.style.backgroundColor = colorInfo.statusBarBackground;249} else if (!configuration.workspace && colorInfo.statusBarNoFolderBackground) {250statusDiv.style.backgroundColor = colorInfo.statusBarNoFolderBackground;251}252splash.appendChild(statusDiv);253254if (colorInfo.statusBarBorder) {255const statusBorderDiv = document.createElement('div');256statusBorderDiv.style.position = 'absolute';257statusBorderDiv.style.width = '100%';258statusBorderDiv.style.height = '1px';259statusBorderDiv.style.top = '0';260statusBorderDiv.style.borderTop = `1px solid ${colorInfo.statusBarBorder}`;261statusDiv.appendChild(statusBorderDiv);262}263}264265window.document.body.appendChild(splash);266}267268performance.mark('code/didShowPartsSplash');269}270271//#endregion272273//#region Window Helpers274275async function load<M, T extends ISandboxConfiguration>(esModule: string, options: ILoadOptions<T>): Promise<ILoadResult<M, T>> {276277// Window Configuration from Preload Script278const configuration = await resolveWindowConfiguration<T>();279280// Signal before import()281options?.beforeImport?.(configuration);282283// Developer settings284const { enableDeveloperKeybindings, removeDeveloperKeybindingsAfterLoad, developerDeveloperKeybindingsDisposable, forceDisableShowDevtoolsOnError } = setupDeveloperKeybindings(configuration, options);285286// NLS287setupNLS<T>(configuration);288289// Compute base URL and set as global290const baseUrl = new URL(`${fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out/`);291globalThis._VSCODE_FILE_ROOT = baseUrl.toString();292293// Dev only: CSS import map tricks294setupCSSImportMaps<T>(configuration, baseUrl);295296// ESM Import297try {298const result = await import(new URL(`${esModule}.js`, baseUrl).href);299300if (developerDeveloperKeybindingsDisposable && removeDeveloperKeybindingsAfterLoad) {301developerDeveloperKeybindingsDisposable();302}303304return { result, configuration };305} catch (error) {306onUnexpectedError(error, enableDeveloperKeybindings && !forceDisableShowDevtoolsOnError);307308throw error;309}310}311312async function resolveWindowConfiguration<T extends ISandboxConfiguration>() {313const timeout = setTimeout(() => { console.error(`[resolve window config] Could not resolve window configuration within 10 seconds, but will continue to wait...`); }, 10000);314performance.mark('code/willWaitForWindowConfig');315316const configuration = await preloadGlobals.context.resolveConfiguration() as T;317performance.mark('code/didWaitForWindowConfig');318319clearTimeout(timeout);320321return configuration;322}323324function setupDeveloperKeybindings<T extends ISandboxConfiguration>(configuration: T, options: ILoadOptions<T>) {325const {326forceEnableDeveloperKeybindings,327disallowReloadKeybinding,328removeDeveloperKeybindingsAfterLoad,329forceDisableShowDevtoolsOnError330} = typeof options?.configureDeveloperSettings === 'function' ? options.configureDeveloperSettings(configuration) : {331forceEnableDeveloperKeybindings: false,332disallowReloadKeybinding: false,333removeDeveloperKeybindingsAfterLoad: false,334forceDisableShowDevtoolsOnError: false335};336337const isDev = !!safeProcess.env['VSCODE_DEV'];338const enableDeveloperKeybindings = Boolean(isDev || forceEnableDeveloperKeybindings);339let developerDeveloperKeybindingsDisposable: Function | undefined = undefined;340if (enableDeveloperKeybindings) {341developerDeveloperKeybindingsDisposable = registerDeveloperKeybindings(disallowReloadKeybinding);342}343344return {345enableDeveloperKeybindings,346removeDeveloperKeybindingsAfterLoad,347developerDeveloperKeybindingsDisposable,348forceDisableShowDevtoolsOnError349};350}351352function registerDeveloperKeybindings(disallowReloadKeybinding: boolean | undefined): Function {353const ipcRenderer = preloadGlobals.ipcRenderer;354355const extractKey =356function (e: KeyboardEvent) {357return [358e.ctrlKey ? 'ctrl-' : '',359e.metaKey ? 'meta-' : '',360e.altKey ? 'alt-' : '',361e.shiftKey ? 'shift-' : '',362e.keyCode363].join('');364};365366// Devtools & reload support367const TOGGLE_DEV_TOOLS_KB = (safeProcess.platform === 'darwin' ? 'meta-alt-73' : 'ctrl-shift-73'); // mac: Cmd-Alt-I, rest: Ctrl-Shift-I368const TOGGLE_DEV_TOOLS_KB_ALT = '123'; // F12369const RELOAD_KB = (safeProcess.platform === 'darwin' ? 'meta-82' : 'ctrl-82'); // mac: Cmd-R, rest: Ctrl-R370371let listener: ((e: KeyboardEvent) => void) | undefined = function (e) {372const key = extractKey(e);373if (key === TOGGLE_DEV_TOOLS_KB || key === TOGGLE_DEV_TOOLS_KB_ALT) {374ipcRenderer.send('vscode:toggleDevTools');375} else if (key === RELOAD_KB && !disallowReloadKeybinding) {376ipcRenderer.send('vscode:reloadWindow');377}378};379380window.addEventListener('keydown', listener);381382return function () {383if (listener) {384window.removeEventListener('keydown', listener);385listener = undefined;386}387};388}389390function setupNLS<T extends ISandboxConfiguration>(configuration: T): void {391globalThis._VSCODE_NLS_MESSAGES = configuration.nls.messages;392globalThis._VSCODE_NLS_LANGUAGE = configuration.nls.language;393394let language = configuration.nls.language || 'en';395if (language === 'zh-tw') {396language = 'zh-Hant';397} else if (language === 'zh-cn') {398language = 'zh-Hans';399}400401window.document.documentElement.setAttribute('lang', language);402}403404function onUnexpectedError(error: string | Error, showDevtoolsOnError: boolean): void {405if (showDevtoolsOnError) {406const ipcRenderer = preloadGlobals.ipcRenderer;407ipcRenderer.send('vscode:openDevTools');408}409410console.error(`[uncaught exception]: ${error}`);411412if (error && typeof error !== 'string' && error.stack) {413console.error(error.stack);414}415}416417function fileUriFromPath(path: string, config: { isWindows?: boolean; scheme?: string; fallbackAuthority?: string }): string {418419// Since we are building a URI, we normalize any backslash420// to slashes and we ensure that the path begins with a '/'.421let pathName = path.replace(/\\/g, '/');422if (pathName.length > 0 && pathName.charAt(0) !== '/') {423pathName = `/${pathName}`;424}425426let uri: string;427428// Windows: in order to support UNC paths (which start with '//')429// that have their own authority, we do not use the provided authority430// but rather preserve it.431if (config.isWindows && pathName.startsWith('//')) {432uri = encodeURI(`${config.scheme || 'file'}:${pathName}`);433}434435// Otherwise we optionally add the provided authority if specified436else {437uri = encodeURI(`${config.scheme || 'file'}://${config.fallbackAuthority || ''}${pathName}`);438}439440return uri.replace(/#/g, '%23');441}442443function setupCSSImportMaps<T extends ISandboxConfiguration>(configuration: T, baseUrl: URL) {444445// DEV ---------------------------------------------------------------------------------------446// DEV: This is for development and enables loading CSS via import-statements via import-maps.447// DEV: For each CSS modules that we have we defined an entry in the import map that maps to448// DEV: a blob URL that loads the CSS via a dynamic @import-rule.449// DEV ---------------------------------------------------------------------------------------450451if (Array.isArray(configuration.cssModules) && configuration.cssModules.length > 0) {452performance.mark('code/willAddCssLoader');453454globalThis._VSCODE_CSS_LOAD = function (url) {455const link = document.createElement('link');456link.setAttribute('rel', 'stylesheet');457link.setAttribute('type', 'text/css');458link.setAttribute('href', url);459460window.document.head.appendChild(link);461};462463const importMap: { imports: Record<string, string> } = { imports: {} };464for (const cssModule of configuration.cssModules) {465const cssUrl = new URL(cssModule, baseUrl).href;466const jsSrc = `globalThis._VSCODE_CSS_LOAD('${cssUrl}');\n`;467const blob = new Blob([jsSrc], { type: 'application/javascript' });468importMap.imports[cssUrl] = URL.createObjectURL(blob);469}470471const ttp = window.trustedTypes?.createPolicy('vscode-bootstrapImportMap', { createScript(value) { return value; }, });472const importMapSrc = JSON.stringify(importMap, undefined, 2);473const importMapScript = document.createElement('script');474importMapScript.type = 'importmap';475importMapScript.setAttribute('nonce', '0c6a828f1297');476// @ts-ignore477importMapScript.textContent = ttp?.createScript(importMapSrc) ?? importMapSrc;478window.document.head.appendChild(importMapScript);479480performance.mark('code/didAddCssLoader');481}482}483484//#endregion485486const { result, configuration } = await load<IDesktopMain, INativeWindowConfiguration>('vs/workbench/workbench.desktop.main',487{488configureDeveloperSettings: function (windowConfig) {489return {490// disable automated devtools opening on error when running extension tests491// as this can lead to nondeterministic test execution (devtools steals focus)492forceDisableShowDevtoolsOnError: typeof windowConfig.extensionTestsPath === 'string' || windowConfig['enable-smoke-test-driver'] === true,493// enable devtools keybindings in extension development window494forceEnableDeveloperKeybindings: Array.isArray(windowConfig.extensionDevelopmentPath) && windowConfig.extensionDevelopmentPath.length > 0,495removeDeveloperKeybindingsAfterLoad: true496};497},498beforeImport: function (windowConfig) {499500// Show our splash as early as possible501showSplash(windowConfig);502503// Code windows have a `vscodeWindowId` property to identify them504Object.defineProperty(window, 'vscodeWindowId', {505get: () => windowConfig.windowId506});507508// It looks like browsers only lazily enable509// the <canvas> element when needed. Since we510// leverage canvas elements in our code in many511// locations, we try to help the browser to512// initialize canvas when it is idle, right513// before we wait for the scripts to be loaded.514window.requestIdleCallback(() => {515const canvas = document.createElement('canvas');516const context = canvas.getContext('2d');517context?.clearRect(0, 0, canvas.width, canvas.height);518canvas.remove();519}, { timeout: 50 });520521// Track import() perf522performance.mark('code/willLoadWorkbenchMain');523}524}525);526527// Mark start of workbench528performance.mark('code/didLoadWorkbenchMain');529530// Load workbench531result.main(configuration);532}());533534535