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