From 87b94727b4e9f66db7c6515b6569fc89d00ffa71 Mon Sep 17 00:00:00 2001 From: "Elian H. Thiele-Evans" <60372411+ElianHugh@users.noreply.github.com> Date: Thu, 29 Jul 2021 22:14:16 +1000 Subject: [PATCH 01/12] Implement mutation observer Observe changes in the DOM, in order to ensure urls are appropriate for webview restrictions --- html/webview/observer.js | 22 ++++++++++++++++++++ src/session.ts | 26 ++++++----------------- src/webview.ts | 45 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 20 deletions(-) create mode 100644 html/webview/observer.js create mode 100644 src/webview.ts diff --git a/html/webview/observer.js b/html/webview/observer.js new file mode 100644 index 000000000..303f0bf98 --- /dev/null +++ b/html/webview/observer.js @@ -0,0 +1,22 @@ +const reg = /vscode-webview:\/\//g; + +/* eslint-disable */ +const MutationObserver = window.MutationObserver || window.WebKitMutationObserver; +const observer = new MutationObserver(function(mutations, observer) { + for (const mut in mutations) { + const targ = mutations[mut].target; + if (targ.src) { + if (reg.test(targ.src)) { + targ.src = targ.src.replace(reg, 'https://') + } + } + } + }); + +// define what element should be observed by the observer +// and what types of mutations trigger the callback +observer.observe(document, { + subtree: true, + attributes: true, + attributeFilter: ["src", "href", "style", "class"] +}); \ No newline at end of file diff --git a/src/session.ts b/src/session.ts index c266e5d9a..fc9d79ace 100644 --- a/src/session.ts +++ b/src/session.ts @@ -13,8 +13,9 @@ import { runTextInTerm } from './rTerminal'; import { FSWatcher } from 'fs-extra'; import { config, readContent } from './util'; import { purgeAddinPickerItems, dispatchRStudioAPICall } from './rstudioapi'; +import { newWebview } from './webview'; -import { homeExtDir, rWorkspace, globalRHelp, globalHttpgdManager } from './extension'; +import { homeExtDir, rWorkspace, globalRHelp, globalHttpgdManager, extensionContext } from './extension'; import { UUID, rHostService, rGuestService, isLiveShare, isHost, isGuestSession, closeBrowser, guestResDir, shareBrowser, openVirtualDoc, shareWorkspace } from './liveshare'; export let globalenv: any; @@ -269,27 +270,11 @@ export function openExternalBrowser(): void { export async function showWebView(file: string, title: string, viewer: string | boolean): Promise { console.info(`[showWebView] file: ${file}, viewer: ${viewer.toString()}`); + console.log(file); if (viewer === false) { void env.openExternal(Uri.parse(file)); } else { - const dir = path.dirname(file); - const panel = window.createWebviewPanel('webview', title, - { - preserveFocus: true, - viewColumn: ViewColumn[String(viewer)], - }, - { - enableScripts: true, - enableFindWidget: true, - retainContextWhenHidden: true, - localResourceRoots: [Uri.file(dir)], - }); - const content = await readContent(file, 'utf8'); - const html = content.toString() - .replace('', '') - .replace(/<(\w+)\s+(href|src)="(?!\w+:)/g, - `<$1 $2="${String(panel.webview.asWebviewUri(Uri.file(dir)))}/`); - panel.webview.html = html; + await newWebview(file, title, viewer); } console.info('[showWebView] Done'); } @@ -645,7 +630,7 @@ async function updateRequest(sessionStatusBarItem: StatusBarItem) { break; } case 'httpgd': { - if(request.url){ + if (request.url) { globalHttpgdManager?.showViewer(request.url); } break; @@ -667,6 +652,7 @@ async function updateRequest(sessionStatusBarItem: StatusBarItem) { break; } case 'webview': { + console.log(request); void showWebView(request.file, request.title, request.viewer); break; } diff --git a/src/webview.ts b/src/webview.ts new file mode 100644 index 000000000..0bd07feea --- /dev/null +++ b/src/webview.ts @@ -0,0 +1,45 @@ +import path = require('path'); +import * as vscode from 'vscode'; +import { extensionContext } from './extension'; +import { readContent } from './util'; + +export async function newWebview(file: string, title: string, viewer: string | boolean): Promise { + const dir = path.dirname(file); + const panel = vscode.window.createWebviewPanel('webview', title, + { + preserveFocus: true, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + viewColumn: vscode.ViewColumn[String(viewer)], + }, + { + enableScripts: true, + enableFindWidget: true, + retainContextWhenHidden: true, + localResourceRoots: [vscode.Uri.file(dir), extensionContext.extensionUri], + }); + + const observerPath = vscode.Uri.file(extensionContext.asAbsolutePath('html/webview/observer.js')); + const body = (await readContent(file, 'utf8')).toString() + .replace('', '') + .replace(/<(\w+)\s+(href|src)="(?!\w+:)/g, + `<$1 $2="${String(panel.webview.asWebviewUri(vscode.Uri.file(dir)))}/`); + + const htmlOut = ` + + + + + + ${title} + + +
+                ${body}
+            
+ + + `; + + panel.webview.html = htmlOut; +} + From 2a824d71f8decd4241b822c40f7a74c639d87f0a Mon Sep 17 00:00:00 2001 From: ElianHugh Date: Fri, 30 Jul 2021 02:08:29 +1000 Subject: [PATCH 02/12] pre -> span, clean up observer method --- html/webview/observer.js | 21 +++++++++------------ src/webview.ts | 4 ++-- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/html/webview/observer.js b/html/webview/observer.js index 303f0bf98..343f0dc2c 100644 --- a/html/webview/observer.js +++ b/html/webview/observer.js @@ -1,20 +1,17 @@ const reg = /vscode-webview:\/\//g; - -/* eslint-disable */ const MutationObserver = window.MutationObserver || window.WebKitMutationObserver; -const observer = new MutationObserver(function(mutations, observer) { + +const observer = new MutationObserver(function (mutations) { for (const mut in mutations) { const targ = mutations[mut].target; - if (targ.src) { - if (reg.test(targ.src)) { - targ.src = targ.src.replace(reg, 'https://') - } - } - } - }); + if (reg.test(targ.src)) { + const newSrc = targ.src.replace(reg, 'https://'); + console.log(`[VSC-R] ${targ.src} changed to ${newSrc}`); + targ.src = newSrc; + } + } +}); -// define what element should be observed by the observer -// and what types of mutations trigger the callback observer.observe(document, { subtree: true, attributes: true, diff --git a/src/webview.ts b/src/webview.ts index 0bd07feea..5e10faf81 100644 --- a/src/webview.ts +++ b/src/webview.ts @@ -33,9 +33,9 @@ export async function newWebview(file: string, title: string, viewer: string | b ${title} -
+            
                 ${body}
-            
+ `; From aa1ef2296603bab233bdd7a13b331637d363d31a Mon Sep 17 00:00:00 2001 From: ElianHugh Date: Fri, 30 Jul 2021 13:14:22 +1000 Subject: [PATCH 03/12] Refactor webview --- html/webview/observer.js | 5 +++-- src/session.ts | 39 +++++++++++++++++++++++++++++++++- src/webview.ts | 45 ---------------------------------------- 3 files changed, 41 insertions(+), 48 deletions(-) delete mode 100644 src/webview.ts diff --git a/html/webview/observer.js b/html/webview/observer.js index 343f0dc2c..836c6cafb 100644 --- a/html/webview/observer.js +++ b/html/webview/observer.js @@ -2,8 +2,9 @@ const reg = /vscode-webview:\/\//g; const MutationObserver = window.MutationObserver || window.WebKitMutationObserver; const observer = new MutationObserver(function (mutations) { - for (const mut in mutations) { - const targ = mutations[mut].target; + const len = mutations.length; + for (let i = 0; i < len; i++) { + const targ = mutations[i].target; if (reg.test(targ.src)) { const newSrc = targ.src.replace(reg, 'https://'); console.log(`[VSC-R] ${targ.src} changed to ${newSrc}`); diff --git a/src/session.ts b/src/session.ts index fc9d79ace..1cc391644 100644 --- a/src/session.ts +++ b/src/session.ts @@ -274,7 +274,44 @@ export async function showWebView(file: string, title: string, viewer: string | if (viewer === false) { void env.openExternal(Uri.parse(file)); } else { - await newWebview(file, title, viewer); + const dir = path.dirname(file); + const webviewDir = extensionContext.asAbsolutePath('html/webview/'); + const panel = window.createWebviewPanel('webview', title, + { + preserveFocus: true, + viewColumn: ViewColumn[String(viewer)], + }, + { + enableScripts: true, + enableFindWidget: true, + retainContextWhenHidden: true, + localResourceRoots: [Uri.file(dir), Uri.file(webviewDir)], + }); + + const observerPath = Uri.file(path.join(webviewDir, 'observer.js')); + const body = (await readContent(file, 'utf8')).toString() + .replace('', '') + .replace(/<(\w+)\s+(href|src)="(?!\w+:)/g, + `<$1 $2="${String(panel.webview.asWebviewUri(Uri.file(dir)))}/`); + + const htmlOut = + ` + + + + + + ${title} + + + + ${body} + + + + `; + + panel.webview.html = htmlOut; } console.info('[showWebView] Done'); } diff --git a/src/webview.ts b/src/webview.ts deleted file mode 100644 index 5e10faf81..000000000 --- a/src/webview.ts +++ /dev/null @@ -1,45 +0,0 @@ -import path = require('path'); -import * as vscode from 'vscode'; -import { extensionContext } from './extension'; -import { readContent } from './util'; - -export async function newWebview(file: string, title: string, viewer: string | boolean): Promise { - const dir = path.dirname(file); - const panel = vscode.window.createWebviewPanel('webview', title, - { - preserveFocus: true, - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - viewColumn: vscode.ViewColumn[String(viewer)], - }, - { - enableScripts: true, - enableFindWidget: true, - retainContextWhenHidden: true, - localResourceRoots: [vscode.Uri.file(dir), extensionContext.extensionUri], - }); - - const observerPath = vscode.Uri.file(extensionContext.asAbsolutePath('html/webview/observer.js')); - const body = (await readContent(file, 'utf8')).toString() - .replace('', '') - .replace(/<(\w+)\s+(href|src)="(?!\w+:)/g, - `<$1 $2="${String(panel.webview.asWebviewUri(vscode.Uri.file(dir)))}/`); - - const htmlOut = ` - - - - - - ${title} - - - - ${body} - - - - `; - - panel.webview.html = htmlOut; -} - From 43948f4b97495045b844bd14dfa474b54e505d07 Mon Sep 17 00:00:00 2001 From: "Elian H. Thiele-Evans" <60372411+ElianHugh@users.noreply.github.com> Date: Fri, 30 Jul 2021 18:28:42 +1000 Subject: [PATCH 04/12] Refactor html into func, observe content only --- html/webview/observer.js | 6 ++--- src/session.ts | 51 ++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/html/webview/observer.js b/html/webview/observer.js index 836c6cafb..edcd59839 100644 --- a/html/webview/observer.js +++ b/html/webview/observer.js @@ -13,8 +13,8 @@ const observer = new MutationObserver(function (mutations) { } }); -observer.observe(document, { +observer.observe(document.getElementById("webview-content"), { subtree: true, attributes: true, - attributeFilter: ["src", "href", "style", "class"] -}); \ No newline at end of file + attributeFilter: ["src", "href", "style", "class"], +}); diff --git a/src/session.ts b/src/session.ts index 1cc391644..3cc82bd95 100644 --- a/src/session.ts +++ b/src/session.ts @@ -13,7 +13,6 @@ import { runTextInTerm } from './rTerminal'; import { FSWatcher } from 'fs-extra'; import { config, readContent } from './util'; import { purgeAddinPickerItems, dispatchRStudioAPICall } from './rstudioapi'; -import { newWebview } from './webview'; import { homeExtDir, rWorkspace, globalRHelp, globalHttpgdManager, extensionContext } from './extension'; import { UUID, rHostService, rGuestService, isLiveShare, isHost, isGuestSession, closeBrowser, guestResDir, shareBrowser, openVirtualDoc, shareWorkspace } from './liveshare'; @@ -288,30 +287,7 @@ export async function showWebView(file: string, title: string, viewer: string | localResourceRoots: [Uri.file(dir), Uri.file(webviewDir)], }); - const observerPath = Uri.file(path.join(webviewDir, 'observer.js')); - const body = (await readContent(file, 'utf8')).toString() - .replace('', '') - .replace(/<(\w+)\s+(href|src)="(?!\w+:)/g, - `<$1 $2="${String(panel.webview.asWebviewUri(Uri.file(dir)))}/`); - - const htmlOut = - ` - - - - - - ${title} - - - - ${body} - - - - `; - - panel.webview.html = htmlOut; + panel.webview.html = await getWebviewHtml(panel.webview, file, title, dir, webviewDir); } console.info('[showWebView] Done'); } @@ -606,6 +582,31 @@ export async function getListHtml(webview: Webview, file: string): Promise { + const observerPath = Uri.file(path.join(webviewDir, 'observer.js')); + const body = (await readContent(file, 'utf8')).toString() + .replace('', '') + .replace(/<(\w+)\s+(href|src)="(?!\w+:)/g, + `<$1 $2="${String(webview.asWebviewUri(Uri.file(dir)))}/`); + + return ` + + + + + + + ${title} + + + + ${body} + + + + `; +} + function isFromWorkspace(dir: string) { if (workspace.workspaceFolders === undefined) { const rel = path.relative(os.homedir(), dir); From 91083e7fe5ad594c27f139f259795923f458710e Mon Sep 17 00:00:00 2001 From: "Elian H. Thiele-Evans" <60372411+ElianHugh@users.noreply.github.com> Date: Sun, 1 Aug 2021 18:11:08 +1000 Subject: [PATCH 05/12] Improve docs, change URL testing --- html/webview/observer.js | 17 +++++++++++++---- src/session.ts | 15 ++++++++++++--- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/html/webview/observer.js b/html/webview/observer.js index edcd59839..4ce3173f0 100644 --- a/html/webview/observer.js +++ b/html/webview/observer.js @@ -1,14 +1,22 @@ -const reg = /vscode-webview:\/\//g; const MutationObserver = window.MutationObserver || window.WebKitMutationObserver; +const reg = /vscode-webview:\/\//g; +const dotReg = /.*\.[A-Za-z/0-9]*?\/[A-Za-z/0-9]+/; -const observer = new MutationObserver(function (mutations) { +const observer = new MutationObserver(mutations => { const len = mutations.length; for (let i = 0; i < len; i++) { const targ = mutations[i].target; if (reg.test(targ.src)) { - const newSrc = targ.src.replace(reg, 'https://'); - console.log(`[VSC-R] ${targ.src} changed to ${newSrc}`); + const newSrc = targ.src.replace(reg, 'https://'); + if (dotReg.test(newSrc)) { + console.log( + `%c[VSC-R] %cThe file request '${targ.src}' was converted to the URL '${newSrc}'. Reason: the request appears to refer to a URL, not a local file as suggested by the file scheme. %cIf you believe this to be in error, please log an issue on GitHub.`, + "color: orange", + "color: inherit", + "font-style: italic" + ); targ.src = newSrc; + } } } }); @@ -17,4 +25,5 @@ observer.observe(document.getElementById("webview-content"), { subtree: true, attributes: true, attributeFilter: ["src", "href", "style", "class"], + characterData: false }); diff --git a/src/session.ts b/src/session.ts index 3cc82bd95..255a79a9e 100644 --- a/src/session.ts +++ b/src/session.ts @@ -589,21 +589,31 @@ export async function getWebviewHtml(webview: Webview, file: string, title: stri .replace(/<(\w+)\s+(href|src)="(?!\w+:)/g, `<$1 $2="${String(webview.asWebviewUri(Uri.file(dir)))}/`); + // define the content security policy for the webview + // * whilst it is recommended to be strict as possible, + // * there are several packages that require unsafe requests + const CSP = ` + upgrade-insecure-requests; + default-src https: data: filesystem:; + style-src https: data: filesystem: 'unsafe-inline'; + script-src https: data: filesystem: 'unsafe-eval'; + `; + return ` - + ${title} ${body} - + `; } @@ -690,7 +700,6 @@ async function updateRequest(sessionStatusBarItem: StatusBarItem) { break; } case 'webview': { - console.log(request); void showWebView(request.file, request.title, request.viewer); break; } From 72d7be800fdc047ddcc7e27fa3dd3f4a99adf671 Mon Sep 17 00:00:00 2001 From: Kun Ren Date: Sun, 1 Aug 2021 22:01:58 +0800 Subject: [PATCH 06/12] Minor update --- src/session.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/session.ts b/src/session.ts index 255a79a9e..89bdec18c 100644 --- a/src/session.ts +++ b/src/session.ts @@ -585,7 +585,6 @@ export async function getListHtml(webview: Webview, file: string): Promise { const observerPath = Uri.file(path.join(webviewDir, 'observer.js')); const body = (await readContent(file, 'utf8')).toString() - .replace('', '') .replace(/<(\w+)\s+(href|src)="(?!\w+:)/g, `<$1 $2="${String(webview.asWebviewUri(Uri.file(dir)))}/`); @@ -607,13 +606,18 @@ export async function getWebviewHtml(webview: Webview, file: string, title: stri ${title} + - + ${body} - + `; } From 2131fd10579e5390dd96c68d1723f63f1bef6875 Mon Sep 17 00:00:00 2001 From: "Elian H. Thiele-Evans" <60372411+ElianHugh@users.noreply.github.com> Date: Mon, 2 Aug 2021 02:22:07 +1000 Subject: [PATCH 07/12] Simplify regex usage - Remove superfluous console.log - Simplify regex usage --- html/webview/observer.js | 24 +++++++++++------------- src/session.ts | 1 - 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/html/webview/observer.js b/html/webview/observer.js index 4ce3173f0..465274195 100644 --- a/html/webview/observer.js +++ b/html/webview/observer.js @@ -1,22 +1,20 @@ const MutationObserver = window.MutationObserver || window.WebKitMutationObserver; -const reg = /vscode-webview:\/\//g; -const dotReg = /.*\.[A-Za-z/0-9]*?\/[A-Za-z/0-9]+/; +const replaceReg = /vscode-webview:\/\//g; +const testReg = /vscode-webview:\/\/.*\.[A-Za-z/0-9]*?\/[A-Za-z/0-9]+/; const observer = new MutationObserver(mutations => { const len = mutations.length; for (let i = 0; i < len; i++) { const targ = mutations[i].target; - if (reg.test(targ.src)) { - const newSrc = targ.src.replace(reg, 'https://'); - if (dotReg.test(newSrc)) { - console.log( - `%c[VSC-R] %cThe file request '${targ.src}' was converted to the URL '${newSrc}'. Reason: the request appears to refer to a URL, not a local file as suggested by the file scheme. %cIf you believe this to be in error, please log an issue on GitHub.`, - "color: orange", - "color: inherit", - "font-style: italic" - ); - targ.src = newSrc; - } + if (testReg.test(targ.src)) { + const newSrc = targ.src.replace(replaceReg, 'https://'); + console.log( + `%c[VSC-R] %cThe file request '${targ.src}' was converted to the URL '${newSrc}'. Reason: the request appears to refer to a URL, not a local file as suggested by the file scheme. %cIf you believe this to be in error, please log an issue on GitHub.`, + "color: orange", + "color: inherit", + "font-style: italic" + ); + targ.src = newSrc; } } }); diff --git a/src/session.ts b/src/session.ts index 89bdec18c..2eaf7fd41 100644 --- a/src/session.ts +++ b/src/session.ts @@ -269,7 +269,6 @@ export function openExternalBrowser(): void { export async function showWebView(file: string, title: string, viewer: string | boolean): Promise { console.info(`[showWebView] file: ${file}, viewer: ${viewer.toString()}`); - console.log(file); if (viewer === false) { void env.openExternal(Uri.parse(file)); } else { From 87ec772fcf2912b913bbe747380b5b78893ab7c2 Mon Sep 17 00:00:00 2001 From: "Elian H. Thiele-Evans" <60372411+ElianHugh@users.noreply.github.com> Date: Mon, 2 Aug 2021 13:51:53 +1000 Subject: [PATCH 08/12] (Misc) change directory - Change webview/observer.js to session/webview/observer.js --- html/{ => session}/webview/observer.js | 0 src/session.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename html/{ => session}/webview/observer.js (100%) diff --git a/html/webview/observer.js b/html/session/webview/observer.js similarity index 100% rename from html/webview/observer.js rename to html/session/webview/observer.js diff --git a/src/session.ts b/src/session.ts index 2eaf7fd41..1081d49d5 100644 --- a/src/session.ts +++ b/src/session.ts @@ -273,7 +273,7 @@ export async function showWebView(file: string, title: string, viewer: string | void env.openExternal(Uri.parse(file)); } else { const dir = path.dirname(file); - const webviewDir = extensionContext.asAbsolutePath('html/webview/'); + const webviewDir = extensionContext.asAbsolutePath('html/session/webview/'); const panel = window.createWebviewPanel('webview', title, { preserveFocus: true, From 4dce16ecaa9fef1f6a92109734088fe4cfce737b Mon Sep 17 00:00:00 2001 From: Kun Ren Date: Mon, 2 Aug 2021 13:26:47 +0800 Subject: [PATCH 09/12] Fix merge conflict --- src/session.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/session.ts b/src/session.ts index 089ccfeb8..ba425259b 100644 --- a/src/session.ts +++ b/src/session.ts @@ -14,7 +14,7 @@ import { FSWatcher } from 'fs-extra'; import { config, readContent } from './util'; import { purgeAddinPickerItems, dispatchRStudioAPICall } from './rstudioapi'; -import { homeExtDir, rWorkspace, globalRHelp, globalHttpgdManager } from './extension'; +import { homeExtDir, rWorkspace, globalRHelp, globalHttpgdManager, extensionContext } from './extension'; import { UUID, rHostService, rGuestService, isLiveShare, isHost, isGuestSession, closeBrowser, guestResDir, shareBrowser, openVirtualDoc, shareWorkspace } from './liveShare'; export let globalenv: any; From 8ab7998d1aacbcd613a3b9c13d6458a5b60df693 Mon Sep 17 00:00:00 2001 From: "Elian H. Thiele-Evans" <60372411+ElianHugh@users.noreply.github.com> Date: Tue, 3 Aug 2021 01:55:40 +1000 Subject: [PATCH 10/12] Broaden regex, bug fix - Fix slowdown for DOMs that have many updates a second - Fix more resource request paths --- html/session/webview/observer.js | 6 +++--- src/session.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/html/session/webview/observer.js b/html/session/webview/observer.js index 465274195..d1c9d1121 100644 --- a/html/session/webview/observer.js +++ b/html/session/webview/observer.js @@ -1,6 +1,6 @@ const MutationObserver = window.MutationObserver || window.WebKitMutationObserver; -const replaceReg = /vscode-webview:\/\//g; -const testReg = /vscode-webview:\/\/.*\.[A-Za-z/0-9]*?\/[A-Za-z/0-9]+/; +const replaceReg = /vscode-webview:\/\//; +const testReg = /vscode-webview:\/\/.*\.[A-Za-z/0-9_-]*?\/.+/; const observer = new MutationObserver(mutations => { const len = mutations.length; @@ -22,6 +22,6 @@ const observer = new MutationObserver(mutations => { observer.observe(document.getElementById("webview-content"), { subtree: true, attributes: true, - attributeFilter: ["src", "href", "style", "class"], + attributeFilter: ["src", "href", "style"], characterData: false }); diff --git a/src/session.ts b/src/session.ts index ba425259b..a702bb90d 100644 --- a/src/session.ts +++ b/src/session.ts @@ -607,8 +607,8 @@ export async function getListHtml(webview: Webview, file: string): Promise { const observerPath = Uri.file(path.join(webviewDir, 'observer.js')); const body = (await readContent(file, 'utf8')).toString() - .replace(/<(\w+)\s+(href|src)="(?!\w+:)/g, - `<$1 $2="${String(webview.asWebviewUri(Uri.file(dir)))}/`); + .replace(/<(\w+)(.*)\s+(href|src)="(?!\w+:)/g, + `<$1 $2 $3="${String(webview.asWebviewUri(Uri.file(dir)))}/`); // define the content security policy for the webview // * whilst it is recommended to be strict as possible, From 3b4717c90b87b43aa9442fa4e14cef784f8af768 Mon Sep 17 00:00:00 2001 From: "Elian H. Thiele-Evans" <60372411+ElianHugh@users.noreply.github.com> Date: Tue, 3 Aug 2021 13:14:12 +1000 Subject: [PATCH 11/12] Only test specific tags - No need to test tags that do not typically have src values. This can be expanded if a tag is missing from the list --- html/session/webview/observer.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/html/session/webview/observer.js b/html/session/webview/observer.js index d1c9d1121..3a7c93f65 100644 --- a/html/session/webview/observer.js +++ b/html/session/webview/observer.js @@ -1,12 +1,18 @@ const MutationObserver = window.MutationObserver || window.WebKitMutationObserver; const replaceReg = /vscode-webview:\/\//; const testReg = /vscode-webview:\/\/.*\.[A-Za-z/0-9_-]*?\/.+/; +const watchedTags = [ + "IMG", + "A", + "LINK", + "SCRIPT" +] const observer = new MutationObserver(mutations => { const len = mutations.length; for (let i = 0; i < len; i++) { const targ = mutations[i].target; - if (testReg.test(targ.src)) { + if (watchedTags.includes(targ.tagName) && testReg.test(targ.src)) { const newSrc = targ.src.replace(replaceReg, 'https://'); console.log( `%c[VSC-R] %cThe file request '${targ.src}' was converted to the URL '${newSrc}'. Reason: the request appears to refer to a URL, not a local file as suggested by the file scheme. %cIf you believe this to be in error, please log an issue on GitHub.`, From 47c40dbb0176c6374ea78630c57a93f65ba39d27 Mon Sep 17 00:00:00 2001 From: "Elian H. Thiele-Evans" <60372411+ElianHugh@users.noreply.github.com> Date: Tue, 3 Aug 2021 14:06:18 +1000 Subject: [PATCH 12/12] Debounce mut. observer --- html/session/webview/observer.js | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/html/session/webview/observer.js b/html/session/webview/observer.js index 3a7c93f65..1f48a8357 100644 --- a/html/session/webview/observer.js +++ b/html/session/webview/observer.js @@ -6,12 +6,26 @@ const watchedTags = [ "A", "LINK", "SCRIPT" -] +]; +const mutationQueue = []; const observer = new MutationObserver(mutations => { - const len = mutations.length; - for (let i = 0; i < len; i++) { - const targ = mutations[i].target; + if (!mutationQueue.length) { + requestAnimationFrame(setSrc); + } + mutationQueue.push(mutations); +}); + +observer.observe(document.getElementById("webview-content"), { + subtree: true, + attributes: true, + attributeFilter: ["src", "href", "style", "class"], + characterData: false +}); + +function setSrc() { + for (const mutations of mutationQueue) { + const targ = mutations[0].target; if (watchedTags.includes(targ.tagName) && testReg.test(targ.src)) { const newSrc = targ.src.replace(replaceReg, 'https://'); console.log( @@ -23,11 +37,5 @@ const observer = new MutationObserver(mutations => { targ.src = newSrc; } } -}); - -observer.observe(document.getElementById("webview-content"), { - subtree: true, - attributes: true, - attributeFilter: ["src", "href", "style"], - characterData: false -}); + mutationQueue.length = 0; +}