为什么我无法将此消息传递给选项卡中运行的所有框架?

Luk*_*uke 1 javascript dom google-chrome message-passing google-chrome-extension

主题

我正在创建一个 chrome 扩展,它要求我将消息从我的事件页面发送到在特定框架上运行的内容脚本

单击上下文菜单选项时将调用消息传递操作。仅当焦点元素被 API 分类时,该选项才可editablechrome.contextMenus。我需要将它发送到包含该元素的最里面的框架。(这样我就可以通过访问该对象来修改其内容document.activeElement

从 Chrome 41 开始,您可以通过填充函数frameID参数的参数值来向特定框架发送消息。optionschrome.tabs.sendMessage()

然而,Chrome 41 目前还处于测试阶段,所以我显然不能指望这个功能与我用户的浏览器兼容。

因此,我计划向选项卡中的所有框架发送消息(通过在属性设置all_frames为 true 的内容脚本上注册侦听器)。window.location.href进入内容脚本后,我可以通过将 的值与我期望的值进行比较来检查这是否是正确的框架。

问题

看起来好像我对事件侦听器或消息传递的工作方式有误解。文档非常清楚地表明,chrome.tabs.sendMessage()应该向选项卡中的每个框架发送一条消息,但似乎每条chrome.runtime.onMessage()消息只能调用一个事件侦听器,即使注册了许多侦听器。实际调用哪一个似乎是由页面的初始加载以某种方式决定的。

这可以通过我的扩展的简化版本来演示:

清单.json:

{
    "manifest_version":2,

    "name":"Demo",
    "description":"This is a demonstration",
    "version":"1.0",

    "permissions":[
     "<all_urls>",
     "contextMenus"
     ],

    "background": {
       "page":"background.html",
       "persistent":false
    },

    "content_scripts":[{
        "matches":["<all_urls>"],
        "js":["content.js"],
        "all_frames":true
     }]
}
Run Code Online (Sandbox Code Playgroud)

背景.html:

<!DOCTYPE text/html>
<html>
 <head>
  <textarea id="temp"></textarea>
  <script src="event.js"></script>
 </head>
 <body>
 </body>                                                                                                                                                                   
</html>
Run Code Online (Sandbox Code Playgroud)

事件.js:

chrome.contextMenus.onClicked.addListener(requestHandler);  
chrome.contextMenus.create({
    "title":"Click Me!!",
    "contexts":["editable"],
    "id":"1"                                                                                                                                               
});

function requestHandler(info, tab)
{
    chrome.tabs.sendMessage(tab.id, {"destination":info.frameUrl});

    //this should get sent to every frame in the tab
}
Run Code Online (Sandbox Code Playgroud)

内容.js:

chrome.runtime.onMessage.addListener(handleRequest);

alert("Hi, i'm: " + window.location.host);
function handleRequest(message)
{
    alert("You tried to send a message to: " + message.destination);
    alert("I am: " + window.location.href);
}
Run Code Online (Sandbox Code Playgroud)

如果您访问包含许多框架的页面(例如youtube) ,您应该注意到以下行为:

  • 页面初始加载时您会收到许多警报,表明正在加载许多框架(全部带有侦听器)

  • 当您单击上下文菜单选项时,仅弹出单个警报,表明只有单个框架收到消息

  • 在多次单击上下文选项的过程中,接收消息的框架保持不变,但在刷新页面时可能会发生变化。这表明帧最初加载的顺序决定了哪个帧将获取您的消息。


那么,我如何确保我尝试与之通信的框架确实收到我的消息,而不求助于frameID仅在 chrome 41 中可用的选项?

起初我以为我可以从内容脚本发回响应,指示消息是否发送到正确的位置。我可以在事件页面中继续在 while 循环中执行此操作,直到收到一条消息告诉我停止,但正如我之前所说,该消息似乎总是进入同一帧,直到刷新页面,所以这种方法对我没有任何作用。

额外细节

我注意到的另一件事是,如果页面上有两个具有相同来源的框架(例如 plus.google.com),则两个框架都会响应消息(如果这是消息碰巧传递的框架集)到。因此,接收者似乎是基于来源而不是某些唯一的帧 ID。

这是我拍摄的有关我所说的现象的视频的链接:

请注意,我最初收到的警报来自:youtube.com、accounts.google.com、plus.google.com 和 client6.google.com,但只有 client6 收到了我的消息

Rob*_*b W 5

您的chrome.runtime.onMessage事件侦听器调用alert. 此方法会阻塞运行时,直到对话框关闭。由于某种原因,这也会阻止 Chrome 在其他框架中正确触发 onMessage 事件(报告为crbug.com/456482)。

解决方法是使用console.logdebug 而不是alert,或将代码包装在其中setTimeout以异步处理onMessage事件。例如

chrome.runtime.onMessage.addListener(function(message) {
    setTimeout(function() {
        alert('Message: ' + message);
    });
});
Run Code Online (Sandbox Code Playgroud)