Lesson 0001 · ~12 minutes

You cannot call into another window

One skill: when you hold a WindowProxy, you send data with postMessage. You do not call a function on the other window.

Win for this lesson

You can say this: “I do not call a function on the iframe. I use postMessage. I set targetOrigin. I check event.origin.”

1. The problem

You have a page.

The page has an iframe.

The iframe shows a widget from a different site.

You want a value from the widget.

You cannot read the widget document.

You cannot call a function on the widget.

The same-origin policy blocks that access. postMessage is the path that the browser allows.

2. Origin

An origin is a scheme, a host, and a port.

Example: https://app.example and https://widget.example are different origins.

Two pages have the same origin only when the scheme, the host, and the port all match.

If two pages have the same origin, they can read each other.

If two pages have different origins, they cannot.

3. WindowProxy

You get a handle to the other window in one of these ways:

That handle is a WindowProxy.

A WindowProxy is not the other window.

If the origins are the same, you can use the WindowProxy as the window. You can read document.title.

If the origins are different, the browser blocks most access. A read of document throws SecurityError.

You can still send data with postMessage.

React, same rule
iframeRef.current.contentWindow.postMessage(
  { type: "set-filter", q },
  "https://widget.example",
);
// Wrong: iframeRef.current.contentWindow.setFilter(q)

The ref holds a WindowProxy. You send data. You do not call setFilter.

4. How you send data

Write this:

otherWindow.postMessage(data, targetOrigin);

data is the value. The browser makes a structured clone of the value. You already know that copy from the Structured Clone topic.

The copy arrives later. This is not a function call.

targetOrigin is the origin that may receive the data.

If the other window does not have that origin, the browser discards the data. The browser does not throw an error. (HTML Standard)

your page
    |
    |  postMessage(data, "https://widget.example")
    v
iframe  (receives only if its origin is https://widget.example)

5. How you receive data

Add a listener:

window.addEventListener("message", (event) => {
  if (event.origin !== "https://widget.example") return;
  // use event.data
});

The event has three fields that matter here:

Any window that has your WindowProxy can send data to you (MDN).

You must check event.origin. If event.origin is not the origin you expect, return.

To reply, write this:

event.source.postMessage(reply, event.origin);
iframe
    |
    |  message event: data, origin, source
    v
your page  (you check event.origin, then you use event.data)

6. Do these steps

The box below is a real iframe. Do the steps in order. Look at the log after each step.

  1. Click Peek child document.title. The child has the same origin. The peek must work.
  2. Click Opaque sandbox. Click Peek again. The peek must fail with SecurityError. You still have a WindowProxy. You do not have the document.
  3. Click This origin. The data must not arrive. The child origin does not match.
  4. Click Wildcard *. The data must arrive.
  5. In the child, click Reply to parent. Look at event.origin in the log.

This lesson file uses the file:// protocol. The origin of this page is the text null. The origin of the sandbox child is also the text null. A string check cannot tell the two origins apart. In production you name a real https:// origin (HTML §9.3.2.1).

7. Practice

Do the five steps first. Then answer these questions.

Question A

You have iframe.contentWindow for a widget on https://widget.example. You need one field from that page.

Question B

You send a session token with targetOrigin: "*". The popup goes to https://evil.example before the data arrives.

Question C

A page adds window.addEventListener("message", …) and does not read event.origin.

Question D

You send data with targetOrigin: "https://widget.example". The iframe is now on https://other.example.

Question E

You call otherWindow.postMessage(payload) with no second argument.

8. 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)

MDN — Window.postMessage(). Read “Security concerns” and “The dispatched event”. Then read HTML §9.3.2.1.