static void dumpInstanceTree(unsigned int& depth, String& text, SVGElementInstance* targetInstance) { SVGElement* element = targetInstance->correspondingElement(); ASSERT(element); if (element->hasTagName(SVGNames::useTag)) { if (toSVGUseElement(element)->cachedDocumentIsStillLoading()) return; } SVGElement* shadowTreeElement = targetInstance->shadowTreeElement(); ASSERT(shadowTreeElement); SVGUseElement* directUseElement = targetInstance->directUseElement(); String directUseElementName = directUseElement ? directUseElement->nodeName() : "null"; String elementId = element->getIdAttribute(); String elementNodeName = element->nodeName(); String shadowTreeElementNodeName = shadowTreeElement->nodeName(); String parentNodeName = element->parentNode() ? element->parentNode()->nodeName() : "null"; String firstChildNodeName = element->firstChild() ? element->firstChild()->nodeName() : "null"; for (unsigned int i = 0; i < depth; ++i) text += " "; text += String::format("SVGElementInstance this=%p, (parentNode=%s (%p), firstChild=%s (%p), correspondingElement=%s (%p), directUseElement=%s (%p), shadowTreeElement=%s (%p), id=%s)\n", targetInstance, parentNodeName.latin1().data(), element->parentNode(), firstChildNodeName.latin1().data(), element->firstChild(), elementNodeName.latin1().data(), element, directUseElementName.latin1().data(), directUseElement, shadowTreeElementNodeName.latin1().data(), shadowTreeElement, elementId.latin1().data()); for (unsigned int i = 0; i < depth; ++i) text += " "; const HashSet<SVGElementInstance*>& elementInstances = element->instancesForElement(); text += "Corresponding element is associated with " + String::number(elementInstances.size()) + " instance(s):\n"; const HashSet<SVGElementInstance*>::const_iterator end = elementInstances.end(); for (HashSet<SVGElementInstance*>::const_iterator it = elementInstances.begin(); it != end; ++it) { for (unsigned int i = 0; i < depth; ++i) text += " "; text += String::format(" -> SVGElementInstance this=%p, (refCount: %i, shadowTreeElement in document? %i)\n", *it, (*it)->refCount(), (*it)->shadowTreeElement()->inDocument()); } ++depth; for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling()) dumpInstanceTree(depth, text, instance); --depth; }
void dumpInstanceTree(unsigned int& depth, String& text, SVGElementInstance* targetInstance) { SVGElement* element = targetInstance->correspondingElement(); ASSERT(element); String elementId = element->getIDAttribute(); String elementNodeName = element->nodeName(); String parentNodeName = element->parentNode() ? element->parentNode()->nodeName() : "null"; String firstChildNodeName = element->firstChild() ? element->firstChild()->nodeName() : "null"; for (unsigned int i = 0; i < depth; ++i) text += " "; text += String::format("SVGElementInstance (parentNode=%s, firstChild=%s, correspondingElement=%s, id=%s)\n", parentNodeName.latin1().data(), firstChildNodeName.latin1().data(), elementNodeName.latin1().data(), elementId.latin1().data()); depth++; for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling()) dumpInstanceTree(depth, text, instance); depth--; }
void SVGUseElement::buildShadowAndInstanceTree(SVGShadowTreeRootElement* shadowRoot) { String id = SVGURIReference::getTarget(href()); Element* targetElement = document()->getElementById(id); if (!targetElement) { // The only time we should get here is when the use element has not been // given a resource to target. ASSERT(m_resourceId.isEmpty()); return; } // Do not build the shadow/instance tree for <use> elements living in a shadow tree. // The will be expanded soon anyway - see expandUseElementsInShadowTree(). Node* parent = parentNode(); while (parent) { if (parent->isShadowNode()) return; parent = parent->parentNode(); } SVGElement* target = 0; if (targetElement && targetElement->isSVGElement()) target = static_cast<SVGElement*>(targetElement); if (m_targetElementInstance) m_targetElementInstance = 0; // Do not allow self-referencing. // 'target' may be null, if it's a non SVG namespaced element. if (!target || target == this) 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 = SVGElementInstance::create(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; return; } // Assure instance tree building was successfull ASSERT(m_targetElementInstance); ASSERT(!m_targetElementInstance->shadowTreeElement()); ASSERT(m_targetElementInstance->correspondingUseElement() == this); ASSERT(m_targetElementInstance->correspondingElement() == target); // Build shadow tree from instance tree // This also handles the special cases: <use> on <symbol>, <use> on <svg>. buildShadowTree(shadowRoot, 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(shadowRoot, shadowRoot); // Expand all <symbol> elements in the shadow tree. // Expand means: replace the actual <symbol> element by the <svg> element. expandSymbolElementsInShadowTree(shadowRoot, shadowRoot); #endif // Now that the shadow tree is completly expanded, we can associate // shadow tree elements <-> instances in the instance tree. associateInstancesWithShadowTreeElements(shadowRoot->firstChild(), m_targetElementInstance.get()); // If no shadow tree element is present, this means that the reference root // element was removed, as it is disallowed (ie. <use> on <foreignObject>) // Do NOT leave an inconsistent instance tree around, instead destruct it. if (!m_targetElementInstance->shadowTreeElement()) { shadowRoot->removeAllChildren(); m_targetElementInstance = 0; return; } // Consistency checks - this is assumed in updateContainerOffset(). ASSERT(m_targetElementInstance->shadowTreeElement()->parentNode() == shadowRoot); // 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(shadowRoot, ec); ASSERT(!ec); 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()); // Update container offset/size updateContainerOffsets(); updateContainerSizes(); }
void SVGUseElement::buildShadowAndInstanceTree(SVGElement* target) { ASSERT(!m_targetElementInstance); // Do not build the shadow/instance tree for <use> elements living in a shadow tree. // The will be expanded soon anyway - see expandUseElementsInShadowTree(). if (isInShadowTree()) return; // Do not allow self-referencing. // 'target' may be null, if it's a non SVG namespaced element. if (!target || target == this) 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 = SVGElementInstance::create(this, this, target); // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children bool foundProblem = false; buildInstanceTree(target, m_targetElementInstance.get(), foundProblem, false); if (instanceTreeIsLoading(m_targetElementInstance.get())) return; // 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) { clearResourceReferences(); return; } // Assure instance tree building was successfull ASSERT(m_targetElementInstance); ASSERT(!m_targetElementInstance->shadowTreeElement()); ASSERT(m_targetElementInstance->correspondingUseElement() == this); ASSERT(m_targetElementInstance->directUseElement() == this); ASSERT(m_targetElementInstance->correspondingElement() == target); ShadowRoot* shadowTreeRootElement = shadowRoot(); ASSERT(shadowTreeRootElement); // Build shadow tree from instance tree // This also handles the special cases: <use> on <symbol>, <use> on <svg>. buildShadowTree(target, m_targetElementInstance.get()); // Expand all <use> elements in the shadow tree. // Expand means: replace the actual <use> element by what it references. expandUseElementsInShadowTree(shadowTreeRootElement); // Expand all <symbol> elements in the shadow tree. // Expand means: replace the actual <symbol> element by the <svg> element. expandSymbolElementsInShadowTree(shadowTreeRootElement); // Now that the shadow tree is completly expanded, we can associate // shadow tree elements <-> instances in the instance tree. associateInstancesWithShadowTreeElements(shadowTreeRootElement->firstChild(), m_targetElementInstance.get()); // If no shadow tree element is present, this means that the reference root // element was removed, as it is disallowed (ie. <use> on <foreignObject>) // Do NOT leave an inconsistent instance tree around, instead destruct it. if (!m_targetElementInstance->shadowTreeElement()) { clearResourceReferences(); return; } ASSERT(m_targetElementInstance->shadowTreeElement()->parentNode() == shadowTreeRootElement); // Transfer event listeners assigned to the referenced element to our shadow tree elements. transferEventListenersToShadowTree(m_targetElementInstance.get()); // Update relative length information. updateRelativeLengthsInformation(); // 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 RefPtr<XMLSerializer> serializer = XMLSerializer::create(); String markup = serializer->serializeToString(shadowTreeRootElement, ASSERT_NO_EXCEPTION); fprintf(stderr, "Dumping <use> shadow tree markup:\n%s\n", markup.latin1().data()); #endif }
void SVGUseElement::buildPendingResource() { String id = SVGURIReference::getTarget(href()); Element* targetElement = document()->getElementById(id); if (!targetElement) { // TODO: We want to deregister as pending resource, if our href() changed! // TODO: Move to svgAttributeChanged, once we're fixing use & the new dynamic update concept. document()->accessSVGExtensions()->addPendingResource(id, this); return; } // Do not build the shadow/instance tree for <use> elements living in a shadow tree. // The will be expanded soon anyway - see expandUseElementsInShadowTree(). Node* parent = parentNode(); while (parent) { if (parent->isShadowNode()) return; parent = parent->parentNode(); } SVGElement* target = 0; if (targetElement && targetElement->isSVGElement()) target = static_cast<SVGElement*>(targetElement); // Do not allow self-referencing. // 'target' may be null, if it's a non SVG namespaced element. if (!target || target == this) { m_targetElementInstance = 0; 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() != 0.0 || y().value() != 0.0) { String transformString = String::format("translate(%f, %f)", x().value(), y().value()); 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 // 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(); }