Bumping manifests a=b2g-bump
[gecko.git] / layout / style / nsCSSRuleProcessor.cpp
blob3738360cd6a0efb8e4f3ad97b62ed9276e0d4a9c
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:tabstop=2:expandtab:shiftwidth=2:
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * style rule processor for CSS style sheets, responsible for selector
9 * matching and cascading
12 #define PL_ARENA_CONST_ALIGN_MASK 7
13 // We want page-sized arenas so there's no fragmentation involved.
14 // Including plarena.h must come first to avoid it being included by some
15 // header file thereby making PL_ARENA_CONST_ALIGN_MASK ineffective.
16 #define NS_CASCADEENUMDATA_ARENA_BLOCK_SIZE (4096)
17 #include "plarena.h"
19 #include "nsCSSRuleProcessor.h"
20 #include "nsRuleProcessorData.h"
21 #include <algorithm>
22 #include "nsIAtom.h"
23 #include "pldhash.h"
24 #include "nsICSSPseudoComparator.h"
25 #include "mozilla/MemoryReporting.h"
26 #include "mozilla/css/StyleRule.h"
27 #include "mozilla/css/GroupRule.h"
28 #include "nsIDocument.h"
29 #include "nsPresContext.h"
30 #include "nsGkAtoms.h"
31 #include "nsUnicharUtils.h"
32 #include "nsError.h"
33 #include "nsRuleWalker.h"
34 #include "nsCSSPseudoClasses.h"
35 #include "nsCSSPseudoElements.h"
36 #include "nsIContent.h"
37 #include "nsCOMPtr.h"
38 #include "nsHashKeys.h"
39 #include "nsStyleUtil.h"
40 #include "nsQuickSort.h"
41 #include "nsAttrValue.h"
42 #include "nsAttrValueInlines.h"
43 #include "nsAttrName.h"
44 #include "nsTArray.h"
45 #include "nsContentUtils.h"
46 #include "nsIMediaList.h"
47 #include "nsCSSRules.h"
48 #include "nsStyleSet.h"
49 #include "mozilla/dom/Element.h"
50 #include "nsNthIndexCache.h"
51 #include "mozilla/ArrayUtils.h"
52 #include "mozilla/EventStates.h"
53 #include "mozilla/Preferences.h"
54 #include "mozilla/LookAndFeel.h"
55 #include "mozilla/Likely.h"
57 using namespace mozilla;
58 using namespace mozilla::dom;
60 #define VISITED_PSEUDO_PREF "layout.css.visited_links_enabled"
62 static bool gSupportVisitedPseudo = true;
64 static nsTArray< nsCOMPtr<nsIAtom> >* sSystemMetrics = 0;
66 #ifdef XP_WIN
67 uint8_t nsCSSRuleProcessor::sWinThemeId = LookAndFeel::eWindowsTheme_Generic;
68 #endif
70 /**
71 * A struct representing a given CSS rule and a particular selector
72 * from that rule's selector list.
74 struct RuleSelectorPair {
75 RuleSelectorPair(css::StyleRule* aRule, nsCSSSelector* aSelector)
76 : mRule(aRule), mSelector(aSelector) {}
77 // If this class ever grows a destructor, deal with
78 // PerWeightDataListItem appropriately.
80 css::StyleRule* mRule;
81 nsCSSSelector* mSelector; // which of |mRule|'s selectors
84 #define NS_IS_ANCESTOR_OPERATOR(ch) \
85 ((ch) == char16_t(' ') || (ch) == char16_t('>'))
87 /**
88 * A struct representing a particular rule in an ordered list of rules
89 * (the ordering depending on the weight of mSelector and the order of
90 * our rules to start with).
92 struct RuleValue : RuleSelectorPair {
93 enum {
94 eMaxAncestorHashes = 4
97 RuleValue(const RuleSelectorPair& aRuleSelectorPair, int32_t aIndex,
98 bool aQuirksMode) :
99 RuleSelectorPair(aRuleSelectorPair),
100 mIndex(aIndex)
102 CollectAncestorHashes(aQuirksMode);
105 int32_t mIndex; // High index means high weight/order.
106 uint32_t mAncestorSelectorHashes[eMaxAncestorHashes];
108 private:
109 void CollectAncestorHashes(bool aQuirksMode) {
110 // Collect up our mAncestorSelectorHashes. It's not clear whether it's
111 // better to stop once we've found eMaxAncestorHashes of them or to keep
112 // going and preferentially collect information from selectors higher up the
113 // chain... Let's do the former for now.
114 size_t hashIndex = 0;
115 for (nsCSSSelector* sel = mSelector->mNext; sel; sel = sel->mNext) {
116 if (!NS_IS_ANCESTOR_OPERATOR(sel->mOperator)) {
117 // |sel| is going to select something that's not actually one of our
118 // ancestors, so don't add it to mAncestorSelectorHashes. But keep
119 // going, because it'll select a sibling of one of our ancestors, so its
120 // ancestors would be our ancestors too.
121 continue;
124 // Now sel is supposed to select one of our ancestors. Grab
125 // whatever info we can from it into mAncestorSelectorHashes.
126 // But in qurks mode, don't grab IDs and classes because those
127 // need to be matched case-insensitively.
128 if (!aQuirksMode) {
129 nsAtomList* ids = sel->mIDList;
130 while (ids) {
131 mAncestorSelectorHashes[hashIndex++] = ids->mAtom->hash();
132 if (hashIndex == eMaxAncestorHashes) {
133 return;
135 ids = ids->mNext;
138 nsAtomList* classes = sel->mClassList;
139 while (classes) {
140 mAncestorSelectorHashes[hashIndex++] = classes->mAtom->hash();
141 if (hashIndex == eMaxAncestorHashes) {
142 return;
144 classes = classes->mNext;
148 // Only put in the tag name if it's all-lowercase. Otherwise we run into
149 // trouble because we may test the wrong one of mLowercaseTag and
150 // mCasedTag against the filter.
151 if (sel->mLowercaseTag && sel->mCasedTag == sel->mLowercaseTag) {
152 mAncestorSelectorHashes[hashIndex++] = sel->mLowercaseTag->hash();
153 if (hashIndex == eMaxAncestorHashes) {
154 return;
159 while (hashIndex != eMaxAncestorHashes) {
160 mAncestorSelectorHashes[hashIndex++] = 0;
165 // ------------------------------
166 // Rule hash table
169 // Uses any of the sets of ops below.
170 struct RuleHashTableEntry : public PLDHashEntryHdr {
171 // If you add members that have heap allocated memory be sure to change the
172 // logic in SizeOfRuleHashTableEntry().
173 // Auto length 1, because we always have at least one entry in mRules.
174 nsAutoTArray<RuleValue, 1> mRules;
177 struct RuleHashTagTableEntry : public RuleHashTableEntry {
178 // If you add members that have heap allocated memory be sure to change the
179 // logic in RuleHash::SizeOf{In,Ex}cludingThis.
180 nsCOMPtr<nsIAtom> mTag;
183 static PLDHashNumber
184 RuleHash_CIHashKey(PLDHashTable *table, const void *key)
186 nsIAtom *atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
188 nsAutoString str;
189 atom->ToString(str);
190 nsContentUtils::ASCIIToLower(str);
191 return HashString(str);
194 typedef nsIAtom*
195 (* RuleHashGetKey) (PLDHashTable *table, const PLDHashEntryHdr *entry);
197 struct RuleHashTableOps {
198 const PLDHashTableOps ops;
199 // Extra callback to avoid duplicating the matchEntry callback for
200 // each table. (There used to be a getKey callback in
201 // PLDHashTableOps.)
202 RuleHashGetKey getKey;
205 inline const RuleHashTableOps*
206 ToLocalOps(const PLDHashTableOps *aOps)
208 return (const RuleHashTableOps*)
209 (((const char*) aOps) - offsetof(RuleHashTableOps, ops));
212 static bool
213 RuleHash_CIMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
214 const void *key)
216 nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>
217 (key));
218 // Use our extra |getKey| callback to avoid code duplication.
219 nsIAtom *entry_atom = ToLocalOps(table->ops)->getKey(table, hdr);
221 // Check for case-sensitive match first.
222 if (match_atom == entry_atom)
223 return true;
225 // Use EqualsIgnoreASCIICase instead of full on unicode case conversion
226 // in order to save on performance. This is only used in quirks mode
227 // anyway.
229 return
230 nsContentUtils::EqualsIgnoreASCIICase(nsDependentAtomString(entry_atom),
231 nsDependentAtomString(match_atom));
234 static bool
235 RuleHash_CSMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
236 const void *key)
238 nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>
239 (key));
240 // Use our extra |getKey| callback to avoid code duplication.
241 nsIAtom *entry_atom = ToLocalOps(table->ops)->getKey(table, hdr);
243 return match_atom == entry_atom;
246 static bool
247 RuleHash_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
248 const void *key)
250 RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(hdr);
251 new (entry) RuleHashTableEntry();
252 return true;
255 static void
256 RuleHash_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
258 RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(hdr);
259 entry->~RuleHashTableEntry();
262 static void
263 RuleHash_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
264 PLDHashEntryHdr *to)
266 NS_PRECONDITION(from != to, "This is not going to work!");
267 RuleHashTableEntry *oldEntry =
268 const_cast<RuleHashTableEntry*>(
269 static_cast<const RuleHashTableEntry*>(from));
270 RuleHashTableEntry *newEntry = new (to) RuleHashTableEntry();
271 newEntry->mRules.SwapElements(oldEntry->mRules);
272 oldEntry->~RuleHashTableEntry();
275 static bool
276 RuleHash_TagTable_MatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
277 const void *key)
279 nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>
280 (key));
281 nsIAtom *entry_atom = static_cast<const RuleHashTagTableEntry*>(hdr)->mTag;
283 return match_atom == entry_atom;
286 static bool
287 RuleHash_TagTable_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
288 const void *key)
290 RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>(hdr);
291 new (entry) RuleHashTagTableEntry();
292 entry->mTag = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
293 return true;
296 static void
297 RuleHash_TagTable_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
299 RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>(hdr);
300 entry->~RuleHashTagTableEntry();
303 static void
304 RuleHash_TagTable_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
305 PLDHashEntryHdr *to)
307 NS_PRECONDITION(from != to, "This is not going to work!");
308 RuleHashTagTableEntry *oldEntry =
309 const_cast<RuleHashTagTableEntry*>(
310 static_cast<const RuleHashTagTableEntry*>(from));
311 RuleHashTagTableEntry *newEntry = new (to) RuleHashTagTableEntry();
312 newEntry->mTag.swap(oldEntry->mTag);
313 newEntry->mRules.SwapElements(oldEntry->mRules);
314 oldEntry->~RuleHashTagTableEntry();
317 static nsIAtom*
318 RuleHash_ClassTable_GetKey(PLDHashTable *table, const PLDHashEntryHdr *hdr)
320 const RuleHashTableEntry *entry =
321 static_cast<const RuleHashTableEntry*>(hdr);
322 nsCSSSelector* selector = entry->mRules[0].mSelector;
323 if (selector->IsPseudoElement()) {
324 selector = selector->mNext;
326 return selector->mClassList->mAtom;
329 static nsIAtom*
330 RuleHash_IdTable_GetKey(PLDHashTable *table, const PLDHashEntryHdr *hdr)
332 const RuleHashTableEntry *entry =
333 static_cast<const RuleHashTableEntry*>(hdr);
334 nsCSSSelector* selector = entry->mRules[0].mSelector;
335 if (selector->IsPseudoElement()) {
336 selector = selector->mNext;
338 return selector->mIDList->mAtom;
341 static PLDHashNumber
342 RuleHash_NameSpaceTable_HashKey(PLDHashTable *table, const void *key)
344 return NS_PTR_TO_INT32(key);
347 static bool
348 RuleHash_NameSpaceTable_MatchEntry(PLDHashTable *table,
349 const PLDHashEntryHdr *hdr,
350 const void *key)
352 const RuleHashTableEntry *entry =
353 static_cast<const RuleHashTableEntry*>(hdr);
355 nsCSSSelector* selector = entry->mRules[0].mSelector;
356 if (selector->IsPseudoElement()) {
357 selector = selector->mNext;
359 return NS_PTR_TO_INT32(key) == selector->mNameSpace;
362 static const PLDHashTableOps RuleHash_TagTable_Ops = {
363 PL_DHashAllocTable,
364 PL_DHashFreeTable,
365 PL_DHashVoidPtrKeyStub,
366 RuleHash_TagTable_MatchEntry,
367 RuleHash_TagTable_MoveEntry,
368 RuleHash_TagTable_ClearEntry,
369 PL_DHashFinalizeStub,
370 RuleHash_TagTable_InitEntry
373 // Case-sensitive ops.
374 static const RuleHashTableOps RuleHash_ClassTable_CSOps = {
376 PL_DHashAllocTable,
377 PL_DHashFreeTable,
378 PL_DHashVoidPtrKeyStub,
379 RuleHash_CSMatchEntry,
380 RuleHash_MoveEntry,
381 RuleHash_ClearEntry,
382 PL_DHashFinalizeStub,
383 RuleHash_InitEntry
385 RuleHash_ClassTable_GetKey
388 // Case-insensitive ops.
389 static const RuleHashTableOps RuleHash_ClassTable_CIOps = {
391 PL_DHashAllocTable,
392 PL_DHashFreeTable,
393 RuleHash_CIHashKey,
394 RuleHash_CIMatchEntry,
395 RuleHash_MoveEntry,
396 RuleHash_ClearEntry,
397 PL_DHashFinalizeStub,
398 RuleHash_InitEntry
400 RuleHash_ClassTable_GetKey
403 // Case-sensitive ops.
404 static const RuleHashTableOps RuleHash_IdTable_CSOps = {
406 PL_DHashAllocTable,
407 PL_DHashFreeTable,
408 PL_DHashVoidPtrKeyStub,
409 RuleHash_CSMatchEntry,
410 RuleHash_MoveEntry,
411 RuleHash_ClearEntry,
412 PL_DHashFinalizeStub,
413 RuleHash_InitEntry
415 RuleHash_IdTable_GetKey
418 // Case-insensitive ops.
419 static const RuleHashTableOps RuleHash_IdTable_CIOps = {
421 PL_DHashAllocTable,
422 PL_DHashFreeTable,
423 RuleHash_CIHashKey,
424 RuleHash_CIMatchEntry,
425 RuleHash_MoveEntry,
426 RuleHash_ClearEntry,
427 PL_DHashFinalizeStub,
428 RuleHash_InitEntry
430 RuleHash_IdTable_GetKey
433 static const PLDHashTableOps RuleHash_NameSpaceTable_Ops = {
434 PL_DHashAllocTable,
435 PL_DHashFreeTable,
436 RuleHash_NameSpaceTable_HashKey,
437 RuleHash_NameSpaceTable_MatchEntry,
438 RuleHash_MoveEntry,
439 RuleHash_ClearEntry,
440 PL_DHashFinalizeStub,
441 RuleHash_InitEntry
444 #undef RULE_HASH_STATS
445 #undef PRINT_UNIVERSAL_RULES
447 #ifdef RULE_HASH_STATS
448 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO ++(var_); PR_END_MACRO
449 #else
450 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO PR_END_MACRO
451 #endif
453 struct NodeMatchContext;
455 class RuleHash {
456 public:
457 explicit RuleHash(bool aQuirksMode);
458 ~RuleHash();
459 void AppendRule(const RuleSelectorPair &aRuleInfo);
460 void EnumerateAllRules(Element* aElement, ElementDependentRuleProcessorData* aData,
461 NodeMatchContext& aNodeMatchContext);
463 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
464 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
466 protected:
467 typedef nsTArray<RuleValue> RuleValueList;
468 void AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
469 const RuleSelectorPair& aRuleInfo);
470 void AppendUniversalRule(const RuleSelectorPair& aRuleInfo);
472 int32_t mRuleCount;
473 // The hashtables are lazily initialized; we use a null .ops to
474 // indicate that they need initialization.
475 PLDHashTable mIdTable;
476 PLDHashTable mClassTable;
477 PLDHashTable mTagTable;
478 PLDHashTable mNameSpaceTable;
479 RuleValueList mUniversalRules;
481 struct EnumData {
482 const RuleValue* mCurValue;
483 const RuleValue* mEnd;
485 EnumData* mEnumList;
486 int32_t mEnumListSize;
488 bool mQuirksMode;
490 inline EnumData ToEnumData(const RuleValueList& arr) {
491 EnumData data = { arr.Elements(), arr.Elements() + arr.Length() };
492 return data;
495 #ifdef RULE_HASH_STATS
496 uint32_t mUniversalSelectors;
497 uint32_t mNameSpaceSelectors;
498 uint32_t mTagSelectors;
499 uint32_t mClassSelectors;
500 uint32_t mIdSelectors;
502 uint32_t mElementsMatched;
504 uint32_t mElementUniversalCalls;
505 uint32_t mElementNameSpaceCalls;
506 uint32_t mElementTagCalls;
507 uint32_t mElementClassCalls;
508 uint32_t mElementIdCalls;
509 #endif // RULE_HASH_STATS
512 RuleHash::RuleHash(bool aQuirksMode)
513 : mRuleCount(0),
514 mUniversalRules(0),
515 mEnumList(nullptr), mEnumListSize(0),
516 mQuirksMode(aQuirksMode)
517 #ifdef RULE_HASH_STATS
519 mUniversalSelectors(0),
520 mNameSpaceSelectors(0),
521 mTagSelectors(0),
522 mClassSelectors(0),
523 mIdSelectors(0),
524 mElementsMatched(0),
525 mElementUniversalCalls(0),
526 mElementNameSpaceCalls(0),
527 mElementTagCalls(0),
528 mElementClassCalls(0),
529 mElementIdCalls(0)
530 #endif
532 MOZ_COUNT_CTOR(RuleHash);
534 mTagTable.ops = nullptr;
535 mIdTable.ops = nullptr;
536 mClassTable.ops = nullptr;
537 mNameSpaceTable.ops = nullptr;
540 RuleHash::~RuleHash()
542 MOZ_COUNT_DTOR(RuleHash);
543 #ifdef RULE_HASH_STATS
544 printf(
545 "RuleHash(%p):\n"
546 " Selectors: Universal (%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
547 " Content Nodes: Elements(%u)\n"
548 " Element Calls: Universal(%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
549 static_cast<void*>(this),
550 mUniversalSelectors, mNameSpaceSelectors, mTagSelectors,
551 mClassSelectors, mIdSelectors,
552 mElementsMatched,
553 mElementUniversalCalls, mElementNameSpaceCalls, mElementTagCalls,
554 mElementClassCalls, mElementIdCalls);
555 #ifdef PRINT_UNIVERSAL_RULES
557 if (mUniversalRules.Length() > 0) {
558 printf(" Universal rules:\n");
559 for (uint32_t i = 0; i < mUniversalRules.Length(); ++i) {
560 RuleValue* value = &(mUniversalRules[i]);
561 nsAutoString selectorText;
562 uint32_t lineNumber = value->mRule->GetLineNumber();
563 nsRefPtr<CSSStyleSheet> cssSheet = value->mRule->GetStyleSheet();
564 value->mSelector->ToString(selectorText, cssSheet);
566 printf(" line %d, %s\n",
567 lineNumber, NS_ConvertUTF16toUTF8(selectorText).get());
571 #endif // PRINT_UNIVERSAL_RULES
572 #endif // RULE_HASH_STATS
573 // Rule Values are arena allocated no need to delete them. Their destructor
574 // isn't doing any cleanup. So we dont even bother to enumerate through
575 // the hash tables and call their destructors.
576 if (nullptr != mEnumList) {
577 delete [] mEnumList;
579 // delete arena for strings and small objects
580 if (mIdTable.ops) {
581 PL_DHashTableFinish(&mIdTable);
583 if (mClassTable.ops) {
584 PL_DHashTableFinish(&mClassTable);
586 if (mTagTable.ops) {
587 PL_DHashTableFinish(&mTagTable);
589 if (mNameSpaceTable.ops) {
590 PL_DHashTableFinish(&mNameSpaceTable);
594 void RuleHash::AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
595 const RuleSelectorPair& aRuleInfo)
597 // Get a new or existing entry.
598 RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
599 (PL_DHashTableOperate(aTable, aKey, PL_DHASH_ADD));
600 if (!entry)
601 return;
602 entry->mRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode));
605 static void
606 AppendRuleToTagTable(PLDHashTable* aTable, nsIAtom* aKey,
607 const RuleValue& aRuleInfo)
609 // Get a new or exisiting entry
610 RuleHashTagTableEntry *entry = static_cast<RuleHashTagTableEntry*>
611 (PL_DHashTableOperate(aTable, aKey, PL_DHASH_ADD));
612 if (!entry)
613 return;
615 entry->mRules.AppendElement(aRuleInfo);
618 void RuleHash::AppendUniversalRule(const RuleSelectorPair& aRuleInfo)
620 mUniversalRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode));
623 void RuleHash::AppendRule(const RuleSelectorPair& aRuleInfo)
625 nsCSSSelector *selector = aRuleInfo.mSelector;
626 if (selector->IsPseudoElement()) {
627 selector = selector->mNext;
629 if (nullptr != selector->mIDList) {
630 if (!mIdTable.ops) {
631 PL_DHashTableInit(&mIdTable,
632 mQuirksMode ? &RuleHash_IdTable_CIOps.ops
633 : &RuleHash_IdTable_CSOps.ops,
634 nullptr, sizeof(RuleHashTableEntry));
636 AppendRuleToTable(&mIdTable, selector->mIDList->mAtom, aRuleInfo);
637 RULE_HASH_STAT_INCREMENT(mIdSelectors);
639 else if (nullptr != selector->mClassList) {
640 if (!mClassTable.ops) {
641 PL_DHashTableInit(&mClassTable,
642 mQuirksMode ? &RuleHash_ClassTable_CIOps.ops
643 : &RuleHash_ClassTable_CSOps.ops,
644 nullptr, sizeof(RuleHashTableEntry));
646 AppendRuleToTable(&mClassTable, selector->mClassList->mAtom, aRuleInfo);
647 RULE_HASH_STAT_INCREMENT(mClassSelectors);
649 else if (selector->mLowercaseTag) {
650 RuleValue ruleValue(aRuleInfo, mRuleCount++, mQuirksMode);
651 if (!mTagTable.ops) {
652 PL_DHashTableInit(&mTagTable, &RuleHash_TagTable_Ops, nullptr,
653 sizeof(RuleHashTagTableEntry));
655 AppendRuleToTagTable(&mTagTable, selector->mLowercaseTag, ruleValue);
656 RULE_HASH_STAT_INCREMENT(mTagSelectors);
657 if (selector->mCasedTag &&
658 selector->mCasedTag != selector->mLowercaseTag) {
659 AppendRuleToTagTable(&mTagTable, selector->mCasedTag, ruleValue);
660 RULE_HASH_STAT_INCREMENT(mTagSelectors);
663 else if (kNameSpaceID_Unknown != selector->mNameSpace) {
664 if (!mNameSpaceTable.ops) {
665 PL_DHashTableInit(&mNameSpaceTable, &RuleHash_NameSpaceTable_Ops, nullptr,
666 sizeof(RuleHashTableEntry));
668 AppendRuleToTable(&mNameSpaceTable,
669 NS_INT32_TO_PTR(selector->mNameSpace), aRuleInfo);
670 RULE_HASH_STAT_INCREMENT(mNameSpaceSelectors);
672 else { // universal tag selector
673 AppendUniversalRule(aRuleInfo);
674 RULE_HASH_STAT_INCREMENT(mUniversalSelectors);
678 // this should cover practically all cases so we don't need to reallocate
679 #define MIN_ENUM_LIST_SIZE 8
681 #ifdef RULE_HASH_STATS
682 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
683 (var_) += (list_).Length()
684 #else
685 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
686 PR_BEGIN_MACRO PR_END_MACRO
687 #endif
689 static inline
690 void ContentEnumFunc(const RuleValue &value, nsCSSSelector* selector,
691 ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
692 AncestorFilter *ancestorFilter);
694 void RuleHash::EnumerateAllRules(Element* aElement, ElementDependentRuleProcessorData* aData,
695 NodeMatchContext& aNodeContext)
697 int32_t nameSpace = aElement->GetNameSpaceID();
698 nsIAtom* tag = aElement->Tag();
699 nsIAtom* id = aElement->GetID();
700 const nsAttrValue* classList = aElement->GetClasses();
702 NS_ABORT_IF_FALSE(tag, "How could we not have a tag?");
704 int32_t classCount = classList ? classList->GetAtomCount() : 0;
706 // assume 1 universal, tag, id, and namespace, rather than wasting
707 // time counting
708 int32_t testCount = classCount + 4;
710 if (mEnumListSize < testCount) {
711 delete [] mEnumList;
712 mEnumListSize = std::max(testCount, MIN_ENUM_LIST_SIZE);
713 mEnumList = new EnumData[mEnumListSize];
716 int32_t valueCount = 0;
717 RULE_HASH_STAT_INCREMENT(mElementsMatched);
719 if (mUniversalRules.Length() != 0) { // universal rules
720 mEnumList[valueCount++] = ToEnumData(mUniversalRules);
721 RULE_HASH_STAT_INCREMENT_LIST_COUNT(mUniversalRules, mElementUniversalCalls);
723 // universal rules within the namespace
724 if (kNameSpaceID_Unknown != nameSpace && mNameSpaceTable.ops) {
725 RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
726 (PL_DHashTableOperate(&mNameSpaceTable, NS_INT32_TO_PTR(nameSpace),
727 PL_DHASH_LOOKUP));
728 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
729 mEnumList[valueCount++] = ToEnumData(entry->mRules);
730 RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementNameSpaceCalls);
733 if (mTagTable.ops) {
734 RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
735 (PL_DHashTableOperate(&mTagTable, tag, PL_DHASH_LOOKUP));
736 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
737 mEnumList[valueCount++] = ToEnumData(entry->mRules);
738 RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementTagCalls);
741 if (id && mIdTable.ops) {
742 RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
743 (PL_DHashTableOperate(&mIdTable, id, PL_DHASH_LOOKUP));
744 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
745 mEnumList[valueCount++] = ToEnumData(entry->mRules);
746 RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementIdCalls);
749 if (mClassTable.ops) {
750 for (int32_t index = 0; index < classCount; ++index) {
751 RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
752 (PL_DHashTableOperate(&mClassTable, classList->AtomAt(index),
753 PL_DHASH_LOOKUP));
754 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
755 mEnumList[valueCount++] = ToEnumData(entry->mRules);
756 RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementClassCalls);
760 NS_ASSERTION(valueCount <= testCount, "values exceeded list size");
762 if (valueCount > 0) {
763 AncestorFilter *filter =
764 aData->mTreeMatchContext.mAncestorFilter.HasFilter() ?
765 &aData->mTreeMatchContext.mAncestorFilter : nullptr;
766 #ifdef DEBUG
767 if (filter) {
768 filter->AssertHasAllAncestors(aElement);
770 #endif
771 // Merge the lists while there are still multiple lists to merge.
772 while (valueCount > 1) {
773 int32_t valueIndex = 0;
774 int32_t lowestRuleIndex = mEnumList[valueIndex].mCurValue->mIndex;
775 for (int32_t index = 1; index < valueCount; ++index) {
776 int32_t ruleIndex = mEnumList[index].mCurValue->mIndex;
777 if (ruleIndex < lowestRuleIndex) {
778 valueIndex = index;
779 lowestRuleIndex = ruleIndex;
782 const RuleValue *cur = mEnumList[valueIndex].mCurValue;
783 ContentEnumFunc(*cur, cur->mSelector, aData, aNodeContext, filter);
784 cur++;
785 if (cur == mEnumList[valueIndex].mEnd) {
786 mEnumList[valueIndex] = mEnumList[--valueCount];
787 } else {
788 mEnumList[valueIndex].mCurValue = cur;
792 // Fast loop over single value.
793 for (const RuleValue *value = mEnumList[0].mCurValue,
794 *end = mEnumList[0].mEnd;
795 value != end; ++value) {
796 ContentEnumFunc(*value, value->mSelector, aData, aNodeContext, filter);
801 static size_t
802 SizeOfRuleHashTableEntry(PLDHashEntryHdr* aHdr, MallocSizeOf aMallocSizeOf, void *)
804 RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(aHdr);
805 return entry->mRules.SizeOfExcludingThis(aMallocSizeOf);
808 size_t
809 RuleHash::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
811 size_t n = 0;
813 if (mIdTable.ops) {
814 n += PL_DHashTableSizeOfExcludingThis(&mIdTable,
815 SizeOfRuleHashTableEntry,
816 aMallocSizeOf);
819 if (mClassTable.ops) {
820 n += PL_DHashTableSizeOfExcludingThis(&mClassTable,
821 SizeOfRuleHashTableEntry,
822 aMallocSizeOf);
825 if (mTagTable.ops) {
826 n += PL_DHashTableSizeOfExcludingThis(&mTagTable,
827 SizeOfRuleHashTableEntry,
828 aMallocSizeOf);
831 if (mNameSpaceTable.ops) {
832 n += PL_DHashTableSizeOfExcludingThis(&mNameSpaceTable,
833 SizeOfRuleHashTableEntry,
834 aMallocSizeOf);
837 n += mUniversalRules.SizeOfExcludingThis(aMallocSizeOf);
839 return n;
842 size_t
843 RuleHash::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
845 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
848 //--------------------------------
850 // A hash table mapping atoms to lists of selectors
851 struct AtomSelectorEntry : public PLDHashEntryHdr {
852 nsIAtom *mAtom;
853 // Auto length 2, because a decent fraction of these arrays ends up
854 // with 2 elements, and each entry is cheap.
855 nsAutoTArray<nsCSSSelector*, 2> mSelectors;
858 static void
859 AtomSelector_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
861 (static_cast<AtomSelectorEntry*>(hdr))->~AtomSelectorEntry();
864 static bool
865 AtomSelector_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
866 const void *key)
868 AtomSelectorEntry *entry = static_cast<AtomSelectorEntry*>(hdr);
869 new (entry) AtomSelectorEntry();
870 entry->mAtom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
871 return true;
874 static void
875 AtomSelector_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
876 PLDHashEntryHdr *to)
878 NS_PRECONDITION(from != to, "This is not going to work!");
879 AtomSelectorEntry *oldEntry =
880 const_cast<AtomSelectorEntry*>(static_cast<const AtomSelectorEntry*>(from));
881 AtomSelectorEntry *newEntry = new (to) AtomSelectorEntry();
882 newEntry->mAtom = oldEntry->mAtom;
883 newEntry->mSelectors.SwapElements(oldEntry->mSelectors);
884 oldEntry->~AtomSelectorEntry();
887 static nsIAtom*
888 AtomSelector_GetKey(PLDHashTable *table, const PLDHashEntryHdr *hdr)
890 const AtomSelectorEntry *entry = static_cast<const AtomSelectorEntry*>(hdr);
891 return entry->mAtom;
894 // Case-sensitive ops.
895 static const PLDHashTableOps AtomSelector_CSOps = {
896 PL_DHashAllocTable,
897 PL_DHashFreeTable,
898 PL_DHashVoidPtrKeyStub,
899 PL_DHashMatchEntryStub,
900 AtomSelector_MoveEntry,
901 AtomSelector_ClearEntry,
902 PL_DHashFinalizeStub,
903 AtomSelector_InitEntry
906 // Case-insensitive ops.
907 static const RuleHashTableOps AtomSelector_CIOps = {
909 PL_DHashAllocTable,
910 PL_DHashFreeTable,
911 RuleHash_CIHashKey,
912 RuleHash_CIMatchEntry,
913 AtomSelector_MoveEntry,
914 AtomSelector_ClearEntry,
915 PL_DHashFinalizeStub,
916 AtomSelector_InitEntry
918 AtomSelector_GetKey
921 //--------------------------------
923 struct RuleCascadeData {
924 RuleCascadeData(nsIAtom *aMedium, bool aQuirksMode)
925 : mRuleHash(aQuirksMode),
926 mStateSelectors(),
927 mSelectorDocumentStates(0),
928 mKeyframesRuleTable(),
929 mCounterStyleRuleTable(),
930 mCacheKey(aMedium),
931 mNext(nullptr),
932 mQuirksMode(aQuirksMode)
934 // mAttributeSelectors is matching on the attribute _name_, not the value,
935 // and we case-fold names at parse-time, so this is a case-sensitive match.
936 PL_DHashTableInit(&mAttributeSelectors, &AtomSelector_CSOps, nullptr,
937 sizeof(AtomSelectorEntry));
938 PL_DHashTableInit(&mAnonBoxRules, &RuleHash_TagTable_Ops, nullptr,
939 sizeof(RuleHashTagTableEntry));
940 PL_DHashTableInit(&mIdSelectors,
941 aQuirksMode ? &AtomSelector_CIOps.ops :
942 &AtomSelector_CSOps,
943 nullptr, sizeof(AtomSelectorEntry));
944 PL_DHashTableInit(&mClassSelectors,
945 aQuirksMode ? &AtomSelector_CIOps.ops :
946 &AtomSelector_CSOps,
947 nullptr, sizeof(AtomSelectorEntry));
948 memset(mPseudoElementRuleHashes, 0, sizeof(mPseudoElementRuleHashes));
949 #ifdef MOZ_XUL
950 PL_DHashTableInit(&mXULTreeRules, &RuleHash_TagTable_Ops, nullptr,
951 sizeof(RuleHashTagTableEntry));
952 #endif
955 ~RuleCascadeData()
957 PL_DHashTableFinish(&mAttributeSelectors);
958 PL_DHashTableFinish(&mAnonBoxRules);
959 PL_DHashTableFinish(&mIdSelectors);
960 PL_DHashTableFinish(&mClassSelectors);
961 #ifdef MOZ_XUL
962 PL_DHashTableFinish(&mXULTreeRules);
963 #endif
964 for (uint32_t i = 0; i < ArrayLength(mPseudoElementRuleHashes); ++i) {
965 delete mPseudoElementRuleHashes[i];
969 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
971 RuleHash mRuleHash;
972 RuleHash*
973 mPseudoElementRuleHashes[nsCSSPseudoElements::ePseudo_PseudoElementCount];
974 nsTArray<nsCSSRuleProcessor::StateSelector> mStateSelectors;
975 EventStates mSelectorDocumentStates;
976 PLDHashTable mClassSelectors;
977 PLDHashTable mIdSelectors;
978 nsTArray<nsCSSSelector*> mPossiblyNegatedClassSelectors;
979 nsTArray<nsCSSSelector*> mPossiblyNegatedIDSelectors;
980 PLDHashTable mAttributeSelectors;
981 PLDHashTable mAnonBoxRules;
982 #ifdef MOZ_XUL
983 PLDHashTable mXULTreeRules;
984 #endif
986 nsTArray<nsFontFaceRuleContainer> mFontFaceRules;
987 nsTArray<nsCSSKeyframesRule*> mKeyframesRules;
988 nsTArray<nsCSSFontFeatureValuesRule*> mFontFeatureValuesRules;
989 nsTArray<nsCSSPageRule*> mPageRules;
990 nsTArray<nsCSSCounterStyleRule*> mCounterStyleRules;
992 nsDataHashtable<nsStringHashKey, nsCSSKeyframesRule*> mKeyframesRuleTable;
993 nsDataHashtable<nsStringHashKey, nsCSSCounterStyleRule*> mCounterStyleRuleTable;
995 // Looks up or creates the appropriate list in |mAttributeSelectors|.
996 // Returns null only on allocation failure.
997 nsTArray<nsCSSSelector*>* AttributeListFor(nsIAtom* aAttribute);
999 nsMediaQueryResultCacheKey mCacheKey;
1000 RuleCascadeData* mNext; // for a different medium
1002 const bool mQuirksMode;
1005 static size_t
1006 SizeOfSelectorsEntry(PLDHashEntryHdr* aHdr, MallocSizeOf aMallocSizeOf, void *)
1008 AtomSelectorEntry* entry = static_cast<AtomSelectorEntry*>(aHdr);
1009 return entry->mSelectors.SizeOfExcludingThis(aMallocSizeOf);
1012 static size_t
1013 SizeOfKeyframesRuleEntryExcludingThis(nsStringHashKey::KeyType aKey,
1014 nsCSSKeyframesRule* const& aData,
1015 mozilla::MallocSizeOf aMallocSizeOf,
1016 void* aUserArg)
1018 // We don't own the nsCSSKeyframesRule objects so we don't count them. We do
1019 // care about the size of the keys' nsAString members' buffers though.
1021 // Note that we depend on nsStringHashKey::GetKey() returning a reference,
1022 // since otherwise aKey would be a copy of the string key and we would not be
1023 // measuring the right object here.
1025 return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1028 size_t
1029 RuleCascadeData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
1031 size_t n = aMallocSizeOf(this);
1033 n += mRuleHash.SizeOfExcludingThis(aMallocSizeOf);
1034 for (uint32_t i = 0; i < ArrayLength(mPseudoElementRuleHashes); ++i) {
1035 if (mPseudoElementRuleHashes[i])
1036 n += mPseudoElementRuleHashes[i]->SizeOfIncludingThis(aMallocSizeOf);
1039 n += mStateSelectors.SizeOfExcludingThis(aMallocSizeOf);
1041 n += PL_DHashTableSizeOfExcludingThis(&mIdSelectors,
1042 SizeOfSelectorsEntry, aMallocSizeOf);
1043 n += PL_DHashTableSizeOfExcludingThis(&mClassSelectors,
1044 SizeOfSelectorsEntry, aMallocSizeOf);
1046 n += mPossiblyNegatedClassSelectors.SizeOfExcludingThis(aMallocSizeOf);
1047 n += mPossiblyNegatedIDSelectors.SizeOfExcludingThis(aMallocSizeOf);
1049 n += PL_DHashTableSizeOfExcludingThis(&mAttributeSelectors,
1050 SizeOfSelectorsEntry, aMallocSizeOf);
1051 n += PL_DHashTableSizeOfExcludingThis(&mAnonBoxRules,
1052 SizeOfRuleHashTableEntry, aMallocSizeOf);
1053 #ifdef MOZ_XUL
1054 n += PL_DHashTableSizeOfExcludingThis(&mXULTreeRules,
1055 SizeOfRuleHashTableEntry, aMallocSizeOf);
1056 #endif
1058 n += mFontFaceRules.SizeOfExcludingThis(aMallocSizeOf);
1059 n += mKeyframesRules.SizeOfExcludingThis(aMallocSizeOf);
1060 n += mFontFeatureValuesRules.SizeOfExcludingThis(aMallocSizeOf);
1061 n += mPageRules.SizeOfExcludingThis(aMallocSizeOf);
1062 n += mCounterStyleRules.SizeOfExcludingThis(aMallocSizeOf);
1063 n += mKeyframesRuleTable.SizeOfExcludingThis(SizeOfKeyframesRuleEntryExcludingThis,
1064 aMallocSizeOf,
1065 nullptr);
1067 return n;
1070 nsTArray<nsCSSSelector*>*
1071 RuleCascadeData::AttributeListFor(nsIAtom* aAttribute)
1073 AtomSelectorEntry *entry =
1074 static_cast<AtomSelectorEntry*>
1075 (PL_DHashTableOperate(&mAttributeSelectors, aAttribute,
1076 PL_DHASH_ADD));
1077 if (!entry)
1078 return nullptr;
1079 return &entry->mSelectors;
1082 // -------------------------------
1083 // CSS Style rule processor implementation
1086 nsCSSRuleProcessor::nsCSSRuleProcessor(const sheet_array_type& aSheets,
1087 uint8_t aSheetType,
1088 Element* aScopeElement)
1089 : mSheets(aSheets)
1090 , mRuleCascades(nullptr)
1091 , mLastPresContext(nullptr)
1092 , mScopeElement(aScopeElement)
1093 , mSheetType(aSheetType)
1095 NS_ASSERTION(!!mScopeElement == (aSheetType == nsStyleSet::eScopedDocSheet),
1096 "aScopeElement must be specified iff aSheetType is "
1097 "eScopedDocSheet");
1098 for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) {
1099 mSheets[i]->AddRuleProcessor(this);
1103 nsCSSRuleProcessor::~nsCSSRuleProcessor()
1105 ClearSheets();
1106 ClearRuleCascades();
1109 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSRuleProcessor)
1110 NS_INTERFACE_MAP_ENTRY(nsIStyleRuleProcessor)
1111 NS_INTERFACE_MAP_END
1113 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSRuleProcessor)
1114 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSRuleProcessor)
1116 NS_IMPL_CYCLE_COLLECTION_CLASS(nsCSSRuleProcessor)
1118 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCSSRuleProcessor)
1119 tmp->ClearSheets();
1120 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScopeElement)
1121 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1123 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCSSRuleProcessor)
1124 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSheets)
1125 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScopeElement)
1126 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1128 void
1129 nsCSSRuleProcessor::ClearSheets()
1131 for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) {
1132 mSheets[i]->DropRuleProcessor(this);
1134 mSheets.Clear();
1137 /* static */ nsresult
1138 nsCSSRuleProcessor::Startup()
1140 Preferences::AddBoolVarCache(&gSupportVisitedPseudo, VISITED_PSEUDO_PREF,
1141 true);
1143 return NS_OK;
1146 static bool
1147 InitSystemMetrics()
1149 NS_ASSERTION(!sSystemMetrics, "already initialized");
1151 sSystemMetrics = new nsTArray< nsCOMPtr<nsIAtom> >;
1152 NS_ENSURE_TRUE(sSystemMetrics, false);
1154 /***************************************************************************
1155 * ANY METRICS ADDED HERE SHOULD ALSO BE ADDED AS MEDIA QUERIES IN *
1156 * nsMediaFeatures.cpp *
1157 ***************************************************************************/
1159 int32_t metricResult =
1160 LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollArrowStyle);
1161 if (metricResult & LookAndFeel::eScrollArrow_StartBackward) {
1162 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_start_backward);
1164 if (metricResult & LookAndFeel::eScrollArrow_StartForward) {
1165 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_start_forward);
1167 if (metricResult & LookAndFeel::eScrollArrow_EndBackward) {
1168 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_end_backward);
1170 if (metricResult & LookAndFeel::eScrollArrow_EndForward) {
1171 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_end_forward);
1174 metricResult =
1175 LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollSliderStyle);
1176 if (metricResult != LookAndFeel::eScrollThumbStyle_Normal) {
1177 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_thumb_proportional);
1180 metricResult =
1181 LookAndFeel::GetInt(LookAndFeel::eIntID_ImagesInMenus);
1182 if (metricResult) {
1183 sSystemMetrics->AppendElement(nsGkAtoms::images_in_menus);
1186 metricResult =
1187 LookAndFeel::GetInt(LookAndFeel::eIntID_ImagesInButtons);
1188 if (metricResult) {
1189 sSystemMetrics->AppendElement(nsGkAtoms::images_in_buttons);
1192 metricResult =
1193 LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars);
1194 if (metricResult) {
1195 sSystemMetrics->AppendElement(nsGkAtoms::overlay_scrollbars);
1198 metricResult =
1199 LookAndFeel::GetInt(LookAndFeel::eIntID_MenuBarDrag);
1200 if (metricResult) {
1201 sSystemMetrics->AppendElement(nsGkAtoms::menubar_drag);
1204 nsresult rv =
1205 LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsDefaultTheme, &metricResult);
1206 if (NS_SUCCEEDED(rv) && metricResult) {
1207 sSystemMetrics->AppendElement(nsGkAtoms::windows_default_theme);
1210 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacGraphiteTheme, &metricResult);
1211 if (NS_SUCCEEDED(rv) && metricResult) {
1212 sSystemMetrics->AppendElement(nsGkAtoms::mac_graphite_theme);
1215 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacLionTheme, &metricResult);
1216 if (NS_SUCCEEDED(rv) && metricResult) {
1217 sSystemMetrics->AppendElement(nsGkAtoms::mac_lion_theme);
1220 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacYosemiteTheme, &metricResult);
1221 if (NS_SUCCEEDED(rv) && metricResult) {
1222 sSystemMetrics->AppendElement(nsGkAtoms::mac_yosemite_theme);
1225 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_DWMCompositor, &metricResult);
1226 if (NS_SUCCEEDED(rv) && metricResult) {
1227 sSystemMetrics->AppendElement(nsGkAtoms::windows_compositor);
1230 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsGlass, &metricResult);
1231 if (NS_SUCCEEDED(rv) && metricResult) {
1232 sSystemMetrics->AppendElement(nsGkAtoms::windows_glass);
1235 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_ColorPickerAvailable, &metricResult);
1236 if (NS_SUCCEEDED(rv) && metricResult) {
1237 sSystemMetrics->AppendElement(nsGkAtoms::color_picker_available);
1240 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsClassic, &metricResult);
1241 if (NS_SUCCEEDED(rv) && metricResult) {
1242 sSystemMetrics->AppendElement(nsGkAtoms::windows_classic);
1245 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_TouchEnabled, &metricResult);
1246 if (NS_SUCCEEDED(rv) && metricResult) {
1247 sSystemMetrics->AppendElement(nsGkAtoms::touch_enabled);
1250 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_SwipeAnimationEnabled,
1251 &metricResult);
1252 if (NS_SUCCEEDED(rv) && metricResult) {
1253 sSystemMetrics->AppendElement(nsGkAtoms::swipe_animation_enabled);
1256 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_PhysicalHomeButton,
1257 &metricResult);
1258 if (NS_SUCCEEDED(rv) && metricResult) {
1259 sSystemMetrics->AppendElement(nsGkAtoms::physical_home_button);
1262 #ifdef XP_WIN
1263 if (NS_SUCCEEDED(
1264 LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsThemeIdentifier,
1265 &metricResult))) {
1266 nsCSSRuleProcessor::SetWindowsThemeIdentifier(static_cast<uint8_t>(metricResult));
1267 switch(metricResult) {
1268 case LookAndFeel::eWindowsTheme_Aero:
1269 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero);
1270 break;
1271 case LookAndFeel::eWindowsTheme_AeroLite:
1272 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero_lite);
1273 break;
1274 case LookAndFeel::eWindowsTheme_LunaBlue:
1275 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_blue);
1276 break;
1277 case LookAndFeel::eWindowsTheme_LunaOlive:
1278 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_olive);
1279 break;
1280 case LookAndFeel::eWindowsTheme_LunaSilver:
1281 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_silver);
1282 break;
1283 case LookAndFeel::eWindowsTheme_Royale:
1284 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_royale);
1285 break;
1286 case LookAndFeel::eWindowsTheme_Zune:
1287 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_zune);
1288 break;
1289 case LookAndFeel::eWindowsTheme_Generic:
1290 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_generic);
1291 break;
1294 #endif
1296 return true;
1299 /* static */ void
1300 nsCSSRuleProcessor::FreeSystemMetrics()
1302 delete sSystemMetrics;
1303 sSystemMetrics = nullptr;
1306 /* static */ void
1307 nsCSSRuleProcessor::Shutdown()
1309 FreeSystemMetrics();
1312 /* static */ bool
1313 nsCSSRuleProcessor::HasSystemMetric(nsIAtom* aMetric)
1315 if (!sSystemMetrics && !InitSystemMetrics()) {
1316 return false;
1318 return sSystemMetrics->IndexOf(aMetric) != sSystemMetrics->NoIndex;
1321 #ifdef XP_WIN
1322 /* static */ uint8_t
1323 nsCSSRuleProcessor::GetWindowsThemeIdentifier()
1325 if (!sSystemMetrics)
1326 InitSystemMetrics();
1327 return sWinThemeId;
1329 #endif
1331 /* static */
1332 EventStates
1333 nsCSSRuleProcessor::GetContentState(Element* aElement, const TreeMatchContext& aTreeMatchContext)
1335 EventStates state = aElement->StyleState();
1337 // If we are not supposed to mark visited links as such, be sure to
1338 // flip the bits appropriately. We want to do this here, rather
1339 // than in GetContentStateForVisitedHandling, so that we don't
1340 // expose that :visited support is disabled to the Web page.
1341 if (state.HasState(NS_EVENT_STATE_VISITED) &&
1342 (!gSupportVisitedPseudo ||
1343 aElement->OwnerDoc()->IsBeingUsedAsImage() ||
1344 aTreeMatchContext.mUsingPrivateBrowsing)) {
1345 state &= ~NS_EVENT_STATE_VISITED;
1346 state |= NS_EVENT_STATE_UNVISITED;
1348 return state;
1351 /* static */
1352 bool
1353 nsCSSRuleProcessor::IsLink(Element* aElement)
1355 EventStates state = aElement->StyleState();
1356 return state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED);
1359 /* static */
1360 EventStates
1361 nsCSSRuleProcessor::GetContentStateForVisitedHandling(
1362 Element* aElement,
1363 const TreeMatchContext& aTreeMatchContext,
1364 nsRuleWalker::VisitedHandlingType aVisitedHandling,
1365 bool aIsRelevantLink)
1367 EventStates contentState = GetContentState(aElement, aTreeMatchContext);
1368 if (contentState.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)) {
1369 NS_ABORT_IF_FALSE(IsLink(aElement), "IsLink() should match state");
1370 contentState &= ~(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED);
1371 if (aIsRelevantLink) {
1372 switch (aVisitedHandling) {
1373 case nsRuleWalker::eRelevantLinkUnvisited:
1374 contentState |= NS_EVENT_STATE_UNVISITED;
1375 break;
1376 case nsRuleWalker::eRelevantLinkVisited:
1377 contentState |= NS_EVENT_STATE_VISITED;
1378 break;
1379 case nsRuleWalker::eLinksVisitedOrUnvisited:
1380 contentState |= NS_EVENT_STATE_UNVISITED | NS_EVENT_STATE_VISITED;
1381 break;
1383 } else {
1384 contentState |= NS_EVENT_STATE_UNVISITED;
1387 return contentState;
1391 * A |NodeMatchContext| has data about matching a selector (without
1392 * combinators) against a single node. It contains only input to the
1393 * matching.
1395 * Unlike |RuleProcessorData|, which is similar, a |NodeMatchContext|
1396 * can vary depending on the selector matching process. In other words,
1397 * there might be multiple NodeMatchContexts corresponding to a single
1398 * node, but only one possible RuleProcessorData.
1400 struct NodeMatchContext {
1401 // In order to implement nsCSSRuleProcessor::HasStateDependentStyle,
1402 // we need to be able to see if a node might match an
1403 // event-state-dependent selector for any value of that event state.
1404 // So mStateMask contains the states that should NOT be tested.
1406 // NOTE: For |mStateMask| to work correctly, it's important that any
1407 // change that changes multiple state bits include all those state
1408 // bits in the notification. Otherwise, if multiple states change but
1409 // we do separate notifications then we might determine the style is
1410 // not state-dependent when it really is (e.g., determining that a
1411 // :hover:active rule no longer matches when both states are unset).
1412 const EventStates mStateMask;
1414 // Is this link the unique link whose visitedness can affect the style
1415 // of the node being matched? (That link is the nearest link to the
1416 // node being matched that is itself or an ancestor.)
1418 // Always false when TreeMatchContext::mForStyling is false. (We
1419 // could figure it out for SelectorListMatches, but we're starting
1420 // from the middle of the selector list when doing
1421 // Has{Attribute,State}DependentStyle, so we can't tell. So when
1422 // mForStyling is false, we have to assume we don't know.)
1423 const bool mIsRelevantLink;
1425 NodeMatchContext(EventStates aStateMask, bool aIsRelevantLink)
1426 : mStateMask(aStateMask)
1427 , mIsRelevantLink(aIsRelevantLink)
1432 static bool ValueIncludes(const nsSubstring& aValueList,
1433 const nsSubstring& aValue,
1434 const nsStringComparator& aComparator)
1436 const char16_t *p = aValueList.BeginReading(),
1437 *p_end = aValueList.EndReading();
1439 while (p < p_end) {
1440 // skip leading space
1441 while (p != p_end && nsContentUtils::IsHTMLWhitespace(*p))
1442 ++p;
1444 const char16_t *val_start = p;
1446 // look for space or end
1447 while (p != p_end && !nsContentUtils::IsHTMLWhitespace(*p))
1448 ++p;
1450 const char16_t *val_end = p;
1452 if (val_start < val_end &&
1453 aValue.Equals(Substring(val_start, val_end), aComparator))
1454 return true;
1456 ++p; // we know the next character is not whitespace
1458 return false;
1461 // Return whether we should apply a "global" (i.e., universal-tag)
1462 // selector for event states in quirks mode. Note that
1463 // |IsLink()| is checked separately by the caller, so we return
1464 // false for |nsGkAtoms::a|, which here means a named anchor.
1465 inline bool IsQuirkEventSensitive(nsIAtom *aContentTag)
1467 return bool ((nsGkAtoms::button == aContentTag) ||
1468 (nsGkAtoms::img == aContentTag) ||
1469 (nsGkAtoms::input == aContentTag) ||
1470 (nsGkAtoms::label == aContentTag) ||
1471 (nsGkAtoms::select == aContentTag) ||
1472 (nsGkAtoms::textarea == aContentTag));
1476 static inline bool
1477 IsSignificantChild(nsIContent* aChild, bool aTextIsSignificant,
1478 bool aWhitespaceIsSignificant)
1480 return nsStyleUtil::IsSignificantChild(aChild, aTextIsSignificant,
1481 aWhitespaceIsSignificant);
1484 // This function is to be called once we have fetched a value for an attribute
1485 // whose namespace and name match those of aAttrSelector. This function
1486 // performs comparisons on the value only, based on aAttrSelector->mFunction.
1487 static bool AttrMatchesValue(const nsAttrSelector* aAttrSelector,
1488 const nsString& aValue, bool isHTML)
1490 NS_PRECONDITION(aAttrSelector, "Must have an attribute selector");
1492 // http://lists.w3.org/Archives/Public/www-style/2008Apr/0038.html
1493 // *= (CONTAINSMATCH) ~= (INCLUDES) ^= (BEGINSMATCH) $= (ENDSMATCH)
1494 // all accept the empty string, but match nothing.
1495 if (aAttrSelector->mValue.IsEmpty() &&
1496 (aAttrSelector->mFunction == NS_ATTR_FUNC_INCLUDES ||
1497 aAttrSelector->mFunction == NS_ATTR_FUNC_ENDSMATCH ||
1498 aAttrSelector->mFunction == NS_ATTR_FUNC_BEGINSMATCH ||
1499 aAttrSelector->mFunction == NS_ATTR_FUNC_CONTAINSMATCH))
1500 return false;
1502 const nsDefaultStringComparator defaultComparator;
1503 const nsASCIICaseInsensitiveStringComparator ciComparator;
1504 const nsStringComparator& comparator =
1505 (aAttrSelector->mCaseSensitive || !isHTML)
1506 ? static_cast<const nsStringComparator&>(defaultComparator)
1507 : static_cast<const nsStringComparator&>(ciComparator);
1509 switch (aAttrSelector->mFunction) {
1510 case NS_ATTR_FUNC_EQUALS:
1511 return aValue.Equals(aAttrSelector->mValue, comparator);
1512 case NS_ATTR_FUNC_INCLUDES:
1513 return ValueIncludes(aValue, aAttrSelector->mValue, comparator);
1514 case NS_ATTR_FUNC_DASHMATCH:
1515 return nsStyleUtil::DashMatchCompare(aValue, aAttrSelector->mValue, comparator);
1516 case NS_ATTR_FUNC_ENDSMATCH:
1517 return StringEndsWith(aValue, aAttrSelector->mValue, comparator);
1518 case NS_ATTR_FUNC_BEGINSMATCH:
1519 return StringBeginsWith(aValue, aAttrSelector->mValue, comparator);
1520 case NS_ATTR_FUNC_CONTAINSMATCH:
1521 return FindInReadable(aAttrSelector->mValue, aValue, comparator);
1522 default:
1523 NS_NOTREACHED("Shouldn't be ending up here");
1524 return false;
1528 static inline bool
1529 edgeChildMatches(Element* aElement, TreeMatchContext& aTreeMatchContext,
1530 bool checkFirst, bool checkLast)
1532 nsIContent *parent = aElement->GetParent();
1533 if (!parent) {
1534 return false;
1537 if (aTreeMatchContext.mForStyling)
1538 parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
1540 return (!checkFirst ||
1541 aTreeMatchContext.mNthIndexCache.
1542 GetNthIndex(aElement, false, false, true) == 1) &&
1543 (!checkLast ||
1544 aTreeMatchContext.mNthIndexCache.
1545 GetNthIndex(aElement, false, true, true) == 1);
1548 static inline bool
1549 nthChildGenericMatches(Element* aElement,
1550 TreeMatchContext& aTreeMatchContext,
1551 nsPseudoClassList* pseudoClass,
1552 bool isOfType, bool isFromEnd)
1554 nsIContent *parent = aElement->GetParent();
1555 if (!parent) {
1556 return false;
1559 if (aTreeMatchContext.mForStyling) {
1560 if (isFromEnd)
1561 parent->SetFlags(NODE_HAS_SLOW_SELECTOR);
1562 else
1563 parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
1566 const int32_t index = aTreeMatchContext.mNthIndexCache.
1567 GetNthIndex(aElement, isOfType, isFromEnd, false);
1568 if (index <= 0) {
1569 // Node is anonymous content (not really a child of its parent).
1570 return false;
1573 const int32_t a = pseudoClass->u.mNumbers[0];
1574 const int32_t b = pseudoClass->u.mNumbers[1];
1575 // result should be true if there exists n >= 0 such that
1576 // a * n + b == index.
1577 if (a == 0) {
1578 return b == index;
1581 // Integer division in C does truncation (towards 0). So
1582 // check that the result is nonnegative, and that there was no
1583 // truncation.
1584 const int32_t n = (index - b) / a;
1585 return n >= 0 && (a * n == index - b);
1588 static inline bool
1589 edgeOfTypeMatches(Element* aElement, TreeMatchContext& aTreeMatchContext,
1590 bool checkFirst, bool checkLast)
1592 nsIContent *parent = aElement->GetParent();
1593 if (!parent) {
1594 return false;
1597 if (aTreeMatchContext.mForStyling) {
1598 if (checkLast)
1599 parent->SetFlags(NODE_HAS_SLOW_SELECTOR);
1600 else
1601 parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
1604 return (!checkFirst ||
1605 aTreeMatchContext.mNthIndexCache.
1606 GetNthIndex(aElement, true, false, true) == 1) &&
1607 (!checkLast ||
1608 aTreeMatchContext.mNthIndexCache.
1609 GetNthIndex(aElement, true, true, true) == 1);
1612 static inline bool
1613 checkGenericEmptyMatches(Element* aElement,
1614 TreeMatchContext& aTreeMatchContext,
1615 bool isWhitespaceSignificant)
1617 nsIContent *child = nullptr;
1618 int32_t index = -1;
1620 if (aTreeMatchContext.mForStyling)
1621 aElement->SetFlags(NODE_HAS_EMPTY_SELECTOR);
1623 do {
1624 child = aElement->GetChildAt(++index);
1625 // stop at first non-comment (and non-whitespace for
1626 // :-moz-only-whitespace) node
1627 } while (child && !IsSignificantChild(child, true, isWhitespaceSignificant));
1628 return (child == nullptr);
1631 // Arrays of the states that are relevant for various pseudoclasses.
1632 static const EventStates sPseudoClassStateDependences[] = {
1633 #define CSS_PSEUDO_CLASS(_name, _value, _pref) \
1634 EventStates(),
1635 #define CSS_STATE_DEPENDENT_PSEUDO_CLASS(_name, _value, _pref, _states) \
1636 _states,
1637 #include "nsCSSPseudoClassList.h"
1638 #undef CSS_STATE_DEPENDENT_PSEUDO_CLASS
1639 #undef CSS_PSEUDO_CLASS
1640 // Add more entries for our fake values to make sure we can't
1641 // index out of bounds into this array no matter what.
1642 EventStates(),
1643 EventStates()
1646 static const EventStates sPseudoClassStates[] = {
1647 #define CSS_PSEUDO_CLASS(_name, _value, _pref) \
1648 EventStates(),
1649 #define CSS_STATE_PSEUDO_CLASS(_name, _value, _pref, _states) \
1650 _states,
1651 #include "nsCSSPseudoClassList.h"
1652 #undef CSS_STATE_PSEUDO_CLASS
1653 #undef CSS_PSEUDO_CLASS
1654 // Add more entries for our fake values to make sure we can't
1655 // index out of bounds into this array no matter what.
1656 EventStates(),
1657 EventStates()
1659 static_assert(MOZ_ARRAY_LENGTH(sPseudoClassStates) ==
1660 nsCSSPseudoClasses::ePseudoClass_NotPseudoClass + 1,
1661 "ePseudoClass_NotPseudoClass is no longer at the end of"
1662 "sPseudoClassStates");
1664 static bool
1665 StateSelectorMatches(Element* aElement,
1666 nsCSSSelector* aSelector,
1667 NodeMatchContext& aNodeMatchContext,
1668 TreeMatchContext& aTreeMatchContext,
1669 bool* const aDependence,
1670 EventStates aStatesToCheck)
1672 NS_PRECONDITION(!aStatesToCheck.IsEmpty(),
1673 "should only need to call StateSelectorMatches if "
1674 "aStatesToCheck is not empty");
1676 const bool isNegated = aDependence != nullptr;
1678 // Bit-based pseudo-classes
1679 if (aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE) &&
1680 aTreeMatchContext.mCompatMode == eCompatibility_NavQuirks &&
1681 // global selector:
1682 !aSelector->HasTagSelector() && !aSelector->mIDList &&
1683 !aSelector->mClassList && !aSelector->mAttrList &&
1684 // This (or the other way around) both make :not() asymmetric
1685 // in quirks mode (and it's hard to work around since we're
1686 // testing the current mNegations, not the first
1687 // (unnegated)). This at least makes it closer to the spec.
1688 !isNegated &&
1689 // important for |IsQuirkEventSensitive|:
1690 aElement->IsHTML() && !nsCSSRuleProcessor::IsLink(aElement) &&
1691 !IsQuirkEventSensitive(aElement->Tag())) {
1692 // In quirks mode, only make certain elements sensitive to
1693 // selectors ":hover" and ":active".
1694 return false;
1697 if (aTreeMatchContext.mForStyling &&
1698 aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_HOVER)) {
1699 // Mark the element as having :hover-dependent style
1700 aElement->SetHasRelevantHoverRules();
1703 if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(aStatesToCheck)) {
1704 if (aDependence) {
1705 *aDependence = true;
1707 } else {
1708 EventStates contentState =
1709 nsCSSRuleProcessor::GetContentStateForVisitedHandling(
1710 aElement,
1711 aTreeMatchContext,
1712 aTreeMatchContext.VisitedHandling(),
1713 aNodeMatchContext.mIsRelevantLink);
1714 if (!contentState.HasAtLeastOneOfStates(aStatesToCheck)) {
1715 return false;
1719 return true;
1722 static bool
1723 StateSelectorMatches(Element* aElement,
1724 nsCSSSelector* aSelector,
1725 NodeMatchContext& aNodeMatchContext,
1726 TreeMatchContext& aTreeMatchContext)
1728 for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
1729 pseudoClass; pseudoClass = pseudoClass->mNext) {
1730 EventStates statesToCheck = sPseudoClassStates[pseudoClass->mType];
1731 if (!statesToCheck.IsEmpty() &&
1732 !StateSelectorMatches(aElement, aSelector, aNodeMatchContext,
1733 aTreeMatchContext, nullptr, statesToCheck)) {
1734 return false;
1737 return true;
1740 // |aDependence| has two functions:
1741 // * when non-null, it indicates that we're processing a negation,
1742 // which is done only when SelectorMatches calls itself recursively
1743 // * what it points to should be set to true whenever a test is skipped
1744 // because of aNodeMatchContent.mStateMask
1745 static bool SelectorMatches(Element* aElement,
1746 nsCSSSelector* aSelector,
1747 NodeMatchContext& aNodeMatchContext,
1748 TreeMatchContext& aTreeMatchContext,
1749 bool* const aDependence = nullptr)
1752 NS_PRECONDITION(!aSelector->IsPseudoElement(),
1753 "Pseudo-element snuck into SelectorMatches?");
1754 NS_ABORT_IF_FALSE(aTreeMatchContext.mForStyling ||
1755 !aNodeMatchContext.mIsRelevantLink,
1756 "mIsRelevantLink should be set to false when mForStyling "
1757 "is false since we don't know how to set it correctly in "
1758 "Has(Attribute|State)DependentStyle");
1760 // namespace/tag match
1761 // optimization : bail out early if we can
1762 if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
1763 aElement->GetNameSpaceID() != aSelector->mNameSpace))
1764 return false;
1766 if (aSelector->mLowercaseTag) {
1767 nsIAtom* selectorTag =
1768 (aTreeMatchContext.mIsHTMLDocument && aElement->IsHTML()) ?
1769 aSelector->mLowercaseTag : aSelector->mCasedTag;
1770 if (selectorTag != aElement->Tag()) {
1771 return false;
1775 nsAtomList* IDList = aSelector->mIDList;
1776 if (IDList) {
1777 nsIAtom* id = aElement->GetID();
1778 if (id) {
1779 // case sensitivity: bug 93371
1780 const bool isCaseSensitive =
1781 aTreeMatchContext.mCompatMode != eCompatibility_NavQuirks;
1783 if (isCaseSensitive) {
1784 do {
1785 if (IDList->mAtom != id) {
1786 return false;
1788 IDList = IDList->mNext;
1789 } while (IDList);
1790 } else {
1791 // Use EqualsIgnoreASCIICase instead of full on unicode case conversion
1792 // in order to save on performance. This is only used in quirks mode
1793 // anyway.
1794 nsDependentAtomString id1Str(id);
1795 do {
1796 if (!nsContentUtils::EqualsIgnoreASCIICase(id1Str,
1797 nsDependentAtomString(IDList->mAtom))) {
1798 return false;
1800 IDList = IDList->mNext;
1801 } while (IDList);
1803 } else {
1804 // Element has no id but we have an id selector
1805 return false;
1809 nsAtomList* classList = aSelector->mClassList;
1810 if (classList) {
1811 // test for class match
1812 const nsAttrValue *elementClasses = aElement->GetClasses();
1813 if (!elementClasses) {
1814 // Element has no classes but we have a class selector
1815 return false;
1818 // case sensitivity: bug 93371
1819 const bool isCaseSensitive =
1820 aTreeMatchContext.mCompatMode != eCompatibility_NavQuirks;
1822 while (classList) {
1823 if (!elementClasses->Contains(classList->mAtom,
1824 isCaseSensitive ?
1825 eCaseMatters : eIgnoreCase)) {
1826 return false;
1828 classList = classList->mNext;
1832 const bool isNegated = (aDependence != nullptr);
1833 // The selectors for which we set node bits are, unfortunately, early
1834 // in this function (because they're pseudo-classes, which are
1835 // generally quick to test, and thus earlier). If they were later,
1836 // we'd probably avoid setting those bits in more cases where setting
1837 // them is unnecessary.
1838 NS_ASSERTION(aNodeMatchContext.mStateMask.IsEmpty() ||
1839 !aTreeMatchContext.mForStyling,
1840 "mForStyling must be false if we're just testing for "
1841 "state-dependence");
1843 // test for pseudo class match
1844 for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
1845 pseudoClass; pseudoClass = pseudoClass->mNext) {
1846 EventStates statesToCheck = sPseudoClassStates[pseudoClass->mType];
1847 if (statesToCheck.IsEmpty()) {
1848 // keep the cases here in the same order as the list in
1849 // nsCSSPseudoClassList.h
1850 switch (pseudoClass->mType) {
1851 case nsCSSPseudoClasses::ePseudoClass_empty:
1852 if (!checkGenericEmptyMatches(aElement, aTreeMatchContext, true)) {
1853 return false;
1855 break;
1857 case nsCSSPseudoClasses::ePseudoClass_mozOnlyWhitespace:
1858 if (!checkGenericEmptyMatches(aElement, aTreeMatchContext, false)) {
1859 return false;
1861 break;
1863 case nsCSSPseudoClasses::ePseudoClass_mozEmptyExceptChildrenWithLocalname:
1865 NS_ASSERTION(pseudoClass->u.mString, "Must have string!");
1866 nsIContent *child = nullptr;
1867 int32_t index = -1;
1869 if (aTreeMatchContext.mForStyling)
1870 // FIXME: This isn't sufficient to handle:
1871 // :-moz-empty-except-children-with-localname() + E
1872 // :-moz-empty-except-children-with-localname() ~ E
1873 // because we don't know to restyle the grandparent of the
1874 // inserted/removed element (as in bug 534804 for :empty).
1875 aElement->SetFlags(NODE_HAS_SLOW_SELECTOR);
1876 do {
1877 child = aElement->GetChildAt(++index);
1878 } while (child &&
1879 (!IsSignificantChild(child, true, false) ||
1880 (child->GetNameSpaceID() == aElement->GetNameSpaceID() &&
1881 child->Tag()->Equals(nsDependentString(pseudoClass->u.mString)))));
1882 if (child != nullptr) {
1883 return false;
1886 break;
1888 case nsCSSPseudoClasses::ePseudoClass_lang:
1890 NS_ASSERTION(nullptr != pseudoClass->u.mString, "null lang parameter");
1891 if (!pseudoClass->u.mString || !*pseudoClass->u.mString) {
1892 return false;
1895 // We have to determine the language of the current element. Since
1896 // this is currently no property and since the language is inherited
1897 // from the parent we have to be prepared to look at all parent
1898 // nodes. The language itself is encoded in the LANG attribute.
1899 nsAutoString language;
1900 aElement->GetLang(language);
1901 if (!language.IsEmpty()) {
1902 if (!nsStyleUtil::DashMatchCompare(language,
1903 nsDependentString(pseudoClass->u.mString),
1904 nsASCIICaseInsensitiveStringComparator())) {
1905 return false;
1907 // This pseudo-class matched; move on to the next thing
1908 break;
1911 nsIDocument* doc = aTreeMatchContext.mDocument;
1912 if (doc) {
1913 // Try to get the language from the HTTP header or if this
1914 // is missing as well from the preferences.
1915 // The content language can be a comma-separated list of
1916 // language codes.
1917 doc->GetContentLanguage(language);
1919 nsDependentString langString(pseudoClass->u.mString);
1920 language.StripWhitespace();
1921 int32_t begin = 0;
1922 int32_t len = language.Length();
1923 while (begin < len) {
1924 int32_t end = language.FindChar(char16_t(','), begin);
1925 if (end == kNotFound) {
1926 end = len;
1928 if (nsStyleUtil::DashMatchCompare(Substring(language, begin,
1929 end-begin),
1930 langString,
1931 nsASCIICaseInsensitiveStringComparator())) {
1932 break;
1934 begin = end + 1;
1936 if (begin < len) {
1937 // This pseudo-class matched
1938 break;
1942 return false;
1944 break;
1946 case nsCSSPseudoClasses::ePseudoClass_mozBoundElement:
1947 if (aTreeMatchContext.mScopedRoot != aElement) {
1948 return false;
1950 break;
1952 case nsCSSPseudoClasses::ePseudoClass_root:
1953 if (aElement != aElement->OwnerDoc()->GetRootElement()) {
1954 return false;
1956 break;
1958 case nsCSSPseudoClasses::ePseudoClass_any:
1960 nsCSSSelectorList *l;
1961 for (l = pseudoClass->u.mSelectors; l; l = l->mNext) {
1962 nsCSSSelector *s = l->mSelectors;
1963 NS_ABORT_IF_FALSE(!s->mNext && !s->IsPseudoElement(),
1964 "parser failed");
1965 if (SelectorMatches(aElement, s, aNodeMatchContext,
1966 aTreeMatchContext)) {
1967 break;
1970 if (!l) {
1971 return false;
1974 break;
1976 case nsCSSPseudoClasses::ePseudoClass_firstChild:
1977 if (!edgeChildMatches(aElement, aTreeMatchContext, true, false)) {
1978 return false;
1980 break;
1982 case nsCSSPseudoClasses::ePseudoClass_firstNode:
1984 nsIContent *firstNode = nullptr;
1985 nsIContent *parent = aElement->GetParent();
1986 if (parent) {
1987 if (aTreeMatchContext.mForStyling)
1988 parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
1990 int32_t index = -1;
1991 do {
1992 firstNode = parent->GetChildAt(++index);
1993 // stop at first non-comment and non-whitespace node
1994 } while (firstNode &&
1995 !IsSignificantChild(firstNode, true, false));
1997 if (aElement != firstNode) {
1998 return false;
2001 break;
2003 case nsCSSPseudoClasses::ePseudoClass_lastChild:
2004 if (!edgeChildMatches(aElement, aTreeMatchContext, false, true)) {
2005 return false;
2007 break;
2009 case nsCSSPseudoClasses::ePseudoClass_lastNode:
2011 nsIContent *lastNode = nullptr;
2012 nsIContent *parent = aElement->GetParent();
2013 if (parent) {
2014 if (aTreeMatchContext.mForStyling)
2015 parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
2017 uint32_t index = parent->GetChildCount();
2018 do {
2019 lastNode = parent->GetChildAt(--index);
2020 // stop at first non-comment and non-whitespace node
2021 } while (lastNode &&
2022 !IsSignificantChild(lastNode, true, false));
2024 if (aElement != lastNode) {
2025 return false;
2028 break;
2030 case nsCSSPseudoClasses::ePseudoClass_onlyChild:
2031 if (!edgeChildMatches(aElement, aTreeMatchContext, true, true)) {
2032 return false;
2034 break;
2036 case nsCSSPseudoClasses::ePseudoClass_firstOfType:
2037 if (!edgeOfTypeMatches(aElement, aTreeMatchContext, true, false)) {
2038 return false;
2040 break;
2042 case nsCSSPseudoClasses::ePseudoClass_lastOfType:
2043 if (!edgeOfTypeMatches(aElement, aTreeMatchContext, false, true)) {
2044 return false;
2046 break;
2048 case nsCSSPseudoClasses::ePseudoClass_onlyOfType:
2049 if (!edgeOfTypeMatches(aElement, aTreeMatchContext, true, true)) {
2050 return false;
2052 break;
2054 case nsCSSPseudoClasses::ePseudoClass_nthChild:
2055 if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
2056 false, false)) {
2057 return false;
2059 break;
2061 case nsCSSPseudoClasses::ePseudoClass_nthLastChild:
2062 if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
2063 false, true)) {
2064 return false;
2066 break;
2068 case nsCSSPseudoClasses::ePseudoClass_nthOfType:
2069 if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
2070 true, false)) {
2071 return false;
2073 break;
2075 case nsCSSPseudoClasses::ePseudoClass_nthLastOfType:
2076 if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
2077 true, true)) {
2078 return false;
2080 break;
2082 case nsCSSPseudoClasses::ePseudoClass_mozIsHTML:
2083 if (!aTreeMatchContext.mIsHTMLDocument || !aElement->IsHTML()) {
2084 return false;
2086 break;
2088 case nsCSSPseudoClasses::ePseudoClass_mozSystemMetric:
2090 nsCOMPtr<nsIAtom> metric = do_GetAtom(pseudoClass->u.mString);
2091 if (!nsCSSRuleProcessor::HasSystemMetric(metric)) {
2092 return false;
2095 break;
2097 case nsCSSPseudoClasses::ePseudoClass_mozLocaleDir:
2099 bool docIsRTL =
2100 aTreeMatchContext.mDocument->GetDocumentState().
2101 HasState(NS_DOCUMENT_STATE_RTL_LOCALE);
2103 nsDependentString dirString(pseudoClass->u.mString);
2104 NS_ASSERTION(dirString.EqualsLiteral("ltr") ||
2105 dirString.EqualsLiteral("rtl"),
2106 "invalid value for -moz-locale-dir");
2108 if (dirString.EqualsLiteral("rtl") != docIsRTL) {
2109 return false;
2112 break;
2114 case nsCSSPseudoClasses::ePseudoClass_mozLWTheme:
2116 if (aTreeMatchContext.mDocument->GetDocumentLWTheme() <=
2117 nsIDocument::Doc_Theme_None) {
2118 return false;
2121 break;
2123 case nsCSSPseudoClasses::ePseudoClass_mozLWThemeBrightText:
2125 if (aTreeMatchContext.mDocument->GetDocumentLWTheme() !=
2126 nsIDocument::Doc_Theme_Bright) {
2127 return false;
2130 break;
2132 case nsCSSPseudoClasses::ePseudoClass_mozLWThemeDarkText:
2134 if (aTreeMatchContext.mDocument->GetDocumentLWTheme() !=
2135 nsIDocument::Doc_Theme_Dark) {
2136 return false;
2139 break;
2141 case nsCSSPseudoClasses::ePseudoClass_mozWindowInactive:
2142 if (!aTreeMatchContext.mDocument->GetDocumentState().
2143 HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
2144 return false;
2146 break;
2148 case nsCSSPseudoClasses::ePseudoClass_mozTableBorderNonzero:
2150 if (!aElement->IsHTML(nsGkAtoms::table)) {
2151 return false;
2153 const nsAttrValue *val = aElement->GetParsedAttr(nsGkAtoms::border);
2154 if (!val ||
2155 (val->Type() == nsAttrValue::eInteger &&
2156 val->GetIntegerValue() == 0)) {
2157 return false;
2160 break;
2162 case nsCSSPseudoClasses::ePseudoClass_dir:
2164 if (aDependence) {
2165 EventStates states
2166 = sPseudoClassStateDependences[pseudoClass->mType];
2167 if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(states)) {
2168 *aDependence = true;
2169 return false;
2173 // if we only had to consider HTML, directionality would be exclusively
2174 // LTR or RTL, and this could be just
2176 // if (dirString.EqualsLiteral("rtl") !=
2177 // aElement->StyleState().HasState(NS_EVENT_STATE_RTL)
2179 // However, in markup languages where there is no direction attribute
2180 // we have to consider the possibility that neither -moz-dir(rtl) nor
2181 // -moz-dir(ltr) matches.
2182 EventStates state = aElement->StyleState();
2183 bool elementIsRTL = state.HasState(NS_EVENT_STATE_RTL);
2184 bool elementIsLTR = state.HasState(NS_EVENT_STATE_LTR);
2185 nsDependentString dirString(pseudoClass->u.mString);
2187 if ((dirString.EqualsLiteral("rtl") && !elementIsRTL) ||
2188 (dirString.EqualsLiteral("ltr") && !elementIsLTR)) {
2189 return false;
2192 break;
2194 case nsCSSPseudoClasses::ePseudoClass_scope:
2195 if (aTreeMatchContext.mForScopedStyle) {
2196 if (aTreeMatchContext.mCurrentStyleScope) {
2197 // If mCurrentStyleScope is null, aElement must be the style
2198 // scope root. This is because the PopStyleScopeForSelectorMatching
2199 // call in SelectorMatchesTree sets mCurrentStyleScope to null
2200 // as soon as we visit the style scope element, and we won't
2201 // progress further up the tree after this call to
2202 // SelectorMatches. Thus if mCurrentStyleScope is still set,
2203 // we know the selector does not match.
2204 return false;
2206 } else if (aTreeMatchContext.HasSpecifiedScope()) {
2207 if (!aTreeMatchContext.IsScopeElement(aElement)) {
2208 return false;
2210 } else {
2211 if (aElement != aElement->OwnerDoc()->GetRootElement()) {
2212 return false;
2215 break;
2217 default:
2218 NS_ABORT_IF_FALSE(false, "How did that happen?");
2220 } else {
2221 if (!StateSelectorMatches(aElement, aSelector, aNodeMatchContext,
2222 aTreeMatchContext, aDependence,
2223 statesToCheck)) {
2224 return false;
2229 bool result = true;
2230 if (aSelector->mAttrList) {
2231 // test for attribute match
2232 if (!aElement->HasAttrs()) {
2233 // if no attributes on the content, no match
2234 return false;
2235 } else {
2236 result = true;
2237 nsAttrSelector* attr = aSelector->mAttrList;
2238 nsIAtom* matchAttribute;
2240 do {
2241 bool isHTML =
2242 (aTreeMatchContext.mIsHTMLDocument && aElement->IsHTML());
2243 matchAttribute = isHTML ? attr->mLowercaseAttr : attr->mCasedAttr;
2244 if (attr->mNameSpace == kNameSpaceID_Unknown) {
2245 // Attr selector with a wildcard namespace. We have to examine all
2246 // the attributes on our content node.... This sort of selector is
2247 // essentially a boolean OR, over all namespaces, of equivalent attr
2248 // selectors with those namespaces. So to evaluate whether it
2249 // matches, evaluate for each namespace (the only namespaces that
2250 // have a chance at matching, of course, are ones that the element
2251 // actually has attributes in), short-circuiting if we ever match.
2252 result = false;
2253 const nsAttrName* attrName;
2254 for (uint32_t i = 0; (attrName = aElement->GetAttrNameAt(i)); ++i) {
2255 if (attrName->LocalName() != matchAttribute) {
2256 continue;
2258 if (attr->mFunction == NS_ATTR_FUNC_SET) {
2259 result = true;
2260 } else {
2261 nsAutoString value;
2262 #ifdef DEBUG
2263 bool hasAttr =
2264 #endif
2265 aElement->GetAttr(attrName->NamespaceID(),
2266 attrName->LocalName(), value);
2267 NS_ASSERTION(hasAttr, "GetAttrNameAt lied");
2268 result = AttrMatchesValue(attr, value, isHTML);
2271 // At this point |result| has been set by us
2272 // explicitly in this loop. If it's false, we may still match
2273 // -- the content may have another attribute with the same name but
2274 // in a different namespace. But if it's true, we are done (we
2275 // can short-circuit the boolean OR described above).
2276 if (result) {
2277 break;
2281 else if (attr->mFunction == NS_ATTR_FUNC_EQUALS) {
2282 result =
2283 aElement->
2284 AttrValueIs(attr->mNameSpace, matchAttribute, attr->mValue,
2285 (!isHTML || attr->mCaseSensitive) ? eCaseMatters
2286 : eIgnoreCase);
2288 else if (!aElement->HasAttr(attr->mNameSpace, matchAttribute)) {
2289 result = false;
2291 else if (attr->mFunction != NS_ATTR_FUNC_SET) {
2292 nsAutoString value;
2293 #ifdef DEBUG
2294 bool hasAttr =
2295 #endif
2296 aElement->GetAttr(attr->mNameSpace, matchAttribute, value);
2297 NS_ASSERTION(hasAttr, "HasAttr lied");
2298 result = AttrMatchesValue(attr, value, isHTML);
2301 attr = attr->mNext;
2302 } while (attr && result);
2306 // apply SelectorMatches to the negated selectors in the chain
2307 if (!isNegated) {
2308 for (nsCSSSelector *negation = aSelector->mNegations;
2309 result && negation; negation = negation->mNegations) {
2310 bool dependence = false;
2311 result = !SelectorMatches(aElement, negation, aNodeMatchContext,
2312 aTreeMatchContext, &dependence);
2313 // If the selector does match due to the dependence on
2314 // aNodeMatchContext.mStateMask, then we want to keep result true
2315 // so that the final result of SelectorMatches is true. Doing so
2316 // tells StateEnumFunc that there is a dependence on the state.
2317 result = result || dependence;
2320 return result;
2323 #undef STATE_CHECK
2325 // Right now, there are four operators:
2326 // ' ', the descendant combinator, is greedy
2327 // '~', the indirect adjacent sibling combinator, is greedy
2328 // '+' and '>', the direct adjacent sibling and child combinators, are not
2329 #define NS_IS_GREEDY_OPERATOR(ch) \
2330 ((ch) == char16_t(' ') || (ch) == char16_t('~'))
2332 static bool SelectorMatchesTree(Element* aPrevElement,
2333 nsCSSSelector* aSelector,
2334 TreeMatchContext& aTreeMatchContext,
2335 bool aLookForRelevantLink)
2337 MOZ_ASSERT(!aSelector || !aSelector->IsPseudoElement());
2338 nsCSSSelector* selector = aSelector;
2339 Element* prevElement = aPrevElement;
2340 while (selector) { // check compound selectors
2341 NS_ASSERTION(!selector->mNext ||
2342 selector->mNext->mOperator != char16_t(0),
2343 "compound selector without combinator");
2345 // If after the previous selector match we are now outside the
2346 // current style scope, we don't need to match any further.
2347 if (aTreeMatchContext.mForScopedStyle &&
2348 !aTreeMatchContext.IsWithinStyleScopeForSelectorMatching()) {
2349 return false;
2352 // for adjacent sibling combinators, the content to test against the
2353 // selector is the previous sibling *element*
2354 Element* element = nullptr;
2355 if (char16_t('+') == selector->mOperator ||
2356 char16_t('~') == selector->mOperator) {
2357 // The relevant link must be an ancestor of the node being matched.
2358 aLookForRelevantLink = false;
2359 nsIContent* parent = prevElement->GetParent();
2360 if (parent) {
2361 if (aTreeMatchContext.mForStyling)
2362 parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
2364 element = prevElement->GetPreviousElementSibling();
2367 // for descendant combinators and child combinators, the element
2368 // to test against is the parent
2369 else {
2370 nsIContent *content = prevElement->GetParent();
2371 // GetParent could return a document fragment; we only want
2372 // element parents.
2373 if (content && content->IsElement()) {
2374 element = content->AsElement();
2375 if (aTreeMatchContext.mForScopedStyle) {
2376 // We are moving up to the parent element; tell the
2377 // TreeMatchContext, so that in case this element is the
2378 // style scope element, selector matching stops before we
2379 // traverse further up the tree.
2380 aTreeMatchContext.PopStyleScopeForSelectorMatching(element);
2383 // Compatibility hack: First try matching this selector as though the
2384 // <xbl:children> element wasn't in the tree to allow old selectors
2385 // were written before <xbl:children> participated in CSS selector
2386 // matching to work.
2387 if (selector->mOperator == '>' && element->IsActiveChildrenElement()) {
2388 Element* styleScope = aTreeMatchContext.mCurrentStyleScope;
2389 if (SelectorMatchesTree(element, selector, aTreeMatchContext,
2390 aLookForRelevantLink)) {
2391 // It matched, don't try matching on the <xbl:children> element at
2392 // all.
2393 return true;
2395 // We want to reset mCurrentStyleScope on aTreeMatchContext
2396 // back to its state before the SelectorMatchesTree call, in
2397 // case that call happens to traverse past the style scope element
2398 // and sets it to null.
2399 aTreeMatchContext.mCurrentStyleScope = styleScope;
2403 if (!element) {
2404 return false;
2406 NodeMatchContext nodeContext(EventStates(),
2407 aLookForRelevantLink &&
2408 nsCSSRuleProcessor::IsLink(element));
2409 if (nodeContext.mIsRelevantLink) {
2410 // If we find an ancestor of the matched node that is a link
2411 // during the matching process, then it's the relevant link (see
2412 // constructor call above).
2413 // Since we are still matching against selectors that contain
2414 // :visited (they'll just fail), we will always find such a node
2415 // during the selector matching process if there is a relevant
2416 // link that can influence selector matching.
2417 aLookForRelevantLink = false;
2418 aTreeMatchContext.SetHaveRelevantLink();
2420 if (SelectorMatches(element, selector, nodeContext, aTreeMatchContext)) {
2421 // to avoid greedy matching, we need to recur if this is a
2422 // descendant or general sibling combinator and the next
2423 // combinator is different, but we can make an exception for
2424 // sibling, then parent, since a sibling's parent is always the
2425 // same.
2426 if (NS_IS_GREEDY_OPERATOR(selector->mOperator) &&
2427 selector->mNext &&
2428 selector->mNext->mOperator != selector->mOperator &&
2429 !(selector->mOperator == '~' &&
2430 NS_IS_ANCESTOR_OPERATOR(selector->mNext->mOperator))) {
2432 // pretend the selector didn't match, and step through content
2433 // while testing the same selector
2435 // This approach is slightly strange in that when it recurs
2436 // it tests from the top of the content tree, down. This
2437 // doesn't matter much for performance since most selectors
2438 // don't match. (If most did, it might be faster...)
2439 Element* styleScope = aTreeMatchContext.mCurrentStyleScope;
2440 if (SelectorMatchesTree(element, selector, aTreeMatchContext,
2441 aLookForRelevantLink)) {
2442 return true;
2444 // We want to reset mCurrentStyleScope on aTreeMatchContext
2445 // back to its state before the SelectorMatchesTree call, in
2446 // case that call happens to traverse past the style scope element
2447 // and sets it to null.
2448 aTreeMatchContext.mCurrentStyleScope = styleScope;
2450 selector = selector->mNext;
2452 else {
2453 // for adjacent sibling and child combinators, if we didn't find
2454 // a match, we're done
2455 if (!NS_IS_GREEDY_OPERATOR(selector->mOperator)) {
2456 return false; // parent was required to match
2459 prevElement = element;
2461 return true; // all the selectors matched.
2464 static inline
2465 void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
2466 ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
2467 AncestorFilter *ancestorFilter)
2469 if (nodeContext.mIsRelevantLink) {
2470 data->mTreeMatchContext.SetHaveRelevantLink();
2472 if (ancestorFilter &&
2473 !ancestorFilter->MightHaveMatchingAncestor<RuleValue::eMaxAncestorHashes>(
2474 value.mAncestorSelectorHashes)) {
2475 // We won't match; nothing else to do here
2476 return;
2478 if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
2479 data->mScope)) {
2480 // The selector is for a rule in a scoped style sheet, and the subject
2481 // of the selector matching is not in its scope.
2482 return;
2484 nsCSSSelector* selector = aSelector;
2485 if (selector->IsPseudoElement()) {
2486 PseudoElementRuleProcessorData* pdata =
2487 static_cast<PseudoElementRuleProcessorData*>(data);
2488 if (!pdata->mPseudoElement && selector->mPseudoClassList) {
2489 // We can get here when calling getComputedStyle(aElt, aPseudo) if:
2491 // * aPseudo is a pseudo-element that supports a user action
2492 // pseudo-class, like "::-moz-placeholder";
2493 // * there is a style rule that uses a pseudo-class on this
2494 // pseudo-element in the document, like ::-moz-placeholder:hover; and
2495 // * aElt does not have such a pseudo-element.
2497 // We know that the selector can't match, since there is no element for
2498 // the user action pseudo-class to match against.
2499 return;
2501 if (!StateSelectorMatches(pdata->mPseudoElement, aSelector, nodeContext,
2502 data->mTreeMatchContext)) {
2503 return;
2505 selector = selector->mNext;
2507 if (SelectorMatches(data->mElement, selector, nodeContext,
2508 data->mTreeMatchContext)) {
2509 nsCSSSelector *next = selector->mNext;
2510 if (!next || SelectorMatchesTree(data->mElement, next,
2511 data->mTreeMatchContext,
2512 !nodeContext.mIsRelevantLink)) {
2513 css::StyleRule *rule = value.mRule;
2514 rule->RuleMatched();
2515 data->mRuleWalker->Forward(rule);
2516 // nsStyleSet will deal with the !important rule
2521 /* virtual */ void
2522 nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData *aData)
2524 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
2526 if (cascade) {
2527 NodeMatchContext nodeContext(EventStates(),
2528 nsCSSRuleProcessor::IsLink(aData->mElement));
2529 cascade->mRuleHash.EnumerateAllRules(aData->mElement, aData, nodeContext);
2533 /* virtual */ void
2534 nsCSSRuleProcessor::RulesMatching(PseudoElementRuleProcessorData* aData)
2536 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
2538 if (cascade) {
2539 RuleHash* ruleHash = cascade->mPseudoElementRuleHashes[aData->mPseudoType];
2540 if (ruleHash) {
2541 NodeMatchContext nodeContext(EventStates(),
2542 nsCSSRuleProcessor::IsLink(aData->mElement));
2543 ruleHash->EnumerateAllRules(aData->mElement, aData, nodeContext);
2548 /* virtual */ void
2549 nsCSSRuleProcessor::RulesMatching(AnonBoxRuleProcessorData* aData)
2551 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
2553 if (cascade && cascade->mAnonBoxRules.EntryCount()) {
2554 RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>
2555 (PL_DHashTableOperate(&cascade->mAnonBoxRules, aData->mPseudoTag,
2556 PL_DHASH_LOOKUP));
2557 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
2558 nsTArray<RuleValue>& rules = entry->mRules;
2559 for (RuleValue *value = rules.Elements(), *end = value + rules.Length();
2560 value != end; ++value) {
2561 value->mRule->RuleMatched();
2562 aData->mRuleWalker->Forward(value->mRule);
2568 #ifdef MOZ_XUL
2569 /* virtual */ void
2570 nsCSSRuleProcessor::RulesMatching(XULTreeRuleProcessorData* aData)
2572 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
2574 if (cascade && cascade->mXULTreeRules.EntryCount()) {
2575 RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>
2576 (PL_DHashTableOperate(&cascade->mXULTreeRules, aData->mPseudoTag,
2577 PL_DHASH_LOOKUP));
2578 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
2579 NodeMatchContext nodeContext(EventStates(),
2580 nsCSSRuleProcessor::IsLink(aData->mElement));
2581 nsTArray<RuleValue>& rules = entry->mRules;
2582 for (RuleValue *value = rules.Elements(), *end = value + rules.Length();
2583 value != end; ++value) {
2584 if (aData->mComparator->PseudoMatches(value->mSelector)) {
2585 ContentEnumFunc(*value, value->mSelector->mNext, aData, nodeContext,
2586 nullptr);
2592 #endif
2594 static inline nsRestyleHint RestyleHintForOp(char16_t oper)
2596 if (oper == char16_t('+') || oper == char16_t('~')) {
2597 return eRestyle_LaterSiblings;
2600 if (oper != char16_t(0)) {
2601 return eRestyle_Subtree;
2604 return eRestyle_Self;
2607 nsRestyleHint
2608 nsCSSRuleProcessor::HasStateDependentStyle(ElementDependentRuleProcessorData* aData,
2609 Element* aStatefulElement,
2610 nsCSSPseudoElements::Type aPseudoType,
2611 EventStates aStateMask)
2613 MOZ_ASSERT(!aData->mTreeMatchContext.mForScopedStyle,
2614 "mCurrentStyleScope will need to be saved and restored after the "
2615 "SelectorMatchesTree call");
2617 bool isPseudoElement =
2618 aPseudoType != nsCSSPseudoElements::ePseudo_NotPseudoElement;
2620 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
2622 // Look up the content node in the state rule list, which points to
2623 // any (CSS2 definition) simple selector (whether or not it is the
2624 // subject) that has a state pseudo-class on it. This means that this
2625 // code will be matching selectors that aren't real selectors in any
2626 // stylesheet (e.g., if there is a selector "body > p:hover > a", then
2627 // "body > p:hover" will be in |cascade->mStateSelectors|). Note that
2628 // |ComputeSelectorStateDependence| below determines which selectors are in
2629 // |cascade->mStateSelectors|.
2630 nsRestyleHint hint = nsRestyleHint(0);
2631 if (cascade) {
2632 StateSelector *iter = cascade->mStateSelectors.Elements(),
2633 *end = iter + cascade->mStateSelectors.Length();
2634 NodeMatchContext nodeContext(aStateMask, false);
2635 for(; iter != end; ++iter) {
2636 nsCSSSelector* selector = iter->mSelector;
2637 EventStates states = iter->mStates;
2639 if (selector->IsPseudoElement() != isPseudoElement) {
2640 continue;
2643 nsCSSSelector* selectorForPseudo;
2644 if (isPseudoElement) {
2645 if (selector->PseudoType() != aPseudoType) {
2646 continue;
2648 selectorForPseudo = selector;
2649 selector = selector->mNext;
2652 nsRestyleHint possibleChange = RestyleHintForOp(selector->mOperator);
2654 // If hint already includes all the bits of possibleChange,
2655 // don't bother calling SelectorMatches, since even if it returns false
2656 // hint won't change.
2657 // Also don't bother calling SelectorMatches if none of the
2658 // states passed in are relevant here.
2659 if ((possibleChange & ~hint) &&
2660 states.HasAtLeastOneOfStates(aStateMask) &&
2661 // We can optimize away testing selectors that only involve :hover, a
2662 // namespace, and a tag name against nodes that don't have the
2663 // NodeHasRelevantHoverRules flag: such a selector didn't match
2664 // the tag name or namespace the first time around (since the :hover
2665 // didn't set the NodeHasRelevantHoverRules flag), so it won't
2666 // match it now. Check for our selector only having :hover states, or
2667 // the element having the hover rules flag, or the selector having
2668 // some sort of non-namespace, non-tagname data in it.
2669 (states != NS_EVENT_STATE_HOVER ||
2670 aStatefulElement->HasRelevantHoverRules() ||
2671 selector->mIDList || selector->mClassList ||
2672 // We generally expect an mPseudoClassList, since we have a :hover.
2673 // The question is whether we have anything else in there.
2674 (selector->mPseudoClassList &&
2675 (selector->mPseudoClassList->mNext ||
2676 selector->mPseudoClassList->mType !=
2677 nsCSSPseudoClasses::ePseudoClass_hover)) ||
2678 selector->mAttrList || selector->mNegations) &&
2679 (!isPseudoElement ||
2680 StateSelectorMatches(aStatefulElement, selectorForPseudo,
2681 nodeContext, aData->mTreeMatchContext,
2682 nullptr, aStateMask)) &&
2683 SelectorMatches(aData->mElement, selector, nodeContext,
2684 aData->mTreeMatchContext) &&
2685 SelectorMatchesTree(aData->mElement, selector->mNext,
2686 aData->mTreeMatchContext,
2687 false))
2689 hint = nsRestyleHint(hint | possibleChange);
2693 return hint;
2696 nsRestyleHint
2697 nsCSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData)
2699 return HasStateDependentStyle(aData,
2700 aData->mElement,
2701 nsCSSPseudoElements::ePseudo_NotPseudoElement,
2702 aData->mStateMask);
2705 nsRestyleHint
2706 nsCSSRuleProcessor::HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData)
2708 return HasStateDependentStyle(aData,
2709 aData->mPseudoElement,
2710 aData->mPseudoType,
2711 aData->mStateMask);
2714 bool
2715 nsCSSRuleProcessor::HasDocumentStateDependentStyle(StateRuleProcessorData* aData)
2717 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
2719 return cascade && cascade->mSelectorDocumentStates.HasAtLeastOneOfStates(aData->mStateMask);
2722 struct AttributeEnumData {
2723 explicit AttributeEnumData(AttributeRuleProcessorData *aData)
2724 : data(aData), change(nsRestyleHint(0)) {}
2726 AttributeRuleProcessorData *data;
2727 nsRestyleHint change;
2731 static void
2732 AttributeEnumFunc(nsCSSSelector* aSelector, AttributeEnumData* aData)
2734 AttributeRuleProcessorData *data = aData->data;
2736 if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
2737 data->mScope)) {
2738 // The selector is for a rule in a scoped style sheet, and the subject
2739 // of the selector matching is not in its scope.
2740 return;
2743 nsRestyleHint possibleChange = RestyleHintForOp(aSelector->mOperator);
2745 // If enumData->change already includes all the bits of possibleChange, don't
2746 // bother calling SelectorMatches, since even if it returns false
2747 // enumData->change won't change.
2748 NodeMatchContext nodeContext(EventStates(), false);
2749 if ((possibleChange & ~(aData->change)) &&
2750 SelectorMatches(data->mElement, aSelector, nodeContext,
2751 data->mTreeMatchContext) &&
2752 SelectorMatchesTree(data->mElement, aSelector->mNext,
2753 data->mTreeMatchContext, false)) {
2754 aData->change = nsRestyleHint(aData->change | possibleChange);
2758 static MOZ_ALWAYS_INLINE void
2759 EnumerateSelectors(nsTArray<nsCSSSelector*>& aSelectors, AttributeEnumData* aData)
2761 nsCSSSelector **iter = aSelectors.Elements(),
2762 **end = iter + aSelectors.Length();
2763 for (; iter != end; ++iter) {
2764 AttributeEnumFunc(*iter, aData);
2768 nsRestyleHint
2769 nsCSSRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData)
2771 // We could try making use of aData->mModType, but :not rules make it a bit
2772 // of a pain to do so... So just ignore it for now.
2774 AttributeEnumData data(aData);
2776 // Don't do our special handling of certain attributes if the attr
2777 // hasn't changed yet.
2778 if (aData->mAttrHasChanged) {
2779 // check for the lwtheme and lwthemetextcolor attribute on root XUL elements
2780 if ((aData->mAttribute == nsGkAtoms::lwtheme ||
2781 aData->mAttribute == nsGkAtoms::lwthemetextcolor) &&
2782 aData->mElement->GetNameSpaceID() == kNameSpaceID_XUL &&
2783 aData->mElement == aData->mElement->OwnerDoc()->GetRootElement())
2785 data.change = nsRestyleHint(data.change | eRestyle_Subtree);
2788 // We don't know the namespace of the attribute, and xml:lang applies to
2789 // all elements. If the lang attribute changes, we need to restyle our
2790 // whole subtree, since the :lang selector on our descendants can examine
2791 // our lang attribute.
2792 if (aData->mAttribute == nsGkAtoms::lang) {
2793 data.change = nsRestyleHint(data.change | eRestyle_Subtree);
2797 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
2799 // Since we get both before and after notifications for attributes, we
2800 // don't have to ignore aData->mAttribute while matching. Just check
2801 // whether we have selectors relevant to aData->mAttribute that we
2802 // match. If this is the before change notification, that will catch
2803 // rules we might stop matching; if the after change notification, the
2804 // ones we might have started matching.
2805 if (cascade) {
2806 if (aData->mAttribute == nsGkAtoms::id) {
2807 nsIAtom* id = aData->mElement->GetID();
2808 if (id) {
2809 AtomSelectorEntry *entry =
2810 static_cast<AtomSelectorEntry*>
2811 (PL_DHashTableOperate(&cascade->mIdSelectors,
2812 id, PL_DHASH_LOOKUP));
2813 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
2814 EnumerateSelectors(entry->mSelectors, &data);
2818 EnumerateSelectors(cascade->mPossiblyNegatedIDSelectors, &data);
2821 if (aData->mAttribute == nsGkAtoms::_class) {
2822 const nsAttrValue* elementClasses = aData->mElement->GetClasses();
2823 if (elementClasses) {
2824 int32_t atomCount = elementClasses->GetAtomCount();
2825 for (int32_t i = 0; i < atomCount; ++i) {
2826 nsIAtom* curClass = elementClasses->AtomAt(i);
2827 AtomSelectorEntry *entry =
2828 static_cast<AtomSelectorEntry*>
2829 (PL_DHashTableOperate(&cascade->mClassSelectors,
2830 curClass, PL_DHASH_LOOKUP));
2831 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
2832 EnumerateSelectors(entry->mSelectors, &data);
2837 EnumerateSelectors(cascade->mPossiblyNegatedClassSelectors, &data);
2840 AtomSelectorEntry *entry =
2841 static_cast<AtomSelectorEntry*>
2842 (PL_DHashTableOperate(&cascade->mAttributeSelectors,
2843 aData->mAttribute, PL_DHASH_LOOKUP));
2844 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
2845 EnumerateSelectors(entry->mSelectors, &data);
2849 return data.change;
2852 /* virtual */ bool
2853 nsCSSRuleProcessor::MediumFeaturesChanged(nsPresContext* aPresContext)
2855 RuleCascadeData *old = mRuleCascades;
2856 // We don't want to do anything if there aren't any sets of rules
2857 // cached yet (or somebody cleared them and is thus responsible for
2858 // rebuilding things), since we should not build the rule cascade too
2859 // early (e.g., before we know whether the quirk style sheet should be
2860 // enabled). And if there's nothing cached, it doesn't matter if
2861 // anything changed. See bug 448281.
2862 if (old) {
2863 RefreshRuleCascade(aPresContext);
2865 return (old != mRuleCascades);
2868 /* virtual */ size_t
2869 nsCSSRuleProcessor::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
2871 size_t n = 0;
2872 n += mSheets.SizeOfExcludingThis(aMallocSizeOf);
2873 for (RuleCascadeData* cascade = mRuleCascades; cascade;
2874 cascade = cascade->mNext) {
2875 n += cascade->SizeOfIncludingThis(aMallocSizeOf);
2878 return n;
2881 /* virtual */ size_t
2882 nsCSSRuleProcessor::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
2884 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
2887 // Append all the currently-active font face rules to aArray. Return
2888 // true for success and false for failure.
2889 bool
2890 nsCSSRuleProcessor::AppendFontFaceRules(
2891 nsPresContext *aPresContext,
2892 nsTArray<nsFontFaceRuleContainer>& aArray)
2894 RuleCascadeData* cascade = GetRuleCascade(aPresContext);
2896 if (cascade) {
2897 if (!aArray.AppendElements(cascade->mFontFaceRules))
2898 return false;
2901 return true;
2904 nsCSSKeyframesRule*
2905 nsCSSRuleProcessor::KeyframesRuleForName(nsPresContext* aPresContext,
2906 const nsString& aName)
2908 RuleCascadeData* cascade = GetRuleCascade(aPresContext);
2910 if (cascade) {
2911 return cascade->mKeyframesRuleTable.Get(aName);
2914 return nullptr;
2917 nsCSSCounterStyleRule*
2918 nsCSSRuleProcessor::CounterStyleRuleForName(nsPresContext* aPresContext,
2919 const nsAString& aName)
2921 RuleCascadeData* cascade = GetRuleCascade(aPresContext);
2923 if (cascade) {
2924 return cascade->mCounterStyleRuleTable.Get(aName);
2927 return nullptr;
2930 // Append all the currently-active page rules to aArray. Return
2931 // true for success and false for failure.
2932 bool
2933 nsCSSRuleProcessor::AppendPageRules(
2934 nsPresContext* aPresContext,
2935 nsTArray<nsCSSPageRule*>& aArray)
2937 RuleCascadeData* cascade = GetRuleCascade(aPresContext);
2939 if (cascade) {
2940 if (!aArray.AppendElements(cascade->mPageRules)) {
2941 return false;
2945 return true;
2948 bool
2949 nsCSSRuleProcessor::AppendFontFeatureValuesRules(
2950 nsPresContext *aPresContext,
2951 nsTArray<nsCSSFontFeatureValuesRule*>& aArray)
2953 RuleCascadeData* cascade = GetRuleCascade(aPresContext);
2955 if (cascade) {
2956 if (!aArray.AppendElements(cascade->mFontFeatureValuesRules))
2957 return false;
2960 return true;
2963 nsresult
2964 nsCSSRuleProcessor::ClearRuleCascades()
2966 // We rely on our caller (perhaps indirectly) to do something that
2967 // will rebuild style data and the user font set (either
2968 // nsIPresShell::ReconstructStyleData or
2969 // nsPresContext::RebuildAllStyleData).
2970 RuleCascadeData *data = mRuleCascades;
2971 mRuleCascades = nullptr;
2972 while (data) {
2973 RuleCascadeData *next = data->mNext;
2974 delete data;
2975 data = next;
2977 return NS_OK;
2981 // This function should return the set of states that this selector
2982 // depends on; this is used to implement HasStateDependentStyle. It
2983 // does NOT recur down into things like :not and :-moz-any.
2984 inline
2985 EventStates ComputeSelectorStateDependence(nsCSSSelector& aSelector)
2987 EventStates states;
2988 for (nsPseudoClassList* pseudoClass = aSelector.mPseudoClassList;
2989 pseudoClass; pseudoClass = pseudoClass->mNext) {
2990 // Tree pseudo-elements overload mPseudoClassList for things that
2991 // aren't pseudo-classes.
2992 if (pseudoClass->mType >= nsCSSPseudoClasses::ePseudoClass_Count) {
2993 continue;
2995 states |= sPseudoClassStateDependences[pseudoClass->mType];
2997 return states;
3000 static bool
3001 AddSelector(RuleCascadeData* aCascade,
3002 // The part between combinators at the top level of the selector
3003 nsCSSSelector* aSelectorInTopLevel,
3004 // The part we should look through (might be in :not or :-moz-any())
3005 nsCSSSelector* aSelectorPart)
3007 // It's worth noting that this loop over negations isn't quite
3008 // optimal for two reasons. One, we could add something to one of
3009 // these lists twice, which means we'll check it twice, but I don't
3010 // think that's worth worrying about. (We do the same for multiple
3011 // attribute selectors on the same attribute.) Two, we don't really
3012 // need to check negations past the first in the current
3013 // implementation (and they're rare as well), but that might change
3014 // in the future if :not() is extended.
3015 for (nsCSSSelector* negation = aSelectorPart; negation;
3016 negation = negation->mNegations) {
3017 // Track both document states and attribute dependence in pseudo-classes.
3018 for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList;
3019 pseudoClass; pseudoClass = pseudoClass->mNext) {
3020 switch (pseudoClass->mType) {
3021 case nsCSSPseudoClasses::ePseudoClass_mozLocaleDir: {
3022 aCascade->mSelectorDocumentStates |= NS_DOCUMENT_STATE_RTL_LOCALE;
3023 break;
3025 case nsCSSPseudoClasses::ePseudoClass_mozWindowInactive: {
3026 aCascade->mSelectorDocumentStates |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
3027 break;
3029 case nsCSSPseudoClasses::ePseudoClass_mozTableBorderNonzero: {
3030 nsTArray<nsCSSSelector*> *array =
3031 aCascade->AttributeListFor(nsGkAtoms::border);
3032 if (!array) {
3033 return false;
3035 array->AppendElement(aSelectorInTopLevel);
3036 break;
3038 default: {
3039 break;
3044 // Build mStateSelectors.
3045 EventStates dependentStates = ComputeSelectorStateDependence(*negation);
3046 if (!dependentStates.IsEmpty()) {
3047 aCascade->mStateSelectors.AppendElement(
3048 nsCSSRuleProcessor::StateSelector(dependentStates,
3049 aSelectorInTopLevel));
3052 // Build mIDSelectors
3053 if (negation == aSelectorInTopLevel) {
3054 for (nsAtomList* curID = negation->mIDList; curID;
3055 curID = curID->mNext) {
3056 AtomSelectorEntry *entry =
3057 static_cast<AtomSelectorEntry*>(PL_DHashTableOperate(&aCascade->mIdSelectors,
3058 curID->mAtom,
3059 PL_DHASH_ADD));
3060 if (entry) {
3061 entry->mSelectors.AppendElement(aSelectorInTopLevel);
3064 } else if (negation->mIDList) {
3065 aCascade->mPossiblyNegatedIDSelectors.AppendElement(aSelectorInTopLevel);
3068 // Build mClassSelectors
3069 if (negation == aSelectorInTopLevel) {
3070 for (nsAtomList* curClass = negation->mClassList; curClass;
3071 curClass = curClass->mNext) {
3072 AtomSelectorEntry *entry =
3073 static_cast<AtomSelectorEntry*>(PL_DHashTableOperate(&aCascade->mClassSelectors,
3074 curClass->mAtom,
3075 PL_DHASH_ADD));
3076 if (entry) {
3077 entry->mSelectors.AppendElement(aSelectorInTopLevel);
3080 } else if (negation->mClassList) {
3081 aCascade->mPossiblyNegatedClassSelectors.AppendElement(aSelectorInTopLevel);
3084 // Build mAttributeSelectors.
3085 for (nsAttrSelector *attr = negation->mAttrList; attr;
3086 attr = attr->mNext) {
3087 nsTArray<nsCSSSelector*> *array =
3088 aCascade->AttributeListFor(attr->mCasedAttr);
3089 if (!array) {
3090 return false;
3092 array->AppendElement(aSelectorInTopLevel);
3093 if (attr->mLowercaseAttr != attr->mCasedAttr) {
3094 array = aCascade->AttributeListFor(attr->mLowercaseAttr);
3095 if (!array) {
3096 return false;
3098 array->AppendElement(aSelectorInTopLevel);
3102 // Recur through any :-moz-any selectors
3103 for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList;
3104 pseudoClass; pseudoClass = pseudoClass->mNext) {
3105 if (pseudoClass->mType == nsCSSPseudoClasses::ePseudoClass_any) {
3106 for (nsCSSSelectorList *l = pseudoClass->u.mSelectors; l; l = l->mNext) {
3107 nsCSSSelector *s = l->mSelectors;
3108 if (!AddSelector(aCascade, aSelectorInTopLevel, s)) {
3109 return false;
3116 return true;
3119 static bool
3120 AddRule(RuleSelectorPair* aRuleInfo, RuleCascadeData* aCascade)
3122 RuleCascadeData * const cascade = aCascade;
3124 // Build the rule hash.
3125 nsCSSPseudoElements::Type pseudoType = aRuleInfo->mSelector->PseudoType();
3126 if (MOZ_LIKELY(pseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement)) {
3127 cascade->mRuleHash.AppendRule(*aRuleInfo);
3128 } else if (pseudoType < nsCSSPseudoElements::ePseudo_PseudoElementCount) {
3129 RuleHash*& ruleHash = cascade->mPseudoElementRuleHashes[pseudoType];
3130 if (!ruleHash) {
3131 ruleHash = new RuleHash(cascade->mQuirksMode);
3132 if (!ruleHash) {
3133 // Out of memory; give up
3134 return false;
3137 NS_ASSERTION(aRuleInfo->mSelector->mNext,
3138 "Must have mNext; parser screwed up");
3139 NS_ASSERTION(aRuleInfo->mSelector->mNext->mOperator == ':',
3140 "Unexpected mNext combinator");
3141 ruleHash->AppendRule(*aRuleInfo);
3142 } else if (pseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
3143 NS_ASSERTION(!aRuleInfo->mSelector->mCasedTag &&
3144 !aRuleInfo->mSelector->mIDList &&
3145 !aRuleInfo->mSelector->mClassList &&
3146 !aRuleInfo->mSelector->mPseudoClassList &&
3147 !aRuleInfo->mSelector->mAttrList &&
3148 !aRuleInfo->mSelector->mNegations &&
3149 !aRuleInfo->mSelector->mNext &&
3150 aRuleInfo->mSelector->mNameSpace == kNameSpaceID_Unknown,
3151 "Parser messed up with anon box selector");
3153 // Index doesn't matter here, since we'll just be walking these
3154 // rules in order; just pass 0.
3155 AppendRuleToTagTable(&cascade->mAnonBoxRules,
3156 aRuleInfo->mSelector->mLowercaseTag,
3157 RuleValue(*aRuleInfo, 0, aCascade->mQuirksMode));
3158 } else {
3159 #ifdef MOZ_XUL
3160 NS_ASSERTION(pseudoType == nsCSSPseudoElements::ePseudo_XULTree,
3161 "Unexpected pseudo type");
3162 // Index doesn't matter here, since we'll just be walking these
3163 // rules in order; just pass 0.
3164 AppendRuleToTagTable(&cascade->mXULTreeRules,
3165 aRuleInfo->mSelector->mLowercaseTag,
3166 RuleValue(*aRuleInfo, 0, aCascade->mQuirksMode));
3167 #else
3168 NS_NOTREACHED("Unexpected pseudo type");
3169 #endif
3172 for (nsCSSSelector* selector = aRuleInfo->mSelector;
3173 selector; selector = selector->mNext) {
3174 if (selector->IsPseudoElement()) {
3175 nsCSSPseudoElements::Type pseudo = selector->PseudoType();
3176 if (pseudo >= nsCSSPseudoElements::ePseudo_PseudoElementCount ||
3177 !nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudo)) {
3178 NS_ASSERTION(!selector->mNegations, "Shouldn't have negations");
3179 // We do store selectors ending with pseudo-elements that allow :hover
3180 // and :active after them in the hashtables corresponding to that
3181 // selector's mNext (i.e. the thing that matches against the element),
3182 // but we want to make sure that selectors for any other kinds of
3183 // pseudo-elements don't end up in the hashtables. In particular, tree
3184 // pseudos store strange things in mPseudoClassList that we don't want
3185 // to try to match elements against.
3186 continue;
3189 if (!AddSelector(cascade, selector, selector)) {
3190 return false;
3194 return true;
3197 struct PerWeightDataListItem : public RuleSelectorPair {
3198 PerWeightDataListItem(css::StyleRule* aRule, nsCSSSelector* aSelector)
3199 : RuleSelectorPair(aRule, aSelector)
3200 , mNext(nullptr)
3202 // No destructor; these are arena-allocated
3205 // Placement new to arena allocate the PerWeightDataListItem
3206 void *operator new(size_t aSize, PLArenaPool &aArena) CPP_THROW_NEW {
3207 void *mem;
3208 PL_ARENA_ALLOCATE(mem, &aArena, aSize);
3209 return mem;
3212 PerWeightDataListItem *mNext;
3215 struct PerWeightData {
3216 PerWeightData()
3217 : mRuleSelectorPairs(nullptr)
3218 , mTail(&mRuleSelectorPairs)
3221 int32_t mWeight;
3222 PerWeightDataListItem *mRuleSelectorPairs;
3223 PerWeightDataListItem **mTail;
3226 struct RuleByWeightEntry : public PLDHashEntryHdr {
3227 PerWeightData data; // mWeight is key, mRuleSelectorPairs are value
3230 static PLDHashNumber
3231 HashIntKey(PLDHashTable *table, const void *key)
3233 return PLDHashNumber(NS_PTR_TO_INT32(key));
3236 static bool
3237 MatchWeightEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
3238 const void *key)
3240 const RuleByWeightEntry *entry = (const RuleByWeightEntry *)hdr;
3241 return entry->data.mWeight == NS_PTR_TO_INT32(key);
3244 static bool
3245 InitWeightEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
3246 const void *key)
3248 RuleByWeightEntry* entry = static_cast<RuleByWeightEntry*>(hdr);
3249 new (entry) RuleByWeightEntry();
3250 return true;
3253 static const PLDHashTableOps gRulesByWeightOps = {
3254 PL_DHashAllocTable,
3255 PL_DHashFreeTable,
3256 HashIntKey,
3257 MatchWeightEntry,
3258 PL_DHashMoveEntryStub,
3259 PL_DHashClearEntryStub,
3260 PL_DHashFinalizeStub,
3261 InitWeightEntry
3264 struct CascadeEnumData {
3265 CascadeEnumData(nsPresContext* aPresContext,
3266 nsTArray<nsFontFaceRuleContainer>& aFontFaceRules,
3267 nsTArray<nsCSSKeyframesRule*>& aKeyframesRules,
3268 nsTArray<nsCSSFontFeatureValuesRule*>& aFontFeatureValuesRules,
3269 nsTArray<nsCSSPageRule*>& aPageRules,
3270 nsTArray<nsCSSCounterStyleRule*>& aCounterStyleRules,
3271 nsMediaQueryResultCacheKey& aKey,
3272 uint8_t aSheetType)
3273 : mPresContext(aPresContext),
3274 mFontFaceRules(aFontFaceRules),
3275 mKeyframesRules(aKeyframesRules),
3276 mFontFeatureValuesRules(aFontFeatureValuesRules),
3277 mPageRules(aPageRules),
3278 mCounterStyleRules(aCounterStyleRules),
3279 mCacheKey(aKey),
3280 mSheetType(aSheetType)
3282 if (!PL_DHashTableInit(&mRulesByWeight, &gRulesByWeightOps, nullptr,
3283 sizeof(RuleByWeightEntry), fallible_t(), 32))
3284 mRulesByWeight.ops = nullptr;
3286 // Initialize our arena
3287 PL_INIT_ARENA_POOL(&mArena, "CascadeEnumDataArena",
3288 NS_CASCADEENUMDATA_ARENA_BLOCK_SIZE);
3291 ~CascadeEnumData()
3293 if (mRulesByWeight.ops)
3294 PL_DHashTableFinish(&mRulesByWeight);
3295 PL_FinishArenaPool(&mArena);
3298 nsPresContext* mPresContext;
3299 nsTArray<nsFontFaceRuleContainer>& mFontFaceRules;
3300 nsTArray<nsCSSKeyframesRule*>& mKeyframesRules;
3301 nsTArray<nsCSSFontFeatureValuesRule*>& mFontFeatureValuesRules;
3302 nsTArray<nsCSSPageRule*>& mPageRules;
3303 nsTArray<nsCSSCounterStyleRule*>& mCounterStyleRules;
3304 nsMediaQueryResultCacheKey& mCacheKey;
3305 PLArenaPool mArena;
3306 // Hooray, a manual PLDHashTable since nsClassHashtable doesn't
3307 // provide a getter that gives me a *reference* to the value.
3308 PLDHashTable mRulesByWeight; // of PerWeightDataListItem linked lists
3309 uint8_t mSheetType;
3313 * This enumerates style rules in a sheet (and recursively into any
3314 * grouping rules) in order to:
3315 * (1) add any style rules, in order, into data->mRulesByWeight (for
3316 * the primary CSS cascade), where they are separated by weight
3317 * but kept in order per-weight, and
3318 * (2) add any @font-face rules, in order, into data->mFontFaceRules.
3319 * (3) add any @keyframes rules, in order, into data->mKeyframesRules.
3320 * (4) add any @font-feature-value rules, in order,
3321 * into data->mFontFeatureValuesRules.
3322 * (5) add any @page rules, in order, into data->mPageRules.
3323 * (6) add any @counter-style rules, in order, into data->mCounterStyleRules.
3325 static bool
3326 CascadeRuleEnumFunc(css::Rule* aRule, void* aData)
3328 CascadeEnumData* data = (CascadeEnumData*)aData;
3329 int32_t type = aRule->GetType();
3331 if (css::Rule::STYLE_RULE == type) {
3332 css::StyleRule* styleRule = static_cast<css::StyleRule*>(aRule);
3334 for (nsCSSSelectorList *sel = styleRule->Selector();
3335 sel; sel = sel->mNext) {
3336 int32_t weight = sel->mWeight;
3337 RuleByWeightEntry *entry = static_cast<RuleByWeightEntry*>(
3338 PL_DHashTableOperate(&data->mRulesByWeight, NS_INT32_TO_PTR(weight),
3339 PL_DHASH_ADD));
3340 if (!entry)
3341 return false;
3342 entry->data.mWeight = weight;
3343 // entry->data.mRuleSelectorPairs should be linked in forward order;
3344 // entry->data.mTail is the slot to write to.
3345 PerWeightDataListItem *newItem =
3346 new (data->mArena) PerWeightDataListItem(styleRule, sel->mSelectors);
3347 if (newItem) {
3348 *(entry->data.mTail) = newItem;
3349 entry->data.mTail = &newItem->mNext;
3353 else if (css::Rule::MEDIA_RULE == type ||
3354 css::Rule::DOCUMENT_RULE == type ||
3355 css::Rule::SUPPORTS_RULE == type) {
3356 css::GroupRule* groupRule = static_cast<css::GroupRule*>(aRule);
3357 if (groupRule->UseForPresentation(data->mPresContext, data->mCacheKey))
3358 if (!groupRule->EnumerateRulesForwards(CascadeRuleEnumFunc, aData))
3359 return false;
3361 else if (css::Rule::FONT_FACE_RULE == type) {
3362 nsCSSFontFaceRule *fontFaceRule = static_cast<nsCSSFontFaceRule*>(aRule);
3363 nsFontFaceRuleContainer *ptr = data->mFontFaceRules.AppendElement();
3364 if (!ptr)
3365 return false;
3366 ptr->mRule = fontFaceRule;
3367 ptr->mSheetType = data->mSheetType;
3369 else if (css::Rule::KEYFRAMES_RULE == type) {
3370 nsCSSKeyframesRule *keyframesRule =
3371 static_cast<nsCSSKeyframesRule*>(aRule);
3372 if (!data->mKeyframesRules.AppendElement(keyframesRule)) {
3373 return false;
3376 else if (css::Rule::FONT_FEATURE_VALUES_RULE == type) {
3377 nsCSSFontFeatureValuesRule *fontFeatureValuesRule =
3378 static_cast<nsCSSFontFeatureValuesRule*>(aRule);
3379 if (!data->mFontFeatureValuesRules.AppendElement(fontFeatureValuesRule)) {
3380 return false;
3383 else if (css::Rule::PAGE_RULE == type) {
3384 nsCSSPageRule* pageRule = static_cast<nsCSSPageRule*>(aRule);
3385 if (!data->mPageRules.AppendElement(pageRule)) {
3386 return false;
3389 else if (css::Rule::COUNTER_STYLE_RULE == type) {
3390 nsCSSCounterStyleRule* counterStyleRule =
3391 static_cast<nsCSSCounterStyleRule*>(aRule);
3392 if (!data->mCounterStyleRules.AppendElement(counterStyleRule)) {
3393 return false;
3396 return true;
3399 /* static */ bool
3400 nsCSSRuleProcessor::CascadeSheet(CSSStyleSheet* aSheet, CascadeEnumData* aData)
3402 if (aSheet->IsApplicable() &&
3403 aSheet->UseForPresentation(aData->mPresContext, aData->mCacheKey) &&
3404 aSheet->mInner) {
3405 CSSStyleSheet* child = aSheet->mInner->mFirstChild;
3406 while (child) {
3407 CascadeSheet(child, aData);
3408 child = child->mNext;
3411 if (!aSheet->mInner->mOrderedRules.EnumerateForwards(CascadeRuleEnumFunc,
3412 aData))
3413 return false;
3415 return true;
3418 static int CompareWeightData(const void* aArg1, const void* aArg2,
3419 void* closure)
3421 const PerWeightData* arg1 = static_cast<const PerWeightData*>(aArg1);
3422 const PerWeightData* arg2 = static_cast<const PerWeightData*>(aArg2);
3423 return arg1->mWeight - arg2->mWeight; // put lower weight first
3427 struct FillWeightArrayData {
3428 explicit FillWeightArrayData(PerWeightData* aArrayData) :
3429 mIndex(0),
3430 mWeightArray(aArrayData)
3433 int32_t mIndex;
3434 PerWeightData* mWeightArray;
3438 static PLDHashOperator
3439 FillWeightArray(PLDHashTable *table, PLDHashEntryHdr *hdr,
3440 uint32_t number, void *arg)
3442 FillWeightArrayData* data = static_cast<FillWeightArrayData*>(arg);
3443 const RuleByWeightEntry *entry = (const RuleByWeightEntry *)hdr;
3445 data->mWeightArray[data->mIndex++] = entry->data;
3447 return PL_DHASH_NEXT;
3450 RuleCascadeData*
3451 nsCSSRuleProcessor::GetRuleCascade(nsPresContext* aPresContext)
3453 // FIXME: Make this infallible!
3455 // If anything changes about the presentation context, we will be
3456 // notified. Otherwise, our cache is valid if mLastPresContext
3457 // matches aPresContext. (The only rule processors used for multiple
3458 // pres contexts are for XBL. These rule processors are probably less
3459 // likely to have @media rules, and thus the cache is pretty likely to
3460 // hit instantly even when we're switching between pres contexts.)
3462 if (!mRuleCascades || aPresContext != mLastPresContext) {
3463 RefreshRuleCascade(aPresContext);
3465 mLastPresContext = aPresContext;
3467 return mRuleCascades;
3470 void
3471 nsCSSRuleProcessor::RefreshRuleCascade(nsPresContext* aPresContext)
3473 // Having RuleCascadeData objects be per-medium (over all variation
3474 // caused by media queries, handled through mCacheKey) works for now
3475 // since nsCSSRuleProcessor objects are per-document. (For a given
3476 // set of stylesheets they can vary based on medium (@media) or
3477 // document (@-moz-document).)
3479 for (RuleCascadeData **cascadep = &mRuleCascades, *cascade;
3480 (cascade = *cascadep); cascadep = &cascade->mNext) {
3481 if (cascade->mCacheKey.Matches(aPresContext)) {
3482 // Ensure that the current one is always mRuleCascades.
3483 *cascadep = cascade->mNext;
3484 cascade->mNext = mRuleCascades;
3485 mRuleCascades = cascade;
3487 return;
3491 if (mSheets.Length() != 0) {
3492 nsAutoPtr<RuleCascadeData> newCascade(
3493 new RuleCascadeData(aPresContext->Medium(),
3494 eCompatibility_NavQuirks == aPresContext->CompatibilityMode()));
3495 if (newCascade) {
3496 CascadeEnumData data(aPresContext, newCascade->mFontFaceRules,
3497 newCascade->mKeyframesRules,
3498 newCascade->mFontFeatureValuesRules,
3499 newCascade->mPageRules,
3500 newCascade->mCounterStyleRules,
3501 newCascade->mCacheKey,
3502 mSheetType);
3503 if (!data.mRulesByWeight.ops)
3504 return; /* out of memory */
3506 for (uint32_t i = 0; i < mSheets.Length(); ++i) {
3507 if (!CascadeSheet(mSheets.ElementAt(i), &data))
3508 return; /* out of memory */
3511 // Sort the hash table of per-weight linked lists by weight.
3512 uint32_t weightCount = data.mRulesByWeight.EntryCount();
3513 nsAutoArrayPtr<PerWeightData> weightArray(new PerWeightData[weightCount]);
3514 FillWeightArrayData fwData(weightArray);
3515 PL_DHashTableEnumerate(&data.mRulesByWeight, FillWeightArray, &fwData);
3516 NS_QuickSort(weightArray, weightCount, sizeof(PerWeightData),
3517 CompareWeightData, nullptr);
3519 // Put things into the rule hash.
3520 // The primary sort is by weight...
3521 for (uint32_t i = 0; i < weightCount; ++i) {
3522 // and the secondary sort is by order. mRuleSelectorPairs is already in
3523 // the right order..
3524 for (PerWeightDataListItem *cur = weightArray[i].mRuleSelectorPairs;
3525 cur;
3526 cur = cur->mNext) {
3527 if (!AddRule(cur, newCascade))
3528 return; /* out of memory */
3532 // Build mKeyframesRuleTable.
3533 for (nsTArray<nsCSSKeyframesRule*>::size_type i = 0,
3534 iEnd = newCascade->mKeyframesRules.Length(); i < iEnd; ++i) {
3535 nsCSSKeyframesRule* rule = newCascade->mKeyframesRules[i];
3536 newCascade->mKeyframesRuleTable.Put(rule->GetName(), rule);
3539 // Build mCounterStyleRuleTable
3540 for (nsTArray<nsCSSCounterStyleRule*>::size_type i = 0,
3541 iEnd = newCascade->mCounterStyleRules.Length(); i < iEnd; ++i) {
3542 nsCSSCounterStyleRule* rule = newCascade->mCounterStyleRules[i];
3543 newCascade->mCounterStyleRuleTable.Put(rule->GetName(), rule);
3546 // Ensure that the current one is always mRuleCascades.
3547 newCascade->mNext = mRuleCascades;
3548 mRuleCascades = newCascade.forget();
3551 return;
3554 /* static */ bool
3555 nsCSSRuleProcessor::SelectorListMatches(Element* aElement,
3556 TreeMatchContext& aTreeMatchContext,
3557 nsCSSSelectorList* aSelectorList)
3559 MOZ_ASSERT(!aTreeMatchContext.mForScopedStyle,
3560 "mCurrentStyleScope will need to be saved and restored after the "
3561 "SelectorMatchesTree call");
3563 while (aSelectorList) {
3564 nsCSSSelector* sel = aSelectorList->mSelectors;
3565 NS_ASSERTION(sel, "Should have *some* selectors");
3566 NS_ASSERTION(!sel->IsPseudoElement(), "Shouldn't have been called");
3567 NodeMatchContext nodeContext(EventStates(), false);
3568 if (SelectorMatches(aElement, sel, nodeContext, aTreeMatchContext)) {
3569 nsCSSSelector* next = sel->mNext;
3570 if (!next ||
3571 SelectorMatchesTree(aElement, next, aTreeMatchContext, false)) {
3572 return true;
3576 aSelectorList = aSelectorList->mNext;
3579 return false;
3582 // TreeMatchContext and AncestorFilter out of line methods
3583 void
3584 TreeMatchContext::InitAncestors(Element *aElement)
3586 MOZ_ASSERT(!mAncestorFilter.mFilter);
3587 MOZ_ASSERT(mAncestorFilter.mHashes.IsEmpty());
3588 MOZ_ASSERT(mStyleScopes.IsEmpty());
3590 mAncestorFilter.mFilter = new AncestorFilter::Filter();
3592 if (MOZ_LIKELY(aElement)) {
3593 MOZ_ASSERT(aElement->GetCurrentDoc() ||
3594 aElement->HasFlag(NODE_IS_IN_SHADOW_TREE),
3595 "aElement must be in the document or in shadow tree "
3596 "for the assumption that GetParentNode() is non-null "
3597 "on all element ancestors of aElement to be true");
3598 // Collect up the ancestors
3599 nsAutoTArray<Element*, 50> ancestors;
3600 Element* cur = aElement;
3601 do {
3602 ancestors.AppendElement(cur);
3603 cur = cur->GetParentElementCrossingShadowRoot();
3604 } while (cur);
3606 // Now push them in reverse order.
3607 for (uint32_t i = ancestors.Length(); i-- != 0; ) {
3608 mAncestorFilter.PushAncestor(ancestors[i]);
3609 PushStyleScope(ancestors[i]);
3614 void
3615 TreeMatchContext::InitStyleScopes(Element* aElement)
3617 MOZ_ASSERT(mStyleScopes.IsEmpty());
3619 if (MOZ_LIKELY(aElement)) {
3620 // Collect up the ancestors
3621 nsAutoTArray<Element*, 50> ancestors;
3622 Element* cur = aElement;
3623 do {
3624 ancestors.AppendElement(cur);
3625 cur = cur->GetParentElementCrossingShadowRoot();
3626 } while (cur);
3628 // Now push them in reverse order.
3629 for (uint32_t i = ancestors.Length(); i-- != 0; ) {
3630 PushStyleScope(ancestors[i]);
3635 void
3636 AncestorFilter::PushAncestor(Element *aElement)
3638 MOZ_ASSERT(mFilter);
3640 uint32_t oldLength = mHashes.Length();
3642 mPopTargets.AppendElement(oldLength);
3643 #ifdef DEBUG
3644 mElements.AppendElement(aElement);
3645 #endif
3646 mHashes.AppendElement(aElement->Tag()->hash());
3647 nsIAtom *id = aElement->GetID();
3648 if (id) {
3649 mHashes.AppendElement(id->hash());
3651 const nsAttrValue *classes = aElement->GetClasses();
3652 if (classes) {
3653 uint32_t classCount = classes->GetAtomCount();
3654 for (uint32_t i = 0; i < classCount; ++i) {
3655 mHashes.AppendElement(classes->AtomAt(i)->hash());
3659 uint32_t newLength = mHashes.Length();
3660 for (uint32_t i = oldLength; i < newLength; ++i) {
3661 mFilter->add(mHashes[i]);
3665 void
3666 AncestorFilter::PopAncestor()
3668 MOZ_ASSERT(!mPopTargets.IsEmpty());
3669 MOZ_ASSERT(mPopTargets.Length() == mElements.Length());
3671 uint32_t popTargetLength = mPopTargets.Length();
3672 uint32_t newLength = mPopTargets[popTargetLength-1];
3674 mPopTargets.TruncateLength(popTargetLength-1);
3675 #ifdef DEBUG
3676 mElements.TruncateLength(popTargetLength-1);
3677 #endif
3679 uint32_t oldLength = mHashes.Length();
3680 for (uint32_t i = newLength; i < oldLength; ++i) {
3681 mFilter->remove(mHashes[i]);
3683 mHashes.TruncateLength(newLength);
3686 #ifdef DEBUG
3687 void
3688 AncestorFilter::AssertHasAllAncestors(Element *aElement) const
3690 Element* cur = aElement->GetParentElementCrossingShadowRoot();
3691 while (cur) {
3692 MOZ_ASSERT(mElements.Contains(cur));
3693 cur = cur->GetParentElementCrossingShadowRoot();
3697 void
3698 TreeMatchContext::AssertHasAllStyleScopes(Element* aElement) const
3700 Element* cur = aElement->GetParentElementCrossingShadowRoot();
3701 while (cur) {
3702 if (cur->IsScopedStyleRoot()) {
3703 MOZ_ASSERT(mStyleScopes.Contains(cur));
3705 cur = cur->GetParentElementCrossingShadowRoot();
3708 #endif