从 chrome 扩展访问 iframe [英] Accessing iframe from chrome extension
问题描述
我正在开发一个 chrome 扩展程序,但遇到了一个大问题.
I'm developing a chrome extension and bumped into a big problem.
我正在使用内容脚本在网站上注入我的 javascript 代码.该网站有一个 iframe.我可以更改 iframe 的源代码,但似乎无法访问 iframe 的 contentWindow 属性.我需要它在当前插入位置插入文本.
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.
所以基本上这段代码在页面上下文中完美运行:
So basically this code works perfectly in the context of the page:
$("#iframe1").contentWindow.document.execCommand("InsertHTML", false, 'test text');
但是当我尝试在我的 chrome 扩展的上下文中运行时,我收到此错误:
But when I try it to run in the context of my chrome extension I get this error:
TypeError: Cannot read property 'document' of undefined
奇怪的是我可以访问 iframe 的 html.所以这段代码在 chrome 扩展中完美运行:
What's strange is that I can access the html of the iframe. So this code works perfectly from the chrome extension:
$("#iframe1").contents().find('div').html('test')
我尝试将 "all_frames": true 放在清单文件中,但没有运气:(
I tried putting "all_frames": true in the manifest file but no luck :(
推荐答案
要了解为什么您的代码不起作用,我添加了 我之前回答的一个片段:
To understand why your code does not work, I include a fragment of my previous answer:
内容脚本无法访问页面的全局 window
对象.对于内容脚本,以下内容适用:
Content scripts do not have any access to a page's global
window
object. For content scripts, the following applies:
window
变量不引用页面的全局对象.相反,它指的是一个新的上下文,页面上的一个层".页面的 DOM 是完全可访问的.#execution-environment莉>
给定一个由 组成的文档<iframe id="frameName" src="http://domain/"></iframe>
:
Given a document consisting of <iframe id="frameName" src="http://domain/"></iframe>
:
- 对框架内容的访问受到页面同源政策的限制;您的扩展程序的权限并未放宽政策.
frames[0]
和frames['frameName']
,(通常是指包含全局window
对象的框架)是未定义
.var iframe = document.getElementById('frameName');
iframe.contentDocument
返回包含框架的document
对象,因为内容脚本可以访问页面的 DOM.当同源策略适用时,此属性为null
.iframe.contentDocument.defaultView
(指与文档关联的window
对象)未定义.iframe.contentWindow
未定义.
- 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.
同源帧的解决方案
在您的情况下,以下任一方法都适用:
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);
通用解决方案
通用的解决方案是在清单文件中使用
"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
来传递消息.这些事件也可以由客户端生成,从而导致安全漏洞(XSS:任意 HTML 注入).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
的替代方案是 Chrome 的消息 API.如需演示,请参阅此答案.但是,您将无法比较window
对象.你可以做的是依赖window.name
属性.window.name
属性会自动设置为 iframe 的name
属性的值(仅在加载 iframe 时设置一次).The alternative to
postMessage
is Chrome's message API. For a demo, see this answer. You won't be able to compare thewindow
objects though. What you can do is to rely thewindow.name
property. Thewindow.name
property is automatically set to the value of the iframe'sname
attribute (just once, when the iframe is loaded).这篇关于从 chrome 扩展访问 iframe的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!