mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-12 11:39:57 +01:00
Add Enhanced Security Settings to Markdown Preview
Adds enhanced security settings for the markdown preview. The new flow disable all scripts within the preview itself. Users can enable scripts on a per workspace basis. When a markdown document that uses scripts is loaded, a warning is shown inside the document itself. This warning triggers a new security selector quick pick which allows users to enable or disable enahanced security in the workspace.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
(function () {
|
||||
const settings = JSON.parse(document.getElementById('vscode-markdown-preview-data').getAttribute('data-settings'));
|
||||
const strings = JSON.parse(document.getElementById('vscode-markdown-preview-data').getAttribute('data-strings'));
|
||||
|
||||
let didShow = false;
|
||||
|
||||
document.addEventListener('securitypolicyviolation', () => {
|
||||
if (didShow) {
|
||||
return;
|
||||
}
|
||||
didShow = true;
|
||||
const args = [settings.previewUri];
|
||||
|
||||
const notification = document.createElement('a');
|
||||
notification.innerText = strings.cspAlertMessageText;
|
||||
notification.setAttribute('id', 'code-csp-warning');
|
||||
notification.setAttribute('title', strings.cspAlertMessageTitle);
|
||||
|
||||
notification.setAttribute('role', 'button');
|
||||
notification.setAttribute('aria-label', strings.cspAlertMessageLabel);
|
||||
notification.setAttribute('href', `command:markdown.showPreviewSecuritySelector?${encodeURIComponent(JSON.stringify(args))}`);
|
||||
|
||||
document.body.appendChild(notification);
|
||||
});
|
||||
}());
|
||||
@@ -89,7 +89,7 @@
|
||||
function scrollToRevealSourceLine(line) {
|
||||
const {previous, next} = getElementsForSourceLine(line);
|
||||
marker.update(previous && previous.element);
|
||||
if (previous && window.initialData.scrollPreviewWithEditorSelection) {
|
||||
if (previous && settings.scrollPreviewWithEditorSelection) {
|
||||
let scrollTo = 0;
|
||||
if (next) {
|
||||
// Between two elements. Go to percentage offset between them.
|
||||
@@ -141,10 +141,11 @@
|
||||
|
||||
var scrollDisabled = true;
|
||||
var marker = new ActiveLineMarker();
|
||||
const settings = JSON.parse(document.getElementById('vscode-markdown-preview-data').getAttribute('data-settings'));
|
||||
|
||||
window.onload = () => {
|
||||
if (window.initialData.scrollPreviewWithEditorSelection) {
|
||||
const initialLine = +window.initialData.line;
|
||||
if (settings.scrollPreviewWithEditorSelection) {
|
||||
const initialLine = +settings.line;
|
||||
if (!isNaN(initialLine)) {
|
||||
setTimeout(() => {
|
||||
scrollDisabled = true;
|
||||
@@ -172,7 +173,7 @@
|
||||
})(), false);
|
||||
|
||||
document.addEventListener('dblclick', event => {
|
||||
if (!window.initialData.doubleClickToSwitchToEditor) {
|
||||
if (!settings.doubleClickToSwitchToEditor) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -186,7 +187,7 @@
|
||||
const offset = event.pageY;
|
||||
const line = getEditorLineNumberForPageOffset(offset);
|
||||
if (!isNaN(line)) {
|
||||
const args = [window.initialData.source, line];
|
||||
const args = [settings.source, line];
|
||||
window.parent.postMessage({
|
||||
command: "did-click-link",
|
||||
data: `command:_markdown.didClick?${encodeURIComponent(JSON.stringify(args))}`
|
||||
@@ -194,14 +195,14 @@
|
||||
}
|
||||
});
|
||||
|
||||
if (window.initialData.scrollEditorWithPreview) {
|
||||
if (settings.scrollEditorWithPreview) {
|
||||
window.addEventListener('scroll', throttle(() => {
|
||||
if (scrollDisabled) {
|
||||
scrollDisabled = false;
|
||||
} else {
|
||||
const line = getEditorLineNumberForPageOffset(window.scrollY);
|
||||
if (!isNaN(line)) {
|
||||
const args = [window.initialData.source, line];
|
||||
const args = [settings.source, line];
|
||||
window.parent.postMessage({
|
||||
command: "did-click-link",
|
||||
data: `command:_markdown.revealLine?${encodeURIComponent(JSON.stringify(args))}`
|
||||
|
||||
@@ -11,6 +11,28 @@ body {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
#code-csp-warning {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
color: white;
|
||||
margin: 16px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-family: sans-serif;
|
||||
background-color:#444444;
|
||||
cursor: pointer;
|
||||
padding: 6px;
|
||||
box-shadow: 1px 1px 1px rgba(0,0,0,.25);
|
||||
}
|
||||
|
||||
#code-csp-warning:hover {
|
||||
text-decoration: none;
|
||||
background-color:#007acc;
|
||||
box-shadow: 2px 2px 2px rgba(0,0,0,.25);
|
||||
}
|
||||
|
||||
|
||||
body.scrollBeyondLastLine {
|
||||
margin-bottom: calc(100vh - 22px);
|
||||
}
|
||||
|
||||
Generated
+5
@@ -62,6 +62,11 @@
|
||||
"from": "vscode-extension-telemetry@>=0.0.6 <0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.6.tgz"
|
||||
},
|
||||
"vscode-nls": {
|
||||
"version": "2.0.2",
|
||||
"from": "vscode-nls@latest",
|
||||
"resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-2.0.2.tgz"
|
||||
},
|
||||
"winreg": {
|
||||
"version": "1.2.3",
|
||||
"from": "winreg@1.2.3",
|
||||
|
||||
@@ -69,6 +69,11 @@
|
||||
"light": "./media/ViewSource.svg",
|
||||
"dark": "./media/ViewSource_inverse.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "markdown.showPreviewSecuritySelector",
|
||||
"title": "%markdown.showPreviewSecuritySelector.title%",
|
||||
"category": "Markdown"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
@@ -83,6 +88,10 @@
|
||||
"when": "resourceScheme == markdown",
|
||||
"command": "markdown.showSource",
|
||||
"group": "navigation"
|
||||
},
|
||||
{
|
||||
"when": "resourceScheme == markdown",
|
||||
"command": "markdown.showPreviewSecuritySelector"
|
||||
}
|
||||
],
|
||||
"explorer/context": [
|
||||
@@ -183,7 +192,8 @@
|
||||
"highlight.js": "^9.3.0",
|
||||
"markdown-it": "^8.2.2",
|
||||
"markdown-it-named-headers": "0.0.4",
|
||||
"vscode-extension-telemetry": "^0.0.6"
|
||||
"vscode-extension-telemetry": "^0.0.6",
|
||||
"vscode-nls": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^7.0.4"
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
"markdown.previewFrontMatter.dec": "Sets how YAML front matter should be rendered in the markdown preview. 'hide' removes the front matter. Otherwise, the front matter is treated as markdown content.",
|
||||
"markdown.previewSide.title" : "Open Preview to the Side",
|
||||
"markdown.showSource.title" : "Show Source",
|
||||
"markdown.styles.dec": "A list of URLs or local paths to CSS style sheets to use from the markdown preview. Relative paths are interpreted relative to the folder open in the explorer. If there is no open folder, they are interpreted relative to the location of the markdown file. All '\\' need to be written as '\\\\'."
|
||||
"markdown.styles.dec": "A list of URLs or local paths to CSS style sheets to use from the markdown preview. Relative paths are interpreted relative to the folder open in the explorer. If there is no open folder, they are interpreted relative to the location of the markdown file. All '\\' need to be written as '\\\\'.",
|
||||
"markdown.showPreviewSecuritySelector.title": "Change Markdown Preview Security Settings"
|
||||
}
|
||||
@@ -11,9 +11,14 @@ import TelemetryReporter from 'vscode-extension-telemetry';
|
||||
import { MarkdownEngine } from './markdownEngine';
|
||||
import DocumentLinkProvider from './documentLinkProvider';
|
||||
import MDDocumentSymbolProvider from './documentSymbolProvider';
|
||||
import { MDDocumentContentProvider, getMarkdownUri, isMarkdownFile } from './previewContentProvider';
|
||||
import { MDDocumentContentProvider, getMarkdownUri, isMarkdownFile, ContentSecurityPolicyArbiter } from './previewContentProvider';
|
||||
import { TableOfContentsProvider } from './tableOfContentsProvider';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
||||
interface IPackageInfo {
|
||||
name: string;
|
||||
version: string;
|
||||
@@ -25,6 +30,37 @@ interface OpenDocumentLinkArgs {
|
||||
fragment: string;
|
||||
}
|
||||
|
||||
enum PreviewSecuritySelection {
|
||||
None,
|
||||
DisableEnhancedSecurityForWorkspace,
|
||||
EnableEnhancedSecurityForWorkspace
|
||||
}
|
||||
|
||||
interface PreviewSecurityPickItem extends vscode.QuickPickItem {
|
||||
id: PreviewSecuritySelection;
|
||||
}
|
||||
|
||||
class ExtensionContentSecurityProlicyArbiter implements ContentSecurityPolicyArbiter {
|
||||
private readonly key = 'trusted_preview_workspace:';
|
||||
|
||||
constructor(
|
||||
private globalState: vscode.Memento
|
||||
) { }
|
||||
|
||||
public isEnhancedSecurityDisableForWorkspace(): boolean {
|
||||
return this.globalState.get<boolean>(this.key + vscode.workspace.rootPath, false);
|
||||
}
|
||||
|
||||
public addTrustedWorkspace(rootPath: string): Thenable<void> {
|
||||
return this.globalState.update(this.key + rootPath, true);
|
||||
}
|
||||
|
||||
public removeTrustedWorkspace(rootPath: string): Thenable<void> {
|
||||
return this.globalState.update(this.key + rootPath, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var telemetryReporter: TelemetryReporter | null;
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
@@ -34,9 +70,10 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
context.subscriptions.push(telemetryReporter);
|
||||
}
|
||||
|
||||
const cspArbiter = new ExtensionContentSecurityProlicyArbiter(context.globalState);
|
||||
const engine = new MarkdownEngine();
|
||||
|
||||
const contentProvider = new MDDocumentContentProvider(engine, context);
|
||||
const contentProvider = new MDDocumentContentProvider(engine, context, cspArbiter);
|
||||
const contentProviderRegistration = vscode.workspace.registerTextDocumentContentProvider('markdown', contentProvider);
|
||||
|
||||
const symbolsProvider = new MDDocumentSymbolProvider(engine);
|
||||
@@ -64,7 +101,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
});
|
||||
}));
|
||||
|
||||
context.subscriptions.push(vscode.commands.registerCommand('_markdown.didClick', (uri, line) => {
|
||||
context.subscriptions.push(vscode.commands.registerCommand('_markdown.didClick', (uri: string, line) => {
|
||||
const sourceUri = vscode.Uri.parse(decodeURIComponent(uri));
|
||||
return vscode.workspace.openTextDocument(sourceUri)
|
||||
.then(document => vscode.window.showTextDocument(document))
|
||||
@@ -100,6 +137,68 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
}
|
||||
}));
|
||||
|
||||
context.subscriptions.push(vscode.commands.registerCommand('markdown.showPreviewSecuritySelector', (resource: string | undefined) => {
|
||||
const workspacePath = vscode.workspace.rootPath || resource;
|
||||
if (!workspacePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
let sourceUri: vscode.Uri | null = null;
|
||||
if (resource) {
|
||||
sourceUri = vscode.Uri.parse(decodeURIComponent(resource));
|
||||
}
|
||||
|
||||
if (!sourceUri && vscode.window.activeTextEditor) {
|
||||
const activeDocument = vscode.window.activeTextEditor.document;
|
||||
if (activeDocument.uri.scheme === 'markdown') {
|
||||
sourceUri = activeDocument.uri;
|
||||
} else {
|
||||
sourceUri = getMarkdownUri(activeDocument.uri);
|
||||
}
|
||||
}
|
||||
|
||||
vscode.window.showQuickPick<PreviewSecurityPickItem>(
|
||||
[
|
||||
{
|
||||
id: PreviewSecuritySelection.EnableEnhancedSecurityForWorkspace,
|
||||
label: localize(
|
||||
'preview.showPreviewSecuritySelector.disallowScriptsForWorkspaceTitle',
|
||||
'Disable script execution in markdown previews for this workspace'),
|
||||
description: '',
|
||||
detail: cspArbiter.isEnhancedSecurityDisableForWorkspace()
|
||||
? ''
|
||||
: localize('preview.showPreviewSecuritySelector.currentSelection', 'Current setting')
|
||||
}, {
|
||||
id: PreviewSecuritySelection.DisableEnhancedSecurityForWorkspace,
|
||||
label: localize(
|
||||
'preview.showPreviewSecuritySelector.allowScriptsForWorkspaceTitle',
|
||||
'Enable script execution in markdown previews for this workspace'),
|
||||
description: '',
|
||||
detail: cspArbiter.isEnhancedSecurityDisableForWorkspace()
|
||||
? localize('preview.showPreviewSecuritySelector.currentSelection', 'Current setting')
|
||||
: ''
|
||||
},
|
||||
], {
|
||||
placeHolder: localize('preview.showPreviewSecuritySelector.title', 'Change security settings for the Markdown preview'),
|
||||
}).then(selection => {
|
||||
if (!workspacePath) {
|
||||
return false;
|
||||
}
|
||||
switch (selection && selection.id) {
|
||||
case PreviewSecuritySelection.DisableEnhancedSecurityForWorkspace:
|
||||
return cspArbiter.addTrustedWorkspace(workspacePath).then(() => true);
|
||||
|
||||
case PreviewSecuritySelection.EnableEnhancedSecurityForWorkspace:
|
||||
return cspArbiter.removeTrustedWorkspace(workspacePath).then(() => true);
|
||||
}
|
||||
return false;
|
||||
}).then(shouldUpdate => {
|
||||
if (shouldUpdate && sourceUri) {
|
||||
contentProvider.update(sourceUri);
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
context.subscriptions.push(vscode.workspace.onDidSaveTextDocument(document => {
|
||||
if (isMarkdownFile(document)) {
|
||||
const uri = getMarkdownUri(document.uri);
|
||||
|
||||
@@ -9,6 +9,13 @@ import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import { MarkdownEngine } from './markdownEngine';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export interface ContentSecurityPolicyArbiter {
|
||||
isEnhancedSecurityDisableForWorkspace(): boolean;
|
||||
}
|
||||
|
||||
export function isMarkdownFile(document: vscode.TextDocument) {
|
||||
return document.languageId === 'markdown'
|
||||
&& document.uri.scheme !== 'markdown'; // prevent processing of own documents
|
||||
@@ -24,7 +31,8 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
|
||||
|
||||
constructor(
|
||||
private engine: MarkdownEngine,
|
||||
private context: vscode.ExtensionContext
|
||||
private context: vscode.ExtensionContext,
|
||||
private cspArbiter: ContentSecurityPolicyArbiter
|
||||
) { }
|
||||
|
||||
private getMediaPath(mediaFile: string): string {
|
||||
@@ -60,7 +68,7 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
|
||||
return vscode.Uri.file(path.join(path.dirname(resource.fsPath), href)).toString();
|
||||
}
|
||||
|
||||
private computeCustomStyleSheetIncludes(uri: vscode.Uri): string {
|
||||
private computeCustomStyleSheetIncludes(uri: vscode.Uri, _nonce: string): string {
|
||||
const styles = vscode.workspace.getConfiguration('markdown')['styles'];
|
||||
if (styles && Array.isArray(styles) && styles.length > 0) {
|
||||
return styles.map((style) => {
|
||||
@@ -70,13 +78,13 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
|
||||
return '';
|
||||
}
|
||||
|
||||
private getSettingsOverrideStyles(): string {
|
||||
private getSettingsOverrideStyles(nonce: string): string {
|
||||
const previewSettings = vscode.workspace.getConfiguration('markdown')['preview'];
|
||||
if (!previewSettings) {
|
||||
return '';
|
||||
}
|
||||
const { fontFamily, fontSize, lineHeight } = previewSettings;
|
||||
return `<style>
|
||||
return `<style nonce="${nonce}">
|
||||
body {
|
||||
${fontFamily ? `font-family: ${fontFamily};` : ''}
|
||||
${+fontSize > 0 ? `font-size: ${fontSize}px;` : ''}
|
||||
@@ -100,29 +108,45 @@ export class MDDocumentContentProvider implements vscode.TextDocumentContentProv
|
||||
initialLine = editor.selection.active.line;
|
||||
}
|
||||
|
||||
const initialData = {
|
||||
previewUri: encodeURIComponent(uri.toString(true)),
|
||||
source: encodeURIComponent(sourceUri.toString(true)),
|
||||
line: initialLine,
|
||||
scrollPreviewWithEditorSelection: !!markdownConfig.get('preview.scrollPreviewWithEditorSelection', true),
|
||||
scrollEditorWithPreview: !!markdownConfig.get('preview.scrollEditorWithPreview', true),
|
||||
doubleClickToSwitchToEditor: !!markdownConfig.get('preview.doubleClickToSwitchToEditor', true),
|
||||
};
|
||||
|
||||
const previewStrings = {
|
||||
cspAlertMessageText: localize('preview.securityMessage.text', 'Scripts have been disabled in this document'),
|
||||
cspAlertMessageTitle: localize('preview.securityMessage.title', 'Scripts are disabled in the markdown preview. Change the Markdown preview secuirty setting to enable scripts'),
|
||||
cspAlertMessageLabel: localize('preview.securityMessage.label', 'Scripts Disabled Security Warning')
|
||||
};
|
||||
|
||||
// Content Security Policy
|
||||
const nonce = new Date().getTime() + '' + new Date().getMilliseconds();
|
||||
let csp = `<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src 'self' http: https: data:; media-src 'self' http: https: data:; child-src 'none'; script-src 'nonce-${nonce}'; style-src 'nonce-${nonce}' 'self' 'unsafe-inline' http: https: data:;">`;
|
||||
if (this.cspArbiter.isEnhancedSecurityDisableForWorkspace()) {
|
||||
csp = '';
|
||||
}
|
||||
|
||||
const body = this.engine.render(sourceUri, previewFrontMatter === 'hide', document.getText());
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
||||
${csp}
|
||||
<meta id="vscode-markdown-preview-data" data-settings="${JSON.stringify(initialData).replace(/"/g, '"')}" data-strings="${JSON.stringify(previewStrings).replace(/"/g, '"')}">
|
||||
<script src="${this.getMediaPath('csp.js')}" nonce="${nonce}"></script>
|
||||
<link rel="stylesheet" type="text/css" href="${this.getMediaPath('markdown.css')}">
|
||||
<link rel="stylesheet" type="text/css" href="${this.getMediaPath('tomorrow.css')}">
|
||||
${this.getSettingsOverrideStyles()}
|
||||
${this.computeCustomStyleSheetIncludes(uri)}
|
||||
${this.getSettingsOverrideStyles(nonce)}
|
||||
${this.computeCustomStyleSheetIncludes(uri, nonce)}
|
||||
<base href="${document.uri.toString(true)}">
|
||||
<script>
|
||||
window.initialData = {
|
||||
source: "${encodeURIComponent(sourceUri.toString(true))}",
|
||||
line: ${initialLine},
|
||||
scrollPreviewWithEditorSelection: ${!!markdownConfig.get('preview.scrollPreviewWithEditorSelection', true)},
|
||||
scrollEditorWithPreview: ${!!markdownConfig.get('preview.scrollEditorWithPreview', true)},
|
||||
doubleClickToSwitchToEditor: ${!!markdownConfig.get('preview.doubleClickToSwitchToEditor', true)},
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body class="${scrollBeyondLastLine ? 'scrollBeyondLastLine' : ''} ${wordWrap ? 'wordWrap' : ''} ${!!markdownConfig.get('preview.markEditorSelection') ? 'showEditorSelection' : ''}">
|
||||
${body}
|
||||
<script src="${this.getMediaPath('main.js')}"></script>
|
||||
<script src="${this.getMediaPath('main.js')}" nonce="${nonce}"></script>
|
||||
</body>
|
||||
</html>`;
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es5",
|
||||
"es6",
|
||||
"es2015.promise"
|
||||
],
|
||||
"outDir": "./out",
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
|
||||
|
||||
var initData = {};
|
||||
|
||||
function styleBody(body) {
|
||||
@@ -12,13 +15,37 @@ function styleBody(body) {
|
||||
}
|
||||
body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast');
|
||||
body.classList.add(initData.activeTheme);
|
||||
};
|
||||
}
|
||||
|
||||
function getTarget() {
|
||||
return document.getElementById('_target');
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function (event) {
|
||||
ipcRenderer.on('baseUrl', function (event, value) {
|
||||
@@ -86,33 +113,6 @@ document.addEventListener("DOMContentLoaded", function (event) {
|
||||
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 (node.getAttribute("href") === "#") {',
|
||||
' window.scrollTo(0, 0);',
|
||||
' } else if (node.hash && (node.getAttribute("href") === node.hash || (baseElement && node.href.indexOf(baseElement.href) >= 0))) {',
|
||||
' 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 }, "*");',
|
||||
' }',
|
||||
' event.preventDefault();',
|
||||
' break;',
|
||||
' }',
|
||||
' node = node.parentNode;',
|
||||
' }',
|
||||
'});'].join('\n')
|
||||
|
||||
|
||||
newDocument.body.appendChild(defaultScripts);
|
||||
styleBody(newDocument.body);
|
||||
|
||||
const frame = getTarget();
|
||||
@@ -140,6 +140,11 @@ document.addEventListener("DOMContentLoaded", function (event) {
|
||||
newFrame.contentDocument.body.scrollTop = scrollTop;
|
||||
}
|
||||
|
||||
// bubble out link-clicks
|
||||
if (newFrame.contentDocument.body) {
|
||||
newFrame.contentDocument.body.addEventListener("click", handleInnerClick);
|
||||
}
|
||||
|
||||
if (frame) {
|
||||
document.body.removeChild(frame);
|
||||
}
|
||||
@@ -152,6 +157,7 @@ document.addEventListener("DOMContentLoaded", function (event) {
|
||||
newFrame.contentDocument.write(newDocument.documentElement.innerHTML);
|
||||
newFrame.contentDocument.close();
|
||||
|
||||
|
||||
ipcRenderer.sendToHost('did-set-content', stats);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user