bool SVGUseElement::hasCycleUseReferencing(SVGUseElement* use, SVGElementInstance* targetInstance, SVGElement*& newTarget) { String id = SVGURIReference::getTarget(use->href()); Element* targetElement = document()->getElementById(id); newTarget = 0; if (targetElement && targetElement->isSVGElement()) newTarget = static_cast<SVGElement*>(targetElement); if (!newTarget) return false; // Shortcut for self-references if (newTarget == this) return true; SVGElementInstance* instance = targetInstance->parentNode(); while (instance) { SVGElement* element = instance->correspondingElement(); // FIXME: This should probably be using getIdAttribute instead of idForStyleResolution. if (element->hasID() && element->idForStyleResolution() == id) return true; instance = instance->parentNode(); } return false; }
bool SVGUseElement::hasCycleUseReferencing(SVGUseElement* use, SVGElementInstance* targetInstance, SVGElement*& newTarget) { ASSERT(referencedDocument()); Element* targetElement = SVGURIReference::targetElementFromIRIString(use->href(), *referencedDocument()); newTarget = 0; if (targetElement && targetElement->isSVGElement()) newTarget = toSVGElement(targetElement); if (!newTarget) return false; // Shortcut for self-references if (newTarget == this) return true; AtomicString targetId = newTarget->getIdAttribute(); SVGElementInstance* instance = targetInstance->parentNode(); while (instance) { SVGElement* element = instance->correspondingElement(); if (element->hasID() && element->getIdAttribute() == targetId && &element->document() == &newTarget->document()) return true; instance = instance->parentNode(); } return false; }
void SVGUseElement::transferEventListenersToShadowTree(SVGElementInstance* target) { if (!target) return; SVGElement* originalElement = target->correspondingElement(); ASSERT(originalElement); if (SVGElement* shadowTreeElement = target->shadowTreeElement()) { if (EventTargetData* d = originalElement->eventTargetData()) { EventListenerMap& map = d->eventListenerMap; EventListenerMap::iterator end = map.end(); for (EventListenerMap::iterator it = map.begin(); it != end; ++it) { EventListenerVector& entry = *it->second; for (size_t i = 0; i < entry.size(); ++i) { // Event listeners created from markup have already been transfered to the shadow tree during cloning. if (entry[i].listener->wasCreatedFromMarkup()) continue; shadowTreeElement->addEventListener(it->first, entry[i].listener, entry[i].useCapture); } } } } for (SVGElementInstance* instance = target->firstChild(); instance; instance = instance->nextSibling()) transferEventListenersToShadowTree(instance); }
unsigned int SVGElementInstanceList::length() const { // NOTE: We could use the same caching facilities, "ChildNodeList" uses. unsigned length = 0; SVGElementInstance* instance; for (instance = m_rootInstance->firstChild(); instance; instance = instance->nextSibling()) length++; return length; }
bool SVGUseElement::instanceTreeIsLoading(SVGElementInstance* targetElementInstance) { for (SVGElementInstance* instance = targetElementInstance->firstChild(); instance; instance = instance->nextSibling()) { if (SVGUseElement* use = instance->correspondingUseElement()) { if (use->cachedDocumentIsStillLoading()) return true; } if (instance->hasChildNodes()) instanceTreeIsLoading(instance); } return false; }
RefPtr<SVGElementInstance> SVGElementInstanceList::item(unsigned int index) { unsigned int pos = 0; SVGElementInstance* instance = m_rootInstance->firstChild(); while (instance && pos < index) { instance = instance->nextSibling(); pos++; } return instance; }
static void updateContainerOffset(SVGElementInstance* targetInstance) { // Depth-first used to write the method in early exit style, no particular other reason. for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling()) updateContainerOffset(instance); SVGElement* correspondingElement = targetInstance->correspondingElement(); ASSERT(correspondingElement); if (!correspondingElement->hasTagName(SVGNames::useTag)) return; SVGElement* shadowTreeElement = targetInstance->shadowTreeElement(); ASSERT(shadowTreeElement); ASSERT(shadowTreeElement->hasTagName(SVGNames::gTag)); if (!static_cast<SVGGElement*>(shadowTreeElement)->isShadowTreeContainerElement()) return; // 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. SVGUseElement* useElement = static_cast<SVGUseElement*>(correspondingElement); SVGShadowTreeContainerElement* containerElement = static_cast<SVGShadowTreeContainerElement*>(shadowTreeElement); containerElement->setContainerOffset(useElement->x(), useElement->y()); }
void SVGUseElement::transferEventListenersToShadowTree(SVGElementInstance* target) { if (!target) return; SVGElement* originalElement = target->correspondingElement(); ASSERT(originalElement); if (SVGElement* shadowTreeElement = target->shadowTreeElement()) { if (EventTargetData* data = originalElement->eventTargetData()) data->eventListenerMap.copyEventListenersNotCreatedFromMarkupToTarget(shadowTreeElement); } for (SVGElementInstance* instance = target->firstChild(); instance; instance = instance->nextSibling()) transferEventListenersToShadowTree(instance); }
static void updateContainerSize(SVGUseElement* useElement, SVGElementInstance* targetInstance) { // Depth-first used to write the method in early exit style, no particular other reason. for (SVGElementInstance* instance = targetInstance->firstChild(); instance; instance = instance->nextSibling()) updateContainerSize(useElement, instance); SVGElement* correspondingElement = targetInstance->correspondingElement(); ASSERT(correspondingElement); bool isSymbolTag = correspondingElement->hasTagName(SVGNames::symbolTag); if (!correspondingElement->hasTagName(SVGNames::svgTag) && !isSymbolTag) return; SVGElement* shadowTreeElement = targetInstance->shadowTreeElement(); ASSERT(shadowTreeElement); ASSERT(shadowTreeElement->hasTagName(SVGNames::svgTag)); // Spec (<use> on <symbol>): This generated 'svg' will always have explicit values for attributes width and height. // If attributes width and/or height are provided on the 'use' element, then these attributes // will be transferred to the generated 'svg'. If attributes width and/or height are not specified, // the generated 'svg' element will use values of 100% for these attributes. // Spec (<use> on <svg>): If attributes width and/or height are provided on the 'use' element, then these // values will override the corresponding attributes on the 'svg' in the generated tree. if (useElement->hasAttribute(SVGNames::widthAttr)) shadowTreeElement->setAttribute(SVGNames::widthAttr, useElement->getAttribute(SVGNames::widthAttr)); else if (isSymbolTag && shadowTreeElement->hasAttribute(SVGNames::widthAttr)) shadowTreeElement->setAttribute(SVGNames::widthAttr, "100%"); if (useElement->hasAttribute(SVGNames::heightAttr)) shadowTreeElement->setAttribute(SVGNames::heightAttr, useElement->getAttribute(SVGNames::heightAttr)); else if (isSymbolTag && shadowTreeElement->hasAttribute(SVGNames::heightAttr)) shadowTreeElement->setAttribute(SVGNames::heightAttr, "100%"); }
SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element, SVGElementInstance* instance) const { ASSERT(element); ASSERT(instance); ASSERT(instance->shadowTreeElement()); if (element == instance->shadowTreeElement()) return instance; for (SVGElementInstance* current = instance->firstChild(); current; current = current->nextSibling()) { SVGElementInstance* search = instanceForShadowTreeElement(element, current); if (search) return search; } return 0; }
void SVGElementInstance::detach() { // Clear all pointers. When the node is detached from the shadow DOM it should be removed but, // due to ref counting, it may not be. So clear everything to avoid dangling pointers. for (SVGElementInstance* node = firstChild(); node; node = node->nextSibling()) node->detach(); // Deregister as instance for passed element, if we haven't already. if (m_element->instancesForElement().contains(this)) m_element->removeInstanceMapping(this); // DO NOT clear ref to m_element because JavaScriptCore uses it for garbage collection m_shadowTreeElement = 0; m_directUseElement = 0; m_correspondingUseElement = 0; removeDetachedChildrenInContainer<SVGElementInstance, SVGElementInstance>(*this); }
SVGElementInstance* SVGUseElement::instanceForShadowTreeElement(Node* element, SVGElementInstance* instance) const { ASSERT(element); ASSERT(instance); // We're dispatching a mutation event during shadow tree construction // this instance hasn't yet been associated to a shadowTree element. if (!instance->shadowTreeElement()) return 0; if (element == instance->shadowTreeElement()) return instance; for (SVGElementInstance* current = instance->firstChild(); current; current = current->nextSibling()) { if (SVGElementInstance* search = instanceForShadowTreeElement(element, current)) return search; } return 0; }
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 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 RefPtr<SVGElementInstance> newInstance = SVGElementInstance::create(this, target); SVGElementInstance* newInstancePtr = newInstance.get(); targetInstance->appendChild(newInstance.release()); // Eventually enter recursion to build SVGElementInstance objects for the sub-tree children buildInstanceTree(target, newInstancePtr, foundProblem); }
void SVGUseElement::associateInstancesWithShadowTreeElements(Node* target, SVGElementInstance* targetInstance) { if (!target || !targetInstance) return; SVGElement* originalElement = targetInstance->correspondingElement(); if (originalElement->hasTagName(SVGNames::useTag)) { #if ENABLE(SVG) && ENABLE(SVG_USE) // <use> gets replaced by <g> ASSERT(target->nodeName() == SVGNames::gTag); #else ASSERT(target->nodeName() == SVGNames::gTag || target->nodeName() == SVGNames::useTag); #endif } else if (originalElement->hasTagName(SVGNames::symbolTag)) { // <symbol> gets replaced by <svg> #if ENABLE(SVG) && ENABLE(SVG_USE) && ENABLE(SVG_FOREIGN_OBJECT) ASSERT(target->nodeName() == SVGNames::svgTag); #endif } else ASSERT(target->nodeName() == originalElement->nodeName()); SVGElement* element = 0; if (target->isSVGElement()) element = static_cast<SVGElement*>(target); ASSERT(!targetInstance->shadowTreeElement()); targetInstance->setShadowTreeElement(element); Node* node = target->firstChild(); for (SVGElementInstance* instance = targetInstance->firstChild(); node && instance; instance = instance->nextSibling()) { // Skip any non-svg elements in shadow tree while (node && !node->isSVGElement()) node = node->nextSibling(); if (!node) break; associateInstancesWithShadowTreeElements(node, instance); node = node->nextSibling(); } }
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--; }
JSValue* JSSVGElementInstance::getValueProperty(ExecState* exec, int token) const { switch (token) { case CorrespondingElementAttrNum: { SVGElementInstance* imp = static_cast<SVGElementInstance*>(impl()); return toJS(exec, WTF::getPtr(imp->correspondingElement())); } case CorrespondingUseElementAttrNum: { SVGElementInstance* imp = static_cast<SVGElementInstance*>(impl()); return toJS(exec, WTF::getPtr(imp->correspondingUseElement())); } case ParentNodeAttrNum: { SVGElementInstance* imp = static_cast<SVGElementInstance*>(impl()); return toJS(exec, WTF::getPtr(imp->parentNode())); } case ChildNodesAttrNum: { SVGElementInstance* imp = static_cast<SVGElementInstance*>(impl()); return toJS(exec, WTF::getPtr(imp->childNodes())); } case FirstChildAttrNum: { SVGElementInstance* imp = static_cast<SVGElementInstance*>(impl()); return toJS(exec, WTF::getPtr(imp->firstChild())); } case LastChildAttrNum: { SVGElementInstance* imp = static_cast<SVGElementInstance*>(impl()); return toJS(exec, WTF::getPtr(imp->lastChild())); } case PreviousSiblingAttrNum: { SVGElementInstance* imp = static_cast<SVGElementInstance*>(impl()); return toJS(exec, WTF::getPtr(imp->previousSibling())); } case NextSiblingAttrNum: { SVGElementInstance* imp = static_cast<SVGElementInstance*>(impl()); return toJS(exec, WTF::getPtr(imp->nextSibling())); } } return 0; }