diff --git a/Libraries/LibWeb/Animations/KeyframeEffect.cpp b/Libraries/LibWeb/Animations/KeyframeEffect.cpp index 1e48cb9f2dd..02518616261 100644 --- a/Libraries/LibWeb/Animations/KeyframeEffect.cpp +++ b/Libraries/LibWeb/Animations/KeyframeEffect.cpp @@ -974,9 +974,9 @@ void KeyframeEffect::update_computed_properties() // We mark layout tree for rebuild starting from parent element to correctly invalidate // "display" property change to/from "contents" value. if (auto parent_element = target->parent_element()) { - parent_element->set_needs_layout_tree_update(true); + parent_element->set_needs_layout_tree_update(true, DOM::SetNeedsLayoutTreeUpdateReason::KeyframeEffect); } else { - target->set_needs_layout_tree_update(true); + target->set_needs_layout_tree_update(true, DOM::SetNeedsLayoutTreeUpdateReason::KeyframeEffect); } } if (invalidation.repaint) { diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 6cdafcae499..8cfee2c7ba4 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -1446,12 +1446,11 @@ void Document::update_layout(UpdateLayoutReason reason) // We mark layout tree for rebuild starting from parent element to correctly invalidate // "display" property change to/from "contents" value. if (auto parent_element = node.parent_element()) { - parent_element->set_needs_layout_tree_update(true); + parent_element->set_needs_layout_tree_update(true, SetNeedsLayoutTreeUpdateReason::StyleChange); } else { - node.set_needs_layout_tree_update(true); + node.set_needs_layout_tree_update(true, SetNeedsLayoutTreeUpdateReason::StyleChange); } } - invalidation |= node_invalidation; node.set_needs_style_update(false); invalidation |= node_invalidation; @@ -6562,6 +6561,18 @@ StringView to_string(SetNeedsLayoutReason reason) VERIFY_NOT_REACHED(); } +StringView to_string(SetNeedsLayoutTreeUpdateReason reason) +{ + switch (reason) { +#define ENUMERATE_SET_NEEDS_LAYOUT_TREE_UPDATE_REASON(e) \ + case SetNeedsLayoutTreeUpdateReason::e: \ + return #e##sv; + ENUMERATE_SET_NEEDS_LAYOUT_TREE_UPDATE_REASONS(ENUMERATE_SET_NEEDS_LAYOUT_TREE_UPDATE_REASON) +#undef ENUMERATE_SET_NEEDS_LAYOUT_TREE_UPDATE_REASON + } + VERIFY_NOT_REACHED(); +} + StringView to_string(InvalidateLayoutTreeReason reason) { switch (reason) { diff --git a/Libraries/LibWeb/DOM/Element.cpp b/Libraries/LibWeb/DOM/Element.cpp index 53f690b14cc..7ddcd37b06e 100644 --- a/Libraries/LibWeb/DOM/Element.cpp +++ b/Libraries/LibWeb/DOM/Element.cpp @@ -883,7 +883,7 @@ WebIDL::ExceptionOr Element::set_inner_html(StringView value) if (context->is_connected()) { // NOTE: Since the DOM has changed, we have to rebuild the layout tree. - context->set_needs_layout_tree_update(true); + context->set_needs_layout_tree_update(true, DOM::SetNeedsLayoutTreeUpdateReason::ElementSetInnerHTML); } } diff --git a/Libraries/LibWeb/DOM/Node.cpp b/Libraries/LibWeb/DOM/Node.cpp index fd81e42b1ba..29ad2c138ad 100644 --- a/Libraries/LibWeb/DOM/Node.cpp +++ b/Libraries/LibWeb/DOM/Node.cpp @@ -229,7 +229,7 @@ void Node::set_text_content(Optional const& maybe_content) if (is_connected()) { invalidate_style(StyleInvalidationReason::NodeSetTextContent); - set_needs_layout_tree_update(true); + set_needs_layout_tree_update(true, SetNeedsLayoutTreeUpdateReason::NodeSetTextContent); } document().bump_dom_tree_version(); @@ -802,9 +802,9 @@ void Node::insert_before(GC::Ref node, GC::Ptr child, bool suppress_ if (is_connected()) { if (layout_node() && layout_node()->display().is_contents() && parent_element()) { - parent_element()->set_needs_layout_tree_update(true); + parent_element()->set_needs_layout_tree_update(true, SetNeedsLayoutTreeUpdateReason::NodeInsertBeforeWithDisplayContents); } - set_needs_layout_tree_update(true); + set_needs_layout_tree_update(true, SetNeedsLayoutTreeUpdateReason::NodeInsertBefore); } document().bump_dom_tree_version(); @@ -913,7 +913,7 @@ void Node::remove(bool suppress_observers) // NOTE: If we didn't have a layout node before, rebuilding the layout tree isn't gonna give us one // after we've been removed from the DOM. if (layout_node()) - parent->set_needs_layout_tree_update(true); + parent->set_needs_layout_tree_update(true, SetNeedsLayoutTreeUpdateReason::NodeRemove); } // 11. Remove node from its parent’s children. @@ -1399,17 +1399,34 @@ EventTarget* Node::get_parent(Event const&) return parent(); } -void Node::set_needs_layout_tree_update(bool value) +void Node::set_needs_layout_tree_update(bool value, SetNeedsLayoutTreeUpdateReason reason) { if (m_needs_layout_tree_update == value) return; m_needs_layout_tree_update = value; + if constexpr (UPDATE_LAYOUT_DEBUG) { + if (m_needs_layout_tree_update) { + // NOTE: We check some conditions here to avoid debug spam in documents that don't do layout. + auto navigable = this->navigable(); + bool any_ancestor_needs_layout_tree_update = false; + for (auto* ancestor = parent_or_shadow_host(); ancestor; ancestor = ancestor->parent_or_shadow_host()) { + if (ancestor->needs_layout_tree_update()) { + any_ancestor_needs_layout_tree_update = true; + break; + } + } + if (!any_ancestor_needs_layout_tree_update && navigable && navigable->active_document() == &document()) { + dbgln("Need tree update ({}): {}", to_string(reason), debug_description()); + } + } + } + // NOTE: If this is a shadow root, we need to propagate the layout tree update to the host. if (is_shadow_root()) { auto& shadow_root = static_cast(*this); if (auto host = shadow_root.host()) - host->set_needs_layout_tree_update(value); + host->set_needs_layout_tree_update(value, reason); } if (m_needs_layout_tree_update) { diff --git a/Libraries/LibWeb/DOM/Node.h b/Libraries/LibWeb/DOM/Node.h index 5459be632d7..4718ebf2a42 100644 --- a/Libraries/LibWeb/DOM/Node.h +++ b/Libraries/LibWeb/DOM/Node.h @@ -108,6 +108,27 @@ enum class SetNeedsLayoutReason { [[nodiscard]] StringView to_string(SetNeedsLayoutReason); +#define ENUMERATE_SET_NEEDS_LAYOUT_TREE_UPDATE_REASONS(X) \ + X(ElementSetInnerHTML) \ + X(HTMLInputElementSrcAttribute) \ + X(HTMLObjectElementUpdateLayoutAndChildObjects) \ + X(KeyframeEffect) \ + X(NodeInsertBefore) \ + X(NodeInsertBeforeWithDisplayContents) \ + X(NodeRemove) \ + X(NodeSetTextContent) \ + X(None) \ + X(SVGGraphicsElementTransformChange) \ + X(StyleChange) + +enum class SetNeedsLayoutTreeUpdateReason { +#define ENUMERATE_SET_NEEDS_LAYOUT_TREE_UPDATE_REASON(e) e, + ENUMERATE_SET_NEEDS_LAYOUT_TREE_UPDATE_REASONS(ENUMERATE_SET_NEEDS_LAYOUT_TREE_UPDATE_REASON) +#undef ENUMERATE_SET_NEEDS_LAYOUT_TREE_UPDATE_REASON +}; + +[[nodiscard]] StringView to_string(SetNeedsLayoutTreeUpdateReason); + class Node : public EventTarget , public TreeNode { WEB_PLATFORM_OBJECT(Node, EventTarget); @@ -307,7 +328,7 @@ public: virtual bool is_child_allowed(Node const&) const { return true; } [[nodiscard]] bool needs_layout_tree_update() const { return m_needs_layout_tree_update; } - void set_needs_layout_tree_update(bool); + void set_needs_layout_tree_update(bool, SetNeedsLayoutTreeUpdateReason); [[nodiscard]] bool child_needs_layout_tree_update() const { return m_child_needs_layout_tree_update; } void set_child_needs_layout_tree_update(bool b) { m_child_needs_layout_tree_update = b; } diff --git a/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 6f78ca35ec0..72ee5fef62b 100644 --- a/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -1515,7 +1515,7 @@ WebIDL::ExceptionOr HTMLInputElement::handle_src_attribute(String const& v }); m_load_event_delayer.clear(); - set_needs_layout_tree_update(true); + set_needs_layout_tree_update(true, DOM::SetNeedsLayoutTreeUpdateReason::HTMLInputElementSrcAttribute); }, [this, &realm]() { // 2. Otherwise, if the fetching process fails without a response from the remote server, or completes but the diff --git a/Libraries/LibWeb/HTML/HTMLObjectElement.cpp b/Libraries/LibWeb/HTML/HTMLObjectElement.cpp index fd5c75e6cba..85a82cc0d1b 100644 --- a/Libraries/LibWeb/HTML/HTMLObjectElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLObjectElement.cpp @@ -564,7 +564,7 @@ void HTMLObjectElement::update_layout_and_child_objects(Representation represent invalidate_style(DOM::StyleInvalidationReason::HTMLObjectElementUpdateLayoutAndChildObjects); if (auto parent_element = this->parent_element()) - parent_element->set_needs_layout_tree_update(true); + parent_element->set_needs_layout_tree_update(true, DOM::SetNeedsLayoutTreeUpdateReason::HTMLObjectElementUpdateLayoutAndChildObjects); } // https://html.spec.whatwg.org/multipage/interaction.html#dom-tabindex diff --git a/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Libraries/LibWeb/Layout/TreeBuilder.cpp index 04eeafde4d7..0315c647b07 100644 --- a/Libraries/LibWeb/Layout/TreeBuilder.cpp +++ b/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -446,7 +446,7 @@ void TreeBuilder::update_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& // go through the DOM tree and remove any old layout & paint nodes since they are now all stale. if (!layout_node) { dom_node.for_each_in_inclusive_subtree([&](auto& node) { - node.set_needs_layout_tree_update(false); + node.set_needs_layout_tree_update(false, DOM::SetNeedsLayoutTreeUpdateReason::None); node.set_child_needs_layout_tree_update(false); auto layout_node = node.layout_node(); if (layout_node && layout_node->parent()) { @@ -555,7 +555,7 @@ void TreeBuilder::update_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& update_layout_tree(*node, context, should_create_layout_node ? MustCreateSubtree::Yes : MustCreateSubtree::No); } shadow_root->set_child_needs_layout_tree_update(false); - shadow_root->set_needs_layout_tree_update(false); + shadow_root->set_needs_layout_tree_update(false, DOM::SetNeedsLayoutTreeUpdateReason::None); } else { // This is the same as as(dom_node).for_each_child for (auto* node = as(dom_node).first_child(); node; node = node->next_sibling()) @@ -637,7 +637,7 @@ void TreeBuilder::update_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& m_quote_nesting_level = prior_quote_nesting_level; } - dom_node.set_needs_layout_tree_update(false); + dom_node.set_needs_layout_tree_update(false, DOM::SetNeedsLayoutTreeUpdateReason::None); dom_node.set_child_needs_layout_tree_update(false); } diff --git a/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp b/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp index 218649d0d8e..89b044800b4 100644 --- a/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp +++ b/Libraries/LibWeb/SVG/SVGGraphicsElement.cpp @@ -45,7 +45,7 @@ void SVGGraphicsElement::attribute_changed(FlyString const& name, Optional