实时标准 — 最后更新 2024 年 9 月 12 日
所有当前引擎都支持。
出于安全和隐私原因,Web 浏览器会阻止不同域中的文档相互影响;也就是说,不允许跨站点脚本攻击。
虽然这是一个重要的安全功能,但它会阻止来自不同域的页面进行通信,即使这些页面并不具有恶意。本节介绍了一种消息传递系统,允许文档相互通信,而无论其源域是什么,并且这种方式旨在防止跨站点脚本攻击。
postMessage()
API 可以用作跟踪向量。
本节是非规范性的。
例如,如果文档 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 参数中使用通配符(*),否则无法保证消息仅被传递给其预期接收方。
接受来自任何来源的消息的作者应考虑拒绝服务攻击的风险。攻击者可以发送大量消息;如果接收页面对每条此类消息执行昂贵的计算或导致网络流量发送,则攻击者的消息可能会被放大为拒绝服务攻击。建议作者采用限速机制(每分钟只接受一定数量的消息),使此类攻击变得不切实际。
此 API 的完整性基于一个起源的脚本无法向其他起源(非相同起源)中的对象发布任意事件(使用dispatchEvent()
或其他方式)。
强烈建议实施者在实施此功能时格外小心。它允许作者将信息从一个域传输到另一个域,这通常出于安全原因是不允许的。它还要求 UA 谨慎地允许访问某些属性,但不允许访问其他属性。
建议用户代理考虑限制不同起源之间的消息流量,以保护天真的网站免受拒绝服务攻击。
window.postMessage(message [, options ])
所有当前引擎都支持。
将消息发布到给定的窗口。消息可以是结构化对象,例如嵌套对象和数组,可以包含 JavaScript 值(字符串、数字、Date
对象等),并且可以包含某些数据对象,例如File
Blob
、FileList
和ArrayBuffer
对象。
在options 的transfer
成员中列出的对象将被传输,而不仅仅是克隆,这意味着它们在发送方将不再可用。
可以使用options 的targetOrigin
成员指定目标起源。如果未提供,则默认为“/
”。此默认值将消息限制为仅相同起源的目标。
如果目标窗口的起源与给定的目标起源不匹配,则消息将被丢弃,以避免信息泄露。要将消息发送到目标,而不管起源如何,请将目标起源设置为“*
”。
如果transfer 数组包含重复对象,或者message 无法克隆,则抛出"DataCloneError
" DOMException
。
window.postMessage(message, targetOrigin [, transfer ])
这是postMessage()
的另一个版本,其中目标起源作为参数指定。调用window.postMessage(message, target, transfer)
等效于window.postMessage(message, {targetOrigin, transfer})
。
当将消息发布到刚刚导航到新Document
的浏览上下文 的Window
时,消息可能无法接收其预期接收方:目标浏览上下文 中的脚本必须有时间为消息设置监听器。因此,例如,在将消息发送到新创建的子iframe
的Window
的情况下,建议作者让子Document
向其父级发布消息,宣布他们已准备好接收消息,并让父级等待此消息,然后再开始发布消息。
给定targetWindow、message 和options 的窗口发布消息步骤 如下所示
令targetRealm 为targetWindow 的领域。
令incumbentSettings 为当前设置对象。
令targetOrigin 为options["targetOrigin
"]。
如果targetOrigin 是单个 U+002F 斜杠字符 (/),则将targetOrigin 设置为incumbentSettings 的起源。
否则,如果targetOrigin 不是单个 U+002A 星号字符 (*),则
令parsedURL 为对targetOrigin 运行URL 解析器 的结果。
如果parsedURL 为失败,则抛出"SyntaxError
" DOMException
。
将targetOrigin 设置为parsedURL 的起源。
令transfer 为options["transfer
"]。
令serializeWithTransferResult 为StructuredSerializeWithTransfer(message, transfer)。重新抛出任何异常。
在给定targetWindow 的发布的消息任务源 上排队一个全局任务,以运行以下步骤
如果targetOrigin 参数不是单个字面量 U+002A 星号字符 (*),并且targetWindow 的关联的 Document
的起源 与targetOrigin 不是相同起源,则返回。
令source 为对应于incumbentSettings 的全局对象(一个Window
对象)的WindowProxy
对象。
令deserializeRecord 为StructuredDeserializeWithTransfer(serializeWithTransferResult, targetRealm)。
如果这抛出一个异常,捕获它,触发一个事件,名为 messageerror
,在 targetWindow 上,使用 MessageEvent
,其 origin
属性初始化为 origin,source
属性初始化为 source,然后返回。
令 messageClone 为 deserializeRecord.[[Deserialized]]。
令 newPorts 为一个新的 冻结数组,它包含 deserializeRecord.[[TransferredValues]] 中的所有 MessagePort
对象(如果有),并保持它们之间的相对顺序。
触发一个事件,名为 message
,在 targetWindow 上,使用 MessageEvent
,其 origin
属性初始化为 origin,source
属性初始化为 source,data
属性初始化为 messageClone,ports
属性初始化为 newPorts。
Window
接口的 postMessage(message, options)
方法步骤是运行给定 this、message 和 options 的 window post message 步骤。
Window
接口的 postMessage(message, targetOrigin, transfer)
方法步骤是运行给定 this、message 和 «[ "targetOrigin
" → targetOrigin, "transfer
" → transfer ]» 的 window post message 步骤。
所有当前引擎都支持。
Channel_Messaging_API/Using_channel_messaging
所有当前引擎都支持。
本节是非规范性的。
为了使独立的代码片段(例如,在不同的 浏览上下文中运行)能够直接通信,作者可以使用 通道消息传递。
此机制中的通信通道实现为双向管道,每端都有一个端口。在一个端口发送的消息将被传递到另一个端口,反之亦然。消息作为 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' ]);
本节是非规范性的。
在本例中,两个 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 >
"addContactsProvider()" 函数的实现可能如下所示
function addContactsProvider( port) {
port. onmessage = function ( event) {
switch ( event. data. messageType) {
case 'search-result' : handleSearchResult( event. data. results); break ;
case 'search-done' : handleSearchDone(); break ;
case '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()
。
start()
方法(无论是显式调用还是隐式调用(通过设置 onmessage
))启动消息流:最初暂停在消息端口发布的消息,这样在脚本有机会设置其处理程序之前,这些消息不会被丢弃。
本节是非规范性的。
端口可以被视为一种向系统中其他参与者公开有限能力(从对象能力模型意义上讲)的方式。这可以是一个弱能力系统,其中端口仅仅用作特定原点内的便捷模型,也可以是一个强能力系统,其中端口由一个原点 provider 提供,作为另一个原点 consumer 影响 provider 中的更改或从 provider 获取信息的唯一机制。
例如,考虑一个社交网站在其中嵌入用户电子邮件联系人提供程序(一个地址簿网站,来自第二个原点)的一个 iframe
,并在第二个 iframe
中嵌入一个游戏(来自第三个原点)。外部社交网站和第二个 iframe
中的游戏无法访问第一个 iframe
中的任何内容;它们只能一起
导航 iframe
到新的 URL,例如同一个 URL,但具有不同的 片段,导致 Window
在 iframe
中接收 hashchange
事件。
使用 window.postMessage()
API 向 iframe
中的 Window
发送 message
事件。
联系人提供程序可以使用这些方法,尤其是第三种方法,来提供一个 API,其他原点可以访问该 API 以操作用户的地址簿。例如,它可以响应消息“add-contact Guillaume Tell <[email protected]>
”,通过添加给定的人员和电子邮件地址到用户的地址簿。
为了避免 Web 上的任何网站都能操作用户的联系人,联系人提供程序可能只允许某些受信任的网站(例如社交网站)这样做。
现在假设游戏想要添加一个联系人到用户的地址簿,并且社交网站愿意代表游戏这样做,本质上是“分享”联系人提供程序与社交网站之间的信任。它可以做到这一点有几种方法;最简单的方法是,它可以简单地代理游戏网站和联系人网站之间的消息。但是,这种解决方案有许多困难:它要求社交网站要么完全信任游戏网站不会滥用特权,要么要求社交网站验证每个请求以确保它不是它不想允许的请求(例如添加多个联系人、读取联系人或删除它们);如果有多个游戏同时尝试与联系人提供程序交互,它还需要一些额外的复杂性。
但是,使用消息通道和 MessagePort
对象,所有这些问题都可以消失。当游戏告诉社交网站它想要添加一个联系人时,社交网站可以请求联系人提供程序不是为它添加联系人,而是请求添加单个联系人的能力。然后,联系人提供程序创建一对 MessagePort
对象,并将其中一个对象发送回社交网站,社交网站将其转发到游戏。然后游戏和联系人提供程序之间建立了直接连接,并且联系人提供程序知道只接受一个“添加联系人”请求,而不是其他任何请求。换句话说,游戏被授予了添加单个联系人的能力。
本节是非规范性的。
继续上一节中的例子,尤其是联系人提供程序。虽然初始实现可能只是在服务 iframe
中使用了 XMLHttpRequest
对象,但服务的演变可能希望使用一个具有单个 WebSocket
连接的 共享工作线程。
如果初始设计使用 MessagePort
对象来授予功能,甚至只是为了允许多个同时独立的会话,服务实现可以从 XMLHttpRequest
s-in-each-iframe
模型切换到共享 WebSocket
模型,而无需更改 API:服务提供者端的所有端口都可以转发到共享工作者,而不会对 API 用户造成任何影响。
所有当前引擎都支持。
[Exposed =(Window ,Worker )]
interface MessageChannel {
constructor ();
readonly attribute MessagePort port1 ;
readonly attribute MessagePort port2 ;
};
channel = new MessageChannel()
所有当前引擎都支持。
返回一个新的 MessageChannel
对象,该对象包含两个新的 MessagePort
对象。
channel.port1
所有当前引擎都支持。
返回第一个 MessagePort
对象。
channel.port2
所有当前引擎都支持。
返回第二个 MessagePort
对象。
一个 MessageChannel
对象具有关联的 端口 1 和关联的 端口 2,它们都是 MessagePort
对象。
new MessageChannel()
构造函数的步骤如下:
所有当前引擎都支持。
每个通道有两个消息端口。通过一个端口发送的数据由另一个端口接收,反之亦然。
[Exposed =(Window ,Worker ,AudioWorklet ), Transferable ]
interface MessagePort : EventTarget {
undefined postMessage (any message , sequence <object > transfer );
undefined postMessage (any message , optional StructuredSerializeOptions options = {});
undefined start ();
undefined close ();
// event handlers
attribute EventHandler onmessage ;
attribute EventHandler onmessageerror ;
attribute EventHandler onclose ;
};
dictionary StructuredSerializeOptions {
sequence <object > transfer = [];
};
port.postMessage(message [, transfer])
所有当前引擎都支持。
port.postMessage(message [, { transfer }])
通过通道发布消息。列在 transfer 中的对象会被传输,而不仅仅是克隆,这意味着它们在发送端不再可用。
如果 transfer 包含重复对象或 port,或者如果 message 无法克隆,则抛出 "DataCloneError
" DOMException
。
port.start()
所有当前引擎都支持。
开始调度在端口上接收到的消息。
port.close()
所有当前引擎都支持。
断开端口连接,使其不再处于活动状态。
每个 MessagePort
对象可以与另一个对象纠缠(对称关系)。每个 MessagePort
对象还具有一个名为 任务源 的 端口消息队列,最初为空。端口消息队列可以启用或禁用,并且最初为禁用状态。启用后,端口永远无法再次禁用(虽然队列中的消息可以移动到另一个队列或完全删除,这具有相同的效果)。一个 MessagePort
还具有一个 已发货 标志,最初必须为 false。
当端口的 端口消息队列 启用时,事件循环 必须将其用作其 任务源 之一。当端口的 相关全局对象 是 Window
时,在它的 端口消息队列 上排队的 任务 必须与端口的 相关全局对象 的 关联的 Document
相关联。
如果文档处于 完全活动 状态,但所有事件监听器都在不处于 完全活动 状态的文档的上下文中创建,则除非文档再次处于 完全活动 状态,否则不会收到消息。
每个 事件循环 都具有一个名为 未发货端口消息队列 的 任务源。这是一个虚拟 任务源:它必须表现得好像它包含了每个 端口消息队列 中的 任务,这些队列属于 MessagePort
,其 已发货 标志为 false,其 端口消息队列 已启用,并且其 相关代理 的 事件循环 是该 事件循环,顺序与它们添加到各自 任务源 中的顺序相同。当 任务 将从 未发货端口消息队列 中移除时,它必须改为从其 端口消息队列 中移除。
当 MessagePort
的 已发货 标志为 false 时,其 端口消息队列 必须为 事件循环 的目的而被忽略。(未发货端口消息队列 替代使用。)
当端口、它的孪生兄弟或它被克隆的对象被传输或已被传输时,已发货 标志被设置为 true。当 MessagePort
的 已发货 标志为 true 时,其 端口消息队列 充当一流的 任务源,不受任何 未发货端口消息队列 的影响。
当用户代理要 纠缠 两个 MessagePort
对象时,它必须运行以下步骤:
如果其中一个端口已经纠缠,则解开它与其纠缠的端口。
如果这两个先前纠缠的端口是 MessageChannel
对象的两个端口,则该 MessageChannel
对象不再表示实际的通道:该对象中的两个端口不再纠缠。
将要纠缠的两个端口关联起来,使它们形成一个新通道的两个部分。(没有 MessageChannel
对象表示此通道。)
经过此步骤的两个端口 A 和 B 现在被称为纠缠;一个与另一个纠缠,反之亦然。
虽然本规范将此过程描述为瞬时的,但实现更有可能通过消息传递来实现它。与所有算法一样,关键是“仅仅”最终结果在黑盒意义上与规范不可区分。
给定一个 MessagePort
initiatorPort,它启动解开纠缠,解开纠缠 步骤如下:
令 otherPort 为与 initiatorPort 纠缠的 MessagePort
。
断言:otherPort 存在。
解开 initiatorPort 和 otherPort 的纠缠,使它们不再纠缠或彼此关联。
即使端口未明确关闭,close
事件也会被触发。触发此事件的案例包括:
close()
方法;Document
被 销毁;或者MessagePort
被 垃圾回收。我们只在 otherPort 上触发事件,因为 initiatorPort 明确触发了关闭,它的 Document
不再存在,或者它已经被垃圾回收,如上所述。
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
事件的 任务 移动到 value 的 端口消息队列(如果有),将 value 的 端口消息队列 保持在初始禁用状态,并且,如果 value 的 相关全局对象 是一个 Window
,则将移动的 任务 与 value 的 相关全局对象 的 关联的 Document
关联起来。
如果 dataHolder.[[RemotePort]] 不为 null,则将 dataHolder.[[RemotePort]] 和 value 纠缠。(这将使 dataHolder.[[RemotePort]] 与最初被传输的端口解缠。)
给定 sourcePort、targetPort、message 和 options 的 端口消息发布步骤 如下
令 transfer 为 options["transfer
"]。
如果 transfer 包含 sourcePort,则抛出一个 "DataCloneError
" DOMException
。
令 doomed 为 false。
如果 targetPort 不为 null 且 transfer 包含 targetPort,则将 doomed 设置为 true,并可选地向开发者控制台报告目标端口已发布到自身,导致通信通道丢失。
令 serializeWithTransferResult 为 StructuredSerializeWithTransfer(message, transfer)。重新抛出任何异常。
如果 targetPort 为 null 或 doomed 为 true,则返回。
向 targetPort 的 端口消息队列 添加一个运行以下步骤的 任务
令 finalTargetPort 为现在在其中找到任务的 MessagePort
的 端口消息队列。
如果 targetPort 本身已被传输,因此所有任务都随之移动,则这可能与 targetPort 不同。
令 targetRealm 为 finalTargetPort 的 相关领域。
令 deserializeRecord 为 StructuredDeserializeWithTransfer(serializeWithTransferResult, targetRealm)。
如果这抛出一个异常,则捕获它,使用 MessageEvent
在 finalTargetPort 上 触发一个名为 messageerror
的事件,然后返回。
令 messageClone 为 deserializeRecord.[[Deserialized]]。
令 newPorts 为一个新的 冻结数组,它包含 deserializeRecord.[[TransferredValues]] 中的所有 MessagePort
对象(如果有),并保持其相对顺序。
使用 MessageEvent
在 finalTargetPort 上 触发一个名为 message
的事件,其 data
属性初始化为 messageClone,ports
属性初始化为 newPorts。
给定 postMessage(message, options)
方法的步骤为
给定 postMessage(message, transfer)
方法的步骤为
给定 start()
方法的步骤为:启用 this 的 端口消息队列(如果它尚未启用)。
给定 close()
方法的步骤为
将 this 的 [[Detached]] 内部插槽值设置为 true。
以下是所有实现 MessagePort
接口的对象必须支持的 事件处理程序(及其相应的 事件处理程序事件类型),作为 事件处理程序 IDL 属性
事件处理程序 | 事件处理程序事件类型 |
---|---|
onmessage 所有当前引擎都支持。 Firefox41+Safari5+Chrome2+ Opera10.6+Edge79+ Edge(传统)12+Internet Explorer10+ Firefox Android?Safari iOS?Chrome Android?WebView Android37+Samsung Internet?Opera Android11.5+ | message
|
onmessageerror MessagePort/messageerror_event 所有当前引擎都支持。 Firefox57+Safari16.4+Chrome60+ Opera?Edge79+ Edge (Legacy)18Internet ExplorerNo Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android47+ | messageerror
|
onclose | close
|
第一次设置 MessagePort
对象的 onmessage
IDL 属性时,必须启用端口的 端口消息队列,就好像已调用 start()
方法一样。
当 MessagePort
对象 o 被垃圾回收时,如果 o 纠缠,则用户代理必须 解缠 o。
当 MessagePort
对象 o 纠缠且注册了 message
或 messageerror
事件监听器时,用户代理必须表现得好像 o 的纠缠的 MessagePort
对象对 o 具有强引用。
此外,如果在 任务队列 中存在一个由要在该 MessagePort
对象上分派的 任务 引用的事件,或者 MessagePort
对象的 端口消息队列 已启用且不为空,则 MessagePort
对象不得被垃圾回收。
因此,在给定一个事件监听器的情况下,可以接收一个消息端口,然后将其遗忘,只要该事件监听器可以接收消息,该通道就会保持。
当然,如果这种情况发生在通道的双方,那么两个端口都可能被垃圾回收,因为它们将无法从实时代码中访问,尽管它们彼此具有强引用。但是,如果一个消息端口有一个待处理的消息,则它不会被垃圾回收。
强烈建议作者显式关闭 MessagePort
对象以将其解缠,以便可以回收其资源。创建许多 MessagePort
对象并将其丢弃而不关闭它们会导致高瞬态内存使用率,因为垃圾回收不一定立即执行,特别是对于 MessagePort
,其中垃圾回收可能涉及跨进程协调。
所有当前引擎都支持。
所有当前引擎都支持。
在同一个用户代理中,由同一用户打开的同一个 源 上的页面,但在不同的无关 浏览上下文 中,有时需要互相发送通知,例如“嘿,用户在这里登录了,请重新检查您的凭据”。
对于复杂的用例,例如管理共享状态的锁定、管理服务器和多个本地客户端之间的资源同步、与远程主机共享 WebSocket
连接,等等,共享工作线程 是最合适的解决方案。
但是,对于简单的用例,使用共享工作线程会造成不必要的开销,作者可以使用本节中描述的简单通道广播机制。
[Exposed =(Window ,Worker )]
interface BroadcastChannel : EventTarget {
constructor (DOMString name );
readonly attribute DOMString name ;
undefined postMessage (any message );
undefined close ();
attribute EventHandler onmessage ;
attribute EventHandler onmessageerror ;
};
broadcastChannel = new BroadcastChannel(name)
BroadcastChannel/BroadcastChannel
所有当前引擎都支持。
返回一个新的 BroadcastChannel
对象,通过该对象可以发送和接收给定通道名称的消息。
broadcastChannel.name
所有当前引擎都支持。
返回通道名称(与构造函数中传递的名称相同)。
broadcastChannel.postMessage(message)
所有当前引擎都支持。
将给定的消息发送到为该通道设置的其它 BroadcastChannel
对象。消息可以是结构化对象,例如嵌套对象和数组。
broadcastChannel.close()
所有当前引擎都支持。
关闭 BroadcastChannel
对象,使其可以被垃圾回收。
一个 BroadcastChannel
对象具有一个 通道名称 和一个 关闭标志。
构造函数 new BroadcastChannel(name)
的步骤是
当 BroadcastChannel
对象的 相关全局对象 为以下两种情况时,则称该对象为 可进行消息传递的:
一个 Window
对象,其 关联的 Document
是 完全活动的;或者
一个 WorkerGlobalScope
对象,其 关闭 标志为 false 且其 工作线程 不是 可挂起的 工作线程。
方法 postMessage(message)
的步骤是
如果 this 的 关闭标志 为 true,则抛出 "InvalidStateError
" DOMException
。
令 serialized 为使用 StructuredSerialize(message) 获得的值。重新抛出任何异常。
令 sourceStorageKey 为使用 为非存储目的获取存储密钥 方法,并使用 this 的 相关设置对象 获得的值。
令 destinations 为匹配以下条件的 BroadcastChannel
对象列表:
从 destinations 中移除 source。
对 destinations 进行排序,使所有 相关代理 相同的 BroadcastChannel
对象按创建顺序排序,最早的排在最前面。(这并没有定义一个完整的排序。在这个约束下,用户代理可以以任何 实现定义 的方式对列表进行排序。)
对于 destinations 中的每一个 destination,在全局任务队列 上,使用给定 destination 的 相关全局对象 的 DOM 操作任务源,执行以下步骤:
如果 destination 的 关闭标志 为 true,则中止这些步骤。
令 targetRealm 为 destination 的 相关领域。
令 data 为使用 StructuredDeserialize(serialized, targetRealm) 获得的值。
如果这会抛出异常,则捕获该异常,在 destination 上触发名为 messageerror
的事件,使用 MessageEvent
,将 origin
属性初始化为 sourceOrigin 的 序列化,然后中止这些步骤。
在 destination 上触发名为 message
的事件,使用 MessageEvent
,将 data
属性初始化为 data,将 origin
属性初始化为 sourceOrigin 的 序列化。
当 BroadcastChannel
对象的 关闭标志 为 false 且注册了 message
或 messageerror
事件的事件监听器时,BroadcastChannel
对象的 相关全局对象 必须对 BroadcastChannel
对象本身有一个强引用。
方法 close()
的步骤是将 this 的 关闭标志 设置为 true。
强烈建议作者在不再需要 BroadcastChannel
对象时显式关闭它们,以便它们可以被垃圾回收。创建许多 BroadcastChannel
对象并丢弃它们,同时保留它们的事件监听器而不关闭它们,会导致明显的内存泄漏,因为这些对象将一直存活,直到它们拥有一个事件监听器(或直到它们的页面或工作线程被关闭)。
以下列出了所有实现 BroadcastChannel
接口的对象必须支持的 事件处理程序(及其对应的 事件处理程序事件类型),作为 事件处理程序 IDL 属性:
事件处理程序 | 事件处理程序事件类型 |
---|---|
onmessage BroadcastChannel/message_event 所有当前引擎都支持。 Firefox38+Safari15.4+Chrome54+ Opera?Edge79+ Edge (Legacy)?Internet ExplorerNo Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android? | message
|
onmessageerror BroadcastChannel/messageerror_event 所有当前引擎都支持。 Firefox57+Safari15.4+Chrome60+ Opera?Edge79+ Edge (Legacy)?Internet ExplorerNo Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?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
// ...
}