1. 9.3 異なる文書間のメッセージング
      1. 9.3.1 導入
      2. 9.3.2 セキュリティ
        1. 9.3.2.1 著者
        2. 9.3.2.2 User agents
      3. 9.3.3 メッセージのポスト
    2. 9.4 チャンネルメッセージング
      1. 9.4.1 導入
        1. 9.4.1.1
        2. 9.4.1.2 ウェブ上のオブジェクト機能モデルの基礎としてのポート
        3. 9.4.1.3 サービス実装を抽象化する基礎としてのポート
      2. 9.4.2 メッセージチャンネル
      3. 9.4.3 MessageEventTargetミックスイン
      4. 9.4.4 メッセージポート
      5. 9.4.5 ポートおよびガベージコレクション
    3. 9.5 Broadcasting to other browsing contexts

9.3 異なる文書間のメッセージング

Window/postMessage

Support in all current engines.

Firefox3+Safari4+Chrome2+
Opera9.5+Edge79+
Edge (Legacy)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android≤37+Samsung Internet?Opera Android10.1+

ウェブブラウザーは、セキュリティおよびプライバシー上の理由から、異なるドメインの文書が相互に影響を与えないようにしている。つまり、クロスサイトスクリプティングは許可されてない。

これは重要なセキュリティ機能であるが、たとえ敵対的でないページであっても、異なるドメインにあるそれらのページの通信を妨げる。このセクションでは、クロスサイトスクリプティング攻撃を可能にしないように設計された、ソースドメインに関係なく文書が相互に通信を可能にするメッセージングシステムを紹介する。

postMessage() APIは、トラッキングベクターとして使用できる。

9.3.1 導入

この節は非規範的である。

たとえば、文書Aに文書Bを含むiframe要素が含まれ、文書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);
    }
  }
}

このスクリプトは、ドメインが予想されるドメインであることを最初に確認し、次にメッセージを調べる。メッセージは、ユーザーに表示されるまたは、最初にメッセージを送信した文書にメッセージを送り返すことによって応答される。

9.3.2 セキュリティ

9.3.2.1 著者

このAPIを使用するには、サイトを悪用する悪意のある組織からユーザーを保護するために、特別な注意が必要である。

著者は、origin属性をチェックし、メッセージを受信すると予想されるドメインからのみメッセージが受け入れられるようにすべきである。さもなければ、著者のメッセージ処理コードのバグが悪意のあるサイトによって悪用される可能性がある。

さらに、origin属性をチェックした後でも、著者は問題のデータが期待されたフォーマットであることもチェックすべきである。さもなければ、イベントのソースがクロスサイトスクリプティングの欠陥を使用して攻撃された場合、 postMessage()メソッドを使用して送信された情報の処理がさらにチェックされないことで、攻撃が受信側に伝播する可能性がある。

著者は、機密情報を含むメッセージのtargetOrigin 引数にワイルドカードキーワード(*)を使用すべきではない。さもなければ、メッセージが意図した受信者にのみ配信されることを保証できなくなる。


あらゆる生成元からのメッセージを受け入れる著者は、サービス拒否攻撃のリスクを考慮することが奨励される。攻撃者は大量のメッセージを送信する可能性がある。受信ページがコストのかかる計算を実行したり、そのようなメッセージごとにネットワークトラフィックが送信されたりすると、攻撃者のメッセージはサービス拒否攻撃に増幅される可能性がある。著者は、そのような攻撃を非現実的にするために、レート制限(1分あたり特定の数のメッセージのみを受け入れる)を採用することが奨励される。

9.3.2.2 User agents

The integrity of this API is based on the inability for scripts of one origin to post arbitrary events (using dispatchEvent() or otherwise) to objects in other origins (those that are not the same).

Implementers are urged to take extra care in the implementation of this feature. It allows authors to transmit information from one domain to another domain, which is normally disallowed for security reasons. It also requires that UAs be careful to allow access to certain properties but not others.


User agents are also encouraged to consider rate-limiting message traffic between different origins, to protect naïve sites from denial-of-service attacks.

9.3.3 メッセージのポスト

window.postMessage(message [, options ])

Window/postMessage

Support in all current engines.

Firefox3+Safari4+Chrome2+
Opera9.5+Edge79+
Edge (Legacy)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android37+Samsung Internet?Opera Android10.1+

指定されたウィンドウにメッセージをポストする。メッセージは、ネストされたオブジェクト、配列などの構造化されたオブジェクトにすることができ、JavaScriptの値(文字列、数値、Dateオブジェクトなど)を含めることができ、 File BlobFileListArrayBufferオブジェクトなどの特定のデータオブジェクトを含めることができる。

optionstransferメンバーにリストされているオブジェクトは、複製されるだけでなく、転送される。つまり、送信側では使用できなくなる。

ターゲットの生成元は、optionstargetOriginメンバーを使用して指定できる。指定しない場合、デフォルトの"/"になる。このデフォルトでは、メッセージは生成元が同じターゲットだけに制限される。

ターゲットウィンドウの生成元が指定されたターゲットの生成元とマッチしない場合、情報の漏洩を避けるためにメッセージは破棄される。生成元に関係なく宛先にメッセージを送信するには、ターゲットの生成元を"*"に設定する。

transfer配列に重複するオブジェクトが含まれている場合、またはmessageをクローンできなかった場合、"DataCloneError" DOMExceptionを投げる。

window.postMessage(message, targetOrigin [, transfer ])

これはpostMessage()の代替バージョンで、ターゲットの生成元がパラメータとして指定される。window.postMessage(message, target, transfer)の呼び出しは、window.postMessage(message, {targetOrigin, transfer})と同じである。

新しいDocumentにナビゲートされたばかりのブラウジングコンテキストWindowにメッセージを投稿するとき、メッセージが意図した受信者を受信しない可能性がある。ターゲットブラウジングコンテキストのスクリプトには、メッセージのリスナーを設定する時間が必要である。したがって、たとえば、メッセージが新しく作成された子iframeWindowに送信される状況では、著者は、子<Documentにメッセージを受信する準備ができていることを知らせるメッセージを親に投稿させ、親がこのメッセージを待ってからメッセージの投稿を開始するようにアドバイスされる。

The window post message steps, given a targetWindow, message, and options, are as follows:

  1. Let targetRealm be targetWindow's realm.

  2. Let incumbentSettings be the incumbent settings object.

  3. Let targetOrigin be options["targetOrigin"].

  4. If targetOrigin is a single U+002F SOLIDUS character (/), then set targetOrigin to incumbentSettings's origin.

  5. Otherwise, if targetOrigin is not a single U+002A ASTERISK character (*), then:

    1. Let parsedURL be the result of running the URL parser on targetOrigin.

    2. If parsedURL is failure, then throw a "SyntaxError" DOMException.

    3. Set targetOrigin to parsedURL's origin.

  6. Let transfer be options["transfer"].

  7. Let serializeWithTransferResult be StructuredSerializeWithTransfer(message, transfer). 例外を再度投げる。

  8. Queue a global task on the posted message task source given targetWindow to run the following steps:

    1. If the targetOrigin argument is not a single literal U+002A ASTERISK character (*) and targetWindow's associated Document's origin is not same origin with targetOrigin, then return.

    2. Let origin be the serialization of incumbentSettings's origin.

    3. Let source be the WindowProxy object corresponding to incumbentSettings's global object (a Window object).

    4. Let deserializeRecord be StructuredDeserializeWithTransfer(serializeWithTransferResult, targetRealm).

      If this throws an exception, catch it, fire an event named messageerror at targetWindow, using MessageEvent, with the origin attribute initialized to origin and the source attribute initialized to source, and then return.

    5. Let messageClone be deserializeRecord.[[Deserialized]].

    6. Let newPorts be a new frozen array consisting of all MessagePort objects in deserializeRecord.[[TransferredValues]], if any, maintaining their relative order.

    7. Fire an event named message at targetWindow, using MessageEvent, with the origin attribute initialized to origin, the source attribute initialized to source, the data attribute initialized to messageClone, and the ports attribute initialized to newPorts.

The Window interface's postMessage(message, options) method steps are to run the window post message steps given this, message, and options.

The Window interface's postMessage(message, targetOrigin, transfer) method steps are to run the window post message steps given this, message, and «[ "targetOrigin" → targetOrigin, "transfer" → transfer ]».

9.4 チャンネルメッセージング

Channel_Messaging_API

Support in all current engines.

Firefox41+Safari5+Chrome2+
Opera10.6+Edge79+
Edge (Legacy)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android11+

Channel_Messaging_API/Using_channel_messaging

Support in all current engines.

Firefox41+Safari5+Chrome2+
Opera10.6+Edge79+
Edge (Legacy)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android11+

9.4.1 導入

この節は非規範的である。

独立したコード部分(たとえば、異なるブラウジングコンテキストで実行される )が直接通信できるようにするために、著者はチャンネルメッセージングを使用することができる。

このメカニズムの通信チャンネルは、両端にポートを持つ双方向パイプとして実装される。一方のポートで送信されたメッセージはもう一方のポートに配信され、その逆も同様である。メッセージはDOMイベントとして配信され、実行タスクを中断したりブロックしたりすることはない。

接続(2つの"もつれた"ポート)を作成するには、MessageChannel()コンストラクターを呼び出す:

var channel = new MessageChannel();

ポートの1つはローカルポートとして保持され、もう1つのポートはリモートコードに送信される。たとえば、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']);
9.4.1.1

この節は非規範的である。

この例では、2つのJavaScriptライブラリがMessagePortを使用して相互に接続されている。これにより、APIを変更することなく、ライブラリを後で別のフレームまたはWorkerオブジェクトにホストできるようになる。

<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を設定することで)暗黙的に呼び出された場合でも、メッセージのフローを開始する。メッセージポートにポストされたメッセージは、スクリプトがハンドラーをセットアップする前に破棄されないように、最初は一時停止される。

9.4.1.2 ウェブ上のオブジェクト機能モデルの基礎としてのポート

この節は非規範的である。

ポートは、システム内の他の行為者に対して、限定された機能(オブジェクト機能モデルの意味で)を公開する手段と捉えることができる。これは、ポートが特定の生成元内での便利なモデルとしてのみ使用される弱い機能システムと、別の生成元の消費者プロバイダーに変更を加えたり情報を取得したりするための唯一の手段として、ある生成元のプロバイダーによって提供される強い機能モデルのいずれかである。

たとえば、ソーシャルウェブサイトが、あるiframeにユーザーのメール連絡先プロバイダー(別の生成元のアドレス帳サイト)を埋め込み、別のiframeにゲーム(別の生成元のゲーム)を埋め込んでいる状況を考えてみる。外側のソーシャルサイトと2番目のiframe内のゲームは、1つ目のiframe内のいかなる機能にもアクセスできない。つまり、これら2つのサイトでできることは次のとおりである:

連絡先プロバイダーはこれらのメソッド、特に3番目のメソッドを使用することで、他の生成元がユーザーのアドレス帳を操作できるAPIを提供できる。たとえば、"add-contact Guillaume Tell <tell@pomme.example.net>"というメッセージに応答し、指定された人物およびメールアドレスをユーザーのアドレス帳に追加することができる。

ウェブ上のどのサイトもユーザーの連絡先を操作できないようにするために、連絡先プロバイダーは、ソーシャルサイトなどの特定の信頼できるサイトのみにこの操作を許可する場合がある。

さて、ゲームがユーザーのアドレス帳に連絡先を追加したいと考えており、ソーシャルサイトがゲームに代わってその操作を許可し、連絡先プロバイダーがソーシャルサイトと築いていた信頼を「共有」することを望んだとする。これにはいくつかの方法があるが、最も単純な方法は、ゲームサイトと連絡先サイトとの間のメッセージをプロキシすることである。しかし、この解決策にはいくつかの難点がある。ソーシャルサイトは、ゲームサイトが権限を乱用しないことを完全に信頼するか、ソーシャルサイトが各リクエストを検証して、許可したくないリクエスト(複数の連絡先の追加、連絡先の閲覧、削除など)ではないことを確認する必要がある。また、複数のゲームが同時に連絡先プロバイダーとやり取りする可能性がある場合、複雑さが増す。

しかし、メッセージチャンネルおよびMessagePortオブジェクトを使用すれば、これらの問題はすべて解消する。ゲームがソーシャルサイトに連絡先を追加したいと伝えると、ソーシャルサイトは連絡先プロバイダーに、連絡先を1つ追加するのではなく、1つの連絡先を追加する機能を要求できる。連絡先プロバイダーはMessagePortオブジェクトのペアを作成し、そのうちの1つをソーシャルサイトに送り返し、ソーシャルサイトはそれをゲームに転送する。こうしてゲームと連絡先プロバイダーは直接接続され、連絡先プロバイダーは"連絡先の追加"リクエスト1件のみを承認すればよく、それ以外のリクエストは受け付けない。つまり、ゲームには1つの連絡先を追加する機能が付与されたことになる。

9.4.1.3 サービス実装を抽象化する基礎としてのポート

この節は非規範的である。

前のセクションからの例に続いて、特に連絡先プロバイダーについて考えてみる。初期の実装では、サービスのiframeで単にXMLHttpRequestオブジェクトを使用していたかもしれないが、サービスの進化により、代わりに単一のWebSocket接続で共有ワーカーを使用したい場合がある。

最初の設計でMessagePortオブジェクトを使用して機能を付与した場合、または単に複数の同時独立セッションを許可した場合でも、サービス実装は、APIをまったく変更することなく、 XMLHttpRequestの各iframeモデルから共有WebSocketモデルに切り替えることができる。サービスプロバイダー側のポートはすべて、APIのユーザーに少しも影響を与えることなく、共有ワーカーに転送できる。

9.4.2 メッセージチャンネル

MessageChannel

Support in all current engines.

Firefox41+Safari5+Chrome2+
Opera10.6+Edge79+
Edge (Legacy)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android11+
[Exposed=(Window,Worker)]
interface MessageChannel {
  constructor();

  readonly attribute MessagePort port1;
  readonly attribute MessagePort port2;
};
channel = new MessageChannel()

MessageChannel/MessageChannel

Support in all current engines.

Firefox41+Safari5+Chrome2+
Opera10.6+Edge79+
Edge (Legacy)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android11+

2つの新しいMessagePortオブジェクトをもつ新しいMessageChannelオブジェクトを返す。

channel.port1

MessageChannel/port1

Support in all current engines.

Firefox41+Safari5+Chrome2+
Opera10.6+Edge79+
Edge (Legacy)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android11+

1つ目のMessagePortオブジェクトを返す。

channel.port2

MessageChannel/port2

Support in all current engines.

Firefox41+Safari5+Chrome2+
Opera10.6+Edge79+
Edge (Legacy)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android11+

2つ目のMessagePortオブジェクトを返す。

A MessageChannel object has an associated port 1 and an associated port 2, both MessagePort objects.

The new MessageChannel() constructor steps are:

  1. Set this's port 1 to a new MessagePort in this's relevant realm.

  2. Set this's port 2 to a new MessagePort in this's relevant realm.

  3. Entangle this's port 1 and this's port 2.

The port1 getter steps are to return this's port 1.

The port2 getter steps are to return this's port 2.

9.4.3 MessageEventTargetミックスイン

interface mixin MessageEventTarget {
  attribute EventHandler onmessage;
  attribute EventHandler onmessageerror;
};

以下は、MessageEventTargetインターフェイスを実装するすべてのオブジェクトによって、イベントハンドラーIDL属性として、サポートされるイベントハンドラー(および対応するイベントハンドラーイベント型)である:

イベントハンドラーイベントハンドラーイベント型
onmessage

MessagePort/message_event

Support in all current engines.

Firefox41+Safari5+Chrome2+
Opera10.6+Edge79+
Edge (Legacy)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android37+Samsung Internet?Opera Android11.5+

DedicatedWorkerGlobalScope/message_event

Support in all current engines.

Firefox3.5+Safari4+Chrome4+
Opera10.6+Edge79+
Edge (Legacy)12+Internet Explorer10+
Firefox Android?Safari iOS5+Chrome Android?WebView Android37+Samsung Internet?Opera Android11.5+
message
onmessageerror

MessagePort/messageerror_event

Support in all current engines.

Firefox57+Safari16.4+Chrome60+
Opera?Edge79+
Edge (Legacy)18Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android47+

DedicatedWorkerGlobalScope/messageerror_event

Support in all current engines.

Firefox57+Safari16.4+Chrome60+
Opera?Edge79+
Edge (Legacy)18Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android47+
messageerror

9.4.4 メッセージポート

MessagePort

Support in all current engines.

Firefox41+Safari5+Chrome2+
Opera10.6+Edge79+
Edge (Legacy)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android11+

各チャネルには2つのメッセージポートがある。一方のポートから送信されたデータはもう一方のポートで受信され、その逆も同様である。

[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 onclose;
};

MessagePort includes MessageEventTarget;

dictionary StructuredSerializeOptions {
  sequence<object> transfer = [];
};
port.postMessage(message [, transfer])

MessagePort/postMessage

Support in all current engines.

Firefox41+Safari5+Chrome2+
Opera10.6+Edge79+
Edge (Legacy)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android11+
port.postMessage(message [, { transfer }])

チャンネルを通じてメッセージをポストする。Objects listed in transferにリストされたオブジェクトは、単に複製されるのではなく転送される。つまり、送信側では使用できなくなる。

transferが重複するオブジェクトもしくはportを含む場合、またはmessageを複製できなかった場合、"DataCloneError" DOMExceptionを投げる。

port.start()

MessagePort/start

Support in all current engines.

Firefox41+Safari5+Chrome2+
Opera10.6+Edge79+
Edge (Legacy)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android11+

ポートで受信したメッセージの発送を開始する。

port.close()

MessagePort/close

Support in all current engines.

Firefox41+Safari5+Chrome2+
Opera10.6+Edge79+
Edge (Legacy)12+Internet Explorer10+
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android11+

ポートを切断し、アクティブでなくなる。

Each MessagePort object has a message event target (a MessageEventTarget), to which the message and messageerror events are dispatched. Unless otherwise specified, it defaults to the MessagePort object itself.

Each MessagePort object can be entangled with another (a symmetric relationship). Each MessagePort object also has a task source called the port message queue, initially empty. A port message queue can be enabled or disabled, and is initially disabled. Once enabled, a port can never be disabled again (though messages in the queue can get moved to another queue or removed altogether, which has much the same effect). A MessagePort also has a has been shipped flag, which must initially be false.

When a port's port message queue is enabled, the event loop must use it as one of its task sources. When a port's relevant global object is a Window, all tasks queued on its port message queue must be associated with the port's relevant global object's associated Document.

If the document is fully active, but the event listeners were all created in the context of documents that are not fully active, then the messages will not be received unless and until the documents become fully active again.

Each event loop has a task source called the unshipped port message queue. This is a virtual task source: it must act as if it contained the tasks of each port message queue of each MessagePort whose has been shipped flag is false, whose port message queue is enabled, and whose relevant agent's event loop is that event loop, in the order in which they were added to their respective task source. When a task would be removed from the unshipped port message queue, it must instead be removed from its port message queue.

When a MessagePort's has been shipped flag is false, its port message queue must be ignored for the purposes of the event loop. (The unshipped port message queue is used instead.)

The has been shipped flag is set to true when a port, its twin, or the object it was cloned from, is or has been transferred. When a MessagePort's has been shipped flag is true, its port message queue acts as a first-class task source, unaffected to any unshipped port message queue.

When the user agent is to entangle two MessagePort objects, it must run the following steps:

  1. If one of the ports is already entangled, then disentangle it and the port that it was entangled with.

    If those two previously entangled ports were the two ports of a MessageChannel object, then that MessageChannel object no longer represents an actual channel: the two ports in that object are no longer entangled.

  2. Associate the two ports to be entangled, so that they form the two parts of a new channel. (There is no MessageChannel object that represents this channel.)

    Two ports A and B that have gone through this step are now said to be entangled; one is entangled to the other, and vice versa.

    While this specification describes this process as instantaneous, implementations are more likely to implement it via message passing. As with all algorithms, the key is "merely" that the end result be indistinguishable, in a black-box sense, from the specification.

The disentangle steps, given a MessagePort initiatorPort which initiates disentangling, are as follows:

  1. Let otherPort be the MessagePort which initiatorPort was entangled with.

  2. Assert: otherPort exists.

  3. Disentangle initiatorPort and otherPort, so that they are no longer entangled or associated with each other.

  4. Fire an event named close at otherPort.

The close event will be fired even if the port is not explicitly closed. The cases where this event is dispatched are:

We only dispatch the event on otherPort because initiatorPort explicitly triggered the close, its Document no longer exists, or it was already garbage collected, as described above.


MessagePort objects are transferable objects. Their transfer steps, given value and dataHolder, are:

  1. Set value's has been shipped flag to true.

  2. Set dataHolder.[[PortMessageQueue]] to value's port message queue.

  3. If value is entangled with another port remotePort, then:

    1. Set remotePort's has been shipped flag to true.

    2. Set dataHolder.[[RemotePort]] to remotePort.

  4. Otherwise, set dataHolder.[[RemotePort]] to null.

Their transfer-receiving steps, given dataHolder and value, are:

  1. Set value's has been shipped flag to true.

  2. Move all the tasks that are to fire message events in dataHolder.[[PortMessageQueue]] to the port message queue of value, if any, leaving value's port message queue in its initial disabled state, and, if value's relevant global object is a Window, associating the moved tasks with value's relevant global object's associated Document.

  3. If dataHolder.[[RemotePort]] is not null, then entangle dataHolder.[[RemotePort]] and value. (This will disentangle dataHolder.[[RemotePort]] from the original port that was transferred.)


The message port post message steps, given sourcePort, targetPort, message, and options are as follows:

  1. Let transfer be options["transfer"].

  2. If transfer contains sourcePort, then throw a "DataCloneError" DOMException.

  3. Let doomed be false.

  4. If targetPort is not null and transfer contains targetPort, then set doomed to true and optionally report to a developer console that the target port was posted to itself, causing the communication channel to be lost.

  5. Let serializeWithTransferResult be StructuredSerializeWithTransfer(message, transfer). 例外を再度投げる。

  6. If targetPort is null, or if doomed is true, then return.

  7. Add a task that runs the following steps to the port message queue of targetPort:

    1. Let finalTargetPort be the MessagePort in whose port message queue the task now finds itself.

      This can be different from targetPort, if targetPort itself was transferred and thus all its tasks moved along with it.

    2. Let messageEventTarget be finalTargetPort's message event target.

    3. Let targetRealm be finalTargetPort's relevant realm.

    4. Let deserializeRecord be StructuredDeserializeWithTransfer(serializeWithTransferResult, targetRealm).

      If this throws an exception, catch it, fire an event named messageerror at messageEventTarget, using MessageEvent, and then return.

    5. Let messageClone be deserializeRecord.[[Deserialized]].

    6. Let newPorts be a new frozen array consisting of all MessagePort objects in deserializeRecord.[[TransferredValues]], if any, maintaining their relative order.

    7. Fire an event named message at messageEventTarget, using MessageEvent, with the data attribute initialized to messageClone and the ports attribute initialized to newPorts.

The postMessage(message, options) method steps are:

  1. Let targetPort be the port with which this is entangled, if any; otherwise let it be null.

  2. Run the message port post message steps providing this, targetPort, message and options.

The postMessage(message, transfer) method steps are:

  1. Let targetPort be the port with which this is entangled, if any; otherwise let it be null.

  2. Let options be «[ "transfer" → transfer ]».

  3. Run the message port post message steps providing this, targetPort, message and options.


The start() method steps are to enable this's port message queue, if it is not already enabled.


The close() method steps are:

  1. Set this's [[Detached]] internal slot value to true.

  2. If this is entangled, disentangle it.


The following are the event handlers (and their corresponding event handler event types) that must be supported, as event handler IDL attributes, by all objects implementing the MessagePort interface:

イベントハンドラーイベントハンドラーイベント型
oncloseclose

The first time a MessagePort object's onmessage IDL attribute is set, the port's port message queue must be enabled, as if the start() method had been called.

9.4.5 ポートおよびガベージコレクション

When a MessagePort object o is garbage collected, if o is entangled, then the user agent must disentangle o.

When a MessagePort object o is entangled and message or messageerror event listener is registered, user agents must act as if o's entangled MessagePort object has a strong reference to o.

Furthermore, a MessagePort object must not be garbage collected while there exists an event referenced by a task in a task queue that is to be dispatched on that MessagePort object, or while the MessagePort object's port message queue is enabled and not empty.

Thus, a message port can be received, given an event listener, and then forgotten, and so long as that event listener could receive a message, the channel will be maintained.

Of course, if this was to occur on both sides of the channel, then both ports could be garbage collected, since they would not be reachable from live code, despite having a strong reference to each other. However, if a message port has a pending message, it is not garbage collected.

著者は、リソースを再収集できるように、MessagePortオブジェクトを明示的に閉じて、それらを分離することを強く勧める。多くのMessagePortオブジェクトを作成し、それらを閉じずに破棄すると、ガベージコレクションが必ずしも迅速に実行されるとは限らないため、一時的にメモリーの使用率が高くなる可能性がある。特に、ガベージコレクションにプロセス間の調整が含まれるMessagePortの場合はそうなる。

9.5 Broadcasting to other browsing contexts

BroadcastChannel

Support in all current engines.

Firefox38+Safari15.4+Chrome54+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

Broadcast_Channel_API

Support in all current engines.

Firefox38+Safari15.4+Chrome54+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

Pages on a single origin opened by the same user in the same user agent but in different unrelated browsing contexts sometimes need to send notifications to each other, for example "hey, the user logged in over here, check your credentials again".

For elaborate cases, e.g. to manage locking of shared state, to manage synchronization of resources between a server and multiple local clients, to share a WebSocket connection with a remote host, and so forth, shared workers are the most appropriate solution.

For simple cases, though, where a shared worker would be an unreasonable overhead, authors can use the simple channel-based broadcast mechanism described in this section.

[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

Support in all current engines.

Firefox38+Safari15.4+Chrome54+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

Returns a new BroadcastChannel object via which messages for the given channel name can be sent and received.

broadcastChannel.name

BroadcastChannel/name

Support in all current engines.

Firefox38+Safari15.4+Chrome54+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

Returns the channel name (as passed to the constructor).

broadcastChannel.postMessage(message)

BroadcastChannel/postMessage

Support in all current engines.

Firefox38+Safari15.4+Chrome54+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

Sends the given message to other BroadcastChannel objects set up for this channel. Messages can be structured objects, e.g. nested objects and arrays.

broadcastChannel.close()

BroadcastChannel/close

Support in all current engines.

Firefox38+Safari15.4+Chrome54+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android?

Closes the BroadcastChannel object, opening it up to garbage collection.

A BroadcastChannel object has a channel name and a closed flag.

The new BroadcastChannel(name) constructor steps are:

  1. Set this's channel name to name.

  2. Set this's closed flag to false.

The name getter steps are to return this's channel name.

A BroadcastChannel object is said to be eligible for messaging when its relevant global object is either:

The postMessage(message) method steps are:

  1. If this is not eligible for messaging, then return.

  2. If this's closed flag is true, then throw an "InvalidStateError" DOMException.

  3. Let serialized be StructuredSerialize(message). 例外を再度投げる。

  4. Let sourceOrigin be this's relevant settings object's origin.

  5. Let sourceStorageKey be the result of running obtain a storage key for non-storage purposes with this's relevant settings object.

  6. Let destinations be a list of BroadcastChannel objects that match the following criteria:

  7. Remove source from destinations.

  8. Sort destinations such that all BroadcastChannel objects whose relevant agents are the same are sorted in creation order, oldest first. (This does not define a complete ordering. Within this constraint, user agents may sort the list in any implementation-defined manner.)

  9. For each destination in destinations, queue a global task on the DOM manipulation task source given destination's relevant global object to perform the following steps:

    1. If destination's closed flag is true, then abort these steps.

    2. Let targetRealm be destination's relevant realm.

    3. Let data be StructuredDeserialize(serialized, targetRealm).

      If this throws an exception, catch it, fire an event named messageerror at destination, using MessageEvent, with the origin attribute initialized to the serialization of sourceOrigin, and then abort these steps.

    4. Fire an event named message at destination, using MessageEvent, with the data attribute initialized to data and the origin attribute initialized to the serialization of sourceOrigin.

While a BroadcastChannel object whose closed flag is false has an event listener registered for message or messageerror events, there must be a strong reference from the BroadcastChannel object's relevant global object to the BroadcastChannel object itself.

The close() method steps are to set this's closed flag to true.

Authors are strongly encouraged to explicitly close BroadcastChannel objects when they are no longer needed, so that they can be garbage collected. Creating many BroadcastChannel objects and discarding them while leaving them with an event listener and without closing them can lead to an apparent memory leak, since the objects will continue to live for as long as they have an event listener (or until their page or worker is closed).


The following are the event handlers (and their corresponding event handler event types) that must be supported, as event handler IDL attributes, by all objects implementing the BroadcastChannel interface:

イベントハンドラーイベントハンドラーイベント型
onmessage

BroadcastChannel/message_event

Support in all current engines.

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

Support in all current engines.

Firefox57+Safari15.4+Chrome60+
Opera?Edge79+
Edge (Legacy)?Internet ExplorerNo
Firefox Android?Safari iOS?Chrome Android?WebView Android?Samsung Internet?Opera Android47+
messageerror

Suppose a page wants to know when the user logs out, even when the user does so from another tab at the same site:

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
  // ...
}