Path: blob/main/src/vs/workbench/electron-browser/desktop.main.ts
5259 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*--------------------------------------------------------------------------------------------*/45import { localize } from '../../nls.js';6import product from '../../platform/product/common/product.js';7import { INativeWindowConfiguration, IWindowsConfiguration } from '../../platform/window/common/window.js';8import { Workbench } from '../browser/workbench.js';9import { NativeWindow } from './window.js';10import { setFullscreen } from '../../base/browser/browser.js';11import { domContentLoaded } from '../../base/browser/dom.js';12import { onUnexpectedError } from '../../base/common/errors.js';13import { URI } from '../../base/common/uri.js';14import { WorkspaceService } from '../services/configuration/browser/configurationService.js';15import { INativeWorkbenchEnvironmentService, NativeWorkbenchEnvironmentService } from '../services/environment/electron-browser/environmentService.js';16import { ServiceCollection } from '../../platform/instantiation/common/serviceCollection.js';17import { ILoggerService, ILogService, LogLevel } from '../../platform/log/common/log.js';18import { NativeWorkbenchStorageService } from '../services/storage/electron-browser/storageService.js';19import { IWorkspaceContextService, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IAnyWorkspaceIdentifier, reviveIdentifier, toWorkspaceIdentifier } from '../../platform/workspace/common/workspace.js';20import { IWorkbenchConfigurationService } from '../services/configuration/common/configuration.js';21import { IStorageService } from '../../platform/storage/common/storage.js';22import { Disposable } from '../../base/common/lifecycle.js';23import { ISharedProcessService } from '../../platform/ipc/electron-browser/services.js';24import { IMainProcessService } from '../../platform/ipc/common/mainProcessService.js';25import { SharedProcessService } from '../services/sharedProcess/electron-browser/sharedProcessService.js';26import { RemoteAuthorityResolverService } from '../../platform/remote/electron-browser/remoteAuthorityResolverService.js';27import { IRemoteAuthorityResolverService, RemoteConnectionType } from '../../platform/remote/common/remoteAuthorityResolver.js';28import { RemoteAgentService } from '../services/remote/electron-browser/remoteAgentService.js';29import { IRemoteAgentService } from '../services/remote/common/remoteAgentService.js';30import { FileService } from '../../platform/files/common/fileService.js';31import { IFileService } from '../../platform/files/common/files.js';32import { RemoteFileSystemProviderClient } from '../services/remote/common/remoteFileSystemProviderClient.js';33import { ConfigurationCache } from '../services/configuration/common/configurationCache.js';34import { ISignService } from '../../platform/sign/common/sign.js';35import { IProductService } from '../../platform/product/common/productService.js';36import { IUriIdentityService } from '../../platform/uriIdentity/common/uriIdentity.js';37import { UriIdentityService } from '../../platform/uriIdentity/common/uriIdentityService.js';38import { INativeKeyboardLayoutService, NativeKeyboardLayoutService } from '../services/keybinding/electron-browser/nativeKeyboardLayoutService.js';39import { ElectronIPCMainProcessService } from '../../platform/ipc/electron-browser/mainProcessService.js';40import { LoggerChannelClient } from '../../platform/log/common/logIpc.js';41import { ProxyChannel } from '../../base/parts/ipc/common/ipc.js';42import { NativeLogService } from '../services/log/electron-browser/logService.js';43import { WorkspaceTrustEnablementService, WorkspaceTrustManagementService } from '../services/workspaces/common/workspaceTrust.js';44import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService } from '../../platform/workspace/common/workspaceTrust.js';45import { safeStringify } from '../../base/common/objects.js';46import { IUtilityProcessWorkerWorkbenchService, UtilityProcessWorkerWorkbenchService } from '../services/utilityProcess/electron-browser/utilityProcessWorkerWorkbenchService.js';47import { isCI, isMacintosh, isTahoeOrNewer } from '../../base/common/platform.js';48import { Schemas } from '../../base/common/network.js';49import { DiskFileSystemProvider } from '../services/files/electron-browser/diskFileSystemProvider.js';50import { FileUserDataProvider } from '../../platform/userData/common/fileUserDataProvider.js';51import { IUserDataProfilesService, reviveProfile } from '../../platform/userDataProfile/common/userDataProfile.js';52import { UserDataProfilesService } from '../../platform/userDataProfile/common/userDataProfileIpc.js';53import { PolicyChannelClient } from '../../platform/policy/common/policyIpc.js';54import { IPolicyService } from '../../platform/policy/common/policy.js';55import { UserDataProfileService } from '../services/userDataProfile/common/userDataProfileService.js';56import { IUserDataProfileService } from '../services/userDataProfile/common/userDataProfile.js';57import { BrowserSocketFactory } from '../../platform/remote/browser/browserSocketFactory.js';58import { RemoteSocketFactoryService, IRemoteSocketFactoryService } from '../../platform/remote/common/remoteSocketFactoryService.js';59import { ElectronRemoteResourceLoader } from '../../platform/remote/electron-browser/electronRemoteResourceLoader.js';60import { IConfigurationService } from '../../platform/configuration/common/configuration.js';61import { applyZoom } from '../../platform/window/electron-browser/window.js';62import { mainWindow } from '../../base/browser/window.js';63import { IDefaultAccountService } from '../../platform/defaultAccount/common/defaultAccount.js';64import { DefaultAccountService } from '../services/accounts/browser/defaultAccount.js';65import { AccountPolicyService } from '../services/policies/common/accountPolicyService.js';66import { MultiplexPolicyService } from '../services/policies/common/multiplexPolicyService.js';67import { WorkbenchModeService } from '../services/layout/browser/workbenchModeService.js';68import { IWorkbenchModeService } from '../services/layout/common/workbenchModeService.js';6970export class DesktopMain extends Disposable {7172constructor(73private readonly configuration: INativeWindowConfiguration74) {75super();7677this.init();78}7980private init(): void {8182// Massage configuration file URIs83this.reviveUris();8485// Apply fullscreen early if configured86setFullscreen(!!this.configuration.fullscreen, mainWindow);87}8889private reviveUris() {9091// Workspace92const workspace = reviveIdentifier(this.configuration.workspace);93if (isWorkspaceIdentifier(workspace) || isSingleFolderWorkspaceIdentifier(workspace)) {94this.configuration.workspace = workspace;95}9697// Files98const filesToWait = this.configuration.filesToWait;99const filesToWaitPaths = filesToWait?.paths;100for (const paths of [filesToWaitPaths, this.configuration.filesToOpenOrCreate, this.configuration.filesToDiff, this.configuration.filesToMerge]) {101if (Array.isArray(paths)) {102for (const path of paths) {103if (path.fileUri) {104path.fileUri = URI.revive(path.fileUri);105}106}107}108}109110if (filesToWait) {111filesToWait.waitMarkerFileUri = URI.revive(filesToWait.waitMarkerFileUri);112}113}114115async open(): Promise<void> {116117// Init services and wait for DOM to be ready in parallel118const [services] = await Promise.all([this.initServices(), domContentLoaded(mainWindow)]);119120// Apply zoom level early once we have a configuration service121// and before the workbench is created to prevent flickering.122// We also need to respect that zoom level can be configured per123// workspace, so we need the resolved configuration service.124// Finally, it is possible for the window to have a custom125// zoom level that is not derived from settings.126// (fixes https://github.com/microsoft/vscode/issues/187982)127this.applyWindowZoomLevel(services.configurationService);128129// Create Workbench130const workbench = new Workbench(mainWindow.document.body, {131extraClasses: this.getExtraClasses(),132resetLayout: this.configuration['disable-layout-restore'] === true133}, services.serviceCollection, services.logService);134135// Listeners136this.registerListeners(workbench, services.storageService);137138// Startup139const instantiationService = workbench.startup();140141// Window142this._register(instantiationService.createInstance(NativeWindow));143}144145private applyWindowZoomLevel(configurationService: IConfigurationService) {146let zoomLevel: number | undefined = undefined;147if (this.configuration.isCustomZoomLevel && typeof this.configuration.zoomLevel === 'number') {148zoomLevel = this.configuration.zoomLevel;149} else {150const windowConfig = configurationService.getValue<IWindowsConfiguration>();151zoomLevel = typeof windowConfig.window?.zoomLevel === 'number' ? windowConfig.window.zoomLevel : 0;152}153154applyZoom(zoomLevel, mainWindow);155}156157private getExtraClasses(): string[] {158if (isMacintosh && isTahoeOrNewer(this.configuration.os.release)) {159return ['macos-tahoe'];160}161162return [];163}164165private registerListeners(workbench: Workbench, storageService: NativeWorkbenchStorageService): void {166167// Workbench Lifecycle168this._register(workbench.onWillShutdown(event => event.join(storageService.close(), { id: 'join.closeStorage', label: localize('join.closeStorage', "Saving UI state") })));169this._register(workbench.onDidShutdown(() => this.dispose()));170}171172private async initServices(): Promise<{ serviceCollection: ServiceCollection; logService: ILogService; storageService: NativeWorkbenchStorageService; configurationService: IConfigurationService }> {173const serviceCollection = new ServiceCollection();174175176// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!177//178// NOTE: Please do NOT register services here. Use `registerSingleton()`179// from `workbench.common.main.ts` if the service is shared between180// desktop and web or `workbench.desktop.main.ts` if the service181// is desktop only.182//183// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!184185186// Main Process187const mainProcessService = this._register(new ElectronIPCMainProcessService(this.configuration.windowId));188serviceCollection.set(IMainProcessService, mainProcessService);189190// Product191const productService: IProductService = { _serviceBrand: undefined, ...product };192serviceCollection.set(IProductService, productService);193194// Environment195const environmentService = new NativeWorkbenchEnvironmentService(this.configuration, productService);196serviceCollection.set(INativeWorkbenchEnvironmentService, environmentService);197198// Logger199const loggers = this.configuration.loggers.map(loggerResource => ({ ...loggerResource, resource: URI.revive(loggerResource.resource) }));200const loggerService = new LoggerChannelClient(this.configuration.windowId, this.configuration.logLevel, environmentService.windowLogsPath, loggers, mainProcessService.getChannel('logger'));201serviceCollection.set(ILoggerService, loggerService);202203// Log204const logService = this._register(new NativeLogService(loggerService, environmentService));205serviceCollection.set(ILogService, logService);206if (isCI) {207logService.info('workbench#open()'); // marking workbench open helps to diagnose flaky integration/smoke tests208}209if (logService.getLevel() === LogLevel.Trace) {210logService.trace('workbench#open(): with configuration', safeStringify({ ...this.configuration, nls: undefined /* exclude large property */ }));211}212213// Default Account214const defaultAccountService = this._register(new DefaultAccountService(productService));215serviceCollection.set(IDefaultAccountService, defaultAccountService);216217// Policies218let policyService: IPolicyService;219const accountPolicy = new AccountPolicyService(logService, defaultAccountService);220if (this.configuration.policiesData) {221const policyChannel = new PolicyChannelClient(this.configuration.policiesData, mainProcessService.getChannel('policy'));222policyService = new MultiplexPolicyService([policyChannel, accountPolicy], logService);223} else {224policyService = accountPolicy;225}226serviceCollection.set(IPolicyService, policyService);227228// Shared Process229const sharedProcessService = new SharedProcessService(this.configuration.windowId, logService);230serviceCollection.set(ISharedProcessService, sharedProcessService);231232// Utility Process Worker233const utilityProcessWorkerWorkbenchService = new UtilityProcessWorkerWorkbenchService(this.configuration.windowId, logService, mainProcessService);234serviceCollection.set(IUtilityProcessWorkerWorkbenchService, utilityProcessWorkerWorkbenchService);235236// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!237//238// NOTE: Please do NOT register services here. Use `registerSingleton()`239// from `workbench.common.main.ts` if the service is shared between240// desktop and web or `workbench.desktop.main.ts` if the service241// is desktop only.242//243// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!244245246// Sign247const signService = ProxyChannel.toService<ISignService>(mainProcessService.getChannel('sign'));248serviceCollection.set(ISignService, signService);249250// Files251const fileService = this._register(new FileService(logService));252serviceCollection.set(IFileService, fileService);253254// Remote255const remoteAuthorityResolverService = new RemoteAuthorityResolverService(productService, new ElectronRemoteResourceLoader(environmentService.window.id, mainProcessService, fileService));256serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService);257258// Local Files259const diskFileSystemProvider = this._register(new DiskFileSystemProvider(mainProcessService, utilityProcessWorkerWorkbenchService, logService, loggerService));260fileService.registerProvider(Schemas.file, diskFileSystemProvider);261262// URI Identity263const uriIdentityService = new UriIdentityService(fileService);264serviceCollection.set(IUriIdentityService, uriIdentityService);265266// User Data Profiles267const userDataProfilesService = new UserDataProfilesService(this.configuration.profiles.all, URI.revive(this.configuration.profiles.home).with({ scheme: environmentService.userRoamingDataHome.scheme }), mainProcessService.getChannel('userDataProfiles'));268serviceCollection.set(IUserDataProfilesService, userDataProfilesService);269const userDataProfileService = new UserDataProfileService(reviveProfile(this.configuration.profiles.profile, userDataProfilesService.profilesHome.scheme));270serviceCollection.set(IUserDataProfileService, userDataProfileService);271272// Use FileUserDataProvider for user data to273// enable atomic read / write operations.274fileService.registerProvider(Schemas.vscodeUserData, this._register(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.vscodeUserData, userDataProfilesService, uriIdentityService, logService)));275276// Remote Agent277const remoteSocketFactoryService = new RemoteSocketFactoryService();278remoteSocketFactoryService.register(RemoteConnectionType.WebSocket, new BrowserSocketFactory(null));279serviceCollection.set(IRemoteSocketFactoryService, remoteSocketFactoryService);280const remoteAgentService = this._register(new RemoteAgentService(remoteSocketFactoryService, userDataProfileService, environmentService, productService, remoteAuthorityResolverService, signService, logService));281serviceCollection.set(IRemoteAgentService, remoteAgentService);282283// Remote Files284this._register(RemoteFileSystemProviderClient.register(remoteAgentService, fileService, logService));285286// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!287//288// NOTE: Please do NOT register services here. Use `registerSingleton()`289// from `workbench.common.main.ts` if the service is shared between290// desktop and web or `workbench.desktop.main.ts` if the service291// is desktop only.292//293// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!294295// Create services that require resolving in parallel296const workspace = this.resolveWorkspaceIdentifier(environmentService);297const [configurationService, storageService] = await Promise.all([298this.createWorkspaceService(workspace, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, policyService).then(service => {299300// Workspace301serviceCollection.set(IWorkspaceContextService, service);302303// Configuration304serviceCollection.set(IWorkbenchConfigurationService, service);305306return service;307}),308309this.createStorageService(workspace, environmentService, userDataProfileService, userDataProfilesService, mainProcessService).then(service => {310311// Storage312serviceCollection.set(IStorageService, service);313314return service;315}),316317this.createKeyboardLayoutService(mainProcessService).then(service => {318319// KeyboardLayout320serviceCollection.set(INativeKeyboardLayoutService, service);321322return service;323})324]);325326// Workbench Mode327const workbenchModeService: WorkbenchModeService = this._register(new WorkbenchModeService(configurationService, fileService, environmentService, uriIdentityService, logService, storageService));328serviceCollection.set(IWorkbenchModeService, workbenchModeService);329330try {331await workbenchModeService.initialize();332} catch (error) {333logService.error('Error while initializing workbench mode service', error);334}335336// Workspace Trust Service337const workspaceTrustEnablementService = new WorkspaceTrustEnablementService(configurationService, environmentService);338serviceCollection.set(IWorkspaceTrustEnablementService, workspaceTrustEnablementService);339340const workspaceTrustManagementService = new WorkspaceTrustManagementService(configurationService, remoteAuthorityResolverService, storageService, uriIdentityService, environmentService, configurationService, workspaceTrustEnablementService, fileService);341serviceCollection.set(IWorkspaceTrustManagementService, workspaceTrustManagementService);342343// Update workspace trust so that configuration is updated accordingly344configurationService.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted());345this._register(workspaceTrustManagementService.onDidChangeTrust(() => configurationService.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted())));346347348// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!349//350// NOTE: Please do NOT register services here. Use `registerSingleton()`351// from `workbench.common.main.ts` if the service is shared between352// desktop and web or `workbench.desktop.main.ts` if the service353// is desktop only.354//355// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!356357358return { serviceCollection, logService, storageService, configurationService };359}360361private resolveWorkspaceIdentifier(environmentService: INativeWorkbenchEnvironmentService): IAnyWorkspaceIdentifier {362363// Return early for when a folder or multi-root is opened364if (this.configuration.workspace) {365return this.configuration.workspace;366}367368// Otherwise, workspace is empty, so we derive an identifier369return toWorkspaceIdentifier(this.configuration.backupPath, environmentService.isExtensionDevelopment);370}371372private async createWorkspaceService(373workspace: IAnyWorkspaceIdentifier,374environmentService: INativeWorkbenchEnvironmentService,375userDataProfileService: IUserDataProfileService,376userDataProfilesService: IUserDataProfilesService,377fileService: FileService,378remoteAgentService: IRemoteAgentService,379uriIdentityService: IUriIdentityService,380logService: ILogService,381policyService: IPolicyService382): Promise<WorkspaceService> {383const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData] /* Cache all non native resources */, environmentService, fileService);384const workspaceService = new WorkspaceService({ remoteAuthority: environmentService.remoteAuthority, configurationCache }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, policyService);385386try {387await workspaceService.initialize(workspace);388389return workspaceService;390} catch (error) {391onUnexpectedError(error);392393return workspaceService;394}395}396397private async createStorageService(workspace: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService, userDataProfilesService: IUserDataProfilesService, mainProcessService: IMainProcessService): Promise<NativeWorkbenchStorageService> {398const storageService = new NativeWorkbenchStorageService(workspace, userDataProfileService, userDataProfilesService, mainProcessService, environmentService);399400try {401await storageService.initialize();402403return storageService;404} catch (error) {405onUnexpectedError(error);406407return storageService;408}409}410411private async createKeyboardLayoutService(mainProcessService: IMainProcessService): Promise<NativeKeyboardLayoutService> {412const keyboardLayoutService = new NativeKeyboardLayoutService(mainProcessService);413414try {415await keyboardLayoutService.initialize();416417return keyboardLayoutService;418} catch (error) {419onUnexpectedError(error);420421return keyboardLayoutService;422}423}424}425426export interface IDesktopMain {427main(configuration: INativeWindowConfiguration): Promise<void>;428}429430export function main(configuration: INativeWindowConfiguration): Promise<void> {431const workbench = new DesktopMain(configuration);432433return workbench.open();434}435436437