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

标签 javascript iframe google-chrome-extension firefox-addon-webextensions

我正在开发一个带有 list 的 Chrome 扩展程序,目前可以访问所有主机。后台脚本将内容脚本注入(inject)所有框架。 DOM 加载完成后,顶部页面/框架中的内容脚本开始遍历 DOM 树。当 walker 遇到 iframe 时,它​​需要向与该 iframe 窗口相关联的特定内容脚本发送消息(可能是跨源)以开始其工作,并在此消息中包含一些序列化数据。父窗口暂停执行并等待子窗口完成它的行走,并发送回一条消息,表明它已完成并连同序列化数据。 parent 然后继续其工作。我已经尝试了两种方法来解决这个问题:

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

有没有办法让我可靠地将消息发送到与遍历 DOM 树时发现的 iframe 元素关联的内容脚本?

最佳答案

当您调用 chrome.runtime.sendMessage 时来自内容脚本,chrome.runtime.onMessage 的第二个参数listener ("sender") 包括属性 urlframeId。 您可以使用 chrome.tabs.sendMessage 将消息(从扩展页面,例如后台页面)发送到特定框架。使用给定的 frameId

如果您想随时了解所有帧(及其帧 ID)的列表,请使用 chrome.webNavigation.getAllFrames .如果这样做,则可以在选项卡中构造一棵框架树,然后将此信息发送到所有框架以进行进一步处理。

可靠的postMessage/onMessage

frameElement.contentWindow.postMessage: this works most of the time, but not always. Sometimes the message is never received by the content script message event listener associated with the iframe window. I have not been able to confirm the cause but I think it is listeners attached before my listener calling event.stopImmediatePropagation()

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

结合两种方法

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

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

  1. A 中的内容脚本:

    1. 向后台页面发送消息(例如包含框架B索引的消息)。
  2. 背景页面:

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

    1. 从后台页面接收R
    2. 在帧 B 上调用 postMessage 并传递 R
  4. B 中的内容脚本:

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

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

关于javascript - 在 chrome 扩展中,如何将跨源消息从父内容脚本发送到特定子 iframe 中的内容脚本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38103534/

相关文章:

javascript - 不同页面有不同的验证设置

javascript - 模板编译时出错 'SharedModule' 不支持表达式形式

jquery - 使用 jQuery 从 iFrame 中的元素获取值

javascript - iframe - 有没有办法在 iframe 中显示浏览器的菜单栏以及地址栏等其他项目

java - 如何使用扩展和 Selenium 打开新选项卡?

javascript - Chrome 扩展捕获流

javascript - Laravel ajax 请求不适用于 foreach 循环

javascript - Chrome 漏洞 : Some CSS rules ignored

google-chrome-extension - chrome 文件系统检查 - 更改时重新加载

javascript - Jquery/AJAX 功能不起作用