为什么我不能将此消息传送到选项卡中的所有框架 运行?
Why can't I deliver this message to all of the frames running in a tab?
主题
我正在创建一个 chrome 扩展,它要求我从我的活动页面向 内容脚本发送消息,该脚本在特定帧 运行
单击上下文菜单选项时调用消息传递操作。仅当焦点元素被 chrome.contextMenus
API 分类为 editable
时,该选项才可用。我需要将它发送到包含该元素的最内层框架。 (所以我可以通过访问 document.activeElement
对象来修改它的内容)
从 Chrome 41 开始,您可以通过将 options
参数的 frameID
参数的值填充到 chrome.tabs.sendMessage()
函数。
但是,Chrome 41 目前处于测试阶段,因此我显然不能期望此功能与我用户的浏览器兼容。
因此,我计划向选项卡中的所有框架发送一条消息(通过在 all_frames
属性 设置为 true 的内容脚本上注册侦听器)。进入内容脚本后,我可以通过将 window.location.href
的值与我期望的值进行比较来检查这是否是正确的框架。
问题
看来我对事件侦听器或消息传递的工作方式有误解。该文档非常清楚 chrome.tabs.sendMessage()
应该向选项卡中的每个框架发送一条消息,但似乎每条消息只能调用一个 chrome.runtime.onMessage()
事件侦听器,即使有注册的听众很多。实际调用哪个似乎是由页面的初始加载以某种方式决定的。
这可以用我的扩展的简化版本来演示:
manifest.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
}]
}
background.html:
<!DOCTYPE text/html>
<html>
<head>
<textarea id="temp"></textarea>
<script src="event.js"></script>
</head>
<body>
</body>
</html>
event.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
}
content.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);
}
如果您访问包含许多框架的页面,例如 youtube,您应该注意以下行为:
您在页面初始加载时收到很多警报,表明有很多框架正在加载(全部带有侦听器)
点击上下文菜单选项时,只弹出一个alert,表示只有一个框架收到消息
在多次单击上下文选项的过程中,接收消息的框架保持不变,但在您刷新页面时可能会发生变化。这表明最初加载框架的顺序决定了哪个框架将收到您的消息。
那么,我如何才能确保我尝试与之交谈的框架确实收到了我的消息,而不求助于仅在 chrome 41 中可用的 frameID
选项?
起初我以为我可以从内容脚本发回一个响应,表明消息是否到达了正确的位置。我可以在事件页面的 while 循环中继续这样做,直到我收到一条消息告诉我停止,但就像我之前说的那样,在刷新页面之前,消息似乎总是转到同一帧,所以这种方法对我没有任何作用。
其他详细信息
我注意到的另一件事是,如果页面上有两个具有相同来源的框架,例如 plus.google.com,两个框架都会响应消息,如果这是框架集消息碰巧被传递到。因此,收件人似乎是基于来源而不是某些唯一的帧 ID。
这是我拍摄的一段 link 视频,讲述了我所说的现象:
请注意,我最初收到的警报来自:youtube.com、accounts.google.com、plus.google.com 和 client6.google.com,但只有 client6 收到了我的消息
您的 chrome.runtime.onMessage
事件侦听器调用 alert
。此方法会阻塞运行时,直到对话框关闭。出于某种原因,这也阻止了 Chrome 在其他帧中正确触发 onMessage 事件(报告为 crbug.com/456482)。
解决方法是使用 console.log
代替 alert
进行调试,或者将代码包装在 setTimeout
中以异步处理 onMessage
事件。例如
chrome.runtime.onMessage.addListener(function(message) {
setTimeout(function() {
alert('Message: ' + message);
});
});
主题
我正在创建一个 chrome 扩展,它要求我从我的活动页面向 内容脚本发送消息,该脚本在特定帧 运行
单击上下文菜单选项时调用消息传递操作。仅当焦点元素被 chrome.contextMenus
API 分类为 editable
时,该选项才可用。我需要将它发送到包含该元素的最内层框架。 (所以我可以通过访问 document.activeElement
对象来修改它的内容)
从 Chrome 41 开始,您可以通过将 options
参数的 frameID
参数的值填充到 chrome.tabs.sendMessage()
函数。
但是,Chrome 41 目前处于测试阶段,因此我显然不能期望此功能与我用户的浏览器兼容。
因此,我计划向选项卡中的所有框架发送一条消息(通过在 all_frames
属性 设置为 true 的内容脚本上注册侦听器)。进入内容脚本后,我可以通过将 window.location.href
的值与我期望的值进行比较来检查这是否是正确的框架。
问题
看来我对事件侦听器或消息传递的工作方式有误解。该文档非常清楚 chrome.tabs.sendMessage()
应该向选项卡中的每个框架发送一条消息,但似乎每条消息只能调用一个 chrome.runtime.onMessage()
事件侦听器,即使有注册的听众很多。实际调用哪个似乎是由页面的初始加载以某种方式决定的。
这可以用我的扩展的简化版本来演示:
manifest.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
}]
}
background.html:
<!DOCTYPE text/html>
<html>
<head>
<textarea id="temp"></textarea>
<script src="event.js"></script>
</head>
<body>
</body>
</html>
event.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
}
content.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);
}
如果您访问包含许多框架的页面,例如 youtube,您应该注意以下行为:
您在页面初始加载时收到很多警报,表明有很多框架正在加载(全部带有侦听器)
点击上下文菜单选项时,只弹出一个alert,表示只有一个框架收到消息
在多次单击上下文选项的过程中,接收消息的框架保持不变,但在您刷新页面时可能会发生变化。这表明最初加载框架的顺序决定了哪个框架将收到您的消息。
那么,我如何才能确保我尝试与之交谈的框架确实收到了我的消息,而不求助于仅在 chrome 41 中可用的 frameID
选项?
起初我以为我可以从内容脚本发回一个响应,表明消息是否到达了正确的位置。我可以在事件页面的 while 循环中继续这样做,直到我收到一条消息告诉我停止,但就像我之前说的那样,在刷新页面之前,消息似乎总是转到同一帧,所以这种方法对我没有任何作用。
其他详细信息
我注意到的另一件事是,如果页面上有两个具有相同来源的框架,例如 plus.google.com,两个框架都会响应消息,如果这是框架集消息碰巧被传递到。因此,收件人似乎是基于来源而不是某些唯一的帧 ID。
这是我拍摄的一段 link 视频,讲述了我所说的现象:
请注意,我最初收到的警报来自:youtube.com、accounts.google.com、plus.google.com 和 client6.google.com,但只有 client6 收到了我的消息
您的 chrome.runtime.onMessage
事件侦听器调用 alert
。此方法会阻塞运行时,直到对话框关闭。出于某种原因,这也阻止了 Chrome 在其他帧中正确触发 onMessage 事件(报告为 crbug.com/456482)。
解决方法是使用 console.log
代替 alert
进行调试,或者将代码包装在 setTimeout
中以异步处理 onMessage
事件。例如
chrome.runtime.onMessage.addListener(function(message) {
setTimeout(function() {
alert('Message: ' + message);
});
});