提交 a817a588 编写于 作者: M Matt Bierner

Moving resolve uri logic into openerService

- Add a resolveExternalUri function to the opener service. This service already has to know how to resolve external uris so it makes sense that the function lives there

- Move the tunneling logic for desktop into window.ts

- Make result of resolve disposable
上级 6710df44
......@@ -69,18 +69,28 @@ export class OpenerService extends Disposable implements IOpenerService {
return this._doOpen(resource, options);
}
private _doOpen(resource: URI, options?: OpenOptions): Promise<boolean> {
public async resolveExternalUri(resource: URI, options?: { readonly allowTunneling?: boolean }): Promise<{ resolved: URI, dispose(): void }> {
for (const resolver of this._resolvers.toArray()) {
const result = await resolver.resolveExternalUri(resource, options);
if (result) {
return result;
}
}
return { resolved: resource, dispose: () => { } };
}
private _doOpen(resource: URI, options: OpenOptions | undefined): Promise<boolean> {
const { scheme, path, query, fragment } = resource;
if (equalsIgnoreCase(scheme, Schemas.mailto) || (options && options.openExternal)) {
// open default mail application
return this._doOpenExternal(resource);
return this._doOpenExternal(resource, options);
}
if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) {
// open link in default browser
return this._doOpenExternal(resource);
return this._doOpenExternal(resource, options);
} else if (equalsIgnoreCase(scheme, Schemas.command)) {
// run command or bail out if command isn't known
if (!CommandsRegistry.getCommand(path)) {
......@@ -124,12 +134,9 @@ export class OpenerService extends Disposable implements IOpenerService {
}
}
private async _doOpenExternal(resource: URI): Promise<boolean> {
for (const resolver of this._resolvers.toArray()) {
resource = await resolver.resolveExternalUri(resource);
}
dom.windowOpenNoOpener(encodeURI(resource.toString(true)));
private async _doOpenExternal(resource: URI, options: OpenOptions | undefined): Promise<boolean> {
const { resolved } = await this.resolveExternalUri(resource, options);
dom.windowOpenNoOpener(encodeURI(resolved.toString(true)));
return Promise.resolve(true);
}
......
......@@ -24,7 +24,7 @@ export interface IValidator {
}
export interface IExternalUriResolver {
resolveExternalUri(resource: URI, options?: OpenOptions): Promise<URI>;
resolveExternalUri(resource: URI, options?: OpenOptions): Promise<{ resolved: URI, dispose(): void } | undefined>;
}
export interface IOpenerService {
......@@ -55,6 +55,8 @@ export interface IOpenerService {
*/
open(resource: URI, options?: OpenToSideOptions): Promise<boolean>;
open(resource: URI, options?: OpenExternalOptions): Promise<boolean>;
resolveExternalUri(resource: URI, options?: { readonly allowTunneling?: boolean }): Promise<{ resolved: URI, dispose(): void }>;
}
export const NullOpenerService: IOpenerService = Object.freeze({
......@@ -63,4 +65,5 @@ export const NullOpenerService: IOpenerService = Object.freeze({
registerValidator() { return Disposable.None; },
registerExternalUriResolver() { return Disposable.None; },
open() { return Promise.resolve(false); },
async resolveExternalUri(uri) { return { resolved: uri, dispose() { } }; },
});
......@@ -4,28 +4,26 @@
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, ExtHostWindowShape, IExtHostContext, MainContext, MainThreadWindowShape, IOpenUriOptions } from '../common/extHost.protocol';
import { ITunnelService, RemoteTunnel, extractLocalHostUriMetaDataForPortMapping } from 'vs/platform/remote/common/tunnel';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ExtHostContext, ExtHostWindowShape, IExtHostContext, IOpenUriOptions, MainContext, MainThreadWindowShape } from '../common/extHost.protocol';
@extHostNamedCustomer(MainContext.MainThreadWindow)
export class MainThreadWindow implements MainThreadWindowShape {
private handlePool = 1;
private readonly proxy: ExtHostWindowShape;
private readonly disposables = new DisposableStore();
private readonly _tunnels = new Map<number, { refcount: number, readonly value: Promise<RemoteTunnel> }>();
private readonly resolved = new Map<number, IDisposable>();
constructor(
extHostContext: IExtHostContext,
@IWindowService private readonly windowService: IWindowService,
@IOpenerService private readonly openerService: IOpenerService,
@ITunnelService private readonly tunnelService: ITunnelService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
) {
this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostWindow);
......@@ -36,10 +34,10 @@ export class MainThreadWindow implements MainThreadWindowShape {
dispose(): void {
this.disposables.dispose();
for (const { value } of this._tunnels.values()) {
value.then(tunnel => tunnel.dispose());
for (const value of this.resolved.values()) {
value.dispose();
}
this._tunnels.clear();
this.resolved.clear();
}
$getWindowVisibility(): Promise<boolean> {
......@@ -51,61 +49,23 @@ export class MainThreadWindow implements MainThreadWindowShape {
return this.openerService.open(uri, { openExternal: true, allowTunneling: options.allowTunneling });
}
async $resolveExternalUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise<UriComponents> {
async $resolveExternalUri(uriComponents: UriComponents, options: IOpenUriOptions): Promise<{ handle: number, result: UriComponents }> {
const uri = URI.revive(uriComponents);
const embedderResolver = this.getEmbedderResolver();
if (embedderResolver) {
return embedderResolver(uri);
}
return this.resolveExternalUri(uri, options);
}
const handle = ++this.handlePool;
async $releaseResolvedExternalUri(uriComponents: UriComponents): Promise<boolean> {
if (this.getEmbedderResolver()) {
// Embedder handled forwarding so there's nothing for us to clean up
return true;
}
const result = await this.openerService.resolveExternalUri(uri, options);
this.resolved.set(handle, result);
const portMappingRequest = extractLocalHostUriMetaDataForPortMapping(URI.from(uriComponents));
if (portMappingRequest) {
const existing = this._tunnels.get(portMappingRequest.port);
if (existing) {
if (--existing.refcount <= 0) {
existing.value.then(tunnel => tunnel.dispose());
this._tunnels.delete(portMappingRequest.port);
}
}
}
return true;
return { handle, result: result.resolved };
}
private getEmbedderResolver(): undefined | ((uri: URI) => Promise<URI>) {
return this.environmentService.options && this.environmentService.options.resolveExternalUri;
}
private async resolveExternalUri(uri: URI, options: IOpenUriOptions): Promise<URI> {
if (options.allowTunneling && !!this.environmentService.configuration.remoteAuthority) {
const portMappingRequest = extractLocalHostUriMetaDataForPortMapping(uri);
if (portMappingRequest) {
const tunnel = await this.retainOrCreateTunnel(portMappingRequest.port);
if (tunnel) {
return uri.with({ authority: `127.0.0.1:${tunnel.tunnelLocalPort}` });
}
}
}
return uri;
}
private retainOrCreateTunnel(remotePort: number): Promise<RemoteTunnel> | undefined {
const existing = this._tunnels.get(remotePort);
if (existing) {
++existing.refcount;
return existing.value;
}
const tunnel = this.tunnelService.openTunnel(remotePort);
if (tunnel) {
this._tunnels.set(remotePort, { refcount: 1, value: tunnel });
async $releaseResolvedExternalUri(handle: number): Promise<boolean> {
const entry = this.resolved.get(handle);
if (entry) {
entry.dispose();
this.resolved.delete(handle);
return true;
}
return tunnel;
return false;
}
}
......@@ -744,8 +744,8 @@ export interface IOpenUriOptions {
export interface MainThreadWindowShape extends IDisposable {
$getWindowVisibility(): Promise<boolean>;
$openUri(uri: UriComponents, options: IOpenUriOptions): Promise<boolean>;
$resolveExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise<UriComponents>;
$releaseResolvedExternalUri(uri: UriComponents): Promise<boolean>;
$resolveExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise<{ readonly handle: number, readonly result: UriComponents }>;
$releaseResolvedExternalUri(handle: number): Promise<boolean>;
}
// -- extension host
......
......@@ -62,11 +62,11 @@ export class ExtHostWindow implements ExtHostWindowShape {
return Promise.reject(`Invalid scheme '${uri.scheme}'`);
}
const resolved = await this._proxy.$resolveExternalUri(uri, options);
const { result, handle } = await this._proxy.$resolveExternalUri(uri, options);
return {
resolved: URI.from(resolved),
resolved: URI.from(result),
dispose: once(() => {
this._proxy.$releaseResolvedExternalUri(uri);
this._proxy.$releaseResolvedExternalUri(handle);
}),
};
}
......
......@@ -18,7 +18,12 @@ export class ExternalUriResolverContribution extends Disposable implements IWork
if (_workbenchEnvironmentService.options && _workbenchEnvironmentService.options.resolveExternalUri) {
this._register(_openerService.registerExternalUriResolver({
resolveExternalUri: async (resource) => {
return _workbenchEnvironmentService.options!.resolveExternalUri!(resource);
return {
resolved: await _workbenchEnvironmentService.options!.resolveExternalUri!(resource),
dispose: () => {
// TODO
}
};
}
}));
}
......
......@@ -439,17 +439,20 @@ export class ElectronWindow extends Disposable {
});
this.openerService.registerExternalUriResolver({
resolveExternalUri: async (uri: URI, options?: OpenOptions): Promise<URI> => {
resolveExternalUri: async (uri: URI, options?: OpenOptions) => {
if (options && options.allowTunneling) {
const portMappingRequest = extractLocalHostUriMetaDataForPortMapping(uri);
if (portMappingRequest) {
const tunnel = await this.tunnelService.openTunnel(portMappingRequest.port);
if (tunnel) {
return uri.with({ authority: `127.0.0.1:${tunnel.tunnelLocalPort}` });
return {
resolved: uri.with({ authority: `127.0.0.1:${tunnel.tunnelLocalPort}` }),
dispose: () => tunnel.dispose(),
};
}
}
}
return uri;
return undefined;
}
});
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册