void SVGElement::updateRelativeLengthsInformation(bool clientHasRelativeLengths, SVGElement* clientElement) { // If we're not yet in a document, this function will be called again from insertedInto(). Do nothing now. if (!inDocument()) return; // An element wants to notify us that its own relative lengths state changed. // Register it in the relative length map, and register us in the parent relative length map. // Register the parent in the grandparents map, etc. Repeat procedure until the root of the SVG tree. for (ContainerNode* currentNode = this; currentNode && currentNode->isSVGElement(); currentNode = currentNode->parentNode()) { SVGElement* currentElement = toSVGElement(currentNode); ASSERT(!currentElement->m_inRelativeLengthClientsInvalidation); bool hadRelativeLengths = currentElement->hasRelativeLengths(); if (clientHasRelativeLengths) currentElement->m_elementsWithRelativeLengths.add(clientElement); else currentElement->m_elementsWithRelativeLengths.remove(clientElement); // If the relative length state hasn't changed, we can stop propagating the notfication. if (hadRelativeLengths == currentElement->hasRelativeLengths()) break; clientElement = currentElement; clientHasRelativeLengths = clientElement->hasRelativeLengths(); } }
void SVGElement::updateRelativeLengthsInformation(bool hasRelativeLengths, SVGElement* element) { // If we're not yet in a document, this function will be called again from insertedInto(). Do nothing now. if (!inDocument()) return; // An element wants to notify us that its own relative lengths state changed. // Register it in the relative length map, and register us in the parent relative length map. // Register the parent in the grandparents map, etc. Repeat procedure until the root of the SVG tree. if (hasRelativeLengths) m_elementsWithRelativeLengths.add(element); else { if (!m_elementsWithRelativeLengths.contains(element)) { // We were never registered. Do nothing. return; } m_elementsWithRelativeLengths.remove(element); } if (!element->isSVGGraphicsElement()) return; // Find first styled parent node, and notify it that we've changed our relative length state. ContainerNode* node = parentNode(); while (node) { if (!node->isSVGElement()) break; // Register us in the parent element map. downcast<SVGElement>(*node).updateRelativeLengthsInformation(hasRelativeLengths, this); break; } }
bool SVGUseElement::hasCycleUseReferencing(SVGUseElement* use, ContainerNode* targetInstance, SVGElement*& newTarget) { ASSERT(referencedScope()); Element* targetElement = SVGURIReference::targetElementFromIRIString(use->hrefString(), *referencedScope()); 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(); ContainerNode* instance = targetInstance->parentNode(); while (instance && instance->isSVGElement()) { SVGElement* element = toSVGElement(instance); if (element->hasID() && element->getIdAttribute() == targetId && element->document() == newTarget->document()) return true; instance = instance->parentNode(); } return false; }
SVGElement* SVGSMILElement::targetElement() { if (m_targetElement) return m_targetElement; String href = xlinkHref(); ContainerNode* target = href.isEmpty() ? parentNode() : SVGURIReference::targetElementFromIRIString(href, document()); if (!target || !target->isSVGElement()) return 0; m_targetElement = static_cast<SVGElement*>(target); document()->accessSVGExtensions()->addAnimationElementToTarget(this, m_targetElement); return m_targetElement; }
String SVGStyledElement::title() const { // According to spec, we should not return titles when hovering over root <svg> elements (those // <title> elements are the title of the document, not a tooltip) so we instantly return. if (hasTagName(SVGNames::svgTag)) { const SVGSVGElement* svg = static_cast<const SVGSVGElement*>(this); if (svg->isOutermostSVG()) return String(); } // Walk up the tree, to find out whether we're inside a <use> shadow tree, to find the right title. Node* parent = const_cast<SVGStyledElement*>(this); while (parent) { if (!parent->isShadowRoot()) { parent = parent->parentNodeGuaranteedHostFree(); continue; } // Get the <use> element. ContainerNode* shadowParent = parent->shadowParentNode(); if (shadowParent && shadowParent->isSVGElement() && shadowParent->hasTagName(SVGNames::useTag)) { SVGUseElement* useElement = static_cast<SVGUseElement*>(shadowParent); // If the <use> title is not empty we found the title to use. String useTitle(useElement->title()); if (useTitle.isEmpty()) break; return useTitle; } parent = parent->parentNode(); } // If we aren't an instance in a <use> or the <use> title was not found, then find the first // <title> child of this element. Element* titleElement = firstElementChild(); for (; titleElement; titleElement = titleElement->nextElementSibling()) { if (titleElement->hasTagName(SVGNames::titleTag) && titleElement->isSVGElement()) break; } // If a title child was found, return the text contents. if (titleElement) return titleElement->innerText(); // Otherwise return a null/empty string. return String(); }
PassRefPtr<JSLazyEventListener> JSLazyEventListener::createForNode(ContainerNode& node, const QualifiedName& attributeName, const AtomicString& attributeValue) { if (attributeValue.isNull()) return nullptr; TextPosition position = TextPosition::minimumPosition(); String sourceURL; // FIXME: We should be able to provide source information for frameless documents too (e.g. for importing nodes from XMLHttpRequest.responseXML). if (Frame* frame = node.document().frame()) { if (!frame->script().canExecuteScripts(AboutToExecuteScript)) return nullptr; position = frame->script().eventHandlerPosition(); sourceURL = node.document().url().string(); } return adoptRef(new JSLazyEventListener(attributeName.localName().string(), eventParameterName(node.isSVGElement()), attributeValue, &node, sourceURL, position, nullptr, mainThreadNormalWorld())); }
void SVGUseElement::updateContainerOffsets() { if (!m_targetElementInstance) return; // Update root container offset (not reachable through instance tree) SVGElement* shadowRoot = m_targetElementInstance->shadowTreeElement(); ASSERT(shadowRoot); ContainerNode* parentNode = shadowRoot->parentNode(); ASSERT(parentNode); ASSERT(parentNode->isSVGElement()); ASSERT(parentNode->hasTagName(SVGNames::gTag)); ASSERT(static_cast<SVGGElement*>(parentNode)->isShadowTreeContainerElement()); SVGShadowTreeContainerElement* containerElement = static_cast<SVGShadowTreeContainerElement*>(parentNode); containerElement->setContainerOffset(x(), y()); // Update whole subtree, scanning for shadow container elements, marking a cloned use subtree updateContainerOffset(m_targetElementInstance.get()); if (RenderObject* object = renderer()) RenderSVGResource::markForLayoutAndParentResourceInvalidation(object); }
bool EventTargetNode::dispatchGenericEvent(PassRefPtr<Event> prpEvent) { RefPtr<Event> event(prpEvent); ASSERT(!eventDispatchForbidden()); ASSERT(event->target()); ASSERT(!event->type().isNull()); // JavaScript code can create an event with an empty name, but not null. // Make a vector of ancestors to send the event to. // If the node is not in a document just send the event to it. // Be sure to ref all of nodes since event handlers could result in the last reference going away. RefPtr<EventTargetNode> thisNode(this); Vector<RefPtr<ContainerNode> > ancestors; if (inDocument()) { for (ContainerNode* ancestor = eventParentNode(); ancestor; ancestor = ancestor->eventParentNode()) { #if ENABLE(SVG) // Skip <use> shadow tree elements. if (ancestor->isSVGElement() && ancestor->isShadowNode()) continue; #endif ancestors.append(ancestor); } } // Set up a pointer to indicate whether to dispatch window events. // We don't dispatch load events to the window. That quirk was originally // added because Mozilla doesn't propagate load events to the window object. Document* documentForWindowEvents = 0; if (event->type() != eventNames().loadEvent) { EventTargetNode* topLevelContainer = ancestors.isEmpty() ? this : ancestors.last().get(); if (topLevelContainer->isDocumentNode()) documentForWindowEvents = static_cast<Document*>(topLevelContainer); } // Give the target node a chance to do some work before DOM event handlers get a crack. void* data = preDispatchEventHandler(event.get()); if (event->propagationStopped()) goto doneDispatching; // Trigger capturing event handlers, starting at the top and working our way down. event->setEventPhase(Event::CAPTURING_PHASE); if (documentForWindowEvents) { event->setCurrentTarget(documentForWindowEvents); documentForWindowEvents->handleWindowEvent(event.get(), true); if (event->propagationStopped()) goto doneDispatching; } for (size_t i = ancestors.size(); i; --i) { ContainerNode* ancestor = ancestors[i - 1].get(); event->setCurrentTarget(eventTargetRespectingSVGTargetRules(ancestor)); ancestor->handleLocalEvents(event.get(), true); if (event->propagationStopped()) goto doneDispatching; } event->setEventPhase(Event::AT_TARGET); // We do want capturing event listeners to be invoked here, even though // that violates some versions of the DOM specification; Mozilla does it. event->setCurrentTarget(eventTargetRespectingSVGTargetRules(this)); handleLocalEvents(event.get(), true); if (event->propagationStopped()) goto doneDispatching; handleLocalEvents(event.get(), false); if (event->propagationStopped()) goto doneDispatching; if (event->bubbles() && !event->cancelBubble()) { // Trigger bubbling event handlers, starting at the bottom and working our way up. event->setEventPhase(Event::BUBBLING_PHASE); size_t size = ancestors.size(); for (size_t i = 0; i < size; ++i) { ContainerNode* ancestor = ancestors[i].get(); event->setCurrentTarget(eventTargetRespectingSVGTargetRules(ancestor)); ancestor->handleLocalEvents(event.get(), false); if (event->propagationStopped() || event->cancelBubble()) goto doneDispatching; } if (documentForWindowEvents) { event->setCurrentTarget(documentForWindowEvents); documentForWindowEvents->handleWindowEvent(event.get(), false); if (event->propagationStopped() || event->cancelBubble()) goto doneDispatching; } } doneDispatching: event->setCurrentTarget(0); event->setEventPhase(0); // Pass the data from the preDispatchEventHandler to the postDispatchEventHandler. postDispatchEventHandler(event.get(), data); // Call default event handlers. While the DOM does have a concept of preventing // default handling, the detail of which handlers are called is an internal // implementation detail and not part of the DOM. if (!event->defaultPrevented() && !event->defaultHandled()) { // Non-bubbling events call only one default event handler, the one for the target. defaultEventHandler(event.get()); ASSERT(!event->defaultPrevented()); if (event->defaultHandled()) goto doneWithDefault; // For bubbling events, call default event handlers on the same targets in the // same order as the bubbling phase. if (event->bubbles()) { size_t size = ancestors.size(); for (size_t i = 0; i < size; ++i) { ContainerNode* ancestor = ancestors[i].get(); ancestor->defaultEventHandler(event.get()); ASSERT(!event->defaultPrevented()); if (event->defaultHandled()) goto doneWithDefault; } } } doneWithDefault: Document::updateDocumentsRendering(); return !event->defaultPrevented(); }