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 = 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. if (target) { // Setup sub-shadow tree root node RefPtr<SVGElement> cloneParent = new SVGGElement(SVGNames::gTag, 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()); // Spec: An additional transformation translate(x,y) is appended to the end // (i.e., right-side) of the transform attribute on the generated 'g', where x // and y represent the values of the x and y attributes on the 'use' element. if (use->x().value() != 0.0 || use->y().value() != 0.0) { if (!cloneParent->hasAttribute(SVGNames::transformAttr)) { String transformString = String::format("translate(%f, %f)", use->x().value(), use->y().value()); cloneParent->setAttribute(SVGNames::transformAttr, transformString); } else { String transformString = String::format(" translate(%f, %f)", use->x().value(), use->y().value()); const AtomicString& transformAttribute = cloneParent->getAttribute(SVGNames::transformAttr); cloneParent->setAttribute(SVGNames::transformAttr, transformAttribute + transformString); } } ExceptionCode ec = 0; // For instance <use> on <foreignObject> (direct case). if (isDisallowedElement(target)) { // We still have to setup the <use> replacment (<g>). Otherwhise // associateInstancesWithShadowTreeElements() makes wrong assumptions. // Replace <use> with referenced content. ASSERT(use->parentNode()); use->parentNode()->replaceChild(cloneParent.release(), use, ec); ASSERT(ec == 0); return; } RefPtr<Node> newChild = target->cloneNode(true); // We don't walk the target tree element-by-element, and clone each element, // but instead use cloneNode(deep=true). 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 == 0); // Replace <use> with referenced content. ASSERT(use->parentNode()); use->parentNode()->replaceChild(cloneParent.release(), use, ec); ASSERT(ec == 0); // Handle use referencing <svg> special case if (target->hasTagName(SVGNames::svgTag)) alterShadowTreeForSVGTag(newChildPtr); // Immediately stop here, and restart expanding. expandUseElementsInShadowTree(m_shadowTreeRootElement.get()); return; } } for (RefPtr<Node> child = element->firstChild(); child; child = child->nextSibling()) expandUseElementsInShadowTree(child.get()); }