Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/sessions/electron-browser/sessions.main.ts
13389 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, hasNativeMenu } from '../../platform/window/common/window.js';
9
import { NativeWindow } from '../../workbench/electron-browser/window.js';
10
import { setFullscreen } from '../../base/browser/browser.js';
11
import { domContentLoaded } from '../../base/browser/dom.js';
12
import { onUnexpectedError } from '../../base/common/errors.js';
13
import { URI } from '../../base/common/uri.js';
14
import { INativeWorkbenchEnvironmentService, NativeWorkbenchEnvironmentService } from '../../workbench/services/environment/electron-browser/environmentService.js';
15
import { ServiceCollection } from '../../platform/instantiation/common/serviceCollection.js';
16
import { ILoggerService, ILogService, LogLevel } from '../../platform/log/common/log.js';
17
import { NativeWorkbenchStorageService } from '../../workbench/services/storage/electron-browser/storageService.js';
18
import { IWorkspaceContextService, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IAnyWorkspaceIdentifier, reviveIdentifier } from '../../platform/workspace/common/workspace.js';
19
import { IWorkbenchConfigurationService } from '../../workbench/services/configuration/common/configuration.js';
20
import { IStorageService } from '../../platform/storage/common/storage.js';
21
import { Disposable } from '../../base/common/lifecycle.js';
22
import { ISharedProcessService } from '../../platform/ipc/electron-browser/services.js';
23
import { IMainProcessService } from '../../platform/ipc/common/mainProcessService.js';
24
import { SharedProcessService } from '../../workbench/services/sharedProcess/electron-browser/sharedProcessService.js';
25
import { RemoteAuthorityResolverService } from '../../platform/remote/electron-browser/remoteAuthorityResolverService.js';
26
import { IRemoteAuthorityResolverService, RemoteConnectionType } from '../../platform/remote/common/remoteAuthorityResolver.js';
27
import { RemoteAgentService } from '../../workbench/services/remote/electron-browser/remoteAgentService.js';
28
import { IRemoteAgentService } from '../../workbench/services/remote/common/remoteAgentService.js';
29
import { FileService } from '../../platform/files/common/fileService.js';
30
import { IFileService } from '../../platform/files/common/files.js';
31
import { RemoteFileSystemProviderClient } from '../../workbench/services/remote/common/remoteFileSystemProviderClient.js';
32
import { ISignService } from '../../platform/sign/common/sign.js';
33
import { IProductService } from '../../platform/product/common/productService.js';
34
import { IUriIdentityService } from '../../platform/uriIdentity/common/uriIdentity.js';
35
import { UriIdentityService } from '../../platform/uriIdentity/common/uriIdentityService.js';
36
import { INativeKeyboardLayoutService, NativeKeyboardLayoutService } from '../../workbench/services/keybinding/electron-browser/nativeKeyboardLayoutService.js';
37
import { ElectronIPCMainProcessService } from '../../platform/ipc/electron-browser/mainProcessService.js';
38
import { LoggerChannelClient } from '../../platform/log/common/logIpc.js';
39
import { ProxyChannel } from '../../base/parts/ipc/common/ipc.js';
40
import { NativeLogService } from '../../workbench/services/log/electron-browser/logService.js';
41
import { WorkspaceTrustEnablementService, WorkspaceTrustManagementService } from '../../workbench/services/workspaces/common/workspaceTrust.js';
42
import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService } from '../../platform/workspace/common/workspaceTrust.js';
43
import { safeStringify } from '../../base/common/objects.js';
44
import { IUtilityProcessWorkerWorkbenchService, UtilityProcessWorkerWorkbenchService } from '../../workbench/services/utilityProcess/electron-browser/utilityProcessWorkerWorkbenchService.js';
45
import { isCI, isMacintosh, isTahoeOrNewer } from '../../base/common/platform.js';
46
import { Schemas } from '../../base/common/network.js';
47
import { DiskFileSystemProvider } from '../../workbench/services/files/electron-browser/diskFileSystemProvider.js';
48
import { FileUserDataProvider } from '../../platform/userData/common/fileUserDataProvider.js';
49
import { IUserDataProfilesService, reviveProfile } from '../../platform/userDataProfile/common/userDataProfile.js';
50
import { UserDataProfilesService } from '../../platform/userDataProfile/common/userDataProfileIpc.js';
51
import { PolicyChannelClient } from '../../platform/policy/common/policyIpc.js';
52
import { IPolicyService } from '../../platform/policy/common/policy.js';
53
import { UserDataProfileService } from '../../workbench/services/userDataProfile/common/userDataProfileService.js';
54
import { IUserDataProfileService } from '../../workbench/services/userDataProfile/common/userDataProfile.js';
55
import { BrowserSocketFactory } from '../../platform/remote/browser/browserSocketFactory.js';
56
import { RemoteSocketFactoryService, IRemoteSocketFactoryService } from '../../platform/remote/common/remoteSocketFactoryService.js';
57
import { ElectronRemoteResourceLoader } from '../../platform/remote/electron-browser/electronRemoteResourceLoader.js';
58
import { IConfigurationService } from '../../platform/configuration/common/configuration.js';
59
import { applyZoom } from '../../platform/window/electron-browser/window.js';
60
import { mainWindow } from '../../base/browser/window.js';
61
import { IDefaultAccountService } from '../../platform/defaultAccount/common/defaultAccount.js';
62
import { DefaultAccountService } from '../../workbench/services/accounts/browser/defaultAccount.js';
63
import { AccountPolicyService, IAccountPolicyGateService } from '../../workbench/services/policies/common/accountPolicyService.js';
64
import { MultiplexPolicyService } from '../../workbench/services/policies/common/multiplexPolicyService.js';
65
import { Workbench as AgenticWorkbench } from '../browser/workbench.js';
66
import { NativeMenubarControl } from '../../workbench/electron-browser/parts/titlebar/menubarControl.js';
67
import { IWorkspaceEditingService } from '../../workbench/services/workspaces/common/workspaceEditing.js';
68
import { ConfigurationService } from '../services/configuration/browser/configurationService.js';
69
import { SessionsWorkspaceContextService } from '../services/workspace/browser/workspaceContextService.js';
70
import { getWorkspaceIdentifier } from '../../workbench/services/workspaces/browser/workspaces.js';
71
72
export class SessionsMain extends Disposable {
73
74
constructor(
75
private readonly configuration: INativeWindowConfiguration
76
) {
77
super();
78
79
this.init();
80
}
81
82
private init(): void {
83
84
// Massage configuration file URIs
85
this.reviveUris();
86
87
// Apply fullscreen early if configured
88
setFullscreen(!!this.configuration.fullscreen, mainWindow);
89
}
90
91
private reviveUris() {
92
93
// Workspace
94
const workspace = reviveIdentifier(this.configuration.workspace);
95
if (isWorkspaceIdentifier(workspace) || isSingleFolderWorkspaceIdentifier(workspace)) {
96
this.configuration.workspace = workspace;
97
}
98
99
// Files
100
const filesToWait = this.configuration.filesToWait;
101
const filesToWaitPaths = filesToWait?.paths;
102
for (const paths of [filesToWaitPaths, this.configuration.filesToOpenOrCreate, this.configuration.filesToDiff, this.configuration.filesToMerge]) {
103
if (Array.isArray(paths)) {
104
for (const path of paths) {
105
if (path.fileUri) {
106
path.fileUri = URI.revive(path.fileUri);
107
}
108
}
109
}
110
}
111
112
if (filesToWait) {
113
filesToWait.waitMarkerFileUri = URI.revive(filesToWait.waitMarkerFileUri);
114
}
115
}
116
117
async open(): Promise<void> {
118
119
// Init services and wait for DOM to be ready in parallel
120
const [services] = await Promise.all([this.initServices(), domContentLoaded(mainWindow)]);
121
122
// Apply zoom level early
123
this.applyWindowZoomLevel(services.configurationService);
124
125
// Create Agentic Workbench
126
const workbench = new AgenticWorkbench(mainWindow.document.body, {
127
extraClasses: this.getExtraClasses(),
128
}, services.serviceCollection, services.logService);
129
130
// Listeners
131
this.registerListeners(workbench, services.storageService);
132
133
// Startup
134
const instantiationService = workbench.startup();
135
136
// Window
137
this._register(instantiationService.createInstance(NativeWindow));
138
139
// Native menu controller
140
if (isMacintosh || hasNativeMenu(services.configurationService)) {
141
this._register(instantiationService.createInstance(NativeMenubarControl));
142
}
143
}
144
145
private applyWindowZoomLevel(configurationService: IConfigurationService) {
146
let zoomLevel: number | undefined = undefined;
147
if (this.configuration.isCustomZoomLevel && typeof this.configuration.zoomLevel === 'number') {
148
zoomLevel = this.configuration.zoomLevel;
149
} else {
150
const windowConfig = configurationService.getValue<IWindowsConfiguration>();
151
zoomLevel = typeof windowConfig.window?.zoomLevel === 'number' ? windowConfig.window.zoomLevel : 0;
152
}
153
154
applyZoom(zoomLevel, mainWindow);
155
}
156
157
private getExtraClasses(): string[] {
158
if (isMacintosh && isTahoeOrNewer(this.configuration.os.release)) {
159
return ['macos-tahoe'];
160
}
161
162
return [];
163
}
164
165
private registerListeners(workbench: AgenticWorkbench, storageService: NativeWorkbenchStorageService): void {
166
167
// Workbench Lifecycle
168
this._register(workbench.onWillShutdown(event => event.join(storageService.close(), { id: 'join.closeStorage', label: localize('join.closeStorage', "Saving UI state") })));
169
this._register(workbench.onDidShutdown(() => this.dispose()));
170
}
171
172
private async initServices(): Promise<{ serviceCollection: ServiceCollection; logService: ILogService; storageService: NativeWorkbenchStorageService; configurationService: ConfigurationService }> {
173
const serviceCollection = new ServiceCollection();
174
175
176
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
177
//
178
// NOTE: Please do NOT register services here. Use `registerSingleton()`
179
// from `workbench.common.main.ts` if the service is shared between
180
// desktop and web or `sessions/sessions.desktop.main.ts` if the service
181
// is sessions desktop only.
182
//
183
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
184
185
186
// Main Process
187
const mainProcessService = this._register(new ElectronIPCMainProcessService(this.configuration.windowId));
188
serviceCollection.set(IMainProcessService, mainProcessService);
189
190
// Product
191
const productService: IProductService = { _serviceBrand: undefined, ...product };
192
serviceCollection.set(IProductService, productService);
193
194
// Environment
195
const environmentService = new NativeWorkbenchEnvironmentService(this.configuration, productService);
196
serviceCollection.set(INativeWorkbenchEnvironmentService, environmentService);
197
198
// Logger
199
const loggers = this.configuration.loggers.map(loggerResource => ({ ...loggerResource, resource: URI.revive(loggerResource.resource) }));
200
const loggerService = new LoggerChannelClient(this.configuration.windowId, this.configuration.logLevel, environmentService.windowLogsPath, loggers, mainProcessService.getChannel('logger'));
201
serviceCollection.set(ILoggerService, loggerService);
202
203
// Log
204
const logService = this._register(new NativeLogService(loggerService, environmentService));
205
serviceCollection.set(ILogService, logService);
206
if (isCI) {
207
logService.info('workbench#open()'); // marking workbench open helps to diagnose flaky integration/smoke tests
208
}
209
if (logService.getLevel() === LogLevel.Trace) {
210
logService.trace('workbench#open(): with configuration', safeStringify({ ...this.configuration, nls: undefined /* exclude large property */ }));
211
}
212
213
// Default Account
214
const defaultAccountService = this._register(new DefaultAccountService(productService));
215
serviceCollection.set(IDefaultAccountService, defaultAccountService);
216
217
// Policies
218
let policyService: IPolicyService;
219
const policyChannel = this.configuration.policiesData ? new PolicyChannelClient(this.configuration.policiesData, mainProcessService.getChannel('policy')) : undefined;
220
const accountPolicy = new AccountPolicyService(logService, defaultAccountService, policyChannel);
221
if (policyChannel) {
222
policyService = new MultiplexPolicyService([policyChannel, accountPolicy], logService);
223
} else {
224
policyService = accountPolicy;
225
}
226
serviceCollection.set(IPolicyService, policyService);
227
serviceCollection.set(IAccountPolicyGateService, accountPolicy);
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 `sessions/sessions.desktop.main.ts` if the service
242
// is sessions 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 `sessions/sessions.desktop.main.ts` if the service
292
// is sessions desktop only.
293
//
294
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
295
296
const workspaceIdentifier = getWorkspaceIdentifier(environmentService.agentSessionsWorkspace);
297
const workspaceContextService = new SessionsWorkspaceContextService(workspaceIdentifier, uriIdentityService);
298
299
// Workspace
300
serviceCollection.set(IWorkspaceContextService, workspaceContextService);
301
serviceCollection.set(IWorkspaceEditingService, workspaceContextService);
302
303
const [configurationService, storageService] = await Promise.all([
304
this.createConfigurationService(workspaceContextService, userDataProfileService, uriIdentityService, fileService, logService, policyService).then(configurationService => {
305
306
// Configuration
307
serviceCollection.set(IWorkbenchConfigurationService, configurationService);
308
309
return configurationService;
310
}),
311
312
this.createStorageService(workspaceIdentifier, environmentService, userDataProfileService, userDataProfilesService, mainProcessService).then(service => {
313
314
// Storage
315
serviceCollection.set(IStorageService, service);
316
317
return service;
318
}),
319
320
this.createKeyboardLayoutService(mainProcessService).then(service => {
321
322
// KeyboardLayout
323
serviceCollection.set(INativeKeyboardLayoutService, service);
324
325
return service;
326
})
327
]);
328
329
// Workspace Trust Service
330
const workspaceTrustEnablementService = new WorkspaceTrustEnablementService(configurationService, environmentService);
331
serviceCollection.set(IWorkspaceTrustEnablementService, workspaceTrustEnablementService);
332
333
const workspaceTrustManagementService = new WorkspaceTrustManagementService(configurationService, remoteAuthorityResolverService, storageService, uriIdentityService, environmentService, workspaceContextService, workspaceTrustEnablementService, fileService);
334
serviceCollection.set(IWorkspaceTrustManagementService, workspaceTrustManagementService);
335
336
337
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
338
//
339
// NOTE: Please do NOT register services here. Use `registerSingleton()`
340
// from `workbench.common.main.ts` if the service is shared between
341
// desktop and web or `sessions/sessions.desktop.main.ts` if the service
342
// is sessions desktop only.
343
//
344
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
345
346
347
return { serviceCollection, logService, storageService, configurationService };
348
}
349
350
private async createConfigurationService(
351
workspaceContextService: SessionsWorkspaceContextService,
352
userDataProfileService: IUserDataProfileService,
353
uriIdentityService: IUriIdentityService,
354
fileService: FileService,
355
logService: ILogService,
356
policyService: IPolicyService
357
): Promise<ConfigurationService> {
358
const configurationService = new ConfigurationService(userDataProfileService, workspaceContextService, uriIdentityService, fileService, policyService, logService);
359
try {
360
await configurationService.initialize();
361
} catch (error) {
362
onUnexpectedError(error);
363
}
364
365
return configurationService;
366
}
367
368
private async createStorageService(workspace: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService, userDataProfilesService: IUserDataProfilesService, mainProcessService: IMainProcessService): Promise<NativeWorkbenchStorageService> {
369
const storageService = new NativeWorkbenchStorageService(workspace, userDataProfileService, userDataProfilesService, mainProcessService, environmentService);
370
371
try {
372
await storageService.initialize();
373
374
return storageService;
375
} catch (error) {
376
onUnexpectedError(error);
377
378
return storageService;
379
}
380
}
381
382
private async createKeyboardLayoutService(mainProcessService: IMainProcessService): Promise<NativeKeyboardLayoutService> {
383
const keyboardLayoutService = new NativeKeyboardLayoutService(mainProcessService);
384
385
try {
386
await keyboardLayoutService.initialize();
387
388
return keyboardLayoutService;
389
} catch (error) {
390
onUnexpectedError(error);
391
392
return keyboardLayoutService;
393
}
394
}
395
}
396
397
export interface IDesktopMain {
398
main(configuration: INativeWindowConfiguration): Promise<void>;
399
}
400
401
export function main(configuration: INativeWindowConfiguration): Promise<void> {
402
const workbench = new SessionsMain(configuration);
403
404
return workbench.open();
405
}
406
407