2023-05-30 17:23:52 -03:00
/*
* Copyright ( c ) 2023 , Preston Taylor < 95388976 + PrestonLTaylor @ users . noreply . github . com >
2026-01-11 19:00:25 -03:00
* Copyright ( c ) 2024 - 2026 , Shannon Booth < shannon @ serenityos . org >
2023-05-30 17:23:52 -03:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2026-01-11 19:00:25 -03:00
# include <LibGC/HeapHashTable.h>
2023-05-30 17:23:52 -03:00
# include <LibWeb/Bindings/Intrinsics.h>
2026-04-18 05:54:06 -03:00
# include <LibWeb/Bindings/SVGUseElement.h>
2023-05-30 17:23:52 -03:00
# include <LibWeb/DOM/Document.h>
2024-07-27 11:34:05 -03:00
# include <LibWeb/DOM/DocumentLoadEventDelayer.h>
2023-05-30 17:23:52 -03:00
# include <LibWeb/DOM/ElementFactory.h>
# include <LibWeb/DOM/Event.h>
# include <LibWeb/DOM/ShadowRoot.h>
2024-07-27 11:34:05 -03:00
# include <LibWeb/HTML/PotentialCORSRequest.h>
2025-04-10 11:52:33 -03:00
# include <LibWeb/HTML/SharedResourceRequest.h>
2023-08-19 14:42:31 -03:00
# include <LibWeb/Layout/Box.h>
2024-03-10 10:41:00 -03:00
# include <LibWeb/Layout/SVGGraphicsBox.h>
2023-05-30 17:23:52 -03:00
# include <LibWeb/Namespace.h>
# include <LibWeb/SVG/AttributeNames.h>
2024-07-27 11:34:05 -03:00
# include <LibWeb/SVG/SVGDecodedImageData.h>
2023-05-30 17:23:52 -03:00
# include <LibWeb/SVG/SVGSVGElement.h>
2025-08-26 09:44:50 -03:00
# include <LibWeb/SVG/SVGSymbolElement.h>
2023-05-30 17:23:52 -03:00
# include <LibWeb/SVG/SVGUseElement.h>
namespace Web : : SVG {
2024-11-14 12:01:23 -03:00
GC_DEFINE_ALLOCATOR ( SVGUseElement ) ;
2023-11-19 15:47:52 -03:00
2023-05-30 17:23:52 -03:00
SVGUseElement : : SVGUseElement ( DOM : : Document & document , DOM : : QualifiedName qualified_name )
2025-11-26 09:03:59 -03:00
: SVGGraphicsElement ( document , move ( qualified_name ) )
2023-05-30 17:23:52 -03:00
{
}
2023-08-07 03:41:28 -03:00
void SVGUseElement : : initialize ( JS : : Realm & realm )
2023-05-30 17:23:52 -03:00
{
2024-03-16 09:13:08 -03:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( SVGUseElement ) ;
2025-04-20 11:22:57 -03:00
Base : : initialize ( realm ) ;
2023-05-30 17:23:52 -03:00
2025-08-07 11:32:28 -03:00
// NOTE: The spec says "The shadow tree is open (inspectable by script), but read-only."
// This doesn't actually match other browsers, and there's a spec issue to change it.
// Spec bug: https://github.com/w3c/svgwg/issues/875
auto shadow_root = realm . create < DOM : : ShadowRoot > ( document ( ) , * this , Bindings : : ShadowRootMode : : Closed ) ;
2026-02-02 06:49:39 -03:00
shadow_root - > set_user_agent_internal ( true ) ;
2023-05-30 17:23:52 -03:00
// The user agent must create a use-element shadow tree whose host is the ‘ use’ element itself
set_shadow_root ( shadow_root ) ;
2024-11-13 13:50:17 -03:00
m_document_observer = realm . create < DOM : : DocumentObserver > ( realm , document ( ) ) ;
2023-09-27 12:23:48 -03:00
m_document_observer - > set_document_completely_loaded ( [ this ] ( ) {
2026-05-31 07:10:47 -03:00
// The href processing path already populated the shadow tree for resolved references,
// unless the referenced subtree changed while the document was still loading.
if ( instance_root ( ) & & ! m_needs_document_complete_reclone )
return ;
m_needs_document_complete_reclone = false ;
2023-05-30 17:23:52 -03:00
clone_element_tree_as_our_shadow_tree ( referenced_element ( ) ) ;
2023-09-27 12:23:48 -03:00
} ) ;
2023-05-30 17:23:52 -03:00
}
void SVGUseElement : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2023-11-13 22:03:19 -03:00
SVGURIReferenceMixin : : visit_edges ( visitor ) ;
2023-05-30 17:23:52 -03:00
visitor . visit ( m_document_observer ) ;
2024-08-03 00:27:08 -03:00
visitor . visit ( m_resource_request ) ;
2023-05-30 17:23:52 -03:00
}
2026-06-10 16:58:06 -03:00
void SVGUseElement : : adopted_from ( DOM : : Document & old_document )
{
Base : : adopted_from ( old_document ) ;
if ( m_load_event_delayer . has_value ( ) )
m_load_event_delayer . emplace ( document ( ) ) ;
}
LibWeb: Track connected SVG use elements in a per-document list
Every SVG element insertion, removal, attribute change, and children
change walked the entire document looking for use elements to notify
about possible referenced-subtree changes. On pages with large SVG
documents this is quadratic: loading chatgpt.com spent 7% of all CPU
samples in these full-document scans, nearly all of it while parsing
an SVG icon sprite sheet.
Instead, keep every use element connected to a document's node tree in
an intrusive list owned by that document, and only iterate that list
(usually empty or tiny) when an SVG element changes.
Subtleties:
- A use element inserted by the same subtree insertion as its
referenced element, but after it in tree order, used to be found by
the document-wide scan from the referenced element's insertion
steps. Now SVGUseElement::inserted() re-resolves the reference if
the shadow tree is still unpopulated. A new test covers both tree
orders.
- Node.moveBefore() runs moving steps without insertion or removal
hooks. Now SVGUseElement::moved_from() updates list membership when
moving across document-tree and shadow-tree boundaries. A new test
covers both directions.
- Removal hooks run after the subtree has been detached, so use
elements being removed alongside the changed element may still be
registered. Filter them out structurally via root().is_document(),
since Node::is_connected() is a flag that is updated in hook order
and can still be stale at this point.
2026-06-11 08:26:57 -03:00
void SVGUseElement : : inserted ( )
{
Base : : inserted ( ) ;
// Only use elements in the document's node tree react to changes of the elements they reference, mirroring the
// document-wide traversal this registry replaced. A use element inside a shadow tree is itself part of a clone.
if ( ! root ( ) . is_document ( ) )
return ;
register_for_referenced_element_changes ( ) ;
// The insertion that connected us may have inserted our referenced element along with us, with its insertion
// steps running before ours, i.e. before we were registered to be notified about changes to it. If our shadow
// tree has not been populated yet, try resolving the reference again.
// NB: While the document is still loading, the document-completely-loaded hook repopulates empty shadow trees,
// so there is nothing to do here in that case.
if ( ! instance_root ( ) & & document ( ) . is_completely_loaded ( )
& & m_href . has_value ( ) & & m_href - > fragment ( ) . has_value ( ) & & is_referenced_element_same_document ( ) ) {
clone_element_tree_as_our_shadow_tree ( referenced_element ( ) ) ;
}
}
void SVGUseElement : : removed_from ( IsSubtreeRoot is_subtree_root , Node * old_ancestor , Node & old_root )
{
Base : : removed_from ( is_subtree_root , old_ancestor , old_root ) ;
if ( old_root . is_document ( ) )
unregister_for_referenced_element_changes ( ) ;
}
void SVGUseElement : : moved_from ( IsSubtreeRoot is_subtree_root , GC : : Ptr < Node > old_ancestor )
{
Base : : moved_from ( is_subtree_root , old_ancestor ) ;
if ( ! old_ancestor )
return ;
auto was_in_document_tree = old_ancestor - > root ( ) . is_document ( ) ;
auto is_in_document_tree = root ( ) . is_document ( ) ;
if ( was_in_document_tree = = is_in_document_tree )
return ;
if ( was_in_document_tree ) {
unregister_for_referenced_element_changes ( ) ;
return ;
}
register_for_referenced_element_changes ( ) ;
if ( ! instance_root ( ) & & document ( ) . is_completely_loaded ( )
& & m_href . has_value ( ) & & m_href - > fragment ( ) . has_value ( ) & & is_referenced_element_same_document ( ) ) {
clone_element_tree_as_our_shadow_tree ( referenced_element ( ) ) ;
}
}
2026-06-17 07:15:24 -03:00
void SVGUseElement : : finalize ( )
{
Base : : finalize ( ) ;
// A GC'ed "use" element may never run its removal steps. So, unlink it from the document's list of use elements
// here — to avoid destroying a still-linked list node.
unregister_for_referenced_element_changes ( ) ;
}
LibWeb: Track connected SVG use elements in a per-document list
Every SVG element insertion, removal, attribute change, and children
change walked the entire document looking for use elements to notify
about possible referenced-subtree changes. On pages with large SVG
documents this is quadratic: loading chatgpt.com spent 7% of all CPU
samples in these full-document scans, nearly all of it while parsing
an SVG icon sprite sheet.
Instead, keep every use element connected to a document's node tree in
an intrusive list owned by that document, and only iterate that list
(usually empty or tiny) when an SVG element changes.
Subtleties:
- A use element inserted by the same subtree insertion as its
referenced element, but after it in tree order, used to be found by
the document-wide scan from the referenced element's insertion
steps. Now SVGUseElement::inserted() re-resolves the reference if
the shadow tree is still unpopulated. A new test covers both tree
orders.
- Node.moveBefore() runs moving steps without insertion or removal
hooks. Now SVGUseElement::moved_from() updates list membership when
moving across document-tree and shadow-tree boundaries. A new test
covers both directions.
- Removal hooks run after the subtree has been detached, so use
elements being removed alongside the changed element may still be
registered. Filter them out structurally via root().is_document(),
since Node::is_connected() is a flag that is updated in hook order
and can still be stale at this point.
2026-06-11 08:26:57 -03:00
void SVGUseElement : : register_for_referenced_element_changes ( )
{
if ( m_list_node . is_in_list ( ) )
return ;
document ( ) . register_svg_use_element ( { } , * this ) ;
}
void SVGUseElement : : unregister_for_referenced_element_changes ( )
{
if ( ! m_list_node . is_in_list ( ) )
return ;
document ( ) . unregister_svg_use_element ( { } , * this ) ;
}
2024-11-14 10:14:16 -03:00
void SVGUseElement : : attribute_changed ( FlyString const & name , Optional < String > const & old_value , Optional < String > const & value , Optional < FlyString > const & namespace_ )
2023-05-30 17:23:52 -03:00
{
2024-11-14 10:14:16 -03:00
Base : : attribute_changed ( name , old_value , value , namespace_ ) ;
2023-05-30 17:23:52 -03:00
// https://svgwg.org/svg2-draft/struct.html#UseLayout
if ( name = = SVG : : AttributeNames : : x ) {
2023-11-19 02:10:36 -03:00
m_x = AttributeParser : : parse_coordinate ( value . value_or ( String { } ) ) ;
2023-05-30 17:23:52 -03:00
} else if ( name = = SVG : : AttributeNames : : y ) {
2023-11-19 02:10:36 -03:00
m_y = AttributeParser : : parse_coordinate ( value . value_or ( String { } ) ) ;
2024-03-26 21:08:14 -03:00
} else if ( name = = SVG : : AttributeNames : : href | | name = = " xlink:href " _fly_string ) {
2024-07-27 00:43:56 -03:00
// When the ‘ href’ attribute is set (or, in the absence of an ‘ href’ attribute, an ‘ xlink:href’ attribute), the user agent must process the URL.
process_the_url ( value ) ;
2023-05-30 17:23:52 -03:00
}
}
2024-07-27 00:43:56 -03:00
// https://www.w3.org/TR/SVG2/linking.html#processingURL
void SVGUseElement : : process_the_url ( Optional < String > const & href )
{
2024-07-27 11:34:05 -03:00
// In all other cases, the URL is for a resource to be used in this SVG document. The user agent
// must parse the URL to separate out the target fragment from the rest of the URL, and compare
// it with the document base URL. If all parts other than the target fragment are equal, this is
// a same-document URL reference, and processing the URL must continue as indicated in Identifying
// the target element with the current document as the referenced document.
m_href = document ( ) . url ( ) . complete_url ( href . value_or ( String { } ) ) ;
2025-02-15 06:55:46 -03:00
if ( ! m_href . has_value ( ) )
2024-07-27 11:34:05 -03:00
return ;
2024-07-27 00:43:56 -03:00
2025-04-07 07:46:22 -03:00
if ( is_referenced_element_same_document ( ) ) {
2024-07-27 11:34:05 -03:00
clone_element_tree_as_our_shadow_tree ( referenced_element ( ) ) ;
} else {
2025-02-15 06:55:46 -03:00
fetch_the_document ( * m_href ) ;
2024-07-27 11:34:05 -03:00
}
2024-07-27 00:43:56 -03:00
}
2025-04-07 07:46:22 -03:00
bool SVGUseElement : : is_referenced_element_same_document ( ) const
2023-05-30 17:23:52 -03:00
{
2025-02-15 06:55:46 -03:00
return m_href - > equals ( document ( ) . url ( ) , URL : : ExcludeFragment : : Yes ) ;
2023-05-30 17:23:52 -03:00
}
2023-08-31 04:58:41 -03:00
Gfx : : AffineTransform SVGUseElement : : element_transform ( ) const
2023-05-30 17:23:52 -03:00
{
// The x and y properties define an additional transformation (translate(x,y), where x and y represent the computed value of the corresponding property)
// to be applied to the ‘ use’ element, after any transformations specified with other properties
2023-08-31 04:58:41 -03:00
return Base : : element_transform ( ) . translate ( m_x . value_or ( 0 ) , m_y . value_or ( 0 ) ) ;
}
2023-05-30 17:23:52 -03:00
void SVGUseElement : : svg_element_changed ( SVGElement & svg_element )
{
auto to_clone = referenced_element ( ) ;
if ( ! to_clone ) {
return ;
}
2023-07-03 12:08:37 -03:00
// NOTE: We need to check the ancestor because attribute_changed of a child doesn't call children_changed on the parent(s)
2023-05-30 17:23:52 -03:00
if ( to_clone = = & svg_element | | to_clone - > is_ancestor_of ( svg_element ) ) {
clone_element_tree_as_our_shadow_tree ( to_clone ) ;
}
}
2026-05-31 07:10:47 -03:00
void SVGUseElement : : svg_element_changed_before_document_complete ( SVGElement & svg_element )
{
auto to_clone = referenced_element ( ) ;
if ( ! to_clone )
return ;
// NOTE: We need to check the ancestor because attribute_changed of a child doesn't call children_changed on the parent(s)
if ( to_clone = = & svg_element | | to_clone - > is_ancestor_of ( svg_element ) )
m_needs_document_complete_reclone = true ;
}
2023-05-30 17:23:52 -03:00
void SVGUseElement : : svg_element_removed ( SVGElement & svg_element )
{
2025-04-07 07:46:22 -03:00
if ( ! m_href . has_value ( ) | | ! m_href - > fragment ( ) . has_value ( ) | | ! is_referenced_element_same_document ( ) ) {
2023-05-30 17:23:52 -03:00
return ;
}
2025-12-22 12:18:11 -03:00
auto id = String : : from_utf8_with_replacement_character ( URL : : percent_decode ( * m_href - > fragment ( ) ) , String : : WithBOMHandling : : No ) ;
if ( AK : : StringUtils : : matches ( svg_element . get_attribute_value ( " id " _fly_string ) , id ) ) {
2023-05-30 17:23:52 -03:00
shadow_root ( ) - > remove_all_children ( ) ;
}
}
2024-07-27 11:34:05 -03:00
// https://svgwg.org/svg2-draft/linking.html#processingURL-target
2026-01-11 19:00:25 -03:00
GC : : Ptr < DOM : : Element > SVGUseElement : : referenced_element ( ) const
2023-05-30 17:23:52 -03:00
{
2025-02-15 06:55:46 -03:00
if ( ! m_href . has_value ( ) )
2023-05-30 17:23:52 -03:00
return nullptr ;
2025-02-15 06:55:46 -03:00
if ( ! m_href - > fragment ( ) . has_value ( ) )
2024-07-27 11:34:05 -03:00
return nullptr ;
2025-12-22 12:18:11 -03:00
if ( is_referenced_element_same_document ( ) ) {
auto id = String : : from_utf8_with_replacement_character ( URL : : percent_decode ( * m_href - > fragment ( ) ) , String : : WithBOMHandling : : No ) ;
return document ( ) . get_element_by_id ( id ) ;
}
2024-07-27 11:34:05 -03:00
2024-08-03 00:27:08 -03:00
if ( ! m_resource_request )
2024-07-27 11:34:05 -03:00
return nullptr ;
2024-08-03 00:27:08 -03:00
auto data = m_resource_request - > image_data ( ) ;
2024-07-27 11:34:05 -03:00
if ( ! data | | ! is < SVG : : SVGDecodedImageData > ( * data ) )
return nullptr ;
2025-02-15 06:55:46 -03:00
return as < SVG : : SVGDecodedImageData > ( * data ) . svg_document ( ) . get_element_by_id ( * m_href - > fragment ( ) ) ;
2024-07-27 11:34:05 -03:00
}
// https://svgwg.org/svg2-draft/linking.html#processingURL-fetch
void SVGUseElement : : fetch_the_document ( URL : : URL const & url )
{
m_load_event_delayer . emplace ( document ( ) ) ;
2024-08-03 00:27:08 -03:00
m_resource_request = HTML : : SharedResourceRequest : : get_or_create ( realm ( ) , document ( ) . page ( ) , url ) ;
m_resource_request - > add_callbacks (
2024-07-27 11:34:05 -03:00
[ this ] {
clone_element_tree_as_our_shadow_tree ( referenced_element ( ) ) ;
m_load_event_delayer . clear ( ) ;
} ,
[ this ] {
m_load_event_delayer . clear ( ) ;
} ) ;
2024-08-03 00:27:08 -03:00
if ( m_resource_request - > needs_fetching ( ) ) {
2024-07-27 11:34:05 -03:00
auto request = HTML : : create_potential_CORS_request ( vm ( ) , url , Fetch : : Infrastructure : : Request : : Destination : : Image , HTML : : CORSSettingAttribute : : NoCORS ) ;
request - > set_client ( & document ( ) . relevant_settings_object ( ) ) ;
2024-08-03 00:27:08 -03:00
m_resource_request - > fetch_resource ( realm ( ) , request ) ;
2024-07-27 11:34:05 -03:00
}
2023-05-30 17:23:52 -03:00
}
// https://svgwg.org/svg2-draft/struct.html#UseShadowTree
2024-07-27 09:53:52 -03:00
void SVGUseElement : : clone_element_tree_as_our_shadow_tree ( Element * to_clone )
2023-05-30 17:23:52 -03:00
{
2024-07-27 09:53:52 -03:00
shadow_root ( ) - > remove_all_children ( ) ;
2023-05-30 17:23:52 -03:00
2026-02-28 20:27:44 -03:00
// https://svgwg.org/svg2-draft/struct.html#UseStyleInheritance
// When the referenced element is from the same document as the ‘ use’ element, the same document stylesheets will
// apply in both the original document and the shadow tree document fragment.
shadow_root ( ) - > set_uses_document_style_sheets ( to_clone & & is_referenced_element_same_document ( ) ) ;
2024-07-27 09:48:59 -03:00
if ( to_clone & & is_valid_reference_element ( * to_clone ) ) {
2023-05-30 17:23:52 -03:00
// The ‘ use’ element references another element, a copy of which is rendered in place of the ‘ use’ in the document.
2024-06-25 06:06:41 -03:00
auto cloned_reference_node = MUST ( to_clone - > clone_node ( nullptr , true ) ) ;
2025-08-26 09:44:50 -03:00
if ( is < SVGSVGElement > ( cloned_reference_node . ptr ( ) ) | | is < SVGSymbolElement > ( cloned_reference_node . ptr ( ) ) ) {
auto & cloned_element = as < SVGElement > ( * cloned_reference_node ) ;
// The width and height properties on the ‘ use’ element override the values for the corresponding
// properties on a referenced ‘ svg’ or ‘ symbol’ element when determining the used value for that property
// on the instance root element. However, if the computed value for the property on the ‘ use’ element is
// auto, then the property is computed as normal for the element instance.
if ( has_attribute ( AttributeNames : : width ) ) {
2025-10-31 09:30:47 -03:00
cloned_element . set_attribute_value ( AttributeNames : : width , get_attribute_value ( AttributeNames : : width ) ) ;
2025-08-26 09:44:50 -03:00
}
if ( has_attribute ( AttributeNames : : height ) ) {
2025-10-31 09:30:47 -03:00
cloned_element . set_attribute_value ( AttributeNames : : height , get_attribute_value ( AttributeNames : : height ) ) ;
2025-08-26 09:44:50 -03:00
}
}
2024-07-27 09:53:52 -03:00
shadow_root ( ) - > append_child ( cloned_reference_node ) . release_value_but_fixme_should_propagate_errors ( ) ;
2023-05-30 17:23:52 -03:00
}
}
2024-07-27 09:48:59 -03:00
bool SVGUseElement : : is_valid_reference_element ( Element const & reference_element ) const
2023-05-30 17:23:52 -03:00
{
// If the referenced element that results from resolving the URL is not an SVG element, then the reference is invalid and the ‘ use’ element is in error.
2026-01-11 19:00:25 -03:00
if ( ! reference_element . is_svg_element ( ) )
return false ;
// https://svgwg.org/svg2-draft/struct.html#UseShadowTree
// When a ‘ use’ references another element which is another ‘ use’ or whose content contains a ‘ use’ element, then
// the shadow DOM cloning approach described above is recursive. However, a set of references that directly or
// indirectly reference a element to create a circular dependency is an invalid circular reference. The ‘ use’
// element or element instance whose shadow tree would create the circular reference is in error and must not be
// rendered by the user agent.
return ! would_create_circular_reference ( reference_element ) ;
}
bool SVGUseElement : : would_create_circular_reference ( Element const & target ) const
{
auto visited = heap ( ) . allocate < GC : : HeapHashTable < GC : : Ref < Element const > > > ( ) ;
return would_create_circular_reference_impl ( target , visited ) ;
}
bool SVGUseElement : : would_create_circular_reference_impl (
Element const & target ,
GC : : HeapHashTable < GC : : Ref < Element const > > & visited ) const
{
// FIXME: I am certain there is a much more efficient way of keeping track of cycles here!
if ( visited . table ( ) . contains ( target ) )
return true ;
visited . table ( ) . set ( target ) ;
bool found_circular_reference = false ;
target . for_each_in_inclusive_subtree_of_type < SVGUseElement > ( [ & ] ( auto & element ) {
auto referenced = element . referenced_element ( ) ;
if ( ! referenced )
return TraversalDecision : : Continue ;
if ( would_create_circular_reference_impl ( * referenced , visited ) ) {
found_circular_reference = true ;
return TraversalDecision : : Break ;
}
return TraversalDecision : : Continue ;
} ) ;
visited . table ( ) . remove ( target ) ;
return found_circular_reference ;
2023-05-30 17:23:52 -03:00
}
// https://www.w3.org/TR/SVG11/shapes.html#RectElementXAttribute
2024-11-14 12:01:23 -03:00
GC : : Ref < SVGAnimatedLength > SVGUseElement : : x ( ) const
2023-05-30 17:23:52 -03:00
{
// FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported.
2025-08-26 11:51:46 -03:00
auto base_length = SVGLength : : create ( realm ( ) , 0 , m_x . value_or ( 0 ) , SVGLength : : ReadOnly : : No ) ;
auto anim_length = SVGLength : : create ( realm ( ) , 0 , m_x . value_or ( 0 ) , SVGLength : : ReadOnly : : Yes ) ;
return SVGAnimatedLength : : create ( realm ( ) , base_length , anim_length ) ;
2023-05-30 17:23:52 -03:00
}
// https://www.w3.org/TR/SVG11/shapes.html#RectElementYAttribute
2024-11-14 12:01:23 -03:00
GC : : Ref < SVGAnimatedLength > SVGUseElement : : y ( ) const
2023-05-30 17:23:52 -03:00
{
// FIXME: Populate the unit type when it is parsed (0 here is "unknown").
// FIXME: Create a proper animated value when animations are supported.
2025-08-26 11:51:46 -03:00
auto base_length = SVGLength : : create ( realm ( ) , 0 , m_y . value_or ( 0 ) , SVGLength : : ReadOnly : : No ) ;
auto anim_length = SVGLength : : create ( realm ( ) , 0 , m_y . value_or ( 0 ) , SVGLength : : ReadOnly : : Yes ) ;
return SVGAnimatedLength : : create ( realm ( ) , base_length , anim_length ) ;
2023-05-30 17:23:52 -03:00
}
2024-11-14 12:01:23 -03:00
GC : : Ref < SVGAnimatedLength > SVGUseElement : : width ( ) const
2023-05-30 17:23:52 -03:00
{
2025-08-26 11:51:46 -03:00
return fake_animated_length_fixme ( ) ;
2023-05-30 17:23:52 -03:00
}
2024-11-14 12:01:23 -03:00
GC : : Ref < SVGAnimatedLength > SVGUseElement : : height ( ) const
2023-05-30 17:23:52 -03:00
{
2025-08-26 11:51:46 -03:00
return fake_animated_length_fixme ( ) ;
2023-05-30 17:23:52 -03:00
}
// https://svgwg.org/svg2-draft/struct.html#TermInstanceRoot
2024-11-14 12:01:23 -03:00
GC : : Ptr < SVGElement > SVGUseElement : : instance_root ( ) const
2023-05-30 17:23:52 -03:00
{
2024-06-25 06:28:58 -03:00
return const_cast < DOM : : ShadowRoot & > ( * shadow_root ( ) ) . first_child_of_type < SVGElement > ( ) ;
2023-05-30 17:23:52 -03:00
}
2024-11-14 12:01:23 -03:00
GC : : Ptr < SVGElement > SVGUseElement : : animated_instance_root ( ) const
2023-05-30 17:23:52 -03:00
{
return instance_root ( ) ;
}
LibWeb: Make layout nodes refcounted
Move the layout tree from GC allocation to refcounted ownership so
removed layout and paint subtrees are destroyed synchronously instead
of waiting for the next GC sweep. This dramatically reduces GC memory
usage peaks after layout tree churn and makes it easier for memory use
to fall back after large document updates.
Update layout factories, tree traversal, SVG layout node creation,
paintable back-pointers, and pseudo-element layout links to use RefPtr
ownership.
Make display: contents follow the same shape as Blink and WebKit: the
element itself does not create a layout node, and its children are
flattened into the nearest layout parent. Wrap direct non-whitespace
text in an anonymous inline node when the boxless element contributes
inherited style to that text.
Use an internal inline wrapper for display: contents pseudo-elements
so generated content can still participate in layout, painting, hit
testing, and pseudo-element queries. Keep CSSOM reporting the computed
display value from the pseudo style, not the internal wrapper.
Remove the retained out-of-tree layout node list and its testing hook,
since the flattened model does not need a side owner for boxless
elements. Add coverage for inherited text style, dynamic insertion
order, pseudo-element hit testing, and computed style queries.
2026-06-07 12:50:33 -03:00
RefPtr < Layout : : Node > SVGUseElement : : create_layout_node ( CSS : : ComputedProperties const & style )
2023-08-19 14:42:31 -03:00
{
LibWeb: Make layout nodes refcounted
Move the layout tree from GC allocation to refcounted ownership so
removed layout and paint subtrees are destroyed synchronously instead
of waiting for the next GC sweep. This dramatically reduces GC memory
usage peaks after layout tree churn and makes it easier for memory use
to fall back after large document updates.
Update layout factories, tree traversal, SVG layout node creation,
paintable back-pointers, and pseudo-element layout links to use RefPtr
ownership.
Make display: contents follow the same shape as Blink and WebKit: the
element itself does not create a layout node, and its children are
flattened into the nearest layout parent. Wrap direct non-whitespace
text in an anonymous inline node when the boxless element contributes
inherited style to that text.
Use an internal inline wrapper for display: contents pseudo-elements
so generated content can still participate in layout, painting, hit
testing, and pseudo-element queries. Keep CSSOM reporting the computed
display value from the pseudo style, not the internal wrapper.
Remove the retained out-of-tree layout node list and its testing hook,
since the flattened model does not need a side owner for boxless
elements. Add coverage for inherited text style, dynamic insertion
order, pseudo-element hit testing, and computed style queries.
2026-06-07 12:50:33 -03:00
return make_ref_counted < Layout : : SVGGraphicsBox > ( document ( ) , * this , style ) ;
2023-08-19 14:42:31 -03:00
}
2023-05-30 17:23:52 -03:00
}