Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/code/electron-main/app.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 { app, powerMonitor, protocol, session, Session, systemPreferences, WebFrameMain } from 'electron';
7
import { addUNCHostToAllowlist, disableUNCAccessRestrictions } from '../../base/node/unc.js';
8
import { validatedIpcMain } from '../../base/parts/ipc/electron-main/ipcMain.js';
9
import { hostname, release } from 'os';
10
import { VSBuffer } from '../../base/common/buffer.js';
11
import { toErrorMessage } from '../../base/common/errorMessage.js';
12
import { Event } from '../../base/common/event.js';
13
import { parse } from '../../base/common/jsonc.js';
14
import { getPathLabel } from '../../base/common/labels.js';
15
import { Disposable, DisposableStore } from '../../base/common/lifecycle.js';
16
import { Schemas, VSCODE_AUTHORITY } from '../../base/common/network.js';
17
import { join, posix } from '../../base/common/path.js';
18
import { IProcessEnvironment, isLinux, isLinuxSnap, isMacintosh, isWindows, OS } from '../../base/common/platform.js';
19
import { assertType } from '../../base/common/types.js';
20
import { URI } from '../../base/common/uri.js';
21
import { generateUuid } from '../../base/common/uuid.js';
22
import { registerContextMenuListener } from '../../base/parts/contextmenu/electron-main/contextmenu.js';
23
import { getDelayedChannel, ProxyChannel, StaticRouter } from '../../base/parts/ipc/common/ipc.js';
24
import { Server as ElectronIPCServer } from '../../base/parts/ipc/electron-main/ipc.electron.js';
25
import { Client as MessagePortClient } from '../../base/parts/ipc/electron-main/ipc.mp.js';
26
import { Server as NodeIPCServer } from '../../base/parts/ipc/node/ipc.net.js';
27
import { IProxyAuthService, ProxyAuthService } from '../../platform/native/electron-main/auth.js';
28
import { localize } from '../../nls.js';
29
import { IBackupMainService } from '../../platform/backup/electron-main/backup.js';
30
import { BackupMainService } from '../../platform/backup/electron-main/backupMainService.js';
31
import { IConfigurationService } from '../../platform/configuration/common/configuration.js';
32
import { ElectronExtensionHostDebugBroadcastChannel } from '../../platform/debug/electron-main/extensionHostDebugIpc.js';
33
import { IDiagnosticsService } from '../../platform/diagnostics/common/diagnostics.js';
34
import { DiagnosticsMainService, IDiagnosticsMainService } from '../../platform/diagnostics/electron-main/diagnosticsMainService.js';
35
import { DialogMainService, IDialogMainService } from '../../platform/dialogs/electron-main/dialogMainService.js';
36
import { IEncryptionMainService } from '../../platform/encryption/common/encryptionService.js';
37
import { EncryptionMainService } from '../../platform/encryption/electron-main/encryptionMainService.js';
38
import { NativeBrowserElementsMainService, INativeBrowserElementsMainService } from '../../platform/browserElements/electron-main/nativeBrowserElementsMainService.js';
39
import { ipcBrowserViewChannelName } from '../../platform/browserView/common/browserView.js';
40
import { BrowserViewMainService, IBrowserViewMainService } from '../../platform/browserView/electron-main/browserViewMainService.js';
41
import { NativeParsedArgs } from '../../platform/environment/common/argv.js';
42
import { IEnvironmentMainService } from '../../platform/environment/electron-main/environmentMainService.js';
43
import { isLaunchedFromCli } from '../../platform/environment/node/argvHelper.js';
44
import { getResolvedShellEnv } from '../../platform/shell/node/shellEnv.js';
45
import { IExtensionHostStarter, ipcExtensionHostStarterChannelName } from '../../platform/extensions/common/extensionHostStarter.js';
46
import { ExtensionHostStarter } from '../../platform/extensions/electron-main/extensionHostStarter.js';
47
import { IExternalTerminalMainService } from '../../platform/externalTerminal/electron-main/externalTerminal.js';
48
import { LinuxExternalTerminalService, MacExternalTerminalService, WindowsExternalTerminalService } from '../../platform/externalTerminal/node/externalTerminalService.js';
49
import { LOCAL_FILE_SYSTEM_CHANNEL_NAME } from '../../platform/files/common/diskFileSystemProviderClient.js';
50
import { IFileService } from '../../platform/files/common/files.js';
51
import { DiskFileSystemProviderChannel } from '../../platform/files/electron-main/diskFileSystemProviderServer.js';
52
import { DiskFileSystemProvider } from '../../platform/files/node/diskFileSystemProvider.js';
53
import { SyncDescriptor } from '../../platform/instantiation/common/descriptors.js';
54
import { IInstantiationService, ServicesAccessor } from '../../platform/instantiation/common/instantiation.js';
55
import { ServiceCollection } from '../../platform/instantiation/common/serviceCollection.js';
56
import { ProcessMainService } from '../../platform/process/electron-main/processMainService.js';
57
import { IKeyboardLayoutMainService, KeyboardLayoutMainService } from '../../platform/keyboardLayout/electron-main/keyboardLayoutMainService.js';
58
import { ILaunchMainService, LaunchMainService } from '../../platform/launch/electron-main/launchMainService.js';
59
import { ILifecycleMainService, LifecycleMainPhase, ShutdownReason } from '../../platform/lifecycle/electron-main/lifecycleMainService.js';
60
import { ILoggerService, ILogService } from '../../platform/log/common/log.js';
61
import { IMenubarMainService, MenubarMainService } from '../../platform/menubar/electron-main/menubarMainService.js';
62
import { INativeHostMainService, NativeHostMainService } from '../../platform/native/electron-main/nativeHostMainService.js';
63
import { IMeteredConnectionService } from '../../platform/meteredConnection/common/meteredConnection.js';
64
import { METERED_CONNECTION_CHANNEL } from '../../platform/meteredConnection/common/meteredConnectionIpc.js';
65
import { MeteredConnectionChannel } from '../../platform/meteredConnection/electron-main/meteredConnectionChannel.js';
66
import { MeteredConnectionMainService } from '../../platform/meteredConnection/electron-main/meteredConnectionMainService.js';
67
import { IProductService } from '../../platform/product/common/productService.js';
68
import { getRemoteAuthority } from '../../platform/remote/common/remoteHosts.js';
69
import { SharedProcess } from '../../platform/sharedProcess/electron-main/sharedProcess.js';
70
import { ISignService } from '../../platform/sign/common/sign.js';
71
import { IStateService } from '../../platform/state/node/state.js';
72
import { StorageDatabaseChannel } from '../../platform/storage/electron-main/storageIpc.js';
73
import { ApplicationStorageMainService, IApplicationStorageMainService, IStorageMainService, StorageMainService } from '../../platform/storage/electron-main/storageMainService.js';
74
import { resolveCommonProperties } from '../../platform/telemetry/common/commonProperties.js';
75
import { ITelemetryService, TelemetryLevel } from '../../platform/telemetry/common/telemetry.js';
76
import { TelemetryAppenderClient } from '../../platform/telemetry/common/telemetryIpc.js';
77
import { ITelemetryServiceConfig, TelemetryService } from '../../platform/telemetry/common/telemetryService.js';
78
import { getPiiPathsFromEnvironment, getTelemetryLevel, isInternalTelemetry, NullTelemetryService, supportsTelemetry } from '../../platform/telemetry/common/telemetryUtils.js';
79
import { IUpdateService } from '../../platform/update/common/update.js';
80
import { UpdateChannel } from '../../platform/update/common/updateIpc.js';
81
import { DarwinUpdateService } from '../../platform/update/electron-main/updateService.darwin.js';
82
import { LinuxUpdateService } from '../../platform/update/electron-main/updateService.linux.js';
83
import { SnapUpdateService } from '../../platform/update/electron-main/updateService.snap.js';
84
import { Win32UpdateService } from '../../platform/update/electron-main/updateService.win32.js';
85
import { IOpenURLOptions, IURLService } from '../../platform/url/common/url.js';
86
import { URLHandlerChannelClient, URLHandlerRouter } from '../../platform/url/common/urlIpc.js';
87
import { NativeURLService } from '../../platform/url/common/urlService.js';
88
import { ElectronURLListener } from '../../platform/url/electron-main/electronUrlListener.js';
89
import { IWebviewManagerService } from '../../platform/webview/common/webviewManagerService.js';
90
import { WebviewMainService } from '../../platform/webview/electron-main/webviewMainService.js';
91
import { isFolderToOpen, isWorkspaceToOpen, IWindowOpenable } from '../../platform/window/common/window.js';
92
import { getAllWindowsExcludingOffscreen, IWindowsMainService, OpenContext } from '../../platform/windows/electron-main/windows.js';
93
import { ICodeWindow } from '../../platform/window/electron-main/window.js';
94
import { WindowsMainService } from '../../platform/windows/electron-main/windowsMainService.js';
95
import { ActiveWindowManager } from '../../platform/windows/node/windowTracker.js';
96
import { hasWorkspaceFileExtension } from '../../platform/workspace/common/workspace.js';
97
import { IWorkspacesService } from '../../platform/workspaces/common/workspaces.js';
98
import { IWorkspacesHistoryMainService, WorkspacesHistoryMainService } from '../../platform/workspaces/electron-main/workspacesHistoryMainService.js';
99
import { WorkspacesMainService } from '../../platform/workspaces/electron-main/workspacesMainService.js';
100
import { IWorkspacesManagementMainService, WorkspacesManagementMainService } from '../../platform/workspaces/electron-main/workspacesManagementMainService.js';
101
import { IPolicyService } from '../../platform/policy/common/policy.js';
102
import { PolicyChannel } from '../../platform/policy/common/policyIpc.js';
103
import { IUserDataProfilesMainService } from '../../platform/userDataProfile/electron-main/userDataProfile.js';
104
import { IExtensionsProfileScannerService } from '../../platform/extensionManagement/common/extensionsProfileScannerService.js';
105
import { IExtensionsScannerService } from '../../platform/extensionManagement/common/extensionsScannerService.js';
106
import { ExtensionsScannerService } from '../../platform/extensionManagement/node/extensionsScannerService.js';
107
import { UserDataProfilesHandler } from '../../platform/userDataProfile/electron-main/userDataProfilesHandler.js';
108
import { ProfileStorageChangesListenerChannel } from '../../platform/userDataProfile/electron-main/userDataProfileStorageIpc.js';
109
import { Promises, RunOnceScheduler, runWhenGlobalIdle } from '../../base/common/async.js';
110
import { resolveMachineId, resolveSqmId, resolveDevDeviceId, validateDevDeviceId } from '../../platform/telemetry/electron-main/telemetryUtils.js';
111
import { ExtensionsProfileScannerService } from '../../platform/extensionManagement/node/extensionsProfileScannerService.js';
112
import { LoggerChannel } from '../../platform/log/electron-main/logIpc.js';
113
import { ILoggerMainService } from '../../platform/log/electron-main/loggerService.js';
114
import { IInitialProtocolUrls, IProtocolUrl } from '../../platform/url/electron-main/url.js';
115
import { IUtilityProcessWorkerMainService, UtilityProcessWorkerMainService } from '../../platform/utilityProcess/electron-main/utilityProcessWorkerMainService.js';
116
import { ipcUtilityProcessWorkerChannelName } from '../../platform/utilityProcess/common/utilityProcessWorkerService.js';
117
import { ILocalPtyService, LocalReconnectConstants, TerminalIpcChannels, TerminalSettingId } from '../../platform/terminal/common/terminal.js';
118
import { ElectronPtyHostStarter } from '../../platform/terminal/electron-main/electronPtyHostStarter.js';
119
import { PtyHostService } from '../../platform/terminal/node/ptyHostService.js';
120
import { NODE_REMOTE_RESOURCE_CHANNEL_NAME, NODE_REMOTE_RESOURCE_IPC_METHOD_NAME, NodeRemoteResourceResponse, NodeRemoteResourceRouter } from '../../platform/remote/common/electronRemoteResources.js';
121
import { Lazy } from '../../base/common/lazy.js';
122
import { IAuxiliaryWindowsMainService } from '../../platform/auxiliaryWindow/electron-main/auxiliaryWindows.js';
123
import { AuxiliaryWindowsMainService } from '../../platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.js';
124
import { normalizeNFC } from '../../base/common/normalization.js';
125
import { ICSSDevelopmentService, CSSDevelopmentService } from '../../platform/cssDev/node/cssDevService.js';
126
import { INativeMcpDiscoveryHelperService, NativeMcpDiscoveryHelperChannelName } from '../../platform/mcp/common/nativeMcpDiscoveryHelper.js';
127
import { NativeMcpDiscoveryHelperService } from '../../platform/mcp/node/nativeMcpDiscoveryHelperService.js';
128
import { IMcpGatewayService, McpGatewayChannelName } from '../../platform/mcp/common/mcpGateway.js';
129
import { McpGatewayService } from '../../platform/mcp/node/mcpGatewayService.js';
130
import { McpGatewayChannel } from '../../platform/mcp/node/mcpGatewayChannel.js';
131
import { IWebContentExtractorService } from '../../platform/webContentExtractor/common/webContentExtractor.js';
132
import { NativeWebContentExtractorService } from '../../platform/webContentExtractor/electron-main/webContentExtractorService.js';
133
import ErrorTelemetry from '../../platform/telemetry/electron-main/errorTelemetry.js';
134
135
/**
136
* The main VS Code application. There will only ever be one instance,
137
* even if the user starts many instances (e.g. from the command line).
138
*/
139
export class CodeApplication extends Disposable {
140
141
private static readonly SECURITY_PROTOCOL_HANDLING_CONFIRMATION_SETTING_KEY = {
142
[Schemas.file]: 'security.promptForLocalFileProtocolHandling' as const,
143
[Schemas.vscodeRemote]: 'security.promptForRemoteFileProtocolHandling' as const
144
};
145
146
private windowsMainService: IWindowsMainService | undefined;
147
private auxiliaryWindowsMainService: IAuxiliaryWindowsMainService | undefined;
148
private nativeHostMainService: INativeHostMainService | undefined;
149
150
constructor(
151
private readonly mainProcessNodeIpcServer: NodeIPCServer,
152
private readonly userEnv: IProcessEnvironment,
153
@IInstantiationService private readonly mainInstantiationService: IInstantiationService,
154
@ILogService private readonly logService: ILogService,
155
@ILoggerService private readonly loggerService: ILoggerService,
156
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
157
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
158
@IConfigurationService private readonly configurationService: IConfigurationService,
159
@IStateService private readonly stateService: IStateService,
160
@IFileService private readonly fileService: IFileService,
161
@IProductService private readonly productService: IProductService,
162
@IUserDataProfilesMainService private readonly userDataProfilesMainService: IUserDataProfilesMainService
163
) {
164
super();
165
166
this.configureSession();
167
this.registerListeners();
168
}
169
170
private configureSession(): void {
171
172
//#region Security related measures (https://electronjs.org/docs/tutorial/security)
173
//
174
// !!! DO NOT CHANGE without consulting the documentation !!!
175
//
176
177
const isUrlFromWindow = (requestingUrl?: string | undefined) => requestingUrl?.startsWith(`${Schemas.vscodeFileResource}://${VSCODE_AUTHORITY}`);
178
const isUrlFromWebview = (requestingUrl: string | undefined) => requestingUrl?.startsWith(`${Schemas.vscodeWebview}://`);
179
180
const alwaysAllowedPermissions = new Set(['pointerLock', 'notifications']);
181
182
const allowedPermissionsInWebview = new Set([
183
...alwaysAllowedPermissions,
184
'clipboard-read',
185
'clipboard-sanitized-write',
186
// TODO(deepak1556): Should be removed once migration is complete
187
// https://github.com/microsoft/vscode/issues/239228
188
'deprecated-sync-clipboard-read',
189
]);
190
191
const allowedPermissionsInCore = new Set([
192
...alwaysAllowedPermissions,
193
'media',
194
'local-fonts',
195
// TODO(deepak1556): Should be removed once migration is complete
196
// https://github.com/microsoft/vscode/issues/239228
197
'deprecated-sync-clipboard-read',
198
]);
199
200
session.defaultSession.setPermissionRequestHandler((_webContents, permission, callback, details) => {
201
if (isUrlFromWebview(details.requestingUrl)) {
202
return callback(allowedPermissionsInWebview.has(permission));
203
}
204
if (isUrlFromWindow(details.requestingUrl)) {
205
return callback(allowedPermissionsInCore.has(permission));
206
}
207
return callback(false);
208
});
209
210
session.defaultSession.setPermissionCheckHandler((_webContents, permission, _origin, details) => {
211
if (isUrlFromWebview(details.requestingUrl)) {
212
return allowedPermissionsInWebview.has(permission);
213
}
214
if (isUrlFromWindow(details.requestingUrl)) {
215
return allowedPermissionsInCore.has(permission);
216
}
217
return false;
218
});
219
220
//#endregion
221
222
//#region Request filtering
223
224
// Block all SVG requests from unsupported origins
225
const supportedSvgSchemes = new Set([Schemas.file, Schemas.vscodeFileResource, Schemas.vscodeRemoteResource, Schemas.vscodeManagedRemoteResource, 'devtools']);
226
227
// But allow them if they are made from inside an webview
228
const isSafeFrame = (requestFrame: WebFrameMain | null | undefined): boolean => {
229
for (let frame: WebFrameMain | null | undefined = requestFrame; frame; frame = frame.parent) {
230
if (frame.url.startsWith(`${Schemas.vscodeWebview}://`)) {
231
return true;
232
}
233
}
234
return false;
235
};
236
237
const isSvgRequestFromSafeContext = (details: Electron.OnBeforeRequestListenerDetails | Electron.OnHeadersReceivedListenerDetails): boolean => {
238
return details.resourceType === 'xhr' || isSafeFrame(details.frame);
239
};
240
241
const isAllowedVsCodeFileRequest = (details: Electron.OnBeforeRequestListenerDetails) => {
242
const frame = details.frame;
243
if (!frame || !this.windowsMainService) {
244
return false;
245
}
246
247
// Check to see if the request comes from one of the main windows (or shared process) and not from embedded content
248
const windows = getAllWindowsExcludingOffscreen();
249
for (const window of windows) {
250
if (frame.processId === window.webContents.mainFrame.processId) {
251
return true;
252
}
253
}
254
255
return false;
256
};
257
258
const isAllowedWebviewRequest = (uri: URI, details: Electron.OnBeforeRequestListenerDetails): boolean => {
259
if (uri.path !== '/index.html') {
260
return true; // Only restrict top level page of webviews: index.html
261
}
262
263
const frame = details.frame;
264
if (!frame || !this.windowsMainService) {
265
return false;
266
}
267
268
// Check to see if the request comes from one of the main editor windows.
269
for (const window of this.windowsMainService.getWindows()) {
270
if (window.win) {
271
if (frame.processId === window.win.webContents.mainFrame.processId) {
272
return true;
273
}
274
}
275
}
276
277
return false;
278
};
279
280
session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
281
const uri = URI.parse(details.url);
282
if (uri.scheme === Schemas.vscodeWebview) {
283
if (!isAllowedWebviewRequest(uri, details)) {
284
this.logService.error('Blocked vscode-webview request', details.url);
285
return callback({ cancel: true });
286
}
287
}
288
289
if (uri.scheme === Schemas.vscodeFileResource) {
290
if (!isAllowedVsCodeFileRequest(details)) {
291
this.logService.error('Blocked vscode-file request', details.url);
292
return callback({ cancel: true });
293
}
294
}
295
296
// Block most svgs
297
if (uri.path.endsWith('.svg')) {
298
const isSafeResourceUrl = supportedSvgSchemes.has(uri.scheme);
299
if (!isSafeResourceUrl) {
300
return callback({ cancel: !isSvgRequestFromSafeContext(details) });
301
}
302
}
303
304
return callback({ cancel: false });
305
});
306
307
// Configure SVG header content type properly
308
// https://github.com/microsoft/vscode/issues/97564
309
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
310
const responseHeaders = details.responseHeaders as Record<string, (string) | (string[])>;
311
const contentTypes = (responseHeaders['content-type'] || responseHeaders['Content-Type']);
312
313
if (contentTypes && Array.isArray(contentTypes)) {
314
const uri = URI.parse(details.url);
315
if (uri.path.endsWith('.svg')) {
316
if (supportedSvgSchemes.has(uri.scheme)) {
317
responseHeaders['Content-Type'] = ['image/svg+xml'];
318
319
return callback({ cancel: false, responseHeaders });
320
}
321
}
322
323
// remote extension schemes have the following format
324
// http://127.0.0.1:<port>/vscode-remote-resource?path=
325
if (!uri.path.endsWith(Schemas.vscodeRemoteResource) && contentTypes.some(contentType => contentType.toLowerCase().includes('image/svg'))) {
326
return callback({ cancel: !isSvgRequestFromSafeContext(details) });
327
}
328
}
329
330
return callback({ cancel: false });
331
});
332
333
//#endregion
334
335
//#region Allow CORS for the PRSS CDN
336
337
// https://github.com/microsoft/vscode-remote-release/issues/9246
338
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
339
if (details.url.startsWith('https://vscode.download.prss.microsoft.com/')) {
340
const responseHeaders = details.responseHeaders ?? Object.create(null);
341
342
if (responseHeaders['Access-Control-Allow-Origin'] === undefined) {
343
responseHeaders['Access-Control-Allow-Origin'] = ['*'];
344
return callback({ cancel: false, responseHeaders });
345
}
346
}
347
348
return callback({ cancel: false });
349
});
350
351
//#endregion
352
353
//#region Code Cache
354
355
type SessionWithCodeCachePathSupport = Session & {
356
/**
357
* Sets code cache directory. By default, the directory will be `Code Cache` under
358
* the respective user data folder.
359
*/
360
setCodeCachePath?(path: string): void;
361
};
362
363
const defaultSession = session.defaultSession as unknown as SessionWithCodeCachePathSupport;
364
if (typeof defaultSession.setCodeCachePath === 'function' && this.environmentMainService.codeCachePath) {
365
// Make sure to partition Chrome's code cache folder
366
// in the same way as our code cache path to help
367
// invalidate caches that we know are invalid
368
// (https://github.com/microsoft/vscode/issues/120655)
369
defaultSession.setCodeCachePath(join(this.environmentMainService.codeCachePath, 'chrome'));
370
}
371
372
//#endregion
373
374
//#region UNC Host Allowlist (Windows)
375
376
if (isWindows) {
377
if (this.configurationService.getValue('security.restrictUNCAccess') === false) {
378
disableUNCAccessRestrictions();
379
} else {
380
addUNCHostToAllowlist(this.configurationService.getValue('security.allowedUNCHosts'));
381
}
382
}
383
384
//#endregion
385
}
386
387
private registerListeners(): void {
388
389
// Dispose on shutdown
390
Event.once(this.lifecycleMainService.onWillShutdown)(() => this.dispose());
391
392
// Contextmenu via IPC support
393
registerContextMenuListener();
394
395
// Accessibility change event
396
app.on('accessibility-support-changed', (event, accessibilitySupportEnabled) => {
397
this.windowsMainService?.sendToAll('vscode:accessibilitySupportChanged', accessibilitySupportEnabled);
398
});
399
400
// macOS dock activate
401
app.on('activate', async (event, hasVisibleWindows) => {
402
this.logService.trace('app#activate');
403
404
// Mac only event: open new window when we get activated
405
if (!hasVisibleWindows) {
406
await this.windowsMainService?.openEmptyWindow({ context: OpenContext.DOCK });
407
}
408
});
409
410
//#region Security related measures (https://electronjs.org/docs/tutorial/security)
411
//
412
// !!! DO NOT CHANGE without consulting the documentation !!!
413
//
414
app.on('web-contents-created', (event, contents) => {
415
416
// Auxiliary Window: delegate to `AuxiliaryWindow` class
417
if (contents?.opener?.url.startsWith(`${Schemas.vscodeFileResource}://${VSCODE_AUTHORITY}/`)) {
418
this.logService.trace('[aux window] app.on("web-contents-created"): Registering auxiliary window');
419
420
this.auxiliaryWindowsMainService?.registerWindow(contents);
421
}
422
423
// Handle any in-page navigation
424
contents.on('will-navigate', event => {
425
if (BrowserViewMainService.isBrowserViewWebContents(contents)) {
426
return; // Allow navigation in integrated browser views
427
}
428
429
this.logService.error('webContents#will-navigate: Prevented webcontent navigation');
430
431
event.preventDefault(); // Prevent any in-page navigation
432
});
433
434
// All Windows: only allow about:blank auxiliary windows to open
435
// For all other URLs, delegate to the OS.
436
contents.setWindowOpenHandler(details => {
437
438
// about:blank windows can open as window witho our default options
439
if (details.url === 'about:blank') {
440
this.logService.trace('[aux window] webContents#setWindowOpenHandler: Allowing auxiliary window to open on about:blank');
441
442
return {
443
action: 'allow',
444
overrideBrowserWindowOptions: this.auxiliaryWindowsMainService?.createWindow(details)
445
};
446
}
447
448
// Any other URL: delegate to OS
449
else {
450
this.logService.trace(`webContents#setWindowOpenHandler: Prevented opening window with URL ${details.url}}`);
451
452
this.nativeHostMainService?.openExternal(undefined, details.url);
453
454
return { action: 'deny' };
455
}
456
});
457
});
458
459
//#endregion
460
461
let macOpenFileURIs: IWindowOpenable[] = [];
462
let runningTimeout: Timeout | undefined = undefined;
463
app.on('open-file', (event, path) => {
464
path = normalizeNFC(path); // macOS only: normalize paths to NFC form
465
466
this.logService.trace('app#open-file: ', path);
467
event.preventDefault();
468
469
// Keep in array because more might come!
470
macOpenFileURIs.push(hasWorkspaceFileExtension(path) ? { workspaceUri: URI.file(path) } : { fileUri: URI.file(path) });
471
472
// Clear previous handler if any
473
if (runningTimeout !== undefined) {
474
clearTimeout(runningTimeout);
475
runningTimeout = undefined;
476
}
477
478
// Handle paths delayed in case more are coming!
479
runningTimeout = setTimeout(async () => {
480
await this.windowsMainService?.open({
481
context: OpenContext.DOCK /* can also be opening from finder while app is running */,
482
cli: this.environmentMainService.args,
483
urisToOpen: macOpenFileURIs,
484
gotoLineMode: false,
485
preferNewWindow: true /* dropping on the dock or opening from finder prefers to open in a new window */
486
});
487
488
macOpenFileURIs = [];
489
runningTimeout = undefined;
490
}, 100);
491
});
492
493
app.on('new-window-for-tab', async () => {
494
await this.windowsMainService?.openEmptyWindow({ context: OpenContext.DESKTOP }); //macOS native tab "+" button
495
});
496
497
//#region Bootstrap IPC Handlers
498
499
validatedIpcMain.handle('vscode:fetchShellEnv', event => {
500
501
// Prefer to use the args and env from the target window
502
// when resolving the shell env. It is possible that
503
// a first window was opened from the UI but a second
504
// from the CLI and that has implications for whether to
505
// resolve the shell environment or not.
506
//
507
// Window can be undefined for e.g. the shared process
508
// that is not part of our windows registry!
509
const window = this.windowsMainService?.getWindowByWebContents(event.sender); // Note: this can be `undefined` for the shared process
510
let args: NativeParsedArgs;
511
let env: IProcessEnvironment;
512
if (window?.config) {
513
args = window.config;
514
env = { ...process.env, ...window.config.userEnv };
515
} else {
516
args = this.environmentMainService.args;
517
env = process.env;
518
}
519
520
// Resolve shell env
521
return this.resolveShellEnvironment(args, env, false);
522
});
523
524
validatedIpcMain.on('vscode:toggleDevTools', event => event.sender.toggleDevTools());
525
validatedIpcMain.on('vscode:openDevTools', event => event.sender.openDevTools());
526
527
validatedIpcMain.on('vscode:reloadWindow', event => event.sender.reload());
528
529
validatedIpcMain.handle('vscode:notifyZoomLevel', async (event, zoomLevel: number | undefined) => {
530
const window = this.windowsMainService?.getWindowByWebContents(event.sender);
531
if (window) {
532
window.notifyZoomLevel(zoomLevel);
533
}
534
});
535
536
//#endregion
537
}
538
539
async startup(): Promise<void> {
540
this.logService.debug('Starting VS Code');
541
this.logService.debug(`from: ${this.environmentMainService.appRoot}`);
542
this.logService.debug('args:', this.environmentMainService.args);
543
544
// Make sure we associate the program with the app user model id
545
// This will help Windows to associate the running program with
546
// any shortcut that is pinned to the taskbar and prevent showing
547
// two icons in the taskbar for the same app.
548
const win32AppUserModelId = this.productService.win32AppUserModelId;
549
if (isWindows && win32AppUserModelId) {
550
app.setAppUserModelId(win32AppUserModelId);
551
}
552
553
// Fix native tabs on macOS 10.13
554
// macOS enables a compatibility patch for any bundle ID beginning with
555
// "com.microsoft.", which breaks native tabs for VS Code when using this
556
// identifier (from the official build).
557
// Explicitly opt out of the patch here before creating any windows.
558
// See: https://github.com/microsoft/vscode/issues/35361#issuecomment-399794085
559
try {
560
if (isMacintosh && this.configurationService.getValue('window.nativeTabs') === true && !systemPreferences.getUserDefault('NSUseImprovedLayoutPass', 'boolean')) {
561
systemPreferences.setUserDefault('NSUseImprovedLayoutPass', 'boolean', true);
562
}
563
} catch (error) {
564
this.logService.error(error);
565
}
566
567
// Main process server (electron IPC based)
568
const mainProcessElectronServer = new ElectronIPCServer();
569
Event.once(this.lifecycleMainService.onWillShutdown)(e => {
570
if (e.reason === ShutdownReason.KILL) {
571
// When we go down abnormally, make sure to free up
572
// any IPC we accept from other windows to reduce
573
// the chance of doing work after we go down. Kill
574
// is special in that it does not orderly shutdown
575
// windows.
576
mainProcessElectronServer.dispose();
577
}
578
});
579
580
// Resolve unique machine ID
581
const [machineId, sqmId, devDeviceId] = await Promise.all([
582
resolveMachineId(this.stateService, this.logService),
583
resolveSqmId(this.stateService, this.logService),
584
resolveDevDeviceId(this.stateService, this.logService)
585
]);
586
587
// Shared process
588
const { sharedProcessReady, sharedProcessClient } = this.setupSharedProcess(machineId, sqmId, devDeviceId);
589
590
// Services
591
const appInstantiationService = await this.initServices(machineId, sqmId, devDeviceId, sharedProcessReady);
592
593
// Error telemetry
594
appInstantiationService.invokeFunction(accessor => this._register(new ErrorTelemetry(accessor.get(ILogService), accessor.get(ITelemetryService))));
595
596
// Metered connection telemetry
597
appInstantiationService.invokeFunction(accessor => {
598
(accessor.get(IMeteredConnectionService) as MeteredConnectionMainService).setTelemetryService(accessor.get(ITelemetryService));
599
});
600
601
// Auth Handler
602
appInstantiationService.invokeFunction(accessor => accessor.get(IProxyAuthService));
603
604
// Transient profiles handler
605
this._register(appInstantiationService.createInstance(UserDataProfilesHandler));
606
607
// Init Channels
608
appInstantiationService.invokeFunction(accessor => this.initChannels(accessor, mainProcessElectronServer, sharedProcessClient));
609
610
// Setup Protocol URL Handlers
611
const initialProtocolUrls = await appInstantiationService.invokeFunction(accessor => this.setupProtocolUrlHandlers(accessor, mainProcessElectronServer));
612
613
// Setup vscode-remote-resource protocol handler
614
this.setupManagedRemoteResourceUrlHandler(mainProcessElectronServer);
615
616
// Signal phase: ready - before opening first window
617
this.lifecycleMainService.phase = LifecycleMainPhase.Ready;
618
619
// Open Windows
620
await appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, initialProtocolUrls));
621
622
// Signal phase: after window open
623
this.lifecycleMainService.phase = LifecycleMainPhase.AfterWindowOpen;
624
625
// Post Open Windows Tasks
626
this.afterWindowOpen(appInstantiationService);
627
628
// Set lifecycle phase to `Eventually` after a short delay and when idle (min 2.5sec, max 5sec)
629
const eventuallyPhaseScheduler = this._register(new RunOnceScheduler(() => {
630
this._register(runWhenGlobalIdle(() => {
631
632
// Signal phase: eventually
633
this.lifecycleMainService.phase = LifecycleMainPhase.Eventually;
634
635
// Eventually Post Open Window Tasks
636
this.eventuallyAfterWindowOpen();
637
}, 2500));
638
}, 2500));
639
eventuallyPhaseScheduler.schedule();
640
}
641
642
private async setupProtocolUrlHandlers(accessor: ServicesAccessor, mainProcessElectronServer: ElectronIPCServer): Promise<IInitialProtocolUrls | undefined> {
643
const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService);
644
const urlService = accessor.get(IURLService);
645
const nativeHostMainService = this.nativeHostMainService = accessor.get(INativeHostMainService);
646
const dialogMainService = accessor.get(IDialogMainService);
647
648
// Install URL handlers that deal with protocl URLs either
649
// from this process by opening windows and/or by forwarding
650
// the URLs into a window process to be handled there.
651
652
const app = this;
653
urlService.registerHandler({
654
async handleURL(uri: URI, options?: IOpenURLOptions): Promise<boolean> {
655
return app.handleProtocolUrl(windowsMainService, dialogMainService, urlService, uri, options);
656
}
657
});
658
659
const activeWindowManager = this._register(new ActiveWindowManager({
660
onDidOpenMainWindow: nativeHostMainService.onDidOpenMainWindow,
661
onDidFocusMainWindow: nativeHostMainService.onDidFocusMainWindow,
662
getActiveWindowId: () => nativeHostMainService.getActiveWindowId(-1)
663
}));
664
const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id));
665
const urlHandlerRouter = new URLHandlerRouter(activeWindowRouter, this.logService);
666
const urlHandlerChannel = mainProcessElectronServer.getChannel('urlHandler', urlHandlerRouter);
667
urlService.registerHandler(new URLHandlerChannelClient(urlHandlerChannel));
668
669
const initialProtocolUrls = await this.resolveInitialProtocolUrls(windowsMainService, dialogMainService);
670
this._register(new ElectronURLListener(initialProtocolUrls?.urls, urlService, windowsMainService, this.environmentMainService, this.productService, this.logService));
671
672
return initialProtocolUrls;
673
}
674
675
private setupManagedRemoteResourceUrlHandler(mainProcessElectronServer: ElectronIPCServer) {
676
const notFound = (): Electron.ProtocolResponse => ({ statusCode: 404, data: 'Not found' });
677
const remoteResourceChannel = new Lazy(() => mainProcessElectronServer.getChannel(
678
NODE_REMOTE_RESOURCE_CHANNEL_NAME,
679
new NodeRemoteResourceRouter(),
680
));
681
682
protocol.registerBufferProtocol(Schemas.vscodeManagedRemoteResource, (request, callback) => {
683
const url = URI.parse(request.url);
684
if (!url.authority.startsWith('window:')) {
685
return callback(notFound());
686
}
687
688
remoteResourceChannel.value.call<NodeRemoteResourceResponse>(NODE_REMOTE_RESOURCE_IPC_METHOD_NAME, [url]).then(
689
r => callback({ ...r, data: Buffer.from(r.body, 'base64') }),
690
err => {
691
this.logService.warn('error dispatching remote resource call', err);
692
callback({ statusCode: 500, data: String(err) });
693
});
694
});
695
}
696
697
private async resolveInitialProtocolUrls(windowsMainService: IWindowsMainService, dialogMainService: IDialogMainService): Promise<IInitialProtocolUrls | undefined> {
698
699
/**
700
* Protocol URL handling on startup is complex, refer to
701
* {@link IInitialProtocolUrls} for an explainer.
702
*/
703
704
// Windows/Linux: protocol handler invokes CLI with --open-url
705
const protocolUrlsFromCommandLine = this.environmentMainService.args['open-url'] ? this.environmentMainService.args._urls || [] : [];
706
if (protocolUrlsFromCommandLine.length > 0) {
707
this.logService.trace('app#resolveInitialProtocolUrls() protocol urls from command line:', protocolUrlsFromCommandLine);
708
}
709
710
// macOS: open-url events that were received before the app is ready
711
const protocolUrlsFromEvent = ((global as { getOpenUrls?: () => string[] }).getOpenUrls?.() || []);
712
if (protocolUrlsFromEvent.length > 0) {
713
this.logService.trace(`app#resolveInitialProtocolUrls() protocol urls from macOS 'open-url' event:`, protocolUrlsFromEvent);
714
}
715
716
if (protocolUrlsFromCommandLine.length + protocolUrlsFromEvent.length === 0) {
717
return undefined;
718
}
719
720
const protocolUrls = [
721
...protocolUrlsFromCommandLine,
722
...protocolUrlsFromEvent
723
].map(url => {
724
try {
725
return { uri: URI.parse(url), originalUrl: url };
726
} catch {
727
this.logService.trace('app#resolveInitialProtocolUrls() protocol url failed to parse:', url);
728
729
return undefined;
730
}
731
});
732
733
const openables: IWindowOpenable[] = [];
734
const urls: IProtocolUrl[] = [];
735
for (const protocolUrl of protocolUrls) {
736
if (!protocolUrl) {
737
continue; // invalid
738
}
739
740
const windowOpenable = this.getWindowOpenableFromProtocolUrl(protocolUrl.uri);
741
if (windowOpenable) {
742
if (await this.shouldBlockOpenable(windowOpenable, windowsMainService, dialogMainService)) {
743
this.logService.trace('app#resolveInitialProtocolUrls() protocol url was blocked:', protocolUrl.uri.toString(true));
744
745
continue; // blocked
746
} else {
747
this.logService.trace('app#resolveInitialProtocolUrls() protocol url will be handled as window to open:', protocolUrl.uri.toString(true), windowOpenable);
748
749
openables.push(windowOpenable); // handled as window to open
750
}
751
} else {
752
this.logService.trace('app#resolveInitialProtocolUrls() protocol url will be passed to active window for handling:', protocolUrl.uri.toString(true));
753
754
urls.push(protocolUrl); // handled within active window
755
}
756
}
757
758
return { urls, openables };
759
}
760
761
private async shouldBlockOpenable(openable: IWindowOpenable, windowsMainService: IWindowsMainService, dialogMainService: IDialogMainService): Promise<boolean> {
762
let openableUri: URI;
763
let message: string;
764
if (isWorkspaceToOpen(openable)) {
765
openableUri = openable.workspaceUri;
766
message = localize('confirmOpenMessageWorkspace', "An external application wants to open '{0}' in {1}. Do you want to open this workspace file?", openableUri.scheme === Schemas.file ? getPathLabel(openableUri, { os: OS, tildify: this.environmentMainService }) : openableUri.toString(true), this.productService.nameShort);
767
} else if (isFolderToOpen(openable)) {
768
openableUri = openable.folderUri;
769
message = localize('confirmOpenMessageFolder', "An external application wants to open '{0}' in {1}. Do you want to open this folder?", openableUri.scheme === Schemas.file ? getPathLabel(openableUri, { os: OS, tildify: this.environmentMainService }) : openableUri.toString(true), this.productService.nameShort);
770
} else {
771
openableUri = openable.fileUri;
772
message = localize('confirmOpenMessageFileOrFolder', "An external application wants to open '{0}' in {1}. Do you want to open this file or folder?", openableUri.scheme === Schemas.file ? getPathLabel(openableUri, { os: OS, tildify: this.environmentMainService }) : openableUri.toString(true), this.productService.nameShort);
773
}
774
775
if (openableUri.scheme !== Schemas.file && openableUri.scheme !== Schemas.vscodeRemote) {
776
777
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
778
//
779
// NOTE: we currently only ask for confirmation for `file` and `vscode-remote`
780
// authorities here. There is an additional confirmation for `extension.id`
781
// authorities from within the window.
782
//
783
// IF YOU ARE PLANNING ON ADDING ANOTHER AUTHORITY HERE, MAKE SURE TO ALSO
784
// ADD IT TO THE CONFIRMATION CODE BELOW OR INSIDE THE WINDOW!
785
//
786
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
787
788
return false;
789
}
790
791
const askForConfirmation = this.configurationService.getValue<unknown>(CodeApplication.SECURITY_PROTOCOL_HANDLING_CONFIRMATION_SETTING_KEY[openableUri.scheme]);
792
if (askForConfirmation === false) {
793
return false; // not blocked via settings
794
}
795
796
const { response, checkboxChecked } = await dialogMainService.showMessageBox({
797
type: 'warning',
798
buttons: [
799
localize({ key: 'open', comment: ['&& denotes a mnemonic'] }, "&&Yes"),
800
localize({ key: 'cancel', comment: ['&& denotes a mnemonic'] }, "&&No")
801
],
802
message,
803
detail: localize('confirmOpenDetail', "If you did not initiate this request, it may represent an attempted attack on your system. Unless you took an explicit action to initiate this request, you should press 'No'"),
804
checkboxLabel: openableUri.scheme === Schemas.file ? localize('doNotAskAgainLocal', "Allow opening local paths without asking") : localize('doNotAskAgainRemote', "Allow opening remote paths without asking"),
805
cancelId: 1
806
});
807
808
if (response !== 0) {
809
return true; // blocked by user choice
810
}
811
812
if (checkboxChecked) {
813
// Due to https://github.com/microsoft/vscode/issues/195436, we can only
814
// update settings from within a window. But we do not know if a window
815
// is about to open or can already handle the request, so we have to send
816
// to any current window and any newly opening window.
817
const request = { channel: 'vscode:disablePromptForProtocolHandling', args: openableUri.scheme === Schemas.file ? 'local' : 'remote' };
818
windowsMainService.sendToFocused(request.channel, request.args);
819
windowsMainService.sendToOpeningWindow(request.channel, request.args);
820
}
821
822
return false; // not blocked by user choice
823
}
824
825
private getWindowOpenableFromProtocolUrl(uri: URI): IWindowOpenable | undefined {
826
if (!uri.path) {
827
return undefined;
828
}
829
830
// File path
831
if (uri.authority === Schemas.file) {
832
const fileUri = URI.file(uri.fsPath);
833
834
if (hasWorkspaceFileExtension(fileUri)) {
835
return { workspaceUri: fileUri };
836
}
837
838
return { fileUri };
839
}
840
841
// Remote path
842
else if (uri.authority === Schemas.vscodeRemote) {
843
844
// Example conversion:
845
// From: vscode://vscode-remote/wsl+ubuntu/mnt/c/GitDevelopment/monaco
846
// To: vscode-remote://wsl+ubuntu/mnt/c/GitDevelopment/monaco
847
848
const secondSlash = uri.path.indexOf(posix.sep, 1 /* skip over the leading slash */);
849
let authority: string;
850
let path: string;
851
if (secondSlash !== -1) {
852
authority = uri.path.substring(1, secondSlash);
853
path = uri.path.substring(secondSlash);
854
} else {
855
authority = uri.path.substring(1);
856
path = '/';
857
}
858
859
let query = uri.query;
860
const params = new URLSearchParams(uri.query);
861
if (params.get('windowId') === '_blank') {
862
// Make sure to unset any `windowId=_blank` here
863
// https://github.com/microsoft/vscode/issues/191902
864
params.delete('windowId');
865
query = params.toString();
866
}
867
868
const remoteUri = URI.from({ scheme: Schemas.vscodeRemote, authority, path, query, fragment: uri.fragment });
869
870
if (hasWorkspaceFileExtension(path)) {
871
return { workspaceUri: remoteUri };
872
}
873
874
if (/:[\d]+$/.test(path)) {
875
// path with :line:column syntax
876
return { fileUri: remoteUri };
877
}
878
879
return { folderUri: remoteUri };
880
}
881
return undefined;
882
}
883
884
private async handleProtocolUrl(windowsMainService: IWindowsMainService, dialogMainService: IDialogMainService, urlService: IURLService, uri: URI, options?: IOpenURLOptions): Promise<boolean> {
885
this.logService.trace('app#handleProtocolUrl():', uri.toString(true), options);
886
887
// Support 'workspace' URLs (https://github.com/microsoft/vscode/issues/124263)
888
if (uri.scheme === this.productService.urlProtocol && uri.path === 'workspace') {
889
uri = uri.with({
890
authority: 'file',
891
path: URI.parse(uri.query).path,
892
query: ''
893
});
894
}
895
896
let shouldOpenInNewWindow = false;
897
898
// We should handle the URI in a new window if the URL contains `windowId=_blank`
899
const params = new URLSearchParams(uri.query);
900
if (params.get('windowId') === '_blank') {
901
this.logService.trace(`app#handleProtocolUrl() found 'windowId=_blank' as parameter, setting shouldOpenInNewWindow=true:`, uri.toString(true));
902
903
params.delete('windowId');
904
uri = uri.with({ query: params.toString() });
905
906
shouldOpenInNewWindow = true;
907
}
908
909
// or if no window is open (macOS only)
910
else if (isMacintosh && windowsMainService.getWindowCount() === 0) {
911
this.logService.trace(`app#handleProtocolUrl() running on macOS with no window open, setting shouldOpenInNewWindow=true:`, uri.toString(true));
912
913
shouldOpenInNewWindow = true;
914
}
915
916
// Pass along whether the application is being opened via a Continue On flow
917
const continueOn = params.get('continueOn');
918
if (continueOn !== null) {
919
this.logService.trace(`app#handleProtocolUrl() found 'continueOn' as parameter:`, uri.toString(true));
920
921
params.delete('continueOn');
922
uri = uri.with({ query: params.toString() });
923
924
this.environmentMainService.continueOn = continueOn ?? undefined;
925
}
926
927
// Check if the protocol URL is a window openable to open...
928
const windowOpenableFromProtocolUrl = this.getWindowOpenableFromProtocolUrl(uri);
929
if (windowOpenableFromProtocolUrl) {
930
if (await this.shouldBlockOpenable(windowOpenableFromProtocolUrl, windowsMainService, dialogMainService)) {
931
this.logService.trace('app#handleProtocolUrl() protocol url was blocked:', uri.toString(true));
932
933
return true; // If openable should be blocked, behave as if it's handled
934
} else {
935
this.logService.trace('app#handleProtocolUrl() opening protocol url as window:', windowOpenableFromProtocolUrl, uri.toString(true));
936
937
const window = (await windowsMainService.open({
938
context: OpenContext.LINK,
939
cli: { ...this.environmentMainService.args },
940
urisToOpen: [windowOpenableFromProtocolUrl],
941
forceNewWindow: shouldOpenInNewWindow,
942
gotoLineMode: true
943
// remoteAuthority: will be determined based on windowOpenableFromProtocolUrl
944
})).at(0);
945
946
window?.focus(); // this should help ensuring that the right window gets focus when multiple are opened
947
948
return true;
949
}
950
}
951
952
// ...or if we should open in a new window and then handle it within that window
953
if (shouldOpenInNewWindow) {
954
this.logService.trace('app#handleProtocolUrl() opening empty window and passing in protocol url:', uri.toString(true));
955
956
const window = (await windowsMainService.open({
957
context: OpenContext.LINK,
958
cli: { ...this.environmentMainService.args },
959
forceNewWindow: true,
960
forceEmpty: true,
961
gotoLineMode: true,
962
remoteAuthority: getRemoteAuthority(uri)
963
})).at(0);
964
965
await window?.ready();
966
967
return urlService.open(uri, options);
968
}
969
970
this.logService.trace('app#handleProtocolUrl(): not handled', uri.toString(true), options);
971
972
return false;
973
}
974
975
private setupSharedProcess(machineId: string, sqmId: string, devDeviceId: string): { sharedProcessReady: Promise<MessagePortClient>; sharedProcessClient: Promise<MessagePortClient> } {
976
const sharedProcess = this._register(this.mainInstantiationService.createInstance(SharedProcess, machineId, sqmId, devDeviceId));
977
978
this._register(sharedProcess.onDidCrash(() => this.windowsMainService?.sendToFocused('vscode:reportSharedProcessCrash')));
979
980
const sharedProcessClient = (async () => {
981
this.logService.trace('Main->SharedProcess#connect');
982
983
const port = await sharedProcess.connect();
984
985
this.logService.trace('Main->SharedProcess#connect: connection established');
986
987
return new MessagePortClient(port, 'main');
988
})();
989
990
const sharedProcessReady = (async () => {
991
await sharedProcess.whenReady();
992
993
return sharedProcessClient;
994
})();
995
996
return { sharedProcessReady, sharedProcessClient };
997
}
998
999
private async initServices(machineId: string, sqmId: string, devDeviceId: string, sharedProcessReady: Promise<MessagePortClient>): Promise<IInstantiationService> {
1000
const services = new ServiceCollection();
1001
1002
// Update
1003
switch (process.platform) {
1004
case 'win32':
1005
services.set(IUpdateService, new SyncDescriptor(Win32UpdateService));
1006
break;
1007
1008
case 'linux':
1009
if (isLinuxSnap) {
1010
services.set(IUpdateService, new SyncDescriptor(SnapUpdateService, [process.env['SNAP'], process.env['SNAP_REVISION']]));
1011
} else {
1012
services.set(IUpdateService, new SyncDescriptor(LinuxUpdateService));
1013
}
1014
break;
1015
1016
case 'darwin':
1017
services.set(IUpdateService, new SyncDescriptor(DarwinUpdateService));
1018
break;
1019
}
1020
1021
// Windows
1022
services.set(IWindowsMainService, new SyncDescriptor(WindowsMainService, [machineId, sqmId, devDeviceId, this.userEnv], false));
1023
services.set(IAuxiliaryWindowsMainService, new SyncDescriptor(AuxiliaryWindowsMainService, undefined, false));
1024
1025
// Dialogs
1026
const dialogMainService = new DialogMainService(this.logService, this.productService);
1027
services.set(IDialogMainService, dialogMainService);
1028
1029
// Launch
1030
services.set(ILaunchMainService, new SyncDescriptor(LaunchMainService, undefined, false /* proxied to other processes */));
1031
1032
// Diagnostics
1033
services.set(IDiagnosticsMainService, new SyncDescriptor(DiagnosticsMainService, undefined, false /* proxied to other processes */));
1034
services.set(IDiagnosticsService, ProxyChannel.toService(getDelayedChannel(sharedProcessReady.then(client => client.getChannel('diagnostics')))));
1035
1036
// Encryption
1037
services.set(IEncryptionMainService, new SyncDescriptor(EncryptionMainService));
1038
1039
// Browser Elements
1040
services.set(INativeBrowserElementsMainService, new SyncDescriptor(NativeBrowserElementsMainService, undefined, false /* proxied to other processes */));
1041
1042
// Browser View
1043
services.set(IBrowserViewMainService, new SyncDescriptor(BrowserViewMainService, undefined, false /* proxied to other processes */));
1044
1045
// Keyboard Layout
1046
services.set(IKeyboardLayoutMainService, new SyncDescriptor(KeyboardLayoutMainService));
1047
1048
// Native Host
1049
services.set(INativeHostMainService, new SyncDescriptor(NativeHostMainService, undefined, false /* proxied to other processes */));
1050
1051
// Metered Connection
1052
const meteredConnectionService = new MeteredConnectionMainService(this.configurationService);
1053
services.set(IMeteredConnectionService, meteredConnectionService);
1054
1055
// Web Contents Extractor
1056
services.set(IWebContentExtractorService, new SyncDescriptor(NativeWebContentExtractorService, undefined, false /* proxied to other processes */));
1057
1058
// Webview Manager
1059
services.set(IWebviewManagerService, new SyncDescriptor(WebviewMainService));
1060
1061
// Menubar
1062
services.set(IMenubarMainService, new SyncDescriptor(MenubarMainService));
1063
1064
// Extension Host Starter
1065
services.set(IExtensionHostStarter, new SyncDescriptor(ExtensionHostStarter));
1066
1067
// Storage
1068
services.set(IStorageMainService, new SyncDescriptor(StorageMainService));
1069
services.set(IApplicationStorageMainService, new SyncDescriptor(ApplicationStorageMainService));
1070
1071
// Terminal
1072
const ptyHostStarter = new ElectronPtyHostStarter({
1073
graceTime: LocalReconnectConstants.GraceTime,
1074
shortGraceTime: LocalReconnectConstants.ShortGraceTime,
1075
scrollback: this.configurationService.getValue<number>(TerminalSettingId.PersistentSessionScrollback) ?? 100
1076
}, this.configurationService, this.environmentMainService, this.lifecycleMainService, this.logService);
1077
const ptyHostService = new PtyHostService(
1078
ptyHostStarter,
1079
this.configurationService,
1080
this.logService,
1081
this.loggerService
1082
);
1083
services.set(ILocalPtyService, ptyHostService);
1084
1085
// External terminal
1086
if (isWindows) {
1087
services.set(IExternalTerminalMainService, new SyncDescriptor(WindowsExternalTerminalService));
1088
} else if (isMacintosh) {
1089
services.set(IExternalTerminalMainService, new SyncDescriptor(MacExternalTerminalService));
1090
} else if (isLinux) {
1091
services.set(IExternalTerminalMainService, new SyncDescriptor(LinuxExternalTerminalService));
1092
}
1093
1094
// Backups
1095
const backupMainService = new BackupMainService(this.environmentMainService, this.configurationService, this.logService, this.stateService);
1096
services.set(IBackupMainService, backupMainService);
1097
1098
// Workspaces
1099
const workspacesManagementMainService = new WorkspacesManagementMainService(this.environmentMainService, this.logService, this.userDataProfilesMainService, backupMainService, dialogMainService);
1100
services.set(IWorkspacesManagementMainService, workspacesManagementMainService);
1101
services.set(IWorkspacesService, new SyncDescriptor(WorkspacesMainService, undefined, false /* proxied to other processes */));
1102
services.set(IWorkspacesHistoryMainService, new SyncDescriptor(WorkspacesHistoryMainService, undefined, false));
1103
1104
// URL handling
1105
services.set(IURLService, new SyncDescriptor(NativeURLService, undefined, false /* proxied to other processes */));
1106
1107
// Telemetry
1108
if (supportsTelemetry(this.productService, this.environmentMainService)) {
1109
const isInternal = isInternalTelemetry(this.productService, this.configurationService);
1110
const channel = getDelayedChannel(sharedProcessReady.then(client => client.getChannel('telemetryAppender')));
1111
const appender = new TelemetryAppenderClient(channel);
1112
const commonProperties = resolveCommonProperties(release(), hostname(), process.arch, this.productService.commit, this.productService.version, machineId, sqmId, devDeviceId, isInternal, this.productService.date);
1113
const piiPaths = getPiiPathsFromEnvironment(this.environmentMainService);
1114
const config: ITelemetryServiceConfig = { appenders: [appender], commonProperties, piiPaths, sendErrorTelemetry: true };
1115
1116
services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [config], false));
1117
} else {
1118
services.set(ITelemetryService, NullTelemetryService);
1119
}
1120
1121
// Default Extensions Profile Init
1122
services.set(IExtensionsProfileScannerService, new SyncDescriptor(ExtensionsProfileScannerService, undefined, true));
1123
services.set(IExtensionsScannerService, new SyncDescriptor(ExtensionsScannerService, undefined, true));
1124
1125
// Utility Process Worker
1126
services.set(IUtilityProcessWorkerMainService, new SyncDescriptor(UtilityProcessWorkerMainService, undefined, true));
1127
1128
// Proxy Auth
1129
services.set(IProxyAuthService, new SyncDescriptor(ProxyAuthService));
1130
1131
// MCP
1132
services.set(INativeMcpDiscoveryHelperService, new SyncDescriptor(NativeMcpDiscoveryHelperService));
1133
services.set(IMcpGatewayService, new SyncDescriptor(McpGatewayService));
1134
1135
1136
// Dev Only: CSS service (for ESM)
1137
services.set(ICSSDevelopmentService, new SyncDescriptor(CSSDevelopmentService, undefined, true));
1138
1139
// Init services that require it
1140
await Promises.settled([
1141
backupMainService.initialize(),
1142
workspacesManagementMainService.initialize()
1143
]);
1144
1145
return this.mainInstantiationService.createChild(services);
1146
}
1147
1148
private initChannels(accessor: ServicesAccessor, mainProcessElectronServer: ElectronIPCServer, sharedProcessClient: Promise<MessagePortClient>): void {
1149
1150
// Channels registered to node.js are exposed to second instances
1151
// launching because that is the only way the second instance
1152
// can talk to the first instance. Electron IPC does not work
1153
// across apps until `requestSingleInstance` APIs are adopted.
1154
1155
const disposables = this._register(new DisposableStore());
1156
1157
const launchChannel = ProxyChannel.fromService(accessor.get(ILaunchMainService), disposables, { disableMarshalling: true });
1158
this.mainProcessNodeIpcServer.registerChannel('launch', launchChannel);
1159
1160
const diagnosticsChannel = ProxyChannel.fromService(accessor.get(IDiagnosticsMainService), disposables, { disableMarshalling: true });
1161
this.mainProcessNodeIpcServer.registerChannel('diagnostics', diagnosticsChannel);
1162
1163
// Policies (main & shared process)
1164
const policyChannel = disposables.add(new PolicyChannel(accessor.get(IPolicyService)));
1165
mainProcessElectronServer.registerChannel('policy', policyChannel);
1166
sharedProcessClient.then(client => client.registerChannel('policy', policyChannel));
1167
1168
// Local Files
1169
const diskFileSystemProvider = this.fileService.getProvider(Schemas.file);
1170
assertType(diskFileSystemProvider instanceof DiskFileSystemProvider);
1171
const fileSystemProviderChannel = disposables.add(new DiskFileSystemProviderChannel(diskFileSystemProvider, this.logService, this.environmentMainService));
1172
mainProcessElectronServer.registerChannel(LOCAL_FILE_SYSTEM_CHANNEL_NAME, fileSystemProviderChannel);
1173
sharedProcessClient.then(client => client.registerChannel(LOCAL_FILE_SYSTEM_CHANNEL_NAME, fileSystemProviderChannel));
1174
1175
// User Data Profiles
1176
const userDataProfilesService = ProxyChannel.fromService(accessor.get(IUserDataProfilesMainService), disposables);
1177
mainProcessElectronServer.registerChannel('userDataProfiles', userDataProfilesService);
1178
sharedProcessClient.then(client => client.registerChannel('userDataProfiles', userDataProfilesService));
1179
1180
// Update
1181
const updateChannel = new UpdateChannel(accessor.get(IUpdateService));
1182
mainProcessElectronServer.registerChannel('update', updateChannel);
1183
1184
// Metered Connection
1185
const meteredConnectionChannel = new MeteredConnectionChannel(accessor.get(IMeteredConnectionService) as MeteredConnectionMainService);
1186
mainProcessElectronServer.registerChannel(METERED_CONNECTION_CHANNEL, meteredConnectionChannel);
1187
sharedProcessClient.then(client => client.registerChannel(METERED_CONNECTION_CHANNEL, meteredConnectionChannel));
1188
1189
// Process
1190
const processChannel = ProxyChannel.fromService(new ProcessMainService(this.logService, accessor.get(IDiagnosticsService), accessor.get(IDiagnosticsMainService)), disposables);
1191
mainProcessElectronServer.registerChannel('process', processChannel);
1192
1193
// Encryption
1194
const encryptionChannel = ProxyChannel.fromService(accessor.get(IEncryptionMainService), disposables);
1195
mainProcessElectronServer.registerChannel('encryption', encryptionChannel);
1196
1197
// Browser Elements
1198
const browserElementsChannel = ProxyChannel.fromService(accessor.get(INativeBrowserElementsMainService), disposables);
1199
mainProcessElectronServer.registerChannel('browserElements', browserElementsChannel);
1200
sharedProcessClient.then(client => client.registerChannel('browserElements', browserElementsChannel));
1201
1202
// Browser View
1203
const browserViewChannel = ProxyChannel.fromService(accessor.get(IBrowserViewMainService), disposables);
1204
mainProcessElectronServer.registerChannel(ipcBrowserViewChannelName, browserViewChannel);
1205
1206
// Signing
1207
const signChannel = ProxyChannel.fromService(accessor.get(ISignService), disposables);
1208
mainProcessElectronServer.registerChannel('sign', signChannel);
1209
1210
// Keyboard Layout
1211
const keyboardLayoutChannel = ProxyChannel.fromService(accessor.get(IKeyboardLayoutMainService), disposables);
1212
mainProcessElectronServer.registerChannel('keyboardLayout', keyboardLayoutChannel);
1213
1214
// Native host (main & shared process)
1215
this.nativeHostMainService = accessor.get(INativeHostMainService);
1216
const nativeHostChannel = ProxyChannel.fromService(this.nativeHostMainService, disposables);
1217
mainProcessElectronServer.registerChannel('nativeHost', nativeHostChannel);
1218
sharedProcessClient.then(client => client.registerChannel('nativeHost', nativeHostChannel));
1219
1220
// Web Content Extractor
1221
const webContentExtractorChannel = ProxyChannel.fromService(accessor.get(IWebContentExtractorService), disposables);
1222
mainProcessElectronServer.registerChannel('webContentExtractor', webContentExtractorChannel);
1223
1224
// Workspaces
1225
const workspacesChannel = ProxyChannel.fromService(accessor.get(IWorkspacesService), disposables);
1226
mainProcessElectronServer.registerChannel('workspaces', workspacesChannel);
1227
1228
// Menubar
1229
const menubarChannel = ProxyChannel.fromService(accessor.get(IMenubarMainService), disposables);
1230
mainProcessElectronServer.registerChannel('menubar', menubarChannel);
1231
1232
// URL handling
1233
const urlChannel = ProxyChannel.fromService(accessor.get(IURLService), disposables);
1234
mainProcessElectronServer.registerChannel('url', urlChannel);
1235
1236
// Webview Manager
1237
const webviewChannel = ProxyChannel.fromService(accessor.get(IWebviewManagerService), disposables);
1238
mainProcessElectronServer.registerChannel('webview', webviewChannel);
1239
1240
// Storage (main & shared process)
1241
const storageChannel = disposables.add((new StorageDatabaseChannel(this.logService, accessor.get(IStorageMainService))));
1242
mainProcessElectronServer.registerChannel('storage', storageChannel);
1243
sharedProcessClient.then(client => client.registerChannel('storage', storageChannel));
1244
1245
// Profile Storage Changes Listener (shared process)
1246
const profileStorageListener = disposables.add((new ProfileStorageChangesListenerChannel(accessor.get(IStorageMainService), accessor.get(IUserDataProfilesMainService), this.logService)));
1247
sharedProcessClient.then(client => client.registerChannel('profileStorageListener', profileStorageListener));
1248
1249
// Terminal
1250
const ptyHostChannel = ProxyChannel.fromService(accessor.get(ILocalPtyService), disposables);
1251
mainProcessElectronServer.registerChannel(TerminalIpcChannels.LocalPty, ptyHostChannel);
1252
1253
// External Terminal
1254
const externalTerminalChannel = ProxyChannel.fromService(accessor.get(IExternalTerminalMainService), disposables);
1255
mainProcessElectronServer.registerChannel('externalTerminal', externalTerminalChannel);
1256
1257
// MCP
1258
const mcpDiscoveryChannel = ProxyChannel.fromService(accessor.get(INativeMcpDiscoveryHelperService), disposables);
1259
mainProcessElectronServer.registerChannel(NativeMcpDiscoveryHelperChannelName, mcpDiscoveryChannel);
1260
const mcpGatewayChannel = this._register(new McpGatewayChannel(mainProcessElectronServer, accessor.get(IMcpGatewayService)));
1261
mainProcessElectronServer.registerChannel(McpGatewayChannelName, mcpGatewayChannel);
1262
1263
// Logger
1264
const loggerChannel = new LoggerChannel(accessor.get(ILoggerMainService),);
1265
mainProcessElectronServer.registerChannel('logger', loggerChannel);
1266
sharedProcessClient.then(client => client.registerChannel('logger', loggerChannel));
1267
1268
// Extension Host Debug Broadcasting
1269
const electronExtensionHostDebugBroadcastChannel = new ElectronExtensionHostDebugBroadcastChannel(accessor.get(IWindowsMainService));
1270
mainProcessElectronServer.registerChannel('extensionhostdebugservice', electronExtensionHostDebugBroadcastChannel);
1271
1272
// Extension Host Starter
1273
const extensionHostStarterChannel = ProxyChannel.fromService(accessor.get(IExtensionHostStarter), disposables);
1274
mainProcessElectronServer.registerChannel(ipcExtensionHostStarterChannelName, extensionHostStarterChannel);
1275
1276
// Utility Process Worker
1277
const utilityProcessWorkerChannel = ProxyChannel.fromService(accessor.get(IUtilityProcessWorkerMainService), disposables);
1278
mainProcessElectronServer.registerChannel(ipcUtilityProcessWorkerChannelName, utilityProcessWorkerChannel);
1279
}
1280
1281
private async openFirstWindow(accessor: ServicesAccessor, initialProtocolUrls: IInitialProtocolUrls | undefined): Promise<ICodeWindow[]> {
1282
const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService);
1283
this.auxiliaryWindowsMainService = accessor.get(IAuxiliaryWindowsMainService);
1284
1285
const context = isLaunchedFromCli(process.env) ? OpenContext.CLI : OpenContext.DESKTOP;
1286
const args = this.environmentMainService.args;
1287
1288
// First check for windows from protocol links to open
1289
if (initialProtocolUrls) {
1290
1291
// Openables can open as windows directly
1292
if (initialProtocolUrls.openables.length > 0) {
1293
return windowsMainService.open({
1294
context,
1295
cli: args,
1296
urisToOpen: initialProtocolUrls.openables,
1297
gotoLineMode: true,
1298
initialStartup: true
1299
// remoteAuthority: will be determined based on openables
1300
});
1301
}
1302
1303
// Protocol links with `windowId=_blank` on startup
1304
// should be handled in a special way:
1305
// We take the first one of these and open an empty
1306
// window for it. This ensures we are not restoring
1307
// all windows of the previous session.
1308
// If there are any more URLs like these, they will
1309
// be handled from the URL listeners installed later.
1310
1311
if (initialProtocolUrls.urls.length > 0) {
1312
for (const protocolUrl of initialProtocolUrls.urls) {
1313
const params = new URLSearchParams(protocolUrl.uri.query);
1314
if (params.get('windowId') === '_blank') {
1315
1316
// It is important here that we remove `windowId=_blank` from
1317
// this URL because here we open an empty window for it.
1318
1319
params.delete('windowId');
1320
protocolUrl.originalUrl = protocolUrl.uri.toString(true);
1321
protocolUrl.uri = protocolUrl.uri.with({ query: params.toString() });
1322
1323
return windowsMainService.open({
1324
context,
1325
cli: args,
1326
forceNewWindow: true,
1327
forceEmpty: true,
1328
gotoLineMode: true,
1329
initialStartup: true
1330
// remoteAuthority: will be determined based on openables
1331
});
1332
}
1333
}
1334
}
1335
}
1336
1337
const macOpenFiles: string[] = (global as { macOpenFiles?: string[] }).macOpenFiles ?? [];
1338
const hasCliArgs = args._.length;
1339
const hasFolderURIs = !!args['folder-uri'];
1340
const hasFileURIs = !!args['file-uri'];
1341
const noRecentEntry = args['skip-add-to-recently-opened'] === true;
1342
const waitMarkerFileURI = args.wait && args.waitMarkerFilePath ? URI.file(args.waitMarkerFilePath) : undefined;
1343
const remoteAuthority = args.remote || undefined;
1344
const forceProfile = args.profile;
1345
const forceTempProfile = args['profile-temp'];
1346
1347
// Started without file/folder arguments
1348
if (!hasCliArgs && !hasFolderURIs && !hasFileURIs) {
1349
1350
// Force new window
1351
if (args['new-window'] || forceProfile || forceTempProfile) {
1352
return windowsMainService.open({
1353
context,
1354
cli: args,
1355
forceNewWindow: true,
1356
forceEmpty: true,
1357
noRecentEntry,
1358
waitMarkerFileURI,
1359
initialStartup: true,
1360
remoteAuthority,
1361
forceProfile,
1362
forceTempProfile
1363
});
1364
}
1365
1366
// mac: open-file event received on startup
1367
if (macOpenFiles.length) {
1368
return windowsMainService.open({
1369
context: OpenContext.DOCK,
1370
cli: args,
1371
urisToOpen: macOpenFiles.map(path => {
1372
path = normalizeNFC(path); // macOS only: normalize paths to NFC form
1373
1374
return (hasWorkspaceFileExtension(path) ? { workspaceUri: URI.file(path) } : { fileUri: URI.file(path) });
1375
}),
1376
noRecentEntry,
1377
waitMarkerFileURI,
1378
initialStartup: true,
1379
// remoteAuthority: will be determined based on macOpenFiles
1380
});
1381
}
1382
}
1383
1384
// default: read paths from cli
1385
return windowsMainService.open({
1386
context,
1387
cli: args,
1388
forceNewWindow: args['new-window'],
1389
diffMode: args.diff,
1390
mergeMode: args.merge,
1391
noRecentEntry,
1392
waitMarkerFileURI,
1393
gotoLineMode: args.goto,
1394
initialStartup: true,
1395
remoteAuthority,
1396
forceProfile,
1397
forceTempProfile
1398
});
1399
}
1400
1401
private afterWindowOpen(instantiationService: IInstantiationService): void {
1402
1403
// Windows: mutex
1404
this.installMutex();
1405
1406
// Remote Authorities
1407
protocol.registerHttpProtocol(Schemas.vscodeRemoteResource, (request, callback) => {
1408
callback({
1409
url: request.url.replace(/^vscode-remote-resource:/, 'http:'),
1410
method: request.method
1411
});
1412
});
1413
1414
// Start to fetch shell environment (if needed) after window has opened
1415
// Since this operation can take a long time, we want to warm it up while
1416
// the window is opening.
1417
// We also show an error to the user in case this fails.
1418
this.resolveShellEnvironment(this.environmentMainService.args, process.env, true);
1419
1420
// Crash reporter
1421
this.updateCrashReporterEnablement();
1422
1423
// macOS: rosetta translation warning
1424
if (isMacintosh && app.runningUnderARM64Translation) {
1425
this.windowsMainService?.sendToFocused('vscode:showTranslatedBuildWarning');
1426
}
1427
1428
// Power telemetry
1429
instantiationService.invokeFunction(accessor => {
1430
const telemetryService = accessor.get(ITelemetryService);
1431
1432
type PowerEvent = {
1433
readonly idleState: string;
1434
readonly idleTime: number;
1435
readonly thermalState: string;
1436
readonly onBattery: boolean;
1437
};
1438
type PowerEventClassification = {
1439
idleState: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The system idle state (active, idle, locked, unknown).' };
1440
idleTime: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The system idle time in seconds.' };
1441
thermalState: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The system thermal state (unknown, nominal, fair, serious, critical).' };
1442
onBattery: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Whether the system is running on battery power.' };
1443
owner: 'chrmarti';
1444
comment: 'Tracks OS power suspend and resume events for reliability insights.';
1445
};
1446
1447
const getPowerEventData = (): PowerEvent => ({
1448
idleState: powerMonitor.getSystemIdleState(60),
1449
idleTime: powerMonitor.getSystemIdleTime(),
1450
thermalState: powerMonitor.getCurrentThermalState(),
1451
onBattery: powerMonitor.isOnBatteryPower()
1452
});
1453
1454
this._register(Event.fromNodeEventEmitter(powerMonitor, 'suspend')(() => {
1455
telemetryService.publicLog2<PowerEvent, PowerEventClassification>('power.suspend', getPowerEventData());
1456
}));
1457
1458
this._register(Event.fromNodeEventEmitter(powerMonitor, 'resume')(() => {
1459
telemetryService.publicLog2<PowerEvent, PowerEventClassification>('power.resume', getPowerEventData());
1460
}));
1461
});
1462
}
1463
1464
private async installMutex(): Promise<void> {
1465
const win32MutexName = this.productService.win32MutexName;
1466
if (isWindows && win32MutexName) {
1467
try {
1468
const WindowsMutex = await import('@vscode/windows-mutex');
1469
const mutex = new WindowsMutex.Mutex(win32MutexName);
1470
Event.once(this.lifecycleMainService.onWillShutdown)(() => mutex.release());
1471
} catch (error) {
1472
this.logService.error(error);
1473
}
1474
}
1475
}
1476
1477
private async resolveShellEnvironment(args: NativeParsedArgs, env: IProcessEnvironment, notifyOnError: boolean): Promise<typeof process.env> {
1478
try {
1479
return await getResolvedShellEnv(this.configurationService, this.logService, args, env);
1480
} catch (error) {
1481
const errorMessage = toErrorMessage(error);
1482
if (notifyOnError) {
1483
this.windowsMainService?.sendToFocused('vscode:showResolveShellEnvError', errorMessage);
1484
} else {
1485
this.logService.error(errorMessage);
1486
}
1487
}
1488
1489
return {};
1490
}
1491
1492
private async updateCrashReporterEnablement(): Promise<void> {
1493
1494
// If enable-crash-reporter argv is undefined then this is a fresh start,
1495
// based on `telemetry.enableCrashreporter` settings, generate a UUID which
1496
// will be used as crash reporter id and also update the json file.
1497
1498
try {
1499
const argvContent = await this.fileService.readFile(this.environmentMainService.argvResource);
1500
const argvString = argvContent.value.toString();
1501
const argvJSON = parse<{ 'enable-crash-reporter'?: boolean }>(argvString);
1502
const telemetryLevel = getTelemetryLevel(this.configurationService);
1503
const enableCrashReporter = telemetryLevel >= TelemetryLevel.CRASH;
1504
1505
// Initial startup
1506
if (argvJSON['enable-crash-reporter'] === undefined) {
1507
const additionalArgvContent = [
1508
'',
1509
' // Allows to disable crash reporting.',
1510
' // Should restart the app if the value is changed.',
1511
` "enable-crash-reporter": ${enableCrashReporter},`,
1512
'',
1513
' // Unique id used for correlating crash reports sent from this instance.',
1514
' // Do not edit this value.',
1515
` "crash-reporter-id": "${generateUuid()}"`,
1516
'}'
1517
];
1518
const newArgvString = argvString.substring(0, argvString.length - 2).concat(',\n', additionalArgvContent.join('\n'));
1519
1520
await this.fileService.writeFile(this.environmentMainService.argvResource, VSBuffer.fromString(newArgvString));
1521
}
1522
1523
// Subsequent startup: update crash reporter value if changed
1524
else {
1525
const newArgvString = argvString.replace(/"enable-crash-reporter": .*,/, `"enable-crash-reporter": ${enableCrashReporter},`);
1526
if (newArgvString !== argvString) {
1527
await this.fileService.writeFile(this.environmentMainService.argvResource, VSBuffer.fromString(newArgvString));
1528
}
1529
}
1530
} catch (error) {
1531
this.logService.error(error);
1532
1533
// Inform the user via notification
1534
this.windowsMainService?.sendToFocused('vscode:showArgvParseWarning');
1535
}
1536
}
1537
1538
private eventuallyAfterWindowOpen(): void {
1539
1540
// Validate Device ID is up to date (delay this as it has shown significant perf impact)
1541
// Refs: https://github.com/microsoft/vscode/issues/234064
1542
validateDevDeviceId(this.stateService, this.logService);
1543
}
1544
}
1545
1546