Skip to content

Commit

Permalink
LibWeb: Implement window.close and window.closed
Browse files Browse the repository at this point in the history
(cherry picked from commit aa1df95b31deef706e785909ec617a599112ab49)
  • Loading branch information
trflynn89 authored and nico committed Nov 27, 2024
1 parent 886f901 commit 94e37ff
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 6 deletions.
3 changes: 3 additions & 0 deletions Tests/LibWeb/Text/expected/HTML/Window-close.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
window.closed = false
window.closed = true
window.closed = true
15 changes: 15 additions & 0 deletions Tests/LibWeb/Text/input/HTML/Window-close.html
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions Userland/Libraries/LibWeb/HTML/BrowsingContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
15 changes: 15 additions & 0 deletions Userland/Libraries/LibWeb/HTML/Navigable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,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) {
Expand Down
1 change: 1 addition & 0 deletions Userland/Libraries/LibWeb/HTML/Navigable.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,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(); }
Expand Down
5 changes: 3 additions & 2 deletions Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1041,8 +1041,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();
Expand Down
52 changes: 48 additions & 4 deletions Userland/Libraries/LibWeb/HTML/Window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -799,15 +799,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::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
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;
}

Expand Down

0 comments on commit 94e37ff

Please sign in to comment.