From d4acdac3175da4edf0ca88c246a9e58307e3eefd Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 19 Sep 2022 20:50:33 +0200 Subject: [PATCH] LibWeb+WebContent+Browser: Plumb visibility state from GUI to web pages OOPWV now reacts to show/hide events and informs LibWeb about the state change. This makes visibilitychange events fire when switching tabs. :^) --- Userland/Applications/Browser/Tab.cpp | 10 ++++ Userland/Applications/Browser/Tab.h | 3 ++ Userland/Libraries/LibWeb/DOM/Document.cpp | 33 +++++++++++- Userland/Libraries/LibWeb/DOM/Document.h | 8 ++- .../Libraries/LibWeb/HTML/BrowsingContext.cpp | 51 ++++++++++++++++--- .../Libraries/LibWeb/HTML/BrowsingContext.h | 8 +++ .../LibWeb/HTML/BrowsingContextContainer.cpp | 6 +++ .../LibWeb/HTML/BrowsingContextContainer.h | 2 + .../Libraries/LibWeb/HTML/VisibilityState.h | 16 ++++++ .../LibWebView/OutOfProcessWebView.cpp | 10 ++++ .../LibWebView/OutOfProcessWebView.h | 2 + .../WebContent/ConnectionFromClient.cpp | 9 ++++ .../WebContent/ConnectionFromClient.h | 1 + .../Services/WebContent/WebContentServer.ipc | 2 + 14 files changed, 150 insertions(+), 11 deletions(-) create mode 100644 Userland/Libraries/LibWeb/HTML/VisibilityState.h diff --git a/Userland/Applications/Browser/Tab.cpp b/Userland/Applications/Browser/Tab.cpp index c75fdef9cd..33c7d73a1d 100644 --- a/Userland/Applications/Browser/Tab.cpp +++ b/Userland/Applications/Browser/Tab.cpp @@ -636,4 +636,14 @@ void Tab::show_storage_inspector() window->move_to_front(); } +void Tab::show_event(GUI::ShowEvent&) +{ + m_web_content_view->set_visible(true); +} + +void Tab::hide_event(GUI::HideEvent&) +{ + m_web_content_view->set_visible(false); +} + } diff --git a/Userland/Applications/Browser/Tab.h b/Userland/Applications/Browser/Tab.h index 1a49c08a34..0d3515925b 100644 --- a/Userland/Applications/Browser/Tab.h +++ b/Userland/Applications/Browser/Tab.h @@ -85,6 +85,9 @@ public: private: explicit Tab(BrowserWindow&); + virtual void show_event(GUI::ShowEvent&) override; + virtual void hide_event(GUI::HideEvent&) override; + BrowserWindow const& window() const; BrowserWindow& window(); diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index 35b6444e34..8397ef9e93 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -647,6 +647,8 @@ void Document::set_title(String const& title) void Document::attach_to_browsing_context(Badge, HTML::BrowsingContext& browsing_context) { m_browsing_context = browsing_context; + + update_the_visibility_state(browsing_context.system_visibility_state()); } void Document::detach_from_browsing_context(Badge, HTML::BrowsingContext& browsing_context) @@ -1587,11 +1589,17 @@ bool Document::hidden() const // https://html.spec.whatwg.org/multipage/interaction.html#dom-document-visibilitystate String Document::visibility_state() const { - return m_visibility_state; + switch (m_visibility_state) { + case HTML::VisibilityState::Hidden: + return "hidden"sv; + case HTML::VisibilityState::Visible: + return "visible"sv; + } + VERIFY_NOT_REACHED(); } // https://html.spec.whatwg.org/multipage/interaction.html#update-the-visibility-state -void Document::update_the_visibility_state(String visibility_state) +void Document::update_the_visibility_state(HTML::VisibilityState visibility_state) { // 1. If document's visibility state equals visibilityState, then return. if (m_visibility_state == visibility_state) @@ -1985,4 +1993,25 @@ HTML::PolicyContainer Document::policy_container() const return m_policy_container; } +// https://html.spec.whatwg.org/multipage/browsers.html#list-of-the-descendant-browsing-contexts +Vector> Document::list_of_descendant_browsing_contexts() const +{ + // 1. Let list be an empty list. + Vector> list; + + // 2. For each browsing context container container, + // whose nested browsing context is non-null and whose shadow-including root is d, in shadow-including tree order: + + // NOTE: We already store our browsing contexts in a tree structure, so we can simply collect all the descendants + // of this document's browsing context. + if (browsing_context()) { + browsing_context()->for_each_in_subtree([&](auto& context) { + list.append(context); + return IterationDecision::Continue; + }); + } + + return list; +} + } diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h index 8be46b9dc1..230d810f71 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.h +++ b/Userland/Libraries/LibWeb/DOM/Document.h @@ -31,6 +31,7 @@ #include #include #include +#include #include namespace Web::DOM { @@ -319,7 +320,7 @@ public: String visibility_state() const; // https://html.spec.whatwg.org/multipage/interaction.html#update-the-visibility-state - void update_the_visibility_state(String visibility_state); + void update_the_visibility_state(HTML::VisibilityState); void run_the_resize_steps(); void run_the_scroll_steps(); @@ -382,6 +383,9 @@ public: // https://html.spec.whatwg.org/multipage/dom.html#concept-document-policy-container HTML::PolicyContainer policy_container() const; + // https://html.spec.whatwg.org/multipage/browsers.html#list-of-the-descendant-browsing-contexts + Vector> list_of_descendant_browsing_contexts() const; + protected: virtual void visit_edges(Cell::Visitor&) override; @@ -527,7 +531,7 @@ private: HTML::PolicyContainer m_policy_container; // https://html.spec.whatwg.org/multipage/interaction.html#visibility-state - String m_visibility_state { "hidden" }; + HTML::VisibilityState m_visibility_state { HTML::VisibilityState::Hidden }; }; } diff --git a/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp b/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp index cb0e82e47b..f25f38c466 100644 --- a/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp +++ b/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp @@ -1113,7 +1113,7 @@ DOM::ExceptionOr BrowsingContext::traverse_the_history(size_t entry_index, new_document->set_page_showing(true); // 3. Update the visibility state of newDocument to "hidden". - new_document->update_the_visibility_state("hidden"); + new_document->update_the_visibility_state(VisibilityState::Hidden); // 4. Fire a page transition event named pageshow at newDocument's relevant global object with true. auto& window = verify_cast(relevant_global_object(*new_document)); @@ -1248,18 +1248,55 @@ BrowsingContext const* BrowsingContext::the_one_permitted_sandboxed_navigator() } // https://html.spec.whatwg.org/multipage/browsers.html#document-family -bool BrowsingContext::document_family_contains(DOM::Document const& document) const +Vector> BrowsingContext::document_family() const { - HashTable family; - + HashTable documents; for (auto& entry : m_session_history) { if (!entry.document) continue; - if (family.set(entry.document) == AK::HashSetResult::ReplacedExistingEntry) + if (documents.set(entry.document.ptr()) == AK::HashSetResult::ReplacedExistingEntry) continue; - // FIXME: The document family of a Document object consists of the union of all the document families of the browsing contexts in the list of the descendant browsing contexts of the Document object. + for (auto& context : entry.document->list_of_descendant_browsing_contexts()) { + for (auto& document : context->document_family()) { + documents.set(document.ptr()); + } + } } - return family.contains(&document); + Vector> family; + for (auto* document : documents) { + family.append(*document); + } + return family; } + +// https://html.spec.whatwg.org/multipage/browsers.html#document-family +bool BrowsingContext::document_family_contains(DOM::Document const& document) const +{ + return document_family().first_matching([&](auto& entry) { return entry.ptr() == &document; }).has_value(); +} + +VisibilityState BrowsingContext::system_visibility_state() const +{ + return m_system_visibility_state; +} + +// https://html.spec.whatwg.org/multipage/interaction.html#system-visibility-state +void BrowsingContext::set_system_visibility_state(VisibilityState visibility_state) +{ + if (m_system_visibility_state == visibility_state) + return; + m_system_visibility_state = visibility_state; + + // When a user-agent determines that the system visibility state for top-level browsing context context + // has changed to newState, it must queue a task on the user interaction task source to update + // the visibility state of all the Document objects in the top-level browsing context's document family with newState. + auto document_family = top_level_browsing_context().document_family(); + queue_global_task(Task::Source::UserInteraction, Bindings::main_thread_vm().current_realm()->global_object(), [visibility_state, document_family = move(document_family)]() mutable { + for (auto& document : document_family) { + document->update_the_visibility_state(visibility_state); + } + }); +} + } diff --git a/Userland/Libraries/LibWeb/HTML/BrowsingContext.h b/Userland/Libraries/LibWeb/HTML/BrowsingContext.h index ac841455b2..05c986f636 100644 --- a/Userland/Libraries/LibWeb/HTML/BrowsingContext.h +++ b/Userland/Libraries/LibWeb/HTML/BrowsingContext.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -158,8 +159,12 @@ public: // https://html.spec.whatwg.org/multipage/browsing-the-web.html#traverse-the-history DOM::ExceptionOr traverse_the_history(size_t entry_index, HistoryHandlingBehavior = HistoryHandlingBehavior::Default, bool explicit_history_navigation = false); + Vector> document_family() const; bool document_family_contains(DOM::Document const&) const; + VisibilityState system_visibility_state() const; + void set_system_visibility_state(VisibilityState); + private: explicit BrowsingContext(Page&, HTML::BrowsingContextContainer*); @@ -205,6 +210,9 @@ private: // https://html.spec.whatwg.org/multipage/browsers.html#tlbc-group RefPtr m_group; + + // https://html.spec.whatwg.org/multipage/interaction.html#system-visibility-state + VisibilityState m_system_visibility_state { VisibilityState::Hidden }; }; HTML::Origin determine_the_origin(BrowsingContext const& browsing_context, Optional url, SandboxingFlagSet sandbox_flags, Optional invocation_origin); diff --git a/Userland/Libraries/LibWeb/HTML/BrowsingContextContainer.cpp b/Userland/Libraries/LibWeb/HTML/BrowsingContextContainer.cpp index 52ad2f8abf..48ed8132a4 100644 --- a/Userland/Libraries/LibWeb/HTML/BrowsingContextContainer.cpp +++ b/Userland/Libraries/LibWeb/HTML/BrowsingContextContainer.cpp @@ -18,6 +18,12 @@ namespace Web::HTML { +HashTable& BrowsingContextContainer::all_instances() +{ + static HashTable set; + return set; +} + BrowsingContextContainer::BrowsingContextContainer(DOM::Document& document, DOM::QualifiedName qualified_name) : HTMLElement(document, move(qualified_name)) { diff --git a/Userland/Libraries/LibWeb/HTML/BrowsingContextContainer.h b/Userland/Libraries/LibWeb/HTML/BrowsingContextContainer.h index 7009a3867a..f1b14c246a 100644 --- a/Userland/Libraries/LibWeb/HTML/BrowsingContextContainer.h +++ b/Userland/Libraries/LibWeb/HTML/BrowsingContextContainer.h @@ -16,6 +16,8 @@ class BrowsingContextContainer : public HTMLElement { public: virtual ~BrowsingContextContainer() override; + static HashTable& all_instances(); + BrowsingContext* nested_browsing_context() { return m_nested_browsing_context; } BrowsingContext const* nested_browsing_context() const { return m_nested_browsing_context; } diff --git a/Userland/Libraries/LibWeb/HTML/VisibilityState.h b/Userland/Libraries/LibWeb/HTML/VisibilityState.h new file mode 100644 index 0000000000..002f893748 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/VisibilityState.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2022, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +namespace Web::HTML { + +enum VisibilityState { + Hidden, + Visible, +}; + +} diff --git a/Userland/Libraries/LibWebView/OutOfProcessWebView.cpp b/Userland/Libraries/LibWebView/OutOfProcessWebView.cpp index 88bb329817..4ece205b89 100644 --- a/Userland/Libraries/LibWebView/OutOfProcessWebView.cpp +++ b/Userland/Libraries/LibWebView/OutOfProcessWebView.cpp @@ -540,4 +540,14 @@ void OutOfProcessWebView::focusout_event(GUI::FocusEvent&) client().async_set_has_focus(false); } +void OutOfProcessWebView::show_event(GUI::ShowEvent&) +{ + client().async_set_system_visibility_state(true); +} + +void OutOfProcessWebView::hide_event(GUI::HideEvent&) +{ + client().async_set_system_visibility_state(false); +} + } diff --git a/Userland/Libraries/LibWebView/OutOfProcessWebView.h b/Userland/Libraries/LibWebView/OutOfProcessWebView.h index 2132fbf290..d5990d8ebb 100644 --- a/Userland/Libraries/LibWebView/OutOfProcessWebView.h +++ b/Userland/Libraries/LibWebView/OutOfProcessWebView.h @@ -131,6 +131,8 @@ private: virtual void screen_rects_change_event(GUI::ScreenRectsChangeEvent&) override; virtual void focusin_event(GUI::FocusEvent&) override; virtual void focusout_event(GUI::FocusEvent&) override; + virtual void show_event(GUI::ShowEvent&) override; + virtual void hide_event(GUI::HideEvent&) override; // ^AbstractScrollableWidget virtual void did_scroll() override; diff --git a/Userland/Services/WebContent/ConnectionFromClient.cpp b/Userland/Services/WebContent/ConnectionFromClient.cpp index 65a0cb3ca7..2f4107b62c 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.cpp +++ b/Userland/Services/WebContent/ConnectionFromClient.cpp @@ -526,4 +526,13 @@ void ConnectionFromClient::request_file(NonnullRefPtr& file_re async_did_request_file(file_request->path(), id); } + +void ConnectionFromClient::set_system_visibility_state(bool visible) +{ + m_page_host->page().top_level_browsing_context().set_system_visibility_state( + visible + ? Web::HTML::VisibilityState::Visible + : Web::HTML::VisibilityState::Hidden); +} + } diff --git a/Userland/Services/WebContent/ConnectionFromClient.h b/Userland/Services/WebContent/ConnectionFromClient.h index 676426630f..74be424709 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.h +++ b/Userland/Services/WebContent/ConnectionFromClient.h @@ -68,6 +68,7 @@ private: virtual void set_has_focus(bool) override; virtual void set_is_scripting_enabled(bool) override; virtual void handle_file_return(i32 error, Optional const& file, i32 request_id) override; + virtual void set_system_visibility_state(bool visible) override; virtual void js_console_input(String const&) override; virtual void run_javascript(String const&) override; diff --git a/Userland/Services/WebContent/WebContentServer.ipc b/Userland/Services/WebContent/WebContentServer.ipc index 8b7bb8a3b0..8eb8e8277b 100644 --- a/Userland/Services/WebContent/WebContentServer.ipc +++ b/Userland/Services/WebContent/WebContentServer.ipc @@ -54,4 +54,6 @@ endpoint WebContentServer get_session_storage_entries() => (OrderedHashMap entries) handle_file_return(i32 error, Optional file, i32 request_id) =| + + set_system_visibility_state(bool visible) =| }