void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* targetInstance, bool& foundProblem, bool foundUse) { ASSERT(target); ASSERT(targetInstance); // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree. bool targetHasUseTag = target->hasTagName(SVGNames::useTag); SVGElement* newTarget = 0; if (targetHasUseTag) { foundProblem = hasCycleUseReferencing(toSVGUseElement(target), targetInstance, newTarget); if (foundProblem) return; // We only need to track first degree <use> dependencies. Indirect references are handled // as the invalidation bubbles up the dependency chain. if (!foundUse) { document().accessSVGExtensions()->addElementReferencingTarget(this, target); foundUse = true; } } else if (isDisallowedElement(*target)) { foundProblem = true; return; } // A general description from the SVG spec, describing what buildInstanceTree() actually does. // // Spec: If the 'use' element references a 'g' which contains two 'rect' elements, then the instance tree // contains three SVGElementInstance objects, a root SVGElementInstance object whose correspondingElement // is the SVGGElement object for the 'g', and then two child SVGElementInstance objects, each of which has // its correspondingElement that is an SVGRectElement object. for (auto& element : childrenOfType<SVGElement>(*target)) { // Skip any non-svg nodes or any disallowed element. if (isDisallowedElement(element)) continue; // Create SVGElementInstance object, for both container/non-container nodes. RefPtr<SVGElementInstance> instance = SVGElementInstance::create(this, 0, &element); SVGElementInstance* instancePtr = instance.get(); targetInstance->appendChild(instance.release()); // Enter recursion, appending new instance tree nodes to the "instance" object. buildInstanceTree(&element, instancePtr, foundProblem, foundUse); if (foundProblem) return; } if (!targetHasUseTag || !newTarget) return; RefPtr<SVGElementInstance> newInstance = SVGElementInstance::create(this, toSVGUseElement(target), newTarget); SVGElementInstance* newInstancePtr = newInstance.get(); targetInstance->appendChild(newInstance.release()); buildInstanceTree(newTarget, newInstancePtr, foundProblem, foundUse); }
void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* targetInstance, bool& foundProblem) { ASSERT(target); ASSERT(targetInstance); // Spec: If the referenced object is itself a 'use', or if there are 'use' subelements within the referenced // object, the instance tree will contain recursive expansion of the indirect references to form a complete tree. bool targetHasUseTag = target->hasTagName(SVGNames::useTag); SVGElement* newTarget = 0; if (targetHasUseTag) { foundProblem = hasCycleUseReferencing(static_cast<SVGUseElement*>(target), targetInstance, newTarget); if (foundProblem) return; } // A general description from the SVG spec, describing what buildInstanceTree() actually does. // // Spec: If the 'use' element references a 'g' which contains two 'rect' elements, then the instance tree // contains three SVGElementInstance objects, a root SVGElementInstance object whose correspondingElement // is the SVGGElement object for the 'g', and then two child SVGElementInstance objects, each of which has // its correspondingElement that is an SVGRectElement object. for (Node* node = target->firstChild(); node; node = node->nextSibling()) { SVGElement* element = 0; if (node->isSVGElement()) element = static_cast<SVGElement*>(node); // Skip any non-svg nodes or any disallowed element. if (!element || isDisallowedElement(element)) continue; // Create SVGElementInstance object, for both container/non-container nodes. RefPtr<SVGElementInstance> instance = SVGElementInstance::create(this, element); SVGElementInstance* instancePtr = instance.get(); targetInstance->appendChild(instance.release()); // Enter recursion, appending new instance tree nodes to the "instance" object. buildInstanceTree(element, instancePtr, foundProblem); if (foundProblem) return; } if (!targetHasUseTag || !newTarget) return; RefPtr<SVGElementInstance> newInstance = SVGElementInstance::create(this, newTarget); SVGElementInstance* newInstancePtr = newInstance.get(); targetInstance->appendChild(newInstance.release()); buildInstanceTree(newTarget, newInstancePtr, foundProblem); }
bool SVGUseElement::expandUseElementsInShadowTree(SVGElement* element) { ASSERT(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. For example, if a <symbol> // contains <use> tags, we'd miss them. So once we're done with setting 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 (isSVGUseElement(*element)) { SVGUseElement* use = toSVGUseElement(element); ASSERT(!use->resourceIsStillLoading()); SVGElement* target = 0; if (hasCycleUseReferencing(toSVGUseElement(use->correspondingElement()), use, target)) return false; if (target && isDisallowedElement(target)) return false; // Don't ASSERT(target) here, it may be "pending", too. // Setup sub-shadow tree root node RefPtrWillBeRawPtr<SVGGElement> cloneParent = SVGGElement::create(referencedScope()->document()); cloneParent->setCorrespondingElement(use->correspondingElement()); // Move already cloned elements to the new <g> element for (RefPtrWillBeRawPtr<Node> child = use->firstChild(); child; ) { RefPtrWillBeRawPtr<Node> nextChild = child->nextSibling(); cloneParent->appendChild(child); child = nextChild.release(); } // 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) { RefPtrWillBeRawPtr<Node> newChild = cloneNodeAndAssociate(*target); ASSERT(newChild->isSVGElement()); transferUseWidthAndHeightIfNeeded(*use, toSVGElement(newChild.get()), *target); 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.get())) removeDisallowedElementsFromSubtree(*cloneParent); RefPtrWillBeRawPtr<SVGElement> 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 (RefPtrWillBeRawPtr<SVGElement> sibling = Traversal<SVGElement>::nextSibling(*element); sibling; sibling = Traversal<SVGElement>::nextSibling(*sibling)) { if (!expandUseElementsInShadowTree(sibling.get())) return false; } } for (RefPtrWillBeRawPtr<SVGElement> child = Traversal<SVGElement>::firstChild(*element); child; child = Traversal<SVGElement>::nextSibling(*child)) { if (!expandUseElementsInShadowTree(child.get())) return false; } return true; }