Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/extensions/electron-browser/nativeExtensionService.ts
3296 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 { runWhenWindowIdle } from '../../../../base/browser/dom.js';
7
import { mainWindow } from '../../../../base/browser/window.js';
8
import { CancellationToken } from '../../../../base/common/cancellation.js';
9
import { Schemas } from '../../../../base/common/network.js';
10
import * as performance from '../../../../base/common/performance.js';
11
import { isCI } from '../../../../base/common/platform.js';
12
import { URI } from '../../../../base/common/uri.js';
13
import * as nls from '../../../../nls.js';
14
import { Categories } from '../../../../platform/action/common/actionCommonCategories.js';
15
import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js';
16
import { ICommandService } from '../../../../platform/commands/common/commands.js';
17
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
18
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
19
import { ExtensionKind } from '../../../../platform/environment/common/environment.js';
20
import { IExtensionGalleryService } from '../../../../platform/extensionManagement/common/extensionManagement.js';
21
import { ExtensionIdentifier, IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';
22
import { IFileService } from '../../../../platform/files/common/files.js';
23
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
24
import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js';
25
import { ILogService } from '../../../../platform/log/common/log.js';
26
import { INativeHostService } from '../../../../platform/native/common/native.js';
27
import { INotificationService, IPromptChoice, NotificationPriority, Severity } from '../../../../platform/notification/common/notification.js';
28
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
29
import { IProductService } from '../../../../platform/product/common/productService.js';
30
import { PersistentConnectionEventType } from '../../../../platform/remote/common/remoteAgentConnection.js';
31
import { IRemoteAgentEnvironment } from '../../../../platform/remote/common/remoteAgentEnvironment.js';
32
import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, RemoteConnectionType, ResolverResult, getRemoteAuthorityPrefix } from '../../../../platform/remote/common/remoteAuthorityResolver.js';
33
import { IRemoteExtensionsScannerService } from '../../../../platform/remote/common/remoteExtensionsScanner.js';
34
import { getRemoteName, parseAuthorityWithPort } from '../../../../platform/remote/common/remoteHosts.js';
35
import { updateProxyConfigurationsScope } from '../../../../platform/request/common/request.js';
36
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
37
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
38
import { IWorkspaceTrustManagementService } from '../../../../platform/workspace/common/workspaceTrust.js';
39
import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';
40
import { EnablementState, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from '../../extensionManagement/common/extensionManagement.js';
41
import { IWebWorkerExtensionHostDataProvider, IWebWorkerExtensionHostInitData, WebWorkerExtensionHost } from '../browser/webWorkerExtensionHost.js';
42
import { AbstractExtensionService, ExtensionHostCrashTracker, IExtensionHostFactory, LocalExtensions, RemoteExtensions, ResolvedExtensions, ResolverExtensions, checkEnabledAndProposedAPI, extensionIsEnabled, isResolverExtension } from '../common/abstractExtensionService.js';
43
import { ExtensionDescriptionRegistrySnapshot } from '../common/extensionDescriptionRegistry.js';
44
import { parseExtensionDevOptions } from '../common/extensionDevOptions.js';
45
import { ExtensionHostKind, ExtensionRunningPreference, IExtensionHostKindPicker, extensionHostKindToString, extensionRunningPreferenceToString } from '../common/extensionHostKind.js';
46
import { IExtensionHostManager } from '../common/extensionHostManagers.js';
47
import { ExtensionHostExitCode } from '../common/extensionHostProtocol.js';
48
import { IExtensionManifestPropertiesService } from '../common/extensionManifestPropertiesService.js';
49
import { ExtensionRunningLocation, LocalProcessRunningLocation, LocalWebWorkerRunningLocation } from '../common/extensionRunningLocation.js';
50
import { ExtensionRunningLocationTracker, filterExtensionDescriptions } from '../common/extensionRunningLocationTracker.js';
51
import { ExtensionHostExtensions, ExtensionHostStartup, IExtensionHost, IExtensionService, WebWorkerExtHostConfigValue, toExtension, webWorkerExtHostConfig } from '../common/extensions.js';
52
import { ExtensionsProposedApi } from '../common/extensionsProposedApi.js';
53
import { IRemoteExtensionHostDataProvider, IRemoteExtensionHostInitData, RemoteExtensionHost } from '../common/remoteExtensionHost.js';
54
import { CachedExtensionScanner } from './cachedExtensionScanner.js';
55
import { ILocalProcessExtensionHostDataProvider, ILocalProcessExtensionHostInitData, NativeLocalProcessExtensionHost } from './localProcessExtensionHost.js';
56
import { IHostService } from '../../host/browser/host.js';
57
import { ILifecycleService, LifecyclePhase } from '../../lifecycle/common/lifecycle.js';
58
import { IRemoteAgentService } from '../../remote/common/remoteAgentService.js';
59
import { IRemoteExplorerService } from '../../remote/common/remoteExplorerService.js';
60
import { AsyncIterableEmitter, AsyncIterableObject } from '../../../../base/common/async.js';
61
62
export class NativeExtensionService extends AbstractExtensionService implements IExtensionService {
63
64
private readonly _extensionScanner: CachedExtensionScanner;
65
private readonly _localCrashTracker = new ExtensionHostCrashTracker();
66
67
constructor(
68
@IInstantiationService instantiationService: IInstantiationService,
69
@INotificationService notificationService: INotificationService,
70
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
71
@ITelemetryService telemetryService: ITelemetryService,
72
@IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService,
73
@IFileService fileService: IFileService,
74
@IProductService productService: IProductService,
75
@IWorkbenchExtensionManagementService extensionManagementService: IWorkbenchExtensionManagementService,
76
@IWorkspaceContextService contextService: IWorkspaceContextService,
77
@IConfigurationService configurationService: IConfigurationService,
78
@IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService,
79
@ILogService logService: ILogService,
80
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
81
@IRemoteExtensionsScannerService remoteExtensionsScannerService: IRemoteExtensionsScannerService,
82
@ILifecycleService lifecycleService: ILifecycleService,
83
@IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService,
84
@INativeHostService private readonly _nativeHostService: INativeHostService,
85
@IHostService private readonly _hostService: IHostService,
86
@IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService,
87
@IExtensionGalleryService private readonly _extensionGalleryService: IExtensionGalleryService,
88
@IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService,
89
@IDialogService dialogService: IDialogService,
90
) {
91
const extensionsProposedApi = instantiationService.createInstance(ExtensionsProposedApi);
92
const extensionScanner = instantiationService.createInstance(CachedExtensionScanner);
93
const extensionHostFactory = new NativeExtensionHostFactory(
94
extensionsProposedApi,
95
extensionScanner,
96
() => this._getExtensionRegistrySnapshotWhenReady(),
97
instantiationService,
98
environmentService,
99
extensionEnablementService,
100
configurationService,
101
remoteAgentService,
102
remoteAuthorityResolverService,
103
logService
104
);
105
super(
106
{ hasLocalProcess: true, allowRemoteExtensionsInLocalWebWorker: false },
107
extensionsProposedApi,
108
extensionHostFactory,
109
new NativeExtensionHostKindPicker(environmentService, configurationService, logService),
110
instantiationService,
111
notificationService,
112
environmentService,
113
telemetryService,
114
extensionEnablementService,
115
fileService,
116
productService,
117
extensionManagementService,
118
contextService,
119
configurationService,
120
extensionManifestPropertiesService,
121
logService,
122
remoteAgentService,
123
remoteExtensionsScannerService,
124
lifecycleService,
125
remoteAuthorityResolverService,
126
dialogService
127
);
128
129
this._extensionScanner = extensionScanner;
130
131
// delay extension host creation and extension scanning
132
// until the workbench is running. we cannot defer the
133
// extension host more (LifecyclePhase.Restored) because
134
// some editors require the extension host to restore
135
// and this would result in a deadlock
136
// see https://github.com/microsoft/vscode/issues/41322
137
lifecycleService.when(LifecyclePhase.Ready).then(() => {
138
// reschedule to ensure this runs after restoring viewlets, panels, and editors
139
runWhenWindowIdle(mainWindow, () => {
140
this._initialize();
141
}, 50 /*max delay*/);
142
});
143
}
144
145
private async _scanAllLocalExtensions(): Promise<IExtensionDescription[]> {
146
return this._extensionScanner.scannedExtensions;
147
}
148
149
protected override _onExtensionHostCrashed(extensionHost: IExtensionHostManager, code: number, signal: string | null): void {
150
151
const activatedExtensions: ExtensionIdentifier[] = [];
152
const extensionsStatus = this.getExtensionsStatus();
153
for (const key of Object.keys(extensionsStatus)) {
154
const extensionStatus = extensionsStatus[key];
155
if (extensionStatus.activationStarted && extensionHost.containsExtension(extensionStatus.id)) {
156
activatedExtensions.push(extensionStatus.id);
157
}
158
}
159
160
super._onExtensionHostCrashed(extensionHost, code, signal);
161
162
if (extensionHost.kind === ExtensionHostKind.LocalProcess) {
163
if (code === ExtensionHostExitCode.VersionMismatch) {
164
this._notificationService.prompt(
165
Severity.Error,
166
nls.localize('extensionService.versionMismatchCrash', "Extension host cannot start: version mismatch."),
167
[{
168
label: nls.localize('relaunch', "Relaunch VS Code"),
169
run: () => {
170
this._instantiationService.invokeFunction((accessor) => {
171
const hostService = accessor.get(IHostService);
172
hostService.restart();
173
});
174
}
175
}]
176
);
177
return;
178
}
179
180
this._logExtensionHostCrash(extensionHost);
181
this._sendExtensionHostCrashTelemetry(code, signal, activatedExtensions);
182
183
this._localCrashTracker.registerCrash();
184
185
if (this._localCrashTracker.shouldAutomaticallyRestart()) {
186
this._logService.info(`Automatically restarting the extension host.`);
187
this._notificationService.status(nls.localize('extensionService.autoRestart', "The extension host terminated unexpectedly. Restarting..."), { hideAfter: 5000 });
188
this.startExtensionHosts();
189
} else {
190
const choices: IPromptChoice[] = [];
191
if (this._environmentService.isBuilt) {
192
choices.push({
193
label: nls.localize('startBisect', "Start Extension Bisect"),
194
run: () => {
195
this._instantiationService.invokeFunction(accessor => {
196
const commandService = accessor.get(ICommandService);
197
commandService.executeCommand('extension.bisect.start');
198
});
199
}
200
});
201
} else {
202
choices.push({
203
label: nls.localize('devTools', "Open Developer Tools"),
204
run: () => this._nativeHostService.openDevTools()
205
});
206
}
207
208
choices.push({
209
label: nls.localize('restart', "Restart Extension Host"),
210
run: () => this.startExtensionHosts()
211
});
212
213
if (this._environmentService.isBuilt) {
214
choices.push({
215
label: nls.localize('learnMore', "Learn More"),
216
run: () => {
217
this._instantiationService.invokeFunction(accessor => {
218
const openerService = accessor.get(IOpenerService);
219
openerService.open('https://aka.ms/vscode-extension-bisect');
220
});
221
}
222
});
223
}
224
225
this._notificationService.prompt(Severity.Error, nls.localize('extensionService.crash', "Extension host terminated unexpectedly 3 times within the last 5 minutes."), choices);
226
}
227
}
228
}
229
230
private _sendExtensionHostCrashTelemetry(code: number, signal: string | null, activatedExtensions: ExtensionIdentifier[]): void {
231
type ExtensionHostCrashClassification = {
232
owner: 'alexdima';
233
comment: 'The extension host has terminated unexpectedly';
234
code: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The exit code of the extension host process.' };
235
signal: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The signal that caused the extension host process to exit.' };
236
extensionIds: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The list of loaded extensions.' };
237
};
238
type ExtensionHostCrashEvent = {
239
code: number;
240
signal: string | null;
241
extensionIds: string[];
242
};
243
this._telemetryService.publicLog2<ExtensionHostCrashEvent, ExtensionHostCrashClassification>('extensionHostCrash', {
244
code,
245
signal,
246
extensionIds: activatedExtensions.map(e => e.value)
247
});
248
249
for (const extensionId of activatedExtensions) {
250
type ExtensionHostCrashExtensionClassification = {
251
owner: 'alexdima';
252
comment: 'The extension host has terminated unexpectedly';
253
code: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The exit code of the extension host process.' };
254
signal: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The signal that caused the extension host process to exit.' };
255
extensionId: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The identifier of the extension.' };
256
};
257
type ExtensionHostCrashExtensionEvent = {
258
code: number;
259
signal: string | null;
260
extensionId: string;
261
};
262
this._telemetryService.publicLog2<ExtensionHostCrashExtensionEvent, ExtensionHostCrashExtensionClassification>('extensionHostCrashExtension', {
263
code,
264
signal,
265
extensionId: extensionId.value
266
});
267
}
268
}
269
270
// --- impl
271
272
protected async _resolveAuthority(remoteAuthority: string): Promise<ResolverResult> {
273
274
const authorityPlusIndex = remoteAuthority.indexOf('+');
275
if (authorityPlusIndex === -1) {
276
// This authority does not need to be resolved, simply parse the port number
277
const { host, port } = parseAuthorityWithPort(remoteAuthority);
278
return {
279
authority: {
280
authority: remoteAuthority,
281
connectTo: {
282
type: RemoteConnectionType.WebSocket,
283
host,
284
port
285
},
286
connectionToken: undefined
287
}
288
};
289
}
290
291
return this._resolveAuthorityOnExtensionHosts(ExtensionHostKind.LocalProcess, remoteAuthority);
292
}
293
294
private async _getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI> {
295
296
const authorityPlusIndex = remoteAuthority.indexOf('+');
297
if (authorityPlusIndex === -1) {
298
// This authority does not use a resolver
299
return uri;
300
}
301
302
const localProcessExtensionHosts = this._getExtensionHostManagers(ExtensionHostKind.LocalProcess);
303
if (localProcessExtensionHosts.length === 0) {
304
// no local process extension hosts
305
throw new Error(`Cannot resolve canonical URI`);
306
}
307
308
const results = await Promise.all(localProcessExtensionHosts.map(extHost => extHost.getCanonicalURI(remoteAuthority, uri)));
309
310
for (const result of results) {
311
if (result) {
312
return result;
313
}
314
}
315
316
// we can only reach this if there was no resolver extension that can return the cannonical uri
317
throw new Error(`Cannot get canonical URI because no extension is installed to resolve ${getRemoteAuthorityPrefix(remoteAuthority)}`);
318
}
319
320
protected _resolveExtensions(): AsyncIterable<ResolvedExtensions> {
321
return new AsyncIterableObject(emitter => this._doResolveExtensions(emitter));
322
}
323
324
private async _doResolveExtensions(emitter: AsyncIterableEmitter<ResolvedExtensions>): Promise<void> {
325
this._extensionScanner.startScanningExtensions();
326
327
const remoteAuthority = this._environmentService.remoteAuthority;
328
329
let remoteEnv: IRemoteAgentEnvironment | null = null;
330
let remoteExtensions: IExtensionDescription[] = [];
331
332
if (remoteAuthority) {
333
334
this._remoteAuthorityResolverService._setCanonicalURIProvider(async (uri) => {
335
if (uri.scheme !== Schemas.vscodeRemote || uri.authority !== remoteAuthority) {
336
// The current remote authority resolver cannot give the canonical URI for this URI
337
return uri;
338
}
339
performance.mark(`code/willGetCanonicalURI/${getRemoteAuthorityPrefix(remoteAuthority)}`);
340
if (isCI) {
341
this._logService.info(`Invoking getCanonicalURI for authority ${getRemoteAuthorityPrefix(remoteAuthority)}...`);
342
}
343
try {
344
return this._getCanonicalURI(remoteAuthority, uri);
345
} finally {
346
performance.mark(`code/didGetCanonicalURI/${getRemoteAuthorityPrefix(remoteAuthority)}`);
347
if (isCI) {
348
this._logService.info(`getCanonicalURI returned for authority ${getRemoteAuthorityPrefix(remoteAuthority)}.`);
349
}
350
}
351
});
352
353
if (isCI) {
354
this._logService.info(`Starting to wait on IWorkspaceTrustManagementService.workspaceResolved...`);
355
}
356
357
// Now that the canonical URI provider has been registered, we need to wait for the trust state to be
358
// calculated. The trust state will be used while resolving the authority, however the resolver can
359
// override the trust state through the resolver result.
360
await this._workspaceTrustManagementService.workspaceResolved;
361
362
if (isCI) {
363
this._logService.info(`Finished waiting on IWorkspaceTrustManagementService.workspaceResolved.`);
364
}
365
366
const localExtensions = await this._scanAllLocalExtensions();
367
const resolverExtensions = localExtensions.filter(extension => isResolverExtension(extension));
368
if (resolverExtensions.length) {
369
emitter.emitOne(new ResolverExtensions(resolverExtensions));
370
}
371
372
let resolverResult: ResolverResult;
373
try {
374
resolverResult = await this._resolveAuthorityInitial(remoteAuthority);
375
} catch (err) {
376
if (RemoteAuthorityResolverError.isNoResolverFound(err)) {
377
err.isHandled = await this._handleNoResolverFound(remoteAuthority);
378
} else {
379
if (RemoteAuthorityResolverError.isHandled(err)) {
380
console.log(`Error handled: Not showing a notification for the error`);
381
}
382
}
383
this._remoteAuthorityResolverService._setResolvedAuthorityError(remoteAuthority, err);
384
385
// Proceed with the local extension host
386
return this._startLocalExtensionHost(emitter);
387
}
388
389
// set the resolved authority
390
this._remoteAuthorityResolverService._setResolvedAuthority(resolverResult.authority, resolverResult.options);
391
this._remoteExplorerService.setTunnelInformation(resolverResult.tunnelInformation);
392
393
// monitor for breakage
394
const connection = this._remoteAgentService.getConnection();
395
if (connection) {
396
this._register(connection.onDidStateChange(async (e) => {
397
if (e.type === PersistentConnectionEventType.ConnectionLost) {
398
this._remoteAuthorityResolverService._clearResolvedAuthority(remoteAuthority);
399
}
400
}));
401
this._register(connection.onReconnecting(() => this._resolveAuthorityAgain()));
402
}
403
404
// fetch the remote environment
405
[remoteEnv, remoteExtensions] = await Promise.all([
406
this._remoteAgentService.getEnvironment(),
407
this._remoteExtensionsScannerService.scanExtensions()
408
]);
409
410
if (!remoteEnv) {
411
this._notificationService.notify({ severity: Severity.Error, message: nls.localize('getEnvironmentFailure', "Could not fetch remote environment") });
412
// Proceed with the local extension host
413
return this._startLocalExtensionHost(emitter);
414
}
415
416
const useHostProxyDefault = remoteEnv.useHostProxy;
417
this._register(this._configurationService.onDidChangeConfiguration(e => {
418
if (e.affectsConfiguration('http.useLocalProxyConfiguration')) {
419
updateProxyConfigurationsScope(this._configurationService.getValue('http.useLocalProxyConfiguration'), useHostProxyDefault);
420
}
421
}));
422
updateProxyConfigurationsScope(this._configurationService.getValue('http.useLocalProxyConfiguration'), useHostProxyDefault);
423
} else {
424
425
this._remoteAuthorityResolverService._setCanonicalURIProvider(async (uri) => uri);
426
427
}
428
429
return this._startLocalExtensionHost(emitter, remoteExtensions);
430
}
431
432
private async _startLocalExtensionHost(emitter: AsyncIterableEmitter<ResolvedExtensions>, remoteExtensions: IExtensionDescription[] = []): Promise<void> {
433
// Ensure that the workspace trust state has been fully initialized so
434
// that the extension host can start with the correct set of extensions.
435
await this._workspaceTrustManagementService.workspaceTrustInitialized;
436
437
if (remoteExtensions.length) {
438
emitter.emitOne(new RemoteExtensions(remoteExtensions));
439
}
440
441
emitter.emitOne(new LocalExtensions(await this._scanAllLocalExtensions()));
442
}
443
444
protected async _onExtensionHostExit(code: number): Promise<void> {
445
// Dispose everything associated with the extension host
446
await this._doStopExtensionHosts();
447
448
// Dispose the management connection to avoid reconnecting after the extension host exits
449
const connection = this._remoteAgentService.getConnection();
450
connection?.dispose();
451
452
if (parseExtensionDevOptions(this._environmentService).isExtensionDevTestFromCli) {
453
// When CLI testing make sure to exit with proper exit code
454
if (isCI) {
455
this._logService.info(`Asking native host service to exit with code ${code}.`);
456
}
457
this._nativeHostService.exit(code);
458
} else {
459
// Expected development extension termination: When the extension host goes down we also shutdown the window
460
this._nativeHostService.closeWindow();
461
}
462
}
463
464
private async _handleNoResolverFound(remoteAuthority: string): Promise<boolean> {
465
const remoteName = getRemoteName(remoteAuthority);
466
const recommendation = this._productService.remoteExtensionTips?.[remoteName];
467
if (!recommendation) {
468
return false;
469
}
470
471
const resolverExtensionId = recommendation.extensionId;
472
const allExtensions = await this._scanAllLocalExtensions();
473
const extension = allExtensions.filter(e => e.identifier.value === resolverExtensionId)[0];
474
if (extension) {
475
if (!extensionIsEnabled(this._logService, this._extensionEnablementService, extension, false)) {
476
const message = nls.localize('enableResolver', "Extension '{0}' is required to open the remote window.\nOK to enable?", recommendation.friendlyName);
477
this._notificationService.prompt(Severity.Info, message,
478
[{
479
label: nls.localize('enable', 'Enable and Reload'),
480
run: async () => {
481
await this._extensionEnablementService.setEnablement([toExtension(extension)], EnablementState.EnabledGlobally);
482
await this._hostService.reload();
483
}
484
}],
485
{
486
sticky: true,
487
priority: NotificationPriority.URGENT
488
}
489
);
490
}
491
} else {
492
// Install the Extension and reload the window to handle.
493
const message = nls.localize('installResolver', "Extension '{0}' is required to open the remote window.\nDo you want to install the extension?", recommendation.friendlyName);
494
this._notificationService.prompt(Severity.Info, message,
495
[{
496
label: nls.localize('install', 'Install and Reload'),
497
run: async () => {
498
const [galleryExtension] = await this._extensionGalleryService.getExtensions([{ id: resolverExtensionId }], CancellationToken.None);
499
if (galleryExtension) {
500
await this._extensionManagementService.installFromGallery(galleryExtension);
501
await this._hostService.reload();
502
} else {
503
this._notificationService.error(nls.localize('resolverExtensionNotFound', "`{0}` not found on marketplace"));
504
}
505
506
}
507
}],
508
{
509
sticky: true,
510
priority: NotificationPriority.URGENT,
511
}
512
);
513
514
}
515
return true;
516
}
517
}
518
519
class NativeExtensionHostFactory implements IExtensionHostFactory {
520
521
private readonly _webWorkerExtHostEnablement: LocalWebWorkerExtHostEnablement;
522
523
constructor(
524
private readonly _extensionsProposedApi: ExtensionsProposedApi,
525
private readonly _extensionScanner: CachedExtensionScanner,
526
private readonly _getExtensionRegistrySnapshotWhenReady: () => Promise<ExtensionDescriptionRegistrySnapshot>,
527
@IInstantiationService private readonly _instantiationService: IInstantiationService,
528
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
529
@IWorkbenchExtensionEnablementService private readonly _extensionEnablementService: IWorkbenchExtensionEnablementService,
530
@IConfigurationService configurationService: IConfigurationService,
531
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
532
@IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService,
533
@ILogService private readonly _logService: ILogService,
534
) {
535
this._webWorkerExtHostEnablement = determineLocalWebWorkerExtHostEnablement(environmentService, configurationService);
536
}
537
538
public createExtensionHost(runningLocations: ExtensionRunningLocationTracker, runningLocation: ExtensionRunningLocation, isInitialStart: boolean): IExtensionHost | null {
539
switch (runningLocation.kind) {
540
case ExtensionHostKind.LocalProcess: {
541
const startup = (
542
isInitialStart
543
? ExtensionHostStartup.EagerManualStart
544
: ExtensionHostStartup.EagerAutoStart
545
);
546
return this._instantiationService.createInstance(NativeLocalProcessExtensionHost, runningLocation, startup, this._createLocalProcessExtensionHostDataProvider(runningLocations, isInitialStart, runningLocation));
547
}
548
case ExtensionHostKind.LocalWebWorker: {
549
if (this._webWorkerExtHostEnablement !== LocalWebWorkerExtHostEnablement.Disabled) {
550
const startup = this._webWorkerExtHostEnablement === LocalWebWorkerExtHostEnablement.Lazy ? ExtensionHostStartup.LazyAutoStart : ExtensionHostStartup.EagerManualStart;
551
return this._instantiationService.createInstance(WebWorkerExtensionHost, runningLocation, startup, this._createWebWorkerExtensionHostDataProvider(runningLocations, runningLocation));
552
}
553
return null;
554
}
555
case ExtensionHostKind.Remote: {
556
const remoteAgentConnection = this._remoteAgentService.getConnection();
557
if (remoteAgentConnection) {
558
return this._instantiationService.createInstance(RemoteExtensionHost, runningLocation, this._createRemoteExtensionHostDataProvider(runningLocations, remoteAgentConnection.remoteAuthority));
559
}
560
return null;
561
}
562
}
563
}
564
565
private _createLocalProcessExtensionHostDataProvider(runningLocations: ExtensionRunningLocationTracker, isInitialStart: boolean, desiredRunningLocation: LocalProcessRunningLocation): ILocalProcessExtensionHostDataProvider {
566
return {
567
getInitData: async (): Promise<ILocalProcessExtensionHostInitData> => {
568
if (isInitialStart) {
569
// Here we load even extensions that would be disabled by workspace trust
570
const scannedExtensions = await this._extensionScanner.scannedExtensions;
571
if (isCI) {
572
this._logService.info(`NativeExtensionHostFactory._createLocalProcessExtensionHostDataProvider.scannedExtensions: ${scannedExtensions.map(ext => ext.identifier.value).join(',')}`);
573
}
574
575
const localExtensions = checkEnabledAndProposedAPI(this._logService, this._extensionEnablementService, this._extensionsProposedApi, scannedExtensions, /* ignore workspace trust */true);
576
if (isCI) {
577
this._logService.info(`NativeExtensionHostFactory._createLocalProcessExtensionHostDataProvider.localExtensions: ${localExtensions.map(ext => ext.identifier.value).join(',')}`);
578
}
579
580
const runningLocation = runningLocations.computeRunningLocation(localExtensions, [], false);
581
const myExtensions = filterExtensionDescriptions(localExtensions, runningLocation, extRunningLocation => desiredRunningLocation.equals(extRunningLocation));
582
const extensions = new ExtensionHostExtensions(0, localExtensions, myExtensions.map(extension => extension.identifier));
583
if (isCI) {
584
this._logService.info(`NativeExtensionHostFactory._createLocalProcessExtensionHostDataProvider.myExtensions: ${myExtensions.map(ext => ext.identifier.value).join(',')}`);
585
}
586
return { extensions };
587
} else {
588
// restart case
589
const snapshot = await this._getExtensionRegistrySnapshotWhenReady();
590
const myExtensions = runningLocations.filterByRunningLocation(snapshot.extensions, desiredRunningLocation);
591
const extensions = new ExtensionHostExtensions(snapshot.versionId, snapshot.extensions, myExtensions.map(extension => extension.identifier));
592
return { extensions };
593
}
594
}
595
};
596
}
597
598
private _createWebWorkerExtensionHostDataProvider(runningLocations: ExtensionRunningLocationTracker, desiredRunningLocation: LocalWebWorkerRunningLocation): IWebWorkerExtensionHostDataProvider {
599
return {
600
getInitData: async (): Promise<IWebWorkerExtensionHostInitData> => {
601
const snapshot = await this._getExtensionRegistrySnapshotWhenReady();
602
const myExtensions = runningLocations.filterByRunningLocation(snapshot.extensions, desiredRunningLocation);
603
const extensions = new ExtensionHostExtensions(snapshot.versionId, snapshot.extensions, myExtensions.map(extension => extension.identifier));
604
return { extensions };
605
}
606
};
607
}
608
609
private _createRemoteExtensionHostDataProvider(runningLocations: ExtensionRunningLocationTracker, remoteAuthority: string): IRemoteExtensionHostDataProvider {
610
return {
611
remoteAuthority: remoteAuthority,
612
getInitData: async (): Promise<IRemoteExtensionHostInitData> => {
613
const snapshot = await this._getExtensionRegistrySnapshotWhenReady();
614
615
const remoteEnv = await this._remoteAgentService.getEnvironment();
616
if (!remoteEnv) {
617
throw new Error('Cannot provide init data for remote extension host!');
618
}
619
620
const myExtensions = runningLocations.filterByExtensionHostKind(snapshot.extensions, ExtensionHostKind.Remote);
621
const extensions = new ExtensionHostExtensions(snapshot.versionId, snapshot.extensions, myExtensions.map(extension => extension.identifier));
622
623
return {
624
connectionData: this._remoteAuthorityResolverService.getConnectionData(remoteAuthority),
625
pid: remoteEnv.pid,
626
appRoot: remoteEnv.appRoot,
627
extensionHostLogsPath: remoteEnv.extensionHostLogsPath,
628
globalStorageHome: remoteEnv.globalStorageHome,
629
workspaceStorageHome: remoteEnv.workspaceStorageHome,
630
extensions,
631
};
632
}
633
};
634
}
635
}
636
637
function determineLocalWebWorkerExtHostEnablement(environmentService: IWorkbenchEnvironmentService, configurationService: IConfigurationService): LocalWebWorkerExtHostEnablement {
638
if (environmentService.isExtensionDevelopment && environmentService.extensionDevelopmentKind?.some(k => k === 'web')) {
639
return LocalWebWorkerExtHostEnablement.Eager;
640
} else {
641
const config = configurationService.getValue<WebWorkerExtHostConfigValue>(webWorkerExtHostConfig);
642
if (config === true) {
643
return LocalWebWorkerExtHostEnablement.Eager;
644
} else if (config === 'auto') {
645
return LocalWebWorkerExtHostEnablement.Lazy;
646
} else {
647
return LocalWebWorkerExtHostEnablement.Disabled;
648
}
649
}
650
}
651
652
const enum LocalWebWorkerExtHostEnablement {
653
Disabled = 0,
654
Eager = 1,
655
Lazy = 2
656
}
657
658
export class NativeExtensionHostKindPicker implements IExtensionHostKindPicker {
659
660
private readonly _hasRemoteExtHost: boolean;
661
private readonly _hasWebWorkerExtHost: boolean;
662
663
constructor(
664
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
665
@IConfigurationService configurationService: IConfigurationService,
666
@ILogService private readonly _logService: ILogService,
667
) {
668
this._hasRemoteExtHost = Boolean(environmentService.remoteAuthority);
669
const webWorkerExtHostEnablement = determineLocalWebWorkerExtHostEnablement(environmentService, configurationService);
670
this._hasWebWorkerExtHost = (webWorkerExtHostEnablement !== LocalWebWorkerExtHostEnablement.Disabled);
671
}
672
673
public pickExtensionHostKind(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null {
674
const result = NativeExtensionHostKindPicker.pickExtensionHostKind(extensionKinds, isInstalledLocally, isInstalledRemotely, preference, this._hasRemoteExtHost, this._hasWebWorkerExtHost);
675
this._logService.trace(`pickRunningLocation for ${extensionId.value}, extension kinds: [${extensionKinds.join(', ')}], isInstalledLocally: ${isInstalledLocally}, isInstalledRemotely: ${isInstalledRemotely}, preference: ${extensionRunningPreferenceToString(preference)} => ${extensionHostKindToString(result)}`);
676
return result;
677
}
678
679
public static pickExtensionHostKind(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference, hasRemoteExtHost: boolean, hasWebWorkerExtHost: boolean): ExtensionHostKind | null {
680
const result: ExtensionHostKind[] = [];
681
for (const extensionKind of extensionKinds) {
682
if (extensionKind === 'ui' && isInstalledLocally) {
683
// ui extensions run locally if possible
684
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
685
return ExtensionHostKind.LocalProcess;
686
} else {
687
result.push(ExtensionHostKind.LocalProcess);
688
}
689
}
690
if (extensionKind === 'workspace' && isInstalledRemotely) {
691
// workspace extensions run remotely if possible
692
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Remote) {
693
return ExtensionHostKind.Remote;
694
} else {
695
result.push(ExtensionHostKind.Remote);
696
}
697
}
698
if (extensionKind === 'workspace' && !hasRemoteExtHost) {
699
// workspace extensions also run locally if there is no remote
700
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
701
return ExtensionHostKind.LocalProcess;
702
} else {
703
result.push(ExtensionHostKind.LocalProcess);
704
}
705
}
706
if (extensionKind === 'web' && isInstalledLocally && hasWebWorkerExtHost) {
707
// web worker extensions run in the local web worker if possible
708
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
709
return ExtensionHostKind.LocalWebWorker;
710
} else {
711
result.push(ExtensionHostKind.LocalWebWorker);
712
}
713
}
714
}
715
return (result.length > 0 ? result[0] : null);
716
}
717
}
718
719
class RestartExtensionHostAction extends Action2 {
720
721
constructor() {
722
super({
723
id: 'workbench.action.restartExtensionHost',
724
title: nls.localize2('restartExtensionHost', "Restart Extension Host"),
725
category: Categories.Developer,
726
f1: true
727
});
728
}
729
730
async run(accessor: ServicesAccessor): Promise<void> {
731
const extensionService = accessor.get(IExtensionService);
732
733
const stopped = await extensionService.stopExtensionHosts(nls.localize('restartExtensionHost.reason', "An explicit request"));
734
if (stopped) {
735
extensionService.startExtensionHosts();
736
}
737
}
738
}
739
740
registerAction2(RestartExtensionHostAction);
741
742
registerSingleton(IExtensionService, NativeExtensionService, InstantiationType.Eager);
743
744