在chrome扩展中,如何将来自父内容脚本的跨源消息发送到特定子iframe中的内容脚本

DAR*_*DAR 5 javascript iframe google-chrome-extension firefox-addon-webextensions

我正在开发一个带有清单的Chrome扩展程序,目前可以访问所有主机.后台脚本将内容脚本注入所有帧.加载DOM后,首页/框架中的内容脚本开始遍历DOM树.当walker遇到iframe时,它需要发送与该iframe窗口相关联的特定内容脚本(可能是跨源)以开始它的工作并包含一些带有此消息的序列化数据.父窗口暂停执行并等待孩子完成它的步行并发回消息,它与序列化数据一起完成.然后父母继续工作.我尝试了两种解决这个问题的方法:

  1. frameElement.contentWindow.postMessage:这大部分时间都有效,但并非总是如此.有时,与iframe窗口关联的内容脚本消息事件侦听器永远不会收到该消息.我无法确认原因,但我认为在听众打电话之前是听众event.stopImmediatePropagation().例如,在雅虎主页(https://www.yahoo.com)上,将消息发布到与iframe源关联的内容脚本https://s.yimg.com/rq/darla/2-9- 9/html/r-sf.html,永远不会收到该消息.这是与广告相关的iframe.也许阻止消息是故意的.发布消息时没有错误,我使用targetOrigin为"*".
  2. chrome.runtime.sendMessage:我可以向后台页面发送消息,但无法弄清楚如何告诉后台页面中继消息的帧.父窗口内容脚本不知道与在DOM漫游中遇到的子帧元素关联的chrome扩展frameId.所以它无法告诉后台页面如何指导消息.

对于第2点,我尝试了两种在stackoverflow上找到的技术:

  1. 使用此问题中描述的概念:在父窗口中,确定iframe在window.frames数组中的位置,并使用此索引将消息发布到后台页面.后台页面将消息发布到消息数据中具有所需索引的所有帧.只有在window.parent.frames数组中找到它的窗口对象位置的iframe才能匹配从消息中收到的索引,并继续它的行走.这工作正常但window.frames在异步消息传递过程中容易受到数组中的更改的影响(如果在发送消息后删除了iframe,则索引值可能不再与所需的帧匹配).
  2. 而不是从第1点开始的索引值,frameElement.name在父窗口中使用.使用相同的消息传递技术,将名称发送到子iframe以与其window.name值进行比较.我相信window.nameframeElement.nameiframe元素创建时获得它的价值.但是,由于我不控制框架元素的创建,因此name属性通常是一个空字符串,不能依赖它来将iframe元素唯一地匹配到它们的窗口.

有没有办法让我可靠地将消息发送到与在DOM树中行走时找到的iframe元素关联的内容脚本?

Rob*_*b W 6

chrome.runtime.sendMessage从内容脚本调用时,chrome.runtime.onMessage侦听器的第二个参数("sender")包含属性urlframeId.您可以(从一个扩展页面,例如背景页)发送邮件使用一个特定的框架chrome.tabs.sendMessage给定frameId.

如果您想随时知道所有帧的列表(及其帧ID),请使用chrome.webNavigation.getAllFrames.如果这样做,那么您可以在选项卡中构建帧的树,然后将此信息发送到所有帧以供进一步处理.

可靠postMessage/onMessage

frameElement.contentWindow.postMessage:这大部分时间都有效,但并非总是如此.有时,与iframe窗口关联的内容脚本消息事件侦听器永远不会收到该消息.我无法确认原因,但我认为在听众打电话之前是听众event.stopImmediatePropagation()

这可以通过运行脚本"run_at":"document_start"并立即注册message事件监听器来解决.然后将始终首先调用您的处理程序,并且页面无法通过它取消它event.stopImmediatePropagation().但是,不要盲目信任来自其他帧的信息并始终验证消息(例如,通过后台页面与其他帧通信).

结合两种方法

第一种方法提供了一种在帧之间交换数据的安全方法,但没有提供将帧链接到特定DOM元素的通用方法.
第二种方法允许您定位特定的(i)框架元素,但任何网页都可以这样做,因此该方法本身并不可靠.通过组合两者,您将获得一个链接到DOM元素的安全通信通道.

这是应用上述方法在帧A和帧B之间进行通信的基本示例:

  1. A中的内容脚本:

    1. 向背景页面发送消息(例如,包括帧B的索引的消息).
  2. 背景页面:

    1. 收到来自A的消息.
    2. 生成一个随机的随机数,比如说R(crypto.getRandomValues).
    3. 存储从RframeId(以及可选地包含在来自A的消息中的其他信息)的映射.
    4. 使用此随机值调用响应回调.
  3. A中的内容脚本:

    1. 从后台页面接收R.
    2. 调用postMessage在框架B和通过ř.
  4. B中的内容脚本:

    1. 从A 接收R
    2. 将消息发送到后台页面以检索frameId(以及可选的A中的其他信息).

注意:对于坚如磐石的应用程序,您需要考虑在任何这些步骤中删除框架的事实.如果忽略此过程的异步特性,则可能会使应用程序处于不一致状态.