Merge from vscode 2b0b9136329c181a9e381463a1f7dc3a2d105a34 (#4880)

This commit is contained in:
Karl Burtram
2019-04-05 10:09:18 -07:00
committed by GitHub
parent 9bd7e30d18
commit cb5bcf2248
433 changed files with 8915 additions and 8361 deletions

View File

@@ -3,56 +3,104 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
(function () {
'use strict';
'use strict';
// @ts-ignore
const ipcRenderer = require('electron').ipcRenderer;
/**
* Use polling to track focus of main webview and iframes within the webview
*
* @param {Object} handlers
* @param {() => void} handlers.onFocus
* @param {() => void} handlers.onBlur
*/
const trackFocus = ({ onFocus, onBlur }) => {
const interval = 50;
let isFocused = document.hasFocus();
setInterval(() => {
const isCurrentlyFocused = document.hasFocus();
if (isCurrentlyFocused === isFocused) {
return;
}
isFocused = isCurrentlyFocused;
if (isCurrentlyFocused) {
onFocus();
} else {
onBlur();
}
}, interval);
};
const registerVscodeResourceScheme = (function () {
let hasRegistered = false;
return () => {
if (hasRegistered) {
return;
}
const getActiveFrame = () => {
return /** @type {HTMLIFrameElement} */ (document.getElementById('active-frame'));
};
hasRegistered = true;
const getPendingFrame = () => {
return /** @type {HTMLIFrameElement} */ (document.getElementById('pending-frame'));
};
// @ts-ignore
require('electron').webFrame.registerURLSchemeAsPrivileged('vscode-resource', {
secure: true,
bypassCSP: false,
allowServiceWorkers: false,
supportFetchAPI: true,
corsEnabled: true
});
};
}());
const defaultCssRules = `
body {
background-color: var(--vscode-editor-background);
color: var(--vscode-editor-foreground);
font-family: var(--vscode-editor-font-family);
font-weight: var(--vscode-editor-font-weight);
font-size: var(--vscode-editor-font-size);
margin: 0;
padding: 0 20px;
}
/**
* Use polling to track focus of main webview and iframes within the webview
*
* @param {Object} handlers
* @param {() => void} handlers.onFocus
* @param {() => void} handlers.onBlur
*/
const trackFocus = ({ onFocus, onBlur }) => {
const interval = 50;
let isFocused = document.hasFocus();
setInterval(() => {
const isCurrentlyFocused = document.hasFocus();
if (isCurrentlyFocused === isFocused) {
return;
}
isFocused = isCurrentlyFocused;
if (isCurrentlyFocused) {
onFocus();
} else {
onBlur();
}
}, interval);
};
img {
max-width: 100%;
max-height: 100%;
}
a {
color: var(--vscode-textLink-foreground);
}
a:hover {
color: var(--vscode-textLink-activeForeground);
}
a:focus,
input:focus,
select:focus,
textarea:focus {
outline: 1px solid -webkit-focus-ring-color;
outline-offset: -1px;
}
code {
color: var(--vscode-textPreformat-foreground);
}
blockquote {
background: var(--vscode-textBlockQuote-background);
border-color: var(--vscode-textBlockQuote-border);
}
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-thumb {
background-color: var(--vscode-scrollbarSlider-background);
}
::-webkit-scrollbar-thumb:hover {
background-color: var(--vscode-scrollbarSlider-hoverBackground);
}
::-webkit-scrollbar-thumb:active {
background-color: var(--vscode-scrollbarSlider-activeBackground);
}`;
/**
* @typedef {{ postMessage: (channel: string, data?: any) => void, onMessage: (channel: string, handler: any) => void }} HostCommunications
*/
/**
* @param {HostCommunications} host
*/
module.exports = function createWebviewManager(host) {
// state
let firstLoad = true;
let loadTimeout;
@@ -82,14 +130,6 @@
}
};
const getActiveFrame = () => {
return /** @type {HTMLIFrameElement} */ (document.getElementById('active-frame'));
};
const getPendingFrame = () => {
return /** @type {HTMLIFrameElement} */ (document.getElementById('pending-frame'));
};
/**
* @param {MouseEvent} event
*/
@@ -111,7 +151,7 @@
scrollTarget.scrollIntoView();
}
} else {
ipcRenderer.sendToHost('did-click-link', node.href);
host.postMessage('did-click-link', node.href);
}
event.preventDefault();
break;
@@ -124,7 +164,7 @@
* @param {KeyboardEvent} e
*/
const handleInnerKeydown = (e) => {
ipcRenderer.sendToHost('did-keydown', {
host.postMessage('did-keydown', {
key: e.key,
keyCode: e.keyCode,
code: e.code,
@@ -137,7 +177,7 @@
};
const onMessage = (message) => {
ipcRenderer.sendToHost(message.data.command, message.data.data);
host.postMessage(message.data.command, message.data.data);
};
let isHandlingScroll = false;
@@ -154,7 +194,7 @@
isHandlingScroll = true;
window.requestAnimationFrame(() => {
try {
ipcRenderer.sendToHost('did-scroll', progress);
host.postMessage('did-scroll', progress);
} catch (e) {
// noop
}
@@ -163,11 +203,7 @@
};
document.addEventListener('DOMContentLoaded', () => {
ipcRenderer.on('baseUrl', (_event, value) => {
initData.baseUrl = value;
});
ipcRenderer.on('styles', (_event, variables, activeTheme) => {
host.onMessage('styles', (_event, variables, activeTheme) => {
initData.styles = variables;
initData.activeTheme = activeTheme;
@@ -180,7 +216,7 @@
});
// propagate focus
ipcRenderer.on('focus', () => {
host.onMessage('focus', () => {
const target = getActiveFrame();
if (target) {
target.contentWindow.focus();
@@ -188,11 +224,9 @@
});
// update iframe-contents
ipcRenderer.on('content', (_event, data) => {
host.onMessage('content', (_event, data) => {
const options = data.options;
registerVscodeResourceScheme();
const text = data.contents;
const newDocument = new DOMParser().parseFromString(text, 'text/html');
@@ -202,13 +236,6 @@
}
});
// set base-url if applicable
if (initData.baseUrl && newDocument.head.getElementsByTagName('base').length === 0) {
const baseElement = newDocument.createElement('base');
baseElement.href = initData.baseUrl;
newDocument.head.appendChild(baseElement);
}
// apply default script
if (options.allowScripts) {
const defaultScript = newDocument.createElement('script');
@@ -299,7 +326,7 @@
newFrame.contentWindow.addEventListener('keydown', handleInnerKeydown);
newFrame.contentWindow.onbeforeunload = () => {
if (isInDevelopmentMode) { // Allow reloads while developing a webview
ipcRenderer.sendToHost('do-reload');
host.postMessage('do-reload');
return false;
}
@@ -361,11 +388,11 @@
newFrame.contentDocument.write(newDocument.documentElement.innerHTML);
newFrame.contentDocument.close();
ipcRenderer.sendToHost('did-set-content');
host.postMessage('did-set-content', undefined);
});
// Forward message to the embedded iframe
ipcRenderer.on('message', (_event, data) => {
host.onMessage('message', (_event, data) => {
const pending = getPendingFrame();
if (!pending) {
const target = getActiveFrame();
@@ -377,79 +404,23 @@
pendingMessages.push(data);
});
ipcRenderer.on('initial-scroll-position', (_event, progress) => {
host.onMessage('initial-scroll-position', (_event, progress) => {
initData.initialScrollProgress = progress;
});
ipcRenderer.on('devtools-opened', () => {
host.onMessage('devtools-opened', () => {
isInDevelopmentMode = true;
});
trackFocus({
onFocus: () => { ipcRenderer.sendToHost('did-focus'); },
onBlur: () => { ipcRenderer.sendToHost('did-blur'); }
onFocus: () => host.postMessage('did-focus'),
onBlur: () => host.postMessage('did-blur')
});
// Forward messages from the embedded iframe
window.onmessage = onMessage;
// signal ready
ipcRenderer.sendToHost('webview-ready', process.pid);
host.postMessage('webview-ready', process.pid);
});
const defaultCssRules = `
body {
background-color: var(--vscode-editor-background);
color: var(--vscode-editor-foreground);
font-family: var(--vscode-editor-font-family);
font-weight: var(--vscode-editor-font-weight);
font-size: var(--vscode-editor-font-size);
margin: 0;
padding: 0 20px;
}
img {
max-width: 100%;
max-height: 100%;
}
a {
color: var(--vscode-textLink-foreground);
}
a:hover {
color: var(--vscode-textLink-activeForeground);
}
a:focus,
input:focus,
select:focus,
textarea:focus {
outline: 1px solid -webkit-focus-ring-color;
outline-offset: -1px;
}
code {
color: var(--vscode-textPreformat-foreground);
}
blockquote {
background: var(--vscode-textBlockQuote-background);
border-color: var(--vscode-textBlockQuote-border);
}
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-thumb {
background-color: var(--vscode-scrollbarSlider-background);
}
::-webkit-scrollbar-thumb:hover {
background-color: var(--vscode-scrollbarSlider-hoverBackground);
}
::-webkit-scrollbar-thumb:active {
background-color: var(--vscode-scrollbarSlider-activeBackground);
}`;
}());
};

View File

@@ -0,0 +1,138 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { isMacintosh } from 'vs/base/common/platform';
import { localize } from 'vs/nls';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor';
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory';
import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from 'vs/workbench/contrib/webview/common/webview';
import { CopyWebviewEditorCommand, CutWebviewEditorCommand, HideWebViewEditorFindCommand, OpenWebviewDeveloperToolsAction, PasteWebviewEditorCommand, RedoWebviewEditorCommand, ReloadWebviewAction, SelectAllWebviewEditorCommand, ShowWebViewEditorFindWidgetCommand, UndoWebviewEditorCommand } from '../browser/webviewCommands';
import { WebviewEditor } from '../browser/webviewEditor';
import { WebviewEditorInput } from '../browser/webviewEditorInput';
import { IWebviewEditorService, WebviewEditorService } from '../browser/webviewEditorService';
(Registry.as<IEditorRegistry>(EditorExtensions.Editors)).registerEditor(new EditorDescriptor(
WebviewEditor,
WebviewEditor.ID,
localize('webview.editor.label', "webview editor")),
[new SyncDescriptor(WebviewEditorInput)]);
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(
WebviewEditorInputFactory.ID,
WebviewEditorInputFactory);
registerSingleton(IWebviewEditorService, WebviewEditorService, true);
const webviewDeveloperCategory = localize('developer', "Developer");
const actionRegistry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
export function registerWebViewCommands(editorId: string): void {
const contextKeyExpr = ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', editorId), ContextKeyExpr.not('editorFocus') /* https://github.com/Microsoft/vscode/issues/58668 */);
const showNextFindWidgetCommand = new ShowWebViewEditorFindWidgetCommand({
id: ShowWebViewEditorFindWidgetCommand.ID,
precondition: contextKeyExpr,
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_F,
weight: KeybindingWeight.EditorContrib
}
});
showNextFindWidgetCommand.register();
(new HideWebViewEditorFindCommand({
id: HideWebViewEditorFindCommand.ID,
precondition: ContextKeyExpr.and(
contextKeyExpr,
KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE),
kbOpts: {
primary: KeyCode.Escape,
weight: KeybindingWeight.EditorContrib
}
})).register();
(new SelectAllWebviewEditorCommand({
id: SelectAllWebviewEditorCommand.ID,
precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)),
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_A,
weight: KeybindingWeight.EditorContrib
}
})).register();
// These commands are only needed on MacOS where we have to disable the menu bar commands
if (isMacintosh) {
(new CopyWebviewEditorCommand({
id: CopyWebviewEditorCommand.ID,
precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)),
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_C,
weight: KeybindingWeight.EditorContrib
}
})).register();
(new PasteWebviewEditorCommand({
id: PasteWebviewEditorCommand.ID,
precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)),
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
weight: KeybindingWeight.EditorContrib
}
})).register();
(new CutWebviewEditorCommand({
id: CutWebviewEditorCommand.ID,
precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)),
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_X,
weight: KeybindingWeight.EditorContrib
}
})).register();
(new UndoWebviewEditorCommand({
id: UndoWebviewEditorCommand.ID,
precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)),
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_Z,
weight: KeybindingWeight.EditorContrib
}
})).register();
(new RedoWebviewEditorCommand({
id: RedoWebviewEditorCommand.ID,
precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)),
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_Y,
secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z],
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z },
weight: KeybindingWeight.EditorContrib
}
})).register();
}
}
registerWebViewCommands(WebviewEditor.ID);
actionRegistry.registerWorkbenchAction(
new SyncActionDescriptor(OpenWebviewDeveloperToolsAction, OpenWebviewDeveloperToolsAction.ID, OpenWebviewDeveloperToolsAction.LABEL),
'Webview Tools',
webviewDeveloperCategory);
actionRegistry.registerWorkbenchAction(
new SyncActionDescriptor(ReloadWebviewAction, ReloadWebviewAction.ID, ReloadWebviewAction.LABEL),
'Reload Webview',
webviewDeveloperCategory);

View File

@@ -8,7 +8,7 @@ import { Command } from 'vs/editor/browser/editorExtensions';
import * as nls from 'vs/nls';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { WebviewEditor } from 'vs/workbench/contrib/webview/electron-browser/webviewEditor';
import { WebviewEditor } from 'vs/workbench/contrib/webview/browser/webviewEditor';
export class ShowWebViewEditorFindWidgetCommand extends Command {
public static readonly ID = 'editor.action.webvieweditor.showFind';

View File

@@ -8,8 +8,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -17,19 +16,15 @@ import { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { EditorOptions } from 'vs/workbench/common/editor';
import { WebviewEditorInput } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorInput';
import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
import { IWebviewService, Webview, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from 'vs/workbench/contrib/webview/common/webview';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { WebviewElement } from './webviewElement';
/** A context key that is set when the find widget in a webview is visible. */
export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE = new RawContextKey<boolean>('webviewFindWidgetVisible', false);
export class WebviewEditor extends BaseEditor {
protected _webview: WebviewElement | undefined;
protected _webview: Webview | undefined;
protected findWidgetVisible: IContextKey<boolean>;
public static readonly ID = 'WebviewEditor';
@@ -50,9 +45,8 @@ export class WebviewEditor extends BaseEditor {
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IContextKeyService private _contextKeyService: IContextKeyService,
@IWorkbenchLayoutService private readonly _layoutService: IWorkbenchLayoutService,
@IWebviewService private readonly _webviewService: IWebviewService,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IEditorService private readonly _editorService: IEditorService,
@IWindowService private readonly _windowService: IWindowService,
@IStorageService storageService: IStorageService
@@ -130,11 +124,11 @@ export class WebviewEditor extends BaseEditor {
}
public reload() {
this.withWebviewElement(webview => webview.reload());
this.withWebview(webview => webview.reload());
}
public layout(_dimension: DOM.Dimension): void {
this.withWebviewElement(webview => {
this.withWebview(webview => {
this.doUpdateContainer();
webview.layout();
});
@@ -151,34 +145,34 @@ export class WebviewEditor extends BaseEditor {
}
});
}
this.withWebviewElement(webview => webview.focus());
this.withWebview(webview => webview.focus());
}
public selectAll(): void {
this.withWebviewElement(webview => webview.selectAll());
this.withWebview(webview => webview.selectAll());
}
public copy(): void {
this.withWebviewElement(webview => webview.copy());
this.withWebview(webview => webview.copy());
}
public paste(): void {
this.withWebviewElement(webview => webview.paste());
this.withWebview(webview => webview.paste());
}
public cut(): void {
this.withWebviewElement(webview => webview.cut());
this.withWebview(webview => webview.cut());
}
public undo(): void {
this.withWebviewElement(webview => webview.undo());
this.withWebview(webview => webview.undo());
}
public redo(): void {
this.withWebviewElement(webview => webview.redo());
this.withWebview(webview => webview.redo());
}
private withWebviewElement(f: (element: WebviewElement) => void): void {
private withWebview(f: (element: Webview) => void): void {
if (this._webview) {
f(this._webview);
}
@@ -264,7 +258,7 @@ export class WebviewEditor extends BaseEditor {
return rootPaths;
}
private getWebview(input: WebviewEditorInput): WebviewElement {
private getWebview(input: WebviewEditorInput): Webview {
if (this._webview) {
return this._webview;
}
@@ -279,14 +273,12 @@ export class WebviewEditor extends BaseEditor {
this.findWidgetVisible = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService);
}
this._webview = this._instantiationService.createInstance(WebviewElement,
this._layoutService.getContainer(Parts.EDITOR_PART),
this._webview = this._webviewService.createWebview(
{
allowSvgs: true,
extension: input.extension,
enableFindWidget: input.options.enableFindWidget
},
{});
}, {});
this._webview.mountTo(this._webviewContent);
input.webview = this._webview;

View File

@@ -11,7 +11,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { EditorInput, EditorModel, GroupIdentifier, IEditorInput } from 'vs/workbench/common/editor';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { WebviewEvents, WebviewInputOptions } from './webviewEditorService';
import { WebviewElement, WebviewOptions } from './webviewElement';
import { Webview, WebviewOptions } from 'vs/workbench/contrib/webview/common/webview';
export class WebviewEditorInput extends EditorInput {
private static handlePool = 0;
@@ -57,7 +57,7 @@ export class WebviewEditorInput extends EditorInput {
private _currentWebviewHtml: string = '';
public _events: WebviewEvents | undefined;
private _container?: HTMLElement;
private _webview: WebviewElement | undefined;
private _webview?: Webview;
private _webviewOwner: any;
private _webviewDisposables: IDisposable[] = [];
private _group?: GroupIdentifier;
@@ -72,7 +72,6 @@ export class WebviewEditorInput extends EditorInput {
constructor(
public readonly viewType: string,
id: number | undefined,
name: string,
options: WebviewInputOptions,
state: any,
@@ -85,12 +84,7 @@ export class WebviewEditorInput extends EditorInput {
) {
super();
if (typeof id === 'number') {
this._id = id;
WebviewEditorInput.handlePool = Math.max(id, WebviewEditorInput.handlePool) + 1;
} else {
this._id = WebviewEditorInput.handlePool++;
}
this._id = WebviewEditorInput.handlePool++;
this._name = name;
this._options = options;
@@ -103,10 +97,6 @@ export class WebviewEditorInput extends EditorInput {
return WebviewEditorInput.typeId;
}
public getId(): number {
return this._id;
}
private readonly _onDidChangeIcon = this._register(new Emitter<void>());
public readonly onDidChangeIcon = this._onDidChangeIcon.event;
@@ -161,7 +151,7 @@ export class WebviewEditorInput extends EditorInput {
}
public matches(other: IEditorInput): boolean {
return other === this || (other instanceof WebviewEditorInput && other._id === this._id);
return other === this;
}
public get group(): GroupIdentifier | undefined {
@@ -234,11 +224,11 @@ export class WebviewEditorInput extends EditorInput {
return this._container;
}
public get webview(): WebviewElement | undefined {
public get webview(): Webview | undefined {
return this._webview;
}
public set webview(value: WebviewElement | undefined) {
public set webview(value: Webview | undefined) {
this._webviewDisposables = dispose(this._webviewDisposables);
this._webview = value;
@@ -272,6 +262,7 @@ export class WebviewEditorInput extends EditorInput {
}
public claimWebview(owner: any) {
this._webviewOwner = owner;
}
@@ -315,7 +306,6 @@ export class RevivedWebviewEditorInput extends WebviewEditorInput {
constructor(
viewType: string,
id: number | undefined,
name: string,
options: WebviewInputOptions,
state: any,
@@ -324,10 +314,10 @@ export class RevivedWebviewEditorInput extends WebviewEditorInput {
readonly location: URI;
readonly id: ExtensionIdentifier
},
public readonly reviver: (input: WebviewEditorInput) => Promise<void>,
private readonly reviver: (input: WebviewEditorInput) => Promise<void>,
@IWorkbenchLayoutService partService: IWorkbenchLayoutService,
) {
super(viewType, id, name, options, state, events, extension, partService);
super(viewType, name, options, state, events, extension, partService);
}
public async resolve(): Promise<IEditorModel> {

View File

@@ -17,7 +17,6 @@ interface SerializedIconPath {
interface SerializedWebview {
readonly viewType: string;
readonly id: number;
readonly title: string;
readonly options: WebviewInputOptions;
readonly extensionLocation: string | UriComponents | undefined;
@@ -44,7 +43,6 @@ export class WebviewEditorInputFactory implements IEditorInputFactory {
const data: SerializedWebview = {
viewType: input.viewType,
id: input.getId(),
title: input.getName(),
options: input.options,
extensionLocation: input.extension ? input.extension.location : undefined,
@@ -69,7 +67,7 @@ export class WebviewEditorInputFactory implements IEditorInputFactory {
const extensionLocation = reviveUri(data.extensionLocation);
const extensionId = data.extensionId ? new ExtensionIdentifier(data.extensionId) : undefined;
const iconPath = reviveIconPath(data.iconPath);
return this._webviewService.reviveWebview(data.viewType, data.id, data.title, iconPath, data.state, data.options, extensionLocation ? {
return this._webviewService.reviveWebview(data.viewType, data.title, iconPath, data.state, data.options, extensionLocation ? {
location: extensionLocation,
id: extensionId
} : undefined, data.group);

View File

@@ -39,7 +39,6 @@ export interface IWebviewEditorService {
reviveWebview(
viewType: string,
id: number,
title: string,
iconPath: { light: URI, dark: URI } | undefined,
state: any,
@@ -143,7 +142,7 @@ export class WebviewEditorService implements IWebviewEditorService {
},
events: WebviewEvents
): WebviewEditorInput {
const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, viewType, undefined, title, options, {}, events, extension);
const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, viewType, title, options, {}, events, extension);
this._editorService.openEditor(webviewInput, { pinned: true, preserveFocus: showOptions.preserveFocus }, showOptions.group);
return webviewInput;
}
@@ -165,7 +164,6 @@ export class WebviewEditorService implements IWebviewEditorService {
public reviveWebview(
viewType: string,
id: number,
title: string,
iconPath: { light: URI, dark: URI } | undefined,
state: any,
@@ -176,7 +174,7 @@ export class WebviewEditorService implements IWebviewEditorService {
},
group: number | undefined,
): WebviewEditorInput {
const webviewInput = this._instantiationService.createInstance(RevivedWebviewEditorInput, viewType, id, title, options, state, {}, extension, async (webview: WebviewEditorInput): Promise<void> => {
const webviewInput = this._instantiationService.createInstance(RevivedWebviewEditorInput, viewType, title, options, state, {}, extension, async (webview: WebviewEditorInput): Promise<void> => {
const didRevive = await this.tryRevive(webview);
if (didRevive) {
return Promise.resolve(undefined);
@@ -220,7 +218,7 @@ export class WebviewEditorService implements IWebviewEditorService {
// Revived webviews may not have an actively registered reviver but we still want to presist them
// since a reviver should exist when it is actually needed.
return !(webview instanceof RevivedWebviewEditorInput);
return webview instanceof RevivedWebviewEditorInput;
}
private async tryRevive(

View File

@@ -4,52 +4,45 @@
*--------------------------------------------------------------------------------------------*/
import { SimpleFindWidget } from 'vs/editor/contrib/find/simpleFindWidget';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { WebviewElement } from './webviewElement';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
export interface WebviewFindDelegate {
find(value: string, previous: boolean): void;
startFind(value: string): void;
stopFind(keepSelection?: boolean): void;
focus(): void;
}
export class WebviewFindWidget extends SimpleFindWidget {
constructor(
private _webview: WebviewElement | undefined,
private readonly _delegate: WebviewFindDelegate,
@IContextViewService contextViewService: IContextViewService,
@IContextKeyService contextKeyService: IContextKeyService
) {
super(contextViewService, contextKeyService);
}
dispose() {
this._webview = undefined;
super.dispose();
}
public find(previous: boolean) {
if (!this._webview) {
return;
}
const val = this.inputValue;
if (val) {
this._webview.find(val, { findNext: true, forward: !previous });
this._delegate.find(val, previous);
}
}
public hide() {
super.hide();
if (this._webview) {
this._webview.stopFind(true);
this._webview.focus();
}
this._delegate.stopFind(true);
this._delegate.focus();
}
public onInputChanged() {
if (!this._webview) {
return;
}
const val = this.inputValue;
if (val) {
this._webview.startFind(val);
this._delegate.startFind(val);
} else {
this._webview.stopFind(false);
this._delegate.stopFind(false);
}
}

View File

@@ -0,0 +1,88 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
/**
* Set when the find widget in a webview is visible.
*/
export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE = new RawContextKey<boolean>('webviewFindWidgetVisible', false);
export const IWebviewService = createDecorator<IWebviewService>('webviewService');
/**
* Handles the creation of webview elements.
*/
export interface IWebviewService {
_serviceBrand: any;
createWebview(
options: WebviewOptions,
contentOptions: WebviewContentOptions,
): Webview;
}
export interface WebviewPortMapping {
readonly port: number;
readonly resolvedPort: number;
}
export interface WebviewOptions {
readonly allowSvgs?: boolean;
readonly extension?: {
readonly location: URI;
readonly id?: ExtensionIdentifier;
};
readonly enableFindWidget?: boolean;
}
export interface WebviewContentOptions {
readonly allowScripts?: boolean;
readonly svgWhiteList?: string[];
readonly localResourceRoots?: ReadonlyArray<URI>;
readonly portMappings?: ReadonlyArray<WebviewPortMapping>;
}
export interface Webview {
contents: string;
options: WebviewContentOptions;
initialScrollProgress: number;
state: string | undefined;
readonly onDidFocus: Event<void>;
readonly onDidClickLink: Event<URI>;
readonly onDidScroll: Event<{ scrollYPercentage: number }>;
readonly onDidUpdateState: Event<string | undefined>;
readonly onMessage: Event<any>;
sendMessage(data: any): void;
update(
value: string,
options: WebviewContentOptions,
retainContextWhenHidden: boolean
): void;
layout(): void;
mountTo(parent: HTMLElement): void;
focus(): void;
dispose(): void;
reload(): void;
selectAll(): void;
copy(): void;
paste(): void;
cut(): void;
undo(): void;
redo(): void;
showFind(): void;
hideFind(): void;
}

View File

@@ -0,0 +1,43 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
(function () {
'use strict';
const registerVscodeResourceScheme = (function () {
let hasRegistered = false;
return () => {
if (hasRegistered) {
return;
}
hasRegistered = true;
// @ts-ignore
require('electron').webFrame.registerURLSchemeAsPrivileged('vscode-resource', {
secure: true,
bypassCSP: false,
allowServiceWorkers: false,
supportFetchAPI: true,
corsEnabled: true
});
};
}());
// @ts-ignore
const ipcRenderer = require('electron').ipcRenderer;
require('../../browser/pre/main')({
postMessage: (channel, data) => {
ipcRenderer.sendToHost(channel, data);
},
onMessage: (channel, handler) => {
ipcRenderer.on(channel, handler);
}
});
document.addEventListener('DOMContentLoaded', () => {
registerVscodeResourceScheme();
});
}());

View File

@@ -3,135 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { localize } from 'vs/nls';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } from 'vs/workbench/browser/editor';
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/electron-browser/webviewEditorInputFactory';
import { HideWebViewEditorFindCommand, OpenWebviewDeveloperToolsAction, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand, SelectAllWebviewEditorCommand, CopyWebviewEditorCommand, PasteWebviewEditorCommand, CutWebviewEditorCommand, UndoWebviewEditorCommand, RedoWebviewEditorCommand } from './webviewCommands';
import { WebviewEditor, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from './webviewEditor';
import { WebviewEditorInput } from './webviewEditorInput';
import { IWebviewEditorService, WebviewEditorService } from './webviewEditorService';
import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
import { isMacintosh } from 'vs/base/common/platform';
import { IWebviewService } from 'vs/workbench/contrib/webview/common/webview';
import { WebviewService } from 'vs/workbench/contrib/webview/electron-browser/webviewService';
(Registry.as<IEditorRegistry>(EditorExtensions.Editors)).registerEditor(new EditorDescriptor(
WebviewEditor,
WebviewEditor.ID,
localize('webview.editor.label', "webview editor")),
[new SyncDescriptor(WebviewEditorInput)]);
Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory(
WebviewEditorInputFactory.ID,
WebviewEditorInputFactory);
registerSingleton(IWebviewEditorService, WebviewEditorService, true);
const webviewDeveloperCategory = localize('developer', "Developer");
const actionRegistry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
export function registerWebViewCommands(editorId: string): void {
const contextKeyExpr = ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', editorId), ContextKeyExpr.not('editorFocus') /* https://github.com/Microsoft/vscode/issues/58668 */);
const showNextFindWidgetCommand = new ShowWebViewEditorFindWidgetCommand({
id: ShowWebViewEditorFindWidgetCommand.ID,
precondition: contextKeyExpr,
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_F,
weight: KeybindingWeight.EditorContrib
}
});
showNextFindWidgetCommand.register();
(new HideWebViewEditorFindCommand({
id: HideWebViewEditorFindCommand.ID,
precondition: ContextKeyExpr.and(
contextKeyExpr,
KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE),
kbOpts: {
primary: KeyCode.Escape,
weight: KeybindingWeight.EditorContrib
}
})).register();
(new SelectAllWebviewEditorCommand({
id: SelectAllWebviewEditorCommand.ID,
precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)),
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_A,
weight: KeybindingWeight.EditorContrib
}
})).register();
// These commands are only needed on MacOS where we have to disable the menu bar commands
if (isMacintosh) {
(new CopyWebviewEditorCommand({
id: CopyWebviewEditorCommand.ID,
precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)),
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_C,
weight: KeybindingWeight.EditorContrib
}
})).register();
(new PasteWebviewEditorCommand({
id: PasteWebviewEditorCommand.ID,
precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)),
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
weight: KeybindingWeight.EditorContrib
}
})).register();
(new CutWebviewEditorCommand({
id: CutWebviewEditorCommand.ID,
precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)),
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_X,
weight: KeybindingWeight.EditorContrib
}
})).register();
(new UndoWebviewEditorCommand({
id: UndoWebviewEditorCommand.ID,
precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)),
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_Z,
weight: KeybindingWeight.EditorContrib
}
})).register();
(new RedoWebviewEditorCommand({
id: RedoWebviewEditorCommand.ID,
precondition: ContextKeyExpr.and(contextKeyExpr, ContextKeyExpr.not(InputFocusedContextKey)),
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_Y,
secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z],
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z },
weight: KeybindingWeight.EditorContrib
}
})).register();
}
}
registerWebViewCommands(WebviewEditor.ID);
actionRegistry.registerWorkbenchAction(
new SyncActionDescriptor(OpenWebviewDeveloperToolsAction, OpenWebviewDeveloperToolsAction.ID, OpenWebviewDeveloperToolsAction.LABEL),
'Webview Tools',
webviewDeveloperCategory);
actionRegistry.registerWorkbenchAction(
new SyncActionDescriptor(ReloadWebviewAction, ReloadWebviewAction.ID, ReloadWebviewAction.LABEL),
'Reload Webview',
webviewDeveloperCategory);
registerSingleton(IWebviewService, WebviewService, true);

View File

@@ -17,36 +17,12 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import * as colorRegistry from 'vs/platform/theme/common/colorRegistry';
import { DARK, ITheme, IThemeService, LIGHT } from 'vs/platform/theme/common/themeService';
import { registerFileProtocol, WebviewProtocol } from 'vs/workbench/contrib/webview/electron-browser/webviewProtocols';
import { areWebviewInputOptionsEqual } from './webviewEditorService';
import { WebviewFindWidget } from './webviewFindWidget';
import { areWebviewInputOptionsEqual } from '../browser/webviewEditorService';
import { WebviewFindWidget } from '../browser/webviewFindWidget';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { WebviewContentOptions, WebviewPortMapping, WebviewOptions, Webview } from 'vs/workbench/contrib/webview/common/webview';
export interface WebviewPortMapping {
readonly port: number;
readonly resolvedPort: number;
}
export interface WebviewPortMapping {
readonly port: number;
readonly resolvedPort: number;
}
export interface WebviewOptions {
readonly allowSvgs?: boolean;
readonly extension?: {
readonly location: URI;
readonly id?: ExtensionIdentifier;
};
readonly enableFindWidget?: boolean;
}
export interface WebviewContentOptions {
readonly allowScripts?: boolean;
readonly svgWhiteList?: string[];
readonly localResourceRoots?: ReadonlyArray<URI>;
readonly portMappings?: ReadonlyArray<WebviewPortMapping>;
}
interface IKeydownEvent {
key: string;
@@ -316,7 +292,7 @@ class WebviewKeyboardHandler extends Disposable {
}
export class WebviewElement extends Disposable {
export class WebviewElement extends Disposable implements Webview {
private _webview: Electron.WebviewTag;
private _ready: Promise<void>;
@@ -350,7 +326,7 @@ export class WebviewElement extends Disposable {
this._webview.style.height = '0';
this._webview.style.outline = '0';
this._webview.preload = require.toUrl('./webview-pre.js');
this._webview.preload = require.toUrl('./pre/electron-index.js');
this._webview.src = 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E';
this._ready = new Promise(resolve => {
@@ -539,10 +515,6 @@ export class WebviewElement extends Disposable {
});
}
public set baseUrl(value: string) {
this._send('baseUrl', value);
}
public focus(): void {
this._webview.focus();
this._send('focus');
@@ -641,12 +613,13 @@ export class WebviewElement extends Disposable {
*
* @param value The string to search for. Empty strings are ignored.
*/
public find(value: string, options?: Electron.FindInPageOptions): void {
public find(value: string, previous: boolean): void {
// Searching with an empty value will throw an exception
if (!value) {
return;
}
const options = { findNext: true, forward: !previous };
if (!this._findStarted) {
this.startFind(value, options);
return;

View File

@@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { WebviewElement } from 'vs/workbench/contrib/webview/electron-browser/webviewElement';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { IWebviewService, WebviewOptions, WebviewContentOptions, Webview } from 'vs/workbench/contrib/webview/common/webview';
export class WebviewService implements IWebviewService {
_serviceBrand: any;
constructor(
@IWorkbenchLayoutService private readonly _layoutService: IWorkbenchLayoutService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
) { }
createWebview(
options: WebviewOptions,
contentOptions: WebviewContentOptions
): Webview {
const element = this._instantiationService.createInstance(WebviewElement,
this._layoutService.getContainer(Parts.EDITOR_PART),
options,
contentOptions);
return element;
}
}