v155 · privacy sandbox · feature removal

Shipping through the removal

An embed that used a fenced frame has to keep rendering on browsers that never had one, browsers that have the element but nothing to put in it, and browsers where the tag is now as meaningful as <banana>. One of those three is new, and it is the one that breaks the check most people wrote.

Which check survives the removal

Six ways to ask "can I use a fenced frame here". Each is evaluated live; the last column is what it will report once the element is gone, which is the column that matters.

Not evaluated yet.
Feature-detection candidates
checkhereverdictwhy
Press the button.

An ad slot that keeps rendering

The slot below fills itself the way production code should: it asks whether it can actually obtain a config, not whether the element exists, and falls back to an iframe when it cannot. Force the element out of consideration to watch the other branch take over — the switch changes which path runs, not what the browser supports.

Slot empty.

Nothing rendered yet.

the pattern

The trap is that document.createElement() never fails. Ask it for a tag no browser has ever heard of and it hands back an element, so a truthiness check on the result is true before, during and after any removal. The same goes for setting attributes on it, which is why a slot built that way renders a permanently empty box rather than falling back.

// Wrong: true in every browser, forever.
if (document.createElement("fencedframe")) { … }

// Better: the element is gone once this is false.
if ("HTMLFencedFrameElement" in window) { … }

// Right: the element is useless without a config, so ask for the config.
const config = await getConfigSomehow();   // runAdAuction / selectURL
if (config && "HTMLFencedFrameElement" in window) {
  const frame = document.createElement("fencedframe");
  frame.config = config;
  slot.append(frame);
} else {
  const frame = document.createElement("iframe");
  frame.src = fallbackURL;
  slot.append(frame);
}

The config-first order is what makes this survive all three stages of the removal at once. Chrome 152 takes away the producers, so config is null and the fallback runs. Chrome 154 takes away window.fence, which this code never touches. Chrome 155 turns the tag into an unknown element, and the branch guarding it is already false. Nothing needs a version check, and nothing needs to know which stage a given browser is at.

what to do about the reporting

Code that called fence.reportEvent() has no equivalent to move to, because the thing it was reporting from no longer renders. An embed running in an ordinary iframe reports the ordinary way — fetch() with keepalive, or navigator.sendBeacon() — and is subject to the ordinary rules about third-party storage, which is the trade the whole Privacy Sandbox effort was trying to avoid making and, for this element, has stopped trying to avoid.