从Chrome扩展访问iframe

从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”:真正的清单文件,但没有运气:(

+2

请参阅http://*.com/a/6645359/502149。您应该从注入到iframe的内容脚本中操作iframe的内容,而不是从包含页面操作。 –

要理解为什么你的代码不能正常工作,我包括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-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 policy of 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 window object) is undefined.
  • var iframe = document.getElementById('frameName');
    • iframe.contentDocument returns a document object of the containing frame, because content scripts have access to the DOM of a page. This property is null when the Same origin policy applies.
    • iframe.contentDocument.defaultView (refers to the window 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时一次)。

+1

谢谢。这非常有帮助! –

+0

@Rob W我试着做同样的事情,但现在chrome显示'same-origin-policy'错误。我想他们已经更新了他们的旗帜。 '阻止父母访问跨源帧(子帧)' –