See my followup question for continued discussion.
我正在撰写Chrome扩展程序,需要将代码添加到当前浏览器页面。它是通过扩展沙箱中运行的JavaScript代码执行此操作的,如下所示:
$initializationTemplate.appendTo(document.body);
$ initializationTemplate变量包含以下HTML:
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
<script>
console.log("prepare-jQuery is initializing jQuery");
console.log("jQuery's type is " + typeof jQuery);
window.MyChromeExtension_jQuery = jQuery.noConflict(true);
console.log("prepare-jQuery was successful...");
</script>
这不起作用,因为当我的脚本运行时,jQuery还没有完全加载。我最终得到了两种情况之一:
1)如果该页面有一个以前存在的jQuery include,我会为自己窃取页面的jQuery并且页面的JavaScript中断,或者
2)如果页面没有jQuery,我的代码会因为jQuery尚未定义而中断。
如果我在没有jQuery的页面上添加以下内容,那么我可以自己劫持jQuery(因为我知道页面不使用jQuery),一切都很好。
<script>
setTimeout(function()
{
console.log("prepare-jQuery is initializing jQuery");
console.log("jQuery's type is " + typeof jQuery);
window.MyChromeExtension_jQuery = jQuery.noConflict(true);
console.log("prepare-jQuery was successful...");
}, 200);
</script>
由于我从CDN下载了整个jQuery,因此实际上非常受欢迎或错过,所以如果下载需要一秒钟,我的脚本仍会运行并且找不到可用的jQuery。如果我将超时设置为2-3秒(至少在我相对较快的连接上),我会一直抓住它,但这确实不是一种可以接受的方式。
我认为这些不同的脚本标记是异步处理的,因为Chrome正在处理我正在插入主页的HTML(请参阅下面的CSP讨论)。如果我将这个场景模拟为一个独立的页面(这样做只是为了证明我没有做一些愚蠢的事情),这不是问题。
我也在扩展代码中使用jQuery(不是来自CDN),但似乎没有办法将相同的脚本注入页面(你不能引用chrome-extension://主机页面上的脚本标记中的文件)。因此我使用了CDN链接。
我希望有一种方法可以检测页面上是否已经存在jQuery,并且我已经尝试检查document.jQuery以查看它是否已经加载,但即使“run_at”设置为“document_end”我也总是获取document.jQuery的“undefined”。使用调试器我可以看到文档完全加载了包含对其他库的引用的标记,但我无法访问这些JavaScript对象。
当然,我可以告诉jQuery是否在我插入页面的JavaScript页面上,但是这已经太迟了,因为如果它不存在我就无法动态加载jQuery(违反了扩展安全规则)虽然我不知道为什么我可以将脚本标记指向页面上的任何旧脚本文件,但不能以编程方式执行此操作。我试过这样做:
var script = document.createElement('script');
script.type = "text/javascript";
script.src = "https://code.jquery.com/jquery-latest.min.js";
document.getElementsByTagName('head')[0].appendChild(script);
得到了这个错误(为了清楚起见被截断):
Refused to load the script 'https://code.jquery.com/jquery-latest.min.js' because it violates the following Content Security Policy directive: ...'unsafe-inline' 'unsafe-eval' 127.0.0.1:*".
我已经考虑过将jQuery中的手动流式传输到主机页面,但它听起来并不像是注入100KB,或多或少,是解决问题的合理方法。我可以试试这个...
任何想法或建议都将受到赞赏。
更新(2017/02/03):Makyen在这里回答了一个非常相似的问题:How to sequentially insert scripts into the page context using <script> tags
具体来说,我有一些JavaScript来加载库,然后在完成后运行我的一些代码:
var jQueryScript = document.createElement('script');
$(document.head).append(jQueryScript);
jQueryScript.addEventListener('load', () =>
{
console.log("prepare-jQuery is initializing jQuery");
console.log("jQuery's type is " + typeof jQuery);
window.MyChromeExtension_jQuery = jQuery.noConflict(true);
console.log("prepare-jQuery was successful...");
}, false);
jQueryScript.src = chrome.extension.getURL('/thirdParty/jquery/3.1.0/jquery.min.js');
虽然他的方法确实加载了脚本,但它确实运行了我的onLoad监听器,设置window.MyChromeExtension_jQuery在扩展上下文中发生,因此我在主机页面上注入的代码不可用。
另外,请注意OnLoad必须发生的顺序(在添加侦听器或告诉它要加载哪个文件之前,必须将其附加到主机页面DOM)。