PassRefPtr<StaticNodeList> createSelectorNodeList(Node* rootNode, const CSSSelectorList& querySelectorList) { Vector<RefPtr<Node> > nodes; Document* document = rootNode->document(); CSSSelector* onlySelector = querySelectorList.hasOneSelector() ? querySelectorList.first() : 0; bool strictParsing = !document->inCompatMode(); CSSStyleSelector::SelectorChecker selectorChecker(document, strictParsing); if (strictParsing && rootNode->inDocument() && onlySelector && onlySelector->m_match == CSSSelector::Id && !document->containsMultipleElementsWithId(onlySelector->m_value)) { Element* element = document->getElementById(onlySelector->m_value); if (element && (rootNode->isDocumentNode() || element->isDescendantOf(rootNode)) && selectorChecker.checkSelector(onlySelector, element)) nodes.append(element); } else { for (Node* n = rootNode->firstChild(); n; n = n->traverseNextNode(rootNode)) { if (n->isElementNode()) { Element* element = static_cast<Element*>(n); for (CSSSelector* selector = querySelectorList.first(); selector; selector = CSSSelectorList::next(selector)) { if (selectorChecker.checkSelector(selector, element)) { nodes.append(n); break; } } } } } return StaticNodeList::adopt(nodes); }
void SelectorDataList::initialize(const CSSSelectorList& selectorList) { ASSERT(m_selectors.isEmpty()); unsigned selectorCount = 0; for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) selectorCount++; m_selectors.reserveInitialCapacity(selectorCount); for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) m_selectors.uncheckedAppend(SelectorData(*selector, SelectorCheckerFastPath::canUse(*selector))); }
SelectorQuery* SelectorQueryCache::add(const String& selectors, Document& document, ExceptionCode& ec) { auto it = m_entries.find(selectors); if (it != m_entries.end()) return it->value.get(); CSSParser parser(document); CSSSelectorList selectorList; parser.parseSelector(selectors, selectorList); if (!selectorList.first() || selectorList.hasInvalidSelector()) { ec = SYNTAX_ERR; return nullptr; } // Throw a NAMESPACE_ERR if the selector includes any namespace prefixes. if (selectorList.selectorsNeedNamespaceResolution()) { ec = NAMESPACE_ERR; return nullptr; } const int maximumSelectorQueryCacheSize = 256; if (m_entries.size() == maximumSelectorQueryCacheSize) m_entries.remove(m_entries.begin()); return m_entries.add(selectors, std::make_unique<SelectorQuery>(std::move(selectorList))).iterator->value.get(); }
void SelectorDataList::initialize(const CSSSelectorList& selectorList) { ASSERT(m_selectors.isEmpty()); for (CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) m_selectors.append(SelectorData(selector, SelectorChecker::isFastCheckableSelector(selector))); }
static bool determineSelectorScopes(const CSSSelectorList& selectorList, HashSet<StringImpl*>& idScopes, HashSet<StringImpl*>& classScopes) { for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) { const CSSSelector* scopeSelector = 0; // This picks the widest scope, not the narrowest, to minimize the number of found scopes. for (const CSSSelector* current = selector; current; current = current->tagHistory()) { // Prefer ids over classes. if (current->match() == CSSSelector::Id) scopeSelector = current; else if (current->match() == CSSSelector::Class && (!scopeSelector || scopeSelector->match() != CSSSelector::Id)) scopeSelector = current; CSSSelector::RelationType relation = current->relation(); // FIXME: it would be better to use setNeedsStyleRecalc for all shadow hosts matching // scopeSelector. Currently requests full style recalc. if (relation == CSSSelector::ShadowDeep || relation == CSSSelector::ShadowPseudo) return false; if (relation != CSSSelector::Descendant && relation != CSSSelector::Child && relation != CSSSelector::SubSelector) break; } if (!scopeSelector) return false; ASSERT(scopeSelector->match() == CSSSelector::Class || scopeSelector->match() == CSSSelector::Id); if (scopeSelector->match() == CSSSelector::Id) idScopes.add(scopeSelector->value().impl()); else classScopes.add(scopeSelector->value().impl()); } return true; }
void SelectorDataList::initialize(const CSSSelectorList& selectorList) { ASSERT(m_selectors.isEmpty()); unsigned selectorCount = 0; for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) selectorCount++; m_crossesTreeBoundary = false; m_selectors.reserveInitialCapacity(selectorCount); unsigned index = 0; for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector), ++index) { m_selectors.uncheckedAppend(selector); m_crossesTreeBoundary |= selectorList.selectorCrossesTreeScopes(index); } }
SelectorQuery* SelectorQueryCache::add(const AtomicString& selectors, Document* document, ExceptionCode& ec) { HashMap<AtomicString, OwnPtr<SelectorQuery> >::iterator it = m_entries.find(selectors); if (it != m_entries.end()) return it->value.get(); CSSParser parser(document); CSSSelectorList selectorList; parser.parseSelector(selectors, selectorList); if (!selectorList.first() || selectorList.hasInvalidSelector()) { ec = SYNTAX_ERR; return 0; } // throw a NAMESPACE_ERR if the selector includes any namespace prefixes. if (selectorList.selectorsNeedNamespaceResolution()) { ec = NAMESPACE_ERR; return 0; } const int maximumSelectorQueryCacheSize = 256; if (m_entries.size() == maximumSelectorQueryCacheSize) m_entries.remove(m_entries.begin()); OwnPtr<SelectorQuery> selectorQuery = adoptPtr(new SelectorQuery(selectorList)); SelectorQuery* rawSelectorQuery = selectorQuery.get(); m_entries.add(selectors, selectorQuery.release()); return rawSelectorQuery; }
bool CSSLazyParsingState::shouldLazilyParseProperties( const CSSSelectorList& selectors, const CSSParserTokenRange& block) const { // Simple heuristic for an empty block. Note that |block| here does not // include {} brackets. We avoid lazy parsing empty blocks so we can avoid // considering them when possible for matching. Lazy blocks must always be // considered. Three tokens is a reasonable minimum for a block: // ident ':' <value>. if (block.end() - block.begin() <= 2) return false; // Disallow lazy parsing for blocks which have before/after in their selector // list. This ensures we don't cause a collectFeatures() when we trigger // parsing for attr() functions which would trigger expensive invalidation // propagation. for (const auto* s = selectors.first(); s; s = CSSSelectorList::next(*s)) { for (const CSSSelector* current = s; current; current = current->tagHistory()) { const CSSSelector::PseudoType type(current->getPseudoType()); if (type == CSSSelector::PseudoBefore || type == CSSSelector::PseudoAfter) return false; if (current->relation() != CSSSelector::SubSelector) break; } } return true; }
void CSSStyleRule::setSelectorText(const String& selectorText) { Document* doc = 0; StyleSheet* ownerStyleSheet = m_style->stylesheet(); if (ownerStyleSheet) { if (ownerStyleSheet->isCSSStyleSheet()) doc = static_cast<CSSStyleSheet*>(ownerStyleSheet)->document(); if (!doc) doc = ownerStyleSheet->ownerNode() ? ownerStyleSheet->ownerNode()->document() : 0; } if (!doc) doc = m_style->node() ? m_style->node()->document() : 0; if (!doc) return; CSSParser p; CSSSelectorList selectorList; p.parseSelector(selectorText, doc, selectorList); if (!selectorList.first()) return; String oldSelectorText = this->selectorText(); m_selectorList.adopt(selectorList); if (this->selectorText() == oldSelectorText) return; doc->styleSelectorChanged(DeferRecalcStyle); }
static bool determineSelectorScopes(const CSSSelectorList& selectorList, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes) { for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) { const CSSSelector* scopeSelector = 0; // This picks the widest scope, not the narrowest, to minimize the number of found scopes. for (const CSSSelector* current = selector; current; current = current->tagHistory()) { // Prefer ids over classes. if (current->m_match == CSSSelector::Id) scopeSelector = current; else if (current->m_match == CSSSelector::Class && (!scopeSelector || scopeSelector->m_match != CSSSelector::Id)) scopeSelector = current; CSSSelector::Relation relation = current->relation(); if (relation != CSSSelector::Descendant && relation != CSSSelector::Child && relation != CSSSelector::SubSelector) break; } if (!scopeSelector) return false; ASSERT(scopeSelector->m_match == CSSSelector::Class || scopeSelector->m_match == CSSSelector::Id); if (scopeSelector->m_match == CSSSelector::Id) idScopes.add(scopeSelector->value().impl()); else classScopes.add(scopeSelector->value().impl()); } return true; }
SelectorQuery* SelectorQueryCache::add(const AtomicString& selectors, const Document& document, ExceptionState& exceptionState) { HashMap<AtomicString, OwnPtr<SelectorQuery> >::iterator it = m_entries.find(selectors); if (it != m_entries.end()) return it->value.get(); BisonCSSParser parser(CSSParserContext(document, 0)); CSSSelectorList selectorList; parser.parseSelector(selectors, selectorList); if (!selectorList.first()) { exceptionState.throwDOMException(SyntaxError, "'" + selectors + "' is not a valid selector."); return 0; } // throw a NamespaceError if the selector includes any namespace prefixes. if (selectorList.selectorsNeedNamespaceResolution()) { exceptionState.throwDOMException(NamespaceError, "'" + selectors + "' contains namespaces, which are not supported."); return 0; } const unsigned maximumSelectorQueryCacheSize = 256; if (m_entries.size() == maximumSelectorQueryCacheSize) m_entries.remove(m_entries.begin()); OwnPtr<SelectorQuery> selectorQuery = adoptPtr(new SelectorQuery(selectorList)); SelectorQuery* rawSelectorQuery = selectorQuery.get(); m_entries.add(selectors, selectorQuery.release()); return rawSelectorQuery; }
static unsigned maxSpecificity(const CSSSelectorList& selectorList) { unsigned maxSpecificity = 0; for (const CSSSelector* subSelector = selectorList.first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) maxSpecificity = std::max(maxSpecificity, selectorSpecificity(*subSelector, true)); return maxSpecificity; }
static bool allCompound(const CSSSelectorList& selectorList) { for (const CSSSelector* selector = selectorList.first(); selector; selector = selectorList.next(selector)) { if (!selector->isCompound()) return false; } return true; }
SelectorDataList::SelectorDataList(const CSSSelectorList& selectorList) { unsigned selectorCount = 0; for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) selectorCount++; m_selectors.reserveInitialCapacity(selectorCount); for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) m_selectors.uncheckedAppend(SelectorData(selector, SelectorCheckerFastPath::canUse(selector))); if (selectorCount == 1) { const CSSSelector& selector = *m_selectors.first().selector; if (selector.isLastInTagHistory()) { switch (selector.m_match) { case CSSSelector::Tag: m_matchType = TagNameMatch; break; case CSSSelector::Class: m_matchType = ClassNameMatch; break; case CSSSelector::Id: m_matchType = RightMostWithIdMatch; break; default: m_matchType = CompilableSingle; break; } } else { switch (findIdMatchingType(selector)) { case IdMatchingType::None: m_matchType = CompilableSingle; break; case IdMatchingType::Rightmost: m_matchType = RightMostWithIdMatch; break; case IdMatchingType::Filter: m_matchType = CompilableSingleWithRootFilter; break; } } } else m_matchType = MultipleSelectorMatch; }
void SelectorDataList::initialize(const CSSSelectorList& selectorList) { ASSERT(m_selectors.isEmpty()); unsigned selectorCount = 0; for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) selectorCount++; m_usesDeepCombinatorOrShadowPseudo = false; m_needsUpdatedDistribution = false; m_selectors.reserveInitialCapacity(selectorCount); unsigned index = 0; for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector), ++index) { if (selector->matchesPseudoElement()) continue; m_selectors.uncheckedAppend(selector); m_usesDeepCombinatorOrShadowPseudo |= selectorList.selectorUsesDeepCombinatorOrShadowPseudo(index); m_needsUpdatedDistribution |= selectorList.selectorNeedsUpdatedDistribution(index); } }
static inline bool selectorListContainsUncommonAttributeSelector(const CSSSelector* selector) { CSSSelectorList* selectorList = selector->selectorList(); if (!selectorList) return false; for (CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(subSelector)) { if (subSelector->isAttributeSelector()) return true; } return false; }
WebString canonicalizeSelector(WebString webSelector, WebSelectorType restriction) { CSSSelectorList selectorList = CSSParser::parseSelector(strictCSSParserContext(), nullptr, webSelector); if (restriction == WebSelectorTypeCompound) { for (const CSSSelector* selector = selectorList.first(); selector; selector = selectorList.next(*selector)) { if (!selector->isCompound()) return WebString(); } } return selectorList.selectorsText(); }
void CSSPageRule::setSelectorText(const String& selectorText) { CSSParser parser(parserContext()); CSSSelectorList selectorList; parser.parseSelector(selectorText, selectorList); if (!selectorList.first()) return; CSSStyleSheet::RuleMutationScope mutationScope(this); String oldSelectorText = this->selectorText(); m_pageRule->wrapperAdoptSelectorList(selectorList); }
void SelectRuleFeatureSet::collectFeaturesFromSelectorList( const CSSSelectorList& list) { for (const CSSSelector* selector = list.first(); selector; selector = CSSSelectorList::next(*selector)) { for (const CSSSelector* component = selector; component; component = component->tagHistory()) { if (invalidationSetForSimpleSelector(*component, InvalidateDescendants)) continue; if (component->selectorList()) collectFeaturesFromSelectorList(*component->selectorList()); } } }
void CSSStyleRule::setSelectorText(const String& selectorText) { CSSParser p(parserContext()); CSSSelectorList selectorList; p.parseSelector(selectorText, selectorList); if (!selectorList.first()) return; CSSStyleSheet::RuleMutationScope mutationScope(this); m_styleRule->wrapperAdoptSelectorList(selectorList); if (hasCachedSelectorText()) { selectorTextCache().remove(this); setHasCachedSelectorText(false); } }
static void recordSelectorStats(const CSSParserContext& context, const CSSSelectorList& selectorList) { if (!context.useCounter()) return; for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) { for (const CSSSelector* current = selector; current ; current = current->tagHistory()) { UseCounter::Feature feature = UseCounter::NumberOfFeatures; switch (current->pseudoType()) { case CSSSelector::PseudoUnresolved: feature = UseCounter::CSSSelectorPseudoUnresolved; break; case CSSSelector::PseudoShadow: feature = UseCounter::CSSSelectorPseudoShadow; break; case CSSSelector::PseudoContent: feature = UseCounter::CSSSelectorPseudoContent; break; case CSSSelector::PseudoHost: feature = UseCounter::CSSSelectorPseudoHost; break; case CSSSelector::PseudoHostContext: feature = UseCounter::CSSSelectorPseudoHostContext; break; case CSSSelector::PseudoFullScreenDocument: feature = UseCounter::CSSSelectorPseudoFullScreenDocument; break; case CSSSelector::PseudoFullScreenAncestor: feature = UseCounter::CSSSelectorPseudoFullScreenAncestor; break; case CSSSelector::PseudoFullScreen: feature = UseCounter::CSSSelectorPseudoFullScreen; break; default: break; } if (feature != UseCounter::NumberOfFeatures) context.useCounter()->count(feature); if (current->relation() == CSSSelector::ShadowDeep) context.useCounter()->count(UseCounter::CSSDeepCombinator); if (current->selectorList()) recordSelectorStats(context, *current->selectorList()); } } }
SelectorQuery* SelectorQueryCache::add(const AtomicString& selectors, const Document& document, ExceptionState& exceptionState) { HashMap<AtomicString, OwnPtr<SelectorQuery>>::iterator it = m_entries.find(selectors); if (it != m_entries.end()) return it->value.get(); CSSSelectorList selectorList = CSSParser::parseSelector(CSSParserContext(document, nullptr), nullptr, selectors); if (!selectorList.first()) { exceptionState.throwDOMException(SyntaxError, "'" + selectors + "' is not a valid selector."); return nullptr; } const unsigned maximumSelectorQueryCacheSize = 256; if (m_entries.size() == maximumSelectorQueryCacheSize) m_entries.remove(m_entries.begin()); return m_entries.add(selectors, SelectorQuery::adopt(std::move(selectorList))).storedValue->value.get(); }
void CSSPageRule::setSelectorText(const String& selectorText) { Document* doc = 0; if (CSSStyleSheet* styleSheet = parentStyleSheet()) doc = styleSheet->findDocument(); if (!doc) return; CSSParser p; CSSSelectorList selectorList; p.parseSelector(selectorText, doc, selectorList); if (!selectorList.first()) return; String oldSelectorText = this->selectorText(); m_selectorList.adopt(selectorList); if (this->selectorText() == oldSelectorText) return; doc->styleSelectorChanged(DeferRecalcStyle); }
static void recordSelectorStats(const CSSParserContext& context, const CSSSelectorList& selectorList) { if (!context.useCounter()) return; for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector)) { for (const CSSSelector* current = selector; current; current = current->tagHistory()) { UseCounter::Feature feature = UseCounter::NumberOfFeatures; switch (current->getPseudoType()) { case CSSSelector::PseudoAny: feature = UseCounter::CSSSelectorPseudoAny; break; case CSSSelector::PseudoUnresolved: feature = UseCounter::CSSSelectorPseudoUnresolved; break; case CSSSelector::PseudoDefined: feature = UseCounter::CSSSelectorPseudoDefined; break; case CSSSelector::PseudoSlotted: feature = UseCounter::CSSSelectorPseudoSlotted; break; case CSSSelector::PseudoContent: feature = UseCounter::CSSSelectorPseudoContent; break; case CSSSelector::PseudoHost: feature = UseCounter::CSSSelectorPseudoHost; break; case CSSSelector::PseudoHostContext: feature = UseCounter::CSSSelectorPseudoHostContext; break; case CSSSelector::PseudoFullScreenAncestor: feature = UseCounter::CSSSelectorPseudoFullScreenAncestor; break; case CSSSelector::PseudoFullScreen: feature = UseCounter::CSSSelectorPseudoFullScreen; break; case CSSSelector::PseudoListBox: if (context.mode() != UASheetMode) feature = UseCounter::CSSSelectorInternalPseudoListBox; break; case CSSSelector::PseudoWebKitCustomElement: if (context.mode() != UASheetMode) { if (current->value() == "-internal-media-controls-cast-button") feature = UseCounter::CSSSelectorInternalMediaControlsCastButton; else if (current->value() == "-internal-media-controls-overlay-cast-button") feature = UseCounter::CSSSelectorInternalMediaControlsOverlayCastButton; } break; case CSSSelector::PseudoSpatialNavigationFocus: if (context.mode() != UASheetMode) feature = UseCounter::CSSSelectorInternalPseudoSpatialNavigationFocus; break; case CSSSelector::PseudoReadOnly: if (context.mode() != UASheetMode) feature = UseCounter::CSSSelectorPseudoReadOnly; break; case CSSSelector::PseudoReadWrite: if (context.mode() != UASheetMode) feature = UseCounter::CSSSelectorPseudoReadWrite; break; default: break; } if (feature != UseCounter::NumberOfFeatures) context.useCounter()->count(feature); if (current->relation() == CSSSelector::IndirectAdjacent) context.useCounter()->count(UseCounter::CSSSelectorIndirectAdjacent); if (current->selectorList()) recordSelectorStats(context, *current->selectorList()); } } }