diff --git a/Libraries/LibWeb/Animations/KeyframeEffect.cpp b/Libraries/LibWeb/Animations/KeyframeEffect.cpp index b7db16ffdea..3a776e4a136 100644 --- a/Libraries/LibWeb/Animations/KeyframeEffect.cpp +++ b/Libraries/LibWeb/Animations/KeyframeEffect.cpp @@ -962,7 +962,7 @@ void KeyframeEffect::update_computed_properties() } if (invalidation.relayout) - document.set_needs_layout(); + document.set_needs_layout(DOM::SetNeedsLayoutReason::KeyframeEffect); if (invalidation.rebuild_layout_tree) document.invalidate_layout_tree(DOM::InvalidateLayoutTreeReason::KeyframeEffect); if (invalidation.repaint) { diff --git a/Libraries/LibWeb/DOM/CharacterData.cpp b/Libraries/LibWeb/DOM/CharacterData.cpp index 63e84fdbde3..8b0347195c8 100644 --- a/Libraries/LibWeb/DOM/CharacterData.cpp +++ b/Libraries/LibWeb/DOM/CharacterData.cpp @@ -146,7 +146,7 @@ WebIDL::ExceptionOr CharacterData::replace_data(size_t offset, size_t coun if (auto* layout_node = this->layout_node(); layout_node && layout_node->is_text_node()) static_cast(*layout_node).invalidate_text_for_rendering(); - document().set_needs_layout(); + document().set_needs_layout(SetNeedsLayoutReason::CharacterDataReplaceData); document().bump_character_data_version(); if (m_grapheme_segmenter) diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 26d9eaf3810..011429087b2 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -1211,10 +1211,16 @@ Optional Document::encoding_parse_and_serialize_url(StringView url) cons return parsed_url->serialize(); } -void Document::set_needs_layout() +void Document::set_needs_layout(SetNeedsLayoutReason reason) { if (m_needs_layout) return; + if constexpr (UPDATE_LAYOUT_DEBUG) { + // NOTE: We check some conditions here to avoid debug spam in documents that don't do layout. + auto navigable = this->navigable(); + if (m_layout_root && navigable && navigable->active_document() == this) + dbgln_if(UPDATE_LAYOUT_DEBUG, "NEED LAYOUT {}", to_string(reason)); + } m_needs_layout = true; schedule_layout_update(); } @@ -1527,7 +1533,7 @@ void Document::update_style() if (!invalidation.is_none()) invalidate_display_list(); if (invalidation.relayout) - set_needs_layout(); + set_needs_layout(SetNeedsLayoutReason::StyleChange); if (invalidation.rebuild_stacking_context_tree) invalidate_stacking_context_tree(); m_needs_full_style_update = false; @@ -6405,6 +6411,18 @@ void Document::set_onvisibilitychange(WebIDL::CallbackType* value) set_event_handler_attribute(HTML::EventNames::visibilitychange, value); } +StringView to_string(SetNeedsLayoutReason reason) +{ + switch (reason) { +#define ENUMERATE_SET_NEEDS_LAYOUT_REASON(e) \ + case SetNeedsLayoutReason::e: \ + return #e##sv; + ENUMERATE_SET_NEEDS_LAYOUT_REASONS(ENUMERATE_SET_NEEDS_LAYOUT_REASON) +#undef ENUMERATE_SET_NEEDS_LAYOUT_REASON + } + VERIFY_NOT_REACHED(); +} + StringView to_string(InvalidateLayoutTreeReason reason) { switch (reason) { diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index 07a4488da49..ed7dacfbebe 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -50,6 +50,26 @@ enum class QuirksMode { Yes }; +#define ENUMERATE_SET_NEEDS_LAYOUT_REASONS(X) \ + X(CharacterDataReplaceData) \ + X(FinalizeACrossDocumentNavigation) \ + X(HTMLImageElementReactToChangesInTheEnvironment) \ + X(HTMLImageElementUpdateTheImageData) \ + X(HTMLVideoElementSetVideoTrack) \ + X(KeyframeEffect) \ + X(LayoutTreeUpdate) \ + X(NavigableSetViewportSize) \ + X(SVGImageElementFetchTheDocument) \ + X(StyleChange) + +enum class SetNeedsLayoutReason { +#define ENUMERATE_SET_NEEDS_LAYOUT_REASON(e) e, + ENUMERATE_SET_NEEDS_LAYOUT_REASONS(ENUMERATE_SET_NEEDS_LAYOUT_REASON) +#undef ENUMERATE_SET_NEEDS_LAYOUT_REASON +}; + +[[nodiscard]] StringView to_string(SetNeedsLayoutReason); + #define ENUMERATE_INVALIDATE_LAYOUT_TREE_REASONS(X) \ X(DocumentAddAnElementToTheTopLayer) \ X(DocumentRequestAnElementToBeRemovedFromTheTopLayer) \ @@ -340,7 +360,7 @@ public: void update_paint_and_hit_testing_properties_if_needed(); void update_animated_style_if_needed(); - void set_needs_layout(); + void set_needs_layout(SetNeedsLayoutReason); void invalidate_layout_tree(InvalidateLayoutTreeReason); void invalidate_stacking_context_tree(); diff --git a/Libraries/LibWeb/DOM/Node.cpp b/Libraries/LibWeb/DOM/Node.cpp index 588e20897f9..e5c14bf0570 100644 --- a/Libraries/LibWeb/DOM/Node.cpp +++ b/Libraries/LibWeb/DOM/Node.cpp @@ -1405,7 +1405,7 @@ void Node::set_needs_layout_tree_update(bool value) break; ancestor->m_child_needs_layout_tree_update = true; } - document().set_needs_layout(); + document().set_needs_layout(SetNeedsLayoutReason::LayoutTreeUpdate); } } diff --git a/Libraries/LibWeb/HTML/HTMLImageElement.cpp b/Libraries/LibWeb/HTML/HTMLImageElement.cpp index 29eaf73e522..3e416926463 100644 --- a/Libraries/LibWeb/HTML/HTMLImageElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLImageElement.cpp @@ -762,7 +762,7 @@ void HTMLImageElement::add_callbacks_to_image_request(GC::Ref imag document().list_of_available_images().add(key, *image_data, true); set_needs_style_update(true); - document().set_needs_layout(); + document().set_needs_layout(DOM::SetNeedsLayoutReason::HTMLImageElementUpdateTheImageData); // 4. If maybe omit events is not set or previousURL is not equal to urlString, then fire an event named load at the img element. if (!maybe_omit_events || previous_url != url_string.serialize()) @@ -902,7 +902,7 @@ void HTMLImageElement::react_to_changes_in_the_environment() image_request->prepare_for_presentation(*this); // FIXME: This is ad-hoc, updating the layout here should probably be handled by prepare_for_presentation(). set_needs_style_update(true); - document().set_needs_layout(); + document().set_needs_layout(DOM::SetNeedsLayoutReason::HTMLImageElementReactToChangesInTheEnvironment); // 7. Fire an event named load at the img element. dispatch_event(DOM::Event::create(realm(), HTML::EventNames::load)); diff --git a/Libraries/LibWeb/HTML/HTMLVideoElement.cpp b/Libraries/LibWeb/HTML/HTMLVideoElement.cpp index 49b1da1b0af..8d83f6cba7c 100644 --- a/Libraries/LibWeb/HTML/HTMLVideoElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLVideoElement.cpp @@ -111,7 +111,7 @@ u32 HTMLVideoElement::video_height() const void HTMLVideoElement::set_video_track(GC::Ptr video_track) { set_needs_style_update(true); - document().set_needs_layout(); + document().set_needs_layout(DOM::SetNeedsLayoutReason::HTMLVideoElementSetVideoTrack); if (m_video_track) m_video_track->pause_video({}); diff --git a/Libraries/LibWeb/HTML/Navigable.cpp b/Libraries/LibWeb/HTML/Navigable.cpp index ed98f6fda9b..b0006c7b527 100644 --- a/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Libraries/LibWeb/HTML/Navigable.cpp @@ -2100,7 +2100,7 @@ void finalize_a_cross_document_navigation(GC::Ref navigable, HistoryH // AD-HOC: If we're inside a navigable container, let's trigger a relayout in the container document. // This allows size negotiation between the containing document and SVG documents to happen. if (auto container = navigable->container()) { - container->document().set_needs_layout(); + container->document().set_needs_layout(DOM::SetNeedsLayoutReason::FinalizeACrossDocumentNavigation); } } @@ -2223,7 +2223,7 @@ void Navigable::set_viewport_size(CSSPixelSize size) if (auto document = active_document()) { // NOTE: Resizing the viewport changes the reference value for viewport-relative CSS lengths. document->invalidate_style(DOM::StyleInvalidationReason::NavigableSetViewportSize); - document->set_needs_layout(); + document->set_needs_layout(DOM::SetNeedsLayoutReason::NavigableSetViewportSize); } if (auto document = active_document()) { diff --git a/Libraries/LibWeb/SVG/SVGImageElement.cpp b/Libraries/LibWeb/SVG/SVGImageElement.cpp index b83137e50d2..338e97beebd 100644 --- a/Libraries/LibWeb/SVG/SVGImageElement.cpp +++ b/Libraries/LibWeb/SVG/SVGImageElement.cpp @@ -168,7 +168,7 @@ void SVGImageElement::fetch_the_document(URL::URL const& url) m_animation_timer->start(); } set_needs_style_update(true); - document().set_needs_layout(); + document().set_needs_layout(DOM::SetNeedsLayoutReason::SVGImageElementFetchTheDocument); dispatch_event(DOM::Event::create(realm(), HTML::EventNames::load)); },