Support in all current engines.
Web 浏览器由于安全和隐私等原因禁止不同域的文档见互相影响,也就是说不允许跨站脚本。
虽然这是一个非常重要的安全特性,但它也阻止了不同域的页面之间进行非恶意的通信。 本章引入了一个消息系统来允许不同源的文档间通信,这一设计不会引起跨站脚本攻击。
postMessage()
API 可以用作 跟踪向量。
This section is non-normative.
例如,如果文档 A 包含一个 iframe
元素,其中包含文档 B,
A 中的脚本调用了 B 的 Window
对象上的
postMessage()
,
然后这个对象上会产生一个消息事件,标记为源自文档 A 的 Window
。
文档 A 中的脚本可能像这样:
var o = document.getElementsByTagName('iframe')[0]; o.contentWindow.postMessage('Hello world', 'https://b.example.org/');
脚本通过 addEventListener()
(或类似机制)
为传入的事件注册事件处理器。例如文档 B 中的脚本可能像这样:
window.addEventListener('message', receiver, false); function receiver(e) { if (e.origin == 'https://example.com') { if (e.data == 'Hello world') { e.source.postMessage('Hello', e.origin); } else { alert(e.data); } } }
该脚本先检查域是否符合预期, 然后再检查消息,把它显示给用户或者把它发送回首先发送消息的文档。
使用此 API 需要格外小心, 以防止恶意实体为达到自己的目的在网站上滥用。
作者应该检查 origin
属性以确保只从他们期望的域接收消息。否则,作者的消息处理代码中的错误可能会被恶意网站利用。
此外,即使在检查
origin
属性后,作者也应该检查相关数据的格式。否则,如果事件的源被跨站脚本攻击,对通过
postMessage()
方法发送的消息的未经检查的处理可能会把攻击传播到接收者。
对于任何包含机密信息的消息中,作者不应在 targetOrigin 参数中使用通配符关键字(*),否则无法保证消息仅传递给它希望的接收方。
鼓励接受任何来源消息的作者考虑 DoS 攻击的风险。攻击者可能会发送大量的消息; 如果接收页面执行了耗时的计算或为每个此类消息产生网络流量, 则攻击者的消息可能会被成倍放大,从而导致 DoS 攻击。 鼓励作者采用速率限制(每分钟只接受一定数量的消息)以使这种攻击不可行。
本 API 的完整性基于一个 域
的脚本无法向其他(不同的)域
(使用 dispatchEvent()
或其他方法)发送任意事件。
强烈建议实现方在实现这项功能时要格外小心。 它允许作者将信息从一个域传送到另一个域,通常出于安全原因这是不允许的。 它还要求 UA 小心地允许访问某些属性,但禁止其他的。
也鼓励用户代理考虑对不同 域 之间的消息进行速率限制,来保护简单的网站不受 DoS 攻击。
postMessage
(message [, options ] )Support in all current engines.
像指定的 window 发送消息。消息可以是结构化的对象,
例如嵌套对象和数组,可以包含 JavaScript 值(字符串、数字、Date
、对象等等),
也可以包含数据对象比如 File
Blob
,FileList
,
以及 ArrayBuffer
对象。
options 的 transfer
成员中的对象会被转移(不是克隆),
意味着它们在发送侧不再可用。
可以用 options 的 targetOrigin
成员来指定目标源。
如果没有指定,默认为 "/
"。这个默认值限制了消息只能发送到同源目标。
为避免信息泄露,如果目标窗口的源不匹配给定的源消息会被丢弃。如果要无视目标源直接发送消息,需要设置目标源为 "*
"。
如果 transfer 数组包含重复的对象,或者 message 不可克隆时,
抛出一个 "DataCloneError
" DOMException
。
postMessage
(message, targetOrigin [, transfer ] )这是 postMessage()
的另一个版本,以目标源作为参数。调用
window.postMessage(message, target, transfer)
等价于
window.postMessage(message, {targetOrigin, transfer})
。
给刚导航到新 Document
的 browsing context 上的
Window
发布消息时,消息可能无法到达接收者:
目标 浏览上下文 中的脚本必须有时间设置消息监听器。
因此,如果要发送消息给一个刚创建的子 iframe
的 Window
,
建议作者在子 Document
中发送消息给父级来声明它已经准备好接收消息了,
父级则等待该消息到来后再开发发送消息。
给定 targetWindow, message 和 options 的 窗口发送消息步骤 如下:
令 targetRealm 为 targetWindow 的 Realm。
令 incumbentSettings 为 当前设置对象。
令 targetOrigin 为 options["targetOrigin
"]。
如果 targetOrigin 是一个 U+002F SOLIDUS 字符 (/),则将 targetOrigin 设置为 incumbentSettings 的 origin。
否则,如果 targetOrigin 不是一个 U+002A ASTERISK 字符 (*),则:
令 parsedURL 为在 targetOrigin 上执行 URL 解析 的结果。
如果 parsedURL 是失败,则抛出 "SyntaxError
"
DOMException
。
设置 targetOrigin 为 parsedURL 的 origin。
令 transfer 为 options["transfer
"]。
令 serializeWithTransferResult 为 StructuredSerializeWithTransfer(message, transfer)。 重新抛出任何异常。
在 已发送消息任务源 上给定 targetWindow 入队一个全局任务 来执行以下步骤:
如果 targetOrigin 参数不是单个 U+002A ASTERISK 字符 (*) 且
targetWindow 的 关联
Document
的 源 不
与 targetOrigin 同源,则返回。
令 source 为 WindowProxy
对象对应的
incumbentSettings 的 全局对象
(一个 Window
对象)。
令 deserializeRecord 为 StructuredDeserializeWithTransfer(serializeWithTransferResult, targetRealm)。
如果这抛出了异常,捕获它并使用 MessageEvent
在 targetWindow 上
产生 一个名为
messageerror
的事件,
并把 origin
属性初始化为 origin,
source
属性初始化为 source,
然后返回。
令 messageClone 为 deserializeRecord.[[Deserialized]]。
令 newPorts 为一个新的 frozen array,包含
deserializeRecord.[[TransferredValues]] 中所有的
MessagePort
对象(如果有的话),
并保持它们的相对顺序。
使用 MessageEvent
在 targetWindow 上
产生 一个名为 message
的事件,
其 origin
属性初始化为 origin,
source
属性初始化为 source,
data
属性初始化为 messageClone,
ports
属性初始化为 newPorts。
在 Window
上调用 postMessage(message,
options)
方法时,必须执行以下步骤:
在 Window
上调用 postMessage(message,
targetOrigin, transfer)
方法时,必须执行以下步骤:
令 targetWindow 为这个 Window
对象。
令 options 为 «[ "targetOrigin
" →
targetOrigin, "transfer
" →
transfer ]»。
给定 targetWindow,message 和 options, 执行 窗口发送消息步骤。
This section is non-normative.
为了使独立的代码片段之间可以相互通信(例如运行在不同 浏览上下文 的代码直接通信), 作者可以使用 Channel 通信。
该机制下的通信 Channel 实现为双向管道,两端各一个端口。 从一个端口发送的消息被传递到另一个端口,反之亦然。 消息被作为 DOM 事件传递,不会中断或阻塞正在执行的 任务。
创建连接(两个关联的端口)使用 MessageChannel()
构造函数:
var channel = new MessageChannel();
其中一个端口作为本地端口保存,另一个发送到了远程代码,例如
使用 postMessage()
:
otherWindow.postMessage('hello', 'https://example.com', [channel.port2]);
发送消息使用端口上的 postMessage()
方法:
channel.port1.postMessage('hello');
接收消息需要监听 message
事件:
channel.port1.onmessage = handleMessage; function handleMessage(event) { // message is in event.data // ... }
发送到端口的数据可以是结构化的;例如向 MessagePort
传递字符串数组:
port1.postMessage(['hello', 'world']);
This section is non-normative.
在这个例子中,两个 JavaScript 库通过 MessagePort
相互连接。
这允许它们托管在不同的框架或 Worker
对象中而不需要改变 API。
<script src="contacts.js"></script> <!-- exposes a contacts object --> <script src="compose-mail.js"></script> <!-- exposes a composer object --> <script> var channel = new MessageChannel(); composer.addContactsProvider(channel.port1); contacts.registerConsumer(channel.port2); </script>
Here's what the "addContactsProvider()" function's implementation could look like:
function addContactsProvider(port) { port.onmessage = function (event) { switch (event.data.messageType) { 'search-result': handleSearchResult(event.data.results); break; 'search-done': handleSearchDone(); break; 'search-error': handleSearchError(event.data.message); break; // ... } }; };
或者也可以这样实现:
function addContactsProvider(port) { port.addEventListener('message', function (event) { if (event.data.messageType == 'search-result') handleSearchResult(event.data.results); }); port.addEventListener('message', function (event) { if (event.data.messageType == 'search-done') handleSearchDone(); }); port.addEventListener('message', function (event) { if (event.data.messageType == 'search-error') handleSearchError(event.data.message); }); // ... port.start(); };
关键的区别在于,当使用 addEventListener()
时,必须调用 start()
方法。当使用 onmessage
时,已经默认调用了 start()
。
无论显式或隐式地(通过设置 onmessage
)
调用 start()
方法,都会启动消息流:
发送到消息端口的消息初始是暂停的,这样就不会在脚本还没能建立监听器之前被丢掉。
This section is non-normative.
可以把端口当做为系统中其他角色提供有限能力的一种方式(在对象能力模型的意义上), 这可能是一个弱能力的系统,用端口只是为了在源内通信的方便, 也可能是一个强能力的系统,由一个源 provider 提供,作为唯一的机制给另一个源 consumer 来从 provider 获取信息或造成改变。
例如,考虑一个社交网站嵌入了一个 iframe
它是用户的邮件联系人提供者(一个联系人网站,来自第二个源),
以及第二个 iframe
(一个游戏网站,来自第三个源)
外面的社交网站和第二个 iframe
中的游戏无法访问第一个 iframe
中的任何东西;它们只能:
iframe
导航 到新的 URL,
比如同样的 URL 但有不同的 fragment,
使 iframe
中的 Window
收到一个
hashchange
事件。iframe
的大小,使 iframe
中的 Window
收到一个 resize
事件。window.postMessage()
API 发送一个
message
事件给
iframe
中的 Window
。联系人提供者使用这些方法(尤其是第三个)来给其他域提供操作用户地址簿的 API。
例如,响应 "add-contact Guillaume Tell
<tell@pomme.example.net>
" 时,在用户的地址簿中新增指定的人和 e-mail。
为了避免 Web 上任何站点都可以操作用户的地址簿,联系人提供者可能只允许确定的信任站点调用, 比如这个社交网站。
现在假设这个游戏希望添加联系人到用户的地址簿,而且这个社交网站也愿意允许它这样做, 实质上是“共享”联系人提供者对社交网站的信任。实现这一共享有很多方式, 最简单的是在游戏站点和联系人站点之间代理消息。 然而该方案有一些困难:它需要社交网站完全信任游戏网站不会滥用特权, 或者要求社交网站对每个请求进行验证来确保请求是否被允许 (例如添加多个联系人、读取联系人、删除联系人等)。 如果可能有多个游戏同时与联系人提供者通信,还需要额外的复杂性。
然而,使用消息 Channel 和 MessagePort
对象可以解决所有这些问题。
当游戏告诉设计网络它需要添加联系人时,社交网站可以请求联系人提供者添加单个联系人的
能力,而非直接请求添加一个联系人。
然后联系人提供者提供一对 MessagePort
对象,把其中一个返回给社交网站,
社交网站再把它转交给游戏。游戏和联系人提供者就有了直接的连接,
因此联系人提供者知道只对它开放 "添加联系人" 的请求。
换句话说,这个游戏被赋予了添加单个联系人的能力。
This section is non-normative.
继续上一部分提到的例子,特别考虑联系人提供者。
初始的实现可能是简单地在服务的 iframe
中使用 XMLHttpRequest
对象,
一个改进版本可能会使用 共享 worker
和一个 WebSocket
连接。
如果初始的设计使用了 MessagePort
对象来赋予能力,或者甚至仅仅是为了支持多个并发的独立会话,
这个服务的实现就可以直接从
"每个 iframe
中一个 XMLHttpRequest
" 模型迁移到
"共享 WebSocket
" 模型,
完全不需要改变 API:服务提供者一侧的端口可以全部转发到共享 Worker,完全不影响 API 的用户。
[Exposed=(Window,Worker)]
interface MessageChannel {
constructor();
readonly attribute MessagePort port1;
readonly attribute MessagePort port2;
};
MessageChannel
()Support in all current engines.
返回有两个新的 MessagePort
对象的,一个新的 MessageChannel
对象。
port1
Support in all current engines.
返回第一个 MessagePort
对象。
port2
Support in all current engines.
返回第二个 MessagePort
对象。
MessageChannel
对象有一个关联的 port 1 和 port 2,都是 MessagePort
对象。
new MessageChannel()
构造步骤为:
每个通道都有两个消息端口。从一个端口发送的数据会从另一个端口收到,反之亦然。
[Exposed=(Window,Worker,AudioWorklet), Transferable] interface MessagePort : EventTarget { undefined postMessage(any message, optional sequence<object> transfer = []); undefined postMessage(any message, optional PostMessageOptions options = {}); undefined start(); undefined close(); // event handlers attribute EventHandler onmessage; attribute EventHandler onmessageerror; }; dictionary PostMessageOptions { sequence<object> transfer = []; };
postMessage
(message [, transfer] )Support in all current engines.
postMessage
(message [, { transfer
}] )通过通道发布一条消息。列在 transfer 中的对象已经被传输(不仅是克隆), 意味着在发送侧无法使用了。
如果 transfer 数组包含重复的对象、源或目标端口时,
或者 message 不可克隆,
抛出一个 "DataCloneError
" DOMException
。
start
()Support in all current engines.
开始派发端口上收到的消息。
close
()Support in all current engines.
断开端口,端口不再处于激活状态。
每个 MessagePort
对象可以与另一个关联(对称关系)。
每个 MessagePort
对象也可以有一个 任务源
称为 端口消息队列,初始为空。
端口消息队列 可以被启用和禁用,初始禁用。
一旦被启用,就不能再被禁用了
(虽然队列中的消息可以移动到其他队列或者全部移除,也可以达到一样的效果),
MessagePort
还有一个 已经被转移 标志,
初始必须为 false。
当端口的 端口消息队列 被启用时,
事件循环 不许使用它作为其中一个
任务源。当端口的
相关全局对象 是 Window
时,
在它的 端口消息队列 中的所有
任务 必须与该端口的
相关全局对象 的
关联 Document
相关联。
如果该文档是 完全激活的, 但事件监听器的脚本的 设置对象 指定的 负责文档 不 是 完全激活的,消息会在这些文档 完全激活的 时才收到。
每个 事件循环 有一个 任务源
称为 未转移的端口消息队列。
这是一个虚拟的 任务源,它的必须表现地就像它包含满足以下条件的的每个
MessagePort
的 端口消息队列 的所有 任务 一样:
1. 已经被转移 标志位 false;
2. 启用了 端口消息队列;
3. 相关代理 的 事件循环 为该 事件循环。
顺序为它们被添加到对应的 任务源 的顺序。
当 任务 从 未转移的端口消息队列 移除时,
它必须也从它的 端口消息队列 中移除。
当 MessagePort
的 已被转移 标志为 false 时,
事件循环 必须忽略它的
端口消息队列
(此时使用 未转移的端口消息队列)。
当一个端口、其关联端口、或者它克隆自的对象被传输时,
已被转移 标志位设为 true
当 MessagePort
的 已被转移 标志为 true 时,
其 端口消息队列 是一级
任务源,不受任何
未转移的端口消息队列 影响。
当用户代理 关联 两个 MessagePort
对象时,
必须执行以下步骤:
如果其中给一个端口已经关联,则把它与之前关联的端口解关联。
如果那两个之前关联的端口是同一个
MessageChannel
对象的两个端口,则那个 MessageChannel
对象不再表示任何真正的端口:它的两个端口已经不再关联。
关联这两个端口,让它们组成新通道的两部分。
(没有 MessageChannel
对象表示这个通道)
经过这个步骤的两个端口 A 和 B 被称为关联的;其中一个关联到了另一个,反之亦然。
尽管本标准把这个过程描述为瞬间的,但它们更有可能通过消息传递来实现。 就像其他所有算法一样,关键是只要最终结果无法与规范区分(在黑盒的意义上)。
MessagePort
对象是
可传输对象。
给定 value 和 dataHolder,
它们的 传输步骤 是:
设置 value 的 已被转移 标志为 true。
设置 dataHolder.[[PortMessageQueue]] 到 value 的 端口消息队列。
如果 value 与另一个端口 remotePort 关联,则:
设置 remotePort 的 已被转移 标志为 true。
设置 dataHolder.[[RemotePort]] 为 remotePort。
否则,设置 dataHolder.[[RemotePort]] 为 null。
给定 dataHolder 和 value, 它们的 传输-接收步骤 是:
设置 value 的 已被转移 标志为 true。
把所有要在 dataHolder.[[PortMessageQueue]] 上触发
message
事件的
tasks(如果有的话)
移动到
value 的 端口消息队列 上,
保持 value 的 端口消息队列 处于初始的禁用状态,
如果 value 的 相关全局对象 是 Window
,
将移动后的 任务 与 value 的
相关全局对象 的
关联 Document
相关联。
如果 dataHolder.[[RemotePort]] 非 null,则 关联 dataHolder.[[RemotePort]] 和 value。(这将会把 dataHolder.[[RemotePort]] 与它之前关联的被转移的端口解关联)。
给定 targetPort,message 和 options, 消息端口发消息的步骤 如下:
令 transfer 为 options["transfer
"]。
如果在 transfer 中的任何一个对象是这个 MessagePort
,
则抛出一个 "DataCloneError
" DOMException
。
令 doomed 为 false。
如果 targetPort 非 null 且 transfer 中有任何对象是 targetPort,则设置 doomed 为 true,并(可选地)在开发终端中报告 目标端口被发送到了它自己,导致通道丢失。
令 serializeWithTransferResult 为 StructuredSerializeWithTransfer(message, transfer)。 重新抛出任何异常。
如果没有 targetPort (比如这个 MessagePort
未关联),
或者如果 doomed 为 true,则返回。
在 targetPort 的 端口消息队列 中添加一个 任务 执行以下步骤:
令 finalTargetPort 为在当前任务所在的
端口消息队列 的 MessagePort
。
这可能与 targetPort 不同,如果 targetPort 自己被传输,它的所有任务都跟着移动。
令 targetRealm 为 finalTargetPort 的 相关 Realm。
令 deserializeRecord 为 StructuredDeserializeWithTransfer(serializeWithTransferResult, targetRealm)。
如果这抛出了异常,捕获它并在
finalTargetPort 上用 MessageEvent
触发一个
名为 messageerror
的事件,然后返回。
令 messageClone 为 deserializeRecord.[[Deserialized]]。
令 newPorts 为一个新的 冻结的数组
该数组包含所有 deserializeRecord.[[TransferredValues]] 中的
MessagePort
对象(如果有的话),保持它们的相对顺序。
在 finalTargetPort 上用 MessageEvent
,
触发一个 名为 message
的事件,其 data
属性初始化为
messageClone 且 ports
属性初始化为newPorts。
在 MessagePort
对象上调用
postMessage(message,
options)
方法时,执行以下步骤:
令 targetPort 为 MessagePort
绑定的端口(如果有的话),
如果没有就令它为 null。
用 targetPort,message 和 options 执行 消息端口发送消息步骤。
在 MessagePort
对象上调用
postMessage(message,
transfer)
方法时必须执行以下步骤:
令 targetPort 为 MessagePort
绑定的端口(如果有的话),
如果没有就令它为 null。
令 options 为 «[ "transfer
" → transfer ]»。
用 targetPort,message 和 options 执行 消息端口发送消息步骤。
start()
方法必须启用其端口的
端口消息队列,如果还没有启用的话。
在已关联的端口 local port 上调用
close()
方法时,
用户代理必须解关联这两个端口。
如果该方法在一个未关联的端口上调用时,该方法必须什么都不做。
下面是所有实现 MessagePort
接口的对象 必须 支持的
事件处理器
(以及它们相应的事件处理器事件类型),
作为 事件处理器 IDL 属性:
事件处理器 | 事件处理器事件类型 |
---|---|
onmessage Support in all current engines. Firefox41+Safari5+Chrome1+ Opera10.6+Edge79+ Edge (Legacy)12+Internet Explorer10+ Firefox Android41+Safari iOS4.2+Chrome Android18+WebView Android37+Samsung Internet1.0+Opera Android11.5+ | message
|
onmessageerror MessagePort/messageerror_event Firefox57+SafariNoChrome60+ Opera47+Edge79+ Edge (Legacy)18Internet ExplorerNo Firefox Android57+Safari iOSNoChrome Android60+WebView Android60+Samsung Internet8.0+Opera Android47+ | messageerror
|
第一次设置 MessagePort
对象的
onmessage
IDL 属性时,
端口的 端口消息队列 必须被启用,
就像调用了 start()
方法一样。
This section is non-normative.
B广播给多个端口原则上相对简单:维护一个要发消息的
MessagePort
对象数组,发消息时遍历这个数组。
然而这有一个不好的效果:它会阻止端口的垃圾回收,即使另一端已经走了。
为了避免这个问题,实现一个简单的协议让对方确认它还存在。
如果它在某段时间没有确认,就假设它已经不在了,关闭
MessagePort
对象并让它被回收。
当一个 MessagePort
对象 o 被关联时,用户代理的表现必须像是
o 的关联 MessagePort
对象有一个 o 的强引用。
或者就像 o 的 相关全局对象 有一个
o 的强引用。
这样给定一个事件监听器,它收到一个消息端口后可以忘掉它。 只要该事件监听器还可能收到消息,这个通道就仍然被维护着。
当然这在通道的两侧都会发生,只要它们不能从活跃的代码到达,两个端口就都会被垃圾回收。 即使它们之间还有对方的强引用。
进一步地,当
任务队列 中的 任务
仍然有一个要派发在 MessagePort
上的事件时,
或者这个 MessagePort
对象的
端口消息队列 处于启用状态且非空时,
MessagePort
对象不得被垃圾回收。
强烈鼓励作者显式地关闭 MessagePort
对象来解关联它们,
这样它们的资源可以被重新回收。
创建很多歌 MessagePort
对象并直接丢弃它们而不关闭可能导致瞬时内存使用率高,
因为垃圾收集不一定能够及时执行,尤其是对于垃圾收集可能涉及跨进程协调的
MessagePort
而言。
同一个用户,同一个用户代理,同一个 域, 但不同 浏览上下文 的页面有时需要相互发送通知, 例如 “嘿,用户在我这里登录了,重新检查你的信任状态”。
对于复杂情况,例如管理共享状态的锁定,管理服务器和多个本地客户端之间的资源同步,
共享与远程主机的 WebSocket
连接等等,
共享 Worker 是最合适的解决方案。
但对于简单情况,共享 Worker 可能是不合理的开销, 作者可以使用这一章描述的简单的基于通道的广播机制。
[Constructor(DOMString name), Exposed=(Window,Worker)] interface BroadcastChannel : EventTarget { readonly attribute DOMString name; void postMessage(any message); void close(); attribute EventHandler onmessage; attribute EventHandler onmessageerror; };
BroadcastChannel
(name)BroadcastChannel/BroadcastChannel
Support in all current engines.
返回一个新的 BroadcastChannel
对象,通过它可以像指定的通道名发送和接收消息。
name
Support in all current engines.
返回通道名(传递给构造函数的)。
postMessage
(message)Support in all current engines.
向为这个通道建立的 BroadcastChannel
对象发送给定的消息。
消息可以是结构化对象,例如嵌套对象和数组。
close
()Support in all current engines.
关闭 BroadcastChannel
对象,让它可以被垃圾回收。
BroadcastChannel
对象有一个 通道名,
一个 BroadcastChannel
设置对象,
以及一个 关闭标志。
BroadcastChannel()
构造函数被调用时,
必须创建和返回一个 BroadcastChannel
对象,
其 通道名 为构造函数的第一个参数,
其 BroadcastChannel
设置对象
为 当前设置对象,
其 关闭标志 为 false。
name
属性必须返回
通道名。
postMessage(message)
方法在
BroadcastChannel
对象上被调用时,必须执行以下步骤:
令 source 为这个 BroadcastChannel
。
令 sourceSettings 为 source 的
BroadcastChannel
设置对象。
如果 source 的 关闭标志
为 true,则抛出一个 "InvalidStateError
" DOMException
。
令 sourceChannel 为 source 的 通道名。
令 targetRealm 为用户代理定义的 Realm。
令 serialized 为 StructuredSerialize(message)。 重新抛出任何异常。
令 destinations 为符合以下要求的 BroadcastChannel
对象的列表:
其 BroadcastChannel
设置对象 指定了以下之一:
一个 WorkerGlobalScope
全局对象
且它的 正则关闭 标志为 false,
且它的 worker 不是一个 可终止的 Worker。
它们的 BroadcastChannel
设置对象 的 源 与
sourceSettings 的 源
同源。
它们的 通道名 大消息敏感地匹配sourceChannel。
它们的 已关闭标志 为 false。
从 destinations 移除 source。
将 destinations 排序,使得所有
BroadcastChannel
设置对象
指定了同样
负责事件循环 的
BroadcastChannel
对象按照创建顺序排序,老的在先。
(这并没有定义完全排序。在这个约束下,用户代理可以按任意方式排序)
对 destinations 中的每一个 BroadcastChannel
对象 destination,
排一个任务 执行以下步骤:
令 targetRealm 为 destination 的 相关 Realm。
令 data 为 StructuredDeserialize(serialized, targetRealm)。
如果抛出了异常,捕获它,在 destination 上使用 MessageEvent
发生 一个名为
messageerror
的事件,
origin
属性初始化为
sourceSettings 的 origin 的
序列化,然后返回。
在 destination 上使用 MessageEvent
发生 一个名为 message
的事件,其 data
属性初始化为 data,
origin
属性初始化为
sourceSettings 的 origin 的
序列化。
任务 必须使用
DOM 操作任务源,
而且对于那些目标 BroadcastChannel
对象的
BroadcastChannel
设置对象
指定的 事件循环
是一个 浏览上下文
事件循环 的任务,必须与
那个目标的 BroadcastChannel
对象的
BroadcastChannel
设置对象
指定的 负责文档 相关联。
当 已关闭标志 为 false 的
BroadcastChannel
对象还有事件处理器注册在 message
事件上时,BroadcastChannel
对象的
BroadcastChannel
设置对象
指定的 全局对象 上必须有一个强引用指向
BroadcastChannel
对象自己。
close()
方法必须
把调用它的 BroadcastChannel
对象的
已关闭标志
设置为 true。
强烈鼓励作者在不需要它们时,显式地关闭 BroadcastChannel
对象,
这样它们就可以被垃圾回收了。
创建很多 BroadcastChannel
对象并在留有一个事件监听器时抛弃但不关闭他们,
会导致明显的内存泄露,因为只要它们有事件处理器(或页面或 Worker 别关闭),这些对象就会继续活着。
下面的例子是所有实现 BroadcastChannel
接口的对象
必须
以 事件处理器 IDL 属性 方式支持的
事件处理器
(以及对应的 事件处理器事件类型):
事件处理器 | 事件处理器事件类型 |
---|---|
onmessage BroadcastChannel/message_event Support in all current engines. Firefox38+Safari15.4+Chrome54+ Opera41+Edge79+ Edge (Legacy)NoInternet ExplorerNo Firefox Android38+Safari iOS15.4+Chrome Android54+WebView Android54+Samsung Internet6.0+Opera Android41+ | message
|
onmessageerror BroadcastChannel/messageerror_event Support in all current engines. Firefox57+Safari15.4+Chrome60+ Opera47+Edge79+ Edge (Legacy)NoInternet ExplorerNo Firefox Android57+Safari iOS15.4+Chrome Android60+WebView Android60+Samsung Internet8.0+Opera Android47+ | messageerror
|
假设一个页面想要知道用户登出,包括从其他标签页登出:
var authChannel = new BroadcastChannel('auth'); authChannel.onmessage = function (event) { if (event.data == 'logout') showLogout(); } function logoutRequested() { // called when the user asks us to log them out doLogout(); showLogout(); authChannel.postMessage('logout'); } function doLogout() { // actually log the user out (e.g. clearing cookies) // ... } function showLogout() { // update the UI to indicate we're logged out // ... }