📄 svguseelement.cpp
字号:
return; parent = parent->parentNode(); } SVGElement* target = 0; if (targetElement && targetElement->isSVGElement()) target = static_cast<SVGElement*>(targetElement); if (m_targetElementInstance) { m_targetElementInstance->forgetWrapper(); m_targetElementInstance = 0; } // Do not allow self-referencing. // 'target' may be null, if it's a non SVG namespaced element. if (!target || target == this) { m_shadowTreeRootElement = 0; return; } // Why a seperated instance/shadow tree? SVG demands it: // The instance tree is accesable from JavaScript, and has to // expose a 1:1 copy of the referenced tree, whereas internally we need // to alter the tree for correct "use-on-symbol", "use-on-svg" support. // Build instance tree. Create root SVGElementInstance object for the first sub-tree node. // // Spec: If the 'use' element references a simple graphics element such as a 'rect', then there is only a // single SVGElementInstance object, and the correspondingElement attribute on this SVGElementInstance object // is the SVGRectElement that corresponds to the referenced 'rect' element. m_targetElementInstance = new SVGElementInstance(this, target); // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children bool foundProblem = false; buildInstanceTree(target, m_targetElementInstance.get(), foundProblem); // SVG specification does not say a word about <use> & cycles. My view on this is: just ignore it! // Non-appearing <use> content is easier to debug, then half-appearing content. if (foundProblem) { m_targetElementInstance = 0; m_shadowTreeRootElement = 0; return; } // Assure instance tree building was successfull ASSERT(m_targetElementInstance); ASSERT(m_targetElementInstance->correspondingUseElement() == this); // Setup shadow tree root node m_shadowTreeRootElement = new SVGGElement(SVGNames::gTag, document()); m_shadowTreeRootElement->setInDocument(); m_shadowTreeRootElement->setShadowParentNode(this); // 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 (x().value(this) != 0.0 || y().value(this) != 0.0) { String transformString = String::format("translate(%f, %f)", x().value(this), y().value(this)); m_shadowTreeRootElement->setAttribute(SVGNames::transformAttr, transformString); } // Build shadow tree from instance tree // This also handles the special cases: <use> on <symbol>, <use> on <svg>. buildShadowTree(target, m_targetElementInstance.get());#if ENABLE(SVG) && ENABLE(SVG_USE) // Expand all <use> elements in the shadow tree. // Expand means: replace the actual <use> element by what it references. expandUseElementsInShadowTree(m_shadowTreeRootElement.get()); // Expand all <symbol> elements in the shadow tree. // Expand means: replace the actual <symbol> element by the <svg> element. expandSymbolElementsInShadowTree(m_shadowTreeRootElement.get());#endif // Now that the shadow tree is completly expanded, we can associate // shadow tree elements <-> instances in the instance tree. associateInstancesWithShadowTreeElements(m_shadowTreeRootElement->firstChild(), m_targetElementInstance.get()); // Eventually dump instance tree#ifdef DUMP_INSTANCE_TREE String text; unsigned int depth = 0; dumpInstanceTree(depth, text, m_targetElementInstance.get()); fprintf(stderr, "\nDumping <use> instance tree:\n%s\n", text.latin1().data());#endif // Eventually dump shadow tree#ifdef DUMP_SHADOW_TREE ExceptionCode ec = 0; PassRefPtr<XMLSerializer> serializer = XMLSerializer::create(); String markup = serializer->serializeToString(m_shadowTreeRootElement.get(), ec); ASSERT(ec == 0); fprintf(stderr, "Dumping <use> shadow tree markup:\n%s\n", markup.latin1().data());#endif // Transfer event listeners assigned to the referenced element to our shadow tree elements. transferEventListenersToShadowTree(m_targetElementInstance.get()); // The DOM side is setup properly. Now we have to attach the root shadow // tree element manually - using attach() won't work for "shadow nodes". attachShadowTree();}RenderObject* SVGUseElement::createRenderer(RenderArena* arena, RenderStyle*){ return new (arena) RenderSVGTransformableContainer(this);}void SVGUseElement::attach(){ SVGStyledTransformableElement::attach(); // If we're a pending resource, this doesn't have any effect. attachShadowTree();}void SVGUseElement::detach(){ SVGStyledTransformableElement::detach(); if (m_shadowTreeRootElement) m_shadowTreeRootElement->detach();}static bool isDirectReference(Node* n){ return n->hasTagName(SVGNames::pathTag) || n->hasTagName(SVGNames::rectTag) || n->hasTagName(SVGNames::circleTag) || n->hasTagName(SVGNames::ellipseTag) || n->hasTagName(SVGNames::polygonTag) || n->hasTagName(SVGNames::polylineTag) || n->hasTagName(SVGNames::textTag);}Path SVGUseElement::toClipPath() const{ if (!m_shadowTreeRootElement) const_cast<SVGUseElement*>(this)->buildPendingResource(); Node* n = m_shadowTreeRootElement->firstChild(); if (n->isSVGElement() && static_cast<SVGElement*>(n)->isStyledTransformable()) { if (!isDirectReference(n)) // Spec: Indirect references are an error (14.3.5) document()->accessSVGExtensions()->reportError("Not allowed to use indirect reference in <clip-path>"); else return static_cast<SVGStyledTransformableElement*>(n)->toClipPath(); } return Path();}void SVGUseElement::buildInstanceTree(SVGElement* target, SVGElementInstance* targetInstance, bool& foundProblem){ ASSERT(target); ASSERT(targetInstance); // 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. SVGElementInstance* instancePtr = new SVGElementInstance(this, element); targetInstance->appendChild(instancePtr); // Enter recursion, appending new instance tree nodes to the "instance" object. if (element->hasChildNodes()) buildInstanceTree(element, instancePtr, foundProblem); // 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. if (element->hasTagName(SVGNames::useTag)) handleDeepUseReferencing(static_cast<SVGUseElement*>(element), instancePtr, foundProblem); } // 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. if (target->hasTagName(SVGNames::useTag)) handleDeepUseReferencing(static_cast<SVGUseElement*>(target), targetInstance, foundProblem);}void SVGUseElement::handleDeepUseReferencing(SVGUseElement* use, SVGElementInstance* targetInstance, bool& foundProblem){ String id = SVGURIReference::getTarget(use->href()); Element* targetElement = document()->getElementById(id); SVGElement* target = 0; if (targetElement && targetElement->isSVGElement()) target = static_cast<SVGElement*>(targetElement); if (!target) return; // Cycle detection first! foundProblem = (target == this); // Shortcut for self-references if (foundProblem) return; SVGElementInstance* instance = targetInstance->parentNode(); while (instance) { SVGElement* element = instance->correspondingElement(); if (element->getIDAttribute() == id) { foundProblem = true; return; } instance = instance->parentNode(); } // Create an instance object, even if we're dealing with a cycle SVGElementInstance* newInstance = new SVGElementInstance(this, target); targetInstance->appendChild(newInstance); // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children buildInstanceTree(target, newInstance, foundProblem);}void SVGUseElement::alterShadowTreeForSVGTag(SVGElement* target){ String widthString = String::number(width().value(this)); String heightString = String::number(height().value(this)); if (hasAttribute(SVGNames::widthAttr)) target->setAttribute(SVGNames::widthAttr, widthString); if (hasAttribute(SVGNames::heightAttr)) target->setAttribute(SVGNames::heightAttr, heightString);}void SVGUseElement::removeDisallowedElementsFromSubtree(Node* subtree){ ASSERT(!subtree->inDocument()); ExceptionCode ec; Node* node = subtree->firstChild(); while (node) { if (isDisallowedElement(node)) { Node* next = node->traverseNextSibling(subtree); // The subtree is not in document so this won't generate events that could mutate the tree. node->parent()->removeChild(node, ec); node = next; } else node = node->traverseNextNode(subtree); }}void SVGUseElement::buildShadowTree(SVGElement* target, SVGElementInstance* targetInstance){ // For instance <use> on <foreignObject> (direct case). if (isDisallowedElement(target)) return; RefPtr<Node> newChild = targetInstance->correspondingElement()->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); ExceptionCode ec = 0; m_shadowTreeRootElement->appendChild(newChild.release(), ec); ASSERT(ec == 0); // Handle use referencing <svg> special case if (target->hasTagName(SVGNames::svgTag)) alterShadowTreeForSVGTag(newChildPtr);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -