从内容脚本使用Chrome扩展程序V3注入JavaScript

7

我正在将我的扩展程序从V2迁移到V3。现在除了一个问题之外,一切都正常。在我的V2版本中,我做了:

const actualCode = '(' + function () { 'console.log("demo");' } + `)();`;
const script = document.createElement('script');
script.textContent = actualCode;
(document.head || document.documentElement).appendChild(script);
script.remove();

请注意,console.log("demo")只是我需要注入的内容的简化形式:)
我需要注入一些JavaScript代码才能启动我的Chrome扩展程序。
但是在V3中,这种方法不再适用。我在开发工具控制台中遇到了以下错误提示。
content.js:23114 
    
   Refused to execute inline script because it violates the following 
   ContentSecurity Policy directive: "script-src 'self'". Either the 
   'unsafe-inline' keyword, a hash ('sha256-tN52+5...6d2I/Szq8='), or a nonce
   ('nonce-...') is required to enable inline execution.

在迁移指南中,我注意到了这个部分。

"content_security_policy": {
   "extension_pages": "...",
   "sandbox": "..."
}

但是那里没有太多的描述,所以对我来说这就像魔法一样。因此,我希望有人能帮助我解决这个问题?


2
请按照method 1 here中所示,使用单独的文件。它以异步方式运行,因此可能比页面上的某些脚本运行得晚。在未来,chrome.scripting.registerContentScripts将允许指定“world”。 - wOxxOm
1
我看到你使用它来覆盖XHR / fetch,所以这里有一个替代方案(以防网站不使用已弃用的同步XHR):通过Object.getOwnPropertyDescriptor + Object.defineProperty覆盖XMLHttpRequest.prototype.response getter(也是responseText)和Response.prototype.text getter(也是json,blob,arrayBuffer,formData)。这些getter在远程服务器响应后使用,因此您的脚本应始终运行得更早。 - wOxxOm
由于chrome.storage始终在本地查询(即使是同步存储),因此它应该比网站的远程请求快得多,所以除非Chrome中存在错误,否则应该没问题。 - wOxxOm
这就是为什么需要 MCVE。根据实际工作流程和实际数据,可能会有解决方案,例如在内容脚本启动时查询整个存储。 - wOxxOm
是的,所有这些都与主题无关。感谢您的帮助! - Jeanluca Scaljeri
显示剩余6条评论
3个回答

11

请参考使用内容脚本访问在页面上定义的变量和函数

由于content scripts在“隔离的世界”环境中执行,因此我们无法在content_script js中执行某些特殊的dom操作。

此示例将向您展示如何在文档开始之前向网页注入inject.js:

// document_start.js
var s = document.createElement('script');
s.src = chrome.runtime.getURL('inject.js');
s.onload = function() {
    this.remove();
};
(document.head || document.documentElement).appendChild(s);

ManifestV3 的 manifest.json 示例

"content_scripts": [
      {
            "matches": ["<all_urls>"],
            "js": ["document_start.js"],
            "run_at": "document_start" //default document end
      }
]
"web_accessible_resources": [{
  "resources": ["inject.js"],
  "matches": ["<all_urls>"]
}]

更新日期:2023年3月8日

支持ES6模块导入

如果您想要使用ES6模块导入,只需添加s.type="module"即可。

function injectScript(src) {
    const s = document.createElement('script');
    s.src = chrome.runtime.getURL(src);
    s.type = "module"
    s.onload = function() {
        this.remove();
    };
    (document.head || document.documentElement).appendChild(s);
}

injectScript('inject/main.js')

然后你可以像这样使用ES6导入:

// inject/main.js
import { initColor } from './init.js'; //import inject/init.js

initColor()

我们需要为这个 appendChild 添加任何 CSP 行/指令吗? - user5858
@user5858 我们应该像我的例子一样将路径“intject.js”放到“web_accessible_resources”中。 - ahuigo
我们不需要 CSP 策略吗?如果我们注入内联 JS 代码呢? - user5858
我已经成功地注入了脚本,我可以看到标签,但是在我的脚本中定义的对象未被定义。有什么想法吗? - user1735894
这个很有用,非常感谢。我正在从v2迁移到v3,原来我们直接使用注入的脚本 script.textContent = content,但在v3中这不再起作用了,改成了 script.src = chrome.runtime.getURL('test.js'); - Yuefei Ma

0
var content = document.createElement("script");

content.src = chrome.extension.getURL("content.js");
content.onload = function() {
    this.parentNode.removeChild(this)
};

(document.head || document.documentElement).appendChild(content);

//Error to manifest version 3

我的电子邮件:baratiali116@gmail.com

大家好,这段代码是我在Chrome清单版本2中的background.js代码。但现在我已经将插件更新到清单版本3,你可以看到的background.js代码会出现一个错误,提示文档尚未创建。请帮助我,谢谢。


这并没有真正回答问题。如果您有不同的问题,可以通过点击提问来提出。如果您想在此问题获得新的答案时得到通知,您可以关注此问题。一旦您拥有足够的声望,您还可以添加悬赏以吸引更多关注。- 来自审核 - Dhaval Purohit
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community

0
有一种更安全的方法来注入JS,而不是使用动态导入脚本。它可能是像下面这样的:
(async () => {
  const src = chrome.extension.getURL('src/js/main.js');
  const contentScript = await import(src);
  contentScript.main();
})();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接