diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 6d43b392dfb..66c0baf0401 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -70,7 +70,7 @@ import { startsWith } from 'vs/base/common/strings'; import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; import { IBackupMainService } from 'vs/platform/backup/common/backup'; import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; -import { URLService } from 'vs/platform/url/common/urlService'; +import { URLService } from 'vs/platform/url/node/urlService'; import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { statSync } from 'fs'; import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc'; diff --git a/src/vs/platform/url/common/url.ts b/src/vs/platform/url/common/url.ts index f73971c4e00..87a5a8fbeec 100644 --- a/src/vs/platform/url/common/url.ts +++ b/src/vs/platform/url/common/url.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from 'vs/base/common/uri'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable } from 'vs/base/common/lifecycle'; export const IURLService = createDecorator('urlService'); @@ -14,8 +14,17 @@ export interface IURLHandler { } export interface IURLService { - _serviceBrand: any; + + _serviceBrand: ServiceIdentifier; + + /** + * Create a URL that can be called to trigger IURLhandlers. + * The URL that gets passed to the IURLHandlers carries over + * any of the provided IURLCreateOption values. + */ + create(options?: Partial): URI; open(url: URI): Promise; + registerHandler(handler: IURLHandler): IDisposable; } diff --git a/src/vs/platform/url/common/urlService.ts b/src/vs/platform/url/common/urlService.ts index 66d4abd6722..eb216a6b335 100644 --- a/src/vs/platform/url/common/urlService.ts +++ b/src/vs/platform/url/common/urlService.ts @@ -4,18 +4,20 @@ *--------------------------------------------------------------------------------------------*/ import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; -import { URI } from 'vs/base/common/uri'; -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { first } from 'vs/base/common/async'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { values } from 'vs/base/common/map'; +import { first } from 'vs/base/common/async'; +import { toDisposable, IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; -export class URLService implements IURLService { +export abstract class AbstractURLService extends Disposable implements IURLService { _serviceBrand!: ServiceIdentifier; private handlers = new Set(); + abstract create(options?: Partial): URI; + open(uri: URI): Promise { const handlers = values(this.handlers); return first(handlers.map(h => () => h.handleURL(uri)), undefined, false).then(val => val || false); diff --git a/src/vs/platform/url/node/urlIpc.ts b/src/vs/platform/url/node/urlIpc.ts index 2ced486d02d..0e70e9c6534 100644 --- a/src/vs/platform/url/node/urlIpc.ts +++ b/src/vs/platform/url/node/urlIpc.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; @@ -39,6 +39,10 @@ export class URLServiceChannelClient implements IURLService { registerHandler(handler: IURLHandler): IDisposable { throw new Error('Not implemented.'); } + + create(_options?: Partial): URI { + throw new Error('Method not implemented.'); + } } export class URLHandlerChannel implements IServerChannel { diff --git a/src/vs/platform/url/node/urlService.ts b/src/vs/platform/url/node/urlService.ts new file mode 100644 index 00000000000..adf857493e8 --- /dev/null +++ b/src/vs/platform/url/node/urlService.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI, UriComponents } from 'vs/base/common/uri'; +import product from 'vs/platform/product/node/product'; +import { AbstractURLService } from 'vs/platform/url/common/urlService'; + +export class URLService extends AbstractURLService { + + create(options?: Partial): URI { + const { authority, path, query, fragment } = options ? options : { authority: undefined, path: undefined, query: undefined, fragment: undefined }; + + return URI.from({ scheme: product.urlProtocol, authority, path, query, fragment }); + } +} diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 30f5775ce51..654f61ec02f 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1157,4 +1157,50 @@ declare module 'vscode' { } //#endregion + + // #region Ben - extension auth flow (desktop+web) + + export namespace env { + + export interface AppUriOptions { + payload?: { + path?: string; + query?: string; + fragment?: string; + }; + } + + /** + * Creates a Uri that - if opened in a browser - will result in a + * registered [UriHandler](#UriHandler) to fire. The handler's + * Uri will be configured with the path, query and fragment of + * [AppUriOptions](#AppUriOptions) if provided, otherwise it will be empty. + * + * Extensions should not make any assumptions about the resulting + * Uri and should not alter it in anyway. Rather, extensions can e.g. + * use this Uri in an authentication flow, by adding the Uri as + * callback query argument to the server to authenticate to. + * + * Note: If the server decides to add additional query parameters to the Uri + * (e.g. a token or secret), it will appear in the Uri that is passed + * to the [UriHandler](#UriHandler). + * + * **Example** of an authentication flow: + * ```typescript + * vscode.window.registerUriHandler({ + * handleUri(uri: vscode.Uri): vscode.ProviderResult { + * if (uri.path === '/did-authenticate') { + * console.log(uri.toString()); + * } + * } + * }); + * + * const callableUri = await vscode.env.createAppUri({ payload: { path: '/did-authenticate' } }); + * await vscode.env.openExternal(callableUri); + * ``` + */ + export function createAppUri(options?: AppUriOptions): Thenable; + } + + //#endregion } diff --git a/src/vs/workbench/api/browser/mainThreadUrls.ts b/src/vs/workbench/api/browser/mainThreadUrls.ts index 224d51f978c..75fdde3e495 100644 --- a/src/vs/workbench/api/browser/mainThreadUrls.ts +++ b/src/vs/workbench/api/browser/mainThreadUrls.ts @@ -6,7 +6,7 @@ import { ExtHostContext, IExtHostContext, MainContext, MainThreadUrlsShape, ExtHostUrlsShape } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer } from '../common/extHostCustomers'; import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; -import { URI } from 'vs/base/common/uri'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IExtensionUrlHandler } from 'vs/workbench/services/extensions/common/inactiveExtensionUrlHandler'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; @@ -68,6 +68,16 @@ export class MainThreadUrls implements MainThreadUrlsShape { return Promise.resolve(undefined); } + async $createAppUri(extensionId: ExtensionIdentifier, options?: { payload?: Partial }): Promise { + const payload: Partial = options && options.payload ? options.payload : Object.create(null); + + // we define the authority to be the extension ID to ensure + // that the Uri gets routed back to the extension properly. + payload.authority = extensionId.value; + + return this.urlService.create(payload); + } + dispose(): void { this.handlers.forEach(({ disposable }) => disposable.dispose()); this.handlers.clear(); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 755d23b1d4f..5b4a69da168 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -234,6 +234,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I get appName() { return initData.environment.appName; }, get appRoot() { return initData.environment.appRoot!.fsPath; }, get uriScheme() { return initData.environment.appUriScheme; }, + createAppUri(options?) { + checkProposedApiEnabled(extension); + return extHostUrls.createAppUri(extension.identifier, options); + }, get logLevel() { checkProposedApiEnabled(extension); return typeConverters.LogLevel.to(extHostLogService.getLevel()); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9e9abe2e834..d6ca16a738c 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -567,6 +567,7 @@ export interface ExtHostWebviewsShape { export interface MainThreadUrlsShape extends IDisposable { $registerUriHandler(handle: number, extensionId: ExtensionIdentifier): Promise; $unregisterUriHandler(handle: number): Promise; + $createAppUri(extensionId: ExtensionIdentifier, options?: { payload?: Partial }): Promise; } export interface ExtHostUrlsShape { diff --git a/src/vs/workbench/api/common/extHostUrls.ts b/src/vs/workbench/api/common/extHostUrls.ts index 778d957cbc7..28647224848 100644 --- a/src/vs/workbench/api/common/extHostUrls.ts +++ b/src/vs/workbench/api/common/extHostUrls.ts @@ -55,4 +55,8 @@ export class ExtHostUrls implements ExtHostUrlsShape { return Promise.resolve(undefined); } + + async createAppUri(extensionId: ExtensionIdentifier, options?: vscode.env.AppUriOptions): Promise { + return URI.revive(await this._proxy.$createAppUri(extensionId, options)); + } } diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 93708d0bc40..895a8a0393f 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -7,11 +7,8 @@ import { URI } from 'vs/base/common/uri'; import * as browser from 'vs/base/browser/browser'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Event } from 'vs/base/common/event'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IURLHandler, IURLService } from 'vs/platform/url/common/url'; import { ILogService } from 'vs/platform/log/common/log'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IUpdateService, State } from 'vs/platform/update/common/update'; import { IWindowService, INativeOpenDialogOptions, IEnterWorkspaceResult, IURIToOpen, IMessageBoxResult, IWindowsService, IOpenSettings, IWindowSettings } from 'vs/platform/windows/common/windows'; @@ -37,29 +34,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService // tslint:disable-next-line: import-patterns import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats'; -//#region Extension URL Handler - -export const IExtensionUrlHandler = createDecorator('inactiveExtensionUrlHandler'); - -export interface IExtensionUrlHandler { - readonly _serviceBrand: any; - registerExtensionHandler(extensionId: ExtensionIdentifier, handler: IURLHandler): void; - unregisterExtensionHandler(extensionId: ExtensionIdentifier): void; -} - -export class SimpleExtensionURLHandler implements IExtensionUrlHandler { - - _serviceBrand: any; - - registerExtensionHandler(extensionId: ExtensionIdentifier, handler: IURLHandler): void { } - - unregisterExtensionHandler(extensionId: ExtensionIdentifier): void { } -} - -registerSingleton(IExtensionUrlHandler, SimpleExtensionURLHandler, true); - -//#endregion - //#region Update export class SimpleUpdateService implements IUpdateService { @@ -94,24 +68,6 @@ registerSingleton(IUpdateService, SimpleUpdateService); //#endregion -//#region URL - -export class SimpleURLService implements IURLService { - _serviceBrand: any; - - open(url: URI): Promise { - return Promise.resolve(false); - } - - registerHandler(handler: IURLHandler): IDisposable { - return Disposable.None; - } -} - -registerSingleton(IURLService, SimpleURLService); - -//#endregion - //#region Window export class SimpleWindowService extends Disposable implements IWindowService { diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts index 70851b908c9..ab7a75444b5 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts @@ -15,7 +15,7 @@ import { IExtensionEnablementService } from 'vs/workbench/services/extensionMana import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { Emitter } from 'vs/base/common/event'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test'; -import { URLService } from 'vs/platform/url/common/urlService'; +import { URLService } from 'vs/platform/url/node/urlService'; import { IURLService } from 'vs/platform/url/common/url'; import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index 32c9e48f8aa..93e10545ebd 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -31,7 +31,7 @@ import { TestContextService, TestWindowService, TestSharedProcessService } from import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IWindowService } from 'vs/platform/windows/common/windows'; -import { URLService } from 'vs/platform/url/common/urlService'; +import { URLService } from 'vs/platform/url/node/urlService'; import { URI } from 'vs/base/common/uri'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts index 83231f515ea..1ac40b499a2 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -40,7 +40,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { INotificationService, Severity, IPromptChoice, IPromptOptions } from 'vs/platform/notification/common/notification'; -import { URLService } from 'vs/platform/url/common/urlService'; +import { URLService } from 'vs/platform/url/node/urlService'; import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index 318a7e17e45..50dac5ecc8b 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -31,7 +31,7 @@ import { TestContextService, TestWindowService, TestSharedProcessService } from import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IWindowService } from 'vs/platform/windows/common/windows'; -import { URLService } from 'vs/platform/url/common/urlService'; +import { URLService } from 'vs/platform/url/node/urlService'; import { URI } from 'vs/base/common/uri'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { SinonStub } from 'sinon'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index c87fed721f4..f6ca00e7b88 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -34,7 +34,7 @@ import { IWindowService } from 'vs/platform/windows/common/windows'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { ProgressService } from 'vs/workbench/services/progress/browser/progressService'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { URLService } from 'vs/platform/url/common/urlService'; +import { URLService } from 'vs/platform/url/node/urlService'; import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; diff --git a/src/vs/workbench/services/url/browser/urlService.ts b/src/vs/workbench/services/url/browser/urlService.ts new file mode 100644 index 00000000000..f69c231457d --- /dev/null +++ b/src/vs/workbench/services/url/browser/urlService.ts @@ -0,0 +1,177 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IURLService } from 'vs/platform/url/common/url'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { ServiceIdentifier, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { AbstractURLService } from 'vs/platform/url/common/urlService'; +import { Event, Emitter } from 'vs/base/common/event'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IRequestService } from 'vs/platform/request/common/request'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { streamToBuffer } from 'vs/base/common/buffer'; +import { ILogService } from 'vs/platform/log/common/log'; +import { generateUuid } from 'vs/base/common/uuid'; + +export interface IURLCallbackProvider { + + /** + * Indicates that a Uri has been opened outside of VSCode. The Uri + * will be forwarded to all installed Uri handlers in the system. + */ + readonly onCallback: Event; + + /** + * Creates a Uri that - if opened in a browser - must result in + * the `onCallback` to fire. + * + * The optional `Partial` must be properly restored for + * the Uri passed to the `onCallback` handler. + * + * For example: if a Uri is to be created with `scheme:"vscode"`, + * `authority:"foo"` and `path:"bar"` the `onCallback` should fire + * with a Uri `vscode://foo/bar`. + * + * If there are additional `query` values in the Uri, they should + * be added to the list of provided `query` arguments from the + * `Partial`. + */ + create(options?: Partial): URI; +} + +export class BrowserURLService extends AbstractURLService { + + _serviceBrand!: ServiceIdentifier; + + private provider: IURLCallbackProvider; + + constructor( + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(); + + this.provider = environmentService.options && environmentService.options.urlCallbackProvider ? environmentService.options.urlCallbackProvider : instantiationService.createInstance(SelfhostURLCallbackProvider); + + this.registerListeners(); + } + + private registerListeners(): void { + this._register(this.provider.onCallback(uri => this.open(uri))); + } + + create(options?: Partial): URI { + return this.provider.create(options); + } +} + +class SelfhostURLCallbackProvider extends Disposable implements IURLCallbackProvider { + + static FETCH_INTERVAL = 500; // fetch every 500ms + static FETCH_TIMEOUT = 5 * 60 * 1000; // ...but stop after 5min + + static QUERY_KEYS = { + REQUEST_ID: 'vscode-requestId', + SCHEME: 'vscode-scheme', + AUTHORITY: 'vscode-authority', + PATH: 'vscode-path', + QUERY: 'vscode-query', + FRAGMENT: 'vscode-fragment' + }; + + private readonly _onCallback: Emitter = this._register(new Emitter()); + readonly onCallback: Event = this._onCallback.event; + + constructor( + @IRequestService private readonly requestService: IRequestService, + @ILogService private readonly logService: ILogService + ) { + super(); + } + + create(options?: Partial): URI { + const queryValues: Map = new Map(); + + const requestId = generateUuid(); + queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.REQUEST_ID, requestId); + + const { scheme, authority, path, query, fragment } = options ? options : { scheme: undefined, authority: undefined, path: undefined, query: undefined, fragment: undefined }; + + if (scheme) { + queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.SCHEME, scheme); + } + + if (authority) { + queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.AUTHORITY, authority); + } + + if (path) { + queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.PATH, path); + } + + if (query) { + queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.QUERY, query); + } + + if (fragment) { + queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.FRAGMENT, fragment); + } + + // Start to poll on the callback being fired + this.periodicFetchCallback(requestId, Date.now()); + + return this.doCreateUri('callback', queryValues); + } + + private async periodicFetchCallback(requestId: string, startTime: number): Promise { + + // Ask server for callback results + const queryValues: Map = new Map(); + queryValues.set(SelfhostURLCallbackProvider.QUERY_KEYS.REQUEST_ID, requestId); + + const result = await this.requestService.request({ + url: this.doCreateUri('fetch-callback', queryValues).toString(true) + }, CancellationToken.None); + + // Check for callback results + const content = await streamToBuffer(result.stream); + if (content.byteLength > 0) { + try { + this._onCallback.fire(URI.revive(JSON.parse(content.toString()))); + } catch (error) { + this.logService.error(error); + } + + return; // done + } + + // Continue fetching unless we hit the timeout + if (Date.now() - startTime < SelfhostURLCallbackProvider.FETCH_TIMEOUT) { + setTimeout(() => this.periodicFetchCallback(requestId, startTime), SelfhostURLCallbackProvider.FETCH_INTERVAL); + } + } + + private doCreateUri(path: string, queryValues: Map): URI { + let query: string | undefined = undefined; + + if (queryValues) { + let index = 0; + queryValues.forEach((value, key) => { + if (!query) { + query = ''; + } + + const prefix = (index++ === 0) ? '' : '&'; + query += `${prefix}${key}=${encodeURIComponent(value)}`; + }); + } + + return URI.parse(window.location.href).with({ path, query }); + } +} + +registerSingleton(IURLService, BrowserURLService, true); diff --git a/src/vs/platform/url/electron-browser/urlService.ts b/src/vs/workbench/services/url/electron-browser/urlService.ts similarity index 89% rename from src/vs/platform/url/electron-browser/urlService.ts rename to src/vs/workbench/services/url/electron-browser/urlService.ts index 1b1c88721b7..f1a73d383d9 100644 --- a/src/vs/platform/url/electron-browser/urlService.ts +++ b/src/vs/workbench/services/url/electron-browser/urlService.ts @@ -7,9 +7,10 @@ import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { URI } from 'vs/base/common/uri'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { URLServiceChannelClient, URLHandlerChannel } from 'vs/platform/url/node/urlIpc'; -import { URLService } from 'vs/platform/url/common/urlService'; +import { URLService } from 'vs/platform/url/node/urlService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import product from 'vs/platform/product/node/product'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class RelayURLService extends URLService implements IURLHandler { @@ -43,3 +44,5 @@ export class RelayURLService extends URLService implements IURLHandler { return super.open(uri); } } + +registerSingleton(IURLService, RelayURLService); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 198e68e65bb..2fdb06fe058 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -52,6 +52,7 @@ import 'vs/workbench/browser/parts/views/views'; //#region --- workbench services +import 'vs/workbench/services/extensions/common/inactiveExtensionUrlHandler'; import 'vs/workbench/services/bulkEdit/browser/bulkEditService'; import 'vs/workbench/services/keybinding/common/keybindingEditing'; import 'vs/workbench/services/decorations/browser/decorationsService'; diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index edfd8a273f3..a566d1d7900 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -30,7 +30,6 @@ import 'vs/workbench/electron-browser/desktop.main'; import 'vs/workbench/services/integrity/node/integrityService'; import 'vs/workbench/services/textMate/electron-browser/textMateService'; import 'vs/workbench/services/workspace/electron-browser/workspaceEditingService'; -import 'vs/workbench/services/extensions/common/inactiveExtensionUrlHandler'; import 'vs/workbench/services/search/node/searchService'; import 'vs/workbench/services/output/node/outputChannelModelService'; import 'vs/workbench/services/textfile/node/textFileService'; @@ -49,6 +48,7 @@ import 'vs/workbench/services/accessibility/node/accessibilityService'; import 'vs/workbench/services/remote/node/tunnelService'; import 'vs/workbench/services/backup/node/backupFileService'; import 'vs/workbench/services/credentials/node/credentialsService'; +import 'vs/workbench/services/url/electron-browser/urlService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -70,8 +70,6 @@ import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { WorkspacesService } from 'vs/platform/workspaces/electron-browser/workspacesService'; import { IMenubarService } from 'vs/platform/menubar/node/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-browser/menubarService'; -import { IURLService } from 'vs/platform/url/common/url'; -import { RelayURLService } from 'vs/platform/url/electron-browser/urlService'; import { StaticExtensionsService, IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; registerSingleton(IClipboardService, ClipboardService, true); @@ -84,7 +82,6 @@ registerSingleton(IUpdateService, UpdateService); registerSingleton(IIssueService, IssueService); registerSingleton(IWorkspacesService, WorkspacesService); registerSingleton(IMenubarService, MenubarService); -registerSingleton(IURLService, RelayURLService); registerSingleton(IStaticExtensionsService, class extends StaticExtensionsService { constructor() { super([]); } }); //#endregion diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index dd84853e87a..844ff1da56a 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -10,6 +10,7 @@ import { IFileSystemProvider } from 'vs/platform/files/common/files'; import { IWebSocketFactory } from 'vs/platform/remote/browser/browserSocketFactory'; import { ICredentialsProvider } from 'vs/workbench/services/credentials/browser/credentialsService'; import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; +import { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService'; export interface IWorkbenchConstructionOptions { @@ -65,6 +66,11 @@ export interface IWorkbenchConstructionOptions { * Experimental: Add static extensions that cannot be uninstalled but only be disabled. */ staticExtensions?: { packageJSON: IExtensionManifest, extensionLocation: UriComponents }[]; + + /** + * Experimental: Support for URL callbacks. + */ + urlCallbackProvider?: IURLCallbackProvider; } /** diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 20db7d07378..681fc606b6a 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -37,6 +37,7 @@ import 'vs/workbench/services/extensionManagement/common/extensionManagementServ import 'vs/workbench/services/telemetry/browser/telemetryService'; import 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; import 'vs/workbench/services/credentials/browser/credentialsService'; +import 'vs/workbench/services/url/browser/urlService'; import 'vs/workbench/browser/web.simpleservices'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions';