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