我正在开发一个Chrome扩展,并撞上了一个大问题。
我使用的内容脚本注入在一个网站上我的JavaScript代码。 该网站有一个iframe中。 我可以改变iframe的源代码,但似乎没有得到iframe的contentWindow财产的任何访问。 我需要它在当前位置CARRET插入文本。
所以基本上这个代码工作完全在页面的上下文:
$("#iframe1").contentWindow.document.execCommand("InsertHTML", false, 'test text');
但是,当我尝试在我的Chrome扩展程序的上下文中运行我得到这个错误:
TypeError: Cannot read property 'document' of undefined
什么奇怪的是,我可以访问iframe的HTML。 所以这个代码在Chrome扩展完美的作品:
$("#iframe1").contents().find('div').html('test')
我试图把“all_frames”:真正的清单文件,但没有运气:(
要理解为什么你的代码不能正常工作,我包括我以前的答案的一个片段 :
内容脚本没有一个网页的全球任何访问window
对象。 对于内容的脚本,应用如下:
- 该
window
变量并不是指页面的全局对象。 相反,它指的是一个新的背景下,“层”在页面上。 该页面的DOM是完全无障碍。 #执行环境
给定组成的文件<iframe id="frameName" src="http://domain/"></iframe>
- 访问受限制的帧的内容同源策略页面; 扩展的权限不放松的政策。
-
frames[0]
和frames['frameName']
通常指的是在框架的包含全局window
对象)是undefined
。 -
var iframe = document.getElementById('frameName');
-
iframe.contentDocument
返回一个document
含有帧的对象,因为内容的脚本可以访问一个页面的DOM。 此属性为null
时,同根同源的政策适用。 -
iframe.contentDocument.defaultView
(指的是window
与所述文档相关联的对象)是不确定的 。 -
iframe.contentWindow
是不确定的 。
解决方案同源框
在你的情况,无论是下面方法:
// jQuery:
$("#iframe1").contents()[0].execCommand( ... );
// VanillaJS
document.getElementById("iframe1").contentDocument.execCommand( ... );
// "Unlock" contentWindow property by injecting code in context of page
var s = document.createElement('script');
s.textContent = 'document.getElementById("iframe1").contentWindow.document.execCommand( ... );';
document.head.appendChild(s);
通用解决方案
通用解决方案是使用"all_frames": true
清单中的文件,使用这样的:
if (window != top) {
parent.postMessage({fromExtension:true}, '*');
addEventListener('message', function(event) {
if (event.data && event.data.inserHTML) {
document.execCommand('insertHTML', false, event.data.insertHTML);
}
});
} else {
var test_html = 'test string';
// Explanation of injection at https://stackoverflow.com/a/9517879/938089 :
// Run code in the context of the page, so that the `contentWindow`
// property becomes accessible
var script = document.createElement('script');
script.textContent = '(' + function(s_html) {
addEventListener('message', function(event) {
if (event.data.fromExtension === true) {
var iframe = document.getElementById('iframe1');
if (iframe && (iframe.contentWindow === event.source)) {
// Window recognised, post message back
iframe.contentWindow.postMessage({insertHTML: s_html}, '*');
}
}
});
} + ')(' + JSON.stringify(test_html) + ');';
(document.head||document.documentElement).appendChild(script);
script.parentNode.removeChild(script);
}
这个演示是仅用于教育目的, 不要在一个真正的扩展使用演示 。 为什么? 由于它采用postMessage
来传递消息左右。 这些事件也可以由客户端,这会导致安全泄漏(:任意HTML注射XSS)生成。
以替代postMessage
是Chrome的消息API。 对于一个演示,看到这个答案 。 您将无法比较window
对象虽然。 你可以做的是依靠window.name
属性。 该window.name
属性被自动设置为iframe的价值name
(只有一次,被加载的iframe时)属性。