Skip to content

Commit

Permalink
add IOpenerService and use it with webview, #3676
Browse files Browse the repository at this point in the history
  • Loading branch information
jrieken authored and isidorn committed Apr 15, 2016
1 parent ea21527 commit 074406e
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 2 deletions.
25 changes: 25 additions & 0 deletions src/vs/platform/opener/common/opener.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';

import URI from 'vs/base/common/uri';
import {TPromise} from 'vs/base/common/winjs.base';
import {createDecorator} from 'vs/platform/instantiation/common/instantiation';

export const IOpenerService = createDecorator<IOpenerService>('openerService');


export interface IOpenerService {

serviceId: any;

/**
* Opens a resource, like a webadress, a document uri, or executes command.
*
* @param resource A resource
* @return A promise that resolves when the opening is done.
*/
open(resource: URI): TPromise<any>;
}
12 changes: 12 additions & 0 deletions src/vs/platform/opener/electron-browser/opener.contribution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

'use strict';

import {registerSingleton} from 'vs/platform/instantiation/common/extensions';
import {OpenerService} from 'vs/platform/opener/electron-browser/openerService';
import {IOpenerService} from 'vs/platform/opener/common/opener';

registerSingleton(IOpenerService, OpenerService);
65 changes: 65 additions & 0 deletions src/vs/platform/opener/electron-browser/openerService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';

import URI from 'vs/base/common/uri';
import {Schemas} from 'vs/base/common/network';
import {TPromise} from 'vs/base/common/winjs.base';
import {IEditorService} from 'vs/platform/editor/common/editor';
import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingService';
import {IOpenerService} from '../common/opener';

export class OpenerService implements IOpenerService {

serviceId: any;

constructor(
@IEditorService private _editorService: IEditorService,
@IKeybindingService private _keybindingService: IKeybindingService
) {
//
}

open(resource: URI): TPromise<any> {

const {scheme, path, query, fragment} = resource;
let promise: TPromise<any>;
if (scheme === Schemas.http || scheme === Schemas.https) {
// open http
window.open(resource.toString(true));

} else if (scheme === 'command' && this._keybindingService.hasCommand(path)) {
// execute as command
let args: any;
try {
args = JSON.parse(query);
} catch (e) {
//
}
promise = this._keybindingService.executeCommand(path, Array.isArray(args) ? args : [args]);

} else {
promise = this._editorService.resolveEditorModel({ resource }).then(model => {
if (!model) {
return;
}
// support file:///some/file.js#L73
let selection: {
startLineNumber: number;
startColumn: number;
};
if (/^L\d+$/.test(fragment)) {
selection = {
startLineNumber: parseInt(fragment.substr(1)),
startColumn: 1
};
}
return this._editorService.openEditor({ resource, options: { selection } });
});
}

return TPromise.as(promise).then(undefined, err => { }); // !ignores all errors
}
}
3 changes: 3 additions & 0 deletions src/vs/workbench/electron-browser/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ import {IExtensionsService} from 'vs/workbench/parts/extensions/common/extension
import {ExtensionsService} from 'vs/workbench/parts/extensions/node/extensionsService';
import {ReloadWindowAction} from 'vs/workbench/electron-browser/actions';

// self registering service
import 'vs/platform/opener/electron-browser/opener.contribution';

/**
* Services that we require for the Shell
*/
Expand Down
15 changes: 13 additions & 2 deletions src/vs/workbench/parts/html/browser/htmlPreviewPart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {BaseTextEditorModel} from 'vs/workbench/common/editor/textEditorModel';
import {HtmlInput} from 'vs/workbench/parts/html/common/htmlInput';
import {IThemeService} from 'vs/workbench/services/themes/common/themeService';
import {KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry';
import {IOpenerService} from 'vs/platform/opener/common/opener';

KeybindingsRegistry.registerCommandDesc({
id: '_webview.openDevTools',
Expand Down Expand Up @@ -62,7 +63,7 @@ class ManagedWebview {
private _ready: TPromise<this>;
private _disposables: IDisposable[];

constructor(private _parent: HTMLElement, private _layoutParent: HTMLElement, private _styleElement) {
constructor(private _parent: HTMLElement, private _layoutParent: HTMLElement, private _styleElement, onDidClickLink:(uri:URI)=>any) {
this._webview = <Webview>document.createElement('webview');
this._webview.style.zIndex = '1';
this._webview.style.position = 'absolute';
Expand All @@ -88,6 +89,12 @@ class ManagedWebview {
}),
addDisposableListener(this._webview, 'crashed', function () {
console.error('embedded page crashed');
}),
addDisposableListener(this._webview, 'ipc-message', (event) => {
if (event.channel === 'did-click-link') {
let [uri] = event.args;
onDidClickLink(URI.parse(uri));
}
})
];

Expand Down Expand Up @@ -190,6 +197,7 @@ export class HtmlPreviewPart extends BaseEditor {

private _editorService: IWorkbenchEditorService;
private _themeService: IThemeService;
private _openerService: IOpenerService;
private _webview: ManagedWebview;
private _container: HTMLDivElement;

Expand All @@ -203,12 +211,14 @@ export class HtmlPreviewPart extends BaseEditor {
@ITelemetryService telemetryService: ITelemetryService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
@IThemeService themeService: IThemeService,
@IOpenerService openerService: IOpenerService,
@IWorkspaceContextService contextService: IWorkspaceContextService
) {
super(HtmlPreviewPart.ID, telemetryService);

this._editorService = editorService;
this._themeService = themeService;
this._openerService = openerService;
this._baseUrl = contextService.toResource('/');
}

Expand All @@ -232,7 +242,8 @@ export class HtmlPreviewPart extends BaseEditor {
if (!this._webview) {
this._webview = new ManagedWebview(document.getElementById('workbench.main.container'),
this._container,
document.querySelector('.monaco-editor-background'));
document.querySelector('.monaco-editor-background'),
uri => this._openerService.open(uri));

this._webview.baseUrl = this._baseUrl && this._baseUrl.toString();
}
Expand Down
17 changes: 17 additions & 0 deletions src/vs/workbench/parts/html/browser/webview.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,30 @@
newDocument.head.appendChild(defaultStyles);
}

// script to bubble out link-clicks
const defaultScripts = newDocument.createElement('script');
defaultScripts.innerHTML = `
document.body.addEventListener('click', function (event) {
if(event.target.tagName === 'A' && event.target.href) {
window.parent.postMessage({ command: 'did-click-link', data: event.target.href }, 'file://');
event.preventDefault();
}
});`
newDocument.body.appendChild(defaultScripts);

// write new content onto iframe
target.contentDocument.open('text/html', 'replace');
target.contentDocument.write(newDocument.documentElement.innerHTML);
target.contentDocument.close();

});

// forward messages from the embedded iframe
window.onmessage = function(message) {
const { command, data} = message.data;
ipcRenderer.sendToHost(command, data);
};

// signal ready, needs a short timeout for an
// unknown reason
setTimeout(function() {
Expand Down

0 comments on commit 074406e

Please sign in to comment.