4

我正在开发浏览器扩展/附加组件。我们让它在 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

4

2 回答 2

5

具有外部源 ( <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只是几行代码,那么上面的就可以用了。

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

如果您的内容脚本不是由构建脚本生成的,并且您仍希望将脚本分开,那么您还可以使用同步XMLHttpRequest来获取脚本。出于性能原因,不推荐使用同步 XHR,因此使用它需要您自担风险。扩展代码通常与您的扩展捆绑在一起,因此使用 sync 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();
于 2016-07-25T22:41:31.073 回答
0

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

于 2016-07-26T03:41:27.253 回答