Skip to content

Commit

Permalink
Implement stubbed window#registerURIhandler (#13306)
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 authored Aug 28, 2024
1 parent 2fe7d5d commit 754eb84
Show file tree
Hide file tree
Showing 21 changed files with 343 additions and 39 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

- [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/)


## Unreleased
- [plugin] implement stubbed API window registerUriHandler() [#13306](https://github.com/eclipse-theia/theia/pull/13306) - contributed on behalf of STMicroelectronics
- [core] Download json schema catalog at build-time - [#14065](https://github.com/eclipse-theia/theia/pull/14065/) - Contributed on behalf of STMicroelectronics

<a name="breaking_changes_1.53.0">[Breaking Changes:](#breaking_changes_1.53.0)</a>
- [dependencies] Updated electron to version 30.1.2 - [#14041](https://github.com/eclipse-theia/theia/pull/14041) - Contributed on behalf of STMicroelectronics
- [dependencies] increased minimum node version to 18. [#14027](https://github.com/eclipse-theia/theia/pull/14027) - Contributed on behalf of STMicroelectronics


## 1.52.0 - 07/25/2024

- [application-package] bumped the default supported API from `1.90.2` to `1.91.1` [#13955](https://github.com/eclipse-theia/theia/pull/13955) - Contributed on behalf of STMicroelectronics
Expand Down
11 changes: 9 additions & 2 deletions dev-packages/application-package/src/application-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ export namespace ElectronFrontendApplicationConfig {
export const DEFAULT: ElectronFrontendApplicationConfig = {
windowOptions: {},
showWindowEarly: true,
splashScreenOptions: {}
splashScreenOptions: {},
uriScheme: 'theia'
};
export interface SplashScreenOptions {
/**
Expand Down Expand Up @@ -85,6 +86,11 @@ export namespace ElectronFrontendApplicationConfig {
* Defaults to `{}` which results in no splash screen being displayed.
*/
readonly splashScreenOptions?: SplashScreenOptions;

/**
* The custom uri scheme the application registers to in the operating system.
*/
readonly uriScheme: string;
}
}

Expand Down Expand Up @@ -122,7 +128,8 @@ export namespace FrontendApplicationConfig {
electron: ElectronFrontendApplicationConfig.DEFAULT,
defaultLocale: '',
validatePreferencesSchema: true,
reloadOnReconnect: false
reloadOnReconnect: false,
uriScheme: 'theia'
};
export interface Partial extends ApplicationConfig {

Expand Down
1 change: 0 additions & 1 deletion packages/core/src/browser/frontend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,6 @@ export const frontendApplicationModule = new ContainerModule((bind, _unbind, _is
bind(FrontendApplicationContribution).toService(StylingService);

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

bind(ViewColumnService).toSelf().inSingletonScope();

bind(UndoRedoHandlerService).toSelf().inSingletonScope();
Expand Down
14 changes: 12 additions & 2 deletions packages/core/src/browser/opener-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ export interface OpenerService {
* Add open handler i.e. for custom editors
*/
addHandler?(openHandler: OpenHandler): Disposable;

/**
* Remove open handler
*/
removeHandler?(openHandler: OpenHandler): void;

/**
* Event that fires when a new opener is added or removed.
*/
Expand Down Expand Up @@ -108,11 +114,15 @@ export class DefaultOpenerService implements OpenerService {
this.onDidChangeOpenersEmitter.fire();

return Disposable.create(() => {
this.customEditorOpenHandlers.splice(this.customEditorOpenHandlers.indexOf(openHandler), 1);
this.onDidChangeOpenersEmitter.fire();
this.removeHandler(openHandler);
});
}

removeHandler(openHandler: OpenHandler): void {
this.customEditorOpenHandlers.splice(this.customEditorOpenHandlers.indexOf(openHandler), 1);
this.onDidChangeOpenersEmitter.fire();
}

async getOpener(uri: URI, options?: OpenerOptions): Promise<OpenHandler> {
const handlers = await this.prioritize(uri, options);
if (handlers.length >= 1) {
Expand Down
42 changes: 42 additions & 0 deletions packages/core/src/electron-browser/electron-uri-handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// *****************************************************************************
// Copyright (C) 2024 STMicroelectronics and others.
//
// 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 { FrontendApplicationContribution, OpenerService } from '../browser';

import { injectable, inject } from 'inversify';
import { URI } from '../common';

@injectable()
export class ElectronUriHandlerContribution implements FrontendApplicationContribution {
@inject(OpenerService)
protected readonly openenerService: OpenerService;

initialize(): void {
window.electronTheiaCore.setOpenUrlHandler(async url => {
const uri = new URI(url);
try {
const handler = await this.openenerService.getOpener(uri);
if (handler) {
await handler.open(uri);
return true;
}
} catch (e) {
// no handler
}
return false;
});
}
}
17 changes: 16 additions & 1 deletion packages/core/src/electron-browser/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ import {
CHANNEL_IS_FULL_SCREEN, CHANNEL_SET_MENU_BAR_VISIBLE, CHANNEL_REQUEST_CLOSE, CHANNEL_SET_TITLE_STYLE, CHANNEL_RESTART,
CHANNEL_REQUEST_RELOAD, CHANNEL_APP_STATE_CHANGED, CHANNEL_SHOW_ITEM_IN_FOLDER, CHANNEL_READ_CLIPBOARD, CHANNEL_WRITE_CLIPBOARD,
CHANNEL_KEYBOARD_LAYOUT_CHANGED, CHANNEL_IPC_CONNECTION, InternalMenuDto, CHANNEL_REQUEST_SECONDARY_CLOSE, CHANNEL_SET_BACKGROUND_COLOR,
CHANNEL_WC_METADATA, CHANNEL_ABOUT_TO_CLOSE, CHANNEL_OPEN_WITH_SYSTEM_APP
CHANNEL_WC_METADATA, CHANNEL_ABOUT_TO_CLOSE, CHANNEL_OPEN_WITH_SYSTEM_APP,
CHANNEL_OPEN_URL
} from '../electron-common/electron-api';

// eslint-disable-next-line import/no-extraneous-dependencies
Expand All @@ -38,6 +39,16 @@ let nextHandlerId = 0;
const mainMenuId = 0;
let nextMenuId = mainMenuId + 1;

let openUrlHandler: ((url: string) => Promise<boolean>) | undefined;

ipcRenderer.on(CHANNEL_OPEN_URL, async (event: Electron.IpcRendererEvent, url: string, replyChannel: string) => {
if (openUrlHandler) {
event.sender.send(replyChannel, await openUrlHandler(url));
} else {
event.sender.send(replyChannel, false);
}
});

function convertMenu(menu: MenuDto[] | undefined, handlerMap: Map<number, () => void>): InternalMenuDto[] | undefined {
if (!menu) {
return undefined;
Expand Down Expand Up @@ -135,6 +146,10 @@ const api: TheiaCoreAPI = {
return Disposable.create(() => ipcRenderer.off(CHANNEL_ABOUT_TO_CLOSE, h));
},

setOpenUrlHandler(handler: (url: string) => Promise<boolean>): void {
openUrlHandler = handler;
},

onWindowEvent: function (event: WindowEvent, handler: () => void): Disposable {
const h = (_event: unknown, evt: WindowEvent) => {
if (event === evt) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { ElectronSecondaryWindowService } from './electron-secondary-window-serv
import { bindWindowPreferences } from './electron-window-preferences';
import { ElectronWindowService } from './electron-window-service';
import { ExternalAppOpenHandler } from './external-app-open-handler';
import { ElectronUriHandlerContribution } from '../electron-uri-handler';

export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(ElectronMainWindowService).toDynamicValue(context =>
Expand All @@ -37,6 +38,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bindWindowPreferences(bind);
bind(WindowService).to(ElectronWindowService).inSingletonScope();
bind(FrontendApplicationContribution).toService(WindowService);
bind(ElectronUriHandlerContribution).toSelf().inSingletonScope();
bind(FrontendApplicationContribution).toService(ElectronUriHandlerContribution);
bind(ClipboardService).to(ElectronClipboardService).inSingletonScope();
rebind(FrontendApplicationStateService).to(ElectronFrontendApplicationStateService).inSingletonScope();
bind(SecondaryWindowService).to(ElectronSecondaryWindowService).inSingletonScope();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class ExternalAppOpenHandler implements OpenHandler {
async open(uri: URI): Promise<undefined> {
// For files 'file:' scheme, system accepts only the path.
// For other protocols e.g. 'vscode:' we use the full URI to propagate target app information.
window.electronTheiaCore.openWithSystemApp(uri.scheme === 'file' ? uri.path.fsPath() : uri.toString(true));
window.electronTheiaCore.openWithSystemApp(uri.toString(true));
return undefined;
}
}
3 changes: 3 additions & 0 deletions packages/core/src/electron-common/electron-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ export interface TheiaCoreAPI {
onAboutToClose(handler: () => void): Disposable;
setCloseRequestHandler(handler: (reason: StopReason) => Promise<boolean>): void;

setOpenUrlHandler(handler: (url: string) => Promise<boolean>): void;

setSecondaryWindowCloseRequestHandler(windowName: string, handler: () => Promise<boolean>): void;

toggleDevTools(): void;
Expand Down Expand Up @@ -129,6 +131,7 @@ export const CHANNEL_MAXIMIZE = 'Maximize';
export const CHANNEL_IS_MAXIMIZED = 'IsMaximized';

export const CHANNEL_ABOUT_TO_CLOSE = 'AboutToClose';
export const CHANNEL_OPEN_URL = 'OpenUrl';

export const CHANNEL_UNMAXIMIZE = 'UnMaximize';
export const CHANNEL_ON_WINDOW_EVENT = 'OnWindowEvent';
Expand Down
21 changes: 18 additions & 3 deletions packages/core/src/electron-main/electron-api-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ import {
CHANNEL_SET_BACKGROUND_COLOR,
CHANNEL_WC_METADATA,
CHANNEL_ABOUT_TO_CLOSE,
CHANNEL_OPEN_WITH_SYSTEM_APP
CHANNEL_OPEN_WITH_SYSTEM_APP,
CHANNEL_OPEN_URL
} from '../electron-common/electron-api';
import { ElectronMainApplication, ElectronMainApplicationContribution } from './electron-main-application';
import { Disposable, DisposableCollection, isOSX, MaybePromise } from '../common';
Expand Down Expand Up @@ -165,8 +166,8 @@ export class TheiaMainApi implements ElectronMainApplicationContribution {
shell.showItemInFolder(fsPath);
});

ipcMain.on(CHANNEL_OPEN_WITH_SYSTEM_APP, (event, fsPath) => {
shell.openPath(fsPath);
ipcMain.on(CHANNEL_OPEN_WITH_SYSTEM_APP, (event, uri) => {
shell.openExternal(uri);
});

ipcMain.handle(CHANNEL_GET_TITLE_STYLE_AT_STARTUP, event => application.getTitleBarStyleAtStartup(event.sender));
Expand Down Expand Up @@ -285,6 +286,20 @@ export namespace TheiaRendererAPI {
wc.send(CHANNEL_ON_WINDOW_EVENT, event);
}

export function openUrl(wc: WebContents, url: string): Promise<boolean> {
return new Promise<boolean>(resolve => {
const channelNr = nextReplyChannel++;
const replyChannel = `openUrl${channelNr}`;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const l = createDisposableListener(ipcMain, replyChannel, (e, args: any[]) => {
l.dispose();
resolve(args[0]);
});

wc.send(CHANNEL_OPEN_URL, url, replyChannel);
});
}

export function sendAboutToClose(wc: WebContents): Promise<void> {
return new Promise<void>(resolve => {
const channelNr = nextReplyChannel++;
Expand Down
Loading

0 comments on commit 754eb84

Please sign in to comment.