Merge from vscode 2cd495805cf99b31b6926f08ff4348124b2cf73d

This commit is contained in:
ADS Merger
2020-06-30 04:40:21 +00:00
committed by AzureDataStudio
parent a8a7559229
commit 1388493cc1
602 changed files with 16375 additions and 12940 deletions

View File

@@ -0,0 +1,72 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IBuiltinExtensionsScannerService, IScannedExtension, ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { isWeb } from 'vs/base/common/platform';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { URI } from 'vs/base/common/uri';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
interface IScannedBuiltinExtension {
extensionPath: string,
packageJSON: IExtensionManifest,
packageNLSPath?: string,
readmePath?: string,
changelogPath?: string,
}
export class BuiltinExtensionsScannerService implements IBuiltinExtensionsScannerService {
declare readonly _serviceBrand: undefined;
private readonly builtinExtensions: IScannedExtension[] = [];
constructor(
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IUriIdentityService uriIdentityService: IUriIdentityService,
) {
const builtinExtensionsServiceUrl = environmentService.options?.builtinExtensionsServiceUrl ? URI.parse(environmentService.options?.builtinExtensionsServiceUrl) : undefined;
if (isWeb && builtinExtensionsServiceUrl) {
let scannedBuiltinExtensions: IScannedBuiltinExtension[] = [];
if (environmentService.isBuilt) {
// Built time configuration (do NOT modify)
scannedBuiltinExtensions = [/*BUILD->INSERT_BUILTIN_EXTENSIONS*/];
} else {
// Find builtin extensions by checking for DOM
const builtinExtensionsElement = document.getElementById('vscode-workbench-builtin-extensions');
const builtinExtensionsElementAttribute = builtinExtensionsElement ? builtinExtensionsElement.getAttribute('data-settings') : undefined;
if (builtinExtensionsElementAttribute) {
try {
scannedBuiltinExtensions = JSON.parse(builtinExtensionsElementAttribute);
} catch (error) { /* ignore error*/ }
}
}
this.builtinExtensions = scannedBuiltinExtensions.map(e => ({
identifier: { id: getGalleryExtensionId(e.packageJSON.publisher, e.packageJSON.name) },
location: uriIdentityService.extUri.joinPath(builtinExtensionsServiceUrl!, e.extensionPath),
type: ExtensionType.System,
packageJSON: e.packageJSON,
packageNLSUrl: e.packageNLSPath ? uriIdentityService.extUri.joinPath(builtinExtensionsServiceUrl!, e.packageNLSPath) : undefined,
readmeUrl: e.readmePath ? uriIdentityService.extUri.joinPath(builtinExtensionsServiceUrl!, e.readmePath) : undefined,
changelogUrl: e.changelogPath ? uriIdentityService.extUri.joinPath(builtinExtensionsServiceUrl!, e.changelogPath) : undefined,
}));
}
}
async scanBuiltinExtensions(): Promise<IScannedExtension[]> {
if (isWeb) {
return this.builtinExtensions;
}
throw new Error('not supported');
}
}
registerSingleton(IBuiltinExtensionsScannerService, BuiltinExtensionsScannerService);

View File

@@ -18,6 +18,7 @@ import { getExtensionKind } from 'vs/workbench/services/extensions/common/extens
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IProductService } from 'vs/platform/product/common/productService';
import { StorageManager } from 'vs/platform/extensionManagement/common/extensionEnablementService';
import { webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions';
const SOURCE = 'IWorkbenchExtensionEnablementService';
@@ -137,8 +138,8 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
}
private _isDisabledByExtensionKind(extension: IExtension): boolean {
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
const server = this.extensionManagementServerService.getExtensionManagementServer(extension.location);
if (this.extensionManagementServerService.remoteExtensionManagementServer || this.extensionManagementServerService.webExtensionManagementServer) {
const server = this.extensionManagementServerService.getExtensionManagementServer(extension);
for (const extensionKind of getExtensionKind(extension.manifest, this.productService, this.configurationService)) {
if (extensionKind === 'ui') {
if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.localExtensionManagementServer === server) {
@@ -151,8 +152,13 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
}
}
if (extensionKind === 'web') {
// Web extensions are not yet supported to be disabled by kind. Enable them always on web.
const enableLocalWebWorker = this.configurationService.getValue<boolean>(webWorkerExtHostConfig);
if (enableLocalWebWorker) {
// Web extensions are enabled on all configurations
return false;
}
if (this.extensionManagementServerService.localExtensionManagementServer === null) {
// Web extensions run only in the web
return false;
}
}

View File

@@ -6,24 +6,25 @@
import { Event } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { IExtension } from 'vs/platform/extensions/common/extensions';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtension, IScannedExtension, ExtensionType } from 'vs/platform/extensions/common/extensions';
import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IStringDictionary } from 'vs/base/common/collections';
export const IExtensionManagementServerService = createDecorator<IExtensionManagementServerService>('extensionManagementServerService');
export interface IExtensionManagementServer {
extensionManagementService: IExtensionManagementService;
authority: string;
id: string;
label: string;
extensionManagementService: IExtensionManagementService;
}
export interface IExtensionManagementServerService {
readonly _serviceBrand: undefined;
readonly localExtensionManagementServer: IExtensionManagementServer | null;
readonly remoteExtensionManagementServer: IExtensionManagementServer | null;
getExtensionManagementServer(location: URI): IExtensionManagementServer | null;
readonly webExtensionManagementServer: IExtensionManagementServer | null;
getExtensionManagementServer(extension: IExtension): IExtensionManagementServer | null;
}
export const enum EnablementState {
@@ -139,3 +140,11 @@ export interface IExtensionRecommendationsService {
getRecommendedExtensionsByScenario(scenarioType: string): Promise<IExtensionRecommendation[]>; // {{SQL CARBON EDIT}}
promptRecommendedExtensionsByScenario(scenarioType: string): void; // {{SQL CARBON EDIT}}
}
export const IWebExtensionsScannerService = createDecorator<IWebExtensionsScannerService>('IWebExtensionsScannerService');
export interface IWebExtensionsScannerService {
readonly _serviceBrand: undefined;
scanExtensions(type?: ExtensionType): Promise<IScannedExtension[]>;
addExtension(galleryExtension: IGalleryExtension): Promise<IScannedExtension>;
removeExtension(identifier: IExtensionIdentifier, version?: string): Promise<void>;
}

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { URI } from 'vs/base/common/uri';
import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
@@ -12,6 +11,10 @@ import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ILabelService } from 'vs/platform/label/common/label';
import { isWeb } from 'vs/base/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { WebExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/webExtensionManagementService';
import { IExtension } from 'vs/platform/extensions/common/extensions';
export class ExtensionManagementServerService implements IExtensionManagementServerService {
@@ -19,27 +22,40 @@ export class ExtensionManagementServerService implements IExtensionManagementSer
readonly localExtensionManagementServer: IExtensionManagementServer | null = null;
readonly remoteExtensionManagementServer: IExtensionManagementServer | null = null;
readonly webExtensionManagementServer: IExtensionManagementServer | null = null;
constructor(
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
@ILabelService labelService: ILabelService,
@IInstantiationService instantiationService: IInstantiationService,
) {
const remoteAgentConnection = remoteAgentService.getConnection();
if (remoteAgentConnection) {
const extensionManagementService = new ExtensionManagementChannelClient(remoteAgentConnection!.getChannel<IChannel>('extensions'));
this.remoteExtensionManagementServer = {
authority: remoteAgentConnection.remoteAuthority,
id: 'remote',
extensionManagementService,
get label() { return labelService.getHostLabel(REMOTE_HOST_SCHEME, remoteAgentConnection!.remoteAuthority) || localize('remote', "Remote"); }
};
}
if (isWeb) {
const extensionManagementService = instantiationService.createInstance(WebExtensionManagementService);
this.webExtensionManagementServer = {
id: 'web',
extensionManagementService,
label: localize('web', "Web")
};
}
}
getExtensionManagementServer(location: URI): IExtensionManagementServer | null {
if (location.scheme === REMOTE_HOST_SCHEME) {
return this.remoteExtensionManagementServer;
getExtensionManagementServer(extension: IExtension): IExtensionManagementServer {
if (extension.location.scheme === REMOTE_HOST_SCHEME) {
return this.remoteExtensionManagementServer!;
}
return null;
if (this.webExtensionManagementServer) {
return this.webExtensionManagementServer;
}
throw new Error(`Invalid Extension ${extension.location}`);
}
}

View File

@@ -15,7 +15,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { CancellationToken } from 'vs/base/common/cancellation';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { localize } from 'vs/nls';
import { prefersExecuteOnUI, canExecuteOnWorkspace } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { prefersExecuteOnUI, canExecuteOnWorkspace, prefersExecuteOnWorkspace, canExecuteOnUI, prefersExecuteOnWeb, canExecuteOnWeb } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { IProductService } from 'vs/platform/product/common/productService';
import { Schemas } from 'vs/base/common/network';
import { IDownloadService } from 'vs/platform/download/common/download';
@@ -45,6 +45,9 @@ export class ExtensionManagementService extends Disposable implements IExtension
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
this.servers.push(this.extensionManagementServerService.remoteExtensionManagementServer);
}
if (this.extensionManagementServerService.webExtensionManagementServer) {
this.servers.push(this.extensionManagementServerService.webExtensionManagementServer);
}
this.onInstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer<InstallExtensionEvent>, server) => { emitter.add(server.extensionManagementService.onInstallExtension); return emitter; }, new EventMultiplexer<InstallExtensionEvent>())).event;
this.onDidInstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer<DidInstallExtensionEvent>, server) => { emitter.add(server.extensionManagementService.onDidInstallExtension); return emitter; }, new EventMultiplexer<DidInstallExtensionEvent>())).event;
@@ -64,7 +67,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
if (!server) {
return Promise.reject(`Invalid location ${extension.location.toString()}`);
}
if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
if (this.servers.length > 1) {
if (isLanguagePackExtension(extension.manifest)) {
return this.uninstallEverywhere(extension);
}
@@ -79,12 +82,14 @@ export class ExtensionManagementService extends Disposable implements IExtension
return Promise.reject(`Invalid location ${extension.location.toString()}`);
}
const promise = server.extensionManagementService.uninstall(extension);
const anotherServer: IExtensionManagementServer | null = server === this.extensionManagementServerService.localExtensionManagementServer ? this.extensionManagementServerService.remoteExtensionManagementServer! : this.extensionManagementServerService.localExtensionManagementServer;
if (anotherServer) {
const installed = await anotherServer.extensionManagementService.getInstalled(ExtensionType.User);
extension = installed.filter(i => areSameExtensions(i.identifier, extension.identifier))[0];
if (extension) {
await anotherServer.extensionManagementService.uninstall(extension);
const otherServers: IExtensionManagementServer[] = this.servers.filter(s => s !== server);
if (otherServers.length) {
for (const otherServer of otherServers) {
const installed = await otherServer.extensionManagementService.getInstalled(ExtensionType.User);
extension = installed.filter(i => areSameExtensions(i.identifier, extension.identifier))[0];
if (extension) {
await otherServer.extensionManagementService.uninstall(extension);
}
}
}
return promise;
@@ -141,7 +146,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
unzip(zipLocation: URI): Promise<IExtensionIdentifier> {
return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.unzip(zipLocation))).then(([extensionIdentifier]) => extensionIdentifier);
return Promise.all(this.servers
// Filter out web server
.filter(server => server !== this.extensionManagementServerService.webExtensionManagementServer)
.map(({ extensionManagementService }) => extensionManagementService.unzip(zipLocation))).then(([extensionIdentifier]) => extensionIdentifier);
}
async install(vsix: URI): Promise<ILocalExtension> {
@@ -149,7 +157,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
const manifest = await this.getManifest(vsix);
if (isLanguagePackExtension(manifest)) {
// Install on both servers
const [local] = await Promise.all(this.servers.map(server => this.installVSIX(vsix, server)));
const [local] = await Promise.all([this.extensionManagementServerService.localExtensionManagementServer, this.extensionManagementServerService.remoteExtensionManagementServer].map(server => this.installVSIX(vsix, server)));
return local;
}
if (prefersExecuteOnUI(manifest, this.productService, this.configurationService)) {
@@ -183,39 +191,61 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
async installFromGallery(gallery: IGalleryExtension): Promise<ILocalExtension> {
if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None);
if (manifest) {
if (isLanguagePackExtension(manifest)) {
// Install on both servers
return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local);
}
if (prefersExecuteOnUI(manifest, this.productService, this.configurationService)) {
// Install only on local server
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
}
// Install only on remote server
return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
} else {
return Promise.reject(localize('Manifest is not found', "Installing Extension {0} failed: Manifest is not found.", gallery.displayName || gallery.name));
}
}
if (this.extensionManagementServerService.localExtensionManagementServer) {
// Only local server, install without any checks
if (this.servers.length === 1 && this.extensionManagementServerService.localExtensionManagementServer) {
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
}
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None);
if (!manifest) {
return Promise.reject(localize('Manifest is not found', "Installing Extension {0} failed: Manifest is not found.", gallery.displayName || gallery.name));
}
if (!isLanguagePackExtension(manifest) && !canExecuteOnWorkspace(manifest, this.productService, this.configurationService)) {
const error = new Error(localize('cannot be installed', "Cannot install '{0}' because this extension has defined that it cannot run on the remote server.", gallery.displayName || gallery.name));
error.name = INSTALL_ERROR_NOT_SUPPORTED;
return Promise.reject(error);
}
const manifest = await this.extensionGalleryService.getManifest(gallery, CancellationToken.None);
if (!manifest) {
return Promise.reject(localize('Manifest is not found', "Installing Extension {0} failed: Manifest is not found.", gallery.displayName || gallery.name));
}
// Install Language pack on all servers
if (isLanguagePackExtension(manifest)) {
return Promise.all(this.servers.map(server => server.extensionManagementService.installFromGallery(gallery))).then(([local]) => local);
}
// 1. Install on preferred location
// Install UI preferred extension on local server
if (prefersExecuteOnUI(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.localExtensionManagementServer) {
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
}
// Install Workspace preferred extension on remote server
if (prefersExecuteOnWorkspace(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) {
return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
}
return Promise.reject('No Servers to Install');
// Install Web preferred extension on web server
if (prefersExecuteOnWeb(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.webExtensionManagementServer) {
return this.extensionManagementServerService.webExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
}
// 2. Install on supported location
// Install UI supported extension on local server
if (canExecuteOnUI(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.localExtensionManagementServer) {
return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
}
// Install Workspace supported extension on remote server
if (canExecuteOnWorkspace(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.remoteExtensionManagementServer) {
return this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
}
// Install Web supported extension on web server
if (canExecuteOnWeb(manifest, this.productService, this.configurationService) && this.extensionManagementServerService.webExtensionManagementServer) {
return this.extensionManagementServerService.webExtensionManagementServer.extensionManagementService.installFromGallery(gallery);
}
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
const error = new Error(localize('cannot be installed', "Cannot install '{0}' because this extension has defined that it cannot run on the remote server.", gallery.displayName || gallery.name));
error.name = INSTALL_ERROR_NOT_SUPPORTED;
return Promise.reject(error);
}
const error = new Error(localize('cannot be installed on web', "Cannot install '{0}' because this extension has defined that it cannot run on the web server.", gallery.displayName || gallery.name));
error.name = INSTALL_ERROR_NOT_SUPPORTED;
return Promise.reject(error);
}
getExtensionsReport(): Promise<IReportedExtension[]> {
@@ -229,6 +259,6 @@ export class ExtensionManagementService extends Disposable implements IExtension
}
private getServer(extension: ILocalExtension): IExtensionManagementServer | null {
return this.extensionManagementServerService.getExtensionManagementServer(extension.location);
return this.extensionManagementServerService.getExtensionManagementServer(extension);
}
}

View File

@@ -0,0 +1,117 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionType, IExtensionIdentifier, IExtensionManifest, IScannedExtension } from 'vs/platform/extensions/common/extensions';
import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, IGalleryExtension, IReportedExtension, IGalleryMetadata, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement';
import { Event, Emitter } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { IRequestService, isSuccess, asText } from 'vs/platform/request/common/request';
import { CancellationToken } from 'vs/base/common/cancellation';
import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';
export class WebExtensionManagementService extends Disposable implements IExtensionManagementService {
declare readonly _serviceBrand: undefined;
private readonly _onInstallExtension = this._register(new Emitter<InstallExtensionEvent>());
readonly onInstallExtension: Event<InstallExtensionEvent> = this._onInstallExtension.event;
private readonly _onDidInstallExtension = this._register(new Emitter<DidInstallExtensionEvent>());
readonly onDidInstallExtension: Event<DidInstallExtensionEvent> = this._onDidInstallExtension.event;
private readonly _onUninstallExtension = this._register(new Emitter<IExtensionIdentifier>());
readonly onUninstallExtension: Event<IExtensionIdentifier> = this._onUninstallExtension.event;
private _onDidUninstallExtension = this._register(new Emitter<DidUninstallExtensionEvent>());
onDidUninstallExtension: Event<DidUninstallExtensionEvent> = this._onDidUninstallExtension.event;
constructor(
@IWebExtensionsScannerService private readonly webExtensionsScannerService: IWebExtensionsScannerService,
@IRequestService private readonly requestService: IRequestService,
@ILogService private readonly logService: ILogService,
) {
super();
}
async getInstalled(type?: ExtensionType): Promise<ILocalExtension[]> {
const extensions = await this.webExtensionsScannerService.scanExtensions(type);
return Promise.all(extensions.map(e => this.toLocalExtension(e)));
}
async installFromGallery(gallery: IGalleryExtension): Promise<ILocalExtension> {
this.logService.info('Installing extension:', gallery.identifier.id);
this._onInstallExtension.fire({ identifier: gallery.identifier, gallery });
try {
const existingExtension = await this.getUserExtension(gallery.identifier);
if (existingExtension && existingExtension.manifest.version !== gallery.version) {
await this.webExtensionsScannerService.removeExtension(existingExtension.identifier, existingExtension.manifest.version);
}
const scannedExtension = await this.webExtensionsScannerService.addExtension(gallery);
const local = await this.toLocalExtension(scannedExtension);
this._onDidInstallExtension.fire({ local, identifier: gallery.identifier, operation: InstallOperation.Install, gallery });
return local;
} catch (error) {
this._onDidInstallExtension.fire({ error, identifier: gallery.identifier, operation: InstallOperation.Install, gallery });
throw error;
}
}
async uninstall(extension: ILocalExtension): Promise<void> {
this._onUninstallExtension.fire(extension.identifier);
try {
await this.webExtensionsScannerService.removeExtension(extension.identifier);
this._onDidUninstallExtension.fire({ identifier: extension.identifier });
} catch (error) {
this.logService.error(error);
this._onDidUninstallExtension.fire({ error, identifier: extension.identifier });
throw error;
}
}
async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise<ILocalExtension> {
return local;
}
private async getUserExtension(identifier: IExtensionIdentifier): Promise<ILocalExtension | undefined> {
const userExtensions = await this.getInstalled(ExtensionType.User);
return userExtensions.find(e => areSameExtensions(e.identifier, identifier));
}
private async toLocalExtension(scannedExtension: IScannedExtension): Promise<ILocalExtension> {
let manifest = scannedExtension.packageJSON;
if (scannedExtension.packageNLSUrl) {
try {
const context = await this.requestService.request({ type: 'GET', url: scannedExtension.packageNLSUrl.toString() }, CancellationToken.None);
if (isSuccess(context)) {
const content = await asText(context);
if (content) {
manifest = localizeManifest(manifest, JSON.parse(content));
}
}
} catch (error) { /* ignore */ }
}
return <ILocalExtension>{
type: scannedExtension.type,
identifier: scannedExtension.identifier,
manifest,
location: scannedExtension.location,
isMachineScoped: false,
publisherId: null,
publisherDisplayName: null
};
}
zip(extension: ILocalExtension): Promise<URI> { throw new Error('unsupported'); }
unzip(zipLocation: URI): Promise<IExtensionIdentifier> { throw new Error('unsupported'); }
getManifest(vsix: URI): Promise<IExtensionManifest> { throw new Error('unsupported'); }
install(vsix: URI, isMachineScoped?: boolean): Promise<ILocalExtension> { throw new Error('unsupported'); }
reinstallFromGallery(extension: ILocalExtension): Promise<void> { throw new Error('unsupported'); }
getExtensionsReport(): Promise<IReportedExtension[]> { throw new Error('unsupported'); }
}

View File

@@ -0,0 +1,193 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as semver from 'semver-umd';
import { IBuiltinExtensionsScannerService, IScannedExtension, ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { isWeb } from 'vs/base/common/platform';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { joinPath } from 'vs/base/common/resources';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { Queue } from 'vs/base/common/async';
import { VSBuffer } from 'vs/base/common/buffer';
import { asText, isSuccess, IRequestService } from 'vs/platform/request/common/request';
import { ILogService } from 'vs/platform/log/common/log';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
import { groupByExtension, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
interface IUserExtension {
identifier: IExtensionIdentifier;
version: string;
uri: URI;
readmeUri?: URI;
changelogUri?: URI;
packageNLSUri?: URI;
}
interface IStoredUserExtension {
identifier: IExtensionIdentifier;
version: string;
uri: UriComponents;
readmeUri?: UriComponents;
changelogUri?: UriComponents;
packageNLSUri?: UriComponents;
}
const AssetTypeWebResource = 'Microsoft.VisualStudio.Code.WebResources';
function getExtensionLocation(assetUri: URI): URI { return joinPath(assetUri, AssetTypeWebResource, 'extension'); }
export class WebExtensionsScannerService implements IWebExtensionsScannerService {
declare readonly _serviceBrand: undefined;
private readonly systemExtensionsPromise: Promise<IScannedExtension[]>;
private readonly staticExtensions: IScannedExtension[];
private readonly extensionsResource: URI;
private readonly userExtensionsResourceLimiter: Queue<IUserExtension[]>;
constructor(
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IBuiltinExtensionsScannerService private readonly builtinExtensionsScannerService: IBuiltinExtensionsScannerService,
@IFileService private readonly fileService: IFileService,
@IRequestService private readonly requestService: IRequestService,
@ILogService private readonly logService: ILogService,
) {
this.extensionsResource = joinPath(environmentService.userRoamingDataHome, 'extensions.json');
this.userExtensionsResourceLimiter = new Queue<IUserExtension[]>();
this.systemExtensionsPromise = isWeb ? this.builtinExtensionsScannerService.scanBuiltinExtensions() : Promise.resolve([]);
const staticExtensions = environmentService.options && Array.isArray(environmentService.options.staticExtensions) ? environmentService.options.staticExtensions : [];
this.staticExtensions = staticExtensions.map(data => <IScannedExtension>{
location: data.extensionLocation,
type: ExtensionType.User,
packageJSON: data.packageJSON,
});
}
async scanExtensions(type?: ExtensionType): Promise<IScannedExtension[]> {
const extensions = [];
if (type === undefined || type === ExtensionType.System) {
const systemExtensions = await this.systemExtensionsPromise;
extensions.push(...systemExtensions);
}
if (type === undefined || type === ExtensionType.User) {
extensions.push(...this.staticExtensions);
const userExtensions = await this.scanUserExtensions();
extensions.push(...userExtensions);
}
return extensions;
}
async addExtension(galleryExtension: IGalleryExtension): Promise<IScannedExtension> {
if (!galleryExtension.assetTypes.some(type => type.startsWith(AssetTypeWebResource))) {
throw new Error(`Missing ${AssetTypeWebResource} asset type`);
}
const packageNLSUri = joinPath(getExtensionLocation(galleryExtension.assetUri), 'package.nls.json');
const context = await this.requestService.request({ type: 'GET', url: packageNLSUri.toString() }, CancellationToken.None);
const packageNLSExists = isSuccess(context);
const userExtensions = await this.readUserExtensions();
const userExtension: IUserExtension = {
identifier: galleryExtension.identifier,
version: galleryExtension.version,
uri: galleryExtension.assetUri,
readmeUri: galleryExtension.assets.readme ? URI.parse(galleryExtension.assets.readme.uri) : undefined,
changelogUri: galleryExtension.assets.changelog ? URI.parse(galleryExtension.assets.changelog.uri) : undefined,
packageNLSUri: packageNLSExists ? packageNLSUri : undefined
};
userExtensions.push(userExtension);
await this.writeUserExtensions(userExtensions);
const scannedExtension = await this.toScannedExtension(userExtension);
if (scannedExtension) {
return scannedExtension;
}
throw new Error('Error while scanning extension');
}
async removeExtension(identifier: IExtensionIdentifier, version?: string): Promise<void> {
let userExtensions = await this.readUserExtensions();
userExtensions = userExtensions.filter(extension => !(areSameExtensions(extension.identifier, identifier) && version ? extension.version === version : true));
await this.writeUserExtensions(userExtensions);
}
private async scanUserExtensions(): Promise<IScannedExtension[]> {
let userExtensions = await this.readUserExtensions();
const byExtension: IUserExtension[][] = groupByExtension(userExtensions, e => e.identifier);
userExtensions = byExtension.map(p => p.sort((a, b) => semver.rcompare(a.version, b.version))[0]);
const scannedExtensions: IScannedExtension[] = [];
await Promise.all(userExtensions.map(async userExtension => {
try {
const scannedExtension = await this.toScannedExtension(userExtension);
if (scannedExtension) {
scannedExtensions.push(scannedExtension);
}
} catch (error) {
this.logService.error(error, 'Error while scanning user extension', userExtension.identifier.id);
}
}));
return scannedExtensions;
}
private async toScannedExtension(userExtension: IUserExtension): Promise<IScannedExtension | null> {
const context = await this.requestService.request({ type: 'GET', url: joinPath(userExtension.uri, 'Microsoft.VisualStudio.Code.Manifest').toString() }, CancellationToken.None);
if (isSuccess(context)) {
const content = await asText(context);
if (content) {
const packageJSON = JSON.parse(content);
return {
identifier: userExtension.identifier,
location: getExtensionLocation(userExtension.uri),
packageJSON,
type: ExtensionType.User,
readmeUrl: userExtension.readmeUri,
changelogUrl: userExtension.changelogUri,
packageNLSUrl: userExtension.packageNLSUri,
};
}
}
return null;
}
private readUserExtensions(): Promise<IUserExtension[]> {
return this.userExtensionsResourceLimiter.queue(async () => {
try {
const content = await this.fileService.readFile(this.extensionsResource);
const storedUserExtensions: IStoredUserExtension[] = JSON.parse(content.value.toString());
return storedUserExtensions.map(e => ({
identifier: e.identifier,
version: e.version,
uri: URI.revive(e.uri),
readmeUri: URI.revive(e.readmeUri),
changelogUri: URI.revive(e.changelogUri),
packageNLSUri: URI.revive(e.packageNLSUri),
}));
} catch (error) { /* Ignore */ }
return [];
});
}
private writeUserExtensions(userExtensions: IUserExtension[]): Promise<IUserExtension[]> {
return this.userExtensionsResourceLimiter.queue(async () => {
const storedUserExtensions: IStoredUserExtension[] = userExtensions.map(e => ({
identifier: e.identifier,
version: e.version,
uri: e.uri.toJSON(),
readmeUri: e.readmeUri?.toJSON(),
changelogUri: e.changelogUri?.toJSON(),
packageNLSUri: e.packageNLSUri?.toJSON(),
}));
await this.fileService.writeFile(this.extensionsResource, VSBuffer.fromString(JSON.stringify(storedUserExtensions)));
return userExtensions;
});
}
}
registerSingleton(IWebExtensionsScannerService, WebExtensionsScannerService);

View File

@@ -5,7 +5,6 @@
import { localize } from 'vs/nls';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
@@ -19,19 +18,16 @@ import { RemoteExtensionManagementChannelClient } from 'vs/workbench/services/ex
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IProductService } from 'vs/platform/product/common/productService';
import { ILabelService } from 'vs/platform/label/common/label';
const localExtensionManagementServerAuthority: string = 'vscode-local';
import { IExtension } from 'vs/platform/extensions/common/extensions';
export class ExtensionManagementServerService implements IExtensionManagementServerService {
declare readonly _serviceBrand: undefined;
private readonly _localExtensionManagementServer: IExtensionManagementServer;
public get localExtensionManagementServer(): IExtensionManagementServer {
return this._localExtensionManagementServer;
}
public get localExtensionManagementServer(): IExtensionManagementServer { return this._localExtensionManagementServer; }
readonly remoteExtensionManagementServer: IExtensionManagementServer | null = null;
readonly isSingleServer: boolean = false;
readonly webExtensionManagementServer: IExtensionManagementServer | null = null;
constructor(
@ISharedProcessService sharedProcessService: ISharedProcessService,
@@ -44,26 +40,26 @@ export class ExtensionManagementServerService implements IExtensionManagementSer
) {
const localExtensionManagementService = new ExtensionManagementChannelClient(sharedProcessService.getChannel('extensions'));
this._localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, authority: localExtensionManagementServerAuthority, label: localize('local', "Local") };
this._localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, id: 'local', label: localize('local', "Local") };
const remoteAgentConnection = remoteAgentService.getConnection();
if (remoteAgentConnection) {
const extensionManagementService = new RemoteExtensionManagementChannelClient(remoteAgentConnection.getChannel<IChannel>('extensions'), this.localExtensionManagementServer.extensionManagementService, galleryService, logService, configurationService, productService);
this.remoteExtensionManagementServer = {
authority: remoteAgentConnection.remoteAuthority,
id: 'remote',
extensionManagementService,
get label() { return labelService.getHostLabel(REMOTE_HOST_SCHEME, remoteAgentConnection!.remoteAuthority) || localize('remote', "Remote"); }
};
}
}
getExtensionManagementServer(location: URI): IExtensionManagementServer | null {
if (location.scheme === Schemas.file) {
getExtensionManagementServer(extension: IExtension): IExtensionManagementServer {
if (extension.location.scheme === Schemas.file) {
return this.localExtensionManagementServer;
}
if (location.scheme === REMOTE_HOST_SCHEME) {
if (this.remoteExtensionManagementServer && extension.location.scheme === REMOTE_HOST_SCHEME) {
return this.remoteExtensionManagementServer;
}
return null;
throw new Error(`Invalid Extension ${extension.location}`);
}
}

View File

@@ -461,7 +461,7 @@ suite('ExtensionEnablementService Test', () => {
});
test('test remote ui extension is disabled by kind when there is no local server', async () => {
instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService)));
instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService), null));
const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['ui'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
testObject = new TestExtensionEnablementService(instantiationService);
assert.ok(!testObject.isEnabled(localWorkspaceExtension));
@@ -499,7 +499,7 @@ suite('ExtensionEnablementService Test', () => {
});
test('test web extension on remote server is not disabled by kind when there is no local server', async () => {
instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService)));
instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService), anExtensionManagementServer('web', instantiationService)));
const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
testObject = new TestExtensionEnablementService(instantiationService);
assert.ok(testObject.isEnabled(localWorkspaceExtension));
@@ -507,7 +507,7 @@ suite('ExtensionEnablementService Test', () => {
});
test('test web extension with no server is not disabled by kind when there is no local server', async () => {
instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService)));
instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, anExtensionManagementServer('vscode-remote', instantiationService), anExtensionManagementServer('web', instantiationService)));
const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.https }) });
testObject = new TestExtensionEnablementService(instantiationService);
assert.ok(testObject.isEnabled(localWorkspaceExtension));
@@ -515,7 +515,7 @@ suite('ExtensionEnablementService Test', () => {
});
test('test web extension with no server is not disabled by kind when there is no local and remote server', async () => {
instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, null));
instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService(null, null, anExtensionManagementServer('web', instantiationService)));
const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: ['web'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.https }) });
testObject = new TestExtensionEnablementService(instantiationService);
assert.ok(testObject.isEnabled(localWorkspaceExtension));
@@ -526,7 +526,7 @@ suite('ExtensionEnablementService Test', () => {
function anExtensionManagementServer(authority: string, instantiationService: TestInstantiationService): IExtensionManagementServer {
return {
authority,
id: authority,
label: authority,
extensionManagementService: instantiationService.get(IExtensionManagementService)
};
@@ -535,22 +535,23 @@ function anExtensionManagementServer(authority: string, instantiationService: Te
function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService): IExtensionManagementServerService {
const localExtensionManagementServer = anExtensionManagementServer('vscode-local', instantiationService);
const remoteExtensionManagementServer = anExtensionManagementServer('vscode-remote', instantiationService);
return anExtensionManagementServerService(localExtensionManagementServer, remoteExtensionManagementServer);
return anExtensionManagementServerService(localExtensionManagementServer, remoteExtensionManagementServer, null);
}
function anExtensionManagementServerService(localExtensionManagementServer: IExtensionManagementServer | null, remoteExtensionManagementServer: IExtensionManagementServer | null): IExtensionManagementServerService {
function anExtensionManagementServerService(localExtensionManagementServer: IExtensionManagementServer | null, remoteExtensionManagementServer: IExtensionManagementServer | null, webExtensionManagementServer: IExtensionManagementServer | null): IExtensionManagementServerService {
return {
_serviceBrand: undefined,
localExtensionManagementServer,
remoteExtensionManagementServer,
getExtensionManagementServer: (location: URI) => {
if (location.scheme === Schemas.file) {
webExtensionManagementServer: null,
getExtensionManagementServer: (extension: IExtension) => {
if (extension.location.scheme === Schemas.file) {
return localExtensionManagementServer;
}
if (location.scheme === REMOTE_HOST_SCHEME) {
if (extension.location.scheme === REMOTE_HOST_SCHEME) {
return remoteExtensionManagementServer;
}
return null;
return webExtensionManagementServer;
}
};
}