width_style_value_from_attribute() and
height_style_value_from_attribute() were showing up as hot functions
when profiling YouTube Music. They call parse_css_value() on every
invocation, but the underlying attributes rarely change. The call path
is SVGSVGBox::natural_size() → negotiate_natural_metrics() → these
functions, so they are invoked on every layout pass.
Use the existing bitmap() method which caches the ImmutableBitmap,
instead of creating a fresh snapshot from the painting surface on
every paint call.
This was regressed by d9e04ec9e8 which replaced draw_painting_surface
with a per-call snapshot to remove the DrawPaintingSurface dependency,
but didn't use the existing bitmap cache.
SVG elements (except the outermost <svg>) use SVG's coordinate system,
not the CSS box model, so CSS positioning doesn't apply to them.
This adds SVGElement::adjust_computed_style() to force position:static
on all SVG elements except the outermost <svg> element (which has no
owner_svg_element()). SVGSymbolElement's existing override now calls
Base::adjust_computed_style() to inherit this behavior.
With this in place, the FIXME in layout_absolutely_positioned_element()
for SVG boxes becomes unreachable and is replaced with
VERIFY_NOT_REACHED().
The last consumer of draw_painting_surface now snapshots the surface
into an ImmutableBitmap and uses draw_scaled_immutable_bitmap instead,
removing the final dependency on DrawPaintingSurface.
This is already fully absolutized as part of the computation process so
we can resolve it in `ComputedProperties::stroke_dasharray` instead of
persisting it as a `NumberOrCalculated`
Remove unused/redundant includes from Element.h:
- AK/IterationDecision.h (redundant)
- ARIA/AttributeNames.h (redundant via ARIAMixin.h)
- CSS/CascadedProperties.h (redundant via PseudoElement.h)
- CSS/StylePropertyMapReadOnly.h (pointer types only)
- HTML/LazyLoadingElement.h (unused in header)
Extract IntersectionObserverRegistration struct from
IntersectionObserver.h into its own lightweight header.
This breaks the heavy transitive include chain through
IntersectionObserverEntry.h and Geometry/DOMRect.h that
was pulled into every file including Element.h.
Indirect recompilation impact reductions:
- IntersectionObserver.h: ~1387 -> ~27 files
- LazyLoadingElement.h: ~1387 -> ~1002 files
Remove includes from Node.h that are only needed for forward
declarations (AccessibilityTreeNode.h, XMLSerializer.h,
JsonObjectSerializer.h). Extract StyleInvalidationReason and
FragmentSerializationMode enums into standalone lightweight
headers so downstream headers (CSSStyleSheet.h, CSSStyleProperties.h,
HTMLParser.h) can include just the enum they need instead of all of
Node.h. Replace Node.h with forward declarations in headers that only
use Node by pointer/reference.
This breaks the circular dependency between Node.h and
AccessibilityTreeNode.h, reducing AccessibilityTreeNode.h's
recompilation footprint from ~1399 to ~25 files.
These attributes are consumed during layout in SVGFormattingContext to
compute the viewbox transform. They don't affect the layout tree
structure, so a layout-only invalidation is sufficient instead of a
full layout tree rebuild.
...and SVGDecodedImageData.h
These headers only use Document via forward-declarable references and
smart pointers. Add explicit Document.h includes to .cpp files that
were relying on the transitive include.
This reduces the recompilation cascade when Document.h is modified,
cutting off the transitive path through ~30 SVG element headers.
Move the inline try_resolve_url_to() template body in
SVGGraphicsElement.h to a non-template helper in the .cpp file to
avoid needing Document.h and ShadowRoot.h in the header.
Add explicit includes to files that relied on the transitive dependency.
The SVG `transform` attribute is stored on the DOM element and read
directly during layout by
`SVGFormattingContext::layout_graphics_element()`. Since changing the
transform doesn't affect which DOM nodes produce layout boxes or how
they're structured, we only need to re-run layout on the existing tree
instead of rebuild it from scratch.
SVGPolylineElement::get_path() produces a path based on the points
attribute. During layout, this path is copied into paintables. If the
points attribute changes after layout, the path stored in the paintable
becomes stale. Fix by calling set_needs_layout_update() when it changes
so the path is recomputed.
SVGPolygonElement::get_path() produces a path based on the points
attribute. During layout, this path is copied into paintables. If the
points attribute changes after layout, the path stored in the paintable
becomes stale. Fix by calling set_needs_layout_update() when it changes
so the path is recomputed.
SVGPathElement::get_path() produces a path based on the d attribute.
During layout, this path is copied into paintables. If the d attribute
changes after layout, the path stored in the paintable becomes stale.
Fix by calling set_needs_layout_update() when it changes so the path
is recomputed.
SVGLineElement::get_path() produces a path based on x1, y1, x2, and
y2 attributes. During layout, this path is copied into paintables.
If any of these attributes change after layout, the path stored in the
paintable becomes stale. Fix by calling set_needs_layout_update() when
a geometry attribute changes so the path is recomputed.
Every user of this actually wants an ancestor in the flat tree - taking
things like `<slot>` into account. So rename it and adjust its behavior
to use that.
No observable behavior change — this is a refactoring of how SVG
transforms are computed for mask and clip content.
Previously, SVGGraphicsElement::get_transform() computed accumulated
transforms by walking the DOM tree. This didn't work correctly for masks
and clips because their DOM structure differs from layout: in the DOM,
mask/clip elements are siblings or ancestors of their targets, but in
the layout tree they become children of the target element. Walking the
DOM tree caused transforms from the target's ancestors to incorrectly
leak into mask/clip content.
The fix walks the layout tree instead of the DOM tree, which means
transform computation must happen during layout (where we have access to
the layout tree structure) rather than on-demand from the DOM element.
This moves the logic to SVGFormattingContext and removes get_transform()
since it can no longer serve its purpose — the DOM element only provides
element_transform() for its own transform now.
During layout, we walk up the layout tree and stop at mask/clip
boundaries, ensuring mask/clip content stays in its own coordinate
space. The target's accumulated transform is applied separately at paint
time.
SVG images loaded as <img> elements must not execute scripts per spec.
Previously, SVGScriptElement::process_the_script_element() did not
check whether scripting was disabled, so script processing was
triggered via the children_changed() callback during XML parsing,
causing a nested event loop spin.
Fix this by:
- Disabling scripting on the SVG image's Page
- Passing XMLScriptingSupport::Disabled to the XML document builder
- Checking is_scripting_disabled() in SVGScriptElement before
processing any script element
- Logging a diagnostic when SVG XML parsing fails (previously the
parse result was silently discarded)
Previously, referencing an element that eventually pointed back to
the original <use> element would cause a stack overflow.
We now look at the referenced element and follow every <use> element
in its subtree and aim to detect any duplicates. If so, we consider
that referenced element invalid.
This ensures that we are explicitly declaring the allocator to use when
allocating a cell(-inheriting) type, instead of silently falling back
to size-based allocation.
Since this is done in allocate_cell, this will only be detected for
types that are actively being allocated. However, since that means
they're _not_ being allocated, that means it's safe to not declare
an allocator to use for those. For example, the base TypedArray<T>,
which is never directly allocated and only the defined specializations
are ever allocated.
- Add WindowManagement to PolicyControlledFeature enum
- Add screen_count() virtual method to PageClient
- Store all screen rects in WebContent::PageClient, derive both
screen_rect() and screen_count() from stored data
- Implement screen_count() overrides in SVGPageClient and PageHost
- Replace FIXME stub in Screen.cpp with spec-compliant implementation
When an SVGElement is removed from a <use> element's shadow tree, we
need to check if it was in a use element's shadow root to avoid
notifying use elements about the removal of their own clones.
The check was incorrectly using root() instead of old_root. Since the
element has already been detached when removed_from() is called,
root() no longer returns the shadow root, causing the early-return
check to fail.
This led to O(n) recursion depth when clearing a use element's shadow
tree, as each removed clone would trigger another round of
remove_all_children() on use elements referencing the same ID.
For XHTML documents, resolve named character entities (e.g., )
using the HTML entity table via a getEntity SAX callback. This avoids
parsing a large embedded DTD on every document and matches the approach
used by Blink and WebKit.
This also removes the now-unused DTD infrastructure:
- Remove resolve_external_resource callback from Parser::Options
- Remove resolve_xml_resource() function and its ~60KB embedded DTD
- Remove all call sites passing the unused callback
This allows us to render SVGs on the GPU even when requesting a bitmap.
Since SVG bitmaps are an ImmutableBitmap, it can store a painting
surface snapshot, so we don't need to render bitmaps on the CPU.
Fixes Shopify homepage freezing for ~20 seconds rendering an SVG
background-image on the CPU on my machine.
`SVGUseElement::referenced_element()` previously passed
`m_href->fragment()` directly to `Document::get_element_by_id()`. URL
fragments are stored in their serialized form, so references to elements
whose IDs contain non-ASCII characters would fail to resolve.