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