void ScopedStyleResolver::addTreeBoundaryCrossingRules(const RuleSet& authorRules, CSSStyleSheet* parentStyleSheet, unsigned sheetIndex) { bool isDocumentScope = treeScope().rootNode().isDocumentNode(); if (authorRules.deepCombinatorOrShadowPseudoRules().isEmpty() && (isDocumentScope || (authorRules.contentPseudoElementRules().isEmpty() && authorRules.slottedPseudoElementRules().isEmpty()))) return; if (!authorRules.deepCombinatorOrShadowPseudoRules().isEmpty()) m_hasDeepOrShadowSelector = true; OwnPtrWillBeRawPtr<RuleSet> ruleSetForScope = RuleSet::create(); addRules(ruleSetForScope.get(), authorRules.deepCombinatorOrShadowPseudoRules()); if (!isDocumentScope) { addRules(ruleSetForScope.get(), authorRules.contentPseudoElementRules()); addRules(ruleSetForScope.get(), authorRules.slottedPseudoElementRules()); } if (!m_treeBoundaryCrossingRuleSet) { m_treeBoundaryCrossingRuleSet = adoptPtrWillBeNoop(new CSSStyleSheetRuleSubSet()); treeScope().document().styleResolver()->addTreeBoundaryCrossingScope(treeScope().rootNode()); } m_treeBoundaryCrossingRuleSet->append(RuleSubSet::create(parentStyleSheet, sheetIndex, ruleSetForScope.release())); }
void HTMLMapElement::parseAttribute(const QualifiedName& name, const AtomicString& value) { // FIXME: This logic seems wrong for XML documents. // Either the id or name will be used depending on the order the attributes are parsed. if (isIdAttributeName(name) || name == nameAttr) { if (isIdAttributeName(name)) { // Call base class so that hasID bit gets set. HTMLElement::parseAttribute(name, value); if (document().isHTMLDocument()) return; } if (inDocument()) treeScope().removeImageMap(this); String mapName = value; if (mapName[0] == '#') mapName = mapName.substring(1); m_name = AtomicString(document().isHTMLDocument() ? mapName.lower() : mapName); if (inDocument()) treeScope().addImageMap(this); return; } HTMLElement::parseAttribute(name, value); }
void ScopedStyleResolver::appendCSSStyleSheet(CSSStyleSheet& cssSheet, const MediaQueryEvaluator& medium) { unsigned index = m_authorStyleSheets.size(); m_authorStyleSheets.append(&cssSheet); StyleSheetContents* sheet = cssSheet.contents(); AddRuleFlags addRuleFlags = treeScope().document().securityOrigin()->canRequest(sheet->baseURL()) ? RuleHasDocumentSecurityOrigin : RuleHasNoSpecialState; const RuleSet& ruleSet = sheet->ensureRuleSet(medium, addRuleFlags); addKeyframeRules(ruleSet); addFontFaceRules(ruleSet); addTreeBoundaryCrossingRules(ruleSet, &cssSheet, index); treeScope().document().styleResolver()->addMediaQueryResults(ruleSet.viewportDependentMediaQueryResults()); }
// getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element. // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement Element* SVGSVGElement::getElementById(const String& id) { Element* element = treeScope().getElementById(id); if (element && element->isDescendantOf(this)) return element; if (treeScope().containsMultipleElementsWithId(id)) { for (auto element : *treeScope().getAllElementsById(id)) { if (element->isDescendantOf(this)) return element; } } return nullptr; }
void ContainerNode::parserAppendChild(PassRefPtr<Node> newChild) { ASSERT(newChild); ASSERT(!newChild->parentNode()); // Use appendChild if you need to handle reparenting (and want DOM mutation events). ASSERT(!newChild->isDocumentFragment()); #if ENABLE(TEMPLATE_ELEMENT) ASSERT(!hasTagName(HTMLNames::templateTag)); #endif if (&document() != &newChild->document()) document().adoptNode(newChild.get(), ASSERT_NO_EXCEPTION); { NoEventDispatchAssertion assertNoEventDispatch; // FIXME: This method should take a PassRefPtr. appendChildToContainer(newChild.get(), *this); treeScope().adoptIfNeeded(newChild.get()); } newChild->updateAncestorConnectedSubframeCountForInsertion(); ChildListMutationScope(*this).childAdded(*newChild); notifyChildInserted(*newChild, ChildChangeSourceParser); ChildNodeInsertionNotifier(*this).notify(*newChild); newChild->setNeedsStyleRecalc(ReconstructRenderTree); }
void DocumentStyleSheetCollection::updateActiveStyleSheets(StyleEngine* engine, StyleResolverUpdateMode updateMode) { StyleSheetCollection collection; ActiveDocumentStyleSheetCollector collector(collection); collectStyleSheets(engine, collector); StyleSheetChange change; analyzeStyleSheetChange(updateMode, collection, change); if (change.styleResolverUpdateType == Reconstruct) { engine->clearMasterResolver(); // FIMXE: The following depends on whether StyleRuleFontFace was modified or not. // No need to always-clear font cache. engine->clearFontCache(); } else if (StyleResolver* styleResolver = engine->resolver()) { if (change.styleResolverUpdateType != Additive) { ASSERT(change.styleResolverUpdateType == Reset); styleResolver->resetAuthorStyle(treeScope()); engine->removeFontFaceRules(change.fontFaceRulesToRemove); styleResolver->removePendingAuthorStyleSheets(m_activeAuthorStyleSheets); styleResolver->lazyAppendAuthorStyleSheets(0, collection.activeAuthorStyleSheets()); } else { styleResolver->lazyAppendAuthorStyleSheets(m_activeAuthorStyleSheets.size(), collection.activeAuthorStyleSheets()); } } if (change.requiresFullStyleRecalc) document().setNeedsStyleRecalc(SubtreeStyleChange); collection.swap(*this); updateUsesRemUnits(); }
void ContainerNode::takeAllChildrenFrom(ContainerNode* oldParent) { NodeVector children; getChildNodes(oldParent, children); if (oldParent->document()->hasMutationObserversOfType(MutationObserver::ChildList)) { ChildListMutationScope mutation(oldParent); for (unsigned i = 0; i < children.size(); ++i) mutation.willRemoveChild(children[i].get()); } // FIXME: We need to do notifyMutationObserversNodeWillDetach() for each child, // probably inside removeDetachedChildrenInContainer. oldParent->removeDetachedChildren(); for (unsigned i = 0; i < children.size(); ++i) { if (children[i]->attached()) children[i]->detach(); // FIXME: We need a no mutation event version of adoptNode. RefPtr<Node> child = document()->adoptNode(children[i].release(), ASSERT_NO_EXCEPTION); parserAppendChild(child.get()); // FIXME: Together with adoptNode above, the tree scope might get updated recursively twice // (if the document changed or oldParent was in a shadow tree, AND *this is in a shadow tree). // Can we do better? treeScope()->adoptIfNeeded(child.get()); if (attached() && !child->attached()) child->attach(); } }
void SVGUseElement::buildPendingResource() { if (inUseShadowTree()) return; clearShadowTree(); cancelShadowTreeRecreation(); if (!referencedScope() || !inDocument()) return; AtomicString id; Element* target = SVGURIReference::targetElementFromIRIString(hrefString(), treeScope(), &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; referencedScope()->document().accessSVGExtensions().addPendingResource(id, this); ASSERT(hasPendingResources()); return; } if (target->isSVGElement()) { buildShadowAndInstanceTree(toSVGElement(target)); invalidateDependentShadowTrees(); } ASSERT(!m_needsShadowTreeRecreation); }
void ScopedStyleResolver::collectMatchingTreeBoundaryCrossingRules(ElementRuleCollector& collector, CascadeOrder cascadeOrder) { for (const auto& rules : *m_treeBoundaryCrossingRuleSet) { MatchRequest request(rules->m_ruleSet.get(), &treeScope().rootNode(), rules->m_parentStyleSheet, rules->m_parentIndex); collector.collectMatchingRules(request, cascadeOrder, true); } }
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 ScopedStyleResolver::addFontFaceRules(const RuleSet& ruleSet) { // FIXME(BUG 72461): We don't add @font-face rules of scoped style sheets for the moment. if (!treeScope().rootNode().isDocumentNode()) return; Document& document = treeScope().document(); CSSFontSelector* cssFontSelector = document.styleEngine().fontSelector(); const WillBeHeapVector<RawPtrWillBeMember<StyleRuleFontFace>> fontFaceRules = ruleSet.fontFaceRules(); for (auto& fontFaceRule : fontFaceRules) { if (RefPtrWillBeRawPtr<FontFace> fontFace = FontFace::create(&document, fontFaceRule)) cssFontSelector->fontFaceCache()->add(cssFontSelector, fontFaceRule, fontFace); } if (fontFaceRules.size()) document.styleResolver()->invalidateMatchedPropertiesCache(); }
void ContainerNode::parserAppendChild(PassRefPtr<Node> newChild) { ASSERT(newChild); ASSERT(!newChild->parentNode()); // Use appendChild if you need to handle reparenting (and want DOM mutation events). ASSERT(!newChild->isDocumentFragment()); ASSERT(!hasTagName(HTMLNames::templateTag)); if (document() != newChild->document()) document()->adoptNode(newChild.get(), ASSERT_NO_EXCEPTION); Node* last = m_lastChild; { NoEventDispatchAssertion assertNoEventDispatch; // FIXME: This method should take a PassRefPtr. appendChildToContainer(newChild.get(), this); treeScope()->adoptIfNeeded(newChild.get()); } newChild->updateAncestorConnectedSubframeCountForInsertion(); ChildListMutationScope(this).childAdded(newChild.get()); childrenChanged(true, last, 0, 1); ChildNodeInsertionNotifier(this).notify(newChild.get()); }
void HTMLElement::parseAttribute(const QualifiedName& name, const AtomicString& value) { if (isIdAttributeName(name) || name == classAttr || name == styleAttr) return Element::parseAttribute(name, value); if (name == dirAttr) dirAttributeChanged(value); else if (name == tabindexAttr) { int tabindex = 0; if (value.isEmpty()) { clearTabIndexExplicitlyIfNeeded(); if (treeScope().adjustedFocusedElement() == this) { // We might want to call blur(), but it's dangerous to dispatch // events here. document().setNeedsFocusedElementCheck(); } } else if (parseHTMLInteger(value, tabindex)) { // Clamp tabindex to the range of 'short' to match Firefox's behavior. setTabIndexExplicitly(max(static_cast<int>(std::numeric_limits<short>::min()), min(tabindex, static_cast<int>(std::numeric_limits<short>::max())))); } } else { const AtomicString& eventName = eventNameForAttributeName(name); if (!eventName.isNull()) setAttributeEventListener(eventName, createAttributeEventListener(this, name, value)); } }
inline SVGElement* SVGSMILElement::eventBaseFor(const Condition& condition) { Element* eventBase = condition.baseID().isEmpty() ? targetElement() : treeScope().getElementById(AtomicString(condition.baseID())); if (eventBase && eventBase->isSVGElement()) return toSVGElement(eventBase); return nullptr; }
void ScopedStyleResolver::collectMatchingTreeBoundaryCrossingRules(ElementRuleCollector& collector, bool includeEmptyRules, CascadeOrder cascadeOrder) { RuleRange ruleRange = collector.matchedResult().ranges.authorRuleRange(); for (const auto& rules : *m_treeBoundaryCrossingRuleSet) { MatchRequest request(rules->m_ruleSet.get(), includeEmptyRules, &treeScope().rootNode(), rules->m_parentStyleSheet, rules->m_parentIndex); collector.collectMatchingRules(request, ruleRange, cascadeOrder, true); } }
bool Element::affectedByAttributeSelector(const AtomicString& attributeName) const { if (attributeName.isEmpty()) return false; if (treeScope().scopedStyleResolver().hasSelectorForAttribute(attributeName)) return true; return false; }
bool Element::affectedByIdSelector(const AtomicString& idValue) const { if (idValue.isEmpty()) return false; if (treeScope().scopedStyleResolver().hasSelectorForId(idValue)) return true; return false; }
bool HTMLShadowElement::doesSelectFromHostChildren() const { TreeScope* scope = treeScope(); if (scope->isShadowRoot()) return toShadowRoot(scope)->isOldest(); return false; }
ScopedStyleResolver* ScopedStyleResolver::parent() const { for (TreeScope* scope = treeScope().parentTreeScope(); scope; scope = scope->parentTreeScope()) { if (ScopedStyleResolver* resolver = scope->scopedStyleResolver()) return resolver; } return nullptr; }
void SVGTRefElement::updateReferencedText() { Element* target = treeScope()->getElementById(SVGURIReference::getTarget(href())); String textContent; if (target && target->isSVGElement()) textContent = static_cast<SVGElement*>(target)->textContent(); ExceptionCode ignore = 0; setTextContent(textContent, ignore); }
bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, AttachBehavior attachBehavior) { RefPtr<ContainerNode> protect(this); // Check that this node is not "floating". // If it is, it can be deleted as a side effect of sending mutation events. ASSERT(refCount() || parentOrShadowHostNode()); ec = 0; // Make sure adding the new child is ok if (!checkAddChild(this, newChild.get(), ec)) return false; if (newChild == m_lastChild) // nothing to do return newChild; NodeVector targets; collectChildrenAndRemoveFromOldParent(newChild.get(), targets, ec); if (ec) return false; if (targets.isEmpty()) return true; // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events. if (!checkAcceptChildGuaranteedNodeTypes(this, newChild.get(), ec)) return false; InspectorInstrumentation::willInsertDOMNode(document(), this); // Now actually add the child(ren) ChildListMutationScope mutation(this); for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) { Node* child = it->get(); // If the child has a parent again, just stop what we're doing, because // that means someone is doing something with DOM mutation -- can't re-parent // a child that already has a parent. if (child->parentNode()) break; treeScope()->adoptIfNeeded(child); // Append child to the end of the list { NoEventDispatchAssertion assertNoEventDispatch; appendChildToContainer(child, this); } updateTreeAfterInsertion(this, child, attachBehavior); } dispatchSubtreeModifiedEvent(); return true; }
void ScopedStyleResolver::addTreeBoundaryCrossingRules(const RuleSet& authorRules, CSSStyleSheet* parentStyleSheet, unsigned sheetIndex) { bool isDocumentScope = treeScope().rootNode().isDocumentNode(); if (authorRules.treeBoundaryCrossingRules().isEmpty() && (isDocumentScope || authorRules.shadowDistributedRules().isEmpty())) return; OwnPtrWillBeRawPtr<RuleSet> ruleSetForScope = RuleSet::create(); addRules(ruleSetForScope.get(), authorRules.treeBoundaryCrossingRules()); if (!isDocumentScope) addRules(ruleSetForScope.get(), authorRules.shadowDistributedRules()); if (!m_treeBoundaryCrossingRuleSet) { m_treeBoundaryCrossingRuleSet = adoptPtrWillBeNoop(new CSSStyleSheetRuleSubSet()); treeScope().document().styleResolver()->addTreeBoundaryCrossingScope(treeScope().rootNode()); } m_treeBoundaryCrossingRuleSet->append(RuleSubSet::create(parentStyleSheet, sheetIndex, ruleSetForScope.release())); }
void Element::removedFrom(ContainerNode* insertionPoint) { if (insertionPoint->isInTreeScope() && treeScope() == document()) { const AtomicString& idValue = getIdAttribute(); if (!idValue.isNull()) updateId(insertionPoint->treeScope(), idValue, nullAtom); } ContainerNode::removedFrom(insertionPoint); }
inline void Element::updateId(const AtomicString& oldId, const AtomicString& newId) { if (!isInTreeScope()) return; if (oldId == newId) return; updateId(treeScope(), oldId, newId); }
PassRefPtrWillBeRawPtr<FilterEffect> SVGFEImageElement::build(SVGFilterBuilder*, Filter* filter) { if (m_cachedImage) { // Don't use the broken image icon on image loading errors. RefPtr<Image> image = m_cachedImage->errorOccurred() ? nullptr : m_cachedImage->image(); return FEImage::createWithImage(filter, image, m_preserveAspectRatio->currentValue()); } return FEImage::createWithIRIReference(filter, treeScope(), hrefString(), m_preserveAspectRatio->currentValue()); }
ShadowRoot* InsertionPoint::assignedFrom() const { TreeScope* scope = treeScope(); if (!scope->isShadowRoot()) return 0; ShadowRoot* olderShadowRoot = toShadowRoot(scope)->olderShadowRoot(); if (olderShadowRoot && olderShadowRoot->assignedTo() == this) return olderShadowRoot; return 0; }
HTMLMenuElement* HTMLElement::contextMenu() const { const AtomicString& contextMenuId(fastGetAttribute(contextmenuAttr)); if (contextMenuId.isNull()) return nullptr; Element* element = treeScope().getElementById(contextMenuId); // Not checking if the menu element is of type "popup". // Ignoring menu element type attribute is intentional according to the standard. return isHTMLMenuElement(element) ? toHTMLMenuElement(element) : nullptr; }
ShadowRoot* InsertionPoint::assignedFrom() const { Node* treeScopeRoot = treeScope()->rootNode(); if (!treeScopeRoot->isShadowRoot()) return 0; ShadowRoot* olderShadowRoot = toShadowRoot(treeScopeRoot)->olderShadowRoot(); if (olderShadowRoot && olderShadowRoot->assignedTo() == this) return olderShadowRoot; return 0; }
// getElementById on SVGSVGElement is restricted to only the child subtree defined by the <svg> element. // See http://www.w3.org/TR/SVG11/struct.html#InterfaceSVGSVGElement Element* SVGSVGElement::getElementById(const AtomicString& id) { Element* element = treeScope().getElementById(id); if (element && element->isDescendantOf(this)) return element; // Fall back to traversing our subtree. Duplicate ids are allowed, the first found will // be returned. for (auto& element : descendantsOfType<Element>(*this)) { if (element.getIdAttribute() == id) return &element; } return 0; }
Element* DocumentFragment::getElementById(const AtomicString& id) const { // Fast path for ShadowRoot, where we are both a DocumentFragment and a TreeScope. if (isTreeScope()) return treeScope().getElementById(id); // Otherwise, fall back to iterating all of the element descendants. for (auto& element : elementDescendants(*this)) { if (element.getIdAttribute() == id) return const_cast<Element*>(&element); } return nullptr; }