Skip to content

Commit a8cb99a

Browse files
committed
1 parent 113fea1 commit a8cb99a

File tree

5 files changed

+91
-7
lines changed

5 files changed

+91
-7
lines changed

src/vs/platform/extensions/common/extensions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,10 @@ export function isLanguagePackExtension(manifest: IExtensionManifest): boolean {
274274
return manifest.contributes && manifest.contributes.localizations ? manifest.contributes.localizations.length > 0 : false;
275275
}
276276

277+
export function isAuthenticaionProviderExtension(manifest: IExtensionManifest): boolean {
278+
return manifest.contributes && manifest.contributes.authentication ? manifest.contributes.authentication.length > 0 : false;
279+
}
280+
277281
export interface IScannedExtension {
278282
readonly identifier: IExtensionIdentifier;
279283
readonly location: URI;

src/vs/workbench/contrib/extensions/browser/extensionsActions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -970,7 +970,7 @@ export class DisableForWorkspaceAction extends ExtensionAction {
970970
if (this.extension && this.extension.local && this.runningExtensions.some(e => areSameExtensions({ id: e.identifier.value, uuid: e.uuid }, this.extension!.identifier) && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY)) {
971971
this.enabled = this.extension.state === ExtensionState.Installed
972972
&& (this.extension.enablementState === EnablementState.EnabledGlobally || this.extension.enablementState === EnablementState.EnabledWorkspace)
973-
&& this.extensionEnablementService.canChangeEnablement(this.extension.local);
973+
&& this.extensionEnablementService.canChangeWorkspaceEnablement(this.extension.local);
974974
}
975975
}
976976

src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens
1212
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
1313
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
1414
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
15-
import { ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions';
15+
import { ExtensionType, IExtension, isAuthenticaionProviderExtension, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
1616
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
1717
import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil';
1818
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
1919
import { IProductService } from 'vs/platform/product/common/productService';
2020
import { StorageManager } from 'vs/platform/extensionManagement/common/extensionEnablementService';
2121
import { webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions';
22+
import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount';
2223

2324
const SOURCE = 'IWorkbenchExtensionEnablementService';
2425

@@ -40,6 +41,7 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
4041
@IConfigurationService private readonly configurationService: IConfigurationService,
4142
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
4243
@IProductService private readonly productService: IProductService,
44+
@IUserDataSyncAccountService private readonly userDataSyncAccountService: IUserDataSyncAccountService,
4345
) {
4446
super();
4547
this.storageManger = this._register(new StorageManager(storageService));
@@ -66,7 +68,9 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
6668
}
6769

6870
canChangeEnablement(extension: IExtension): boolean {
69-
if (extension.manifest && extension.manifest.contributes && extension.manifest.contributes.localizations && extension.manifest.contributes.localizations.length) {
71+
try {
72+
this.throwErrorIfCannotChangeEnablement(extension);
73+
} catch (error) {
7074
return false;
7175
}
7276
const enablementState = this.getEnablementState(extension);
@@ -76,11 +80,47 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
7680
return true;
7781
}
7882

83+
private throwErrorIfCannotChangeEnablement(extension: IExtension): void {
84+
if (isLanguagePackExtension(extension.manifest)) {
85+
throw new Error(localize('cannot disable language pack extension', "Cannot disable {0} extension because it contributes language packs.", extension.manifest.displayName || extension.identifier.id));
86+
}
87+
88+
if (this.userDataSyncAccountService.account &&
89+
isAuthenticaionProviderExtension(extension.manifest) && extension.manifest.contributes!.authentication!.some(a => a.id === this.userDataSyncAccountService.account!.authenticationProviderId)) {
90+
throw new Error(localize('cannot disable auth extension', "Cannot disable {0} extension because Settings Sync depends on it.", extension.manifest.displayName || extension.identifier.id));
91+
}
92+
}
93+
94+
canChangeWorkspaceEnablement(extension: IExtension): boolean {
95+
if (!this.canChangeEnablement(extension)) {
96+
return false;
97+
}
98+
try {
99+
this.throwErrorIfCannotChangeWorkspaceEnablement(extension);
100+
} catch (error) {
101+
return false;
102+
}
103+
return true;
104+
}
105+
106+
private throwErrorIfCannotChangeWorkspaceEnablement(extension: IExtension): void {
107+
if (!this.hasWorkspace) {
108+
throw new Error(localize('noWorkspace', "No workspace."));
109+
}
110+
if (isAuthenticaionProviderExtension(extension.manifest)) {
111+
throw new Error(localize('cannot disable auth extension in workspace', "Cannot disable {0} extension in workspace because it contributes authentication providers", extension.manifest.displayName || extension.identifier.id));
112+
}
113+
}
114+
79115
async setEnablement(extensions: IExtension[], newState: EnablementState): Promise<boolean[]> {
80116

81117
const workspace = newState === EnablementState.DisabledWorkspace || newState === EnablementState.EnabledWorkspace;
82-
if (workspace && !this.hasWorkspace) {
83-
return Promise.reject(new Error(localize('noWorkspace', "No workspace.")));
118+
for (const extension of extensions) {
119+
if (workspace) {
120+
this.throwErrorIfCannotChangeWorkspaceEnablement(extension);
121+
} else {
122+
this.throwErrorIfCannotChangeEnablement(extension);
123+
}
84124
}
85125

86126
const result = await Promise.all(extensions.map(e => this._setEnablement(e, newState)));
@@ -316,4 +356,4 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
316356
}
317357
}
318358

319-
registerSingleton(IWorkbenchExtensionEnablementService, ExtensionEnablementService, true);
359+
registerSingleton(IWorkbenchExtensionEnablementService, ExtensionEnablementService);

src/vs/workbench/services/extensionManagement/common/extensionManagement.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ export interface IWorkbenchExtensionEnablementService {
5858
*/
5959
canChangeEnablement(extension: IExtension): boolean;
6060

61+
/**
62+
* Returns `true` if the enablement can be changed.
63+
*/
64+
canChangeWorkspaceEnablement(extension: IExtension): boolean;
65+
6166
/**
6267
* Returns `true` if the given extension identifier is enabled.
6368
*/

src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { assign } from 'vs/base/common/objects';
2323
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
2424
import { productService } from 'vs/workbench/test/browser/workbenchTestServices';
2525
import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService';
26+
import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount';
2627

2728
function createStorageService(instantiationService: TestInstantiationService): IStorageService {
2829
let service = instantiationService.get(IStorageService);
@@ -51,7 +52,8 @@ export class TestExtensionEnablementService extends ExtensionEnablementService {
5152
extensionManagementService,
5253
instantiationService.get(IConfigurationService),
5354
extensionManagementServerService,
54-
productService
55+
productService,
56+
instantiationService.get(IUserDataSyncAccountService) || instantiationService.stub(IUserDataSyncAccountService, UserDataSyncAccountService)
5557
);
5658
}
5759

@@ -371,6 +373,39 @@ suite('ExtensionEnablementService Test', () => {
371373
assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a', { localizations: [{ languageId: 'gr', translations: [{ id: 'vscode', path: 'path' }] }] })), false);
372374
});
373375

376+
test('test canChangeEnablement return true for auth extension', () => {
377+
assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a', { authentication: [{ id: 'a', label: 'a' }] })), true);
378+
});
379+
380+
test('test canChangeEnablement return true for auth extension when user data sync account does not depends on it', () => {
381+
instantiationService.stub(IUserDataSyncAccountService, <Partial<IUserDataSyncAccountService>>{
382+
account: { authenticationProviderId: 'b' }
383+
});
384+
testObject = new TestExtensionEnablementService(instantiationService);
385+
assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a', { authentication: [{ id: 'a', label: 'a' }] })), true);
386+
});
387+
388+
test('test canChangeEnablement return false for auth extension and user data sync account depends on it', () => {
389+
instantiationService.stub(IUserDataSyncAccountService, <Partial<IUserDataSyncAccountService>>{
390+
account: { authenticationProviderId: 'a' }
391+
});
392+
testObject = new TestExtensionEnablementService(instantiationService);
393+
assert.equal(testObject.canChangeEnablement(aLocalExtension('pub.a', { authentication: [{ id: 'a', label: 'a' }] })), false);
394+
});
395+
396+
test('test canChangeWorkspaceEnablement return true', () => {
397+
assert.equal(testObject.canChangeWorkspaceEnablement(aLocalExtension('pub.a')), true);
398+
});
399+
400+
test('test canChangeWorkspaceEnablement return false if there is no workspace', () => {
401+
instantiationService.stub(IWorkspaceContextService, 'getWorkbenchState', WorkbenchState.EMPTY);
402+
assert.equal(testObject.canChangeWorkspaceEnablement(aLocalExtension('pub.a')), false);
403+
});
404+
405+
test('test canChangeWorkspaceEnablement return false for auth extension', () => {
406+
assert.equal(testObject.canChangeWorkspaceEnablement(aLocalExtension('pub.a', { authentication: [{ id: 'a', label: 'a' }] })), false);
407+
});
408+
374409
test('test canChangeEnablement return false when extensions are disabled in environment', () => {
375410
instantiationService.stub(IWorkbenchEnvironmentService, { disableExtensions: true } as IWorkbenchEnvironmentService);
376411
testObject = new TestExtensionEnablementService(instantiationService);

0 commit comments

Comments
 (0)