void SVGUseElement::expandUseElementsInShadowTree(SVGShadowTreeRootElement* shadowRoot, Node* element) { // Why expand the <use> elements in the shadow tree here, and not just // do this directly in buildShadowTree, if we encounter a <use> element? // // Short answer: Because we may miss to expand some elements. Ie. if a <symbol> // contains <use> tags, we'd miss them. So once we're done with settin' up the // actual shadow tree (after the special case modification for svg/symbol) we have // to walk it completely and expand all <use> elements. if (element->hasTagName(SVGNames::useTag)) { SVGUseElement* use = static_cast<SVGUseElement*>(element); String id = SVGURIReference::getTarget(use->href()); Element* targetElement = document()->getElementById(id); SVGElement* target = 0; if (targetElement && targetElement->isSVGElement()) target = static_cast<SVGElement*>(targetElement); // Don't ASSERT(target) here, it may be "pending", too. // Setup sub-shadow tree root node RefPtr<SVGShadowTreeContainerElement> cloneParent = new SVGShadowTreeContainerElement(document()); // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element. transferUseAttributesToReplacedElement(use, cloneParent.get()); ExceptionCode ec = 0; if (target && !isDisallowedElement(target)) { RefPtr<Element> newChild = target->cloneElementWithChildren(); // We don't walk the target tree element-by-element, and clone each element, // but instead use cloneElementWithChildren(). This is an optimization for the common // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). // Though if there are disallowed elements in the subtree, we have to remove them. // For instance: <use> on <g> containing <foreignObject> (indirect case). if (subtreeContainsDisallowedElement(newChild.get())) removeDisallowedElementsFromSubtree(newChild.get()); SVGElement* newChildPtr = 0; if (newChild->isSVGElement()) newChildPtr = static_cast<SVGElement*>(newChild.get()); ASSERT(newChildPtr); cloneParent->appendChild(newChild.release(), ec); ASSERT(!ec); } // Replace <use> with referenced content. ASSERT(use->parentNode()); use->parentNode()->replaceChild(cloneParent.release(), use, ec); ASSERT(!ec); // Immediately stop here, and restart expanding. expandUseElementsInShadowTree(shadowRoot, shadowRoot); return; } for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) expandUseElementsInShadowTree(shadowRoot, child.get()); }
void SVGUseElement::cloneTarget(ContainerNode& container, SVGElement& target) const { Ref<SVGElement> targetClone = static_pointer_cast<SVGElement>(target.cloneElementWithChildren(document())).releaseNonNull(); associateClonesWithOriginals(targetClone.get(), target); removeDisallowedElementsFromSubtree(targetClone.get()); transferSizeAttributesToTargetClone(targetClone.get()); container.appendChild(WTF::move(targetClone)); }
void SVGUseElement::expandUseElementsInShadowTree(Node* element) { // Why expand the <use> elements in the shadow tree here, and not just // do this directly in buildShadowTree, if we encounter a <use> element? // // Short answer: Because we may miss to expand some elements. Ie. if a <symbol> // contains <use> tags, we'd miss them. So once we're done with settin' up the // actual shadow tree (after the special case modification for svg/symbol) we have // to walk it completely and expand all <use> elements. if (element->hasTagName(SVGNames::useTag)) { SVGUseElement* use = toSVGUseElement(element); ASSERT(!use->cachedDocumentIsStillLoading()); ASSERT(referencedDocument()); Element* targetElement = SVGURIReference::targetElementFromIRIString(use->href(), *referencedDocument()); SVGElement* target = 0; if (targetElement && targetElement->isSVGElement()) target = toSVGElement(targetElement); // Don't ASSERT(target) here, it may be "pending", too. // Setup sub-shadow tree root node RefPtr<SVGGElement> cloneParent = SVGGElement::create(SVGNames::gTag, *referencedDocument()); use->cloneChildNodes(cloneParent.get()); // Spec: In the generated content, the 'use' will be replaced by 'g', where all attributes from the // 'use' element except for x, y, width, height and xlink:href are transferred to the generated 'g' element. transferUseAttributesToReplacedElement(use, cloneParent.get()); if (target && !isDisallowedElement(*target)) { RefPtr<Element> newChild = target->cloneElementWithChildren(); ASSERT(newChild->isSVGElement()); cloneParent->appendChild(newChild.release()); } // We don't walk the target tree element-by-element, and clone each element, // but instead use cloneElementWithChildren(). This is an optimization for the common // case where <use> doesn't contain disallowed elements (ie. <foreignObject>). // Though if there are disallowed elements in the subtree, we have to remove them. // For instance: <use> on <g> containing <foreignObject> (indirect case). if (subtreeContainsDisallowedElement(*cloneParent)) removeDisallowedElementsFromSubtree(*cloneParent); RefPtr<Node> replacingElement(cloneParent.get()); // Replace <use> with referenced content. ASSERT(use->parentNode()); use->parentNode()->replaceChild(cloneParent.release(), use); // Expand the siblings because the *element* is replaced and we will // lose the sibling chain when we are back from recursion. element = replacingElement.get(); for (RefPtr<Node> sibling = element->nextSibling(); sibling; sibling = sibling->nextSibling()) expandUseElementsInShadowTree(sibling.get()); } for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) expandUseElementsInShadowTree(child.get()); }