OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) | 3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) |
4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.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 r ights reserved. | 5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All r ights reserved. |
6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> | 6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> |
7 * Copyright (C) 2007, 2008 Eric Seidel <eric@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.t orchmobile.com/) | 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.t orchmobile.com/) |
9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved. | 9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved. |
10 * Copyright (C) Research In Motion Limited 2011. All rights reserved. | 10 * Copyright (C) Research In Motion Limited 2011. All rights reserved. |
(...skipping 29 matching lines...) Expand all Loading... | |
40 #include "core/css/invalidation/InvalidationSet.h" | 40 #include "core/css/invalidation/InvalidationSet.h" |
41 #include "core/dom/Element.h" | 41 #include "core/dom/Element.h" |
42 #include "core/dom/Node.h" | 42 #include "core/dom/Node.h" |
43 #include "core/inspector/InspectorTraceEvents.h" | 43 #include "core/inspector/InspectorTraceEvents.h" |
44 #include "wtf/BitVector.h" | 44 #include "wtf/BitVector.h" |
45 | 45 |
46 namespace blink { | 46 namespace blink { |
47 | 47 |
48 namespace { | 48 namespace { |
49 | 49 |
50 #if ENABLE(ASSERT) | |
51 | |
52 bool supportsInvalidation(CSSSelector::MatchType match) | 50 bool supportsInvalidation(CSSSelector::MatchType match) |
53 { | 51 { |
54 switch (match) { | 52 switch (match) { |
55 case CSSSelector::Tag: | 53 case CSSSelector::Tag: |
56 case CSSSelector::Id: | 54 case CSSSelector::Id: |
57 case CSSSelector::Class: | 55 case CSSSelector::Class: |
58 case CSSSelector::AttributeExact: | 56 case CSSSelector::AttributeExact: |
59 case CSSSelector::AttributeSet: | 57 case CSSSelector::AttributeSet: |
60 case CSSSelector::AttributeHyphen: | 58 case CSSSelector::AttributeHyphen: |
61 case CSSSelector::AttributeList: | 59 case CSSSelector::AttributeList: |
62 case CSSSelector::AttributeContain: | 60 case CSSSelector::AttributeContain: |
63 case CSSSelector::AttributeBegin: | 61 case CSSSelector::AttributeBegin: |
64 case CSSSelector::AttributeEnd: | 62 case CSSSelector::AttributeEnd: |
65 return true; | 63 return true; |
66 case CSSSelector::Unknown: | 64 case CSSSelector::Unknown: |
67 case CSSSelector::PagePseudoClass: | 65 case CSSSelector::PagePseudoClass: |
68 // These should not appear in StyleRule selectors. | 66 // These should not appear in StyleRule selectors. |
69 ASSERT_NOT_REACHED(); | 67 NOTREACHED(); |
70 return false; | 68 return false; |
71 default: | 69 default: |
72 // New match type added. Figure out if it needs a subtree invalidation o r not. | 70 // New match type added. Figure out if it needs a subtree invalidation o r not. |
73 ASSERT_NOT_REACHED(); | 71 NOTREACHED(); |
74 return false; | 72 return false; |
75 } | 73 } |
76 } | 74 } |
77 | 75 |
78 bool supportsInvalidation(CSSSelector::PseudoType type) | 76 bool supportsInvalidation(CSSSelector::PseudoType type) |
79 { | 77 { |
80 switch (type) { | 78 switch (type) { |
81 case CSSSelector::PseudoEmpty: | 79 case CSSSelector::PseudoEmpty: |
82 case CSSSelector::PseudoFirstChild: | 80 case CSSSelector::PseudoFirstChild: |
83 case CSSSelector::PseudoFirstOfType: | 81 case CSSSelector::PseudoFirstOfType: |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
155 case CSSSelector::PseudoSpatialNavigationFocus: | 153 case CSSSelector::PseudoSpatialNavigationFocus: |
156 case CSSSelector::PseudoListBox: | 154 case CSSSelector::PseudoListBox: |
157 case CSSSelector::PseudoHostHasAppearance: | 155 case CSSSelector::PseudoHostHasAppearance: |
158 case CSSSelector::PseudoSlotted: | 156 case CSSSelector::PseudoSlotted: |
159 return true; | 157 return true; |
160 case CSSSelector::PseudoUnknown: | 158 case CSSSelector::PseudoUnknown: |
161 case CSSSelector::PseudoLeftPage: | 159 case CSSSelector::PseudoLeftPage: |
162 case CSSSelector::PseudoRightPage: | 160 case CSSSelector::PseudoRightPage: |
163 case CSSSelector::PseudoFirstPage: | 161 case CSSSelector::PseudoFirstPage: |
164 // These should not appear in StyleRule selectors. | 162 // These should not appear in StyleRule selectors. |
165 ASSERT_NOT_REACHED(); | 163 NOTREACHED(); |
166 return false; | 164 return false; |
167 default: | 165 default: |
168 // New pseudo type added. Figure out if it needs a subtree invalidation or not. | 166 // New pseudo type added. Figure out if it needs a subtree invalidation or not. |
169 ASSERT_NOT_REACHED(); | 167 NOTREACHED(); |
170 return false; | 168 return false; |
171 } | 169 } |
172 } | 170 } |
173 | 171 |
174 bool supportsInvalidationWithSelectorList(CSSSelector::PseudoType pseudo) | 172 bool supportsInvalidationWithSelectorList(CSSSelector::PseudoType pseudo) |
175 { | 173 { |
176 return pseudo == CSSSelector::PseudoAny | 174 return pseudo == CSSSelector::PseudoAny |
177 || pseudo == CSSSelector::PseudoCue | 175 || pseudo == CSSSelector::PseudoCue |
178 || pseudo == CSSSelector::PseudoHost | 176 || pseudo == CSSSelector::PseudoHost |
179 || pseudo == CSSSelector::PseudoHostContext | 177 || pseudo == CSSSelector::PseudoHostContext |
180 || pseudo == CSSSelector::PseudoNot | 178 || pseudo == CSSSelector::PseudoNot |
181 || pseudo == CSSSelector::PseudoSlotted; | 179 || pseudo == CSSSelector::PseudoSlotted; |
182 } | 180 } |
183 | 181 |
184 #endif // ENABLE(ASSERT) | |
185 | |
186 bool requiresSubtreeInvalidation(const CSSSelector& selector) | 182 bool requiresSubtreeInvalidation(const CSSSelector& selector) |
187 { | 183 { |
188 if (selector.match() != CSSSelector::PseudoElement && selector.match() != CS SSelector::PseudoClass) { | 184 if (selector.match() != CSSSelector::PseudoElement && selector.match() != CS SSelector::PseudoClass) { |
189 ASSERT(supportsInvalidation(selector.match())); | 185 DCHECK(supportsInvalidation(selector.match())); |
190 return false; | 186 return false; |
191 } | 187 } |
192 | 188 |
193 switch (selector.getPseudoType()) { | 189 switch (selector.getPseudoType()) { |
194 case CSSSelector::PseudoFirstLine: | 190 case CSSSelector::PseudoFirstLine: |
195 case CSSSelector::PseudoFirstLetter: | 191 case CSSSelector::PseudoFirstLetter: |
196 // FIXME: Most pseudo classes/elements above can be supported and moved | 192 // FIXME: Most pseudo classes/elements above can be supported and moved |
197 // to assertSupportedPseudo(). Move on a case-by-case basis. If they | 193 // to assertSupportedPseudo(). Move on a case-by-case basis. If they |
198 // require subtree invalidation, document why. | 194 // require subtree invalidation, document why. |
199 case CSSSelector::PseudoHostContext: | 195 case CSSSelector::PseudoHostContext: |
200 // :host-context matches a shadow host, yet the simple selectors inside | 196 // :host-context matches a shadow host, yet the simple selectors inside |
201 // :host-context matches an ancestor of the shadow host. | 197 // :host-context matches an ancestor of the shadow host. |
202 return true; | 198 return true; |
203 default: | 199 default: |
204 ASSERT(supportsInvalidation(selector.getPseudoType())); | 200 DCHECK(supportsInvalidation(selector.getPseudoType())); |
205 return false; | 201 return false; |
206 } | 202 } |
207 } | 203 } |
208 | 204 |
209 template<class Map> | 205 template<class Map> |
210 InvalidationSet& ensureInvalidationSet(Map& map, const typename Map::KeyType& ke y, InvalidationType type) | 206 InvalidationSet& ensureInvalidationSet(Map& map, const typename Map::KeyType& ke y, InvalidationType type) |
211 { | 207 { |
212 typename Map::AddResult addResult = map.add(key, nullptr); | 208 typename Map::AddResult addResult = map.add(key, nullptr); |
213 if (addResult.isNewEntry) { | 209 if (addResult.isNewEntry) { |
214 if (type == InvalidateDescendants) | 210 if (type == InvalidateDescendants) |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
287 ALWAYS_INLINE InvalidationSet& RuleFeatureSet::ensureIdInvalidationSet(const Ato micString& id, InvalidationType type) | 283 ALWAYS_INLINE InvalidationSet& RuleFeatureSet::ensureIdInvalidationSet(const Ato micString& id, InvalidationType type) |
288 { | 284 { |
289 return ensureInvalidationSet(m_idInvalidationSets, id, type); | 285 return ensureInvalidationSet(m_idInvalidationSets, id, type); |
290 } | 286 } |
291 | 287 |
292 ALWAYS_INLINE InvalidationSet& RuleFeatureSet::ensurePseudoInvalidationSet(CSSSe lector::PseudoType pseudoType, InvalidationType type) | 288 ALWAYS_INLINE InvalidationSet& RuleFeatureSet::ensurePseudoInvalidationSet(CSSSe lector::PseudoType pseudoType, InvalidationType type) |
293 { | 289 { |
294 return ensureInvalidationSet(m_pseudoInvalidationSets, pseudoType, type); | 290 return ensureInvalidationSet(m_pseudoInvalidationSets, pseudoType, type); |
295 } | 291 } |
296 | 292 |
297 bool RuleFeatureSet::extractInvalidationSetFeature(const CSSSelector& selector, InvalidationSetFeatures& features) | 293 void RuleFeatureSet::updateFeaturesFromCombinator( |
294 const CSSSelector& lastInCompound, | |
295 const CSSSelector* lastCompoundInAdjacentChain, | |
296 InvalidationSetFeatures& lastCompoundInAdjacentChainFeatures, | |
297 InvalidationSetFeatures*& siblingFeatures, | |
298 InvalidationSetFeatures& descendantFeatures) | |
299 { | |
300 if (lastInCompound.isAdjacentSelector()) { | |
301 if (!siblingFeatures) { | |
302 siblingFeatures = &lastCompoundInAdjacentChainFeatures; | |
303 if (lastCompoundInAdjacentChain) { | |
304 extractInvalidationSetFeaturesFromCompound(*lastCompoundInAdjace ntChain, lastCompoundInAdjacentChainFeatures, Ancestor); | |
305 if (!lastCompoundInAdjacentChainFeatures.hasFeatures()) | |
306 lastCompoundInAdjacentChainFeatures.forceSubtree = true; | |
307 } | |
308 } | |
309 if (siblingFeatures->maxDirectAdjacentSelectors == UINT_MAX) | |
310 return; | |
311 if (lastInCompound.relation() == CSSSelector::DirectAdjacent) | |
312 ++siblingFeatures->maxDirectAdjacentSelectors; | |
313 else | |
314 siblingFeatures->maxDirectAdjacentSelectors = UINT_MAX; | |
315 return; | |
316 } | |
317 | |
318 if (siblingFeatures && lastCompoundInAdjacentChainFeatures.maxDirectAdjacent Selectors) | |
319 lastCompoundInAdjacentChainFeatures = InvalidationSetFeatures(); | |
320 | |
321 siblingFeatures = nullptr; | |
322 | |
323 if (lastInCompound.isShadowSelector()) | |
324 descendantFeatures.treeBoundaryCrossing = true; | |
325 if (lastInCompound.relation() == CSSSelector::ShadowSlot || lastInCompound.r elationIsAffectedByPseudoContent()) | |
326 descendantFeatures.insertionPointCrossing = true; | |
327 if (lastInCompound.relationIsAffectedByPseudoContent()) | |
328 descendantFeatures.contentPseudoCrossing = true; | |
329 } | |
330 | |
331 void RuleFeatureSet::extractInvalidationSetFeaturesFromSimpleSelector(const CSSS elector& selector, InvalidationSetFeatures& features) | |
298 { | 332 { |
299 if (selector.match() == CSSSelector::Tag && selector.tagQName().localName() != starAtom) { | 333 if (selector.match() == CSSSelector::Tag && selector.tagQName().localName() != starAtom) { |
300 features.tagNames.append(selector.tagQName().localName()); | 334 features.tagNames.append(selector.tagQName().localName()); |
301 return true; | 335 return; |
302 } | 336 } |
303 if (selector.match() == CSSSelector::Id) { | 337 if (selector.match() == CSSSelector::Id) { |
304 features.ids.append(selector.value()); | 338 features.ids.append(selector.value()); |
305 return true; | 339 return; |
306 } | 340 } |
307 if (selector.match() == CSSSelector::Class) { | 341 if (selector.match() == CSSSelector::Class) { |
308 features.classes.append(selector.value()); | 342 features.classes.append(selector.value()); |
309 return true; | 343 return; |
310 } | 344 } |
311 if (selector.isAttributeSelector()) { | 345 if (selector.isAttributeSelector()) { |
312 features.attributes.append(selector.attribute().localName()); | 346 features.attributes.append(selector.attribute().localName()); |
313 return true; | 347 return; |
314 } | 348 } |
315 if (selector.getPseudoType() == CSSSelector::PseudoWebKitCustomElement || se lector.getPseudoType() == CSSSelector::PseudoBlinkInternalElement) { | 349 switch (selector.getPseudoType()) { |
350 case CSSSelector::PseudoWebKitCustomElement: | |
351 case CSSSelector::PseudoBlinkInternalElement: | |
316 features.customPseudoElement = true; | 352 features.customPseudoElement = true; |
317 return true; | 353 return; |
354 case CSSSelector::PseudoBefore: | |
355 case CSSSelector::PseudoAfter: | |
356 features.hasBeforeOrAfter = true; | |
357 return; | |
358 case CSSSelector::PseudoSlotted: | |
359 features.invalidatesSlotted = true; | |
360 return; | |
361 default: | |
362 return; | |
318 } | 363 } |
319 // Returning false for ::before and ::after as they are not used as | |
320 // invalidation set features, only used later to generate invalidation sets | |
321 // for attributes present in "content: attr(...)" declarations. | |
322 if (selector.getPseudoType() == CSSSelector::PseudoBefore || selector.getPse udoType() == CSSSelector::PseudoAfter) | |
323 features.hasBeforeOrAfter = true; | |
324 return false; | |
325 } | 364 } |
326 | 365 |
327 InvalidationSet* RuleFeatureSet::invalidationSetForSelector(const CSSSelector& s elector, InvalidationType type) | 366 InvalidationSet* RuleFeatureSet::invalidationSetForSimpleSelector(const CSSSelec tor& selector, InvalidationType type) |
328 { | 367 { |
329 if (selector.match() == CSSSelector::Class) | 368 if (selector.match() == CSSSelector::Class) |
330 return &ensureClassInvalidationSet(selector.value(), type); | 369 return &ensureClassInvalidationSet(selector.value(), type); |
331 if (selector.isAttributeSelector()) | 370 if (selector.isAttributeSelector()) |
332 return &ensureAttributeInvalidationSet(selector.attribute().localName(), type); | 371 return &ensureAttributeInvalidationSet(selector.attribute().localName(), type); |
333 if (selector.match() == CSSSelector::Id) | 372 if (selector.match() == CSSSelector::Id) |
334 return &ensureIdInvalidationSet(selector.value(), type); | 373 return &ensureIdInvalidationSet(selector.value(), type); |
335 if (selector.match() == CSSSelector::PseudoClass) { | 374 if (selector.match() == CSSSelector::PseudoClass) { |
336 switch (selector.getPseudoType()) { | 375 switch (selector.getPseudoType()) { |
337 case CSSSelector::PseudoEmpty: | 376 case CSSSelector::PseudoEmpty: |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
375 case CSSSelector::PseudoNthLastChild: | 414 case CSSSelector::PseudoNthLastChild: |
376 case CSSSelector::PseudoNthLastOfType: | 415 case CSSSelector::PseudoNthLastOfType: |
377 return &ensureNthInvalidationSet(); | 416 return &ensureNthInvalidationSet(); |
378 default: | 417 default: |
379 break; | 418 break; |
380 } | 419 } |
381 } | 420 } |
382 return nullptr; | 421 return nullptr; |
383 } | 422 } |
384 | 423 |
385 // Given a rule, update the descendant invalidation sets for the features found | |
386 // in its selector. The first step is to extract the features from the rightmost | |
387 // compound selector (extractInvalidationSetFeatures). Secondly, add those featu res | |
388 // to the invalidation sets for the features found in the other compound selecto rs | |
389 // (addFeaturesToInvalidationSets). If we find a feature in the right-most compo und | |
390 // selector that requires a subtree recalc, we addFeaturesToInvalidationSets for the | |
391 // rightmost compound selector as well. | |
392 | |
393 void RuleFeatureSet::updateInvalidationSets(const RuleData& ruleData) | 424 void RuleFeatureSet::updateInvalidationSets(const RuleData& ruleData) |
394 { | 425 { |
426 // Given a rule, update the descendant invalidation sets for the features | |
427 // found in its selector. The first step is to extract the features from the | |
428 // rightmost compound selector (extractInvalidationSetFeatures). Secondly, | |
Eric Willigers
2016/09/03 02:57:57
extractInvalidationSetFeatures becomes
extractInva
rune
2016/09/05 08:16:20
Done.
| |
429 // add those features to the invalidation sets for the features found in the | |
430 // other compound selectors (addFeaturesToInvalidationSets). If we find a | |
431 // feature in the right-most compound selector that requires a subtree | |
432 // recalc, nextCompound will be the rightmost compound and we will | |
433 // addFeaturesToInvalidationSets for that one as well. | |
434 | |
395 InvalidationSetFeatures features; | 435 InvalidationSetFeatures features; |
396 auto result = extractInvalidationSetFeatures(ruleData.selector(), features, Subject); | 436 const CSSSelector* nextCompound = extractInvalidationSetFeaturesFromCompound (ruleData.selector(), features, Subject); |
397 | 437 |
398 features.forceSubtree = result.second == ForceSubtree; | 438 if (!features.hasFeatures()) |
399 if (result.first) { | 439 features.forceSubtree = true; |
400 addFeaturesToInvalidationSets(result.first, features.adjacent ? &feature s : nullptr, features); | |
401 } else if (features.hasNthPseudo) { | |
402 DCHECK(m_nthInvalidationSet); | |
403 addFeaturesToInvalidationSet(*m_nthInvalidationSet, features); | |
404 } | |
405 | 440 |
406 // If any ::before and ::after rules specify 'content: attr(...)', we | 441 if (nextCompound) |
407 // need to create invalidation sets for those attributes. | 442 addFeaturesToInvalidationSets(*nextCompound, features); |
443 else if (features.hasNthPseudo) | |
444 addFeaturesToInvalidationSet(ensureNthInvalidationSet(), features); | |
445 | |
408 if (features.hasBeforeOrAfter) | 446 if (features.hasBeforeOrAfter) |
409 updateInvalidationSetsForContentAttribute(ruleData); | 447 updateInvalidationSetsForContentAttribute(ruleData); |
410 } | 448 } |
411 | 449 |
412 void RuleFeatureSet::updateInvalidationSetsForContentAttribute(const RuleData& r uleData) | 450 void RuleFeatureSet::updateInvalidationSetsForContentAttribute(const RuleData& r uleData) |
413 { | 451 { |
452 // If any ::before and ::after rules specify 'content: attr(...)', we | |
453 // need to create invalidation sets for those attributes to have content | |
454 // changes applied through style recalc. | |
455 | |
414 const StylePropertySet& propertySet = ruleData.rule()->properties(); | 456 const StylePropertySet& propertySet = ruleData.rule()->properties(); |
415 | 457 |
416 int propertyIndex = propertySet.findPropertyIndex(CSSPropertyContent); | 458 int propertyIndex = propertySet.findPropertyIndex(CSSPropertyContent); |
417 | 459 |
418 if (propertyIndex == -1) | 460 if (propertyIndex == -1) |
419 return; | 461 return; |
420 | 462 |
421 StylePropertySet::PropertyReference contentProperty = propertySet.propertyAt (propertyIndex); | 463 StylePropertySet::PropertyReference contentProperty = propertySet.propertyAt (propertyIndex); |
422 const CSSValue& contentValue = contentProperty.value(); | 464 const CSSValue& contentValue = contentProperty.value(); |
423 | 465 |
424 if (!contentValue.isValueList()) | 466 if (!contentValue.isValueList()) |
425 return; | 467 return; |
426 | 468 |
427 for (auto& item : toCSSValueList(contentValue)) { | 469 for (auto& item : toCSSValueList(contentValue)) { |
428 if (!item->isFunctionValue()) | 470 if (!item->isFunctionValue()) |
429 continue; | 471 continue; |
430 const CSSFunctionValue* functionValue = toCSSFunctionValue(item.get()); | 472 const CSSFunctionValue* functionValue = toCSSFunctionValue(item.get()); |
431 if (functionValue->functionType() != CSSValueAttr) | 473 if (functionValue->functionType() != CSSValueAttr) |
432 continue; | 474 continue; |
433 ensureAttributeInvalidationSet(AtomicString(toCSSCustomIdentValue(functi onValue->item(0)).value()), InvalidateDescendants).setInvalidatesSelf(); | 475 ensureAttributeInvalidationSet(AtomicString(toCSSCustomIdentValue(functi onValue->item(0)).value()), InvalidateDescendants).setInvalidatesSelf(); |
434 } | 476 } |
435 } | 477 } |
436 | 478 |
437 std::pair<const CSSSelector*, RuleFeatureSet::UseFeaturesType> | 479 const CSSSelector* |
438 RuleFeatureSet::extractInvalidationSetFeatures(const CSSSelector& selector, Inva lidationSetFeatures& features, PositionType position, CSSSelector::PseudoType ps eudo) | 480 RuleFeatureSet::extractInvalidationSetFeaturesFromSelectorList(const CSSSelector & simpleSelector, InvalidationSetFeatures& features, PositionType position) |
439 { | 481 { |
440 bool foundFeatures = false; | 482 const CSSSelectorList* selectorList = simpleSelector.selectorList(); |
441 for (const CSSSelector* current = &selector; current; current = current->tag History()) { | 483 if (!selectorList) |
442 if (pseudo != CSSSelector::PseudoNot) | 484 return nullptr; |
443 foundFeatures |= extractInvalidationSetFeature(*current, features); | 485 |
444 // Initialize the entry in the invalidation set map, if supported. | 486 DCHECK(supportsInvalidationWithSelectorList(simpleSelector.getPseudoType())) ; |
445 if (InvalidationSet* invalidationSet = invalidationSetForSelector(*curre nt, InvalidateDescendants)) { | 487 |
446 if (position == Subject) { | 488 const CSSSelector* subSelector = selectorList->first(); |
447 if (invalidationSet == m_nthInvalidationSet) | 489 |
448 features.hasNthPseudo = true; | 490 bool allSubSelectorsHaveFeatures = true; |
449 else | 491 InvalidationSetFeatures anyFeatures; |
450 invalidationSet->setInvalidatesSelf(); | 492 |
451 } | 493 for (; subSelector; subSelector = CSSSelectorList::next(*subSelector)) { |
452 } else { | 494 InvalidationSetFeatures compoundFeatures; |
453 if (requiresSubtreeInvalidation(*current)) { | 495 if (extractInvalidationSetFeaturesFromCompound(*subSelector, compoundFea tures, position, simpleSelector.getPseudoType())) { |
454 // Fall back to use subtree invalidations, even for features in the | 496 // A non-null selector return means the sub-selector contained a |
455 // rightmost compound selector. Returning the start &selector he re | 497 // selector which requiresSubtreeInvalidation(). |
456 // will make addFeaturesToInvalidationSets start marking invalid ation | 498 DCHECK(compoundFeatures.forceSubtree); |
457 // sets for subtree recalc for features in the rightmost compoun d | 499 features.forceSubtree = true; |
458 // selector. | 500 return &simpleSelector; |
459 return std::make_pair(&selector, ForceSubtree); | 501 } |
460 } | 502 if (allSubSelectorsHaveFeatures && !compoundFeatures.hasFeatures()) |
461 if (const CSSSelectorList* selectorList = current->selectorList()) { | 503 allSubSelectorsHaveFeatures = false; |
462 if (current->getPseudoType() == CSSSelector::PseudoSlotted) { | 504 else |
463 ASSERT(position == Subject); | 505 anyFeatures.add(compoundFeatures); |
464 features.invalidatesSlotted = true; | 506 if (compoundFeatures.hasNthPseudo) |
465 } | 507 features.hasNthPseudo = true; |
466 ASSERT(supportsInvalidationWithSelectorList(current->getPseudoTy pe())); | 508 } |
467 const CSSSelector* subSelector = selectorList->first(); | 509 // Don't add any features if one of the sub-selectors of does not contain |
468 bool allSubSelectorsHaveFeatures = !!subSelector; | 510 // any invalidation set features. E.g. :-webkit-any(*, span). |
469 for (; subSelector; subSelector = CSSSelectorList::next(*subSele ctor)) { | 511 if (allSubSelectorsHaveFeatures) |
470 auto result = extractInvalidationSetFeatures(*subSelector, f eatures, position, current->getPseudoType()); | 512 features.add(anyFeatures); |
471 if (result.first) { | 513 return nullptr; |
472 // A non-null selector return means the sub-selector con tained a | 514 } |
473 // selector which requiresSubtreeInvalidation(). Return the rightmost | 515 |
474 // selector to mark for subtree recalcs like above. | 516 const CSSSelector* |
475 return std::make_pair(&selector, ForceSubtree); | 517 RuleFeatureSet::extractInvalidationSetFeaturesFromCompound(const CSSSelector& co mpound, InvalidationSetFeatures& features, PositionType position, CSSSelector::P seudoType pseudo) |
476 } | 518 { |
477 allSubSelectorsHaveFeatures &= result.second == UseFeatures; | 519 const CSSSelector* simpleSelector = &compound; |
478 } | 520 for (; simpleSelector; simpleSelector = simpleSelector->tagHistory()) { |
479 foundFeatures |= allSubSelectorsHaveFeatures; | 521 |
480 } | 522 // Fall back to use subtree invalidations, even for features in the |
523 // rightmost compound selector. Returning the start &selector here | |
524 // will make addFeaturesToInvalidationSets start marking invalidation | |
525 // sets for subtree recalc for features in the rightmost compound | |
526 // selector. | |
527 if (requiresSubtreeInvalidation(*simpleSelector)) { | |
528 features.forceSubtree = true; | |
529 return &compound; | |
481 } | 530 } |
482 | 531 |
483 if (current->relation() == CSSSelector::SubSelector) | 532 // When inside a :not(), we should not use the found features for |
533 // invalidation because we should invalidate elements _without_ that | |
534 // feature. On the other hand, we should still have invalidation sets | |
535 // for the features since we are able to detect when they change. | |
536 // That is, ".a" should not have ".b" in its invalidation set for | |
537 // ".a :not(.b)", but there should be an invalidation set for ".a" in | |
538 // ":not(.a) .b". | |
539 if (pseudo != CSSSelector::PseudoNot) | |
540 extractInvalidationSetFeaturesFromSimpleSelector(*simpleSelector, fe atures); | |
541 | |
542 // Initialize the entry in the invalidation set map for self- | |
543 // invalidation, if supported. | |
544 if (InvalidationSet* invalidationSet = invalidationSetForSimpleSelector( *simpleSelector, InvalidateDescendants)) { | |
545 if (invalidationSet == m_nthInvalidationSet) | |
546 features.hasNthPseudo = true; | |
547 else if (position == Subject) | |
548 invalidationSet->setInvalidatesSelf(); | |
549 } | |
550 | |
551 if (extractInvalidationSetFeaturesFromSelectorList(*simpleSelector, feat ures, position)) { | |
552 DCHECK(features.forceSubtree); | |
553 return &compound; | |
554 } | |
555 | |
556 if (simpleSelector->relation() == CSSSelector::SubSelector) | |
484 continue; | 557 continue; |
485 | 558 |
486 if (features.hasNthPseudo && position == Subject) { | 559 if (position == Subject) { |
487 DCHECK(m_nthInvalidationSet); | 560 if (features.hasNthPseudo) { |
488 if (foundFeatures) | 561 DCHECK(m_nthInvalidationSet); |
489 addFeaturesToInvalidationSet(*m_nthInvalidationSet, features); | 562 if (features.hasFeatures()) |
490 else | 563 addFeaturesToInvalidationSet(*m_nthInvalidationSet, features ); |
491 m_nthInvalidationSet->setWholeSubtreeInvalid(); | 564 else |
565 m_nthInvalidationSet->setWholeSubtreeInvalid(); | |
566 } | |
567 InvalidationSetFeatures* siblingFeatures = nullptr; | |
568 updateFeaturesFromCombinator(*simpleSelector, nullptr, features, sib lingFeatures, features); | |
492 } | 569 } |
570 return simpleSelector->tagHistory(); | |
571 } | |
493 | 572 |
494 features.treeBoundaryCrossing = current->isShadowSelector(); | 573 return nullptr; |
495 if (current->relationIsAffectedByPseudoContent()) { | |
496 features.contentPseudoCrossing = true; | |
497 features.insertionPointCrossing = true; | |
498 } | |
499 features.adjacent = current->isAdjacentSelector(); | |
500 if (current->relation() == CSSSelector::DirectAdjacent) | |
501 features.maxDirectAdjacentSelectors = 1; | |
502 return std::make_pair(current->tagHistory(), foundFeatures ? UseFeatures : ForceSubtree); | |
503 } | |
504 return std::make_pair(nullptr, foundFeatures ? UseFeatures : ForceSubtree); | |
505 } | 574 } |
506 | 575 |
507 // Add features extracted from the rightmost compound selector to descendant inv alidation | 576 // Add features extracted from the rightmost compound selector to descendant inv alidation |
508 // sets for features found in other compound selectors. | 577 // sets for features found in other compound selectors. |
509 // | 578 // |
510 // We use descendant invalidation for descendants, sibling invalidation for sibl ings and their subtrees. | 579 // We use descendant invalidation for descendants, sibling invalidation for sibl ings and their subtrees. |
511 // | 580 // |
512 // As we encounter a descendant type of combinator, the features only need to be checked | 581 // As we encounter a descendant type of combinator, the features only need to be checked |
513 // against descendants in the same subtree only. features.adjacent is set to fal se, and | 582 // against descendants in the same subtree only. features.adjacent is set to fal se, and |
514 // we start adding features to the descendant invalidation set. | 583 // we start adding features to the descendant invalidation set. |
(...skipping 16 matching lines...) Expand all Loading... | |
531 for (const auto& tagName : features.tagNames) | 600 for (const auto& tagName : features.tagNames) |
532 invalidationSet.addTagName(tagName); | 601 invalidationSet.addTagName(tagName); |
533 for (const auto& className : features.classes) | 602 for (const auto& className : features.classes) |
534 invalidationSet.addClass(className); | 603 invalidationSet.addClass(className); |
535 for (const auto& attribute : features.attributes) | 604 for (const auto& attribute : features.attributes) |
536 invalidationSet.addAttribute(attribute); | 605 invalidationSet.addAttribute(attribute); |
537 if (features.customPseudoElement) | 606 if (features.customPseudoElement) |
538 invalidationSet.setCustomPseudoInvalid(); | 607 invalidationSet.setCustomPseudoInvalid(); |
539 } | 608 } |
540 | 609 |
541 // selector is the selector immediately to the left of the rightmost combinator. | 610 |
542 // siblingFeatures is null if selector is not immediately to the left of a sibli ng combinator. | 611 void RuleFeatureSet::addFeaturesToInvalidationSetsForSelectorList(const CSSSelec tor& simpleSelector, InvalidationSetFeatures* siblingFeatures, InvalidationSetFe atures& descendantFeatures) |
543 // descendantFeatures has the features of the rightmost compound selector. | |
544 void RuleFeatureSet::addFeaturesToInvalidationSets(const CSSSelector* selector, InvalidationSetFeatures* siblingFeatures, InvalidationSetFeatures& descendantFea tures) | |
545 { | 612 { |
546 const CSSSelector* lastCompoundSelectorInAdjacentChain = selector; | 613 if (!simpleSelector.selectorList()) |
614 return; | |
547 | 615 |
548 // We set siblingFeatures to &localFeatures if we find a rightmost sibling c ombinator. | 616 DCHECK(supportsInvalidationWithSelectorList(simpleSelector.getPseudoType())) ; |
549 InvalidationSetFeatures localFeatures; | |
550 | 617 |
551 bool universalCompound = true; | 618 for (const CSSSelector* subSelector = simpleSelector.selectorList()->first() ; subSelector; subSelector = CSSSelectorList::next(*subSelector)) |
619 addFeaturesToInvalidationSetsForCompoundSelector(*subSelector, siblingFe atures, descendantFeatures); | |
620 } | |
552 | 621 |
553 for (const CSSSelector* current = selector; current; current = current->tagH istory()) { | 622 void RuleFeatureSet::addFeaturesToInvalidationSetsForSimpleSelector(const CSSSel ector& simpleSelector, InvalidationSetFeatures* siblingFeatures, InvalidationSet Features& descendantFeatures) |
554 InvalidationType type = siblingFeatures ? InvalidateSiblings : Invalidat eDescendants; | 623 { |
555 if (InvalidationSet* invalidationSet = invalidationSetForSelector(*curre nt, type)) { | 624 if (InvalidationSet* invalidationSet = invalidationSetForSimpleSelector(simp leSelector, siblingFeatures ? InvalidateSiblings : InvalidateDescendants)) { |
556 if (current->match() != CSSSelector::PseudoClass) | 625 if (!siblingFeatures || invalidationSet == m_nthInvalidationSet) { |
557 universalCompound = false; | 626 addFeaturesToInvalidationSet(*invalidationSet, descendantFeatures); |
558 if (siblingFeatures && invalidationSet != m_nthInvalidationSet) { | 627 return; |
559 SiblingInvalidationSet* siblingInvalidationSet = toSiblingInvali dationSet(invalidationSet); | |
560 siblingInvalidationSet->updateMaxDirectAdjacentSelectors(sibling Features->maxDirectAdjacentSelectors); | |
561 | |
562 addFeaturesToInvalidationSet(*invalidationSet, *siblingFeatures) ; | |
563 if (siblingFeatures == &descendantFeatures) | |
564 siblingInvalidationSet->setInvalidatesSelf(); | |
565 else | |
566 addFeaturesToInvalidationSet(siblingInvalidationSet->ensureS iblingDescendants(), descendantFeatures); | |
567 } else { | |
568 addFeaturesToInvalidationSet(*invalidationSet, descendantFeature s); | |
569 } | |
570 } else { | |
571 if (current->isHostPseudoClass()) | |
572 descendantFeatures.treeBoundaryCrossing = true; | |
573 if (current->isInsertionPointCrossing()) | |
574 descendantFeatures.insertionPointCrossing = true; | |
575 if (const CSSSelectorList* selectorList = current->selectorList()) { | |
576 ASSERT(supportsInvalidationWithSelectorList(current->getPseudoTy pe())); | |
577 for (const CSSSelector* subSelector = selectorList->first(); sub Selector; subSelector = CSSSelectorList::next(*subSelector)) | |
578 addFeaturesToInvalidationSets(subSelector, siblingFeatures, descendantFeatures); | |
579 } | |
580 } | 628 } |
581 | 629 |
582 if (current->relation() == CSSSelector::SubSelector) | 630 SiblingInvalidationSet* siblingInvalidationSet = toSiblingInvalidationSe t(invalidationSet); |
583 continue; | 631 siblingInvalidationSet->updateMaxDirectAdjacentSelectors(siblingFeatures ->maxDirectAdjacentSelectors); |
584 | 632 addFeaturesToInvalidationSet(*invalidationSet, *siblingFeatures); |
585 if (universalCompound && siblingFeatures) | 633 if (siblingFeatures == &descendantFeatures) |
586 addFeaturesToUniversalSiblingInvalidationSet(*siblingFeatures, desce ndantFeatures); | 634 siblingInvalidationSet->setInvalidatesSelf(); |
587 universalCompound = true; | 635 else |
588 | 636 addFeaturesToInvalidationSet(siblingInvalidationSet->ensureSiblingDe scendants(), descendantFeatures); |
589 if (current->relationIsAffectedByPseudoContent() || current->relation() == CSSSelector::ShadowSlot) { | 637 return; |
590 descendantFeatures.insertionPointCrossing = true; | |
591 descendantFeatures.contentPseudoCrossing = true; | |
592 } | |
593 if (current->isShadowSelector()) | |
594 descendantFeatures.treeBoundaryCrossing = true; | |
595 if (!current->isAdjacentSelector()) { | |
596 lastCompoundSelectorInAdjacentChain = current->tagHistory(); | |
597 siblingFeatures = nullptr; | |
598 continue; | |
599 } | |
600 | |
601 if (siblingFeatures) { | |
602 if (siblingFeatures->maxDirectAdjacentSelectors == UINT_MAX) | |
603 continue; | |
604 | |
605 if (current->relation() == CSSSelector::DirectAdjacent) | |
606 siblingFeatures->maxDirectAdjacentSelectors++; | |
607 else | |
608 siblingFeatures->maxDirectAdjacentSelectors = UINT_MAX; | |
609 continue; | |
610 } | |
611 | |
612 localFeatures = InvalidationSetFeatures(); | |
613 auto result = extractInvalidationSetFeatures(*lastCompoundSelectorInAdja centChain, localFeatures, Ancestor); | |
614 ASSERT(result.first); | |
615 localFeatures.forceSubtree = result.second == ForceSubtree; | |
616 siblingFeatures = &localFeatures; | |
617 } | 638 } |
618 | 639 |
619 if (universalCompound && siblingFeatures) | 640 if (simpleSelector.isHostPseudoClass()) |
641 descendantFeatures.treeBoundaryCrossing = true; | |
642 if (simpleSelector.isInsertionPointCrossing()) | |
643 descendantFeatures.insertionPointCrossing = true; | |
644 | |
645 addFeaturesToInvalidationSetsForSelectorList(simpleSelector, siblingFeatures , descendantFeatures); | |
646 } | |
647 | |
648 const CSSSelector* RuleFeatureSet::addFeaturesToInvalidationSetsForCompoundSelec tor(const CSSSelector& compound, InvalidationSetFeatures* siblingFeatures, Inval idationSetFeatures& descendantFeatures) | |
649 { | |
650 bool compoundHasTagIdClassOrAttribute = false; | |
651 const CSSSelector* simpleSelector = &compound; | |
652 for (; simpleSelector; simpleSelector = simpleSelector->tagHistory()) { | |
653 addFeaturesToInvalidationSetsForSimpleSelector(*simpleSelector, siblingF eatures, descendantFeatures); | |
654 if (siblingFeatures) | |
655 compoundHasTagIdClassOrAttribute |= simpleSelector->isIdClassOrAttri buteSelector(); | |
Eric Willigers
2016/09/03 02:57:57
tag? Should the CSSSelector method also consider t
rune
2016/09/05 08:16:20
Then we would need to have invalidation sets for t
| |
656 if (simpleSelector->relation() != CSSSelector::SubSelector) | |
657 break; | |
658 if (!simpleSelector->tagHistory()) | |
659 break; | |
660 } | |
661 | |
662 if (siblingFeatures && !compoundHasTagIdClassOrAttribute) | |
620 addFeaturesToUniversalSiblingInvalidationSet(*siblingFeatures, descendan tFeatures); | 663 addFeaturesToUniversalSiblingInvalidationSet(*siblingFeatures, descendan tFeatures); |
664 | |
665 return simpleSelector; | |
666 } | |
667 | |
668 void RuleFeatureSet::addFeaturesToInvalidationSets(const CSSSelector& selector, InvalidationSetFeatures& descendantFeatures) | |
669 { | |
670 // selector is the selector immediately to the left of the rightmost combina tor. | |
671 // descendantFeatures has the features of the rightmost compound selector. | |
672 | |
673 InvalidationSetFeatures lastCompoundInSiblingChainFeatures; | |
674 InvalidationSetFeatures* siblingFeatures = descendantFeatures.maxDirectAdjac entSelectors ? &descendantFeatures : nullptr; | |
675 | |
676 const CSSSelector* compound = &selector; | |
677 while (compound) { | |
678 const CSSSelector* lastInCompound = addFeaturesToInvalidationSetsForComp oundSelector(*compound, siblingFeatures, descendantFeatures); | |
679 DCHECK(lastInCompound); | |
680 updateFeaturesFromCombinator(*lastInCompound, compound, lastCompoundInSi blingChainFeatures, siblingFeatures, descendantFeatures); | |
681 compound = lastInCompound->tagHistory(); | |
682 } | |
621 } | 683 } |
622 | 684 |
623 RuleFeatureSet::SelectorPreMatch RuleFeatureSet::collectFeaturesFromRuleData(con st RuleData& ruleData) | 685 RuleFeatureSet::SelectorPreMatch RuleFeatureSet::collectFeaturesFromRuleData(con st RuleData& ruleData) |
624 { | 686 { |
625 RELEASE_ASSERT(m_isAlive); | 687 RELEASE_ASSERT(m_isAlive); |
626 FeatureMetadata metadata; | 688 FeatureMetadata metadata; |
627 if (collectFeaturesFromSelector(ruleData.selector(), metadata) == SelectorNe verMatches) | 689 if (collectFeaturesFromSelector(ruleData.selector(), metadata) == SelectorNe verMatches) |
628 return SelectorNeverMatches; | 690 return SelectorNeverMatches; |
629 | 691 |
630 m_metadata.add(metadata); | 692 m_metadata.add(metadata); |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
699 && ((relation != CSSSelector::SubSelector) || current->isLastInTagHi story())) { | 761 && ((relation != CSSSelector::SubSelector) || current->isLastInTagHi story())) { |
700 if (maxDirectAdjacentSelectors > metadata.maxDirectAdjacentSelectors ) | 762 if (maxDirectAdjacentSelectors > metadata.maxDirectAdjacentSelectors ) |
701 metadata.maxDirectAdjacentSelectors = maxDirectAdjacentSelectors ; | 763 metadata.maxDirectAdjacentSelectors = maxDirectAdjacentSelectors ; |
702 maxDirectAdjacentSelectors = 0; | 764 maxDirectAdjacentSelectors = 0; |
703 } | 765 } |
704 | 766 |
705 if (!metadata.foundInsertionPointCrossing && current->isAdjacentSelector ()) | 767 if (!metadata.foundInsertionPointCrossing && current->isAdjacentSelector ()) |
706 metadata.foundSiblingSelector = true; | 768 metadata.foundSiblingSelector = true; |
707 } | 769 } |
708 | 770 |
709 ASSERT(!maxDirectAdjacentSelectors); | 771 DCHECK(!maxDirectAdjacentSelectors); |
710 return SelectorMayMatch; | 772 return SelectorMayMatch; |
711 } | 773 } |
712 | 774 |
713 void RuleFeatureSet::FeatureMetadata::add(const FeatureMetadata& other) | 775 void RuleFeatureSet::FeatureMetadata::add(const FeatureMetadata& other) |
714 { | 776 { |
715 usesFirstLineRules = usesFirstLineRules || other.usesFirstLineRules; | 777 usesFirstLineRules = usesFirstLineRules || other.usesFirstLineRules; |
716 usesWindowInactiveSelector = usesWindowInactiveSelector || other.usesWindowI nactiveSelector; | 778 usesWindowInactiveSelector = usesWindowInactiveSelector || other.usesWindowI nactiveSelector; |
717 maxDirectAdjacentSelectors = std::max(maxDirectAdjacentSelectors, other.maxD irectAdjacentSelectors); | 779 maxDirectAdjacentSelectors = std::max(maxDirectAdjacentSelectors, other.maxD irectAdjacentSelectors); |
718 } | 780 } |
719 | 781 |
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
938 else | 1000 else |
939 addFeaturesToInvalidationSet(universalSet.ensureSiblingDescendants(), de scendantFeatures); | 1001 addFeaturesToInvalidationSet(universalSet.ensureSiblingDescendants(), de scendantFeatures); |
940 } | 1002 } |
941 | 1003 |
942 DEFINE_TRACE(RuleFeatureSet) | 1004 DEFINE_TRACE(RuleFeatureSet) |
943 { | 1005 { |
944 visitor->trace(siblingRules); | 1006 visitor->trace(siblingRules); |
945 visitor->trace(uncommonAttributeRules); | 1007 visitor->trace(uncommonAttributeRules); |
946 } | 1008 } |
947 | 1009 |
1010 void RuleFeatureSet::InvalidationSetFeatures::add(const InvalidationSetFeatures& other) | |
1011 { | |
1012 classes.appendVector(other.classes); | |
1013 attributes.appendVector(other.attributes); | |
1014 ids.appendVector(other.ids); | |
1015 tagNames.appendVector(other.tagNames); | |
1016 maxDirectAdjacentSelectors = std::max(maxDirectAdjacentSelectors, other.maxD irectAdjacentSelectors); | |
1017 customPseudoElement |= other.customPseudoElement; | |
1018 hasBeforeOrAfter |= other.hasBeforeOrAfter; | |
1019 treeBoundaryCrossing |= other.treeBoundaryCrossing; | |
1020 insertionPointCrossing |= other.insertionPointCrossing; | |
1021 forceSubtree |= other.forceSubtree; | |
1022 contentPseudoCrossing |= other.contentPseudoCrossing; | |
1023 invalidatesSlotted |= other.invalidatesSlotted; | |
1024 hasNthPseudo |= other.hasNthPseudo; | |
1025 } | |
1026 | |
1027 bool RuleFeatureSet::InvalidationSetFeatures::hasFeatures() const | |
1028 { | |
1029 return !classes.isEmpty() | |
1030 || !attributes.isEmpty() | |
1031 || !ids.isEmpty() | |
1032 || !tagNames.isEmpty() | |
1033 || customPseudoElement; | |
1034 } | |
1035 | |
948 } // namespace blink | 1036 } // namespace blink |
OLD | NEW |