【问题标题】:How can I make a firefox add-on contentscript inject and run a script before other page scripts?如何让 firefox 附加内容脚本在其他页面脚本之前注入和运行脚本?
【发布时间】:2016-07-25 21:22:59
【问题描述】:

我正在开发浏览器扩展/插件。我们让它在 Chrome 中运行,所以我正在尝试让它在 Firefox 中运行。

我的插件已加载到 Firefox Developer Edition 49.0a2 (2016-07-25)。

我的扩展涉及一个 content_script 设置为run_at: document_start,所以它可以在其他页面脚本运行之前注入一个脚本标签,这样它就可以使一个对象对网站全局可用。

这在 Chrome 中似乎运行良好,但在 Firefox 中已被证明有点竞争条件,大多数时间其他页面资源首先加载。

是否有一种策略来加载内容脚本,使其可以在任何其他页面脚本运行之前注入和加载脚本?

当我添加日志时,我可以很好地隔离正在发生的事情。在此示例内容脚本中:

// inject in-page script
console.log('STEP 1, this always happens first')
var scriptTag = document.createElement('script')
scriptTag.src = chrome.extension.getURL('scripts/inpage.js')
scriptTag.onload = function () { this.parentNode.removeChild(this) }
var container = document.head || document.documentElement
// append as first child
container.insertBefore(scriptTag, container.children[0])

现在如果文件scripts/inpage.js 只是运行一个日志,比如

console.log('STEP 2, this should always run second')

我访问了一个带有这样脚本的页面:

console.log('Step 3, the page itself, should run last')

在实践中,第 2 步和第 3 步以不确定的顺序运行。

非常感谢!

如果你敢自己尝试的话,我在一个特殊分支的公共存储库中有 Firefox 兼容版本的脚本:https://github.com/MetaMask/metamask-plugin/tree/FirefoxCompatibility

【问题讨论】:

标签: firefox-addon content-script firefox-addon-webextensions


【解决方案1】:

具有外部源 (<script src>) 的动态插入脚本不会阻止脚本的执行,因此无法保证您的脚本会加载。如果您的扩展程序可以在 Chrome 中运行,那纯属运气。

如果你真的想在其他脚本之前运行一些脚本,你必须内联运行它:

var actualCode = `
// Content of scripts/inpage.js here
`;

var s = document.createElement('script');
s.textContent = actualCode;
(document.head || document.documentElement).appendChild(s);
s.remove();

理想情况下,您的构建脚本会读取scripts/inpage.js,将其序列化为字符串并将其放入actualCode 变量中。但是如果inpage.js只是几行代码,那么上面的就可以了。

请注意,除非绝对必要,否则您不应在网页中注入代码。原因是页面的执行环境不受信任。如果你在document_start 注入,那么你可以保存函数和(原型)方法以供以后(在闭包中)使用,但需要非常仔细的编码。

如果您的内容脚本不是由构建脚本生成的,并且您仍希望将脚本分开,那么您还可以使用同步 XMLHttpRequest 来获取脚本。出于性能原因,不推荐使用同步 XHR,因此使用它需要您自担风险。扩展代码通常与您的扩展捆绑在一起,因此使用同步 xhr 应该是低风险的:

// Note: do not use synchronous XHR in production!
var x = new XMLHttpRequest();
x.open('GET', chrome.runtime.getURL('scripts/inpage.js'), false);
x.send();
var actualCode = x.responseText;

var s = document.createElement('script');
s.textContent = actualCode;
(document.head || document.documentElement).appendChild(s);
s.remove();

【讨论】:

  • 我将添加一条注释,在 IRC 中您还指出,FireFox 提供了其他方法来干净地完成我的目标,即通过导出助手将 API 从内容脚本公开到主页:@ 987654321@
  • @DanF Component.utils.exportFunction 等尚不可用(它们将Components 命名空间中可用;WebExtensions 不能使用Components)。当bugzil.la/1280482 的补丁登陆时,您可以使用全局exportFunction 方法获得相同的功能。
【解决方案2】:

如果您使用基于 bootstrap.js 的插件,您可以使用框架脚本和 DOMWindowCreated 在 HTML DOM(document.body 的过去基础等)呈现之前使用文档 - https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Frame_script_environment#Events - innerHTML 将可用,但不会执行任何脚本。正如@Rob 提到的那样,您可以将您的内联脚本放在顶部。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-10-07
    • 2018-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-05
    • 2016-01-15
    • 2017-01-13
    相关资源
    最近更新 更多