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.