Skip to content

Commit aa1df95

Browse files
trflynn89tcl3
authored andcommitted
LibWeb: Implement window.close and window.closed
1 parent ae13082 commit aa1df95

File tree

7 files changed

+86
-6
lines changed

7 files changed

+86
-6
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
window.closed = false
2+
window.closed = true
3+
window.closed = true
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script src="../include.js"></script>
2+
<script>
3+
asyncTest(done => {
4+
const newWindow = window.open("about:blank", "_blank");
5+
6+
newWindow.addEventListener("beforeunload", () => {
7+
println(`window.closed = ${newWindow.closed}`);
8+
done();
9+
});
10+
11+
println(`window.closed = ${newWindow.closed}`);
12+
newWindow.close();
13+
println(`window.closed = ${newWindow.closed}`);
14+
});
15+
</script>

Userland/Libraries/LibWeb/HTML/BrowsingContext.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class BrowsingContext final : public JS::Cell {
9696
}
9797

9898
bool is_top_level() const;
99+
bool is_auxiliary() const { return m_is_auxiliary; }
99100

100101
DOM::Document const* active_document() const;
101102
DOM::Document* active_document();

Userland/Libraries/LibWeb/HTML/Navigable.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,21 @@ void Navigable::visit_edges(Cell::Visitor& visitor)
128128
m_event_handler.visit_edges(visitor);
129129
}
130130

131+
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#script-closable
132+
bool Navigable::is_script_closable()
133+
{
134+
// A navigable is script-closable if its active browsing context is an auxiliary browsing context that was created
135+
// by a script (as opposed to by an action of the user), or if it is a top-level traversable whose session history
136+
// entries's size is 1.
137+
if (auto browsing_context = active_browsing_context(); browsing_context && browsing_context->is_auxiliary())
138+
return true;
139+
140+
if (is_top_level_traversable())
141+
return get_session_history_entries().size() == 1;
142+
143+
return false;
144+
}
145+
131146
void Navigable::set_delaying_load_events(bool value)
132147
{
133148
if (value) {

Userland/Libraries/LibWeb/HTML/Navigable.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class Navigable : public JS::Cell {
6969

7070
bool is_closing() const { return m_closing; }
7171
void set_closing(bool value) { m_closing = value; }
72+
bool is_script_closable();
7273

7374
void set_delaying_load_events(bool value);
7475
bool is_delaying_load_events() const { return m_delaying_the_load_event.has_value(); }

Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,8 +1178,9 @@ void TraversableNavigable::close_top_level_traversable()
11781178
VERIFY(is_top_level_traversable());
11791179

11801180
// 1. If traversable's is closing is true, then return.
1181-
if (is_closing())
1182-
return;
1181+
// FIXME: Spec-issue: The only place in the spec that sets the `is closing` flag to true is `window.close`, and it
1182+
// does so immediately before invoking this method. So it does not make sense to return early here.
1183+
// https://github.com/whatwg/html/issues/10678
11831184

11841185
// 2. Let toUnload be traversable's active document's inclusive descendant navigables.
11851186
auto to_unload = active_document()->inclusive_descendant_navigables();

Userland/Libraries/LibWeb/HTML/Window.cpp

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -787,15 +787,59 @@ String Window::status() const
787787
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-window-close
788788
void Window::close()
789789
{
790-
// FIXME: Implement this properly
791-
dbgln("(STUBBED) Window::close()");
790+
// 1. Let thisTraversable be this's navigable.
791+
auto traversable = navigable();
792+
793+
// 2. If thisTraversable is not a top-level traversable, then return.
794+
if (!traversable || !traversable->is_top_level_traversable())
795+
return;
796+
797+
// 3. If thisTraversable's is closing is true, then return.
798+
if (traversable->is_closing())
799+
return;
800+
801+
// 4. Let browsingContext be thisTraversable's active browsing context.
802+
auto browsing_context = traversable->active_browsing_context();
803+
804+
// 5. Let sourceSnapshotParams be the result of snapshotting source snapshot params given thisTraversable's active document.
805+
auto source_snapshot_params = traversable->active_document()->snapshot_source_snapshot_params();
806+
807+
auto& incumbent_global_object = verify_cast<HTML::Window>(HTML::incumbent_global_object());
808+
809+
// 6. If all the following are true:
810+
if (
811+
// thisTraversable is script-closable;
812+
traversable->is_script_closable()
813+
814+
// the incumbent global object's browsing context is familiar with browsingContext; and
815+
&& incumbent_global_object.browsing_context()->is_familiar_with(*browsing_context)
816+
817+
// the incumbent global object's navigable is allowed by sandboxing to navigate thisTraversable, given sourceSnapshotParams,
818+
&& incumbent_global_object.navigable()->allowed_by_sandboxing_to_navigate(*traversable, source_snapshot_params))
819+
// then:
820+
{
821+
// 1. Set thisTraversable's is closing to true.
822+
traversable->set_closing(true);
823+
824+
// 2. Queue a task on the DOM manipulation task source to close thisTraversable.
825+
HTML::queue_global_task(HTML::Task::Source::DOMManipulation, incumbent_global_object, JS::create_heap_function(heap(), [traversable] {
826+
verify_cast<TraversableNavigable>(*traversable).close_top_level_traversable();
827+
}));
828+
}
792829
}
793830

794831
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-window-closed
795832
bool Window::closed() const
796833
{
797-
// FIXME: Implement this properly
798-
dbgln("(STUBBED) Window::closed");
834+
// The closed getter steps are to return true if this's browsing context is null or its is closing is true;
835+
// otherwise false.
836+
if (!browsing_context())
837+
return true;
838+
839+
// FIXME: The spec seems a bit out of date. The `is closing` flag is on the navigable, not the browsing context.
840+
if (auto navigable = this->navigable(); !navigable || navigable->is_closing())
841+
return true;
842+
799843
return false;
800844
}
801845

0 commit comments

Comments
 (0)