1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 // vim:cindent:tabstop=2:expandtab:shiftwidth=2: 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* 8 * style rule processor for CSS style sheets, responsible for selector 9 * matching and cascading 10 */ 11 12 #define PL_ARENA_CONST_ALIGN_MASK 7 13 // We want page-sized arenas so there's no fragmentation involved. 14 // Including plarena.h must come first to avoid it being included by some 15 // header file thereby making PL_ARENA_CONST_ALIGN_MASK ineffective. 16 #define NS_CASCADEENUMDATA_ARENA_BLOCK_SIZE (4096) 17 #include "plarena.h" 18 19 #include "nsAutoPtr.h" 20 #include "nsCSSRuleProcessor.h" 21 #include "nsRuleProcessorData.h" 22 #include <algorithm> 23 #include "nsIAtom.h" 24 #include "PLDHashTable.h" 25 #include "nsICSSPseudoComparator.h" 26 #include "mozilla/MemoryReporting.h" 27 #include "mozilla/css/StyleRule.h" 28 #include "mozilla/css/GroupRule.h" 29 #include "nsIDocument.h" 30 #include "nsPresContext.h" 31 #include "nsGkAtoms.h" 32 #include "nsUnicharUtils.h" 33 #include "nsError.h" 34 #include "nsRuleWalker.h" 35 #include "nsCSSPseudoClasses.h" 36 #include "nsCSSPseudoElements.h" 37 #include "nsIContent.h" 38 #include "nsCOMPtr.h" 39 #include "nsHashKeys.h" 40 #include "nsStyleUtil.h" 41 #include "nsQuickSort.h" 42 #include "nsAttrValue.h" 43 #include "nsAttrValueInlines.h" 44 #include "nsAttrName.h" 45 #include "nsTArray.h" 46 #include "nsContentUtils.h" 47 #include "nsIMediaList.h" 48 #include "nsCSSRules.h" 49 #include "nsStyleSet.h" 50 #include "mozilla/dom/Element.h" 51 #include "nsNthIndexCache.h" 52 #include "mozilla/ArrayUtils.h" 53 #include "mozilla/EventStates.h" 54 #include "mozilla/Preferences.h" 55 #include "mozilla/LookAndFeel.h" 56 #include "mozilla/Likely.h" 57 #include "mozilla/OperatorNewExtensions.h" 58 #include "mozilla/TypedEnumBits.h" 59 #include "RuleProcessorCache.h" 60 #include "nsIDOMMutationEvent.h" 61 #include "nsIMozBrowserFrame.h" 62 63 using namespace mozilla; 64 using namespace mozilla::dom; 65 66 #define VISITED_PSEUDO_PREF "layout.css.visited_links_enabled" 67 68 static bool gSupportVisitedPseudo = true; 69 70 static nsTArray< nsCOMPtr<nsIAtom> >* sSystemMetrics = 0; 71 72 #ifdef XP_WIN 73 uint8_t nsCSSRuleProcessor::sWinThemeId = LookAndFeel::eWindowsTheme_Generic; 74 #endif 75 76 /** 77 * A struct representing a given CSS rule and a particular selector 78 * from that rule's selector list. 79 */ 80 struct RuleSelectorPair { 81 RuleSelectorPair(css::StyleRule* aRule, nsCSSSelector* aSelector) 82 : mRule(aRule), mSelector(aSelector) {} 83 // If this class ever grows a destructor, deal with 84 // PerWeightDataListItem appropriately. 85 86 css::StyleRule* mRule; 87 nsCSSSelector* mSelector; // which of |mRule|'s selectors 88 }; 89 90 #define NS_IS_ANCESTOR_OPERATOR(ch) \ 91 ((ch) == char16_t(' ') || (ch) == char16_t('>')) 92 93 /** 94 * A struct representing a particular rule in an ordered list of rules 95 * (the ordering depending on the weight of mSelector and the order of 96 * our rules to start with). 97 */ 98 struct RuleValue : RuleSelectorPair { 99 enum { 100 eMaxAncestorHashes = 4 101 }; 102 103 RuleValue(const RuleSelectorPair& aRuleSelectorPair, int32_t aIndex, 104 bool aQuirksMode) : 105 RuleSelectorPair(aRuleSelectorPair), 106 mIndex(aIndex) 107 { 108 CollectAncestorHashes(aQuirksMode); 109 } 110 111 int32_t mIndex; // High index means high weight/order. 112 uint32_t mAncestorSelectorHashes[eMaxAncestorHashes]; 113 114 private: 115 void CollectAncestorHashes(bool aQuirksMode) { 116 // Collect up our mAncestorSelectorHashes. It's not clear whether it's 117 // better to stop once we've found eMaxAncestorHashes of them or to keep 118 // going and preferentially collect information from selectors higher up the 119 // chain... Let's do the former for now. 120 size_t hashIndex = 0; 121 for (nsCSSSelector* sel = mSelector->mNext; sel; sel = sel->mNext) { 122 if (!NS_IS_ANCESTOR_OPERATOR(sel->mOperator)) { 123 // |sel| is going to select something that's not actually one of our 124 // ancestors, so don't add it to mAncestorSelectorHashes. But keep 125 // going, because it'll select a sibling of one of our ancestors, so its 126 // ancestors would be our ancestors too. 127 continue; 128 } 129 130 // Now sel is supposed to select one of our ancestors. Grab 131 // whatever info we can from it into mAncestorSelectorHashes. 132 // But in qurks mode, don't grab IDs and classes because those 133 // need to be matched case-insensitively. 134 if (!aQuirksMode) { 135 nsAtomList* ids = sel->mIDList; 136 while (ids) { 137 mAncestorSelectorHashes[hashIndex++] = ids->mAtom->hash(); 138 if (hashIndex == eMaxAncestorHashes) { 139 return; 140 } 141 ids = ids->mNext; 142 } 143 144 nsAtomList* classes = sel->mClassList; 145 while (classes) { 146 mAncestorSelectorHashes[hashIndex++] = classes->mAtom->hash(); 147 if (hashIndex == eMaxAncestorHashes) { 148 return; 149 } 150 classes = classes->mNext; 151 } 152 } 153 154 // Only put in the tag name if it's all-lowercase. Otherwise we run into 155 // trouble because we may test the wrong one of mLowercaseTag and 156 // mCasedTag against the filter. 157 if (sel->mLowercaseTag && sel->mCasedTag == sel->mLowercaseTag) { 158 mAncestorSelectorHashes[hashIndex++] = sel->mLowercaseTag->hash(); 159 if (hashIndex == eMaxAncestorHashes) { 160 return; 161 } 162 } 163 } 164 165 while (hashIndex != eMaxAncestorHashes) { 166 mAncestorSelectorHashes[hashIndex++] = 0; 167 } 168 } 169 }; 170 171 // ------------------------------ 172 // Rule hash table 173 // 174 175 // Uses any of the sets of ops below. 176 struct RuleHashTableEntry : public PLDHashEntryHdr { 177 // If you add members that have heap allocated memory be sure to change the 178 // logic in SizeOfRuleHashTable(). 179 // Auto length 1, because we always have at least one entry in mRules. 180 AutoTArray<RuleValue, 1> mRules; 181 }; 182 183 struct RuleHashTagTableEntry : public RuleHashTableEntry { 184 // If you add members that have heap allocated memory be sure to change the 185 // logic in RuleHash::SizeOf{In,Ex}cludingThis. 186 nsCOMPtr<nsIAtom> mTag; 187 }; 188 189 static PLDHashNumber 190 RuleHash_CIHashKey(const void *key) 191 { 192 nsIAtom *atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key)); 193 194 nsAutoString str; 195 atom->ToString(str); 196 nsContentUtils::ASCIIToLower(str); 197 return HashString(str); 198 } 199 200 static inline nsCSSSelector* 201 SubjectSelectorForRuleHash(const PLDHashEntryHdr *hdr) 202 { 203 auto entry = static_cast<const RuleHashTableEntry*>(hdr); 204 nsCSSSelector* selector = entry->mRules[0].mSelector; 205 if (selector->IsPseudoElement()) { 206 selector = selector->mNext; 207 } 208 return selector; 209 } 210 211 static inline bool 212 CIMatchAtoms(const void* key, nsIAtom *entry_atom) 213 { 214 auto match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key)); 215 216 // Check for case-sensitive match first. 217 if (match_atom == entry_atom) { 218 return true; 219 } 220 221 // Use EqualsIgnoreASCIICase instead of full on unicode case conversion 222 // in order to save on performance. This is only used in quirks mode 223 // anyway. 224 return 225 nsContentUtils::EqualsIgnoreASCIICase(nsDependentAtomString(entry_atom), 226 nsDependentAtomString(match_atom)); 227 } 228 229 static inline bool 230 CSMatchAtoms(const void* key, nsIAtom *entry_atom) 231 { 232 auto match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key)); 233 return match_atom == entry_atom; 234 } 235 236 static bool 237 RuleHash_ClassCIMatchEntry(const PLDHashEntryHdr *hdr, const void *key) 238 { 239 return CIMatchAtoms(key, SubjectSelectorForRuleHash(hdr)->mClassList->mAtom); 240 } 241 242 static bool 243 RuleHash_IdCIMatchEntry(const PLDHashEntryHdr *hdr, const void *key) 244 { 245 return CIMatchAtoms(key, SubjectSelectorForRuleHash(hdr)->mIDList->mAtom); 246 } 247 248 static bool 249 RuleHash_ClassCSMatchEntry(const PLDHashEntryHdr *hdr, const void *key) 250 { 251 return CSMatchAtoms(key, SubjectSelectorForRuleHash(hdr)->mClassList->mAtom); 252 } 253 254 static bool 255 RuleHash_IdCSMatchEntry(const PLDHashEntryHdr *hdr, const void *key) 256 { 257 return CSMatchAtoms(key, SubjectSelectorForRuleHash(hdr)->mIDList->mAtom); 258 } 259 260 static void 261 RuleHash_InitEntry(PLDHashEntryHdr *hdr, const void *key) 262 { 263 RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(hdr); 264 new (KnownNotNull, entry) RuleHashTableEntry(); 265 } 266 267 static void 268 RuleHash_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr) 269 { 270 RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(hdr); 271 entry->~RuleHashTableEntry(); 272 } 273 274 static void 275 RuleHash_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from, 276 PLDHashEntryHdr *to) 277 { 278 NS_PRECONDITION(from != to, "This is not going to work!"); 279 RuleHashTableEntry *oldEntry = 280 const_cast<RuleHashTableEntry*>( 281 static_cast<const RuleHashTableEntry*>(from)); 282 auto* newEntry = new (KnownNotNull, to) RuleHashTableEntry(); 283 newEntry->mRules.SwapElements(oldEntry->mRules); 284 oldEntry->~RuleHashTableEntry(); 285 } 286 287 static bool 288 RuleHash_TagTable_MatchEntry(const PLDHashEntryHdr *hdr, const void *key) 289 { 290 nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key)); 291 nsIAtom *entry_atom = static_cast<const RuleHashTagTableEntry*>(hdr)->mTag; 292 293 return match_atom == entry_atom; 294 } 295 296 static void 297 RuleHash_TagTable_InitEntry(PLDHashEntryHdr *hdr, const void *key) 298 { 299 RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>(hdr); 300 new (KnownNotNull, entry) RuleHashTagTableEntry(); 301 entry->mTag = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key)); 302 } 303 304 static void 305 RuleHash_TagTable_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr) 306 { 307 RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>(hdr); 308 entry->~RuleHashTagTableEntry(); 309 } 310 311 static void 312 RuleHash_TagTable_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from, 313 PLDHashEntryHdr *to) 314 { 315 NS_PRECONDITION(from != to, "This is not going to work!"); 316 RuleHashTagTableEntry *oldEntry = 317 const_cast<RuleHashTagTableEntry*>( 318 static_cast<const RuleHashTagTableEntry*>(from)); 319 auto* newEntry = new (KnownNotNull, to) RuleHashTagTableEntry(); 320 newEntry->mTag.swap(oldEntry->mTag); 321 newEntry->mRules.SwapElements(oldEntry->mRules); 322 oldEntry->~RuleHashTagTableEntry(); 323 } 324 325 static PLDHashNumber 326 RuleHash_NameSpaceTable_HashKey(const void *key) 327 { 328 return NS_PTR_TO_INT32(key); 329 } 330 331 static bool 332 RuleHash_NameSpaceTable_MatchEntry(const PLDHashEntryHdr *hdr, const void *key) 333 { 334 const RuleHashTableEntry *entry = 335 static_cast<const RuleHashTableEntry*>(hdr); 336 337 nsCSSSelector* selector = entry->mRules[0].mSelector; 338 if (selector->IsPseudoElement()) { 339 selector = selector->mNext; 340 } 341 return NS_PTR_TO_INT32(key) == selector->mNameSpace; 342 } 343 344 static const PLDHashTableOps RuleHash_TagTable_Ops = { 345 PLDHashTable::HashVoidPtrKeyStub, 346 RuleHash_TagTable_MatchEntry, 347 RuleHash_TagTable_MoveEntry, 348 RuleHash_TagTable_ClearEntry, 349 RuleHash_TagTable_InitEntry 350 }; 351 352 // Case-sensitive ops. 353 static const PLDHashTableOps RuleHash_ClassTable_CSOps = { 354 PLDHashTable::HashVoidPtrKeyStub, 355 RuleHash_ClassCSMatchEntry, 356 RuleHash_MoveEntry, 357 RuleHash_ClearEntry, 358 RuleHash_InitEntry 359 }; 360 361 // Case-insensitive ops. 362 static const PLDHashTableOps RuleHash_ClassTable_CIOps = { 363 RuleHash_CIHashKey, 364 RuleHash_ClassCIMatchEntry, 365 RuleHash_MoveEntry, 366 RuleHash_ClearEntry, 367 RuleHash_InitEntry 368 }; 369 370 // Case-sensitive ops. 371 static const PLDHashTableOps RuleHash_IdTable_CSOps = { 372 PLDHashTable::HashVoidPtrKeyStub, 373 RuleHash_IdCSMatchEntry, 374 RuleHash_MoveEntry, 375 RuleHash_ClearEntry, 376 RuleHash_InitEntry 377 }; 378 379 // Case-insensitive ops. 380 static const PLDHashTableOps RuleHash_IdTable_CIOps = { 381 RuleHash_CIHashKey, 382 RuleHash_IdCIMatchEntry, 383 RuleHash_MoveEntry, 384 RuleHash_ClearEntry, 385 RuleHash_InitEntry 386 }; 387 388 static const PLDHashTableOps RuleHash_NameSpaceTable_Ops = { 389 RuleHash_NameSpaceTable_HashKey, 390 RuleHash_NameSpaceTable_MatchEntry, 391 RuleHash_MoveEntry, 392 RuleHash_ClearEntry, 393 RuleHash_InitEntry 394 }; 395 396 #undef RULE_HASH_STATS 397 #undef PRINT_UNIVERSAL_RULES 398 399 #ifdef RULE_HASH_STATS 400 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO ++(var_); PR_END_MACRO 401 #else 402 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO PR_END_MACRO 403 #endif 404 405 struct NodeMatchContext; 406 407 class RuleHash { 408 public: 409 explicit RuleHash(bool aQuirksMode); 410 ~RuleHash(); 411 void AppendRule(const RuleSelectorPair &aRuleInfo); 412 void EnumerateAllRules(Element* aElement, ElementDependentRuleProcessorData* aData, 413 NodeMatchContext& aNodeMatchContext); 414 415 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const; 416 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; 417 418 protected: 419 typedef nsTArray<RuleValue> RuleValueList; 420 void AppendRuleToTable(PLDHashTable* aTable, const void* aKey, 421 const RuleSelectorPair& aRuleInfo); 422 void AppendUniversalRule(const RuleSelectorPair& aRuleInfo); 423 424 int32_t mRuleCount; 425 426 PLDHashTable mIdTable; 427 PLDHashTable mClassTable; 428 PLDHashTable mTagTable; 429 PLDHashTable mNameSpaceTable; 430 RuleValueList mUniversalRules; 431 432 struct EnumData { 433 const RuleValue* mCurValue; 434 const RuleValue* mEnd; 435 }; 436 EnumData* mEnumList; 437 int32_t mEnumListSize; 438 439 bool mQuirksMode; 440 441 inline EnumData ToEnumData(const RuleValueList& arr) { 442 EnumData data = { arr.Elements(), arr.Elements() + arr.Length() }; 443 return data; 444 } 445 446 #ifdef RULE_HASH_STATS 447 uint32_t mUniversalSelectors; 448 uint32_t mNameSpaceSelectors; 449 uint32_t mTagSelectors; 450 uint32_t mClassSelectors; 451 uint32_t mIdSelectors; 452 453 uint32_t mElementsMatched; 454 455 uint32_t mElementUniversalCalls; 456 uint32_t mElementNameSpaceCalls; 457 uint32_t mElementTagCalls; 458 uint32_t mElementClassCalls; 459 uint32_t mElementIdCalls; 460 #endif // RULE_HASH_STATS 461 }; 462 463 RuleHash::RuleHash(bool aQuirksMode) 464 : mRuleCount(0), 465 mIdTable(aQuirksMode ? &RuleHash_IdTable_CIOps 466 : &RuleHash_IdTable_CSOps, 467 sizeof(RuleHashTableEntry)), 468 mClassTable(aQuirksMode ? &RuleHash_ClassTable_CIOps 469 : &RuleHash_ClassTable_CSOps, 470 sizeof(RuleHashTableEntry)), 471 mTagTable(&RuleHash_TagTable_Ops, sizeof(RuleHashTagTableEntry)), 472 mNameSpaceTable(&RuleHash_NameSpaceTable_Ops, sizeof(RuleHashTableEntry)), 473 mUniversalRules(0), 474 mEnumList(nullptr), mEnumListSize(0), 475 mQuirksMode(aQuirksMode) 476 #ifdef RULE_HASH_STATS 477 , 478 mUniversalSelectors(0), 479 mNameSpaceSelectors(0), 480 mTagSelectors(0), 481 mClassSelectors(0), 482 mIdSelectors(0), 483 mElementsMatched(0), 484 mElementUniversalCalls(0), 485 mElementNameSpaceCalls(0), 486 mElementTagCalls(0), 487 mElementClassCalls(0), 488 mElementIdCalls(0) 489 #endif 490 { 491 MOZ_COUNT_CTOR(RuleHash); 492 } 493 494 RuleHash::~RuleHash() 495 { 496 MOZ_COUNT_DTOR(RuleHash); 497 #ifdef RULE_HASH_STATS 498 printf( 499 "RuleHash(%p):\n" 500 " Selectors: Universal (%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n" 501 " Content Nodes: Elements(%u)\n" 502 " Element Calls: Universal(%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n" 503 static_cast<void*>(this), 504 mUniversalSelectors, mNameSpaceSelectors, mTagSelectors, 505 mClassSelectors, mIdSelectors, 506 mElementsMatched, 507 mElementUniversalCalls, mElementNameSpaceCalls, mElementTagCalls, 508 mElementClassCalls, mElementIdCalls); 509 #ifdef PRINT_UNIVERSAL_RULES 510 { 511 if (mUniversalRules.Length() > 0) { 512 printf(" Universal rules:\n"); 513 for (uint32_t i = 0; i < mUniversalRules.Length(); ++i) { 514 RuleValue* value = &(mUniversalRules[i]); 515 nsAutoString selectorText; 516 uint32_t lineNumber = value->mRule->GetLineNumber(); 517 RefPtr<CSSStyleSheet> cssSheet = value->mRule->GetStyleSheet(); 518 value->mSelector->ToString(selectorText, cssSheet); 519 520 printf(" line %d, %s\n", 521 lineNumber, NS_ConvertUTF16toUTF8(selectorText).get()); 522 } 523 } 524 } 525 #endif // PRINT_UNIVERSAL_RULES 526 #endif // RULE_HASH_STATS 527 // Rule Values are arena allocated no need to delete them. Their destructor 528 // isn't doing any cleanup. So we dont even bother to enumerate through 529 // the hash tables and call their destructors. 530 if (nullptr != mEnumList) { 531 delete [] mEnumList; 532 } 533 } 534 535 void RuleHash::AppendRuleToTable(PLDHashTable* aTable, const void* aKey, 536 const RuleSelectorPair& aRuleInfo) 537 { 538 // Get a new or existing entry. 539 auto entry = static_cast<RuleHashTableEntry*>(aTable->Add(aKey, fallible)); 540 if (!entry) 541 return; 542 entry->mRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode)); 543 } 544 545 static void 546 AppendRuleToTagTable(PLDHashTable* aTable, nsIAtom* aKey, 547 const RuleValue& aRuleInfo) 548 { 549 // Get a new or exisiting entry 550 auto entry = static_cast<RuleHashTagTableEntry*>(aTable->Add(aKey, fallible)); 551 if (!entry) 552 return; 553 554 entry->mRules.AppendElement(aRuleInfo); 555 } 556 557 void RuleHash::AppendUniversalRule(const RuleSelectorPair& aRuleInfo) 558 { 559 mUniversalRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode)); 560 } 561 562 void RuleHash::AppendRule(const RuleSelectorPair& aRuleInfo) 563 { 564 nsCSSSelector *selector = aRuleInfo.mSelector; 565 if (selector->IsPseudoElement()) { 566 selector = selector->mNext; 567 } 568 if (nullptr != selector->mIDList) { 569 AppendRuleToTable(&mIdTable, selector->mIDList->mAtom, aRuleInfo); 570 RULE_HASH_STAT_INCREMENT(mIdSelectors); 571 } 572 else if (nullptr != selector->mClassList) { 573 AppendRuleToTable(&mClassTable, selector->mClassList->mAtom, aRuleInfo); 574 RULE_HASH_STAT_INCREMENT(mClassSelectors); 575 } 576 else if (selector->mLowercaseTag) { 577 RuleValue ruleValue(aRuleInfo, mRuleCount++, mQuirksMode); 578 AppendRuleToTagTable(&mTagTable, selector->mLowercaseTag, ruleValue); 579 RULE_HASH_STAT_INCREMENT(mTagSelectors); 580 if (selector->mCasedTag && 581 selector->mCasedTag != selector->mLowercaseTag) { 582 AppendRuleToTagTable(&mTagTable, selector->mCasedTag, ruleValue); 583 RULE_HASH_STAT_INCREMENT(mTagSelectors); 584 } 585 } 586 else if (kNameSpaceID_Unknown != selector->mNameSpace) { 587 AppendRuleToTable(&mNameSpaceTable, 588 NS_INT32_TO_PTR(selector->mNameSpace), aRuleInfo); 589 RULE_HASH_STAT_INCREMENT(mNameSpaceSelectors); 590 } 591 else { // universal tag selector 592 AppendUniversalRule(aRuleInfo); 593 RULE_HASH_STAT_INCREMENT(mUniversalSelectors); 594 } 595 } 596 597 // this should cover practically all cases so we don't need to reallocate 598 #define MIN_ENUM_LIST_SIZE 8 599 600 #ifdef RULE_HASH_STATS 601 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \ 602 (var_) += (list_).Length() 603 #else 604 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \ 605 PR_BEGIN_MACRO PR_END_MACRO 606 #endif 607 608 static inline 609 void ContentEnumFunc(const RuleValue &value, nsCSSSelector* selector, 610 ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext, 611 AncestorFilter *ancestorFilter); 612 613 void RuleHash::EnumerateAllRules(Element* aElement, ElementDependentRuleProcessorData* aData, 614 NodeMatchContext& aNodeContext) 615 { 616 int32_t nameSpace = aElement->GetNameSpaceID(); 617 nsIAtom* tag = aElement->NodeInfo()->NameAtom(); 618 nsIAtom* id = aElement->GetID(); 619 const nsAttrValue* classList = aElement->GetClasses(); 620 621 MOZ_ASSERT(tag, "How could we not have a tag?"); 622 623 int32_t classCount = classList ? classList->GetAtomCount() : 0; 624 625 // assume 1 universal, tag, id, and namespace, rather than wasting 626 // time counting 627 int32_t testCount = classCount + 4; 628 629 if (mEnumListSize < testCount) { 630 delete [] mEnumList; 631 mEnumListSize = std::max(testCount, MIN_ENUM_LIST_SIZE); 632 mEnumList = new EnumData[mEnumListSize]; 633 } 634 635 int32_t valueCount = 0; 636 RULE_HASH_STAT_INCREMENT(mElementsMatched); 637 638 if (mUniversalRules.Length() != 0) { // universal rules 639 mEnumList[valueCount++] = ToEnumData(mUniversalRules); 640 RULE_HASH_STAT_INCREMENT_LIST_COUNT(mUniversalRules, mElementUniversalCalls); 641 } 642 // universal rules within the namespace 643 if (kNameSpaceID_Unknown != nameSpace && mNameSpaceTable.EntryCount() > 0) { 644 auto entry = static_cast<RuleHashTableEntry*> 645 (mNameSpaceTable.Search(NS_INT32_TO_PTR(nameSpace))); 646 if (entry) { 647 mEnumList[valueCount++] = ToEnumData(entry->mRules); 648 RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementNameSpaceCalls); 649 } 650 } 651 if (mTagTable.EntryCount() > 0) { 652 auto entry = static_cast<RuleHashTableEntry*>(mTagTable.Search(tag)); 653 if (entry) { 654 mEnumList[valueCount++] = ToEnumData(entry->mRules); 655 RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementTagCalls); 656 } 657 } 658 if (id && mIdTable.EntryCount() > 0) { 659 auto entry = static_cast<RuleHashTableEntry*>(mIdTable.Search(id)); 660 if (entry) { 661 mEnumList[valueCount++] = ToEnumData(entry->mRules); 662 RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementIdCalls); 663 } 664 } 665 if (mClassTable.EntryCount() > 0) { 666 for (int32_t index = 0; index < classCount; ++index) { 667 auto entry = static_cast<RuleHashTableEntry*> 668 (mClassTable.Search(classList->AtomAt(index))); 669 if (entry) { 670 mEnumList[valueCount++] = ToEnumData(entry->mRules); 671 RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementClassCalls); 672 } 673 } 674 } 675 NS_ASSERTION(valueCount <= testCount, "values exceeded list size"); 676 677 if (valueCount > 0) { 678 AncestorFilter *filter = 679 aData->mTreeMatchContext.mAncestorFilter.HasFilter() ? 680 &aData->mTreeMatchContext.mAncestorFilter : nullptr; 681 #ifdef DEBUG 682 if (filter) { 683 filter->AssertHasAllAncestors(aElement); 684 } 685 #endif 686 // Merge the lists while there are still multiple lists to merge. 687 while (valueCount > 1) { 688 int32_t valueIndex = 0; 689 int32_t lowestRuleIndex = mEnumList[valueIndex].mCurValue->mIndex; 690 for (int32_t index = 1; index < valueCount; ++index) { 691 int32_t ruleIndex = mEnumList[index].mCurValue->mIndex; 692 if (ruleIndex < lowestRuleIndex) { 693 valueIndex = index; 694 lowestRuleIndex = ruleIndex; 695 } 696 } 697 const RuleValue *cur = mEnumList[valueIndex].mCurValue; 698 ContentEnumFunc(*cur, cur->mSelector, aData, aNodeContext, filter); 699 cur++; 700 if (cur == mEnumList[valueIndex].mEnd) { 701 mEnumList[valueIndex] = mEnumList[--valueCount]; 702 } else { 703 mEnumList[valueIndex].mCurValue = cur; 704 } 705 } 706 707 // Fast loop over single value. 708 for (const RuleValue *value = mEnumList[0].mCurValue, 709 *end = mEnumList[0].mEnd; 710 value != end; ++value) { 711 ContentEnumFunc(*value, value->mSelector, aData, aNodeContext, filter); 712 } 713 } 714 } 715 716 static size_t 717 SizeOfRuleHashTable(const PLDHashTable& aTable, MallocSizeOf aMallocSizeOf) 718 { 719 size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf); 720 for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) { 721 auto entry = static_cast<RuleHashTableEntry*>(iter.Get()); 722 n += entry->mRules.ShallowSizeOfExcludingThis(aMallocSizeOf); 723 } 724 return n; 725 } 726 727 size_t 728 RuleHash::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const 729 { 730 size_t n = 0; 731 732 n += SizeOfRuleHashTable(mIdTable, aMallocSizeOf); 733 734 n += SizeOfRuleHashTable(mClassTable, aMallocSizeOf); 735 736 n += SizeOfRuleHashTable(mTagTable, aMallocSizeOf); 737 738 n += SizeOfRuleHashTable(mNameSpaceTable, aMallocSizeOf); 739 740 n += mUniversalRules.ShallowSizeOfExcludingThis(aMallocSizeOf); 741 742 return n; 743 } 744 745 size_t 746 RuleHash::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const 747 { 748 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 749 } 750 751 //-------------------------------- 752 753 /** 754 * A struct that stores an nsCSSSelector pointer along side a pointer to 755 * the rightmost nsCSSSelector in the selector. For example, for 756 * 757 * .main p > span 758 * 759 * if mSelector points to the |p| nsCSSSelector, mRightmostSelector would 760 * point to the |span| nsCSSSelector. 761 * 762 * Both mSelector and mRightmostSelector are always top-level selectors, 763 * i.e. they aren't selectors within a :not() or :-moz-any(). 764 */ 765 struct SelectorPair 766 { 767 SelectorPair(nsCSSSelector* aSelector, nsCSSSelector* aRightmostSelector) 768 : mSelector(aSelector), mRightmostSelector(aRightmostSelector) 769 { 770 MOZ_ASSERT(aSelector); 771 MOZ_ASSERT(mRightmostSelector); 772 } 773 SelectorPair(const SelectorPair& aOther) = default; 774 nsCSSSelector* const mSelector; 775 nsCSSSelector* const mRightmostSelector; 776 }; 777 778 // A hash table mapping atoms to lists of selectors 779 struct AtomSelectorEntry : public PLDHashEntryHdr { 780 nsIAtom *mAtom; 781 // Auto length 2, because a decent fraction of these arrays ends up 782 // with 2 elements, and each entry is cheap. 783 AutoTArray<SelectorPair, 2> mSelectors; 784 }; 785 786 static void 787 AtomSelector_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr) 788 { 789 (static_cast<AtomSelectorEntry*>(hdr))->~AtomSelectorEntry(); 790 } 791 792 static void 793 AtomSelector_InitEntry(PLDHashEntryHdr *hdr, const void *key) 794 { 795 AtomSelectorEntry *entry = static_cast<AtomSelectorEntry*>(hdr); 796 new (KnownNotNull, entry) AtomSelectorEntry(); 797 entry->mAtom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key)); 798 } 799 800 static void 801 AtomSelector_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from, 802 PLDHashEntryHdr *to) 803 { 804 NS_PRECONDITION(from != to, "This is not going to work!"); 805 AtomSelectorEntry *oldEntry = 806 const_cast<AtomSelectorEntry*>(static_cast<const AtomSelectorEntry*>(from)); 807 auto* newEntry = new (KnownNotNull, to) AtomSelectorEntry(); 808 newEntry->mAtom = oldEntry->mAtom; 809 newEntry->mSelectors.SwapElements(oldEntry->mSelectors); 810 oldEntry->~AtomSelectorEntry(); 811 } 812 813 static bool 814 AtomSelector_CIMatchEntry(const PLDHashEntryHdr *hdr, const void *key) 815 { 816 const AtomSelectorEntry *entry = static_cast<const AtomSelectorEntry*>(hdr); 817 return CIMatchAtoms(key, entry->mAtom); 818 } 819 820 // Case-sensitive ops. 821 static const PLDHashTableOps AtomSelector_CSOps = { 822 PLDHashTable::HashVoidPtrKeyStub, 823 PLDHashTable::MatchEntryStub, 824 AtomSelector_MoveEntry, 825 AtomSelector_ClearEntry, 826 AtomSelector_InitEntry 827 }; 828 829 // Case-insensitive ops. 830 static const PLDHashTableOps AtomSelector_CIOps = { 831 RuleHash_CIHashKey, 832 AtomSelector_CIMatchEntry, 833 AtomSelector_MoveEntry, 834 AtomSelector_ClearEntry, 835 AtomSelector_InitEntry 836 }; 837 838 //-------------------------------- 839 840 struct RuleCascadeData { 841 RuleCascadeData(nsIAtom *aMedium, bool aQuirksMode) 842 : mRuleHash(aQuirksMode), 843 mStateSelectors(), 844 mSelectorDocumentStates(0), 845 mClassSelectors(aQuirksMode ? &AtomSelector_CIOps 846 : &AtomSelector_CSOps, 847 sizeof(AtomSelectorEntry)), 848 mIdSelectors(aQuirksMode ? &AtomSelector_CIOps 849 : &AtomSelector_CSOps, 850 sizeof(AtomSelectorEntry)), 851 // mAttributeSelectors is matching on the attribute _name_, not the 852 // value, and we case-fold names at parse-time, so this is a 853 // case-sensitive match. 854 mAttributeSelectors(&AtomSelector_CSOps, sizeof(AtomSelectorEntry)), 855 mAnonBoxRules(&RuleHash_TagTable_Ops, sizeof(RuleHashTagTableEntry)), 856 #ifdef MOZ_XUL 857 mXULTreeRules(&RuleHash_TagTable_Ops, sizeof(RuleHashTagTableEntry)), 858 #endif 859 mKeyframesRuleTable(), 860 mCounterStyleRuleTable(), 861 mCacheKey(aMedium), 862 mNext(nullptr), 863 mQuirksMode(aQuirksMode) 864 { 865 memset(mPseudoElementRuleHashes, 0, sizeof(mPseudoElementRuleHashes)); 866 } 867 868 ~RuleCascadeData() 869 { 870 for (uint32_t i = 0; i < ArrayLength(mPseudoElementRuleHashes); ++i) { 871 delete mPseudoElementRuleHashes[i]; 872 } 873 } 874 875 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; 876 877 RuleHash mRuleHash; 878 RuleHash* mPseudoElementRuleHashes[ 879 static_cast<CSSPseudoElementTypeBase>(CSSPseudoElementType::Count)]; 880 nsTArray<nsCSSRuleProcessor::StateSelector> mStateSelectors; 881 EventStates mSelectorDocumentStates; 882 PLDHashTable mClassSelectors; 883 PLDHashTable mIdSelectors; 884 nsTArray<nsCSSSelector*> mPossiblyNegatedClassSelectors; 885 nsTArray<nsCSSSelector*> mPossiblyNegatedIDSelectors; 886 PLDHashTable mAttributeSelectors; 887 PLDHashTable mAnonBoxRules; 888 #ifdef MOZ_XUL 889 PLDHashTable mXULTreeRules; 890 #endif 891 892 nsTArray<nsFontFaceRuleContainer> mFontFaceRules; 893 nsTArray<nsCSSKeyframesRule*> mKeyframesRules; 894 nsTArray<nsCSSFontFeatureValuesRule*> mFontFeatureValuesRules; 895 nsTArray<nsCSSPageRule*> mPageRules; 896 nsTArray<nsCSSCounterStyleRule*> mCounterStyleRules; 897 898 nsDataHashtable<nsStringHashKey, nsCSSKeyframesRule*> mKeyframesRuleTable; 899 nsDataHashtable<nsStringHashKey, nsCSSCounterStyleRule*> mCounterStyleRuleTable; 900 901 // Looks up or creates the appropriate list in |mAttributeSelectors|. 902 // Returns null only on allocation failure. 903 nsTArray<SelectorPair>* AttributeListFor(nsIAtom* aAttribute); 904 905 nsMediaQueryResultCacheKey mCacheKey; 906 RuleCascadeData* mNext; // for a different medium 907 908 const bool mQuirksMode; 909 }; 910 911 static size_t 912 SizeOfSelectorsHashTable(const PLDHashTable& aTable, MallocSizeOf aMallocSizeOf) 913 { 914 size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf); 915 for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) { 916 auto entry = static_cast<AtomSelectorEntry*>(iter.Get()); 917 n += entry->mSelectors.ShallowSizeOfExcludingThis(aMallocSizeOf); 918 } 919 return n; 920 } 921 922 size_t 923 RuleCascadeData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const 924 { 925 size_t n = aMallocSizeOf(this); 926 927 n += mRuleHash.SizeOfExcludingThis(aMallocSizeOf); 928 for (uint32_t i = 0; i < ArrayLength(mPseudoElementRuleHashes); ++i) { 929 if (mPseudoElementRuleHashes[i]) 930 n += mPseudoElementRuleHashes[i]->SizeOfIncludingThis(aMallocSizeOf); 931 } 932 933 n += mStateSelectors.ShallowSizeOfExcludingThis(aMallocSizeOf); 934 935 n += SizeOfSelectorsHashTable(mIdSelectors, aMallocSizeOf); 936 n += SizeOfSelectorsHashTable(mClassSelectors, aMallocSizeOf); 937 938 n += mPossiblyNegatedClassSelectors.ShallowSizeOfExcludingThis(aMallocSizeOf); 939 n += mPossiblyNegatedIDSelectors.ShallowSizeOfExcludingThis(aMallocSizeOf); 940 941 n += SizeOfSelectorsHashTable(mAttributeSelectors, aMallocSizeOf); 942 n += SizeOfRuleHashTable(mAnonBoxRules, aMallocSizeOf); 943 #ifdef MOZ_XUL 944 n += SizeOfRuleHashTable(mXULTreeRules, aMallocSizeOf); 945 #endif 946 947 n += mFontFaceRules.ShallowSizeOfExcludingThis(aMallocSizeOf); 948 n += mKeyframesRules.ShallowSizeOfExcludingThis(aMallocSizeOf); 949 n += mFontFeatureValuesRules.ShallowSizeOfExcludingThis(aMallocSizeOf); 950 n += mPageRules.ShallowSizeOfExcludingThis(aMallocSizeOf); 951 n += mCounterStyleRules.ShallowSizeOfExcludingThis(aMallocSizeOf); 952 953 n += mKeyframesRuleTable.ShallowSizeOfExcludingThis(aMallocSizeOf); 954 for (auto iter = mKeyframesRuleTable.ConstIter(); !iter.Done(); iter.Next()) { 955 // We don't own the nsCSSKeyframesRule objects so we don't count them. We 956 // do care about the size of the keys' nsAString members' buffers though. 957 // 958 // Note that we depend on nsStringHashKey::GetKey() returning a reference, 959 // since otherwise aKey would be a copy of the string key and we would not 960 // be measuring the right object here. 961 n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf); 962 } 963 964 return n; 965 } 966 967 nsTArray<SelectorPair>* 968 RuleCascadeData::AttributeListFor(nsIAtom* aAttribute) 969 { 970 auto entry = static_cast<AtomSelectorEntry*> 971 (mAttributeSelectors.Add(aAttribute, fallible)); 972 if (!entry) 973 return nullptr; 974 return &entry->mSelectors; 975 } 976 977 // ------------------------------- 978 // CSS Style rule processor implementation 979 // 980 981 nsCSSRuleProcessor::nsCSSRuleProcessor(const sheet_array_type& aSheets, 982 SheetType aSheetType, 983 Element* aScopeElement, 984 nsCSSRuleProcessor* 985 aPreviousCSSRuleProcessor, 986 bool aIsShared) 987 : nsCSSRuleProcessor(sheet_array_type(aSheets), aSheetType, aScopeElement, 988 aPreviousCSSRuleProcessor, aIsShared) 989 { 990 } 991 992 nsCSSRuleProcessor::nsCSSRuleProcessor(sheet_array_type&& aSheets, 993 SheetType aSheetType, 994 Element* aScopeElement, 995 nsCSSRuleProcessor* 996 aPreviousCSSRuleProcessor, 997 bool aIsShared) 998 : mSheets(aSheets) 999 , mRuleCascades(nullptr) 1000 , mPreviousCacheKey(aPreviousCSSRuleProcessor 1001 ? aPreviousCSSRuleProcessor->CloneMQCacheKey() 1002 : UniquePtr<nsMediaQueryResultCacheKey>()) 1003 , mLastPresContext(nullptr) 1004 , mScopeElement(aScopeElement) 1005 , mStyleSetRefCnt(0) 1006 , mSheetType(aSheetType) 1007 , mIsShared(aIsShared) 1008 , mMustGatherDocumentRules(aIsShared) 1009 , mInRuleProcessorCache(false) 1010 #ifdef DEBUG 1011 , mDocumentRulesAndCacheKeyValid(false) 1012 #endif 1013 { 1014 NS_ASSERTION(!!mScopeElement == (aSheetType == SheetType::ScopedDoc), 1015 "aScopeElement must be specified iff aSheetType is " 1016 "eScopedDocSheet"); 1017 for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) { 1018 mSheets[i]->AddRuleProcessor(this); 1019 } 1020 } 1021 1022 nsCSSRuleProcessor::~nsCSSRuleProcessor() 1023 { 1024 if (mInRuleProcessorCache) { 1025 RuleProcessorCache::RemoveRuleProcessor(this); 1026 } 1027 MOZ_ASSERT(!mExpirationState.IsTracked()); 1028 MOZ_ASSERT(mStyleSetRefCnt == 0); 1029 ClearSheets(); 1030 ClearRuleCascades(); 1031 } 1032 1033 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSRuleProcessor) 1034 NS_INTERFACE_MAP_ENTRY(nsIStyleRuleProcessor) 1035 NS_INTERFACE_MAP_END 1036 1037 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSRuleProcessor) 1038 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSRuleProcessor) 1039 1040 NS_IMPL_CYCLE_COLLECTION_CLASS(nsCSSRuleProcessor) 1041 1042 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCSSRuleProcessor) 1043 tmp->ClearSheets(); 1044 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScopeElement) 1045 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1046 1047 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCSSRuleProcessor) 1048 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSheets) 1049 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScopeElement) 1050 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1051 1052 void 1053 nsCSSRuleProcessor::ClearSheets() 1054 { 1055 for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) { 1056 mSheets[i]->DropRuleProcessor(this); 1057 } 1058 mSheets.Clear(); 1059 } 1060 1061 /* static */ void 1062 nsCSSRuleProcessor::Startup() 1063 { 1064 Preferences::AddBoolVarCache(&gSupportVisitedPseudo, VISITED_PSEUDO_PREF, 1065 true); 1066 } 1067 1068 static bool 1069 InitSystemMetrics() 1070 { 1071 NS_ASSERTION(!sSystemMetrics, "already initialized"); 1072 1073 sSystemMetrics = new nsTArray< nsCOMPtr<nsIAtom> >; 1074 NS_ENSURE_TRUE(sSystemMetrics, false); 1075 1076 /*************************************************************************** 1077 * ANY METRICS ADDED HERE SHOULD ALSO BE ADDED AS MEDIA QUERIES IN * 1078 * nsMediaFeatures.cpp * 1079 ***************************************************************************/ 1080 1081 int32_t metricResult = 1082 LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollArrowStyle); 1083 if (metricResult & LookAndFeel::eScrollArrow_StartBackward) { 1084 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_start_backward); 1085 } 1086 if (metricResult & LookAndFeel::eScrollArrow_StartForward) { 1087 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_start_forward); 1088 } 1089 if (metricResult & LookAndFeel::eScrollArrow_EndBackward) { 1090 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_end_backward); 1091 } 1092 if (metricResult & LookAndFeel::eScrollArrow_EndForward) { 1093 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_end_forward); 1094 } 1095 1096 metricResult = 1097 LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollSliderStyle); 1098 if (metricResult != LookAndFeel::eScrollThumbStyle_Normal) { 1099 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_thumb_proportional); 1100 } 1101 1102 metricResult = 1103 LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars); 1104 if (metricResult) { 1105 sSystemMetrics->AppendElement(nsGkAtoms::overlay_scrollbars); 1106 } 1107 1108 metricResult = 1109 LookAndFeel::GetInt(LookAndFeel::eIntID_MenuBarDrag); 1110 if (metricResult) { 1111 sSystemMetrics->AppendElement(nsGkAtoms::menubar_drag); 1112 } 1113 1114 nsresult rv = 1115 LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsDefaultTheme, &metricResult); 1116 if (NS_SUCCEEDED(rv) && metricResult) { 1117 sSystemMetrics->AppendElement(nsGkAtoms::windows_default_theme); 1118 } 1119 1120 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacGraphiteTheme, &metricResult); 1121 if (NS_SUCCEEDED(rv) && metricResult) { 1122 sSystemMetrics->AppendElement(nsGkAtoms::mac_graphite_theme); 1123 } 1124 1125 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacYosemiteTheme, &metricResult); 1126 if (NS_SUCCEEDED(rv) && metricResult) { 1127 sSystemMetrics->AppendElement(nsGkAtoms::mac_yosemite_theme); 1128 } 1129 1130 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_DWMCompositor, &metricResult); 1131 if (NS_SUCCEEDED(rv) && metricResult) { 1132 sSystemMetrics->AppendElement(nsGkAtoms::windows_compositor); 1133 } 1134 1135 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsGlass, &metricResult); 1136 if (NS_SUCCEEDED(rv) && metricResult) { 1137 sSystemMetrics->AppendElement(nsGkAtoms::windows_glass); 1138 } 1139 1140 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_ColorPickerAvailable, &metricResult); 1141 if (NS_SUCCEEDED(rv) && metricResult) { 1142 sSystemMetrics->AppendElement(nsGkAtoms::color_picker_available); 1143 } 1144 1145 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsClassic, &metricResult); 1146 if (NS_SUCCEEDED(rv) && metricResult) { 1147 sSystemMetrics->AppendElement(nsGkAtoms::windows_classic); 1148 } 1149 1150 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_TouchEnabled, &metricResult); 1151 if (NS_SUCCEEDED(rv) && metricResult) { 1152 sSystemMetrics->AppendElement(nsGkAtoms::touch_enabled); 1153 } 1154 1155 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_SwipeAnimationEnabled, 1156 &metricResult); 1157 if (NS_SUCCEEDED(rv) && metricResult) { 1158 sSystemMetrics->AppendElement(nsGkAtoms::swipe_animation_enabled); 1159 } 1160 1161 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_PhysicalHomeButton, 1162 &metricResult); 1163 if (NS_SUCCEEDED(rv) && metricResult) { 1164 sSystemMetrics->AppendElement(nsGkAtoms::physical_home_button); 1165 } 1166 1167 #ifdef XP_WIN 1168 if (NS_SUCCEEDED( 1169 LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsThemeIdentifier, 1170 &metricResult))) { 1171 nsCSSRuleProcessor::SetWindowsThemeIdentifier(static_cast<uint8_t>(metricResult)); 1172 switch(metricResult) { 1173 case LookAndFeel::eWindowsTheme_Aero: 1174 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero); 1175 break; 1176 case LookAndFeel::eWindowsTheme_AeroLite: 1177 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero_lite); 1178 break; 1179 case LookAndFeel::eWindowsTheme_LunaBlue: 1180 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_blue); 1181 break; 1182 case LookAndFeel::eWindowsTheme_LunaOlive: 1183 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_olive); 1184 break; 1185 case LookAndFeel::eWindowsTheme_LunaSilver: 1186 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_silver); 1187 break; 1188 case LookAndFeel::eWindowsTheme_Royale: 1189 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_royale); 1190 break; 1191 case LookAndFeel::eWindowsTheme_Zune: 1192 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_zune); 1193 break; 1194 case LookAndFeel::eWindowsTheme_Generic: 1195 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_generic); 1196 break; 1197 } 1198 } 1199 #endif 1200 1201 return true; 1202 } 1203 1204 /* static */ void 1205 nsCSSRuleProcessor::FreeSystemMetrics() 1206 { 1207 delete sSystemMetrics; 1208 sSystemMetrics = nullptr; 1209 } 1210 1211 /* static */ void 1212 nsCSSRuleProcessor::Shutdown() 1213 { 1214 FreeSystemMetrics(); 1215 } 1216 1217 /* static */ bool 1218 nsCSSRuleProcessor::HasSystemMetric(nsIAtom* aMetric) 1219 { 1220 if (!sSystemMetrics && !InitSystemMetrics()) { 1221 return false; 1222 } 1223 return sSystemMetrics->IndexOf(aMetric) != sSystemMetrics->NoIndex; 1224 } 1225 1226 #ifdef XP_WIN 1227 /* static */ uint8_t 1228 nsCSSRuleProcessor::GetWindowsThemeIdentifier() 1229 { 1230 if (!sSystemMetrics) 1231 InitSystemMetrics(); 1232 return sWinThemeId; 1233 } 1234 #endif 1235 1236 /* static */ 1237 EventStates 1238 nsCSSRuleProcessor::GetContentState(Element* aElement, const TreeMatchContext& aTreeMatchContext) 1239 { 1240 EventStates state = aElement->StyleState(); 1241 1242 // If we are not supposed to mark visited links as such, be sure to 1243 // flip the bits appropriately. We want to do this here, rather 1244 // than in GetContentStateForVisitedHandling, so that we don't 1245 // expose that :visited support is disabled to the Web page. 1246 if (state.HasState(NS_EVENT_STATE_VISITED) && 1247 (!gSupportVisitedPseudo || 1248 aElement->OwnerDoc()->IsBeingUsedAsImage() || 1249 aTreeMatchContext.mUsingPrivateBrowsing)) { 1250 state &= ~NS_EVENT_STATE_VISITED; 1251 state |= NS_EVENT_STATE_UNVISITED; 1252 } 1253 return state; 1254 } 1255 1256 /* static */ 1257 bool 1258 nsCSSRuleProcessor::IsLink(const Element* aElement) 1259 { 1260 EventStates state = aElement->StyleState(); 1261 return state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED); 1262 } 1263 1264 /* static */ 1265 EventStates 1266 nsCSSRuleProcessor::GetContentStateForVisitedHandling( 1267 Element* aElement, 1268 const TreeMatchContext& aTreeMatchContext, 1269 nsRuleWalker::VisitedHandlingType aVisitedHandling, 1270 bool aIsRelevantLink) 1271 { 1272 EventStates contentState = GetContentState(aElement, aTreeMatchContext); 1273 if (contentState.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)) { 1274 MOZ_ASSERT(IsLink(aElement), "IsLink() should match state"); 1275 contentState &= ~(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED); 1276 if (aIsRelevantLink) { 1277 switch (aVisitedHandling) { 1278 case nsRuleWalker::eRelevantLinkUnvisited: 1279 contentState |= NS_EVENT_STATE_UNVISITED; 1280 break; 1281 case nsRuleWalker::eRelevantLinkVisited: 1282 contentState |= NS_EVENT_STATE_VISITED; 1283 break; 1284 case nsRuleWalker::eLinksVisitedOrUnvisited: 1285 contentState |= NS_EVENT_STATE_UNVISITED | NS_EVENT_STATE_VISITED; 1286 break; 1287 } 1288 } else { 1289 contentState |= NS_EVENT_STATE_UNVISITED; 1290 } 1291 } 1292 return contentState; 1293 } 1294 1295 /** 1296 * A |NodeMatchContext| has data about matching a selector (without 1297 * combinators) against a single node. It contains only input to the 1298 * matching. 1299 * 1300 * Unlike |RuleProcessorData|, which is similar, a |NodeMatchContext| 1301 * can vary depending on the selector matching process. In other words, 1302 * there might be multiple NodeMatchContexts corresponding to a single 1303 * node, but only one possible RuleProcessorData. 1304 */ 1305 struct NodeMatchContext { 1306 // In order to implement nsCSSRuleProcessor::HasStateDependentStyle, 1307 // we need to be able to see if a node might match an 1308 // event-state-dependent selector for any value of that event state. 1309 // So mStateMask contains the states that should NOT be tested. 1310 // 1311 // NOTE: For |mStateMask| to work correctly, it's important that any 1312 // change that changes multiple state bits include all those state 1313 // bits in the notification. Otherwise, if multiple states change but 1314 // we do separate notifications then we might determine the style is 1315 // not state-dependent when it really is (e.g., determining that a 1316 // :hover:active rule no longer matches when both states are unset). 1317 const EventStates mStateMask; 1318 1319 // Is this link the unique link whose visitedness can affect the style 1320 // of the node being matched? (That link is the nearest link to the 1321 // node being matched that is itself or an ancestor.) 1322 // 1323 // Always false when TreeMatchContext::mForStyling is false. (We 1324 // could figure it out for SelectorListMatches, but we're starting 1325 // from the middle of the selector list when doing 1326 // Has{Attribute,State}DependentStyle, so we can't tell. So when 1327 // mForStyling is false, we have to assume we don't know.) 1328 const bool mIsRelevantLink; 1329 1330 NodeMatchContext(EventStates aStateMask, bool aIsRelevantLink) 1331 : mStateMask(aStateMask) 1332 , mIsRelevantLink(aIsRelevantLink) 1333 { 1334 } 1335 }; 1336 1337 /** 1338 * Additional information about a selector (without combinators) that is 1339 * being matched. 1340 */ 1341 enum class SelectorMatchesFlags : uint8_t { 1342 NONE = 0, 1343 1344 // The selector's flags are unknown. This happens when you don't know 1345 // if you're starting from the top of a selector. Only used in cases 1346 // where it's acceptable for matching to return a false positive. 1347 // (It's not OK to return a false negative.) 1348 UNKNOWN = 1 << 0, 1349 1350 // The selector is part of a compound selector which has been split in 1351 // half, where the other half is a pseudo-element. The current 1352 // selector is not a pseudo-element itself. 1353 HAS_PSEUDO_ELEMENT = 1 << 1, 1354 1355 // The selector is part of an argument to a functional pseudo-class or 1356 // pseudo-element. 1357 IS_PSEUDO_CLASS_ARGUMENT = 1 << 2 1358 }; 1359 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SelectorMatchesFlags) 1360 1361 // Return whether the selector matches conditions for the :active and 1362 // :hover quirk. 1363 static inline bool ActiveHoverQuirkMatches(nsCSSSelector* aSelector, 1364 SelectorMatchesFlags aSelectorFlags) 1365 { 1366 if (aSelector->HasTagSelector() || aSelector->mAttrList || 1367 aSelector->mIDList || aSelector->mClassList || 1368 aSelector->IsPseudoElement() || 1369 // Having this quirk means that some selectors will no longer match, 1370 // so it's better to return false when we aren't sure (i.e., the 1371 // flags are unknown). 1372 aSelectorFlags & (SelectorMatchesFlags::UNKNOWN | 1373 SelectorMatchesFlags::HAS_PSEUDO_ELEMENT | 1374 SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) { 1375 return false; 1376 } 1377 1378 // No pseudo-class other than :active and :hover. 1379 for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList; 1380 pseudoClass; pseudoClass = pseudoClass->mNext) { 1381 if (pseudoClass->mType != CSSPseudoClassType::hover && 1382 pseudoClass->mType != CSSPseudoClassType::active) { 1383 return false; 1384 } 1385 } 1386 1387 return true; 1388 } 1389 1390 1391 static inline bool 1392 IsSignificantChild(nsIContent* aChild, bool aTextIsSignificant, 1393 bool aWhitespaceIsSignificant) 1394 { 1395 return nsStyleUtil::IsSignificantChild(aChild, aTextIsSignificant, 1396 aWhitespaceIsSignificant); 1397 } 1398 1399 // This function is to be called once we have fetched a value for an attribute 1400 // whose namespace and name match those of aAttrSelector. This function 1401 // performs comparisons on the value only, based on aAttrSelector->mFunction. 1402 static bool AttrMatchesValue(const nsAttrSelector* aAttrSelector, 1403 const nsString& aValue, bool isHTML) 1404 { 1405 NS_PRECONDITION(aAttrSelector, "Must have an attribute selector"); 1406 1407 // http://lists.w3.org/Archives/Public/www-style/2008Apr/0038.html 1408 // *= (CONTAINSMATCH) ~= (INCLUDES) ^= (BEGINSMATCH) $= (ENDSMATCH) 1409 // all accept the empty string, but match nothing. 1410 if (aAttrSelector->mValue.IsEmpty() && 1411 (aAttrSelector->mFunction == NS_ATTR_FUNC_INCLUDES || 1412 aAttrSelector->mFunction == NS_ATTR_FUNC_ENDSMATCH || 1413 aAttrSelector->mFunction == NS_ATTR_FUNC_BEGINSMATCH || 1414 aAttrSelector->mFunction == NS_ATTR_FUNC_CONTAINSMATCH)) 1415 return false; 1416 1417 const nsDefaultStringComparator defaultComparator; 1418 const nsASCIICaseInsensitiveStringComparator ciComparator; 1419 const nsStringComparator& comparator = 1420 aAttrSelector->IsValueCaseSensitive(isHTML) 1421 ? static_cast<const nsStringComparator&>(defaultComparator) 1422 : static_cast<const nsStringComparator&>(ciComparator); 1423 1424 switch (aAttrSelector->mFunction) { 1425 case NS_ATTR_FUNC_EQUALS: 1426 return aValue.Equals(aAttrSelector->mValue, comparator); 1427 case NS_ATTR_FUNC_INCLUDES: 1428 return nsStyleUtil::ValueIncludes(aValue, aAttrSelector->mValue, comparator); 1429 case NS_ATTR_FUNC_DASHMATCH: 1430 return nsStyleUtil::DashMatchCompare(aValue, aAttrSelector->mValue, comparator); 1431 case NS_ATTR_FUNC_ENDSMATCH: 1432 return StringEndsWith(aValue, aAttrSelector->mValue, comparator); 1433 case NS_ATTR_FUNC_BEGINSMATCH: 1434 return StringBeginsWith(aValue, aAttrSelector->mValue, comparator); 1435 case NS_ATTR_FUNC_CONTAINSMATCH: 1436 return FindInReadable(aAttrSelector->mValue, aValue, comparator); 1437 default: 1438 NS_NOTREACHED("Shouldn't be ending up here"); 1439 return false; 1440 } 1441 } 1442 1443 static inline bool 1444 edgeChildMatches(Element* aElement, TreeMatchContext& aTreeMatchContext, 1445 bool checkFirst, bool checkLast) 1446 { 1447 nsIContent* parent = aElement->GetParent(); 1448 if (parent && aTreeMatchContext.mForStyling) 1449 parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR); 1450 1451 return (!checkFirst || 1452 aTreeMatchContext.mNthIndexCache. 1453 GetNthIndex(aElement, false, false, true) == 1) && 1454 (!checkLast || 1455 aTreeMatchContext.mNthIndexCache. 1456 GetNthIndex(aElement, false, true, true) == 1); 1457 } 1458 1459 static inline bool 1460 nthChildGenericMatches(Element* aElement, 1461 TreeMatchContext& aTreeMatchContext, 1462 nsPseudoClassList* pseudoClass, 1463 bool isOfType, bool isFromEnd) 1464 { 1465 nsIContent* parent = aElement->GetParent(); 1466 if (parent && aTreeMatchContext.mForStyling) { 1467 if (isFromEnd) 1468 parent->SetFlags(NODE_HAS_SLOW_SELECTOR); 1469 else 1470 parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS); 1471 } 1472 1473 const int32_t index = aTreeMatchContext.mNthIndexCache. 1474 GetNthIndex(aElement, isOfType, isFromEnd, false); 1475 if (index <= 0) { 1476 // Node is anonymous content (not really a child of its parent). 1477 return false; 1478 } 1479 1480 const int32_t a = pseudoClass->u.mNumbers[0]; 1481 const int32_t b = pseudoClass->u.mNumbers[1]; 1482 // result should be true if there exists n >= 0 such that 1483 // a * n + b == index. 1484 if (a == 0) { 1485 return b == index; 1486 } 1487 1488 // Integer division in C does truncation (towards 0). So 1489 // check that the result is nonnegative, and that there was no 1490 // truncation. 1491 const CheckedInt<int32_t> indexMinusB = CheckedInt<int32_t>(index) - b; 1492 const CheckedInt<int32_t> n = indexMinusB / a; 1493 return n.isValid() && 1494 n.value() >= 0 && 1495 a * n == indexMinusB; 1496 } 1497 1498 static inline bool 1499 edgeOfTypeMatches(Element* aElement, TreeMatchContext& aTreeMatchContext, 1500 bool checkFirst, bool checkLast) 1501 { 1502 nsIContent *parent = aElement->GetParent(); 1503 if (parent && aTreeMatchContext.mForStyling) { 1504 if (checkLast) 1505 parent->SetFlags(NODE_HAS_SLOW_SELECTOR); 1506 else 1507 parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS); 1508 } 1509 1510 return (!checkFirst || 1511 aTreeMatchContext.mNthIndexCache. 1512 GetNthIndex(aElement, true, false, true) == 1) && 1513 (!checkLast || 1514 aTreeMatchContext.mNthIndexCache. 1515 GetNthIndex(aElement, true, true, true) == 1); 1516 } 1517 1518 static inline bool 1519 checkGenericEmptyMatches(Element* aElement, 1520 TreeMatchContext& aTreeMatchContext, 1521 bool isWhitespaceSignificant) 1522 { 1523 nsIContent *child = nullptr; 1524 int32_t index = -1; 1525 1526 if (aTreeMatchContext.mForStyling) 1527 aElement->SetFlags(NODE_HAS_EMPTY_SELECTOR); 1528 1529 do { 1530 child = aElement->GetChildAt(++index); 1531 // stop at first non-comment (and non-whitespace for 1532 // :-moz-only-whitespace) node 1533 } while (child && !IsSignificantChild(child, true, isWhitespaceSignificant)); 1534 return (child == nullptr); 1535 } 1536 1537 // Arrays of the states that are relevant for various pseudoclasses. 1538 static const EventStates sPseudoClassStateDependences[] = { 1539 #define CSS_PSEUDO_CLASS(_name, _value, _flags, _pref) \ 1540 EventStates(), 1541 #define CSS_STATE_DEPENDENT_PSEUDO_CLASS(_name, _value, _flags, _pref, _states) \ 1542 _states, 1543 #include "nsCSSPseudoClassList.h" 1544 #undef CSS_STATE_DEPENDENT_PSEUDO_CLASS 1545 #undef CSS_PSEUDO_CLASS 1546 // Add more entries for our fake values to make sure we can't 1547 // index out of bounds into this array no matter what. 1548 EventStates(), 1549 EventStates() 1550 }; 1551 1552 static const EventStates sPseudoClassStates[] = { 1553 #define CSS_PSEUDO_CLASS(_name, _value, _flags, _pref) \ 1554 EventStates(), 1555 #define CSS_STATE_PSEUDO_CLASS(_name, _value, _flags, _pref, _states) \ 1556 _states, 1557 #include "nsCSSPseudoClassList.h" 1558 #undef CSS_STATE_PSEUDO_CLASS 1559 #undef CSS_PSEUDO_CLASS 1560 // Add more entries for our fake values to make sure we can't 1561 // index out of bounds into this array no matter what. 1562 EventStates(), 1563 EventStates() 1564 }; 1565 static_assert(MOZ_ARRAY_LENGTH(sPseudoClassStates) == 1566 static_cast<size_t>(CSSPseudoClassType::MAX), 1567 "CSSPseudoClassType::MAX is no longer equal to the length of " 1568 "sPseudoClassStates"); 1569 1570 static bool 1571 StateSelectorMatches(Element* aElement, 1572 nsCSSSelector* aSelector, 1573 NodeMatchContext& aNodeMatchContext, 1574 TreeMatchContext& aTreeMatchContext, 1575 SelectorMatchesFlags aSelectorFlags, 1576 bool* const aDependence, 1577 EventStates aStatesToCheck) 1578 { 1579 NS_PRECONDITION(!aStatesToCheck.IsEmpty(), 1580 "should only need to call StateSelectorMatches if " 1581 "aStatesToCheck is not empty"); 1582 1583 // Bit-based pseudo-classes 1584 if (aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_ACTIVE | 1585 NS_EVENT_STATE_HOVER) && 1586 aTreeMatchContext.mCompatMode == eCompatibility_NavQuirks && 1587 ActiveHoverQuirkMatches(aSelector, aSelectorFlags) && 1588 aElement->IsHTMLElement() && !nsCSSRuleProcessor::IsLink(aElement)) { 1589 // In quirks mode, only make links sensitive to selectors ":active" 1590 // and ":hover". 1591 return false; 1592 } 1593 1594 if (aTreeMatchContext.mForStyling && 1595 aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_HOVER)) { 1596 // Mark the element as having :hover-dependent style 1597 aElement->SetHasRelevantHoverRules(); 1598 } 1599 1600 if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(aStatesToCheck)) { 1601 if (aDependence) { 1602 *aDependence = true; 1603 } 1604 } else { 1605 EventStates contentState = 1606 nsCSSRuleProcessor::GetContentStateForVisitedHandling( 1607 aElement, 1608 aTreeMatchContext, 1609 aTreeMatchContext.VisitedHandling(), 1610 aNodeMatchContext.mIsRelevantLink); 1611 if (!contentState.HasAtLeastOneOfStates(aStatesToCheck)) { 1612 return false; 1613 } 1614 } 1615 1616 return true; 1617 } 1618 1619 static bool 1620 StateSelectorMatches(Element* aElement, 1621 nsCSSSelector* aSelector, 1622 NodeMatchContext& aNodeMatchContext, 1623 TreeMatchContext& aTreeMatchContext, 1624 SelectorMatchesFlags aSelectorFlags) 1625 { 1626 for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList; 1627 pseudoClass; pseudoClass = pseudoClass->mNext) { 1628 auto idx = static_cast<CSSPseudoClassTypeBase>(pseudoClass->mType); 1629 EventStates statesToCheck = sPseudoClassStates[idx]; 1630 if (!statesToCheck.IsEmpty() && 1631 !StateSelectorMatches(aElement, aSelector, aNodeMatchContext, 1632 aTreeMatchContext, aSelectorFlags, nullptr, 1633 statesToCheck)) { 1634 return false; 1635 } 1636 } 1637 return true; 1638 } 1639 1640 // |aDependence| has two functions: 1641 // * when non-null, it indicates that we're processing a negation, 1642 // which is done only when SelectorMatches calls itself recursively 1643 // * what it points to should be set to true whenever a test is skipped 1644 // because of aNodeMatchContent.mStateMask 1645 static bool SelectorMatches(Element* aElement, 1646 nsCSSSelector* aSelector, 1647 NodeMatchContext& aNodeMatchContext, 1648 TreeMatchContext& aTreeMatchContext, 1649 SelectorMatchesFlags aSelectorFlags, 1650 bool* const aDependence = nullptr) 1651 { 1652 NS_PRECONDITION(!aSelector->IsPseudoElement(), 1653 "Pseudo-element snuck into SelectorMatches?"); 1654 MOZ_ASSERT(aTreeMatchContext.mForStyling || 1655 !aNodeMatchContext.mIsRelevantLink, 1656 "mIsRelevantLink should be set to false when mForStyling " 1657 "is false since we don't know how to set it correctly in " 1658 "Has(Attribute|State)DependentStyle"); 1659 1660 // namespace/tag match 1661 // optimization : bail out early if we can 1662 if ((kNameSpaceID_Unknown != aSelector->mNameSpace && 1663 aElement->GetNameSpaceID() != aSelector->mNameSpace)) 1664 return false; 1665 1666 if (aSelector->mLowercaseTag) { 1667 nsIAtom* selectorTag = 1668 (aTreeMatchContext.mIsHTMLDocument && aElement->IsHTMLElement()) ? 1669 aSelector->mLowercaseTag : aSelector->mCasedTag; 1670 if (selectorTag != aElement->NodeInfo()->NameAtom()) { 1671 return false; 1672 } 1673 } 1674 1675 nsAtomList* IDList = aSelector->mIDList; 1676 if (IDList) { 1677 nsIAtom* id = aElement->GetID(); 1678 if (id) { 1679 // case sensitivity: bug 93371 1680 const bool isCaseSensitive = 1681 aTreeMatchContext.mCompatMode != eCompatibility_NavQuirks; 1682 1683 if (isCaseSensitive) { 1684 do { 1685 if (IDList->mAtom != id) { 1686 return false; 1687 } 1688 IDList = IDList->mNext; 1689 } while (IDList); 1690 } else { 1691 // Use EqualsIgnoreASCIICase instead of full on unicode case conversion 1692 // in order to save on performance. This is only used in quirks mode 1693 // anyway. 1694 nsDependentAtomString id1Str(id); 1695 do { 1696 if (!nsContentUtils::EqualsIgnoreASCIICase(id1Str, 1697 nsDependentAtomString(IDList->mAtom))) { 1698 return false; 1699 } 1700 IDList = IDList->mNext; 1701 } while (IDList); 1702 } 1703 } else { 1704 // Element has no id but we have an id selector 1705 return false; 1706 } 1707 } 1708 1709 nsAtomList* classList = aSelector->mClassList; 1710 if (classList) { 1711 // test for class match 1712 const nsAttrValue *elementClasses = aElement->GetClasses(); 1713 if (!elementClasses) { 1714 // Element has no classes but we have a class selector 1715 return false; 1716 } 1717 1718 // case sensitivity: bug 93371 1719 const bool isCaseSensitive = 1720 aTreeMatchContext.mCompatMode != eCompatibility_NavQuirks; 1721 1722 while (classList) { 1723 if (!elementClasses->Contains(classList->mAtom, 1724 isCaseSensitive ? 1725 eCaseMatters : eIgnoreCase)) { 1726 return false; 1727 } 1728 classList = classList->mNext; 1729 } 1730 } 1731 1732 const bool isNegated = (aDependence != nullptr); 1733 // The selectors for which we set node bits are, unfortunately, early 1734 // in this function (because they're pseudo-classes, which are 1735 // generally quick to test, and thus earlier). If they were later, 1736 // we'd probably avoid setting those bits in more cases where setting 1737 // them is unnecessary. 1738 NS_ASSERTION(aNodeMatchContext.mStateMask.IsEmpty() || 1739 !aTreeMatchContext.mForStyling, 1740 "mForStyling must be false if we're just testing for " 1741 "state-dependence"); 1742 1743 // test for pseudo class match 1744 for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList; 1745 pseudoClass; pseudoClass = pseudoClass->mNext) { 1746 auto idx = static_cast<CSSPseudoClassTypeBase>(pseudoClass->mType); 1747 EventStates statesToCheck = sPseudoClassStates[idx]; 1748 if (statesToCheck.IsEmpty()) { 1749 // keep the cases here in the same order as the list in 1750 // nsCSSPseudoClassList.h 1751 switch (pseudoClass->mType) { 1752 case CSSPseudoClassType::empty: 1753 if (!checkGenericEmptyMatches(aElement, aTreeMatchContext, true)) { 1754 return false; 1755 } 1756 break; 1757 1758 case CSSPseudoClassType::mozOnlyWhitespace: 1759 if (!checkGenericEmptyMatches(aElement, aTreeMatchContext, false)) { 1760 return false; 1761 } 1762 break; 1763 1764 case CSSPseudoClassType::mozEmptyExceptChildrenWithLocalname: 1765 { 1766 NS_ASSERTION(pseudoClass->u.mString, "Must have string!"); 1767 nsIContent *child = nullptr; 1768 int32_t index = -1; 1769 1770 if (aTreeMatchContext.mForStyling) 1771 // FIXME: This isn't sufficient to handle: 1772 // :-moz-empty-except-children-with-localname() + E 1773 // :-moz-empty-except-children-with-localname() ~ E 1774 // because we don't know to restyle the grandparent of the 1775 // inserted/removed element (as in bug 534804 for :empty). 1776 aElement->SetFlags(NODE_HAS_SLOW_SELECTOR); 1777 do { 1778 child = aElement->GetChildAt(++index); 1779 } while (child && 1780 (!IsSignificantChild(child, true, false) || 1781 (child->GetNameSpaceID() == aElement->GetNameSpaceID() && 1782 child->NodeInfo()->NameAtom()->Equals(nsDependentString(pseudoClass->u.mString))))); 1783 if (child != nullptr) { 1784 return false; 1785 } 1786 } 1787 break; 1788 1789 case CSSPseudoClassType::lang: 1790 { 1791 NS_ASSERTION(nullptr != pseudoClass->u.mString, "null lang parameter"); 1792 if (!pseudoClass->u.mString || !*pseudoClass->u.mString) { 1793 return false; 1794 } 1795 1796 // We have to determine the language of the current element. Since 1797 // this is currently no property and since the language is inherited 1798 // from the parent we have to be prepared to look at all parent 1799 // nodes. The language itself is encoded in the LANG attribute. 1800 nsAutoString language; 1801 if (aElement->GetLang(language)) { 1802 if (!nsStyleUtil::DashMatchCompare(language, 1803 nsDependentString(pseudoClass->u.mString), 1804 nsASCIICaseInsensitiveStringComparator())) { 1805 return false; 1806 } 1807 // This pseudo-class matched; move on to the next thing 1808 break; 1809 } 1810 1811 nsIDocument* doc = aTreeMatchContext.mDocument; 1812 if (doc) { 1813 // Try to get the language from the HTTP header or if this 1814 // is missing as well from the preferences. 1815 // The content language can be a comma-separated list of 1816 // language codes. 1817 doc->GetContentLanguage(language); 1818 1819 nsDependentString langString(pseudoClass->u.mString); 1820 language.StripWhitespace(); 1821 int32_t begin = 0; 1822 int32_t len = language.Length(); 1823 while (begin < len) { 1824 int32_t end = language.FindChar(char16_t(','), begin); 1825 if (end == kNotFound) { 1826 end = len; 1827 } 1828 if (nsStyleUtil::DashMatchCompare(Substring(language, begin, 1829 end-begin), 1830 langString, 1831 nsASCIICaseInsensitiveStringComparator())) { 1832 break; 1833 } 1834 begin = end + 1; 1835 } 1836 if (begin < len) { 1837 // This pseudo-class matched 1838 break; 1839 } 1840 } 1841 } 1842 return false; 1843 1844 case CSSPseudoClassType::mozBoundElement: 1845 if (aTreeMatchContext.mScopedRoot != aElement) { 1846 return false; 1847 } 1848 break; 1849 1850 case CSSPseudoClassType::root: 1851 if (aElement != aElement->OwnerDoc()->GetRootElement()) { 1852 return false; 1853 } 1854 break; 1855 1856 case CSSPseudoClassType::any: 1857 { 1858 nsCSSSelectorList *l; 1859 for (l = pseudoClass->u.mSelectors; l; l = l->mNext) { 1860 nsCSSSelector *s = l->mSelectors; 1861 MOZ_ASSERT(!s->mNext && !s->IsPseudoElement(), 1862 "parser failed"); 1863 if (SelectorMatches( 1864 aElement, s, aNodeMatchContext, aTreeMatchContext, 1865 SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) { 1866 break; 1867 } 1868 } 1869 if (!l) { 1870 return false; 1871 } 1872 } 1873 break; 1874 1875 case CSSPseudoClassType::firstChild: 1876 if (!edgeChildMatches(aElement, aTreeMatchContext, true, false)) { 1877 return false; 1878 } 1879 break; 1880 1881 case CSSPseudoClassType::firstNode: 1882 { 1883 nsIContent *firstNode = nullptr; 1884 nsIContent *parent = aElement->GetParent(); 1885 if (parent) { 1886 if (aTreeMatchContext.mForStyling) 1887 parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR); 1888 1889 int32_t index = -1; 1890 do { 1891 firstNode = parent->GetChildAt(++index); 1892 // stop at first non-comment and non-whitespace node 1893 } while (firstNode && 1894 !IsSignificantChild(firstNode, true, false)); 1895 } 1896 if (aElement != firstNode) { 1897 return false; 1898 } 1899 } 1900 break; 1901 1902 case CSSPseudoClassType::lastChild: 1903 if (!edgeChildMatches(aElement, aTreeMatchContext, false, true)) { 1904 return false; 1905 } 1906 break; 1907 1908 case CSSPseudoClassType::lastNode: 1909 { 1910 nsIContent *lastNode = nullptr; 1911 nsIContent *parent = aElement->GetParent(); 1912 if (parent) { 1913 if (aTreeMatchContext.mForStyling) 1914 parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR); 1915 1916 uint32_t index = parent->GetChildCount(); 1917 do { 1918 lastNode = parent->GetChildAt(--index); 1919 // stop at first non-comment and non-whitespace node 1920 } while (lastNode && 1921 !IsSignificantChild(lastNode, true, false)); 1922 } 1923 if (aElement != lastNode) { 1924 return false; 1925 } 1926 } 1927 break; 1928 1929 case CSSPseudoClassType::onlyChild: 1930 if (!edgeChildMatches(aElement, aTreeMatchContext, true, true)) { 1931 return false; 1932 } 1933 break; 1934 1935 case CSSPseudoClassType::firstOfType: 1936 if (!edgeOfTypeMatches(aElement, aTreeMatchContext, true, false)) { 1937 return false; 1938 } 1939 break; 1940 1941 case CSSPseudoClassType::lastOfType: 1942 if (!edgeOfTypeMatches(aElement, aTreeMatchContext, false, true)) { 1943 return false; 1944 } 1945 break; 1946 1947 case CSSPseudoClassType::onlyOfType: 1948 if (!edgeOfTypeMatches(aElement, aTreeMatchContext, true, true)) { 1949 return false; 1950 } 1951 break; 1952 1953 case CSSPseudoClassType::nthChild: 1954 if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass, 1955 false, false)) { 1956 return false; 1957 } 1958 break; 1959 1960 case CSSPseudoClassType::nthLastChild: 1961 if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass, 1962 false, true)) { 1963 return false; 1964 } 1965 break; 1966 1967 case CSSPseudoClassType::nthOfType: 1968 if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass, 1969 true, false)) { 1970 return false; 1971 } 1972 break; 1973 1974 case CSSPseudoClassType::nthLastOfType: 1975 if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass, 1976 true, true)) { 1977 return false; 1978 } 1979 break; 1980 1981 case CSSPseudoClassType::mozIsHTML: 1982 if (!aTreeMatchContext.mIsHTMLDocument || !aElement->IsHTMLElement()) { 1983 return false; 1984 } 1985 break; 1986 1987 case CSSPseudoClassType::mozNativeAnonymous: 1988 if (!aElement->IsInNativeAnonymousSubtree()) { 1989 return false; 1990 } 1991 break; 1992 1993 case CSSPseudoClassType::mozSystemMetric: 1994 { 1995 nsCOMPtr<nsIAtom> metric = NS_Atomize(pseudoClass->u.mString); 1996 if (!nsCSSRuleProcessor::HasSystemMetric(metric)) { 1997 return false; 1998 } 1999 } 2000 break; 2001 2002 case CSSPseudoClassType::mozLocaleDir: 2003 { 2004 bool docIsRTL = 2005 aTreeMatchContext.mDocument->GetDocumentState(). 2006 HasState(NS_DOCUMENT_STATE_RTL_LOCALE); 2007 2008 nsDependentString dirString(pseudoClass->u.mString); 2009 2010 if (dirString.EqualsLiteral("rtl")) { 2011 if (!docIsRTL) { 2012 return false; 2013 } 2014 } else if (dirString.EqualsLiteral("ltr")) { 2015 if (docIsRTL) { 2016 return false; 2017 } 2018 } else { 2019 // Selectors specifying other directions never match. 2020 return false; 2021 } 2022 } 2023 break; 2024 2025 case CSSPseudoClassType::mozLWTheme: 2026 { 2027 if (aTreeMatchContext.mDocument->GetDocumentLWTheme() <= 2028 nsIDocument::Doc_Theme_None) { 2029 return false; 2030 } 2031 } 2032 break; 2033 2034 case CSSPseudoClassType::mozLWThemeBrightText: 2035 { 2036 if (aTreeMatchContext.mDocument->GetDocumentLWTheme() != 2037 nsIDocument::Doc_Theme_Bright) { 2038 return false; 2039 } 2040 } 2041 break; 2042 2043 case CSSPseudoClassType::mozLWThemeDarkText: 2044 { 2045 if (aTreeMatchContext.mDocument->GetDocumentLWTheme() != 2046 nsIDocument::Doc_Theme_Dark) { 2047 return false; 2048 } 2049 } 2050 break; 2051 2052 case CSSPseudoClassType::mozWindowInactive: 2053 if (!aTreeMatchContext.mDocument->GetDocumentState(). 2054 HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) { 2055 return false; 2056 } 2057 break; 2058 2059 case CSSPseudoClassType::mozTableBorderNonzero: 2060 { 2061 if (!aElement->IsHTMLElement(nsGkAtoms::table)) { 2062 return false; 2063 } 2064 const nsAttrValue *val = aElement->GetParsedAttr(nsGkAtoms::border); 2065 if (!val || 2066 (val->Type() == nsAttrValue::eInteger && 2067 val->GetIntegerValue() == 0)) { 2068 return false; 2069 } 2070 } 2071 break; 2072 2073 case CSSPseudoClassType::mozBrowserFrame: 2074 { 2075 nsCOMPtr<nsIMozBrowserFrame> 2076 browserFrame = do_QueryInterface(aElement); 2077 if (!browserFrame || 2078 !browserFrame->GetReallyIsBrowserOrApp()) { 2079 return false; 2080 } 2081 } 2082 break; 2083 2084 case CSSPseudoClassType::mozDir: 2085 case CSSPseudoClassType::dir: 2086 { 2087 if (aDependence) { 2088 EventStates states = sPseudoClassStateDependences[ 2089 static_cast<CSSPseudoClassTypeBase>(pseudoClass->mType)]; 2090 if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(states)) { 2091 *aDependence = true; 2092 return false; 2093 } 2094 } 2095 2096 // If we only had to consider HTML, directionality would be 2097 // exclusively LTR or RTL. 2098 // 2099 // However, in markup languages where there is no direction attribute 2100 // we have to consider the possibility that neither dir(rtl) nor 2101 // dir(ltr) matches. 2102 EventStates state = aElement->StyleState(); 2103 nsDependentString dirString(pseudoClass->u.mString); 2104 2105 if (dirString.EqualsLiteral("rtl")) { 2106 if (!state.HasState(NS_EVENT_STATE_RTL)) { 2107 return false; 2108 } 2109 } else if (dirString.EqualsLiteral("ltr")) { 2110 if (!state.HasState(NS_EVENT_STATE_LTR)) { 2111 return false; 2112 } 2113 } else { 2114 // Selectors specifying other directions never match. 2115 return false; 2116 } 2117 } 2118 break; 2119 2120 case CSSPseudoClassType::scope: 2121 if (aTreeMatchContext.mForScopedStyle) { 2122 if (aTreeMatchContext.mCurrentStyleScope) { 2123 // If mCurrentStyleScope is null, aElement must be the style 2124 // scope root. This is because the PopStyleScopeForSelectorMatching 2125 // call in SelectorMatchesTree sets mCurrentStyleScope to null 2126 // as soon as we visit the style scope element, and we won't 2127 // progress further up the tree after this call to 2128 // SelectorMatches. Thus if mCurrentStyleScope is still set, 2129 // we know the selector does not match. 2130 return false; 2131 } 2132 } else if (aTreeMatchContext.HasSpecifiedScope()) { 2133 if (!aTreeMatchContext.IsScopeElement(aElement)) { 2134 return false; 2135 } 2136 } else { 2137 if (aElement != aElement->OwnerDoc()->GetRootElement()) { 2138 return false; 2139 } 2140 } 2141 break; 2142 2143 default: 2144 MOZ_ASSERT(false, "How did that happen?"); 2145 } 2146 } else { 2147 if (!StateSelectorMatches(aElement, aSelector, aNodeMatchContext, 2148 aTreeMatchContext, aSelectorFlags, aDependence, 2149 statesToCheck)) { 2150 return false; 2151 } 2152 } 2153 } 2154 2155 bool result = true; 2156 if (aSelector->mAttrList) { 2157 // test for attribute match 2158 if (!aElement->HasAttrs()) { 2159 // if no attributes on the content, no match 2160 return false; 2161 } else { 2162 result = true; 2163 nsAttrSelector* attr = aSelector->mAttrList; 2164 nsIAtom* matchAttribute; 2165 2166 do { 2167 bool isHTML = 2168 (aTreeMatchContext.mIsHTMLDocument && aElement->IsHTMLElement()); 2169 matchAttribute = isHTML ? attr->mLowercaseAttr : attr->mCasedAttr; 2170 if (attr->mNameSpace == kNameSpaceID_Unknown) { 2171 // Attr selector with a wildcard namespace. We have to examine all 2172 // the attributes on our content node.... This sort of selector is 2173 // essentially a boolean OR, over all namespaces, of equivalent attr 2174 // selectors with those namespaces. So to evaluate whether it 2175 // matches, evaluate for each namespace (the only namespaces that 2176 // have a chance at matching, of course, are ones that the element 2177 // actually has attributes in), short-circuiting if we ever match. 2178 result = false; 2179 const nsAttrName* attrName; 2180 for (uint32_t i = 0; (attrName = aElement->GetAttrNameAt(i)); ++i) { 2181 if (attrName->LocalName() != matchAttribute) { 2182 continue; 2183 } 2184 if (attr->mFunction == NS_ATTR_FUNC_SET) { 2185 result = true; 2186 } else { 2187 nsAutoString value; 2188 #ifdef DEBUG 2189 bool hasAttr = 2190 #endif 2191 aElement->GetAttr(attrName->NamespaceID(), 2192 attrName->LocalName(), value); 2193 NS_ASSERTION(hasAttr, "GetAttrNameAt lied"); 2194 result = AttrMatchesValue(attr, value, isHTML); 2195 } 2196 2197 // At this point |result| has been set by us 2198 // explicitly in this loop. If it's false, we may still match 2199 // -- the content may have another attribute with the same name but 2200 // in a different namespace. But if it's true, we are done (we 2201 // can short-circuit the boolean OR described above). 2202 if (result) { 2203 break; 2204 } 2205 } 2206 } 2207 else if (attr->mFunction == NS_ATTR_FUNC_EQUALS) { 2208 result = 2209 aElement-> 2210 AttrValueIs(attr->mNameSpace, matchAttribute, attr->mValue, 2211 attr->IsValueCaseSensitive(isHTML) ? eCaseMatters 2212 : eIgnoreCase); 2213 } 2214 else if (!aElement->HasAttr(attr->mNameSpace, matchAttribute)) { 2215 result = false; 2216 } 2217 else if (attr->mFunction != NS_ATTR_FUNC_SET) { 2218 nsAutoString value; 2219 #ifdef DEBUG 2220 bool hasAttr = 2221 #endif 2222 aElement->GetAttr(attr->mNameSpace, matchAttribute, value); 2223 NS_ASSERTION(hasAttr, "HasAttr lied"); 2224 result = AttrMatchesValue(attr, value, isHTML); 2225 } 2226 2227 attr = attr->mNext; 2228 } while (attr && result); 2229 } 2230 } 2231 2232 // apply SelectorMatches to the negated selectors in the chain 2233 if (!isNegated) { 2234 for (nsCSSSelector *negation = aSelector->mNegations; 2235 result && negation; negation = negation->mNegations) { 2236 bool dependence = false; 2237 result = !SelectorMatches(aElement, negation, aNodeMatchContext, 2238 aTreeMatchContext, 2239 SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT, 2240 &dependence); 2241 // If the selector does match due to the dependence on 2242 // aNodeMatchContext.mStateMask, then we want to keep result true 2243 // so that the final result of SelectorMatches is true. Doing so 2244 // tells StateEnumFunc that there is a dependence on the state. 2245 result = result || dependence; 2246 } 2247 } 2248 return result; 2249 } 2250 2251 #undef STATE_CHECK 2252 2253 #ifdef DEBUG 2254 static bool 2255 HasPseudoClassSelectorArgsWithCombinators(nsCSSSelector* aSelector) 2256 { 2257 for (nsPseudoClassList* p = aSelector->mPseudoClassList; p; p = p->mNext) { 2258 if (nsCSSPseudoClasses::HasSelectorListArg(p->mType)) { 2259 for (nsCSSSelectorList* l = p->u.mSelectors; l; l = l->mNext) { 2260 if (l->mSelectors->mNext) { 2261 return true; 2262 } 2263 } 2264 } 2265 } 2266 for (nsCSSSelector* n = aSelector->mNegations; n; n = n->mNegations) { 2267 if (n->mNext) { 2268 return true; 2269 } 2270 } 2271 return false; 2272 } 2273 #endif 2274 2275 /* static */ bool 2276 nsCSSRuleProcessor::RestrictedSelectorMatches( 2277 Element* aElement, 2278 nsCSSSelector* aSelector, 2279 TreeMatchContext& aTreeMatchContext) 2280 { 2281 MOZ_ASSERT(aSelector->IsRestrictedSelector(), 2282 "aSelector must not have a pseudo-element"); 2283 2284 NS_WARNING_ASSERTION( 2285 !HasPseudoClassSelectorArgsWithCombinators(aSelector), 2286 "processing eRestyle_SomeDescendants can be slow if pseudo-classes with " 2287 "selector arguments can now have combinators in them"); 2288 2289 // We match aSelector as if :visited and :link both match visited and 2290 // unvisited links. 2291 2292 NodeMatchContext nodeContext(EventStates(), 2293 nsCSSRuleProcessor::IsLink(aElement)); 2294 if (nodeContext.mIsRelevantLink) { 2295 aTreeMatchContext.SetHaveRelevantLink(); 2296 } 2297 aTreeMatchContext.ResetForUnvisitedMatching(); 2298 bool matches = SelectorMatches(aElement, aSelector, nodeContext, 2299 aTreeMatchContext, SelectorMatchesFlags::NONE); 2300 if (nodeContext.mIsRelevantLink) { 2301 aTreeMatchContext.ResetForVisitedMatching(); 2302 if (SelectorMatches(aElement, aSelector, nodeContext, aTreeMatchContext, 2303 SelectorMatchesFlags::NONE)) { 2304 matches = true; 2305 } 2306 } 2307 return matches; 2308 } 2309 2310 // Right now, there are four operators: 2311 // ' ', the descendant combinator, is greedy 2312 // '~', the indirect adjacent sibling combinator, is greedy 2313 // '+' and '>', the direct adjacent sibling and child combinators, are not 2314 #define NS_IS_GREEDY_OPERATOR(ch) \ 2315 ((ch) == char16_t(' ') || (ch) == char16_t('~')) 2316 2317 /** 2318 * Flags for SelectorMatchesTree. 2319 */ 2320 enum SelectorMatchesTreeFlags { 2321 // Whether we still have not found the closest ancestor link element and 2322 // thus have to check the current element for it. 2323 eLookForRelevantLink = 0x1, 2324 2325 // Whether SelectorMatchesTree should check for, and return true upon 2326 // finding, an ancestor element that has an eRestyle_SomeDescendants 2327 // restyle hint pending. 2328 eMatchOnConditionalRestyleAncestor = 0x2, 2329 }; 2330 2331 static bool 2332 SelectorMatchesTree(Element* aPrevElement, 2333 nsCSSSelector* aSelector, 2334 TreeMatchContext& aTreeMatchContext, 2335 SelectorMatchesTreeFlags aFlags) 2336 { 2337 MOZ_ASSERT(!aSelector || !aSelector->IsPseudoElement()); 2338 nsCSSSelector* selector = aSelector; 2339 Element* prevElement = aPrevElement; 2340 while (selector) { // check compound selectors 2341 NS_ASSERTION(!selector->mNext || 2342 selector->mNext->mOperator != char16_t(0), 2343 "compound selector without combinator"); 2344 2345 // If after the previous selector match we are now outside the 2346 // current style scope, we don't need to match any further. 2347 if (aTreeMatchContext.mForScopedStyle && 2348 !aTreeMatchContext.IsWithinStyleScopeForSelectorMatching()) { 2349 return false; 2350 } 2351 2352 // for adjacent sibling combinators, the content to test against the 2353 // selector is the previous sibling *element* 2354 Element* element = nullptr; 2355 if (char16_t('+') == selector->mOperator || 2356 char16_t('~') == selector->mOperator) { 2357 // The relevant link must be an ancestor of the node being matched. 2358 aFlags = SelectorMatchesTreeFlags(aFlags & ~eLookForRelevantLink); 2359 nsIContent* parent = prevElement->GetParent(); 2360 if (parent) { 2361 if (aTreeMatchContext.mForStyling) 2362 parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS); 2363 2364 element = prevElement->GetPreviousElementSibling(); 2365 } 2366 } 2367 // for descendant combinators and child combinators, the element 2368 // to test against is the parent 2369 else { 2370 nsIContent *content = prevElement->GetParent(); 2371 // GetParent could return a document fragment; we only want 2372 // element parents. 2373 if (content && content->IsElement()) { 2374 element = content->AsElement(); 2375 if (aTreeMatchContext.mForScopedStyle) { 2376 // We are moving up to the parent element; tell the 2377 // TreeMatchContext, so that in case this element is the 2378 // style scope element, selector matching stops before we 2379 // traverse further up the tree. 2380 aTreeMatchContext.PopStyleScopeForSelectorMatching(element); 2381 } 2382 2383 // Compatibility hack: First try matching this selector as though the 2384 // <xbl:children> element wasn't in the tree to allow old selectors 2385 // were written before <xbl:children> participated in CSS selector 2386 // matching to work. 2387 if (selector->mOperator == '>' && element->IsActiveChildrenElement()) { 2388 Element* styleScope = aTreeMatchContext.mCurrentStyleScope; 2389 if (SelectorMatchesTree(element, selector, aTreeMatchContext, 2390 aFlags)) { 2391 // It matched, don't try matching on the <xbl:children> element at 2392 // all. 2393 return true; 2394 } 2395 // We want to reset mCurrentStyleScope on aTreeMatchContext 2396 // back to its state before the SelectorMatchesTree call, in 2397 // case that call happens to traverse past the style scope element 2398 // and sets it to null. 2399 aTreeMatchContext.mCurrentStyleScope = styleScope; 2400 } 2401 } 2402 } 2403 if (!element) { 2404 return false; 2405 } 2406 if ((aFlags & eMatchOnConditionalRestyleAncestor) && 2407 element->HasFlag(ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR)) { 2408 // If we're looking at an element that we already generated an 2409 // eRestyle_SomeDescendants restyle hint for, then we should pretend 2410 // that we matched here, because we don't know what the values of 2411 // attributes on |element| were at the time we generated the 2412 // eRestyle_SomeDescendants. This causes AttributeEnumFunc and 2413 // HasStateDependentStyle below to generate a restyle hint for the 2414 // change we're currently looking at, as we don't know whether the LHS 2415 // of the selector we looked up matches or not. (We only pass in aFlags 2416 // to cause us to look for eRestyle_SomeDescendants here under 2417 // AttributeEnumFunc and HasStateDependentStyle.) 2418 return true; 2419 } 2420 const bool isRelevantLink = (aFlags & eLookForRelevantLink) && 2421 nsCSSRuleProcessor::IsLink(element); 2422 NodeMatchContext nodeContext(EventStates(), isRelevantLink); 2423 if (isRelevantLink) { 2424 // If we find an ancestor of the matched node that is a link 2425 // during the matching process, then it's the relevant link (see 2426 // constructor call above). 2427 // Since we are still matching against selectors that contain 2428 // :visited (they'll just fail), we will always find such a node 2429 // during the selector matching process if there is a relevant 2430 // link that can influence selector matching. 2431 aFlags = SelectorMatchesTreeFlags(aFlags & ~eLookForRelevantLink); 2432 aTreeMatchContext.SetHaveRelevantLink(); 2433 } 2434 if (SelectorMatches(element, selector, nodeContext, aTreeMatchContext, 2435 SelectorMatchesFlags::NONE)) { 2436 // to avoid greedy matching, we need to recur if this is a 2437 // descendant or general sibling combinator and the next 2438 // combinator is different, but we can make an exception for 2439 // sibling, then parent, since a sibling's parent is always the 2440 // same. 2441 if (NS_IS_GREEDY_OPERATOR(selector->mOperator) && 2442 selector->mNext && 2443 selector->mNext->mOperator != selector->mOperator && 2444 !(selector->mOperator == '~' && 2445 NS_IS_ANCESTOR_OPERATOR(selector->mNext->mOperator))) { 2446 2447 // pretend the selector didn't match, and step through content 2448 // while testing the same selector 2449 2450 // This approach is slightly strange in that when it recurs 2451 // it tests from the top of the content tree, down. This 2452 // doesn't matter much for performance since most selectors 2453 // don't match. (If most did, it might be faster...) 2454 Element* styleScope = aTreeMatchContext.mCurrentStyleScope; 2455 if (SelectorMatchesTree(element, selector, aTreeMatchContext, aFlags)) { 2456 return true; 2457 } 2458 // We want to reset mCurrentStyleScope on aTreeMatchContext 2459 // back to its state before the SelectorMatchesTree call, in 2460 // case that call happens to traverse past the style scope element 2461 // and sets it to null. 2462 aTreeMatchContext.mCurrentStyleScope = styleScope; 2463 } 2464 selector = selector->mNext; 2465 } 2466 else { 2467 // for adjacent sibling and child combinators, if we didn't find 2468 // a match, we're done 2469 if (!NS_IS_GREEDY_OPERATOR(selector->mOperator)) { 2470 return false; // parent was required to match 2471 } 2472 } 2473 prevElement = element; 2474 } 2475 return true; // all the selectors matched. 2476 } 2477 2478 static inline 2479 void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector, 2480 ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext, 2481 AncestorFilter *ancestorFilter) 2482 { 2483 if (nodeContext.mIsRelevantLink) { 2484 data->mTreeMatchContext.SetHaveRelevantLink(); 2485 } 2486 if (ancestorFilter && 2487 !ancestorFilter->MightHaveMatchingAncestor<RuleValue::eMaxAncestorHashes>( 2488 value.mAncestorSelectorHashes)) { 2489 // We won't match; nothing else to do here 2490 return; 2491 } 2492 if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement, 2493 data->mScope)) { 2494 // The selector is for a rule in a scoped style sheet, and the subject 2495 // of the selector matching is not in its scope. 2496 return; 2497 } 2498 nsCSSSelector* selector = aSelector; 2499 if (selector->IsPseudoElement()) { 2500 PseudoElementRuleProcessorData* pdata = 2501 static_cast<PseudoElementRuleProcessorData*>(data); 2502 if (!pdata->mPseudoElement && selector->mPseudoClassList) { 2503 // We can get here when calling getComputedStyle(aElt, aPseudo) if: 2504 // 2505 // * aPseudo is a pseudo-element that supports a user action 2506 // pseudo-class, like "::placeholder"; 2507 // * there is a style rule that uses a pseudo-class on this 2508 // pseudo-element in the document, like ::placeholder:hover; and 2509 // * aElt does not have such a pseudo-element. 2510 // 2511 // We know that the selector can't match, since there is no element for 2512 // the user action pseudo-class to match against. 2513 return; 2514 } 2515 if (!StateSelectorMatches(pdata->mPseudoElement, aSelector, nodeContext, 2516 data->mTreeMatchContext, 2517 SelectorMatchesFlags::NONE)) { 2518 return; 2519 } 2520 selector = selector->mNext; 2521 } 2522 2523 SelectorMatchesFlags selectorFlags = SelectorMatchesFlags::NONE; 2524 if (aSelector->IsPseudoElement()) { 2525 selectorFlags |= SelectorMatchesFlags::HAS_PSEUDO_ELEMENT; 2526 } 2527 if (SelectorMatches(data->mElement, selector, nodeContext, 2528 data->mTreeMatchContext, selectorFlags)) { 2529 nsCSSSelector *next = selector->mNext; 2530 if (!next || 2531 SelectorMatchesTree(data->mElement, next, 2532 data->mTreeMatchContext, 2533 nodeContext.mIsRelevantLink ? 2534 SelectorMatchesTreeFlags(0) : 2535 eLookForRelevantLink)) { 2536 css::Declaration* declaration = value.mRule->GetDeclaration(); 2537 declaration->SetImmutable(); 2538 data->mRuleWalker->Forward(declaration); 2539 // nsStyleSet will deal with the !important rule 2540 } 2541 } 2542 } 2543 2544 /* virtual */ void 2545 nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData *aData) 2546 { 2547 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); 2548 2549 if (cascade) { 2550 NodeMatchContext nodeContext(EventStates(), 2551 nsCSSRuleProcessor::IsLink(aData->mElement)); 2552 cascade->mRuleHash.EnumerateAllRules(aData->mElement, aData, nodeContext); 2553 } 2554 } 2555 2556 /* virtual */ void 2557 nsCSSRuleProcessor::RulesMatching(PseudoElementRuleProcessorData* aData) 2558 { 2559 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); 2560 2561 if (cascade) { 2562 RuleHash* ruleHash = cascade->mPseudoElementRuleHashes[ 2563 static_cast<CSSPseudoElementTypeBase>(aData->mPseudoType)]; 2564 if (ruleHash) { 2565 NodeMatchContext nodeContext(EventStates(), 2566 nsCSSRuleProcessor::IsLink(aData->mElement)); 2567 ruleHash->EnumerateAllRules(aData->mElement, aData, nodeContext); 2568 } 2569 } 2570 } 2571 2572 /* virtual */ void 2573 nsCSSRuleProcessor::RulesMatching(AnonBoxRuleProcessorData* aData) 2574 { 2575 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); 2576 2577 if (cascade && cascade->mAnonBoxRules.EntryCount()) { 2578 auto entry = static_cast<RuleHashTagTableEntry*> 2579 (cascade->mAnonBoxRules.Search(aData->mPseudoTag)); 2580 if (entry) { 2581 nsTArray<RuleValue>& rules = entry->mRules; 2582 for (RuleValue *value = rules.Elements(), *end = value + rules.Length(); 2583 value != end; ++value) { 2584 css::Declaration* declaration = value->mRule->GetDeclaration(); 2585 declaration->SetImmutable(); 2586 aData->mRuleWalker->Forward(declaration); 2587 } 2588 } 2589 } 2590 } 2591 2592 #ifdef MOZ_XUL 2593 /* virtual */ void 2594 nsCSSRuleProcessor::RulesMatching(XULTreeRuleProcessorData* aData) 2595 { 2596 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); 2597 2598 if (cascade && cascade->mXULTreeRules.EntryCount()) { 2599 auto entry = static_cast<RuleHashTagTableEntry*> 2600 (cascade->mXULTreeRules.Search(aData->mPseudoTag)); 2601 if (entry) { 2602 NodeMatchContext nodeContext(EventStates(), 2603 nsCSSRuleProcessor::IsLink(aData->mElement)); 2604 nsTArray<RuleValue>& rules = entry->mRules; 2605 for (RuleValue *value = rules.Elements(), *end = value + rules.Length(); 2606 value != end; ++value) { 2607 if (aData->mComparator->PseudoMatches(value->mSelector)) { 2608 ContentEnumFunc(*value, value->mSelector->mNext, aData, nodeContext, 2609 nullptr); 2610 } 2611 } 2612 } 2613 } 2614 } 2615 #endif 2616 2617 static inline nsRestyleHint RestyleHintForOp(char16_t oper) 2618 { 2619 if (oper == char16_t('+') || oper == char16_t('~')) { 2620 return eRestyle_LaterSiblings; 2621 } 2622 2623 if (oper != char16_t(0)) { 2624 return eRestyle_Subtree; 2625 } 2626 2627 return eRestyle_Self; 2628 } 2629 2630 nsRestyleHint 2631 nsCSSRuleProcessor::HasStateDependentStyle(ElementDependentRuleProcessorData* aData, 2632 Element* aStatefulElement, 2633 CSSPseudoElementType aPseudoType, 2634 EventStates aStateMask) 2635 { 2636 MOZ_ASSERT(!aData->mTreeMatchContext.mForScopedStyle, 2637 "mCurrentStyleScope will need to be saved and restored after the " 2638 "SelectorMatchesTree call"); 2639 2640 bool isPseudoElement = 2641 aPseudoType != CSSPseudoElementType::NotPseudo; 2642 2643 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); 2644 2645 // Look up the content node in the state rule list, which points to 2646 // any (CSS2 definition) simple selector (whether or not it is the 2647 // subject) that has a state pseudo-class on it. This means that this 2648 // code will be matching selectors that aren't real selectors in any 2649 // stylesheet (e.g., if there is a selector "body > p:hover > a", then 2650 // "body > p:hover" will be in |cascade->mStateSelectors|). Note that 2651 // |ComputeSelectorStateDependence| below determines which selectors are in 2652 // |cascade->mStateSelectors|. 2653 nsRestyleHint hint = nsRestyleHint(0); 2654 if (cascade) { 2655 StateSelector *iter = cascade->mStateSelectors.Elements(), 2656 *end = iter + cascade->mStateSelectors.Length(); 2657 NodeMatchContext nodeContext(aStateMask, false); 2658 for(; iter != end; ++iter) { 2659 nsCSSSelector* selector = iter->mSelector; 2660 EventStates states = iter->mStates; 2661 2662 if (selector->IsPseudoElement() != isPseudoElement) { 2663 continue; 2664 } 2665 2666 nsCSSSelector* selectorForPseudo; 2667 if (isPseudoElement) { 2668 if (selector->PseudoType() != aPseudoType) { 2669 continue; 2670 } 2671 selectorForPseudo = selector; 2672 selector = selector->mNext; 2673 } 2674 2675 nsRestyleHint possibleChange = RestyleHintForOp(selector->mOperator); 2676 SelectorMatchesFlags selectorFlags = SelectorMatchesFlags::UNKNOWN; 2677 2678 // If hint already includes all the bits of possibleChange, 2679 // don't bother calling SelectorMatches, since even if it returns false 2680 // hint won't change. 2681 // Also don't bother calling SelectorMatches if none of the 2682 // states passed in are relevant here. 2683 if ((possibleChange & ~hint) && 2684 states.HasAtLeastOneOfStates(aStateMask) && 2685 // We can optimize away testing selectors that only involve :hover, a 2686 // namespace, and a tag name against nodes that don't have the 2687 // NodeHasRelevantHoverRules flag: such a selector didn't match 2688 // the tag name or namespace the first time around (since the :hover 2689 // didn't set the NodeHasRelevantHoverRules flag), so it won't 2690 // match it now. Check for our selector only having :hover states, or 2691 // the element having the hover rules flag, or the selector having 2692 // some sort of non-namespace, non-tagname data in it. 2693 (states != NS_EVENT_STATE_HOVER || 2694 aStatefulElement->HasRelevantHoverRules() || 2695 selector->mIDList || selector->mClassList || 2696 // We generally expect an mPseudoClassList, since we have a :hover. 2697 // The question is whether we have anything else in there. 2698 (selector->mPseudoClassList && 2699 (selector->mPseudoClassList->mNext || 2700 selector->mPseudoClassList->mType != 2701 CSSPseudoClassType::hover)) || 2702 selector->mAttrList || selector->mNegations) && 2703 (!isPseudoElement || 2704 StateSelectorMatches(aStatefulElement, selectorForPseudo, 2705 nodeContext, aData->mTreeMatchContext, 2706 selectorFlags, nullptr, aStateMask)) && 2707 SelectorMatches(aData->mElement, selector, nodeContext, 2708 aData->mTreeMatchContext, selectorFlags) && 2709 SelectorMatchesTree(aData->mElement, selector->mNext, 2710 aData->mTreeMatchContext, 2711 eMatchOnConditionalRestyleAncestor)) 2712 { 2713 hint = nsRestyleHint(hint | possibleChange); 2714 } 2715 } 2716 } 2717 return hint; 2718 } 2719 2720 nsRestyleHint 2721 nsCSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData) 2722 { 2723 return HasStateDependentStyle(aData, 2724 aData->mElement, 2725 CSSPseudoElementType::NotPseudo, 2726 aData->mStateMask); 2727 } 2728 2729 nsRestyleHint 2730 nsCSSRuleProcessor::HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) 2731 { 2732 return HasStateDependentStyle(aData, 2733 aData->mPseudoElement, 2734 aData->mPseudoType, 2735 aData->mStateMask); 2736 } 2737 2738 bool 2739 nsCSSRuleProcessor::HasDocumentStateDependentStyle(StateRuleProcessorData* aData) 2740 { 2741 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); 2742 2743 return cascade && cascade->mSelectorDocumentStates.HasAtLeastOneOfStates(aData->mStateMask); 2744 } 2745 2746 struct AttributeEnumData { 2747 AttributeEnumData(AttributeRuleProcessorData *aData, 2748 RestyleHintData& aRestyleHintData) 2749 : data(aData), change(nsRestyleHint(0)), hintData(aRestyleHintData) {} 2750 2751 AttributeRuleProcessorData *data; 2752 nsRestyleHint change; 2753 RestyleHintData& hintData; 2754 }; 2755 2756 2757 static inline nsRestyleHint 2758 RestyleHintForSelectorWithAttributeChange(nsRestyleHint aCurrentHint, 2759 nsCSSSelector* aSelector, 2760 nsCSSSelector* aRightmostSelector) 2761 { 2762 MOZ_ASSERT(aSelector); 2763 2764 char16_t oper = aSelector->mOperator; 2765 2766 if (oper == char16_t('+') || oper == char16_t('~')) { 2767 return eRestyle_LaterSiblings; 2768 } 2769 2770 if (oper == char16_t(':')) { 2771 return eRestyle_Subtree; 2772 } 2773 2774 if (oper != char16_t(0)) { 2775 // Check whether the selector is in a form that supports 2776 // eRestyle_SomeDescendants. If it isn't, return eRestyle_Subtree. 2777 2778 if (aCurrentHint & eRestyle_Subtree) { 2779 // No point checking, since we'll end up restyling the whole 2780 // subtree anyway. 2781 return eRestyle_Subtree; 2782 } 2783 2784 if (!aRightmostSelector) { 2785 // aSelector wasn't a top-level selector, which means we were inside 2786 // a :not() or :-moz-any(). We don't support that. 2787 return eRestyle_Subtree; 2788 } 2789 2790 MOZ_ASSERT(aSelector != aRightmostSelector, 2791 "if aSelector == aRightmostSelector then we should have " 2792 "no operator"); 2793 2794 // Check that aRightmostSelector can be passed to RestrictedSelectorMatches. 2795 if (!aRightmostSelector->IsRestrictedSelector()) { 2796 return eRestyle_Subtree; 2797 } 2798 2799 // We also don't support pseudo-elements on any of the selectors 2800 // between aRightmostSelector and aSelector. 2801 // XXX Can we lift this restriction, so that we don't have to loop 2802 // over all the selectors? 2803 for (nsCSSSelector* sel = aRightmostSelector->mNext; 2804 sel != aSelector; 2805 sel = sel->mNext) { 2806 MOZ_ASSERT(sel, "aSelector must be reachable from aRightmostSelector"); 2807 if (sel->PseudoType() != CSSPseudoElementType::NotPseudo) { 2808 return eRestyle_Subtree; 2809 } 2810 } 2811 2812 return eRestyle_SomeDescendants; 2813 } 2814 2815 return eRestyle_Self; 2816 } 2817 2818 static void 2819 AttributeEnumFunc(nsCSSSelector* aSelector, 2820 nsCSSSelector* aRightmostSelector, 2821 AttributeEnumData* aData) 2822 { 2823 AttributeRuleProcessorData *data = aData->data; 2824 2825 if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement, 2826 data->mScope)) { 2827 // The selector is for a rule in a scoped style sheet, and the subject 2828 // of the selector matching is not in its scope. 2829 return; 2830 } 2831 2832 nsRestyleHint possibleChange = 2833 RestyleHintForSelectorWithAttributeChange(aData->change, 2834 aSelector, aRightmostSelector); 2835 2836 // If, ignoring eRestyle_SomeDescendants, enumData->change already includes 2837 // all the bits of possibleChange, don't bother calling SelectorMatches, since 2838 // even if it returns false enumData->change won't change. If possibleChange 2839 // has eRestyle_SomeDescendants, we need to call SelectorMatches(Tree) 2840 // regardless as it might give us new selectors to append to 2841 // mSelectorsForDescendants. 2842 NodeMatchContext nodeContext(EventStates(), false); 2843 if (((possibleChange & (~(aData->change) | eRestyle_SomeDescendants))) && 2844 SelectorMatches(data->mElement, aSelector, nodeContext, 2845 data->mTreeMatchContext, SelectorMatchesFlags::UNKNOWN) && 2846 SelectorMatchesTree(data->mElement, aSelector->mNext, 2847 data->mTreeMatchContext, 2848 eMatchOnConditionalRestyleAncestor)) { 2849 aData->change = nsRestyleHint(aData->change | possibleChange); 2850 if (possibleChange & eRestyle_SomeDescendants) { 2851 aData->hintData.mSelectorsForDescendants.AppendElement(aRightmostSelector); 2852 } 2853 } 2854 } 2855 2856 static MOZ_ALWAYS_INLINE void 2857 EnumerateSelectors(nsTArray<SelectorPair>& aSelectors, AttributeEnumData* aData) 2858 { 2859 SelectorPair *iter = aSelectors.Elements(), 2860 *end = iter + aSelectors.Length(); 2861 for (; iter != end; ++iter) { 2862 AttributeEnumFunc(iter->mSelector, iter->mRightmostSelector, aData); 2863 } 2864 } 2865 2866 static MOZ_ALWAYS_INLINE void 2867 EnumerateSelectors(nsTArray<nsCSSSelector*>& aSelectors, AttributeEnumData* aData) 2868 { 2869 nsCSSSelector **iter = aSelectors.Elements(), 2870 **end = iter + aSelectors.Length(); 2871 for (; iter != end; ++iter) { 2872 AttributeEnumFunc(*iter, nullptr, aData); 2873 } 2874 } 2875 2876 nsRestyleHint 2877 nsCSSRuleProcessor::HasAttributeDependentStyle( 2878 AttributeRuleProcessorData* aData, 2879 RestyleHintData& aRestyleHintDataResult) 2880 { 2881 // We could try making use of aData->mModType, but :not rules make it a bit 2882 // of a pain to do so... So just ignore it for now. 2883 2884 AttributeEnumData data(aData, aRestyleHintDataResult); 2885 2886 // Don't do our special handling of certain attributes if the attr 2887 // hasn't changed yet. 2888 if (aData->mAttrHasChanged) { 2889 // check for the lwtheme and lwthemetextcolor attribute on root XUL elements 2890 if ((aData->mAttribute == nsGkAtoms::lwtheme || 2891 aData->mAttribute == nsGkAtoms::lwthemetextcolor) && 2892 aData->mElement->GetNameSpaceID() == kNameSpaceID_XUL && 2893 aData->mElement == aData->mElement->OwnerDoc()->GetRootElement()) 2894 { 2895 data.change = nsRestyleHint(data.change | eRestyle_Subtree); 2896 } 2897 2898 // We don't know the namespace of the attribute, and xml:lang applies to 2899 // all elements. If the lang attribute changes, we need to restyle our 2900 // whole subtree, since the :lang selector on our descendants can examine 2901 // our lang attribute. 2902 if (aData->mAttribute == nsGkAtoms::lang) { 2903 data.change = nsRestyleHint(data.change | eRestyle_Subtree); 2904 } 2905 } 2906 2907 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); 2908 2909 // Since we get both before and after notifications for attributes, we 2910 // don't have to ignore aData->mAttribute while matching. Just check 2911 // whether we have selectors relevant to aData->mAttribute that we 2912 // match. If this is the before change notification, that will catch 2913 // rules we might stop matching; if the after change notification, the 2914 // ones we might have started matching. 2915 if (cascade) { 2916 if (aData->mAttribute == nsGkAtoms::id) { 2917 nsIAtom* id = aData->mElement->GetID(); 2918 if (id) { 2919 auto entry = 2920 static_cast<AtomSelectorEntry*>(cascade->mIdSelectors.Search(id)); 2921 if (entry) { 2922 EnumerateSelectors(entry->mSelectors, &data); 2923 } 2924 } 2925 2926 EnumerateSelectors(cascade->mPossiblyNegatedIDSelectors, &data); 2927 } 2928 2929 if (aData->mAttribute == nsGkAtoms::_class && 2930 aData->mNameSpaceID == kNameSpaceID_None) { 2931 const nsAttrValue* otherClasses = aData->mOtherValue; 2932 NS_ASSERTION(otherClasses || 2933 aData->mModType == nsIDOMMutationEvent::REMOVAL, 2934 "All class values should be StoresOwnData and parsed" 2935 "via Element::BeforeSetAttr, so available here"); 2936 // For WillChange, enumerate classes that will be removed to see which 2937 // rules apply before the change. 2938 // For Changed, enumerate classes that have been added to see which rules 2939 // apply after the change. 2940 // In both cases we're interested in the classes that are currently on 2941 // the element but not in mOtherValue. 2942 const nsAttrValue* elementClasses = aData->mElement->GetClasses(); 2943 if (elementClasses) { 2944 int32_t atomCount = elementClasses->GetAtomCount(); 2945 if (atomCount > 0) { 2946 nsTHashtable<nsPtrHashKey<nsIAtom>> otherClassesTable; 2947 if (otherClasses) { 2948 int32_t otherClassesCount = otherClasses->GetAtomCount(); 2949 for (int32_t i = 0; i < otherClassesCount; ++i) { 2950 otherClassesTable.PutEntry(otherClasses->AtomAt(i)); 2951 } 2952 } 2953 for (int32_t i = 0; i < atomCount; ++i) { 2954 nsIAtom* curClass = elementClasses->AtomAt(i); 2955 if (!otherClassesTable.Contains(curClass)) { 2956 auto entry = 2957 static_cast<AtomSelectorEntry*> 2958 (cascade->mClassSelectors.Search(curClass)); 2959 if (entry) { 2960 EnumerateSelectors(entry->mSelectors, &data); 2961 } 2962 } 2963 } 2964 } 2965 } 2966 2967 EnumerateSelectors(cascade->mPossiblyNegatedClassSelectors, &data); 2968 } 2969 2970 auto entry = 2971 static_cast<AtomSelectorEntry*> 2972 (cascade->mAttributeSelectors.Search(aData->mAttribute)); 2973 if (entry) { 2974 EnumerateSelectors(entry->mSelectors, &data); 2975 } 2976 } 2977 2978 return data.change; 2979 } 2980 2981 /* virtual */ bool 2982 nsCSSRuleProcessor::MediumFeaturesChanged(nsPresContext* aPresContext) 2983 { 2984 // We don't want to do anything if there aren't any sets of rules 2985 // cached yet, since we should not build the rule cascade too early 2986 // (e.g., before we know whether the quirk style sheet should be 2987 // enabled). And if there's nothing cached, it doesn't matter if 2988 // anything changed. But in the cases where it does matter, we've 2989 // cached a previous cache key to test against, instead of our current 2990 // rule cascades. See bug 448281 and bug 1089417. 2991 MOZ_ASSERT(!(mRuleCascades && mPreviousCacheKey)); 2992 RuleCascadeData *old = mRuleCascades; 2993 if (old) { 2994 RefreshRuleCascade(aPresContext); 2995 return (old != mRuleCascades); 2996 } 2997 2998 if (mPreviousCacheKey) { 2999 // RefreshRuleCascade will get rid of mPreviousCacheKey anyway to 3000 // maintain the invariant that we can't have both an mRuleCascades 3001 // and an mPreviousCacheKey. But we need to hold it a little 3002 // longer. 3003 UniquePtr<nsMediaQueryResultCacheKey> previousCacheKey( 3004 Move(mPreviousCacheKey)); 3005 RefreshRuleCascade(aPresContext); 3006 3007 // This test is a bit pessimistic since the cache key's operator== 3008 // just does list comparison rather than set comparison, but it 3009 // should catch all the cases we care about (i.e., where the cascade 3010 // order hasn't changed). Other cases will do a restyle anyway, so 3011 // we shouldn't need to worry about posting a second. 3012 return !mRuleCascades || // all sheets gone, but we had sheets before 3013 mRuleCascades->mCacheKey != *previousCacheKey; 3014 } 3015 3016 return false; 3017 } 3018 3019 UniquePtr<nsMediaQueryResultCacheKey> 3020 nsCSSRuleProcessor::CloneMQCacheKey() 3021 { 3022 MOZ_ASSERT(!(mRuleCascades && mPreviousCacheKey)); 3023 3024 RuleCascadeData* c = mRuleCascades; 3025 if (!c) { 3026 // We might have an mPreviousCacheKey. It already comes from a call 3027 // to CloneMQCacheKey, so don't bother checking 3028 // HasFeatureConditions(). 3029 if (mPreviousCacheKey) { 3030 NS_ASSERTION(mPreviousCacheKey->HasFeatureConditions(), 3031 "we shouldn't have a previous cache key unless it has " 3032 "feature conditions"); 3033 return MakeUnique<nsMediaQueryResultCacheKey>(*mPreviousCacheKey); 3034 } 3035 3036 return UniquePtr<nsMediaQueryResultCacheKey>(); 3037 } 3038 3039 if (!c->mCacheKey.HasFeatureConditions()) { 3040 return UniquePtr<nsMediaQueryResultCacheKey>(); 3041 } 3042 3043 return MakeUnique<nsMediaQueryResultCacheKey>(c->mCacheKey); 3044 } 3045 3046 /* virtual */ size_t 3047 nsCSSRuleProcessor::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const 3048 { 3049 size_t n = 0; 3050 n += mSheets.ShallowSizeOfExcludingThis(aMallocSizeOf); 3051 for (RuleCascadeData* cascade = mRuleCascades; cascade; 3052 cascade = cascade->mNext) { 3053 n += cascade->SizeOfIncludingThis(aMallocSizeOf); 3054 } 3055 3056 return n; 3057 } 3058 3059 /* virtual */ size_t 3060 nsCSSRuleProcessor::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const 3061 { 3062 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 3063 } 3064 3065 // Append all the currently-active font face rules to aArray. Return 3066 // true for success and false for failure. 3067 bool 3068 nsCSSRuleProcessor::AppendFontFaceRules( 3069 nsPresContext *aPresContext, 3070 nsTArray<nsFontFaceRuleContainer>& aArray) 3071 { 3072 RuleCascadeData* cascade = GetRuleCascade(aPresContext); 3073 3074 if (cascade) { 3075 if (!aArray.AppendElements(cascade->mFontFaceRules)) 3076 return false; 3077 } 3078 3079 return true; 3080 } 3081 3082 nsCSSKeyframesRule* 3083 nsCSSRuleProcessor::KeyframesRuleForName(nsPresContext* aPresContext, 3084 const nsString& aName) 3085 { 3086 RuleCascadeData* cascade = GetRuleCascade(aPresContext); 3087 3088 if (cascade) { 3089 return cascade->mKeyframesRuleTable.Get(aName); 3090 } 3091 3092 return nullptr; 3093 } 3094 3095 nsCSSCounterStyleRule* 3096 nsCSSRuleProcessor::CounterStyleRuleForName(nsPresContext* aPresContext, 3097 const nsAString& aName) 3098 { 3099 RuleCascadeData* cascade = GetRuleCascade(aPresContext); 3100 3101 if (cascade) { 3102 return cascade->mCounterStyleRuleTable.Get(aName); 3103 } 3104 3105 return nullptr; 3106 } 3107 3108 // Append all the currently-active page rules to aArray. Return 3109 // true for success and false for failure. 3110 bool 3111 nsCSSRuleProcessor::AppendPageRules( 3112 nsPresContext* aPresContext, 3113 nsTArray<nsCSSPageRule*>& aArray) 3114 { 3115 RuleCascadeData* cascade = GetRuleCascade(aPresContext); 3116 3117 if (cascade) { 3118 if (!aArray.AppendElements(cascade->mPageRules)) { 3119 return false; 3120 } 3121 } 3122 3123 return true; 3124 } 3125 3126 bool 3127 nsCSSRuleProcessor::AppendFontFeatureValuesRules( 3128 nsPresContext *aPresContext, 3129 nsTArray<nsCSSFontFeatureValuesRule*>& aArray) 3130 { 3131 RuleCascadeData* cascade = GetRuleCascade(aPresContext); 3132 3133 if (cascade) { 3134 if (!aArray.AppendElements(cascade->mFontFeatureValuesRules)) 3135 return false; 3136 } 3137 3138 return true; 3139 } 3140 3141 nsresult 3142 nsCSSRuleProcessor::ClearRuleCascades() 3143 { 3144 if (!mPreviousCacheKey) { 3145 mPreviousCacheKey = CloneMQCacheKey(); 3146 } 3147 3148 // No need to remove the rule processor from the RuleProcessorCache here, 3149 // since CSSStyleSheet::ClearRuleCascades will have called 3150 // RuleProcessorCache::RemoveSheet() passing itself, which will catch 3151 // this rule processor (and any others for different @-moz-document 3152 // cache key results). 3153 MOZ_ASSERT(!RuleProcessorCache::HasRuleProcessor(this)); 3154 3155 #ifdef DEBUG 3156 // For shared rule processors, if we've already gathered document 3157 // rules, then they will now be out of date. We don't actually need 3158 // them to be up-to-date (see the comment in RefreshRuleCascade), so 3159 // record their invalidity so we can assert if we try to use them. 3160 if (!mMustGatherDocumentRules) { 3161 mDocumentRulesAndCacheKeyValid = false; 3162 } 3163 #endif 3164 3165 // We rely on our caller (perhaps indirectly) to do something that 3166 // will rebuild style data and the user font set (either 3167 // nsIPresShell::RestyleForCSSRuleChanges or 3168 // nsPresContext::RebuildAllStyleData). 3169 RuleCascadeData *data = mRuleCascades; 3170 mRuleCascades = nullptr; 3171 while (data) { 3172 RuleCascadeData *next = data->mNext; 3173 delete data; 3174 data = next; 3175 } 3176 return NS_OK; 3177 } 3178 3179 3180 // This function should return the set of states that this selector 3181 // depends on; this is used to implement HasStateDependentStyle. It 3182 // does NOT recur down into things like :not and :-moz-any. 3183 inline 3184 EventStates ComputeSelectorStateDependence(nsCSSSelector& aSelector) 3185 { 3186 EventStates states; 3187 for (nsPseudoClassList* pseudoClass = aSelector.mPseudoClassList; 3188 pseudoClass; pseudoClass = pseudoClass->mNext) { 3189 // Tree pseudo-elements overload mPseudoClassList for things that 3190 // aren't pseudo-classes. 3191 if (pseudoClass->mType >= CSSPseudoClassType::Count) { 3192 continue; 3193 } 3194 3195 auto idx = static_cast<CSSPseudoClassTypeBase>(pseudoClass->mType); 3196 states |= sPseudoClassStateDependences[idx]; 3197 } 3198 return states; 3199 } 3200 3201 static bool 3202 AddSelector(RuleCascadeData* aCascade, 3203 // The part between combinators at the top level of the selector 3204 nsCSSSelector* aSelectorInTopLevel, 3205 // The part we should look through (might be in :not or :-moz-any()) 3206 nsCSSSelector* aSelectorPart, 3207 // The right-most selector at the top level 3208 nsCSSSelector* aRightmostSelector) 3209 { 3210 // It's worth noting that this loop over negations isn't quite 3211 // optimal for two reasons. One, we could add something to one of 3212 // these lists twice, which means we'll check it twice, but I don't 3213 // think that's worth worrying about. (We do the same for multiple 3214 // attribute selectors on the same attribute.) Two, we don't really 3215 // need to check negations past the first in the current 3216 // implementation (and they're rare as well), but that might change 3217 // in the future if :not() is extended. 3218 for (nsCSSSelector* negation = aSelectorPart; negation; 3219 negation = negation->mNegations) { 3220 // Track both document states and attribute dependence in pseudo-classes. 3221 for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList; 3222 pseudoClass; pseudoClass = pseudoClass->mNext) { 3223 switch (pseudoClass->mType) { 3224 case CSSPseudoClassType::mozLocaleDir: { 3225 aCascade->mSelectorDocumentStates |= NS_DOCUMENT_STATE_RTL_LOCALE; 3226 break; 3227 } 3228 case CSSPseudoClassType::mozWindowInactive: { 3229 aCascade->mSelectorDocumentStates |= NS_DOCUMENT_STATE_WINDOW_INACTIVE; 3230 break; 3231 } 3232 case CSSPseudoClassType::mozTableBorderNonzero: { 3233 nsTArray<SelectorPair> *array = 3234 aCascade->AttributeListFor(nsGkAtoms::border); 3235 if (!array) { 3236 return false; 3237 } 3238 array->AppendElement(SelectorPair(aSelectorInTopLevel, 3239 aRightmostSelector)); 3240 break; 3241 } 3242 default: { 3243 break; 3244 } 3245 } 3246 } 3247 3248 // Build mStateSelectors. 3249 EventStates dependentStates = ComputeSelectorStateDependence(*negation); 3250 if (!dependentStates.IsEmpty()) { 3251 aCascade->mStateSelectors.AppendElement( 3252 nsCSSRuleProcessor::StateSelector(dependentStates, 3253 aSelectorInTopLevel)); 3254 } 3255 3256 // Build mIDSelectors 3257 if (negation == aSelectorInTopLevel) { 3258 for (nsAtomList* curID = negation->mIDList; curID; 3259 curID = curID->mNext) { 3260 auto entry = static_cast<AtomSelectorEntry*> 3261 (aCascade->mIdSelectors.Add(curID->mAtom, fallible)); 3262 if (entry) { 3263 entry->mSelectors.AppendElement(SelectorPair(aSelectorInTopLevel, 3264 aRightmostSelector)); 3265 } 3266 } 3267 } else if (negation->mIDList) { 3268 aCascade->mPossiblyNegatedIDSelectors.AppendElement(aSelectorInTopLevel); 3269 } 3270 3271 // Build mClassSelectors 3272 if (negation == aSelectorInTopLevel) { 3273 for (nsAtomList* curClass = negation->mClassList; curClass; 3274 curClass = curClass->mNext) { 3275 auto entry = static_cast<AtomSelectorEntry*> 3276 (aCascade->mClassSelectors.Add(curClass->mAtom, fallible)); 3277 if (entry) { 3278 entry->mSelectors.AppendElement(SelectorPair(aSelectorInTopLevel, 3279 aRightmostSelector)); 3280 } 3281 } 3282 } else if (negation->mClassList) { 3283 aCascade->mPossiblyNegatedClassSelectors.AppendElement(aSelectorInTopLevel); 3284 } 3285 3286 // Build mAttributeSelectors. 3287 for (nsAttrSelector *attr = negation->mAttrList; attr; 3288 attr = attr->mNext) { 3289 nsTArray<SelectorPair> *array = 3290 aCascade->AttributeListFor(attr->mCasedAttr); 3291 if (!array) { 3292 return false; 3293 } 3294 array->AppendElement(SelectorPair(aSelectorInTopLevel, 3295 aRightmostSelector)); 3296 if (attr->mLowercaseAttr != attr->mCasedAttr) { 3297 array = aCascade->AttributeListFor(attr->mLowercaseAttr); 3298 if (!array) { 3299 return false; 3300 } 3301 array->AppendElement(SelectorPair(aSelectorInTopLevel, 3302 aRightmostSelector)); 3303 } 3304 } 3305 3306 // Recur through any :-moz-any selectors 3307 for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList; 3308 pseudoClass; pseudoClass = pseudoClass->mNext) { 3309 if (pseudoClass->mType == CSSPseudoClassType::any) { 3310 for (nsCSSSelectorList *l = pseudoClass->u.mSelectors; l; l = l->mNext) { 3311 nsCSSSelector *s = l->mSelectors; 3312 if (!AddSelector(aCascade, aSelectorInTopLevel, s, 3313 aRightmostSelector)) { 3314 return false; 3315 } 3316 } 3317 } 3318 } 3319 } 3320 3321 return true; 3322 } 3323 3324 static bool 3325 AddRule(RuleSelectorPair* aRuleInfo, RuleCascadeData* aCascade) 3326 { 3327 RuleCascadeData * const cascade = aCascade; 3328 3329 // Build the rule hash. 3330 CSSPseudoElementType pseudoType = aRuleInfo->mSelector->PseudoType(); 3331 if (MOZ_LIKELY(pseudoType == CSSPseudoElementType::NotPseudo)) { 3332 cascade->mRuleHash.AppendRule(*aRuleInfo); 3333 } else if (pseudoType < CSSPseudoElementType::Count) { 3334 RuleHash*& ruleHash = cascade->mPseudoElementRuleHashes[ 3335 static_cast<CSSPseudoElementTypeBase>(pseudoType)]; 3336 if (!ruleHash) { 3337 ruleHash = new RuleHash(cascade->mQuirksMode); 3338 if (!ruleHash) { 3339 // Out of memory; give up 3340 return false; 3341 } 3342 } 3343 NS_ASSERTION(aRuleInfo->mSelector->mNext, 3344 "Must have mNext; parser screwed up"); 3345 NS_ASSERTION(aRuleInfo->mSelector->mNext->mOperator == ':', 3346 "Unexpected mNext combinator"); 3347 ruleHash->AppendRule(*aRuleInfo); 3348 } else if (pseudoType == CSSPseudoElementType::AnonBox) { 3349 NS_ASSERTION(!aRuleInfo->mSelector->mCasedTag && 3350 !aRuleInfo->mSelector->mIDList && 3351 !aRuleInfo->mSelector->mClassList && 3352 !aRuleInfo->mSelector->mPseudoClassList && 3353 !aRuleInfo->mSelector->mAttrList && 3354 !aRuleInfo->mSelector->mNegations && 3355 !aRuleInfo->mSelector->mNext && 3356 aRuleInfo->mSelector->mNameSpace == kNameSpaceID_Unknown, 3357 "Parser messed up with anon box selector"); 3358 3359 // Index doesn't matter here, since we'll just be walking these 3360 // rules in order; just pass 0. 3361 AppendRuleToTagTable(&cascade->mAnonBoxRules, 3362 aRuleInfo->mSelector->mLowercaseTag, 3363 RuleValue(*aRuleInfo, 0, aCascade->mQuirksMode)); 3364 } else { 3365 #ifdef MOZ_XUL 3366 NS_ASSERTION(pseudoType == CSSPseudoElementType::XULTree, 3367 "Unexpected pseudo type"); 3368 // Index doesn't matter here, since we'll just be walking these 3369 // rules in order; just pass 0. 3370 AppendRuleToTagTable(&cascade->mXULTreeRules, 3371 aRuleInfo->mSelector->mLowercaseTag, 3372 RuleValue(*aRuleInfo, 0, aCascade->mQuirksMode)); 3373 #else 3374 NS_NOTREACHED("Unexpected pseudo type"); 3375 #endif 3376 } 3377 3378 for (nsCSSSelector* selector = aRuleInfo->mSelector; 3379 selector; selector = selector->mNext) { 3380 if (selector->IsPseudoElement()) { 3381 CSSPseudoElementType pseudo = selector->PseudoType(); 3382 if (pseudo >= CSSPseudoElementType::Count || 3383 !nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudo)) { 3384 NS_ASSERTION(!selector->mNegations, "Shouldn't have negations"); 3385 // We do store selectors ending with pseudo-elements that allow :hover 3386 // and :active after them in the hashtables corresponding to that 3387 // selector's mNext (i.e. the thing that matches against the element), 3388 // but we want to make sure that selectors for any other kinds of 3389 // pseudo-elements don't end up in the hashtables. In particular, tree 3390 // pseudos store strange things in mPseudoClassList that we don't want 3391 // to try to match elements against. 3392 continue; 3393 } 3394 } 3395 if (!AddSelector(cascade, selector, selector, aRuleInfo->mSelector)) { 3396 return false; 3397 } 3398 } 3399 3400 return true; 3401 } 3402 3403 struct PerWeightDataListItem : public RuleSelectorPair { 3404 PerWeightDataListItem(css::StyleRule* aRule, nsCSSSelector* aSelector) 3405 : RuleSelectorPair(aRule, aSelector) 3406 , mNext(nullptr) 3407 {} 3408 // No destructor; these are arena-allocated 3409 3410 3411 // Placement new to arena allocate the PerWeightDataListItem 3412 void *operator new(size_t aSize, PLArenaPool &aArena) CPP_THROW_NEW { 3413 void *mem; 3414 PL_ARENA_ALLOCATE(mem, &aArena, aSize); 3415 return mem; 3416 } 3417 3418 PerWeightDataListItem *mNext; 3419 }; 3420 3421 struct PerWeightData { 3422 PerWeightData() 3423 : mRuleSelectorPairs(nullptr) 3424 , mTail(&mRuleSelectorPairs) 3425 {} 3426 3427 int32_t mWeight; 3428 PerWeightDataListItem *mRuleSelectorPairs; 3429 PerWeightDataListItem **mTail; 3430 }; 3431 3432 struct RuleByWeightEntry : public PLDHashEntryHdr { 3433 PerWeightData data; // mWeight is key, mRuleSelectorPairs are value 3434 }; 3435 3436 static PLDHashNumber 3437 HashIntKey(const void *key) 3438 { 3439 return PLDHashNumber(NS_PTR_TO_INT32(key)); 3440 } 3441 3442 static bool 3443 MatchWeightEntry(const PLDHashEntryHdr *hdr, const void *key) 3444 { 3445 const RuleByWeightEntry *entry = (const RuleByWeightEntry *)hdr; 3446 return entry->data.mWeight == NS_PTR_TO_INT32(key); 3447 } 3448 3449 static void 3450 InitWeightEntry(PLDHashEntryHdr *hdr, const void *key) 3451 { 3452 RuleByWeightEntry* entry = static_cast<RuleByWeightEntry*>(hdr); 3453 new (KnownNotNull, entry) RuleByWeightEntry(); 3454 } 3455 3456 static const PLDHashTableOps gRulesByWeightOps = { 3457 HashIntKey, 3458 MatchWeightEntry, 3459 PLDHashTable::MoveEntryStub, 3460 PLDHashTable::ClearEntryStub, 3461 InitWeightEntry 3462 }; 3463 3464 struct CascadeEnumData { 3465 CascadeEnumData(nsPresContext* aPresContext, 3466 nsTArray<nsFontFaceRuleContainer>& aFontFaceRules, 3467 nsTArray<nsCSSKeyframesRule*>& aKeyframesRules, 3468 nsTArray<nsCSSFontFeatureValuesRule*>& aFontFeatureValuesRules, 3469 nsTArray<nsCSSPageRule*>& aPageRules, 3470 nsTArray<nsCSSCounterStyleRule*>& aCounterStyleRules, 3471 nsTArray<css::DocumentRule*>& aDocumentRules, 3472 nsMediaQueryResultCacheKey& aKey, 3473 nsDocumentRuleResultCacheKey& aDocumentKey, 3474 SheetType aSheetType, 3475 bool aMustGatherDocumentRules) 3476 : mPresContext(aPresContext), 3477 mFontFaceRules(aFontFaceRules), 3478 mKeyframesRules(aKeyframesRules), 3479 mFontFeatureValuesRules(aFontFeatureValuesRules), 3480 mPageRules(aPageRules), 3481 mCounterStyleRules(aCounterStyleRules), 3482 mDocumentRules(aDocumentRules), 3483 mCacheKey(aKey), 3484 mDocumentCacheKey(aDocumentKey), 3485 mRulesByWeight(&gRulesByWeightOps, sizeof(RuleByWeightEntry), 32), 3486 mSheetType(aSheetType), 3487 mMustGatherDocumentRules(aMustGatherDocumentRules) 3488 { 3489 // Initialize our arena 3490 PL_INIT_ARENA_POOL(&mArena, "CascadeEnumDataArena", 3491 NS_CASCADEENUMDATA_ARENA_BLOCK_SIZE); 3492 } 3493 3494 ~CascadeEnumData() 3495 { 3496 PL_FinishArenaPool(&mArena); 3497 } 3498 3499 nsPresContext* mPresContext; 3500 nsTArray<nsFontFaceRuleContainer>& mFontFaceRules; 3501 nsTArray<nsCSSKeyframesRule*>& mKeyframesRules; 3502 nsTArray<nsCSSFontFeatureValuesRule*>& mFontFeatureValuesRules; 3503 nsTArray<nsCSSPageRule*>& mPageRules; 3504 nsTArray<nsCSSCounterStyleRule*>& mCounterStyleRules; 3505 nsTArray<css::DocumentRule*>& mDocumentRules; 3506 nsMediaQueryResultCacheKey& mCacheKey; 3507 nsDocumentRuleResultCacheKey& mDocumentCacheKey; 3508 PLArenaPool mArena; 3509 // Hooray, a manual PLDHashTable since nsClassHashtable doesn't 3510 // provide a getter that gives me a *reference* to the value. 3511 PLDHashTable mRulesByWeight; // of PerWeightDataListItem linked lists 3512 SheetType mSheetType; 3513 bool mMustGatherDocumentRules; 3514 }; 3515 3516 /** 3517 * Recursively traverses rules in order to: 3518 * (1) add any @-moz-document rules into data->mDocumentRules. 3519 * (2) record any @-moz-document rules whose conditions evaluate to true 3520 * on data->mDocumentCacheKey. 3521 * 3522 * See also CascadeRuleEnumFunc below, which calls us via 3523 * EnumerateRulesForwards. If modifying this function you may need to 3524 * update CascadeRuleEnumFunc too. 3525 */ 3526 static bool 3527 GatherDocRuleEnumFunc(css::Rule* aRule, void* aData) 3528 { 3529 CascadeEnumData* data = (CascadeEnumData*)aData; 3530 int32_t type = aRule->GetType(); 3531 3532 MOZ_ASSERT(data->mMustGatherDocumentRules, 3533 "should only call GatherDocRuleEnumFunc if " 3534 "mMustGatherDocumentRules is true"); 3535 3536 if (css::Rule::MEDIA_RULE == type || 3537 css::Rule::SUPPORTS_RULE == type) { 3538 css::GroupRule* groupRule = static_cast<css::GroupRule*>(aRule); 3539 if (!groupRule->EnumerateRulesForwards(GatherDocRuleEnumFunc, aData)) { 3540 return false; 3541 } 3542 } 3543 else if (css::Rule::DOCUMENT_RULE == type) { 3544 css::DocumentRule* docRule = static_cast<css::DocumentRule*>(aRule); 3545 if (!data->mDocumentRules.AppendElement(docRule)) { 3546 return false; 3547 } 3548 if (docRule->UseForPresentation(data->mPresContext)) { 3549 if (!data->mDocumentCacheKey.AddMatchingRule(docRule)) { 3550 return false; 3551 } 3552 } 3553 if (!docRule->EnumerateRulesForwards(GatherDocRuleEnumFunc, aData)) { 3554 return false; 3555 } 3556 } 3557 return true; 3558 } 3559 3560 /* 3561 * This enumerates style rules in a sheet (and recursively into any 3562 * grouping rules) in order to: 3563 * (1) add any style rules, in order, into data->mRulesByWeight (for 3564 * the primary CSS cascade), where they are separated by weight 3565 * but kept in order per-weight, and 3566 * (2) add any @font-face rules, in order, into data->mFontFaceRules. 3567 * (3) add any @keyframes rules, in order, into data->mKeyframesRules. 3568 * (4) add any @font-feature-value rules, in order, 3569 * into data->mFontFeatureValuesRules. 3570 * (5) add any @page rules, in order, into data->mPageRules. 3571 * (6) add any @counter-style rules, in order, into data->mCounterStyleRules. 3572 * (7) add any @-moz-document rules into data->mDocumentRules. 3573 * (8) record any @-moz-document rules whose conditions evaluate to true 3574 * on data->mDocumentCacheKey. 3575 * 3576 * See also GatherDocRuleEnumFunc above, which we call to traverse into 3577 * @-moz-document rules even if their (or an ancestor's) condition 3578 * fails. This means we might look at the result of some @-moz-document 3579 * rules that don't actually affect whether a RuleProcessorCache lookup 3580 * is a hit or a miss. The presence of @-moz-document rules inside 3581 * @media etc. rules should be rare, and looking at all of them in the 3582 * sheets lets us avoid the complication of having different document 3583 * cache key results for different media. 3584 * 3585 * If modifying this function you may need to update 3586 * GatherDocRuleEnumFunc too. 3587 */ 3588 static bool 3589 CascadeRuleEnumFunc(css::Rule* aRule, void* aData) 3590 { 3591 CascadeEnumData* data = (CascadeEnumData*)aData; 3592 int32_t type = aRule->GetType(); 3593 3594 if (css::Rule::STYLE_RULE == type) { 3595 css::StyleRule* styleRule = static_cast<css::StyleRule*>(aRule); 3596 3597 for (nsCSSSelectorList *sel = styleRule->Selector(); 3598 sel; sel = sel->mNext) { 3599 int32_t weight = sel->mWeight; 3600 auto entry = static_cast<RuleByWeightEntry*> 3601 (data->mRulesByWeight.Add(NS_INT32_TO_PTR(weight), fallible)); 3602 if (!entry) 3603 return false; 3604 entry->data.mWeight = weight; 3605 // entry->data.mRuleSelectorPairs should be linked in forward order; 3606 // entry->data.mTail is the slot to write to. 3607 auto* newItem = 3608 new (data->mArena) PerWeightDataListItem(styleRule, sel->mSelectors); 3609 if (newItem) { 3610 *(entry->data.mTail) = newItem; 3611 entry->data.mTail = &newItem->mNext; 3612 } 3613 } 3614 } 3615 else if (css::Rule::MEDIA_RULE == type || 3616 css::Rule::SUPPORTS_RULE == type) { 3617 css::GroupRule* groupRule = static_cast<css::GroupRule*>(aRule); 3618 const bool use = 3619 groupRule->UseForPresentation(data->mPresContext, data->mCacheKey); 3620 if (use || data->mMustGatherDocumentRules) { 3621 if (!groupRule->EnumerateRulesForwards(use ? CascadeRuleEnumFunc : 3622 GatherDocRuleEnumFunc, 3623 aData)) { 3624 return false; 3625 } 3626 } 3627 } 3628 else if (css::Rule::DOCUMENT_RULE == type) { 3629 css::DocumentRule* docRule = static_cast<css::DocumentRule*>(aRule); 3630 if (data->mMustGatherDocumentRules) { 3631 if (!data->mDocumentRules.AppendElement(docRule)) { 3632 return false; 3633 } 3634 } 3635 const bool use = docRule->UseForPresentation(data->mPresContext); 3636 if (use && data->mMustGatherDocumentRules) { 3637 if (!data->mDocumentCacheKey.AddMatchingRule(docRule)) { 3638 return false; 3639 } 3640 } 3641 if (use || data->mMustGatherDocumentRules) { 3642 if (!docRule->EnumerateRulesForwards(use ? CascadeRuleEnumFunc 3643 : GatherDocRuleEnumFunc, 3644 aData)) { 3645 return false; 3646 } 3647 } 3648 } 3649 else if (css::Rule::FONT_FACE_RULE == type) { 3650 nsCSSFontFaceRule *fontFaceRule = static_cast<nsCSSFontFaceRule*>(aRule); 3651 nsFontFaceRuleContainer *ptr = data->mFontFaceRules.AppendElement(); 3652 if (!ptr) 3653 return false; 3654 ptr->mRule = fontFaceRule; 3655 ptr->mSheetType = data->mSheetType; 3656 } 3657 else if (css::Rule::KEYFRAMES_RULE == type) { 3658 nsCSSKeyframesRule *keyframesRule = 3659 static_cast<nsCSSKeyframesRule*>(aRule); 3660 if (!data->mKeyframesRules.AppendElement(keyframesRule)) { 3661 return false; 3662 } 3663 } 3664 else if (css::Rule::FONT_FEATURE_VALUES_RULE == type) { 3665 nsCSSFontFeatureValuesRule *fontFeatureValuesRule = 3666 static_cast<nsCSSFontFeatureValuesRule*>(aRule); 3667 if (!data->mFontFeatureValuesRules.AppendElement(fontFeatureValuesRule)) { 3668 return false; 3669 } 3670 } 3671 else if (css::Rule::PAGE_RULE == type) { 3672 nsCSSPageRule* pageRule = static_cast<nsCSSPageRule*>(aRule); 3673 if (!data->mPageRules.AppendElement(pageRule)) { 3674 return false; 3675 } 3676 } 3677 else if (css::Rule::COUNTER_STYLE_RULE == type) { 3678 nsCSSCounterStyleRule* counterStyleRule = 3679 static_cast<nsCSSCounterStyleRule*>(aRule); 3680 if (!data->mCounterStyleRules.AppendElement(counterStyleRule)) { 3681 return false; 3682 } 3683 } 3684 return true; 3685 } 3686 3687 /* static */ bool 3688 nsCSSRuleProcessor::CascadeSheet(CSSStyleSheet* aSheet, CascadeEnumData* aData) 3689 { 3690 if (aSheet->IsApplicable() && 3691 aSheet->UseForPresentation(aData->mPresContext, aData->mCacheKey) && 3692 aSheet->mInner) { 3693 CSSStyleSheet* child = aSheet->mInner->mFirstChild; 3694 while (child) { 3695 CascadeSheet(child, aData); 3696 child = child->mNext; 3697 } 3698 3699 if (!aSheet->mInner->mOrderedRules.EnumerateForwards(CascadeRuleEnumFunc, 3700 aData)) 3701 return false; 3702 } 3703 return true; 3704 } 3705 3706 static int CompareWeightData(const void* aArg1, const void* aArg2, 3707 void* closure) 3708 { 3709 const PerWeightData* arg1 = static_cast<const PerWeightData*>(aArg1); 3710 const PerWeightData* arg2 = static_cast<const PerWeightData*>(aArg2); 3711 return arg1->mWeight - arg2->mWeight; // put lower weight first 3712 } 3713 3714 RuleCascadeData* 3715 nsCSSRuleProcessor::GetRuleCascade(nsPresContext* aPresContext) 3716 { 3717 // FIXME: Make this infallible! 3718 3719 // If anything changes about the presentation context, we will be 3720 // notified. Otherwise, our cache is valid if mLastPresContext 3721 // matches aPresContext. (The only rule processors used for multiple 3722 // pres contexts are for XBL. These rule processors are probably less 3723 // likely to have @media rules, and thus the cache is pretty likely to 3724 // hit instantly even when we're switching between pres contexts.) 3725 3726 if (!mRuleCascades || aPresContext != mLastPresContext) { 3727 RefreshRuleCascade(aPresContext); 3728 } 3729 mLastPresContext = aPresContext; 3730 3731 return mRuleCascades; 3732 } 3733 3734 void 3735 nsCSSRuleProcessor::RefreshRuleCascade(nsPresContext* aPresContext) 3736 { 3737 // Having RuleCascadeData objects be per-medium (over all variation 3738 // caused by media queries, handled through mCacheKey) works for now 3739 // since nsCSSRuleProcessor objects are per-document. (For a given 3740 // set of stylesheets they can vary based on medium (@media) or 3741 // document (@-moz-document).) 3742 3743 for (RuleCascadeData **cascadep = &mRuleCascades, *cascade; 3744 (cascade = *cascadep); cascadep = &cascade->mNext) { 3745 if (cascade->mCacheKey.Matches(aPresContext)) { 3746 // Ensure that the current one is always mRuleCascades. 3747 *cascadep = cascade->mNext; 3748 cascade->mNext = mRuleCascades; 3749 mRuleCascades = cascade; 3750 3751 return; 3752 } 3753 } 3754 3755 // We're going to make a new rule cascade; this means that we should 3756 // now stop using the previous cache key that we're holding on to from 3757 // the last time we had rule cascades. 3758 mPreviousCacheKey = nullptr; 3759 3760 if (mSheets.Length() != 0) { 3761 nsAutoPtr<RuleCascadeData> newCascade( 3762 new RuleCascadeData(aPresContext->Medium(), 3763 eCompatibility_NavQuirks == aPresContext->CompatibilityMode())); 3764 if (newCascade) { 3765 CascadeEnumData data(aPresContext, newCascade->mFontFaceRules, 3766 newCascade->mKeyframesRules, 3767 newCascade->mFontFeatureValuesRules, 3768 newCascade->mPageRules, 3769 newCascade->mCounterStyleRules, 3770 mDocumentRules, 3771 newCascade->mCacheKey, 3772 mDocumentCacheKey, 3773 mSheetType, 3774 mMustGatherDocumentRules); 3775 3776 for (uint32_t i = 0; i < mSheets.Length(); ++i) { 3777 if (!CascadeSheet(mSheets.ElementAt(i), &data)) 3778 return; /* out of memory */ 3779 } 3780 3781 // Sort the hash table of per-weight linked lists by weight. 3782 uint32_t weightCount = data.mRulesByWeight.EntryCount(); 3783 auto weightArray = MakeUnique<PerWeightData[]>(weightCount); 3784 int32_t j = 0; 3785 for (auto iter = data.mRulesByWeight.Iter(); !iter.Done(); iter.Next()) { 3786 auto entry = static_cast<const RuleByWeightEntry*>(iter.Get()); 3787 weightArray[j++] = entry->data; 3788 } 3789 NS_QuickSort(weightArray.get(), weightCount, sizeof(PerWeightData), 3790 CompareWeightData, nullptr); 3791 3792 // Put things into the rule hash. 3793 // The primary sort is by weight... 3794 for (uint32_t i = 0; i < weightCount; ++i) { 3795 // and the secondary sort is by order. mRuleSelectorPairs is already in 3796 // the right order.. 3797 for (PerWeightDataListItem *cur = weightArray[i].mRuleSelectorPairs; 3798 cur; 3799 cur = cur->mNext) { 3800 if (!AddRule(cur, newCascade)) 3801 return; /* out of memory */ 3802 } 3803 } 3804 3805 // Build mKeyframesRuleTable. 3806 for (nsTArray<nsCSSKeyframesRule*>::size_type i = 0, 3807 iEnd = newCascade->mKeyframesRules.Length(); i < iEnd; ++i) { 3808 nsCSSKeyframesRule* rule = newCascade->mKeyframesRules[i]; 3809 newCascade->mKeyframesRuleTable.Put(rule->GetName(), rule); 3810 } 3811 3812 // Build mCounterStyleRuleTable 3813 for (nsTArray<nsCSSCounterStyleRule*>::size_type i = 0, 3814 iEnd = newCascade->mCounterStyleRules.Length(); i < iEnd; ++i) { 3815 nsCSSCounterStyleRule* rule = newCascade->mCounterStyleRules[i]; 3816 newCascade->mCounterStyleRuleTable.Put(rule->GetName(), rule); 3817 } 3818 3819 // mMustGatherDocumentRules controls whether we build mDocumentRules 3820 // and mDocumentCacheKey so that they can be used as keys by the 3821 // RuleProcessorCache, as obtained by TakeDocumentRulesAndCacheKey 3822 // later. We set it to false just below so that we only do this 3823 // the first time we build a RuleProcessorCache for a shared rule 3824 // processor. 3825 // 3826 // An up-to-date mDocumentCacheKey is only needed if we 3827 // are still in the RuleProcessorCache (as we store a copy of the 3828 // cache key in the RuleProcessorCache), and an up-to-date 3829 // mDocumentRules is only needed at the time TakeDocumentRulesAndCacheKey 3830 // is called, which is immediately after the rule processor is created 3831 // (by nsStyleSet). 3832 // 3833 // Note that when nsCSSRuleProcessor::ClearRuleCascades is called, 3834 // by CSSStyleSheet::ClearRuleCascades, we will have called 3835 // RuleProcessorCache::RemoveSheet, which will remove the rule 3836 // processor from the cache. (This is because the list of document 3837 // rules now may not match the one used as they key in the 3838 // RuleProcessorCache.) 3839 // 3840 // Thus, as we'll no longer be in the RuleProcessorCache, and we won't 3841 // have TakeDocumentRulesAndCacheKey called on us, we don't need to ensure 3842 // mDocumentCacheKey and mDocumentRules are up-to-date after the 3843 // first time GetRuleCascade is called. 3844 if (mMustGatherDocumentRules) { 3845 mDocumentRules.Sort(); 3846 mDocumentCacheKey.Finalize(); 3847 mMustGatherDocumentRules = false; 3848 #ifdef DEBUG 3849 mDocumentRulesAndCacheKeyValid = true; 3850 #endif 3851 } 3852 3853 // Ensure that the current one is always mRuleCascades. 3854 newCascade->mNext = mRuleCascades; 3855 mRuleCascades = newCascade.forget(); 3856 } 3857 } 3858 return; 3859 } 3860 3861 /* static */ bool 3862 nsCSSRuleProcessor::SelectorListMatches(Element* aElement, 3863 TreeMatchContext& aTreeMatchContext, 3864 nsCSSSelectorList* aSelectorList) 3865 { 3866 MOZ_ASSERT(!aTreeMatchContext.mForScopedStyle, 3867 "mCurrentStyleScope will need to be saved and restored after the " 3868 "SelectorMatchesTree call"); 3869 3870 while (aSelectorList) { 3871 nsCSSSelector* sel = aSelectorList->mSelectors; 3872 NS_ASSERTION(sel, "Should have *some* selectors"); 3873 NS_ASSERTION(!sel->IsPseudoElement(), "Shouldn't have been called"); 3874 NodeMatchContext nodeContext(EventStates(), false); 3875 if (SelectorMatches(aElement, sel, nodeContext, aTreeMatchContext, 3876 SelectorMatchesFlags::NONE)) { 3877 nsCSSSelector* next = sel->mNext; 3878 if (!next || 3879 SelectorMatchesTree(aElement, next, aTreeMatchContext, 3880 SelectorMatchesTreeFlags(0))) { 3881 return true; 3882 } 3883 } 3884 3885 aSelectorList = aSelectorList->mNext; 3886 } 3887 3888 return false; 3889 } 3890 3891 void 3892 nsCSSRuleProcessor::TakeDocumentRulesAndCacheKey( 3893 nsPresContext* aPresContext, 3894 nsTArray<css::DocumentRule*>& aDocumentRules, 3895 nsDocumentRuleResultCacheKey& aCacheKey) 3896 { 3897 MOZ_ASSERT(mIsShared); 3898 3899 GetRuleCascade(aPresContext); 3900 MOZ_ASSERT(mDocumentRulesAndCacheKeyValid); 3901 3902 aDocumentRules.Clear(); 3903 aDocumentRules.SwapElements(mDocumentRules); 3904 aCacheKey.Swap(mDocumentCacheKey); 3905 3906 #ifdef DEBUG 3907 mDocumentRulesAndCacheKeyValid = false; 3908 #endif 3909 } 3910 3911 void 3912 nsCSSRuleProcessor::AddStyleSetRef() 3913 { 3914 MOZ_ASSERT(mIsShared); 3915 if (++mStyleSetRefCnt == 1) { 3916 RuleProcessorCache::StopTracking(this); 3917 } 3918 } 3919 3920 void 3921 nsCSSRuleProcessor::ReleaseStyleSetRef() 3922 { 3923 MOZ_ASSERT(mIsShared); 3924 MOZ_ASSERT(mStyleSetRefCnt > 0); 3925 if (--mStyleSetRefCnt == 0 && mInRuleProcessorCache) { 3926 RuleProcessorCache::StartTracking(this); 3927 } 3928 } 3929 3930 // TreeMatchContext and AncestorFilter out of line methods 3931 void 3932 TreeMatchContext::InitAncestors(Element *aElement) 3933 { 3934 MOZ_ASSERT(!mAncestorFilter.mFilter); 3935 MOZ_ASSERT(mAncestorFilter.mHashes.IsEmpty()); 3936 MOZ_ASSERT(mStyleScopes.IsEmpty()); 3937 3938 mAncestorFilter.mFilter = new AncestorFilter::Filter(); 3939 3940 if (MOZ_LIKELY(aElement)) { 3941 MOZ_ASSERT(aElement->GetUncomposedDoc() || 3942 aElement->HasFlag(NODE_IS_IN_SHADOW_TREE), 3943 "aElement must be in the document or in shadow tree " 3944 "for the assumption that GetParentNode() is non-null " 3945 "on all element ancestors of aElement to be true"); 3946 // Collect up the ancestors 3947 AutoTArray<Element*, 50> ancestors; 3948 Element* cur = aElement; 3949 do { 3950 ancestors.AppendElement(cur); 3951 cur = cur->GetParentElementCrossingShadowRoot(); 3952 } while (cur); 3953 3954 // Now push them in reverse order. 3955 for (uint32_t i = ancestors.Length(); i-- != 0; ) { 3956 mAncestorFilter.PushAncestor(ancestors[i]); 3957 PushStyleScope(ancestors[i]); 3958 } 3959 } 3960 } 3961 3962 void 3963 TreeMatchContext::InitStyleScopes(Element* aElement) 3964 { 3965 MOZ_ASSERT(mStyleScopes.IsEmpty()); 3966 3967 if (MOZ_LIKELY(aElement)) { 3968 // Collect up the ancestors 3969 AutoTArray<Element*, 50> ancestors; 3970 Element* cur = aElement; 3971 do { 3972 ancestors.AppendElement(cur); 3973 cur = cur->GetParentElementCrossingShadowRoot(); 3974 } while (cur); 3975 3976 // Now push them in reverse order. 3977 for (uint32_t i = ancestors.Length(); i-- != 0; ) { 3978 PushStyleScope(ancestors[i]); 3979 } 3980 } 3981 } 3982 3983 void 3984 AncestorFilter::PushAncestor(Element *aElement) 3985 { 3986 MOZ_ASSERT(mFilter); 3987 3988 uint32_t oldLength = mHashes.Length(); 3989 3990 mPopTargets.AppendElement(oldLength); 3991 #ifdef DEBUG 3992 mElements.AppendElement(aElement); 3993 #endif 3994 mHashes.AppendElement(aElement->NodeInfo()->NameAtom()->hash()); 3995 nsIAtom *id = aElement->GetID(); 3996 if (id) { 3997 mHashes.AppendElement(id->hash()); 3998 } 3999 const nsAttrValue *classes = aElement->GetClasses(); 4000 if (classes) { 4001 uint32_t classCount = classes->GetAtomCount(); 4002 for (uint32_t i = 0; i < classCount; ++i) { 4003 mHashes.AppendElement(classes->AtomAt(i)->hash()); 4004 } 4005 } 4006 4007 uint32_t newLength = mHashes.Length(); 4008 for (uint32_t i = oldLength; i < newLength; ++i) { 4009 mFilter->add(mHashes[i]); 4010 } 4011 } 4012 4013 void 4014 AncestorFilter::PopAncestor() 4015 { 4016 MOZ_ASSERT(!mPopTargets.IsEmpty()); 4017 MOZ_ASSERT(mPopTargets.Length() == mElements.Length()); 4018 4019 uint32_t popTargetLength = mPopTargets.Length(); 4020 uint32_t newLength = mPopTargets[popTargetLength-1]; 4021 4022 mPopTargets.TruncateLength(popTargetLength-1); 4023 #ifdef DEBUG 4024 mElements.TruncateLength(popTargetLength-1); 4025 #endif 4026 4027 uint32_t oldLength = mHashes.Length(); 4028 for (uint32_t i = newLength; i < oldLength; ++i) { 4029 mFilter->remove(mHashes[i]); 4030 } 4031 mHashes.TruncateLength(newLength); 4032 } 4033 4034 #ifdef DEBUG 4035 void 4036 AncestorFilter::AssertHasAllAncestors(Element *aElement) const 4037 { 4038 Element* cur = aElement->GetParentElementCrossingShadowRoot(); 4039 while (cur) { 4040 MOZ_ASSERT(mElements.Contains(cur)); 4041 cur = cur->GetParentElementCrossingShadowRoot(); 4042 } 4043 } 4044 4045 void 4046 TreeMatchContext::AssertHasAllStyleScopes(Element* aElement) const 4047 { 4048 if (aElement->IsInNativeAnonymousSubtree()) { 4049 // Document style sheets are never applied to native anonymous content, 4050 // so it's not possible for them to be in a <style scoped> scope. 4051 return; 4052 } 4053 Element* cur = aElement->GetParentElementCrossingShadowRoot(); 4054 while (cur) { 4055 if (cur->IsScopedStyleRoot()) { 4056 MOZ_ASSERT(mStyleScopes.Contains(cur)); 4057 } 4058 cur = cur->GetParentElementCrossingShadowRoot(); 4059 } 4060 } 4061 #endif 4062