commit f37b7bd1f23ffcf5a50bb18efb771d64d9df679d
parent 9212b47900ea7a1a701a832ece3e7ca0ce88da05
Author: Dominic Farolino <dom@chromium.org>
Date: Mon, 8 Dec 2025 12:29:24 +0000
Bug 2004566 [wpt PR 56537] - Blink: Add lifecycle regression test for about:srcdoc iframes, a=testonly
Automatic update from web-platform-tests
Blink: Add lifecycle regression test for about:srcdoc iframes
See https://crbug.com/40937729 for the exploration that led to this
regression test.
Specifically see this comment thread [1] for discussion about the fact
that Mojo callbacks are not supposed to be able to run on HeapMojoRemote
after the associated Document/execution context becomes inactive. This
bug is caused by the fact that we reuse the execution context/
LocalDOMWindow of the initial empty document for subsequent navigations.
This allows for IPCs that target the document to be scheduled, by
virtue of its now-reused Window still being alive. This causes mojo
callbacks to run on the detached document, which have the reasonable
expectation that the document is attached. See [2] for a full diagnosis
and write-up.
[1]:
https://chromium-review.googlesource.com/c/chromium/src/+/4908152/comment/f5e0fbe8_2b1d3aaf/
[2]:
https://docs.google.com/document/u/1/d/1wmvqbNZGtp1QluxW1sntZd_W1o1XsHlQd-ywNqwyu-Y/edit?tab=t.0
R=dcheng
Bug: 40937729
Change-Id: I2cf4766892b7f6ea2550ecd7dac8ab5548158cb6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6148512
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Commit-Queue: Dominic Farolino <dom@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1554835}
--
wpt-commits: 25c0ff0e51a009a17a8b8ab4bffb322fdd6f8ba3
wpt-pr: 56537
Diffstat:
1 file changed, 60 insertions(+), 0 deletions(-)
diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc-lifecycle-crash-crbug-1472607.https.html b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/srcdoc-lifecycle-crash-crbug-1472607.https.html
@@ -0,0 +1,60 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Chromium regression test for a Promise-related lifecycle crash in about:srcdoc iframes</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+
+<body>
+<script>
+promise_test(async t => {
+ await test_driver.set_permission({ name: 'storage-access' }, 'granted');
+}, "common setup");
+
+// In Chromium, this test would previously crash the renderer process. See
+// documentation below.
+promise_test(async t => {
+ const new_iframe = document.createElement('iframe');
+ // Having this come before `append()` keeps the iframe on the "initial empty
+ // Document" (see [1]) while the about:srcdoc navigation loads asynchronously.
+ //
+ // [1]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/dom/document.h;l=2574;drc=69640c83afac0bde8247ceac5fe259f13a5265a7.
+ new_iframe.srcdoc = 'stuff';
+ document.body.append(new_iframe);
+
+ // It is not clear why this is needed, but it appears to reliably slow the
+ // *next*/last `requestStorageAccess()` call down such that the IPC coming
+ // from the browser to resolve its promise comes after the about:srcdoc
+ // navigation commits (and after the initial empty document is detached).
+ //
+ // If for some reason the about:srcdoc navigation commits after this first
+ // call to `requestStorageAccess()` resolves, then the next assert will fail.
+ await new_iframe.contentDocument.requestStorageAccess();
+
+ assert_equals(new_iframe.contentDocument.URL, "about:blank");
+ // Now we're set up to trigger the Chromium bug that this test is exercising.
+ // The series of events that we're relying on, and trying to trigger, is:
+ // 1. Call `requestStorageAccess()` from the initial empty about:blank
+ // Document, before it gets replaced by a same-origin navigation (to
+ // about:srcdoc in this case).
+ // 2. While waiting for the Promise to resolve, the Document gets replaced by
+ // the navigation. This new Document shares a Window with the old
+ // now-detached initial one (per spec) since it is the first same-origin
+ // navigation after the initial empty Document, and those "special" in
+ // that they are done with replacement, and share the same Window.
+ // 3. After the navigation commits and the initial empty about:blank Document
+ // is replaced, the `requestStorageAccess()` Promise finally resolves. The
+ // callback runs in the renderer, since the lifetime of the callback is
+ // tied to the Window.
+ //
+ // In Chromium, this previously put us in a weird state where the callback ran
+ // against a live Window, but was associated with a detached Document, which
+ // bucked some assumptions and crashed the renderer; see
+ // https://crbug.com/40937729. The root cause of this is discussed here:
+ // https://docs.google.com/document/d/1wmvqbNZGtp1QluxW1sntZd_W1o1XsHlQd-ywNqwyu-Y/edit.
+ return new_iframe.contentDocument.requestStorageAccess();
+}, "requestStorageAccess() from about:blank Document before it gets " +
+ "replaced with a srcdoc resource does not crash the host process");
+</script>
+</body>