提交 0cef61f4 编写于 作者: M Matt Bierner

Use Preload for WebView

This extracts the JS content from the webview html into its own file which is loaded using preload instead.
上级 d3171a38
......@@ -319,7 +319,8 @@ export class ExtensionEditor extends BaseEditor {
.then<void>(body => {
const webview = new WebView(
this.content,
document.querySelector('.monaco-editor-background')
document.querySelector('.monaco-editor-background'),
{ nodeintegration: false }
);
webview.style(this.themeService.getColorTheme());
......
......@@ -41,7 +41,7 @@ class HtmlZone implements IViewZone {
private _onVisibilityChanged(): void {
if (this._domNode.hasAttribute('monaco-visible-view-zone') && !this._webview) {
this._webview = new Webview(this.domNode, document.querySelector('.monaco-editor-background'));
this._webview = new Webview(this.domNode, document.querySelector('.monaco-editor-background'), { nodeintegration: true });
this._disposables.push(this._webview);
this._webview.contents = [this.htmlContent];
}
......
......@@ -77,7 +77,7 @@ export class HtmlPreviewPart extends BaseEditor {
private get webview(): Webview {
if (!this._webview) {
this._webview = new Webview(this._container, document.querySelector('.monaco-editor-background'));
this._webview = new Webview(this._container, document.querySelector('.monaco-editor-background'), { nodeintegration: true });
this._webview.baseUrl = this._baseUrl && this._baseUrl.toString(true);
this._webviewDisposables = [
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
var initData = {};
function styleBody(body) {
if (!body) {
return
}
body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast');
body.classList.add(initData.activeTheme);
};
function getTarget() {
return document.getElementById('_target');
};
const ipcRenderer = require('electron').ipcRenderer;
document.addEventListener("DOMContentLoaded", function (event) {
ipcRenderer.on('baseUrl', function (event, value) {
initData.baseUrl = value;
});
ipcRenderer.on('styles', function (event, value, activeTheme) {
initData.styles = value;
initData.activeTheme = activeTheme;
// webview
let defaultStyles = document.getElementById('_defaultStyles');
defaultStyles.innerHTML = initData.styles;
let body = getTarget().contentDocument.getElementsByTagName('body');
styleBody(body[0]);
// iframe
defaultStyles = getTarget().contentDocument.getElementById('_defaultStyles');
if (defaultStyles) {
defaultStyles.innerHTML = initData.styles;
}
});
// propagate focus
ipcRenderer.on('focus', function () {
getTarget().contentWindow.focus();
});
// update iframe-contents
ipcRenderer.on('content', function (event, value) {
const text = value.join('\n');
const newDocument = new DOMParser().parseFromString(text, 'text/html');
// know what happens here
const stats = {
scriptTags: newDocument.documentElement.querySelectorAll('script').length,
inputTags: newDocument.documentElement.querySelectorAll('input').length,
styleTags: newDocument.documentElement.querySelectorAll('style').length,
linkStyleSheetTags: newDocument.documentElement.querySelectorAll('link[rel=stylesheet]').length,
stringLen: text.length
};
// set base-url if applicable
if (initData.baseUrl && newDocument.head.getElementsByTagName('base').length === 0) {
const baseElement = document.createElement('base');
baseElement.href = initData.baseUrl;
newDocument.head.appendChild(baseElement);
}
// apply default styles
const defaultStyles = newDocument.createElement('style');
defaultStyles.id = '_defaultStyles';
defaultStyles.innerHTML = initData.styles;
if (newDocument.head.hasChildNodes()) {
newDocument.head.insertBefore(defaultStyles, newDocument.head.firstChild);
} else {
newDocument.head.appendChild(defaultStyles);
}
// script to bubble out link-clicks
const defaultScripts = newDocument.createElement('script');
defaultScripts.innerHTML = `
document.body.addEventListener('click', function(event) {
let node = event.target;
while (node) {
if (node.tagName === 'A' && node.href) {
let baseElement = window.document.getElementsByTagName('base')[0];
if (baseElement && node.href.indexOf(baseElement.href) >= 0 && node.hash) {
let scrollTarget = window.document.getElementById(node.hash.substr(1, node.hash.length - 1));
if (scrollTarget) {
scrollgetTarget().scrollIntoView();
}
} else {
window.parent.postMessage({ command: 'did-click-link', data: node.href }, 'file://');
}
event.preventDefault();
break;
}
node = node.parentNode;
}
});`
newDocument.body.appendChild(defaultScripts);
styleBody(newDocument.body);
// keep current scrollTop around and use later
const {scrollTop} = getTarget().contentDocument.body;
// write new content onto iframe
getTarget().contentDocument.open('text/html', 'replace');
// set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off
// and DOCTYPE is needed in the iframe to ensure that the user agent stylesheet is correctly overridden
getTarget().contentDocument.write('<!DOCTYPE html>');
getTarget().contentDocument.write(newDocument.documentElement.innerHTML);
getTarget().contentDocument.close();
// workaround for https://github.com/Microsoft/vscode/issues/12865
// check new scrollTop and reset if neccessary
setTimeout(() => {
if (scrollTop !== getTarget().contentDocument.body.scrollTop) {
getTarget().contentDocument.body.scrollTop = scrollTop;
}
}, 0);
ipcRenderer.sendToHost('did-set-content', stats);
});
// forward messages from the embedded iframe
window.onmessage = function (message) {
const { command, data} = message.data;
ipcRenderer.sendToHost(command, data);
};
// signal ready
ipcRenderer.sendToHost('webview-ready', process.pid);
});
\ No newline at end of file
......@@ -6,138 +6,5 @@
</head>
<body style="margin: 0; overflow: hidden; width: 100%; height: 100%">
<iframe id="_target" frameborder="0" style="margin: 0; overflow: hidden; position: absolute; width: 100%; height: 100%"></iframe>
<script>
'use strict';
var initData = {};
function styleBody(body) {
if (!body) {
return
}
body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast');
body.classList.add(initData.activeTheme);
}
const ipcRenderer = require('electron').ipcRenderer;
const target = document.getElementById('_target');
ipcRenderer.on('baseUrl', function(event, value) {
initData.baseUrl = value;
});
ipcRenderer.on('styles', function(event, value, activeTheme) {
initData.styles = value;
initData.activeTheme = activeTheme;
// webview
let defaultStyles = document.getElementById('_defaultStyles');
defaultStyles.innerHTML = initData.styles;
let body = target.contentDocument.getElementsByTagName('body');
styleBody(body[0]);
// iframe
defaultStyles = target.contentDocument.getElementById('_defaultStyles');
if(defaultStyles) {
defaultStyles.innerHTML = initData.styles;
}
});
// propagate focus
ipcRenderer.on('focus', function() {
target.contentWindow.focus();
});
// update iframe-contents
ipcRenderer.on('content', function(event, value) {
const text = value.join('\n');
const newDocument = new DOMParser().parseFromString(text, 'text/html');
// know what happens here
const stats = {
scriptTags: newDocument.documentElement.querySelectorAll('script').length,
inputTags: newDocument.documentElement.querySelectorAll('input').length,
styleTags: newDocument.documentElement.querySelectorAll('style').length,
linkStyleSheetTags: newDocument.documentElement.querySelectorAll('link[rel=stylesheet]').length,
stringLen: text.length
};
// set base-url if applicable
if(initData.baseUrl && newDocument.head.getElementsByTagName('base').length === 0) {
const baseElement = document.createElement('base');
baseElement.href = initData.baseUrl;
newDocument.head.appendChild(baseElement);
}
// apply default styles
const defaultStyles = newDocument.createElement('style');
defaultStyles.id = '_defaultStyles';
defaultStyles.innerHTML = initData.styles;
if (newDocument.head.hasChildNodes()) {
newDocument.head.insertBefore(defaultStyles, newDocument.head.firstChild);
} else {
newDocument.head.appendChild(defaultStyles);
}
// script to bubble out link-clicks
const defaultScripts = newDocument.createElement('script');
defaultScripts.innerHTML = `
document.body.addEventListener('click', function(event) {
let node = event.target;
while (node) {
if (node.tagName === 'A' && node.href) {
let baseElement = window.document.getElementsByTagName('base')[0];
if (baseElement && node.href.indexOf(baseElement.href) >= 0 && node.hash) {
let scrollTarget = window.document.getElementById(node.hash.substr(1, node.hash.length - 1));
if (scrollTarget) {
scrollTarget.scrollIntoView();
}
} else {
window.parent.postMessage({ command: 'did-click-link', data: node.href }, 'file://');
}
event.preventDefault();
break;
}
node = node.parentNode;
}
});`
newDocument.body.appendChild(defaultScripts);
styleBody(newDocument.body);
// keep current scrollTop around and use later
const {scrollTop} = target.contentDocument.body;
// write new content onto iframe
target.contentDocument.open('text/html', 'replace');
// set DOCTYPE for newDocument explicitly as DOMParser.parseFromString strips it off
// and DOCTYPE is needed in the iframe to ensure that the user agent stylesheet is correctly overridden
target.contentDocument.write('<!DOCTYPE html>');
target.contentDocument.write(newDocument.documentElement.innerHTML);
target.contentDocument.close();
// workaround for https://github.com/Microsoft/vscode/issues/12865
// check new scrollTop and reset if neccessary
setTimeout(() => {
if(scrollTop !== target.contentDocument.body.scrollTop) {
target.contentDocument.body.scrollTop = scrollTop;
}
}, 0);
ipcRenderer.sendToHost('did-set-content', stats);
});
// forward messages from the embedded iframe
window.onmessage = function(message) {
const { command, data} = message.data;
ipcRenderer.sendToHost(command, data);
};
// signal ready
ipcRenderer.sendToHost('webview-ready', process.pid);
</script>
</body>
</html>
\ No newline at end of file
......@@ -20,6 +20,7 @@ declare interface WebviewElement extends HTMLElement {
autoSize: 'on';
nodeintegration: 'on';
disablewebsecurity: 'on';
preload: string;
getURL(): string;
getTitle(): string;
......@@ -47,6 +48,10 @@ MenuRegistry.addCommand({
type ApiThemeClassName = 'vscode-light' | 'vscode-dark' | 'vscode-high-contrast';
export interface WebviewOptions {
nodeintegration: boolean;
}
export default class Webview {
private _webview: WebviewElement;
......@@ -55,7 +60,7 @@ export default class Webview {
private _onDidClickLink = new Emitter<URI>();
private _onDidLoadContent = new Emitter<{ stats: any }>();
constructor(parent: HTMLElement, private _styleElement: Element) {
constructor(parent: HTMLElement, private _styleElement: Element, options: WebviewOptions) {
this._webview = <any>document.createElement('webview');
this._webview.style.width = '100%';
......@@ -63,7 +68,11 @@ export default class Webview {
this._webview.style.outline = '0';
this._webview.style.opacity = '0';
this._webview.autoSize = 'on';
this._webview.nodeintegration = 'on';
if (options.nodeintegration) {
this._webview.nodeintegration = 'on';
}
this._webview.preload = require.toUrl('./webview-pre.js');
this._webview.src = require.toUrl('./webview.html');
this._ready = new TPromise<this>(resolve => {
......
......@@ -91,7 +91,8 @@ export class ReleaseNotesEditor extends BaseEditor {
.then<void>(body => {
this.webview = new WebView(
this.content,
document.querySelector('.monaco-editor-background')
document.querySelector('.monaco-editor-background'),
{ nodeintegration: false }
);
this.webview.baseUrl = `https://code.visualstudio.com/raw/`;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册