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