从Chrome扩展访问iframe
我正在开发一个Chrome扩展,遇到了一个大问题。从Chrome扩展访问iframe
我正在使用内容脚本在网站上注入我的JavaScript代码。该网站有一个iframe。 我可以更改iframe的源代码,但似乎没有任何访问iframe的contentWindow属性的权限。我需要它在当前的插图位置插入文本。
所以基本上这个代码工作完全在页面的上下文:
$("#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”:真正的清单文件,但没有运气:(
要理解为什么你的代码不能正常工作,我包括a fragment of my previous answer :
Content scripts do not have any access to a page's global
window
object. For content scripts, the following applies:
- The
window
variable does not refer to the page's global object. Instead, it refers to a new context, a "layer" over the page. The page's DOM is fully accessible. #execution-environmentGiven a document consisting of
<iframe id="frameName" src="http://domain/"></iframe>
:
- Access to the contents of a frame is restricted by the Same origin policy of the page; the permissions of your extension does not relax the policy.
frames[0]
andframes['frameName']
, (normally referring to the the frame's containing globalwindow
object) isundefined
.var iframe = document.getElementById('frameName');
iframe.contentDocument
returns adocument
object of the containing frame, because content scripts have access to the DOM of a page. This property isnull
when the Same origin policy applies.iframe.contentDocument.defaultView
(refers to thewindow
object associated with the document) is undefined.iframe.contentWindow
is undefined.
解决方案同源框
在你的情况,无论是下面方法:
// 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://*.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
来传递消息。这些事件也可以由客户端生成,导致安全漏洞(XSS:任意HTML注入)。
postMessage
的替代方法是Chrome的消息API。有关演示,请参阅this answer。尽管如此,您将无法比较window
对象。你可以做的是依靠window.name
属性。 window.name
属性会自动设置为iframe的name
属性的值(仅在加载iframe时一次)。
谢谢。这非常有帮助! –
@Rob W我试着做同样的事情,但现在chrome显示'same-origin-policy'错误。我想他们已经更新了他们的旗帜。 '阻止父母访问跨源帧(子帧)' –
请参阅http://*.com/a/6645359/502149。您应该从注入到iframe的内容脚本中操作iframe的内容,而不是从包含页面操作。 –