Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/electron-browser/desktop.main.ts
5259 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { localize } from '../../nls.js';
7
import product from '../../platform/product/common/product.js';
8
import { INativeWindowConfiguration, IWindowsConfiguration } from '../../platform/window/common/window.js';
9
import { Workbench } from '../browser/workbench.js';
10
import { NativeWindow } from './window.js';
11
import { setFullscreen } from '../../base/browser/browser.js';
12
import { domContentLoaded } from '../../base/browser/dom.js';
13
import { onUnexpectedError } from '../../base/common/errors.js';
14
import { URI } from '../../base/common/uri.js';
15
import { WorkspaceService } from '../services/configuration/browser/configurationService.js';
16
import { INativeWorkbenchEnvironmentService, NativeWorkbenchEnvironmentService } from '../services/environment/electron-browser/environmentService.js';
17
import { ServiceCollection } from '../../platform/instantiation/common/serviceCollection.js';
18
import { ILoggerService, ILogService, LogLevel } from '../../platform/log/common/log.js';
19
import { NativeWorkbenchStorageService } from '../services/storage/electron-browser/storageService.js';
20
import { IWorkspaceContextService, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IAnyWorkspaceIdentifier, reviveIdentifier, toWorkspaceIdentifier } from '../../platform/workspace/common/workspace.js';
21
import { IWorkbenchConfigurationService } from '../services/configuration/common/configuration.js';
22
import { IStorageService } from '../../platform/storage/common/storage.js';
23
import { Disposable } from '../../base/common/lifecycle.js';
24
import { ISharedProcessService } from '../../platform/ipc/electron-browser/services.js';
25
import { IMainProcessService } from '../../platform/ipc/common/mainProcessService.js';
26
import { SharedProcessService } from '../services/sharedProcess/electron-browser/sharedProcessService.js';
27
import { RemoteAuthorityResolverService } from '../../platform/remote/electron-browser/remoteAuthorityResolverService.js';
28
import { IRemoteAuthorityResolverService, RemoteConnectionType } from '../../platform/remote/common/remoteAuthorityResolver.js';
29
import { RemoteAgentService } from '../services/remote/electron-browser/remoteAgentService.js';
30
import { IRemoteAgentService } from '../services/remote/common/remoteAgentService.js';
31
import { FileService } from '../../platform/files/common/fileService.js';
32
import { IFileService } from '../../platform/files/common/files.js';
33
import { RemoteFileSystemProviderClient } from '../services/remote/common/remoteFileSystemProviderClient.js';
34
import { ConfigurationCache } from '../services/configuration/common/configurationCache.js';
35
import { ISignService } from '../../platform/sign/common/sign.js';
36
import { IProductService } from '../../platform/product/common/productService.js';
37
import { IUriIdentityService } from '../../platform/uriIdentity/common/uriIdentity.js';
38
import { UriIdentityService } from '../../platform/uriIdentity/common/uriIdentityService.js';
39
import { INativeKeyboardLayoutService, NativeKeyboardLayoutService } from '../services/keybinding/electron-browser/nativeKeyboardLayoutService.js';
40
import { ElectronIPCMainProcessService } from '../../platform/ipc/electron-browser/mainProcessService.js';
41
import { LoggerChannelClient } from '../../platform/log/common/logIpc.js';
42
import { ProxyChannel } from '../../base/parts/ipc/common/ipc.js';
43
import { NativeLogService } from '../services/log/electron-browser/logService.js';
44
import { WorkspaceTrustEnablementService, WorkspaceTrustManagementService } from '../services/workspaces/common/workspaceTrust.js';
45
import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService } from '../../platform/workspace/common/workspaceTrust.js';
46
import { safeStringify } from '../../base/common/objects.js';
47
import { IUtilityProcessWorkerWorkbenchService, UtilityProcessWorkerWorkbenchService } from '../services/utilityProcess/electron-browser/utilityProcessWorkerWorkbenchService.js';
48
import { isCI, isMacintosh, isTahoeOrNewer } from '../../base/common/platform.js';
49
import { Schemas } from '../../base/common/network.js';
50
import { DiskFileSystemProvider } from '../services/files/electron-browser/diskFileSystemProvider.js';
51
import { FileUserDataProvider } from '../../platform/userData/common/fileUserDataProvider.js';
52
import { IUserDataProfilesService, reviveProfile } from '../../platform/userDataProfile/common/userDataProfile.js';
53
import { UserDataProfilesService } from '../../platform/userDataProfile/common/userDataProfileIpc.js';
54
import { PolicyChannelClient } from '../../platform/policy/common/policyIpc.js';
55
import { IPolicyService } from '../../platform/policy/common/policy.js';
56
import { UserDataProfileService } from '../services/userDataProfile/common/userDataProfileService.js';
57
import { IUserDataProfileService } from '../services/userDataProfile/common/userDataProfile.js';
58
import { BrowserSocketFactory } from '../../platform/remote/browser/browserSocketFactory.js';
59
import { RemoteSocketFactoryService, IRemoteSocketFactoryService } from '../../platform/remote/common/remoteSocketFactoryService.js';
60
import { ElectronRemoteResourceLoader } from '../../platform/remote/electron-browser/electronRemoteResourceLoader.js';
61
import { IConfigurationService } from '../../platform/configuration/common/configuration.js';
62
import { applyZoom } from '../../platform/window/electron-browser/window.js';
63
import { mainWindow } from '../../base/browser/window.js';
64
import { IDefaultAccountService } from '../../platform/defaultAccount/common/defaultAccount.js';
65
import { DefaultAccountService } from '../services/accounts/browser/defaultAccount.js';
66
import { AccountPolicyService } from '../services/policies/common/accountPolicyService.js';
67
import { MultiplexPolicyService } from '../services/policies/common/multiplexPolicyService.js';
68
import { WorkbenchModeService } from '../services/layout/browser/workbenchModeService.js';
69
import { IWorkbenchModeService } from '../services/layout/common/workbenchModeService.js';
70
71
export class DesktopMain extends Disposable {
72
73
constructor(
74
private readonly configuration: INativeWindowConfiguration
75
) {
76
super();
77
78
this.init();
79
}
80
81
private init(): void {
82
83
// Massage configuration file URIs
84
this.reviveUris();
85
86
// Apply fullscreen early if configured
87
setFullscreen(!!this.configuration.fullscreen, mainWindow);
88
}
89
90
private reviveUris() {
91
92
// Workspace
93
const workspace = reviveIdentifier(this.configuration.workspace);
94
if (isWorkspaceIdentifier(workspace) || isSingleFolderWorkspaceIdentifier(workspace)) {
95
this.configuration.workspace = workspace;
96
}
97
98
// Files
99
const filesToWait = this.configuration.filesToWait;
100
const filesToWaitPaths = filesToWait?.paths;
101
for (const paths of [filesToWaitPaths, this.configuration.filesToOpenOrCreate, this.configuration.filesToDiff, this.configuration.filesToMerge]) {
102
if (Array.isArray(paths)) {
103
for (const path of paths) {
104
if (path.fileUri) {
105
path.fileUri = URI.revive(path.fileUri);
106
}
107
}
108
}
109
}
110
111
if (filesToWait) {
112
filesToWait.waitMarkerFileUri = URI.revive(filesToWait.waitMarkerFileUri);
113
}
114
}
115
116
async open(): Promise<void> {
117
118
// Init services and wait for DOM to be ready in parallel
119
const [services] = await Promise.all([this.initServices(), domContentLoaded(mainWindow)]);
120
121
// Apply zoom level early once we have a configuration service
122
// and before the workbench is created to prevent flickering.
123
// We also need to respect that zoom level can be configured per
124
// workspace, so we need the resolved configuration service.
125
// Finally, it is possible for the window to have a custom
126
// zoom level that is not derived from settings.
127
// (fixes https://github.com/microsoft/vscode/issues/187982)
128
this.applyWindowZoomLevel(services.configurationService);
129
130
// Create Workbench
131
const workbench = new Workbench(mainWindow.document.body, {
132
extraClasses: this.getExtraClasses(),
133
resetLayout: this.configuration['disable-layout-restore'] === true
134
}, services.serviceCollection, services.logService);
135
136
// Listeners
137
this.registerListeners(workbench, services.storageService);
138
139
// Startup
140
const instantiationService = workbench.startup();
141
142
// Window
143
this._register(instantiationService.createInstance(NativeWindow));
144
}
145
146
private applyWindowZoomLevel(configurationService: IConfigurationService) {
147
let zoomLevel: number | undefined = undefined;
148
if (this.configuration.isCustomZoomLevel && typeof this.configuration.zoomLevel === 'number') {
149
zoomLevel = this.configuration.zoomLevel;
150
} else {
151
const windowConfig = configurationService.getValue<IWindowsConfiguration>();
152
zoomLevel = typeof windowConfig.window?.zoomLevel === 'number' ? windowConfig.window.zoomLevel : 0;
153
}
154
155
applyZoom(zoomLevel, mainWindow);
156
}
157
158
private getExtraClasses(): string[] {
159
if (isMacintosh && isTahoeOrNewer(this.configuration.os.release)) {
160
return ['macos-tahoe'];
161
}
162
163
return [];
164
}
165
166
private registerListeners(workbench: Workbench, storageService: NativeWorkbenchStorageService): void {
167
168
// Workbench Lifecycle
169
this._register(workbench.onWillShutdown(event => event.join(storageService.close(), { id: 'join.closeStorage', label: localize('join.closeStorage', "Saving UI state") })));
170
this._register(workbench.onDidShutdown(() => this.dispose()));
171
}
172
173
private async initServices(): Promise<{ serviceCollection: ServiceCollection; logService: ILogService; storageService: NativeWorkbenchStorageService; configurationService: IConfigurationService }> {
174
const serviceCollection = new ServiceCollection();
175
176
177
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
178
//
179
// NOTE: Please do NOT register services here. Use `registerSingleton()`
180
// from `workbench.common.main.ts` if the service is shared between
181
// desktop and web or `workbench.desktop.main.ts` if the service
182
// is desktop only.
183
//
184
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
185
186
187
// Main Process
188
const mainProcessService = this._register(new ElectronIPCMainProcessService(this.configuration.windowId));
189
serviceCollection.set(IMainProcessService, mainProcessService);
190
191
// Product
192
const productService: IProductService = { _serviceBrand: undefined, ...product };
193
serviceCollection.set(IProductService, productService);
194
195
// Environment
196
const environmentService = new NativeWorkbenchEnvironmentService(this.configuration, productService);
197
serviceCollection.set(INativeWorkbenchEnvironmentService, environmentService);
198
199
// Logger
200
const loggers = this.configuration.loggers.map(loggerResource => ({ ...loggerResource, resource: URI.revive(loggerResource.resource) }));
201
const loggerService = new LoggerChannelClient(this.configuration.windowId, this.configuration.logLevel, environmentService.windowLogsPath, loggers, mainProcessService.getChannel('logger'));
202
serviceCollection.set(ILoggerService, loggerService);
203
204
// Log
205
const logService = this._register(new NativeLogService(loggerService, environmentService));
206
serviceCollection.set(ILogService, logService);
207
if (isCI) {
208
logService.info('workbench#open()'); // marking workbench open helps to diagnose flaky integration/smoke tests
209
}
210
if (logService.getLevel() === LogLevel.Trace) {
211
logService.trace('workbench#open(): with configuration', safeStringify({ ...this.configuration, nls: undefined /* exclude large property */ }));
212
}
213
214
// Default Account
215
const defaultAccountService = this._register(new DefaultAccountService(productService));
216
serviceCollection.set(IDefaultAccountService, defaultAccountService);
217
218
// Policies
219
let policyService: IPolicyService;
220
const accountPolicy = new AccountPolicyService(logService, defaultAccountService);
221
if (this.configuration.policiesData) {
222
const policyChannel = new PolicyChannelClient(this.configuration.policiesData, mainProcessService.getChannel('policy'));
223
policyService = new MultiplexPolicyService([policyChannel, accountPolicy], logService);
224
} else {
225
policyService = accountPolicy;
226
}
227
serviceCollection.set(IPolicyService, policyService);
228
229
// Shared Process
230
const sharedProcessService = new SharedProcessService(this.configuration.windowId, logService);
231
serviceCollection.set(ISharedProcessService, sharedProcessService);
232
233
// Utility Process Worker
234
const utilityProcessWorkerWorkbenchService = new UtilityProcessWorkerWorkbenchService(this.configuration.windowId, logService, mainProcessService);
235
serviceCollection.set(IUtilityProcessWorkerWorkbenchService, utilityProcessWorkerWorkbenchService);
236
237
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
238
//
239
// NOTE: Please do NOT register services here. Use `registerSingleton()`
240
// from `workbench.common.main.ts` if the service is shared between
241
// desktop and web or `workbench.desktop.main.ts` if the service
242
// is desktop only.
243
//
244
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
245
246
247
// Sign
248
const signService = ProxyChannel.toService<ISignService>(mainProcessService.getChannel('sign'));
249
serviceCollection.set(ISignService, signService);
250
251
// Files
252
const fileService = this._register(new FileService(logService));
253
serviceCollection.set(IFileService, fileService);
254
255
// Remote
256
const remoteAuthorityResolverService = new RemoteAuthorityResolverService(productService, new ElectronRemoteResourceLoader(environmentService.window.id, mainProcessService, fileService));
257
serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService);
258
259
// Local Files
260
const diskFileSystemProvider = this._register(new DiskFileSystemProvider(mainProcessService, utilityProcessWorkerWorkbenchService, logService, loggerService));
261
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
262
263
// URI Identity
264
const uriIdentityService = new UriIdentityService(fileService);
265
serviceCollection.set(IUriIdentityService, uriIdentityService);
266
267
// User Data Profiles
268
const userDataProfilesService = new UserDataProfilesService(this.configuration.profiles.all, URI.revive(this.configuration.profiles.home).with({ scheme: environmentService.userRoamingDataHome.scheme }), mainProcessService.getChannel('userDataProfiles'));
269
serviceCollection.set(IUserDataProfilesService, userDataProfilesService);
270
const userDataProfileService = new UserDataProfileService(reviveProfile(this.configuration.profiles.profile, userDataProfilesService.profilesHome.scheme));
271
serviceCollection.set(IUserDataProfileService, userDataProfileService);
272
273
// Use FileUserDataProvider for user data to
274
// enable atomic read / write operations.
275
fileService.registerProvider(Schemas.vscodeUserData, this._register(new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.vscodeUserData, userDataProfilesService, uriIdentityService, logService)));
276
277
// Remote Agent
278
const remoteSocketFactoryService = new RemoteSocketFactoryService();
279
remoteSocketFactoryService.register(RemoteConnectionType.WebSocket, new BrowserSocketFactory(null));
280
serviceCollection.set(IRemoteSocketFactoryService, remoteSocketFactoryService);
281
const remoteAgentService = this._register(new RemoteAgentService(remoteSocketFactoryService, userDataProfileService, environmentService, productService, remoteAuthorityResolverService, signService, logService));
282
serviceCollection.set(IRemoteAgentService, remoteAgentService);
283
284
// Remote Files
285
this._register(RemoteFileSystemProviderClient.register(remoteAgentService, fileService, logService));
286
287
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
288
//
289
// NOTE: Please do NOT register services here. Use `registerSingleton()`
290
// from `workbench.common.main.ts` if the service is shared between
291
// desktop and web or `workbench.desktop.main.ts` if the service
292
// is desktop only.
293
//
294
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
295
296
// Create services that require resolving in parallel
297
const workspace = this.resolveWorkspaceIdentifier(environmentService);
298
const [configurationService, storageService] = await Promise.all([
299
this.createWorkspaceService(workspace, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, policyService).then(service => {
300
301
// Workspace
302
serviceCollection.set(IWorkspaceContextService, service);
303
304
// Configuration
305
serviceCollection.set(IWorkbenchConfigurationService, service);
306
307
return service;
308
}),
309
310
this.createStorageService(workspace, environmentService, userDataProfileService, userDataProfilesService, mainProcessService).then(service => {
311
312
// Storage
313
serviceCollection.set(IStorageService, service);
314
315
return service;
316
}),
317
318
this.createKeyboardLayoutService(mainProcessService).then(service => {
319
320
// KeyboardLayout
321
serviceCollection.set(INativeKeyboardLayoutService, service);
322
323
return service;
324
})
325
]);
326
327
// Workbench Mode
328
const workbenchModeService: WorkbenchModeService = this._register(new WorkbenchModeService(configurationService, fileService, environmentService, uriIdentityService, logService, storageService));
329
serviceCollection.set(IWorkbenchModeService, workbenchModeService);
330
331
try {
332
await workbenchModeService.initialize();
333
} catch (error) {
334
logService.error('Error while initializing workbench mode service', error);
335
}
336
337
// Workspace Trust Service
338
const workspaceTrustEnablementService = new WorkspaceTrustEnablementService(configurationService, environmentService);
339
serviceCollection.set(IWorkspaceTrustEnablementService, workspaceTrustEnablementService);
340
341
const workspaceTrustManagementService = new WorkspaceTrustManagementService(configurationService, remoteAuthorityResolverService, storageService, uriIdentityService, environmentService, configurationService, workspaceTrustEnablementService, fileService);
342
serviceCollection.set(IWorkspaceTrustManagementService, workspaceTrustManagementService);
343
344
// Update workspace trust so that configuration is updated accordingly
345
configurationService.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted());
346
this._register(workspaceTrustManagementService.onDidChangeTrust(() => configurationService.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted())));
347
348
349
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
350
//
351
// NOTE: Please do NOT register services here. Use `registerSingleton()`
352
// from `workbench.common.main.ts` if the service is shared between
353
// desktop and web or `workbench.desktop.main.ts` if the service
354
// is desktop only.
355
//
356
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
357
358
359
return { serviceCollection, logService, storageService, configurationService };
360
}
361
362
private resolveWorkspaceIdentifier(environmentService: INativeWorkbenchEnvironmentService): IAnyWorkspaceIdentifier {
363
364
// Return early for when a folder or multi-root is opened
365
if (this.configuration.workspace) {
366
return this.configuration.workspace;
367
}
368
369
// Otherwise, workspace is empty, so we derive an identifier
370
return toWorkspaceIdentifier(this.configuration.backupPath, environmentService.isExtensionDevelopment);
371
}
372
373
private async createWorkspaceService(
374
workspace: IAnyWorkspaceIdentifier,
375
environmentService: INativeWorkbenchEnvironmentService,
376
userDataProfileService: IUserDataProfileService,
377
userDataProfilesService: IUserDataProfilesService,
378
fileService: FileService,
379
remoteAgentService: IRemoteAgentService,
380
uriIdentityService: IUriIdentityService,
381
logService: ILogService,
382
policyService: IPolicyService
383
): Promise<WorkspaceService> {
384
const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData] /* Cache all non native resources */, environmentService, fileService);
385
const workspaceService = new WorkspaceService({ remoteAuthority: environmentService.remoteAuthority, configurationCache }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, policyService);
386
387
try {
388
await workspaceService.initialize(workspace);
389
390
return workspaceService;
391
} catch (error) {
392
onUnexpectedError(error);
393
394
return workspaceService;
395
}
396
}
397
398
private async createStorageService(workspace: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService, userDataProfilesService: IUserDataProfilesService, mainProcessService: IMainProcessService): Promise<NativeWorkbenchStorageService> {
399
const storageService = new NativeWorkbenchStorageService(workspace, userDataProfileService, userDataProfilesService, mainProcessService, environmentService);
400
401
try {
402
await storageService.initialize();
403
404
return storageService;
405
} catch (error) {
406
onUnexpectedError(error);
407
408
return storageService;
409
}
410
}
411
412
private async createKeyboardLayoutService(mainProcessService: IMainProcessService): Promise<NativeKeyboardLayoutService> {
413
const keyboardLayoutService = new NativeKeyboardLayoutService(mainProcessService);
414
415
try {
416
await keyboardLayoutService.initialize();
417
418
return keyboardLayoutService;
419
} catch (error) {
420
onUnexpectedError(error);
421
422
return keyboardLayoutService;
423
}
424
}
425
}
426
427
export interface IDesktopMain {
428
main(configuration: INativeWindowConfiguration): Promise<void>;
429
}
430
431
export function main(configuration: INativeWindowConfiguration): Promise<void> {
432
const workbench = new DesktopMain(configuration);
433
434
return workbench.open();
435
}
436
437