提交 3e2c2fe5 编写于 作者: P Peng Lyu

reusable webview

上级 8137a92d
......@@ -21,6 +21,7 @@ import { equals, distinct } from 'vs/base/common/arrays';
import { DataTransfers, StaticDND, IDragAndDropData } from 'vs/base/browser/dnd';
import { disposableTimeout, Delayer } from 'vs/base/common/async';
import { isFirefox } from 'vs/base/browser/browser';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
interface IItem<T> {
readonly id: string;
......@@ -268,6 +269,10 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.layout();
}
public triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) {
this.scrollableElement.triggerScrollFromMouseWheelEvent(browserEvent);
}
updateDynamicHeight(index: number, element: T, size: number): void {
this.rangeMap.splice(index, 1, [
{
......@@ -283,6 +288,8 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.updateItemInDOM(this.items[i], i);
}
}
this._onDidChangeContentHeight.fire(this.contentHeight);
}
splice(start: number, deleteCount: number, elements: T[] = []): T[] {
......
......@@ -26,6 +26,7 @@ import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice';
import { clamp } from 'vs/base/common/numbers';
import { matchesPrefix } from 'vs/base/common/filters';
import { IDragAndDropData } from 'vs/base/browser/dnd';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
interface ITraitChangeEvent {
indexes: number[];
......@@ -1305,6 +1306,10 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.eventBufferer.bufferEvents(() => this.spliceable.splice(start, deleteCount, elements));
}
public triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) {
this.view.triggerScrollFromMouseWheelEvent(browserEvent);
}
updateDynamicHeight(index: number, element: T, size: number): void {
this.view.updateDynamicHeight(index, element, size);
}
......
......@@ -298,6 +298,10 @@ export abstract class AbstractScrollableElement extends Widget {
this._revealOnScroll = value;
}
public triggerScrollFromMouseWheelEvent(browserEvent: IMouseWheelEvent) {
this._onMouseWheel(new StandardWheelEvent(browserEvent));
}
// -------------------- mouse wheel scrolling --------------------
private _setListeningToMouseWheel(shouldListen: boolean): void {
......
......@@ -32,13 +32,13 @@ import { ITextModel } from 'vs/editor/common/model';
import { IModeService } from 'vs/editor/common/services/modeService';
import { Emitter } from 'vs/base/common/event';
import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
export class ViewCell {
private _textModel: ITextModel | null = null;
private _mdRenderer: marked.Renderer | null = null;
private _html: string | null = null;
private _dynamicHeight: number | null = null;
private _output: HTMLElement | null = null;
protected readonly _onDidDispose = new Emitter<void>();
readonly onDidDispose = this._onDidDispose.event;
......@@ -171,6 +171,8 @@ export interface NotebookHandler {
saveNotebookCell(cell: ViewCell): void;
layoutElement(cell: ViewCell, height: number): void;
createContentWidget(cell: ViewCell, shadowContent: string, shadowElement: HTMLElement, offset: number): void;
disposeViewCell(cell: ViewCell): void;
triggerWheel(event: IMouseWheelEvent): void;
}
export interface CellRenderTemplate {
......@@ -618,6 +620,8 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende
this.disposables.delete(templateData.outputContainer!);
}
}
this.handler.disposeViewCell(element);
}
getSimpleCodeEditorWidgetOptions(): ICodeEditorWidgetOptions {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { NotebookHandler, ViewCell } from 'vs/workbench/contrib/notebook/browser/cellRenderer';
import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
import * as DOM from 'vs/base/browser/dom';
import * as UUID from 'vs/base/common/uuid';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
export class WebviewContentWidget extends Disposable {
public element: HTMLElement;
public webview: WebviewElement;
private _dimension: DOM.Dimension | null = null;
private readonly _localStore = new DisposableStore();
private _detachedFromViewEvents: boolean = false;
get detachedFromViewEvents() {
return this._detachedFromViewEvents;
}
set detachedFromViewEvents(newState: boolean) {
this._detachedFromViewEvents = newState;
this._localStore.clear();
}
constructor(public shadowElement: HTMLElement, public cell: ViewCell, public offset: number, public webviewService: IWebviewService, shadowContent: string, public notebookHandler: NotebookHandler) {
super();
this.element = document.createElement('div');
this.element.style.width = 'calc(100% - 36px)';
this.element.style.height = '700px';
this.element.style.position = 'absolute';
this.element.style.margin = '0px 24px 0px 24px';
this.webview = this._createInset(webviewService, shadowContent);
this.webview.mountTo(this.element);
this._localStore.add(this.webview.onDidSetInitialDimension(dimension => {
this._dimension = dimension;
this.shadowElement.style.height = `${dimension.height}px`;
this.shadowElement.style.maxWidth = '100%';
this.shadowElement.style.maxHeight = '700px';
this.element.style.height = `${dimension.height}px`;
this.element.style.maxWidth = '100%';
this.element.style.maxHeight = '700px';
const lineNum = cell.lineCount;
const totalHeight = Math.max(lineNum + 1, 5) * 21;
cell.setDynamicHeight(totalHeight + 32 + dimension.height);
notebookHandler.layoutElement(cell, totalHeight + 32 + dimension.height);
}));
this._localStore.add(this.webview.onDidWheel(e => {
this.notebookHandler.triggerWheel(e);
}));
}
updateInitialization(shadowElement: HTMLElement, cell: ViewCell, offset: number, shadowContent: string) {
this._localStore.clear();
this.shadowElement = shadowElement;
this.cell = cell;
this.offset = offset;
this.element.style.height = '700px';
this.element.style.width = 'calc(100% - 36px)';
this.webview.html = shadowContent;
this._localStore.add(this.webview.onDidSetInitialDimension(dimension => {
this._dimension = dimension;
this.shadowElement.style.height = `${dimension.height}px`;
this.shadowElement.style.maxWidth = '100%';
this.shadowElement.style.maxHeight = '700px';
this.element.style.height = `${dimension.height}px`;
this.element.style.maxWidth = '100%';
this.element.style.maxHeight = '700px';
const lineNum = cell.lineCount;
const totalHeight = Math.max(lineNum + 1, 5) * 21;
cell.setDynamicHeight(totalHeight + 32 + dimension.height);
this.notebookHandler.layoutElement(cell, totalHeight + 32 + dimension.height);
}));
this._localStore.add(this.webview.onDidWheel(e => {
this.notebookHandler.triggerWheel(e);
}));
}
public updateShadowElement(element: HTMLElement) {
this.shadowElement = element;
if (this._dimension) {
this.shadowElement.style.minWidth = `${this._dimension.width}px`;
this.shadowElement.style.height = `${this._dimension.height}px`;
this.shadowElement.style.maxWidth = '100%';
this.shadowElement.style.maxHeight = '700px';
const lineNum = this.cell.lineCount;
const totalHeight = Math.max(lineNum + 1, 5) * 21;
this.cell.setDynamicHeight(totalHeight + 32 + this._dimension.height);
this.notebookHandler.layoutElement(this.cell, totalHeight + 32 + this._dimension.height);
}
}
private _createInset(webviewService: IWebviewService, content: string) {
const webview = webviewService.createWebview('' + UUID.generateUuid(), {
enableFindWidget: false,
}, {
allowScripts: true
});
webview.html = content;
return webview;
}
}
......@@ -11,7 +11,6 @@ import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/noteb
import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IEditorInput } from 'vs/workbench/common/editor';
import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { endsWith } from 'vs/base/common/strings';
......@@ -30,7 +29,6 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
);
export class NotebookContribution implements IWorkbenchContribution {
private editorOpeningListener: IDisposable | undefined;
private _resourceMapping: Map<string, NotebookEditorInput> = new Map<string, NotebookEditorInput>();
constructor(
......@@ -38,7 +36,7 @@ export class NotebookContribution implements IWorkbenchContribution {
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
this.editorOpeningListener = this.editorService.overrideOpenEditor((editor, options, group) => this.onEditorOpening(editor, options, group));
this.editorService.overrideOpenEditor((editor, options, group) => this.onEditorOpening(editor, options, group));
}
private onEditorOpening(editor: IEditorInput, options: IEditorOptions | ITextEditorOptions | undefined, group: IEditorGroup): IOpenEditorOverride | undefined {
......
......@@ -20,93 +20,19 @@ import { WorkbenchList } from 'vs/platform/list/browser/listService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { NotebookHandler, ViewCell, MarkdownCellRenderer, CodeCellRenderer, NotebookCellListDelegate } from 'vs/workbench/contrib/notebook/browser/cellRenderer';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
import * as UUID from 'vs/base/common/uuid';
import { IWebviewService } from 'vs/workbench/contrib/webview/browser/webview';
import { WebviewContentWidget } from 'vs/workbench/contrib/notebook/browser/contentWidget';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
const $ = DOM.$;
export interface IContentWidget {
offset: number;
cell: ViewCell;
element: HTMLElement;
webview: WebviewElement;
}
export class WebviewContentWidget implements IContentWidget {
public element: HTMLElement;
public webview: WebviewElement;
private _dimension: DOM.Dimension | null = null;
constructor(
public shadowElement: HTMLElement,
public cell: ViewCell,
public offset: number,
webviewService: IWebviewService,
shadowContent: string,
public notebookHandler: NotebookHandler
) {
this.element = document.createElement('div');
this.element.style.width = 'calc(100% - 36px)';
this.element.style.height = '700px';
this.element.style.position = 'absolute';
this.element.style.margin = '0px 24px 0px 24px';
this.webview = this._createInset(webviewService, shadowContent);
this.webview.mountTo(this.element);
this.webview.onDidSetInitialDimension(dimension => {
this._dimension = dimension;
// this.shadowElement.style.minWidth = `${dimension.width}px`;
this.shadowElement.style.height = `${dimension.height}px`;
this.shadowElement.style.maxWidth = '100%';
this.shadowElement.style.maxHeight = '700px';
// this.element.style.minWidth= `${dimension.width}px`;
this.element.style.height = `${dimension.height}px`;
this.element.style.maxWidth = '100%';
this.element.style.maxHeight = '700px';
const lineNum = cell.lineCount;
const totalHeight = Math.max(lineNum + 1, 5) * 21;
cell.setDynamicHeight(totalHeight + 32 + dimension.height);
notebookHandler.layoutElement(cell, totalHeight + 32 + dimension.height);
});
}
public updateShadowElement(element: HTMLElement) {
this.shadowElement = element;
if (this._dimension) {
this.shadowElement.style.minWidth = `${this._dimension.width}px`;
this.shadowElement.style.height = `${this._dimension.height}px`;
this.shadowElement.style.maxWidth = '100%';
this.shadowElement.style.maxHeight = '700px';
const lineNum = this.cell.lineCount;
const totalHeight = Math.max(lineNum + 1, 5) * 21;
this.cell.setDynamicHeight(totalHeight + 32 + this._dimension.height);
this.notebookHandler.layoutElement(this.cell, totalHeight + 32 + this._dimension.height);
}
}
private _createInset(webviewService: IWebviewService, content: string) {
const webview = webviewService.createWebview('' + UUID.generateUuid(), {
enableFindWidget: false,
}, {
allowScripts: true
});
webview.html = content;
return webview;
}
dispose() {
}
}
export class NotebookEditor extends BaseEditor implements NotebookHandler {
static readonly ID: string = 'workbench.editor.notebook';
private rootElement!: HTMLElement;
private body!: HTMLElement;
private contentWidgets!: HTMLElement;
private contentWidgetsMap: Map<ViewCell, WebviewContentWidget> = new Map();
private contentWidgetsPool: WebviewContentWidget[] = [];
private list: WorkbenchList<ViewCell> | undefined;
private model: NotebookEditorModel | undefined;
......@@ -189,8 +115,12 @@ export class NotebookEditor extends BaseEditor implements NotebookHandler {
}
);
this.list.onDidScroll((e) => {
this._register(this.list.onDidScroll((e) => {
this.contentWidgetsMap.forEach((value, cell) => {
if (value.detachedFromViewEvents) {
return;
}
let index = this.model!.getNotebook().cells.indexOf(cell.cell);
let top = this.list?.getElementTop(index);
if (top !== null && top !== undefined) {
......@@ -199,13 +129,35 @@ export class NotebookEditor extends BaseEditor implements NotebookHandler {
domElement.style.top = `${-scrollTop + top + value.offset}px`;
}
});
});
}));
this._register(this.list);
}
triggerWheel(event: IMouseWheelEvent) {
this.list?.triggerScrollFromMouseWheelEvent(event);
}
createContentWidget(cell: ViewCell, shadowContent: string, shadowElement: HTMLElement, offset: number) {
let zone = this.contentWidgetsMap.get(cell);
if (!zone) {
let existingContentWidget = this.contentWidgetsPool.pop();
if (existingContentWidget) {
existingContentWidget.detachedFromViewEvents = false;
existingContentWidget.updateInitialization(shadowElement, cell, offset, shadowContent);
this.contentWidgetsMap.set(cell, existingContentWidget);
let index = this.model!.getNotebook().cells.indexOf(cell.cell);
let top = this.list?.getElementTop(index);
if (top !== null && top !== undefined) {
let domElement = existingContentWidget.element;
let scrollTop = this.list?.scrollTop || 0;
domElement.style.top = `${-scrollTop + top + existingContentWidget.offset}px`;
}
return;
}
let contentWidget = new WebviewContentWidget(
shadowElement,
cell,
......@@ -222,6 +174,28 @@ export class NotebookEditor extends BaseEditor implements NotebookHandler {
}
}
disposeViewCell(cell: ViewCell) {
let zone = this.contentWidgetsMap.get(cell);
if (zone) {
// we are going to dispose a view who has a webview
if (!zone.webview.containsScript) {
// this view can be disposed
zone.detachedFromViewEvents = true;
zone.element.style.top = '-2400px';
zone.element.style.height = '700px';
if (this.contentWidgetsPool.length < 10) {
this.contentWidgetsPool.push(zone);
this.contentWidgetsMap.delete(cell);
} else {
this.contentWidgets.removeChild(zone.element);
this.contentWidgetsMap.delete(cell);
}
}
}
}
onHide() {
super.onHide();
......@@ -268,6 +242,7 @@ export class NotebookEditor extends BaseEditor implements NotebookHandler {
// list.splice -> renderElement -> resize -> layoutElement
// above flow will actually break how list view renders it self as it messes up with the internal state
// instead we run the layout update in next tick
//. @TODO @rebornix, it should be batched.
let index = this.model!.getNotebook().cells.indexOf(cell.cell);
this.list?.updateDynamicHeight(index, cell, height);
}, 0);
......
......@@ -97,12 +97,10 @@ class RichDisplayRenderer implements IMimeRenderer {
outputNode.appendChild(display);
hasDynamicHeight = true;
} else if (output.data && output.data['text/html']) {
let data = output.data['text/html'];
let str = isArray(data) ? data.join('') : data;
display.style.width = '100%';
display.style.height = '100px';
// display.style.backgroundColor = 'gray';
hasDynamicHeight = true;
outputNode.appendChild(display);
......
......@@ -13,6 +13,9 @@ import { WebviewExtensionDescription, WebviewOptions, WebviewContentOptions } fr
import { areWebviewInputOptionsEqual } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService';
import { WebviewThemeDataProvider } from 'vs/workbench/contrib/webview/common/themeing';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
import { VSBuffer } from 'vs/base/common/buffer';
import { StopWatch } from 'vs/base/common/stopwatch';
export const enum WebviewMessageChannels {
onmessage = 'onmessage',
......@@ -25,7 +28,10 @@ export const enum WebviewMessageChannels {
loadResource = 'load-resource',
loadLocalhost = 'load-localhost',
webviewReady = 'webview-ready',
didSetInitialDimension = 'did-set-initial-dimension'
didSetInitialDimension = 'did-set-initial-dimension',
containsScripts = 'content-contains-scripts',
wheel = 'did-scroll-wheel',
ack = 'speed-test-ack'
}
interface IKeydownEvent {
......@@ -59,6 +65,8 @@ export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
public extension: WebviewExtensionDescription | undefined;
private sw: StopWatch | null = null;
constructor(
// TODO: matb, this should not be protected. The only reason it needs to be is that the base class ends up using it in the call to createElement
protected readonly id: string,
......@@ -122,6 +130,19 @@ export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
this.handleInitialDimension(dimension);
}));
this._register(this.on(WebviewMessageChannels.containsScripts, (containsScript: boolean) => {
this.containsScript = containsScript;
}));
this._register(this.on(WebviewMessageChannels.wheel, (event: IMouseWheelEvent) => {
this._onDidWheel.fire(event);
}));
this._register(this.on(WebviewMessageChannels.ack, (buf) => {
this.sw!.stop();
console.log(this._printSpeed(this._convert(2 * 10 * 1024 * 1024, this.sw!.elapsed())));
}));
this._register(this.on(WebviewMessageChannels.didBlur, () => {
this.handleFocusChange(false);
}));
......@@ -137,6 +158,20 @@ export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
this._register(webviewThemeDataProvider.onThemeDataChanged(this.style, this));
}
private _convert(byteCount: number, elapsedMillis: number): number {
return (byteCount * 1000 * 8) / elapsedMillis;
}
private _printSpeed(n: number): string {
if (n <= 1024) {
return `${n} bps`;
}
if (n < 1024 * 1024) {
return `${(n / 1024).toFixed(1)} kbps`;
}
return `${(n / 1024 / 1024).toFixed(1)} Mbps`;
}
dispose(): void {
if (this.element) {
this.element.remove();
......@@ -158,6 +193,9 @@ export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
private readonly _onDidScroll = this._register(new Emitter<{ readonly scrollYPercentage: number; }>());
public readonly onDidScroll = this._onDidScroll.event;
private readonly _onDidWheel = this._register(new Emitter<IMouseWheelEvent>());
public readonly onDidWheel= this._onDidWheel.event;
private readonly _onDidUpdateState = this._register(new Emitter<string | undefined>());
public readonly onDidUpdateState = this._onDidUpdateState.event;
......@@ -167,6 +205,8 @@ export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
private readonly _onDidSetInitialDimension = this._register(new Emitter<Dimension>());
public readonly onDidSetInitialDimension = this._onDidSetInitialDimension.event;
public containsScript: boolean = false;
public sendMessage(data: any): void {
this._send('message', data);
}
......@@ -221,6 +261,9 @@ export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
state: this.content.state,
};
this.doUpdateContent();
setTimeout(() => {
this.speedTest();
}, 3000);
}
public set contentOptions(options: WebviewContentOptions) {
......@@ -248,6 +291,18 @@ export abstract class BaseWebview<T extends HTMLElement> extends Disposable {
this._send('initial-scroll-position', value);
}
public speedTest() {
const SIZE = 10 * 1024 * 1024; // 10MB
let buff = VSBuffer.alloc(SIZE);
let value = Math.random() % 256;
for (let i = 0; i < SIZE; i++) {
buff.writeUInt8(value, i);
}
this.sw = StopWatch.create(true);
this._send('speedTest', buff.buffer);
}
private doUpdateContent() {
this._send('content', {
contents: this.content.html,
......
......@@ -10,11 +10,15 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IWebviewService, Webview, WebviewContentOptions, WebviewEditorOverlay, WebviewElement, WebviewOptions, WebviewExtensionDescription } from 'vs/workbench/contrib/webview/browser/webview';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { Dimension } from 'vs/base/browser/dom';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
/**
* Webview editor overlay that creates and destroys the underlying webview as needed.
*/
export class DynamicWebviewEditorOverlay extends Disposable implements WebviewEditorOverlay {
private readonly _onDidWheel= this._register(new Emitter<IMouseWheelEvent>());
public readonly onDidWheel = this._onDidWheel.event;
private readonly _onDidSetInitialDimension = this._register(new Emitter<Dimension>());
public readonly onDidSetInitialDimension = this._onDidSetInitialDimension.event;
......@@ -31,6 +35,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewEd
private _options: WebviewOptions;
private _owner: any = undefined;
containsScript: boolean = false;
public constructor(
private readonly id: string,
......
......@@ -60,7 +60,6 @@
font-weight: var(--vscode-font-weight);
font-size: var(--vscode-font-size);
margin: 0;
padding: 0 20px;
height: 100%;
width: 100%;
}
......@@ -271,6 +270,21 @@
};
let isHandlingScroll = false;
const handleWheel = (event) => {
if (isHandlingScroll) {
return;
}
host.postMessage('did-scroll-wheel', {
deltaMode: event.deltaMode,
deltaX: event.deltaX,
deltaY: event.deltaY,
deltaZ: event.deltaZ,
detail: event.detail,
type: event.type
});
};
const handleInnerScroll = (event) => {
if (!event.target || !event.target.body) {
return;
......@@ -312,6 +326,7 @@
// apply default script
if (options.allowScripts) {
const defaultScript = newDocument.createElement('script');
defaultScript.id = '_vscodeApiScript';
defaultScript.textContent = getVsCodeApiScript(data.state);
newDocument.head.prepend(defaultScript);
}
......@@ -453,6 +468,23 @@
}, 0);
});
const checkScripts = (document) => {
let scripts = document.getElementsByTagName('script');
if (scripts.length > 1) {
return true;
}
if (scripts[0].id !== '_vscodeApiScript') {
return true;
}
// no scripts
let iframes = document.getElementsByTagName('iframe');
return iframes.length > 0;
};
const onLoad = (contentDocument, contentWindow) => {
if (contentDocument && contentDocument.body) {
// Workaround for https://github.com/Microsoft/vscode/issues/12865
......@@ -475,8 +507,10 @@
}
host.postMessage('did-set-initial-dimension', { width: newFrame.contentWindow.document.body.scrollWidth, height: newFrame.contentWindow.document.body.scrollHeight });
host.postMessage('content-contains-scripts', checkScripts(newFrame.contentWindow.document));
contentWindow.addEventListener('scroll', handleInnerScroll);
contentWindow.addEventListener('wheel', handleWheel);
pendingMessages.forEach((data) => {
contentWindow.postMessage(data, '*');
......@@ -545,6 +579,9 @@
initData.initialScrollProgress = progress;
});
host.onMessage('speedTest', (_event, buf) => {
host.postMessage('speed-test-ack', buf);
});
trackFocus({
onFocus: () => host.postMessage('did-focus'),
......
......@@ -12,6 +12,7 @@ import * as nls from 'vs/nls';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent';
/**
* Set when the find widget in a webview is visible.
......@@ -69,11 +70,13 @@ export interface Webview extends IDisposable {
extension: WebviewExtensionDescription | undefined;
initialScrollProgress: number;
state: string | undefined;
containsScript: boolean;
readonly onDidFocus: Event<void>;
readonly onDidSetInitialDimension: Event<Dimension>;
readonly onDidClickLink: Event<string>;
readonly onDidScroll: Event<{ scrollYPercentage: number }>;
readonly onDidWheel: Event<IMouseWheelEvent>;
readonly onDidUpdateState: Event<string | undefined>;
readonly onMessage: Event<any>;
readonly onMissingCsp: Event<ExtensionIdentifier>;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册