Skip to content

Running custom code on someone else's page

A widget can carry hand-written code, and that code never touches the page around it.

Last updated

The decision every builder has to make

Let customers write JavaScript and you have to answer whose page it runs on. Most tools answer by not allowing it, which makes the tool smaller than the problems people bring to it. The alternative answer is that it runs somewhere that is not your customer's page, and then the work is making that somewhere real rather than nominal.

An opaque origin, and what it takes to get one

The program is served from a separate host at an address derived from a hash of the program itself, inside a frame the browser is told to sandbox. That combination gives the document an opaque origin: one that is same-origin with nothing at all, including the host that served it. It cannot read the surrounding page, it cannot be read by it, and everything that passes between them goes through a typed set of messages over a dedicated channel — a theme, the visitor's answers, a request for more height.

Why this one is tested in a real browser

Because we got it wrong once and every test we had agreed with us. The rule that matters here is enforced by the browser and not modelled by our test environment, which has no opaque origins and does not apply the check at all — so a test written there can only ever confirm the model its author already had. Ours was wrong, so our tests were wrong in the same direction, and they agreed with each other for the life of the feature. There is now one that drives real Chromium, a real cross-origin frame and the real policy, and it is the one that gets to settle disagreements.

What it costs the widget's record

A widget carrying a program stops being able to claim that nothing in it is executed as author-supplied code, and its record says so. That is not a hole in the argument, it is the argument: the record describes the widget rather than the vendor, so it has to be able to come back negative. The calculators are worth noticing next to this, because arithmetic used to be the thing people reached for a code block to do, and now it is a formula the record stays clean for.

What this is evidence from

lib/sandbox-doc.ts
the document the program is served inside, and the content policy that ships with it
lib/sandbox-bridge.ts
the message contract between the program and the widget around it, which is the whole of what can pass
tests/sandbox-browser.test.ts
the test that runs real Chromium, and its own account of why the earlier ones could not have caught the bug
lib/schemas/widget-guarantee.ts
where a code block flips the first claim to false, and why the record is designed to be able to