瀏覽代碼

LibWeb: Implement window.close and window.closed

Timothy Flynn 9 月之前
父節點
當前提交
aa1df95b31

+ 3 - 0
Tests/LibWeb/Text/expected/HTML/Window-close.txt

@@ -0,0 +1,3 @@
+window.closed = false
+window.closed = true
+window.closed = true

+ 15 - 0
Tests/LibWeb/Text/input/HTML/Window-close.html

@@ -0,0 +1,15 @@
+<script src="../include.js"></script>
+<script>
+    asyncTest(done => {
+        const newWindow = window.open("about:blank", "_blank");
+
+        newWindow.addEventListener("beforeunload", () => {
+            println(`window.closed = ${newWindow.closed}`);
+            done();
+        });
+
+        println(`window.closed = ${newWindow.closed}`);
+        newWindow.close();
+        println(`window.closed = ${newWindow.closed}`);
+    });
+</script>

+ 1 - 0
Userland/Libraries/LibWeb/HTML/BrowsingContext.h

@@ -96,6 +96,7 @@ public:
     }
     }
 
 
     bool is_top_level() const;
     bool is_top_level() const;
+    bool is_auxiliary() const { return m_is_auxiliary; }
 
 
     DOM::Document const* active_document() const;
     DOM::Document const* active_document() const;
     DOM::Document* active_document();
     DOM::Document* active_document();

+ 15 - 0
Userland/Libraries/LibWeb/HTML/Navigable.cpp

@@ -128,6 +128,21 @@ void Navigable::visit_edges(Cell::Visitor& visitor)
     m_event_handler.visit_edges(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)
 void Navigable::set_delaying_load_events(bool value)
 {
 {
     if (value) {
     if (value) {

+ 1 - 0
Userland/Libraries/LibWeb/HTML/Navigable.h

@@ -69,6 +69,7 @@ public:
 
 
     bool is_closing() const { return m_closing; }
     bool is_closing() const { return m_closing; }
     void set_closing(bool value) { m_closing = value; }
     void set_closing(bool value) { m_closing = value; }
+    bool is_script_closable();
 
 
     void set_delaying_load_events(bool value);
     void set_delaying_load_events(bool value);
     bool is_delaying_load_events() const { return m_delaying_the_load_event.has_value(); }
     bool is_delaying_load_events() const { return m_delaying_the_load_event.has_value(); }

+ 3 - 2
Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp

@@ -1178,8 +1178,9 @@ void TraversableNavigable::close_top_level_traversable()
     VERIFY(is_top_level_traversable());
     VERIFY(is_top_level_traversable());
 
 
     // 1. If traversable's is closing is true, then return.
     // 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.
     // 2. Let toUnload be traversable's active document's inclusive descendant navigables.
     auto to_unload = active_document()->inclusive_descendant_navigables();
     auto to_unload = active_document()->inclusive_descendant_navigables();

+ 48 - 4
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
 // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-window-close
 void 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::Window>(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<TraversableNavigable>(*traversable).close_top_level_traversable();
+        }));
+    }
 }
 }
 
 
 // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-window-closed
 // https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-window-closed
 bool Window::closed() const
 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;
     return false;
 }
 }