2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
7 * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
10 * Copyright (C) Research In Motion Limited 2011. All rights reserved.
11 * Copyright (C) 2012 Google Inc. All rights reserved.
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Library General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Library General Public License for more details.
23 * You should have received a copy of the GNU Library General Public License
24 * along with this library; see the file COPYING.LIB. If not, write to
25 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 * Boston, MA 02110-1301, USA.
30 #include "core/css/RuleFeature.h"
32 #include "HTMLNames.h"
33 #include "RuntimeEnabledFeatures.h"
34 #include "core/css/CSSSelector.h"
35 #include "core/css/CSSSelectorList.h"
36 #include "core/css/RuleSet.h"
37 #include "core/dom/Element.h"
38 #include "core/dom/Node.h"
39 #include "wtf/BitVector.h"
43 static bool isSkippableComponentForInvalidation(const CSSSelector& selector)
45 if (selector.m_match == CSSSelector::Tag
46 || selector.m_match == CSSSelector::Id
47 || selector.isAttributeSelector())
49 if (selector.m_match == CSSSelector::PseudoElement) {
50 switch (selector.pseudoType()) {
51 case CSSSelector::PseudoBefore:
52 case CSSSelector::PseudoAfter:
53 case CSSSelector::PseudoBackdrop:
54 case CSSSelector::PseudoShadow:
60 if (selector.m_match != CSSSelector::PseudoClass)
62 switch (selector.pseudoType()) {
63 case CSSSelector::PseudoEmpty:
64 case CSSSelector::PseudoFirstChild:
65 case CSSSelector::PseudoFirstOfType:
66 case CSSSelector::PseudoLastChild:
67 case CSSSelector::PseudoLastOfType:
68 case CSSSelector::PseudoOnlyChild:
69 case CSSSelector::PseudoOnlyOfType:
70 case CSSSelector::PseudoNthChild:
71 case CSSSelector::PseudoNthOfType:
72 case CSSSelector::PseudoNthLastChild:
73 case CSSSelector::PseudoNthLastOfType:
74 case CSSSelector::PseudoLink:
75 case CSSSelector::PseudoVisited:
76 case CSSSelector::PseudoAnyLink:
77 case CSSSelector::PseudoHover:
78 case CSSSelector::PseudoDrag:
79 case CSSSelector::PseudoFocus:
80 case CSSSelector::PseudoActive:
81 case CSSSelector::PseudoChecked:
82 case CSSSelector::PseudoEnabled:
83 case CSSSelector::PseudoDefault:
84 case CSSSelector::PseudoDisabled:
85 case CSSSelector::PseudoOptional:
86 case CSSSelector::PseudoRequired:
87 case CSSSelector::PseudoReadOnly:
88 case CSSSelector::PseudoReadWrite:
89 case CSSSelector::PseudoValid:
90 case CSSSelector::PseudoInvalid:
91 case CSSSelector::PseudoIndeterminate:
92 case CSSSelector::PseudoTarget:
93 case CSSSelector::PseudoLang:
94 case CSSSelector::PseudoRoot:
95 case CSSSelector::PseudoScope:
96 case CSSSelector::PseudoInRange:
97 case CSSSelector::PseudoOutOfRange:
98 case CSSSelector::PseudoUnresolved:
105 // This method is somewhat conservative in what it accepts.
106 RuleFeatureSet::InvalidationSetMode RuleFeatureSet::invalidationSetModeForSelector(const CSSSelector& selector)
108 bool foundDescendantRelation = false;
109 bool foundIdent = false;
110 for (const CSSSelector* component = &selector; component; component = component->tagHistory()) {
112 // FIXME: next up: Tag and Id.
113 if (component->m_match == CSSSelector::Class || component->isAttributeSelector()) {
114 if (!foundDescendantRelation)
116 } else if (component->pseudoType() == CSSSelector::PseudoHost || component->pseudoType() == CSSSelector::PseudoAny) {
117 if (const CSSSelectorList* selectorList = component->selectorList()) {
118 for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(*selector)) {
119 InvalidationSetMode hostMode = invalidationSetModeForSelector(*selector);
120 if (hostMode == UseSubtreeStyleChange)
121 return foundDescendantRelation ? UseLocalStyleChange : UseSubtreeStyleChange;
122 if (!foundDescendantRelation && hostMode == AddFeatures)
126 } else if (!isSkippableComponentForInvalidation(*component)) {
127 return foundDescendantRelation ? UseLocalStyleChange : UseSubtreeStyleChange;
129 switch (component->relation()) {
130 case CSSSelector::Descendant:
131 case CSSSelector::Child:
132 case CSSSelector::ShadowPseudo:
133 case CSSSelector::ShadowDeep:
134 foundDescendantRelation = true;
136 case CSSSelector::SubSelector:
139 return UseLocalStyleChange;
142 return foundIdent ? AddFeatures : UseLocalStyleChange;
145 void RuleFeatureSet::extractInvalidationSetFeature(const CSSSelector& selector, InvalidationSetFeatures& features)
147 if (selector.m_match == CSSSelector::Tag)
148 features.tagName = selector.tagQName().localName();
149 else if (selector.m_match == CSSSelector::Id)
150 features.id = selector.value();
151 else if (selector.m_match == CSSSelector::Class)
152 features.classes.append(selector.value());
153 else if (selector.isAttributeSelector())
154 features.attributes.append(selector.attribute().localName());
157 RuleFeatureSet::RuleFeatureSet()
158 : m_targetedStyleRecalcEnabled(RuntimeEnabledFeatures::targetedStyleRecalcEnabled())
162 DescendantInvalidationSet* RuleFeatureSet::invalidationSetForSelector(const CSSSelector& selector)
164 if (selector.m_match == CSSSelector::Class)
165 return &ensureClassInvalidationSet(selector.value());
166 if (selector.isAttributeSelector())
167 return &ensureAttributeInvalidationSet(selector.attribute().localName());
171 RuleFeatureSet::InvalidationSetMode RuleFeatureSet::updateInvalidationSets(const CSSSelector& selector)
173 InvalidationSetMode mode = invalidationSetModeForSelector(selector);
174 if (mode != AddFeatures)
177 InvalidationSetFeatures features;
178 const CSSSelector* current = extractInvalidationSetFeatures(selector, features);
180 current = current->tagHistory();
183 addFeaturesToInvalidationSets(*current, features);
187 const CSSSelector* RuleFeatureSet::extractInvalidationSetFeatures(const CSSSelector& selector, InvalidationSetFeatures& features)
189 const CSSSelector* lastSelector = &selector;
190 for (; lastSelector; lastSelector = lastSelector->tagHistory()) {
191 extractInvalidationSetFeature(*lastSelector, features);
192 // Initialize the entry in the invalidation set map, if supported.
193 invalidationSetForSelector(*lastSelector);
194 if (lastSelector->pseudoType() == CSSSelector::PseudoHost || lastSelector->pseudoType() == CSSSelector::PseudoAny) {
195 if (const CSSSelectorList* selectorList = lastSelector->selectorList()) {
196 for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(*selector))
197 extractInvalidationSetFeatures(*selector, features);
201 if (lastSelector->relation() != CSSSelector::SubSelector)
207 void RuleFeatureSet::addFeaturesToInvalidationSets(const CSSSelector& selector, const InvalidationSetFeatures& features)
209 for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
210 if (DescendantInvalidationSet* invalidationSet = invalidationSetForSelector(*current)) {
211 if (!features.id.isEmpty())
212 invalidationSet->addId(features.id);
213 if (!features.tagName.isEmpty())
214 invalidationSet->addTagName(features.tagName);
215 for (Vector<AtomicString>::const_iterator it = features.classes.begin(); it != features.classes.end(); ++it)
216 invalidationSet->addClass(*it);
217 for (Vector<AtomicString>::const_iterator it = features.attributes.begin(); it != features.attributes.end(); ++it)
218 invalidationSet->addAttribute(*it);
219 } else if (current->pseudoType() == CSSSelector::PseudoHost || current->pseudoType() == CSSSelector::PseudoAny) {
220 if (const CSSSelectorList* selectorList = current->selectorList()) {
221 for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(*selector))
222 addFeaturesToInvalidationSets(*selector, features);
228 void RuleFeatureSet::addContentAttr(const AtomicString& attributeName)
230 DescendantInvalidationSet& invalidationSet = ensureAttributeInvalidationSet(attributeName);
231 invalidationSet.setWholeSubtreeInvalid();
234 void RuleFeatureSet::collectFeaturesFromRuleData(const RuleData& ruleData)
236 FeatureMetadata metadata;
237 InvalidationSetMode mode = UseSubtreeStyleChange;
238 if (m_targetedStyleRecalcEnabled)
239 mode = updateInvalidationSets(ruleData.selector());
241 collectFeaturesFromSelector(ruleData.selector(), metadata, mode);
242 m_metadata.add(metadata);
244 if (metadata.foundSiblingSelector)
245 siblingRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
246 if (ruleData.containsUncommonAttributeSelector())
247 uncommonAttributeRules.append(RuleFeature(ruleData.rule(), ruleData.selectorIndex(), ruleData.hasDocumentSecurityOrigin()));
250 DescendantInvalidationSet& RuleFeatureSet::ensureClassInvalidationSet(const AtomicString& className)
252 InvalidationSetMap::AddResult addResult = m_classInvalidationSets.add(className, nullptr);
253 if (addResult.isNewEntry)
254 addResult.storedValue->value = DescendantInvalidationSet::create();
255 return *addResult.storedValue->value;
258 DescendantInvalidationSet& RuleFeatureSet::ensureAttributeInvalidationSet(const AtomicString& attributeName)
260 InvalidationSetMap::AddResult addResult = m_attributeInvalidationSets.add(attributeName, nullptr);
261 if (addResult.isNewEntry)
262 addResult.storedValue->value = DescendantInvalidationSet::create();
263 return *addResult.storedValue->value;
265 void RuleFeatureSet::collectFeaturesFromSelector(const CSSSelector& selector)
267 collectFeaturesFromSelector(selector, m_metadata, UseSubtreeStyleChange);
270 void RuleFeatureSet::collectFeaturesFromSelector(const CSSSelector& selector, RuleFeatureSet::FeatureMetadata& metadata, InvalidationSetMode mode)
272 unsigned maxDirectAdjacentSelectors = 0;
274 for (const CSSSelector* current = &selector; current; current = current->tagHistory()) {
275 if (current->m_match == CSSSelector::Id) {
276 metadata.idsInRules.add(current->value());
277 } else if (mode != AddFeatures && (current->m_match == CSSSelector::Class || current->isAttributeSelector())) {
278 DescendantInvalidationSet* invalidationSet = invalidationSetForSelector(*current);
279 ASSERT(invalidationSet);
280 if (mode == UseSubtreeStyleChange)
281 invalidationSet->setWholeSubtreeInvalid();
283 if (current->pseudoType() == CSSSelector::PseudoFirstLine)
284 metadata.usesFirstLineRules = true;
285 if (current->isDirectAdjacentSelector()) {
286 maxDirectAdjacentSelectors++;
287 } else if (maxDirectAdjacentSelectors) {
288 if (maxDirectAdjacentSelectors > metadata.maxDirectAdjacentSelectors)
289 metadata.maxDirectAdjacentSelectors = maxDirectAdjacentSelectors;
290 maxDirectAdjacentSelectors = 0;
292 if (current->isSiblingSelector())
293 metadata.foundSiblingSelector = true;
295 collectFeaturesFromSelectorList(current->selectorList(), metadata, mode);
297 if (mode == UseLocalStyleChange && current->relation() != CSSSelector::SubSelector)
298 mode = UseSubtreeStyleChange;
301 ASSERT(!maxDirectAdjacentSelectors);
304 void RuleFeatureSet::collectFeaturesFromSelectorList(const CSSSelectorList* selectorList, RuleFeatureSet::FeatureMetadata& metadata, InvalidationSetMode mode)
309 for (const CSSSelector* selector = selectorList->first(); selector; selector = CSSSelectorList::next(*selector))
310 collectFeaturesFromSelector(*selector, metadata, mode);
313 void RuleFeatureSet::FeatureMetadata::add(const FeatureMetadata& other)
315 usesFirstLineRules = usesFirstLineRules || other.usesFirstLineRules;
316 maxDirectAdjacentSelectors = std::max(maxDirectAdjacentSelectors, other.maxDirectAdjacentSelectors);
318 HashSet<AtomicString>::const_iterator end = other.idsInRules.end();
319 for (HashSet<AtomicString>::const_iterator it = other.idsInRules.begin(); it != end; ++it)
323 void RuleFeatureSet::FeatureMetadata::clear()
326 usesFirstLineRules = false;
327 foundSiblingSelector = false;
328 maxDirectAdjacentSelectors = 0;
331 void RuleFeatureSet::add(const RuleFeatureSet& other)
333 for (InvalidationSetMap::const_iterator it = other.m_classInvalidationSets.begin(); it != other.m_classInvalidationSets.end(); ++it)
334 ensureClassInvalidationSet(it->key).combine(*it->value);
335 for (InvalidationSetMap::const_iterator it = other.m_attributeInvalidationSets.begin(); it != other.m_attributeInvalidationSets.end(); ++it)
336 ensureAttributeInvalidationSet(it->key).combine(*it->value);
338 m_metadata.add(other.m_metadata);
340 siblingRules.appendVector(other.siblingRules);
341 uncommonAttributeRules.appendVector(other.uncommonAttributeRules);
344 void RuleFeatureSet::clear()
346 siblingRules.clear();
347 uncommonAttributeRules.clear();
349 m_classInvalidationSets.clear();
350 m_attributeInvalidationSets.clear();
351 // We cannot clear m_styleInvalidator here, because the style invalidator might not
352 // have been evaluated yet. If not yet, in StyleInvalidator, there exists some element
353 // who has needsStyleInvlidation but does not have any invalidation list.
354 // This makes Blink not to recalc style correctly. crbug.com/344729.
357 void RuleFeatureSet::scheduleStyleInvalidationForClassChange(const SpaceSplitString& changedClasses, Element* element)
359 unsigned changedSize = changedClasses.size();
360 for (unsigned i = 0; i < changedSize; ++i) {
361 addClassToInvalidationSet(changedClasses[i], element);
365 void RuleFeatureSet::scheduleStyleInvalidationForClassChange(const SpaceSplitString& oldClasses, const SpaceSplitString& newClasses, Element* element)
367 if (!oldClasses.size())
368 scheduleStyleInvalidationForClassChange(newClasses, element);
370 // Class vectors tend to be very short. This is faster than using a hash table.
371 BitVector remainingClassBits;
372 remainingClassBits.ensureSize(oldClasses.size());
374 for (unsigned i = 0; i < newClasses.size(); ++i) {
376 for (unsigned j = 0; j < oldClasses.size(); ++j) {
377 if (newClasses[i] == oldClasses[j]) {
378 // Mark each class that is still in the newClasses so we can skip doing
379 // an n^2 search below when looking for removals. We can't break from
380 // this loop early since a class can appear more than once.
381 remainingClassBits.quickSet(j);
387 addClassToInvalidationSet(newClasses[i], element);
390 for (unsigned i = 0; i < oldClasses.size(); ++i) {
391 if (remainingClassBits.quickGet(i))
393 // Class was removed.
394 addClassToInvalidationSet(oldClasses[i], element);
398 void RuleFeatureSet::scheduleStyleInvalidationForAttributeChange(const QualifiedName& attributeName, Element* element)
400 if (RefPtr<DescendantInvalidationSet> invalidationSet = m_attributeInvalidationSets.get(attributeName.localName())) {
401 ensurePendingInvalidationList(element).append(invalidationSet);
402 element->setNeedsStyleInvalidation();
406 void RuleFeatureSet::addClassToInvalidationSet(const AtomicString& className, Element* element)
408 if (RefPtr<DescendantInvalidationSet> invalidationSet = m_classInvalidationSets.get(className)) {
409 ensurePendingInvalidationList(element).append(invalidationSet);
410 element->setNeedsStyleInvalidation();
414 RuleFeatureSet::InvalidationList& RuleFeatureSet::ensurePendingInvalidationList(Element* element)
416 PendingInvalidationMap::AddResult addResult = m_pendingInvalidationMap.add(element, nullptr);
417 if (addResult.isNewEntry)
418 addResult.storedValue->value = adoptPtr(new InvalidationList);
419 return *addResult.storedValue->value;
422 void RuleFeatureSet::clearStyleInvalidation(Node* node)
424 node->clearChildNeedsStyleInvalidation();
425 node->clearNeedsStyleInvalidation();
426 if (node->isElementNode())
427 m_pendingInvalidationMap.remove(toElement(node));
430 RuleFeatureSet::PendingInvalidationMap& RuleFeatureSet::pendingInvalidationMap()
432 return m_pendingInvalidationMap;
435 } // namespace WebCore