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
5251 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, AsyncIterableProducer } 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._initializeIfNeeded();
110
});
111
112
this._initFetchFileSystem();
113
}
114
115
private _initFetchFileSystem(): void {
116
const provider = new FetchFileSystemProvider();
117
this._register(this._fileService.registerProvider(Schemas.http, provider));
118
this._register(this._fileService.registerProvider(Schemas.https, provider));
119
}
120
121
protected override async _initialize(): Promise<void> {
122
await this._userDataInitializationService.initializeInstalledExtensions(this._instantiationService);
123
await super._initialize();
124
}
125
126
private _scanWebExtensionsPromise: Promise<IExtensionDescription[]> | undefined;
127
private async _scanWebExtensions(): Promise<IExtensionDescription[]> {
128
if (!this._scanWebExtensionsPromise) {
129
this._scanWebExtensionsPromise = (async () => {
130
const system: IExtensionDescription[] = [], user: IExtensionDescription[] = [], development: IExtensionDescription[] = [];
131
try {
132
await Promise.all([
133
this._webExtensionsScannerService.scanSystemExtensions().then(extensions => system.push(...extensions.map(e => toExtensionDescription(e)))),
134
this._webExtensionsScannerService.scanUserExtensions(this._userDataProfileService.currentProfile.extensionsResource, { skipInvalidExtensions: true }).then(extensions => user.push(...extensions.map(e => toExtensionDescription(e)))),
135
this._webExtensionsScannerService.scanExtensionsUnderDevelopment().then(extensions => development.push(...extensions.map(e => toExtensionDescription(e, true))))
136
]);
137
} catch (error) {
138
this._logService.error(error);
139
}
140
return dedupExtensions(system, user, [], development, this._logService);
141
})();
142
}
143
return this._scanWebExtensionsPromise;
144
}
145
146
private async _resolveExtensionsDefault(emitter: AsyncIterableEmitter<ResolvedExtensions>) {
147
const [localExtensions, remoteExtensions] = await Promise.all([
148
this._scanWebExtensions(),
149
this._remoteExtensionsScannerService.scanExtensions()
150
]);
151
152
if (remoteExtensions.length) {
153
emitter.emitOne(new RemoteExtensions(remoteExtensions));
154
}
155
emitter.emitOne(new LocalExtensions(localExtensions));
156
}
157
158
protected _resolveExtensions(): AsyncIterable<ResolvedExtensions> {
159
return new AsyncIterableProducer(emitter => this._doResolveExtensions(emitter));
160
}
161
162
private async _doResolveExtensions(emitter: AsyncIterableEmitter<ResolvedExtensions>): Promise<void> {
163
if (!this._browserEnvironmentService.expectsResolverExtension) {
164
return this._resolveExtensionsDefault(emitter);
165
}
166
167
const remoteAuthority = this._environmentService.remoteAuthority!;
168
169
// Now that the canonical URI provider has been registered, we need to wait for the trust state to be
170
// calculated. The trust state will be used while resolving the authority, however the resolver can
171
// override the trust state through the resolver result.
172
await this._workspaceTrustManagementService.workspaceResolved;
173
174
const localExtensions = await this._scanWebExtensions();
175
const resolverExtensions = localExtensions.filter(extension => isResolverExtension(extension));
176
if (resolverExtensions.length) {
177
emitter.emitOne(new ResolverExtensions(resolverExtensions));
178
}
179
180
let resolverResult: ResolverResult;
181
try {
182
resolverResult = await this._resolveAuthorityInitial(remoteAuthority);
183
} catch (err) {
184
if (RemoteAuthorityResolverError.isHandled(err)) {
185
console.log(`Error handled: Not showing a notification for the error`);
186
}
187
this._remoteAuthorityResolverService._setResolvedAuthorityError(remoteAuthority, err);
188
189
// Proceed with the local extension host
190
return this._resolveExtensionsDefault(emitter);
191
}
192
193
// set the resolved authority
194
this._remoteAuthorityResolverService._setResolvedAuthority(resolverResult.authority, resolverResult.options);
195
this._remoteExplorerService.setTunnelInformation(resolverResult.tunnelInformation);
196
197
// monitor for breakage
198
const connection = this._remoteAgentService.getConnection();
199
if (connection) {
200
this._register(connection.onDidStateChange(async (e) => {
201
if (e.type === PersistentConnectionEventType.ConnectionLost) {
202
this._remoteAuthorityResolverService._clearResolvedAuthority(remoteAuthority);
203
}
204
}));
205
this._register(connection.onReconnecting(() => this._resolveAuthorityAgain()));
206
}
207
208
return this._resolveExtensionsDefault(emitter);
209
}
210
211
protected async _onExtensionHostExit(code: number): Promise<void> {
212
// Dispose everything associated with the extension host
213
await this._doStopExtensionHosts();
214
215
// If we are running extension tests, forward logs and exit code
216
const automatedWindow = mainWindow as unknown as IAutomatedWindow;
217
if (typeof automatedWindow.codeAutomationExit === 'function') {
218
automatedWindow.codeAutomationExit(code, await getLogs(this._fileService, this._environmentService));
219
}
220
}
221
222
protected async _resolveAuthority(remoteAuthority: string): Promise<ResolverResult> {
223
return this._resolveAuthorityOnExtensionHosts(ExtensionHostKind.LocalWebWorker, remoteAuthority);
224
}
225
}
226
227
class BrowserExtensionHostFactory implements IExtensionHostFactory {
228
229
constructor(
230
private readonly _extensionsProposedApi: ExtensionsProposedApi,
231
private readonly _scanWebExtensions: () => Promise<IExtensionDescription[]>,
232
private readonly _getExtensionRegistrySnapshotWhenReady: () => Promise<ExtensionDescriptionRegistrySnapshot>,
233
@IInstantiationService private readonly _instantiationService: IInstantiationService,
234
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
235
@IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService,
236
@IWorkbenchExtensionEnablementService private readonly _extensionEnablementService: IWorkbenchExtensionEnablementService,
237
@ILogService private readonly _logService: ILogService,
238
) { }
239
240
createExtensionHost(runningLocations: ExtensionRunningLocationTracker, runningLocation: ExtensionRunningLocation, isInitialStart: boolean): IExtensionHost | null {
241
switch (runningLocation.kind) {
242
case ExtensionHostKind.LocalProcess: {
243
return null;
244
}
245
case ExtensionHostKind.LocalWebWorker: {
246
const startup = (
247
isInitialStart
248
? ExtensionHostStartup.EagerManualStart
249
: ExtensionHostStartup.EagerAutoStart
250
);
251
return this._instantiationService.createInstance(WebWorkerExtensionHost, runningLocation, startup, this._createLocalExtensionHostDataProvider(runningLocations, runningLocation, isInitialStart));
252
}
253
case ExtensionHostKind.Remote: {
254
const remoteAgentConnection = this._remoteAgentService.getConnection();
255
if (remoteAgentConnection) {
256
return this._instantiationService.createInstance(RemoteExtensionHost, runningLocation, this._createRemoteExtensionHostDataProvider(runningLocations, remoteAgentConnection.remoteAuthority));
257
}
258
return null;
259
}
260
}
261
}
262
263
private _createLocalExtensionHostDataProvider(runningLocations: ExtensionRunningLocationTracker, desiredRunningLocation: ExtensionRunningLocation, isInitialStart: boolean): IWebWorkerExtensionHostDataProvider {
264
return {
265
getInitData: async (): Promise<IWebWorkerExtensionHostInitData> => {
266
if (isInitialStart) {
267
// Here we load even extensions that would be disabled by workspace trust
268
const localExtensions = checkEnabledAndProposedAPI(this._logService, this._extensionEnablementService, this._extensionsProposedApi, await this._scanWebExtensions(), /* ignore workspace trust */true);
269
const runningLocation = runningLocations.computeRunningLocation(localExtensions, [], false);
270
const myExtensions = filterExtensionDescriptions(localExtensions, runningLocation, extRunningLocation => desiredRunningLocation.equals(extRunningLocation));
271
const extensions = new ExtensionHostExtensions(0, localExtensions, myExtensions.map(extension => extension.identifier));
272
return { extensions };
273
} else {
274
// restart case
275
const snapshot = await this._getExtensionRegistrySnapshotWhenReady();
276
const myExtensions = runningLocations.filterByRunningLocation(snapshot.extensions, desiredRunningLocation);
277
const extensions = new ExtensionHostExtensions(snapshot.versionId, snapshot.extensions, myExtensions.map(extension => extension.identifier));
278
return { extensions };
279
}
280
}
281
};
282
}
283
284
private _createRemoteExtensionHostDataProvider(runningLocations: ExtensionRunningLocationTracker, remoteAuthority: string): IRemoteExtensionHostDataProvider {
285
return {
286
remoteAuthority: remoteAuthority,
287
getInitData: async (): Promise<IRemoteExtensionHostInitData> => {
288
const snapshot = await this._getExtensionRegistrySnapshotWhenReady();
289
290
const remoteEnv = await this._remoteAgentService.getEnvironment();
291
if (!remoteEnv) {
292
throw new Error('Cannot provide init data for remote extension host!');
293
}
294
295
const myExtensions = runningLocations.filterByExtensionHostKind(snapshot.extensions, ExtensionHostKind.Remote);
296
const extensions = new ExtensionHostExtensions(snapshot.versionId, snapshot.extensions, myExtensions.map(extension => extension.identifier));
297
298
return {
299
connectionData: this._remoteAuthorityResolverService.getConnectionData(remoteAuthority),
300
pid: remoteEnv.pid,
301
appRoot: remoteEnv.appRoot,
302
extensionHostLogsPath: remoteEnv.extensionHostLogsPath,
303
globalStorageHome: remoteEnv.globalStorageHome,
304
workspaceStorageHome: remoteEnv.workspaceStorageHome,
305
extensions,
306
};
307
}
308
};
309
}
310
}
311
312
export class BrowserExtensionHostKindPicker implements IExtensionHostKindPicker {
313
314
constructor(
315
@ILogService private readonly _logService: ILogService,
316
) { }
317
318
pickExtensionHostKind(extensionId: ExtensionIdentifier, extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null {
319
const result = BrowserExtensionHostKindPicker.pickRunningLocation(extensionKinds, isInstalledLocally, isInstalledRemotely, preference);
320
this._logService.trace(`pickRunningLocation for ${extensionId.value}, extension kinds: [${extensionKinds.join(', ')}], isInstalledLocally: ${isInstalledLocally}, isInstalledRemotely: ${isInstalledRemotely}, preference: ${extensionRunningPreferenceToString(preference)} => ${extensionHostKindToString(result)}`);
321
return result;
322
}
323
324
public static pickRunningLocation(extensionKinds: ExtensionKind[], isInstalledLocally: boolean, isInstalledRemotely: boolean, preference: ExtensionRunningPreference): ExtensionHostKind | null {
325
const result: ExtensionHostKind[] = [];
326
let canRunRemotely = false;
327
for (const extensionKind of extensionKinds) {
328
if (extensionKind === 'ui' && isInstalledRemotely) {
329
// ui extensions run remotely if possible (but only as a last resort)
330
if (preference === ExtensionRunningPreference.Remote) {
331
return ExtensionHostKind.Remote;
332
} else {
333
canRunRemotely = true;
334
}
335
}
336
if (extensionKind === 'workspace' && isInstalledRemotely) {
337
// workspace extensions run remotely if possible
338
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Remote) {
339
return ExtensionHostKind.Remote;
340
} else {
341
result.push(ExtensionHostKind.Remote);
342
}
343
}
344
if (extensionKind === 'web' && (isInstalledLocally || isInstalledRemotely)) {
345
// web worker extensions run in the local web worker if possible
346
if (preference === ExtensionRunningPreference.None || preference === ExtensionRunningPreference.Local) {
347
return ExtensionHostKind.LocalWebWorker;
348
} else {
349
result.push(ExtensionHostKind.LocalWebWorker);
350
}
351
}
352
}
353
if (canRunRemotely) {
354
result.push(ExtensionHostKind.Remote);
355
}
356
return (result.length > 0 ? result[0] : null);
357
}
358
}
359
360
registerSingleton(IExtensionService, ExtensionService, InstantiationType.Eager);
361
362