javascript 从 chrome 扩展访问 iframe
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13266192/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Accessing iframe from chrome extension
提问by Kirill Ivlev
I'm developing a chrome extension and bumped into a big problem.
我正在开发一个 chrome 扩展并遇到了一个大问题。
I'm using content scripts to inject my javascript code on a web site. The web site has an iframe. I can change the source code of the iframe but don't seem to get any access to the iframe's contentWindow property. I need it to insert text at the current carret position.
我正在使用内容脚本在网站上注入我的 javascript 代码。该网站有一个 iframe。我可以更改 iframe 的源代码,但似乎无法访问 iframe 的 contentWindow 属性。我需要它在当前插入位置插入文本。
So basically this code works perfectly in the context of the page:
所以基本上这段代码在页面的上下文中完美地工作:
$("#iframe1").contentWindow.document.execCommand("InsertHTML", false, 'test text');
But when I try it to run in the context of my chrome extension I get this error:
但是当我尝试在我的 chrome 扩展的上下文中运行时,我收到此错误:
TypeError: Cannot read property 'document' of undefined
What's strange is that I can access the html of the iframe. So this code works perfectly from the chrome extension:
奇怪的是我可以访问 iframe 的 html。所以这段代码在 chrome 扩展中完美运行:
$("#iframe1").contents().find('div').html('test')
I tried putting "all_frames": true in the manifest file but no luck :(
我尝试将 "all_frames": true 放在清单文件中,但没有运气:(
回答by Rob W
To understand why your code does not work, I include 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 policyof 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.
内容脚本无权访问页面的全局
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
与文档关联的对象)是undefined。iframe.contentWindow
是未定义的。
Solution for same-origin frames
同源帧的解决方案
In your case, either of the following will work:
在您的情况下,以下任一方法都有效:
// 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);
Generic solution
通用解决方案
The generic solution is using "all_frames": true
in the manifest file, and use something like this:
通用解决方案"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);
}
This demo is for educational purposes only, do not use this demo in a real extension. Why? Because it uses postMessage
to pass messages around. These events can also be generated by the client, which causes a security leak (XSS: arbitrary HTML injection).
此演示仅用于教育目的,请勿在实际扩展中使用此演示。为什么?因为它postMessage
用来传递消息。这些事件也可以由客户端生成,从而导致安全漏洞(XSS:任意 HTML 注入)。
The alternative to postMessage
is Chrome's message API. For a demo, see this answer. You won't be able to compare the window
objects though. What you cando is to rely the window.name
property. The window.name
property is automatically set to the value of the iframe's name
attribute (just once, when the iframe is loaded).
替代方案postMessage
是 Chrome 的消息 API。有关演示,请参阅此答案。但是,您将无法比较window
对象。你可以做的是依靠window.name
财产。该window.name
属性会自动设置为 iframename
属性的值(仅在 iframe 加载时设置一次)。