Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/browser/mainThreadExtensionService.ts
5236 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 { toAction } from '../../../base/common/actions.js';
7
import { VSBuffer } from '../../../base/common/buffer.js';
8
import { CancellationToken } from '../../../base/common/cancellation.js';
9
import { SerializedError, transformErrorFromSerialization } from '../../../base/common/errors.js';
10
import { FileAccess } from '../../../base/common/network.js';
11
import Severity from '../../../base/common/severity.js';
12
import { URI, UriComponents } from '../../../base/common/uri.js';
13
import { localize } from '../../../nls.js';
14
import { ICommandService } from '../../../platform/commands/common/commands.js';
15
import { ILocalExtension } from '../../../platform/extensionManagement/common/extensionManagement.js';
16
import { areSameExtensions } from '../../../platform/extensionManagement/common/extensionManagementUtil.js';
17
import { ExtensionIdentifier, IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
18
import { INotificationService } from '../../../platform/notification/common/notification.js';
19
import { IRemoteConnectionData, ManagedRemoteConnection, RemoteConnection, RemoteConnectionType, ResolvedAuthority, WebSocketRemoteConnection } from '../../../platform/remote/common/remoteAuthorityResolver.js';
20
import { ExtHostContext, ExtHostExtensionServiceShape, MainContext, MainThreadExtensionServiceShape } from '../common/extHost.protocol.js';
21
import { IExtension, IExtensionsWorkbenchService } from '../../contrib/extensions/common/extensions.js';
22
import { IWorkbenchEnvironmentService } from '../../services/environment/common/environmentService.js';
23
import { EnablementState, IWorkbenchExtensionEnablementService } from '../../services/extensionManagement/common/extensionManagement.js';
24
import { ExtensionHostKind } from '../../services/extensions/common/extensionHostKind.js';
25
import { IExtensionDescriptionDelta } from '../../services/extensions/common/extensionHostProtocol.js';
26
import { IExtensionHostProxy, IResolveAuthorityResult } from '../../services/extensions/common/extensionHostProxy.js';
27
import { ActivationKind, ExtensionActivationReason, IExtensionService, IInternalExtensionService, MissingExtensionDependency } from '../../services/extensions/common/extensions.js';
28
import { extHostNamedCustomer, IExtHostContext, IInternalExtHostContext } from '../../services/extensions/common/extHostCustomers.js';
29
import { Dto } from '../../services/extensions/common/proxyIdentifier.js';
30
import { IHostService } from '../../services/host/browser/host.js';
31
import { ITimerService } from '../../services/timer/browser/timerService.js';
32
33
@extHostNamedCustomer(MainContext.MainThreadExtensionService)
34
export class MainThreadExtensionService implements MainThreadExtensionServiceShape {
35
36
private readonly _extensionHostKind: ExtensionHostKind;
37
private readonly _internalExtensionService: IInternalExtensionService;
38
39
constructor(
40
extHostContext: IExtHostContext,
41
@IExtensionService private readonly _extensionService: IExtensionService,
42
@INotificationService private readonly _notificationService: INotificationService,
43
@IExtensionsWorkbenchService private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService,
44
@IHostService private readonly _hostService: IHostService,
45
@IWorkbenchExtensionEnablementService private readonly _extensionEnablementService: IWorkbenchExtensionEnablementService,
46
@ITimerService private readonly _timerService: ITimerService,
47
@ICommandService private readonly _commandService: ICommandService,
48
@IWorkbenchEnvironmentService protected readonly _environmentService: IWorkbenchEnvironmentService,
49
) {
50
this._extensionHostKind = extHostContext.extensionHostKind;
51
52
const internalExtHostContext = (<IInternalExtHostContext>extHostContext);
53
this._internalExtensionService = internalExtHostContext.internalExtensionService;
54
internalExtHostContext._setExtensionHostProxy(
55
new ExtensionHostProxy(extHostContext.getProxy(ExtHostContext.ExtHostExtensionService))
56
);
57
// eslint-disable-next-line local/code-no-any-casts, @typescript-eslint/no-explicit-any
58
internalExtHostContext._setAllMainProxyIdentifiers(Object.keys(MainContext).map((key) => (<any>MainContext)[key]));
59
}
60
61
public dispose(): void {
62
}
63
64
$getExtension(extensionId: string) {
65
return this._extensionService.getExtension(extensionId);
66
}
67
$activateExtension(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
68
return this._internalExtensionService._activateById(extensionId, reason);
69
}
70
async $onWillActivateExtension(extensionId: ExtensionIdentifier): Promise<void> {
71
this._internalExtensionService._onWillActivateExtension(extensionId);
72
}
73
$onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void {
74
this._internalExtensionService._onDidActivateExtension(extensionId, codeLoadingTime, activateCallTime, activateResolvedTime, activationReason);
75
}
76
$onExtensionRuntimeError(extensionId: ExtensionIdentifier, data: SerializedError): void {
77
const error = transformErrorFromSerialization(data);
78
this._internalExtensionService._onExtensionRuntimeError(extensionId, error);
79
console.error(`[${extensionId.value}]${error.message}`);
80
console.error(error.stack);
81
}
82
async $onExtensionActivationError(extensionId: ExtensionIdentifier, data: SerializedError, missingExtensionDependency: MissingExtensionDependency | null): Promise<void> {
83
const error = transformErrorFromSerialization(data);
84
85
this._internalExtensionService._onDidActivateExtensionError(extensionId, error);
86
87
if (missingExtensionDependency) {
88
const extension = await this._extensionService.getExtension(extensionId.value);
89
if (extension) {
90
const local = await this._extensionsWorkbenchService.queryLocal();
91
const installedDependency = local.find(i => areSameExtensions(i.identifier, { id: missingExtensionDependency.dependency }));
92
if (installedDependency?.local) {
93
await this._handleMissingInstalledDependency(extension, installedDependency.local);
94
return;
95
} else {
96
await this._handleMissingNotInstalledDependency(extension, missingExtensionDependency.dependency);
97
return;
98
}
99
}
100
}
101
102
const isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment;
103
if (isDev) {
104
this._notificationService.error(error);
105
return;
106
}
107
108
console.error(error.message);
109
}
110
111
private async _handleMissingInstalledDependency(extension: IExtensionDescription, missingInstalledDependency: ILocalExtension): Promise<void> {
112
const extName = extension.displayName || extension.name;
113
if (this._extensionEnablementService.isEnabled(missingInstalledDependency)) {
114
this._notificationService.notify({
115
severity: Severity.Error,
116
message: localize('reload window', "Cannot activate the '{0}' extension because it depends on the '{1}' extension, which is not loaded. Would you like to reload the window to load the extension?", extName, missingInstalledDependency.manifest.displayName || missingInstalledDependency.manifest.name),
117
actions: {
118
primary: [toAction({ id: 'reload', label: localize('reload', "Reload Window"), run: () => this._hostService.reload() })]
119
}
120
});
121
} else {
122
const enablementState = this._extensionEnablementService.getEnablementState(missingInstalledDependency);
123
if (enablementState === EnablementState.DisabledByVirtualWorkspace) {
124
this._notificationService.notify({
125
severity: Severity.Error,
126
message: localize('notSupportedInWorkspace', "Cannot activate the '{0}' extension because it depends on the '{1}' extension which is not supported in the current workspace", extName, missingInstalledDependency.manifest.displayName || missingInstalledDependency.manifest.name),
127
});
128
} else if (enablementState === EnablementState.DisabledByTrustRequirement) {
129
this._notificationService.notify({
130
severity: Severity.Error,
131
message: localize('restrictedMode', "Cannot activate the '{0}' extension because it depends on the '{1}' extension which is not supported in Restricted Mode", extName, missingInstalledDependency.manifest.displayName || missingInstalledDependency.manifest.name),
132
actions: {
133
primary: [toAction({ id: 'manageWorkspaceTrust', label: localize('manageWorkspaceTrust', "Manage Workspace Trust"), run: () => this._commandService.executeCommand('workbench.trust.manage') })]
134
}
135
});
136
} else if (this._extensionEnablementService.canChangeEnablement(missingInstalledDependency)) {
137
this._notificationService.notify({
138
severity: Severity.Error,
139
message: localize('disabledDep', "Cannot activate the '{0}' extension because it depends on the '{1}' extension which is disabled. Would you like to enable the extension and reload the window?", extName, missingInstalledDependency.manifest.displayName || missingInstalledDependency.manifest.name),
140
actions: {
141
primary: [toAction({
142
id: 'enable', label: localize('enable dep', "Enable and Reload"), enabled: true,
143
run: () => this._extensionEnablementService.setEnablement([missingInstalledDependency], enablementState === EnablementState.DisabledGlobally ? EnablementState.EnabledGlobally : EnablementState.EnabledWorkspace)
144
.then(() => this._hostService.reload(), e => this._notificationService.error(e))
145
})]
146
}
147
});
148
} else {
149
this._notificationService.notify({
150
severity: Severity.Error,
151
message: localize('disabledDepNoAction', "Cannot activate the '{0}' extension because it depends on the '{1}' extension which is disabled.", extName, missingInstalledDependency.manifest.displayName || missingInstalledDependency.manifest.name),
152
});
153
}
154
}
155
}
156
157
private async _handleMissingNotInstalledDependency(extension: IExtensionDescription, missingDependency: string): Promise<void> {
158
const extName = extension.displayName || extension.name;
159
let dependencyExtension: IExtension | null = null;
160
try {
161
dependencyExtension = (await this._extensionsWorkbenchService.getExtensions([{ id: missingDependency }], CancellationToken.None))[0];
162
} catch (err) {
163
}
164
if (dependencyExtension) {
165
this._notificationService.notify({
166
severity: Severity.Error,
167
message: localize('uninstalledDep', "Cannot activate the '{0}' extension because it depends on the '{1}' extension from '{2}', which is not installed. Would you like to install the extension and reload the window?", extName, dependencyExtension.displayName, dependencyExtension.publisherDisplayName),
168
actions: {
169
primary: [toAction({
170
id: 'install',
171
label: localize('install missing dep', "Install and Reload"),
172
run: () => this._extensionsWorkbenchService.install(dependencyExtension)
173
.then(() => this._hostService.reload(), e => this._notificationService.error(e))
174
})]
175
}
176
});
177
} else {
178
this._notificationService.error(localize('unknownDep', "Cannot activate the '{0}' extension because it depends on an unknown '{1}' extension.", extName, missingDependency));
179
}
180
}
181
182
async $setPerformanceMarks(marks: PerformanceMark[]): Promise<void> {
183
if (this._extensionHostKind === ExtensionHostKind.LocalProcess) {
184
this._timerService.setPerformanceMarks('localExtHost', marks);
185
} else if (this._extensionHostKind === ExtensionHostKind.LocalWebWorker) {
186
this._timerService.setPerformanceMarks('workerExtHost', marks);
187
} else {
188
this._timerService.setPerformanceMarks('remoteExtHost', marks);
189
}
190
}
191
192
async $asBrowserUri(uri: UriComponents): Promise<UriComponents> {
193
return FileAccess.uriToBrowserUri(URI.revive(uri));
194
}
195
}
196
197
class ExtensionHostProxy implements IExtensionHostProxy {
198
constructor(
199
private readonly _actual: ExtHostExtensionServiceShape
200
) { }
201
202
async resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {
203
const resolved = reviveResolveAuthorityResult(await this._actual.$resolveAuthority(remoteAuthority, resolveAttempt));
204
return resolved;
205
}
206
async getCanonicalURI(remoteAuthority: string, uri: URI): Promise<URI | null> {
207
const uriComponents = await this._actual.$getCanonicalURI(remoteAuthority, uri);
208
return (uriComponents ? URI.revive(uriComponents) : uriComponents);
209
}
210
startExtensionHost(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {
211
return this._actual.$startExtensionHost(extensionsDelta);
212
}
213
extensionTestsExecute(): Promise<number> {
214
return this._actual.$extensionTestsExecute();
215
}
216
activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void> {
217
return this._actual.$activateByEvent(activationEvent, activationKind);
218
}
219
activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean> {
220
return this._actual.$activate(extensionId, reason);
221
}
222
setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void> {
223
return this._actual.$setRemoteEnvironment(env);
224
}
225
updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise<void> {
226
return this._actual.$updateRemoteConnectionData(connectionData);
227
}
228
deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise<void> {
229
return this._actual.$deltaExtensions(extensionsDelta);
230
}
231
test_latency(n: number): Promise<number> {
232
return this._actual.$test_latency(n);
233
}
234
test_up(b: VSBuffer): Promise<number> {
235
return this._actual.$test_up(b);
236
}
237
test_down(size: number): Promise<VSBuffer> {
238
return this._actual.$test_down(size);
239
}
240
}
241
242
function reviveResolveAuthorityResult(result: Dto<IResolveAuthorityResult>): IResolveAuthorityResult {
243
if (result.type === 'ok') {
244
return {
245
type: 'ok',
246
value: {
247
...result.value,
248
authority: reviveResolvedAuthority(result.value.authority),
249
}
250
};
251
} else {
252
return result;
253
}
254
}
255
256
function reviveResolvedAuthority(resolvedAuthority: Dto<ResolvedAuthority>): ResolvedAuthority {
257
return {
258
...resolvedAuthority,
259
connectTo: reviveConnection(resolvedAuthority.connectTo),
260
};
261
}
262
263
function reviveConnection(connection: Dto<RemoteConnection>): RemoteConnection {
264
if (connection.type === RemoteConnectionType.WebSocket) {
265
return new WebSocketRemoteConnection(connection.host, connection.port);
266
}
267
return new ManagedRemoteConnection(connection.id);
268
}
269
270