Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/services/extensions/browser/extensionService.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 { mainWindow } from '../../../../base/browser/window.js';
7
import { Schemas } from '../../../../base/common/network.js';
8
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
9
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js';
10
import { ExtensionKind } from '../../../../platform/environment/common/environment.js';
11
import { ExtensionIdentifier, IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';
12
import { IFileService } from '../../../../platform/files/common/files.js';
13
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
14
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
15
import { IAutomatedWindow, getLogs } from '../../../../platform/log/browser/log.js';
16
import { ILogService } from '../../../../platform/log/common/log.js';
17
import { INotificationService } from '../../../../platform/notification/common/notification.js';
18
import { IProductService } from '../../../../platform/product/common/productService.js';
19
import { PersistentConnectionEventType } from '../../../../platform/remote/common/remoteAgentConnection.js';
20
import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, ResolverResult } from '../../../../platform/remote/common/remoteAuthorityResolver.js';
21
import { IRemoteExtensionsScannerService } from '../../../../platform/remote/common/remoteExtensionsScanner.js';
22
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
23
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
24
import { IWorkspaceTrustManagementService } from '../../../../platform/workspace/common/workspaceTrust.js';
25
import { IBrowserWorkbenchEnvironmentService } from '../../environment/browser/environmentService.js';
26
import { IWebExtensionsScannerService, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from '../../extensionManagement/common/extensionManagement.js';
27
import { IWebWorkerExtensionHostDataProvider, IWebWorkerExtensionHostInitData, WebWorkerExtensionHost } from './webWorkerExtensionHost.js';
28
import { FetchFileSystemProvider } from './webWorkerFileSystemProvider.js';
29
import { AbstractExtensionService, IExtensionHostFactory, LocalExtensions, RemoteExtensions, ResolvedExtensions, ResolverExtensions, checkEnabledAndProposedAPI, isResolverExtension } from '../common/abstractExtensionService.js';
30
import { ExtensionDescriptionRegistrySnapshot } from '../common/extensionDescriptionRegistry.js';
31
import { ExtensionHostKind, ExtensionRunningPreference, IExtensionHostKindPicker, extensionHostKindToString, extensionRunningPreferenceToString } from '../common/extensionHostKind.js';
32
import { IExtensionManifestPropertiesService } from '../common/extensionManifestPropertiesService.js';
33
import { ExtensionRunningLocation } from '../common/extensionRunningLocation.js';
34
import { ExtensionRunningLocationTracker, filterExtensionDescriptions } from '../common/extensionRunningLocationTracker.js';
35
import { ExtensionHostExtensions, ExtensionHostStartup, IExtensionHost, IExtensionService, toExtensionDescription } from '../common/extensions.js';
36
import { ExtensionsProposedApi } from '../common/extensionsProposedApi.js';
37
import { dedupExtensions } from '../common/extensionsUtil.js';
38
import { IRemoteExtensionHostDataProvider, IRemoteExtensionHostInitData, RemoteExtensionHost } from '../common/remoteExtensionHost.js';
39
import { ILifecycleService, LifecyclePhase } from '../../lifecycle/common/lifecycle.js';
40
import { IRemoteAgentService } from '../../remote/common/remoteAgentService.js';
41
import { IRemoteExplorerService } from '../../remote/common/remoteExplorerService.js';
42
import { IUserDataInitializationService } from '../../userData/browser/userDataInit.js';
43
import { IUserDataProfileService } from '../../userDataProfile/common/userDataProfile.js';
44
import { AsyncIterableEmitter, AsyncIterableObject } from '../../../../base/common/async.js';
45
46
export class ExtensionService extends AbstractExtensionService implements IExtensionService {
47
48
constructor(
49
@IInstantiationService instantiationService: IInstantiationService,
50
@INotificationService notificationService: INotificationService,
51
@IBrowserWorkbenchEnvironmentService private readonly _browserEnvironmentService: IBrowserWorkbenchEnvironmentService,
52
@ITelemetryService telemetryService: ITelemetryService,
53
@IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService,
54
@IFileService fileService: IFileService,
55
@IProductService productService: IProductService,
56
@IWorkbenchExtensionManagementService extensionManagementService: IWorkbenchExtensionManagementService,
57
@IWorkspaceContextService contextService: IWorkspaceContextService,
58
@IConfigurationService configurationService: IConfigurationService,
59
@IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService,
60
@IWebExtensionsScannerService private readonly _webExtensionsScannerService: IWebExtensionsScannerService,
61
@ILogService logService: ILogService,
62
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
63
@IRemoteExtensionsScannerService remoteExtensionsScannerService: IRemoteExtensionsScannerService,
64
@ILifecycleService lifecycleService: ILifecycleService,
65
@IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService,
66
@IUserDataInitializationService private readonly _userDataInitializationService: IUserDataInitializationService,
67
@IUserDataProfileService private readonly _userDataProfileService: IUserDataProfileService,
68
@IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService,
69
@IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService,
70
@IDialogService dialogService: IDialogService,
71
) {
72
const extensionsProposedApi = instantiationService.createInstance(ExtensionsProposedApi);
73
const extensionHostFactory = new BrowserExtensionHostFactory(
74
extensionsProposedApi,
75
() => this._scanWebExtensions(),
76
() => this._getExtensionRegistrySnapshotWhenReady(),
77
instantiationService,
78
remoteAgentService,
79
remoteAuthorityResolverService,
80
extensionEnablementService,
81
logService
82
);
83
super(
84
{ hasLocalProcess: false, allowRemoteExtensionsInLocalWebWorker: true },
85
extensionsProposedApi,
86
extensionHostFactory,
87
new BrowserExtensionHostKindPicker(logService),
88
instantiationService,
89
notificationService,
90
_browserEnvironmentService,
91
telemetryService,
92
extensionEnablementService,
93
fileService,
94
productService,
95
extensionManagementService,
96
contextService,
97
configurationService,
98
extensionManifestPropertiesService,
99
logService,
100
remoteAgentService,
101
remoteExtensionsScannerService,
102
lifecycleService,
103
remoteAuthorityResolverService,
104
dialogService
105
);
106
107
// Initialize installed extensions first and do it only after workbench is ready
108
lifecycleService.when(LifecyclePhase.Ready).then(async () => {
109
await this._userDataInitializationService.initializeInstalledExtensions(this._instantiationService);
110
this._initialize();
111
});
112
113
this._initFetchFileSystem();
114
}
115
116
private _initFetchFileSystem(): void {
117
const provider = new FetchFileSystemProvider();
118
this._register(this._fileService.registerProvider(Schemas.http, provider));
119
this._register(this._fileService.registerProvider(Schemas.https, provider));
120
}
121
122
private _scanWebExtensionsPromise: Promise<IExtensionDescription[]> | undefined;
123
private async _scanWebExtensions(): Promise<IExtensionDescription[]> {
124
if (!this._scanWebExtensionsPromise) {
125
this._scanWebExtensionsPromise = (async () => {
126
const system: IExtensionDescription[] = [], user: IExtensionDescription[] = [], development: IExtensionDescription[] = [];
127
try {
128
await Promise.all([
129
this._webExtensionsScannerService.scanSystemExtensions().then(extensions => system.push(...extensions.map(e => toExtensionDescription(e)))),
130
this._webExtensionsScannerService.scanUserExtensions(this._userDataProfileService.currentProfile.extensionsResource, { skipInvalidExtensions: true }).then(extensions => user.push(...extensions.map(e => toExtensionDescription(e)))),
131
this._webExtensionsScannerService.scanExtensionsUnderDevelopment().then(extensions => development.push(...extensions.map(e => toExtensionDescription(e, true))))
132
]);
133
} catch (error) {
134
this._logService.error(error);
135
}
136
return dedupExtensions(system, user, [], development, this._logService);
137
})();
138
}
139
return this._scanWebExtensionsPromise;
140
}
141
142
private async _resolveExtensionsDefault(emitter: AsyncIterableEmitter<ResolvedExtensions>) {
143
const [localExtensions, remoteExtensions] = await Promise.all([
144
this._scanWebExtensions(),
145
this._remoteExtensionsScannerService.scanExtensions()
146
]);
147
148
if (remoteExtensions.length) {
149
emitter.emitOne(new RemoteExtensions(remoteExtensions));
150
}
151
emitter.emitOne(new LocalExtensions(localExtensions));
152
}
153
154
protected _resolveExtensions(): AsyncIterable<ResolvedExtensions> {
155
return new AsyncIterableObject(emitter => this._doResolveExtensions(emitter));
156
}
157
158
private async _doResolveExtensions(emitter: AsyncIterableEmitter<ResolvedExtensions>): Promise<void> {
159
if (!this._browserEnvironmentService.expectsResolverExtension) {
160
return this._resolveExtensionsDefault(emitter);
161
}
162
163
const remoteAuthority = this._environmentService.remoteAuthority!;
164
165
// Now that the canonical URI provider has been registered, we need to wait for the trust state to be
166
// calculated. The trust state will be used while resolving the authority, however the resolver can
167
// override the trust state through the resolver result.
168
await this._workspaceTrustManagementService.workspaceResolved;
169
170
const localExtensions = await this._scanWebExtensions();
171
const resolverExtensions = localExtensions.filter(extension => isResolverExtension(extension));
172
if (resolverExtensions.length) {
173
emitter.emitOne(new ResolverExtensions(resolverExtensions));
174
}
175
176
let resolverResult: ResolverResult;
177
try {
178
resolverResult = await this._resolveAuthorityInitial(remoteAuthority);
179
} catch (err) {
180
if (RemoteAuthorityResolverError.isHandled(err)) {
181
console.log(`Error handled: Not showing a notification for the error`);
182
}
183
this._remoteAuthorityResolverService._setResolvedAuthorityError(remoteAuthority, err);
184
185
// Proceed with the local extension host
186
return this._resolveExtensionsDefault(emitter);
187
}
188
189
// set the resolved authority
190
this._remoteAuthorityResolverService._setResolvedAuthority(resolverResult.authority, resolverResult.options);
191
this._remoteExplorerService.setTunnelInformation(resolverResult.tunnelInformation);
192
193
// monitor for breakage
194
const connection = this._remoteAgentService.getConnection();
195
if (connection) {
196
connection.onDidStateChange(async (e) => {
197
if (e.type === PersistentConnectionEventType.ConnectionLost) {
198
this._remoteAuthorityResolverService._clearResolvedAuthority(remoteAuthority);
199
}
200
});
201
connection.onReconnecting(() => this._resolveAuthorityAgain());
202
}
203
204
return this._resolveExtensionsDefault(emitter);
205
}
206
207
protected async _onExtensionHostExit(code: number): Promise<void> {
208
// Dispose everything associated with the extension host
209
await this._doStopExtensionHosts();
210
211
// If we are running extension tests, forward logs and exit code
212
const automatedWindow = mainWindow as unknown as IAutomatedWindow;
213
if (typeof automatedWindow.codeAutomationExit === 'function') {
214
automatedWindow.codeAutomationExit(code, await getLogs(this._fileService, this._environmentService));
215
}
216
}
217
218
protected async _resolveAuthority(remoteAuthority: string): Promise<ResolverResult> {
219
return this._resolveAuthorityOnExtensionHosts(ExtensionHostKind.LocalWebWorker, remoteAuthority);
220
}
221
}
222
223
class BrowserExtensionHostFactory implements IExtensionHostFactory {
224
225
constructor(
226
private readonly _extensionsProposedApi: ExtensionsProposedApi,
227
private readonly _scanWebExtensions: () => Promise<IExtensionDescription[]>,
228
private readonly _getExtensionRegistrySnapshotWhenReady: () => Promise<ExtensionDescriptionRegistrySnapshot>,
229
@IInstantiationService private readonly _instantiationService: IInstantiationService,
230
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
231
@IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService,
232
@IWorkbenchExtensionEnablementService private readonly _extensionEnablementService: IWorkbenchExtensionEnablementService,
233
@ILogService private readonly _logService: ILogService,
234
) { }
235
236
createExtensionHost(runningLocations: ExtensionRunningLocationTracker, runningLocation: ExtensionRunningLocation, isInitialStart: boolean): IExtensionHost | null {
237
switch (runningLocation.kind) {
238
case ExtensionHostKind.LocalProcess: {
239
return null;
240
}
241
case ExtensionHostKind.LocalWebWorker: {
242
const startup = (
243
isInitialStart
244
? ExtensionHostStartup.EagerManualStart
245
: ExtensionHostStartup.EagerAutoStart
246
);
247
return this._instantiationService.createInstance(WebWorkerExtensionHost, runningLocation, startup, this._createLocalExtensionHostDataProvider(runningLocations, runningLocation, isInitialStart));
248
}
249
case ExtensionHostKind.Remote: {
250
const remoteAgentConnection = this._remoteAgentService.getConnection();
251
if (remoteAgentConnection) {
252
return this._instantiationService.createInstance(RemoteExtensionHost, runningLocation, this._createRemoteExtensionHostDataProvider(runningLocations, remoteAgentConnection.remoteAuthority));
253
}
254
return null;
255
}
256
}
257
}
258
259
private _createLocalExtensionHostDataProvider(runningLocations: ExtensionRunningLocationTracker, desiredRunningLocation: ExtensionRunningLocation, isInitialStart: boolean): IWebWorkerExtensionHostDataProvider {
260
return {
261
getInitData: async (): Promise<IWebWorkerExtensionHostInitData> => {
262
if (isInitialStart) {
263
// Here we load even extensions that would be disabled by workspace trust
264
const localExtensions = checkEnabledAndProposedAPI(this._logService, this._extensionEnablementService, this._extensionsProposedApi, await this._scanWebExtensions(), /* ignore workspace trust */true);
265
const runningLocation = runningLocations.computeRunningLocation(localExtensions, [], false);
266
const myExtensions = filterExtensionDescriptions(localExtensions, runningLocation, extRunningLocation => desiredRunningLocation.equals(extRunningLocation));
267
const extensions = new ExtensionHostExtensions(0, localExtensions, myExtensions.map(extension => extension.identifier));
268
return { extensions };
269
} else {
270
// restart case
271
const snapshot = await this._getExtensionRegistrySnapshotWhenReady();
272
const myExtensions = runningLocations.filterByRunningLocation(snapshot.extensions, desiredRunningLocation);
273
const extensions = new ExtensionHostExtensions(snapshot.versionId, snapshot.extensions, myExtensions.map(extension => extension.identifier));
274
return { extensions };
275
}
276
}
277
};
278
}
279
280
private _createRemoteExtensionHostDataProvider(runningLocations: ExtensionRunningLocationTracker, remoteAuthority: string): IRemoteExtensionHostDataProvider {
281
return {
282
remoteAuthority: remoteAuthority,
283
getInitData: async (): Promise<IRemoteExtensionHostInitData> => {
284
const snapshot = await this._getExtensionRegistrySnapshotWhenReady();
285
286
const remoteEnv = await this._remoteAgentService.getEnvironment();
287
if (!remoteEnv) {
288
throw new Error('Cannot provide init data for remote extension host!');
289
}
290
291
const myExtensions = runningLocations.filterByExtensionHostKind(snapshot.extensions, ExtensionHostKind.Remote);
292
const extensions = new ExtensionHostExtensions(snapshot.versionId, snapshot.extensions, myExtensions.map(extension => extension.identifier));
293
294
return {
295
connectionData: this._remoteAuthorityResolverService.getConnectionData(remoteAuthority),
296
pid: remoteEnv.pid,
297
appRoot: remoteEnv.appRoot,
298
extensionHostLogsPath: remoteEnv.extensionHostLogsPath,
299
globalStorageHome: remoteEnv.globalStorageHome,
300
workspaceStorageHome: remoteEnv.workspaceStorageHome,
301
extensions,
302
};
303
}
304
};
305
}
306
}
307
308
export class BrowserExtensionHostKindPicker implements IExtensionHostKindPicker {
309
310
constructor(
311
@ILogService private readonly _logService: ILogService,
312
) { }
313
314
pickExtensionHostKind(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null {
315
const result = BrowserExtensionHostKindPicker.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely, preference);
316
this._logService.trace(`pickRunningLocation for ${extensionId.value}, extension kinds: [${extensionKinds.join(', ')}], isInstalledLocally: ${isInstalledLocally}, isInstalledRemotely: ${isInstalledRemotely}, preference: ${extensionRunningPreferenceToString(preference)} => ${extensionHostKindToString(result)}`);
317
return result;
318
}
319
320
public static pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null {
321
const result: ExtensionHostKind[] = [];
322
let canRunRemotely = false;
323
for (const extensionKind of extensionKinds) {
324
if (extensionKind === 'ui' && isInstalledRemotely) {
325
// ui extensions run remotely if possible (but only as a last resort)
326
if (preference === ExtensionRunningPreference.Remote) {
327
return ExtensionHostKind.Remote;
328
} else {
329
canRunRemotely = true;
330
}
331
}
332
if (extensionKind === 'workspace' && isInstalledRemotely) {
333
// workspace extensions run remotely if possible
334
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Remote) {
335
return ExtensionHostKind.Remote;
336
} else {
337
result.push(ExtensionHostKind.Remote);
338
}
339
}
340
if (extensionKind === 'web' && (isInstalledLocally || isInstalledRemotely)) {
341
// web worker extensions run in the local web worker if possible
342
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
343
return ExtensionHostKind.LocalWebWorker;
344
} else {
345
result.push(ExtensionHostKind.LocalWebWorker);
346
}
347
}
348
}
349
if (canRunRemotely) {
350
result.push(ExtensionHostKind.Remote);
351
}
352
return (result.length > 0 ? result[0] : null);
353
}
354
}
355
356
registerSingleton(IExtensionService, ExtensionService, InstantiationType.Eager);
357
358