webview-pre.js 5.9 KB
Newer Older
M
Matt Bierner 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

7 8 9
const ipcRenderer = require('electron').ipcRenderer;


M
Matt Bierner 已提交
10 11 12 13 14 15 16 17
var initData = {};

function styleBody(body) {
	if (!body) {
		return
	}
	body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast');
	body.classList.add(initData.activeTheme);
18
}
M
Matt Bierner 已提交
19 20

function getTarget() {
B
Benjamin Pasero 已提交
21
	return document.getElementById('_target');
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
}

function handleInnerClick(event) {
	if (!event || !event.view || !event.view.document) {
		return;
	}
	var node = event.target;
	while (node) {
		if (node.tagName === "A" && node.href) {
			var baseElement = event.view.document.getElementsByTagName("base")[0];
			if (node.getAttribute("href") === "#") {
				event.view.scrollTo(0, 0);
			} else if (node.hash && (node.getAttribute("href") === node.hash || (baseElement && node.href.indexOf(baseElement.href) >= 0))) {
				var scrollTarget = event.view.document.getElementById(node.hash.substr(1, node.hash.length - 1));
				if (scrollTarget) {
					scrollTarget.scrollIntoView();
				}
			} else {
				ipcRenderer.sendToHost("did-click-link", node.href);
			}
			event.preventDefault();
			break;
		}
		node = node.parentNode;
	}
}
M
Matt Bierner 已提交
48 49 50 51 52 53 54 55 56 57 58 59


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
M
Matt Bierner 已提交
60 61 62 63 64
		var target = getTarget()
		if (!target) {
			return;
		}
		var body = target.contentDocument.getElementsByTagName('body');
M
Matt Bierner 已提交
65 66 67
		styleBody(body[0]);

		// iframe
68
		defaultStyles = target.contentDocument.getElementById('_defaultStyles');
M
Matt Bierner 已提交
69 70 71 72 73 74 75
		if (defaultStyles) {
			defaultStyles.innerHTML = initData.styles;
		}
	});

	// propagate focus
	ipcRenderer.on('focus', function () {
76 77 78 79
		const target = getTarget();
		if (target) {
			target.contentWindow.focus();
		}
M
Matt Bierner 已提交
80 81 82
	});

	// update iframe-contents
M
Matt Bierner 已提交
83
	ipcRenderer.on('content', function (_event, value) {
M
Matt Bierner 已提交
84 85 86 87 88 89 90 91 92 93 94 95 96 97
		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) {
98
			const baseElement = newDocument.createElement('base');
M
Matt Bierner 已提交
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
			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);
		}

		styleBody(newDocument.body);

115
		const frame = getTarget();
116 117 118
		if (frame) {
			frame.setAttribute('id', '_oldTarget');
		}
119

M
Matt Bierner 已提交
120
		// keep current scrollTop around and use later
121
		const scrollTop = frame && frame.contentDocument && frame.contentDocument.body ? frame.contentDocument.body.scrollTop : 0;
122 123 124 125

		const newFrame = document.createElement('iframe');
		newFrame.setAttribute('id', '_target');
		newFrame.setAttribute('frameborder', '0');
M
Matt Bierner 已提交
126
		newFrame.setAttribute('sandbox', 'allow-scripts allow-forms allow-same-origin');
M
Matt Bierner 已提交
127 128
		newFrame.style.cssText = "margin: 0; overflow: hidden; position: absolute; width: 100%; height: 100%; display: none";
		document.body.appendChild(newFrame);
M
Matt Bierner 已提交
129

M
Matt Bierner 已提交
130 131
		// write new content onto iframe
		newFrame.contentDocument.open('text/html', 'replace');
M
Matt Bierner 已提交
132 133 134 135
		newFrame.contentWindow.onbeforeunload = function (e) {
			console.log('prevented webview navigation');
			return false;
		};
M
Matt Bierner 已提交
136

137 138 139
		newFrame.contentWindow.addEventListener('DOMContentLoaded', function (e) {
			const contentDocument = e.target;
			if (contentDocument.body) {
M
Matt Bierner 已提交
140 141 142

				// Workaround for https://github.com/Microsoft/vscode/issues/12865
				// check new scrollTop and reset if neccessary
143 144 145
				if (scrollTop !== contentDocument.body.scrollTop) {
					contentDocument.body.scrollTop = scrollTop;
				}
M
Matt Bierner 已提交
146

M
Matt Bierner 已提交
147
				// Bubble out link clicks
148
				contentDocument.body.addEventListener('click', handleInnerClick);
149 150
			}

151
			// Clean up old frames
152
			[].forEach.call(document.body.getElementsByTagName('iframe'), function (frame) {
153 154 155 156 157 158 159 160
				if (frame.id !== '_target') {
					document.body.removeChild(frame);
				}
			});

			const newFrame = document.getElementById('_target');
			if (newFrame.contentDocument === contentDocument) {
				newFrame.style.display = 'block';
M
Matt Bierner 已提交
161
			}
162
		});
M
Matt Bierner 已提交
163

164 165 166 167 168 169
		// 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
		newFrame.contentDocument.write('<!DOCTYPE html>');
		newFrame.contentDocument.write(newDocument.documentElement.innerHTML);
		newFrame.contentDocument.close();

M
Matt Bierner 已提交
170 171 172
		ipcRenderer.sendToHost('did-set-content', stats);
	});

173 174 175
	// Forward message to the embedded iframe
	ipcRenderer.on('message', function (event, data) {
		const target = getTarget();
176
		if (target) {
177
			target.contentWindow.postMessage(data, document.location.origin);
178
		}
179 180
	});

M
Matt Bierner 已提交
181 182
	// forward messages from the embedded iframe
	window.onmessage = function (message) {
183
		ipcRenderer.sendToHost(message.data.command, message.data.data);
M
Matt Bierner 已提交
184 185 186 187
	};

	// signal ready
	ipcRenderer.sendToHost('webview-ready', process.pid);
188
});