Path: blob/main/src/vs/workbench/electron-browser/desktop.main.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*--------------------------------------------------------------------------------------------*/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 { isBigSurOrNewer, isCI, isMacintosh } 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 { DefaultAccountService, IDefaultAccountService } from '../services/accounts/common/defaultAccount.js';64import { AccountPolicyService } from '../services/policies/common/accountPolicyService.js';65import { MultiplexPolicyService } from '../services/policies/common/multiplexPolicyService.js';6667export class DesktopMain extends Disposable {6869constructor(70private readonly configuration: INativeWindowConfiguration71) {72super();7374this.init();75}7677private init(): void {7879// Massage configuration file URIs80this.reviveUris();8182// Apply fullscreen early if configured83setFullscreen(!!this.configuration.fullscreen, mainWindow);84}8586private reviveUris() {8788// Workspace89const workspace = reviveIdentifier(this.configuration.workspace);90if (isWorkspaceIdentifier(workspace) || isSingleFolderWorkspaceIdentifier(workspace)) {91this.configuration.workspace = workspace;92}9394// Files95const filesToWait = this.configuration.filesToWait;96const filesToWaitPaths = filesToWait?.paths;97for (const paths of [filesToWaitPaths, this.configuration.filesToOpenOrCreate, this.configuration.filesToDiff, this.configuration.filesToMerge]) {98if (Array.isArray(paths)) {99for (const path of paths) {100if (path.fileUri) {101path.fileUri = URI.revive(path.fileUri);102}103}104}105}106107if (filesToWait) {108filesToWait.waitMarkerFileUri = URI.revive(filesToWait.waitMarkerFileUri);109}110}111112async open(): Promise<void> {113114// Init services and wait for DOM to be ready in parallel115const [services] = await Promise.all([this.initServices(), domContentLoaded(mainWindow)]);116117// Apply zoom level early once we have a configuration service118// and before the workbench is created to prevent flickering.119// We also need to respect that zoom level can be configured per120// workspace, so we need the resolved configuration service.121// Finally, it is possible for the window to have a custom122// zoom level that is not derived from settings.123// (fixes https://github.com/microsoft/vscode/issues/187982)124this.applyWindowZoomLevel(services.configurationService);125126// Create Workbench127const workbench = new Workbench(mainWindow.document.body, {128extraClasses: this.getExtraClasses(),129resetLayout: this.configuration['disable-layout-restore'] === true130}, services.serviceCollection, services.logService);131132// Listeners133this.registerListeners(workbench, services.storageService);134135// Startup136const instantiationService = workbench.startup();137138// Window139this._register(instantiationService.createInstance(NativeWindow));140}141142private applyWindowZoomLevel(configurationService: IConfigurationService) {143let zoomLevel: number | undefined = undefined;144if (this.configuration.isCustomZoomLevel && typeof this.configuration.zoomLevel === 'number') {145zoomLevel = this.configuration.zoomLevel;146} else {147const windowConfig = configurationService.getValue<IWindowsConfiguration>();148zoomLevel = typeof windowConfig.window?.zoomLevel === 'number' ? windowConfig.window.zoomLevel : 0;149}150151applyZoom(zoomLevel, mainWindow);152}153154private getExtraClasses(): string[] {155if (isMacintosh && isBigSurOrNewer(this.configuration.os.release)) {156return ['macos-bigsur-or-newer'];157}158159return [];160}161162private registerListeners(workbench: Workbench, storageService: NativeWorkbenchStorageService): void {163164// Workbench Lifecycle165this._register(workbench.onWillShutdown(event => event.join(storageService.close(), { id: 'join.closeStorage', label: localize('join.closeStorage', "Saving UI state") })));166this._register(workbench.onDidShutdown(() => this.dispose()));167}168169private async initServices(): Promise<{ serviceCollection: ServiceCollection; logService: ILogService; storageService: NativeWorkbenchStorageService; configurationService: IConfigurationService }> {170const serviceCollection = new ServiceCollection();171172173// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!174//175// NOTE: Please do NOT register services here. Use `registerSingleton()`176// from `workbench.common.main.ts` if the service is shared between177// desktop and web or `workbench.desktop.main.ts` if the service178// is desktop only.179//180// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!181182183// Main Process184const mainProcessService = this._register(new ElectronIPCMainProcessService(this.configuration.windowId));185serviceCollection.set(IMainProcessService, mainProcessService);186187// Product188const productService: IProductService = { _serviceBrand: undefined, ...product };189serviceCollection.set(IProductService, productService);190191// Environment192const environmentService = new NativeWorkbenchEnvironmentService(this.configuration, productService);193serviceCollection.set(INativeWorkbenchEnvironmentService, environmentService);194195// Logger196const loggers = this.configuration.loggers.map(loggerResource => ({ ...loggerResource, resource: URI.revive(loggerResource.resource) }));197const loggerService = new LoggerChannelClient(this.configuration.windowId, this.configuration.logLevel, environmentService.windowLogsPath, loggers, mainProcessService.getChannel('logger'));198serviceCollection.set(ILoggerService, loggerService);199200// Log201const logService = this._register(new NativeLogService(loggerService, environmentService));202serviceCollection.set(ILogService, logService);203if (isCI) {204logService.info('workbench#open()'); // marking workbench open helps to diagnose flaky integration/smoke tests205}206if (logService.getLevel() === LogLevel.Trace) {207logService.trace('workbench#open(): with configuration', safeStringify({ ...this.configuration, nls: undefined /* exclude large property */ }));208}209210// Default Account211const defaultAccountService = this._register(new DefaultAccountService());212serviceCollection.set(IDefaultAccountService, defaultAccountService);213214// Policies215let policyService: IPolicyService;216const accountPolicy = new AccountPolicyService(logService, defaultAccountService);217if (this.configuration.policiesData) {218const policyChannel = new PolicyChannelClient(this.configuration.policiesData, mainProcessService.getChannel('policy'));219policyService = new MultiplexPolicyService([policyChannel, accountPolicy], logService);220} else {221policyService = accountPolicy;222}223serviceCollection.set(IPolicyService, policyService);224225// Shared Process226const sharedProcessService = new SharedProcessService(this.configuration.windowId, logService);227serviceCollection.set(ISharedProcessService, sharedProcessService);228229// Utility Process Worker230const utilityProcessWorkerWorkbenchService = new UtilityProcessWorkerWorkbenchService(this.configuration.windowId, logService, mainProcessService);231serviceCollection.set(IUtilityProcessWorkerWorkbenchService, utilityProcessWorkerWorkbenchService);232233// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!234//235// NOTE: Please do NOT register services here. Use `registerSingleton()`236// from `workbench.common.main.ts` if the service is shared between237// desktop and web or `workbench.desktop.main.ts` if the service238// is desktop only.239//240// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!241242243// Sign244const signService = ProxyChannel.toService<ISignService>(mainProcessService.getChannel('sign'));245serviceCollection.set(ISignService, signService);246247// Files248const fileService = this._register(new FileService(logService));249serviceCollection.set(IFileService, fileService);250251// Remote252const remoteAuthorityResolverService = new RemoteAuthorityResolverService(productService, new ElectronRemoteResourceLoader(environmentService.window.id, mainProcessService, fileService));253serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService);254255// Local Files256const diskFileSystemProvider = this._register(new DiskFileSystemProvider(mainProcessService, utilityProcessWorkerWorkbenchService, logService, loggerService));257fileService.registerProvider(Schemas.file, diskFileSystemProvider);258259// URI Identity260const uriIdentityService = new UriIdentityService(fileService);261serviceCollection.set(IUriIdentityService, uriIdentityService);262263// User Data Profiles264const userDataProfilesService = new UserDataProfilesService(this.configuration.profiles.all, URI.revive(this.configuration.profiles.home).with({ scheme: environmentService.userRoamingDataHome.scheme }), mainProcessService.getChannel('userDataProfiles'));265serviceCollection.set(IUserDataProfilesService, userDataProfilesService);266const userDataProfileService = new UserDataProfileService(reviveProfile(this.configuration.profiles.profile, userDataProfilesService.profilesHome.scheme));267serviceCollection.set(IUserDataProfileService, userDataProfileService);268269// Use FileUserDataProvider for user data to270// enable atomic read / write operations.271fileService.registerProvider(Schemas.vscodeUserData, this._register(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.vscodeUserData, userDataProfilesService, uriIdentityService, logService)));272273// Remote Agent274const remoteSocketFactoryService = new RemoteSocketFactoryService();275remoteSocketFactoryService.register(RemoteConnectionType.WebSocket, new BrowserSocketFactory(null));276serviceCollection.set(IRemoteSocketFactoryService, remoteSocketFactoryService);277const remoteAgentService = this._register(new RemoteAgentService(remoteSocketFactoryService, userDataProfileService, environmentService, productService, remoteAuthorityResolverService, signService, logService));278serviceCollection.set(IRemoteAgentService, remoteAgentService);279280// Remote Files281this._register(RemoteFileSystemProviderClient.register(remoteAgentService, fileService, logService));282283// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!284//285// NOTE: Please do NOT register services here. Use `registerSingleton()`286// from `workbench.common.main.ts` if the service is shared between287// desktop and web or `workbench.desktop.main.ts` if the service288// is desktop only.289//290// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!291292// Create services that require resolving in parallel293const workspace = this.resolveWorkspaceIdentifier(environmentService);294const [configurationService, storageService] = await Promise.all([295this.createWorkspaceService(workspace, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, policyService).then(service => {296297// Workspace298serviceCollection.set(IWorkspaceContextService, service);299300// Configuration301serviceCollection.set(IWorkbenchConfigurationService, service);302303return service;304}),305306this.createStorageService(workspace, environmentService, userDataProfileService, userDataProfilesService, mainProcessService).then(service => {307308// Storage309serviceCollection.set(IStorageService, service);310311return service;312}),313314this.createKeyboardLayoutService(mainProcessService).then(service => {315316// KeyboardLayout317serviceCollection.set(INativeKeyboardLayoutService, service);318319return service;320})321]);322323// Workspace Trust Service324const workspaceTrustEnablementService = new WorkspaceTrustEnablementService(configurationService, environmentService);325serviceCollection.set(IWorkspaceTrustEnablementService, workspaceTrustEnablementService);326327const workspaceTrustManagementService = new WorkspaceTrustManagementService(configurationService, remoteAuthorityResolverService, storageService, uriIdentityService, environmentService, configurationService, workspaceTrustEnablementService, fileService);328serviceCollection.set(IWorkspaceTrustManagementService, workspaceTrustManagementService);329330// Update workspace trust so that configuration is updated accordingly331configurationService.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted());332this._register(workspaceTrustManagementService.onDidChangeTrust(() => configurationService.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted())));333334335// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!336//337// NOTE: Please do NOT register services here. Use `registerSingleton()`338// from `workbench.common.main.ts` if the service is shared between339// desktop and web or `workbench.desktop.main.ts` if the service340// is desktop only.341//342// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!343344345return { serviceCollection, logService, storageService, configurationService };346}347348private resolveWorkspaceIdentifier(environmentService: INativeWorkbenchEnvironmentService): IAnyWorkspaceIdentifier {349350// Return early for when a folder or multi-root is opened351if (this.configuration.workspace) {352return this.configuration.workspace;353}354355// Otherwise, workspace is empty, so we derive an identifier356return toWorkspaceIdentifier(this.configuration.backupPath, environmentService.isExtensionDevelopment);357}358359private async createWorkspaceService(360workspace: IAnyWorkspaceIdentifier,361environmentService: INativeWorkbenchEnvironmentService,362userDataProfileService: IUserDataProfileService,363userDataProfilesService: IUserDataProfilesService,364fileService: FileService,365remoteAgentService: IRemoteAgentService,366uriIdentityService: IUriIdentityService,367logService: ILogService,368policyService: IPolicyService369): Promise<WorkspaceService> {370const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData] /* Cache all non native resources */, environmentService, fileService);371const workspaceService = new WorkspaceService({ remoteAuthority: environmentService.remoteAuthority, configurationCache }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, policyService);372373try {374await workspaceService.initialize(workspace);375376return workspaceService;377} catch (error) {378onUnexpectedError(error);379380return workspaceService;381}382}383384private async createStorageService(workspace: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService, userDataProfilesService: IUserDataProfilesService, mainProcessService: IMainProcessService): Promise<NativeWorkbenchStorageService> {385const storageService = new NativeWorkbenchStorageService(workspace, userDataProfileService, userDataProfilesService, mainProcessService, environmentService);386387try {388await storageService.initialize();389390return storageService;391} catch (error) {392onUnexpectedError(error);393394return storageService;395}396}397398private async createKeyboardLayoutService(mainProcessService: IMainProcessService): Promise<NativeKeyboardLayoutService> {399const keyboardLayoutService = new NativeKeyboardLayoutService(mainProcessService);400401try {402await keyboardLayoutService.initialize();403404return keyboardLayoutService;405} catch (error) {406onUnexpectedError(error);407408return keyboardLayoutService;409}410}411}412413export interface IDesktopMain {414main(configuration: INativeWindowConfiguration): Promise<void>;415}416417export function main(configuration: INativeWindowConfiguration): Promise<void> {418const workbench = new DesktopMain(configuration);419420return workbench.open();421}422423424