blob: 6284b30302380f93d0f84389385077434595150f [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
* Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
* Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
* Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
* Copyright (C) Research In Motion Limited 2011. All rights reserved.
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "core/css/RuleFeature.h"
#include "core/HTMLNames.h"
#include "core/css/CSSCustomIdentValue.h"
#include "core/css/CSSFunctionValue.h"
#include "core/css/CSSSelector.h"
#include "core/css/CSSSelectorList.h"
#include "core/css/CSSValueList.h"
#include "core/css/RuleSet.h"
#include "core/css/StylePropertySet.h"
#include "core/css/StyleRule.h"
#include "core/css/invalidation/InvalidationSet.h"
#include "core/dom/Element.h"
#include "core/dom/Node.h"
#include "core/inspector/InspectorTraceEvents.h"
#include "wtf/BitVector.h"
namespace blink {
namespace {
#if ENABLE(ASSERT)
bool supportsInvalidation(CSSSelector::MatchType match)
{
switch (match) {
case CSSSelector::Tag:
case CSSSelector::Id:
case CSSSelector::Class:
case CSSSelector::AttributeExact:
case CSSSelector::AttributeSet:
case CSSSelector::AttributeHyphen:
case CSSSelector::AttributeList:
case CSSSelector::AttributeContain:
case CSSSelector::AttributeBegin:
case CSSSelector::AttributeEnd:
return true;
case CSSSelector::Unknown:
case CSSSelector::PagePseudoClass:
// These should not appear in StyleRule selectors.
ASSERT_NOT_REACHED();
return false;
default:
// New match type added. Figure out if it needs a subtree invalidation or not.
ASSERT_NOT_REACHED();
return false;
}
}
bool supportsInvalidation(CSSSelector::PseudoType type)
{
switch (type) {
case CSSSelector::PseudoEmpty:
case CSSSelector::PseudoFirstChild:
case CSSSelector::PseudoFirstOfType:
case CSSSelector::PseudoLastChild:
case CSSSelector::PseudoLastOfType:
case CSSSelector::PseudoOnlyChild:
case CSSSelector::PseudoOnlyOfType:
case CSSSelector::PseudoNthChild:
case CSSSelector::PseudoNthOfType:
case CSSSelector::PseudoNthLastChild:
case CSSSelector::PseudoNthLastOfType:
case CSSSelector::PseudoLink:
case CSSSelector::PseudoVisited:
case CSSSelector::PseudoAny:
case CSSSelector::PseudoAnyLink:
case CSSSelector::PseudoAutofill:
case CSSSelector::PseudoHover:
case CSSSelector::PseudoDrag:
case CSSSelector::PseudoFocus:
case CSSSelector::PseudoActive:
case CSSSelector::PseudoChecked:
case CSSSelector::PseudoEnabled:
case CSSSelector::PseudoFullPageMedia:
case CSSSelector::PseudoDefault:
case CSSSelector::PseudoDisabled:
case CSSSelector::PseudoOptional:
case CSSSelector::PseudoPlaceholderShown:
case CSSSelector::PseudoRequired:
case CSSSelector::PseudoReadOnly:
case CSSSelector::PseudoReadWrite:
case CSSSelector::PseudoValid:
case CSSSelector::PseudoInvalid:
case CSSSelector::PseudoIndeterminate:
case CSSSelector::PseudoTarget:
case CSSSelector::PseudoBefore:
case CSSSelector::PseudoAfter:
case CSSSelector::PseudoBackdrop:
case CSSSelector::PseudoLang:
case CSSSelector::PseudoNot:
case CSSSelector::PseudoResizer:
case CSSSelector::PseudoRoot:
case CSSSelector::PseudoScope:
case CSSSelector::PseudoScrollbar:
case CSSSelector::PseudoScrollbarButton:
case CSSSelector::PseudoScrollbarCorner:
case CSSSelector::PseudoScrollbarThumb:
case CSSSelector::PseudoScrollbarTrack:
case CSSSelector::PseudoScrollbarTrackPiece:
case CSSSelector::PseudoWindowInactive:
case CSSSelector::PseudoSelection:
case CSSSelector::PseudoCornerPresent:
case CSSSelector::PseudoDecrement:
case CSSSelector::PseudoIncrement:
case CSSSelector::PseudoHorizontal:
case CSSSelector::PseudoVertical:
case CSSSelector::PseudoStart:
case CSSSelector::PseudoEnd:
case CSSSelector::PseudoDoubleButton:
case CSSSelector::PseudoSingleButton:
case CSSSelector::PseudoNoButton:
case CSSSelector::PseudoFullScreen:
case CSSSelector::PseudoFullScreenAncestor:
case CSSSelector::PseudoInRange:
case CSSSelector::PseudoOutOfRange:
case CSSSelector::PseudoWebKitCustomElement:
case CSSSelector::PseudoBlinkInternalElement:
case CSSSelector::PseudoCue:
case CSSSelector::PseudoFutureCue:
case CSSSelector::PseudoPastCue:
case CSSSelector::PseudoUnresolved:
case CSSSelector::PseudoDefined:
case CSSSelector::PseudoContent:
case CSSSelector::PseudoHost:
case CSSSelector::PseudoShadow:
case CSSSelector::PseudoSpatialNavigationFocus:
case CSSSelector::PseudoListBox:
case CSSSelector::PseudoHostHasAppearance:
case CSSSelector::PseudoSlotted:
return true;
case CSSSelector::PseudoUnknown:
case CSSSelector::PseudoLeftPage:
case CSSSelector::PseudoRightPage:
case CSSSelector::PseudoFirstPage:
// These should not appear in StyleRule selectors.
ASSERT_NOT_REACHED();
return false;
default:
// New pseudo type added. Figure out if it needs a subtree invalidation or not.
ASSERT_NOT_REACHED();
return false;
}
}
bool supportsInvalidationWithSelectorList(CSSSelector::PseudoType pseudo)
{
return pseudo == CSSSelector::PseudoAny
|| pseudo == CSSSelector::PseudoCue
|| pseudo == CSSSelector::PseudoHost
|| pseudo == CSSSelector::PseudoHostContext
|| pseudo == CSSSelector::PseudoNot
|| pseudo == CSSSelector::PseudoSlotted;
}
#endif // ENABLE(ASSERT)
bool requiresSubtreeInvalidation(const CSSSelector& selector)
{
if (selector.match() != CSSSelector::PseudoElement && selector.match() != CSSSelector::PseudoClass) {
ASSERT(supportsInvalidation(selector.match()));
return false;
}
switch (selector.getPseudoType()) {
case CSSSelector::PseudoFirstLine:
case CSSSelector::PseudoFirstLetter:
// FIXME: Most pseudo classes/elements above can be supported and moved
// to assertSupportedPseudo(). Move on a case-by-case basis. If they
// require subtree invalidation, document why.
case CSSSelector::PseudoHostContext:
// :host-context matches a shadow host, yet the simple selectors inside
// :host-context matches an ancestor of the shadow host.
return true;
default:
ASSERT(supportsInvalidation(selector.getPseudoType()));
return false;
}
}
template<class Map>
InvalidationSet& ensureInvalidationSet(Map& map, const typename Map::KeyType& key, InvalidationType type)
{
typename Map::AddResult addResult = map.add(key, nullptr);
if (addResult.isNewEntry) {
if (type == InvalidateDescendants)
addResult.storedValue->value = DescendantInvalidationSet::create();
else
addResult.storedValue->value = SiblingInvalidationSet::create(nullptr);
return *addResult.storedValue->value;
}
if (addResult.storedValue->value->type() == type)
return *addResult.storedValue->value;
if (type == InvalidateDescendants)
return toSiblingInvalidationSet(addResult.storedValue->value.get())->ensureDescendants();
addResult.storedValue->value = SiblingInvalidationSet::create(toDescendantInvalidationSet(addResult.storedValue->value.get()));
return *addResult.storedValue->value;
}
void extractInvalidationSets(InvalidationSet* invalidationSet, DescendantInvalidationSet*& descendants, SiblingInvalidationSet*& siblings)
{
if (invalidationSet->type() == InvalidateDescendants) {
descendants = toDescendantInvalidationSet(invalidationSet);
siblings = nullptr;
return;
}
siblings = toSiblingInvalidationSet(invalidationSet);
descendants = siblings->descendants();
}
} // anonymous namespace
RuleFeature::RuleFeature(StyleRule* rule, unsigned selectorIndex, bool hasDocumentSecurityOrigin)
: rule(rule)
, selectorIndex(selectorIndex)
, hasDocumentSecurityOrigin(hasDocumentSecurityOrigin)
{
}
DEFINE_TRACE(RuleFeature)
{
visitor->trace(rule);
}
RuleFeatureSet::RuleFeatureSet()
{
}
RuleFeatureSet::~RuleFeatureSet()
{
}
ALWAYS_INLINE InvalidationSet& RuleFeatureSet::ensureClassInvalidationSet(const AtomicString& className, InvalidationType type)
{
return ensureInvalidationSet(m_classInvalidationSets, className, type);
}
ALWAYS_INLINE InvalidationSet& RuleFeatureSet::ensureAttributeInvalidationSet(const AtomicString& attributeName, InvalidationType type)
{
return ensureInvalidationSet(m_attributeInvalidationSets, attributeName, type);
}
ALWAYS_INLINE InvalidationSet& RuleFeatureSet::ensureIdInvalidationSet(const AtomicString& id, InvalidationType type)
{
return ensureInvalidationSet(m_idInvalidationSets, id, type);
}
ALWAYS_INLINE InvalidationSet& RuleFeatureSet::ensurePseudoInvalidationSet(CSSSelector::PseudoType pseudoType, InvalidationType type)
{
return ensureInvalidationSet(m_pseudoInvalidationSets, pseudoType, type);
}
bool RuleFeatureSet::extractInvalidationSetFeature(const CSSSelector& selector, InvalidationSetFeatures& features)
{
if (selector.match() == CSSSelector::Tag && selector.tagQName().localName() != starAtom) {
features.tagName = selector.tagQName().localName();
return true;
}
if (selector.match() == CSSSelector::Id) {
features.id = selector.value();
return true;
}
if (selector.match() == CSSSelector::Class) {
features.classes.append(selector.value());
return true;
}
if (selector.isAttributeSelector()) {
features.attributes.append(selector.attribute().localName());
return true;
}
if (selector.getPseudoType() == CSSSelector::PseudoWebKitCustomElement || selector.getPseudoType() == CSSSelector::PseudoBlinkInternalElement) {
features.customPseudoElement = true;
return true;
}
if (selector.getPseudoType() == CSSSelector::PseudoBefore || selector.getPseudoType() == CSSSelector::PseudoAfter)
features.hasBeforeOrAfter = true;
return false;
}
InvalidationSet* RuleFeatureSet::invalidationSetForSelector(const CSSSelector& selector, InvalidationType type)
{
if (selector.match() == CSSSelector::Class)
return &ensureClassInvalidationSet(selector.value(), type);
if (selector.isAttributeSelector())
return &ensureAttributeInvalidationSet(selector.attribute().localName(), type);
if (selector.match() == CSSSelector::Id)
return &ensureIdInvalidationSet(selector.value(), type);
if (selector.match() == CSSSelector::PseudoClass) {
switch (selector.getPseudoType()) {
case CSSSelector::PseudoEmpty:
case CSSSelector::PseudoLink:
case CSSSelector::PseudoVisited:
case CSSSelector::PseudoAnyLink:
case CSSSelector::PseudoAutofill:
case CSSSelector::PseudoHover:
case CSSSelector::PseudoDrag:
case CSSSelector::PseudoFocus:
case CSSSelector::PseudoActive:
case CSSSelector::PseudoChecked:
case CSSSelector::PseudoEnabled:
case CSSSelector::PseudoDefault:
case CSSSelector::PseudoDisabled:
case CSSSelector::PseudoOptional:
case CSSSelector::PseudoPlaceholderShown:
case CSSSelector::PseudoRequired:
case CSSSelector::PseudoReadOnly:
case CSSSelector::PseudoReadWrite:
case CSSSelector::PseudoValid:
case CSSSelector::PseudoInvalid:
case CSSSelector::PseudoIndeterminate:
case CSSSelector::PseudoTarget:
case CSSSelector::PseudoLang:
case CSSSelector::PseudoFullScreen:
case CSSSelector::PseudoFullScreenAncestor:
case CSSSelector::PseudoInRange:
case CSSSelector::PseudoOutOfRange:
case CSSSelector::PseudoUnresolved:
case CSSSelector::PseudoDefined:
return &ensurePseudoInvalidationSet(selector.getPseudoType(), type);
default:
break;
}
}
return nullptr;
}
// Given a rule, update the descendant invalidation sets for the features found
// in its selector. The first step is to extract the features from the rightmost
// compound selector (extractInvalidationSetFeatures). Secondly, add those features
// to the invalidation sets for the features found in the other compound selectors
// (addFeaturesToInvalidationSets). If we find a feature in the right-most compound
// selector that requires a subtree recalc, we addFeaturesToInvalidationSets for the
// rightmost compound selector as well.
void RuleFeatureSet::updateInvalidationSets(const RuleData& ruleData)
{
InvalidationSetFeatures features;
auto result = extractInvalidationSetFeatures(ruleData.selector(), features, Subject);
if (result.first) {
features.forceSubtree = result.second == ForceSubtree;
addFeaturesToInvalidationSets(result.first, features.adjacent ? &features : nullptr, features);
}
// If any ::before and ::after rules specify 'content: attr(...)', we
// need to create invalidation sets for those attributes.
if (features.hasBeforeOrAfter)
updateInvalidationSetsForContentAttribute(ruleData);
}
void RuleFeatureSet::updateInvalidationSetsForContentAttribute(const RuleData& ruleData)
{
const StylePropertySet& propertySet = ruleData.rule()->properties();
int propertyIndex = propertySet.findPropertyIndex(CSSPropertyContent);
if (propertyIndex == -1)
return;
StylePropertySet::PropertyReference contentProperty = propertySet.propertyAt(propertyIndex);
CSSValue* contentValue = contentProperty.value();
if (!contentValue->isValueList())
return;
for (auto& item : toCSSValueList(*contentValue)) {
if (!item->isFunctionValue())
continue;
CSSFunctionValue* functionValue = toCSSFunctionValue(item.get());
if (functionValue->functionType() != CSSValueAttr)
continue;
ensureAttributeInvalidationSet(AtomicString(toCSSCustomIdentValue(functionValue->item(0))->value()), InvalidateDescendants).setInvalidatesSelf();
}
}
std::pair<const CSSSelector*, RuleFeatureSet::UseFeaturesType>
RuleFeatureSet::extractInvalidationSetFeatures(const CSSSelector& selector, InvalidationSetFeatures& features, PositionType position, CSSSelector::PseudoType pseudo)
{
bool foundFeatures = false;
for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
if (pseudo != CSSSelector::PseudoNot)
foundFeatures |= extractInvalidationSetFeature(*current, features);
// Initialize the entry in the invalidation set map, if supported.
if (InvalidationSet* invalidationSet = invalidationSetForSelector(*current, InvalidateDescendants)) {
if (position == Subject)
invalidationSet->setInvalidatesSelf();
} else {
if (requiresSubtreeInvalidation(*current)) {
// Fall back to use subtree invalidations, even for features in the
// rightmost compound selector. Returning the start &selector here
// will make addFeaturesToInvalidationSets start marking invalidation
// sets for subtree recalc for features in the rightmost compound
// selector.
return std::make_pair(&selector, ForceSubtree);
}
if (const CSSSelectorList* selectorList = current->selectorList()) {
if (current->getPseudoType() == CSSSelector::PseudoSlotted) {
ASSERT(position == Subject);
features.invalidatesSlotted = true;
}
ASSERT(supportsInvalidationWithSelectorList(current->getPseudoType()));
const CSSSelector* subSelector = selectorList->first();
bool allSubSelectorsHaveFeatures = !!subSelector;
for (; subSelector; subSelector = CSSSelectorList::next(*subSelector)) {
auto result = extractInvalidationSetFeatures(*subSelector, features, position, current->getPseudoType());
if (result.first) {
// A non-null selector return means the sub-selector contained a
// selector which requiresSubtreeInvalidation(). Return the rightmost
// selector to mark for subtree recalcs like above.
return std::make_pair(&selector, ForceSubtree);
}
allSubSelectorsHaveFeatures &= result.second == UseFeatures;
}
foundFeatures |= allSubSelectorsHaveFeatures;
}
}
if (current->relation() == CSSSelector::SubSelector)
continue;
features.treeBoundaryCrossing = current->isShadowSelector();
if (current->relationIsAffectedByPseudoContent()) {
features.contentPseudoCrossing = true;
features.insertionPointCrossing = true;
}
features.adjacent = current->isAdjacentSelector();
if (current->relation() == CSSSelector::DirectAdjacent)
features.maxDirectAdjacentSelectors = 1;
return std::make_pair(current->tagHistory(), foundFeatures ? UseFeatures : ForceSubtree);
}
return std::make_pair(nullptr, foundFeatures ? UseFeatures : ForceSubtree);
}
// Add features extracted from the rightmost compound selector to descendant invalidation
// sets for features found in other compound selectors.
//
// We use descendant invalidation for descendants, sibling invalidation for siblings and their subtrees.
//
// As we encounter a descendant type of combinator, the features only need to be checked
// against descendants in the same subtree only. features.adjacent is set to false, and
// we start adding features to the descendant invalidation set.
void RuleFeatureSet::addFeaturesToInvalidationSet(InvalidationSet& invalidationSet, const InvalidationSetFeatures& features)
{
if (features.treeBoundaryCrossing)
invalidationSet.setTreeBoundaryCrossing();
if (features.insertionPointCrossing)
invalidationSet.setInsertionPointCrossing();
if (features.invalidatesSlotted)
invalidationSet.setInvalidatesSlotted();
if (features.forceSubtree)
invalidationSet.setWholeSubtreeInvalid();
if (features.contentPseudoCrossing || features.forceSubtree)
return;
if (!features.id.isEmpty())
invalidationSet.addId(features.id);
if (!features.tagName.isEmpty())
invalidationSet.addTagName(features.tagName);
for (const auto& className : features.classes)
invalidationSet.addClass(className);
for (const auto& attribute : features.attributes)
invalidationSet.addAttribute(attribute);
if (features.customPseudoElement)
invalidationSet.setCustomPseudoInvalid();
}
// selector is the selector immediately to the left of the rightmost combinator.
// siblingFeatures is null if selector is not immediately to the left of a sibling combinator.
// descendantFeatures has the features of the rightmost compound selector.
void RuleFeatureSet::addFeaturesToInvalidationSets(const CSSSelector* selector, InvalidationSetFeatures* siblingFeatures, InvalidationSetFeatures& descendantFeatures)
{
const CSSSelector* lastCompoundSelectorInAdjacentChain = selector;
// We set siblingFeatures to &localFeatures if we find a rightmost sibling combinator.
InvalidationSetFeatures localFeatures;
for (const CSSSelector* current = selector; current; current = current->tagHistory()) {
InvalidationType type = siblingFeatures ? InvalidateSiblings : InvalidateDescendants;
if (InvalidationSet* invalidationSet = invalidationSetForSelector(*current, type)) {
if (siblingFeatures) {
SiblingInvalidationSet* siblingInvalidationSet = toSiblingInvalidationSet(invalidationSet);
siblingInvalidationSet->updateMaxDirectAdjacentSelectors(siblingFeatures->maxDirectAdjacentSelectors);
addFeaturesToInvalidationSet(*invalidationSet, *siblingFeatures);
if (siblingFeatures == &descendantFeatures)
siblingInvalidationSet->setInvalidatesSelf();
else
addFeaturesToInvalidationSet(siblingInvalidationSet->ensureSiblingDescendants(), descendantFeatures);
} else {
addFeaturesToInvalidationSet(*invalidationSet, descendantFeatures);
}
} else {
if (current->isHostPseudoClass())
descendantFeatures.treeBoundaryCrossing = true;
if (current->isInsertionPointCrossing())
descendantFeatures.insertionPointCrossing = true;
if (const CSSSelectorList* selectorList = current->selectorList()) {
ASSERT(supportsInvalidationWithSelectorList(current->getPseudoType()));
for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(*subSelector))
addFeaturesToInvalidationSets(subSelector, siblingFeatures, descendantFeatures);
}
}
if (current->relation() == CSSSelector::SubSelector)
continue;
if (current->relationIsAffectedByPseudoContent() || current->relation() == CSSSelector::ShadowSlot) {
descendantFeatures.insertionPointCrossing = true;
descendantFeatures.contentPseudoCrossing = true;
}
if (current->isShadowSelector())
descendantFeatures.treeBoundaryCrossing = true;
if (!current->isAdjacentSelector()) {
lastCompoundSelectorInAdjacentChain = current->tagHistory();
siblingFeatures = nullptr;
continue;
}
if (siblingFeatures) {
if (siblingFeatures->maxDirectAdjacentSelectors == UINT_MAX)
continue;
if (current->relation() == CSSSelector::DirectAdjacent)
siblingFeatures->maxDirectAdjacentSelectors++;
else
siblingFeatures->maxDirectAdjacentSelectors = UINT_MAX;
continue;
}
localFeatures = InvalidationSetFeatures();
auto result = extractInvalidationSetFeatures(*lastCompoundSelectorInAdjacentChain, localFeatures, Ancestor);
ASSERT(result.first);
localFeatures.forceSubtree = result.second == ForceSubtree;
siblingFeatures = &localFeatures;
}
}
RuleFeatureSet::SelectorPreMatch RuleFeatureSet::collectFeaturesFromRuleData(const RuleData& ruleData)
{
FeatureMetadata metadata;
if (collectFeaturesFromSelector(ruleData.selector(), metadata) == SelectorNeverMatches)
return SelectorNeverMatches;
m_metadata.add(metadata);
if (metadata.foundSiblingSelector)
siblingRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
if (ruleData.containsUncommonAttributeSelector())
uncommonAttributeRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
updateInvalidationSets(ruleData);
return SelectorMayMatch;
}
RuleFeatureSet::SelectorPreMatch RuleFeatureSet::collectFeaturesFromSelector(const CSSSelector& selector, RuleFeatureSet::FeatureMetadata& metadata)
{
unsigned maxDirectAdjacentSelectors = 0;
CSSSelector::RelationType relation = CSSSelector::Descendant;
bool foundHostPseudo = false;
for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
switch (current->getPseudoType()) {
case CSSSelector::PseudoFirstLine:
metadata.usesFirstLineRules = true;
break;
case CSSSelector::PseudoWindowInactive:
metadata.usesWindowInactiveSelector = true;
break;
case CSSSelector::PseudoEmpty:
case CSSSelector::PseudoFirstChild:
case CSSSelector::PseudoFirstOfType:
case CSSSelector::PseudoLastChild:
case CSSSelector::PseudoLastOfType:
case CSSSelector::PseudoOnlyChild:
case CSSSelector::PseudoOnlyOfType:
case CSSSelector::PseudoNthChild:
case CSSSelector::PseudoNthOfType:
case CSSSelector::PseudoNthLastChild:
case CSSSelector::PseudoNthLastOfType:
if (!metadata.foundInsertionPointCrossing)
metadata.foundSiblingSelector = true;
break;
case CSSSelector::PseudoHost:
case CSSSelector::PseudoHostContext:
if (!foundHostPseudo && relation == CSSSelector::SubSelector)
return SelectorNeverMatches;
if (!current->isLastInTagHistory()
&& current->tagHistory()->match() != CSSSelector::PseudoElement
&& !current->tagHistory()->isHostPseudoClass()) {
return SelectorNeverMatches;
}
foundHostPseudo = true;
// fall through.
default:
if (const CSSSelectorList* selectorList = current->selectorList()) {
for (const CSSSelector* subSelector = selectorList->first(); subSelector; subSelector = CSSSelectorList::next(*subSelector))
collectFeaturesFromSelector(*subSelector, metadata);
}
break;
}
if (current->relationIsAffectedByPseudoContent() || current->getPseudoType() == CSSSelector::PseudoSlotted)
metadata.foundInsertionPointCrossing = true;
relation = current->relation();
if (foundHostPseudo && relation != CSSSelector::SubSelector)
return SelectorNeverMatches;
if (relation == CSSSelector::DirectAdjacent) {
maxDirectAdjacentSelectors++;
} else if (maxDirectAdjacentSelectors
&& ((relation != CSSSelector::SubSelector) || current->isLastInTagHistory())) {
if (maxDirectAdjacentSelectors > metadata.maxDirectAdjacentSelectors)
metadata.maxDirectAdjacentSelectors = maxDirectAdjacentSelectors;
maxDirectAdjacentSelectors = 0;
}
if (!metadata.foundInsertionPointCrossing && current->isAdjacentSelector())
metadata.foundSiblingSelector = true;
}
ASSERT(!maxDirectAdjacentSelectors);
return SelectorMayMatch;
}
void RuleFeatureSet::FeatureMetadata::add(const FeatureMetadata& other)
{
usesFirstLineRules = usesFirstLineRules || other.usesFirstLineRules;
usesWindowInactiveSelector = usesWindowInactiveSelector || other.usesWindowInactiveSelector;
maxDirectAdjacentSelectors = std::max(maxDirectAdjacentSelectors, other.maxDirectAdjacentSelectors);
}
void RuleFeatureSet::FeatureMetadata::clear()
{
usesFirstLineRules = false;
usesWindowInactiveSelector = false;
foundSiblingSelector = false;
foundInsertionPointCrossing = false;
maxDirectAdjacentSelectors = 0;
}
void RuleFeatureSet::add(const RuleFeatureSet& other)
{
for (const auto& entry : other.m_classInvalidationSets)
ensureInvalidationSet(m_classInvalidationSets, entry.key, entry.value->type()).combine(*entry.value);
for (const auto& entry : other.m_attributeInvalidationSets)
ensureInvalidationSet(m_attributeInvalidationSets, entry.key, entry.value->type()).combine(*entry.value);
for (const auto& entry : other.m_idInvalidationSets)
ensureInvalidationSet(m_idInvalidationSets, entry.key, entry.value->type()).combine(*entry.value);
for (const auto& entry : other.m_pseudoInvalidationSets)
ensureInvalidationSet(m_pseudoInvalidationSets, static_cast<CSSSelector::PseudoType>(entry.key), entry.value->type()).combine(*entry.value);
m_metadata.add(other.m_metadata);
siblingRules.appendVector(other.siblingRules);
uncommonAttributeRules.appendVector(other.uncommonAttributeRules);
}
void RuleFeatureSet::clear()
{
siblingRules.clear();
uncommonAttributeRules.clear();
m_metadata.clear();
m_classInvalidationSets.clear();
m_attributeInvalidationSets.clear();
m_idInvalidationSets.clear();
m_pseudoInvalidationSets.clear();
}
void RuleFeatureSet::collectInvalidationSetsForClass(InvalidationLists& invalidationLists, Element& element, const AtomicString& className) const
{
InvalidationSetMap::const_iterator it = m_classInvalidationSets.find(className);
if (it == m_classInvalidationSets.end())
return;
DescendantInvalidationSet* descendants;
SiblingInvalidationSet* siblings;
extractInvalidationSets(it->value.get(), descendants, siblings);
if (descendants) {
TRACE_SCHEDULE_STYLE_INVALIDATION(element, *descendants, classChange, className);
invalidationLists.descendants.append(descendants);
}
if (siblings) {
TRACE_SCHEDULE_STYLE_INVALIDATION(element, *siblings, classChange, className);
invalidationLists.siblings.append(siblings);
}
}
void RuleFeatureSet::collectInvalidationSetsForId(InvalidationLists& invalidationLists, Element& element, const AtomicString& id) const
{
InvalidationSetMap::const_iterator it = m_idInvalidationSets.find(id);
if (it == m_idInvalidationSets.end())
return;
DescendantInvalidationSet* descendants;
SiblingInvalidationSet* siblings;
extractInvalidationSets(it->value.get(), descendants, siblings);
if (descendants) {
TRACE_SCHEDULE_STYLE_INVALIDATION(element, *descendants, idChange, id);
invalidationLists.descendants.append(descendants);
}
if (siblings) {
TRACE_SCHEDULE_STYLE_INVALIDATION(element, *siblings, idChange, id);
invalidationLists.siblings.append(siblings);
}
}
void RuleFeatureSet::collectInvalidationSetsForAttribute(InvalidationLists& invalidationLists, Element& element, const QualifiedName& attributeName) const
{
InvalidationSetMap::const_iterator it = m_attributeInvalidationSets.find(attributeName.localName());
if (it == m_attributeInvalidationSets.end())
return;
DescendantInvalidationSet* descendants;
SiblingInvalidationSet* siblings;
extractInvalidationSets(it->value.get(), descendants, siblings);
if (descendants) {
TRACE_SCHEDULE_STYLE_INVALIDATION(element, *descendants, attributeChange, attributeName);
invalidationLists.descendants.append(descendants);
}
if (siblings) {
TRACE_SCHEDULE_STYLE_INVALIDATION(element, *siblings, attributeChange, attributeName);
invalidationLists.siblings.append(siblings);
}
}
void RuleFeatureSet::collectInvalidationSetsForPseudoClass(InvalidationLists& invalidationLists, Element& element, CSSSelector::PseudoType pseudo) const
{
PseudoTypeInvalidationSetMap::const_iterator it = m_pseudoInvalidationSets.find(pseudo);
if (it == m_pseudoInvalidationSets.end())
return;
DescendantInvalidationSet* descendants;
SiblingInvalidationSet* siblings;
extractInvalidationSets(it->value.get(), descendants, siblings);
if (descendants) {
TRACE_SCHEDULE_STYLE_INVALIDATION(element, *descendants, pseudoChange, pseudo);
invalidationLists.descendants.append(descendants);
}
if (siblings) {
TRACE_SCHEDULE_STYLE_INVALIDATION(element, *siblings, pseudoChange, pseudo);
invalidationLists.siblings.append(siblings);
}
}
DEFINE_TRACE(RuleFeatureSet)
{
visitor->trace(siblingRules);
visitor->trace(uncommonAttributeRules);
}
} // namespace blink