diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 3d0261f25ee97..4c0c85478424a 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -5781,10 +5781,16 @@ void Document::unload(GC::Ptr) // 2. Fire a page transition event named pagehide at oldDocument's relevant global object with oldDocument's // salvageable state. HTML::relevant_window(*this).fire_a_page_transition_event(HTML::EventNames::pagehide, m_salvageable); + } - // 3. Update the visibility state of oldDocument to "hidden". + // 3. Update the visibility state of oldDocument to "hidden". + // AD-HOC: Violate the spec requirement for the page to be showing (in the spec, this step is a substep of step 10 + // above — not a sibling step) and instead do this for any page ready for post-load tasks; e.g., the initial + // about:blank of a never-navigated child. Gecko/WebKit/Blink consider such pages to be completely loaded — + // and update their visibility state when unloading them. + // See https://github.com/whatwg/html/issues/12288 + if (m_ready_for_post_load_tasks) update_the_visibility_state(HTML::VisibilityState::Hidden); - } // FIXME: 11. If unloadTimingInfo is not null, then set unloadTimingInfo's unload event start time to the current high // resolution time given newDocument's relevant global object, coarsened given oldDocument's relevant settings diff --git a/Libraries/LibWeb/HTML/NavigableContainer.cpp b/Libraries/LibWeb/HTML/NavigableContainer.cpp index 6992df0b8f5ae..9993008afe4da 100644 --- a/Libraries/LibWeb/HTML/NavigableContainer.cpp +++ b/Libraries/LibWeb/HTML/NavigableContainer.cpp @@ -311,7 +311,7 @@ void NavigableContainer::destroy_the_child_navigable() // Not in the spec: // Setting container's content navigable makes document *not* be "fully active". - // Therefore, it is moved to run in afterAllDestruction callback of "destroy a document and its descendants" + // Therefore, it is moved to run in the after-all-unloads callback of "unload a document and its descendants" // when all queued tasks are done. // "Has been destroyed" flag is used instead to check whether navigable is already destroyed. auto& local_navigable = as(*navigable); @@ -365,14 +365,21 @@ void NavigableContainer::destroy_the_child_navigable() }); // 5. Destroy a document and its descendants given navigable's active document. + // AD-HOC: We unload the document and its descendants, instead of just destroying. Unloading fires pagehide at the + // document's relevant global object, updates its visibility state to "hidden" (firing visibilitychange), + // and fires unload — before destroying the document. The spec as written would leave a removed container's + // content documents reporting a "visible" visibility state — with no events fired; Gecko/WebKit/Blink all + // fire those events, and report such documents as hidden. This also means starting a view transition in a + // removed document skips the transition — since startViewTransition() skips transitions for hidden docs. + // See https://github.com/whatwg/html/issues/12288 // AD-HOC: The spec assumes the active document is non-null here, but during an ancestor // unload the child documents are unloaded (and destroyed) before the ancestor's // pagehide fires. If that pagehide handler then removes a subtree containing this // container, we reach step 5 with navigable's active document already null. We - // treat the destroy step as a no-op in that case and proceed with the remaining + // treat the unload step as a no-op in that case and proceed with the remaining // post-destruction cleanup. if (auto active_document = local_navigable.active_document()) - active_document->destroy_a_document_and_its_descendants(after_document_destruction); + active_document->unload_a_document_and_its_descendants({}, after_document_destruction); else after_document_destruction->function()(); } diff --git a/Tests/LibWeb/TestConfig.ini b/Tests/LibWeb/TestConfig.ini index a0468ff85b32d..f3300dbef9a2e 100644 --- a/Tests/LibWeb/TestConfig.ini +++ b/Tests/LibWeb/TestConfig.ini @@ -272,6 +272,7 @@ Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.html Text/input/wpt-import/navigation-api/navigation-methods/reload-state-and-info.html Text/input/wpt-import/navigation-api/navigation-methods/reload-state-undefined.html +Text/input/wpt-import/page-visibility/iframe-unload.html Text/input/wpt-import/page-visibility/test_child_document.html Text/input/wpt-import/html/canvas/offscreen/text/2d.text.font.default.worker.html Text/input/wpt-import/html/canvas/offscreen/text/2d.text.font.parse.basic.worker.html diff --git a/Tests/LibWeb/Text/expected/HTML/iframe-removal-sets-visibility-state-hidden.txt b/Tests/LibWeb/Text/expected/HTML/iframe-removal-sets-visibility-state-hidden.txt new file mode 100644 index 0000000000000..e4e4b3476af04 --- /dev/null +++ b/Tests/LibWeb/Text/expected/HTML/iframe-removal-sets-visibility-state-hidden.txt @@ -0,0 +1,5 @@ +before removal: visible +pagehide fired: persisted=false +visibilitychange fired: hidden +unload fired +after removal: hidden diff --git a/Tests/LibWeb/Text/expected/HTML/view-transition-in-removed-iframe-is-skipped.txt b/Tests/LibWeb/Text/expected/HTML/view-transition-in-removed-iframe-is-skipped.txt new file mode 100644 index 0000000000000..7e3d6b0790529 --- /dev/null +++ b/Tests/LibWeb/Text/expected/HTML/view-transition-in-removed-iframe-is-skipped.txt @@ -0,0 +1,2 @@ +after removal: hidden +ready rejected: InvalidStateError diff --git a/Tests/LibWeb/Text/expected/wpt-import/page-visibility/iframe-unload.txt b/Tests/LibWeb/Text/expected/wpt-import/page-visibility/iframe-unload.txt new file mode 100644 index 0000000000000..39505f315f43a --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/page-visibility/iframe-unload.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass visibilitychange fires on unload with iframes \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/HTML/iframe-removal-sets-visibility-state-hidden.html b/Tests/LibWeb/Text/input/HTML/iframe-removal-sets-visibility-state-hidden.html new file mode 100644 index 0000000000000..e21f50d05f68e --- /dev/null +++ b/Tests/LibWeb/Text/input/HTML/iframe-removal-sets-visibility-state-hidden.html @@ -0,0 +1,34 @@ + + + diff --git a/Tests/LibWeb/Text/input/HTML/view-transition-in-removed-iframe-is-skipped.html b/Tests/LibWeb/Text/input/HTML/view-transition-in-removed-iframe-is-skipped.html new file mode 100644 index 0000000000000..24560868c3b77 --- /dev/null +++ b/Tests/LibWeb/Text/input/HTML/view-transition-in-removed-iframe-is-skipped.html @@ -0,0 +1,23 @@ + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/page-visibility/iframe-unload.html b/Tests/LibWeb/Text/input/wpt-import/page-visibility/iframe-unload.html new file mode 100644 index 0000000000000..5d4bed14ee02d --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/page-visibility/iframe-unload.html @@ -0,0 +1,49 @@ + +visibilitychange fires on unload with iframes + + +
+ + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/page-visibility/resources/iframe-with-subframes.html b/Tests/LibWeb/Text/input/wpt-import/page-visibility/resources/iframe-with-subframes.html new file mode 100644 index 0000000000000..febb954369995 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/page-visibility/resources/iframe-with-subframes.html @@ -0,0 +1,6 @@ + + + + + +