Skip to content

Commit

Permalink
Implement stubbed window#registerURIhandler
Browse files Browse the repository at this point in the history
fixes #13169

contributed on behalf of STMicroelectronics

Signed-off-by: Remi Schnekenburger <rschnekenburger@eclipsesource.com>
  • Loading branch information
rschnekenbu committed Jan 24, 2024
1 parent aeee83b commit 7ea3fc1
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 4 deletions.
63 changes: 63 additions & 0 deletions packages/core/src/browser/extension-open-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// *****************************************************************************
// Copyright (C) 2024 STMicroelectronics.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import { injectable } from 'inversify';
import { MaybePromise, URI } from '../common';
import { OpenHandler, OpenerOptions } from './opener-service';

export interface UriHandler {
canHandleURI(uri: URI): boolean;
handleUri(uri: URI): Promise<boolean>;
}

@injectable()
export class ExtensionOpenHandler implements OpenHandler {

readonly id = 'extensionsURIHandlers';

private providers = new Map<string, UriHandler>();

canHandle(uri: URI, options?: OpenerOptions | undefined): MaybePromise<number> {
if (!uri.scheme.startsWith('theia')) {
return 0;
}

const authority = uri.authority;
const handler = this.providers.get(authority);
if (handler?.canHandleURI(uri)) {
return 500;
}
return 0;
}

open(uri: URI, options?: OpenerOptions | undefined): MaybePromise<object | undefined> {
const authority = uri.authority;
const provider = this.providers.get(authority);
if (provider) {
return provider.handleUri(uri);
}
return Promise.reject(`Impossible to handle ${uri}.`);
}

public registerHandler(extensionId: string, handler: UriHandler): void {
this.providers.set(extensionId, handler);
}

public unregisterHandler(extensionId: string): void {
this.providers.delete(extensionId);
}

}
4 changes: 4 additions & 0 deletions packages/core/src/browser/frontend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ import { HoverService } from './hover-service';
import { AdditionalViewsMenuWidget, AdditionalViewsMenuWidgetFactory } from './shell/additional-views-menu-widget';
import { LanguageIconLabelProvider } from './language-icon-provider';
import { bindTreePreferences } from './tree';
import { ExtensionOpenHandler } from './extension-open-handler';

export { bindResourceProvider, bindMessageService, bindPreferenceService };

Expand Down Expand Up @@ -455,4 +456,7 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is
bind(FrontendApplicationContribution).toService(StylingService);

bind(SecondaryWindowHandler).toSelf().inSingletonScope();

bind(ExtensionOpenHandler).toSelf().inSingletonScope();
bind(OpenHandler).toService(ExtensionOpenHandler);
});
17 changes: 15 additions & 2 deletions packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2180,6 +2180,17 @@ export interface TestingExt {
$onResolveChildren(controllerId: string, path: string[]): void;
}

// based from https://github.com/microsoft/vscode/blob/1.85.1/src/vs/workbench/api/common/extHostUrls.ts
export interface UriExt {
registerUriHandler(handler: theia.UriHandler, plugin: PluginInfo): theia.Disposable;
$handleExternalUri(handle: number, uri: UriComponents): Promise<void>;
}

export interface UriMain {
$registerUriHandler(handle: number, extensionId: string, extensionName: string): void;
$unregisterUriHandler(handle: number): void;
}

export interface TestControllerUpdate {
label: string;
canRefresh: boolean;
Expand Down Expand Up @@ -2257,7 +2268,8 @@ export const PLUGIN_RPC_CONTEXT = {
TABS_MAIN: <ProxyIdentifier<TabsMain>>createProxyIdentifier<TabsMain>('TabsMain'),
TELEMETRY_MAIN: <ProxyIdentifier<TelemetryMain>>createProxyIdentifier<TelemetryMain>('TelemetryMain'),
LOCALIZATION_MAIN: <ProxyIdentifier<LocalizationMain>>createProxyIdentifier<LocalizationMain>('LocalizationMain'),
TESTING_MAIN: createProxyIdentifier<TestingMain>('TestingMain')
TESTING_MAIN: createProxyIdentifier<TestingMain>('TestingMain'),
URI_MAIN: createProxyIdentifier<UriMain>('UriMain')
};

export const MAIN_RPC_CONTEXT = {
Expand Down Expand Up @@ -2299,7 +2311,8 @@ export const MAIN_RPC_CONTEXT = {
COMMENTS_EXT: createProxyIdentifier<CommentsExt>('CommentsExt'),
TABS_EXT: createProxyIdentifier<TabsExt>('TabsExt'),
TELEMETRY_EXT: createProxyIdentifier<TelemetryExt>('TelemetryExt)'),
TESTING_EXT: createProxyIdentifier<TestingExt>('TestingExt')
TESTING_EXT: createProxyIdentifier<TestingExt>('TestingExt'),
URI_EXT: createProxyIdentifier<UriExt>('UriExt')
};

export interface TasksExt {
Expand Down
4 changes: 4 additions & 0 deletions packages/plugin-ext/src/main/browser/main-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import { NotebookDocumentsMainImpl } from './notebooks/notebook-documents-main';
import { NotebookKernelsMainImpl } from './notebooks/notebook-kernels-main';
import { NotebooksAndEditorsMain } from './notebooks/notebook-documents-and-editors-main';
import { TestingMainImpl } from './test-main';
import { UriMainImpl } from './uri-main';

export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container): void {
const authenticationMain = new AuthenticationMainImpl(rpc, container);
Expand Down Expand Up @@ -211,4 +212,7 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container

const localizationMain = new LocalizationMainImpl(container);
rpc.set(PLUGIN_RPC_CONTEXT.LOCALIZATION_MAIN, localizationMain);

const uriMain = new UriMainImpl(rpc, container);
rpc.set(PLUGIN_RPC_CONTEXT.URI_MAIN, uriMain);
}
76 changes: 76 additions & 0 deletions packages/plugin-ext/src/main/browser/uri-main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// *****************************************************************************
// Copyright (C) 2024 STMicroelectronics.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import { Disposable, URI } from '@theia/core';
import { ExtensionOpenHandler, UriHandler} from '@theia/core/lib/browser/extension-open-handler';
import { MAIN_RPC_CONTEXT, UriExt, UriMain } from '../../common';
import { RPCProtocol } from '../../common/rpc-protocol';
import { interfaces } from '@theia/core/shared/inversify';

export class UriMainImpl implements UriMain, Disposable {

private readonly proxy: UriExt;
private readonly handlers = new Map<number, string>();
private readonly extensionOpenHandler: ExtensionOpenHandler;

constructor(rpc: RPCProtocol, container: interfaces.Container) {
this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.URI_EXT);
this.extensionOpenHandler = container.get(ExtensionOpenHandler);
}

dispose(): void {
this.handlers.clear();
}

async $registerUriHandler(handle: number, extensionId: string, extensionDisplayName: string): Promise<void> {
const extensionUrlHandler = new ExtensionUriHandler(this.proxy, handle, extensionId, extensionDisplayName);
this.extensionOpenHandler.registerHandler(extensionId, extensionUrlHandler);
this.handlers.set(handle, extensionId);

return Promise.resolve(undefined);
}

async $unregisterUriHandler(handle: number): Promise<void> {
const extensionId = this.handlers.get(handle);
if (extensionId) {
this.handlers.delete(handle);
this.extensionOpenHandler.unregisterHandler(extensionId);
}
}

}

class ExtensionUriHandler implements UriHandler {

constructor(
private proxy: UriExt,
private readonly handle: number,
readonly extensionId: string,
readonly extensionDisplayName: string
) { }

canHandleURI(uri: URI): boolean {
return uri.authority === this.extensionId;
}

handleUri(uri: URI): Promise<boolean> {
if (this.extensionId !== uri.authority) {
return Promise.resolve(false);
}

return Promise.resolve(this.proxy.$handleExternalUri(this.handle, uri.toComponents())).then(() => true);
}
}
5 changes: 3 additions & 2 deletions packages/plugin-ext/src/plugin/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ import { NotebookKernelsExtImpl } from './notebook/notebook-kernels';
import { NotebookDocumentsExtImpl } from './notebook/notebook-documents';
import { NotebookEditorsExtImpl } from './notebook/notebook-editors';
import { TestingExtImpl } from './tests';
import { UriExtImpl } from './uri-ext';

export function createAPIFactory(
rpc: RPCProtocol,
Expand Down Expand Up @@ -298,6 +299,7 @@ export function createAPIFactory(
const webviewViewsExt = rpc.set(MAIN_RPC_CONTEXT.WEBVIEW_VIEWS_EXT, new WebviewViewsExtImpl(rpc, webviewExt));
const telemetryExt = rpc.set(MAIN_RPC_CONTEXT.TELEMETRY_EXT, new TelemetryExtImpl());
const testingExt = rpc.set(MAIN_RPC_CONTEXT.TESTING_EXT, new TestingExtImpl(rpc, commandRegistry));
const uriExt = rpc.set(MAIN_RPC_CONTEXT.URI_EXT, new UriExtImpl(rpc));
rpc.set(MAIN_RPC_CONTEXT.DEBUG_EXT, debugExt);

return function (plugin: InternalPlugin): typeof theia {
Expand Down Expand Up @@ -570,8 +572,7 @@ export function createAPIFactory(
return decorationsExt.registerFileDecorationProvider(provider, pluginToPluginInfo(plugin));
},
registerUriHandler(handler: theia.UriHandler): theia.Disposable {
// TODO ?
return new Disposable(() => { });
return uriExt.registerUriHandler(handler, pluginToPluginInfo(plugin));
},
createInputBox(): theia.InputBox {
return quickOpenExt.createInputBox(plugin);
Expand Down
72 changes: 72 additions & 0 deletions packages/plugin-ext/src/plugin/uri-ext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// *****************************************************************************
// Copyright (C) 2024 STMicroelectronics.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************

import * as theia from '@theia/plugin';
import {
UriExt,
PLUGIN_RPC_CONTEXT, PluginInfo, UriMain
} from '../common/plugin-api-rpc';
import { RPCProtocol } from '../common/rpc-protocol';
import { Disposable, URI } from './types-impl';
import { UriComponents } from '../common/uri-components';

export class UriExtImpl implements UriExt {

private static handle = 0;
private handles = new Set<string>();
private handlers = new Map<number, theia.UriHandler>();

private readonly proxy: UriMain;

constructor(readonly rpc: RPCProtocol) {
this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.URI_MAIN);
console.log(this.proxy);
}

registerUriHandler(handler: theia.UriHandler, plugin: PluginInfo): theia.Disposable {
const extensionId = plugin.id;
if (this.handles.has(extensionId)) {
throw new Error(`URI handler already registered for extension ${extensionId}`);
}

const handle = UriExtImpl.handle++;
this.handles.add(extensionId);
this.handlers.set(handle, handler);
this.proxy.$registerUriHandler(handle, extensionId, plugin.displayName || plugin.name);

return new Disposable(() => {
this.proxy.$unregisterUriHandler(handle);
this.handles.delete(extensionId);
this.handlers.delete(handle);
});
}

$handleExternalUri(handle: number, uri: UriComponents): Promise<void> {
const handler = this.handlers.get(handle);

if (!handler) {
return Promise.resolve(undefined);
}
try {
handler.handleUri(URI.revive(uri));
} catch (err) {
console.log(`error while handling external uri: ${uri}`);
}

return Promise.resolve(undefined);
}

}

0 comments on commit 7ea3fc1

Please sign in to comment.