void SVGUseElement::buildPendingResource() { if (!referencedDocument() || isInShadowTree()) return; clearResourceReferences(); if (!inDocument()) return; String id; Element* target = SVGURIReference::targetElementFromIRIString(href(), document(), &id, externalDocument()); if (!target || !target->inDocument()) { // If we can't find the target of an external element, just give up. // We can't observe if the target somewhen enters the external document, nor should we do it. if (externalDocument()) return; if (id.isEmpty()) return; referencedDocument()->accessSVGExtensions()->addPendingResource(id, this); ASSERT(hasPendingResources()); return; } if (target->isSVGElement()) { buildShadowAndInstanceTree(toSVGElement(target)); invalidateDependentShadowTrees(); } ASSERT(!m_needsShadowTreeRecreation); }
Node::InsertionNotificationRequest SVGSMILElement::insertedInto(ContainerNode& rootParent) { SVGElement::insertedInto(rootParent); if (!rootParent.inDocument()) return InsertionDone; // Verify we are not in <use> instance tree. ASSERT(!isInShadowTree()); setAttributeName(constructQualifiedName(this, fastGetAttribute(SVGNames::attributeNameAttr))); SVGSVGElement* owner = ownerSVGElement(); if (!owner) return InsertionDone; m_timeContainer = owner->timeContainer(); ASSERT(m_timeContainer); m_timeContainer->setDocumentOrderIndexesDirty(); // "If no attribute is present, the default begin value (an offset-value of 0) must be evaluated." if (!fastHasAttribute(SVGNames::beginAttr)) m_beginTimes.append(SMILTimeWithOrigin()); if (m_isWaitingForFirstInterval) resolveFirstInterval(); if (m_timeContainer) m_timeContainer->notifyIntervalsChanged(); buildPendingResource(); return InsertionDone; }
void SVGTRefElement::buildPendingResource() { // Remove any existing event listener. m_targetListener->detach(); // If we're not yet in a document, this function will be called again from insertedInto(). if (!inDocument()) return; String id; RefPtr<Element> target = SVGURIReference::targetElementFromIRIString(href(), document(), &id); if (!target.get()) { if (id.isEmpty()) return; document().accessSVGExtensions().addPendingResource(id, this); ASSERT(hasPendingResources()); return; } // Don't set up event listeners if this is a shadow tree node. // SVGUseElement::transferEventListenersToShadowTree() handles this task, and addEventListener() // expects every element instance to have an associated shadow tree element - which is not the // case when we land here from SVGUseElement::buildShadowTree(). if (!isInShadowTree()) m_targetListener->attach(target); updateReferencedText(target.get()); }
String SVGElement::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 (isOutermostSVGSVGElement()) return String(); // Walk up the tree, to find out whether we're inside a <use> shadow tree, to find the right title. if (isInShadowTree()) { Element* shadowHostElement = toShadowRoot(treeScope().rootNode()).host(); // At this time, SVG nodes are not allowed in non-<use> shadow trees, so any shadow root we do // have should be a use. The assert and following test is here to catch future shadow DOM changes // that do enable SVG in a shadow tree. ASSERT(!shadowHostElement || isSVGUseElement(*shadowHostElement)); if (isSVGUseElement(shadowHostElement)) { SVGUseElement& useElement = toSVGUseElement(*shadowHostElement); // If the <use> title is not empty we found the title to use. String useTitle(useElement.title()); if (!useTitle.isEmpty()) return useTitle; } } // 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. // If a title child was found, return the text contents. if (Element* titleElement = Traversal<SVGTitleElement>::firstChild(*this)) return titleElement->innerText(); // Otherwise return a null/empty string. return String(); }
void HTMLImportElement::insertedInto(ContainerNode* insertionPoint) { HTMLElement::insertedInto(insertionPoint); if (!insertionPoint->inDocument() || isInShadowTree()) return; if (shouldLoad()) load(); }
void HTMLLinkElement::insertedIntoDocument() { HTMLElement::insertedIntoDocument(); m_isInShadowTree = isInShadowTree(); if (m_isInShadowTree) return; document()->addStyleSheetCandidateNode(this, m_createdByParser); process(); }
void CharacterData::dispatchModifiedEvent(const String& oldData) { if (OwnPtr<MutationObserverInterestGroup> mutationRecipients = MutationObserverInterestGroup::createForCharacterDataMutation(this)) mutationRecipients->enqueueMutationRecord(MutationRecord::createCharacterData(this, oldData)); if (!isInShadowTree()) { if (parentNode()) parentNode()->childrenChanged(); if (document()->hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER)) dispatchScopedEvent(MutationEvent::create(eventNames().DOMCharacterDataModifiedEvent, true, 0, oldData, m_data)); dispatchSubtreeModifiedEvent(); } InspectorInstrumentation::characterDataModified(document(), this); }
void CharacterData::didModifyData(const String& oldData, UpdateSource source) { if (OwnPtrWillBeRawPtr<MutationObserverInterestGroup> mutationRecipients = MutationObserverInterestGroup::createForCharacterDataMutation(*this)) mutationRecipients->enqueueMutationRecord(MutationRecord::createCharacterData(this, oldData)); if (parentNode()) { ContainerNode::ChildrenChange change = {ContainerNode::TextChanged, previousSibling(), nextSibling(), ContainerNode::ChildrenChangeSourceAPI}; parentNode()->childrenChanged(change); } // Skip DOM mutation events if the modification is from parser. // Note that mutation observer events will still fire. // Spec: https://html.spec.whatwg.org/multipage/syntax.html#insert-a-character if (source != UpdateFromParser && !isInShadowTree()) { if (document().hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER)) dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMCharacterDataModified, true, nullptr, oldData, m_data)); dispatchSubtreeModifiedEvent(); } InspectorInstrumentation::characterDataModified(this); }
bool SVGElement::isOutermostSVGSVGElement() const { if (!hasTagName(SVGNames::svgTag)) return false; // If we're living in a shadow tree, we're a <svg> element that got created as replacement // for a <symbol> element or a cloned <svg> element in the referenced tree. In that case // we're always an inner <svg> element. if (isInShadowTree() && parentOrShadowHostElement() && parentOrShadowHostElement()->isSVGElement()) return false; // Element may not be in the document, pretend we're outermost for viewport(), getCTM(), etc. if (!parentNode()) return true; // We act like an outermost SVG element, if we're a direct child of a <foreignObject> element. if (parentNode()->hasTagName(SVGNames::foreignObjectTag)) return true; // This is true whenever this is the outermost SVG, even if there are HTML elements outside it return !parentNode()->isSVGElement(); }
void CharacterData::dispatchModifiedEvent(const String& oldData) { if (std::unique_ptr<MutationObserverInterestGroup> mutationRecipients = MutationObserverInterestGroup::createForCharacterDataMutation(*this)) mutationRecipients->enqueueMutationRecord(MutationRecord::createCharacterData(*this, oldData)); if (!isInShadowTree()) { if (parentNode()) { ContainerNode::ChildChange change = { ContainerNode::TextChanged, ElementTraversal::previousSibling(*this), ElementTraversal::nextSibling(*this), ContainerNode::ChildChangeSourceAPI }; parentNode()->childrenChanged(change); } if (document().hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER)) dispatchScopedEvent(MutationEvent::create(eventNames().DOMCharacterDataModifiedEvent, true, nullptr, oldData, m_data)); dispatchSubtreeModifiedEvent(); } InspectorInstrumentation::characterDataModified(document(), *this); }
void SVGElement::buildPendingResourcesIfNeeded() { Document& document = this->document(); if (!needsPendingResourceHandling() || !inDocument() || isInShadowTree()) return; SVGDocumentExtensions& extensions = document.accessSVGExtensions(); AtomicString resourceId = getIdAttribute(); if (!extensions.hasPendingResource(resourceId)) return; // Mark pending resources as pending for removal. extensions.markPendingResourcesForRemoval(resourceId); // Rebuild pending resources for each client of a pending resource that is being removed. while (Element* clientElement = extensions.removeElementFromPendingResourcesForRemoval(resourceId)) { ASSERT(clientElement->hasPendingResources()); if (clientElement->hasPendingResources()) { clientElement->buildPendingResource(); extensions.clearHasPendingResourcesIfPossible(clientElement); } } }
void HTMLTitleElement::childrenChanged(const ChildrenChange& change) { HTMLElement::childrenChanged(change); if (inDocument() && !isInShadowTree() && !m_ignoreTitleUpdatesWhenChildrenChange) document().setTitleElement(this); }
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 HTMLTitleElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) { HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); if (inDocument() && !isInShadowTree() && !m_ignoreTitleUpdatesWhenChildrenChange) document().setTitleElement(text(), this); }