如何在其他页面脚本之前制作firefox附加内容脚本注入并运行脚本?

时间:2016-07-25 21:22:59

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

我正在使用浏览器扩展程序/插件。我们在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

2 个答案:

答案 0 :(得分:4)

带有外部源(<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,因此使用它需要您自担风险。扩展代码通常与您的扩展程序捆绑在一起,因此使用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();

答案 1 :(得分:0)

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