layout/style/nsCSSRuleProcessor.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     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/. */
     7 /*
     8  * style rule processor for CSS style sheets, responsible for selector
     9  * matching and cascading
    10  */
    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"
    19 #include "nsCSSRuleProcessor.h"
    20 #include "nsRuleProcessorData.h"
    21 #include <algorithm>
    22 #include "nsIAtom.h"
    23 #include "pldhash.h"
    24 #include "nsICSSPseudoComparator.h"
    25 #include "mozilla/MemoryReporting.h"
    26 #include "mozilla/css/StyleRule.h"
    27 #include "mozilla/css/GroupRule.h"
    28 #include "nsIDocument.h"
    29 #include "nsPresContext.h"
    30 #include "nsGkAtoms.h"
    31 #include "nsUnicharUtils.h"
    32 #include "nsError.h"
    33 #include "nsRuleWalker.h"
    34 #include "nsCSSPseudoClasses.h"
    35 #include "nsCSSPseudoElements.h"
    36 #include "nsIContent.h"
    37 #include "nsCOMPtr.h"
    38 #include "nsHashKeys.h"
    39 #include "nsStyleUtil.h"
    40 #include "nsQuickSort.h"
    41 #include "nsAttrValue.h"
    42 #include "nsAttrValueInlines.h"
    43 #include "nsAttrName.h"
    44 #include "nsTArray.h"
    45 #include "nsContentUtils.h"
    46 #include "nsIMediaList.h"
    47 #include "nsCSSRules.h"
    48 #include "nsStyleSet.h"
    49 #include "mozilla/dom/Element.h"
    50 #include "nsNthIndexCache.h"
    51 #include "mozilla/ArrayUtils.h"
    52 #include "mozilla/EventStates.h"
    53 #include "mozilla/Preferences.h"
    54 #include "mozilla/LookAndFeel.h"
    55 #include "mozilla/Likely.h"
    57 using namespace mozilla;
    58 using namespace mozilla::dom;
    60 #define VISITED_PSEUDO_PREF "layout.css.visited_links_enabled"
    62 static bool gSupportVisitedPseudo = true;
    64 static nsTArray< nsCOMPtr<nsIAtom> >* sSystemMetrics = 0;
    66 #ifdef XP_WIN
    67 uint8_t nsCSSRuleProcessor::sWinThemeId = LookAndFeel::eWindowsTheme_Generic;
    68 #endif
    70 /**
    71  * A struct representing a given CSS rule and a particular selector
    72  * from that rule's selector list.
    73  */
    74 struct RuleSelectorPair {
    75   RuleSelectorPair(css::StyleRule* aRule, nsCSSSelector* aSelector)
    76     : mRule(aRule), mSelector(aSelector) {}
    77   // If this class ever grows a destructor, deal with
    78   // PerWeightDataListItem appropriately.
    80   css::StyleRule*   mRule;
    81   nsCSSSelector*    mSelector; // which of |mRule|'s selectors
    82 };
    84 #define NS_IS_ANCESTOR_OPERATOR(ch) \
    85   ((ch) == char16_t(' ') || (ch) == char16_t('>'))
    87 /**
    88  * A struct representing a particular rule in an ordered list of rules
    89  * (the ordering depending on the weight of mSelector and the order of
    90  * our rules to start with).
    91  */
    92 struct RuleValue : RuleSelectorPair {
    93   enum {
    94     eMaxAncestorHashes = 4
    95   };
    97   RuleValue(const RuleSelectorPair& aRuleSelectorPair, int32_t aIndex,
    98             bool aQuirksMode) :
    99     RuleSelectorPair(aRuleSelectorPair),
   100     mIndex(aIndex)
   101   {
   102     CollectAncestorHashes(aQuirksMode);
   103   }
   105   int32_t mIndex; // High index means high weight/order.
   106   uint32_t mAncestorSelectorHashes[eMaxAncestorHashes];
   108 private:
   109   void CollectAncestorHashes(bool aQuirksMode) {
   110     // Collect up our mAncestorSelectorHashes.  It's not clear whether it's
   111     // better to stop once we've found eMaxAncestorHashes of them or to keep
   112     // going and preferentially collect information from selectors higher up the
   113     // chain...  Let's do the former for now.
   114     size_t hashIndex = 0;
   115     for (nsCSSSelector* sel = mSelector->mNext; sel; sel = sel->mNext) {
   116       if (!NS_IS_ANCESTOR_OPERATOR(sel->mOperator)) {
   117         // |sel| is going to select something that's not actually one of our
   118         // ancestors, so don't add it to mAncestorSelectorHashes.  But keep
   119         // going, because it'll select a sibling of one of our ancestors, so its
   120         // ancestors would be our ancestors too.
   121         continue;
   122       }
   124       // Now sel is supposed to select one of our ancestors.  Grab
   125       // whatever info we can from it into mAncestorSelectorHashes.
   126       // But in qurks mode, don't grab IDs and classes because those
   127       // need to be matched case-insensitively.
   128       if (!aQuirksMode) {
   129         nsAtomList* ids = sel->mIDList;
   130         while (ids) {
   131           mAncestorSelectorHashes[hashIndex++] = ids->mAtom->hash();
   132           if (hashIndex == eMaxAncestorHashes) {
   133             return;
   134           }
   135           ids = ids->mNext;
   136         }
   138         nsAtomList* classes = sel->mClassList;
   139         while (classes) {
   140           mAncestorSelectorHashes[hashIndex++] = classes->mAtom->hash();
   141           if (hashIndex == eMaxAncestorHashes) {
   142             return;
   143           }
   144           classes = classes->mNext;
   145         }
   146       }
   148       // Only put in the tag name if it's all-lowercase.  Otherwise we run into
   149       // trouble because we may test the wrong one of mLowercaseTag and
   150       // mCasedTag against the filter.
   151       if (sel->mLowercaseTag && sel->mCasedTag == sel->mLowercaseTag) {
   152         mAncestorSelectorHashes[hashIndex++] = sel->mLowercaseTag->hash();
   153         if (hashIndex == eMaxAncestorHashes) {
   154           return;
   155         }
   156       }
   157     }
   159     while (hashIndex != eMaxAncestorHashes) {
   160       mAncestorSelectorHashes[hashIndex++] = 0;
   161     }
   162   }
   163 };
   165 // ------------------------------
   166 // Rule hash table
   167 //
   169 // Uses any of the sets of ops below.
   170 struct RuleHashTableEntry : public PLDHashEntryHdr {
   171   // If you add members that have heap allocated memory be sure to change the
   172   // logic in SizeOfRuleHashTableEntry().
   173   // Auto length 1, because we always have at least one entry in mRules.
   174   nsAutoTArray<RuleValue, 1> mRules;
   175 };
   177 struct RuleHashTagTableEntry : public RuleHashTableEntry {
   178   // If you add members that have heap allocated memory be sure to change the
   179   // logic in RuleHash::SizeOf{In,Ex}cludingThis.
   180   nsCOMPtr<nsIAtom> mTag;
   181 };
   183 static PLDHashNumber
   184 RuleHash_CIHashKey(PLDHashTable *table, const void *key)
   185 {
   186   nsIAtom *atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
   188   nsAutoString str;
   189   atom->ToString(str);
   190   nsContentUtils::ASCIIToLower(str);
   191   return HashString(str);
   192 }
   194 typedef nsIAtom*
   195 (* RuleHashGetKey) (PLDHashTable *table, const PLDHashEntryHdr *entry);
   197 struct RuleHashTableOps {
   198   const PLDHashTableOps ops;
   199   // Extra callback to avoid duplicating the matchEntry callback for
   200   // each table.  (There used to be a getKey callback in
   201   // PLDHashTableOps.)
   202   RuleHashGetKey getKey;
   203 };
   205 inline const RuleHashTableOps*
   206 ToLocalOps(const PLDHashTableOps *aOps)
   207 {
   208   return (const RuleHashTableOps*)
   209            (((const char*) aOps) - offsetof(RuleHashTableOps, ops));
   210 }
   212 static bool
   213 RuleHash_CIMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
   214                       const void *key)
   215 {
   216   nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>
   217                                               (key));
   218   // Use our extra |getKey| callback to avoid code duplication.
   219   nsIAtom *entry_atom = ToLocalOps(table->ops)->getKey(table, hdr);
   221   // Check for case-sensitive match first.
   222   if (match_atom == entry_atom)
   223     return true;
   225   // Use EqualsIgnoreASCIICase instead of full on unicode case conversion
   226   // in order to save on performance. This is only used in quirks mode
   227   // anyway.
   229   return
   230     nsContentUtils::EqualsIgnoreASCIICase(nsDependentAtomString(entry_atom),
   231                                           nsDependentAtomString(match_atom));
   232 }
   234 static bool
   235 RuleHash_CSMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
   236                       const void *key)
   237 {
   238   nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>
   239                                               (key));
   240   // Use our extra |getKey| callback to avoid code duplication.
   241   nsIAtom *entry_atom = ToLocalOps(table->ops)->getKey(table, hdr);
   243   return match_atom == entry_atom;
   244 }
   246 static bool
   247 RuleHash_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
   248                    const void *key)
   249 {
   250   RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(hdr);
   251   new (entry) RuleHashTableEntry();
   252   return true;
   253 }
   255 static void
   256 RuleHash_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
   257 {
   258   RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(hdr);
   259   entry->~RuleHashTableEntry();
   260 }
   262 static void
   263 RuleHash_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
   264                    PLDHashEntryHdr *to)
   265 {
   266   NS_PRECONDITION(from != to, "This is not going to work!");
   267   RuleHashTableEntry *oldEntry =
   268     const_cast<RuleHashTableEntry*>(
   269       static_cast<const RuleHashTableEntry*>(from));
   270   RuleHashTableEntry *newEntry = new (to) RuleHashTableEntry();
   271   newEntry->mRules.SwapElements(oldEntry->mRules);
   272   oldEntry->~RuleHashTableEntry();
   273 }
   275 static bool
   276 RuleHash_TagTable_MatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
   277                       const void *key)
   278 {
   279   nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>
   280                                               (key));
   281   nsIAtom *entry_atom = static_cast<const RuleHashTagTableEntry*>(hdr)->mTag;
   283   return match_atom == entry_atom;
   284 }
   286 static bool
   287 RuleHash_TagTable_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
   288                             const void *key)
   289 {
   290   RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>(hdr);
   291   new (entry) RuleHashTagTableEntry();
   292   entry->mTag = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
   293   return true;
   294 }
   296 static void
   297 RuleHash_TagTable_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
   298 {
   299   RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>(hdr);
   300   entry->~RuleHashTagTableEntry();
   301 }
   303 static void
   304 RuleHash_TagTable_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
   305                             PLDHashEntryHdr *to)
   306 {
   307   NS_PRECONDITION(from != to, "This is not going to work!");
   308   RuleHashTagTableEntry *oldEntry =
   309     const_cast<RuleHashTagTableEntry*>(
   310       static_cast<const RuleHashTagTableEntry*>(from));
   311   RuleHashTagTableEntry *newEntry = new (to) RuleHashTagTableEntry();
   312   newEntry->mTag.swap(oldEntry->mTag);
   313   newEntry->mRules.SwapElements(oldEntry->mRules);
   314   oldEntry->~RuleHashTagTableEntry();
   315 }
   317 static nsIAtom*
   318 RuleHash_ClassTable_GetKey(PLDHashTable *table, const PLDHashEntryHdr *hdr)
   319 {
   320   const RuleHashTableEntry *entry =
   321     static_cast<const RuleHashTableEntry*>(hdr);
   322   nsCSSSelector* selector = entry->mRules[0].mSelector;
   323   if (selector->IsPseudoElement()) {
   324     selector = selector->mNext;
   325   }
   326   return selector->mClassList->mAtom;
   327 }
   329 static nsIAtom*
   330 RuleHash_IdTable_GetKey(PLDHashTable *table, const PLDHashEntryHdr *hdr)
   331 {
   332   const RuleHashTableEntry *entry =
   333     static_cast<const RuleHashTableEntry*>(hdr);
   334   nsCSSSelector* selector = entry->mRules[0].mSelector;
   335   if (selector->IsPseudoElement()) {
   336     selector = selector->mNext;
   337   }
   338   return selector->mIDList->mAtom;
   339 }
   341 static PLDHashNumber
   342 RuleHash_NameSpaceTable_HashKey(PLDHashTable *table, const void *key)
   343 {
   344   return NS_PTR_TO_INT32(key);
   345 }
   347 static bool
   348 RuleHash_NameSpaceTable_MatchEntry(PLDHashTable *table,
   349                                    const PLDHashEntryHdr *hdr,
   350                                    const void *key)
   351 {
   352   const RuleHashTableEntry *entry =
   353     static_cast<const RuleHashTableEntry*>(hdr);
   355   nsCSSSelector* selector = entry->mRules[0].mSelector;
   356   if (selector->IsPseudoElement()) {
   357     selector = selector->mNext;
   358   }
   359   return NS_PTR_TO_INT32(key) == selector->mNameSpace;
   360 }
   362 static const PLDHashTableOps RuleHash_TagTable_Ops = {
   363   PL_DHashAllocTable,
   364   PL_DHashFreeTable,
   365   PL_DHashVoidPtrKeyStub,
   366   RuleHash_TagTable_MatchEntry,
   367   RuleHash_TagTable_MoveEntry,
   368   RuleHash_TagTable_ClearEntry,
   369   PL_DHashFinalizeStub,
   370   RuleHash_TagTable_InitEntry
   371 };
   373 // Case-sensitive ops.
   374 static const RuleHashTableOps RuleHash_ClassTable_CSOps = {
   375   {
   376   PL_DHashAllocTable,
   377   PL_DHashFreeTable,
   378   PL_DHashVoidPtrKeyStub,
   379   RuleHash_CSMatchEntry,
   380   RuleHash_MoveEntry,
   381   RuleHash_ClearEntry,
   382   PL_DHashFinalizeStub,
   383   RuleHash_InitEntry
   384   },
   385   RuleHash_ClassTable_GetKey
   386 };
   388 // Case-insensitive ops.
   389 static const RuleHashTableOps RuleHash_ClassTable_CIOps = {
   390   {
   391   PL_DHashAllocTable,
   392   PL_DHashFreeTable,
   393   RuleHash_CIHashKey,
   394   RuleHash_CIMatchEntry,
   395   RuleHash_MoveEntry,
   396   RuleHash_ClearEntry,
   397   PL_DHashFinalizeStub,
   398   RuleHash_InitEntry
   399   },
   400   RuleHash_ClassTable_GetKey
   401 };
   403 // Case-sensitive ops.
   404 static const RuleHashTableOps RuleHash_IdTable_CSOps = {
   405   {
   406   PL_DHashAllocTable,
   407   PL_DHashFreeTable,
   408   PL_DHashVoidPtrKeyStub,
   409   RuleHash_CSMatchEntry,
   410   RuleHash_MoveEntry,
   411   RuleHash_ClearEntry,
   412   PL_DHashFinalizeStub,
   413   RuleHash_InitEntry
   414   },
   415   RuleHash_IdTable_GetKey
   416 };
   418 // Case-insensitive ops.
   419 static const RuleHashTableOps RuleHash_IdTable_CIOps = {
   420   {
   421   PL_DHashAllocTable,
   422   PL_DHashFreeTable,
   423   RuleHash_CIHashKey,
   424   RuleHash_CIMatchEntry,
   425   RuleHash_MoveEntry,
   426   RuleHash_ClearEntry,
   427   PL_DHashFinalizeStub,
   428   RuleHash_InitEntry
   429   },
   430   RuleHash_IdTable_GetKey
   431 };
   433 static const PLDHashTableOps RuleHash_NameSpaceTable_Ops = {
   434   PL_DHashAllocTable,
   435   PL_DHashFreeTable,
   436   RuleHash_NameSpaceTable_HashKey,
   437   RuleHash_NameSpaceTable_MatchEntry,
   438   RuleHash_MoveEntry,
   439   RuleHash_ClearEntry,
   440   PL_DHashFinalizeStub,
   441   RuleHash_InitEntry
   442 };
   444 #undef RULE_HASH_STATS
   445 #undef PRINT_UNIVERSAL_RULES
   447 #ifdef RULE_HASH_STATS
   448 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO ++(var_); PR_END_MACRO
   449 #else
   450 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO PR_END_MACRO
   451 #endif
   453 struct NodeMatchContext;
   455 class RuleHash {
   456 public:
   457   RuleHash(bool aQuirksMode);
   458   ~RuleHash();
   459   void AppendRule(const RuleSelectorPair &aRuleInfo);
   460   void EnumerateAllRules(Element* aElement, ElementDependentRuleProcessorData* aData,
   461                          NodeMatchContext& aNodeMatchContext);
   463   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
   464   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
   466 protected:
   467   typedef nsTArray<RuleValue> RuleValueList;
   468   void AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
   469                          const RuleSelectorPair& aRuleInfo);
   470   void AppendUniversalRule(const RuleSelectorPair& aRuleInfo);
   472   int32_t     mRuleCount;
   473   // The hashtables are lazily initialized; we use a null .ops to
   474   // indicate that they need initialization.
   475   PLDHashTable mIdTable;
   476   PLDHashTable mClassTable;
   477   PLDHashTable mTagTable;
   478   PLDHashTable mNameSpaceTable;
   479   RuleValueList mUniversalRules;
   481   struct EnumData {
   482     const RuleValue* mCurValue;
   483     const RuleValue* mEnd;
   484   };
   485   EnumData* mEnumList;
   486   int32_t   mEnumListSize;
   488   bool mQuirksMode;
   490   inline EnumData ToEnumData(const RuleValueList& arr) {
   491     EnumData data = { arr.Elements(), arr.Elements() + arr.Length() };
   492     return data;
   493   }
   495 #ifdef RULE_HASH_STATS
   496   uint32_t    mUniversalSelectors;
   497   uint32_t    mNameSpaceSelectors;
   498   uint32_t    mTagSelectors;
   499   uint32_t    mClassSelectors;
   500   uint32_t    mIdSelectors;
   502   uint32_t    mElementsMatched;
   504   uint32_t    mElementUniversalCalls;
   505   uint32_t    mElementNameSpaceCalls;
   506   uint32_t    mElementTagCalls;
   507   uint32_t    mElementClassCalls;
   508   uint32_t    mElementIdCalls;
   509 #endif // RULE_HASH_STATS
   510 };
   512 RuleHash::RuleHash(bool aQuirksMode)
   513   : mRuleCount(0),
   514     mUniversalRules(0),
   515     mEnumList(nullptr), mEnumListSize(0),
   516     mQuirksMode(aQuirksMode)
   517 #ifdef RULE_HASH_STATS
   518     ,
   519     mUniversalSelectors(0),
   520     mNameSpaceSelectors(0),
   521     mTagSelectors(0),
   522     mClassSelectors(0),
   523     mIdSelectors(0),
   524     mElementsMatched(0),
   525     mElementUniversalCalls(0),
   526     mElementNameSpaceCalls(0),
   527     mElementTagCalls(0),
   528     mElementClassCalls(0),
   529     mElementIdCalls(0)
   530 #endif
   531 {
   532   MOZ_COUNT_CTOR(RuleHash);
   534   mTagTable.ops = nullptr;
   535   mIdTable.ops = nullptr;
   536   mClassTable.ops = nullptr;
   537   mNameSpaceTable.ops = nullptr;
   538 }
   540 RuleHash::~RuleHash()
   541 {
   542   MOZ_COUNT_DTOR(RuleHash);
   543 #ifdef RULE_HASH_STATS
   544   printf(
   545 "RuleHash(%p):\n"
   546 "  Selectors: Universal (%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
   547 "  Content Nodes: Elements(%u)\n"
   548 "  Element Calls: Universal(%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
   549          static_cast<void*>(this),
   550          mUniversalSelectors, mNameSpaceSelectors, mTagSelectors,
   551            mClassSelectors, mIdSelectors,
   552          mElementsMatched,
   553          mElementUniversalCalls, mElementNameSpaceCalls, mElementTagCalls,
   554            mElementClassCalls, mElementIdCalls);
   555 #ifdef PRINT_UNIVERSAL_RULES
   556   {
   557     if (mUniversalRules.Length() > 0) {
   558       printf("  Universal rules:\n");
   559       for (uint32_t i = 0; i < mUniversalRules.Length(); ++i) {
   560         RuleValue* value = &(mUniversalRules[i]);
   561         nsAutoString selectorText;
   562         uint32_t lineNumber = value->mRule->GetLineNumber();
   563         nsCOMPtr<nsIStyleSheet> sheet;
   564         value->mRule->GetStyleSheet(*getter_AddRefs(sheet));
   565         nsRefPtr<nsCSSStyleSheet> cssSheet = do_QueryObject(sheet);
   566         value->mSelector->ToString(selectorText, cssSheet);
   568         printf("    line %d, %s\n",
   569                lineNumber, NS_ConvertUTF16toUTF8(selectorText).get());
   570       }
   571     }
   572   }
   573 #endif // PRINT_UNIVERSAL_RULES
   574 #endif // RULE_HASH_STATS
   575   // Rule Values are arena allocated no need to delete them. Their destructor
   576   // isn't doing any cleanup. So we dont even bother to enumerate through
   577   // the hash tables and call their destructors.
   578   if (nullptr != mEnumList) {
   579     delete [] mEnumList;
   580   }
   581   // delete arena for strings and small objects
   582   if (mIdTable.ops) {
   583     PL_DHashTableFinish(&mIdTable);
   584   }
   585   if (mClassTable.ops) {
   586     PL_DHashTableFinish(&mClassTable);
   587   }
   588   if (mTagTable.ops) {
   589     PL_DHashTableFinish(&mTagTable);
   590   }
   591   if (mNameSpaceTable.ops) {
   592     PL_DHashTableFinish(&mNameSpaceTable);
   593   }
   594 }
   596 void RuleHash::AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
   597                                  const RuleSelectorPair& aRuleInfo)
   598 {
   599   // Get a new or existing entry.
   600   RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
   601                                          (PL_DHashTableOperate(aTable, aKey, PL_DHASH_ADD));
   602   if (!entry)
   603     return;
   604   entry->mRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode));
   605 }
   607 static void
   608 AppendRuleToTagTable(PLDHashTable* aTable, nsIAtom* aKey,
   609                      const RuleValue& aRuleInfo)
   610 {
   611   // Get a new or exisiting entry
   612   RuleHashTagTableEntry *entry = static_cast<RuleHashTagTableEntry*>
   613     (PL_DHashTableOperate(aTable, aKey, PL_DHASH_ADD));
   614   if (!entry)
   615     return;
   617   entry->mRules.AppendElement(aRuleInfo);
   618 }
   620 void RuleHash::AppendUniversalRule(const RuleSelectorPair& aRuleInfo)
   621 {
   622   mUniversalRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode));
   623 }
   625 void RuleHash::AppendRule(const RuleSelectorPair& aRuleInfo)
   626 {
   627   nsCSSSelector *selector = aRuleInfo.mSelector;
   628   if (selector->IsPseudoElement()) {
   629     selector = selector->mNext;
   630   }
   631   if (nullptr != selector->mIDList) {
   632     if (!mIdTable.ops) {
   633       PL_DHashTableInit(&mIdTable,
   634                         mQuirksMode ? &RuleHash_IdTable_CIOps.ops
   635                                     : &RuleHash_IdTable_CSOps.ops,
   636                         nullptr, sizeof(RuleHashTableEntry), 16);
   637     }
   638     AppendRuleToTable(&mIdTable, selector->mIDList->mAtom, aRuleInfo);
   639     RULE_HASH_STAT_INCREMENT(mIdSelectors);
   640   }
   641   else if (nullptr != selector->mClassList) {
   642     if (!mClassTable.ops) {
   643       PL_DHashTableInit(&mClassTable,
   644                         mQuirksMode ? &RuleHash_ClassTable_CIOps.ops
   645                                     : &RuleHash_ClassTable_CSOps.ops,
   646                         nullptr, sizeof(RuleHashTableEntry), 16);
   647     }
   648     AppendRuleToTable(&mClassTable, selector->mClassList->mAtom, aRuleInfo);
   649     RULE_HASH_STAT_INCREMENT(mClassSelectors);
   650   }
   651   else if (selector->mLowercaseTag) {
   652     RuleValue ruleValue(aRuleInfo, mRuleCount++, mQuirksMode);
   653     if (!mTagTable.ops) {
   654       PL_DHashTableInit(&mTagTable, &RuleHash_TagTable_Ops, nullptr,
   655                         sizeof(RuleHashTagTableEntry), 16);
   656     }
   657     AppendRuleToTagTable(&mTagTable, selector->mLowercaseTag, ruleValue);
   658     RULE_HASH_STAT_INCREMENT(mTagSelectors);
   659     if (selector->mCasedTag &&
   660         selector->mCasedTag != selector->mLowercaseTag) {
   661       AppendRuleToTagTable(&mTagTable, selector->mCasedTag, ruleValue);
   662       RULE_HASH_STAT_INCREMENT(mTagSelectors);
   663     }
   664   }
   665   else if (kNameSpaceID_Unknown != selector->mNameSpace) {
   666     if (!mNameSpaceTable.ops) {
   667       PL_DHashTableInit(&mNameSpaceTable, &RuleHash_NameSpaceTable_Ops, nullptr,
   668                         sizeof(RuleHashTableEntry), 16);
   669     }
   670     AppendRuleToTable(&mNameSpaceTable,
   671                       NS_INT32_TO_PTR(selector->mNameSpace), aRuleInfo);
   672     RULE_HASH_STAT_INCREMENT(mNameSpaceSelectors);
   673   }
   674   else {  // universal tag selector
   675     AppendUniversalRule(aRuleInfo);
   676     RULE_HASH_STAT_INCREMENT(mUniversalSelectors);
   677   }
   678 }
   680 // this should cover practically all cases so we don't need to reallocate
   681 #define MIN_ENUM_LIST_SIZE 8
   683 #ifdef RULE_HASH_STATS
   684 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
   685   (var_) += (list_).Length()
   686 #else
   687 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
   688   PR_BEGIN_MACRO PR_END_MACRO
   689 #endif
   691 static inline
   692 void ContentEnumFunc(const RuleValue &value, nsCSSSelector* selector,
   693                      ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
   694                      AncestorFilter *ancestorFilter);
   696 void RuleHash::EnumerateAllRules(Element* aElement, ElementDependentRuleProcessorData* aData,
   697                                  NodeMatchContext& aNodeContext)
   698 {
   699   int32_t nameSpace = aElement->GetNameSpaceID();
   700   nsIAtom* tag = aElement->Tag();
   701   nsIAtom* id = aElement->GetID();
   702   const nsAttrValue* classList = aElement->GetClasses();
   704   NS_ABORT_IF_FALSE(tag, "How could we not have a tag?");
   706   int32_t classCount = classList ? classList->GetAtomCount() : 0;
   708   // assume 1 universal, tag, id, and namespace, rather than wasting
   709   // time counting
   710   int32_t testCount = classCount + 4;
   712   if (mEnumListSize < testCount) {
   713     delete [] mEnumList;
   714     mEnumListSize = std::max(testCount, MIN_ENUM_LIST_SIZE);
   715     mEnumList = new EnumData[mEnumListSize];
   716   }
   718   int32_t valueCount = 0;
   719   RULE_HASH_STAT_INCREMENT(mElementsMatched);
   721   if (mUniversalRules.Length() != 0) { // universal rules
   722     mEnumList[valueCount++] = ToEnumData(mUniversalRules);
   723     RULE_HASH_STAT_INCREMENT_LIST_COUNT(mUniversalRules, mElementUniversalCalls);
   724   }
   725   // universal rules within the namespace
   726   if (kNameSpaceID_Unknown != nameSpace && mNameSpaceTable.ops) {
   727     RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
   728                                            (PL_DHashTableOperate(&mNameSpaceTable, NS_INT32_TO_PTR(nameSpace),
   729                              PL_DHASH_LOOKUP));
   730     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
   731       mEnumList[valueCount++] = ToEnumData(entry->mRules);
   732       RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementNameSpaceCalls);
   733     }
   734   }
   735   if (mTagTable.ops) {
   736     RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
   737                                            (PL_DHashTableOperate(&mTagTable, tag, PL_DHASH_LOOKUP));
   738     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
   739       mEnumList[valueCount++] = ToEnumData(entry->mRules);
   740       RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementTagCalls);
   741     }
   742   }
   743   if (id && mIdTable.ops) {
   744     RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
   745                                            (PL_DHashTableOperate(&mIdTable, id, PL_DHASH_LOOKUP));
   746     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
   747       mEnumList[valueCount++] = ToEnumData(entry->mRules);
   748       RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementIdCalls);
   749     }
   750   }
   751   if (mClassTable.ops) {
   752     for (int32_t index = 0; index < classCount; ++index) {
   753       RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
   754                                              (PL_DHashTableOperate(&mClassTable, classList->AtomAt(index),
   755                              PL_DHASH_LOOKUP));
   756       if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
   757         mEnumList[valueCount++] = ToEnumData(entry->mRules);
   758         RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementClassCalls);
   759       }
   760     }
   761   }
   762   NS_ASSERTION(valueCount <= testCount, "values exceeded list size");
   764   if (valueCount > 0) {
   765     AncestorFilter *filter =
   766       aData->mTreeMatchContext.mAncestorFilter.HasFilter() ?
   767         &aData->mTreeMatchContext.mAncestorFilter : nullptr;
   768 #ifdef DEBUG
   769     if (filter) {
   770       filter->AssertHasAllAncestors(aElement);
   771     }
   772 #endif
   773     // Merge the lists while there are still multiple lists to merge.
   774     while (valueCount > 1) {
   775       int32_t valueIndex = 0;
   776       int32_t lowestRuleIndex = mEnumList[valueIndex].mCurValue->mIndex;
   777       for (int32_t index = 1; index < valueCount; ++index) {
   778         int32_t ruleIndex = mEnumList[index].mCurValue->mIndex;
   779         if (ruleIndex < lowestRuleIndex) {
   780           valueIndex = index;
   781           lowestRuleIndex = ruleIndex;
   782         }
   783       }
   784       const RuleValue *cur = mEnumList[valueIndex].mCurValue;
   785       ContentEnumFunc(*cur, cur->mSelector, aData, aNodeContext, filter);
   786       cur++;
   787       if (cur == mEnumList[valueIndex].mEnd) {
   788         mEnumList[valueIndex] = mEnumList[--valueCount];
   789       } else {
   790         mEnumList[valueIndex].mCurValue = cur;
   791       }
   792     }
   794     // Fast loop over single value.
   795     for (const RuleValue *value = mEnumList[0].mCurValue,
   796                          *end = mEnumList[0].mEnd;
   797          value != end; ++value) {
   798       ContentEnumFunc(*value, value->mSelector, aData, aNodeContext, filter);
   799     }
   800   }
   801 }
   803 static size_t
   804 SizeOfRuleHashTableEntry(PLDHashEntryHdr* aHdr, MallocSizeOf aMallocSizeOf, void *)
   805 {
   806   RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(aHdr);
   807   return entry->mRules.SizeOfExcludingThis(aMallocSizeOf);
   808 }
   810 size_t
   811 RuleHash::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
   812 {
   813   size_t n = 0;
   815   if (mIdTable.ops) {
   816     n += PL_DHashTableSizeOfExcludingThis(&mIdTable,
   817                                           SizeOfRuleHashTableEntry,
   818                                           aMallocSizeOf);
   819   }
   821   if (mClassTable.ops) {
   822     n += PL_DHashTableSizeOfExcludingThis(&mClassTable,
   823                                           SizeOfRuleHashTableEntry,
   824                                           aMallocSizeOf);
   825   }
   827   if (mTagTable.ops) {
   828     n += PL_DHashTableSizeOfExcludingThis(&mTagTable,
   829                                           SizeOfRuleHashTableEntry,
   830                                           aMallocSizeOf);
   831   }
   833   if (mNameSpaceTable.ops) {
   834     n += PL_DHashTableSizeOfExcludingThis(&mNameSpaceTable,
   835                                           SizeOfRuleHashTableEntry,
   836                                           aMallocSizeOf);
   837   }
   839   n += mUniversalRules.SizeOfExcludingThis(aMallocSizeOf);
   841   return n;
   842 }
   844 size_t
   845 RuleHash::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
   846 {
   847   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   848 }
   850 //--------------------------------
   852 // A hash table mapping atoms to lists of selectors
   853 struct AtomSelectorEntry : public PLDHashEntryHdr {
   854   nsIAtom *mAtom;
   855   // Auto length 2, because a decent fraction of these arrays ends up
   856   // with 2 elements, and each entry is cheap.
   857   nsAutoTArray<nsCSSSelector*, 2> mSelectors;
   858 };
   860 static void
   861 AtomSelector_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
   862 {
   863   (static_cast<AtomSelectorEntry*>(hdr))->~AtomSelectorEntry();
   864 }
   866 static bool
   867 AtomSelector_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
   868                        const void *key)
   869 {
   870   AtomSelectorEntry *entry = static_cast<AtomSelectorEntry*>(hdr);
   871   new (entry) AtomSelectorEntry();
   872   entry->mAtom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
   873   return true;
   874 }
   876 static void
   877 AtomSelector_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
   878                        PLDHashEntryHdr *to)
   879 {
   880   NS_PRECONDITION(from != to, "This is not going to work!");
   881   AtomSelectorEntry *oldEntry =
   882     const_cast<AtomSelectorEntry*>(static_cast<const AtomSelectorEntry*>(from));
   883   AtomSelectorEntry *newEntry = new (to) AtomSelectorEntry();
   884   newEntry->mAtom = oldEntry->mAtom;
   885   newEntry->mSelectors.SwapElements(oldEntry->mSelectors);
   886   oldEntry->~AtomSelectorEntry();
   887 }
   889 static nsIAtom*
   890 AtomSelector_GetKey(PLDHashTable *table, const PLDHashEntryHdr *hdr)
   891 {
   892   const AtomSelectorEntry *entry = static_cast<const AtomSelectorEntry*>(hdr);
   893   return entry->mAtom;
   894 }
   896 // Case-sensitive ops.
   897 static const PLDHashTableOps AtomSelector_CSOps = {
   898   PL_DHashAllocTable,
   899   PL_DHashFreeTable,
   900   PL_DHashVoidPtrKeyStub,
   901   PL_DHashMatchEntryStub,
   902   AtomSelector_MoveEntry,
   903   AtomSelector_ClearEntry,
   904   PL_DHashFinalizeStub,
   905   AtomSelector_InitEntry
   906 };
   908 // Case-insensitive ops.
   909 static const RuleHashTableOps AtomSelector_CIOps = {
   910   {
   911   PL_DHashAllocTable,
   912   PL_DHashFreeTable,
   913   RuleHash_CIHashKey,
   914   RuleHash_CIMatchEntry,
   915   AtomSelector_MoveEntry,
   916   AtomSelector_ClearEntry,
   917   PL_DHashFinalizeStub,
   918   AtomSelector_InitEntry
   919   },
   920   AtomSelector_GetKey
   921 };
   923 //--------------------------------
   925 struct RuleCascadeData {
   926   RuleCascadeData(nsIAtom *aMedium, bool aQuirksMode)
   927     : mRuleHash(aQuirksMode),
   928       mStateSelectors(),
   929       mSelectorDocumentStates(0),
   930       mKeyframesRuleTable(16),
   931       mCacheKey(aMedium),
   932       mNext(nullptr),
   933       mQuirksMode(aQuirksMode)
   934   {
   935     // mAttributeSelectors is matching on the attribute _name_, not the value,
   936     // and we case-fold names at parse-time, so this is a case-sensitive match.
   937     PL_DHashTableInit(&mAttributeSelectors, &AtomSelector_CSOps, nullptr,
   938                       sizeof(AtomSelectorEntry), 16);
   939     PL_DHashTableInit(&mAnonBoxRules, &RuleHash_TagTable_Ops, nullptr,
   940                       sizeof(RuleHashTagTableEntry), 16);
   941     PL_DHashTableInit(&mIdSelectors,
   942                       aQuirksMode ? &AtomSelector_CIOps.ops :
   943                                     &AtomSelector_CSOps,
   944                       nullptr, sizeof(AtomSelectorEntry), 16);
   945     PL_DHashTableInit(&mClassSelectors,
   946                       aQuirksMode ? &AtomSelector_CIOps.ops :
   947                                     &AtomSelector_CSOps,
   948                       nullptr, sizeof(AtomSelectorEntry), 16);
   949     memset(mPseudoElementRuleHashes, 0, sizeof(mPseudoElementRuleHashes));
   950 #ifdef MOZ_XUL
   951     PL_DHashTableInit(&mXULTreeRules, &RuleHash_TagTable_Ops, nullptr,
   952                       sizeof(RuleHashTagTableEntry), 16);
   953 #endif
   954   }
   956   ~RuleCascadeData()
   957   {
   958     PL_DHashTableFinish(&mAttributeSelectors);
   959     PL_DHashTableFinish(&mAnonBoxRules);
   960     PL_DHashTableFinish(&mIdSelectors);
   961     PL_DHashTableFinish(&mClassSelectors);
   962 #ifdef MOZ_XUL
   963     PL_DHashTableFinish(&mXULTreeRules);
   964 #endif
   965     for (uint32_t i = 0; i < ArrayLength(mPseudoElementRuleHashes); ++i) {
   966       delete mPseudoElementRuleHashes[i];
   967     }
   968   }
   970   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
   972   RuleHash                 mRuleHash;
   973   RuleHash*
   974     mPseudoElementRuleHashes[nsCSSPseudoElements::ePseudo_PseudoElementCount];
   975   nsTArray<nsCSSRuleProcessor::StateSelector>  mStateSelectors;
   976   EventStates              mSelectorDocumentStates;
   977   PLDHashTable             mClassSelectors;
   978   PLDHashTable             mIdSelectors;
   979   nsTArray<nsCSSSelector*> mPossiblyNegatedClassSelectors;
   980   nsTArray<nsCSSSelector*> mPossiblyNegatedIDSelectors;
   981   PLDHashTable             mAttributeSelectors;
   982   PLDHashTable             mAnonBoxRules;
   983 #ifdef MOZ_XUL
   984   PLDHashTable             mXULTreeRules;
   985 #endif
   987   nsTArray<nsFontFaceRuleContainer> mFontFaceRules;
   988   nsTArray<nsCSSKeyframesRule*> mKeyframesRules;
   989   nsTArray<nsCSSFontFeatureValuesRule*> mFontFeatureValuesRules;
   990   nsTArray<nsCSSPageRule*> mPageRules;
   992   nsDataHashtable<nsStringHashKey, nsCSSKeyframesRule*> mKeyframesRuleTable;
   994   // Looks up or creates the appropriate list in |mAttributeSelectors|.
   995   // Returns null only on allocation failure.
   996   nsTArray<nsCSSSelector*>* AttributeListFor(nsIAtom* aAttribute);
   998   nsMediaQueryResultCacheKey mCacheKey;
   999   RuleCascadeData*  mNext; // for a different medium
  1001   const bool mQuirksMode;
  1002 };
  1004 static size_t
  1005 SizeOfSelectorsEntry(PLDHashEntryHdr* aHdr, MallocSizeOf aMallocSizeOf, void *)
  1007   AtomSelectorEntry* entry = static_cast<AtomSelectorEntry*>(aHdr);
  1008   return entry->mSelectors.SizeOfExcludingThis(aMallocSizeOf);
  1011 size_t
  1012 RuleCascadeData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
  1014   size_t n = aMallocSizeOf(this);
  1016   n += mRuleHash.SizeOfExcludingThis(aMallocSizeOf);
  1017   for (uint32_t i = 0; i < ArrayLength(mPseudoElementRuleHashes); ++i) {
  1018     if (mPseudoElementRuleHashes[i])
  1019       n += mPseudoElementRuleHashes[i]->SizeOfIncludingThis(aMallocSizeOf);
  1022   n += mStateSelectors.SizeOfExcludingThis(aMallocSizeOf);
  1024   n += PL_DHashTableSizeOfExcludingThis(&mIdSelectors,
  1025                                         SizeOfSelectorsEntry, aMallocSizeOf);
  1026   n += PL_DHashTableSizeOfExcludingThis(&mClassSelectors,
  1027                                         SizeOfSelectorsEntry, aMallocSizeOf);
  1029   n += mPossiblyNegatedClassSelectors.SizeOfExcludingThis(aMallocSizeOf);
  1030   n += mPossiblyNegatedIDSelectors.SizeOfExcludingThis(aMallocSizeOf);
  1032   n += PL_DHashTableSizeOfExcludingThis(&mAttributeSelectors,
  1033                                         SizeOfSelectorsEntry, aMallocSizeOf);
  1034   n += PL_DHashTableSizeOfExcludingThis(&mAnonBoxRules,
  1035                                         SizeOfRuleHashTableEntry, aMallocSizeOf);
  1036 #ifdef MOZ_XUL
  1037   n += PL_DHashTableSizeOfExcludingThis(&mXULTreeRules,
  1038                                         SizeOfRuleHashTableEntry, aMallocSizeOf);
  1039 #endif
  1041   n += mFontFaceRules.SizeOfExcludingThis(aMallocSizeOf);
  1042   n += mKeyframesRules.SizeOfExcludingThis(aMallocSizeOf);
  1043   n += mFontFeatureValuesRules.SizeOfExcludingThis(aMallocSizeOf);
  1044   n += mPageRules.SizeOfExcludingThis(aMallocSizeOf);
  1046   return n;
  1049 nsTArray<nsCSSSelector*>*
  1050 RuleCascadeData::AttributeListFor(nsIAtom* aAttribute)
  1052   AtomSelectorEntry *entry =
  1053     static_cast<AtomSelectorEntry*>
  1054                (PL_DHashTableOperate(&mAttributeSelectors, aAttribute,
  1055                                      PL_DHASH_ADD));
  1056   if (!entry)
  1057     return nullptr;
  1058   return &entry->mSelectors;
  1061 // -------------------------------
  1062 // CSS Style rule processor implementation
  1063 //
  1065 nsCSSRuleProcessor::nsCSSRuleProcessor(const sheet_array_type& aSheets,
  1066                                        uint8_t aSheetType,
  1067                                        Element* aScopeElement)
  1068   : mSheets(aSheets)
  1069   , mRuleCascades(nullptr)
  1070   , mLastPresContext(nullptr)
  1071   , mScopeElement(aScopeElement)
  1072   , mSheetType(aSheetType)
  1074   NS_ASSERTION(!!mScopeElement == (aSheetType == nsStyleSet::eScopedDocSheet),
  1075                "aScopeElement must be specified iff aSheetType is "
  1076                "eScopedDocSheet");
  1077   for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) {
  1078     mSheets[i]->AddRuleProcessor(this);
  1082 nsCSSRuleProcessor::~nsCSSRuleProcessor()
  1084   for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) {
  1085     mSheets[i]->DropRuleProcessor(this);
  1087   mSheets.Clear();
  1088   ClearRuleCascades();
  1091 NS_IMPL_ISUPPORTS(nsCSSRuleProcessor, nsIStyleRuleProcessor)
  1093 /* static */ nsresult
  1094 nsCSSRuleProcessor::Startup()
  1096   Preferences::AddBoolVarCache(&gSupportVisitedPseudo, VISITED_PSEUDO_PREF,
  1097                                true);
  1099   return NS_OK;
  1102 static bool
  1103 InitSystemMetrics()
  1105   NS_ASSERTION(!sSystemMetrics, "already initialized");
  1107   sSystemMetrics = new nsTArray< nsCOMPtr<nsIAtom> >;
  1108   NS_ENSURE_TRUE(sSystemMetrics, false);
  1110   /***************************************************************************
  1111    * ANY METRICS ADDED HERE SHOULD ALSO BE ADDED AS MEDIA QUERIES IN         *
  1112    * nsMediaFeatures.cpp                                                     *
  1113    ***************************************************************************/
  1115   int32_t metricResult =
  1116     LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollArrowStyle);
  1117   if (metricResult & LookAndFeel::eScrollArrow_StartBackward) {
  1118     sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_start_backward);
  1120   if (metricResult & LookAndFeel::eScrollArrow_StartForward) {
  1121     sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_start_forward);
  1123   if (metricResult & LookAndFeel::eScrollArrow_EndBackward) {
  1124     sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_end_backward);
  1126   if (metricResult & LookAndFeel::eScrollArrow_EndForward) {
  1127     sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_end_forward);
  1130   metricResult =
  1131     LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollSliderStyle);
  1132   if (metricResult != LookAndFeel::eScrollThumbStyle_Normal) {
  1133     sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_thumb_proportional);
  1136   metricResult =
  1137     LookAndFeel::GetInt(LookAndFeel::eIntID_ImagesInMenus);
  1138   if (metricResult) {
  1139     sSystemMetrics->AppendElement(nsGkAtoms::images_in_menus);
  1142   metricResult =
  1143     LookAndFeel::GetInt(LookAndFeel::eIntID_ImagesInButtons);
  1144   if (metricResult) {
  1145     sSystemMetrics->AppendElement(nsGkAtoms::images_in_buttons);
  1148   metricResult =
  1149     LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars);
  1150   if (metricResult) {
  1151     sSystemMetrics->AppendElement(nsGkAtoms::overlay_scrollbars);
  1154   metricResult =
  1155     LookAndFeel::GetInt(LookAndFeel::eIntID_MenuBarDrag);
  1156   if (metricResult) {
  1157     sSystemMetrics->AppendElement(nsGkAtoms::menubar_drag);
  1160   nsresult rv =
  1161     LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsDefaultTheme, &metricResult);
  1162   if (NS_SUCCEEDED(rv) && metricResult) {
  1163     sSystemMetrics->AppendElement(nsGkAtoms::windows_default_theme);
  1166   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacGraphiteTheme, &metricResult);
  1167   if (NS_SUCCEEDED(rv) && metricResult) {
  1168     sSystemMetrics->AppendElement(nsGkAtoms::mac_graphite_theme);
  1171   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacLionTheme, &metricResult);
  1172   if (NS_SUCCEEDED(rv) && metricResult) {
  1173     sSystemMetrics->AppendElement(nsGkAtoms::mac_lion_theme);
  1176   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_DWMCompositor, &metricResult);
  1177   if (NS_SUCCEEDED(rv) && metricResult) {
  1178     sSystemMetrics->AppendElement(nsGkAtoms::windows_compositor);
  1181   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsGlass, &metricResult);
  1182   if (NS_SUCCEEDED(rv) && metricResult) {
  1183     sSystemMetrics->AppendElement(nsGkAtoms::windows_glass);
  1186   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_ColorPickerAvailable, &metricResult);
  1187   if (NS_SUCCEEDED(rv) && metricResult) {
  1188     sSystemMetrics->AppendElement(nsGkAtoms::color_picker_available);
  1191   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsClassic, &metricResult);
  1192   if (NS_SUCCEEDED(rv) && metricResult) {
  1193     sSystemMetrics->AppendElement(nsGkAtoms::windows_classic);
  1196   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_TouchEnabled, &metricResult);
  1197   if (NS_SUCCEEDED(rv) && metricResult) {
  1198     sSystemMetrics->AppendElement(nsGkAtoms::touch_enabled);
  1201   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_SwipeAnimationEnabled,
  1202                            &metricResult);
  1203   if (NS_SUCCEEDED(rv) && metricResult) {
  1204     sSystemMetrics->AppendElement(nsGkAtoms::swipe_animation_enabled);
  1207   rv = LookAndFeel::GetInt(LookAndFeel::eIntID_PhysicalHomeButton,
  1208                            &metricResult);
  1209   if (NS_SUCCEEDED(rv) && metricResult) {
  1210     sSystemMetrics->AppendElement(nsGkAtoms::physical_home_button);
  1213 #ifdef XP_WIN
  1214   if (NS_SUCCEEDED(
  1215         LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsThemeIdentifier,
  1216                             &metricResult))) {
  1217     nsCSSRuleProcessor::SetWindowsThemeIdentifier(static_cast<uint8_t>(metricResult));
  1218     switch(metricResult) {
  1219       case LookAndFeel::eWindowsTheme_Aero:
  1220         sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero);
  1221         break;
  1222       case LookAndFeel::eWindowsTheme_AeroLite:
  1223         sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero_lite);
  1224         break;
  1225       case LookAndFeel::eWindowsTheme_LunaBlue:
  1226         sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_blue);
  1227         break;
  1228       case LookAndFeel::eWindowsTheme_LunaOlive:
  1229         sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_olive);
  1230         break;
  1231       case LookAndFeel::eWindowsTheme_LunaSilver:
  1232         sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_silver);
  1233         break;
  1234       case LookAndFeel::eWindowsTheme_Royale:
  1235         sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_royale);
  1236         break;
  1237       case LookAndFeel::eWindowsTheme_Zune:
  1238         sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_zune);
  1239         break;
  1240       case LookAndFeel::eWindowsTheme_Generic:
  1241         sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_generic);
  1242         break;
  1245 #endif
  1247   return true;
  1250 /* static */ void
  1251 nsCSSRuleProcessor::FreeSystemMetrics()
  1253   delete sSystemMetrics;
  1254   sSystemMetrics = nullptr;
  1257 /* static */ void
  1258 nsCSSRuleProcessor::Shutdown()
  1260   FreeSystemMetrics();
  1263 /* static */ bool
  1264 nsCSSRuleProcessor::HasSystemMetric(nsIAtom* aMetric)
  1266   if (!sSystemMetrics && !InitSystemMetrics()) {
  1267     return false;
  1269   return sSystemMetrics->IndexOf(aMetric) != sSystemMetrics->NoIndex;
  1272 #ifdef XP_WIN
  1273 /* static */ uint8_t
  1274 nsCSSRuleProcessor::GetWindowsThemeIdentifier()
  1276   if (!sSystemMetrics)
  1277     InitSystemMetrics();
  1278   return sWinThemeId;
  1280 #endif
  1282 /* static */
  1283 EventStates
  1284 nsCSSRuleProcessor::GetContentState(Element* aElement, const TreeMatchContext& aTreeMatchContext)
  1286   EventStates state = aElement->StyleState();
  1288   // If we are not supposed to mark visited links as such, be sure to
  1289   // flip the bits appropriately.  We want to do this here, rather
  1290   // than in GetContentStateForVisitedHandling, so that we don't
  1291   // expose that :visited support is disabled to the Web page.
  1292   if (state.HasState(NS_EVENT_STATE_VISITED) &&
  1293       (!gSupportVisitedPseudo ||
  1294        aElement->OwnerDoc()->IsBeingUsedAsImage() ||
  1295        aTreeMatchContext.mUsingPrivateBrowsing)) {
  1296     state &= ~NS_EVENT_STATE_VISITED;
  1297     state |= NS_EVENT_STATE_UNVISITED;
  1299   return state;
  1302 /* static */
  1303 bool
  1304 nsCSSRuleProcessor::IsLink(Element* aElement)
  1306   EventStates state = aElement->StyleState();
  1307   return state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED);
  1310 /* static */
  1311 EventStates
  1312 nsCSSRuleProcessor::GetContentStateForVisitedHandling(
  1313                      Element* aElement,
  1314                      const TreeMatchContext& aTreeMatchContext,
  1315                      nsRuleWalker::VisitedHandlingType aVisitedHandling,
  1316                      bool aIsRelevantLink)
  1318   EventStates contentState = GetContentState(aElement, aTreeMatchContext);
  1319   if (contentState.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)) {
  1320     NS_ABORT_IF_FALSE(IsLink(aElement), "IsLink() should match state");
  1321     contentState &= ~(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED);
  1322     if (aIsRelevantLink) {
  1323       switch (aVisitedHandling) {
  1324         case nsRuleWalker::eRelevantLinkUnvisited:
  1325           contentState |= NS_EVENT_STATE_UNVISITED;
  1326           break;
  1327         case nsRuleWalker::eRelevantLinkVisited:
  1328           contentState |= NS_EVENT_STATE_VISITED;
  1329           break;
  1330         case nsRuleWalker::eLinksVisitedOrUnvisited:
  1331           contentState |= NS_EVENT_STATE_UNVISITED | NS_EVENT_STATE_VISITED;
  1332           break;
  1334     } else {
  1335       contentState |= NS_EVENT_STATE_UNVISITED;
  1338   return contentState;
  1341 /**
  1342  * A |NodeMatchContext| has data about matching a selector (without
  1343  * combinators) against a single node.  It contains only input to the
  1344  * matching.
  1346  * Unlike |RuleProcessorData|, which is similar, a |NodeMatchContext|
  1347  * can vary depending on the selector matching process.  In other words,
  1348  * there might be multiple NodeMatchContexts corresponding to a single
  1349  * node, but only one possible RuleProcessorData.
  1350  */
  1351 struct NodeMatchContext {
  1352   // In order to implement nsCSSRuleProcessor::HasStateDependentStyle,
  1353   // we need to be able to see if a node might match an
  1354   // event-state-dependent selector for any value of that event state.
  1355   // So mStateMask contains the states that should NOT be tested.
  1356   //
  1357   // NOTE: For |mStateMask| to work correctly, it's important that any
  1358   // change that changes multiple state bits include all those state
  1359   // bits in the notification.  Otherwise, if multiple states change but
  1360   // we do separate notifications then we might determine the style is
  1361   // not state-dependent when it really is (e.g., determining that a
  1362   // :hover:active rule no longer matches when both states are unset).
  1363   const EventStates mStateMask;
  1365   // Is this link the unique link whose visitedness can affect the style
  1366   // of the node being matched?  (That link is the nearest link to the
  1367   // node being matched that is itself or an ancestor.)
  1368   //
  1369   // Always false when TreeMatchContext::mForStyling is false.  (We
  1370   // could figure it out for SelectorListMatches, but we're starting
  1371   // from the middle of the selector list when doing
  1372   // Has{Attribute,State}DependentStyle, so we can't tell.  So when
  1373   // mForStyling is false, we have to assume we don't know.)
  1374   const bool mIsRelevantLink;
  1376   NodeMatchContext(EventStates aStateMask, bool aIsRelevantLink)
  1377     : mStateMask(aStateMask)
  1378     , mIsRelevantLink(aIsRelevantLink)
  1381 };
  1383 static bool ValueIncludes(const nsSubstring& aValueList,
  1384                             const nsSubstring& aValue,
  1385                             const nsStringComparator& aComparator)
  1387   const char16_t *p = aValueList.BeginReading(),
  1388               *p_end = aValueList.EndReading();
  1390   while (p < p_end) {
  1391     // skip leading space
  1392     while (p != p_end && nsContentUtils::IsHTMLWhitespace(*p))
  1393       ++p;
  1395     const char16_t *val_start = p;
  1397     // look for space or end
  1398     while (p != p_end && !nsContentUtils::IsHTMLWhitespace(*p))
  1399       ++p;
  1401     const char16_t *val_end = p;
  1403     if (val_start < val_end &&
  1404         aValue.Equals(Substring(val_start, val_end), aComparator))
  1405       return true;
  1407     ++p; // we know the next character is not whitespace
  1409   return false;
  1412 // Return whether we should apply a "global" (i.e., universal-tag)
  1413 // selector for event states in quirks mode.  Note that
  1414 // |IsLink()| is checked separately by the caller, so we return
  1415 // false for |nsGkAtoms::a|, which here means a named anchor.
  1416 inline bool IsQuirkEventSensitive(nsIAtom *aContentTag)
  1418   return bool ((nsGkAtoms::button == aContentTag) ||
  1419                  (nsGkAtoms::img == aContentTag)    ||
  1420                  (nsGkAtoms::input == aContentTag)  ||
  1421                  (nsGkAtoms::label == aContentTag)  ||
  1422                  (nsGkAtoms::select == aContentTag) ||
  1423                  (nsGkAtoms::textarea == aContentTag));
  1427 static inline bool
  1428 IsSignificantChild(nsIContent* aChild, bool aTextIsSignificant,
  1429                    bool aWhitespaceIsSignificant)
  1431   return nsStyleUtil::IsSignificantChild(aChild, aTextIsSignificant,
  1432                                          aWhitespaceIsSignificant);
  1435 // This function is to be called once we have fetched a value for an attribute
  1436 // whose namespace and name match those of aAttrSelector.  This function
  1437 // performs comparisons on the value only, based on aAttrSelector->mFunction.
  1438 static bool AttrMatchesValue(const nsAttrSelector* aAttrSelector,
  1439                                const nsString& aValue, bool isHTML)
  1441   NS_PRECONDITION(aAttrSelector, "Must have an attribute selector");
  1443   // http://lists.w3.org/Archives/Public/www-style/2008Apr/0038.html
  1444   // *= (CONTAINSMATCH) ~= (INCLUDES) ^= (BEGINSMATCH) $= (ENDSMATCH)
  1445   // all accept the empty string, but match nothing.
  1446   if (aAttrSelector->mValue.IsEmpty() &&
  1447       (aAttrSelector->mFunction == NS_ATTR_FUNC_INCLUDES ||
  1448        aAttrSelector->mFunction == NS_ATTR_FUNC_ENDSMATCH ||
  1449        aAttrSelector->mFunction == NS_ATTR_FUNC_BEGINSMATCH ||
  1450        aAttrSelector->mFunction == NS_ATTR_FUNC_CONTAINSMATCH))
  1451     return false;
  1453   const nsDefaultStringComparator defaultComparator;
  1454   const nsASCIICaseInsensitiveStringComparator ciComparator;
  1455   const nsStringComparator& comparator =
  1456       (aAttrSelector->mCaseSensitive || !isHTML)
  1457                 ? static_cast<const nsStringComparator&>(defaultComparator)
  1458                 : static_cast<const nsStringComparator&>(ciComparator);
  1460   switch (aAttrSelector->mFunction) {
  1461     case NS_ATTR_FUNC_EQUALS:
  1462       return aValue.Equals(aAttrSelector->mValue, comparator);
  1463     case NS_ATTR_FUNC_INCLUDES:
  1464       return ValueIncludes(aValue, aAttrSelector->mValue, comparator);
  1465     case NS_ATTR_FUNC_DASHMATCH:
  1466       return nsStyleUtil::DashMatchCompare(aValue, aAttrSelector->mValue, comparator);
  1467     case NS_ATTR_FUNC_ENDSMATCH:
  1468       return StringEndsWith(aValue, aAttrSelector->mValue, comparator);
  1469     case NS_ATTR_FUNC_BEGINSMATCH:
  1470       return StringBeginsWith(aValue, aAttrSelector->mValue, comparator);
  1471     case NS_ATTR_FUNC_CONTAINSMATCH:
  1472       return FindInReadable(aAttrSelector->mValue, aValue, comparator);
  1473     default:
  1474       NS_NOTREACHED("Shouldn't be ending up here");
  1475       return false;
  1479 static inline bool
  1480 edgeChildMatches(Element* aElement, TreeMatchContext& aTreeMatchContext,
  1481                  bool checkFirst, bool checkLast)
  1483   nsIContent *parent = aElement->GetParent();
  1484   if (!parent) {
  1485     return false;
  1488   if (aTreeMatchContext.mForStyling)
  1489     parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
  1491   return (!checkFirst ||
  1492           aTreeMatchContext.mNthIndexCache.
  1493             GetNthIndex(aElement, false, false, true) == 1) &&
  1494          (!checkLast ||
  1495           aTreeMatchContext.mNthIndexCache.
  1496             GetNthIndex(aElement, false, true, true) == 1);
  1499 static inline bool
  1500 nthChildGenericMatches(Element* aElement,
  1501                        TreeMatchContext& aTreeMatchContext,
  1502                        nsPseudoClassList* pseudoClass,
  1503                        bool isOfType, bool isFromEnd)
  1505   nsIContent *parent = aElement->GetParent();
  1506   if (!parent) {
  1507     return false;
  1510   if (aTreeMatchContext.mForStyling) {
  1511     if (isFromEnd)
  1512       parent->SetFlags(NODE_HAS_SLOW_SELECTOR);
  1513     else
  1514       parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
  1517   const int32_t index = aTreeMatchContext.mNthIndexCache.
  1518     GetNthIndex(aElement, isOfType, isFromEnd, false);
  1519   if (index <= 0) {
  1520     // Node is anonymous content (not really a child of its parent).
  1521     return false;
  1524   const int32_t a = pseudoClass->u.mNumbers[0];
  1525   const int32_t b = pseudoClass->u.mNumbers[1];
  1526   // result should be true if there exists n >= 0 such that
  1527   // a * n + b == index.
  1528   if (a == 0) {
  1529     return b == index;
  1532   // Integer division in C does truncation (towards 0).  So
  1533   // check that the result is nonnegative, and that there was no
  1534   // truncation.
  1535   const int32_t n = (index - b) / a;
  1536   return n >= 0 && (a * n == index - b);
  1539 static inline bool
  1540 edgeOfTypeMatches(Element* aElement, TreeMatchContext& aTreeMatchContext,
  1541                   bool checkFirst, bool checkLast)
  1543   nsIContent *parent = aElement->GetParent();
  1544   if (!parent) {
  1545     return false;
  1548   if (aTreeMatchContext.mForStyling) {
  1549     if (checkLast)
  1550       parent->SetFlags(NODE_HAS_SLOW_SELECTOR);
  1551     else
  1552       parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
  1555   return (!checkFirst ||
  1556           aTreeMatchContext.mNthIndexCache.
  1557             GetNthIndex(aElement, true, false, true) == 1) &&
  1558          (!checkLast ||
  1559           aTreeMatchContext.mNthIndexCache.
  1560             GetNthIndex(aElement, true, true, true) == 1);
  1563 static inline bool
  1564 checkGenericEmptyMatches(Element* aElement,
  1565                          TreeMatchContext& aTreeMatchContext,
  1566                          bool isWhitespaceSignificant)
  1568   nsIContent *child = nullptr;
  1569   int32_t index = -1;
  1571   if (aTreeMatchContext.mForStyling)
  1572     aElement->SetFlags(NODE_HAS_EMPTY_SELECTOR);
  1574   do {
  1575     child = aElement->GetChildAt(++index);
  1576     // stop at first non-comment (and non-whitespace for
  1577     // :-moz-only-whitespace) node        
  1578   } while (child && !IsSignificantChild(child, true, isWhitespaceSignificant));
  1579   return (child == nullptr);
  1582 // Arrays of the states that are relevant for various pseudoclasses.
  1583 static const EventStates sPseudoClassStateDependences[] = {
  1584 #define CSS_PSEUDO_CLASS(_name, _value, _pref)  \
  1585   EventStates(),
  1586 #define CSS_STATE_DEPENDENT_PSEUDO_CLASS(_name, _value, _pref, _states)  \
  1587   _states,
  1588 #include "nsCSSPseudoClassList.h"
  1589 #undef CSS_STATE_DEPENDENT_PSEUDO_CLASS
  1590 #undef CSS_PSEUDO_CLASS
  1591   // Add more entries for our fake values to make sure we can't
  1592   // index out of bounds into this array no matter what.
  1593   EventStates(),
  1594   EventStates()
  1595 };
  1597 static const EventStates sPseudoClassStates[] = {
  1598 #define CSS_PSEUDO_CLASS(_name, _value, _pref)  \
  1599   EventStates(),
  1600 #define CSS_STATE_PSEUDO_CLASS(_name, _value, _pref, _states) \
  1601   _states,
  1602 #include "nsCSSPseudoClassList.h"
  1603 #undef CSS_STATE_PSEUDO_CLASS
  1604 #undef CSS_PSEUDO_CLASS
  1605   // Add more entries for our fake values to make sure we can't
  1606   // index out of bounds into this array no matter what.
  1607   EventStates(),
  1608   EventStates()
  1609 };
  1610 static_assert(MOZ_ARRAY_LENGTH(sPseudoClassStates) ==
  1611               nsCSSPseudoClasses::ePseudoClass_NotPseudoClass + 1,
  1612               "ePseudoClass_NotPseudoClass is no longer at the end of"
  1613               "sPseudoClassStates");
  1615 static bool
  1616 StateSelectorMatches(Element* aElement,
  1617                      nsCSSSelector* aSelector,
  1618                      NodeMatchContext& aNodeMatchContext,
  1619                      TreeMatchContext& aTreeMatchContext,
  1620                      bool* const aDependence,
  1621                      EventStates aStatesToCheck)
  1623   NS_PRECONDITION(!aStatesToCheck.IsEmpty(),
  1624                   "should only need to call StateSelectorMatches if "
  1625                   "aStatesToCheck is not empty");
  1627   const bool isNegated = aDependence != nullptr;
  1629   // Bit-based pseudo-classes
  1630   if (aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE) &&
  1631       aTreeMatchContext.mCompatMode == eCompatibility_NavQuirks &&
  1632       // global selector:
  1633       !aSelector->HasTagSelector() && !aSelector->mIDList &&
  1634       !aSelector->mClassList && !aSelector->mAttrList &&
  1635       // This (or the other way around) both make :not() asymmetric
  1636       // in quirks mode (and it's hard to work around since we're
  1637       // testing the current mNegations, not the first
  1638       // (unnegated)). This at least makes it closer to the spec.
  1639       !isNegated &&
  1640       // important for |IsQuirkEventSensitive|:
  1641       aElement->IsHTML() && !nsCSSRuleProcessor::IsLink(aElement) &&
  1642       !IsQuirkEventSensitive(aElement->Tag())) {
  1643     // In quirks mode, only make certain elements sensitive to
  1644     // selectors ":hover" and ":active".
  1645     return false;
  1648   if (aTreeMatchContext.mForStyling &&
  1649       aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_HOVER)) {
  1650     // Mark the element as having :hover-dependent style
  1651     aElement->SetHasRelevantHoverRules();
  1654   if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(aStatesToCheck)) {
  1655     if (aDependence) {
  1656       *aDependence = true;
  1658   } else {
  1659     EventStates contentState =
  1660       nsCSSRuleProcessor::GetContentStateForVisitedHandling(
  1661                                    aElement,
  1662                                    aTreeMatchContext,
  1663                                    aTreeMatchContext.VisitedHandling(),
  1664                                    aNodeMatchContext.mIsRelevantLink);
  1665     if (!contentState.HasAtLeastOneOfStates(aStatesToCheck)) {
  1666       return false;
  1670   return true;
  1673 static bool
  1674 StateSelectorMatches(Element* aElement,
  1675                      nsCSSSelector* aSelector,
  1676                      NodeMatchContext& aNodeMatchContext,
  1677                      TreeMatchContext& aTreeMatchContext)
  1679   for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
  1680        pseudoClass; pseudoClass = pseudoClass->mNext) {
  1681     EventStates statesToCheck = sPseudoClassStates[pseudoClass->mType];
  1682     if (!statesToCheck.IsEmpty() &&
  1683         !StateSelectorMatches(aElement, aSelector, aNodeMatchContext,
  1684                               aTreeMatchContext, nullptr, statesToCheck)) {
  1685       return false;
  1688   return true;
  1691 // |aDependence| has two functions:
  1692 //  * when non-null, it indicates that we're processing a negation,
  1693 //    which is done only when SelectorMatches calls itself recursively
  1694 //  * what it points to should be set to true whenever a test is skipped
  1695 //    because of aNodeMatchContent.mStateMask
  1696 static bool SelectorMatches(Element* aElement,
  1697                               nsCSSSelector* aSelector,
  1698                               NodeMatchContext& aNodeMatchContext,
  1699                               TreeMatchContext& aTreeMatchContext,
  1700                               bool* const aDependence = nullptr)
  1703   NS_PRECONDITION(!aSelector->IsPseudoElement(),
  1704                   "Pseudo-element snuck into SelectorMatches?");
  1705   NS_ABORT_IF_FALSE(aTreeMatchContext.mForStyling ||
  1706                     !aNodeMatchContext.mIsRelevantLink,
  1707                     "mIsRelevantLink should be set to false when mForStyling "
  1708                     "is false since we don't know how to set it correctly in "
  1709                     "Has(Attribute|State)DependentStyle");
  1711   // namespace/tag match
  1712   // optimization : bail out early if we can
  1713   if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
  1714        aElement->GetNameSpaceID() != aSelector->mNameSpace))
  1715     return false;
  1717   if (aSelector->mLowercaseTag) {
  1718     nsIAtom* selectorTag =
  1719       (aTreeMatchContext.mIsHTMLDocument && aElement->IsHTML()) ?
  1720         aSelector->mLowercaseTag : aSelector->mCasedTag;
  1721     if (selectorTag != aElement->Tag()) {
  1722       return false;
  1726   nsAtomList* IDList = aSelector->mIDList;
  1727   if (IDList) {
  1728     nsIAtom* id = aElement->GetID();
  1729     if (id) {
  1730       // case sensitivity: bug 93371
  1731       const bool isCaseSensitive =
  1732         aTreeMatchContext.mCompatMode != eCompatibility_NavQuirks;
  1734       if (isCaseSensitive) {
  1735         do {
  1736           if (IDList->mAtom != id) {
  1737             return false;
  1739           IDList = IDList->mNext;
  1740         } while (IDList);
  1741       } else {
  1742         // Use EqualsIgnoreASCIICase instead of full on unicode case conversion
  1743         // in order to save on performance. This is only used in quirks mode
  1744         // anyway.
  1745         nsDependentAtomString id1Str(id);
  1746         do {
  1747           if (!nsContentUtils::EqualsIgnoreASCIICase(id1Str,
  1748                  nsDependentAtomString(IDList->mAtom))) {
  1749             return false;
  1751           IDList = IDList->mNext;
  1752         } while (IDList);
  1754     } else {
  1755       // Element has no id but we have an id selector
  1756       return false;
  1760   nsAtomList* classList = aSelector->mClassList;
  1761   if (classList) {
  1762     // test for class match
  1763     const nsAttrValue *elementClasses = aElement->GetClasses();
  1764     if (!elementClasses) {
  1765       // Element has no classes but we have a class selector
  1766       return false;
  1769     // case sensitivity: bug 93371
  1770     const bool isCaseSensitive =
  1771       aTreeMatchContext.mCompatMode != eCompatibility_NavQuirks;
  1773     while (classList) {
  1774       if (!elementClasses->Contains(classList->mAtom,
  1775                                     isCaseSensitive ?
  1776                                       eCaseMatters : eIgnoreCase)) {
  1777         return false;
  1779       classList = classList->mNext;
  1783   const bool isNegated = (aDependence != nullptr);
  1784   // The selectors for which we set node bits are, unfortunately, early
  1785   // in this function (because they're pseudo-classes, which are
  1786   // generally quick to test, and thus earlier).  If they were later,
  1787   // we'd probably avoid setting those bits in more cases where setting
  1788   // them is unnecessary.
  1789   NS_ASSERTION(aNodeMatchContext.mStateMask.IsEmpty() ||
  1790                !aTreeMatchContext.mForStyling,
  1791                "mForStyling must be false if we're just testing for "
  1792                "state-dependence");
  1794   // test for pseudo class match
  1795   for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
  1796        pseudoClass; pseudoClass = pseudoClass->mNext) {
  1797     EventStates statesToCheck = sPseudoClassStates[pseudoClass->mType];
  1798     if (statesToCheck.IsEmpty()) {
  1799       // keep the cases here in the same order as the list in
  1800       // nsCSSPseudoClassList.h
  1801       switch (pseudoClass->mType) {
  1802       case nsCSSPseudoClasses::ePseudoClass_empty:
  1803         if (!checkGenericEmptyMatches(aElement, aTreeMatchContext, true)) {
  1804           return false;
  1806         break;
  1808       case nsCSSPseudoClasses::ePseudoClass_mozOnlyWhitespace:
  1809         if (!checkGenericEmptyMatches(aElement, aTreeMatchContext, false)) {
  1810           return false;
  1812         break;
  1814       case nsCSSPseudoClasses::ePseudoClass_mozEmptyExceptChildrenWithLocalname:
  1816           NS_ASSERTION(pseudoClass->u.mString, "Must have string!");
  1817           nsIContent *child = nullptr;
  1818           int32_t index = -1;
  1820           if (aTreeMatchContext.mForStyling)
  1821             // FIXME:  This isn't sufficient to handle:
  1822             //   :-moz-empty-except-children-with-localname() + E
  1823             //   :-moz-empty-except-children-with-localname() ~ E
  1824             // because we don't know to restyle the grandparent of the
  1825             // inserted/removed element (as in bug 534804 for :empty).
  1826             aElement->SetFlags(NODE_HAS_SLOW_SELECTOR);
  1827           do {
  1828             child = aElement->GetChildAt(++index);
  1829           } while (child &&
  1830                    (!IsSignificantChild(child, true, false) ||
  1831                     (child->GetNameSpaceID() == aElement->GetNameSpaceID() &&
  1832                      child->Tag()->Equals(nsDependentString(pseudoClass->u.mString)))));
  1833           if (child != nullptr) {
  1834             return false;
  1837         break;
  1839       case nsCSSPseudoClasses::ePseudoClass_lang:
  1841           NS_ASSERTION(nullptr != pseudoClass->u.mString, "null lang parameter");
  1842           if (!pseudoClass->u.mString || !*pseudoClass->u.mString) {
  1843             return false;
  1846           // We have to determine the language of the current element.  Since
  1847           // this is currently no property and since the language is inherited
  1848           // from the parent we have to be prepared to look at all parent
  1849           // nodes.  The language itself is encoded in the LANG attribute.
  1850           nsAutoString language;
  1851           aElement->GetLang(language);
  1852           if (!language.IsEmpty()) {
  1853             if (!nsStyleUtil::DashMatchCompare(language,
  1854                                                nsDependentString(pseudoClass->u.mString),
  1855                                                nsASCIICaseInsensitiveStringComparator())) {
  1856               return false;
  1858             // This pseudo-class matched; move on to the next thing
  1859             break;
  1862           nsIDocument* doc = aTreeMatchContext.mDocument;
  1863           if (doc) {
  1864             // Try to get the language from the HTTP header or if this
  1865             // is missing as well from the preferences.
  1866             // The content language can be a comma-separated list of
  1867             // language codes.
  1868             doc->GetContentLanguage(language);
  1870             nsDependentString langString(pseudoClass->u.mString);
  1871             language.StripWhitespace();
  1872             int32_t begin = 0;
  1873             int32_t len = language.Length();
  1874             while (begin < len) {
  1875               int32_t end = language.FindChar(char16_t(','), begin);
  1876               if (end == kNotFound) {
  1877                 end = len;
  1879               if (nsStyleUtil::DashMatchCompare(Substring(language, begin,
  1880                                                           end-begin),
  1881                                                 langString,
  1882                                                 nsASCIICaseInsensitiveStringComparator())) {
  1883                 break;
  1885               begin = end + 1;
  1887             if (begin < len) {
  1888               // This pseudo-class matched
  1889               break;
  1893           return false;
  1895         break;
  1897       case nsCSSPseudoClasses::ePseudoClass_mozBoundElement:
  1898         if (aTreeMatchContext.mScopedRoot != aElement) {
  1899           return false;
  1901         break;
  1903       case nsCSSPseudoClasses::ePseudoClass_root:
  1904         if (aElement != aElement->OwnerDoc()->GetRootElement()) {
  1905           return false;
  1907         break;
  1909       case nsCSSPseudoClasses::ePseudoClass_any:
  1911           nsCSSSelectorList *l;
  1912           for (l = pseudoClass->u.mSelectors; l; l = l->mNext) {
  1913             nsCSSSelector *s = l->mSelectors;
  1914             NS_ABORT_IF_FALSE(!s->mNext && !s->IsPseudoElement(),
  1915                               "parser failed");
  1916             if (SelectorMatches(aElement, s, aNodeMatchContext,
  1917                                 aTreeMatchContext)) {
  1918               break;
  1921           if (!l) {
  1922             return false;
  1925         break;
  1927       case nsCSSPseudoClasses::ePseudoClass_firstChild:
  1928         if (!edgeChildMatches(aElement, aTreeMatchContext, true, false)) {
  1929           return false;
  1931         break;
  1933       case nsCSSPseudoClasses::ePseudoClass_firstNode:
  1935           nsIContent *firstNode = nullptr;
  1936           nsIContent *parent = aElement->GetParent();
  1937           if (parent) {
  1938             if (aTreeMatchContext.mForStyling)
  1939               parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
  1941             int32_t index = -1;
  1942             do {
  1943               firstNode = parent->GetChildAt(++index);
  1944               // stop at first non-comment and non-whitespace node
  1945             } while (firstNode &&
  1946                      !IsSignificantChild(firstNode, true, false));
  1948           if (aElement != firstNode) {
  1949             return false;
  1952         break;
  1954       case nsCSSPseudoClasses::ePseudoClass_lastChild:
  1955         if (!edgeChildMatches(aElement, aTreeMatchContext, false, true)) {
  1956           return false;
  1958         break;
  1960       case nsCSSPseudoClasses::ePseudoClass_lastNode:
  1962           nsIContent *lastNode = nullptr;
  1963           nsIContent *parent = aElement->GetParent();
  1964           if (parent) {
  1965             if (aTreeMatchContext.mForStyling)
  1966               parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
  1968             uint32_t index = parent->GetChildCount();
  1969             do {
  1970               lastNode = parent->GetChildAt(--index);
  1971               // stop at first non-comment and non-whitespace node
  1972             } while (lastNode &&
  1973                      !IsSignificantChild(lastNode, true, false));
  1975           if (aElement != lastNode) {
  1976             return false;
  1979         break;
  1981       case nsCSSPseudoClasses::ePseudoClass_onlyChild:
  1982         if (!edgeChildMatches(aElement, aTreeMatchContext, true, true)) {
  1983           return false;
  1985         break;
  1987       case nsCSSPseudoClasses::ePseudoClass_firstOfType:
  1988         if (!edgeOfTypeMatches(aElement, aTreeMatchContext, true, false)) {
  1989           return false;
  1991         break;
  1993       case nsCSSPseudoClasses::ePseudoClass_lastOfType:
  1994         if (!edgeOfTypeMatches(aElement, aTreeMatchContext, false, true)) {
  1995           return false;
  1997         break;
  1999       case nsCSSPseudoClasses::ePseudoClass_onlyOfType:
  2000         if (!edgeOfTypeMatches(aElement, aTreeMatchContext, true, true)) {
  2001           return false;
  2003         break;
  2005       case nsCSSPseudoClasses::ePseudoClass_nthChild:
  2006         if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
  2007                                     false, false)) {
  2008           return false;
  2010         break;
  2012       case nsCSSPseudoClasses::ePseudoClass_nthLastChild:
  2013         if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
  2014                                     false, true)) {
  2015           return false;
  2017       break;
  2019       case nsCSSPseudoClasses::ePseudoClass_nthOfType:
  2020         if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
  2021                                     true, false)) {
  2022           return false;
  2024         break;
  2026       case nsCSSPseudoClasses::ePseudoClass_nthLastOfType:
  2027         if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
  2028                                     true, true)) {
  2029           return false;
  2031         break;
  2033       case nsCSSPseudoClasses::ePseudoClass_mozIsHTML:
  2034         if (!aTreeMatchContext.mIsHTMLDocument || !aElement->IsHTML()) {
  2035           return false;
  2037         break;
  2039       case nsCSSPseudoClasses::ePseudoClass_mozSystemMetric:
  2041           nsCOMPtr<nsIAtom> metric = do_GetAtom(pseudoClass->u.mString);
  2042           if (!nsCSSRuleProcessor::HasSystemMetric(metric)) {
  2043             return false;
  2046         break;
  2048       case nsCSSPseudoClasses::ePseudoClass_mozLocaleDir:
  2050           bool docIsRTL =
  2051             aTreeMatchContext.mDocument->GetDocumentState().
  2052               HasState(NS_DOCUMENT_STATE_RTL_LOCALE);
  2054           nsDependentString dirString(pseudoClass->u.mString);
  2055           NS_ASSERTION(dirString.EqualsLiteral("ltr") ||
  2056                        dirString.EqualsLiteral("rtl"),
  2057                        "invalid value for -moz-locale-dir");
  2059           if (dirString.EqualsLiteral("rtl") != docIsRTL) {
  2060             return false;
  2063         break;
  2065       case nsCSSPseudoClasses::ePseudoClass_mozLWTheme:
  2067           if (aTreeMatchContext.mDocument->GetDocumentLWTheme() <=
  2068                 nsIDocument::Doc_Theme_None) {
  2069             return false;
  2072         break;
  2074       case nsCSSPseudoClasses::ePseudoClass_mozLWThemeBrightText:
  2076           if (aTreeMatchContext.mDocument->GetDocumentLWTheme() !=
  2077                 nsIDocument::Doc_Theme_Bright) {
  2078             return false;
  2081         break;
  2083       case nsCSSPseudoClasses::ePseudoClass_mozLWThemeDarkText:
  2085           if (aTreeMatchContext.mDocument->GetDocumentLWTheme() !=
  2086                 nsIDocument::Doc_Theme_Dark) {
  2087             return false;
  2090         break;
  2092       case nsCSSPseudoClasses::ePseudoClass_mozWindowInactive:
  2093         if (!aTreeMatchContext.mDocument->GetDocumentState().
  2094                HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
  2095           return false;
  2097         break;
  2099       case nsCSSPseudoClasses::ePseudoClass_mozTableBorderNonzero:
  2101           if (!aElement->IsHTML(nsGkAtoms::table)) {
  2102             return false;
  2104           const nsAttrValue *val = aElement->GetParsedAttr(nsGkAtoms::border);
  2105           if (!val ||
  2106               (val->Type() == nsAttrValue::eInteger &&
  2107                val->GetIntegerValue() == 0)) {
  2108             return false;
  2111         break;
  2113       case nsCSSPseudoClasses::ePseudoClass_dir:
  2115           if (aDependence) {
  2116             EventStates states
  2117               = sPseudoClassStateDependences[pseudoClass->mType];
  2118             if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(states)) {
  2119               *aDependence = true;
  2120               return false;
  2124           // if we only had to consider HTML, directionality would be exclusively
  2125           // LTR or RTL, and this could be just
  2126           //
  2127           //  if (dirString.EqualsLiteral("rtl") !=
  2128           //    aElement->StyleState().HasState(NS_EVENT_STATE_RTL)
  2129           //
  2130           // However, in markup languages where there is no direction attribute
  2131           // we have to consider the possibility that neither -moz-dir(rtl) nor
  2132           // -moz-dir(ltr) matches.
  2133           EventStates state = aElement->StyleState();
  2134           bool elementIsRTL = state.HasState(NS_EVENT_STATE_RTL);
  2135           bool elementIsLTR = state.HasState(NS_EVENT_STATE_LTR);
  2136           nsDependentString dirString(pseudoClass->u.mString);
  2138           if ((dirString.EqualsLiteral("rtl") && !elementIsRTL) ||
  2139               (dirString.EqualsLiteral("ltr") && !elementIsLTR)) {
  2140             return false;
  2143         break;
  2145       case nsCSSPseudoClasses::ePseudoClass_scope:
  2146         if (aTreeMatchContext.mForScopedStyle) {
  2147           if (aTreeMatchContext.mCurrentStyleScope) {
  2148             // If mCurrentStyleScope is null, aElement must be the style
  2149             // scope root.  This is because the PopStyleScopeForSelectorMatching
  2150             // call in SelectorMatchesTree sets mCurrentStyleScope to null
  2151             // as soon as we visit the style scope element, and we won't
  2152             // progress further up the tree after this call to
  2153             // SelectorMatches.  Thus if mCurrentStyleScope is still set,
  2154             // we know the selector does not match.
  2155             return false;
  2157         } else if (aTreeMatchContext.HasSpecifiedScope()) {
  2158           if (!aTreeMatchContext.IsScopeElement(aElement)) {
  2159             return false;
  2161         } else {
  2162           if (aElement != aElement->OwnerDoc()->GetRootElement()) {
  2163             return false;
  2166         break;
  2168       default:
  2169         NS_ABORT_IF_FALSE(false, "How did that happen?");
  2171     } else {
  2172       if (!StateSelectorMatches(aElement, aSelector, aNodeMatchContext,
  2173                                 aTreeMatchContext, aDependence,
  2174                                 statesToCheck)) {
  2175         return false;
  2180   bool result = true;
  2181   if (aSelector->mAttrList) {
  2182     // test for attribute match
  2183     if (!aElement->HasAttrs()) {
  2184       // if no attributes on the content, no match
  2185       return false;
  2186     } else {
  2187       result = true;
  2188       nsAttrSelector* attr = aSelector->mAttrList;
  2189       nsIAtom* matchAttribute;
  2191       do {
  2192         bool isHTML =
  2193           (aTreeMatchContext.mIsHTMLDocument && aElement->IsHTML());
  2194         matchAttribute = isHTML ? attr->mLowercaseAttr : attr->mCasedAttr;
  2195         if (attr->mNameSpace == kNameSpaceID_Unknown) {
  2196           // Attr selector with a wildcard namespace.  We have to examine all
  2197           // the attributes on our content node....  This sort of selector is
  2198           // essentially a boolean OR, over all namespaces, of equivalent attr
  2199           // selectors with those namespaces.  So to evaluate whether it
  2200           // matches, evaluate for each namespace (the only namespaces that
  2201           // have a chance at matching, of course, are ones that the element
  2202           // actually has attributes in), short-circuiting if we ever match.
  2203           result = false;
  2204           const nsAttrName* attrName;
  2205           for (uint32_t i = 0; (attrName = aElement->GetAttrNameAt(i)); ++i) {
  2206             if (attrName->LocalName() != matchAttribute) {
  2207               continue;
  2209             if (attr->mFunction == NS_ATTR_FUNC_SET) {
  2210               result = true;
  2211             } else {
  2212               nsAutoString value;
  2213 #ifdef DEBUG
  2214               bool hasAttr =
  2215 #endif
  2216                 aElement->GetAttr(attrName->NamespaceID(),
  2217                                   attrName->LocalName(), value);
  2218               NS_ASSERTION(hasAttr, "GetAttrNameAt lied");
  2219               result = AttrMatchesValue(attr, value, isHTML);
  2222             // At this point |result| has been set by us
  2223             // explicitly in this loop.  If it's false, we may still match
  2224             // -- the content may have another attribute with the same name but
  2225             // in a different namespace.  But if it's true, we are done (we
  2226             // can short-circuit the boolean OR described above).
  2227             if (result) {
  2228               break;
  2232         else if (attr->mFunction == NS_ATTR_FUNC_EQUALS) {
  2233           result =
  2234             aElement->
  2235               AttrValueIs(attr->mNameSpace, matchAttribute, attr->mValue,
  2236                           (!isHTML || attr->mCaseSensitive) ? eCaseMatters
  2237                                                             : eIgnoreCase);
  2239         else if (!aElement->HasAttr(attr->mNameSpace, matchAttribute)) {
  2240           result = false;
  2242         else if (attr->mFunction != NS_ATTR_FUNC_SET) {
  2243           nsAutoString value;
  2244 #ifdef DEBUG
  2245           bool hasAttr =
  2246 #endif
  2247               aElement->GetAttr(attr->mNameSpace, matchAttribute, value);
  2248           NS_ASSERTION(hasAttr, "HasAttr lied");
  2249           result = AttrMatchesValue(attr, value, isHTML);
  2252         attr = attr->mNext;
  2253       } while (attr && result);
  2257   // apply SelectorMatches to the negated selectors in the chain
  2258   if (!isNegated) {
  2259     for (nsCSSSelector *negation = aSelector->mNegations;
  2260          result && negation; negation = negation->mNegations) {
  2261       bool dependence = false;
  2262       result = !SelectorMatches(aElement, negation, aNodeMatchContext,
  2263                                 aTreeMatchContext, &dependence);
  2264       // If the selector does match due to the dependence on
  2265       // aNodeMatchContext.mStateMask, then we want to keep result true
  2266       // so that the final result of SelectorMatches is true.  Doing so
  2267       // tells StateEnumFunc that there is a dependence on the state.
  2268       result = result || dependence;
  2271   return result;
  2274 #undef STATE_CHECK
  2276 // Right now, there are four operators:
  2277 //   ' ', the descendant combinator, is greedy
  2278 //   '~', the indirect adjacent sibling combinator, is greedy
  2279 //   '+' and '>', the direct adjacent sibling and child combinators, are not
  2280 #define NS_IS_GREEDY_OPERATOR(ch) \
  2281   ((ch) == char16_t(' ') || (ch) == char16_t('~'))
  2283 static bool SelectorMatchesTree(Element* aPrevElement,
  2284                                   nsCSSSelector* aSelector,
  2285                                   TreeMatchContext& aTreeMatchContext,
  2286                                   bool aLookForRelevantLink)
  2288   MOZ_ASSERT(!aSelector || !aSelector->IsPseudoElement());
  2289   nsCSSSelector* selector = aSelector;
  2290   Element* prevElement = aPrevElement;
  2291   while (selector) { // check compound selectors
  2292     NS_ASSERTION(!selector->mNext ||
  2293                  selector->mNext->mOperator != char16_t(0),
  2294                  "compound selector without combinator");
  2296     // If after the previous selector match we are now outside the
  2297     // current style scope, we don't need to match any further.
  2298     if (aTreeMatchContext.mForScopedStyle &&
  2299         !aTreeMatchContext.IsWithinStyleScopeForSelectorMatching()) {
  2300       return false;
  2303     // for adjacent sibling combinators, the content to test against the
  2304     // selector is the previous sibling *element*
  2305     Element* element = nullptr;
  2306     if (char16_t('+') == selector->mOperator ||
  2307         char16_t('~') == selector->mOperator) {
  2308       // The relevant link must be an ancestor of the node being matched.
  2309       aLookForRelevantLink = false;
  2310       nsIContent* parent = prevElement->GetParent();
  2311       if (parent) {
  2312         if (aTreeMatchContext.mForStyling)
  2313           parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
  2315         element = prevElement->GetPreviousElementSibling();
  2318     // for descendant combinators and child combinators, the element
  2319     // to test against is the parent
  2320     else {
  2321       nsIContent *content = prevElement->GetParent();
  2322       // GetParent could return a document fragment; we only want
  2323       // element parents.
  2324       if (content && content->IsElement()) {
  2325         element = content->AsElement();
  2326         if (aTreeMatchContext.mForScopedStyle) {
  2327           // We are moving up to the parent element; tell the
  2328           // TreeMatchContext, so that in case this element is the
  2329           // style scope element, selector matching stops before we
  2330           // traverse further up the tree.
  2331           aTreeMatchContext.PopStyleScopeForSelectorMatching(element);
  2334         // Compatibility hack: First try matching this selector as though the
  2335         // <xbl:children> element wasn't in the tree to allow old selectors
  2336         // were written before <xbl:children> participated in CSS selector
  2337         // matching to work.
  2338         if (selector->mOperator == '>' && element->IsActiveChildrenElement()) {
  2339           Element* styleScope = aTreeMatchContext.mCurrentStyleScope;
  2340           if (SelectorMatchesTree(element, selector, aTreeMatchContext,
  2341                                   aLookForRelevantLink)) {
  2342             // It matched, don't try matching on the <xbl:children> element at
  2343             // all.
  2344             return true;
  2346           // We want to reset mCurrentStyleScope on aTreeMatchContext
  2347           // back to its state before the SelectorMatchesTree call, in
  2348           // case that call happens to traverse past the style scope element
  2349           // and sets it to null.
  2350           aTreeMatchContext.mCurrentStyleScope = styleScope;
  2354     if (!element) {
  2355       return false;
  2357     NodeMatchContext nodeContext(EventStates(),
  2358                                  aLookForRelevantLink &&
  2359                                    nsCSSRuleProcessor::IsLink(element));
  2360     if (nodeContext.mIsRelevantLink) {
  2361       // If we find an ancestor of the matched node that is a link
  2362       // during the matching process, then it's the relevant link (see
  2363       // constructor call above).
  2364       // Since we are still matching against selectors that contain
  2365       // :visited (they'll just fail), we will always find such a node
  2366       // during the selector matching process if there is a relevant
  2367       // link that can influence selector matching.
  2368       aLookForRelevantLink = false;
  2369       aTreeMatchContext.SetHaveRelevantLink();
  2371     if (SelectorMatches(element, selector, nodeContext, aTreeMatchContext)) {
  2372       // to avoid greedy matching, we need to recur if this is a
  2373       // descendant or general sibling combinator and the next
  2374       // combinator is different, but we can make an exception for
  2375       // sibling, then parent, since a sibling's parent is always the
  2376       // same.
  2377       if (NS_IS_GREEDY_OPERATOR(selector->mOperator) &&
  2378           selector->mNext &&
  2379           selector->mNext->mOperator != selector->mOperator &&
  2380           !(selector->mOperator == '~' &&
  2381             NS_IS_ANCESTOR_OPERATOR(selector->mNext->mOperator))) {
  2383         // pretend the selector didn't match, and step through content
  2384         // while testing the same selector
  2386         // This approach is slightly strange in that when it recurs
  2387         // it tests from the top of the content tree, down.  This
  2388         // doesn't matter much for performance since most selectors
  2389         // don't match.  (If most did, it might be faster...)
  2390         Element* styleScope = aTreeMatchContext.mCurrentStyleScope;
  2391         if (SelectorMatchesTree(element, selector, aTreeMatchContext,
  2392                                 aLookForRelevantLink)) {
  2393           return true;
  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;
  2401       selector = selector->mNext;
  2403     else {
  2404       // for adjacent sibling and child combinators, if we didn't find
  2405       // a match, we're done
  2406       if (!NS_IS_GREEDY_OPERATOR(selector->mOperator)) {
  2407         return false;  // parent was required to match
  2410     prevElement = element;
  2412   return true; // all the selectors matched.
  2415 static inline
  2416 void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
  2417                      ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
  2418                      AncestorFilter *ancestorFilter)
  2420   if (nodeContext.mIsRelevantLink) {
  2421     data->mTreeMatchContext.SetHaveRelevantLink();
  2423   if (ancestorFilter &&
  2424       !ancestorFilter->MightHaveMatchingAncestor<RuleValue::eMaxAncestorHashes>(
  2425           value.mAncestorSelectorHashes)) {
  2426     // We won't match; nothing else to do here
  2427     return;
  2429   if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
  2430                                                                 data->mScope)) {
  2431     // The selector is for a rule in a scoped style sheet, and the subject
  2432     // of the selector matching is not in its scope.
  2433     return;
  2435   nsCSSSelector* selector = aSelector;
  2436   if (selector->IsPseudoElement()) {
  2437     PseudoElementRuleProcessorData* pdata =
  2438       static_cast<PseudoElementRuleProcessorData*>(data);
  2439     if (!pdata->mPseudoElement && selector->mPseudoClassList) {
  2440       // We can get here when calling getComputedStyle(aElt, aPseudo) if:
  2441       //
  2442       //   * aPseudo is a pseudo-element that supports a user action
  2443       //     pseudo-class, like "::-moz-placeholder";
  2444       //   * there is a style rule that uses a pseudo-class on this
  2445       //     pseudo-element in the document, like ::-moz-placeholder:hover; and
  2446       //   * aElt does not have such a pseudo-element.
  2447       //
  2448       // We know that the selector can't match, since there is no element for
  2449       // the user action pseudo-class to match against.
  2450       return;
  2452     if (!StateSelectorMatches(pdata->mPseudoElement, aSelector, nodeContext,
  2453                               data->mTreeMatchContext)) {
  2454       return;
  2456     selector = selector->mNext;
  2458   if (SelectorMatches(data->mElement, selector, nodeContext,
  2459                       data->mTreeMatchContext)) {
  2460     nsCSSSelector *next = selector->mNext;
  2461     if (!next || SelectorMatchesTree(data->mElement, next,
  2462                                      data->mTreeMatchContext,
  2463                                      !nodeContext.mIsRelevantLink)) {
  2464       css::StyleRule *rule = value.mRule;
  2465       rule->RuleMatched();
  2466       data->mRuleWalker->Forward(rule);
  2467       // nsStyleSet will deal with the !important rule
  2472 /* virtual */ void
  2473 nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData *aData)
  2475   RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
  2477   if (cascade) {
  2478     NodeMatchContext nodeContext(EventStates(),
  2479                                  nsCSSRuleProcessor::IsLink(aData->mElement));
  2480     cascade->mRuleHash.EnumerateAllRules(aData->mElement, aData, nodeContext);
  2484 /* virtual */ void
  2485 nsCSSRuleProcessor::RulesMatching(PseudoElementRuleProcessorData* aData)
  2487   RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
  2489   if (cascade) {
  2490     RuleHash* ruleHash = cascade->mPseudoElementRuleHashes[aData->mPseudoType];
  2491     if (ruleHash) {
  2492       NodeMatchContext nodeContext(EventStates(),
  2493                                    nsCSSRuleProcessor::IsLink(aData->mElement));
  2494       ruleHash->EnumerateAllRules(aData->mElement, aData, nodeContext);
  2499 /* virtual */ void
  2500 nsCSSRuleProcessor::RulesMatching(AnonBoxRuleProcessorData* aData)
  2502   RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
  2504   if (cascade && cascade->mAnonBoxRules.entryCount) {
  2505     RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>
  2506       (PL_DHashTableOperate(&cascade->mAnonBoxRules, aData->mPseudoTag,
  2507                             PL_DHASH_LOOKUP));
  2508     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
  2509       nsTArray<RuleValue>& rules = entry->mRules;
  2510       for (RuleValue *value = rules.Elements(), *end = value + rules.Length();
  2511            value != end; ++value) {
  2512         value->mRule->RuleMatched();
  2513         aData->mRuleWalker->Forward(value->mRule);
  2519 #ifdef MOZ_XUL
  2520 /* virtual */ void
  2521 nsCSSRuleProcessor::RulesMatching(XULTreeRuleProcessorData* aData)
  2523   RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
  2525   if (cascade && cascade->mXULTreeRules.entryCount) {
  2526     RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>
  2527       (PL_DHashTableOperate(&cascade->mXULTreeRules, aData->mPseudoTag,
  2528                             PL_DHASH_LOOKUP));
  2529     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
  2530       NodeMatchContext nodeContext(EventStates(),
  2531                                    nsCSSRuleProcessor::IsLink(aData->mElement));
  2532       nsTArray<RuleValue>& rules = entry->mRules;
  2533       for (RuleValue *value = rules.Elements(), *end = value + rules.Length();
  2534            value != end; ++value) {
  2535         if (aData->mComparator->PseudoMatches(value->mSelector)) {
  2536           ContentEnumFunc(*value, value->mSelector->mNext, aData, nodeContext,
  2537                           nullptr);
  2543 #endif
  2545 static inline nsRestyleHint RestyleHintForOp(char16_t oper)
  2547   if (oper == char16_t('+') || oper == char16_t('~')) {
  2548     return eRestyle_LaterSiblings;
  2551   if (oper != char16_t(0)) {
  2552     return eRestyle_Subtree;
  2555   return eRestyle_Self;
  2558 nsRestyleHint
  2559 nsCSSRuleProcessor::HasStateDependentStyle(ElementDependentRuleProcessorData* aData,
  2560                                            Element* aStatefulElement,
  2561                                            nsCSSPseudoElements::Type aPseudoType,
  2562                                            EventStates aStateMask)
  2564   MOZ_ASSERT(!aData->mTreeMatchContext.mForScopedStyle,
  2565              "mCurrentStyleScope will need to be saved and restored after the "
  2566              "SelectorMatchesTree call");
  2568   bool isPseudoElement =
  2569     aPseudoType != nsCSSPseudoElements::ePseudo_NotPseudoElement;
  2571   RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
  2573   // Look up the content node in the state rule list, which points to
  2574   // any (CSS2 definition) simple selector (whether or not it is the
  2575   // subject) that has a state pseudo-class on it.  This means that this
  2576   // code will be matching selectors that aren't real selectors in any
  2577   // stylesheet (e.g., if there is a selector "body > p:hover > a", then
  2578   // "body > p:hover" will be in |cascade->mStateSelectors|).  Note that
  2579   // |ComputeSelectorStateDependence| below determines which selectors are in
  2580   // |cascade->mStateSelectors|.
  2581   nsRestyleHint hint = nsRestyleHint(0);
  2582   if (cascade) {
  2583     StateSelector *iter = cascade->mStateSelectors.Elements(),
  2584                   *end = iter + cascade->mStateSelectors.Length();
  2585     NodeMatchContext nodeContext(aStateMask, false);
  2586     for(; iter != end; ++iter) {
  2587       nsCSSSelector* selector = iter->mSelector;
  2588       EventStates states = iter->mStates;
  2590       if (selector->IsPseudoElement() != isPseudoElement) {
  2591         continue;
  2594       nsCSSSelector* selectorForPseudo;
  2595       if (isPseudoElement) {
  2596         if (selector->PseudoType() != aPseudoType) {
  2597           continue;
  2599         selectorForPseudo = selector;
  2600         selector = selector->mNext;
  2603       nsRestyleHint possibleChange = RestyleHintForOp(selector->mOperator);
  2605       // If hint already includes all the bits of possibleChange,
  2606       // don't bother calling SelectorMatches, since even if it returns false
  2607       // hint won't change.
  2608       // Also don't bother calling SelectorMatches if none of the
  2609       // states passed in are relevant here.
  2610       if ((possibleChange & ~hint) &&
  2611           states.HasAtLeastOneOfStates(aStateMask) &&
  2612           // We can optimize away testing selectors that only involve :hover, a
  2613           // namespace, and a tag name against nodes that don't have the
  2614           // NodeHasRelevantHoverRules flag: such a selector didn't match
  2615           // the tag name or namespace the first time around (since the :hover
  2616           // didn't set the NodeHasRelevantHoverRules flag), so it won't
  2617           // match it now.  Check for our selector only having :hover states, or
  2618           // the element having the hover rules flag, or the selector having
  2619           // some sort of non-namespace, non-tagname data in it.
  2620           (states != NS_EVENT_STATE_HOVER ||
  2621            aStatefulElement->HasRelevantHoverRules() ||
  2622            selector->mIDList || selector->mClassList ||
  2623            // We generally expect an mPseudoClassList, since we have a :hover.
  2624            // The question is whether we have anything else in there.
  2625            (selector->mPseudoClassList &&
  2626             (selector->mPseudoClassList->mNext ||
  2627              selector->mPseudoClassList->mType !=
  2628                nsCSSPseudoClasses::ePseudoClass_hover)) ||
  2629            selector->mAttrList || selector->mNegations) &&
  2630           (!isPseudoElement ||
  2631            StateSelectorMatches(aStatefulElement, selectorForPseudo,
  2632                                 nodeContext, aData->mTreeMatchContext,
  2633                                 nullptr, aStateMask)) &&
  2634           SelectorMatches(aData->mElement, selector, nodeContext,
  2635                           aData->mTreeMatchContext) &&
  2636           SelectorMatchesTree(aData->mElement, selector->mNext,
  2637                               aData->mTreeMatchContext,
  2638                               false))
  2640         hint = nsRestyleHint(hint | possibleChange);
  2644   return hint;
  2647 nsRestyleHint
  2648 nsCSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData)
  2650   return HasStateDependentStyle(aData,
  2651                                 aData->mElement,
  2652                                 nsCSSPseudoElements::ePseudo_NotPseudoElement,
  2653                                 aData->mStateMask);
  2656 nsRestyleHint
  2657 nsCSSRuleProcessor::HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData)
  2659   return HasStateDependentStyle(aData,
  2660                                 aData->mPseudoElement,
  2661                                 aData->mPseudoType,
  2662                                 aData->mStateMask);
  2665 bool
  2666 nsCSSRuleProcessor::HasDocumentStateDependentStyle(StateRuleProcessorData* aData)
  2668   RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
  2670   return cascade && cascade->mSelectorDocumentStates.HasAtLeastOneOfStates(aData->mStateMask);
  2673 struct AttributeEnumData {
  2674   AttributeEnumData(AttributeRuleProcessorData *aData)
  2675     : data(aData), change(nsRestyleHint(0)) {}
  2677   AttributeRuleProcessorData *data;
  2678   nsRestyleHint change;
  2679 };
  2682 static void
  2683 AttributeEnumFunc(nsCSSSelector* aSelector, AttributeEnumData* aData)
  2685   AttributeRuleProcessorData *data = aData->data;
  2687   if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
  2688                                                                 data->mScope)) {
  2689     // The selector is for a rule in a scoped style sheet, and the subject
  2690     // of the selector matching is not in its scope.
  2691     return;
  2694   nsRestyleHint possibleChange = RestyleHintForOp(aSelector->mOperator);
  2696   // If enumData->change already includes all the bits of possibleChange, don't
  2697   // bother calling SelectorMatches, since even if it returns false
  2698   // enumData->change won't change.
  2699   NodeMatchContext nodeContext(EventStates(), false);
  2700   if ((possibleChange & ~(aData->change)) &&
  2701       SelectorMatches(data->mElement, aSelector, nodeContext,
  2702                       data->mTreeMatchContext) &&
  2703       SelectorMatchesTree(data->mElement, aSelector->mNext,
  2704                           data->mTreeMatchContext, false)) {
  2705     aData->change = nsRestyleHint(aData->change | possibleChange);
  2709 static MOZ_ALWAYS_INLINE void
  2710 EnumerateSelectors(nsTArray<nsCSSSelector*>& aSelectors, AttributeEnumData* aData)
  2712   nsCSSSelector **iter = aSelectors.Elements(),
  2713                 **end = iter + aSelectors.Length();
  2714   for (; iter != end; ++iter) {
  2715     AttributeEnumFunc(*iter, aData);
  2719 nsRestyleHint
  2720 nsCSSRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData)
  2722   //  We could try making use of aData->mModType, but :not rules make it a bit
  2723   //  of a pain to do so...  So just ignore it for now.
  2725   AttributeEnumData data(aData);
  2727   // Don't do our special handling of certain attributes if the attr
  2728   // hasn't changed yet.
  2729   if (aData->mAttrHasChanged) {
  2730     // check for the lwtheme and lwthemetextcolor attribute on root XUL elements
  2731     if ((aData->mAttribute == nsGkAtoms::lwtheme ||
  2732          aData->mAttribute == nsGkAtoms::lwthemetextcolor) &&
  2733         aData->mElement->GetNameSpaceID() == kNameSpaceID_XUL &&
  2734         aData->mElement == aData->mElement->OwnerDoc()->GetRootElement())
  2736         data.change = nsRestyleHint(data.change | eRestyle_Subtree);
  2739     // We don't know the namespace of the attribute, and xml:lang applies to
  2740     // all elements.  If the lang attribute changes, we need to restyle our
  2741     // whole subtree, since the :lang selector on our descendants can examine
  2742     // our lang attribute.
  2743     if (aData->mAttribute == nsGkAtoms::lang) {
  2744       data.change = nsRestyleHint(data.change | eRestyle_Subtree);
  2748   RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
  2750   // Since we get both before and after notifications for attributes, we
  2751   // don't have to ignore aData->mAttribute while matching.  Just check
  2752   // whether we have selectors relevant to aData->mAttribute that we
  2753   // match.  If this is the before change notification, that will catch
  2754   // rules we might stop matching; if the after change notification, the
  2755   // ones we might have started matching.
  2756   if (cascade) {
  2757     if (aData->mAttribute == aData->mElement->GetIDAttributeName()) {
  2758       nsIAtom* id = aData->mElement->GetID();
  2759       if (id) {
  2760         AtomSelectorEntry *entry =
  2761           static_cast<AtomSelectorEntry*>
  2762                      (PL_DHashTableOperate(&cascade->mIdSelectors,
  2763                                            id, PL_DHASH_LOOKUP));
  2764         if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
  2765           EnumerateSelectors(entry->mSelectors, &data);
  2769       EnumerateSelectors(cascade->mPossiblyNegatedIDSelectors, &data);
  2772     if (aData->mAttribute == aData->mElement->GetClassAttributeName()) {
  2773       const nsAttrValue* elementClasses = aData->mElement->GetClasses();
  2774       if (elementClasses) {
  2775         int32_t atomCount = elementClasses->GetAtomCount();
  2776         for (int32_t i = 0; i < atomCount; ++i) {
  2777           nsIAtom* curClass = elementClasses->AtomAt(i);
  2778           AtomSelectorEntry *entry =
  2779             static_cast<AtomSelectorEntry*>
  2780                        (PL_DHashTableOperate(&cascade->mClassSelectors,
  2781                                              curClass, PL_DHASH_LOOKUP));
  2782           if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
  2783             EnumerateSelectors(entry->mSelectors, &data);
  2788       EnumerateSelectors(cascade->mPossiblyNegatedClassSelectors, &data);
  2791     AtomSelectorEntry *entry =
  2792       static_cast<AtomSelectorEntry*>
  2793                  (PL_DHashTableOperate(&cascade->mAttributeSelectors,
  2794                                        aData->mAttribute, PL_DHASH_LOOKUP));
  2795     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
  2796       EnumerateSelectors(entry->mSelectors, &data);
  2800   return data.change;
  2803 /* virtual */ bool
  2804 nsCSSRuleProcessor::MediumFeaturesChanged(nsPresContext* aPresContext)
  2806   RuleCascadeData *old = mRuleCascades;
  2807   // We don't want to do anything if there aren't any sets of rules
  2808   // cached yet (or somebody cleared them and is thus responsible for
  2809   // rebuilding things), since we should not build the rule cascade too
  2810   // early (e.g., before we know whether the quirk style sheet should be
  2811   // enabled).  And if there's nothing cached, it doesn't matter if
  2812   // anything changed.  See bug 448281.
  2813   if (old) {
  2814     RefreshRuleCascade(aPresContext);
  2816   return (old != mRuleCascades);
  2819 /* virtual */ size_t
  2820 nsCSSRuleProcessor::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
  2822   size_t n = 0;
  2823   n += mSheets.SizeOfExcludingThis(aMallocSizeOf);
  2824   for (RuleCascadeData* cascade = mRuleCascades; cascade;
  2825        cascade = cascade->mNext) {
  2826     n += cascade->SizeOfIncludingThis(aMallocSizeOf);
  2829   return n;
  2832 /* virtual */ size_t
  2833 nsCSSRuleProcessor::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
  2835   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
  2838 // Append all the currently-active font face rules to aArray.  Return
  2839 // true for success and false for failure.
  2840 bool
  2841 nsCSSRuleProcessor::AppendFontFaceRules(
  2842                               nsPresContext *aPresContext,
  2843                               nsTArray<nsFontFaceRuleContainer>& aArray)
  2845   RuleCascadeData* cascade = GetRuleCascade(aPresContext);
  2847   if (cascade) {
  2848     if (!aArray.AppendElements(cascade->mFontFaceRules))
  2849       return false;
  2852   return true;
  2855 nsCSSKeyframesRule*
  2856 nsCSSRuleProcessor::KeyframesRuleForName(nsPresContext* aPresContext,
  2857                                          const nsString& aName)
  2859   RuleCascadeData* cascade = GetRuleCascade(aPresContext);
  2861   if (cascade) {
  2862     return cascade->mKeyframesRuleTable.Get(aName);
  2865   return nullptr;
  2868 // Append all the currently-active page rules to aArray.  Return
  2869 // true for success and false for failure.
  2870 bool
  2871 nsCSSRuleProcessor::AppendPageRules(
  2872                               nsPresContext* aPresContext,
  2873                               nsTArray<nsCSSPageRule*>& aArray)
  2875   RuleCascadeData* cascade = GetRuleCascade(aPresContext);
  2877   if (cascade) {
  2878     if (!aArray.AppendElements(cascade->mPageRules)) {
  2879       return false;
  2883   return true;
  2886 bool
  2887 nsCSSRuleProcessor::AppendFontFeatureValuesRules(
  2888                               nsPresContext *aPresContext,
  2889                               nsTArray<nsCSSFontFeatureValuesRule*>& aArray)
  2891   RuleCascadeData* cascade = GetRuleCascade(aPresContext);
  2893   if (cascade) {
  2894     if (!aArray.AppendElements(cascade->mFontFeatureValuesRules))
  2895       return false;
  2898   return true;
  2901 nsresult
  2902 nsCSSRuleProcessor::ClearRuleCascades()
  2904   // We rely on our caller (perhaps indirectly) to do something that
  2905   // will rebuild style data and the user font set (either
  2906   // nsIPresShell::ReconstructStyleData or
  2907   // nsPresContext::RebuildAllStyleData).
  2908   RuleCascadeData *data = mRuleCascades;
  2909   mRuleCascades = nullptr;
  2910   while (data) {
  2911     RuleCascadeData *next = data->mNext;
  2912     delete data;
  2913     data = next;
  2915   return NS_OK;
  2919 // This function should return the set of states that this selector
  2920 // depends on; this is used to implement HasStateDependentStyle.  It
  2921 // does NOT recur down into things like :not and :-moz-any.
  2922 inline
  2923 EventStates ComputeSelectorStateDependence(nsCSSSelector& aSelector)
  2925   EventStates states;
  2926   for (nsPseudoClassList* pseudoClass = aSelector.mPseudoClassList;
  2927        pseudoClass; pseudoClass = pseudoClass->mNext) {
  2928     // Tree pseudo-elements overload mPseudoClassList for things that
  2929     // aren't pseudo-classes.
  2930     if (pseudoClass->mType >= nsCSSPseudoClasses::ePseudoClass_Count) {
  2931       continue;
  2933     states |= sPseudoClassStateDependences[pseudoClass->mType];
  2935   return states;
  2938 static bool
  2939 AddSelector(RuleCascadeData* aCascade,
  2940             // The part between combinators at the top level of the selector
  2941             nsCSSSelector* aSelectorInTopLevel,
  2942             // The part we should look through (might be in :not or :-moz-any())
  2943             nsCSSSelector* aSelectorPart)
  2945   // It's worth noting that this loop over negations isn't quite
  2946   // optimal for two reasons.  One, we could add something to one of
  2947   // these lists twice, which means we'll check it twice, but I don't
  2948   // think that's worth worrying about.   (We do the same for multiple
  2949   // attribute selectors on the same attribute.)  Two, we don't really
  2950   // need to check negations past the first in the current
  2951   // implementation (and they're rare as well), but that might change
  2952   // in the future if :not() is extended.
  2953   for (nsCSSSelector* negation = aSelectorPart; negation;
  2954        negation = negation->mNegations) {
  2955     // Track both document states and attribute dependence in pseudo-classes.
  2956     for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList;
  2957          pseudoClass; pseudoClass = pseudoClass->mNext) {
  2958       switch (pseudoClass->mType) {
  2959         case nsCSSPseudoClasses::ePseudoClass_mozLocaleDir: {
  2960           aCascade->mSelectorDocumentStates |= NS_DOCUMENT_STATE_RTL_LOCALE;
  2961           break;
  2963         case nsCSSPseudoClasses::ePseudoClass_mozWindowInactive: {
  2964           aCascade->mSelectorDocumentStates |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
  2965           break;
  2967         case nsCSSPseudoClasses::ePseudoClass_mozTableBorderNonzero: {
  2968           nsTArray<nsCSSSelector*> *array =
  2969             aCascade->AttributeListFor(nsGkAtoms::border);
  2970           if (!array) {
  2971             return false;
  2973           array->AppendElement(aSelectorInTopLevel);
  2974           break;
  2976         default: {
  2977           break;
  2982     // Build mStateSelectors.
  2983     EventStates dependentStates = ComputeSelectorStateDependence(*negation);
  2984     if (!dependentStates.IsEmpty()) {
  2985       aCascade->mStateSelectors.AppendElement(
  2986         nsCSSRuleProcessor::StateSelector(dependentStates,
  2987                                           aSelectorInTopLevel));
  2990     // Build mIDSelectors
  2991     if (negation == aSelectorInTopLevel) {
  2992       for (nsAtomList* curID = negation->mIDList; curID;
  2993            curID = curID->mNext) {
  2994         AtomSelectorEntry *entry =
  2995           static_cast<AtomSelectorEntry*>(PL_DHashTableOperate(&aCascade->mIdSelectors,
  2996                                                                curID->mAtom,
  2997                                                                PL_DHASH_ADD));
  2998         if (entry) {
  2999           entry->mSelectors.AppendElement(aSelectorInTopLevel);
  3002     } else if (negation->mIDList) {
  3003       aCascade->mPossiblyNegatedIDSelectors.AppendElement(aSelectorInTopLevel);
  3006     // Build mClassSelectors
  3007     if (negation == aSelectorInTopLevel) {
  3008       for (nsAtomList* curClass = negation->mClassList; curClass;
  3009            curClass = curClass->mNext) {
  3010         AtomSelectorEntry *entry =
  3011           static_cast<AtomSelectorEntry*>(PL_DHashTableOperate(&aCascade->mClassSelectors,
  3012                                                                curClass->mAtom,
  3013                                                                PL_DHASH_ADD));
  3014         if (entry) {
  3015           entry->mSelectors.AppendElement(aSelectorInTopLevel);
  3018     } else if (negation->mClassList) {
  3019       aCascade->mPossiblyNegatedClassSelectors.AppendElement(aSelectorInTopLevel);
  3022     // Build mAttributeSelectors.
  3023     for (nsAttrSelector *attr = negation->mAttrList; attr;
  3024          attr = attr->mNext) {
  3025       nsTArray<nsCSSSelector*> *array =
  3026         aCascade->AttributeListFor(attr->mCasedAttr);
  3027       if (!array) {
  3028         return false;
  3030       array->AppendElement(aSelectorInTopLevel);
  3031       if (attr->mLowercaseAttr != attr->mCasedAttr) {
  3032         array = aCascade->AttributeListFor(attr->mLowercaseAttr);
  3033         if (!array) {
  3034           return false;
  3036         array->AppendElement(aSelectorInTopLevel);
  3040     // Recur through any :-moz-any selectors
  3041     for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList;
  3042          pseudoClass; pseudoClass = pseudoClass->mNext) {
  3043       if (pseudoClass->mType == nsCSSPseudoClasses::ePseudoClass_any) {
  3044         for (nsCSSSelectorList *l = pseudoClass->u.mSelectors; l; l = l->mNext) {
  3045           nsCSSSelector *s = l->mSelectors;
  3046           if (!AddSelector(aCascade, aSelectorInTopLevel, s)) {
  3047             return false;
  3054   return true;
  3057 static bool
  3058 AddRule(RuleSelectorPair* aRuleInfo, RuleCascadeData* aCascade)
  3060   RuleCascadeData * const cascade = aCascade;
  3062   // Build the rule hash.
  3063   nsCSSPseudoElements::Type pseudoType = aRuleInfo->mSelector->PseudoType();
  3064   if (MOZ_LIKELY(pseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement)) {
  3065     cascade->mRuleHash.AppendRule(*aRuleInfo);
  3066   } else if (pseudoType < nsCSSPseudoElements::ePseudo_PseudoElementCount) {
  3067     RuleHash*& ruleHash = cascade->mPseudoElementRuleHashes[pseudoType];
  3068     if (!ruleHash) {
  3069       ruleHash = new RuleHash(cascade->mQuirksMode);
  3070       if (!ruleHash) {
  3071         // Out of memory; give up
  3072         return false;
  3075     NS_ASSERTION(aRuleInfo->mSelector->mNext,
  3076                  "Must have mNext; parser screwed up");
  3077     NS_ASSERTION(aRuleInfo->mSelector->mNext->mOperator == ':',
  3078                  "Unexpected mNext combinator");
  3079     ruleHash->AppendRule(*aRuleInfo);
  3080   } else if (pseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
  3081     NS_ASSERTION(!aRuleInfo->mSelector->mCasedTag &&
  3082                  !aRuleInfo->mSelector->mIDList &&
  3083                  !aRuleInfo->mSelector->mClassList &&
  3084                  !aRuleInfo->mSelector->mPseudoClassList &&
  3085                  !aRuleInfo->mSelector->mAttrList &&
  3086                  !aRuleInfo->mSelector->mNegations &&
  3087                  !aRuleInfo->mSelector->mNext &&
  3088                  aRuleInfo->mSelector->mNameSpace == kNameSpaceID_Unknown,
  3089                  "Parser messed up with anon box selector");
  3091     // Index doesn't matter here, since we'll just be walking these
  3092     // rules in order; just pass 0.
  3093     AppendRuleToTagTable(&cascade->mAnonBoxRules,
  3094                          aRuleInfo->mSelector->mLowercaseTag,
  3095                          RuleValue(*aRuleInfo, 0, aCascade->mQuirksMode));
  3096   } else {
  3097 #ifdef MOZ_XUL
  3098     NS_ASSERTION(pseudoType == nsCSSPseudoElements::ePseudo_XULTree,
  3099                  "Unexpected pseudo type");
  3100     // Index doesn't matter here, since we'll just be walking these
  3101     // rules in order; just pass 0.
  3102     AppendRuleToTagTable(&cascade->mXULTreeRules,
  3103                          aRuleInfo->mSelector->mLowercaseTag,
  3104                          RuleValue(*aRuleInfo, 0, aCascade->mQuirksMode));
  3105 #else
  3106     NS_NOTREACHED("Unexpected pseudo type");
  3107 #endif
  3110   for (nsCSSSelector* selector = aRuleInfo->mSelector;
  3111            selector; selector = selector->mNext) {
  3112     if (selector->IsPseudoElement()) {
  3113       nsCSSPseudoElements::Type pseudo = selector->PseudoType();
  3114       if (pseudo >= nsCSSPseudoElements::ePseudo_PseudoElementCount ||
  3115           !nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudo)) {
  3116         NS_ASSERTION(!selector->mNegations, "Shouldn't have negations");
  3117         // We do store selectors ending with pseudo-elements that allow :hover
  3118         // and :active after them in the hashtables corresponding to that
  3119         // selector's mNext (i.e. the thing that matches against the element),
  3120         // but we want to make sure that selectors for any other kinds of
  3121         // pseudo-elements don't end up in the hashtables.  In particular, tree
  3122         // pseudos store strange things in mPseudoClassList that we don't want
  3123         // to try to match elements against.
  3124         continue;
  3127     if (!AddSelector(cascade, selector, selector)) {
  3128       return false;
  3132   return true;
  3135 struct PerWeightDataListItem : public RuleSelectorPair {
  3136   PerWeightDataListItem(css::StyleRule* aRule, nsCSSSelector* aSelector)
  3137     : RuleSelectorPair(aRule, aSelector)
  3138     , mNext(nullptr)
  3139   {}
  3140   // No destructor; these are arena-allocated
  3143   // Placement new to arena allocate the PerWeightDataListItem
  3144   void *operator new(size_t aSize, PLArenaPool &aArena) CPP_THROW_NEW {
  3145     void *mem;
  3146     PL_ARENA_ALLOCATE(mem, &aArena, aSize);
  3147     return mem;
  3150   PerWeightDataListItem *mNext;
  3151 };
  3153 struct PerWeightData {
  3154   PerWeightData()
  3155     : mRuleSelectorPairs(nullptr)
  3156     , mTail(&mRuleSelectorPairs)
  3157   {}
  3159   int32_t mWeight;
  3160   PerWeightDataListItem *mRuleSelectorPairs;
  3161   PerWeightDataListItem **mTail;
  3162 };
  3164 struct RuleByWeightEntry : public PLDHashEntryHdr {
  3165   PerWeightData data; // mWeight is key, mRuleSelectorPairs are value
  3166 };
  3168 static PLDHashNumber
  3169 HashIntKey(PLDHashTable *table, const void *key)
  3171   return PLDHashNumber(NS_PTR_TO_INT32(key));
  3174 static bool
  3175 MatchWeightEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
  3176                  const void *key)
  3178   const RuleByWeightEntry *entry = (const RuleByWeightEntry *)hdr;
  3179   return entry->data.mWeight == NS_PTR_TO_INT32(key);
  3182 static bool
  3183 InitWeightEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
  3184                 const void *key)
  3186   RuleByWeightEntry* entry = static_cast<RuleByWeightEntry*>(hdr);
  3187   new (entry) RuleByWeightEntry();
  3188   return true;
  3191 static const PLDHashTableOps gRulesByWeightOps = {
  3192     PL_DHashAllocTable,
  3193     PL_DHashFreeTable,
  3194     HashIntKey,
  3195     MatchWeightEntry,
  3196     PL_DHashMoveEntryStub,
  3197     PL_DHashClearEntryStub,
  3198     PL_DHashFinalizeStub,
  3199     InitWeightEntry
  3200 };
  3202 struct CascadeEnumData {
  3203   CascadeEnumData(nsPresContext* aPresContext,
  3204                   nsTArray<nsFontFaceRuleContainer>& aFontFaceRules,
  3205                   nsTArray<nsCSSKeyframesRule*>& aKeyframesRules,
  3206                   nsTArray<nsCSSFontFeatureValuesRule*>& aFontFeatureValuesRules,
  3207                   nsTArray<nsCSSPageRule*>& aPageRules,
  3208                   nsMediaQueryResultCacheKey& aKey,
  3209                   uint8_t aSheetType)
  3210     : mPresContext(aPresContext),
  3211       mFontFaceRules(aFontFaceRules),
  3212       mKeyframesRules(aKeyframesRules),
  3213       mFontFeatureValuesRules(aFontFeatureValuesRules),
  3214       mPageRules(aPageRules),
  3215       mCacheKey(aKey),
  3216       mSheetType(aSheetType)
  3218     if (!PL_DHashTableInit(&mRulesByWeight, &gRulesByWeightOps, nullptr,
  3219                            sizeof(RuleByWeightEntry), 64, fallible_t()))
  3220       mRulesByWeight.ops = nullptr;
  3222     // Initialize our arena
  3223     PL_INIT_ARENA_POOL(&mArena, "CascadeEnumDataArena",
  3224                        NS_CASCADEENUMDATA_ARENA_BLOCK_SIZE);
  3227   ~CascadeEnumData()
  3229     if (mRulesByWeight.ops)
  3230       PL_DHashTableFinish(&mRulesByWeight);
  3231     PL_FinishArenaPool(&mArena);
  3234   nsPresContext* mPresContext;
  3235   nsTArray<nsFontFaceRuleContainer>& mFontFaceRules;
  3236   nsTArray<nsCSSKeyframesRule*>& mKeyframesRules;
  3237   nsTArray<nsCSSFontFeatureValuesRule*>& mFontFeatureValuesRules;
  3238   nsTArray<nsCSSPageRule*>& mPageRules;
  3239   nsMediaQueryResultCacheKey& mCacheKey;
  3240   PLArenaPool mArena;
  3241   // Hooray, a manual PLDHashTable since nsClassHashtable doesn't
  3242   // provide a getter that gives me a *reference* to the value.
  3243   PLDHashTable mRulesByWeight; // of PerWeightDataListItem linked lists
  3244   uint8_t mSheetType;
  3245 };
  3247 /*
  3248  * This enumerates style rules in a sheet (and recursively into any
  3249  * grouping rules) in order to:
  3250  *  (1) add any style rules, in order, into data->mRulesByWeight (for
  3251  *      the primary CSS cascade), where they are separated by weight
  3252  *      but kept in order per-weight, and
  3253  *  (2) add any @font-face rules, in order, into data->mFontFaceRules.
  3254  *  (3) add any @keyframes rules, in order, into data->mKeyframesRules.
  3255  *  (4) add any @font-feature-value rules, in order,
  3256  *      into data->mFontFeatureValuesRules.
  3257  *  (5) add any @page rules, in order, into data->mPageRules.
  3258  */
  3259 static bool
  3260 CascadeRuleEnumFunc(css::Rule* aRule, void* aData)
  3262   CascadeEnumData* data = (CascadeEnumData*)aData;
  3263   int32_t type = aRule->GetType();
  3265   if (css::Rule::STYLE_RULE == type) {
  3266     css::StyleRule* styleRule = static_cast<css::StyleRule*>(aRule);
  3268     for (nsCSSSelectorList *sel = styleRule->Selector();
  3269          sel; sel = sel->mNext) {
  3270       int32_t weight = sel->mWeight;
  3271       RuleByWeightEntry *entry = static_cast<RuleByWeightEntry*>(
  3272         PL_DHashTableOperate(&data->mRulesByWeight, NS_INT32_TO_PTR(weight),
  3273                              PL_DHASH_ADD));
  3274       if (!entry)
  3275         return false;
  3276       entry->data.mWeight = weight;
  3277       // entry->data.mRuleSelectorPairs should be linked in forward order;
  3278       // entry->data.mTail is the slot to write to.
  3279       PerWeightDataListItem *newItem =
  3280         new (data->mArena) PerWeightDataListItem(styleRule, sel->mSelectors);
  3281       if (newItem) {
  3282         *(entry->data.mTail) = newItem;
  3283         entry->data.mTail = &newItem->mNext;
  3287   else if (css::Rule::MEDIA_RULE == type ||
  3288            css::Rule::DOCUMENT_RULE == type ||
  3289            css::Rule::SUPPORTS_RULE == type) {
  3290     css::GroupRule* groupRule = static_cast<css::GroupRule*>(aRule);
  3291     if (groupRule->UseForPresentation(data->mPresContext, data->mCacheKey))
  3292       if (!groupRule->EnumerateRulesForwards(CascadeRuleEnumFunc, aData))
  3293         return false;
  3295   else if (css::Rule::FONT_FACE_RULE == type) {
  3296     nsCSSFontFaceRule *fontFaceRule = static_cast<nsCSSFontFaceRule*>(aRule);
  3297     nsFontFaceRuleContainer *ptr = data->mFontFaceRules.AppendElement();
  3298     if (!ptr)
  3299       return false;
  3300     ptr->mRule = fontFaceRule;
  3301     ptr->mSheetType = data->mSheetType;
  3303   else if (css::Rule::KEYFRAMES_RULE == type) {
  3304     nsCSSKeyframesRule *keyframesRule =
  3305       static_cast<nsCSSKeyframesRule*>(aRule);
  3306     if (!data->mKeyframesRules.AppendElement(keyframesRule)) {
  3307       return false;
  3310   else if (css::Rule::FONT_FEATURE_VALUES_RULE == type) {
  3311     nsCSSFontFeatureValuesRule *fontFeatureValuesRule =
  3312       static_cast<nsCSSFontFeatureValuesRule*>(aRule);
  3313     if (!data->mFontFeatureValuesRules.AppendElement(fontFeatureValuesRule)) {
  3314       return false;
  3317   else if (css::Rule::PAGE_RULE == type) {
  3318     nsCSSPageRule* pageRule = static_cast<nsCSSPageRule*>(aRule);
  3319     if (!data->mPageRules.AppendElement(pageRule)) {
  3320       return false;
  3323   return true;
  3326 /* static */ bool
  3327 nsCSSRuleProcessor::CascadeSheet(nsCSSStyleSheet* aSheet, CascadeEnumData* aData)
  3329   if (aSheet->IsApplicable() &&
  3330       aSheet->UseForPresentation(aData->mPresContext, aData->mCacheKey) &&
  3331       aSheet->mInner) {
  3332     nsCSSStyleSheet* child = aSheet->mInner->mFirstChild;
  3333     while (child) {
  3334       CascadeSheet(child, aData);
  3335       child = child->mNext;
  3338     if (!aSheet->mInner->mOrderedRules.EnumerateForwards(CascadeRuleEnumFunc,
  3339                                                          aData))
  3340       return false;
  3342   return true;
  3345 static int CompareWeightData(const void* aArg1, const void* aArg2,
  3346                              void* closure)
  3348   const PerWeightData* arg1 = static_cast<const PerWeightData*>(aArg1);
  3349   const PerWeightData* arg2 = static_cast<const PerWeightData*>(aArg2);
  3350   return arg1->mWeight - arg2->mWeight; // put lower weight first
  3354 struct FillWeightArrayData {
  3355   FillWeightArrayData(PerWeightData* aArrayData) :
  3356     mIndex(0),
  3357     mWeightArray(aArrayData)
  3360   int32_t mIndex;
  3361   PerWeightData* mWeightArray;
  3362 };
  3365 static PLDHashOperator
  3366 FillWeightArray(PLDHashTable *table, PLDHashEntryHdr *hdr,
  3367                 uint32_t number, void *arg)
  3369   FillWeightArrayData* data = static_cast<FillWeightArrayData*>(arg);
  3370   const RuleByWeightEntry *entry = (const RuleByWeightEntry *)hdr;
  3372   data->mWeightArray[data->mIndex++] = entry->data;
  3374   return PL_DHASH_NEXT;
  3377 RuleCascadeData*
  3378 nsCSSRuleProcessor::GetRuleCascade(nsPresContext* aPresContext)
  3380   // FIXME:  Make this infallible!
  3382   // If anything changes about the presentation context, we will be
  3383   // notified.  Otherwise, our cache is valid if mLastPresContext
  3384   // matches aPresContext.  (The only rule processors used for multiple
  3385   // pres contexts are for XBL.  These rule processors are probably less
  3386   // likely to have @media rules, and thus the cache is pretty likely to
  3387   // hit instantly even when we're switching between pres contexts.)
  3389   if (!mRuleCascades || aPresContext != mLastPresContext) {
  3390     RefreshRuleCascade(aPresContext);
  3392   mLastPresContext = aPresContext;
  3394   return mRuleCascades;
  3397 void
  3398 nsCSSRuleProcessor::RefreshRuleCascade(nsPresContext* aPresContext)
  3400   // Having RuleCascadeData objects be per-medium (over all variation
  3401   // caused by media queries, handled through mCacheKey) works for now
  3402   // since nsCSSRuleProcessor objects are per-document.  (For a given
  3403   // set of stylesheets they can vary based on medium (@media) or
  3404   // document (@-moz-document).)
  3406   for (RuleCascadeData **cascadep = &mRuleCascades, *cascade;
  3407        (cascade = *cascadep); cascadep = &cascade->mNext) {
  3408     if (cascade->mCacheKey.Matches(aPresContext)) {
  3409       // Ensure that the current one is always mRuleCascades.
  3410       *cascadep = cascade->mNext;
  3411       cascade->mNext = mRuleCascades;
  3412       mRuleCascades = cascade;
  3414       return;
  3418   if (mSheets.Length() != 0) {
  3419     nsAutoPtr<RuleCascadeData> newCascade(
  3420       new RuleCascadeData(aPresContext->Medium(),
  3421                           eCompatibility_NavQuirks == aPresContext->CompatibilityMode()));
  3422     if (newCascade) {
  3423       CascadeEnumData data(aPresContext, newCascade->mFontFaceRules,
  3424                            newCascade->mKeyframesRules,
  3425                            newCascade->mFontFeatureValuesRules,
  3426                            newCascade->mPageRules,
  3427                            newCascade->mCacheKey,
  3428                            mSheetType);
  3429       if (!data.mRulesByWeight.ops)
  3430         return; /* out of memory */
  3432       for (uint32_t i = 0; i < mSheets.Length(); ++i) {
  3433         if (!CascadeSheet(mSheets.ElementAt(i), &data))
  3434           return; /* out of memory */
  3437       // Sort the hash table of per-weight linked lists by weight.
  3438       uint32_t weightCount = data.mRulesByWeight.entryCount;
  3439       nsAutoArrayPtr<PerWeightData> weightArray(new PerWeightData[weightCount]);
  3440       FillWeightArrayData fwData(weightArray);
  3441       PL_DHashTableEnumerate(&data.mRulesByWeight, FillWeightArray, &fwData);
  3442       NS_QuickSort(weightArray, weightCount, sizeof(PerWeightData),
  3443                    CompareWeightData, nullptr);
  3445       // Put things into the rule hash.
  3446       // The primary sort is by weight...
  3447       for (uint32_t i = 0; i < weightCount; ++i) {
  3448         // and the secondary sort is by order.  mRuleSelectorPairs is already in
  3449         // the right order..
  3450         for (PerWeightDataListItem *cur = weightArray[i].mRuleSelectorPairs;
  3451              cur;
  3452              cur = cur->mNext) {
  3453           if (!AddRule(cur, newCascade))
  3454             return; /* out of memory */
  3458       // Build mKeyframesRuleTable.
  3459       for (nsTArray<nsCSSKeyframesRule*>::size_type i = 0,
  3460              iEnd = newCascade->mKeyframesRules.Length(); i < iEnd; ++i) {
  3461         nsCSSKeyframesRule* rule = newCascade->mKeyframesRules[i];
  3462         newCascade->mKeyframesRuleTable.Put(rule->GetName(), rule);
  3465       // Ensure that the current one is always mRuleCascades.
  3466       newCascade->mNext = mRuleCascades;
  3467       mRuleCascades = newCascade.forget();
  3470   return;
  3473 /* static */ bool
  3474 nsCSSRuleProcessor::SelectorListMatches(Element* aElement,
  3475                                         TreeMatchContext& aTreeMatchContext,
  3476                                         nsCSSSelectorList* aSelectorList)
  3478   MOZ_ASSERT(!aTreeMatchContext.mForScopedStyle,
  3479              "mCurrentStyleScope will need to be saved and restored after the "
  3480              "SelectorMatchesTree call");
  3482   while (aSelectorList) {
  3483     nsCSSSelector* sel = aSelectorList->mSelectors;
  3484     NS_ASSERTION(sel, "Should have *some* selectors");
  3485     NS_ASSERTION(!sel->IsPseudoElement(), "Shouldn't have been called");
  3486     NodeMatchContext nodeContext(EventStates(), false);
  3487     if (SelectorMatches(aElement, sel, nodeContext, aTreeMatchContext)) {
  3488       nsCSSSelector* next = sel->mNext;
  3489       if (!next ||
  3490           SelectorMatchesTree(aElement, next, aTreeMatchContext, false)) {
  3491         return true;
  3495     aSelectorList = aSelectorList->mNext;
  3498   return false;
  3501 // TreeMatchContext and AncestorFilter out of line methods
  3502 void
  3503 TreeMatchContext::InitAncestors(Element *aElement)
  3505   MOZ_ASSERT(!mAncestorFilter.mFilter);
  3506   MOZ_ASSERT(mAncestorFilter.mHashes.IsEmpty());
  3507   MOZ_ASSERT(mStyleScopes.IsEmpty());
  3509   mAncestorFilter.mFilter = new AncestorFilter::Filter();
  3511   if (MOZ_LIKELY(aElement)) {
  3512     MOZ_ASSERT(aElement->IsInDoc(),
  3513                "aElement must be in the document for the assumption that "
  3514                "GetParentNode() is non-null on all element ancestors of "
  3515                "aElement to be true");
  3516     // Collect up the ancestors
  3517     nsAutoTArray<Element*, 50> ancestors;
  3518     Element* cur = aElement;
  3519     do {
  3520       ancestors.AppendElement(cur);
  3521       nsINode* parent = cur->GetParentNode();
  3522       if (!parent->IsElement()) {
  3523         break;
  3526       cur = parent->AsElement();
  3527     } while (true);
  3529     // Now push them in reverse order.
  3530     for (uint32_t i = ancestors.Length(); i-- != 0; ) {
  3531       mAncestorFilter.PushAncestor(ancestors[i]);
  3532       PushStyleScope(ancestors[i]);
  3537 void
  3538 TreeMatchContext::InitStyleScopes(Element* aElement)
  3540   MOZ_ASSERT(mStyleScopes.IsEmpty());
  3542   if (MOZ_LIKELY(aElement)) {
  3543     // Collect up the ancestors
  3544     nsAutoTArray<Element*, 50> ancestors;
  3545     Element* cur = aElement;
  3546     do {
  3547       ancestors.AppendElement(cur);
  3548       nsINode* parent = cur->GetParentNode();
  3549       if (!parent || !parent->IsElement()) {
  3550         break;
  3553       cur = parent->AsElement();
  3554     } while (true);
  3556     // Now push them in reverse order.
  3557     for (uint32_t i = ancestors.Length(); i-- != 0; ) {
  3558       PushStyleScope(ancestors[i]);
  3563 void
  3564 AncestorFilter::PushAncestor(Element *aElement)
  3566   MOZ_ASSERT(mFilter);
  3568   uint32_t oldLength = mHashes.Length();
  3570   mPopTargets.AppendElement(oldLength);
  3571 #ifdef DEBUG
  3572   mElements.AppendElement(aElement);
  3573 #endif
  3574   mHashes.AppendElement(aElement->Tag()->hash());
  3575   nsIAtom *id = aElement->GetID();
  3576   if (id) {
  3577     mHashes.AppendElement(id->hash());
  3579   const nsAttrValue *classes = aElement->GetClasses();
  3580   if (classes) {
  3581     uint32_t classCount = classes->GetAtomCount();
  3582     for (uint32_t i = 0; i < classCount; ++i) {
  3583       mHashes.AppendElement(classes->AtomAt(i)->hash());
  3587   uint32_t newLength = mHashes.Length();
  3588   for (uint32_t i = oldLength; i < newLength; ++i) {
  3589     mFilter->add(mHashes[i]);
  3593 void
  3594 AncestorFilter::PopAncestor()
  3596   MOZ_ASSERT(!mPopTargets.IsEmpty());
  3597   MOZ_ASSERT(mPopTargets.Length() == mElements.Length());
  3599   uint32_t popTargetLength = mPopTargets.Length();
  3600   uint32_t newLength = mPopTargets[popTargetLength-1];
  3602   mPopTargets.TruncateLength(popTargetLength-1);
  3603 #ifdef DEBUG
  3604   mElements.TruncateLength(popTargetLength-1);
  3605 #endif
  3607   uint32_t oldLength = mHashes.Length();
  3608   for (uint32_t i = newLength; i < oldLength; ++i) {
  3609     mFilter->remove(mHashes[i]);
  3611   mHashes.TruncateLength(newLength);
  3614 #ifdef DEBUG
  3615 void
  3616 AncestorFilter::AssertHasAllAncestors(Element *aElement) const
  3618   nsINode* cur = aElement->GetParentNode();
  3619   while (cur && cur->IsElement()) {
  3620     MOZ_ASSERT(mElements.Contains(cur));
  3621     cur = cur->GetParentNode();
  3624 #endif

mercurial