wpt / css / css-sizing / responsive-iframe / responsive-iframe-navigation.html?origin=cross

Open test on wpt.live | wpt.fyi

Runs: Chrome 160.0.8162.0 (wpt@fa649586f, 2026-11-15) | Firefox 162.0a1 (wpt@fa649586f, 2026-11-15) | Safari 256 preview (wpt@fa649586f, 2026-11-15) | Ladybird 1.0-c064b (wpt@c0f2b11f4, 2026-11-15) | Servo Servo 0.8 (wpt@76f3efc65, 2026-11-14) | Blitz ea07ee716 (wpt@bbb30d0f5, 2026-11-15)

ChromeFirefoxSafariLadybirdServoBlitz
Total1/10/10/10/10/10/1
SubtestChromeFirefoxSafariLadybirdServoBlitzBlitz error message
Responsive iframe preserves size during navigationPASSTIMEOUTTIMEOUTTIMEOUTTIMEOUTFAIL
ResizeObserver is not defined

/css/css-sizing/responsive-iframe/responsive-iframe-navigation.html

<!doctype HTML>
<title>Responsive iframe should keep size during navigation</title>
<link rel="author" href="mailto:kojii@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-sizing/">
<meta name="variant" content="?origin=same">
<meta name="variant" content="?origin=cross">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<style>
iframe {
  frame-sizing: content-height;
}
</style>
<script>
const params = new URLSearchParams(window.location.search);
const origin = params.get('origin');

const iframe = document.createElement('iframe');
iframe.id = 'iframe';
iframe.frameBorder = '0';
iframe.src = 'resources/iframe-contents-navigation.html';
document.addEventListener('DOMContentLoaded', () => {
  document.body.appendChild(iframe);
});

async_test(t => {
  let numIframeLoadEvents = 0;
  const heights = [];
  let onHeightObserved = null;

  function waitForObservedHeight(targetHeight) {
    if (heights.at(-1) === targetHeight) {
      return Promise.resolve();
    }
    return new Promise(resolve => {
      onHeightObserved = () => {
        if (heights.at(-1) === targetHeight) {
          onHeightObserved = null;
          resolve();
        }
      };
    });
  }

  // Use `ResizeObserver` to catch all resizes.
  const resizeObserver = new ResizeObserver(t.step_func((entries) => {
    for (const entry of entries) {
      assert_equals(entry.target, iframe);
      if (!heights.length && !entry.contentRect.height) {
        continue;  // Disregard initial 0. It's sometimes flaky.
      }
      heights.push(entry.contentRect.height);
    }
    if (onHeightObserved) {
      onHeightObserved();
    }
  }));
  t.add_cleanup(() => resizeObserver.disconnect());
  resizeObserver.observe(iframe);

  window.addEventListener('message', t.step_func((e) => {
    const data = e.data;
    if (data && data.action === 'slow-will-block') {
      // Check the initial viewport size of the child content.
      const viewportHeight = data.viewportHeight;
      if (origin === 'cross') {
        assert_equals(viewportHeight, 150, "Cross-origin should see reset viewport height");
      } else {
        assert_equals(viewportHeight, 200, "Same-origin should see retained viewport height");
      }

      // Check re-layouts don't reset the height while loading new content.
      triggerLayouts();
    }
  }));

  iframe.addEventListener('load', t.step_func(async () => {
    ++numIframeLoadEvents;
    switch (numIframeLoadEvents) {
      case 1: {
        // Wait until the responsive resize and `ResizeObserver` capture the
        // 200px height before navigating.
        await waitForObservedHeight(200);
        t.step(() => {
          assert_equals(iframe.offsetHeight, 200, "Initial height should be 200px");
          let src = 'iframe-contents-slow.html';
          if (origin === 'cross') {
            const url = new URL(`resources/${src}`, window.location.href);
            src = get_host_info().HTTP_REMOTE_ORIGIN + url.pathname;
          }
          iframe.contentWindow.postMessage({action: 'navigate', url: src}, '*');
        });
        break;
      }
      case 2: {
        await waitForObservedHeight(400);
        const expect = origin === 'cross' ? [150, 200, 150, 400] : [150, 200, 400];
        if (heights[0] !== 150) {
          // The initial 150px height may not be captured if the first content
          // finishes layout before the first rendering frame.
          expect.shift();
        }
        t.step(() => {
          assert_array_equals(heights, expect);
        });
        t.done();
        break;
      }
      default:
        assert_unreached("Unexpected iframe load events");
        break;
    }
  }));

  function triggerLayouts() {
    // Trigger layout by appending a `div`.
    const div = document.createElement('div');
    div.textContent = 'Trigger Layout';
    document.body.appendChild(div);
    document.body.offsetTop;  // Force layout.

    // Trigger layout by toggling `display: none`.
    iframe.style.display = 'none';
    document.body.offsetTop;  // Force layout.
    iframe.style.display = '';
    document.body.offsetTop;  // Force layout.
  }
}, "Responsive iframe preserves size during navigation");
</script>