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/. */
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)
19 #include "nsCSSRuleProcessor.h"
20 #include "nsRuleProcessorData.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"
33 #include "nsRuleWalker.h"
34 #include "nsCSSPseudoClasses.h"
35 #include "nsCSSPseudoElements.h"
36 #include "nsIContent.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"
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;
67 uint8_t nsCSSRuleProcessor::sWinThemeId
= LookAndFeel::eWindowsTheme_Generic
;
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('>'))
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
{
94 eMaxAncestorHashes
= 4
97 RuleValue(const RuleSelectorPair
& aRuleSelectorPair
, int32_t aIndex
,
99 RuleSelectorPair(aRuleSelectorPair
),
102 CollectAncestorHashes(aQuirksMode
);
105 int32_t mIndex
; // High index means high weight/order.
106 uint32_t mAncestorSelectorHashes
[eMaxAncestorHashes
];
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.
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.
129 nsAtomList
* ids
= sel
->mIDList
;
131 mAncestorSelectorHashes
[hashIndex
++] = ids
->mAtom
->hash();
132 if (hashIndex
== eMaxAncestorHashes
) {
138 nsAtomList
* classes
= sel
->mClassList
;
140 mAncestorSelectorHashes
[hashIndex
++] = classes
->mAtom
->hash();
141 if (hashIndex
== eMaxAncestorHashes
) {
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
) {
159 while (hashIndex
!= eMaxAncestorHashes
) {
160 mAncestorSelectorHashes
[hashIndex
++] = 0;
165 // ------------------------------
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
;
184 RuleHash_CIHashKey(PLDHashTable
*table
, const void *key
)
186 nsIAtom
*atom
= const_cast<nsIAtom
*>(static_cast<const nsIAtom
*>(key
));
190 nsContentUtils::ASCIIToLower(str
);
191 return HashString(str
);
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
202 RuleHashGetKey getKey
;
205 inline const RuleHashTableOps
*
206 ToLocalOps(const PLDHashTableOps
*aOps
)
208 return (const RuleHashTableOps
*)
209 (((const char*) aOps
) - offsetof(RuleHashTableOps
, ops
));
213 RuleHash_CIMatchEntry(PLDHashTable
*table
, const PLDHashEntryHdr
*hdr
,
216 nsIAtom
*match_atom
= const_cast<nsIAtom
*>(static_cast<const nsIAtom
*>
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
)
225 // Use EqualsIgnoreASCIICase instead of full on unicode case conversion
226 // in order to save on performance. This is only used in quirks mode
230 nsContentUtils::EqualsIgnoreASCIICase(nsDependentAtomString(entry_atom
),
231 nsDependentAtomString(match_atom
));
235 RuleHash_CSMatchEntry(PLDHashTable
*table
, const PLDHashEntryHdr
*hdr
,
238 nsIAtom
*match_atom
= const_cast<nsIAtom
*>(static_cast<const nsIAtom
*>
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
;
247 RuleHash_InitEntry(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
,
250 RuleHashTableEntry
* entry
= static_cast<RuleHashTableEntry
*>(hdr
);
251 new (entry
) RuleHashTableEntry();
256 RuleHash_ClearEntry(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
)
258 RuleHashTableEntry
* entry
= static_cast<RuleHashTableEntry
*>(hdr
);
259 entry
->~RuleHashTableEntry();
263 RuleHash_MoveEntry(PLDHashTable
*table
, const PLDHashEntryHdr
*from
,
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();
276 RuleHash_TagTable_MatchEntry(PLDHashTable
*table
, const PLDHashEntryHdr
*hdr
,
279 nsIAtom
*match_atom
= const_cast<nsIAtom
*>(static_cast<const nsIAtom
*>
281 nsIAtom
*entry_atom
= static_cast<const RuleHashTagTableEntry
*>(hdr
)->mTag
;
283 return match_atom
== entry_atom
;
287 RuleHash_TagTable_InitEntry(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
,
290 RuleHashTagTableEntry
* entry
= static_cast<RuleHashTagTableEntry
*>(hdr
);
291 new (entry
) RuleHashTagTableEntry();
292 entry
->mTag
= const_cast<nsIAtom
*>(static_cast<const nsIAtom
*>(key
));
297 RuleHash_TagTable_ClearEntry(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
)
299 RuleHashTagTableEntry
* entry
= static_cast<RuleHashTagTableEntry
*>(hdr
);
300 entry
->~RuleHashTagTableEntry();
304 RuleHash_TagTable_MoveEntry(PLDHashTable
*table
, const PLDHashEntryHdr
*from
,
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();
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
;
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
;
342 RuleHash_NameSpaceTable_HashKey(PLDHashTable
*table
, const void *key
)
344 return NS_PTR_TO_INT32(key
);
348 RuleHash_NameSpaceTable_MatchEntry(PLDHashTable
*table
,
349 const PLDHashEntryHdr
*hdr
,
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
= {
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
= {
378 PL_DHashVoidPtrKeyStub
,
379 RuleHash_CSMatchEntry
,
382 PL_DHashFinalizeStub
,
385 RuleHash_ClassTable_GetKey
388 // Case-insensitive ops.
389 static const RuleHashTableOps RuleHash_ClassTable_CIOps
= {
394 RuleHash_CIMatchEntry
,
397 PL_DHashFinalizeStub
,
400 RuleHash_ClassTable_GetKey
403 // Case-sensitive ops.
404 static const RuleHashTableOps RuleHash_IdTable_CSOps
= {
408 PL_DHashVoidPtrKeyStub
,
409 RuleHash_CSMatchEntry
,
412 PL_DHashFinalizeStub
,
415 RuleHash_IdTable_GetKey
418 // Case-insensitive ops.
419 static const RuleHashTableOps RuleHash_IdTable_CIOps
= {
424 RuleHash_CIMatchEntry
,
427 PL_DHashFinalizeStub
,
430 RuleHash_IdTable_GetKey
433 static const PLDHashTableOps RuleHash_NameSpaceTable_Ops
= {
436 RuleHash_NameSpaceTable_HashKey
,
437 RuleHash_NameSpaceTable_MatchEntry
,
440 PL_DHashFinalizeStub
,
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
450 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO PR_END_MACRO
453 struct NodeMatchContext
;
457 explicit RuleHash(bool aQuirksMode
);
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;
467 typedef nsTArray
<RuleValue
> RuleValueList
;
468 void AppendRuleToTable(PLDHashTable
* aTable
, const void* aKey
,
469 const RuleSelectorPair
& aRuleInfo
);
470 void AppendUniversalRule(const RuleSelectorPair
& aRuleInfo
);
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
;
482 const RuleValue
* mCurValue
;
483 const RuleValue
* mEnd
;
486 int32_t mEnumListSize
;
490 inline EnumData
ToEnumData(const RuleValueList
& arr
) {
491 EnumData data
= { arr
.Elements(), arr
.Elements() + arr
.Length() };
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
)
515 mEnumList(nullptr), mEnumListSize(0),
516 mQuirksMode(aQuirksMode
)
517 #ifdef RULE_HASH_STATS
519 mUniversalSelectors(0),
520 mNameSpaceSelectors(0),
525 mElementUniversalCalls(0),
526 mElementNameSpaceCalls(0),
528 mElementClassCalls(0),
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
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
,
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
) {
579 // delete arena for strings and small objects
581 PL_DHashTableFinish(&mIdTable
);
583 if (mClassTable
.ops
) {
584 PL_DHashTableFinish(&mClassTable
);
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
));
602 entry
->mRules
.AppendElement(RuleValue(aRuleInfo
, mRuleCount
++, mQuirksMode
));
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
));
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
) {
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()
685 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
686 PR_BEGIN_MACRO PR_END_MACRO
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
708 int32_t testCount
= classCount
+ 4;
710 if (mEnumListSize
< testCount
) {
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
),
728 if (PL_DHASH_ENTRY_IS_BUSY(entry
)) {
729 mEnumList
[valueCount
++] = ToEnumData(entry
->mRules
);
730 RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry
->mRules
, mElementNameSpaceCalls
);
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
),
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;
768 filter
->AssertHasAllAncestors(aElement
);
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
) {
779 lowestRuleIndex
= ruleIndex
;
782 const RuleValue
*cur
= mEnumList
[valueIndex
].mCurValue
;
783 ContentEnumFunc(*cur
, cur
->mSelector
, aData
, aNodeContext
, filter
);
785 if (cur
== mEnumList
[valueIndex
].mEnd
) {
786 mEnumList
[valueIndex
] = mEnumList
[--valueCount
];
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
);
802 SizeOfRuleHashTableEntry(PLDHashEntryHdr
* aHdr
, MallocSizeOf aMallocSizeOf
, void *)
804 RuleHashTableEntry
* entry
= static_cast<RuleHashTableEntry
*>(aHdr
);
805 return entry
->mRules
.SizeOfExcludingThis(aMallocSizeOf
);
809 RuleHash::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) const
814 n
+= PL_DHashTableSizeOfExcludingThis(&mIdTable
,
815 SizeOfRuleHashTableEntry
,
819 if (mClassTable
.ops
) {
820 n
+= PL_DHashTableSizeOfExcludingThis(&mClassTable
,
821 SizeOfRuleHashTableEntry
,
826 n
+= PL_DHashTableSizeOfExcludingThis(&mTagTable
,
827 SizeOfRuleHashTableEntry
,
831 if (mNameSpaceTable
.ops
) {
832 n
+= PL_DHashTableSizeOfExcludingThis(&mNameSpaceTable
,
833 SizeOfRuleHashTableEntry
,
837 n
+= mUniversalRules
.SizeOfExcludingThis(aMallocSizeOf
);
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
{
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
;
859 AtomSelector_ClearEntry(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
)
861 (static_cast<AtomSelectorEntry
*>(hdr
))->~AtomSelectorEntry();
865 AtomSelector_InitEntry(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
,
868 AtomSelectorEntry
*entry
= static_cast<AtomSelectorEntry
*>(hdr
);
869 new (entry
) AtomSelectorEntry();
870 entry
->mAtom
= const_cast<nsIAtom
*>(static_cast<const nsIAtom
*>(key
));
875 AtomSelector_MoveEntry(PLDHashTable
*table
, const PLDHashEntryHdr
*from
,
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();
888 AtomSelector_GetKey(PLDHashTable
*table
, const PLDHashEntryHdr
*hdr
)
890 const AtomSelectorEntry
*entry
= static_cast<const AtomSelectorEntry
*>(hdr
);
894 // Case-sensitive ops.
895 static const PLDHashTableOps AtomSelector_CSOps
= {
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
= {
912 RuleHash_CIMatchEntry
,
913 AtomSelector_MoveEntry
,
914 AtomSelector_ClearEntry
,
915 PL_DHashFinalizeStub
,
916 AtomSelector_InitEntry
921 //--------------------------------
923 struct RuleCascadeData
{
924 RuleCascadeData(nsIAtom
*aMedium
, bool aQuirksMode
)
925 : mRuleHash(aQuirksMode
),
927 mSelectorDocumentStates(0),
928 mKeyframesRuleTable(),
929 mCounterStyleRuleTable(),
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
:
943 nullptr, sizeof(AtomSelectorEntry
));
944 PL_DHashTableInit(&mClassSelectors
,
945 aQuirksMode
? &AtomSelector_CIOps
.ops
:
947 nullptr, sizeof(AtomSelectorEntry
));
948 memset(mPseudoElementRuleHashes
, 0, sizeof(mPseudoElementRuleHashes
));
950 PL_DHashTableInit(&mXULTreeRules
, &RuleHash_TagTable_Ops
, nullptr,
951 sizeof(RuleHashTagTableEntry
));
957 PL_DHashTableFinish(&mAttributeSelectors
);
958 PL_DHashTableFinish(&mAnonBoxRules
);
959 PL_DHashTableFinish(&mIdSelectors
);
960 PL_DHashTableFinish(&mClassSelectors
);
962 PL_DHashTableFinish(&mXULTreeRules
);
964 for (uint32_t i
= 0; i
< ArrayLength(mPseudoElementRuleHashes
); ++i
) {
965 delete mPseudoElementRuleHashes
[i
];
969 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const;
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
;
983 PLDHashTable mXULTreeRules
;
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
;
1006 SizeOfSelectorsEntry(PLDHashEntryHdr
* aHdr
, MallocSizeOf aMallocSizeOf
, void *)
1008 AtomSelectorEntry
* entry
= static_cast<AtomSelectorEntry
*>(aHdr
);
1009 return entry
->mSelectors
.SizeOfExcludingThis(aMallocSizeOf
);
1013 SizeOfKeyframesRuleEntryExcludingThis(nsStringHashKey::KeyType aKey
,
1014 nsCSSKeyframesRule
* const& aData
,
1015 mozilla::MallocSizeOf aMallocSizeOf
,
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
);
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
);
1054 n
+= PL_DHashTableSizeOfExcludingThis(&mXULTreeRules
,
1055 SizeOfRuleHashTableEntry
, aMallocSizeOf
);
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
,
1070 nsTArray
<nsCSSSelector
*>*
1071 RuleCascadeData::AttributeListFor(nsIAtom
* aAttribute
)
1073 AtomSelectorEntry
*entry
=
1074 static_cast<AtomSelectorEntry
*>
1075 (PL_DHashTableOperate(&mAttributeSelectors
, aAttribute
,
1079 return &entry
->mSelectors
;
1082 // -------------------------------
1083 // CSS Style rule processor implementation
1086 nsCSSRuleProcessor::nsCSSRuleProcessor(const sheet_array_type
& aSheets
,
1088 Element
* aScopeElement
)
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 "
1098 for (sheet_array_type::size_type i
= mSheets
.Length(); i
-- != 0; ) {
1099 mSheets
[i
]->AddRuleProcessor(this);
1103 nsCSSRuleProcessor::~nsCSSRuleProcessor()
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
)
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
1129 nsCSSRuleProcessor::ClearSheets()
1131 for (sheet_array_type::size_type i
= mSheets
.Length(); i
-- != 0; ) {
1132 mSheets
[i
]->DropRuleProcessor(this);
1137 /* static */ nsresult
1138 nsCSSRuleProcessor::Startup()
1140 Preferences::AddBoolVarCache(&gSupportVisitedPseudo
, VISITED_PSEUDO_PREF
,
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
);
1175 LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollSliderStyle
);
1176 if (metricResult
!= LookAndFeel::eScrollThumbStyle_Normal
) {
1177 sSystemMetrics
->AppendElement(nsGkAtoms::scrollbar_thumb_proportional
);
1181 LookAndFeel::GetInt(LookAndFeel::eIntID_ImagesInMenus
);
1183 sSystemMetrics
->AppendElement(nsGkAtoms::images_in_menus
);
1187 LookAndFeel::GetInt(LookAndFeel::eIntID_ImagesInButtons
);
1189 sSystemMetrics
->AppendElement(nsGkAtoms::images_in_buttons
);
1193 LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars
);
1195 sSystemMetrics
->AppendElement(nsGkAtoms::overlay_scrollbars
);
1199 LookAndFeel::GetInt(LookAndFeel::eIntID_MenuBarDrag
);
1201 sSystemMetrics
->AppendElement(nsGkAtoms::menubar_drag
);
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
,
1252 if (NS_SUCCEEDED(rv
) && metricResult
) {
1253 sSystemMetrics
->AppendElement(nsGkAtoms::swipe_animation_enabled
);
1256 rv
= LookAndFeel::GetInt(LookAndFeel::eIntID_PhysicalHomeButton
,
1258 if (NS_SUCCEEDED(rv
) && metricResult
) {
1259 sSystemMetrics
->AppendElement(nsGkAtoms::physical_home_button
);
1264 LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsThemeIdentifier
,
1266 nsCSSRuleProcessor::SetWindowsThemeIdentifier(static_cast<uint8_t>(metricResult
));
1267 switch(metricResult
) {
1268 case LookAndFeel::eWindowsTheme_Aero
:
1269 sSystemMetrics
->AppendElement(nsGkAtoms::windows_theme_aero
);
1271 case LookAndFeel::eWindowsTheme_AeroLite
:
1272 sSystemMetrics
->AppendElement(nsGkAtoms::windows_theme_aero_lite
);
1274 case LookAndFeel::eWindowsTheme_LunaBlue
:
1275 sSystemMetrics
->AppendElement(nsGkAtoms::windows_theme_luna_blue
);
1277 case LookAndFeel::eWindowsTheme_LunaOlive
:
1278 sSystemMetrics
->AppendElement(nsGkAtoms::windows_theme_luna_olive
);
1280 case LookAndFeel::eWindowsTheme_LunaSilver
:
1281 sSystemMetrics
->AppendElement(nsGkAtoms::windows_theme_luna_silver
);
1283 case LookAndFeel::eWindowsTheme_Royale
:
1284 sSystemMetrics
->AppendElement(nsGkAtoms::windows_theme_royale
);
1286 case LookAndFeel::eWindowsTheme_Zune
:
1287 sSystemMetrics
->AppendElement(nsGkAtoms::windows_theme_zune
);
1289 case LookAndFeel::eWindowsTheme_Generic
:
1290 sSystemMetrics
->AppendElement(nsGkAtoms::windows_theme_generic
);
1300 nsCSSRuleProcessor::FreeSystemMetrics()
1302 delete sSystemMetrics
;
1303 sSystemMetrics
= nullptr;
1307 nsCSSRuleProcessor::Shutdown()
1309 FreeSystemMetrics();
1313 nsCSSRuleProcessor::HasSystemMetric(nsIAtom
* aMetric
)
1315 if (!sSystemMetrics
&& !InitSystemMetrics()) {
1318 return sSystemMetrics
->IndexOf(aMetric
) != sSystemMetrics
->NoIndex
;
1322 /* static */ uint8_t
1323 nsCSSRuleProcessor::GetWindowsThemeIdentifier()
1325 if (!sSystemMetrics
)
1326 InitSystemMetrics();
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
;
1353 nsCSSRuleProcessor::IsLink(Element
* aElement
)
1355 EventStates state
= aElement
->StyleState();
1356 return state
.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED
| NS_EVENT_STATE_UNVISITED
);
1361 nsCSSRuleProcessor::GetContentStateForVisitedHandling(
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
;
1376 case nsRuleWalker::eRelevantLinkVisited
:
1377 contentState
|= NS_EVENT_STATE_VISITED
;
1379 case nsRuleWalker::eLinksVisitedOrUnvisited
:
1380 contentState
|= NS_EVENT_STATE_UNVISITED
| NS_EVENT_STATE_VISITED
;
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
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();
1440 // skip leading space
1441 while (p
!= p_end
&& nsContentUtils::IsHTMLWhitespace(*p
))
1444 const char16_t
*val_start
= p
;
1446 // look for space or end
1447 while (p
!= p_end
&& !nsContentUtils::IsHTMLWhitespace(*p
))
1450 const char16_t
*val_end
= p
;
1452 if (val_start
< val_end
&&
1453 aValue
.Equals(Substring(val_start
, val_end
), aComparator
))
1456 ++p
; // we know the next character is not whitespace
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
));
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
))
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
);
1523 NS_NOTREACHED("Shouldn't be ending up here");
1529 edgeChildMatches(Element
* aElement
, TreeMatchContext
& aTreeMatchContext
,
1530 bool checkFirst
, bool checkLast
)
1532 nsIContent
*parent
= aElement
->GetParent();
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) &&
1544 aTreeMatchContext
.mNthIndexCache
.
1545 GetNthIndex(aElement
, false, true, true) == 1);
1549 nthChildGenericMatches(Element
* aElement
,
1550 TreeMatchContext
& aTreeMatchContext
,
1551 nsPseudoClassList
* pseudoClass
,
1552 bool isOfType
, bool isFromEnd
)
1554 nsIContent
*parent
= aElement
->GetParent();
1559 if (aTreeMatchContext
.mForStyling
) {
1561 parent
->SetFlags(NODE_HAS_SLOW_SELECTOR
);
1563 parent
->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS
);
1566 const int32_t index
= aTreeMatchContext
.mNthIndexCache
.
1567 GetNthIndex(aElement
, isOfType
, isFromEnd
, false);
1569 // Node is anonymous content (not really a child of its parent).
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.
1581 // Integer division in C does truncation (towards 0). So
1582 // check that the result is nonnegative, and that there was no
1584 const int32_t n
= (index
- b
) / a
;
1585 return n
>= 0 && (a
* n
== index
- b
);
1589 edgeOfTypeMatches(Element
* aElement
, TreeMatchContext
& aTreeMatchContext
,
1590 bool checkFirst
, bool checkLast
)
1592 nsIContent
*parent
= aElement
->GetParent();
1597 if (aTreeMatchContext
.mForStyling
) {
1599 parent
->SetFlags(NODE_HAS_SLOW_SELECTOR
);
1601 parent
->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS
);
1604 return (!checkFirst
||
1605 aTreeMatchContext
.mNthIndexCache
.
1606 GetNthIndex(aElement
, true, false, true) == 1) &&
1608 aTreeMatchContext
.mNthIndexCache
.
1609 GetNthIndex(aElement
, true, true, true) == 1);
1613 checkGenericEmptyMatches(Element
* aElement
,
1614 TreeMatchContext
& aTreeMatchContext
,
1615 bool isWhitespaceSignificant
)
1617 nsIContent
*child
= nullptr;
1620 if (aTreeMatchContext
.mForStyling
)
1621 aElement
->SetFlags(NODE_HAS_EMPTY_SELECTOR
);
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) \
1635 #define CSS_STATE_DEPENDENT_PSEUDO_CLASS(_name, _value, _pref, _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.
1646 static const EventStates sPseudoClassStates
[] = {
1647 #define CSS_PSEUDO_CLASS(_name, _value, _pref) \
1649 #define CSS_STATE_PSEUDO_CLASS(_name, _value, _pref, _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.
1659 static_assert(MOZ_ARRAY_LENGTH(sPseudoClassStates
) ==
1660 nsCSSPseudoClasses::ePseudoClass_NotPseudoClass
+ 1,
1661 "ePseudoClass_NotPseudoClass is no longer at the end of"
1662 "sPseudoClassStates");
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
&&
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.
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".
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
)) {
1705 *aDependence
= true;
1708 EventStates contentState
=
1709 nsCSSRuleProcessor::GetContentStateForVisitedHandling(
1712 aTreeMatchContext
.VisitedHandling(),
1713 aNodeMatchContext
.mIsRelevantLink
);
1714 if (!contentState
.HasAtLeastOneOfStates(aStatesToCheck
)) {
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
)) {
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
))
1766 if (aSelector
->mLowercaseTag
) {
1767 nsIAtom
* selectorTag
=
1768 (aTreeMatchContext
.mIsHTMLDocument
&& aElement
->IsHTML()) ?
1769 aSelector
->mLowercaseTag
: aSelector
->mCasedTag
;
1770 if (selectorTag
!= aElement
->Tag()) {
1775 nsAtomList
* IDList
= aSelector
->mIDList
;
1777 nsIAtom
* id
= aElement
->GetID();
1779 // case sensitivity: bug 93371
1780 const bool isCaseSensitive
=
1781 aTreeMatchContext
.mCompatMode
!= eCompatibility_NavQuirks
;
1783 if (isCaseSensitive
) {
1785 if (IDList
->mAtom
!= id
) {
1788 IDList
= IDList
->mNext
;
1791 // Use EqualsIgnoreASCIICase instead of full on unicode case conversion
1792 // in order to save on performance. This is only used in quirks mode
1794 nsDependentAtomString
id1Str(id
);
1796 if (!nsContentUtils::EqualsIgnoreASCIICase(id1Str
,
1797 nsDependentAtomString(IDList
->mAtom
))) {
1800 IDList
= IDList
->mNext
;
1804 // Element has no id but we have an id selector
1809 nsAtomList
* classList
= aSelector
->mClassList
;
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
1818 // case sensitivity: bug 93371
1819 const bool isCaseSensitive
=
1820 aTreeMatchContext
.mCompatMode
!= eCompatibility_NavQuirks
;
1823 if (!elementClasses
->Contains(classList
->mAtom
,
1825 eCaseMatters
: eIgnoreCase
)) {
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)) {
1857 case nsCSSPseudoClasses::ePseudoClass_mozOnlyWhitespace
:
1858 if (!checkGenericEmptyMatches(aElement
, aTreeMatchContext
, false)) {
1863 case nsCSSPseudoClasses::ePseudoClass_mozEmptyExceptChildrenWithLocalname
:
1865 NS_ASSERTION(pseudoClass
->u
.mString
, "Must have string!");
1866 nsIContent
*child
= nullptr;
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
);
1877 child
= aElement
->GetChildAt(++index
);
1879 (!IsSignificantChild(child
, true, false) ||
1880 (child
->GetNameSpaceID() == aElement
->GetNameSpaceID() &&
1881 child
->Tag()->Equals(nsDependentString(pseudoClass
->u
.mString
)))));
1882 if (child
!= nullptr) {
1888 case nsCSSPseudoClasses::ePseudoClass_lang
:
1890 NS_ASSERTION(nullptr != pseudoClass
->u
.mString
, "null lang parameter");
1891 if (!pseudoClass
->u
.mString
|| !*pseudoClass
->u
.mString
) {
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())) {
1907 // This pseudo-class matched; move on to the next thing
1911 nsIDocument
* doc
= aTreeMatchContext
.mDocument
;
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
1917 doc
->GetContentLanguage(language
);
1919 nsDependentString
langString(pseudoClass
->u
.mString
);
1920 language
.StripWhitespace();
1922 int32_t len
= language
.Length();
1923 while (begin
< len
) {
1924 int32_t end
= language
.FindChar(char16_t(','), begin
);
1925 if (end
== kNotFound
) {
1928 if (nsStyleUtil::DashMatchCompare(Substring(language
, begin
,
1931 nsASCIICaseInsensitiveStringComparator())) {
1937 // This pseudo-class matched
1946 case nsCSSPseudoClasses::ePseudoClass_mozBoundElement
:
1947 if (aTreeMatchContext
.mScopedRoot
!= aElement
) {
1952 case nsCSSPseudoClasses::ePseudoClass_root
:
1953 if (aElement
!= aElement
->OwnerDoc()->GetRootElement()) {
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(),
1965 if (SelectorMatches(aElement
, s
, aNodeMatchContext
,
1966 aTreeMatchContext
)) {
1976 case nsCSSPseudoClasses::ePseudoClass_firstChild
:
1977 if (!edgeChildMatches(aElement
, aTreeMatchContext
, true, false)) {
1982 case nsCSSPseudoClasses::ePseudoClass_firstNode
:
1984 nsIContent
*firstNode
= nullptr;
1985 nsIContent
*parent
= aElement
->GetParent();
1987 if (aTreeMatchContext
.mForStyling
)
1988 parent
->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR
);
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
) {
2003 case nsCSSPseudoClasses::ePseudoClass_lastChild
:
2004 if (!edgeChildMatches(aElement
, aTreeMatchContext
, false, true)) {
2009 case nsCSSPseudoClasses::ePseudoClass_lastNode
:
2011 nsIContent
*lastNode
= nullptr;
2012 nsIContent
*parent
= aElement
->GetParent();
2014 if (aTreeMatchContext
.mForStyling
)
2015 parent
->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR
);
2017 uint32_t index
= parent
->GetChildCount();
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
) {
2030 case nsCSSPseudoClasses::ePseudoClass_onlyChild
:
2031 if (!edgeChildMatches(aElement
, aTreeMatchContext
, true, true)) {
2036 case nsCSSPseudoClasses::ePseudoClass_firstOfType
:
2037 if (!edgeOfTypeMatches(aElement
, aTreeMatchContext
, true, false)) {
2042 case nsCSSPseudoClasses::ePseudoClass_lastOfType
:
2043 if (!edgeOfTypeMatches(aElement
, aTreeMatchContext
, false, true)) {
2048 case nsCSSPseudoClasses::ePseudoClass_onlyOfType
:
2049 if (!edgeOfTypeMatches(aElement
, aTreeMatchContext
, true, true)) {
2054 case nsCSSPseudoClasses::ePseudoClass_nthChild
:
2055 if (!nthChildGenericMatches(aElement
, aTreeMatchContext
, pseudoClass
,
2061 case nsCSSPseudoClasses::ePseudoClass_nthLastChild
:
2062 if (!nthChildGenericMatches(aElement
, aTreeMatchContext
, pseudoClass
,
2068 case nsCSSPseudoClasses::ePseudoClass_nthOfType
:
2069 if (!nthChildGenericMatches(aElement
, aTreeMatchContext
, pseudoClass
,
2075 case nsCSSPseudoClasses::ePseudoClass_nthLastOfType
:
2076 if (!nthChildGenericMatches(aElement
, aTreeMatchContext
, pseudoClass
,
2082 case nsCSSPseudoClasses::ePseudoClass_mozIsHTML
:
2083 if (!aTreeMatchContext
.mIsHTMLDocument
|| !aElement
->IsHTML()) {
2088 case nsCSSPseudoClasses::ePseudoClass_mozSystemMetric
:
2090 nsCOMPtr
<nsIAtom
> metric
= do_GetAtom(pseudoClass
->u
.mString
);
2091 if (!nsCSSRuleProcessor::HasSystemMetric(metric
)) {
2097 case nsCSSPseudoClasses::ePseudoClass_mozLocaleDir
:
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
) {
2114 case nsCSSPseudoClasses::ePseudoClass_mozLWTheme
:
2116 if (aTreeMatchContext
.mDocument
->GetDocumentLWTheme() <=
2117 nsIDocument::Doc_Theme_None
) {
2123 case nsCSSPseudoClasses::ePseudoClass_mozLWThemeBrightText
:
2125 if (aTreeMatchContext
.mDocument
->GetDocumentLWTheme() !=
2126 nsIDocument::Doc_Theme_Bright
) {
2132 case nsCSSPseudoClasses::ePseudoClass_mozLWThemeDarkText
:
2134 if (aTreeMatchContext
.mDocument
->GetDocumentLWTheme() !=
2135 nsIDocument::Doc_Theme_Dark
) {
2141 case nsCSSPseudoClasses::ePseudoClass_mozWindowInactive
:
2142 if (!aTreeMatchContext
.mDocument
->GetDocumentState().
2143 HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE
)) {
2148 case nsCSSPseudoClasses::ePseudoClass_mozTableBorderNonzero
:
2150 if (!aElement
->IsHTML(nsGkAtoms::table
)) {
2153 const nsAttrValue
*val
= aElement
->GetParsedAttr(nsGkAtoms::border
);
2155 (val
->Type() == nsAttrValue::eInteger
&&
2156 val
->GetIntegerValue() == 0)) {
2162 case nsCSSPseudoClasses::ePseudoClass_dir
:
2166 = sPseudoClassStateDependences
[pseudoClass
->mType
];
2167 if (aNodeMatchContext
.mStateMask
.HasAtLeastOneOfStates(states
)) {
2168 *aDependence
= true;
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
)) {
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.
2206 } else if (aTreeMatchContext
.HasSpecifiedScope()) {
2207 if (!aTreeMatchContext
.IsScopeElement(aElement
)) {
2211 if (aElement
!= aElement
->OwnerDoc()->GetRootElement()) {
2218 NS_ABORT_IF_FALSE(false, "How did that happen?");
2221 if (!StateSelectorMatches(aElement
, aSelector
, aNodeMatchContext
,
2222 aTreeMatchContext
, aDependence
,
2230 if (aSelector
->mAttrList
) {
2231 // test for attribute match
2232 if (!aElement
->HasAttrs()) {
2233 // if no attributes on the content, no match
2237 nsAttrSelector
* attr
= aSelector
->mAttrList
;
2238 nsIAtom
* matchAttribute
;
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.
2253 const nsAttrName
* attrName
;
2254 for (uint32_t i
= 0; (attrName
= aElement
->GetAttrNameAt(i
)); ++i
) {
2255 if (attrName
->LocalName() != matchAttribute
) {
2258 if (attr
->mFunction
== NS_ATTR_FUNC_SET
) {
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).
2281 else if (attr
->mFunction
== NS_ATTR_FUNC_EQUALS
) {
2284 AttrValueIs(attr
->mNameSpace
, matchAttribute
, attr
->mValue
,
2285 (!isHTML
|| attr
->mCaseSensitive
) ? eCaseMatters
2288 else if (!aElement
->HasAttr(attr
->mNameSpace
, matchAttribute
)) {
2291 else if (attr
->mFunction
!= NS_ATTR_FUNC_SET
) {
2296 aElement
->GetAttr(attr
->mNameSpace
, matchAttribute
, value
);
2297 NS_ASSERTION(hasAttr
, "HasAttr lied");
2298 result
= AttrMatchesValue(attr
, value
, isHTML
);
2302 } while (attr
&& result
);
2306 // apply SelectorMatches to the negated selectors in the chain
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
;
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()) {
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();
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
2370 nsIContent
*content
= prevElement
->GetParent();
2371 // GetParent could return a document fragment; we only want
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
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
;
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
2426 if (NS_IS_GREEDY_OPERATOR(selector
->mOperator
) &&
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
)) {
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
;
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.
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
2478 if (!data
->mTreeMatchContext
.SetStyleScopeForSelectorMatching(data
->mElement
,
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.
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.
2501 if (!StateSelectorMatches(pdata
->mPseudoElement
, aSelector
, nodeContext
,
2502 data
->mTreeMatchContext
)) {
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
2522 nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData
*aData
)
2524 RuleCascadeData
* cascade
= GetRuleCascade(aData
->mPresContext
);
2527 NodeMatchContext
nodeContext(EventStates(),
2528 nsCSSRuleProcessor::IsLink(aData
->mElement
));
2529 cascade
->mRuleHash
.EnumerateAllRules(aData
->mElement
, aData
, nodeContext
);
2534 nsCSSRuleProcessor::RulesMatching(PseudoElementRuleProcessorData
* aData
)
2536 RuleCascadeData
* cascade
= GetRuleCascade(aData
->mPresContext
);
2539 RuleHash
* ruleHash
= cascade
->mPseudoElementRuleHashes
[aData
->mPseudoType
];
2541 NodeMatchContext
nodeContext(EventStates(),
2542 nsCSSRuleProcessor::IsLink(aData
->mElement
));
2543 ruleHash
->EnumerateAllRules(aData
->mElement
, aData
, nodeContext
);
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
,
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
);
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
,
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
,
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
;
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);
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
) {
2643 nsCSSSelector
* selectorForPseudo
;
2644 if (isPseudoElement
) {
2645 if (selector
->PseudoType() != aPseudoType
) {
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
,
2689 hint
= nsRestyleHint(hint
| possibleChange
);
2697 nsCSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData
* aData
)
2699 return HasStateDependentStyle(aData
,
2701 nsCSSPseudoElements::ePseudo_NotPseudoElement
,
2706 nsCSSRuleProcessor::HasStateDependentStyle(PseudoElementStateRuleProcessorData
* aData
)
2708 return HasStateDependentStyle(aData
,
2709 aData
->mPseudoElement
,
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
;
2732 AttributeEnumFunc(nsCSSSelector
* aSelector
, AttributeEnumData
* aData
)
2734 AttributeRuleProcessorData
*data
= aData
->data
;
2736 if (!data
->mTreeMatchContext
.SetStyleScopeForSelectorMatching(data
->mElement
,
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.
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
);
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.
2806 if (aData
->mAttribute
== nsGkAtoms::id
) {
2807 nsIAtom
* id
= aData
->mElement
->GetID();
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
);
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.
2863 RefreshRuleCascade(aPresContext
);
2865 return (old
!= mRuleCascades
);
2868 /* virtual */ size_t
2869 nsCSSRuleProcessor::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) const
2872 n
+= mSheets
.SizeOfExcludingThis(aMallocSizeOf
);
2873 for (RuleCascadeData
* cascade
= mRuleCascades
; cascade
;
2874 cascade
= cascade
->mNext
) {
2875 n
+= cascade
->SizeOfIncludingThis(aMallocSizeOf
);
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.
2890 nsCSSRuleProcessor::AppendFontFaceRules(
2891 nsPresContext
*aPresContext
,
2892 nsTArray
<nsFontFaceRuleContainer
>& aArray
)
2894 RuleCascadeData
* cascade
= GetRuleCascade(aPresContext
);
2897 if (!aArray
.AppendElements(cascade
->mFontFaceRules
))
2905 nsCSSRuleProcessor::KeyframesRuleForName(nsPresContext
* aPresContext
,
2906 const nsString
& aName
)
2908 RuleCascadeData
* cascade
= GetRuleCascade(aPresContext
);
2911 return cascade
->mKeyframesRuleTable
.Get(aName
);
2917 nsCSSCounterStyleRule
*
2918 nsCSSRuleProcessor::CounterStyleRuleForName(nsPresContext
* aPresContext
,
2919 const nsAString
& aName
)
2921 RuleCascadeData
* cascade
= GetRuleCascade(aPresContext
);
2924 return cascade
->mCounterStyleRuleTable
.Get(aName
);
2930 // Append all the currently-active page rules to aArray. Return
2931 // true for success and false for failure.
2933 nsCSSRuleProcessor::AppendPageRules(
2934 nsPresContext
* aPresContext
,
2935 nsTArray
<nsCSSPageRule
*>& aArray
)
2937 RuleCascadeData
* cascade
= GetRuleCascade(aPresContext
);
2940 if (!aArray
.AppendElements(cascade
->mPageRules
)) {
2949 nsCSSRuleProcessor::AppendFontFeatureValuesRules(
2950 nsPresContext
*aPresContext
,
2951 nsTArray
<nsCSSFontFeatureValuesRule
*>& aArray
)
2953 RuleCascadeData
* cascade
= GetRuleCascade(aPresContext
);
2956 if (!aArray
.AppendElements(cascade
->mFontFeatureValuesRules
))
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;
2973 RuleCascadeData
*next
= data
->mNext
;
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.
2985 EventStates
ComputeSelectorStateDependence(nsCSSSelector
& aSelector
)
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
) {
2995 states
|= sPseudoClassStateDependences
[pseudoClass
->mType
];
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
;
3025 case nsCSSPseudoClasses::ePseudoClass_mozWindowInactive
: {
3026 aCascade
->mSelectorDocumentStates
|= NS_DOCUMENT_STATE_WINDOW_INACTIVE
;
3029 case nsCSSPseudoClasses::ePseudoClass_mozTableBorderNonzero
: {
3030 nsTArray
<nsCSSSelector
*> *array
=
3031 aCascade
->AttributeListFor(nsGkAtoms::border
);
3035 array
->AppendElement(aSelectorInTopLevel
);
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
,
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
,
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
);
3092 array
->AppendElement(aSelectorInTopLevel
);
3093 if (attr
->mLowercaseAttr
!= attr
->mCasedAttr
) {
3094 array
= aCascade
->AttributeListFor(attr
->mLowercaseAttr
);
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
)) {
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
];
3131 ruleHash
= new RuleHash(cascade
->mQuirksMode
);
3133 // Out of memory; give up
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
));
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
));
3168 NS_NOTREACHED("Unexpected pseudo type");
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.
3189 if (!AddSelector(cascade
, selector
, selector
)) {
3197 struct PerWeightDataListItem
: public RuleSelectorPair
{
3198 PerWeightDataListItem(css::StyleRule
* aRule
, nsCSSSelector
* aSelector
)
3199 : RuleSelectorPair(aRule
, aSelector
)
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
{
3208 PL_ARENA_ALLOCATE(mem
, &aArena
, aSize
);
3212 PerWeightDataListItem
*mNext
;
3215 struct PerWeightData
{
3217 : mRuleSelectorPairs(nullptr)
3218 , mTail(&mRuleSelectorPairs
)
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
));
3237 MatchWeightEntry(PLDHashTable
*table
, const PLDHashEntryHdr
*hdr
,
3240 const RuleByWeightEntry
*entry
= (const RuleByWeightEntry
*)hdr
;
3241 return entry
->data
.mWeight
== NS_PTR_TO_INT32(key
);
3245 InitWeightEntry(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
,
3248 RuleByWeightEntry
* entry
= static_cast<RuleByWeightEntry
*>(hdr
);
3249 new (entry
) RuleByWeightEntry();
3253 static const PLDHashTableOps gRulesByWeightOps
= {
3258 PL_DHashMoveEntryStub
,
3259 PL_DHashClearEntryStub
,
3260 PL_DHashFinalizeStub
,
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
,
3273 : mPresContext(aPresContext
),
3274 mFontFaceRules(aFontFaceRules
),
3275 mKeyframesRules(aKeyframesRules
),
3276 mFontFeatureValuesRules(aFontFeatureValuesRules
),
3277 mPageRules(aPageRules
),
3278 mCounterStyleRules(aCounterStyleRules
),
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
);
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
;
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
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.
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
),
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
);
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
))
3361 else if (css::Rule::FONT_FACE_RULE
== type
) {
3362 nsCSSFontFaceRule
*fontFaceRule
= static_cast<nsCSSFontFaceRule
*>(aRule
);
3363 nsFontFaceRuleContainer
*ptr
= data
->mFontFaceRules
.AppendElement();
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
)) {
3376 else if (css::Rule::FONT_FEATURE_VALUES_RULE
== type
) {
3377 nsCSSFontFeatureValuesRule
*fontFeatureValuesRule
=
3378 static_cast<nsCSSFontFeatureValuesRule
*>(aRule
);
3379 if (!data
->mFontFeatureValuesRules
.AppendElement(fontFeatureValuesRule
)) {
3383 else if (css::Rule::PAGE_RULE
== type
) {
3384 nsCSSPageRule
* pageRule
= static_cast<nsCSSPageRule
*>(aRule
);
3385 if (!data
->mPageRules
.AppendElement(pageRule
)) {
3389 else if (css::Rule::COUNTER_STYLE_RULE
== type
) {
3390 nsCSSCounterStyleRule
* counterStyleRule
=
3391 static_cast<nsCSSCounterStyleRule
*>(aRule
);
3392 if (!data
->mCounterStyleRules
.AppendElement(counterStyleRule
)) {
3400 nsCSSRuleProcessor::CascadeSheet(CSSStyleSheet
* aSheet
, CascadeEnumData
* aData
)
3402 if (aSheet
->IsApplicable() &&
3403 aSheet
->UseForPresentation(aData
->mPresContext
, aData
->mCacheKey
) &&
3405 CSSStyleSheet
* child
= aSheet
->mInner
->mFirstChild
;
3407 CascadeSheet(child
, aData
);
3408 child
= child
->mNext
;
3411 if (!aSheet
->mInner
->mOrderedRules
.EnumerateForwards(CascadeRuleEnumFunc
,
3418 static int CompareWeightData(const void* aArg1
, const void* aArg2
,
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
) :
3430 mWeightArray(aArrayData
)
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
;
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
;
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
;
3491 if (mSheets
.Length() != 0) {
3492 nsAutoPtr
<RuleCascadeData
> newCascade(
3493 new RuleCascadeData(aPresContext
->Medium(),
3494 eCompatibility_NavQuirks
== aPresContext
->CompatibilityMode()));
3496 CascadeEnumData
data(aPresContext
, newCascade
->mFontFaceRules
,
3497 newCascade
->mKeyframesRules
,
3498 newCascade
->mFontFeatureValuesRules
,
3499 newCascade
->mPageRules
,
3500 newCascade
->mCounterStyleRules
,
3501 newCascade
->mCacheKey
,
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
;
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();
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
;
3571 SelectorMatchesTree(aElement
, next
, aTreeMatchContext
, false)) {
3576 aSelectorList
= aSelectorList
->mNext
;
3582 // TreeMatchContext and AncestorFilter out of line methods
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
;
3602 ancestors
.AppendElement(cur
);
3603 cur
= cur
->GetParentElementCrossingShadowRoot();
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
]);
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
;
3624 ancestors
.AppendElement(cur
);
3625 cur
= cur
->GetParentElementCrossingShadowRoot();
3628 // Now push them in reverse order.
3629 for (uint32_t i
= ancestors
.Length(); i
-- != 0; ) {
3630 PushStyleScope(ancestors
[i
]);
3636 AncestorFilter::PushAncestor(Element
*aElement
)
3638 MOZ_ASSERT(mFilter
);
3640 uint32_t oldLength
= mHashes
.Length();
3642 mPopTargets
.AppendElement(oldLength
);
3644 mElements
.AppendElement(aElement
);
3646 mHashes
.AppendElement(aElement
->Tag()->hash());
3647 nsIAtom
*id
= aElement
->GetID();
3649 mHashes
.AppendElement(id
->hash());
3651 const nsAttrValue
*classes
= aElement
->GetClasses();
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
]);
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);
3676 mElements
.TruncateLength(popTargetLength
-1);
3679 uint32_t oldLength
= mHashes
.Length();
3680 for (uint32_t i
= newLength
; i
< oldLength
; ++i
) {
3681 mFilter
->remove(mHashes
[i
]);
3683 mHashes
.TruncateLength(newLength
);
3688 AncestorFilter::AssertHasAllAncestors(Element
*aElement
) const
3690 Element
* cur
= aElement
->GetParentElementCrossingShadowRoot();
3692 MOZ_ASSERT(mElements
.Contains(cur
));
3693 cur
= cur
->GetParentElementCrossingShadowRoot();
3698 TreeMatchContext::AssertHasAllStyleScopes(Element
* aElement
) const
3700 Element
* cur
= aElement
->GetParentElementCrossingShadowRoot();
3702 if (cur
->IsScopedStyleRoot()) {
3703 MOZ_ASSERT(mStyleScopes
.Contains(cur
));
3705 cur
= cur
->GetParentElementCrossingShadowRoot();