提交 b8c4254a 编写于 作者: S Sandeep Somavarapu

Fix #57636

上级 49837d16
...@@ -11,9 +11,16 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' ...@@ -11,9 +11,16 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; import { IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { IExtensionManagementService, IExtensionIdentifier, IExtensionEnablementService, EnablementState, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { Action } from 'vs/base/common/actions';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
const FIVE_MINUTES = 5 * 60 * 1000; const FIVE_MINUTES = 5 * 60 * 1000;
const THIRTY_SECONDS = 30 * 1000; const THIRTY_SECONDS = 30 * 1000;
const URL_TO_HANDLE = 'extensionUrlHandler.urlToHandle';
function isExtensionId(value: string): boolean { function isExtensionId(value: string): boolean {
return /^[a-z0-9][a-z0-9\-]*\.[a-z0-9][a-z0-9\-]*$/i.test(value); return /^[a-z0-9][a-z0-9\-]*\.[a-z0-9][a-z0-9\-]*$/i.test(value);
...@@ -47,9 +54,20 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { ...@@ -47,9 +54,20 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
constructor( constructor(
@IURLService urlService: IURLService, @IURLService urlService: IURLService,
@IExtensionService private extensionService: IExtensionService, @IExtensionService private extensionService: IExtensionService,
@IDialogService private dialogService: IDialogService @IDialogService private dialogService: IDialogService,
@INotificationService private notificationService: INotificationService,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService,
@IExtensionEnablementService private extensionEnablementService: IExtensionEnablementService,
@IWindowService private windowService: IWindowService,
@IExtensionGalleryService private galleryService: IExtensionGalleryService,
@IStorageService private storageService: IStorageService
) { ) {
const interval = setInterval(() => this.garbageCollect(), THIRTY_SECONDS); const interval = setInterval(() => this.garbageCollect(), THIRTY_SECONDS);
const urlToHandleValue = this.storageService.get(URL_TO_HANDLE, StorageScope.WORKSPACE);
if (urlToHandleValue) {
this.storageService.remove(URL_TO_HANDLE, StorageScope.WORKSPACE);
this.handleURL(URI.revive(JSON.parse(urlToHandleValue)), true);
}
this.disposable = combinedDisposable([ this.disposable = combinedDisposable([
urlService.registerHandler(this), urlService.registerHandler(this),
...@@ -57,7 +75,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { ...@@ -57,7 +75,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
]); ]);
} }
handleURL(uri: URI): TPromise<boolean> { handleURL(uri: URI, confirmed?: boolean): TPromise<boolean> {
if (!isExtensionId(uri.authority)) { if (!isExtensionId(uri.authority)) {
return TPromise.as(false); return TPromise.as(false);
} }
...@@ -69,18 +87,10 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { ...@@ -69,18 +87,10 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
const extension = extensions.filter(e => e.id === extensionId)[0]; const extension = extensions.filter(e => e.id === extensionId)[0];
if (!extension) { if (!extension) {
return TPromise.as(false); return this.handleUnhandledURL(uri, { id: extensionId }).then(() => false);
} }
return this.dialogService.confirm({ const handleURL = () => {
message: localize('confirmUrl', "Allow an extension to open this URL?", extensionId),
detail: `${extension.displayName || extension.name} (${extensionId}) wants to open a URL:\n\n${uri.toString()}`
}).then(result => {
if (!result.confirmed) {
return TPromise.as(true);
}
const handler = this.extensionHandlers.get(extensionId); const handler = this.extensionHandlers.get(extensionId);
if (handler) { if (handler) {
if (!wasHandlerAvailable) { if (!wasHandlerAvailable) {
...@@ -106,6 +116,22 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { ...@@ -106,6 +116,22 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
// activate the extension // activate the extension
return this.extensionService.activateByEvent(`onUri:${extensionId}`) return this.extensionService.activateByEvent(`onUri:${extensionId}`)
.then(() => true); .then(() => true);
};
if (confirmed) {
return handleURL();
}
return this.dialogService.confirm({
message: localize('confirmUrl', "Allow an extension to open this URL?", extensionId),
detail: `${extension.displayName || extension.name} (${extensionId}) wants to open a URL:\n\n${uri.toString()}`
}).then(result => {
if (!result.confirmed) {
return TPromise.as(true);
}
return handleURL();
}); });
}); });
} }
...@@ -126,6 +152,93 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { ...@@ -126,6 +152,93 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
this.extensionHandlers.delete(extensionId); this.extensionHandlers.delete(extensionId);
} }
private async handleUnhandledURL(uri: URI, extensionIdentifier: IExtensionIdentifier): Promise<void> {
const installedExtensions = await this.extensionManagementService.getInstalled();
const extension = installedExtensions.filter(e => areSameExtensions(e.galleryIdentifier, extensionIdentifier))[0];
// Extension is installed
if (extension) {
const enabled = this.extensionEnablementService.isEnabled(extension);
// Extension is not running. Reload the window to handle.
if (enabled) {
this.dialogService.confirm({
message: localize('reloadAndHandle', "Extension '{0}' is not loaded. Would you like to reload the window to load the extension and open the URL?", extension.manifest.displayName || extension.manifest.name),
detail: `${extension.manifest.displayName || extension.manifest.name} (${extensionIdentifier.id}) wants to open a URL:\n\n${uri.toString()}`
}).then(result => {
if (result.confirmed) {
return this.reloadAndHandle(uri);
}
return null;
});
}
// Extension is disabled. Enable the extension and reload the window to handle.
else {
this.dialogService.confirm({
message: localize('enableAndHandle', "Extension '{0}' is disabled. Would you like to enable the extension and reload the window to open the URL?", extension.manifest.displayName || extension.manifest.name),
detail: `${extension.manifest.displayName || extension.manifest.name} (${extensionIdentifier.id}) wants to open a URL:\n\n${uri.toString()}`
}).then(result => {
if (result.confirmed) {
return this.extensionEnablementService.setEnablement(extension, EnablementState.Enabled)
.then(() => this.reloadAndHandle(uri));
}
return null;
});
}
}
// Extnesion is not installed
else {
const galleryExtension = await this.galleryService.getExtension(extensionIdentifier);
if (galleryExtension) {
// Install the Extension and reload the window to handle.
this.dialogService.confirm({
message: localize('installAndHandle', "Extension '{0}' is not installed. Would you like to install the extension and reload the window to open this URL?", galleryExtension.displayName || galleryExtension.name),
detail: `${galleryExtension.displayName || galleryExtension.name} (${extensionIdentifier.id}) wants to open a URL:\n\n${uri.toString()}`
}).then(async result => {
if (result.confirmed) {
let notificationHandle = this.notificationService.notify({ severity: Severity.Info, message: localize('Installing', "Installing {0}...", galleryExtension.displayName || galleryExtension.name) });
notificationHandle.progress.infinite();
notificationHandle.onDidClose(() => notificationHandle = null);
try {
await this.extensionManagementService.installFromGallery(galleryExtension);
const reloadMessage = localize('reload', "Would you like to reload the window and open the URL '{0}'?", uri.toString());
const reloadActionLabel = localize('Reload', "Reload Window and Open URL");
if (notificationHandle) {
notificationHandle.progress.done();
notificationHandle.updateMessage(reloadMessage);
notificationHandle.updateActions({
primary: [new Action('reloadWindow', reloadActionLabel, null, true, () => this.reloadAndHandle(uri))]
});
} else {
this.notificationService.prompt(Severity.Info, reloadMessage,
[{
label: reloadActionLabel,
run: () => this.reloadAndHandle(uri)
}]
);
}
} catch (e) {
if (notificationHandle) {
notificationHandle.progress.done();
notificationHandle.updateSeverity(Severity.Error);
notificationHandle.updateMessage(e);
} else {
this.notificationService.error(e);
}
}
}
});
}
}
}
private reloadAndHandle(url: URI): TPromise<void> {
this.storageService.store(URL_TO_HANDLE, JSON.stringify(url.toJSON()), StorageScope.WORKSPACE);
return this.windowService.reloadWindow();
}
// forget about all uris buffered more than 5 minutes ago // forget about all uris buffered more than 5 minutes ago
private garbageCollect(): void { private garbageCollect(): void {
const now = new Date().getTime(); const now = new Date().getTime();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册