Lesson 0007 · ~10 minutes

Close the port

One skill: close the MessagePort when you are done. Do not treat event.data as server truth.

Win for this lesson

You can say this: “I call port.close() when I am done. After that, a send does not arrive. I still check the shape of event.data. A widget can lie.”

1. close disconnects the pair

You still hold port1. That is not the same as transfer. Transfer moved port2. You keep port1.

port.close() disconnects the pair. (MDN)

The other end gets a close event. (HTML Standard)

After close, a send on that port does not arrive. The method may return. It may throw. Either way, the child must not get the data.

Close ports you create. The HTML Standard asks you to do this so the pair can be collected. (HTML §9.4.5)

port1.close();
React, same rule
useEffect(() => {
  const channel = new MessageChannel();
  channel.port1.onmessage = onPrivate;
  iframeRef.current.contentWindow.postMessage(
    { type: "port" },
    "https://widget.example",
    [channel.port2],
  );
  return () => {
    channel.port1.close();
  };
}, []);

Close on unmount. Do not keep a dead port.

2. event.data is not the server

After you check event.origin, you still check the shape of event.data. (HTML §9.3.2.1)

A compromised widget can send { role: "admin" }.

The origin can be the widget you expect. The data can still be a lie.

A port does not fix that. Who holds the port can still lie.

Your page decides. Your server decides. The clone does not.

window.addEventListener("message", (event) => {
  if (event.origin !== "https://widget.example") return;
  const data = event.data;
  if (!data || data.type !== "filter") return;
  if (typeof data.q !== "string") return;
  setQuery(data.q);
});

3. Do these steps

The child below sends ready, then holds a port.

  1. Wait until ready is true.
  2. Click Transfer port2. The child port: line must say held.
  3. Click Send on port. The child must show the data.
  4. Click Close this end. The child must show close event.
  5. Click Send on port again. The child must not show a new payload. Then in the child, click Send to parent on port. The parent log must not get a new port message.

4. Practice

Do the five steps first. Then answer these questions.

Question A

You call port.close(). Then you call port.postMessage here.

Question B

The parent closed port1. The child still holds port2.

Question C

You transferred port2. You later call port1.close().

Question D

event.data is { role: "admin" } from the widget.

Question E

Why do you call port.close() when done?

5. Remember

Ask your teacher If a sentence is not clear, ask. If a lab step does not match the text, ask. That is part of the method.

Primary source (read next)

HTML §9.3.2.1 — Authors (origin, then data format, never * with secrets). Then HTML §9.4.5 (close ports).