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