layout/style/nsCSSRuleProcessor.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/style/nsCSSRuleProcessor.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3624 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +// vim:cindent:tabstop=2:expandtab:shiftwidth=2:
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 + * style rule processor for CSS style sheets, responsible for selector
    1.12 + * matching and cascading
    1.13 + */
    1.14 +
    1.15 +#define PL_ARENA_CONST_ALIGN_MASK 7
    1.16 +// We want page-sized arenas so there's no fragmentation involved.
    1.17 +// Including plarena.h must come first to avoid it being included by some
    1.18 +// header file thereby making PL_ARENA_CONST_ALIGN_MASK ineffective.
    1.19 +#define NS_CASCADEENUMDATA_ARENA_BLOCK_SIZE (4096)
    1.20 +#include "plarena.h"
    1.21 +
    1.22 +#include "nsCSSRuleProcessor.h"
    1.23 +#include "nsRuleProcessorData.h"
    1.24 +#include <algorithm>
    1.25 +#include "nsIAtom.h"
    1.26 +#include "pldhash.h"
    1.27 +#include "nsICSSPseudoComparator.h"
    1.28 +#include "mozilla/MemoryReporting.h"
    1.29 +#include "mozilla/css/StyleRule.h"
    1.30 +#include "mozilla/css/GroupRule.h"
    1.31 +#include "nsIDocument.h"
    1.32 +#include "nsPresContext.h"
    1.33 +#include "nsGkAtoms.h"
    1.34 +#include "nsUnicharUtils.h"
    1.35 +#include "nsError.h"
    1.36 +#include "nsRuleWalker.h"
    1.37 +#include "nsCSSPseudoClasses.h"
    1.38 +#include "nsCSSPseudoElements.h"
    1.39 +#include "nsIContent.h"
    1.40 +#include "nsCOMPtr.h"
    1.41 +#include "nsHashKeys.h"
    1.42 +#include "nsStyleUtil.h"
    1.43 +#include "nsQuickSort.h"
    1.44 +#include "nsAttrValue.h"
    1.45 +#include "nsAttrValueInlines.h"
    1.46 +#include "nsAttrName.h"
    1.47 +#include "nsTArray.h"
    1.48 +#include "nsContentUtils.h"
    1.49 +#include "nsIMediaList.h"
    1.50 +#include "nsCSSRules.h"
    1.51 +#include "nsStyleSet.h"
    1.52 +#include "mozilla/dom/Element.h"
    1.53 +#include "nsNthIndexCache.h"
    1.54 +#include "mozilla/ArrayUtils.h"
    1.55 +#include "mozilla/EventStates.h"
    1.56 +#include "mozilla/Preferences.h"
    1.57 +#include "mozilla/LookAndFeel.h"
    1.58 +#include "mozilla/Likely.h"
    1.59 +
    1.60 +using namespace mozilla;
    1.61 +using namespace mozilla::dom;
    1.62 +
    1.63 +#define VISITED_PSEUDO_PREF "layout.css.visited_links_enabled"
    1.64 +
    1.65 +static bool gSupportVisitedPseudo = true;
    1.66 +
    1.67 +static nsTArray< nsCOMPtr<nsIAtom> >* sSystemMetrics = 0;
    1.68 +
    1.69 +#ifdef XP_WIN
    1.70 +uint8_t nsCSSRuleProcessor::sWinThemeId = LookAndFeel::eWindowsTheme_Generic;
    1.71 +#endif
    1.72 +
    1.73 +/**
    1.74 + * A struct representing a given CSS rule and a particular selector
    1.75 + * from that rule's selector list.
    1.76 + */
    1.77 +struct RuleSelectorPair {
    1.78 +  RuleSelectorPair(css::StyleRule* aRule, nsCSSSelector* aSelector)
    1.79 +    : mRule(aRule), mSelector(aSelector) {}
    1.80 +  // If this class ever grows a destructor, deal with
    1.81 +  // PerWeightDataListItem appropriately.
    1.82 +
    1.83 +  css::StyleRule*   mRule;
    1.84 +  nsCSSSelector*    mSelector; // which of |mRule|'s selectors
    1.85 +};
    1.86 +
    1.87 +#define NS_IS_ANCESTOR_OPERATOR(ch) \
    1.88 +  ((ch) == char16_t(' ') || (ch) == char16_t('>'))
    1.89 +
    1.90 +/**
    1.91 + * A struct representing a particular rule in an ordered list of rules
    1.92 + * (the ordering depending on the weight of mSelector and the order of
    1.93 + * our rules to start with).
    1.94 + */
    1.95 +struct RuleValue : RuleSelectorPair {
    1.96 +  enum {
    1.97 +    eMaxAncestorHashes = 4
    1.98 +  };
    1.99 +
   1.100 +  RuleValue(const RuleSelectorPair& aRuleSelectorPair, int32_t aIndex,
   1.101 +            bool aQuirksMode) :
   1.102 +    RuleSelectorPair(aRuleSelectorPair),
   1.103 +    mIndex(aIndex)
   1.104 +  {
   1.105 +    CollectAncestorHashes(aQuirksMode);
   1.106 +  }
   1.107 +
   1.108 +  int32_t mIndex; // High index means high weight/order.
   1.109 +  uint32_t mAncestorSelectorHashes[eMaxAncestorHashes];
   1.110 +
   1.111 +private:
   1.112 +  void CollectAncestorHashes(bool aQuirksMode) {
   1.113 +    // Collect up our mAncestorSelectorHashes.  It's not clear whether it's
   1.114 +    // better to stop once we've found eMaxAncestorHashes of them or to keep
   1.115 +    // going and preferentially collect information from selectors higher up the
   1.116 +    // chain...  Let's do the former for now.
   1.117 +    size_t hashIndex = 0;
   1.118 +    for (nsCSSSelector* sel = mSelector->mNext; sel; sel = sel->mNext) {
   1.119 +      if (!NS_IS_ANCESTOR_OPERATOR(sel->mOperator)) {
   1.120 +        // |sel| is going to select something that's not actually one of our
   1.121 +        // ancestors, so don't add it to mAncestorSelectorHashes.  But keep
   1.122 +        // going, because it'll select a sibling of one of our ancestors, so its
   1.123 +        // ancestors would be our ancestors too.
   1.124 +        continue;
   1.125 +      }
   1.126 +
   1.127 +      // Now sel is supposed to select one of our ancestors.  Grab
   1.128 +      // whatever info we can from it into mAncestorSelectorHashes.
   1.129 +      // But in qurks mode, don't grab IDs and classes because those
   1.130 +      // need to be matched case-insensitively.
   1.131 +      if (!aQuirksMode) {
   1.132 +        nsAtomList* ids = sel->mIDList;
   1.133 +        while (ids) {
   1.134 +          mAncestorSelectorHashes[hashIndex++] = ids->mAtom->hash();
   1.135 +          if (hashIndex == eMaxAncestorHashes) {
   1.136 +            return;
   1.137 +          }
   1.138 +          ids = ids->mNext;
   1.139 +        }
   1.140 +
   1.141 +        nsAtomList* classes = sel->mClassList;
   1.142 +        while (classes) {
   1.143 +          mAncestorSelectorHashes[hashIndex++] = classes->mAtom->hash();
   1.144 +          if (hashIndex == eMaxAncestorHashes) {
   1.145 +            return;
   1.146 +          }
   1.147 +          classes = classes->mNext;
   1.148 +        }
   1.149 +      }
   1.150 +
   1.151 +      // Only put in the tag name if it's all-lowercase.  Otherwise we run into
   1.152 +      // trouble because we may test the wrong one of mLowercaseTag and
   1.153 +      // mCasedTag against the filter.
   1.154 +      if (sel->mLowercaseTag && sel->mCasedTag == sel->mLowercaseTag) {
   1.155 +        mAncestorSelectorHashes[hashIndex++] = sel->mLowercaseTag->hash();
   1.156 +        if (hashIndex == eMaxAncestorHashes) {
   1.157 +          return;
   1.158 +        }
   1.159 +      }
   1.160 +    }
   1.161 +
   1.162 +    while (hashIndex != eMaxAncestorHashes) {
   1.163 +      mAncestorSelectorHashes[hashIndex++] = 0;
   1.164 +    }
   1.165 +  }
   1.166 +};
   1.167 +
   1.168 +// ------------------------------
   1.169 +// Rule hash table
   1.170 +//
   1.171 +
   1.172 +// Uses any of the sets of ops below.
   1.173 +struct RuleHashTableEntry : public PLDHashEntryHdr {
   1.174 +  // If you add members that have heap allocated memory be sure to change the
   1.175 +  // logic in SizeOfRuleHashTableEntry().
   1.176 +  // Auto length 1, because we always have at least one entry in mRules.
   1.177 +  nsAutoTArray<RuleValue, 1> mRules;
   1.178 +};
   1.179 +
   1.180 +struct RuleHashTagTableEntry : public RuleHashTableEntry {
   1.181 +  // If you add members that have heap allocated memory be sure to change the
   1.182 +  // logic in RuleHash::SizeOf{In,Ex}cludingThis.
   1.183 +  nsCOMPtr<nsIAtom> mTag;
   1.184 +};
   1.185 +
   1.186 +static PLDHashNumber
   1.187 +RuleHash_CIHashKey(PLDHashTable *table, const void *key)
   1.188 +{
   1.189 +  nsIAtom *atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
   1.190 +
   1.191 +  nsAutoString str;
   1.192 +  atom->ToString(str);
   1.193 +  nsContentUtils::ASCIIToLower(str);
   1.194 +  return HashString(str);
   1.195 +}
   1.196 +
   1.197 +typedef nsIAtom*
   1.198 +(* RuleHashGetKey) (PLDHashTable *table, const PLDHashEntryHdr *entry);
   1.199 +
   1.200 +struct RuleHashTableOps {
   1.201 +  const PLDHashTableOps ops;
   1.202 +  // Extra callback to avoid duplicating the matchEntry callback for
   1.203 +  // each table.  (There used to be a getKey callback in
   1.204 +  // PLDHashTableOps.)
   1.205 +  RuleHashGetKey getKey;
   1.206 +};
   1.207 +
   1.208 +inline const RuleHashTableOps*
   1.209 +ToLocalOps(const PLDHashTableOps *aOps)
   1.210 +{
   1.211 +  return (const RuleHashTableOps*)
   1.212 +           (((const char*) aOps) - offsetof(RuleHashTableOps, ops));
   1.213 +}
   1.214 +
   1.215 +static bool
   1.216 +RuleHash_CIMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
   1.217 +                      const void *key)
   1.218 +{
   1.219 +  nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>
   1.220 +                                              (key));
   1.221 +  // Use our extra |getKey| callback to avoid code duplication.
   1.222 +  nsIAtom *entry_atom = ToLocalOps(table->ops)->getKey(table, hdr);
   1.223 +
   1.224 +  // Check for case-sensitive match first.
   1.225 +  if (match_atom == entry_atom)
   1.226 +    return true;
   1.227 +
   1.228 +  // Use EqualsIgnoreASCIICase instead of full on unicode case conversion
   1.229 +  // in order to save on performance. This is only used in quirks mode
   1.230 +  // anyway.
   1.231 +
   1.232 +  return
   1.233 +    nsContentUtils::EqualsIgnoreASCIICase(nsDependentAtomString(entry_atom),
   1.234 +                                          nsDependentAtomString(match_atom));
   1.235 +}
   1.236 +
   1.237 +static bool
   1.238 +RuleHash_CSMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
   1.239 +                      const void *key)
   1.240 +{
   1.241 +  nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>
   1.242 +                                              (key));
   1.243 +  // Use our extra |getKey| callback to avoid code duplication.
   1.244 +  nsIAtom *entry_atom = ToLocalOps(table->ops)->getKey(table, hdr);
   1.245 +
   1.246 +  return match_atom == entry_atom;
   1.247 +}
   1.248 +
   1.249 +static bool
   1.250 +RuleHash_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.251 +                   const void *key)
   1.252 +{
   1.253 +  RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(hdr);
   1.254 +  new (entry) RuleHashTableEntry();
   1.255 +  return true;
   1.256 +}
   1.257 +
   1.258 +static void
   1.259 +RuleHash_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
   1.260 +{
   1.261 +  RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(hdr);
   1.262 +  entry->~RuleHashTableEntry();
   1.263 +}
   1.264 +
   1.265 +static void
   1.266 +RuleHash_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
   1.267 +                   PLDHashEntryHdr *to)
   1.268 +{
   1.269 +  NS_PRECONDITION(from != to, "This is not going to work!");
   1.270 +  RuleHashTableEntry *oldEntry =
   1.271 +    const_cast<RuleHashTableEntry*>(
   1.272 +      static_cast<const RuleHashTableEntry*>(from));
   1.273 +  RuleHashTableEntry *newEntry = new (to) RuleHashTableEntry();
   1.274 +  newEntry->mRules.SwapElements(oldEntry->mRules);
   1.275 +  oldEntry->~RuleHashTableEntry();
   1.276 +}
   1.277 +
   1.278 +static bool
   1.279 +RuleHash_TagTable_MatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
   1.280 +                      const void *key)
   1.281 +{
   1.282 +  nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>
   1.283 +                                              (key));
   1.284 +  nsIAtom *entry_atom = static_cast<const RuleHashTagTableEntry*>(hdr)->mTag;
   1.285 +
   1.286 +  return match_atom == entry_atom;
   1.287 +}
   1.288 +
   1.289 +static bool
   1.290 +RuleHash_TagTable_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.291 +                            const void *key)
   1.292 +{
   1.293 +  RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>(hdr);
   1.294 +  new (entry) RuleHashTagTableEntry();
   1.295 +  entry->mTag = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
   1.296 +  return true;
   1.297 +}
   1.298 +
   1.299 +static void
   1.300 +RuleHash_TagTable_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
   1.301 +{
   1.302 +  RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>(hdr);
   1.303 +  entry->~RuleHashTagTableEntry();
   1.304 +}
   1.305 +
   1.306 +static void
   1.307 +RuleHash_TagTable_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
   1.308 +                            PLDHashEntryHdr *to)
   1.309 +{
   1.310 +  NS_PRECONDITION(from != to, "This is not going to work!");
   1.311 +  RuleHashTagTableEntry *oldEntry =
   1.312 +    const_cast<RuleHashTagTableEntry*>(
   1.313 +      static_cast<const RuleHashTagTableEntry*>(from));
   1.314 +  RuleHashTagTableEntry *newEntry = new (to) RuleHashTagTableEntry();
   1.315 +  newEntry->mTag.swap(oldEntry->mTag);
   1.316 +  newEntry->mRules.SwapElements(oldEntry->mRules);
   1.317 +  oldEntry->~RuleHashTagTableEntry();
   1.318 +}
   1.319 +
   1.320 +static nsIAtom*
   1.321 +RuleHash_ClassTable_GetKey(PLDHashTable *table, const PLDHashEntryHdr *hdr)
   1.322 +{
   1.323 +  const RuleHashTableEntry *entry =
   1.324 +    static_cast<const RuleHashTableEntry*>(hdr);
   1.325 +  nsCSSSelector* selector = entry->mRules[0].mSelector;
   1.326 +  if (selector->IsPseudoElement()) {
   1.327 +    selector = selector->mNext;
   1.328 +  }
   1.329 +  return selector->mClassList->mAtom;
   1.330 +}
   1.331 +
   1.332 +static nsIAtom*
   1.333 +RuleHash_IdTable_GetKey(PLDHashTable *table, const PLDHashEntryHdr *hdr)
   1.334 +{
   1.335 +  const RuleHashTableEntry *entry =
   1.336 +    static_cast<const RuleHashTableEntry*>(hdr);
   1.337 +  nsCSSSelector* selector = entry->mRules[0].mSelector;
   1.338 +  if (selector->IsPseudoElement()) {
   1.339 +    selector = selector->mNext;
   1.340 +  }
   1.341 +  return selector->mIDList->mAtom;
   1.342 +}
   1.343 +
   1.344 +static PLDHashNumber
   1.345 +RuleHash_NameSpaceTable_HashKey(PLDHashTable *table, const void *key)
   1.346 +{
   1.347 +  return NS_PTR_TO_INT32(key);
   1.348 +}
   1.349 +
   1.350 +static bool
   1.351 +RuleHash_NameSpaceTable_MatchEntry(PLDHashTable *table,
   1.352 +                                   const PLDHashEntryHdr *hdr,
   1.353 +                                   const void *key)
   1.354 +{
   1.355 +  const RuleHashTableEntry *entry =
   1.356 +    static_cast<const RuleHashTableEntry*>(hdr);
   1.357 +
   1.358 +  nsCSSSelector* selector = entry->mRules[0].mSelector;
   1.359 +  if (selector->IsPseudoElement()) {
   1.360 +    selector = selector->mNext;
   1.361 +  }
   1.362 +  return NS_PTR_TO_INT32(key) == selector->mNameSpace;
   1.363 +}
   1.364 +
   1.365 +static const PLDHashTableOps RuleHash_TagTable_Ops = {
   1.366 +  PL_DHashAllocTable,
   1.367 +  PL_DHashFreeTable,
   1.368 +  PL_DHashVoidPtrKeyStub,
   1.369 +  RuleHash_TagTable_MatchEntry,
   1.370 +  RuleHash_TagTable_MoveEntry,
   1.371 +  RuleHash_TagTable_ClearEntry,
   1.372 +  PL_DHashFinalizeStub,
   1.373 +  RuleHash_TagTable_InitEntry
   1.374 +};
   1.375 +
   1.376 +// Case-sensitive ops.
   1.377 +static const RuleHashTableOps RuleHash_ClassTable_CSOps = {
   1.378 +  {
   1.379 +  PL_DHashAllocTable,
   1.380 +  PL_DHashFreeTable,
   1.381 +  PL_DHashVoidPtrKeyStub,
   1.382 +  RuleHash_CSMatchEntry,
   1.383 +  RuleHash_MoveEntry,
   1.384 +  RuleHash_ClearEntry,
   1.385 +  PL_DHashFinalizeStub,
   1.386 +  RuleHash_InitEntry
   1.387 +  },
   1.388 +  RuleHash_ClassTable_GetKey
   1.389 +};
   1.390 +
   1.391 +// Case-insensitive ops.
   1.392 +static const RuleHashTableOps RuleHash_ClassTable_CIOps = {
   1.393 +  {
   1.394 +  PL_DHashAllocTable,
   1.395 +  PL_DHashFreeTable,
   1.396 +  RuleHash_CIHashKey,
   1.397 +  RuleHash_CIMatchEntry,
   1.398 +  RuleHash_MoveEntry,
   1.399 +  RuleHash_ClearEntry,
   1.400 +  PL_DHashFinalizeStub,
   1.401 +  RuleHash_InitEntry
   1.402 +  },
   1.403 +  RuleHash_ClassTable_GetKey
   1.404 +};
   1.405 +
   1.406 +// Case-sensitive ops.
   1.407 +static const RuleHashTableOps RuleHash_IdTable_CSOps = {
   1.408 +  {
   1.409 +  PL_DHashAllocTable,
   1.410 +  PL_DHashFreeTable,
   1.411 +  PL_DHashVoidPtrKeyStub,
   1.412 +  RuleHash_CSMatchEntry,
   1.413 +  RuleHash_MoveEntry,
   1.414 +  RuleHash_ClearEntry,
   1.415 +  PL_DHashFinalizeStub,
   1.416 +  RuleHash_InitEntry
   1.417 +  },
   1.418 +  RuleHash_IdTable_GetKey
   1.419 +};
   1.420 +
   1.421 +// Case-insensitive ops.
   1.422 +static const RuleHashTableOps RuleHash_IdTable_CIOps = {
   1.423 +  {
   1.424 +  PL_DHashAllocTable,
   1.425 +  PL_DHashFreeTable,
   1.426 +  RuleHash_CIHashKey,
   1.427 +  RuleHash_CIMatchEntry,
   1.428 +  RuleHash_MoveEntry,
   1.429 +  RuleHash_ClearEntry,
   1.430 +  PL_DHashFinalizeStub,
   1.431 +  RuleHash_InitEntry
   1.432 +  },
   1.433 +  RuleHash_IdTable_GetKey
   1.434 +};
   1.435 +
   1.436 +static const PLDHashTableOps RuleHash_NameSpaceTable_Ops = {
   1.437 +  PL_DHashAllocTable,
   1.438 +  PL_DHashFreeTable,
   1.439 +  RuleHash_NameSpaceTable_HashKey,
   1.440 +  RuleHash_NameSpaceTable_MatchEntry,
   1.441 +  RuleHash_MoveEntry,
   1.442 +  RuleHash_ClearEntry,
   1.443 +  PL_DHashFinalizeStub,
   1.444 +  RuleHash_InitEntry
   1.445 +};
   1.446 +
   1.447 +#undef RULE_HASH_STATS
   1.448 +#undef PRINT_UNIVERSAL_RULES
   1.449 +
   1.450 +#ifdef RULE_HASH_STATS
   1.451 +#define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO ++(var_); PR_END_MACRO
   1.452 +#else
   1.453 +#define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO PR_END_MACRO
   1.454 +#endif
   1.455 +
   1.456 +struct NodeMatchContext;
   1.457 +
   1.458 +class RuleHash {
   1.459 +public:
   1.460 +  RuleHash(bool aQuirksMode);
   1.461 +  ~RuleHash();
   1.462 +  void AppendRule(const RuleSelectorPair &aRuleInfo);
   1.463 +  void EnumerateAllRules(Element* aElement, ElementDependentRuleProcessorData* aData,
   1.464 +                         NodeMatchContext& aNodeMatchContext);
   1.465 +
   1.466 +  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
   1.467 +  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
   1.468 +
   1.469 +protected:
   1.470 +  typedef nsTArray<RuleValue> RuleValueList;
   1.471 +  void AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
   1.472 +                         const RuleSelectorPair& aRuleInfo);
   1.473 +  void AppendUniversalRule(const RuleSelectorPair& aRuleInfo);
   1.474 +
   1.475 +  int32_t     mRuleCount;
   1.476 +  // The hashtables are lazily initialized; we use a null .ops to
   1.477 +  // indicate that they need initialization.
   1.478 +  PLDHashTable mIdTable;
   1.479 +  PLDHashTable mClassTable;
   1.480 +  PLDHashTable mTagTable;
   1.481 +  PLDHashTable mNameSpaceTable;
   1.482 +  RuleValueList mUniversalRules;
   1.483 +
   1.484 +  struct EnumData {
   1.485 +    const RuleValue* mCurValue;
   1.486 +    const RuleValue* mEnd;
   1.487 +  };
   1.488 +  EnumData* mEnumList;
   1.489 +  int32_t   mEnumListSize;
   1.490 +
   1.491 +  bool mQuirksMode;
   1.492 +
   1.493 +  inline EnumData ToEnumData(const RuleValueList& arr) {
   1.494 +    EnumData data = { arr.Elements(), arr.Elements() + arr.Length() };
   1.495 +    return data;
   1.496 +  }
   1.497 +
   1.498 +#ifdef RULE_HASH_STATS
   1.499 +  uint32_t    mUniversalSelectors;
   1.500 +  uint32_t    mNameSpaceSelectors;
   1.501 +  uint32_t    mTagSelectors;
   1.502 +  uint32_t    mClassSelectors;
   1.503 +  uint32_t    mIdSelectors;
   1.504 +
   1.505 +  uint32_t    mElementsMatched;
   1.506 +
   1.507 +  uint32_t    mElementUniversalCalls;
   1.508 +  uint32_t    mElementNameSpaceCalls;
   1.509 +  uint32_t    mElementTagCalls;
   1.510 +  uint32_t    mElementClassCalls;
   1.511 +  uint32_t    mElementIdCalls;
   1.512 +#endif // RULE_HASH_STATS
   1.513 +};
   1.514 +
   1.515 +RuleHash::RuleHash(bool aQuirksMode)
   1.516 +  : mRuleCount(0),
   1.517 +    mUniversalRules(0),
   1.518 +    mEnumList(nullptr), mEnumListSize(0),
   1.519 +    mQuirksMode(aQuirksMode)
   1.520 +#ifdef RULE_HASH_STATS
   1.521 +    ,
   1.522 +    mUniversalSelectors(0),
   1.523 +    mNameSpaceSelectors(0),
   1.524 +    mTagSelectors(0),
   1.525 +    mClassSelectors(0),
   1.526 +    mIdSelectors(0),
   1.527 +    mElementsMatched(0),
   1.528 +    mElementUniversalCalls(0),
   1.529 +    mElementNameSpaceCalls(0),
   1.530 +    mElementTagCalls(0),
   1.531 +    mElementClassCalls(0),
   1.532 +    mElementIdCalls(0)
   1.533 +#endif
   1.534 +{
   1.535 +  MOZ_COUNT_CTOR(RuleHash);
   1.536 +
   1.537 +  mTagTable.ops = nullptr;
   1.538 +  mIdTable.ops = nullptr;
   1.539 +  mClassTable.ops = nullptr;
   1.540 +  mNameSpaceTable.ops = nullptr;
   1.541 +}
   1.542 +
   1.543 +RuleHash::~RuleHash()
   1.544 +{
   1.545 +  MOZ_COUNT_DTOR(RuleHash);
   1.546 +#ifdef RULE_HASH_STATS
   1.547 +  printf(
   1.548 +"RuleHash(%p):\n"
   1.549 +"  Selectors: Universal (%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
   1.550 +"  Content Nodes: Elements(%u)\n"
   1.551 +"  Element Calls: Universal(%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
   1.552 +         static_cast<void*>(this),
   1.553 +         mUniversalSelectors, mNameSpaceSelectors, mTagSelectors,
   1.554 +           mClassSelectors, mIdSelectors,
   1.555 +         mElementsMatched,
   1.556 +         mElementUniversalCalls, mElementNameSpaceCalls, mElementTagCalls,
   1.557 +           mElementClassCalls, mElementIdCalls);
   1.558 +#ifdef PRINT_UNIVERSAL_RULES
   1.559 +  {
   1.560 +    if (mUniversalRules.Length() > 0) {
   1.561 +      printf("  Universal rules:\n");
   1.562 +      for (uint32_t i = 0; i < mUniversalRules.Length(); ++i) {
   1.563 +        RuleValue* value = &(mUniversalRules[i]);
   1.564 +        nsAutoString selectorText;
   1.565 +        uint32_t lineNumber = value->mRule->GetLineNumber();
   1.566 +        nsCOMPtr<nsIStyleSheet> sheet;
   1.567 +        value->mRule->GetStyleSheet(*getter_AddRefs(sheet));
   1.568 +        nsRefPtr<nsCSSStyleSheet> cssSheet = do_QueryObject(sheet);
   1.569 +        value->mSelector->ToString(selectorText, cssSheet);
   1.570 +
   1.571 +        printf("    line %d, %s\n",
   1.572 +               lineNumber, NS_ConvertUTF16toUTF8(selectorText).get());
   1.573 +      }
   1.574 +    }
   1.575 +  }
   1.576 +#endif // PRINT_UNIVERSAL_RULES
   1.577 +#endif // RULE_HASH_STATS
   1.578 +  // Rule Values are arena allocated no need to delete them. Their destructor
   1.579 +  // isn't doing any cleanup. So we dont even bother to enumerate through
   1.580 +  // the hash tables and call their destructors.
   1.581 +  if (nullptr != mEnumList) {
   1.582 +    delete [] mEnumList;
   1.583 +  }
   1.584 +  // delete arena for strings and small objects
   1.585 +  if (mIdTable.ops) {
   1.586 +    PL_DHashTableFinish(&mIdTable);
   1.587 +  }
   1.588 +  if (mClassTable.ops) {
   1.589 +    PL_DHashTableFinish(&mClassTable);
   1.590 +  }
   1.591 +  if (mTagTable.ops) {
   1.592 +    PL_DHashTableFinish(&mTagTable);
   1.593 +  }
   1.594 +  if (mNameSpaceTable.ops) {
   1.595 +    PL_DHashTableFinish(&mNameSpaceTable);
   1.596 +  }
   1.597 +}
   1.598 +
   1.599 +void RuleHash::AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
   1.600 +                                 const RuleSelectorPair& aRuleInfo)
   1.601 +{
   1.602 +  // Get a new or existing entry.
   1.603 +  RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
   1.604 +                                         (PL_DHashTableOperate(aTable, aKey, PL_DHASH_ADD));
   1.605 +  if (!entry)
   1.606 +    return;
   1.607 +  entry->mRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode));
   1.608 +}
   1.609 +
   1.610 +static void
   1.611 +AppendRuleToTagTable(PLDHashTable* aTable, nsIAtom* aKey,
   1.612 +                     const RuleValue& aRuleInfo)
   1.613 +{
   1.614 +  // Get a new or exisiting entry
   1.615 +  RuleHashTagTableEntry *entry = static_cast<RuleHashTagTableEntry*>
   1.616 +    (PL_DHashTableOperate(aTable, aKey, PL_DHASH_ADD));
   1.617 +  if (!entry)
   1.618 +    return;
   1.619 +
   1.620 +  entry->mRules.AppendElement(aRuleInfo);
   1.621 +}
   1.622 +
   1.623 +void RuleHash::AppendUniversalRule(const RuleSelectorPair& aRuleInfo)
   1.624 +{
   1.625 +  mUniversalRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode));
   1.626 +}
   1.627 +
   1.628 +void RuleHash::AppendRule(const RuleSelectorPair& aRuleInfo)
   1.629 +{
   1.630 +  nsCSSSelector *selector = aRuleInfo.mSelector;
   1.631 +  if (selector->IsPseudoElement()) {
   1.632 +    selector = selector->mNext;
   1.633 +  }
   1.634 +  if (nullptr != selector->mIDList) {
   1.635 +    if (!mIdTable.ops) {
   1.636 +      PL_DHashTableInit(&mIdTable,
   1.637 +                        mQuirksMode ? &RuleHash_IdTable_CIOps.ops
   1.638 +                                    : &RuleHash_IdTable_CSOps.ops,
   1.639 +                        nullptr, sizeof(RuleHashTableEntry), 16);
   1.640 +    }
   1.641 +    AppendRuleToTable(&mIdTable, selector->mIDList->mAtom, aRuleInfo);
   1.642 +    RULE_HASH_STAT_INCREMENT(mIdSelectors);
   1.643 +  }
   1.644 +  else if (nullptr != selector->mClassList) {
   1.645 +    if (!mClassTable.ops) {
   1.646 +      PL_DHashTableInit(&mClassTable,
   1.647 +                        mQuirksMode ? &RuleHash_ClassTable_CIOps.ops
   1.648 +                                    : &RuleHash_ClassTable_CSOps.ops,
   1.649 +                        nullptr, sizeof(RuleHashTableEntry), 16);
   1.650 +    }
   1.651 +    AppendRuleToTable(&mClassTable, selector->mClassList->mAtom, aRuleInfo);
   1.652 +    RULE_HASH_STAT_INCREMENT(mClassSelectors);
   1.653 +  }
   1.654 +  else if (selector->mLowercaseTag) {
   1.655 +    RuleValue ruleValue(aRuleInfo, mRuleCount++, mQuirksMode);
   1.656 +    if (!mTagTable.ops) {
   1.657 +      PL_DHashTableInit(&mTagTable, &RuleHash_TagTable_Ops, nullptr,
   1.658 +                        sizeof(RuleHashTagTableEntry), 16);
   1.659 +    }
   1.660 +    AppendRuleToTagTable(&mTagTable, selector->mLowercaseTag, ruleValue);
   1.661 +    RULE_HASH_STAT_INCREMENT(mTagSelectors);
   1.662 +    if (selector->mCasedTag &&
   1.663 +        selector->mCasedTag != selector->mLowercaseTag) {
   1.664 +      AppendRuleToTagTable(&mTagTable, selector->mCasedTag, ruleValue);
   1.665 +      RULE_HASH_STAT_INCREMENT(mTagSelectors);
   1.666 +    }
   1.667 +  }
   1.668 +  else if (kNameSpaceID_Unknown != selector->mNameSpace) {
   1.669 +    if (!mNameSpaceTable.ops) {
   1.670 +      PL_DHashTableInit(&mNameSpaceTable, &RuleHash_NameSpaceTable_Ops, nullptr,
   1.671 +                        sizeof(RuleHashTableEntry), 16);
   1.672 +    }
   1.673 +    AppendRuleToTable(&mNameSpaceTable,
   1.674 +                      NS_INT32_TO_PTR(selector->mNameSpace), aRuleInfo);
   1.675 +    RULE_HASH_STAT_INCREMENT(mNameSpaceSelectors);
   1.676 +  }
   1.677 +  else {  // universal tag selector
   1.678 +    AppendUniversalRule(aRuleInfo);
   1.679 +    RULE_HASH_STAT_INCREMENT(mUniversalSelectors);
   1.680 +  }
   1.681 +}
   1.682 +
   1.683 +// this should cover practically all cases so we don't need to reallocate
   1.684 +#define MIN_ENUM_LIST_SIZE 8
   1.685 +
   1.686 +#ifdef RULE_HASH_STATS
   1.687 +#define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
   1.688 +  (var_) += (list_).Length()
   1.689 +#else
   1.690 +#define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
   1.691 +  PR_BEGIN_MACRO PR_END_MACRO
   1.692 +#endif
   1.693 +
   1.694 +static inline
   1.695 +void ContentEnumFunc(const RuleValue &value, nsCSSSelector* selector,
   1.696 +                     ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
   1.697 +                     AncestorFilter *ancestorFilter);
   1.698 +
   1.699 +void RuleHash::EnumerateAllRules(Element* aElement, ElementDependentRuleProcessorData* aData,
   1.700 +                                 NodeMatchContext& aNodeContext)
   1.701 +{
   1.702 +  int32_t nameSpace = aElement->GetNameSpaceID();
   1.703 +  nsIAtom* tag = aElement->Tag();
   1.704 +  nsIAtom* id = aElement->GetID();
   1.705 +  const nsAttrValue* classList = aElement->GetClasses();
   1.706 +
   1.707 +  NS_ABORT_IF_FALSE(tag, "How could we not have a tag?");
   1.708 +
   1.709 +  int32_t classCount = classList ? classList->GetAtomCount() : 0;
   1.710 +
   1.711 +  // assume 1 universal, tag, id, and namespace, rather than wasting
   1.712 +  // time counting
   1.713 +  int32_t testCount = classCount + 4;
   1.714 +
   1.715 +  if (mEnumListSize < testCount) {
   1.716 +    delete [] mEnumList;
   1.717 +    mEnumListSize = std::max(testCount, MIN_ENUM_LIST_SIZE);
   1.718 +    mEnumList = new EnumData[mEnumListSize];
   1.719 +  }
   1.720 +
   1.721 +  int32_t valueCount = 0;
   1.722 +  RULE_HASH_STAT_INCREMENT(mElementsMatched);
   1.723 +
   1.724 +  if (mUniversalRules.Length() != 0) { // universal rules
   1.725 +    mEnumList[valueCount++] = ToEnumData(mUniversalRules);
   1.726 +    RULE_HASH_STAT_INCREMENT_LIST_COUNT(mUniversalRules, mElementUniversalCalls);
   1.727 +  }
   1.728 +  // universal rules within the namespace
   1.729 +  if (kNameSpaceID_Unknown != nameSpace && mNameSpaceTable.ops) {
   1.730 +    RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
   1.731 +                                           (PL_DHashTableOperate(&mNameSpaceTable, NS_INT32_TO_PTR(nameSpace),
   1.732 +                             PL_DHASH_LOOKUP));
   1.733 +    if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
   1.734 +      mEnumList[valueCount++] = ToEnumData(entry->mRules);
   1.735 +      RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementNameSpaceCalls);
   1.736 +    }
   1.737 +  }
   1.738 +  if (mTagTable.ops) {
   1.739 +    RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
   1.740 +                                           (PL_DHashTableOperate(&mTagTable, tag, PL_DHASH_LOOKUP));
   1.741 +    if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
   1.742 +      mEnumList[valueCount++] = ToEnumData(entry->mRules);
   1.743 +      RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementTagCalls);
   1.744 +    }
   1.745 +  }
   1.746 +  if (id && mIdTable.ops) {
   1.747 +    RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
   1.748 +                                           (PL_DHashTableOperate(&mIdTable, id, PL_DHASH_LOOKUP));
   1.749 +    if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
   1.750 +      mEnumList[valueCount++] = ToEnumData(entry->mRules);
   1.751 +      RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementIdCalls);
   1.752 +    }
   1.753 +  }
   1.754 +  if (mClassTable.ops) {
   1.755 +    for (int32_t index = 0; index < classCount; ++index) {
   1.756 +      RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
   1.757 +                                             (PL_DHashTableOperate(&mClassTable, classList->AtomAt(index),
   1.758 +                             PL_DHASH_LOOKUP));
   1.759 +      if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
   1.760 +        mEnumList[valueCount++] = ToEnumData(entry->mRules);
   1.761 +        RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementClassCalls);
   1.762 +      }
   1.763 +    }
   1.764 +  }
   1.765 +  NS_ASSERTION(valueCount <= testCount, "values exceeded list size");
   1.766 +
   1.767 +  if (valueCount > 0) {
   1.768 +    AncestorFilter *filter =
   1.769 +      aData->mTreeMatchContext.mAncestorFilter.HasFilter() ?
   1.770 +        &aData->mTreeMatchContext.mAncestorFilter : nullptr;
   1.771 +#ifdef DEBUG
   1.772 +    if (filter) {
   1.773 +      filter->AssertHasAllAncestors(aElement);
   1.774 +    }
   1.775 +#endif
   1.776 +    // Merge the lists while there are still multiple lists to merge.
   1.777 +    while (valueCount > 1) {
   1.778 +      int32_t valueIndex = 0;
   1.779 +      int32_t lowestRuleIndex = mEnumList[valueIndex].mCurValue->mIndex;
   1.780 +      for (int32_t index = 1; index < valueCount; ++index) {
   1.781 +        int32_t ruleIndex = mEnumList[index].mCurValue->mIndex;
   1.782 +        if (ruleIndex < lowestRuleIndex) {
   1.783 +          valueIndex = index;
   1.784 +          lowestRuleIndex = ruleIndex;
   1.785 +        }
   1.786 +      }
   1.787 +      const RuleValue *cur = mEnumList[valueIndex].mCurValue;
   1.788 +      ContentEnumFunc(*cur, cur->mSelector, aData, aNodeContext, filter);
   1.789 +      cur++;
   1.790 +      if (cur == mEnumList[valueIndex].mEnd) {
   1.791 +        mEnumList[valueIndex] = mEnumList[--valueCount];
   1.792 +      } else {
   1.793 +        mEnumList[valueIndex].mCurValue = cur;
   1.794 +      }
   1.795 +    }
   1.796 +
   1.797 +    // Fast loop over single value.
   1.798 +    for (const RuleValue *value = mEnumList[0].mCurValue,
   1.799 +                         *end = mEnumList[0].mEnd;
   1.800 +         value != end; ++value) {
   1.801 +      ContentEnumFunc(*value, value->mSelector, aData, aNodeContext, filter);
   1.802 +    }
   1.803 +  }
   1.804 +}
   1.805 +
   1.806 +static size_t
   1.807 +SizeOfRuleHashTableEntry(PLDHashEntryHdr* aHdr, MallocSizeOf aMallocSizeOf, void *)
   1.808 +{
   1.809 +  RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(aHdr);
   1.810 +  return entry->mRules.SizeOfExcludingThis(aMallocSizeOf);
   1.811 +}
   1.812 +
   1.813 +size_t
   1.814 +RuleHash::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
   1.815 +{
   1.816 +  size_t n = 0;
   1.817 +
   1.818 +  if (mIdTable.ops) {
   1.819 +    n += PL_DHashTableSizeOfExcludingThis(&mIdTable,
   1.820 +                                          SizeOfRuleHashTableEntry,
   1.821 +                                          aMallocSizeOf);
   1.822 +  }
   1.823 +
   1.824 +  if (mClassTable.ops) {
   1.825 +    n += PL_DHashTableSizeOfExcludingThis(&mClassTable,
   1.826 +                                          SizeOfRuleHashTableEntry,
   1.827 +                                          aMallocSizeOf);
   1.828 +  }
   1.829 +
   1.830 +  if (mTagTable.ops) {
   1.831 +    n += PL_DHashTableSizeOfExcludingThis(&mTagTable,
   1.832 +                                          SizeOfRuleHashTableEntry,
   1.833 +                                          aMallocSizeOf);
   1.834 +  }
   1.835 +
   1.836 +  if (mNameSpaceTable.ops) {
   1.837 +    n += PL_DHashTableSizeOfExcludingThis(&mNameSpaceTable,
   1.838 +                                          SizeOfRuleHashTableEntry,
   1.839 +                                          aMallocSizeOf);
   1.840 +  }
   1.841 +
   1.842 +  n += mUniversalRules.SizeOfExcludingThis(aMallocSizeOf);
   1.843 +
   1.844 +  return n;
   1.845 +}
   1.846 +
   1.847 +size_t
   1.848 +RuleHash::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
   1.849 +{
   1.850 +  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   1.851 +}
   1.852 +
   1.853 +//--------------------------------
   1.854 +
   1.855 +// A hash table mapping atoms to lists of selectors
   1.856 +struct AtomSelectorEntry : public PLDHashEntryHdr {
   1.857 +  nsIAtom *mAtom;
   1.858 +  // Auto length 2, because a decent fraction of these arrays ends up
   1.859 +  // with 2 elements, and each entry is cheap.
   1.860 +  nsAutoTArray<nsCSSSelector*, 2> mSelectors;
   1.861 +};
   1.862 +
   1.863 +static void
   1.864 +AtomSelector_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
   1.865 +{
   1.866 +  (static_cast<AtomSelectorEntry*>(hdr))->~AtomSelectorEntry();
   1.867 +}
   1.868 +
   1.869 +static bool
   1.870 +AtomSelector_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
   1.871 +                       const void *key)
   1.872 +{
   1.873 +  AtomSelectorEntry *entry = static_cast<AtomSelectorEntry*>(hdr);
   1.874 +  new (entry) AtomSelectorEntry();
   1.875 +  entry->mAtom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
   1.876 +  return true;
   1.877 +}
   1.878 +
   1.879 +static void
   1.880 +AtomSelector_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
   1.881 +                       PLDHashEntryHdr *to)
   1.882 +{
   1.883 +  NS_PRECONDITION(from != to, "This is not going to work!");
   1.884 +  AtomSelectorEntry *oldEntry =
   1.885 +    const_cast<AtomSelectorEntry*>(static_cast<const AtomSelectorEntry*>(from));
   1.886 +  AtomSelectorEntry *newEntry = new (to) AtomSelectorEntry();
   1.887 +  newEntry->mAtom = oldEntry->mAtom;
   1.888 +  newEntry->mSelectors.SwapElements(oldEntry->mSelectors);
   1.889 +  oldEntry->~AtomSelectorEntry();
   1.890 +}
   1.891 +
   1.892 +static nsIAtom*
   1.893 +AtomSelector_GetKey(PLDHashTable *table, const PLDHashEntryHdr *hdr)
   1.894 +{
   1.895 +  const AtomSelectorEntry *entry = static_cast<const AtomSelectorEntry*>(hdr);
   1.896 +  return entry->mAtom;
   1.897 +}
   1.898 +
   1.899 +// Case-sensitive ops.
   1.900 +static const PLDHashTableOps AtomSelector_CSOps = {
   1.901 +  PL_DHashAllocTable,
   1.902 +  PL_DHashFreeTable,
   1.903 +  PL_DHashVoidPtrKeyStub,
   1.904 +  PL_DHashMatchEntryStub,
   1.905 +  AtomSelector_MoveEntry,
   1.906 +  AtomSelector_ClearEntry,
   1.907 +  PL_DHashFinalizeStub,
   1.908 +  AtomSelector_InitEntry
   1.909 +};
   1.910 +
   1.911 +// Case-insensitive ops.
   1.912 +static const RuleHashTableOps AtomSelector_CIOps = {
   1.913 +  {
   1.914 +  PL_DHashAllocTable,
   1.915 +  PL_DHashFreeTable,
   1.916 +  RuleHash_CIHashKey,
   1.917 +  RuleHash_CIMatchEntry,
   1.918 +  AtomSelector_MoveEntry,
   1.919 +  AtomSelector_ClearEntry,
   1.920 +  PL_DHashFinalizeStub,
   1.921 +  AtomSelector_InitEntry
   1.922 +  },
   1.923 +  AtomSelector_GetKey
   1.924 +};
   1.925 +
   1.926 +//--------------------------------
   1.927 +
   1.928 +struct RuleCascadeData {
   1.929 +  RuleCascadeData(nsIAtom *aMedium, bool aQuirksMode)
   1.930 +    : mRuleHash(aQuirksMode),
   1.931 +      mStateSelectors(),
   1.932 +      mSelectorDocumentStates(0),
   1.933 +      mKeyframesRuleTable(16),
   1.934 +      mCacheKey(aMedium),
   1.935 +      mNext(nullptr),
   1.936 +      mQuirksMode(aQuirksMode)
   1.937 +  {
   1.938 +    // mAttributeSelectors is matching on the attribute _name_, not the value,
   1.939 +    // and we case-fold names at parse-time, so this is a case-sensitive match.
   1.940 +    PL_DHashTableInit(&mAttributeSelectors, &AtomSelector_CSOps, nullptr,
   1.941 +                      sizeof(AtomSelectorEntry), 16);
   1.942 +    PL_DHashTableInit(&mAnonBoxRules, &RuleHash_TagTable_Ops, nullptr,
   1.943 +                      sizeof(RuleHashTagTableEntry), 16);
   1.944 +    PL_DHashTableInit(&mIdSelectors,
   1.945 +                      aQuirksMode ? &AtomSelector_CIOps.ops :
   1.946 +                                    &AtomSelector_CSOps,
   1.947 +                      nullptr, sizeof(AtomSelectorEntry), 16);
   1.948 +    PL_DHashTableInit(&mClassSelectors,
   1.949 +                      aQuirksMode ? &AtomSelector_CIOps.ops :
   1.950 +                                    &AtomSelector_CSOps,
   1.951 +                      nullptr, sizeof(AtomSelectorEntry), 16);
   1.952 +    memset(mPseudoElementRuleHashes, 0, sizeof(mPseudoElementRuleHashes));
   1.953 +#ifdef MOZ_XUL
   1.954 +    PL_DHashTableInit(&mXULTreeRules, &RuleHash_TagTable_Ops, nullptr,
   1.955 +                      sizeof(RuleHashTagTableEntry), 16);
   1.956 +#endif
   1.957 +  }
   1.958 +
   1.959 +  ~RuleCascadeData()
   1.960 +  {
   1.961 +    PL_DHashTableFinish(&mAttributeSelectors);
   1.962 +    PL_DHashTableFinish(&mAnonBoxRules);
   1.963 +    PL_DHashTableFinish(&mIdSelectors);
   1.964 +    PL_DHashTableFinish(&mClassSelectors);
   1.965 +#ifdef MOZ_XUL
   1.966 +    PL_DHashTableFinish(&mXULTreeRules);
   1.967 +#endif
   1.968 +    for (uint32_t i = 0; i < ArrayLength(mPseudoElementRuleHashes); ++i) {
   1.969 +      delete mPseudoElementRuleHashes[i];
   1.970 +    }
   1.971 +  }
   1.972 +
   1.973 +  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
   1.974 +
   1.975 +  RuleHash                 mRuleHash;
   1.976 +  RuleHash*
   1.977 +    mPseudoElementRuleHashes[nsCSSPseudoElements::ePseudo_PseudoElementCount];
   1.978 +  nsTArray<nsCSSRuleProcessor::StateSelector>  mStateSelectors;
   1.979 +  EventStates              mSelectorDocumentStates;
   1.980 +  PLDHashTable             mClassSelectors;
   1.981 +  PLDHashTable             mIdSelectors;
   1.982 +  nsTArray<nsCSSSelector*> mPossiblyNegatedClassSelectors;
   1.983 +  nsTArray<nsCSSSelector*> mPossiblyNegatedIDSelectors;
   1.984 +  PLDHashTable             mAttributeSelectors;
   1.985 +  PLDHashTable             mAnonBoxRules;
   1.986 +#ifdef MOZ_XUL
   1.987 +  PLDHashTable             mXULTreeRules;
   1.988 +#endif
   1.989 +
   1.990 +  nsTArray<nsFontFaceRuleContainer> mFontFaceRules;
   1.991 +  nsTArray<nsCSSKeyframesRule*> mKeyframesRules;
   1.992 +  nsTArray<nsCSSFontFeatureValuesRule*> mFontFeatureValuesRules;
   1.993 +  nsTArray<nsCSSPageRule*> mPageRules;
   1.994 +
   1.995 +  nsDataHashtable<nsStringHashKey, nsCSSKeyframesRule*> mKeyframesRuleTable;
   1.996 +
   1.997 +  // Looks up or creates the appropriate list in |mAttributeSelectors|.
   1.998 +  // Returns null only on allocation failure.
   1.999 +  nsTArray<nsCSSSelector*>* AttributeListFor(nsIAtom* aAttribute);
  1.1000 +
  1.1001 +  nsMediaQueryResultCacheKey mCacheKey;
  1.1002 +  RuleCascadeData*  mNext; // for a different medium
  1.1003 +
  1.1004 +  const bool mQuirksMode;
  1.1005 +};
  1.1006 +
  1.1007 +static size_t
  1.1008 +SizeOfSelectorsEntry(PLDHashEntryHdr* aHdr, MallocSizeOf aMallocSizeOf, void *)
  1.1009 +{
  1.1010 +  AtomSelectorEntry* entry = static_cast<AtomSelectorEntry*>(aHdr);
  1.1011 +  return entry->mSelectors.SizeOfExcludingThis(aMallocSizeOf);
  1.1012 +}
  1.1013 +
  1.1014 +size_t
  1.1015 +RuleCascadeData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
  1.1016 +{
  1.1017 +  size_t n = aMallocSizeOf(this);
  1.1018 +
  1.1019 +  n += mRuleHash.SizeOfExcludingThis(aMallocSizeOf);
  1.1020 +  for (uint32_t i = 0; i < ArrayLength(mPseudoElementRuleHashes); ++i) {
  1.1021 +    if (mPseudoElementRuleHashes[i])
  1.1022 +      n += mPseudoElementRuleHashes[i]->SizeOfIncludingThis(aMallocSizeOf);
  1.1023 +  }
  1.1024 +
  1.1025 +  n += mStateSelectors.SizeOfExcludingThis(aMallocSizeOf);
  1.1026 +
  1.1027 +  n += PL_DHashTableSizeOfExcludingThis(&mIdSelectors,
  1.1028 +                                        SizeOfSelectorsEntry, aMallocSizeOf);
  1.1029 +  n += PL_DHashTableSizeOfExcludingThis(&mClassSelectors,
  1.1030 +                                        SizeOfSelectorsEntry, aMallocSizeOf);
  1.1031 +
  1.1032 +  n += mPossiblyNegatedClassSelectors.SizeOfExcludingThis(aMallocSizeOf);
  1.1033 +  n += mPossiblyNegatedIDSelectors.SizeOfExcludingThis(aMallocSizeOf);
  1.1034 +
  1.1035 +  n += PL_DHashTableSizeOfExcludingThis(&mAttributeSelectors,
  1.1036 +                                        SizeOfSelectorsEntry, aMallocSizeOf);
  1.1037 +  n += PL_DHashTableSizeOfExcludingThis(&mAnonBoxRules,
  1.1038 +                                        SizeOfRuleHashTableEntry, aMallocSizeOf);
  1.1039 +#ifdef MOZ_XUL
  1.1040 +  n += PL_DHashTableSizeOfExcludingThis(&mXULTreeRules,
  1.1041 +                                        SizeOfRuleHashTableEntry, aMallocSizeOf);
  1.1042 +#endif
  1.1043 +
  1.1044 +  n += mFontFaceRules.SizeOfExcludingThis(aMallocSizeOf);
  1.1045 +  n += mKeyframesRules.SizeOfExcludingThis(aMallocSizeOf);
  1.1046 +  n += mFontFeatureValuesRules.SizeOfExcludingThis(aMallocSizeOf);
  1.1047 +  n += mPageRules.SizeOfExcludingThis(aMallocSizeOf);
  1.1048 +
  1.1049 +  return n;
  1.1050 +}
  1.1051 +
  1.1052 +nsTArray<nsCSSSelector*>*
  1.1053 +RuleCascadeData::AttributeListFor(nsIAtom* aAttribute)
  1.1054 +{
  1.1055 +  AtomSelectorEntry *entry =
  1.1056 +    static_cast<AtomSelectorEntry*>
  1.1057 +               (PL_DHashTableOperate(&mAttributeSelectors, aAttribute,
  1.1058 +                                     PL_DHASH_ADD));
  1.1059 +  if (!entry)
  1.1060 +    return nullptr;
  1.1061 +  return &entry->mSelectors;
  1.1062 +}
  1.1063 +
  1.1064 +// -------------------------------
  1.1065 +// CSS Style rule processor implementation
  1.1066 +//
  1.1067 +
  1.1068 +nsCSSRuleProcessor::nsCSSRuleProcessor(const sheet_array_type& aSheets,
  1.1069 +                                       uint8_t aSheetType,
  1.1070 +                                       Element* aScopeElement)
  1.1071 +  : mSheets(aSheets)
  1.1072 +  , mRuleCascades(nullptr)
  1.1073 +  , mLastPresContext(nullptr)
  1.1074 +  , mScopeElement(aScopeElement)
  1.1075 +  , mSheetType(aSheetType)
  1.1076 +{
  1.1077 +  NS_ASSERTION(!!mScopeElement == (aSheetType == nsStyleSet::eScopedDocSheet),
  1.1078 +               "aScopeElement must be specified iff aSheetType is "
  1.1079 +               "eScopedDocSheet");
  1.1080 +  for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) {
  1.1081 +    mSheets[i]->AddRuleProcessor(this);
  1.1082 +  }
  1.1083 +}
  1.1084 +
  1.1085 +nsCSSRuleProcessor::~nsCSSRuleProcessor()
  1.1086 +{
  1.1087 +  for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) {
  1.1088 +    mSheets[i]->DropRuleProcessor(this);
  1.1089 +  }
  1.1090 +  mSheets.Clear();
  1.1091 +  ClearRuleCascades();
  1.1092 +}
  1.1093 +
  1.1094 +NS_IMPL_ISUPPORTS(nsCSSRuleProcessor, nsIStyleRuleProcessor)
  1.1095 +
  1.1096 +/* static */ nsresult
  1.1097 +nsCSSRuleProcessor::Startup()
  1.1098 +{
  1.1099 +  Preferences::AddBoolVarCache(&gSupportVisitedPseudo, VISITED_PSEUDO_PREF,
  1.1100 +                               true);
  1.1101 +
  1.1102 +  return NS_OK;
  1.1103 +}
  1.1104 +
  1.1105 +static bool
  1.1106 +InitSystemMetrics()
  1.1107 +{
  1.1108 +  NS_ASSERTION(!sSystemMetrics, "already initialized");
  1.1109 +
  1.1110 +  sSystemMetrics = new nsTArray< nsCOMPtr<nsIAtom> >;
  1.1111 +  NS_ENSURE_TRUE(sSystemMetrics, false);
  1.1112 +
  1.1113 +  /***************************************************************************
  1.1114 +   * ANY METRICS ADDED HERE SHOULD ALSO BE ADDED AS MEDIA QUERIES IN         *
  1.1115 +   * nsMediaFeatures.cpp                                                     *
  1.1116 +   ***************************************************************************/
  1.1117 +
  1.1118 +  int32_t metricResult =
  1.1119 +    LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollArrowStyle);
  1.1120 +  if (metricResult & LookAndFeel::eScrollArrow_StartBackward) {
  1.1121 +    sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_start_backward);
  1.1122 +  }
  1.1123 +  if (metricResult & LookAndFeel::eScrollArrow_StartForward) {
  1.1124 +    sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_start_forward);
  1.1125 +  }
  1.1126 +  if (metricResult & LookAndFeel::eScrollArrow_EndBackward) {
  1.1127 +    sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_end_backward);
  1.1128 +  }
  1.1129 +  if (metricResult & LookAndFeel::eScrollArrow_EndForward) {
  1.1130 +    sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_end_forward);
  1.1131 +  }
  1.1132 +
  1.1133 +  metricResult =
  1.1134 +    LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollSliderStyle);
  1.1135 +  if (metricResult != LookAndFeel::eScrollThumbStyle_Normal) {
  1.1136 +    sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_thumb_proportional);
  1.1137 +  }
  1.1138 +
  1.1139 +  metricResult =
  1.1140 +    LookAndFeel::GetInt(LookAndFeel::eIntID_ImagesInMenus);
  1.1141 +  if (metricResult) {
  1.1142 +    sSystemMetrics->AppendElement(nsGkAtoms::images_in_menus);
  1.1143 +  }
  1.1144 +
  1.1145 +  metricResult =
  1.1146 +    LookAndFeel::GetInt(LookAndFeel::eIntID_ImagesInButtons);
  1.1147 +  if (metricResult) {
  1.1148 +    sSystemMetrics->AppendElement(nsGkAtoms::images_in_buttons);
  1.1149 +  }
  1.1150 +
  1.1151 +  metricResult =
  1.1152 +    LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars);
  1.1153 +  if (metricResult) {
  1.1154 +    sSystemMetrics->AppendElement(nsGkAtoms::overlay_scrollbars);
  1.1155 +  }
  1.1156 +
  1.1157 +  metricResult =
  1.1158 +    LookAndFeel::GetInt(LookAndFeel::eIntID_MenuBarDrag);
  1.1159 +  if (metricResult) {
  1.1160 +    sSystemMetrics->AppendElement(nsGkAtoms::menubar_drag);
  1.1161 +  }
  1.1162 +
  1.1163 +  nsresult rv =
  1.1164 +    LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsDefaultTheme, &metricResult);
  1.1165 +  if (NS_SUCCEEDED(rv) && metricResult) {
  1.1166 +    sSystemMetrics->AppendElement(nsGkAtoms::windows_default_theme);
  1.1167 +  }
  1.1168 +
  1.1169 +  rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacGraphiteTheme, &metricResult);
  1.1170 +  if (NS_SUCCEEDED(rv) && metricResult) {
  1.1171 +    sSystemMetrics->AppendElement(nsGkAtoms::mac_graphite_theme);
  1.1172 +  }
  1.1173 +
  1.1174 +  rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacLionTheme, &metricResult);
  1.1175 +  if (NS_SUCCEEDED(rv) && metricResult) {
  1.1176 +    sSystemMetrics->AppendElement(nsGkAtoms::mac_lion_theme);
  1.1177 +  }
  1.1178 +
  1.1179 +  rv = LookAndFeel::GetInt(LookAndFeel::eIntID_DWMCompositor, &metricResult);
  1.1180 +  if (NS_SUCCEEDED(rv) && metricResult) {
  1.1181 +    sSystemMetrics->AppendElement(nsGkAtoms::windows_compositor);
  1.1182 +  }
  1.1183 +
  1.1184 +  rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsGlass, &metricResult);
  1.1185 +  if (NS_SUCCEEDED(rv) && metricResult) {
  1.1186 +    sSystemMetrics->AppendElement(nsGkAtoms::windows_glass);
  1.1187 +  }
  1.1188 +
  1.1189 +  rv = LookAndFeel::GetInt(LookAndFeel::eIntID_ColorPickerAvailable, &metricResult);
  1.1190 +  if (NS_SUCCEEDED(rv) && metricResult) {
  1.1191 +    sSystemMetrics->AppendElement(nsGkAtoms::color_picker_available);
  1.1192 +  }
  1.1193 +
  1.1194 +  rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsClassic, &metricResult);
  1.1195 +  if (NS_SUCCEEDED(rv) && metricResult) {
  1.1196 +    sSystemMetrics->AppendElement(nsGkAtoms::windows_classic);
  1.1197 +  }
  1.1198 +
  1.1199 +  rv = LookAndFeel::GetInt(LookAndFeel::eIntID_TouchEnabled, &metricResult);
  1.1200 +  if (NS_SUCCEEDED(rv) && metricResult) {
  1.1201 +    sSystemMetrics->AppendElement(nsGkAtoms::touch_enabled);
  1.1202 +  }
  1.1203 + 
  1.1204 +  rv = LookAndFeel::GetInt(LookAndFeel::eIntID_SwipeAnimationEnabled,
  1.1205 +                           &metricResult);
  1.1206 +  if (NS_SUCCEEDED(rv) && metricResult) {
  1.1207 +    sSystemMetrics->AppendElement(nsGkAtoms::swipe_animation_enabled);
  1.1208 +  }
  1.1209 +
  1.1210 +  rv = LookAndFeel::GetInt(LookAndFeel::eIntID_PhysicalHomeButton,
  1.1211 +                           &metricResult);
  1.1212 +  if (NS_SUCCEEDED(rv) && metricResult) {
  1.1213 +    sSystemMetrics->AppendElement(nsGkAtoms::physical_home_button);
  1.1214 +  }
  1.1215 +
  1.1216 +#ifdef XP_WIN
  1.1217 +  if (NS_SUCCEEDED(
  1.1218 +        LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsThemeIdentifier,
  1.1219 +                            &metricResult))) {
  1.1220 +    nsCSSRuleProcessor::SetWindowsThemeIdentifier(static_cast<uint8_t>(metricResult));
  1.1221 +    switch(metricResult) {
  1.1222 +      case LookAndFeel::eWindowsTheme_Aero:
  1.1223 +        sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero);
  1.1224 +        break;
  1.1225 +      case LookAndFeel::eWindowsTheme_AeroLite:
  1.1226 +        sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero_lite);
  1.1227 +        break;
  1.1228 +      case LookAndFeel::eWindowsTheme_LunaBlue:
  1.1229 +        sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_blue);
  1.1230 +        break;
  1.1231 +      case LookAndFeel::eWindowsTheme_LunaOlive:
  1.1232 +        sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_olive);
  1.1233 +        break;
  1.1234 +      case LookAndFeel::eWindowsTheme_LunaSilver:
  1.1235 +        sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_silver);
  1.1236 +        break;
  1.1237 +      case LookAndFeel::eWindowsTheme_Royale:
  1.1238 +        sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_royale);
  1.1239 +        break;
  1.1240 +      case LookAndFeel::eWindowsTheme_Zune:
  1.1241 +        sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_zune);
  1.1242 +        break;
  1.1243 +      case LookAndFeel::eWindowsTheme_Generic:
  1.1244 +        sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_generic);
  1.1245 +        break;
  1.1246 +    }
  1.1247 +  }
  1.1248 +#endif
  1.1249 +
  1.1250 +  return true;
  1.1251 +}
  1.1252 +
  1.1253 +/* static */ void
  1.1254 +nsCSSRuleProcessor::FreeSystemMetrics()
  1.1255 +{
  1.1256 +  delete sSystemMetrics;
  1.1257 +  sSystemMetrics = nullptr;
  1.1258 +}
  1.1259 +
  1.1260 +/* static */ void
  1.1261 +nsCSSRuleProcessor::Shutdown()
  1.1262 +{
  1.1263 +  FreeSystemMetrics();
  1.1264 +}
  1.1265 +
  1.1266 +/* static */ bool
  1.1267 +nsCSSRuleProcessor::HasSystemMetric(nsIAtom* aMetric)
  1.1268 +{
  1.1269 +  if (!sSystemMetrics && !InitSystemMetrics()) {
  1.1270 +    return false;
  1.1271 +  }
  1.1272 +  return sSystemMetrics->IndexOf(aMetric) != sSystemMetrics->NoIndex;
  1.1273 +}
  1.1274 +
  1.1275 +#ifdef XP_WIN
  1.1276 +/* static */ uint8_t
  1.1277 +nsCSSRuleProcessor::GetWindowsThemeIdentifier()
  1.1278 +{
  1.1279 +  if (!sSystemMetrics)
  1.1280 +    InitSystemMetrics();
  1.1281 +  return sWinThemeId;
  1.1282 +}
  1.1283 +#endif
  1.1284 +
  1.1285 +/* static */
  1.1286 +EventStates
  1.1287 +nsCSSRuleProcessor::GetContentState(Element* aElement, const TreeMatchContext& aTreeMatchContext)
  1.1288 +{
  1.1289 +  EventStates state = aElement->StyleState();
  1.1290 +
  1.1291 +  // If we are not supposed to mark visited links as such, be sure to
  1.1292 +  // flip the bits appropriately.  We want to do this here, rather
  1.1293 +  // than in GetContentStateForVisitedHandling, so that we don't
  1.1294 +  // expose that :visited support is disabled to the Web page.
  1.1295 +  if (state.HasState(NS_EVENT_STATE_VISITED) &&
  1.1296 +      (!gSupportVisitedPseudo ||
  1.1297 +       aElement->OwnerDoc()->IsBeingUsedAsImage() ||
  1.1298 +       aTreeMatchContext.mUsingPrivateBrowsing)) {
  1.1299 +    state &= ~NS_EVENT_STATE_VISITED;
  1.1300 +    state |= NS_EVENT_STATE_UNVISITED;
  1.1301 +  }
  1.1302 +  return state;
  1.1303 +}
  1.1304 +
  1.1305 +/* static */
  1.1306 +bool
  1.1307 +nsCSSRuleProcessor::IsLink(Element* aElement)
  1.1308 +{
  1.1309 +  EventStates state = aElement->StyleState();
  1.1310 +  return state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED);
  1.1311 +}
  1.1312 +
  1.1313 +/* static */
  1.1314 +EventStates
  1.1315 +nsCSSRuleProcessor::GetContentStateForVisitedHandling(
  1.1316 +                     Element* aElement,
  1.1317 +                     const TreeMatchContext& aTreeMatchContext,
  1.1318 +                     nsRuleWalker::VisitedHandlingType aVisitedHandling,
  1.1319 +                     bool aIsRelevantLink)
  1.1320 +{
  1.1321 +  EventStates contentState = GetContentState(aElement, aTreeMatchContext);
  1.1322 +  if (contentState.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)) {
  1.1323 +    NS_ABORT_IF_FALSE(IsLink(aElement), "IsLink() should match state");
  1.1324 +    contentState &= ~(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED);
  1.1325 +    if (aIsRelevantLink) {
  1.1326 +      switch (aVisitedHandling) {
  1.1327 +        case nsRuleWalker::eRelevantLinkUnvisited:
  1.1328 +          contentState |= NS_EVENT_STATE_UNVISITED;
  1.1329 +          break;
  1.1330 +        case nsRuleWalker::eRelevantLinkVisited:
  1.1331 +          contentState |= NS_EVENT_STATE_VISITED;
  1.1332 +          break;
  1.1333 +        case nsRuleWalker::eLinksVisitedOrUnvisited:
  1.1334 +          contentState |= NS_EVENT_STATE_UNVISITED | NS_EVENT_STATE_VISITED;
  1.1335 +          break;
  1.1336 +      }
  1.1337 +    } else {
  1.1338 +      contentState |= NS_EVENT_STATE_UNVISITED;
  1.1339 +    }
  1.1340 +  }
  1.1341 +  return contentState;
  1.1342 +}
  1.1343 +
  1.1344 +/**
  1.1345 + * A |NodeMatchContext| has data about matching a selector (without
  1.1346 + * combinators) against a single node.  It contains only input to the
  1.1347 + * matching.
  1.1348 + *
  1.1349 + * Unlike |RuleProcessorData|, which is similar, a |NodeMatchContext|
  1.1350 + * can vary depending on the selector matching process.  In other words,
  1.1351 + * there might be multiple NodeMatchContexts corresponding to a single
  1.1352 + * node, but only one possible RuleProcessorData.
  1.1353 + */
  1.1354 +struct NodeMatchContext {
  1.1355 +  // In order to implement nsCSSRuleProcessor::HasStateDependentStyle,
  1.1356 +  // we need to be able to see if a node might match an
  1.1357 +  // event-state-dependent selector for any value of that event state.
  1.1358 +  // So mStateMask contains the states that should NOT be tested.
  1.1359 +  //
  1.1360 +  // NOTE: For |mStateMask| to work correctly, it's important that any
  1.1361 +  // change that changes multiple state bits include all those state
  1.1362 +  // bits in the notification.  Otherwise, if multiple states change but
  1.1363 +  // we do separate notifications then we might determine the style is
  1.1364 +  // not state-dependent when it really is (e.g., determining that a
  1.1365 +  // :hover:active rule no longer matches when both states are unset).
  1.1366 +  const EventStates mStateMask;
  1.1367 +
  1.1368 +  // Is this link the unique link whose visitedness can affect the style
  1.1369 +  // of the node being matched?  (That link is the nearest link to the
  1.1370 +  // node being matched that is itself or an ancestor.)
  1.1371 +  //
  1.1372 +  // Always false when TreeMatchContext::mForStyling is false.  (We
  1.1373 +  // could figure it out for SelectorListMatches, but we're starting
  1.1374 +  // from the middle of the selector list when doing
  1.1375 +  // Has{Attribute,State}DependentStyle, so we can't tell.  So when
  1.1376 +  // mForStyling is false, we have to assume we don't know.)
  1.1377 +  const bool mIsRelevantLink;
  1.1378 +
  1.1379 +  NodeMatchContext(EventStates aStateMask, bool aIsRelevantLink)
  1.1380 +    : mStateMask(aStateMask)
  1.1381 +    , mIsRelevantLink(aIsRelevantLink)
  1.1382 +  {
  1.1383 +  }
  1.1384 +};
  1.1385 +
  1.1386 +static bool ValueIncludes(const nsSubstring& aValueList,
  1.1387 +                            const nsSubstring& aValue,
  1.1388 +                            const nsStringComparator& aComparator)
  1.1389 +{
  1.1390 +  const char16_t *p = aValueList.BeginReading(),
  1.1391 +              *p_end = aValueList.EndReading();
  1.1392 +
  1.1393 +  while (p < p_end) {
  1.1394 +    // skip leading space
  1.1395 +    while (p != p_end && nsContentUtils::IsHTMLWhitespace(*p))
  1.1396 +      ++p;
  1.1397 +
  1.1398 +    const char16_t *val_start = p;
  1.1399 +
  1.1400 +    // look for space or end
  1.1401 +    while (p != p_end && !nsContentUtils::IsHTMLWhitespace(*p))
  1.1402 +      ++p;
  1.1403 +
  1.1404 +    const char16_t *val_end = p;
  1.1405 +
  1.1406 +    if (val_start < val_end &&
  1.1407 +        aValue.Equals(Substring(val_start, val_end), aComparator))
  1.1408 +      return true;
  1.1409 +
  1.1410 +    ++p; // we know the next character is not whitespace
  1.1411 +  }
  1.1412 +  return false;
  1.1413 +}
  1.1414 +
  1.1415 +// Return whether we should apply a "global" (i.e., universal-tag)
  1.1416 +// selector for event states in quirks mode.  Note that
  1.1417 +// |IsLink()| is checked separately by the caller, so we return
  1.1418 +// false for |nsGkAtoms::a|, which here means a named anchor.
  1.1419 +inline bool IsQuirkEventSensitive(nsIAtom *aContentTag)
  1.1420 +{
  1.1421 +  return bool ((nsGkAtoms::button == aContentTag) ||
  1.1422 +                 (nsGkAtoms::img == aContentTag)    ||
  1.1423 +                 (nsGkAtoms::input == aContentTag)  ||
  1.1424 +                 (nsGkAtoms::label == aContentTag)  ||
  1.1425 +                 (nsGkAtoms::select == aContentTag) ||
  1.1426 +                 (nsGkAtoms::textarea == aContentTag));
  1.1427 +}
  1.1428 +
  1.1429 +
  1.1430 +static inline bool
  1.1431 +IsSignificantChild(nsIContent* aChild, bool aTextIsSignificant,
  1.1432 +                   bool aWhitespaceIsSignificant)
  1.1433 +{
  1.1434 +  return nsStyleUtil::IsSignificantChild(aChild, aTextIsSignificant,
  1.1435 +                                         aWhitespaceIsSignificant);
  1.1436 +}
  1.1437 +
  1.1438 +// This function is to be called once we have fetched a value for an attribute
  1.1439 +// whose namespace and name match those of aAttrSelector.  This function
  1.1440 +// performs comparisons on the value only, based on aAttrSelector->mFunction.
  1.1441 +static bool AttrMatchesValue(const nsAttrSelector* aAttrSelector,
  1.1442 +                               const nsString& aValue, bool isHTML)
  1.1443 +{
  1.1444 +  NS_PRECONDITION(aAttrSelector, "Must have an attribute selector");
  1.1445 +
  1.1446 +  // http://lists.w3.org/Archives/Public/www-style/2008Apr/0038.html
  1.1447 +  // *= (CONTAINSMATCH) ~= (INCLUDES) ^= (BEGINSMATCH) $= (ENDSMATCH)
  1.1448 +  // all accept the empty string, but match nothing.
  1.1449 +  if (aAttrSelector->mValue.IsEmpty() &&
  1.1450 +      (aAttrSelector->mFunction == NS_ATTR_FUNC_INCLUDES ||
  1.1451 +       aAttrSelector->mFunction == NS_ATTR_FUNC_ENDSMATCH ||
  1.1452 +       aAttrSelector->mFunction == NS_ATTR_FUNC_BEGINSMATCH ||
  1.1453 +       aAttrSelector->mFunction == NS_ATTR_FUNC_CONTAINSMATCH))
  1.1454 +    return false;
  1.1455 +
  1.1456 +  const nsDefaultStringComparator defaultComparator;
  1.1457 +  const nsASCIICaseInsensitiveStringComparator ciComparator;
  1.1458 +  const nsStringComparator& comparator =
  1.1459 +      (aAttrSelector->mCaseSensitive || !isHTML)
  1.1460 +                ? static_cast<const nsStringComparator&>(defaultComparator)
  1.1461 +                : static_cast<const nsStringComparator&>(ciComparator);
  1.1462 +
  1.1463 +  switch (aAttrSelector->mFunction) {
  1.1464 +    case NS_ATTR_FUNC_EQUALS:
  1.1465 +      return aValue.Equals(aAttrSelector->mValue, comparator);
  1.1466 +    case NS_ATTR_FUNC_INCLUDES:
  1.1467 +      return ValueIncludes(aValue, aAttrSelector->mValue, comparator);
  1.1468 +    case NS_ATTR_FUNC_DASHMATCH:
  1.1469 +      return nsStyleUtil::DashMatchCompare(aValue, aAttrSelector->mValue, comparator);
  1.1470 +    case NS_ATTR_FUNC_ENDSMATCH:
  1.1471 +      return StringEndsWith(aValue, aAttrSelector->mValue, comparator);
  1.1472 +    case NS_ATTR_FUNC_BEGINSMATCH:
  1.1473 +      return StringBeginsWith(aValue, aAttrSelector->mValue, comparator);
  1.1474 +    case NS_ATTR_FUNC_CONTAINSMATCH:
  1.1475 +      return FindInReadable(aAttrSelector->mValue, aValue, comparator);
  1.1476 +    default:
  1.1477 +      NS_NOTREACHED("Shouldn't be ending up here");
  1.1478 +      return false;
  1.1479 +  }
  1.1480 +}
  1.1481 +
  1.1482 +static inline bool
  1.1483 +edgeChildMatches(Element* aElement, TreeMatchContext& aTreeMatchContext,
  1.1484 +                 bool checkFirst, bool checkLast)
  1.1485 +{
  1.1486 +  nsIContent *parent = aElement->GetParent();
  1.1487 +  if (!parent) {
  1.1488 +    return false;
  1.1489 +  }
  1.1490 +
  1.1491 +  if (aTreeMatchContext.mForStyling)
  1.1492 +    parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
  1.1493 +
  1.1494 +  return (!checkFirst ||
  1.1495 +          aTreeMatchContext.mNthIndexCache.
  1.1496 +            GetNthIndex(aElement, false, false, true) == 1) &&
  1.1497 +         (!checkLast ||
  1.1498 +          aTreeMatchContext.mNthIndexCache.
  1.1499 +            GetNthIndex(aElement, false, true, true) == 1);
  1.1500 +}
  1.1501 +
  1.1502 +static inline bool
  1.1503 +nthChildGenericMatches(Element* aElement,
  1.1504 +                       TreeMatchContext& aTreeMatchContext,
  1.1505 +                       nsPseudoClassList* pseudoClass,
  1.1506 +                       bool isOfType, bool isFromEnd)
  1.1507 +{
  1.1508 +  nsIContent *parent = aElement->GetParent();
  1.1509 +  if (!parent) {
  1.1510 +    return false;
  1.1511 +  }
  1.1512 +
  1.1513 +  if (aTreeMatchContext.mForStyling) {
  1.1514 +    if (isFromEnd)
  1.1515 +      parent->SetFlags(NODE_HAS_SLOW_SELECTOR);
  1.1516 +    else
  1.1517 +      parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
  1.1518 +  }
  1.1519 +
  1.1520 +  const int32_t index = aTreeMatchContext.mNthIndexCache.
  1.1521 +    GetNthIndex(aElement, isOfType, isFromEnd, false);
  1.1522 +  if (index <= 0) {
  1.1523 +    // Node is anonymous content (not really a child of its parent).
  1.1524 +    return false;
  1.1525 +  }
  1.1526 +
  1.1527 +  const int32_t a = pseudoClass->u.mNumbers[0];
  1.1528 +  const int32_t b = pseudoClass->u.mNumbers[1];
  1.1529 +  // result should be true if there exists n >= 0 such that
  1.1530 +  // a * n + b == index.
  1.1531 +  if (a == 0) {
  1.1532 +    return b == index;
  1.1533 +  }
  1.1534 +
  1.1535 +  // Integer division in C does truncation (towards 0).  So
  1.1536 +  // check that the result is nonnegative, and that there was no
  1.1537 +  // truncation.
  1.1538 +  const int32_t n = (index - b) / a;
  1.1539 +  return n >= 0 && (a * n == index - b);
  1.1540 +}
  1.1541 +
  1.1542 +static inline bool
  1.1543 +edgeOfTypeMatches(Element* aElement, TreeMatchContext& aTreeMatchContext,
  1.1544 +                  bool checkFirst, bool checkLast)
  1.1545 +{
  1.1546 +  nsIContent *parent = aElement->GetParent();
  1.1547 +  if (!parent) {
  1.1548 +    return false;
  1.1549 +  }
  1.1550 +
  1.1551 +  if (aTreeMatchContext.mForStyling) {
  1.1552 +    if (checkLast)
  1.1553 +      parent->SetFlags(NODE_HAS_SLOW_SELECTOR);
  1.1554 +    else
  1.1555 +      parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
  1.1556 +  }
  1.1557 +
  1.1558 +  return (!checkFirst ||
  1.1559 +          aTreeMatchContext.mNthIndexCache.
  1.1560 +            GetNthIndex(aElement, true, false, true) == 1) &&
  1.1561 +         (!checkLast ||
  1.1562 +          aTreeMatchContext.mNthIndexCache.
  1.1563 +            GetNthIndex(aElement, true, true, true) == 1);
  1.1564 +}
  1.1565 +
  1.1566 +static inline bool
  1.1567 +checkGenericEmptyMatches(Element* aElement,
  1.1568 +                         TreeMatchContext& aTreeMatchContext,
  1.1569 +                         bool isWhitespaceSignificant)
  1.1570 +{
  1.1571 +  nsIContent *child = nullptr;
  1.1572 +  int32_t index = -1;
  1.1573 +
  1.1574 +  if (aTreeMatchContext.mForStyling)
  1.1575 +    aElement->SetFlags(NODE_HAS_EMPTY_SELECTOR);
  1.1576 +
  1.1577 +  do {
  1.1578 +    child = aElement->GetChildAt(++index);
  1.1579 +    // stop at first non-comment (and non-whitespace for
  1.1580 +    // :-moz-only-whitespace) node        
  1.1581 +  } while (child && !IsSignificantChild(child, true, isWhitespaceSignificant));
  1.1582 +  return (child == nullptr);
  1.1583 +}
  1.1584 +
  1.1585 +// Arrays of the states that are relevant for various pseudoclasses.
  1.1586 +static const EventStates sPseudoClassStateDependences[] = {
  1.1587 +#define CSS_PSEUDO_CLASS(_name, _value, _pref)  \
  1.1588 +  EventStates(),
  1.1589 +#define CSS_STATE_DEPENDENT_PSEUDO_CLASS(_name, _value, _pref, _states)  \
  1.1590 +  _states,
  1.1591 +#include "nsCSSPseudoClassList.h"
  1.1592 +#undef CSS_STATE_DEPENDENT_PSEUDO_CLASS
  1.1593 +#undef CSS_PSEUDO_CLASS
  1.1594 +  // Add more entries for our fake values to make sure we can't
  1.1595 +  // index out of bounds into this array no matter what.
  1.1596 +  EventStates(),
  1.1597 +  EventStates()
  1.1598 +};
  1.1599 +
  1.1600 +static const EventStates sPseudoClassStates[] = {
  1.1601 +#define CSS_PSEUDO_CLASS(_name, _value, _pref)  \
  1.1602 +  EventStates(),
  1.1603 +#define CSS_STATE_PSEUDO_CLASS(_name, _value, _pref, _states) \
  1.1604 +  _states,
  1.1605 +#include "nsCSSPseudoClassList.h"
  1.1606 +#undef CSS_STATE_PSEUDO_CLASS
  1.1607 +#undef CSS_PSEUDO_CLASS
  1.1608 +  // Add more entries for our fake values to make sure we can't
  1.1609 +  // index out of bounds into this array no matter what.
  1.1610 +  EventStates(),
  1.1611 +  EventStates()
  1.1612 +};
  1.1613 +static_assert(MOZ_ARRAY_LENGTH(sPseudoClassStates) ==
  1.1614 +              nsCSSPseudoClasses::ePseudoClass_NotPseudoClass + 1,
  1.1615 +              "ePseudoClass_NotPseudoClass is no longer at the end of"
  1.1616 +              "sPseudoClassStates");
  1.1617 +
  1.1618 +static bool
  1.1619 +StateSelectorMatches(Element* aElement,
  1.1620 +                     nsCSSSelector* aSelector,
  1.1621 +                     NodeMatchContext& aNodeMatchContext,
  1.1622 +                     TreeMatchContext& aTreeMatchContext,
  1.1623 +                     bool* const aDependence,
  1.1624 +                     EventStates aStatesToCheck)
  1.1625 +{
  1.1626 +  NS_PRECONDITION(!aStatesToCheck.IsEmpty(),
  1.1627 +                  "should only need to call StateSelectorMatches if "
  1.1628 +                  "aStatesToCheck is not empty");
  1.1629 +
  1.1630 +  const bool isNegated = aDependence != nullptr;
  1.1631 +
  1.1632 +  // Bit-based pseudo-classes
  1.1633 +  if (aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE) &&
  1.1634 +      aTreeMatchContext.mCompatMode == eCompatibility_NavQuirks &&
  1.1635 +      // global selector:
  1.1636 +      !aSelector->HasTagSelector() && !aSelector->mIDList &&
  1.1637 +      !aSelector->mClassList && !aSelector->mAttrList &&
  1.1638 +      // This (or the other way around) both make :not() asymmetric
  1.1639 +      // in quirks mode (and it's hard to work around since we're
  1.1640 +      // testing the current mNegations, not the first
  1.1641 +      // (unnegated)). This at least makes it closer to the spec.
  1.1642 +      !isNegated &&
  1.1643 +      // important for |IsQuirkEventSensitive|:
  1.1644 +      aElement->IsHTML() && !nsCSSRuleProcessor::IsLink(aElement) &&
  1.1645 +      !IsQuirkEventSensitive(aElement->Tag())) {
  1.1646 +    // In quirks mode, only make certain elements sensitive to
  1.1647 +    // selectors ":hover" and ":active".
  1.1648 +    return false;
  1.1649 +  }
  1.1650 +
  1.1651 +  if (aTreeMatchContext.mForStyling &&
  1.1652 +      aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_HOVER)) {
  1.1653 +    // Mark the element as having :hover-dependent style
  1.1654 +    aElement->SetHasRelevantHoverRules();
  1.1655 +  }
  1.1656 +
  1.1657 +  if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(aStatesToCheck)) {
  1.1658 +    if (aDependence) {
  1.1659 +      *aDependence = true;
  1.1660 +    }
  1.1661 +  } else {
  1.1662 +    EventStates contentState =
  1.1663 +      nsCSSRuleProcessor::GetContentStateForVisitedHandling(
  1.1664 +                                   aElement,
  1.1665 +                                   aTreeMatchContext,
  1.1666 +                                   aTreeMatchContext.VisitedHandling(),
  1.1667 +                                   aNodeMatchContext.mIsRelevantLink);
  1.1668 +    if (!contentState.HasAtLeastOneOfStates(aStatesToCheck)) {
  1.1669 +      return false;
  1.1670 +    }
  1.1671 +  }
  1.1672 +
  1.1673 +  return true;
  1.1674 +}
  1.1675 +
  1.1676 +static bool
  1.1677 +StateSelectorMatches(Element* aElement,
  1.1678 +                     nsCSSSelector* aSelector,
  1.1679 +                     NodeMatchContext& aNodeMatchContext,
  1.1680 +                     TreeMatchContext& aTreeMatchContext)
  1.1681 +{
  1.1682 +  for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
  1.1683 +       pseudoClass; pseudoClass = pseudoClass->mNext) {
  1.1684 +    EventStates statesToCheck = sPseudoClassStates[pseudoClass->mType];
  1.1685 +    if (!statesToCheck.IsEmpty() &&
  1.1686 +        !StateSelectorMatches(aElement, aSelector, aNodeMatchContext,
  1.1687 +                              aTreeMatchContext, nullptr, statesToCheck)) {
  1.1688 +      return false;
  1.1689 +    }
  1.1690 +  }
  1.1691 +  return true;
  1.1692 +}
  1.1693 +
  1.1694 +// |aDependence| has two functions:
  1.1695 +//  * when non-null, it indicates that we're processing a negation,
  1.1696 +//    which is done only when SelectorMatches calls itself recursively
  1.1697 +//  * what it points to should be set to true whenever a test is skipped
  1.1698 +//    because of aNodeMatchContent.mStateMask
  1.1699 +static bool SelectorMatches(Element* aElement,
  1.1700 +                              nsCSSSelector* aSelector,
  1.1701 +                              NodeMatchContext& aNodeMatchContext,
  1.1702 +                              TreeMatchContext& aTreeMatchContext,
  1.1703 +                              bool* const aDependence = nullptr)
  1.1704 +
  1.1705 +{
  1.1706 +  NS_PRECONDITION(!aSelector->IsPseudoElement(),
  1.1707 +                  "Pseudo-element snuck into SelectorMatches?");
  1.1708 +  NS_ABORT_IF_FALSE(aTreeMatchContext.mForStyling ||
  1.1709 +                    !aNodeMatchContext.mIsRelevantLink,
  1.1710 +                    "mIsRelevantLink should be set to false when mForStyling "
  1.1711 +                    "is false since we don't know how to set it correctly in "
  1.1712 +                    "Has(Attribute|State)DependentStyle");
  1.1713 +
  1.1714 +  // namespace/tag match
  1.1715 +  // optimization : bail out early if we can
  1.1716 +  if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
  1.1717 +       aElement->GetNameSpaceID() != aSelector->mNameSpace))
  1.1718 +    return false;
  1.1719 +
  1.1720 +  if (aSelector->mLowercaseTag) {
  1.1721 +    nsIAtom* selectorTag =
  1.1722 +      (aTreeMatchContext.mIsHTMLDocument && aElement->IsHTML()) ?
  1.1723 +        aSelector->mLowercaseTag : aSelector->mCasedTag;
  1.1724 +    if (selectorTag != aElement->Tag()) {
  1.1725 +      return false;
  1.1726 +    }
  1.1727 +  }
  1.1728 +
  1.1729 +  nsAtomList* IDList = aSelector->mIDList;
  1.1730 +  if (IDList) {
  1.1731 +    nsIAtom* id = aElement->GetID();
  1.1732 +    if (id) {
  1.1733 +      // case sensitivity: bug 93371
  1.1734 +      const bool isCaseSensitive =
  1.1735 +        aTreeMatchContext.mCompatMode != eCompatibility_NavQuirks;
  1.1736 +
  1.1737 +      if (isCaseSensitive) {
  1.1738 +        do {
  1.1739 +          if (IDList->mAtom != id) {
  1.1740 +            return false;
  1.1741 +          }
  1.1742 +          IDList = IDList->mNext;
  1.1743 +        } while (IDList);
  1.1744 +      } else {
  1.1745 +        // Use EqualsIgnoreASCIICase instead of full on unicode case conversion
  1.1746 +        // in order to save on performance. This is only used in quirks mode
  1.1747 +        // anyway.
  1.1748 +        nsDependentAtomString id1Str(id);
  1.1749 +        do {
  1.1750 +          if (!nsContentUtils::EqualsIgnoreASCIICase(id1Str,
  1.1751 +                 nsDependentAtomString(IDList->mAtom))) {
  1.1752 +            return false;
  1.1753 +          }
  1.1754 +          IDList = IDList->mNext;
  1.1755 +        } while (IDList);
  1.1756 +      }
  1.1757 +    } else {
  1.1758 +      // Element has no id but we have an id selector
  1.1759 +      return false;
  1.1760 +    }
  1.1761 +  }
  1.1762 +
  1.1763 +  nsAtomList* classList = aSelector->mClassList;
  1.1764 +  if (classList) {
  1.1765 +    // test for class match
  1.1766 +    const nsAttrValue *elementClasses = aElement->GetClasses();
  1.1767 +    if (!elementClasses) {
  1.1768 +      // Element has no classes but we have a class selector
  1.1769 +      return false;
  1.1770 +    }
  1.1771 +
  1.1772 +    // case sensitivity: bug 93371
  1.1773 +    const bool isCaseSensitive =
  1.1774 +      aTreeMatchContext.mCompatMode != eCompatibility_NavQuirks;
  1.1775 +
  1.1776 +    while (classList) {
  1.1777 +      if (!elementClasses->Contains(classList->mAtom,
  1.1778 +                                    isCaseSensitive ?
  1.1779 +                                      eCaseMatters : eIgnoreCase)) {
  1.1780 +        return false;
  1.1781 +      }
  1.1782 +      classList = classList->mNext;
  1.1783 +    }
  1.1784 +  }
  1.1785 +
  1.1786 +  const bool isNegated = (aDependence != nullptr);
  1.1787 +  // The selectors for which we set node bits are, unfortunately, early
  1.1788 +  // in this function (because they're pseudo-classes, which are
  1.1789 +  // generally quick to test, and thus earlier).  If they were later,
  1.1790 +  // we'd probably avoid setting those bits in more cases where setting
  1.1791 +  // them is unnecessary.
  1.1792 +  NS_ASSERTION(aNodeMatchContext.mStateMask.IsEmpty() ||
  1.1793 +               !aTreeMatchContext.mForStyling,
  1.1794 +               "mForStyling must be false if we're just testing for "
  1.1795 +               "state-dependence");
  1.1796 +
  1.1797 +  // test for pseudo class match
  1.1798 +  for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
  1.1799 +       pseudoClass; pseudoClass = pseudoClass->mNext) {
  1.1800 +    EventStates statesToCheck = sPseudoClassStates[pseudoClass->mType];
  1.1801 +    if (statesToCheck.IsEmpty()) {
  1.1802 +      // keep the cases here in the same order as the list in
  1.1803 +      // nsCSSPseudoClassList.h
  1.1804 +      switch (pseudoClass->mType) {
  1.1805 +      case nsCSSPseudoClasses::ePseudoClass_empty:
  1.1806 +        if (!checkGenericEmptyMatches(aElement, aTreeMatchContext, true)) {
  1.1807 +          return false;
  1.1808 +        }
  1.1809 +        break;
  1.1810 +
  1.1811 +      case nsCSSPseudoClasses::ePseudoClass_mozOnlyWhitespace:
  1.1812 +        if (!checkGenericEmptyMatches(aElement, aTreeMatchContext, false)) {
  1.1813 +          return false;
  1.1814 +        }
  1.1815 +        break;
  1.1816 +
  1.1817 +      case nsCSSPseudoClasses::ePseudoClass_mozEmptyExceptChildrenWithLocalname:
  1.1818 +        {
  1.1819 +          NS_ASSERTION(pseudoClass->u.mString, "Must have string!");
  1.1820 +          nsIContent *child = nullptr;
  1.1821 +          int32_t index = -1;
  1.1822 +
  1.1823 +          if (aTreeMatchContext.mForStyling)
  1.1824 +            // FIXME:  This isn't sufficient to handle:
  1.1825 +            //   :-moz-empty-except-children-with-localname() + E
  1.1826 +            //   :-moz-empty-except-children-with-localname() ~ E
  1.1827 +            // because we don't know to restyle the grandparent of the
  1.1828 +            // inserted/removed element (as in bug 534804 for :empty).
  1.1829 +            aElement->SetFlags(NODE_HAS_SLOW_SELECTOR);
  1.1830 +          do {
  1.1831 +            child = aElement->GetChildAt(++index);
  1.1832 +          } while (child &&
  1.1833 +                   (!IsSignificantChild(child, true, false) ||
  1.1834 +                    (child->GetNameSpaceID() == aElement->GetNameSpaceID() &&
  1.1835 +                     child->Tag()->Equals(nsDependentString(pseudoClass->u.mString)))));
  1.1836 +          if (child != nullptr) {
  1.1837 +            return false;
  1.1838 +          }
  1.1839 +        }
  1.1840 +        break;
  1.1841 +
  1.1842 +      case nsCSSPseudoClasses::ePseudoClass_lang:
  1.1843 +        {
  1.1844 +          NS_ASSERTION(nullptr != pseudoClass->u.mString, "null lang parameter");
  1.1845 +          if (!pseudoClass->u.mString || !*pseudoClass->u.mString) {
  1.1846 +            return false;
  1.1847 +          }
  1.1848 +
  1.1849 +          // We have to determine the language of the current element.  Since
  1.1850 +          // this is currently no property and since the language is inherited
  1.1851 +          // from the parent we have to be prepared to look at all parent
  1.1852 +          // nodes.  The language itself is encoded in the LANG attribute.
  1.1853 +          nsAutoString language;
  1.1854 +          aElement->GetLang(language);
  1.1855 +          if (!language.IsEmpty()) {
  1.1856 +            if (!nsStyleUtil::DashMatchCompare(language,
  1.1857 +                                               nsDependentString(pseudoClass->u.mString),
  1.1858 +                                               nsASCIICaseInsensitiveStringComparator())) {
  1.1859 +              return false;
  1.1860 +            }
  1.1861 +            // This pseudo-class matched; move on to the next thing
  1.1862 +            break;
  1.1863 +          }
  1.1864 +
  1.1865 +          nsIDocument* doc = aTreeMatchContext.mDocument;
  1.1866 +          if (doc) {
  1.1867 +            // Try to get the language from the HTTP header or if this
  1.1868 +            // is missing as well from the preferences.
  1.1869 +            // The content language can be a comma-separated list of
  1.1870 +            // language codes.
  1.1871 +            doc->GetContentLanguage(language);
  1.1872 +
  1.1873 +            nsDependentString langString(pseudoClass->u.mString);
  1.1874 +            language.StripWhitespace();
  1.1875 +            int32_t begin = 0;
  1.1876 +            int32_t len = language.Length();
  1.1877 +            while (begin < len) {
  1.1878 +              int32_t end = language.FindChar(char16_t(','), begin);
  1.1879 +              if (end == kNotFound) {
  1.1880 +                end = len;
  1.1881 +              }
  1.1882 +              if (nsStyleUtil::DashMatchCompare(Substring(language, begin,
  1.1883 +                                                          end-begin),
  1.1884 +                                                langString,
  1.1885 +                                                nsASCIICaseInsensitiveStringComparator())) {
  1.1886 +                break;
  1.1887 +              }
  1.1888 +              begin = end + 1;
  1.1889 +            }
  1.1890 +            if (begin < len) {
  1.1891 +              // This pseudo-class matched
  1.1892 +              break;
  1.1893 +            }
  1.1894 +          }
  1.1895 +
  1.1896 +          return false;
  1.1897 +        }
  1.1898 +        break;
  1.1899 +
  1.1900 +      case nsCSSPseudoClasses::ePseudoClass_mozBoundElement:
  1.1901 +        if (aTreeMatchContext.mScopedRoot != aElement) {
  1.1902 +          return false;
  1.1903 +        }
  1.1904 +        break;
  1.1905 +
  1.1906 +      case nsCSSPseudoClasses::ePseudoClass_root:
  1.1907 +        if (aElement != aElement->OwnerDoc()->GetRootElement()) {
  1.1908 +          return false;
  1.1909 +        }
  1.1910 +        break;
  1.1911 +
  1.1912 +      case nsCSSPseudoClasses::ePseudoClass_any:
  1.1913 +        {
  1.1914 +          nsCSSSelectorList *l;
  1.1915 +          for (l = pseudoClass->u.mSelectors; l; l = l->mNext) {
  1.1916 +            nsCSSSelector *s = l->mSelectors;
  1.1917 +            NS_ABORT_IF_FALSE(!s->mNext && !s->IsPseudoElement(),
  1.1918 +                              "parser failed");
  1.1919 +            if (SelectorMatches(aElement, s, aNodeMatchContext,
  1.1920 +                                aTreeMatchContext)) {
  1.1921 +              break;
  1.1922 +            }
  1.1923 +          }
  1.1924 +          if (!l) {
  1.1925 +            return false;
  1.1926 +          }
  1.1927 +        }
  1.1928 +        break;
  1.1929 +
  1.1930 +      case nsCSSPseudoClasses::ePseudoClass_firstChild:
  1.1931 +        if (!edgeChildMatches(aElement, aTreeMatchContext, true, false)) {
  1.1932 +          return false;
  1.1933 +        }
  1.1934 +        break;
  1.1935 +
  1.1936 +      case nsCSSPseudoClasses::ePseudoClass_firstNode:
  1.1937 +        {
  1.1938 +          nsIContent *firstNode = nullptr;
  1.1939 +          nsIContent *parent = aElement->GetParent();
  1.1940 +          if (parent) {
  1.1941 +            if (aTreeMatchContext.mForStyling)
  1.1942 +              parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
  1.1943 +
  1.1944 +            int32_t index = -1;
  1.1945 +            do {
  1.1946 +              firstNode = parent->GetChildAt(++index);
  1.1947 +              // stop at first non-comment and non-whitespace node
  1.1948 +            } while (firstNode &&
  1.1949 +                     !IsSignificantChild(firstNode, true, false));
  1.1950 +          }
  1.1951 +          if (aElement != firstNode) {
  1.1952 +            return false;
  1.1953 +          }
  1.1954 +        }
  1.1955 +        break;
  1.1956 +
  1.1957 +      case nsCSSPseudoClasses::ePseudoClass_lastChild:
  1.1958 +        if (!edgeChildMatches(aElement, aTreeMatchContext, false, true)) {
  1.1959 +          return false;
  1.1960 +        }
  1.1961 +        break;
  1.1962 +
  1.1963 +      case nsCSSPseudoClasses::ePseudoClass_lastNode:
  1.1964 +        {
  1.1965 +          nsIContent *lastNode = nullptr;
  1.1966 +          nsIContent *parent = aElement->GetParent();
  1.1967 +          if (parent) {
  1.1968 +            if (aTreeMatchContext.mForStyling)
  1.1969 +              parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
  1.1970 +            
  1.1971 +            uint32_t index = parent->GetChildCount();
  1.1972 +            do {
  1.1973 +              lastNode = parent->GetChildAt(--index);
  1.1974 +              // stop at first non-comment and non-whitespace node
  1.1975 +            } while (lastNode &&
  1.1976 +                     !IsSignificantChild(lastNode, true, false));
  1.1977 +          }
  1.1978 +          if (aElement != lastNode) {
  1.1979 +            return false;
  1.1980 +          }
  1.1981 +        }
  1.1982 +        break;
  1.1983 +
  1.1984 +      case nsCSSPseudoClasses::ePseudoClass_onlyChild:
  1.1985 +        if (!edgeChildMatches(aElement, aTreeMatchContext, true, true)) {
  1.1986 +          return false;
  1.1987 +        }
  1.1988 +        break;
  1.1989 +
  1.1990 +      case nsCSSPseudoClasses::ePseudoClass_firstOfType:
  1.1991 +        if (!edgeOfTypeMatches(aElement, aTreeMatchContext, true, false)) {
  1.1992 +          return false;
  1.1993 +        }
  1.1994 +        break;
  1.1995 +
  1.1996 +      case nsCSSPseudoClasses::ePseudoClass_lastOfType:
  1.1997 +        if (!edgeOfTypeMatches(aElement, aTreeMatchContext, false, true)) {
  1.1998 +          return false;
  1.1999 +        }
  1.2000 +        break;
  1.2001 +
  1.2002 +      case nsCSSPseudoClasses::ePseudoClass_onlyOfType:
  1.2003 +        if (!edgeOfTypeMatches(aElement, aTreeMatchContext, true, true)) {
  1.2004 +          return false;
  1.2005 +        }
  1.2006 +        break;
  1.2007 +
  1.2008 +      case nsCSSPseudoClasses::ePseudoClass_nthChild:
  1.2009 +        if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
  1.2010 +                                    false, false)) {
  1.2011 +          return false;
  1.2012 +        }
  1.2013 +        break;
  1.2014 +
  1.2015 +      case nsCSSPseudoClasses::ePseudoClass_nthLastChild:
  1.2016 +        if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
  1.2017 +                                    false, true)) {
  1.2018 +          return false;
  1.2019 +        }
  1.2020 +      break;
  1.2021 +
  1.2022 +      case nsCSSPseudoClasses::ePseudoClass_nthOfType:
  1.2023 +        if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
  1.2024 +                                    true, false)) {
  1.2025 +          return false;
  1.2026 +        }
  1.2027 +        break;
  1.2028 +
  1.2029 +      case nsCSSPseudoClasses::ePseudoClass_nthLastOfType:
  1.2030 +        if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
  1.2031 +                                    true, true)) {
  1.2032 +          return false;
  1.2033 +        }
  1.2034 +        break;
  1.2035 +
  1.2036 +      case nsCSSPseudoClasses::ePseudoClass_mozIsHTML:
  1.2037 +        if (!aTreeMatchContext.mIsHTMLDocument || !aElement->IsHTML()) {
  1.2038 +          return false;
  1.2039 +        }
  1.2040 +        break;
  1.2041 +
  1.2042 +      case nsCSSPseudoClasses::ePseudoClass_mozSystemMetric:
  1.2043 +        {
  1.2044 +          nsCOMPtr<nsIAtom> metric = do_GetAtom(pseudoClass->u.mString);
  1.2045 +          if (!nsCSSRuleProcessor::HasSystemMetric(metric)) {
  1.2046 +            return false;
  1.2047 +          }
  1.2048 +        }
  1.2049 +        break;
  1.2050 +
  1.2051 +      case nsCSSPseudoClasses::ePseudoClass_mozLocaleDir:
  1.2052 +        {
  1.2053 +          bool docIsRTL =
  1.2054 +            aTreeMatchContext.mDocument->GetDocumentState().
  1.2055 +              HasState(NS_DOCUMENT_STATE_RTL_LOCALE);
  1.2056 +
  1.2057 +          nsDependentString dirString(pseudoClass->u.mString);
  1.2058 +          NS_ASSERTION(dirString.EqualsLiteral("ltr") ||
  1.2059 +                       dirString.EqualsLiteral("rtl"),
  1.2060 +                       "invalid value for -moz-locale-dir");
  1.2061 +
  1.2062 +          if (dirString.EqualsLiteral("rtl") != docIsRTL) {
  1.2063 +            return false;
  1.2064 +          }
  1.2065 +        }
  1.2066 +        break;
  1.2067 +
  1.2068 +      case nsCSSPseudoClasses::ePseudoClass_mozLWTheme:
  1.2069 +        {
  1.2070 +          if (aTreeMatchContext.mDocument->GetDocumentLWTheme() <=
  1.2071 +                nsIDocument::Doc_Theme_None) {
  1.2072 +            return false;
  1.2073 +          }
  1.2074 +        }
  1.2075 +        break;
  1.2076 +
  1.2077 +      case nsCSSPseudoClasses::ePseudoClass_mozLWThemeBrightText:
  1.2078 +        {
  1.2079 +          if (aTreeMatchContext.mDocument->GetDocumentLWTheme() !=
  1.2080 +                nsIDocument::Doc_Theme_Bright) {
  1.2081 +            return false;
  1.2082 +          }
  1.2083 +        }
  1.2084 +        break;
  1.2085 +
  1.2086 +      case nsCSSPseudoClasses::ePseudoClass_mozLWThemeDarkText:
  1.2087 +        {
  1.2088 +          if (aTreeMatchContext.mDocument->GetDocumentLWTheme() !=
  1.2089 +                nsIDocument::Doc_Theme_Dark) {
  1.2090 +            return false;
  1.2091 +          }
  1.2092 +        }
  1.2093 +        break;
  1.2094 +
  1.2095 +      case nsCSSPseudoClasses::ePseudoClass_mozWindowInactive:
  1.2096 +        if (!aTreeMatchContext.mDocument->GetDocumentState().
  1.2097 +               HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
  1.2098 +          return false;
  1.2099 +        }
  1.2100 +        break;
  1.2101 +
  1.2102 +      case nsCSSPseudoClasses::ePseudoClass_mozTableBorderNonzero:
  1.2103 +        {
  1.2104 +          if (!aElement->IsHTML(nsGkAtoms::table)) {
  1.2105 +            return false;
  1.2106 +          }
  1.2107 +          const nsAttrValue *val = aElement->GetParsedAttr(nsGkAtoms::border);
  1.2108 +          if (!val ||
  1.2109 +              (val->Type() == nsAttrValue::eInteger &&
  1.2110 +               val->GetIntegerValue() == 0)) {
  1.2111 +            return false;
  1.2112 +          }
  1.2113 +        }
  1.2114 +        break;
  1.2115 +
  1.2116 +      case nsCSSPseudoClasses::ePseudoClass_dir:
  1.2117 +        {
  1.2118 +          if (aDependence) {
  1.2119 +            EventStates states
  1.2120 +              = sPseudoClassStateDependences[pseudoClass->mType];
  1.2121 +            if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(states)) {
  1.2122 +              *aDependence = true;
  1.2123 +              return false;
  1.2124 +            }
  1.2125 +          }
  1.2126 +
  1.2127 +          // if we only had to consider HTML, directionality would be exclusively
  1.2128 +          // LTR or RTL, and this could be just
  1.2129 +          //
  1.2130 +          //  if (dirString.EqualsLiteral("rtl") !=
  1.2131 +          //    aElement->StyleState().HasState(NS_EVENT_STATE_RTL)
  1.2132 +          //
  1.2133 +          // However, in markup languages where there is no direction attribute
  1.2134 +          // we have to consider the possibility that neither -moz-dir(rtl) nor
  1.2135 +          // -moz-dir(ltr) matches.
  1.2136 +          EventStates state = aElement->StyleState();
  1.2137 +          bool elementIsRTL = state.HasState(NS_EVENT_STATE_RTL);
  1.2138 +          bool elementIsLTR = state.HasState(NS_EVENT_STATE_LTR);
  1.2139 +          nsDependentString dirString(pseudoClass->u.mString);
  1.2140 +
  1.2141 +          if ((dirString.EqualsLiteral("rtl") && !elementIsRTL) ||
  1.2142 +              (dirString.EqualsLiteral("ltr") && !elementIsLTR)) {
  1.2143 +            return false;
  1.2144 +          }
  1.2145 +        }
  1.2146 +        break;
  1.2147 +
  1.2148 +      case nsCSSPseudoClasses::ePseudoClass_scope:
  1.2149 +        if (aTreeMatchContext.mForScopedStyle) {
  1.2150 +          if (aTreeMatchContext.mCurrentStyleScope) {
  1.2151 +            // If mCurrentStyleScope is null, aElement must be the style
  1.2152 +            // scope root.  This is because the PopStyleScopeForSelectorMatching
  1.2153 +            // call in SelectorMatchesTree sets mCurrentStyleScope to null
  1.2154 +            // as soon as we visit the style scope element, and we won't
  1.2155 +            // progress further up the tree after this call to
  1.2156 +            // SelectorMatches.  Thus if mCurrentStyleScope is still set,
  1.2157 +            // we know the selector does not match.
  1.2158 +            return false;
  1.2159 +          }
  1.2160 +        } else if (aTreeMatchContext.HasSpecifiedScope()) {
  1.2161 +          if (!aTreeMatchContext.IsScopeElement(aElement)) {
  1.2162 +            return false;
  1.2163 +          }
  1.2164 +        } else {
  1.2165 +          if (aElement != aElement->OwnerDoc()->GetRootElement()) {
  1.2166 +            return false;
  1.2167 +          }
  1.2168 +        }
  1.2169 +        break;
  1.2170 +
  1.2171 +      default:
  1.2172 +        NS_ABORT_IF_FALSE(false, "How did that happen?");
  1.2173 +      }
  1.2174 +    } else {
  1.2175 +      if (!StateSelectorMatches(aElement, aSelector, aNodeMatchContext,
  1.2176 +                                aTreeMatchContext, aDependence,
  1.2177 +                                statesToCheck)) {
  1.2178 +        return false;
  1.2179 +      }
  1.2180 +    }
  1.2181 +  }
  1.2182 +
  1.2183 +  bool result = true;
  1.2184 +  if (aSelector->mAttrList) {
  1.2185 +    // test for attribute match
  1.2186 +    if (!aElement->HasAttrs()) {
  1.2187 +      // if no attributes on the content, no match
  1.2188 +      return false;
  1.2189 +    } else {
  1.2190 +      result = true;
  1.2191 +      nsAttrSelector* attr = aSelector->mAttrList;
  1.2192 +      nsIAtom* matchAttribute;
  1.2193 +
  1.2194 +      do {
  1.2195 +        bool isHTML =
  1.2196 +          (aTreeMatchContext.mIsHTMLDocument && aElement->IsHTML());
  1.2197 +        matchAttribute = isHTML ? attr->mLowercaseAttr : attr->mCasedAttr;
  1.2198 +        if (attr->mNameSpace == kNameSpaceID_Unknown) {
  1.2199 +          // Attr selector with a wildcard namespace.  We have to examine all
  1.2200 +          // the attributes on our content node....  This sort of selector is
  1.2201 +          // essentially a boolean OR, over all namespaces, of equivalent attr
  1.2202 +          // selectors with those namespaces.  So to evaluate whether it
  1.2203 +          // matches, evaluate for each namespace (the only namespaces that
  1.2204 +          // have a chance at matching, of course, are ones that the element
  1.2205 +          // actually has attributes in), short-circuiting if we ever match.
  1.2206 +          result = false;
  1.2207 +          const nsAttrName* attrName;
  1.2208 +          for (uint32_t i = 0; (attrName = aElement->GetAttrNameAt(i)); ++i) {
  1.2209 +            if (attrName->LocalName() != matchAttribute) {
  1.2210 +              continue;
  1.2211 +            }
  1.2212 +            if (attr->mFunction == NS_ATTR_FUNC_SET) {
  1.2213 +              result = true;
  1.2214 +            } else {
  1.2215 +              nsAutoString value;
  1.2216 +#ifdef DEBUG
  1.2217 +              bool hasAttr =
  1.2218 +#endif
  1.2219 +                aElement->GetAttr(attrName->NamespaceID(),
  1.2220 +                                  attrName->LocalName(), value);
  1.2221 +              NS_ASSERTION(hasAttr, "GetAttrNameAt lied");
  1.2222 +              result = AttrMatchesValue(attr, value, isHTML);
  1.2223 +            }
  1.2224 +
  1.2225 +            // At this point |result| has been set by us
  1.2226 +            // explicitly in this loop.  If it's false, we may still match
  1.2227 +            // -- the content may have another attribute with the same name but
  1.2228 +            // in a different namespace.  But if it's true, we are done (we
  1.2229 +            // can short-circuit the boolean OR described above).
  1.2230 +            if (result) {
  1.2231 +              break;
  1.2232 +            }
  1.2233 +          }
  1.2234 +        }
  1.2235 +        else if (attr->mFunction == NS_ATTR_FUNC_EQUALS) {
  1.2236 +          result =
  1.2237 +            aElement->
  1.2238 +              AttrValueIs(attr->mNameSpace, matchAttribute, attr->mValue,
  1.2239 +                          (!isHTML || attr->mCaseSensitive) ? eCaseMatters
  1.2240 +                                                            : eIgnoreCase);
  1.2241 +        }
  1.2242 +        else if (!aElement->HasAttr(attr->mNameSpace, matchAttribute)) {
  1.2243 +          result = false;
  1.2244 +        }
  1.2245 +        else if (attr->mFunction != NS_ATTR_FUNC_SET) {
  1.2246 +          nsAutoString value;
  1.2247 +#ifdef DEBUG
  1.2248 +          bool hasAttr =
  1.2249 +#endif
  1.2250 +              aElement->GetAttr(attr->mNameSpace, matchAttribute, value);
  1.2251 +          NS_ASSERTION(hasAttr, "HasAttr lied");
  1.2252 +          result = AttrMatchesValue(attr, value, isHTML);
  1.2253 +        }
  1.2254 +        
  1.2255 +        attr = attr->mNext;
  1.2256 +      } while (attr && result);
  1.2257 +    }
  1.2258 +  }
  1.2259 +
  1.2260 +  // apply SelectorMatches to the negated selectors in the chain
  1.2261 +  if (!isNegated) {
  1.2262 +    for (nsCSSSelector *negation = aSelector->mNegations;
  1.2263 +         result && negation; negation = negation->mNegations) {
  1.2264 +      bool dependence = false;
  1.2265 +      result = !SelectorMatches(aElement, negation, aNodeMatchContext,
  1.2266 +                                aTreeMatchContext, &dependence);
  1.2267 +      // If the selector does match due to the dependence on
  1.2268 +      // aNodeMatchContext.mStateMask, then we want to keep result true
  1.2269 +      // so that the final result of SelectorMatches is true.  Doing so
  1.2270 +      // tells StateEnumFunc that there is a dependence on the state.
  1.2271 +      result = result || dependence;
  1.2272 +    }
  1.2273 +  }
  1.2274 +  return result;
  1.2275 +}
  1.2276 +
  1.2277 +#undef STATE_CHECK
  1.2278 +
  1.2279 +// Right now, there are four operators:
  1.2280 +//   ' ', the descendant combinator, is greedy
  1.2281 +//   '~', the indirect adjacent sibling combinator, is greedy
  1.2282 +//   '+' and '>', the direct adjacent sibling and child combinators, are not
  1.2283 +#define NS_IS_GREEDY_OPERATOR(ch) \
  1.2284 +  ((ch) == char16_t(' ') || (ch) == char16_t('~'))
  1.2285 +
  1.2286 +static bool SelectorMatchesTree(Element* aPrevElement,
  1.2287 +                                  nsCSSSelector* aSelector,
  1.2288 +                                  TreeMatchContext& aTreeMatchContext,
  1.2289 +                                  bool aLookForRelevantLink)
  1.2290 +{
  1.2291 +  MOZ_ASSERT(!aSelector || !aSelector->IsPseudoElement());
  1.2292 +  nsCSSSelector* selector = aSelector;
  1.2293 +  Element* prevElement = aPrevElement;
  1.2294 +  while (selector) { // check compound selectors
  1.2295 +    NS_ASSERTION(!selector->mNext ||
  1.2296 +                 selector->mNext->mOperator != char16_t(0),
  1.2297 +                 "compound selector without combinator");
  1.2298 +
  1.2299 +    // If after the previous selector match we are now outside the
  1.2300 +    // current style scope, we don't need to match any further.
  1.2301 +    if (aTreeMatchContext.mForScopedStyle &&
  1.2302 +        !aTreeMatchContext.IsWithinStyleScopeForSelectorMatching()) {
  1.2303 +      return false;
  1.2304 +    }
  1.2305 +
  1.2306 +    // for adjacent sibling combinators, the content to test against the
  1.2307 +    // selector is the previous sibling *element*
  1.2308 +    Element* element = nullptr;
  1.2309 +    if (char16_t('+') == selector->mOperator ||
  1.2310 +        char16_t('~') == selector->mOperator) {
  1.2311 +      // The relevant link must be an ancestor of the node being matched.
  1.2312 +      aLookForRelevantLink = false;
  1.2313 +      nsIContent* parent = prevElement->GetParent();
  1.2314 +      if (parent) {
  1.2315 +        if (aTreeMatchContext.mForStyling)
  1.2316 +          parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
  1.2317 +
  1.2318 +        element = prevElement->GetPreviousElementSibling();
  1.2319 +      }
  1.2320 +    }
  1.2321 +    // for descendant combinators and child combinators, the element
  1.2322 +    // to test against is the parent
  1.2323 +    else {
  1.2324 +      nsIContent *content = prevElement->GetParent();
  1.2325 +      // GetParent could return a document fragment; we only want
  1.2326 +      // element parents.
  1.2327 +      if (content && content->IsElement()) {
  1.2328 +        element = content->AsElement();
  1.2329 +        if (aTreeMatchContext.mForScopedStyle) {
  1.2330 +          // We are moving up to the parent element; tell the
  1.2331 +          // TreeMatchContext, so that in case this element is the
  1.2332 +          // style scope element, selector matching stops before we
  1.2333 +          // traverse further up the tree.
  1.2334 +          aTreeMatchContext.PopStyleScopeForSelectorMatching(element);
  1.2335 +        }
  1.2336 +
  1.2337 +        // Compatibility hack: First try matching this selector as though the
  1.2338 +        // <xbl:children> element wasn't in the tree to allow old selectors
  1.2339 +        // were written before <xbl:children> participated in CSS selector
  1.2340 +        // matching to work.
  1.2341 +        if (selector->mOperator == '>' && element->IsActiveChildrenElement()) {
  1.2342 +          Element* styleScope = aTreeMatchContext.mCurrentStyleScope;
  1.2343 +          if (SelectorMatchesTree(element, selector, aTreeMatchContext,
  1.2344 +                                  aLookForRelevantLink)) {
  1.2345 +            // It matched, don't try matching on the <xbl:children> element at
  1.2346 +            // all.
  1.2347 +            return true;
  1.2348 +          }
  1.2349 +          // We want to reset mCurrentStyleScope on aTreeMatchContext
  1.2350 +          // back to its state before the SelectorMatchesTree call, in
  1.2351 +          // case that call happens to traverse past the style scope element
  1.2352 +          // and sets it to null.
  1.2353 +          aTreeMatchContext.mCurrentStyleScope = styleScope;
  1.2354 +        }
  1.2355 +      }
  1.2356 +    }
  1.2357 +    if (!element) {
  1.2358 +      return false;
  1.2359 +    }
  1.2360 +    NodeMatchContext nodeContext(EventStates(),
  1.2361 +                                 aLookForRelevantLink &&
  1.2362 +                                   nsCSSRuleProcessor::IsLink(element));
  1.2363 +    if (nodeContext.mIsRelevantLink) {
  1.2364 +      // If we find an ancestor of the matched node that is a link
  1.2365 +      // during the matching process, then it's the relevant link (see
  1.2366 +      // constructor call above).
  1.2367 +      // Since we are still matching against selectors that contain
  1.2368 +      // :visited (they'll just fail), we will always find such a node
  1.2369 +      // during the selector matching process if there is a relevant
  1.2370 +      // link that can influence selector matching.
  1.2371 +      aLookForRelevantLink = false;
  1.2372 +      aTreeMatchContext.SetHaveRelevantLink();
  1.2373 +    }
  1.2374 +    if (SelectorMatches(element, selector, nodeContext, aTreeMatchContext)) {
  1.2375 +      // to avoid greedy matching, we need to recur if this is a
  1.2376 +      // descendant or general sibling combinator and the next
  1.2377 +      // combinator is different, but we can make an exception for
  1.2378 +      // sibling, then parent, since a sibling's parent is always the
  1.2379 +      // same.
  1.2380 +      if (NS_IS_GREEDY_OPERATOR(selector->mOperator) &&
  1.2381 +          selector->mNext &&
  1.2382 +          selector->mNext->mOperator != selector->mOperator &&
  1.2383 +          !(selector->mOperator == '~' &&
  1.2384 +            NS_IS_ANCESTOR_OPERATOR(selector->mNext->mOperator))) {
  1.2385 +
  1.2386 +        // pretend the selector didn't match, and step through content
  1.2387 +        // while testing the same selector
  1.2388 +
  1.2389 +        // This approach is slightly strange in that when it recurs
  1.2390 +        // it tests from the top of the content tree, down.  This
  1.2391 +        // doesn't matter much for performance since most selectors
  1.2392 +        // don't match.  (If most did, it might be faster...)
  1.2393 +        Element* styleScope = aTreeMatchContext.mCurrentStyleScope;
  1.2394 +        if (SelectorMatchesTree(element, selector, aTreeMatchContext,
  1.2395 +                                aLookForRelevantLink)) {
  1.2396 +          return true;
  1.2397 +        }
  1.2398 +        // We want to reset mCurrentStyleScope on aTreeMatchContext
  1.2399 +        // back to its state before the SelectorMatchesTree call, in
  1.2400 +        // case that call happens to traverse past the style scope element
  1.2401 +        // and sets it to null.
  1.2402 +        aTreeMatchContext.mCurrentStyleScope = styleScope;
  1.2403 +      }
  1.2404 +      selector = selector->mNext;
  1.2405 +    }
  1.2406 +    else {
  1.2407 +      // for adjacent sibling and child combinators, if we didn't find
  1.2408 +      // a match, we're done
  1.2409 +      if (!NS_IS_GREEDY_OPERATOR(selector->mOperator)) {
  1.2410 +        return false;  // parent was required to match
  1.2411 +      }
  1.2412 +    }
  1.2413 +    prevElement = element;
  1.2414 +  }
  1.2415 +  return true; // all the selectors matched.
  1.2416 +}
  1.2417 +
  1.2418 +static inline
  1.2419 +void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
  1.2420 +                     ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
  1.2421 +                     AncestorFilter *ancestorFilter)
  1.2422 +{
  1.2423 +  if (nodeContext.mIsRelevantLink) {
  1.2424 +    data->mTreeMatchContext.SetHaveRelevantLink();
  1.2425 +  }
  1.2426 +  if (ancestorFilter &&
  1.2427 +      !ancestorFilter->MightHaveMatchingAncestor<RuleValue::eMaxAncestorHashes>(
  1.2428 +          value.mAncestorSelectorHashes)) {
  1.2429 +    // We won't match; nothing else to do here
  1.2430 +    return;
  1.2431 +  }
  1.2432 +  if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
  1.2433 +                                                                data->mScope)) {
  1.2434 +    // The selector is for a rule in a scoped style sheet, and the subject
  1.2435 +    // of the selector matching is not in its scope.
  1.2436 +    return;
  1.2437 +  }
  1.2438 +  nsCSSSelector* selector = aSelector;
  1.2439 +  if (selector->IsPseudoElement()) {
  1.2440 +    PseudoElementRuleProcessorData* pdata =
  1.2441 +      static_cast<PseudoElementRuleProcessorData*>(data);
  1.2442 +    if (!pdata->mPseudoElement && selector->mPseudoClassList) {
  1.2443 +      // We can get here when calling getComputedStyle(aElt, aPseudo) if:
  1.2444 +      //
  1.2445 +      //   * aPseudo is a pseudo-element that supports a user action
  1.2446 +      //     pseudo-class, like "::-moz-placeholder";
  1.2447 +      //   * there is a style rule that uses a pseudo-class on this
  1.2448 +      //     pseudo-element in the document, like ::-moz-placeholder:hover; and
  1.2449 +      //   * aElt does not have such a pseudo-element.
  1.2450 +      //
  1.2451 +      // We know that the selector can't match, since there is no element for
  1.2452 +      // the user action pseudo-class to match against.
  1.2453 +      return;
  1.2454 +    }
  1.2455 +    if (!StateSelectorMatches(pdata->mPseudoElement, aSelector, nodeContext,
  1.2456 +                              data->mTreeMatchContext)) {
  1.2457 +      return;
  1.2458 +    }
  1.2459 +    selector = selector->mNext;
  1.2460 +  }
  1.2461 +  if (SelectorMatches(data->mElement, selector, nodeContext,
  1.2462 +                      data->mTreeMatchContext)) {
  1.2463 +    nsCSSSelector *next = selector->mNext;
  1.2464 +    if (!next || SelectorMatchesTree(data->mElement, next,
  1.2465 +                                     data->mTreeMatchContext,
  1.2466 +                                     !nodeContext.mIsRelevantLink)) {
  1.2467 +      css::StyleRule *rule = value.mRule;
  1.2468 +      rule->RuleMatched();
  1.2469 +      data->mRuleWalker->Forward(rule);
  1.2470 +      // nsStyleSet will deal with the !important rule
  1.2471 +    }
  1.2472 +  }
  1.2473 +}
  1.2474 +
  1.2475 +/* virtual */ void
  1.2476 +nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData *aData)
  1.2477 +{
  1.2478 +  RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
  1.2479 +
  1.2480 +  if (cascade) {
  1.2481 +    NodeMatchContext nodeContext(EventStates(),
  1.2482 +                                 nsCSSRuleProcessor::IsLink(aData->mElement));
  1.2483 +    cascade->mRuleHash.EnumerateAllRules(aData->mElement, aData, nodeContext);
  1.2484 +  }
  1.2485 +}
  1.2486 +
  1.2487 +/* virtual */ void
  1.2488 +nsCSSRuleProcessor::RulesMatching(PseudoElementRuleProcessorData* aData)
  1.2489 +{
  1.2490 +  RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
  1.2491 +
  1.2492 +  if (cascade) {
  1.2493 +    RuleHash* ruleHash = cascade->mPseudoElementRuleHashes[aData->mPseudoType];
  1.2494 +    if (ruleHash) {
  1.2495 +      NodeMatchContext nodeContext(EventStates(),
  1.2496 +                                   nsCSSRuleProcessor::IsLink(aData->mElement));
  1.2497 +      ruleHash->EnumerateAllRules(aData->mElement, aData, nodeContext);
  1.2498 +    }
  1.2499 +  }
  1.2500 +}
  1.2501 +
  1.2502 +/* virtual */ void
  1.2503 +nsCSSRuleProcessor::RulesMatching(AnonBoxRuleProcessorData* aData)
  1.2504 +{
  1.2505 +  RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
  1.2506 +
  1.2507 +  if (cascade && cascade->mAnonBoxRules.entryCount) {
  1.2508 +    RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>
  1.2509 +      (PL_DHashTableOperate(&cascade->mAnonBoxRules, aData->mPseudoTag,
  1.2510 +                            PL_DHASH_LOOKUP));
  1.2511 +    if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
  1.2512 +      nsTArray<RuleValue>& rules = entry->mRules;
  1.2513 +      for (RuleValue *value = rules.Elements(), *end = value + rules.Length();
  1.2514 +           value != end; ++value) {
  1.2515 +        value->mRule->RuleMatched();
  1.2516 +        aData->mRuleWalker->Forward(value->mRule);
  1.2517 +      }
  1.2518 +    }
  1.2519 +  }
  1.2520 +}
  1.2521 +
  1.2522 +#ifdef MOZ_XUL
  1.2523 +/* virtual */ void
  1.2524 +nsCSSRuleProcessor::RulesMatching(XULTreeRuleProcessorData* aData)
  1.2525 +{
  1.2526 +  RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
  1.2527 +
  1.2528 +  if (cascade && cascade->mXULTreeRules.entryCount) {
  1.2529 +    RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>
  1.2530 +      (PL_DHashTableOperate(&cascade->mXULTreeRules, aData->mPseudoTag,
  1.2531 +                            PL_DHASH_LOOKUP));
  1.2532 +    if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
  1.2533 +      NodeMatchContext nodeContext(EventStates(),
  1.2534 +                                   nsCSSRuleProcessor::IsLink(aData->mElement));
  1.2535 +      nsTArray<RuleValue>& rules = entry->mRules;
  1.2536 +      for (RuleValue *value = rules.Elements(), *end = value + rules.Length();
  1.2537 +           value != end; ++value) {
  1.2538 +        if (aData->mComparator->PseudoMatches(value->mSelector)) {
  1.2539 +          ContentEnumFunc(*value, value->mSelector->mNext, aData, nodeContext,
  1.2540 +                          nullptr);
  1.2541 +        }
  1.2542 +      }
  1.2543 +    }
  1.2544 +  }
  1.2545 +}
  1.2546 +#endif
  1.2547 +
  1.2548 +static inline nsRestyleHint RestyleHintForOp(char16_t oper)
  1.2549 +{
  1.2550 +  if (oper == char16_t('+') || oper == char16_t('~')) {
  1.2551 +    return eRestyle_LaterSiblings;
  1.2552 +  }
  1.2553 +
  1.2554 +  if (oper != char16_t(0)) {
  1.2555 +    return eRestyle_Subtree;
  1.2556 +  }
  1.2557 +
  1.2558 +  return eRestyle_Self;
  1.2559 +}
  1.2560 +
  1.2561 +nsRestyleHint
  1.2562 +nsCSSRuleProcessor::HasStateDependentStyle(ElementDependentRuleProcessorData* aData,
  1.2563 +                                           Element* aStatefulElement,
  1.2564 +                                           nsCSSPseudoElements::Type aPseudoType,
  1.2565 +                                           EventStates aStateMask)
  1.2566 +{
  1.2567 +  MOZ_ASSERT(!aData->mTreeMatchContext.mForScopedStyle,
  1.2568 +             "mCurrentStyleScope will need to be saved and restored after the "
  1.2569 +             "SelectorMatchesTree call");
  1.2570 +
  1.2571 +  bool isPseudoElement =
  1.2572 +    aPseudoType != nsCSSPseudoElements::ePseudo_NotPseudoElement;
  1.2573 +
  1.2574 +  RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
  1.2575 +
  1.2576 +  // Look up the content node in the state rule list, which points to
  1.2577 +  // any (CSS2 definition) simple selector (whether or not it is the
  1.2578 +  // subject) that has a state pseudo-class on it.  This means that this
  1.2579 +  // code will be matching selectors that aren't real selectors in any
  1.2580 +  // stylesheet (e.g., if there is a selector "body > p:hover > a", then
  1.2581 +  // "body > p:hover" will be in |cascade->mStateSelectors|).  Note that
  1.2582 +  // |ComputeSelectorStateDependence| below determines which selectors are in
  1.2583 +  // |cascade->mStateSelectors|.
  1.2584 +  nsRestyleHint hint = nsRestyleHint(0);
  1.2585 +  if (cascade) {
  1.2586 +    StateSelector *iter = cascade->mStateSelectors.Elements(),
  1.2587 +                  *end = iter + cascade->mStateSelectors.Length();
  1.2588 +    NodeMatchContext nodeContext(aStateMask, false);
  1.2589 +    for(; iter != end; ++iter) {
  1.2590 +      nsCSSSelector* selector = iter->mSelector;
  1.2591 +      EventStates states = iter->mStates;
  1.2592 +
  1.2593 +      if (selector->IsPseudoElement() != isPseudoElement) {
  1.2594 +        continue;
  1.2595 +      }
  1.2596 +
  1.2597 +      nsCSSSelector* selectorForPseudo;
  1.2598 +      if (isPseudoElement) {
  1.2599 +        if (selector->PseudoType() != aPseudoType) {
  1.2600 +          continue;
  1.2601 +        }
  1.2602 +        selectorForPseudo = selector;
  1.2603 +        selector = selector->mNext;
  1.2604 +      }
  1.2605 +
  1.2606 +      nsRestyleHint possibleChange = RestyleHintForOp(selector->mOperator);
  1.2607 +
  1.2608 +      // If hint already includes all the bits of possibleChange,
  1.2609 +      // don't bother calling SelectorMatches, since even if it returns false
  1.2610 +      // hint won't change.
  1.2611 +      // Also don't bother calling SelectorMatches if none of the
  1.2612 +      // states passed in are relevant here.
  1.2613 +      if ((possibleChange & ~hint) &&
  1.2614 +          states.HasAtLeastOneOfStates(aStateMask) &&
  1.2615 +          // We can optimize away testing selectors that only involve :hover, a
  1.2616 +          // namespace, and a tag name against nodes that don't have the
  1.2617 +          // NodeHasRelevantHoverRules flag: such a selector didn't match
  1.2618 +          // the tag name or namespace the first time around (since the :hover
  1.2619 +          // didn't set the NodeHasRelevantHoverRules flag), so it won't
  1.2620 +          // match it now.  Check for our selector only having :hover states, or
  1.2621 +          // the element having the hover rules flag, or the selector having
  1.2622 +          // some sort of non-namespace, non-tagname data in it.
  1.2623 +          (states != NS_EVENT_STATE_HOVER ||
  1.2624 +           aStatefulElement->HasRelevantHoverRules() ||
  1.2625 +           selector->mIDList || selector->mClassList ||
  1.2626 +           // We generally expect an mPseudoClassList, since we have a :hover.
  1.2627 +           // The question is whether we have anything else in there.
  1.2628 +           (selector->mPseudoClassList &&
  1.2629 +            (selector->mPseudoClassList->mNext ||
  1.2630 +             selector->mPseudoClassList->mType !=
  1.2631 +               nsCSSPseudoClasses::ePseudoClass_hover)) ||
  1.2632 +           selector->mAttrList || selector->mNegations) &&
  1.2633 +          (!isPseudoElement ||
  1.2634 +           StateSelectorMatches(aStatefulElement, selectorForPseudo,
  1.2635 +                                nodeContext, aData->mTreeMatchContext,
  1.2636 +                                nullptr, aStateMask)) &&
  1.2637 +          SelectorMatches(aData->mElement, selector, nodeContext,
  1.2638 +                          aData->mTreeMatchContext) &&
  1.2639 +          SelectorMatchesTree(aData->mElement, selector->mNext,
  1.2640 +                              aData->mTreeMatchContext,
  1.2641 +                              false))
  1.2642 +      {
  1.2643 +        hint = nsRestyleHint(hint | possibleChange);
  1.2644 +      }
  1.2645 +    }
  1.2646 +  }
  1.2647 +  return hint;
  1.2648 +}
  1.2649 +
  1.2650 +nsRestyleHint
  1.2651 +nsCSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData)
  1.2652 +{
  1.2653 +  return HasStateDependentStyle(aData,
  1.2654 +                                aData->mElement,
  1.2655 +                                nsCSSPseudoElements::ePseudo_NotPseudoElement,
  1.2656 +                                aData->mStateMask);
  1.2657 +}
  1.2658 +
  1.2659 +nsRestyleHint
  1.2660 +nsCSSRuleProcessor::HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData)
  1.2661 +{
  1.2662 +  return HasStateDependentStyle(aData,
  1.2663 +                                aData->mPseudoElement,
  1.2664 +                                aData->mPseudoType,
  1.2665 +                                aData->mStateMask);
  1.2666 +}
  1.2667 +
  1.2668 +bool
  1.2669 +nsCSSRuleProcessor::HasDocumentStateDependentStyle(StateRuleProcessorData* aData)
  1.2670 +{
  1.2671 +  RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
  1.2672 +
  1.2673 +  return cascade && cascade->mSelectorDocumentStates.HasAtLeastOneOfStates(aData->mStateMask);
  1.2674 +}
  1.2675 +
  1.2676 +struct AttributeEnumData {
  1.2677 +  AttributeEnumData(AttributeRuleProcessorData *aData)
  1.2678 +    : data(aData), change(nsRestyleHint(0)) {}
  1.2679 +
  1.2680 +  AttributeRuleProcessorData *data;
  1.2681 +  nsRestyleHint change;
  1.2682 +};
  1.2683 +
  1.2684 +
  1.2685 +static void
  1.2686 +AttributeEnumFunc(nsCSSSelector* aSelector, AttributeEnumData* aData)
  1.2687 +{
  1.2688 +  AttributeRuleProcessorData *data = aData->data;
  1.2689 +
  1.2690 +  if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
  1.2691 +                                                                data->mScope)) {
  1.2692 +    // The selector is for a rule in a scoped style sheet, and the subject
  1.2693 +    // of the selector matching is not in its scope.
  1.2694 +    return;
  1.2695 +  }
  1.2696 +
  1.2697 +  nsRestyleHint possibleChange = RestyleHintForOp(aSelector->mOperator);
  1.2698 +
  1.2699 +  // If enumData->change already includes all the bits of possibleChange, don't
  1.2700 +  // bother calling SelectorMatches, since even if it returns false
  1.2701 +  // enumData->change won't change.
  1.2702 +  NodeMatchContext nodeContext(EventStates(), false);
  1.2703 +  if ((possibleChange & ~(aData->change)) &&
  1.2704 +      SelectorMatches(data->mElement, aSelector, nodeContext,
  1.2705 +                      data->mTreeMatchContext) &&
  1.2706 +      SelectorMatchesTree(data->mElement, aSelector->mNext,
  1.2707 +                          data->mTreeMatchContext, false)) {
  1.2708 +    aData->change = nsRestyleHint(aData->change | possibleChange);
  1.2709 +  }
  1.2710 +}
  1.2711 +
  1.2712 +static MOZ_ALWAYS_INLINE void
  1.2713 +EnumerateSelectors(nsTArray<nsCSSSelector*>& aSelectors, AttributeEnumData* aData)
  1.2714 +{
  1.2715 +  nsCSSSelector **iter = aSelectors.Elements(),
  1.2716 +                **end = iter + aSelectors.Length();
  1.2717 +  for (; iter != end; ++iter) {
  1.2718 +    AttributeEnumFunc(*iter, aData);
  1.2719 +  }
  1.2720 +}
  1.2721 +
  1.2722 +nsRestyleHint
  1.2723 +nsCSSRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData)
  1.2724 +{
  1.2725 +  //  We could try making use of aData->mModType, but :not rules make it a bit
  1.2726 +  //  of a pain to do so...  So just ignore it for now.
  1.2727 +
  1.2728 +  AttributeEnumData data(aData);
  1.2729 +
  1.2730 +  // Don't do our special handling of certain attributes if the attr
  1.2731 +  // hasn't changed yet.
  1.2732 +  if (aData->mAttrHasChanged) {
  1.2733 +    // check for the lwtheme and lwthemetextcolor attribute on root XUL elements
  1.2734 +    if ((aData->mAttribute == nsGkAtoms::lwtheme ||
  1.2735 +         aData->mAttribute == nsGkAtoms::lwthemetextcolor) &&
  1.2736 +        aData->mElement->GetNameSpaceID() == kNameSpaceID_XUL &&
  1.2737 +        aData->mElement == aData->mElement->OwnerDoc()->GetRootElement())
  1.2738 +      {
  1.2739 +        data.change = nsRestyleHint(data.change | eRestyle_Subtree);
  1.2740 +      }
  1.2741 +
  1.2742 +    // We don't know the namespace of the attribute, and xml:lang applies to
  1.2743 +    // all elements.  If the lang attribute changes, we need to restyle our
  1.2744 +    // whole subtree, since the :lang selector on our descendants can examine
  1.2745 +    // our lang attribute.
  1.2746 +    if (aData->mAttribute == nsGkAtoms::lang) {
  1.2747 +      data.change = nsRestyleHint(data.change | eRestyle_Subtree);
  1.2748 +    }
  1.2749 +  }
  1.2750 +
  1.2751 +  RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
  1.2752 +
  1.2753 +  // Since we get both before and after notifications for attributes, we
  1.2754 +  // don't have to ignore aData->mAttribute while matching.  Just check
  1.2755 +  // whether we have selectors relevant to aData->mAttribute that we
  1.2756 +  // match.  If this is the before change notification, that will catch
  1.2757 +  // rules we might stop matching; if the after change notification, the
  1.2758 +  // ones we might have started matching.
  1.2759 +  if (cascade) {
  1.2760 +    if (aData->mAttribute == aData->mElement->GetIDAttributeName()) {
  1.2761 +      nsIAtom* id = aData->mElement->GetID();
  1.2762 +      if (id) {
  1.2763 +        AtomSelectorEntry *entry =
  1.2764 +          static_cast<AtomSelectorEntry*>
  1.2765 +                     (PL_DHashTableOperate(&cascade->mIdSelectors,
  1.2766 +                                           id, PL_DHASH_LOOKUP));
  1.2767 +        if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
  1.2768 +          EnumerateSelectors(entry->mSelectors, &data);
  1.2769 +        }
  1.2770 +      }
  1.2771 +
  1.2772 +      EnumerateSelectors(cascade->mPossiblyNegatedIDSelectors, &data);
  1.2773 +    }
  1.2774 +    
  1.2775 +    if (aData->mAttribute == aData->mElement->GetClassAttributeName()) {
  1.2776 +      const nsAttrValue* elementClasses = aData->mElement->GetClasses();
  1.2777 +      if (elementClasses) {
  1.2778 +        int32_t atomCount = elementClasses->GetAtomCount();
  1.2779 +        for (int32_t i = 0; i < atomCount; ++i) {
  1.2780 +          nsIAtom* curClass = elementClasses->AtomAt(i);
  1.2781 +          AtomSelectorEntry *entry =
  1.2782 +            static_cast<AtomSelectorEntry*>
  1.2783 +                       (PL_DHashTableOperate(&cascade->mClassSelectors,
  1.2784 +                                             curClass, PL_DHASH_LOOKUP));
  1.2785 +          if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
  1.2786 +            EnumerateSelectors(entry->mSelectors, &data);
  1.2787 +          }
  1.2788 +        }
  1.2789 +      }
  1.2790 +
  1.2791 +      EnumerateSelectors(cascade->mPossiblyNegatedClassSelectors, &data);
  1.2792 +    }
  1.2793 +
  1.2794 +    AtomSelectorEntry *entry =
  1.2795 +      static_cast<AtomSelectorEntry*>
  1.2796 +                 (PL_DHashTableOperate(&cascade->mAttributeSelectors,
  1.2797 +                                       aData->mAttribute, PL_DHASH_LOOKUP));
  1.2798 +    if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
  1.2799 +      EnumerateSelectors(entry->mSelectors, &data);
  1.2800 +    }
  1.2801 +  }
  1.2802 +
  1.2803 +  return data.change;
  1.2804 +}
  1.2805 +
  1.2806 +/* virtual */ bool
  1.2807 +nsCSSRuleProcessor::MediumFeaturesChanged(nsPresContext* aPresContext)
  1.2808 +{
  1.2809 +  RuleCascadeData *old = mRuleCascades;
  1.2810 +  // We don't want to do anything if there aren't any sets of rules
  1.2811 +  // cached yet (or somebody cleared them and is thus responsible for
  1.2812 +  // rebuilding things), since we should not build the rule cascade too
  1.2813 +  // early (e.g., before we know whether the quirk style sheet should be
  1.2814 +  // enabled).  And if there's nothing cached, it doesn't matter if
  1.2815 +  // anything changed.  See bug 448281.
  1.2816 +  if (old) {
  1.2817 +    RefreshRuleCascade(aPresContext);
  1.2818 +  }
  1.2819 +  return (old != mRuleCascades);
  1.2820 +}
  1.2821 +
  1.2822 +/* virtual */ size_t
  1.2823 +nsCSSRuleProcessor::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
  1.2824 +{
  1.2825 +  size_t n = 0;
  1.2826 +  n += mSheets.SizeOfExcludingThis(aMallocSizeOf);
  1.2827 +  for (RuleCascadeData* cascade = mRuleCascades; cascade;
  1.2828 +       cascade = cascade->mNext) {
  1.2829 +    n += cascade->SizeOfIncludingThis(aMallocSizeOf);
  1.2830 +  }
  1.2831 +
  1.2832 +  return n;
  1.2833 +}
  1.2834 +
  1.2835 +/* virtual */ size_t
  1.2836 +nsCSSRuleProcessor::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
  1.2837 +{
  1.2838 +  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
  1.2839 +}
  1.2840 +
  1.2841 +// Append all the currently-active font face rules to aArray.  Return
  1.2842 +// true for success and false for failure.
  1.2843 +bool
  1.2844 +nsCSSRuleProcessor::AppendFontFaceRules(
  1.2845 +                              nsPresContext *aPresContext,
  1.2846 +                              nsTArray<nsFontFaceRuleContainer>& aArray)
  1.2847 +{
  1.2848 +  RuleCascadeData* cascade = GetRuleCascade(aPresContext);
  1.2849 +
  1.2850 +  if (cascade) {
  1.2851 +    if (!aArray.AppendElements(cascade->mFontFaceRules))
  1.2852 +      return false;
  1.2853 +  }
  1.2854 +  
  1.2855 +  return true;
  1.2856 +}
  1.2857 +
  1.2858 +nsCSSKeyframesRule*
  1.2859 +nsCSSRuleProcessor::KeyframesRuleForName(nsPresContext* aPresContext,
  1.2860 +                                         const nsString& aName)
  1.2861 +{
  1.2862 +  RuleCascadeData* cascade = GetRuleCascade(aPresContext);
  1.2863 +
  1.2864 +  if (cascade) {
  1.2865 +    return cascade->mKeyframesRuleTable.Get(aName);
  1.2866 +  }
  1.2867 +
  1.2868 +  return nullptr;
  1.2869 +}
  1.2870 +
  1.2871 +// Append all the currently-active page rules to aArray.  Return
  1.2872 +// true for success and false for failure.
  1.2873 +bool
  1.2874 +nsCSSRuleProcessor::AppendPageRules(
  1.2875 +                              nsPresContext* aPresContext,
  1.2876 +                              nsTArray<nsCSSPageRule*>& aArray)
  1.2877 +{
  1.2878 +  RuleCascadeData* cascade = GetRuleCascade(aPresContext);
  1.2879 +
  1.2880 +  if (cascade) {
  1.2881 +    if (!aArray.AppendElements(cascade->mPageRules)) {
  1.2882 +      return false;
  1.2883 +    }
  1.2884 +  }
  1.2885 +  
  1.2886 +  return true;
  1.2887 +}
  1.2888 +
  1.2889 +bool
  1.2890 +nsCSSRuleProcessor::AppendFontFeatureValuesRules(
  1.2891 +                              nsPresContext *aPresContext,
  1.2892 +                              nsTArray<nsCSSFontFeatureValuesRule*>& aArray)
  1.2893 +{
  1.2894 +  RuleCascadeData* cascade = GetRuleCascade(aPresContext);
  1.2895 +
  1.2896 +  if (cascade) {
  1.2897 +    if (!aArray.AppendElements(cascade->mFontFeatureValuesRules))
  1.2898 +      return false;
  1.2899 +  }
  1.2900 +
  1.2901 +  return true;
  1.2902 +}
  1.2903 +
  1.2904 +nsresult
  1.2905 +nsCSSRuleProcessor::ClearRuleCascades()
  1.2906 +{
  1.2907 +  // We rely on our caller (perhaps indirectly) to do something that
  1.2908 +  // will rebuild style data and the user font set (either
  1.2909 +  // nsIPresShell::ReconstructStyleData or
  1.2910 +  // nsPresContext::RebuildAllStyleData).
  1.2911 +  RuleCascadeData *data = mRuleCascades;
  1.2912 +  mRuleCascades = nullptr;
  1.2913 +  while (data) {
  1.2914 +    RuleCascadeData *next = data->mNext;
  1.2915 +    delete data;
  1.2916 +    data = next;
  1.2917 +  }
  1.2918 +  return NS_OK;
  1.2919 +}
  1.2920 +
  1.2921 +
  1.2922 +// This function should return the set of states that this selector
  1.2923 +// depends on; this is used to implement HasStateDependentStyle.  It
  1.2924 +// does NOT recur down into things like :not and :-moz-any.
  1.2925 +inline
  1.2926 +EventStates ComputeSelectorStateDependence(nsCSSSelector& aSelector)
  1.2927 +{
  1.2928 +  EventStates states;
  1.2929 +  for (nsPseudoClassList* pseudoClass = aSelector.mPseudoClassList;
  1.2930 +       pseudoClass; pseudoClass = pseudoClass->mNext) {
  1.2931 +    // Tree pseudo-elements overload mPseudoClassList for things that
  1.2932 +    // aren't pseudo-classes.
  1.2933 +    if (pseudoClass->mType >= nsCSSPseudoClasses::ePseudoClass_Count) {
  1.2934 +      continue;
  1.2935 +    }
  1.2936 +    states |= sPseudoClassStateDependences[pseudoClass->mType];
  1.2937 +  }
  1.2938 +  return states;
  1.2939 +}
  1.2940 +
  1.2941 +static bool
  1.2942 +AddSelector(RuleCascadeData* aCascade,
  1.2943 +            // The part between combinators at the top level of the selector
  1.2944 +            nsCSSSelector* aSelectorInTopLevel,
  1.2945 +            // The part we should look through (might be in :not or :-moz-any())
  1.2946 +            nsCSSSelector* aSelectorPart)
  1.2947 +{
  1.2948 +  // It's worth noting that this loop over negations isn't quite
  1.2949 +  // optimal for two reasons.  One, we could add something to one of
  1.2950 +  // these lists twice, which means we'll check it twice, but I don't
  1.2951 +  // think that's worth worrying about.   (We do the same for multiple
  1.2952 +  // attribute selectors on the same attribute.)  Two, we don't really
  1.2953 +  // need to check negations past the first in the current
  1.2954 +  // implementation (and they're rare as well), but that might change
  1.2955 +  // in the future if :not() is extended.
  1.2956 +  for (nsCSSSelector* negation = aSelectorPart; negation;
  1.2957 +       negation = negation->mNegations) {
  1.2958 +    // Track both document states and attribute dependence in pseudo-classes.
  1.2959 +    for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList;
  1.2960 +         pseudoClass; pseudoClass = pseudoClass->mNext) {
  1.2961 +      switch (pseudoClass->mType) {
  1.2962 +        case nsCSSPseudoClasses::ePseudoClass_mozLocaleDir: {
  1.2963 +          aCascade->mSelectorDocumentStates |= NS_DOCUMENT_STATE_RTL_LOCALE;
  1.2964 +          break;
  1.2965 +        }
  1.2966 +        case nsCSSPseudoClasses::ePseudoClass_mozWindowInactive: {
  1.2967 +          aCascade->mSelectorDocumentStates |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
  1.2968 +          break;
  1.2969 +        }
  1.2970 +        case nsCSSPseudoClasses::ePseudoClass_mozTableBorderNonzero: {
  1.2971 +          nsTArray<nsCSSSelector*> *array =
  1.2972 +            aCascade->AttributeListFor(nsGkAtoms::border);
  1.2973 +          if (!array) {
  1.2974 +            return false;
  1.2975 +          }
  1.2976 +          array->AppendElement(aSelectorInTopLevel);
  1.2977 +          break;
  1.2978 +        }
  1.2979 +        default: {
  1.2980 +          break;
  1.2981 +        }
  1.2982 +      }
  1.2983 +    }
  1.2984 +
  1.2985 +    // Build mStateSelectors.
  1.2986 +    EventStates dependentStates = ComputeSelectorStateDependence(*negation);
  1.2987 +    if (!dependentStates.IsEmpty()) {
  1.2988 +      aCascade->mStateSelectors.AppendElement(
  1.2989 +        nsCSSRuleProcessor::StateSelector(dependentStates,
  1.2990 +                                          aSelectorInTopLevel));
  1.2991 +    }
  1.2992 +
  1.2993 +    // Build mIDSelectors
  1.2994 +    if (negation == aSelectorInTopLevel) {
  1.2995 +      for (nsAtomList* curID = negation->mIDList; curID;
  1.2996 +           curID = curID->mNext) {
  1.2997 +        AtomSelectorEntry *entry =
  1.2998 +          static_cast<AtomSelectorEntry*>(PL_DHashTableOperate(&aCascade->mIdSelectors,
  1.2999 +                                                               curID->mAtom,
  1.3000 +                                                               PL_DHASH_ADD));
  1.3001 +        if (entry) {
  1.3002 +          entry->mSelectors.AppendElement(aSelectorInTopLevel);
  1.3003 +        }
  1.3004 +      }
  1.3005 +    } else if (negation->mIDList) {
  1.3006 +      aCascade->mPossiblyNegatedIDSelectors.AppendElement(aSelectorInTopLevel);
  1.3007 +    }
  1.3008 +
  1.3009 +    // Build mClassSelectors
  1.3010 +    if (negation == aSelectorInTopLevel) {
  1.3011 +      for (nsAtomList* curClass = negation->mClassList; curClass;
  1.3012 +           curClass = curClass->mNext) {
  1.3013 +        AtomSelectorEntry *entry =
  1.3014 +          static_cast<AtomSelectorEntry*>(PL_DHashTableOperate(&aCascade->mClassSelectors,
  1.3015 +                                                               curClass->mAtom,
  1.3016 +                                                               PL_DHASH_ADD));
  1.3017 +        if (entry) {
  1.3018 +          entry->mSelectors.AppendElement(aSelectorInTopLevel);
  1.3019 +        }
  1.3020 +      }
  1.3021 +    } else if (negation->mClassList) {
  1.3022 +      aCascade->mPossiblyNegatedClassSelectors.AppendElement(aSelectorInTopLevel);
  1.3023 +    }
  1.3024 +
  1.3025 +    // Build mAttributeSelectors.
  1.3026 +    for (nsAttrSelector *attr = negation->mAttrList; attr;
  1.3027 +         attr = attr->mNext) {
  1.3028 +      nsTArray<nsCSSSelector*> *array =
  1.3029 +        aCascade->AttributeListFor(attr->mCasedAttr);
  1.3030 +      if (!array) {
  1.3031 +        return false;
  1.3032 +      }
  1.3033 +      array->AppendElement(aSelectorInTopLevel);
  1.3034 +      if (attr->mLowercaseAttr != attr->mCasedAttr) {
  1.3035 +        array = aCascade->AttributeListFor(attr->mLowercaseAttr);
  1.3036 +        if (!array) {
  1.3037 +          return false;
  1.3038 +        }
  1.3039 +        array->AppendElement(aSelectorInTopLevel);
  1.3040 +      }
  1.3041 +    }
  1.3042 +
  1.3043 +    // Recur through any :-moz-any selectors
  1.3044 +    for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList;
  1.3045 +         pseudoClass; pseudoClass = pseudoClass->mNext) {
  1.3046 +      if (pseudoClass->mType == nsCSSPseudoClasses::ePseudoClass_any) {
  1.3047 +        for (nsCSSSelectorList *l = pseudoClass->u.mSelectors; l; l = l->mNext) {
  1.3048 +          nsCSSSelector *s = l->mSelectors;
  1.3049 +          if (!AddSelector(aCascade, aSelectorInTopLevel, s)) {
  1.3050 +            return false;
  1.3051 +          }
  1.3052 +        }
  1.3053 +      }
  1.3054 +    }
  1.3055 +  }
  1.3056 +
  1.3057 +  return true;
  1.3058 +}
  1.3059 +
  1.3060 +static bool
  1.3061 +AddRule(RuleSelectorPair* aRuleInfo, RuleCascadeData* aCascade)
  1.3062 +{
  1.3063 +  RuleCascadeData * const cascade = aCascade;
  1.3064 +
  1.3065 +  // Build the rule hash.
  1.3066 +  nsCSSPseudoElements::Type pseudoType = aRuleInfo->mSelector->PseudoType();
  1.3067 +  if (MOZ_LIKELY(pseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement)) {
  1.3068 +    cascade->mRuleHash.AppendRule(*aRuleInfo);
  1.3069 +  } else if (pseudoType < nsCSSPseudoElements::ePseudo_PseudoElementCount) {
  1.3070 +    RuleHash*& ruleHash = cascade->mPseudoElementRuleHashes[pseudoType];
  1.3071 +    if (!ruleHash) {
  1.3072 +      ruleHash = new RuleHash(cascade->mQuirksMode);
  1.3073 +      if (!ruleHash) {
  1.3074 +        // Out of memory; give up
  1.3075 +        return false;
  1.3076 +      }
  1.3077 +    }
  1.3078 +    NS_ASSERTION(aRuleInfo->mSelector->mNext,
  1.3079 +                 "Must have mNext; parser screwed up");
  1.3080 +    NS_ASSERTION(aRuleInfo->mSelector->mNext->mOperator == ':',
  1.3081 +                 "Unexpected mNext combinator");
  1.3082 +    ruleHash->AppendRule(*aRuleInfo);
  1.3083 +  } else if (pseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
  1.3084 +    NS_ASSERTION(!aRuleInfo->mSelector->mCasedTag &&
  1.3085 +                 !aRuleInfo->mSelector->mIDList &&
  1.3086 +                 !aRuleInfo->mSelector->mClassList &&
  1.3087 +                 !aRuleInfo->mSelector->mPseudoClassList &&
  1.3088 +                 !aRuleInfo->mSelector->mAttrList &&
  1.3089 +                 !aRuleInfo->mSelector->mNegations &&
  1.3090 +                 !aRuleInfo->mSelector->mNext &&
  1.3091 +                 aRuleInfo->mSelector->mNameSpace == kNameSpaceID_Unknown,
  1.3092 +                 "Parser messed up with anon box selector");
  1.3093 +
  1.3094 +    // Index doesn't matter here, since we'll just be walking these
  1.3095 +    // rules in order; just pass 0.
  1.3096 +    AppendRuleToTagTable(&cascade->mAnonBoxRules,
  1.3097 +                         aRuleInfo->mSelector->mLowercaseTag,
  1.3098 +                         RuleValue(*aRuleInfo, 0, aCascade->mQuirksMode));
  1.3099 +  } else {
  1.3100 +#ifdef MOZ_XUL
  1.3101 +    NS_ASSERTION(pseudoType == nsCSSPseudoElements::ePseudo_XULTree,
  1.3102 +                 "Unexpected pseudo type");
  1.3103 +    // Index doesn't matter here, since we'll just be walking these
  1.3104 +    // rules in order; just pass 0.
  1.3105 +    AppendRuleToTagTable(&cascade->mXULTreeRules,
  1.3106 +                         aRuleInfo->mSelector->mLowercaseTag,
  1.3107 +                         RuleValue(*aRuleInfo, 0, aCascade->mQuirksMode));
  1.3108 +#else
  1.3109 +    NS_NOTREACHED("Unexpected pseudo type");
  1.3110 +#endif
  1.3111 +  }
  1.3112 +
  1.3113 +  for (nsCSSSelector* selector = aRuleInfo->mSelector;
  1.3114 +           selector; selector = selector->mNext) {
  1.3115 +    if (selector->IsPseudoElement()) {
  1.3116 +      nsCSSPseudoElements::Type pseudo = selector->PseudoType();
  1.3117 +      if (pseudo >= nsCSSPseudoElements::ePseudo_PseudoElementCount ||
  1.3118 +          !nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudo)) {
  1.3119 +        NS_ASSERTION(!selector->mNegations, "Shouldn't have negations");
  1.3120 +        // We do store selectors ending with pseudo-elements that allow :hover
  1.3121 +        // and :active after them in the hashtables corresponding to that
  1.3122 +        // selector's mNext (i.e. the thing that matches against the element),
  1.3123 +        // but we want to make sure that selectors for any other kinds of
  1.3124 +        // pseudo-elements don't end up in the hashtables.  In particular, tree
  1.3125 +        // pseudos store strange things in mPseudoClassList that we don't want
  1.3126 +        // to try to match elements against.
  1.3127 +        continue;
  1.3128 +      }
  1.3129 +    }
  1.3130 +    if (!AddSelector(cascade, selector, selector)) {
  1.3131 +      return false;
  1.3132 +    }
  1.3133 +  }
  1.3134 +
  1.3135 +  return true;
  1.3136 +}
  1.3137 +
  1.3138 +struct PerWeightDataListItem : public RuleSelectorPair {
  1.3139 +  PerWeightDataListItem(css::StyleRule* aRule, nsCSSSelector* aSelector)
  1.3140 +    : RuleSelectorPair(aRule, aSelector)
  1.3141 +    , mNext(nullptr)
  1.3142 +  {}
  1.3143 +  // No destructor; these are arena-allocated
  1.3144 +
  1.3145 +
  1.3146 +  // Placement new to arena allocate the PerWeightDataListItem
  1.3147 +  void *operator new(size_t aSize, PLArenaPool &aArena) CPP_THROW_NEW {
  1.3148 +    void *mem;
  1.3149 +    PL_ARENA_ALLOCATE(mem, &aArena, aSize);
  1.3150 +    return mem;
  1.3151 +  }
  1.3152 +
  1.3153 +  PerWeightDataListItem *mNext;
  1.3154 +};
  1.3155 +
  1.3156 +struct PerWeightData {
  1.3157 +  PerWeightData()
  1.3158 +    : mRuleSelectorPairs(nullptr)
  1.3159 +    , mTail(&mRuleSelectorPairs)
  1.3160 +  {}
  1.3161 +
  1.3162 +  int32_t mWeight;
  1.3163 +  PerWeightDataListItem *mRuleSelectorPairs;
  1.3164 +  PerWeightDataListItem **mTail;
  1.3165 +};
  1.3166 +
  1.3167 +struct RuleByWeightEntry : public PLDHashEntryHdr {
  1.3168 +  PerWeightData data; // mWeight is key, mRuleSelectorPairs are value
  1.3169 +};
  1.3170 +
  1.3171 +static PLDHashNumber
  1.3172 +HashIntKey(PLDHashTable *table, const void *key)
  1.3173 +{
  1.3174 +  return PLDHashNumber(NS_PTR_TO_INT32(key));
  1.3175 +}
  1.3176 +
  1.3177 +static bool
  1.3178 +MatchWeightEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
  1.3179 +                 const void *key)
  1.3180 +{
  1.3181 +  const RuleByWeightEntry *entry = (const RuleByWeightEntry *)hdr;
  1.3182 +  return entry->data.mWeight == NS_PTR_TO_INT32(key);
  1.3183 +}
  1.3184 +
  1.3185 +static bool
  1.3186 +InitWeightEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
  1.3187 +                const void *key)
  1.3188 +{
  1.3189 +  RuleByWeightEntry* entry = static_cast<RuleByWeightEntry*>(hdr);
  1.3190 +  new (entry) RuleByWeightEntry();
  1.3191 +  return true;
  1.3192 +}
  1.3193 +
  1.3194 +static const PLDHashTableOps gRulesByWeightOps = {
  1.3195 +    PL_DHashAllocTable,
  1.3196 +    PL_DHashFreeTable,
  1.3197 +    HashIntKey,
  1.3198 +    MatchWeightEntry,
  1.3199 +    PL_DHashMoveEntryStub,
  1.3200 +    PL_DHashClearEntryStub,
  1.3201 +    PL_DHashFinalizeStub,
  1.3202 +    InitWeightEntry
  1.3203 +};
  1.3204 +
  1.3205 +struct CascadeEnumData {
  1.3206 +  CascadeEnumData(nsPresContext* aPresContext,
  1.3207 +                  nsTArray<nsFontFaceRuleContainer>& aFontFaceRules,
  1.3208 +                  nsTArray<nsCSSKeyframesRule*>& aKeyframesRules,
  1.3209 +                  nsTArray<nsCSSFontFeatureValuesRule*>& aFontFeatureValuesRules,
  1.3210 +                  nsTArray<nsCSSPageRule*>& aPageRules,
  1.3211 +                  nsMediaQueryResultCacheKey& aKey,
  1.3212 +                  uint8_t aSheetType)
  1.3213 +    : mPresContext(aPresContext),
  1.3214 +      mFontFaceRules(aFontFaceRules),
  1.3215 +      mKeyframesRules(aKeyframesRules),
  1.3216 +      mFontFeatureValuesRules(aFontFeatureValuesRules),
  1.3217 +      mPageRules(aPageRules),
  1.3218 +      mCacheKey(aKey),
  1.3219 +      mSheetType(aSheetType)
  1.3220 +  {
  1.3221 +    if (!PL_DHashTableInit(&mRulesByWeight, &gRulesByWeightOps, nullptr,
  1.3222 +                           sizeof(RuleByWeightEntry), 64, fallible_t()))
  1.3223 +      mRulesByWeight.ops = nullptr;
  1.3224 +
  1.3225 +    // Initialize our arena
  1.3226 +    PL_INIT_ARENA_POOL(&mArena, "CascadeEnumDataArena",
  1.3227 +                       NS_CASCADEENUMDATA_ARENA_BLOCK_SIZE);
  1.3228 +  }
  1.3229 +
  1.3230 +  ~CascadeEnumData()
  1.3231 +  {
  1.3232 +    if (mRulesByWeight.ops)
  1.3233 +      PL_DHashTableFinish(&mRulesByWeight);
  1.3234 +    PL_FinishArenaPool(&mArena);
  1.3235 +  }
  1.3236 +
  1.3237 +  nsPresContext* mPresContext;
  1.3238 +  nsTArray<nsFontFaceRuleContainer>& mFontFaceRules;
  1.3239 +  nsTArray<nsCSSKeyframesRule*>& mKeyframesRules;
  1.3240 +  nsTArray<nsCSSFontFeatureValuesRule*>& mFontFeatureValuesRules;
  1.3241 +  nsTArray<nsCSSPageRule*>& mPageRules;
  1.3242 +  nsMediaQueryResultCacheKey& mCacheKey;
  1.3243 +  PLArenaPool mArena;
  1.3244 +  // Hooray, a manual PLDHashTable since nsClassHashtable doesn't
  1.3245 +  // provide a getter that gives me a *reference* to the value.
  1.3246 +  PLDHashTable mRulesByWeight; // of PerWeightDataListItem linked lists
  1.3247 +  uint8_t mSheetType;
  1.3248 +};
  1.3249 +
  1.3250 +/*
  1.3251 + * This enumerates style rules in a sheet (and recursively into any
  1.3252 + * grouping rules) in order to:
  1.3253 + *  (1) add any style rules, in order, into data->mRulesByWeight (for
  1.3254 + *      the primary CSS cascade), where they are separated by weight
  1.3255 + *      but kept in order per-weight, and
  1.3256 + *  (2) add any @font-face rules, in order, into data->mFontFaceRules.
  1.3257 + *  (3) add any @keyframes rules, in order, into data->mKeyframesRules.
  1.3258 + *  (4) add any @font-feature-value rules, in order,
  1.3259 + *      into data->mFontFeatureValuesRules.
  1.3260 + *  (5) add any @page rules, in order, into data->mPageRules.
  1.3261 + */
  1.3262 +static bool
  1.3263 +CascadeRuleEnumFunc(css::Rule* aRule, void* aData)
  1.3264 +{
  1.3265 +  CascadeEnumData* data = (CascadeEnumData*)aData;
  1.3266 +  int32_t type = aRule->GetType();
  1.3267 +
  1.3268 +  if (css::Rule::STYLE_RULE == type) {
  1.3269 +    css::StyleRule* styleRule = static_cast<css::StyleRule*>(aRule);
  1.3270 +
  1.3271 +    for (nsCSSSelectorList *sel = styleRule->Selector();
  1.3272 +         sel; sel = sel->mNext) {
  1.3273 +      int32_t weight = sel->mWeight;
  1.3274 +      RuleByWeightEntry *entry = static_cast<RuleByWeightEntry*>(
  1.3275 +        PL_DHashTableOperate(&data->mRulesByWeight, NS_INT32_TO_PTR(weight),
  1.3276 +                             PL_DHASH_ADD));
  1.3277 +      if (!entry)
  1.3278 +        return false;
  1.3279 +      entry->data.mWeight = weight;
  1.3280 +      // entry->data.mRuleSelectorPairs should be linked in forward order;
  1.3281 +      // entry->data.mTail is the slot to write to.
  1.3282 +      PerWeightDataListItem *newItem =
  1.3283 +        new (data->mArena) PerWeightDataListItem(styleRule, sel->mSelectors);
  1.3284 +      if (newItem) {
  1.3285 +        *(entry->data.mTail) = newItem;
  1.3286 +        entry->data.mTail = &newItem->mNext;
  1.3287 +      }
  1.3288 +    }
  1.3289 +  }
  1.3290 +  else if (css::Rule::MEDIA_RULE == type ||
  1.3291 +           css::Rule::DOCUMENT_RULE == type ||
  1.3292 +           css::Rule::SUPPORTS_RULE == type) {
  1.3293 +    css::GroupRule* groupRule = static_cast<css::GroupRule*>(aRule);
  1.3294 +    if (groupRule->UseForPresentation(data->mPresContext, data->mCacheKey))
  1.3295 +      if (!groupRule->EnumerateRulesForwards(CascadeRuleEnumFunc, aData))
  1.3296 +        return false;
  1.3297 +  }
  1.3298 +  else if (css::Rule::FONT_FACE_RULE == type) {
  1.3299 +    nsCSSFontFaceRule *fontFaceRule = static_cast<nsCSSFontFaceRule*>(aRule);
  1.3300 +    nsFontFaceRuleContainer *ptr = data->mFontFaceRules.AppendElement();
  1.3301 +    if (!ptr)
  1.3302 +      return false;
  1.3303 +    ptr->mRule = fontFaceRule;
  1.3304 +    ptr->mSheetType = data->mSheetType;
  1.3305 +  }
  1.3306 +  else if (css::Rule::KEYFRAMES_RULE == type) {
  1.3307 +    nsCSSKeyframesRule *keyframesRule =
  1.3308 +      static_cast<nsCSSKeyframesRule*>(aRule);
  1.3309 +    if (!data->mKeyframesRules.AppendElement(keyframesRule)) {
  1.3310 +      return false;
  1.3311 +    }
  1.3312 +  }
  1.3313 +  else if (css::Rule::FONT_FEATURE_VALUES_RULE == type) {
  1.3314 +    nsCSSFontFeatureValuesRule *fontFeatureValuesRule =
  1.3315 +      static_cast<nsCSSFontFeatureValuesRule*>(aRule);
  1.3316 +    if (!data->mFontFeatureValuesRules.AppendElement(fontFeatureValuesRule)) {
  1.3317 +      return false;
  1.3318 +    }
  1.3319 +  }
  1.3320 +  else if (css::Rule::PAGE_RULE == type) {
  1.3321 +    nsCSSPageRule* pageRule = static_cast<nsCSSPageRule*>(aRule);
  1.3322 +    if (!data->mPageRules.AppendElement(pageRule)) {
  1.3323 +      return false;
  1.3324 +    }
  1.3325 +  }
  1.3326 +  return true;
  1.3327 +}
  1.3328 +
  1.3329 +/* static */ bool
  1.3330 +nsCSSRuleProcessor::CascadeSheet(nsCSSStyleSheet* aSheet, CascadeEnumData* aData)
  1.3331 +{
  1.3332 +  if (aSheet->IsApplicable() &&
  1.3333 +      aSheet->UseForPresentation(aData->mPresContext, aData->mCacheKey) &&
  1.3334 +      aSheet->mInner) {
  1.3335 +    nsCSSStyleSheet* child = aSheet->mInner->mFirstChild;
  1.3336 +    while (child) {
  1.3337 +      CascadeSheet(child, aData);
  1.3338 +      child = child->mNext;
  1.3339 +    }
  1.3340 +
  1.3341 +    if (!aSheet->mInner->mOrderedRules.EnumerateForwards(CascadeRuleEnumFunc,
  1.3342 +                                                         aData))
  1.3343 +      return false;
  1.3344 +  }
  1.3345 +  return true;
  1.3346 +}
  1.3347 +
  1.3348 +static int CompareWeightData(const void* aArg1, const void* aArg2,
  1.3349 +                             void* closure)
  1.3350 +{
  1.3351 +  const PerWeightData* arg1 = static_cast<const PerWeightData*>(aArg1);
  1.3352 +  const PerWeightData* arg2 = static_cast<const PerWeightData*>(aArg2);
  1.3353 +  return arg1->mWeight - arg2->mWeight; // put lower weight first
  1.3354 +}
  1.3355 +
  1.3356 +
  1.3357 +struct FillWeightArrayData {
  1.3358 +  FillWeightArrayData(PerWeightData* aArrayData) :
  1.3359 +    mIndex(0),
  1.3360 +    mWeightArray(aArrayData)
  1.3361 +  {
  1.3362 +  }
  1.3363 +  int32_t mIndex;
  1.3364 +  PerWeightData* mWeightArray;
  1.3365 +};
  1.3366 +
  1.3367 +
  1.3368 +static PLDHashOperator
  1.3369 +FillWeightArray(PLDHashTable *table, PLDHashEntryHdr *hdr,
  1.3370 +                uint32_t number, void *arg)
  1.3371 +{
  1.3372 +  FillWeightArrayData* data = static_cast<FillWeightArrayData*>(arg);
  1.3373 +  const RuleByWeightEntry *entry = (const RuleByWeightEntry *)hdr;
  1.3374 +
  1.3375 +  data->mWeightArray[data->mIndex++] = entry->data;
  1.3376 +
  1.3377 +  return PL_DHASH_NEXT;
  1.3378 +}
  1.3379 +
  1.3380 +RuleCascadeData*
  1.3381 +nsCSSRuleProcessor::GetRuleCascade(nsPresContext* aPresContext)
  1.3382 +{
  1.3383 +  // FIXME:  Make this infallible!
  1.3384 +
  1.3385 +  // If anything changes about the presentation context, we will be
  1.3386 +  // notified.  Otherwise, our cache is valid if mLastPresContext
  1.3387 +  // matches aPresContext.  (The only rule processors used for multiple
  1.3388 +  // pres contexts are for XBL.  These rule processors are probably less
  1.3389 +  // likely to have @media rules, and thus the cache is pretty likely to
  1.3390 +  // hit instantly even when we're switching between pres contexts.)
  1.3391 +
  1.3392 +  if (!mRuleCascades || aPresContext != mLastPresContext) {
  1.3393 +    RefreshRuleCascade(aPresContext);
  1.3394 +  }
  1.3395 +  mLastPresContext = aPresContext;
  1.3396 +
  1.3397 +  return mRuleCascades;
  1.3398 +}
  1.3399 +
  1.3400 +void
  1.3401 +nsCSSRuleProcessor::RefreshRuleCascade(nsPresContext* aPresContext)
  1.3402 +{
  1.3403 +  // Having RuleCascadeData objects be per-medium (over all variation
  1.3404 +  // caused by media queries, handled through mCacheKey) works for now
  1.3405 +  // since nsCSSRuleProcessor objects are per-document.  (For a given
  1.3406 +  // set of stylesheets they can vary based on medium (@media) or
  1.3407 +  // document (@-moz-document).)
  1.3408 +
  1.3409 +  for (RuleCascadeData **cascadep = &mRuleCascades, *cascade;
  1.3410 +       (cascade = *cascadep); cascadep = &cascade->mNext) {
  1.3411 +    if (cascade->mCacheKey.Matches(aPresContext)) {
  1.3412 +      // Ensure that the current one is always mRuleCascades.
  1.3413 +      *cascadep = cascade->mNext;
  1.3414 +      cascade->mNext = mRuleCascades;
  1.3415 +      mRuleCascades = cascade;
  1.3416 +
  1.3417 +      return;
  1.3418 +    }
  1.3419 +  }
  1.3420 +
  1.3421 +  if (mSheets.Length() != 0) {
  1.3422 +    nsAutoPtr<RuleCascadeData> newCascade(
  1.3423 +      new RuleCascadeData(aPresContext->Medium(),
  1.3424 +                          eCompatibility_NavQuirks == aPresContext->CompatibilityMode()));
  1.3425 +    if (newCascade) {
  1.3426 +      CascadeEnumData data(aPresContext, newCascade->mFontFaceRules,
  1.3427 +                           newCascade->mKeyframesRules,
  1.3428 +                           newCascade->mFontFeatureValuesRules,
  1.3429 +                           newCascade->mPageRules,
  1.3430 +                           newCascade->mCacheKey,
  1.3431 +                           mSheetType);
  1.3432 +      if (!data.mRulesByWeight.ops)
  1.3433 +        return; /* out of memory */
  1.3434 +
  1.3435 +      for (uint32_t i = 0; i < mSheets.Length(); ++i) {
  1.3436 +        if (!CascadeSheet(mSheets.ElementAt(i), &data))
  1.3437 +          return; /* out of memory */
  1.3438 +      }
  1.3439 +
  1.3440 +      // Sort the hash table of per-weight linked lists by weight.
  1.3441 +      uint32_t weightCount = data.mRulesByWeight.entryCount;
  1.3442 +      nsAutoArrayPtr<PerWeightData> weightArray(new PerWeightData[weightCount]);
  1.3443 +      FillWeightArrayData fwData(weightArray);
  1.3444 +      PL_DHashTableEnumerate(&data.mRulesByWeight, FillWeightArray, &fwData);
  1.3445 +      NS_QuickSort(weightArray, weightCount, sizeof(PerWeightData),
  1.3446 +                   CompareWeightData, nullptr);
  1.3447 +
  1.3448 +      // Put things into the rule hash.
  1.3449 +      // The primary sort is by weight...
  1.3450 +      for (uint32_t i = 0; i < weightCount; ++i) {
  1.3451 +        // and the secondary sort is by order.  mRuleSelectorPairs is already in
  1.3452 +        // the right order..
  1.3453 +        for (PerWeightDataListItem *cur = weightArray[i].mRuleSelectorPairs;
  1.3454 +             cur;
  1.3455 +             cur = cur->mNext) {
  1.3456 +          if (!AddRule(cur, newCascade))
  1.3457 +            return; /* out of memory */
  1.3458 +        }
  1.3459 +      }
  1.3460 +
  1.3461 +      // Build mKeyframesRuleTable.
  1.3462 +      for (nsTArray<nsCSSKeyframesRule*>::size_type i = 0,
  1.3463 +             iEnd = newCascade->mKeyframesRules.Length(); i < iEnd; ++i) {
  1.3464 +        nsCSSKeyframesRule* rule = newCascade->mKeyframesRules[i];
  1.3465 +        newCascade->mKeyframesRuleTable.Put(rule->GetName(), rule);
  1.3466 +      }
  1.3467 +
  1.3468 +      // Ensure that the current one is always mRuleCascades.
  1.3469 +      newCascade->mNext = mRuleCascades;
  1.3470 +      mRuleCascades = newCascade.forget();
  1.3471 +    }
  1.3472 +  }
  1.3473 +  return;
  1.3474 +}
  1.3475 +
  1.3476 +/* static */ bool
  1.3477 +nsCSSRuleProcessor::SelectorListMatches(Element* aElement,
  1.3478 +                                        TreeMatchContext& aTreeMatchContext,
  1.3479 +                                        nsCSSSelectorList* aSelectorList)
  1.3480 +{
  1.3481 +  MOZ_ASSERT(!aTreeMatchContext.mForScopedStyle,
  1.3482 +             "mCurrentStyleScope will need to be saved and restored after the "
  1.3483 +             "SelectorMatchesTree call");
  1.3484 +
  1.3485 +  while (aSelectorList) {
  1.3486 +    nsCSSSelector* sel = aSelectorList->mSelectors;
  1.3487 +    NS_ASSERTION(sel, "Should have *some* selectors");
  1.3488 +    NS_ASSERTION(!sel->IsPseudoElement(), "Shouldn't have been called");
  1.3489 +    NodeMatchContext nodeContext(EventStates(), false);
  1.3490 +    if (SelectorMatches(aElement, sel, nodeContext, aTreeMatchContext)) {
  1.3491 +      nsCSSSelector* next = sel->mNext;
  1.3492 +      if (!next ||
  1.3493 +          SelectorMatchesTree(aElement, next, aTreeMatchContext, false)) {
  1.3494 +        return true;
  1.3495 +      }
  1.3496 +    }
  1.3497 +
  1.3498 +    aSelectorList = aSelectorList->mNext;
  1.3499 +  }
  1.3500 +
  1.3501 +  return false;
  1.3502 +}
  1.3503 +
  1.3504 +// TreeMatchContext and AncestorFilter out of line methods
  1.3505 +void
  1.3506 +TreeMatchContext::InitAncestors(Element *aElement)
  1.3507 +{
  1.3508 +  MOZ_ASSERT(!mAncestorFilter.mFilter);
  1.3509 +  MOZ_ASSERT(mAncestorFilter.mHashes.IsEmpty());
  1.3510 +  MOZ_ASSERT(mStyleScopes.IsEmpty());
  1.3511 +
  1.3512 +  mAncestorFilter.mFilter = new AncestorFilter::Filter();
  1.3513 +
  1.3514 +  if (MOZ_LIKELY(aElement)) {
  1.3515 +    MOZ_ASSERT(aElement->IsInDoc(),
  1.3516 +               "aElement must be in the document for the assumption that "
  1.3517 +               "GetParentNode() is non-null on all element ancestors of "
  1.3518 +               "aElement to be true");
  1.3519 +    // Collect up the ancestors
  1.3520 +    nsAutoTArray<Element*, 50> ancestors;
  1.3521 +    Element* cur = aElement;
  1.3522 +    do {
  1.3523 +      ancestors.AppendElement(cur);
  1.3524 +      nsINode* parent = cur->GetParentNode();
  1.3525 +      if (!parent->IsElement()) {
  1.3526 +        break;
  1.3527 +      }
  1.3528 +
  1.3529 +      cur = parent->AsElement();
  1.3530 +    } while (true);
  1.3531 +
  1.3532 +    // Now push them in reverse order.
  1.3533 +    for (uint32_t i = ancestors.Length(); i-- != 0; ) {
  1.3534 +      mAncestorFilter.PushAncestor(ancestors[i]);
  1.3535 +      PushStyleScope(ancestors[i]);
  1.3536 +    }
  1.3537 +  }
  1.3538 +}
  1.3539 +
  1.3540 +void
  1.3541 +TreeMatchContext::InitStyleScopes(Element* aElement)
  1.3542 +{
  1.3543 +  MOZ_ASSERT(mStyleScopes.IsEmpty());
  1.3544 +
  1.3545 +  if (MOZ_LIKELY(aElement)) {
  1.3546 +    // Collect up the ancestors
  1.3547 +    nsAutoTArray<Element*, 50> ancestors;
  1.3548 +    Element* cur = aElement;
  1.3549 +    do {
  1.3550 +      ancestors.AppendElement(cur);
  1.3551 +      nsINode* parent = cur->GetParentNode();
  1.3552 +      if (!parent || !parent->IsElement()) {
  1.3553 +        break;
  1.3554 +      }
  1.3555 +
  1.3556 +      cur = parent->AsElement();
  1.3557 +    } while (true);
  1.3558 +
  1.3559 +    // Now push them in reverse order.
  1.3560 +    for (uint32_t i = ancestors.Length(); i-- != 0; ) {
  1.3561 +      PushStyleScope(ancestors[i]);
  1.3562 +    }
  1.3563 +  }
  1.3564 +}
  1.3565 +
  1.3566 +void
  1.3567 +AncestorFilter::PushAncestor(Element *aElement)
  1.3568 +{
  1.3569 +  MOZ_ASSERT(mFilter);
  1.3570 +
  1.3571 +  uint32_t oldLength = mHashes.Length();
  1.3572 +
  1.3573 +  mPopTargets.AppendElement(oldLength);
  1.3574 +#ifdef DEBUG
  1.3575 +  mElements.AppendElement(aElement);
  1.3576 +#endif
  1.3577 +  mHashes.AppendElement(aElement->Tag()->hash());
  1.3578 +  nsIAtom *id = aElement->GetID();
  1.3579 +  if (id) {
  1.3580 +    mHashes.AppendElement(id->hash());
  1.3581 +  }
  1.3582 +  const nsAttrValue *classes = aElement->GetClasses();
  1.3583 +  if (classes) {
  1.3584 +    uint32_t classCount = classes->GetAtomCount();
  1.3585 +    for (uint32_t i = 0; i < classCount; ++i) {
  1.3586 +      mHashes.AppendElement(classes->AtomAt(i)->hash());
  1.3587 +    }
  1.3588 +  }
  1.3589 +
  1.3590 +  uint32_t newLength = mHashes.Length();
  1.3591 +  for (uint32_t i = oldLength; i < newLength; ++i) {
  1.3592 +    mFilter->add(mHashes[i]);
  1.3593 +  }
  1.3594 +}
  1.3595 +
  1.3596 +void
  1.3597 +AncestorFilter::PopAncestor()
  1.3598 +{
  1.3599 +  MOZ_ASSERT(!mPopTargets.IsEmpty());
  1.3600 +  MOZ_ASSERT(mPopTargets.Length() == mElements.Length());
  1.3601 +
  1.3602 +  uint32_t popTargetLength = mPopTargets.Length();
  1.3603 +  uint32_t newLength = mPopTargets[popTargetLength-1];
  1.3604 +
  1.3605 +  mPopTargets.TruncateLength(popTargetLength-1);
  1.3606 +#ifdef DEBUG
  1.3607 +  mElements.TruncateLength(popTargetLength-1);
  1.3608 +#endif
  1.3609 +
  1.3610 +  uint32_t oldLength = mHashes.Length();
  1.3611 +  for (uint32_t i = newLength; i < oldLength; ++i) {
  1.3612 +    mFilter->remove(mHashes[i]);
  1.3613 +  }
  1.3614 +  mHashes.TruncateLength(newLength);
  1.3615 +}
  1.3616 +
  1.3617 +#ifdef DEBUG
  1.3618 +void
  1.3619 +AncestorFilter::AssertHasAllAncestors(Element *aElement) const
  1.3620 +{
  1.3621 +  nsINode* cur = aElement->GetParentNode();
  1.3622 +  while (cur && cur->IsElement()) {
  1.3623 +    MOZ_ASSERT(mElements.Contains(cur));
  1.3624 +    cur = cur->GetParentNode();
  1.3625 +  }
  1.3626 +}
  1.3627 +#endif

mercurial