From da451467b175e1a845a1714165838af3684b5393 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 17 Sep 2022 17:40:26 +0200 Subject: [PATCH] LibWeb: Implement basic "scroll" events at the document level --- Userland/Libraries/LibWeb/DOM/Document.cpp | 28 +++++++++++++++++++ Userland/Libraries/LibWeb/DOM/Document.h | 10 +++++++ .../Libraries/LibWeb/HTML/BrowsingContext.cpp | 20 +++++++++++++ .../Libraries/LibWeb/HTML/BrowsingContext.h | 2 ++ .../LibWeb/HTML/EventLoop/EventLoop.cpp | 5 +++- Userland/Libraries/LibWeb/HTML/EventNames.h | 1 + 6 files changed, 65 insertions(+), 1 deletion(-) diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index 81bd541624..753de161d0 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -323,6 +323,11 @@ void Document::visit_edges(Cell::Visitor& visitor) for (auto& node_iterator : m_node_iterators) visitor.visit(node_iterator); + + for (auto& target : m_pending_scroll_event_targets) + visitor.visit(target.ptr()); + for (auto& target : m_pending_scrollend_event_targets) + visitor.visit(target.ptr()); } // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-write @@ -1570,6 +1575,29 @@ void Document::run_the_resize_steps() update_layout(); } +// https://w3c.github.io/csswg-drafts/cssom-view-1/#document-run-the-scroll-steps +void Document::run_the_scroll_steps() +{ + // 1. For each item target in doc’s pending scroll event targets, in the order they were added to the list, run these substeps: + for (auto& target : m_pending_scroll_event_targets) { + // 1. If target is a Document, fire an event named scroll that bubbles at target and fire an event named scroll at the VisualViewport that is associated with target. + if (is(*target)) { + auto event = DOM::Event::create(window(), HTML::EventNames::scroll); + event->set_bubbles(true); + target->dispatch_event(*event); + // FIXME: Fire at the associated VisualViewport + } + // 2. Otherwise, fire an event named scroll at target. + else { + auto event = DOM::Event::create(window(), HTML::EventNames::scroll); + target->dispatch_event(*event); + } + } + + // 2. Empty doc’s pending scroll event targets. + m_pending_scroll_event_targets.clear(); +} + void Document::add_media_query_list(JS::NonnullGCPtr media_query_list) { m_media_query_lists.append(*media_query_list); diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h index f4b2ece073..34351563b3 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.h +++ b/Userland/Libraries/LibWeb/DOM/Document.h @@ -317,6 +317,7 @@ public: String visibility_state() const; void run_the_resize_steps(); + void run_the_scroll_steps(); void evaluate_media_queries_and_report_changes(); void add_media_query_list(JS::NonnullGCPtr); @@ -360,6 +361,9 @@ public: String domain() const; void set_domain(String const& domain); + auto& pending_scroll_event_targets() { return m_pending_scroll_event_targets; } + auto& pending_scrollend_event_targets() { return m_pending_scrollend_event_targets; } + protected: virtual void visit_edges(Cell::Visitor&) override; @@ -456,6 +460,12 @@ private: // Used by run_the_resize_steps(). Gfx::IntSize m_last_viewport_size; + // https://w3c.github.io/csswg-drafts/cssom-view-1/#document-pending-scroll-event-targets + Vector> m_pending_scroll_event_targets; + + // https://w3c.github.io/csswg-drafts/cssom-view-1/#document-pending-scrollend-event-targets + Vector> m_pending_scrollend_event_targets; + // Used by evaluate_media_queries_and_report_changes(). Vector> m_media_query_lists; diff --git a/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp b/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp index a68ce1c19a..e06dc64ef0 100644 --- a/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp +++ b/Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp @@ -320,6 +320,7 @@ void BrowsingContext::set_viewport_rect(Gfx::IntRect const& rect) if (m_viewport_scroll_offset != rect.location()) { m_viewport_scroll_offset = rect.location(); + scroll_offset_did_change(); did_change = true; } @@ -785,4 +786,23 @@ DOM::Document* BrowsingContext::active_document() return m_active_document.cell(); } +void BrowsingContext::scroll_offset_did_change() +{ + // https://w3c.github.io/csswg-drafts/cssom-view-1/#scrolling-events + // Whenever a viewport gets scrolled (whether in response to user interaction or by an API), the user agent must run these steps: + + // 1. Let doc be the viewport’s associated Document. + auto* doc = active_document(); + VERIFY(doc); + + // 2. If doc is already in doc’s pending scroll event targets, abort these steps. + for (auto& target : doc->pending_scroll_event_targets()) { + if (target.ptr() == doc) + return; + } + + // 3. Append doc to doc’s pending scroll event targets. + doc->pending_scroll_event_targets().append(*doc); +} + } diff --git a/Userland/Libraries/LibWeb/HTML/BrowsingContext.h b/Userland/Libraries/LibWeb/HTML/BrowsingContext.h index 55ae5444d9..54f0c1d9a8 100644 --- a/Userland/Libraries/LibWeb/HTML/BrowsingContext.h +++ b/Userland/Libraries/LibWeb/HTML/BrowsingContext.h @@ -129,6 +129,8 @@ private: void reset_cursor_blink_cycle(); + void scroll_offset_did_change(); + WeakPtr m_page; FrameLoader m_loader; diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp index 5602e5545d..3d61816132 100644 --- a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp +++ b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp @@ -169,7 +169,10 @@ void EventLoop::process() document.run_the_resize_steps(); }); - // FIXME: 8. For each fully active Document in docs, run the scroll steps for that Document, passing in now as the timestamp. [CSSOMVIEW] + // 8. For each fully active Document in docs, run the scroll steps for that Document, passing in now as the timestamp. [CSSOMVIEW] + for_each_fully_active_document_in_docs([&](DOM::Document& document) { + document.run_the_scroll_steps(); + }); // 9. For each fully active Document in docs, evaluate media queries and report changes for that Document, passing in now as the timestamp. [CSSOMVIEW] for_each_fully_active_document_in_docs([&](DOM::Document& document) { diff --git a/Userland/Libraries/LibWeb/HTML/EventNames.h b/Userland/Libraries/LibWeb/HTML/EventNames.h index 36d4d44d3a..1aaa6eb91d 100644 --- a/Userland/Libraries/LibWeb/HTML/EventNames.h +++ b/Userland/Libraries/LibWeb/HTML/EventNames.h @@ -49,6 +49,7 @@ namespace Web::HTML::EventNames { __ENUMERATE_HTML_EVENT(readystatechange) \ __ENUMERATE_HTML_EVENT(rejectionhandled) \ __ENUMERATE_HTML_EVENT(reset) \ + __ENUMERATE_HTML_EVENT(scroll) \ __ENUMERATE_HTML_EVENT(securitypolicyviolation) \ __ENUMERATE_HTML_EVENT(select) \ __ENUMERATE_HTML_EVENT(slotchange) \