LibWeb: Skip layout update for disconnected elements querying metrics
Introduce Document::update_layout_if_needed_for_node() which only calls update_layout() when the node is connected. Use it at all call sites that query layout metrics (offsets, client dimensions, image size, SVG bounding box, etc.) so disconnected elements no longer trigger an unnecessary layout.
This commit is contained in:
parent
63113cf5c9
commit
8f7bb7dd2e
7 changed files with 24 additions and 17 deletions
|
|
@ -1462,6 +1462,12 @@ static void propagate_overflow_to_viewport(Element& root_element, Layout::Viewpo
|
|||
overflow_origin_computed_values.set_overflow_y(CSS::Overflow::Visible);
|
||||
}
|
||||
|
||||
void Document::update_layout_if_needed_for_node(Node const& node, UpdateLayoutReason reason)
|
||||
{
|
||||
if (node.is_connected())
|
||||
update_layout(reason);
|
||||
}
|
||||
|
||||
void Document::update_layout(UpdateLayoutReason reason)
|
||||
{
|
||||
auto navigable = this->navigable();
|
||||
|
|
|
|||
|
|
@ -369,6 +369,7 @@ public:
|
|||
void update_style_if_needed_for_element(AbstractElement const&);
|
||||
[[nodiscard]] bool element_needs_style_update(AbstractElement const&) const;
|
||||
void update_layout(UpdateLayoutReason);
|
||||
void update_layout_if_needed_for_node(Node const&, UpdateLayoutReason);
|
||||
[[nodiscard]] bool layout_is_up_to_date() const;
|
||||
void update_paint_and_hit_testing_properties_if_needed();
|
||||
void update_animated_style_if_needed();
|
||||
|
|
|
|||
|
|
@ -1406,7 +1406,7 @@ Vector<CSSPixelRect> Element::get_client_rects() const
|
|||
return {};
|
||||
|
||||
// NOTE: Ensure that layout is up-to-date before looking at metrics.
|
||||
const_cast<Document&>(document()).update_layout(UpdateLayoutReason::ElementGetClientRects);
|
||||
const_cast<Document&>(document()).update_layout_if_needed_for_node(*this, UpdateLayoutReason::ElementGetClientRects);
|
||||
|
||||
// 1. If the element on which it was invoked does not have an associated layout box return an empty DOMRectList
|
||||
// object and stop this algorithm.
|
||||
|
|
@ -1508,7 +1508,7 @@ int Element::client_width() const
|
|||
}
|
||||
|
||||
// NOTE: Ensure that layout is up-to-date before looking at metrics.
|
||||
const_cast<Document&>(document()).update_layout(UpdateLayoutReason::ElementClientWidth);
|
||||
const_cast<Document&>(document()).update_layout_if_needed_for_node(*this, UpdateLayoutReason::ElementClientWidth);
|
||||
|
||||
// 1. If the element has no associated CSS layout box or if the CSS layout box is inline, return zero.
|
||||
if (!paintable_box())
|
||||
|
|
@ -1533,7 +1533,7 @@ int Element::client_height() const
|
|||
}
|
||||
|
||||
// NOTE: Ensure that layout is up-to-date before looking at metrics.
|
||||
const_cast<Document&>(document()).update_layout(UpdateLayoutReason::ElementClientHeight);
|
||||
const_cast<Document&>(document()).update_layout_if_needed_for_node(*this, UpdateLayoutReason::ElementClientHeight);
|
||||
|
||||
// 1. If the element has no associated CSS layout box or if the CSS layout box is inline, return zero.
|
||||
if (!paintable_box())
|
||||
|
|
@ -3726,7 +3726,7 @@ GC::Ref<WebIDL::Promise> Element::scroll_by(HTML::ScrollToOptions options)
|
|||
bool Element::check_visibility(Optional<CheckVisibilityOptions> options)
|
||||
{
|
||||
// NOTE: Ensure that layout is up-to-date before looking at metrics.
|
||||
document().update_layout(UpdateLayoutReason::ElementCheckVisibility);
|
||||
document().update_layout_if_needed_for_node(*this, UpdateLayoutReason::ElementCheckVisibility);
|
||||
|
||||
// 1. If this does not have an associated box, return false.
|
||||
if (!paintable_box())
|
||||
|
|
|
|||
|
|
@ -462,7 +462,7 @@ static bool any_ancestor_establishes_a_fixed_position_containing_block(Layout::N
|
|||
GC::Ptr<DOM::Element> HTMLElement::scroll_parent() const
|
||||
{
|
||||
// NOTE: We have to ensure that the layout is up-to-date before querying the layout tree.
|
||||
const_cast<DOM::Document&>(document()).update_layout(DOM::UpdateLayoutReason::HTMLElementScrollParent);
|
||||
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLElementScrollParent);
|
||||
|
||||
// 1. If any of the following holds true, return null and terminate this algorithm:
|
||||
// - The element does not have an associated box.
|
||||
|
|
@ -514,7 +514,7 @@ GC::Ptr<DOM::Element> HTMLElement::scroll_parent() const
|
|||
GC::Ptr<DOM::Element> HTMLElement::offset_parent() const
|
||||
{
|
||||
// NOTE: We have to ensure that the layout is up-to-date before querying the layout tree.
|
||||
const_cast<DOM::Document&>(document()).update_layout(DOM::UpdateLayoutReason::HTMLElementOffsetParent);
|
||||
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLElementOffsetParent);
|
||||
|
||||
// 1. If any of the following holds true return null and terminate this algorithm:
|
||||
// - The element does not have an associated box.
|
||||
|
|
@ -590,7 +590,7 @@ int HTMLElement::offset_top() const
|
|||
return 0;
|
||||
|
||||
// NOTE: Ensure that layout is up-to-date before looking at metrics.
|
||||
const_cast<DOM::Document&>(document()).update_layout(DOM::UpdateLayoutReason::HTMLElementOffsetTop);
|
||||
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLElementOffsetTop);
|
||||
|
||||
if (!paintable_box())
|
||||
return 0;
|
||||
|
|
@ -632,7 +632,7 @@ int HTMLElement::offset_left() const
|
|||
return 0;
|
||||
|
||||
// NOTE: Ensure that layout is up-to-date before looking at metrics.
|
||||
const_cast<DOM::Document&>(document()).update_layout(DOM::UpdateLayoutReason::HTMLElementOffsetLeft);
|
||||
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLElementOffsetLeft);
|
||||
|
||||
if (!paintable_box())
|
||||
return 0;
|
||||
|
|
@ -670,7 +670,7 @@ int HTMLElement::offset_left() const
|
|||
int HTMLElement::offset_width() const
|
||||
{
|
||||
// NOTE: Ensure that layout is up-to-date before looking at metrics.
|
||||
const_cast<DOM::Document&>(document()).update_layout(DOM::UpdateLayoutReason::HTMLElementOffsetWidth);
|
||||
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLElementOffsetWidth);
|
||||
|
||||
// 1. If the element does not have any associated box return zero and terminate this algorithm.
|
||||
auto const* box = paintable_box();
|
||||
|
|
@ -689,7 +689,7 @@ int HTMLElement::offset_width() const
|
|||
int HTMLElement::offset_height() const
|
||||
{
|
||||
// NOTE: Ensure that layout is up-to-date before looking at metrics.
|
||||
const_cast<DOM::Document&>(document()).update_layout(DOM::UpdateLayoutReason::HTMLElementOffsetHeight);
|
||||
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLElementOffsetHeight);
|
||||
|
||||
// 1. If the element does not have any associated box return zero and terminate this algorithm.
|
||||
auto const* box = paintable_box();
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ void HTMLImageElement::set_visible_in_viewport(bool)
|
|||
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-width
|
||||
WebIDL::UnsignedLong HTMLImageElement::width() const
|
||||
{
|
||||
const_cast<DOM::Document&>(document()).update_layout(DOM::UpdateLayoutReason::HTMLImageElementWidth);
|
||||
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLImageElementWidth);
|
||||
|
||||
// Return the rendered width of the image, in CSS pixels, if the image is being rendered.
|
||||
if (auto* paintable_box = this->paintable_box())
|
||||
|
|
@ -285,7 +285,7 @@ void HTMLImageElement::set_width(WebIDL::UnsignedLong width)
|
|||
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-height
|
||||
WebIDL::UnsignedLong HTMLImageElement::height() const
|
||||
{
|
||||
const_cast<DOM::Document&>(document()).update_layout(DOM::UpdateLayoutReason::HTMLImageElementHeight);
|
||||
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLImageElementHeight);
|
||||
|
||||
// Return the rendered height of the image, in CSS pixels, if the image is being rendered.
|
||||
if (auto* paintable_box = this->paintable_box())
|
||||
|
|
@ -343,7 +343,7 @@ int HTMLImageElement::x() const
|
|||
// The x attribute, on getting, must return the scaled x-coordinate of the left border edge of the first box
|
||||
// associated with the element, relative to the initial containing block origin, ignoring any transforms that apply
|
||||
// to the element and its ancestors, or zero if there is no box.
|
||||
const_cast<DOM::Document&>(document()).update_layout(DOM::UpdateLayoutReason::HTMLImageElementX);
|
||||
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLImageElementX);
|
||||
|
||||
auto const* paintable_box = this->paintable_box();
|
||||
if (!paintable_box)
|
||||
|
|
@ -362,7 +362,7 @@ int HTMLImageElement::y() const
|
|||
// The y attribute, on getting, must return the scaled y-coordinate of the top border edge of the first box
|
||||
// associated with the element, relative to the initial containing block origin, ignoring any transforms that apply
|
||||
// to the element and its ancestors, or zero if there is no box.
|
||||
const_cast<DOM::Document&>(document()).update_layout(DOM::UpdateLayoutReason::HTMLImageElementY);
|
||||
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLImageElementY);
|
||||
|
||||
auto const* paintable_box = this->paintable_box();
|
||||
if (!paintable_box)
|
||||
|
|
|
|||
|
|
@ -2352,7 +2352,7 @@ WebIDL::ExceptionOr<void> HTMLInputElement::set_size(WebIDL::UnsignedLong value)
|
|||
// https://html.spec.whatwg.org/multipage/input.html#dom-input-height
|
||||
WebIDL::UnsignedLong HTMLInputElement::height() const
|
||||
{
|
||||
const_cast<DOM::Document&>(document()).update_layout(DOM::UpdateLayoutReason::HTMLInputElementHeight);
|
||||
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLInputElementHeight);
|
||||
|
||||
// When the input element's type attribute is not in the Image Button state, then no image is available.
|
||||
if (type_state() != TypeAttributeState::ImageButton)
|
||||
|
|
@ -2387,7 +2387,7 @@ void HTMLInputElement::set_height(WebIDL::UnsignedLong value)
|
|||
// https://html.spec.whatwg.org/multipage/input.html#dom-input-width
|
||||
WebIDL::UnsignedLong HTMLInputElement::width() const
|
||||
{
|
||||
const_cast<DOM::Document&>(document()).update_layout(DOM::UpdateLayoutReason::HTMLInputElementWidth);
|
||||
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::HTMLInputElementWidth);
|
||||
|
||||
// When the input element's type attribute is not in the Image Button state, then no image is available.
|
||||
if (type_state() != TypeAttributeState::ImageButton)
|
||||
|
|
|
|||
|
|
@ -355,7 +355,7 @@ WebIDL::ExceptionOr<GC::Ref<Geometry::DOMRect>> SVGGraphicsElement::get_b_box(Op
|
|||
// SVG coordinate space (before any viewbox or other transformations), so it should be possible to
|
||||
// calculate this from SVG geometry without a full layout tree (at least for simple cases).
|
||||
// See: https://svgwg.org/svg2-draft/coords.html#BoundingBoxes
|
||||
const_cast<DOM::Document&>(document()).update_layout(DOM::UpdateLayoutReason::SVGGraphicsElementGetBBox);
|
||||
const_cast<DOM::Document&>(document()).update_layout_if_needed_for_node(*this, DOM::UpdateLayoutReason::SVGGraphicsElementGetBBox);
|
||||
if (!layout_node())
|
||||
return Geometry::DOMRect::create(realm());
|
||||
// Invert the SVG -> screen space transform.
|
||||
|
|
|
|||
Loading…
Reference in a new issue