From aa1df95b31deef706e785909ec617a599112ab49 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sat, 5 Oct 2024 15:56:35 -0400 Subject: [PATCH] LibWeb: Implement window.close and window.closed --- .../Text/expected/HTML/Window-close.txt | 3 ++ .../LibWeb/Text/input/HTML/Window-close.html | 15 ++++++ .../Libraries/LibWeb/HTML/BrowsingContext.h | 1 + Userland/Libraries/LibWeb/HTML/Navigable.cpp | 15 ++++++ Userland/Libraries/LibWeb/HTML/Navigable.h | 1 + .../LibWeb/HTML/TraversableNavigable.cpp | 5 +- Userland/Libraries/LibWeb/HTML/Window.cpp | 52 +++++++++++++++++-- 7 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/HTML/Window-close.txt create mode 100644 Tests/LibWeb/Text/input/HTML/Window-close.html diff --git a/Tests/LibWeb/Text/expected/HTML/Window-close.txt b/Tests/LibWeb/Text/expected/HTML/Window-close.txt new file mode 100644 index 000000000000..cc65910f3ac5 --- /dev/null +++ b/Tests/LibWeb/Text/expected/HTML/Window-close.txt @@ -0,0 +1,3 @@ +window.closed = false +window.closed = true +window.closed = true diff --git a/Tests/LibWeb/Text/input/HTML/Window-close.html b/Tests/LibWeb/Text/input/HTML/Window-close.html new file mode 100644 index 000000000000..d391adcd6d48 --- /dev/null +++ b/Tests/LibWeb/Text/input/HTML/Window-close.html @@ -0,0 +1,15 @@ + + diff --git a/Userland/Libraries/LibWeb/HTML/BrowsingContext.h b/Userland/Libraries/LibWeb/HTML/BrowsingContext.h index 1e75d9d3552d..6900a4201ace 100644 --- a/Userland/Libraries/LibWeb/HTML/BrowsingContext.h +++ b/Userland/Libraries/LibWeb/HTML/BrowsingContext.h @@ -96,6 +96,7 @@ class BrowsingContext final : public JS::Cell { } bool is_top_level() const; + bool is_auxiliary() const { return m_is_auxiliary; } DOM::Document const* active_document() const; DOM::Document* active_document(); diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.cpp b/Userland/Libraries/LibWeb/HTML/Navigable.cpp index a69bfc4212ba..9856661610f6 100644 --- a/Userland/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Userland/Libraries/LibWeb/HTML/Navigable.cpp @@ -128,6 +128,21 @@ void Navigable::visit_edges(Cell::Visitor& visitor) m_event_handler.visit_edges(visitor); } +// https://html.spec.whatwg.org/multipage/nav-history-apis.html#script-closable +bool Navigable::is_script_closable() +{ + // A navigable is script-closable if its active browsing context is an auxiliary browsing context that was created + // by a script (as opposed to by an action of the user), or if it is a top-level traversable whose session history + // entries's size is 1. + if (auto browsing_context = active_browsing_context(); browsing_context && browsing_context->is_auxiliary()) + return true; + + if (is_top_level_traversable()) + return get_session_history_entries().size() == 1; + + return false; +} + void Navigable::set_delaying_load_events(bool value) { if (value) { diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.h b/Userland/Libraries/LibWeb/HTML/Navigable.h index 4b2122f65b43..8283dff578df 100644 --- a/Userland/Libraries/LibWeb/HTML/Navigable.h +++ b/Userland/Libraries/LibWeb/HTML/Navigable.h @@ -69,6 +69,7 @@ class Navigable : public JS::Cell { bool is_closing() const { return m_closing; } void set_closing(bool value) { m_closing = value; } + bool is_script_closable(); void set_delaying_load_events(bool value); bool is_delaying_load_events() const { return m_delaying_the_load_event.has_value(); } diff --git a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp index 3570b5c193c5..75c54595ad2c 100644 --- a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp +++ b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp @@ -1178,8 +1178,9 @@ void TraversableNavigable::close_top_level_traversable() VERIFY(is_top_level_traversable()); // 1. If traversable's is closing is true, then return. - if (is_closing()) - return; + // FIXME: Spec-issue: The only place in the spec that sets the `is closing` flag to true is `window.close`, and it + // does so immediately before invoking this method. So it does not make sense to return early here. + // https://github.com/whatwg/html/issues/10678 // 2. Let toUnload be traversable's active document's inclusive descendant navigables. auto to_unload = active_document()->inclusive_descendant_navigables(); diff --git a/Userland/Libraries/LibWeb/HTML/Window.cpp b/Userland/Libraries/LibWeb/HTML/Window.cpp index ea154e1b8de5..1e5a32727d8d 100644 --- a/Userland/Libraries/LibWeb/HTML/Window.cpp +++ b/Userland/Libraries/LibWeb/HTML/Window.cpp @@ -787,15 +787,59 @@ String Window::status() const // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-window-close void Window::close() { - // FIXME: Implement this properly - dbgln("(STUBBED) Window::close()"); + // 1. Let thisTraversable be this's navigable. + auto traversable = navigable(); + + // 2. If thisTraversable is not a top-level traversable, then return. + if (!traversable || !traversable->is_top_level_traversable()) + return; + + // 3. If thisTraversable's is closing is true, then return. + if (traversable->is_closing()) + return; + + // 4. Let browsingContext be thisTraversable's active browsing context. + auto browsing_context = traversable->active_browsing_context(); + + // 5. Let sourceSnapshotParams be the result of snapshotting source snapshot params given thisTraversable's active document. + auto source_snapshot_params = traversable->active_document()->snapshot_source_snapshot_params(); + + auto& incumbent_global_object = verify_cast(HTML::incumbent_global_object()); + + // 6. If all the following are true: + if ( + // thisTraversable is script-closable; + traversable->is_script_closable() + + // the incumbent global object's browsing context is familiar with browsingContext; and + && incumbent_global_object.browsing_context()->is_familiar_with(*browsing_context) + + // the incumbent global object's navigable is allowed by sandboxing to navigate thisTraversable, given sourceSnapshotParams, + && incumbent_global_object.navigable()->allowed_by_sandboxing_to_navigate(*traversable, source_snapshot_params)) + // then: + { + // 1. Set thisTraversable's is closing to true. + traversable->set_closing(true); + + // 2. Queue a task on the DOM manipulation task source to close thisTraversable. + HTML::queue_global_task(HTML::Task::Source::DOMManipulation, incumbent_global_object, JS::create_heap_function(heap(), [traversable] { + verify_cast(*traversable).close_top_level_traversable(); + })); + } } // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-window-closed bool Window::closed() const { - // FIXME: Implement this properly - dbgln("(STUBBED) Window::closed"); + // The closed getter steps are to return true if this's browsing context is null or its is closing is true; + // otherwise false. + if (!browsing_context()) + return true; + + // FIXME: The spec seems a bit out of date. The `is closing` flag is on the navigable, not the browsing context. + if (auto navigable = this->navigable(); !navigable || navigable->is_closing()) + return true; + return false; }