From 10524a0c1f0fc732e1f9df3497b026e17edf3728 Mon Sep 17 00:00:00 2001 From: Phil Crosby Date: Wed, 11 Oct 2023 14:01:55 -0700 Subject: [PATCH] Use chrome.scripting.executeScript to open bookmarklets in the Vomnibar This fixes #4329. Note that bookmarklets will still raise an error if the site has a restrictive CSP. For the full solution, we need a new API from Chrome. This is tracked in #4331. --- background_scripts/tab_operations.js | 32 ++++++++++++++++++++++++++-- content_scripts/vimium_frontend.js | 3 --- lib/dom_utils.js | 16 -------------- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/background_scripts/tab_operations.js b/background_scripts/tab_operations.js index 6b5852f7a..6b616a476 100644 --- a/background_scripts/tab_operations.js +++ b/background_scripts/tab_operations.js @@ -4,11 +4,39 @@ // TODO(philc): Convert these to Promise-based APIs. // Opens the url in the current tab. +// If the URL is a JavaScript snippet, execute that snippet in the current tab. function openUrlInCurrentTab(request) { + // Note that when injecting JavaScript, it's subject to the site's CSP. Sites with strict CSPs + // (like github.com, developer.mozilla.org) will raise an error when we try to run this code. See + // https://github.com/philc/vimium/issues/4331. if (Utils.hasJavascriptPrefix(request.url)) { const tabId = request.tabId; - const frameId = request.frameId; - chrome.tabs.sendMessage(tabId, { frameId, handler: "executeUserScript", script: request.url }); + const scriptingArgs = { + target: { tabId: request.tabId }, + func: (text) => { + const prefix = "javascript:"; + text = text.slice(prefix.length).trim(); + text = decodeURIComponent(text); + try { + text = decodeURIComponent(text); + } catch { + // Swallow + } + const el = document.createElement("script"); + el.textContent = text; + document.head.appendChild(el); + }, + args: [request.url], + }; + + if (!BgUtils.isFirefox()) { + // The MAIN world -- where the webpage runs -- is less privileged than the ISOLATED world. + // Specifying a world is required for Chrome, but not Firefox. + // As of Firefox 118, specifying "MAIN" as the world is not yet supported. + args.world = "MAIN"; + } + + chrome.scripting.executeScript(scriptingArgs); } else { chrome.tabs.update(request.tabId, { url: Utils.convertToUrl(request.url) }); } diff --git a/content_scripts/vimium_frontend.js b/content_scripts/vimium_frontend.js index e367937f6..bcba35576 100644 --- a/content_scripts/vimium_frontend.js +++ b/content_scripts/vimium_frontend.js @@ -233,9 +233,6 @@ const initializePreDomReady = async function () { showMessage(request) { HUD.show(request.message, 2000); }, - executeUserScript(request) { - DomUtils.injectUserScript(request.script); - }, }; Utils.addChromeRuntimeOnMessageListener( diff --git a/lib/dom_utils.js b/lib/dom_utils.js index 464617cb1..9dfc136be 100644 --- a/lib/dom_utils.js +++ b/lib/dom_utils.js @@ -637,22 +637,6 @@ const DomUtils = { style.textContent = Settings.get("userDefinedLinkHintCss"); document.head.appendChild(style); }, - - // Inject user Javascript. - injectUserScript(text) { - if (text.slice(0, 11) === "javascript:") { - text = text.slice(11).trim(); - try { - text = decodeURIComponent(text); - } catch { - // Swallow - } - } - const script = document.createElement("script"); - script.textContent = text; - // TODO(philc): Can we remove this return? - return document.head.appendChild(script); - }, }; window.DomUtils = DomUtils;