diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp index c9696543f19..298b61244d1 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp @@ -48,7 +48,7 @@ void ImageStyleValue::load_any_resources(DOM::Document& document) m_document->set_needs_to_resolve_paint_only_properties(); // FIXME: Do less than a full repaint if possible? - navigable->set_needs_display(); + m_document->set_needs_display(); } auto image_data = m_resource_request->image_data(); diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index 81cabac4b0d..d6aa370a9fc 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -1153,7 +1153,7 @@ void Document::update_layout() // Broadcast the current viewport rect to any new paintables, so they know whether they're visible or not. inform_all_viewport_clients_about_the_current_viewport_rect(); - navigable->set_needs_display(); + m_document->set_needs_display(); set_needs_to_resolve_paint_only_properties(); paintable()->assign_scroll_frames(); @@ -5367,4 +5367,76 @@ void Document::set_cached_navigable(JS::GCPtr navigable) m_cached_navigable = navigable.ptr(); } +void Document::set_needs_display() +{ + set_needs_display(viewport_rect()); +} + +void Document::set_needs_display(CSSPixelRect const&) +{ + // FIXME: Ignore updates outside the visible viewport rect. + // This requires accounting for fixed-position elements in the input rect, which we don't do yet. + + m_needs_repaint = true; + + auto navigable = this->navigable(); + if (!navigable) + return; + + if (navigable->is_traversable()) { + Web::HTML::main_thread_event_loop().schedule(); + return; + } + + if (navigable->container()) { + navigable->container()->document().set_needs_display(); + } +} + +RefPtr Document::record_display_list(PaintConfig config) +{ + auto display_list = Painting::DisplayList::create(); + Painting::DisplayListRecorder display_list_recorder(display_list); + + if (config.canvas_fill_rect.has_value()) { + display_list_recorder.fill_rect(config.canvas_fill_rect.value(), CSS::SystemColor::canvas()); + } + + auto viewport_rect = page().css_to_device_rect(this->viewport_rect()); + Gfx::IntRect bitmap_rect { {}, viewport_rect.size().to_type() }; + + display_list_recorder.fill_rect(bitmap_rect, background_color()); + if (!paintable()) { + VERIFY_NOT_REACHED(); + } + + Web::PaintContext context(display_list_recorder, page().palette(), page().client().device_pixels_per_css_pixel()); + context.set_device_viewport_rect(viewport_rect); + context.set_should_show_line_box_borders(config.should_show_line_box_borders); + context.set_should_paint_overlay(config.paint_overlay); + context.set_has_focus(config.has_focus); + + update_paint_and_hit_testing_properties_if_needed(); + + auto& viewport_paintable = *paintable(); + + viewport_paintable.refresh_scroll_state(); + + viewport_paintable.paint_all_phases(context); + + display_list->set_device_pixels_per_css_pixel(page().client().device_pixels_per_css_pixel()); + + Vector> scroll_state; + scroll_state.resize(viewport_paintable.scroll_state.size()); + for (auto& [_, scrollable_frame] : viewport_paintable.scroll_state) { + scroll_state[scrollable_frame->id] = scrollable_frame; + } + + display_list->set_scroll_state(move(scroll_state)); + + m_needs_repaint = false; + + return display_list; +} + } diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h index fb38e9f9e19..6b2d1bff0de 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.h +++ b/Userland/Libraries/LibWeb/DOM/Document.h @@ -699,6 +699,18 @@ public: JS::GCPtr cached_navigable(); void set_cached_navigable(JS::GCPtr); + [[nodiscard]] bool needs_repaint() const { return m_needs_repaint; } + void set_needs_display(); + void set_needs_display(CSSPixelRect const&); + + struct PaintConfig { + bool paint_overlay { false }; + bool should_show_line_box_borders { false }; + bool has_focus { false }; + Optional canvas_fill_rect {}; + }; + RefPtr record_display_list(PaintConfig); + protected: virtual void initialize(JS::Realm&) override; virtual void visit_edges(Cell::Visitor&) override; @@ -972,6 +984,8 @@ private: // NOTE: This is WeakPtr, not GCPtr, on purpose. We don't want the document to keep some old detached navigable alive. WeakPtr m_cached_navigable; + + bool m_needs_repaint { false }; }; template<> diff --git a/Userland/Libraries/LibWeb/HTML/AudioTrack.cpp b/Userland/Libraries/LibWeb/HTML/AudioTrack.cpp index 570e06ad778..72dd57d543f 100644 --- a/Userland/Libraries/LibWeb/HTML/AudioTrack.cpp +++ b/Userland/Libraries/LibWeb/HTML/AudioTrack.cpp @@ -31,7 +31,7 @@ AudioTrack::AudioTrack(JS::Realm& realm, JS::NonnullGCPtr medi , m_audio_plugin(Platform::AudioCodecPlugin::create(move(loader)).release_value_but_fixme_should_propagate_errors()) { m_audio_plugin->on_playback_position_updated = [this](auto position) { - if (auto const* paintable = m_media_element->paintable()) + if (auto* paintable = m_media_element->paintable()) paintable->set_needs_display(); auto playback_position = static_cast(position.to_milliseconds()) / 1000.0; diff --git a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp index 9f99bcd8ed1..a3345545177 100644 --- a/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp +++ b/Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp @@ -209,7 +209,7 @@ void EventLoop::process() // loop processing. for_each_fully_active_document_in_docs([&](DOM::Document& document) { auto navigable = document.navigable(); - if (navigable && !navigable->has_a_rendering_opportunity() && navigable->needs_repaint()) + if (navigable && !navigable->has_a_rendering_opportunity() && document.needs_repaint()) schedule(); if (navigable && navigable->has_a_rendering_opportunity()) return; @@ -328,7 +328,7 @@ void EventLoop::process() // 16. For each fully active Document in docs, update the rendering or user interface of that Document and its browsing context to reflect the current state. for_each_fully_active_document_in_docs([&](DOM::Document& document) { auto navigable = document.navigable(); - if (navigable && navigable->needs_repaint()) { + if (navigable && document.needs_repaint()) { auto* browsing_context = document.browsing_context(); auto& page = browsing_context->page(); if (navigable->is_traversable()) { @@ -341,7 +341,7 @@ void EventLoop::process() // FIXME: Not in the spec: If there is a screenshot request queued, process it now. // This prevents tests deadlocking on screenshot requests on macOS. for (auto& document : docs) { - if (document->page().top_level_traversable()->needs_repaint()) + if (document->needs_repaint()) document->page().client().process_screenshot_requests(); } diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.cpp b/Userland/Libraries/LibWeb/HTML/Navigable.cpp index be327ed21d2..18e93ebe44e 100644 --- a/Userland/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Userland/Libraries/LibWeb/HTML/Navigable.cpp @@ -2000,9 +2000,10 @@ void Navigable::set_viewport_size(CSSPixelSize size) document->invalidate_style(); document->set_needs_layout(); } - set_needs_display(); if (auto document = active_document()) { + document->set_needs_display(); + document->inform_all_viewport_clients_about_the_current_viewport_rect(); // Schedule the HTML event loop to ensure that a `resize` event gets fired. @@ -2015,9 +2016,9 @@ void Navigable::perform_scroll_of_viewport(CSSPixelPoint new_position) if (m_viewport_scroll_offset != new_position) { m_viewport_scroll_offset = new_position; scroll_offset_did_change(); - set_needs_display(); if (auto document = active_document()) { + document->set_needs_display(); document->set_needs_to_refresh_scroll_state(true); document->inform_all_viewport_clients_about_the_current_viewport_rect(); } @@ -2029,24 +2030,9 @@ void Navigable::perform_scroll_of_viewport(CSSPixelPoint new_position) void Navigable::set_needs_display() { - set_needs_display(viewport_rect()); -} - -void Navigable::set_needs_display(CSSPixelRect const&) -{ - // FIXME: Ignore updates outside the visible viewport rect. - // This requires accounting for fixed-position elements in the input rect, which we don't do yet. - - m_needs_repaint = true; - - if (is(*this)) { - // Schedule the main thread event loop, which will, in turn, schedule a repaint. - Web::HTML::main_thread_event_loop().schedule(); - return; + if (auto document = active_document(); document) { + document->set_needs_display(); } - - if (container() && container()->paintable()) - container()->paintable()->set_needs_display(); } // https://html.spec.whatwg.org/#rendering-opportunity @@ -2090,59 +2076,6 @@ void Navigable::inform_the_navigation_api_about_aborting_navigation() })); } -RefPtr Navigable::record_display_list(PaintConfig config) -{ - auto document = active_document(); - if (!document) - return {}; - - auto display_list = Painting::DisplayList::create(); - Painting::DisplayListRecorder display_list_recorder(display_list); - - if (config.canvas_fill_rect.has_value()) { - display_list_recorder.fill_rect(config.canvas_fill_rect.value(), CSS::SystemColor::canvas()); - } - - auto const& page = traversable_navigable()->page(); - auto viewport_rect = page.css_to_device_rect(this->viewport_rect()); - Gfx::IntRect bitmap_rect { {}, viewport_rect.size().to_type() }; - - auto background_color = document->background_color(); - - display_list_recorder.fill_rect(bitmap_rect, background_color); - if (!document->paintable()) { - VERIFY_NOT_REACHED(); - } - - Web::PaintContext context(display_list_recorder, page.palette(), page.client().device_pixels_per_css_pixel()); - context.set_device_viewport_rect(viewport_rect); - context.set_should_show_line_box_borders(config.should_show_line_box_borders); - context.set_should_paint_overlay(config.paint_overlay); - context.set_has_focus(config.has_focus); - - document->update_paint_and_hit_testing_properties_if_needed(); - - auto& viewport_paintable = *document->paintable(); - - viewport_paintable.refresh_scroll_state(); - - viewport_paintable.paint_all_phases(context); - - display_list->set_device_pixels_per_css_pixel(page.client().device_pixels_per_css_pixel()); - - Vector> scroll_state; - scroll_state.resize(viewport_paintable.scroll_state.size()); - for (auto& [_, scrollable_frame] : viewport_paintable.scroll_state) { - scroll_state[scrollable_frame->id] = scrollable_frame; - } - - display_list->set_scroll_state(move(scroll_state)); - - m_needs_repaint = false; - - return display_list; -} - // https://html.spec.whatwg.org/multipage/browsing-the-web.html#event-uni UserNavigationInvolvement user_navigation_involvement(DOM::Event const& event) { diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.h b/Userland/Libraries/LibWeb/HTML/Navigable.h index cdf3deb9b51..3745f171e96 100644 --- a/Userland/Libraries/LibWeb/HTML/Navigable.h +++ b/Userland/Libraries/LibWeb/HTML/Navigable.h @@ -170,7 +170,6 @@ public: void perform_scroll_of_viewport(CSSPixelPoint position); void set_needs_display(); - void set_needs_display(CSSPixelRect const&); void set_is_popup(TokenizedFeature::Popup is_popup) { m_is_popup = is_popup; } @@ -179,16 +178,6 @@ public: [[nodiscard]] TargetSnapshotParams snapshot_target_snapshot_params(); - [[nodiscard]] bool needs_repaint() const { return m_needs_repaint; } - - struct PaintConfig { - bool paint_overlay { false }; - bool should_show_line_box_borders { false }; - bool has_focus { false }; - Optional canvas_fill_rect {}; - }; - RefPtr record_display_list(PaintConfig); - Page& page() { return m_page; } Page const& page() const { return m_page; } @@ -245,8 +234,6 @@ private: CSSPixelSize m_size; CSSPixelPoint m_viewport_scroll_offset; - bool m_needs_repaint { false }; - Web::EventHandler m_event_handler; }; diff --git a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp index 596e08547ea..63d87817b6a 100644 --- a/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp +++ b/Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp @@ -1189,12 +1189,16 @@ JS::GCPtr TraversableNavigable::currently_focused_area() void TraversableNavigable::paint(DevicePixelRect const& content_rect, Painting::BackingStore& target, PaintOptions paint_options) { - HTML::Navigable::PaintConfig paint_config; + auto document = active_document(); + if (!document) + return; + + DOM::Document::PaintConfig paint_config; paint_config.paint_overlay = paint_options.paint_overlay == PaintOptions::PaintOverlay::Yes; paint_config.should_show_line_box_borders = paint_options.should_show_line_box_borders; paint_config.has_focus = paint_options.has_focus; paint_config.canvas_fill_rect = Gfx::IntRect { {}, content_rect.size() }; - auto display_list = record_display_list(paint_config); + auto display_list = document->record_display_list(paint_config); if (!display_list) { return; } diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.cpp b/Userland/Libraries/LibWeb/Page/EventHandler.cpp index 27bbdb0941f..c8c9e2053a5 100644 --- a/Userland/Libraries/LibWeb/Page/EventHandler.cpp +++ b/Userland/Libraries/LibWeb/Page/EventHandler.cpp @@ -582,7 +582,7 @@ bool EventHandler::handle_mousemove(CSSPixelPoint viewport_position, CSSPixelPoi if (should_set_cursor_position) document.set_cursor_position(DOM::Position::create(realm, *hit->dom_node(), *start_index)); - document.navigable()->set_needs_display(); + document.set_needs_display(); } } } diff --git a/Userland/Libraries/LibWeb/Painting/NestedBrowsingContextPaintable.cpp b/Userland/Libraries/LibWeb/Painting/NestedBrowsingContextPaintable.cpp index 56dd0a869fb..fea23264140 100644 --- a/Userland/Libraries/LibWeb/Painting/NestedBrowsingContextPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/NestedBrowsingContextPaintable.cpp @@ -54,11 +54,11 @@ void NestedBrowsingContextPaintable::paint(PaintContext& context, PaintPhase pha context.display_list_recorder().add_clip_rect(clip_rect.to_type()); - HTML::Navigable::PaintConfig paint_config; + DOM::Document::PaintConfig paint_config; paint_config.paint_overlay = context.should_paint_overlay(); paint_config.should_show_line_box_borders = context.should_show_line_box_borders(); paint_config.has_focus = context.has_focus(); - auto display_list = const_cast(hosted_document)->navigable()->record_display_list(paint_config); + auto display_list = const_cast(hosted_document)->record_display_list(paint_config); context.display_list_recorder().paint_nested_display_list(display_list, context.enclosing_device_rect(absolute_rect).to_type()); context.display_list_recorder().restore(); diff --git a/Userland/Libraries/LibWeb/Painting/Paintable.cpp b/Userland/Libraries/LibWeb/Painting/Paintable.cpp index e07a2365a76..79dbbe456e8 100644 --- a/Userland/Libraries/LibWeb/Painting/Paintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/Paintable.cpp @@ -127,25 +127,24 @@ void Paintable::invalidate_stacking_context() m_stacking_context = nullptr; } -void Paintable::set_needs_display() const +void Paintable::set_needs_display() { auto* containing_block = this->containing_block(); if (!containing_block) return; - auto navigable = this->navigable(); - if (!navigable) - return; + + auto& document = const_cast(this->document()); if (is(*this)) { auto const& fragments = static_cast(this)->fragments(); for (auto const& fragment : fragments) - navigable->set_needs_display(fragment.absolute_rect()); + document.set_needs_display(fragment.absolute_rect()); } if (!is(*containing_block)) return; static_cast(*containing_block).for_each_fragment([&](auto& fragment) { - navigable->set_needs_display(fragment.absolute_rect()); + document.set_needs_display(fragment.absolute_rect()); return IterationDecision::Continue; }); } diff --git a/Userland/Libraries/LibWeb/Painting/Paintable.h b/Userland/Libraries/LibWeb/Painting/Paintable.h index ea3edc56f80..112e549cec4 100644 --- a/Userland/Libraries/LibWeb/Painting/Paintable.h +++ b/Userland/Libraries/LibWeb/Painting/Paintable.h @@ -198,7 +198,7 @@ public: JS::GCPtr navigable() const; - virtual void set_needs_display() const; + virtual void set_needs_display(); PaintableBox* containing_block() const { diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index d57a1d64d2f..5e8996bd719 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -907,10 +907,9 @@ TraversalDecision PaintableWithLines::hit_test(CSSPixelPoint position, HitTestTy return TraversalDecision::Continue; } -void PaintableBox::set_needs_display() const +void PaintableBox::set_needs_display() { - if (auto navigable = this->navigable()) - navigable->set_needs_display(absolute_rect()); + document().set_needs_display(absolute_rect()); } Optional PaintableBox::get_masking_area() const diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.h b/Userland/Libraries/LibWeb/Painting/PaintableBox.h index 0dc9143b76f..43d8af93acb 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.h +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.h @@ -128,7 +128,7 @@ public: DOM::Node const* dom_node() const { return layout_box().dom_node(); } DOM::Node* dom_node() { return layout_box().dom_node(); } - virtual void set_needs_display() const override; + virtual void set_needs_display() override; virtual void apply_scroll_offset(PaintContext&, PaintPhase) const override; virtual void reset_scroll_offset(PaintContext&, PaintPhase) const override; diff --git a/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp b/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp index 33007243c1a..bcad3099601 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGDecodedImageData.cpp @@ -92,7 +92,7 @@ RefPtr SVGDecodedImageData::render(Gfx::IntSize size) const m_document->navigable()->set_viewport_size(size.to_type()); m_document->update_layout(); - auto display_list = m_document->navigable()->record_display_list({}); + auto display_list = m_document->record_display_list({}); if (!display_list) return {}; diff --git a/Userland/Services/WebContent/ConnectionFromClient.cpp b/Userland/Services/WebContent/ConnectionFromClient.cpp index f20ff2205eb..dcd38c7926e 100644 --- a/Userland/Services/WebContent/ConnectionFromClient.cpp +++ b/Userland/Services/WebContent/ConnectionFromClient.cpp @@ -359,7 +359,7 @@ void ConnectionFromClient::debug_request(u64 page_id, ByteString const& request, if (request == "set-line-box-borders") { bool state = argument == "on"; page->set_should_show_line_box_borders(state); - page->page().top_level_traversable()->set_needs_display(page->page().top_level_traversable()->viewport_rect()); + page->page().top_level_traversable()->set_needs_display(); return; }