Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/browser/web.main.ts
5237 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 { mark } from '../../base/common/performance.js';
7
import { domContentLoaded, detectFullscreen, getCookieValue, getWindow } from '../../base/browser/dom.js';
8
import { assertReturnsDefined } from '../../base/common/types.js';
9
import { ServiceCollection } from '../../platform/instantiation/common/serviceCollection.js';
10
import { ILogService, ConsoleLogger, getLogLevel, ILoggerService, ILogger } from '../../platform/log/common/log.js';
11
import { ConsoleLogInAutomationLogger } from '../../platform/log/browser/log.js';
12
import { Disposable, DisposableStore, toDisposable } from '../../base/common/lifecycle.js';
13
import { BrowserWorkbenchEnvironmentService, IBrowserWorkbenchEnvironmentService } from '../services/environment/browser/environmentService.js';
14
import { Workbench } from './workbench.js';
15
import { RemoteFileSystemProviderClient } from '../services/remote/common/remoteFileSystemProviderClient.js';
16
import { IWorkbenchEnvironmentService } from '../services/environment/common/environmentService.js';
17
import { IProductService } from '../../platform/product/common/productService.js';
18
import product from '../../platform/product/common/product.js';
19
import { RemoteAgentService } from '../services/remote/browser/remoteAgentService.js';
20
import { RemoteAuthorityResolverService } from '../../platform/remote/browser/remoteAuthorityResolverService.js';
21
import { IRemoteAuthorityResolverService, RemoteConnectionType } from '../../platform/remote/common/remoteAuthorityResolver.js';
22
import { IRemoteAgentService } from '../services/remote/common/remoteAgentService.js';
23
import { IFileService } from '../../platform/files/common/files.js';
24
import { FileService } from '../../platform/files/common/fileService.js';
25
import { Schemas, connectionTokenCookieName } from '../../base/common/network.js';
26
import { IAnyWorkspaceIdentifier, IWorkspaceContextService, UNKNOWN_EMPTY_WINDOW_WORKSPACE, isTemporaryWorkspace, isWorkspaceIdentifier } from '../../platform/workspace/common/workspace.js';
27
import { IWorkbenchConfigurationService } from '../services/configuration/common/configuration.js';
28
import { onUnexpectedError } from '../../base/common/errors.js';
29
import { setFullscreen } from '../../base/browser/browser.js';
30
import { URI, UriComponents } from '../../base/common/uri.js';
31
import { WorkspaceService } from '../services/configuration/browser/configurationService.js';
32
import { ConfigurationCache } from '../services/configuration/common/configurationCache.js';
33
import { ISignService } from '../../platform/sign/common/sign.js';
34
import { SignService } from '../../platform/sign/browser/signService.js';
35
import { IWorkbenchConstructionOptions, IWorkbench, IWorkspace, ITunnel } from './web.api.js';
36
import { BrowserStorageService } from '../services/storage/browser/storageService.js';
37
import { IStorageService } from '../../platform/storage/common/storage.js';
38
import { toLocalISOString } from '../../base/common/date.js';
39
import { isWorkspaceToOpen, isFolderToOpen } from '../../platform/window/common/window.js';
40
import { getSingleFolderWorkspaceIdentifier, getWorkspaceIdentifier } from '../services/workspaces/browser/workspaces.js';
41
import { InMemoryFileSystemProvider } from '../../platform/files/common/inMemoryFilesystemProvider.js';
42
import { ICommandService } from '../../platform/commands/common/commands.js';
43
import { IndexedDBFileSystemProvider } from '../../platform/files/browser/indexedDBFileSystemProvider.js';
44
import { BrowserRequestService } from '../services/request/browser/requestService.js';
45
import { IRequestService } from '../../platform/request/common/request.js';
46
import { IUserDataInitializationService, IUserDataInitializer, UserDataInitializationService } from '../services/userData/browser/userDataInit.js';
47
import { UserDataSyncStoreManagementService } from '../../platform/userDataSync/common/userDataSyncStoreService.js';
48
import { IUserDataSyncStoreManagementService } from '../../platform/userDataSync/common/userDataSync.js';
49
import { ILifecycleService } from '../services/lifecycle/common/lifecycle.js';
50
import { Action2, MenuId, registerAction2 } from '../../platform/actions/common/actions.js';
51
import { IInstantiationService, ServicesAccessor } from '../../platform/instantiation/common/instantiation.js';
52
import { localize, localize2 } from '../../nls.js';
53
import { Categories } from '../../platform/action/common/actionCommonCategories.js';
54
import { IDialogService } from '../../platform/dialogs/common/dialogs.js';
55
import { IHostService } from '../services/host/browser/host.js';
56
import { IUriIdentityService } from '../../platform/uriIdentity/common/uriIdentity.js';
57
import { UriIdentityService } from '../../platform/uriIdentity/common/uriIdentityService.js';
58
import { BrowserWindow } from './window.js';
59
import { ITimerService } from '../services/timer/browser/timerService.js';
60
import { WorkspaceTrustEnablementService, WorkspaceTrustManagementService } from '../services/workspaces/common/workspaceTrust.js';
61
import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService } from '../../platform/workspace/common/workspaceTrust.js';
62
import { HTMLFileSystemProvider } from '../../platform/files/browser/htmlFileSystemProvider.js';
63
import { IOpenerService } from '../../platform/opener/common/opener.js';
64
import { mixin, safeStringify } from '../../base/common/objects.js';
65
import { IndexedDB } from '../../base/browser/indexedDB.js';
66
import { WebFileSystemAccess } from '../../platform/files/browser/webFileSystemAccess.js';
67
import { IProgressService } from '../../platform/progress/common/progress.js';
68
import { DelayedLogChannel } from '../services/output/common/delayedLogChannel.js';
69
import { dirname, joinPath } from '../../base/common/resources.js';
70
import { IUserDataProfile, IUserDataProfilesService } from '../../platform/userDataProfile/common/userDataProfile.js';
71
import { IPolicyService } from '../../platform/policy/common/policy.js';
72
import { IRemoteExplorerService } from '../services/remote/common/remoteExplorerService.js';
73
import { DisposableTunnel, TunnelProtocol } from '../../platform/tunnel/common/tunnel.js';
74
import { ILabelService } from '../../platform/label/common/label.js';
75
import { UserDataProfileService } from '../services/userDataProfile/common/userDataProfileService.js';
76
import { IUserDataProfileService } from '../services/userDataProfile/common/userDataProfile.js';
77
import { BrowserUserDataProfilesService } from '../../platform/userDataProfile/browser/userDataProfile.js';
78
import { DeferredPromise, timeout } from '../../base/common/async.js';
79
import { windowLogGroup, windowLogId } from '../services/log/common/logConstants.js';
80
import { LogService } from '../../platform/log/common/logService.js';
81
import { IRemoteSocketFactoryService, RemoteSocketFactoryService } from '../../platform/remote/common/remoteSocketFactoryService.js';
82
import { BrowserSocketFactory } from '../../platform/remote/browser/browserSocketFactory.js';
83
import { VSBuffer } from '../../base/common/buffer.js';
84
import { IStoredWorkspace } from '../../platform/workspaces/common/workspaces.js';
85
import { UserDataProfileInitializer } from '../services/userDataProfile/browser/userDataProfileInit.js';
86
import { UserDataSyncInitializer } from '../services/userDataSync/browser/userDataSyncInit.js';
87
import { BrowserRemoteResourceLoader } from '../services/remote/browser/browserRemoteResourceHandler.js';
88
import { BufferLogger } from '../../platform/log/common/bufferLog.js';
89
import { FileLoggerService } from '../../platform/log/common/fileLog.js';
90
import { IEmbedderTerminalService } from '../services/terminal/common/embedderTerminalService.js';
91
import { BrowserSecretStorageService } from '../services/secrets/browser/secretStorageService.js';
92
import { EncryptionService } from '../services/encryption/browser/encryptionService.js';
93
import { IEncryptionService } from '../../platform/encryption/common/encryptionService.js';
94
import { ISecretStorageService } from '../../platform/secrets/common/secrets.js';
95
import { TunnelSource } from '../services/remote/common/tunnelModel.js';
96
import { mainWindow } from '../../base/browser/window.js';
97
import { INotificationService, Severity } from '../../platform/notification/common/notification.js';
98
import { IDefaultAccountService } from '../../platform/defaultAccount/common/defaultAccount.js';
99
import { DefaultAccountService } from '../services/accounts/browser/defaultAccount.js';
100
import { AccountPolicyService } from '../services/policies/common/accountPolicyService.js';
101
import { WorkbenchModeService } from '../services/layout/browser/workbenchModeService.js';
102
import { IWorkbenchModeService } from '../services/layout/common/workbenchModeService.js';
103
104
export class BrowserMain extends Disposable {
105
106
private readonly onWillShutdownDisposables = this._register(new DisposableStore());
107
private readonly indexedDBFileSystemProviders: IndexedDBFileSystemProvider[] = [];
108
109
constructor(
110
private readonly domElement: HTMLElement,
111
private readonly configuration: IWorkbenchConstructionOptions
112
) {
113
super();
114
115
this.init();
116
}
117
118
private init(): void {
119
120
// Browser config
121
setFullscreen(!!detectFullscreen(mainWindow), mainWindow);
122
}
123
124
async open(): Promise<IWorkbench> {
125
126
// Init services and wait for DOM to be ready in parallel
127
const [services] = await Promise.all([this.initServices(), domContentLoaded(getWindow(this.domElement))]);
128
129
// Create Workbench
130
const workbench = new Workbench(this.domElement, undefined, services.serviceCollection, services.logService);
131
132
// Listeners
133
this.registerListeners(workbench);
134
135
// Startup
136
const instantiationService = workbench.startup();
137
138
// Window
139
this._register(instantiationService.createInstance(BrowserWindow));
140
141
// Logging
142
services.logService.trace('workbench#open with configuration', safeStringify(this.configuration));
143
144
// Return API Facade
145
return instantiationService.invokeFunction(accessor => {
146
const commandService = accessor.get(ICommandService);
147
const lifecycleService = accessor.get(ILifecycleService);
148
const timerService = accessor.get(ITimerService);
149
const openerService = accessor.get(IOpenerService);
150
const productService = accessor.get(IProductService);
151
const progressService = accessor.get(IProgressService);
152
const environmentService = accessor.get(IBrowserWorkbenchEnvironmentService);
153
const instantiationService = accessor.get(IInstantiationService);
154
const remoteExplorerService = accessor.get(IRemoteExplorerService);
155
const labelService = accessor.get(ILabelService);
156
const embedderTerminalService = accessor.get(IEmbedderTerminalService);
157
const remoteAuthorityResolverService = accessor.get(IRemoteAuthorityResolverService);
158
const notificationService = accessor.get(INotificationService);
159
160
async function showMessage<T extends string>(severity: Severity, message: string, ...items: T[]): Promise<T | undefined> {
161
const choice = new DeferredPromise<T | undefined>();
162
const handle = notificationService.prompt(severity, message, items.map(item => ({
163
label: item,
164
run: () => choice.complete(item)
165
})));
166
const disposable = handle.onDidClose(() => {
167
choice.complete(undefined);
168
disposable.dispose();
169
});
170
const result = await choice.p;
171
handle.close();
172
return result;
173
}
174
175
let logger: DelayedLogChannel | undefined = undefined;
176
177
return {
178
commands: {
179
executeCommand: (command, ...args) => commandService.executeCommand(command, ...args)
180
},
181
env: {
182
async getUriScheme(): Promise<string> {
183
return productService.urlProtocol;
184
},
185
async retrievePerformanceMarks() {
186
await timerService.whenReady();
187
188
return timerService.getPerformanceMarks();
189
},
190
async openUri(uri: URI | UriComponents): Promise<boolean> {
191
return openerService.open(URI.isUri(uri) ? uri : URI.from(uri), {});
192
}
193
},
194
logger: {
195
log: (level, message) => {
196
if (!logger) {
197
logger = instantiationService.createInstance(DelayedLogChannel, 'webEmbedder', productService.embedderIdentifier || productService.nameShort, joinPath(dirname(environmentService.logFile), 'webEmbedder.log'));
198
}
199
200
logger.log(level, message);
201
}
202
},
203
window: {
204
withProgress: (options, task) => progressService.withProgress(options, task),
205
createTerminal: async (options) => embedderTerminalService.createTerminal(options),
206
showInformationMessage: (message, ...items) => showMessage(Severity.Info, message, ...items),
207
},
208
workspace: {
209
didResolveRemoteAuthority: async () => {
210
if (!this.configuration.remoteAuthority) {
211
return;
212
}
213
214
await remoteAuthorityResolverService.resolveAuthority(this.configuration.remoteAuthority);
215
},
216
openTunnel: async tunnelOptions => {
217
const tunnel = assertReturnsDefined(await remoteExplorerService.forward({
218
remote: tunnelOptions.remoteAddress,
219
local: tunnelOptions.localAddressPort,
220
name: tunnelOptions.label,
221
source: {
222
source: TunnelSource.Extension,
223
description: labelService.getHostLabel(Schemas.vscodeRemote, this.configuration.remoteAuthority)
224
},
225
elevateIfNeeded: false,
226
privacy: tunnelOptions.privacy
227
}, {
228
label: tunnelOptions.label,
229
elevateIfNeeded: undefined,
230
onAutoForward: undefined,
231
requireLocalPort: undefined,
232
protocol: tunnelOptions.protocol === TunnelProtocol.Https ? tunnelOptions.protocol : TunnelProtocol.Http
233
}));
234
235
if (typeof tunnel === 'string') {
236
throw new Error(tunnel);
237
}
238
239
return new class extends DisposableTunnel implements ITunnel {
240
declare localAddress: string;
241
}({
242
port: tunnel.tunnelRemotePort,
243
host: tunnel.tunnelRemoteHost
244
}, tunnel.localAddress, () => tunnel.dispose());
245
}
246
},
247
shutdown: () => lifecycleService.shutdown()
248
} satisfies IWorkbench;
249
});
250
}
251
252
private registerListeners(workbench: Workbench): void {
253
254
// Workbench Lifecycle
255
this._register(workbench.onWillShutdown(() => this.onWillShutdownDisposables.clear()));
256
this._register(workbench.onDidShutdown(() => this.dispose()));
257
}
258
259
private async initServices(): Promise<{ serviceCollection: ServiceCollection; configurationService: IWorkbenchConfigurationService; logService: ILogService }> {
260
const serviceCollection = new ServiceCollection();
261
262
263
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
264
//
265
// NOTE: Please do NOT register services here. Use `registerSingleton()`
266
// from `workbench.common.main.ts` if the service is shared between
267
// desktop and web or `workbench.web.main.ts` if the service
268
// is web only.
269
//
270
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
271
272
273
const workspace = this.resolveWorkspace();
274
275
// Product
276
const productService: IProductService = mixin({ _serviceBrand: undefined, ...product }, this.configuration.productConfiguration);
277
serviceCollection.set(IProductService, productService);
278
279
// Environment
280
const logsPath = URI.file(toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')).with({ scheme: 'vscode-log' });
281
const environmentService = new BrowserWorkbenchEnvironmentService(workspace.id, logsPath, this.configuration, productService);
282
serviceCollection.set(IBrowserWorkbenchEnvironmentService, environmentService);
283
284
// Files
285
const fileLogger = new BufferLogger();
286
const fileService = this._register(new FileService(fileLogger));
287
serviceCollection.set(IFileService, fileService);
288
289
// Logger
290
const loggerService = new FileLoggerService(getLogLevel(environmentService), logsPath, fileService);
291
serviceCollection.set(ILoggerService, loggerService);
292
293
// Log Service
294
const otherLoggers: ILogger[] = [new ConsoleLogger(loggerService.getLogLevel())];
295
if (environmentService.isExtensionDevelopment && !!environmentService.extensionTestsLocationURI) {
296
otherLoggers.push(new ConsoleLogInAutomationLogger(loggerService.getLogLevel()));
297
}
298
const logger = loggerService.createLogger(environmentService.logFile, { id: windowLogId, name: windowLogGroup.name, group: windowLogGroup });
299
const logService = new LogService(logger, otherLoggers);
300
serviceCollection.set(ILogService, logService);
301
302
// Set the logger of the fileLogger after the log service is ready.
303
// This is to avoid cyclic dependency
304
fileLogger.logger = logService;
305
306
// Register File System Providers depending on IndexedDB support
307
// Register them early because they are needed for the profiles initialization
308
await this.registerIndexedDBFileSystemProviders(environmentService, fileService, logService, loggerService, logsPath);
309
310
311
const connectionToken = environmentService.options.connectionToken || getCookieValue(connectionTokenCookieName);
312
const remoteResourceLoader = this.configuration.remoteResourceProvider ? new BrowserRemoteResourceLoader(fileService, this.configuration.remoteResourceProvider) : undefined;
313
const resourceUriProvider = this.configuration.resourceUriProvider ?? remoteResourceLoader?.getResourceUriProvider();
314
const remoteAuthorityResolverService = new RemoteAuthorityResolverService(!environmentService.expectsResolverExtension, connectionToken, resourceUriProvider, this.configuration.serverBasePath, productService, logService);
315
serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService);
316
317
// Signing
318
const signService = new SignService(productService);
319
serviceCollection.set(ISignService, signService);
320
321
322
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
323
//
324
// NOTE: Please do NOT register services here. Use `registerSingleton()`
325
// from `workbench.common.main.ts` if the service is shared between
326
// desktop and web or `workbench.web.main.ts` if the service
327
// is web only.
328
//
329
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
330
331
332
// URI Identity
333
const uriIdentityService = new UriIdentityService(fileService);
334
serviceCollection.set(IUriIdentityService, uriIdentityService);
335
336
// User Data Profiles
337
const userDataProfilesService = new BrowserUserDataProfilesService(environmentService, fileService, uriIdentityService, logService);
338
serviceCollection.set(IUserDataProfilesService, userDataProfilesService);
339
340
const currentProfile = await this.getCurrentProfile(workspace, userDataProfilesService, environmentService);
341
await userDataProfilesService.setProfileForWorkspace(workspace, currentProfile);
342
const userDataProfileService = new UserDataProfileService(currentProfile);
343
serviceCollection.set(IUserDataProfileService, userDataProfileService);
344
345
// Remote Agent
346
const remoteSocketFactoryService = new RemoteSocketFactoryService();
347
remoteSocketFactoryService.register(RemoteConnectionType.WebSocket, new BrowserSocketFactory(this.configuration.webSocketFactory));
348
serviceCollection.set(IRemoteSocketFactoryService, remoteSocketFactoryService);
349
const remoteAgentService = this._register(new RemoteAgentService(remoteSocketFactoryService, userDataProfileService, environmentService, productService, remoteAuthorityResolverService, signService, logService));
350
serviceCollection.set(IRemoteAgentService, remoteAgentService);
351
this._register(RemoteFileSystemProviderClient.register(remoteAgentService, fileService, logService));
352
353
// Default Account
354
const defaultAccountService = this._register(new DefaultAccountService(productService));
355
serviceCollection.set(IDefaultAccountService, defaultAccountService);
356
357
// Policies
358
const policyService = new AccountPolicyService(logService, defaultAccountService);
359
serviceCollection.set(IPolicyService, policyService);
360
361
// Long running services (workspace, config, storage)
362
const [configurationService, storageService] = await Promise.all([
363
this.createWorkspaceService(workspace, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, policyService, logService).then(service => {
364
365
// Workspace
366
serviceCollection.set(IWorkspaceContextService, service);
367
368
// Configuration
369
serviceCollection.set(IWorkbenchConfigurationService, service);
370
371
return service;
372
}),
373
374
this.createStorageService(workspace, logService, userDataProfileService).then(service => {
375
376
// Storage
377
serviceCollection.set(IStorageService, service);
378
379
return service;
380
})
381
]);
382
383
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
384
//
385
// NOTE: Please do NOT register services here. Use `registerSingleton()`
386
// from `workbench.common.main.ts` if the service is shared between
387
// desktop and web or `workbench.web.main.ts` if the service
388
// is web only.
389
//
390
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
391
392
// Layout Mode
393
const workbenchModeService: WorkbenchModeService = this._register(new WorkbenchModeService(configurationService, fileService, environmentService, uriIdentityService, logService, storageService));
394
serviceCollection.set(IWorkbenchModeService, workbenchModeService);
395
try {
396
await workbenchModeService.initialize();
397
} catch (error) {
398
logService.error('Error while initializing workbench mode service', error);
399
}
400
401
// Workspace Trust Service
402
const workspaceTrustEnablementService = new WorkspaceTrustEnablementService(configurationService, environmentService);
403
serviceCollection.set(IWorkspaceTrustEnablementService, workspaceTrustEnablementService);
404
405
const workspaceTrustManagementService = new WorkspaceTrustManagementService(configurationService, remoteAuthorityResolverService, storageService, uriIdentityService, environmentService, configurationService, workspaceTrustEnablementService, fileService);
406
serviceCollection.set(IWorkspaceTrustManagementService, workspaceTrustManagementService);
407
408
// Update workspace trust so that configuration is updated accordingly
409
configurationService.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted());
410
this._register(workspaceTrustManagementService.onDidChangeTrust(() => configurationService.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted())));
411
412
// Request Service
413
const requestService = new BrowserRequestService(remoteAgentService, configurationService, loggerService);
414
serviceCollection.set(IRequestService, requestService);
415
416
// Userdata Sync Store Management Service
417
const userDataSyncStoreManagementService = new UserDataSyncStoreManagementService(productService, configurationService, storageService);
418
serviceCollection.set(IUserDataSyncStoreManagementService, userDataSyncStoreManagementService);
419
420
421
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
422
//
423
// NOTE: Please do NOT register services here. Use `registerSingleton()`
424
// from `workbench.common.main.ts` if the service is shared between
425
// desktop and web or `workbench.web.main.ts` if the service
426
// is web only.
427
//
428
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
429
430
const encryptionService = new EncryptionService();
431
serviceCollection.set(IEncryptionService, encryptionService);
432
const secretStorageService = new BrowserSecretStorageService(storageService, encryptionService, environmentService, logService);
433
serviceCollection.set(ISecretStorageService, secretStorageService);
434
435
// Userdata Initialize Service
436
const userDataInitializers: IUserDataInitializer[] = [];
437
userDataInitializers.push(new UserDataSyncInitializer(environmentService, secretStorageService, userDataSyncStoreManagementService, fileService, userDataProfilesService, storageService, productService, requestService, logService, uriIdentityService));
438
if (environmentService.options.profile) {
439
userDataInitializers.push(new UserDataProfileInitializer(environmentService, fileService, userDataProfileService, storageService, logService, uriIdentityService, requestService));
440
}
441
const userDataInitializationService = new UserDataInitializationService(userDataInitializers);
442
serviceCollection.set(IUserDataInitializationService, userDataInitializationService);
443
444
try {
445
await Promise.race([
446
// Do not block more than 5s
447
timeout(5000),
448
this.initializeUserData(userDataInitializationService, configurationService)]
449
);
450
} catch (error) {
451
logService.error(error);
452
}
453
454
return { serviceCollection, configurationService, logService };
455
}
456
457
private async initializeUserData(userDataInitializationService: UserDataInitializationService, configurationService: WorkspaceService) {
458
if (await userDataInitializationService.requiresInitialization()) {
459
mark('code/willInitRequiredUserData');
460
461
// Initialize required resources - settings & global state
462
await userDataInitializationService.initializeRequiredResources();
463
464
// Important: Reload only local user configuration after initializing
465
// Reloading complete configuration blocks workbench until remote configuration is loaded.
466
await configurationService.reloadLocalUserConfiguration();
467
468
mark('code/didInitRequiredUserData');
469
}
470
}
471
472
private async registerIndexedDBFileSystemProviders(environmentService: IWorkbenchEnvironmentService, fileService: IFileService, logService: ILogService, loggerService: ILoggerService, logsPath: URI): Promise<void> {
473
474
// IndexedDB is used for logging and user data
475
let indexedDB: IndexedDB | undefined;
476
const userDataStore = 'vscode-userdata-store';
477
const logsStore = 'vscode-logs-store';
478
const handlesStore = 'vscode-filehandles-store';
479
try {
480
indexedDB = await IndexedDB.create('vscode-web-db', 3, [userDataStore, logsStore, handlesStore]);
481
482
// Close onWillShutdown
483
this.onWillShutdownDisposables.add(toDisposable(() => indexedDB?.close()));
484
} catch (error) {
485
logService.error('Error while creating IndexedDB', error);
486
}
487
488
// Logger
489
if (indexedDB) {
490
const logFileSystemProvider = new IndexedDBFileSystemProvider(logsPath.scheme, indexedDB, logsStore, false);
491
this.indexedDBFileSystemProviders.push(logFileSystemProvider);
492
fileService.registerProvider(logsPath.scheme, logFileSystemProvider);
493
} else {
494
fileService.registerProvider(logsPath.scheme, new InMemoryFileSystemProvider());
495
}
496
497
// User data
498
let userDataProvider;
499
if (indexedDB) {
500
userDataProvider = new IndexedDBFileSystemProvider(Schemas.vscodeUserData, indexedDB, userDataStore, true);
501
this.indexedDBFileSystemProviders.push(userDataProvider);
502
this.registerDeveloperActions(userDataProvider);
503
} else {
504
logService.info('Using in-memory user data provider');
505
userDataProvider = new InMemoryFileSystemProvider();
506
}
507
fileService.registerProvider(Schemas.vscodeUserData, userDataProvider);
508
509
// Local file access (if supported by browser)
510
if (WebFileSystemAccess.supported(mainWindow)) {
511
fileService.registerProvider(Schemas.file, new HTMLFileSystemProvider(indexedDB, handlesStore, logService));
512
}
513
514
// In-memory
515
fileService.registerProvider(Schemas.tmp, new InMemoryFileSystemProvider());
516
}
517
518
private registerDeveloperActions(provider: IndexedDBFileSystemProvider): void {
519
this._register(registerAction2(class ResetUserDataAction extends Action2 {
520
constructor() {
521
super({
522
id: 'workbench.action.resetUserData',
523
title: localize2('reset', "Reset User Data"),
524
category: Categories.Developer,
525
menu: {
526
id: MenuId.CommandPalette
527
}
528
});
529
}
530
531
async run(accessor: ServicesAccessor): Promise<void> {
532
const dialogService = accessor.get(IDialogService);
533
const hostService = accessor.get(IHostService);
534
const storageService = accessor.get(IStorageService);
535
const logService = accessor.get(ILogService);
536
const result = await dialogService.confirm({
537
message: localize('reset user data message', "Would you like to reset your data (settings, keybindings, extensions, snippets and UI State) and reload?")
538
});
539
540
if (result.confirmed) {
541
try {
542
await provider?.reset();
543
if (storageService instanceof BrowserStorageService) {
544
await storageService.clear();
545
}
546
} catch (error) {
547
logService.error(error);
548
throw error;
549
}
550
}
551
552
hostService.reload();
553
}
554
}));
555
}
556
557
private async createStorageService(workspace: IAnyWorkspaceIdentifier, logService: ILogService, userDataProfileService: IUserDataProfileService): Promise<IStorageService> {
558
const storageService = new BrowserStorageService(workspace, userDataProfileService, logService);
559
560
try {
561
await storageService.initialize();
562
563
// Register to close on shutdown
564
this.onWillShutdownDisposables.add(toDisposable(() => storageService.close()));
565
566
return storageService;
567
} catch (error) {
568
onUnexpectedError(error);
569
logService.error(error);
570
571
return storageService;
572
}
573
}
574
575
private async createWorkspaceService(workspace: IAnyWorkspaceIdentifier, environmentService: IBrowserWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService, userDataProfilesService: IUserDataProfilesService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, policyService: IPolicyService, logService: ILogService): Promise<WorkspaceService> {
576
577
// Temporary workspaces do not exist on startup because they are
578
// just in memory. As such, detect this case and eagerly create
579
// the workspace file empty so that it is a valid workspace.
580
581
if (isWorkspaceIdentifier(workspace) && isTemporaryWorkspace(workspace.configPath)) {
582
try {
583
const emptyWorkspace: IStoredWorkspace = { folders: [] };
584
await fileService.createFile(workspace.configPath, VSBuffer.fromString(JSON.stringify(emptyWorkspace, null, '\t')), { overwrite: false });
585
} catch (error) {
586
// ignore if workspace file already exists
587
}
588
}
589
590
const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData, Schemas.tmp] /* Cache all non native resources */, environmentService, fileService);
591
const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, policyService);
592
593
try {
594
await workspaceService.initialize(workspace);
595
596
return workspaceService;
597
} catch (error) {
598
onUnexpectedError(error);
599
logService.error(error);
600
601
return workspaceService;
602
}
603
}
604
605
private async getCurrentProfile(workspace: IAnyWorkspaceIdentifier, userDataProfilesService: BrowserUserDataProfilesService, environmentService: BrowserWorkbenchEnvironmentService): Promise<IUserDataProfile> {
606
const profileName = environmentService.options?.profile?.name ?? environmentService.profile;
607
if (profileName) {
608
const profile = userDataProfilesService.profiles.find(p => p.name === profileName);
609
if (profile) {
610
return profile;
611
}
612
return userDataProfilesService.createNamedProfile(profileName, undefined, workspace);
613
}
614
return userDataProfilesService.getProfileForWorkspace(workspace) ?? userDataProfilesService.defaultProfile;
615
}
616
617
private resolveWorkspace(): IAnyWorkspaceIdentifier {
618
let workspace: IWorkspace | undefined = undefined;
619
if (this.configuration.workspaceProvider) {
620
workspace = this.configuration.workspaceProvider.workspace;
621
}
622
623
// Multi-root workspace
624
if (workspace && isWorkspaceToOpen(workspace)) {
625
return getWorkspaceIdentifier(workspace.workspaceUri);
626
}
627
628
// Single-folder workspace
629
if (workspace && isFolderToOpen(workspace)) {
630
return getSingleFolderWorkspaceIdentifier(workspace.folderUri);
631
}
632
633
// Empty window workspace
634
return UNKNOWN_EMPTY_WINDOW_WORKSPACE;
635
}
636
}
637
638