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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 18:16:08  来源:igfitidea点击:

Accessing iframe from chrome extension

javascriptiframegoogle-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 windowobject. For content scripts, the following applies:

  • The windowvariable 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-environment

Given 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]and frames['frameName'], (normally referring to the the frame's containing global windowobject) is undefined.
  • var iframe = document.getElementById('frameName');
    • iframe.contentDocumentreturns a documentobject of the containing frame, because content scripts have access to the DOM of a page. This property is nullwhen the Same origin policy applies.
    • iframe.contentDocument.defaultView(refers to the windowobject associated with the document) is undefined.
    • iframe.contentWindowis 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": truein 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 postMessageto 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 postMessageis Chrome's message API. For a demo, see this answer. You won't be able to compare the windowobjects though. What you cando is to rely the window.nameproperty. The window.nameproperty is automatically set to the value of the iframe's nameattribute (just once, when the iframe is loaded).

替代方案postMessage是 Chrome 的消息 API。有关演示,请参阅此答案。但是,您将无法比较window对象。你可以做的是依靠window.name财产。该window.name属性会自动设置为 iframename属性的值(仅在 iframe 加载时设置一次)。