Chrome 扩展设置为 `run_at` `document_start` 是 运行 太快了吗?

Chrome extension set to `run_at` `document_start` is running too fast?

编辑:我的 Chrome 浏览器出了点问题,并与我的脚本发生冲突,完全重新安装消除了问题的来源。如果我碰巧找出是什么原因造成的,我会把它包括在这里。

EDIT2:只是为了让 2017 年阅读这篇文章的人知道我没有忘记这一点,而且自从我上次编辑以来我从未遇到过这个问题。

EDIT3:现在是 2019 年,到目前为止我再也没有遇到过这个问题。


我一直在学习如何创建一个简单的 Chrome 扩展,它是一个用户脚本端口。该脚本与 Tampermonkey 完美配合,设置 run atdocument-start,所有需要从头开始捕获的必要事件都被捕获。

然而,当我在 Chrome 扩展中设置相同的设置时,我发现相同的 运行ning 设置比 Tampermonkey 的设置更快,这导致第一个功能失败:(Uncaught TypeError: Cannot call method 'appendChild' of null.) 因为它试图将脚本元素附加到 head 部分,该部分直到 0.010 秒后才存在。

到目前为止,我的肮脏解决方案是使用 setInterval 函数并将计时器设置为 10 来检查 document.head 是否存在,然后在条件为真时继续执行代码。

有什么方法可以使它正常工作而不必求助于 setInterval 或者复制 Tampermonkey 的 grant none 选项,该选项出现在 运行 网页上下文的用户脚本中?

以下是我的manifest.json文件:

{
    "manifest_version": 2,
    "content_scripts": [ {
        "js":        [ "simpleuserscript.user.js" ],
        "matches":   [ "https://www.google.com/*"],
        "run_at":    "document_start"
    } ],
    "converted_from_user_script": true,
    "description":  "Chrome extension",
    "name":         "Testing",
    "version":      "1"
}

如果 Chrome 采用 afterscriptexecute 事件,所有这一切都可以避免,但在那之前我一直坚持 load 事件。我提前感谢提供的任何帮助。


编辑:我已经尝试过回复中的建议:使用不同的 run at 点,使用 DOMContentLoaded 并附加到 document.documentElement。全部都不成功,因为:1 和 2 使脚本错过了早期事件,以及 3 returns 与尝试附加到 document.head.

时相同的 TypeError

document.readyState = loading 时脚本必须是 inserted/running 否则它会错过早期的必要事件,但不会太早到无法将子项附加到 documentElement 的地步或 head

里面的代码示例simpleuserscript.user.js:

var script = document.createElement("script");
script.textContent = "console.log('success')";
if(document.head) {
    document.head.appendChild(script);
} else if(document.documentElement) {
    document.documentElement.appendChild(script);
}

控制台将显示 TypeError: Cannot call method 'appendChild' of null

您的问题是,当使用 "run_at": "document_start" 时,唯一允许存在于 DOM 中的元素是 <html> 元素。如果你想避免与页面加载有关的错误,比如试图访问一些尚未创建的元素,你必须:

  • "document_idle""document_end" 处制作脚本 运行。虽然 "document_end" 仍然不授予您页面的所有元素和资源已完全加载(但 DOM 已经被解析),但 "document_idle" 关键字会给您确定 DOM 已被解析并且所有元素和资源都已在您的脚本 运行s.

  • 之前正确加载
  • 或者,您可以继续使用 "document_start" 将您的代码包装在 "DOMContentLoaded" 侦听器中,当 DOM已经完全完成加载和解析,类似于"document_idle"。这是一个例子:

    document.addEventListener("DOMContentLoaded", function() {
        // Run your code here...
    });
    

Chrome 扩展 内容脚本 (来自 manifest.json 的 运行)运行 在 document_start , dodocument.readyStateDoc 达到 interactive 之前触发 - 这是您想要开始弄乱大多数页面元素的最早时间。

但是,您可以根据需要立即注入大多数 <script> 节点。只是不要 document.headdocument.body 因为它们还不存在。
改为附加到 documentElement。例如:

var s = document.createElement ("script");
s.src = "http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js";
s.async = false;
document.documentElement.appendChild (s);

var s = document.createElement ("script");
s.src = chrome.extension.getURL ("MyPwnCode.js");
s.async = false;
document.documentElement.appendChild (s);

如果您要添加或修改 other DOM 元素,请在脚本 运行ning at document_start 中,等待 DOMContentLoaded 像这样的事件:

document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);

function fireContentLoadedEvent () {
    console.log ("DOMContentLoaded");
    // PUT YOUR CODE HERE.
    //document.body.textContent = "Changed this!";
}