mainThreadUriOpeners.ts 3.6 KB
Newer Older
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import { CancellationToken } from 'vs/base/common/cancellation';
7
import { Disposable } from 'vs/base/common/lifecycle';
8 9
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
M
Matt Bierner 已提交
10 11 12
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { ExtHostContext, ExtHostUriOpenersShape, IExtHostContext, MainContext, MainThreadUriOpenersShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExternalOpenerEntry, IExternalOpenerProvider, IExternalUriOpenerService } from 'vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService';
13 14 15
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { extHostNamedCustomer } from '../common/extHostCustomers';

16 17
interface RegisteredOpenerMetadata {
	readonly schemes: ReadonlySet<string>;
M
Matt Bierner 已提交
18 19
	readonly extensionId: ExtensionIdentifier;
	readonly label: string;
20 21
}

22
@extHostNamedCustomer(MainContext.MainThreadUriOpeners)
23
export class MainThreadUriOpeners extends Disposable implements MainThreadUriOpenersShape, IExternalOpenerProvider {
24 25

	private readonly proxy: ExtHostUriOpenersShape;
26
	private readonly registeredOpeners = new Map<number, RegisteredOpenerMetadata>();
27 28 29

	constructor(
		context: IExtHostContext,
30
		@IExternalUriOpenerService private readonly externalUriOpenerService: IExternalUriOpenerService,
31 32
		@IExtensionService private readonly extensionService: IExtensionService,
	) {
33
		super();
34 35
		this.proxy = context.getProxy(ExtHostContext.ExtHostUriOpeners);

36
		this._register(this.externalUriOpenerService.registerExternalOpenerProvider(this));
37
	}
38

M
Matt Bierner 已提交
39
	public async provideExternalOpeners(href: string | URI): Promise<readonly ExternalOpenerEntry[]> {
40
		const targetUri = typeof href === 'string' ? URI.parse(href) : href;
41 42 43

		// Currently we only allow openers for http and https urls
		if (targetUri.scheme !== Schemas.http && targetUri.scheme !== Schemas.https) {
M
Matt Bierner 已提交
44
			return [];
45 46 47 48
		}

		await this.extensionService.activateByEvent(`onUriOpen:${targetUri.scheme}`);

M
Matt Bierner 已提交
49
		// If there are no handlers there is no point in making a round trip
50
		const hasHandler = Array.from(this.registeredOpeners.values()).some(x => x.schemes.has(targetUri.scheme));
M
Matt Bierner 已提交
51
		if (!hasHandler) {
M
Matt Bierner 已提交
52
			return [];
53 54
		}

M
Matt Bierner 已提交
55 56 57
		const openerHandles = await this.proxy.$getOpenersForUri(targetUri, CancellationToken.None);

		return openerHandles.map(handle => this.openerForCommand(handle, targetUri));
58 59
	}

M
Matt Bierner 已提交
60 61
	private openerForCommand(openerHandle: number, sourceUri: URI): ExternalOpenerEntry {
		const metadata = this.registeredOpeners.get(openerHandle)!;
62
		return {
M
Matt Bierner 已提交
63 64
			id: metadata.extensionId.value,
			label: metadata.label,
65
			openExternal: async (href) => {
M
Matt Bierner 已提交
66 67
				const resolveUri = URI.parse(href);
				await this.proxy.$openUri(openerHandle, { resolveUri, sourceUri }, CancellationToken.None);
68
				return true;
69
			},
70
		};
71 72
	}

M
Matt Bierner 已提交
73 74 75 76 77 78 79 80 81 82 83
	async $registerUriOpener(
		handle: number,
		schemes: readonly string[],
		extensionId: ExtensionIdentifier,
		label: string,
	): Promise<void> {
		this.registeredOpeners.set(handle, {
			schemes: new Set(schemes),
			label,
			extensionId,
		});
84 85 86
	}

	async $unregisterUriOpener(handle: number): Promise<void> {
87
		this.registeredOpeners.delete(handle);
88 89 90
	}

	dispose(): void {
91
		super.dispose();
92
		this.registeredOpeners.clear();
93 94
	}
}