Line data Source code
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/. */
6 :
7 : /*
8 : * style rule processor for CSS style sheets, responsible for selector
9 : * matching and cascading
10 : */
11 :
12 : #include "nsCSSRuleProcessor.h"
13 :
14 : #include "nsAutoPtr.h"
15 : #include "nsRuleProcessorData.h"
16 : #include <algorithm>
17 : #include "nsIAtom.h"
18 : #include "PLDHashTable.h"
19 : #include "nsICSSPseudoComparator.h"
20 : #include "mozilla/MemoryReporting.h"
21 : #include "mozilla/css/ImportRule.h"
22 : #include "mozilla/css/StyleRule.h"
23 : #include "mozilla/css/GroupRule.h"
24 : #include "nsIDocument.h"
25 : #include "nsPresContext.h"
26 : #include "nsGkAtoms.h"
27 : #include "nsUnicharUtils.h"
28 : #include "nsError.h"
29 : #include "nsRuleWalker.h"
30 : #include "nsCSSPseudoClasses.h"
31 : #include "nsCSSPseudoElements.h"
32 : #include "nsIContent.h"
33 : #include "nsCOMPtr.h"
34 : #include "nsHashKeys.h"
35 : #include "nsStyleUtil.h"
36 : #include "nsQuickSort.h"
37 : #include "nsAttrValue.h"
38 : #include "nsAttrValueInlines.h"
39 : #include "nsAttrName.h"
40 : #include "nsTArray.h"
41 : #include "nsContentUtils.h"
42 : #include "nsMediaList.h"
43 : #include "nsCSSRules.h"
44 : #include "nsCSSCounterStyleRule.h"
45 : #include "nsCSSFontFaceRule.h"
46 : #include "nsStyleSet.h"
47 : #include "mozilla/dom/Element.h"
48 : #include "nsNthIndexCache.h"
49 : #include "mozilla/ArrayUtils.h"
50 : #include "mozilla/EventStates.h"
51 : #include "mozilla/Preferences.h"
52 : #include "mozilla/ServoStyleSet.h"
53 : #include "mozilla/LookAndFeel.h"
54 : #include "mozilla/Likely.h"
55 : #include "mozilla/OperatorNewExtensions.h"
56 : #include "mozilla/TypedEnumBits.h"
57 : #include "RuleProcessorCache.h"
58 : #include "nsIDOMMutationEvent.h"
59 :
60 : using namespace mozilla;
61 : using namespace mozilla::dom;
62 :
63 : typedef ArenaAllocator<4096, 8> CascadeAllocator;
64 :
65 : #define VISITED_PSEUDO_PREF "layout.css.visited_links_enabled"
66 :
67 : static bool gSupportVisitedPseudo = true;
68 :
69 : static nsTArray< nsCOMPtr<nsIAtom> >* sSystemMetrics = 0;
70 :
71 : #ifdef XP_WIN
72 : uint8_t nsCSSRuleProcessor::sWinThemeId = LookAndFeel::eWindowsTheme_Generic;
73 : #endif
74 :
75 : /**
76 : * A struct representing a given CSS rule and a particular selector
77 : * from that rule's selector list.
78 : */
79 : struct RuleSelectorPair {
80 14586 : RuleSelectorPair(css::StyleRule* aRule, nsCSSSelector* aSelector)
81 14586 : : mRule(aRule), mSelector(aSelector) {}
82 : // If this class ever grows a destructor, deal with
83 : // PerWeightDataListItem appropriately.
84 :
85 : css::StyleRule* mRule;
86 : nsCSSSelector* mSelector; // which of |mRule|'s selectors
87 : };
88 :
89 : #define NS_IS_ANCESTOR_OPERATOR(ch) \
90 : ((ch) == char16_t(' ') || (ch) == char16_t('>'))
91 :
92 : /**
93 : * A struct representing a particular rule in an ordered list of rules
94 : * (the ordering depending on the weight of mSelector and the order of
95 : * our rules to start with).
96 : */
97 : struct RuleValue : RuleSelectorPair {
98 : enum {
99 : eMaxAncestorHashes = 4
100 : };
101 :
102 14586 : RuleValue(const RuleSelectorPair& aRuleSelectorPair, int32_t aIndex,
103 14586 : bool aQuirksMode) :
104 : RuleSelectorPair(aRuleSelectorPair),
105 14586 : mIndex(aIndex)
106 : {
107 14586 : CollectAncestorHashes(aQuirksMode);
108 14586 : }
109 :
110 : int32_t mIndex; // High index means high weight/order.
111 : uint32_t mAncestorSelectorHashes[eMaxAncestorHashes];
112 :
113 : private:
114 14586 : void CollectAncestorHashes(bool aQuirksMode) {
115 : // Collect up our mAncestorSelectorHashes. It's not clear whether it's
116 : // better to stop once we've found eMaxAncestorHashes of them or to keep
117 : // going and preferentially collect information from selectors higher up the
118 : // chain... Let's do the former for now.
119 14586 : size_t hashIndex = 0;
120 23478 : for (nsCSSSelector* sel = mSelector->mNext; sel; sel = sel->mNext) {
121 8998 : if (!NS_IS_ANCESTOR_OPERATOR(sel->mOperator)) {
122 : // |sel| is going to select something that's not actually one of our
123 : // ancestors, so don't add it to mAncestorSelectorHashes. But keep
124 : // going, because it'll select a sibling of one of our ancestors, so its
125 : // ancestors would be our ancestors too.
126 905 : continue;
127 : }
128 :
129 : // Now sel is supposed to select one of our ancestors. Grab
130 : // whatever info we can from it into mAncestorSelectorHashes.
131 : // But in qurks mode, don't grab IDs and classes because those
132 : // need to be matched case-insensitively.
133 8093 : if (!aQuirksMode) {
134 6537 : nsAtomList* ids = sel->mIDList;
135 11109 : while (ids) {
136 2367 : mAncestorSelectorHashes[hashIndex++] = ids->mAtom->hash();
137 2367 : if (hashIndex == eMaxAncestorHashes) {
138 81 : return;
139 : }
140 2286 : ids = ids->mNext;
141 : }
142 :
143 6456 : nsAtomList* classes = sel->mClassList;
144 10726 : while (classes) {
145 2135 : mAncestorSelectorHashes[hashIndex++] = classes->mAtom->hash();
146 2135 : if (hashIndex == eMaxAncestorHashes) {
147 0 : return;
148 : }
149 2135 : classes = classes->mNext;
150 : }
151 : }
152 :
153 : // Only put in the tag name if it's all-lowercase. Otherwise we run into
154 : // trouble because we may test the wrong one of mLowercaseTag and
155 : // mCasedTag against the filter.
156 8012 : if (sel->mLowercaseTag && sel->mCasedTag == sel->mLowercaseTag) {
157 3089 : mAncestorSelectorHashes[hashIndex++] = sel->mLowercaseTag->hash();
158 3089 : if (hashIndex == eMaxAncestorHashes) {
159 25 : return;
160 : }
161 : }
162 : }
163 :
164 115986 : while (hashIndex != eMaxAncestorHashes) {
165 50753 : mAncestorSelectorHashes[hashIndex++] = 0;
166 : }
167 : }
168 : };
169 :
170 : // ------------------------------
171 : // Rule hash table
172 : //
173 :
174 : // Uses any of the sets of ops below.
175 16471 : struct RuleHashTableEntry : public PLDHashEntryHdr {
176 : // If you add members that have heap allocated memory be sure to change the
177 : // logic in SizeOfRuleHashTable().
178 : // Auto length 1, because we always have at least one entry in mRules.
179 : AutoTArray<RuleValue, 1> mRules;
180 : };
181 :
182 7644 : struct RuleHashTagTableEntry : public RuleHashTableEntry {
183 : // If you add members that have heap allocated memory be sure to change the
184 : // logic in RuleHash::SizeOf{In,Ex}cludingThis.
185 : nsCOMPtr<nsIAtom> mTag;
186 : };
187 :
188 : static PLDHashNumber
189 713 : RuleHash_CIHashKey(const void *key)
190 : {
191 713 : nsIAtom *atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
192 :
193 1426 : nsAutoString str;
194 713 : atom->ToString(str);
195 713 : nsContentUtils::ASCIIToLower(str);
196 1426 : return HashString(str);
197 : }
198 :
199 : static inline nsCSSSelector*
200 7397 : SubjectSelectorForRuleHash(const PLDHashEntryHdr *hdr)
201 : {
202 7397 : auto entry = static_cast<const RuleHashTableEntry*>(hdr);
203 7397 : nsCSSSelector* selector = entry->mRules[0].mSelector;
204 7397 : if (selector->IsPseudoElement()) {
205 204 : selector = selector->mNext;
206 : }
207 7397 : return selector;
208 : }
209 :
210 : static inline bool
211 455 : CIMatchAtoms(const void* key, nsIAtom *entry_atom)
212 : {
213 455 : auto match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
214 :
215 : // Check for case-sensitive match first.
216 455 : if (match_atom == entry_atom) {
217 455 : return true;
218 : }
219 :
220 : // Use EqualsIgnoreASCIICase instead of full on unicode case conversion
221 : // in order to save on performance. This is only used in quirks mode
222 : // anyway.
223 : return
224 0 : nsContentUtils::EqualsIgnoreASCIICase(nsDependentAtomString(entry_atom),
225 0 : nsDependentAtomString(match_atom));
226 : }
227 :
228 : static inline bool
229 7241 : CSMatchAtoms(const void* key, nsIAtom *entry_atom)
230 : {
231 7241 : auto match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
232 7241 : return match_atom == entry_atom;
233 : }
234 :
235 : static bool
236 81 : RuleHash_ClassCIMatchEntry(const PLDHashEntryHdr *hdr, const void *key)
237 : {
238 81 : return CIMatchAtoms(key, SubjectSelectorForRuleHash(hdr)->mClassList->mAtom);
239 : }
240 :
241 : static bool
242 75 : RuleHash_IdCIMatchEntry(const PLDHashEntryHdr *hdr, const void *key)
243 : {
244 75 : return CIMatchAtoms(key, SubjectSelectorForRuleHash(hdr)->mIDList->mAtom);
245 : }
246 :
247 : static bool
248 5063 : RuleHash_ClassCSMatchEntry(const PLDHashEntryHdr *hdr, const void *key)
249 : {
250 5063 : return CSMatchAtoms(key, SubjectSelectorForRuleHash(hdr)->mClassList->mAtom);
251 : }
252 :
253 : static bool
254 2178 : RuleHash_IdCSMatchEntry(const PLDHashEntryHdr *hdr, const void *key)
255 : {
256 2178 : return CSMatchAtoms(key, SubjectSelectorForRuleHash(hdr)->mIDList->mAtom);
257 : }
258 :
259 : static void
260 2357 : RuleHash_InitEntry(PLDHashEntryHdr *hdr, const void *key)
261 : {
262 2357 : RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(hdr);
263 2357 : new (KnownNotNull, entry) RuleHashTableEntry();
264 2357 : }
265 :
266 : static void
267 590 : RuleHash_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
268 : {
269 590 : RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(hdr);
270 590 : entry->~RuleHashTableEntry();
271 590 : }
272 :
273 : static void
274 2940 : RuleHash_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
275 : PLDHashEntryHdr *to)
276 : {
277 2940 : NS_PRECONDITION(from != to, "This is not going to work!");
278 : RuleHashTableEntry *oldEntry =
279 : const_cast<RuleHashTableEntry*>(
280 2940 : static_cast<const RuleHashTableEntry*>(from));
281 2940 : auto* newEntry = new (KnownNotNull, to) RuleHashTableEntry();
282 2940 : newEntry->mRules.SwapElements(oldEntry->mRules);
283 2940 : oldEntry->~RuleHashTableEntry();
284 2940 : }
285 :
286 : static bool
287 8700 : RuleHash_TagTable_MatchEntry(const PLDHashEntryHdr *hdr, const void *key)
288 : {
289 8700 : nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
290 8700 : nsIAtom *entry_atom = static_cast<const RuleHashTagTableEntry*>(hdr)->mTag;
291 :
292 8700 : return match_atom == entry_atom;
293 : }
294 :
295 : static void
296 2133 : RuleHash_TagTable_InitEntry(PLDHashEntryHdr *hdr, const void *key)
297 : {
298 2133 : RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>(hdr);
299 2133 : new (KnownNotNull, entry) RuleHashTagTableEntry();
300 2133 : entry->mTag = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
301 2133 : }
302 :
303 : static void
304 51 : RuleHash_TagTable_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
305 : {
306 51 : RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>(hdr);
307 51 : entry->~RuleHashTagTableEntry();
308 51 : }
309 :
310 : static void
311 2730 : RuleHash_TagTable_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
312 : PLDHashEntryHdr *to)
313 : {
314 2730 : NS_PRECONDITION(from != to, "This is not going to work!");
315 : RuleHashTagTableEntry *oldEntry =
316 : const_cast<RuleHashTagTableEntry*>(
317 2730 : static_cast<const RuleHashTagTableEntry*>(from));
318 2730 : auto* newEntry = new (KnownNotNull, to) RuleHashTagTableEntry();
319 2730 : newEntry->mTag.swap(oldEntry->mTag);
320 2730 : newEntry->mRules.SwapElements(oldEntry->mRules);
321 2730 : oldEntry->~RuleHashTagTableEntry();
322 2730 : }
323 :
324 : static PLDHashNumber
325 4820 : RuleHash_NameSpaceTable_HashKey(const void *key)
326 : {
327 4820 : return HashGeneric(key);
328 : }
329 :
330 : static bool
331 4715 : RuleHash_NameSpaceTable_MatchEntry(const PLDHashEntryHdr *hdr, const void *key)
332 : {
333 : const RuleHashTableEntry *entry =
334 4715 : static_cast<const RuleHashTableEntry*>(hdr);
335 :
336 4715 : nsCSSSelector* selector = entry->mRules[0].mSelector;
337 4715 : if (selector->IsPseudoElement()) {
338 24 : selector = selector->mNext;
339 : }
340 4715 : return NS_PTR_TO_INT32(key) == selector->mNameSpace;
341 : }
342 :
343 : static const PLDHashTableOps RuleHash_TagTable_Ops = {
344 : PLDHashTable::HashVoidPtrKeyStub,
345 : RuleHash_TagTable_MatchEntry,
346 : RuleHash_TagTable_MoveEntry,
347 : RuleHash_TagTable_ClearEntry,
348 : RuleHash_TagTable_InitEntry
349 : };
350 :
351 : // Case-sensitive ops.
352 : static const PLDHashTableOps RuleHash_ClassTable_CSOps = {
353 : PLDHashTable::HashVoidPtrKeyStub,
354 : RuleHash_ClassCSMatchEntry,
355 : RuleHash_MoveEntry,
356 : RuleHash_ClearEntry,
357 : RuleHash_InitEntry
358 : };
359 :
360 : // Case-insensitive ops.
361 : static const PLDHashTableOps RuleHash_ClassTable_CIOps = {
362 : RuleHash_CIHashKey,
363 : RuleHash_ClassCIMatchEntry,
364 : RuleHash_MoveEntry,
365 : RuleHash_ClearEntry,
366 : RuleHash_InitEntry
367 : };
368 :
369 : // Case-sensitive ops.
370 : static const PLDHashTableOps RuleHash_IdTable_CSOps = {
371 : PLDHashTable::HashVoidPtrKeyStub,
372 : RuleHash_IdCSMatchEntry,
373 : RuleHash_MoveEntry,
374 : RuleHash_ClearEntry,
375 : RuleHash_InitEntry
376 : };
377 :
378 : // Case-insensitive ops.
379 : static const PLDHashTableOps RuleHash_IdTable_CIOps = {
380 : RuleHash_CIHashKey,
381 : RuleHash_IdCIMatchEntry,
382 : RuleHash_MoveEntry,
383 : RuleHash_ClearEntry,
384 : RuleHash_InitEntry
385 : };
386 :
387 : static const PLDHashTableOps RuleHash_NameSpaceTable_Ops = {
388 : RuleHash_NameSpaceTable_HashKey,
389 : RuleHash_NameSpaceTable_MatchEntry,
390 : RuleHash_MoveEntry,
391 : RuleHash_ClearEntry,
392 : RuleHash_InitEntry
393 : };
394 :
395 : #undef RULE_HASH_STATS
396 : #undef PRINT_UNIVERSAL_RULES
397 :
398 : #ifdef RULE_HASH_STATS
399 : #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO ++(var_); PR_END_MACRO
400 : #else
401 : #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO PR_END_MACRO
402 : #endif
403 :
404 : struct NodeMatchContext;
405 :
406 : class RuleHash {
407 : public:
408 : explicit RuleHash(bool aQuirksMode);
409 : ~RuleHash();
410 : void AppendRule(const RuleSelectorPair &aRuleInfo);
411 : void EnumerateAllRules(Element* aElement, ElementDependentRuleProcessorData* aData,
412 : NodeMatchContext& aNodeMatchContext);
413 :
414 : size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
415 : size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
416 :
417 : protected:
418 : typedef nsTArray<RuleValue> RuleValueList;
419 : void AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
420 : const RuleSelectorPair& aRuleInfo);
421 : void AppendUniversalRule(const RuleSelectorPair& aRuleInfo);
422 :
423 : int32_t mRuleCount;
424 :
425 : PLDHashTable mIdTable;
426 : PLDHashTable mClassTable;
427 : PLDHashTable mTagTable;
428 : PLDHashTable mNameSpaceTable;
429 : RuleValueList mUniversalRules;
430 :
431 : struct EnumData {
432 : const RuleValue* mCurValue;
433 : const RuleValue* mEnd;
434 : };
435 : EnumData* mEnumList;
436 : int32_t mEnumListSize;
437 :
438 : bool mQuirksMode;
439 :
440 15317 : inline EnumData ToEnumData(const RuleValueList& arr) {
441 15317 : EnumData data = { arr.Elements(), arr.Elements() + arr.Length() };
442 15317 : return data;
443 : }
444 :
445 : #ifdef RULE_HASH_STATS
446 : uint32_t mUniversalSelectors;
447 : uint32_t mNameSpaceSelectors;
448 : uint32_t mTagSelectors;
449 : uint32_t mClassSelectors;
450 : uint32_t mIdSelectors;
451 :
452 : uint32_t mElementsMatched;
453 :
454 : uint32_t mElementUniversalCalls;
455 : uint32_t mElementNameSpaceCalls;
456 : uint32_t mElementTagCalls;
457 : uint32_t mElementClassCalls;
458 : uint32_t mElementIdCalls;
459 : #endif // RULE_HASH_STATS
460 : };
461 :
462 211 : RuleHash::RuleHash(bool aQuirksMode)
463 : : mRuleCount(0),
464 : mIdTable(aQuirksMode ? &RuleHash_IdTable_CIOps
465 : : &RuleHash_IdTable_CSOps,
466 : sizeof(RuleHashTableEntry)),
467 : mClassTable(aQuirksMode ? &RuleHash_ClassTable_CIOps
468 : : &RuleHash_ClassTable_CSOps,
469 : sizeof(RuleHashTableEntry)),
470 : mTagTable(&RuleHash_TagTable_Ops, sizeof(RuleHashTagTableEntry)),
471 : mNameSpaceTable(&RuleHash_NameSpaceTable_Ops, sizeof(RuleHashTableEntry)),
472 : mUniversalRules(0),
473 : mEnumList(nullptr), mEnumListSize(0),
474 211 : mQuirksMode(aQuirksMode)
475 : #ifdef RULE_HASH_STATS
476 : ,
477 : mUniversalSelectors(0),
478 : mNameSpaceSelectors(0),
479 : mTagSelectors(0),
480 : mClassSelectors(0),
481 : mIdSelectors(0),
482 : mElementsMatched(0),
483 : mElementUniversalCalls(0),
484 : mElementNameSpaceCalls(0),
485 : mElementTagCalls(0),
486 : mElementClassCalls(0),
487 : mElementIdCalls(0)
488 : #endif
489 : {
490 211 : MOZ_COUNT_CTOR(RuleHash);
491 211 : }
492 :
493 8 : RuleHash::~RuleHash()
494 : {
495 4 : MOZ_COUNT_DTOR(RuleHash);
496 : #ifdef RULE_HASH_STATS
497 : printf(
498 : "RuleHash(%p):\n"
499 : " Selectors: Universal (%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
500 : " Content Nodes: Elements(%u)\n"
501 : " Element Calls: Universal(%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
502 : static_cast<void*>(this),
503 : mUniversalSelectors, mNameSpaceSelectors, mTagSelectors,
504 : mClassSelectors, mIdSelectors,
505 : mElementsMatched,
506 : mElementUniversalCalls, mElementNameSpaceCalls, mElementTagCalls,
507 : mElementClassCalls, mElementIdCalls);
508 : #ifdef PRINT_UNIVERSAL_RULES
509 : {
510 : if (mUniversalRules.Length() > 0) {
511 : printf(" Universal rules:\n");
512 : for (uint32_t i = 0; i < mUniversalRules.Length(); ++i) {
513 : RuleValue* value = &(mUniversalRules[i]);
514 : nsAutoString selectorText;
515 : uint32_t lineNumber = value->mRule->GetLineNumber();
516 : RefPtr<CSSStyleSheet> cssSheet = value->mRule->GetStyleSheet();
517 : value->mSelector->ToString(selectorText, cssSheet);
518 :
519 : printf(" line %d, %s\n",
520 : lineNumber, NS_ConvertUTF16toUTF8(selectorText).get());
521 : }
522 : }
523 : }
524 : #endif // PRINT_UNIVERSAL_RULES
525 : #endif // RULE_HASH_STATS
526 : // Rule Values are arena allocated no need to delete them. Their destructor
527 : // isn't doing any cleanup. So we dont even bother to enumerate through
528 : // the hash tables and call their destructors.
529 4 : if (nullptr != mEnumList) {
530 3 : delete [] mEnumList;
531 : }
532 4 : }
533 :
534 7484 : void RuleHash::AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
535 : const RuleSelectorPair& aRuleInfo)
536 : {
537 : // Get a new or existing entry.
538 7484 : auto entry = static_cast<RuleHashTableEntry*>(aTable->Add(aKey, fallible));
539 7484 : if (!entry)
540 0 : return;
541 7484 : entry->mRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode));
542 : }
543 :
544 : static void
545 6915 : AppendRuleToTagTable(PLDHashTable* aTable, nsIAtom* aKey,
546 : const RuleValue& aRuleInfo)
547 : {
548 : // Get a new or exisiting entry
549 6915 : auto entry = static_cast<RuleHashTagTableEntry*>(aTable->Add(aKey, fallible));
550 6915 : if (!entry)
551 0 : return;
552 :
553 6915 : entry->mRules.AppendElement(aRuleInfo);
554 : }
555 :
556 195 : void RuleHash::AppendUniversalRule(const RuleSelectorPair& aRuleInfo)
557 : {
558 195 : mUniversalRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode));
559 195 : }
560 :
561 14140 : void RuleHash::AppendRule(const RuleSelectorPair& aRuleInfo)
562 : {
563 14140 : nsCSSSelector *selector = aRuleInfo.mSelector;
564 14140 : if (selector->IsPseudoElement()) {
565 696 : selector = selector->mNext;
566 : }
567 14140 : if (nullptr != selector->mIDList) {
568 2755 : AppendRuleToTable(&mIdTable, selector->mIDList->mAtom, aRuleInfo);
569 : RULE_HASH_STAT_INCREMENT(mIdSelectors);
570 : }
571 11385 : else if (nullptr != selector->mClassList) {
572 4494 : AppendRuleToTable(&mClassTable, selector->mClassList->mAtom, aRuleInfo);
573 : RULE_HASH_STAT_INCREMENT(mClassSelectors);
574 : }
575 6891 : else if (selector->mLowercaseTag) {
576 6461 : RuleValue ruleValue(aRuleInfo, mRuleCount++, mQuirksMode);
577 6461 : AppendRuleToTagTable(&mTagTable, selector->mLowercaseTag, ruleValue);
578 : RULE_HASH_STAT_INCREMENT(mTagSelectors);
579 12922 : if (selector->mCasedTag &&
580 12922 : selector->mCasedTag != selector->mLowercaseTag) {
581 8 : AppendRuleToTagTable(&mTagTable, selector->mCasedTag, ruleValue);
582 : RULE_HASH_STAT_INCREMENT(mTagSelectors);
583 : }
584 : }
585 430 : else if (kNameSpaceID_Unknown != selector->mNameSpace) {
586 235 : AppendRuleToTable(&mNameSpaceTable,
587 470 : NS_INT32_TO_PTR(selector->mNameSpace), aRuleInfo);
588 : RULE_HASH_STAT_INCREMENT(mNameSpaceSelectors);
589 : }
590 : else { // universal tag selector
591 195 : AppendUniversalRule(aRuleInfo);
592 : RULE_HASH_STAT_INCREMENT(mUniversalSelectors);
593 : }
594 14140 : }
595 :
596 : // this should cover practically all cases so we don't need to reallocate
597 : #define MIN_ENUM_LIST_SIZE 8
598 :
599 : #ifdef RULE_HASH_STATS
600 : #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
601 : (var_) += (list_).Length()
602 : #else
603 : #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
604 : PR_BEGIN_MACRO PR_END_MACRO
605 : #endif
606 :
607 : static inline
608 : void ContentEnumFunc(const RuleValue &value, nsCSSSelector* selector,
609 : ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
610 : AncestorFilter *ancestorFilter);
611 :
612 8768 : void RuleHash::EnumerateAllRules(Element* aElement, ElementDependentRuleProcessorData* aData,
613 : NodeMatchContext& aNodeContext)
614 : {
615 8768 : int32_t nameSpace = aElement->GetNameSpaceID();
616 8768 : nsIAtom* tag = aElement->NodeInfo()->NameAtom();
617 8768 : nsIAtom* id = aElement->GetID();
618 8768 : const nsAttrValue* classList = aElement->GetClasses();
619 :
620 8768 : MOZ_ASSERT(tag, "How could we not have a tag?");
621 :
622 8768 : int32_t classCount = classList ? classList->GetAtomCount() : 0;
623 :
624 : // assume 1 universal, tag, id, and namespace, rather than wasting
625 : // time counting
626 8768 : int32_t testCount = classCount + 4;
627 :
628 8768 : if (mEnumListSize < testCount) {
629 55 : delete [] mEnumList;
630 55 : mEnumListSize = std::max(testCount, MIN_ENUM_LIST_SIZE);
631 110 : mEnumList = new EnumData[mEnumListSize];
632 : }
633 :
634 8768 : int32_t valueCount = 0;
635 : RULE_HASH_STAT_INCREMENT(mElementsMatched);
636 :
637 8768 : if (mUniversalRules.Length() != 0) { // universal rules
638 4594 : mEnumList[valueCount++] = ToEnumData(mUniversalRules);
639 : RULE_HASH_STAT_INCREMENT_LIST_COUNT(mUniversalRules, mElementUniversalCalls);
640 : }
641 : // universal rules within the namespace
642 8768 : if (kNameSpaceID_Unknown != nameSpace && mNameSpaceTable.EntryCount() > 0) {
643 : auto entry = static_cast<RuleHashTableEntry*>
644 4585 : (mNameSpaceTable.Search(NS_INT32_TO_PTR(nameSpace)));
645 4585 : if (entry) {
646 4525 : mEnumList[valueCount++] = ToEnumData(entry->mRules);
647 : RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementNameSpaceCalls);
648 : }
649 : }
650 8768 : if (mTagTable.EntryCount() > 0) {
651 7626 : auto entry = static_cast<RuleHashTableEntry*>(mTagTable.Search(tag));
652 7626 : if (entry) {
653 3738 : mEnumList[valueCount++] = ToEnumData(entry->mRules);
654 : RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementTagCalls);
655 : }
656 : }
657 8768 : if (id && mIdTable.EntryCount() > 0) {
658 2832 : auto entry = static_cast<RuleHashTableEntry*>(mIdTable.Search(id));
659 2832 : if (entry) {
660 471 : mEnumList[valueCount++] = ToEnumData(entry->mRules);
661 : RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementIdCalls);
662 : }
663 : }
664 8768 : if (mClassTable.EntryCount() > 0) {
665 12317 : for (int32_t index = 0; index < classCount; ++index) {
666 : auto entry = static_cast<RuleHashTableEntry*>
667 5164 : (mClassTable.Search(classList->AtomAt(index)));
668 5164 : if (entry) {
669 1989 : mEnumList[valueCount++] = ToEnumData(entry->mRules);
670 : RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementClassCalls);
671 : }
672 : }
673 : }
674 8768 : NS_ASSERTION(valueCount <= testCount, "values exceeded list size");
675 :
676 8768 : if (valueCount > 0) {
677 : AncestorFilter *filter =
678 10689 : aData->mTreeMatchContext.mAncestorFilter.HasFilter() ?
679 10689 : &aData->mTreeMatchContext.mAncestorFilter : nullptr;
680 : #ifdef DEBUG
681 5687 : if (filter) {
682 5002 : filter->AssertHasAllAncestors(aElement);
683 : }
684 : #endif
685 : // Merge the lists while there are still multiple lists to merge.
686 314581 : while (valueCount > 1) {
687 154447 : int32_t valueIndex = 0;
688 154447 : int32_t lowestRuleIndex = mEnumList[valueIndex].mCurValue->mIndex;
689 453201 : for (int32_t index = 1; index < valueCount; ++index) {
690 298754 : int32_t ruleIndex = mEnumList[index].mCurValue->mIndex;
691 298754 : if (ruleIndex < lowestRuleIndex) {
692 103408 : valueIndex = index;
693 103408 : lowestRuleIndex = ruleIndex;
694 : }
695 : }
696 154447 : const RuleValue *cur = mEnumList[valueIndex].mCurValue;
697 154447 : ContentEnumFunc(*cur, cur->mSelector, aData, aNodeContext, filter);
698 154447 : cur++;
699 154447 : if (cur == mEnumList[valueIndex].mEnd) {
700 9630 : mEnumList[valueIndex] = mEnumList[--valueCount];
701 : } else {
702 144817 : mEnumList[valueIndex].mCurValue = cur;
703 : }
704 : }
705 :
706 : // Fast loop over single value.
707 38438 : for (const RuleValue *value = mEnumList[0].mCurValue,
708 5687 : *end = mEnumList[0].mEnd;
709 38438 : value != end; ++value) {
710 32751 : ContentEnumFunc(*value, value->mSelector, aData, aNodeContext, filter);
711 : }
712 : }
713 8768 : }
714 :
715 : static size_t
716 12 : SizeOfRuleHashTable(const PLDHashTable& aTable, MallocSizeOf aMallocSizeOf)
717 : {
718 12 : size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
719 15 : for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
720 3 : auto entry = static_cast<RuleHashTableEntry*>(iter.Get());
721 3 : n += entry->mRules.ShallowSizeOfExcludingThis(aMallocSizeOf);
722 : }
723 12 : return n;
724 : }
725 :
726 : size_t
727 2 : RuleHash::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
728 : {
729 2 : size_t n = 0;
730 :
731 2 : n += SizeOfRuleHashTable(mIdTable, aMallocSizeOf);
732 :
733 2 : n += SizeOfRuleHashTable(mClassTable, aMallocSizeOf);
734 :
735 2 : n += SizeOfRuleHashTable(mTagTable, aMallocSizeOf);
736 :
737 2 : n += SizeOfRuleHashTable(mNameSpaceTable, aMallocSizeOf);
738 :
739 2 : n += mUniversalRules.ShallowSizeOfExcludingThis(aMallocSizeOf);
740 :
741 2 : return n;
742 : }
743 :
744 : size_t
745 0 : RuleHash::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
746 : {
747 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
748 : }
749 :
750 : //--------------------------------
751 :
752 : /**
753 : * A struct that stores an nsCSSSelector pointer along side a pointer to
754 : * the rightmost nsCSSSelector in the selector. For example, for
755 : *
756 : * .main p > span
757 : *
758 : * if mSelector points to the |p| nsCSSSelector, mRightmostSelector would
759 : * point to the |span| nsCSSSelector.
760 : *
761 : * Both mSelector and mRightmostSelector are always top-level selectors,
762 : * i.e. they aren't selectors within a :not() or :-moz-any().
763 : */
764 : struct SelectorPair
765 : {
766 19679 : SelectorPair(nsCSSSelector* aSelector, nsCSSSelector* aRightmostSelector)
767 19679 : : mSelector(aSelector), mRightmostSelector(aRightmostSelector)
768 : {
769 19679 : MOZ_ASSERT(aSelector);
770 19679 : MOZ_ASSERT(mRightmostSelector);
771 19679 : }
772 : SelectorPair(const SelectorPair& aOther) = default;
773 : nsCSSSelector* const mSelector;
774 : nsCSSSelector* const mRightmostSelector;
775 : };
776 :
777 : // A hash table mapping atoms to lists of selectors
778 12502 : struct AtomSelectorEntry : public PLDHashEntryHdr {
779 : nsIAtom *mAtom;
780 : // Auto length 2, because a decent fraction of these arrays ends up
781 : // with 2 elements, and each entry is cheap.
782 : AutoTArray<SelectorPair, 2> mSelectors;
783 : };
784 :
785 : static void
786 850 : AtomSelector_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
787 : {
788 850 : (static_cast<AtomSelectorEntry*>(hdr))->~AtomSelectorEntry();
789 850 : }
790 :
791 : static void
792 3588 : AtomSelector_InitEntry(PLDHashEntryHdr *hdr, const void *key)
793 : {
794 3588 : AtomSelectorEntry *entry = static_cast<AtomSelectorEntry*>(hdr);
795 3588 : new (KnownNotNull, entry) AtomSelectorEntry();
796 3588 : entry->mAtom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
797 3588 : }
798 :
799 : static void
800 4032 : AtomSelector_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
801 : PLDHashEntryHdr *to)
802 : {
803 4032 : NS_PRECONDITION(from != to, "This is not going to work!");
804 : AtomSelectorEntry *oldEntry =
805 4032 : const_cast<AtomSelectorEntry*>(static_cast<const AtomSelectorEntry*>(from));
806 4032 : auto* newEntry = new (KnownNotNull, to) AtomSelectorEntry();
807 4032 : newEntry->mAtom = oldEntry->mAtom;
808 4032 : newEntry->mSelectors.SwapElements(oldEntry->mSelectors);
809 4032 : oldEntry->~AtomSelectorEntry();
810 4032 : }
811 :
812 : static bool
813 299 : AtomSelector_CIMatchEntry(const PLDHashEntryHdr *hdr, const void *key)
814 : {
815 299 : const AtomSelectorEntry *entry = static_cast<const AtomSelectorEntry*>(hdr);
816 299 : return CIMatchAtoms(key, entry->mAtom);
817 : }
818 :
819 : // Case-sensitive ops.
820 : static const PLDHashTableOps AtomSelector_CSOps = {
821 : PLDHashTable::HashVoidPtrKeyStub,
822 : PLDHashTable::MatchEntryStub,
823 : AtomSelector_MoveEntry,
824 : AtomSelector_ClearEntry,
825 : AtomSelector_InitEntry
826 : };
827 :
828 : // Case-insensitive ops.
829 : static const PLDHashTableOps AtomSelector_CIOps = {
830 : RuleHash_CIHashKey,
831 : AtomSelector_CIMatchEntry,
832 : AtomSelector_MoveEntry,
833 : AtomSelector_ClearEntry,
834 : AtomSelector_InitEntry
835 : };
836 :
837 : //--------------------------------
838 :
839 : struct RuleCascadeData {
840 42 : RuleCascadeData(nsIAtom *aMedium, bool aQuirksMode)
841 42 : : mRuleHash(aQuirksMode),
842 : mStateSelectors(),
843 : mSelectorDocumentStates(0),
844 : mClassSelectors(aQuirksMode ? &AtomSelector_CIOps
845 : : &AtomSelector_CSOps,
846 : sizeof(AtomSelectorEntry)),
847 : mIdSelectors(aQuirksMode ? &AtomSelector_CIOps
848 : : &AtomSelector_CSOps,
849 : sizeof(AtomSelectorEntry)),
850 : // mAttributeSelectors is matching on the attribute _name_, not the
851 : // value, and we case-fold names at parse-time, so this is a
852 : // case-sensitive match.
853 : mAttributeSelectors(&AtomSelector_CSOps, sizeof(AtomSelectorEntry)),
854 : mAnonBoxRules(&RuleHash_TagTable_Ops, sizeof(RuleHashTagTableEntry)),
855 : #ifdef MOZ_XUL
856 : mXULTreeRules(&RuleHash_TagTable_Ops, sizeof(RuleHashTagTableEntry)),
857 : #endif
858 : mKeyframesRuleTable(),
859 : mCounterStyleRuleTable(),
860 : mCacheKey(aMedium),
861 : mNext(nullptr),
862 42 : mQuirksMode(aQuirksMode)
863 : {
864 42 : memset(mPseudoElementRuleHashes, 0, sizeof(mPseudoElementRuleHashes));
865 42 : }
866 :
867 1 : ~RuleCascadeData()
868 1 : {
869 26 : for (uint32_t i = 0; i < ArrayLength(mPseudoElementRuleHashes); ++i) {
870 25 : delete mPseudoElementRuleHashes[i];
871 : }
872 1 : }
873 :
874 : size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
875 :
876 : RuleHash mRuleHash;
877 : RuleHash* mPseudoElementRuleHashes[
878 : static_cast<CSSPseudoElementTypeBase>(CSSPseudoElementType::Count)];
879 : nsTArray<nsCSSRuleProcessor::StateSelector> mStateSelectors;
880 : EventStates mSelectorDocumentStates;
881 : PLDHashTable mClassSelectors;
882 : PLDHashTable mIdSelectors;
883 : nsTArray<nsCSSSelector*> mPossiblyNegatedClassSelectors;
884 : nsTArray<nsCSSSelector*> mPossiblyNegatedIDSelectors;
885 : PLDHashTable mAttributeSelectors;
886 : PLDHashTable mAnonBoxRules;
887 : #ifdef MOZ_XUL
888 : PLDHashTable mXULTreeRules;
889 : #endif
890 :
891 : nsTArray<nsFontFaceRuleContainer> mFontFaceRules;
892 : nsTArray<nsCSSKeyframesRule*> mKeyframesRules;
893 : nsTArray<nsCSSFontFeatureValuesRule*> mFontFeatureValuesRules;
894 : nsTArray<nsCSSPageRule*> mPageRules;
895 : nsTArray<nsCSSCounterStyleRule*> mCounterStyleRules;
896 :
897 : nsDataHashtable<nsStringHashKey, nsCSSKeyframesRule*> mKeyframesRuleTable;
898 : // The hashtable doesn't need to hold a strong reference to the name
899 : // atom, because nsCSSCounterStyleRule always does. If the name changes
900 : // we need to discard this table and rebuild it anyway.
901 : nsDataHashtable<nsPtrHashKey<nsIAtom>,
902 : nsCSSCounterStyleRule*> mCounterStyleRuleTable;
903 :
904 : // Looks up or creates the appropriate list in |mAttributeSelectors|.
905 : // Returns null only on allocation failure.
906 : nsTArray<SelectorPair>* AttributeListFor(nsIAtom* aAttribute);
907 :
908 : nsMediaQueryResultCacheKey mCacheKey;
909 : RuleCascadeData* mNext; // for a different medium
910 :
911 : const bool mQuirksMode;
912 : };
913 :
914 : static size_t
915 6 : SizeOfSelectorsHashTable(const PLDHashTable& aTable, MallocSizeOf aMallocSizeOf)
916 : {
917 6 : size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
918 6 : for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) {
919 0 : auto entry = static_cast<AtomSelectorEntry*>(iter.Get());
920 0 : n += entry->mSelectors.ShallowSizeOfExcludingThis(aMallocSizeOf);
921 : }
922 6 : return n;
923 : }
924 :
925 : size_t
926 2 : RuleCascadeData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
927 : {
928 2 : size_t n = aMallocSizeOf(this);
929 :
930 2 : n += mRuleHash.SizeOfExcludingThis(aMallocSizeOf);
931 52 : for (uint32_t i = 0; i < ArrayLength(mPseudoElementRuleHashes); ++i) {
932 50 : if (mPseudoElementRuleHashes[i])
933 0 : n += mPseudoElementRuleHashes[i]->SizeOfIncludingThis(aMallocSizeOf);
934 : }
935 :
936 2 : n += mStateSelectors.ShallowSizeOfExcludingThis(aMallocSizeOf);
937 :
938 2 : n += SizeOfSelectorsHashTable(mIdSelectors, aMallocSizeOf);
939 2 : n += SizeOfSelectorsHashTable(mClassSelectors, aMallocSizeOf);
940 :
941 2 : n += mPossiblyNegatedClassSelectors.ShallowSizeOfExcludingThis(aMallocSizeOf);
942 2 : n += mPossiblyNegatedIDSelectors.ShallowSizeOfExcludingThis(aMallocSizeOf);
943 :
944 2 : n += SizeOfSelectorsHashTable(mAttributeSelectors, aMallocSizeOf);
945 2 : n += SizeOfRuleHashTable(mAnonBoxRules, aMallocSizeOf);
946 : #ifdef MOZ_XUL
947 2 : n += SizeOfRuleHashTable(mXULTreeRules, aMallocSizeOf);
948 : #endif
949 :
950 2 : n += mFontFaceRules.ShallowSizeOfExcludingThis(aMallocSizeOf);
951 2 : n += mKeyframesRules.ShallowSizeOfExcludingThis(aMallocSizeOf);
952 2 : n += mFontFeatureValuesRules.ShallowSizeOfExcludingThis(aMallocSizeOf);
953 2 : n += mPageRules.ShallowSizeOfExcludingThis(aMallocSizeOf);
954 2 : n += mCounterStyleRules.ShallowSizeOfExcludingThis(aMallocSizeOf);
955 :
956 2 : n += mKeyframesRuleTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
957 2 : for (auto iter = mKeyframesRuleTable.ConstIter(); !iter.Done(); iter.Next()) {
958 : // We don't own the nsCSSKeyframesRule objects so we don't count them. We
959 : // do care about the size of the keys' nsAString members' buffers though.
960 : //
961 : // Note that we depend on nsStringHashKey::GetKey() returning a reference,
962 : // since otherwise aKey would be a copy of the string key and we would not
963 : // be measuring the right object here.
964 0 : n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
965 : }
966 :
967 2 : return n;
968 : }
969 :
970 : nsTArray<SelectorPair>*
971 7460 : RuleCascadeData::AttributeListFor(nsIAtom* aAttribute)
972 : {
973 : auto entry = static_cast<AtomSelectorEntry*>
974 7460 : (mAttributeSelectors.Add(aAttribute, fallible));
975 7460 : if (!entry)
976 0 : return nullptr;
977 7460 : return &entry->mSelectors;
978 : }
979 :
980 : // -------------------------------
981 : // CSS Style rule processor implementation
982 : //
983 :
984 22 : nsCSSRuleProcessor::nsCSSRuleProcessor(const sheet_array_type& aSheets,
985 : SheetType aSheetType,
986 : Element* aScopeElement,
987 : nsCSSRuleProcessor*
988 : aPreviousCSSRuleProcessor,
989 22 : bool aIsShared)
990 44 : : nsCSSRuleProcessor(sheet_array_type(aSheets), aSheetType, aScopeElement,
991 44 : aPreviousCSSRuleProcessor, aIsShared)
992 : {
993 22 : }
994 :
995 54 : nsCSSRuleProcessor::nsCSSRuleProcessor(sheet_array_type&& aSheets,
996 : SheetType aSheetType,
997 : Element* aScopeElement,
998 : nsCSSRuleProcessor*
999 : aPreviousCSSRuleProcessor,
1000 54 : bool aIsShared)
1001 : : mSheets(aSheets)
1002 : , mRuleCascades(nullptr)
1003 : , mPreviousCacheKey(aPreviousCSSRuleProcessor
1004 : ? aPreviousCSSRuleProcessor->CloneMQCacheKey()
1005 : : UniquePtr<nsMediaQueryResultCacheKey>())
1006 : , mLastPresContext(nullptr)
1007 : , mScopeElement(aScopeElement)
1008 : , mStyleSetRefCnt(0)
1009 : , mSheetType(aSheetType)
1010 : , mIsShared(aIsShared)
1011 : , mMustGatherDocumentRules(aIsShared)
1012 : , mInRuleProcessorCache(false)
1013 : #ifdef DEBUG
1014 54 : , mDocumentRulesAndCacheKeyValid(false)
1015 : #endif
1016 : {
1017 54 : NS_ASSERTION(!!mScopeElement == (aSheetType == SheetType::ScopedDoc),
1018 : "aScopeElement must be specified iff aSheetType is "
1019 : "eScopedDocSheet");
1020 198 : for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) {
1021 144 : mSheets[i]->AddRuleProcessor(this);
1022 : }
1023 54 : }
1024 :
1025 45 : nsCSSRuleProcessor::~nsCSSRuleProcessor()
1026 : {
1027 15 : if (mInRuleProcessorCache) {
1028 0 : RuleProcessorCache::RemoveRuleProcessor(this);
1029 : }
1030 15 : MOZ_ASSERT(!mExpirationState.IsTracked());
1031 15 : MOZ_ASSERT(mStyleSetRefCnt == 0);
1032 15 : ClearSheets();
1033 15 : ClearRuleCascades();
1034 45 : }
1035 :
1036 160 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSRuleProcessor)
1037 46 : NS_INTERFACE_MAP_ENTRY(nsIStyleRuleProcessor)
1038 0 : NS_INTERFACE_MAP_END
1039 :
1040 145 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsCSSRuleProcessor)
1041 96 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsCSSRuleProcessor)
1042 :
1043 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsCSSRuleProcessor)
1044 :
1045 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsCSSRuleProcessor)
1046 0 : tmp->ClearSheets();
1047 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mScopeElement)
1048 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1049 :
1050 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsCSSRuleProcessor)
1051 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSheets)
1052 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScopeElement)
1053 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1054 :
1055 : void
1056 15 : nsCSSRuleProcessor::ClearSheets()
1057 : {
1058 46 : for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) {
1059 31 : mSheets[i]->DropRuleProcessor(this);
1060 : }
1061 15 : mSheets.Clear();
1062 15 : }
1063 :
1064 : /* static */ void
1065 3 : nsCSSRuleProcessor::Startup()
1066 : {
1067 : Preferences::AddBoolVarCache(&gSupportVisitedPseudo, VISITED_PSEUDO_PREF,
1068 3 : true);
1069 3 : }
1070 :
1071 : /* static */ void
1072 125 : nsCSSRuleProcessor::InitSystemMetrics()
1073 : {
1074 125 : if (sSystemMetrics)
1075 123 : return;
1076 :
1077 2 : MOZ_ASSERT(NS_IsMainThread());
1078 :
1079 2 : sSystemMetrics = new nsTArray< nsCOMPtr<nsIAtom> >;
1080 :
1081 : /***************************************************************************
1082 : * ANY METRICS ADDED HERE SHOULD ALSO BE ADDED AS MEDIA QUERIES IN *
1083 : * nsMediaFeatures.cpp *
1084 : ***************************************************************************/
1085 :
1086 : int32_t metricResult =
1087 2 : LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollArrowStyle);
1088 2 : if (metricResult & LookAndFeel::eScrollArrow_StartBackward) {
1089 0 : sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_start_backward);
1090 : }
1091 2 : if (metricResult & LookAndFeel::eScrollArrow_StartForward) {
1092 0 : sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_start_forward);
1093 : }
1094 2 : if (metricResult & LookAndFeel::eScrollArrow_EndBackward) {
1095 0 : sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_end_backward);
1096 : }
1097 2 : if (metricResult & LookAndFeel::eScrollArrow_EndForward) {
1098 0 : sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_end_forward);
1099 : }
1100 :
1101 2 : metricResult =
1102 2 : LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollSliderStyle);
1103 2 : if (metricResult != LookAndFeel::eScrollThumbStyle_Normal) {
1104 2 : sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_thumb_proportional);
1105 : }
1106 :
1107 2 : metricResult =
1108 2 : LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars);
1109 2 : if (metricResult) {
1110 0 : sSystemMetrics->AppendElement(nsGkAtoms::overlay_scrollbars);
1111 : }
1112 :
1113 2 : metricResult =
1114 2 : LookAndFeel::GetInt(LookAndFeel::eIntID_MenuBarDrag);
1115 2 : if (metricResult) {
1116 2 : sSystemMetrics->AppendElement(nsGkAtoms::menubar_drag);
1117 : }
1118 :
1119 : nsresult rv =
1120 2 : LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsDefaultTheme, &metricResult);
1121 2 : if (NS_SUCCEEDED(rv) && metricResult) {
1122 0 : sSystemMetrics->AppendElement(nsGkAtoms::windows_default_theme);
1123 : }
1124 :
1125 2 : rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacGraphiteTheme, &metricResult);
1126 2 : if (NS_SUCCEEDED(rv) && metricResult) {
1127 0 : sSystemMetrics->AppendElement(nsGkAtoms::mac_graphite_theme);
1128 : }
1129 :
1130 2 : rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacYosemiteTheme, &metricResult);
1131 2 : if (NS_SUCCEEDED(rv) && metricResult) {
1132 0 : sSystemMetrics->AppendElement(nsGkAtoms::mac_yosemite_theme);
1133 : }
1134 :
1135 2 : rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsAccentColorApplies, &metricResult);
1136 2 : if (NS_SUCCEEDED(rv) && metricResult) {
1137 0 : sSystemMetrics->AppendElement(nsGkAtoms::windows_accent_color_applies);
1138 : }
1139 :
1140 2 : rv = LookAndFeel::GetInt(LookAndFeel::eIntID_DWMCompositor, &metricResult);
1141 2 : if (NS_SUCCEEDED(rv) && metricResult) {
1142 0 : sSystemMetrics->AppendElement(nsGkAtoms::windows_compositor);
1143 : }
1144 :
1145 2 : rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsGlass, &metricResult);
1146 2 : if (NS_SUCCEEDED(rv) && metricResult) {
1147 0 : sSystemMetrics->AppendElement(nsGkAtoms::windows_glass);
1148 : }
1149 :
1150 2 : rv = LookAndFeel::GetInt(LookAndFeel::eIntID_ColorPickerAvailable, &metricResult);
1151 2 : if (NS_SUCCEEDED(rv) && metricResult) {
1152 2 : sSystemMetrics->AppendElement(nsGkAtoms::color_picker_available);
1153 : }
1154 :
1155 2 : rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsClassic, &metricResult);
1156 2 : if (NS_SUCCEEDED(rv) && metricResult) {
1157 0 : sSystemMetrics->AppendElement(nsGkAtoms::windows_classic);
1158 : }
1159 :
1160 2 : rv = LookAndFeel::GetInt(LookAndFeel::eIntID_TouchEnabled, &metricResult);
1161 2 : if (NS_SUCCEEDED(rv) && metricResult) {
1162 0 : sSystemMetrics->AppendElement(nsGkAtoms::touch_enabled);
1163 : }
1164 :
1165 : rv = LookAndFeel::GetInt(LookAndFeel::eIntID_SwipeAnimationEnabled,
1166 2 : &metricResult);
1167 2 : if (NS_SUCCEEDED(rv) && metricResult) {
1168 0 : sSystemMetrics->AppendElement(nsGkAtoms::swipe_animation_enabled);
1169 : }
1170 :
1171 : rv = LookAndFeel::GetInt(LookAndFeel::eIntID_PhysicalHomeButton,
1172 2 : &metricResult);
1173 2 : if (NS_SUCCEEDED(rv) && metricResult) {
1174 0 : sSystemMetrics->AppendElement(nsGkAtoms::physical_home_button);
1175 : }
1176 :
1177 : #ifdef XP_WIN
1178 : if (NS_SUCCEEDED(
1179 : LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsThemeIdentifier,
1180 : &metricResult))) {
1181 : nsCSSRuleProcessor::SetWindowsThemeIdentifier(static_cast<uint8_t>(metricResult));
1182 : switch(metricResult) {
1183 : case LookAndFeel::eWindowsTheme_Aero:
1184 : sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero);
1185 : break;
1186 : case LookAndFeel::eWindowsTheme_AeroLite:
1187 : sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero_lite);
1188 : break;
1189 : case LookAndFeel::eWindowsTheme_LunaBlue:
1190 : sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_blue);
1191 : break;
1192 : case LookAndFeel::eWindowsTheme_LunaOlive:
1193 : sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_olive);
1194 : break;
1195 : case LookAndFeel::eWindowsTheme_LunaSilver:
1196 : sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_silver);
1197 : break;
1198 : case LookAndFeel::eWindowsTheme_Royale:
1199 : sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_royale);
1200 : break;
1201 : case LookAndFeel::eWindowsTheme_Zune:
1202 : sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_zune);
1203 : break;
1204 : case LookAndFeel::eWindowsTheme_Generic:
1205 : sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_generic);
1206 : break;
1207 : }
1208 : }
1209 : #endif
1210 : }
1211 :
1212 : /* static */ void
1213 0 : nsCSSRuleProcessor::FreeSystemMetrics()
1214 : {
1215 0 : delete sSystemMetrics;
1216 0 : sSystemMetrics = nullptr;
1217 0 : }
1218 :
1219 : /* static */ void
1220 0 : nsCSSRuleProcessor::Shutdown()
1221 : {
1222 0 : FreeSystemMetrics();
1223 0 : }
1224 :
1225 : /* static */ bool
1226 125 : nsCSSRuleProcessor::HasSystemMetric(nsIAtom* aMetric)
1227 : {
1228 125 : nsCSSRuleProcessor::InitSystemMetrics();
1229 125 : return sSystemMetrics->IndexOf(aMetric) != sSystemMetrics->NoIndex;
1230 : }
1231 :
1232 : #ifdef XP_WIN
1233 : /* static */ uint8_t
1234 : nsCSSRuleProcessor::GetWindowsThemeIdentifier()
1235 : {
1236 : nsCSSRuleProcessor::InitSystemMetrics();
1237 : return sWinThemeId;
1238 : }
1239 : #endif
1240 :
1241 : /* static */
1242 : EventStates
1243 2501 : nsCSSRuleProcessor::GetContentState(Element* aElement, bool aUsingPrivateBrowsing)
1244 : {
1245 2501 : EventStates state = aElement->StyleState();
1246 :
1247 : // If we are not supposed to mark visited links as such, be sure to
1248 : // flip the bits appropriately. We want to do this here, rather
1249 : // than in GetContentStateForVisitedHandling, so that we don't
1250 : // expose that :visited support is disabled to the Web page.
1251 2501 : if (state.HasState(NS_EVENT_STATE_VISITED) &&
1252 0 : (!gSupportVisitedPseudo ||
1253 0 : aElement->OwnerDoc()->IsBeingUsedAsImage() ||
1254 : aUsingPrivateBrowsing)) {
1255 0 : state &= ~NS_EVENT_STATE_VISITED;
1256 0 : state |= NS_EVENT_STATE_UNVISITED;
1257 : }
1258 2501 : return state;
1259 : }
1260 :
1261 : /* static */
1262 : EventStates
1263 2501 : nsCSSRuleProcessor::GetContentState(Element* aElement, const TreeMatchContext& aTreeMatchContext)
1264 : {
1265 : return nsCSSRuleProcessor::GetContentState(
1266 : aElement,
1267 2501 : aTreeMatchContext.mUsingPrivateBrowsing
1268 2501 : );
1269 : }
1270 :
1271 : /* static */
1272 : EventStates
1273 0 : nsCSSRuleProcessor::GetContentState(Element* aElement)
1274 : {
1275 0 : nsILoadContext* loadContext = aElement->OwnerDoc()->GetLoadContext();
1276 0 : bool usingPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
1277 0 : return nsCSSRuleProcessor::GetContentState(aElement, usingPrivateBrowsing);
1278 : }
1279 :
1280 : /* static */
1281 : bool
1282 35881 : nsCSSRuleProcessor::IsLink(const Element* aElement)
1283 : {
1284 35881 : EventStates state = aElement->StyleState();
1285 35881 : return state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED);
1286 : }
1287 :
1288 : /* static */
1289 : EventStates
1290 16978 : nsCSSRuleProcessor::GetContentStateForVisitedHandling(
1291 : Element* aElement,
1292 : nsRuleWalker::VisitedHandlingType aVisitedHandling,
1293 : bool aIsRelevantLink)
1294 : {
1295 : // It's unnecessary to call GetContentState() here (which may flip visited to
1296 : // unvisited) since this function will remove both unvisited and visited if
1297 : // either is set and produce a new value.
1298 16978 : EventStates state = aElement->StyleState();
1299 16978 : if (state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)) {
1300 0 : MOZ_ASSERT(IsLink(aElement), "IsLink() should match state");
1301 0 : state &= ~(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED);
1302 0 : if (aIsRelevantLink) {
1303 0 : switch (aVisitedHandling) {
1304 : case nsRuleWalker::eRelevantLinkUnvisited:
1305 0 : state |= NS_EVENT_STATE_UNVISITED;
1306 0 : break;
1307 : case nsRuleWalker::eRelevantLinkVisited:
1308 0 : state |= NS_EVENT_STATE_VISITED;
1309 0 : break;
1310 : case nsRuleWalker::eLinksVisitedOrUnvisited:
1311 0 : state |= NS_EVENT_STATE_UNVISITED | NS_EVENT_STATE_VISITED;
1312 0 : break;
1313 : }
1314 : } else {
1315 0 : state |= NS_EVENT_STATE_UNVISITED;
1316 : }
1317 : }
1318 16978 : return state;
1319 : }
1320 :
1321 : /**
1322 : * A |NodeMatchContext| has data about matching a selector (without
1323 : * combinators) against a single node. It contains only input to the
1324 : * matching.
1325 : *
1326 : * Unlike |RuleProcessorData|, which is similar, a |NodeMatchContext|
1327 : * can vary depending on the selector matching process. In other words,
1328 : * there might be multiple NodeMatchContexts corresponding to a single
1329 : * node, but only one possible RuleProcessorData.
1330 : */
1331 : struct NodeMatchContext {
1332 : // In order to implement nsCSSRuleProcessor::HasStateDependentStyle,
1333 : // we need to be able to see if a node might match an
1334 : // event-state-dependent selector for any value of that event state.
1335 : // So mStateMask contains the states that should NOT be tested.
1336 : //
1337 : // NOTE: For |mStateMask| to work correctly, it's important that any
1338 : // change that changes multiple state bits include all those state
1339 : // bits in the notification. Otherwise, if multiple states change but
1340 : // we do separate notifications then we might determine the style is
1341 : // not state-dependent when it really is (e.g., determining that a
1342 : // :hover:active rule no longer matches when both states are unset).
1343 : const EventStates mStateMask;
1344 :
1345 : // Is this link the unique link whose visitedness can affect the style
1346 : // of the node being matched? (That link is the nearest link to the
1347 : // node being matched that is itself or an ancestor.)
1348 : //
1349 : // Always false when TreeMatchContext::mForStyling is false. (We
1350 : // could figure it out for SelectorListMatches, but we're starting
1351 : // from the middle of the selector list when doing
1352 : // Has{Attribute,State}DependentStyle, so we can't tell. So when
1353 : // mForStyling is false, we have to assume we don't know.)
1354 : const bool mIsRelevantLink;
1355 :
1356 49534 : NodeMatchContext(EventStates aStateMask, bool aIsRelevantLink)
1357 49534 : : mStateMask(aStateMask)
1358 49534 : , mIsRelevantLink(aIsRelevantLink)
1359 : {
1360 49534 : }
1361 : };
1362 :
1363 : /**
1364 : * Additional information about a selector (without combinators) that is
1365 : * being matched.
1366 : */
1367 : enum class SelectorMatchesFlags : uint8_t {
1368 : NONE = 0,
1369 :
1370 : // The selector's flags are unknown. This happens when you don't know
1371 : // if you're starting from the top of a selector. Only used in cases
1372 : // where it's acceptable for matching to return a false positive.
1373 : // (It's not OK to return a false negative.)
1374 : UNKNOWN = 1 << 0,
1375 :
1376 : // The selector is part of a compound selector which has been split in
1377 : // half, where the other half is a pseudo-element. The current
1378 : // selector is not a pseudo-element itself.
1379 : HAS_PSEUDO_ELEMENT = 1 << 1,
1380 :
1381 : // The selector is part of an argument to a functional pseudo-class or
1382 : // pseudo-element.
1383 : IS_PSEUDO_CLASS_ARGUMENT = 1 << 2
1384 : };
1385 768 : MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SelectorMatchesFlags)
1386 :
1387 : // Return whether the selector matches conditions for the :active and
1388 : // :hover quirk.
1389 0 : static inline bool ActiveHoverQuirkMatches(nsCSSSelector* aSelector,
1390 : SelectorMatchesFlags aSelectorFlags)
1391 : {
1392 0 : if (aSelector->HasTagSelector() || aSelector->mAttrList ||
1393 0 : aSelector->mIDList || aSelector->mClassList ||
1394 0 : aSelector->IsPseudoElement() ||
1395 : // Having this quirk means that some selectors will no longer match,
1396 : // so it's better to return false when we aren't sure (i.e., the
1397 : // flags are unknown).
1398 0 : aSelectorFlags & (SelectorMatchesFlags::UNKNOWN |
1399 0 : SelectorMatchesFlags::HAS_PSEUDO_ELEMENT |
1400 256 : SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) {
1401 0 : return false;
1402 : }
1403 :
1404 : // No pseudo-class other than :active and :hover.
1405 0 : for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
1406 0 : pseudoClass; pseudoClass = pseudoClass->mNext) {
1407 0 : if (pseudoClass->mType != CSSPseudoClassType::hover &&
1408 0 : pseudoClass->mType != CSSPseudoClassType::active) {
1409 0 : return false;
1410 : }
1411 : }
1412 :
1413 0 : return true;
1414 : }
1415 :
1416 :
1417 : static inline bool
1418 38 : IsSignificantChild(nsIContent* aChild, bool aTextIsSignificant,
1419 : bool aWhitespaceIsSignificant)
1420 : {
1421 38 : return nsStyleUtil::IsSignificantChild(aChild, aTextIsSignificant,
1422 38 : aWhitespaceIsSignificant);
1423 : }
1424 :
1425 : // This function is to be called once we have fetched a value for an attribute
1426 : // whose namespace and name match those of aAttrSelector. This function
1427 : // performs comparisons on the value only, based on aAttrSelector->mFunction.
1428 206 : static bool AttrMatchesValue(const nsAttrSelector* aAttrSelector,
1429 : const nsString& aValue, bool isHTML)
1430 : {
1431 206 : NS_PRECONDITION(aAttrSelector, "Must have an attribute selector");
1432 :
1433 : // http://lists.w3.org/Archives/Public/www-style/2008Apr/0038.html
1434 : // *= (CONTAINSMATCH) ~= (INCLUDES) ^= (BEGINSMATCH) $= (ENDSMATCH)
1435 : // all accept the empty string, but match nothing.
1436 206 : if (aAttrSelector->mValue.IsEmpty() &&
1437 0 : (aAttrSelector->mFunction == NS_ATTR_FUNC_INCLUDES ||
1438 0 : aAttrSelector->mFunction == NS_ATTR_FUNC_ENDSMATCH ||
1439 0 : aAttrSelector->mFunction == NS_ATTR_FUNC_BEGINSMATCH ||
1440 0 : aAttrSelector->mFunction == NS_ATTR_FUNC_CONTAINSMATCH))
1441 0 : return false;
1442 :
1443 206 : const nsDefaultStringComparator defaultComparator;
1444 206 : const nsASCIICaseInsensitiveStringComparator ciComparator;
1445 : const nsStringComparator& comparator =
1446 206 : aAttrSelector->IsValueCaseSensitive(isHTML)
1447 : ? static_cast<const nsStringComparator&>(defaultComparator)
1448 206 : : static_cast<const nsStringComparator&>(ciComparator);
1449 :
1450 206 : switch (aAttrSelector->mFunction) {
1451 : case NS_ATTR_FUNC_EQUALS:
1452 0 : return aValue.Equals(aAttrSelector->mValue, comparator);
1453 : case NS_ATTR_FUNC_INCLUDES:
1454 182 : return nsStyleUtil::ValueIncludes(aValue, aAttrSelector->mValue, comparator);
1455 : case NS_ATTR_FUNC_DASHMATCH:
1456 0 : return nsStyleUtil::DashMatchCompare(aValue, aAttrSelector->mValue, comparator);
1457 : case NS_ATTR_FUNC_ENDSMATCH:
1458 0 : return StringEndsWith(aValue, aAttrSelector->mValue, comparator);
1459 : case NS_ATTR_FUNC_BEGINSMATCH:
1460 17 : return StringBeginsWith(aValue, aAttrSelector->mValue, comparator);
1461 : case NS_ATTR_FUNC_CONTAINSMATCH:
1462 7 : return FindInReadable(aAttrSelector->mValue, aValue, comparator);
1463 : default:
1464 0 : NS_NOTREACHED("Shouldn't be ending up here");
1465 0 : return false;
1466 : }
1467 : }
1468 :
1469 : static inline bool
1470 6 : edgeChildMatches(Element* aElement, TreeMatchContext& aTreeMatchContext,
1471 : bool checkFirst, bool checkLast)
1472 : {
1473 6 : nsIContent* parent = aElement->GetParent();
1474 6 : if (parent && aTreeMatchContext.mForStyling)
1475 6 : parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
1476 :
1477 12 : return (!checkFirst ||
1478 : aTreeMatchContext.mNthIndexCache.
1479 6 : GetNthIndex(aElement, false, false, true) == 1) &&
1480 0 : (!checkLast ||
1481 : aTreeMatchContext.mNthIndexCache.
1482 6 : GetNthIndex(aElement, false, true, true) == 1);
1483 : }
1484 :
1485 : static inline bool
1486 0 : nthChildGenericMatches(Element* aElement,
1487 : TreeMatchContext& aTreeMatchContext,
1488 : nsPseudoClassList* pseudoClass,
1489 : bool isOfType, bool isFromEnd)
1490 : {
1491 0 : nsIContent* parent = aElement->GetParent();
1492 0 : if (parent && aTreeMatchContext.mForStyling) {
1493 0 : if (isFromEnd)
1494 0 : parent->SetFlags(NODE_HAS_SLOW_SELECTOR);
1495 : else
1496 0 : parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
1497 : }
1498 :
1499 : const int32_t index = aTreeMatchContext.mNthIndexCache.
1500 0 : GetNthIndex(aElement, isOfType, isFromEnd, false);
1501 0 : if (index <= 0) {
1502 : // Node is anonymous content (not really a child of its parent).
1503 0 : return false;
1504 : }
1505 :
1506 0 : const int32_t a = pseudoClass->u.mNumbers[0];
1507 0 : const int32_t b = pseudoClass->u.mNumbers[1];
1508 : // result should be true if there exists n >= 0 such that
1509 : // a * n + b == index.
1510 0 : if (a == 0) {
1511 0 : return b == index;
1512 : }
1513 :
1514 : // Integer division in C does truncation (towards 0). So
1515 : // check that the result is nonnegative, and that there was no
1516 : // truncation.
1517 0 : const CheckedInt<int32_t> indexMinusB = CheckedInt<int32_t>(index) - b;
1518 0 : const CheckedInt<int32_t> n = indexMinusB / a;
1519 0 : return n.isValid() &&
1520 0 : n.value() >= 0 &&
1521 0 : a * n == indexMinusB;
1522 : }
1523 :
1524 : static inline bool
1525 0 : edgeOfTypeMatches(Element* aElement, TreeMatchContext& aTreeMatchContext,
1526 : bool checkFirst, bool checkLast)
1527 : {
1528 0 : nsIContent *parent = aElement->GetParent();
1529 0 : if (parent && aTreeMatchContext.mForStyling) {
1530 0 : if (checkLast)
1531 0 : parent->SetFlags(NODE_HAS_SLOW_SELECTOR);
1532 : else
1533 0 : parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
1534 : }
1535 :
1536 0 : return (!checkFirst ||
1537 : aTreeMatchContext.mNthIndexCache.
1538 0 : GetNthIndex(aElement, true, false, true) == 1) &&
1539 0 : (!checkLast ||
1540 : aTreeMatchContext.mNthIndexCache.
1541 0 : GetNthIndex(aElement, true, true, true) == 1);
1542 : }
1543 :
1544 : static inline bool
1545 43 : checkGenericEmptyMatches(Element* aElement,
1546 : TreeMatchContext& aTreeMatchContext,
1547 : bool isWhitespaceSignificant)
1548 : {
1549 43 : nsIContent *child = nullptr;
1550 43 : int32_t index = -1;
1551 :
1552 43 : if (aTreeMatchContext.mForStyling)
1553 43 : aElement->SetFlags(NODE_HAS_EMPTY_SELECTOR);
1554 :
1555 43 : do {
1556 43 : child = aElement->GetChildAt(++index);
1557 : // stop at first non-comment (and non-whitespace for
1558 : // :-moz-only-whitespace) node
1559 43 : } while (child && !IsSignificantChild(child, true, isWhitespaceSignificant));
1560 43 : return (child == nullptr);
1561 : }
1562 :
1563 : // Arrays of the states that are relevant for various pseudoclasses.
1564 : static const EventStates sPseudoClassStateDependences[] = {
1565 : #define CSS_PSEUDO_CLASS(_name, _value, _flags, _pref) \
1566 : EventStates(),
1567 : #define CSS_STATE_DEPENDENT_PSEUDO_CLASS(_name, _value, _flags, _pref, _states) \
1568 : _states,
1569 : #include "nsCSSPseudoClassList.h"
1570 : #undef CSS_STATE_DEPENDENT_PSEUDO_CLASS
1571 : #undef CSS_PSEUDO_CLASS
1572 : // Add more entries for our fake values to make sure we can't
1573 : // index out of bounds into this array no matter what.
1574 : EventStates(),
1575 : EventStates()
1576 : };
1577 :
1578 : static const EventStates sPseudoClassStates[] = {
1579 : #define CSS_PSEUDO_CLASS(_name, _value, _flags, _pref) \
1580 : EventStates(),
1581 : #define CSS_STATE_PSEUDO_CLASS(_name, _value, _flags, _pref, _states) \
1582 : _states,
1583 : #include "nsCSSPseudoClassList.h"
1584 : #undef CSS_STATE_PSEUDO_CLASS
1585 : #undef CSS_PSEUDO_CLASS
1586 : // Add more entries for our fake values to make sure we can't
1587 : // index out of bounds into this array no matter what.
1588 : EventStates(),
1589 : EventStates()
1590 : };
1591 : static_assert(MOZ_ARRAY_LENGTH(sPseudoClassStates) ==
1592 : static_cast<size_t>(CSSPseudoClassType::MAX),
1593 : "CSSPseudoClassType::MAX is no longer equal to the length of "
1594 : "sPseudoClassStates");
1595 :
1596 : static bool
1597 16984 : StateSelectorMatches(Element* aElement,
1598 : nsCSSSelector* aSelector,
1599 : NodeMatchContext& aNodeMatchContext,
1600 : TreeMatchContext& aTreeMatchContext,
1601 : SelectorMatchesFlags aSelectorFlags,
1602 : bool* const aDependence,
1603 : EventStates aStatesToCheck)
1604 : {
1605 16984 : NS_PRECONDITION(!aStatesToCheck.IsEmpty(),
1606 : "should only need to call StateSelectorMatches if "
1607 : "aStatesToCheck is not empty");
1608 :
1609 : // Bit-based pseudo-classes
1610 50952 : if (aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_ACTIVE |
1611 69873 : NS_EVENT_STATE_HOVER) &&
1612 1937 : aTreeMatchContext.mCompatMode == eCompatibility_NavQuirks &&
1613 0 : ActiveHoverQuirkMatches(aSelector, aSelectorFlags) &&
1614 67936 : aElement->IsHTMLElement() && !nsCSSRuleProcessor::IsLink(aElement)) {
1615 : // In quirks mode, only make links sensitive to selectors ":active"
1616 : // and ":hover".
1617 0 : return false;
1618 : }
1619 :
1620 67682 : if (aTreeMatchContext.mForStyling &&
1621 67174 : aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_HOVER)) {
1622 : // Mark the element as having :hover-dependent style
1623 1669 : aElement->SetHasRelevantHoverRules();
1624 : }
1625 :
1626 16984 : if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(aStatesToCheck)) {
1627 6 : if (aDependence) {
1628 2 : *aDependence = true;
1629 : }
1630 : } else {
1631 : EventStates contentState =
1632 : nsCSSRuleProcessor::GetContentStateForVisitedHandling(
1633 : aElement,
1634 : aTreeMatchContext.VisitedHandling(),
1635 16978 : aNodeMatchContext.mIsRelevantLink);
1636 16978 : if (!contentState.HasAtLeastOneOfStates(aStatesToCheck)) {
1637 16966 : return false;
1638 : }
1639 : }
1640 :
1641 18 : return true;
1642 : }
1643 :
1644 : static bool
1645 256 : StateSelectorMatches(Element* aElement,
1646 : nsCSSSelector* aSelector,
1647 : NodeMatchContext& aNodeMatchContext,
1648 : TreeMatchContext& aTreeMatchContext,
1649 : SelectorMatchesFlags aSelectorFlags)
1650 : {
1651 256 : for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
1652 256 : pseudoClass; pseudoClass = pseudoClass->mNext) {
1653 0 : auto idx = static_cast<CSSPseudoClassTypeBase>(pseudoClass->mType);
1654 0 : EventStates statesToCheck = sPseudoClassStates[idx];
1655 0 : if (!statesToCheck.IsEmpty() &&
1656 0 : !StateSelectorMatches(aElement, aSelector, aNodeMatchContext,
1657 : aTreeMatchContext, aSelectorFlags, nullptr,
1658 : statesToCheck)) {
1659 0 : return false;
1660 : }
1661 : }
1662 256 : return true;
1663 : }
1664 :
1665 : // Chooses the thread safe version in Servo mode, and
1666 : // the non-thread safe one in Gecko mode. The non thread safe one does
1667 : // some extra caching, and is preferred when possible.
1668 : static inline bool
1669 0 : IsSignificantChildMaybeThreadSafe(const nsIContent* aContent,
1670 : bool aTextIsSignificant,
1671 : bool aWhitespaceIsSignificant)
1672 : {
1673 0 : if (ServoStyleSet::IsInServoTraversal()) {
1674 : // See bug 1349100 for optimizing this
1675 0 : return nsStyleUtil::ThreadSafeIsSignificantChild(aContent,
1676 : aTextIsSignificant,
1677 0 : aWhitespaceIsSignificant);
1678 : } else {
1679 0 : auto content = const_cast<nsIContent*>(aContent);
1680 0 : return IsSignificantChild(content, aTextIsSignificant, aWhitespaceIsSignificant);
1681 : }
1682 : }
1683 :
1684 : /* static */ bool
1685 0 : nsCSSRuleProcessor::LangPseudoMatches(const mozilla::dom::Element* aElement,
1686 : const nsIAtom* aOverrideLang,
1687 : bool aHasOverrideLang,
1688 : const char16_t* aString,
1689 : const nsIDocument* aDocument)
1690 : {
1691 0 : NS_ASSERTION(aString, "null lang parameter");
1692 0 : if (!aString || !*aString) {
1693 0 : return false;
1694 : }
1695 :
1696 : // We have to determine the language of the current element. Since
1697 : // this is currently no property and since the language is inherited
1698 : // from the parent we have to be prepared to look at all parent
1699 : // nodes. The language itself is encoded in the LANG attribute.
1700 0 : if (auto* language = aHasOverrideLang ? aOverrideLang : aElement->GetLang()) {
1701 0 : return nsStyleUtil::DashMatchCompare(nsDependentAtomString(language),
1702 0 : nsDependentString(aString),
1703 0 : nsASCIICaseInsensitiveStringComparator());
1704 : }
1705 :
1706 0 : if (!aDocument) {
1707 0 : return false;
1708 : }
1709 :
1710 : // Try to get the language from the HTTP header or if this
1711 : // is missing as well from the preferences.
1712 : // The content language can be a comma-separated list of
1713 : // language codes.
1714 0 : nsAutoString language;
1715 0 : aDocument->GetContentLanguage(language);
1716 :
1717 0 : nsDependentString langString(aString);
1718 0 : language.StripWhitespace();
1719 0 : int32_t begin = 0;
1720 0 : int32_t len = language.Length();
1721 0 : while (begin < len) {
1722 0 : int32_t end = language.FindChar(char16_t(','), begin);
1723 0 : if (end == kNotFound) {
1724 0 : end = len;
1725 : }
1726 0 : if (nsStyleUtil::DashMatchCompare(Substring(language, begin, end - begin),
1727 : langString,
1728 0 : nsASCIICaseInsensitiveStringComparator())) {
1729 0 : return true;
1730 : }
1731 0 : begin = end + 1;
1732 : }
1733 0 : if (begin < len) {
1734 0 : return true;
1735 : }
1736 0 : return false;
1737 : }
1738 :
1739 : /* static */ bool
1740 780 : nsCSSRuleProcessor::StringPseudoMatches(const mozilla::dom::Element* aElement,
1741 : CSSPseudoClassType aPseudo,
1742 : const char16_t* aString,
1743 : const nsIDocument* aDocument,
1744 : bool aForStyling,
1745 : EventStates aStateMask,
1746 : bool* aSetSlowSelectorFlag,
1747 : bool* const aDependence)
1748 : {
1749 780 : MOZ_ASSERT(aSetSlowSelectorFlag);
1750 :
1751 780 : switch (aPseudo) {
1752 : case CSSPseudoClassType::mozLocaleDir:
1753 : {
1754 : bool docIsRTL;
1755 667 : if (ServoStyleSet::IsInServoTraversal()) {
1756 0 : docIsRTL = aDocument->ThreadSafeGetDocumentState()
1757 0 : .HasState(NS_DOCUMENT_STATE_RTL_LOCALE);
1758 : } else {
1759 667 : auto doc = const_cast<nsIDocument*>(aDocument);
1760 1334 : docIsRTL = doc->GetDocumentState()
1761 2001 : .HasState(NS_DOCUMENT_STATE_RTL_LOCALE);
1762 : }
1763 :
1764 760 : nsDependentString dirString(aString);
1765 :
1766 667 : if (dirString.EqualsLiteral("rtl")) {
1767 574 : if (!docIsRTL) {
1768 574 : return false;
1769 : }
1770 93 : } else if (dirString.EqualsLiteral("ltr")) {
1771 93 : if (docIsRTL) {
1772 0 : return false;
1773 : }
1774 : } else {
1775 : // Selectors specifying other directions never match.
1776 0 : return false;
1777 : }
1778 : }
1779 93 : break;
1780 :
1781 : case CSSPseudoClassType::mozSystemMetric:
1782 : {
1783 210 : nsCOMPtr<nsIAtom> metric = NS_Atomize(aString);
1784 113 : if (!nsCSSRuleProcessor::HasSystemMetric(metric)) {
1785 16 : return false;
1786 : }
1787 : }
1788 97 : break;
1789 :
1790 : case CSSPseudoClassType::mozEmptyExceptChildrenWithLocalname:
1791 : {
1792 0 : NS_ASSERTION(aString, "Must have string!");
1793 0 : const nsIContent *child = nullptr;
1794 0 : int32_t index = -1;
1795 :
1796 0 : if (aForStyling) {
1797 : // FIXME: This isn't sufficient to handle:
1798 : // :-moz-empty-except-children-with-localname() + E
1799 : // :-moz-empty-except-children-with-localname() ~ E
1800 : // because we don't know to restyle the grandparent of the
1801 : // inserted/removed element (as in bug 534804 for :empty).
1802 0 : *aSetSlowSelectorFlag = true;
1803 : }
1804 0 : do {
1805 0 : child = aElement->GetChildAt(++index);
1806 0 : } while (child &&
1807 0 : (!IsSignificantChildMaybeThreadSafe(child, true, false) ||
1808 0 : (child->GetNameSpaceID() == aElement->GetNameSpaceID() &&
1809 0 : child->NodeInfo()->NameAtom()->Equals(nsDependentString(aString)))));
1810 0 : if (child) {
1811 0 : return false;
1812 : }
1813 : }
1814 0 : break;
1815 :
1816 : case CSSPseudoClassType::dir:
1817 : {
1818 0 : if (aDependence) {
1819 : EventStates states = sPseudoClassStateDependences[
1820 0 : static_cast<CSSPseudoClassTypeBase>(aPseudo)];
1821 0 : if (aStateMask.HasAtLeastOneOfStates(states)) {
1822 0 : *aDependence = true;
1823 0 : return false;
1824 : }
1825 : }
1826 :
1827 : // If we only had to consider HTML, directionality would be
1828 : // exclusively LTR or RTL.
1829 : //
1830 : // However, in markup languages where there is no direction attribute
1831 : // we have to consider the possibility that neither dir(rtl) nor
1832 : // dir(ltr) matches.
1833 0 : EventStates state = aElement->StyleState();
1834 0 : nsDependentString dirString(aString);
1835 :
1836 0 : if (dirString.EqualsLiteral("rtl")) {
1837 0 : if (!state.HasState(NS_EVENT_STATE_RTL)) {
1838 0 : return false;
1839 : }
1840 0 : } else if (dirString.EqualsLiteral("ltr")) {
1841 0 : if (!state.HasState(NS_EVENT_STATE_LTR)) {
1842 0 : return false;
1843 : }
1844 : } else {
1845 : // Selectors specifying other directions never match.
1846 0 : return false;
1847 : }
1848 : }
1849 0 : break;
1850 :
1851 : case CSSPseudoClassType::lang:
1852 0 : if (LangPseudoMatches(aElement, nullptr, false, aString, aDocument)) {
1853 0 : break;
1854 : }
1855 0 : return false;
1856 :
1857 0 : default: MOZ_ASSERT_UNREACHABLE("Called StringPseudoMatches() with unknown string-like pseudo");
1858 : }
1859 190 : return true;
1860 : }
1861 :
1862 : // |aDependence| has two functions:
1863 : // * when non-null, it indicates that we're processing a negation,
1864 : // which is done only when SelectorMatches calls itself recursively
1865 : // * what it points to should be set to true whenever a test is skipped
1866 : // because of aNodeMatchContent.mStateMask
1867 281699 : static bool SelectorMatches(Element* aElement,
1868 : nsCSSSelector* aSelector,
1869 : NodeMatchContext& aNodeMatchContext,
1870 : TreeMatchContext& aTreeMatchContext,
1871 : SelectorMatchesFlags aSelectorFlags,
1872 : bool* const aDependence = nullptr)
1873 : {
1874 281699 : NS_PRECONDITION(!aSelector->IsPseudoElement(),
1875 : "Pseudo-element snuck into SelectorMatches?");
1876 281699 : MOZ_ASSERT(aTreeMatchContext.mForStyling ||
1877 : !aNodeMatchContext.mIsRelevantLink,
1878 : "mIsRelevantLink should be set to false when mForStyling "
1879 : "is false since we don't know how to set it correctly in "
1880 : "Has(Attribute|State)DependentStyle");
1881 :
1882 : // namespace/tag match
1883 : // optimization : bail out early if we can
1884 481426 : if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
1885 199727 : aElement->GetNameSpaceID() != aSelector->mNameSpace))
1886 5465 : return false;
1887 :
1888 276234 : if (aSelector->mLowercaseTag) {
1889 : nsIAtom* selectorTag =
1890 28217 : (aTreeMatchContext.mIsHTMLDocument && aElement->IsHTMLElement()) ?
1891 27961 : aSelector->mLowercaseTag : aSelector->mCasedTag;
1892 27850 : if (selectorTag != aElement->NodeInfo()->NameAtom()) {
1893 13872 : return false;
1894 : }
1895 : }
1896 :
1897 262362 : nsAtomList* IDList = aSelector->mIDList;
1898 262362 : if (IDList) {
1899 115838 : nsIAtom* id = aElement->GetID();
1900 115838 : if (id) {
1901 : // case sensitivity: bug 93371
1902 : const bool isCaseSensitive =
1903 67276 : aTreeMatchContext.mCompatMode != eCompatibility_NavQuirks;
1904 :
1905 67276 : if (isCaseSensitive) {
1906 0 : do {
1907 67276 : if (IDList->mAtom != id) {
1908 63981 : return false;
1909 : }
1910 3295 : IDList = IDList->mNext;
1911 3295 : } while (IDList);
1912 : } else {
1913 : // Use EqualsIgnoreASCIICase instead of full on unicode case conversion
1914 : // in order to save on performance. This is only used in quirks mode
1915 : // anyway.
1916 0 : nsDependentAtomString id1Str(id);
1917 0 : do {
1918 0 : if (!nsContentUtils::EqualsIgnoreASCIICase(id1Str,
1919 0 : nsDependentAtomString(IDList->mAtom))) {
1920 0 : return false;
1921 : }
1922 0 : IDList = IDList->mNext;
1923 0 : } while (IDList);
1924 : }
1925 : } else {
1926 : // Element has no id but we have an id selector
1927 48562 : return false;
1928 : }
1929 : }
1930 :
1931 149819 : nsAtomList* classList = aSelector->mClassList;
1932 149819 : if (classList) {
1933 : // test for class match
1934 20163 : const nsAttrValue *elementClasses = aElement->GetClasses();
1935 20163 : if (!elementClasses) {
1936 : // Element has no classes but we have a class selector
1937 7371 : return false;
1938 : }
1939 :
1940 : // case sensitivity: bug 93371
1941 : const bool isCaseSensitive =
1942 12792 : aTreeMatchContext.mCompatMode != eCompatibility_NavQuirks;
1943 :
1944 28832 : while (classList) {
1945 12963 : if (!elementClasses->Contains(classList->mAtom,
1946 : isCaseSensitive ?
1947 : eCaseMatters : eIgnoreCase)) {
1948 4943 : return false;
1949 : }
1950 8020 : classList = classList->mNext;
1951 : }
1952 : }
1953 :
1954 137505 : const bool isNegated = (aDependence != nullptr);
1955 : // The selectors for which we set node bits are, unfortunately, early
1956 : // in this function (because they're pseudo-classes, which are
1957 : // generally quick to test, and thus earlier). If they were later,
1958 : // we'd probably avoid setting those bits in more cases where setting
1959 : // them is unnecessary.
1960 137505 : NS_ASSERTION(aNodeMatchContext.mStateMask.IsEmpty() ||
1961 : !aTreeMatchContext.mForStyling,
1962 : "mForStyling must be false if we're just testing for "
1963 : "state-dependence");
1964 :
1965 : // test for pseudo class match
1966 138368 : for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
1967 139231 : pseudoClass; pseudoClass = pseudoClass->mNext) {
1968 63993 : auto idx = static_cast<CSSPseudoClassTypeBase>(pseudoClass->mType);
1969 63993 : EventStates statesToCheck = sPseudoClassStates[idx];
1970 63993 : if (!statesToCheck.IsEmpty()) {
1971 16984 : if (!StateSelectorMatches(aElement, aSelector, aNodeMatchContext,
1972 : aTreeMatchContext, aSelectorFlags, aDependence,
1973 : statesToCheck)) {
1974 80096 : return false;
1975 : }
1976 40 : continue;
1977 : }
1978 : Maybe<bool> matchesElement =
1979 47850 : nsCSSPseudoClasses::MatchesElement(pseudoClass->mType, aElement);
1980 47009 : if (matchesElement.isSome()) {
1981 4 : if (!matchesElement.value()) {
1982 0 : return false;
1983 : }
1984 4 : continue;
1985 : }
1986 : // keep the cases here in the same order as the list in
1987 : // nsCSSPseudoClassList.h
1988 47005 : switch (pseudoClass->mType) {
1989 : case CSSPseudoClassType::empty:
1990 43 : if (!checkGenericEmptyMatches(aElement, aTreeMatchContext, true)) {
1991 38 : return false;
1992 : }
1993 5 : break;
1994 :
1995 : case CSSPseudoClassType::mozOnlyWhitespace:
1996 0 : if (!checkGenericEmptyMatches(aElement, aTreeMatchContext, false)) {
1997 0 : return false;
1998 : }
1999 0 : break;
2000 :
2001 : case CSSPseudoClassType::root:
2002 39637 : if (aElement != aElement->OwnerDoc()->GetRootElement()) {
2003 39273 : return false;
2004 : }
2005 364 : break;
2006 :
2007 : case CSSPseudoClassType::any:
2008 : {
2009 : nsCSSSelectorList *l;
2010 118293 : for (l = pseudoClass->u.mSelectors; l; l = l->mNext) {
2011 112974 : nsCSSSelector *s = l->mSelectors;
2012 112974 : MOZ_ASSERT(!s->mNext && !s->IsPseudoElement(),
2013 : "parser failed");
2014 112974 : if (SelectorMatches(
2015 : aElement, s, aNodeMatchContext, aTreeMatchContext,
2016 : SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) {
2017 266 : break;
2018 : }
2019 : }
2020 5585 : if (!l) {
2021 5319 : return false;
2022 : }
2023 : }
2024 266 : break;
2025 :
2026 : case CSSPseudoClassType::firstChild:
2027 6 : if (!edgeChildMatches(aElement, aTreeMatchContext, true, false)) {
2028 6 : return false;
2029 : }
2030 0 : break;
2031 :
2032 : case CSSPseudoClassType::firstNode:
2033 : {
2034 0 : nsIContent *firstNode = nullptr;
2035 0 : nsIContent *parent = aElement->GetParent();
2036 0 : if (parent) {
2037 0 : if (aTreeMatchContext.mForStyling)
2038 0 : parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
2039 :
2040 0 : int32_t index = -1;
2041 0 : do {
2042 0 : firstNode = parent->GetChildAt(++index);
2043 : // stop at first non-comment and non-whitespace node
2044 0 : } while (firstNode &&
2045 0 : !IsSignificantChild(firstNode, true, false));
2046 : }
2047 0 : if (aElement != firstNode) {
2048 0 : return false;
2049 : }
2050 : }
2051 0 : break;
2052 :
2053 : case CSSPseudoClassType::lastChild:
2054 0 : if (!edgeChildMatches(aElement, aTreeMatchContext, false, true)) {
2055 0 : return false;
2056 : }
2057 0 : break;
2058 :
2059 : case CSSPseudoClassType::lastNode:
2060 : {
2061 0 : nsIContent *lastNode = nullptr;
2062 0 : nsIContent *parent = aElement->GetParent();
2063 0 : if (parent) {
2064 0 : if (aTreeMatchContext.mForStyling)
2065 0 : parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
2066 :
2067 0 : uint32_t index = parent->GetChildCount();
2068 0 : do {
2069 0 : lastNode = parent->GetChildAt(--index);
2070 : // stop at first non-comment and non-whitespace node
2071 0 : } while (lastNode &&
2072 0 : !IsSignificantChild(lastNode, true, false));
2073 : }
2074 0 : if (aElement != lastNode) {
2075 0 : return false;
2076 : }
2077 : }
2078 0 : break;
2079 :
2080 : case CSSPseudoClassType::onlyChild:
2081 0 : if (!edgeChildMatches(aElement, aTreeMatchContext, true, true)) {
2082 0 : return false;
2083 : }
2084 0 : break;
2085 :
2086 : case CSSPseudoClassType::firstOfType:
2087 0 : if (!edgeOfTypeMatches(aElement, aTreeMatchContext, true, false)) {
2088 0 : return false;
2089 : }
2090 0 : break;
2091 :
2092 : case CSSPseudoClassType::lastOfType:
2093 0 : if (!edgeOfTypeMatches(aElement, aTreeMatchContext, false, true)) {
2094 0 : return false;
2095 : }
2096 0 : break;
2097 :
2098 : case CSSPseudoClassType::onlyOfType:
2099 0 : if (!edgeOfTypeMatches(aElement, aTreeMatchContext, true, true)) {
2100 0 : return false;
2101 : }
2102 0 : break;
2103 :
2104 : case CSSPseudoClassType::nthChild:
2105 0 : if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
2106 : false, false)) {
2107 0 : return false;
2108 : }
2109 0 : break;
2110 :
2111 : case CSSPseudoClassType::nthLastChild:
2112 0 : if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
2113 : false, true)) {
2114 0 : return false;
2115 : }
2116 0 : break;
2117 :
2118 : case CSSPseudoClassType::nthOfType:
2119 0 : if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
2120 : true, false)) {
2121 0 : return false;
2122 : }
2123 0 : break;
2124 :
2125 : case CSSPseudoClassType::nthLastOfType:
2126 0 : if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
2127 : true, true)) {
2128 0 : return false;
2129 : }
2130 0 : break;
2131 :
2132 : case CSSPseudoClassType::mozIsHTML:
2133 0 : if (!aTreeMatchContext.mIsHTMLDocument || !aElement->IsHTMLElement()) {
2134 0 : return false;
2135 : }
2136 0 : break;
2137 :
2138 : case CSSPseudoClassType::mozLWTheme:
2139 : {
2140 855 : if (aTreeMatchContext.mDocument->GetDocumentLWTheme() <=
2141 : nsIDocument::Doc_Theme_None) {
2142 855 : return false;
2143 : }
2144 : }
2145 0 : break;
2146 :
2147 : case CSSPseudoClassType::mozLWThemeBrightText:
2148 : {
2149 46 : if (aTreeMatchContext.mDocument->GetDocumentLWTheme() !=
2150 : nsIDocument::Doc_Theme_Bright) {
2151 46 : return false;
2152 : }
2153 : }
2154 0 : break;
2155 :
2156 : case CSSPseudoClassType::mozLWThemeDarkText:
2157 : {
2158 31 : if (aTreeMatchContext.mDocument->GetDocumentLWTheme() !=
2159 : nsIDocument::Doc_Theme_Dark) {
2160 31 : return false;
2161 : }
2162 : }
2163 0 : break;
2164 :
2165 : case CSSPseudoClassType::mozWindowInactive:
2166 66 : if (!aTreeMatchContext.mDocument->GetDocumentState().
2167 66 : HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
2168 6 : return false;
2169 : }
2170 16 : break;
2171 :
2172 : case CSSPseudoClassType::scope:
2173 0 : if (aTreeMatchContext.mForScopedStyle) {
2174 0 : if (aTreeMatchContext.mCurrentStyleScope) {
2175 : // If mCurrentStyleScope is null, aElement must be the style
2176 : // scope root. This is because the PopStyleScopeForSelectorMatching
2177 : // call in SelectorMatchesTree sets mCurrentStyleScope to null
2178 : // as soon as we visit the style scope element, and we won't
2179 : // progress further up the tree after this call to
2180 : // SelectorMatches. Thus if mCurrentStyleScope is still set,
2181 : // we know the selector does not match.
2182 0 : return false;
2183 : }
2184 0 : } else if (aTreeMatchContext.HasSpecifiedScope()) {
2185 0 : if (!aTreeMatchContext.IsScopeElement(aElement)) {
2186 0 : return false;
2187 : }
2188 : } else {
2189 0 : if (aElement != aElement->OwnerDoc()->GetRootElement()) {
2190 0 : return false;
2191 : }
2192 : }
2193 0 : break;
2194 :
2195 : default:
2196 : {
2197 780 : MOZ_ASSERT(nsCSSPseudoClasses::HasStringArg(pseudoClass->mType));
2198 780 : bool setSlowSelectorFlag = false;
2199 1560 : bool matched = nsCSSRuleProcessor::StringPseudoMatches(aElement,
2200 : pseudoClass->mType,
2201 780 : pseudoClass->u.mString,
2202 780 : aTreeMatchContext.mDocument,
2203 780 : aTreeMatchContext.mForStyling,
2204 : aNodeMatchContext.mStateMask,
2205 : &setSlowSelectorFlag,
2206 780 : aDependence);
2207 780 : if (setSlowSelectorFlag) {
2208 0 : aElement->SetFlags(NODE_HAS_SLOW_SELECTOR);
2209 : }
2210 :
2211 780 : if (!matched) {
2212 590 : return false;
2213 : }
2214 : }
2215 : }
2216 : }
2217 :
2218 74375 : bool result = true;
2219 74375 : if (aSelector->mAttrList) {
2220 : // test for attribute match
2221 56070 : if (!aElement->HasAttrs()) {
2222 : // if no attributes on the content, no match
2223 418 : return false;
2224 : }
2225 55652 : result = true;
2226 55652 : nsAttrSelector* attr = aSelector->mAttrList;
2227 : nsIAtom* matchAttribute;
2228 :
2229 430 : do {
2230 : bool isHTML =
2231 56082 : (aTreeMatchContext.mIsHTMLDocument && aElement->IsHTMLElement());
2232 56082 : matchAttribute = isHTML ? attr->mLowercaseAttr : attr->mCasedAttr;
2233 56082 : if (attr->mNameSpace == kNameSpaceID_Unknown) {
2234 : // Attr selector with a wildcard namespace. We have to examine all
2235 : // the attributes on our content node.... This sort of selector is
2236 : // essentially a boolean OR, over all namespaces, of equivalent attr
2237 : // selectors with those namespaces. So to evaluate whether it
2238 : // matches, evaluate for each namespace (the only namespaces that
2239 : // have a chance at matching, of course, are ones that the element
2240 : // actually has attributes in), short-circuiting if we ever match.
2241 0 : result = false;
2242 : const nsAttrName* attrName;
2243 0 : for (uint32_t i = 0; (attrName = aElement->GetAttrNameAt(i)); ++i) {
2244 0 : if (attrName->LocalName() != matchAttribute) {
2245 0 : continue;
2246 : }
2247 0 : if (attr->mFunction == NS_ATTR_FUNC_SET) {
2248 0 : result = true;
2249 : } else {
2250 0 : nsAutoString value;
2251 : #ifdef DEBUG
2252 : bool hasAttr =
2253 : #endif
2254 0 : aElement->GetAttr(attrName->NamespaceID(),
2255 0 : attrName->LocalName(), value);
2256 0 : NS_ASSERTION(hasAttr, "GetAttrNameAt lied");
2257 0 : result = AttrMatchesValue(attr, value, isHTML);
2258 : }
2259 :
2260 : // At this point |result| has been set by us
2261 : // explicitly in this loop. If it's false, we may still match
2262 : // -- the content may have another attribute with the same name but
2263 : // in a different namespace. But if it's true, we are done (we
2264 : // can short-circuit the boolean OR described above).
2265 0 : if (result) {
2266 0 : break;
2267 : }
2268 : }
2269 : }
2270 56082 : else if (attr->mFunction == NS_ATTR_FUNC_EQUALS) {
2271 : result =
2272 : aElement->
2273 42438 : AttrValueIs(attr->mNameSpace, matchAttribute, attr->mValue,
2274 42438 : attr->IsValueCaseSensitive(isHTML) ? eCaseMatters
2275 42438 : : eIgnoreCase);
2276 : }
2277 13644 : else if (!aElement->HasAttr(attr->mNameSpace, matchAttribute)) {
2278 12919 : result = false;
2279 : }
2280 725 : else if (attr->mFunction != NS_ATTR_FUNC_SET) {
2281 412 : nsAutoString value;
2282 : #ifdef DEBUG
2283 : bool hasAttr =
2284 : #endif
2285 206 : aElement->GetAttr(attr->mNameSpace, matchAttribute, value);
2286 206 : NS_ASSERTION(hasAttr, "HasAttr lied");
2287 206 : result = AttrMatchesValue(attr, value, isHTML);
2288 : }
2289 :
2290 56082 : attr = attr->mNext;
2291 56082 : } while (attr && result);
2292 : }
2293 :
2294 : // apply SelectorMatches to the negated selectors in the chain
2295 73957 : if (!isNegated) {
2296 74692 : for (nsCSSSelector *negation = aSelector->mNegations;
2297 76701 : result && negation; negation = negation->mNegations) {
2298 2009 : bool dependence = false;
2299 2009 : result = !SelectorMatches(aElement, negation, aNodeMatchContext,
2300 : aTreeMatchContext,
2301 : SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT,
2302 : &dependence);
2303 : // If the selector does match due to the dependence on
2304 : // aNodeMatchContext.mStateMask, then we want to keep result true
2305 : // so that the final result of SelectorMatches is true. Doing so
2306 : // tells StateEnumFunc that there is a dependence on the state.
2307 2009 : result = result || dependence;
2308 : }
2309 : }
2310 73957 : return result;
2311 : }
2312 :
2313 : #undef STATE_CHECK
2314 :
2315 : #ifdef DEBUG
2316 : static bool
2317 11332 : HasPseudoClassSelectorArgsWithCombinators(nsCSSSelector* aSelector)
2318 : {
2319 11332 : for (nsPseudoClassList* p = aSelector->mPseudoClassList; p; p = p->mNext) {
2320 0 : if (nsCSSPseudoClasses::HasSelectorListArg(p->mType)) {
2321 0 : for (nsCSSSelectorList* l = p->u.mSelectors; l; l = l->mNext) {
2322 0 : if (l->mSelectors->mNext) {
2323 0 : return true;
2324 : }
2325 : }
2326 : }
2327 : }
2328 11875 : for (nsCSSSelector* n = aSelector->mNegations; n; n = n->mNegations) {
2329 543 : if (n->mNext) {
2330 0 : return true;
2331 : }
2332 : }
2333 11332 : return false;
2334 : }
2335 : #endif
2336 :
2337 : /* static */ bool
2338 11332 : nsCSSRuleProcessor::RestrictedSelectorMatches(
2339 : Element* aElement,
2340 : nsCSSSelector* aSelector,
2341 : TreeMatchContext& aTreeMatchContext)
2342 : {
2343 11332 : MOZ_ASSERT(aSelector->IsRestrictedSelector(),
2344 : "aSelector must not have a pseudo-element");
2345 :
2346 11332 : NS_WARNING_ASSERTION(
2347 : !HasPseudoClassSelectorArgsWithCombinators(aSelector),
2348 : "processing eRestyle_SomeDescendants can be slow if pseudo-classes with "
2349 : "selector arguments can now have combinators in them");
2350 :
2351 : // We match aSelector as if :visited and :link both match visited and
2352 : // unvisited links.
2353 :
2354 : NodeMatchContext nodeContext(EventStates(),
2355 11332 : nsCSSRuleProcessor::IsLink(aElement));
2356 11332 : if (nodeContext.mIsRelevantLink) {
2357 0 : aTreeMatchContext.SetHaveRelevantLink();
2358 : }
2359 11332 : aTreeMatchContext.ResetForUnvisitedMatching();
2360 : bool matches = SelectorMatches(aElement, aSelector, nodeContext,
2361 11332 : aTreeMatchContext, SelectorMatchesFlags::NONE);
2362 11332 : if (nodeContext.mIsRelevantLink) {
2363 0 : aTreeMatchContext.ResetForVisitedMatching();
2364 0 : if (SelectorMatches(aElement, aSelector, nodeContext, aTreeMatchContext,
2365 : SelectorMatchesFlags::NONE)) {
2366 0 : matches = true;
2367 : }
2368 : }
2369 11332 : return matches;
2370 : }
2371 :
2372 : // Right now, there are four operators:
2373 : // ' ', the descendant combinator, is greedy
2374 : // '~', the indirect adjacent sibling combinator, is greedy
2375 : // '+' and '>', the direct adjacent sibling and child combinators, are not
2376 : #define NS_IS_GREEDY_OPERATOR(ch) \
2377 : ((ch) == char16_t(' ') || (ch) == char16_t('~'))
2378 :
2379 : /**
2380 : * Flags for SelectorMatchesTree.
2381 : */
2382 : enum SelectorMatchesTreeFlags {
2383 : // Whether we still have not found the closest ancestor link element and
2384 : // thus have to check the current element for it.
2385 : eLookForRelevantLink = 0x1,
2386 :
2387 : // Whether SelectorMatchesTree should check for, and return true upon
2388 : // finding, an ancestor element that has an eRestyle_SomeDescendants
2389 : // restyle hint pending.
2390 : eMatchOnConditionalRestyleAncestor = 0x2,
2391 : };
2392 :
2393 : static bool
2394 7439 : SelectorMatchesTree(Element* aPrevElement,
2395 : nsCSSSelector* aSelector,
2396 : TreeMatchContext& aTreeMatchContext,
2397 : SelectorMatchesTreeFlags aFlags)
2398 : {
2399 7439 : MOZ_ASSERT(!aSelector || !aSelector->IsPseudoElement());
2400 7439 : nsCSSSelector* selector = aSelector;
2401 7439 : Element* prevElement = aPrevElement;
2402 26241 : while (selector) { // check compound selectors
2403 15198 : NS_ASSERTION(!selector->mNext ||
2404 : selector->mNext->mOperator != char16_t(0),
2405 : "compound selector without combinator");
2406 :
2407 : // If after the previous selector match we are now outside the
2408 : // current style scope, we don't need to match any further.
2409 15198 : if (aTreeMatchContext.mForScopedStyle &&
2410 0 : !aTreeMatchContext.IsWithinStyleScopeForSelectorMatching()) {
2411 5797 : return false;
2412 : }
2413 :
2414 : // for adjacent sibling combinators, the content to test against the
2415 : // selector is the previous sibling *element*
2416 15198 : Element* element = nullptr;
2417 30305 : if (char16_t('+') == selector->mOperator ||
2418 15107 : char16_t('~') == selector->mOperator) {
2419 : // The relevant link must be an ancestor of the node being matched.
2420 140 : aFlags = SelectorMatchesTreeFlags(aFlags & ~eLookForRelevantLink);
2421 140 : nsIContent* parent = prevElement->GetParent();
2422 140 : if (parent) {
2423 140 : if (aTreeMatchContext.mForStyling)
2424 140 : parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
2425 :
2426 140 : element = prevElement->GetPreviousElementSibling();
2427 140 : }
2428 : }
2429 : // for descendant combinators and child combinators, the element
2430 : // to test against is the parent
2431 : else {
2432 15058 : nsIContent *content = prevElement->GetParent();
2433 15058 : if (prevElement->IsRootOfUseElementShadowTree()) {
2434 : // 'prevElement' is the shadow root of an use-element shadow tree.
2435 : // According to the spec, we should not match rules cross the shadow
2436 : // DOM boundary.
2437 46 : content = nullptr;
2438 : }
2439 : // GetParent could return a document fragment; we only want
2440 : // element parents.
2441 15058 : if (content && content->IsElement()) {
2442 13780 : element = content->AsElement();
2443 13780 : if (aTreeMatchContext.mForScopedStyle) {
2444 : // We are moving up to the parent element; tell the
2445 : // TreeMatchContext, so that in case this element is the
2446 : // style scope element, selector matching stops before we
2447 : // traverse further up the tree.
2448 0 : aTreeMatchContext.PopStyleScopeForSelectorMatching(element);
2449 : }
2450 :
2451 : // Compatibility hack: First try matching this selector as though the
2452 : // <xbl:children> element wasn't in the tree to allow old selectors
2453 : // were written before <xbl:children> participated in CSS selector
2454 : // matching to work.
2455 13780 : if (selector->mOperator == '>' && element->IsActiveChildrenElement()) {
2456 0 : Element* styleScope = aTreeMatchContext.mCurrentStyleScope;
2457 0 : if (SelectorMatchesTree(element, selector, aTreeMatchContext,
2458 : aFlags)) {
2459 : // It matched, don't try matching on the <xbl:children> element at
2460 : // all.
2461 0 : return true;
2462 : }
2463 : // We want to reset mCurrentStyleScope on aTreeMatchContext
2464 : // back to its state before the SelectorMatchesTree call, in
2465 : // case that call happens to traverse past the style scope element
2466 : // and sets it to null.
2467 0 : aTreeMatchContext.mCurrentStyleScope = styleScope;
2468 : }
2469 : }
2470 : }
2471 15198 : if (!element) {
2472 1294 : return false;
2473 : }
2474 14396 : if ((aFlags & eMatchOnConditionalRestyleAncestor) &&
2475 492 : element->HasFlag(ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR)) {
2476 : // If we're looking at an element that we already generated an
2477 : // eRestyle_SomeDescendants restyle hint for, then we should pretend
2478 : // that we matched here, because we don't know what the values of
2479 : // attributes on |element| were at the time we generated the
2480 : // eRestyle_SomeDescendants. This causes AttributeEnumFunc and
2481 : // HasStateDependentStyle below to generate a restyle hint for the
2482 : // change we're currently looking at, as we don't know whether the LHS
2483 : // of the selector we looked up matches or not. (We only pass in aFlags
2484 : // to cause us to look for eRestyle_SomeDescendants here under
2485 : // AttributeEnumFunc and HasStateDependentStyle.)
2486 14 : return true;
2487 : }
2488 27170 : const bool isRelevantLink = (aFlags & eLookForRelevantLink) &&
2489 27170 : nsCSSRuleProcessor::IsLink(element);
2490 13890 : NodeMatchContext nodeContext(EventStates(), isRelevantLink);
2491 13890 : if (isRelevantLink) {
2492 : // If we find an ancestor of the matched node that is a link
2493 : // during the matching process, then it's the relevant link (see
2494 : // constructor call above).
2495 : // Since we are still matching against selectors that contain
2496 : // :visited (they'll just fail), we will always find such a node
2497 : // during the selector matching process if there is a relevant
2498 : // link that can influence selector matching.
2499 0 : aFlags = SelectorMatchesTreeFlags(aFlags & ~eLookForRelevantLink);
2500 0 : aTreeMatchContext.SetHaveRelevantLink();
2501 : }
2502 13890 : if (SelectorMatches(element, selector, nodeContext, aTreeMatchContext,
2503 : SelectorMatchesFlags::NONE)) {
2504 : // to avoid greedy matching, we need to recur if this is a
2505 : // descendant or general sibling combinator and the next
2506 : // combinator is different, but we can make an exception for
2507 : // sibling, then parent, since a sibling's parent is always the
2508 : // same.
2509 3294 : if (NS_IS_GREEDY_OPERATOR(selector->mOperator) &&
2510 833 : selector->mNext &&
2511 18 : selector->mNext->mOperator != selector->mOperator &&
2512 0 : !(selector->mOperator == '~' &&
2513 0 : NS_IS_ANCESTOR_OPERATOR(selector->mNext->mOperator))) {
2514 :
2515 : // pretend the selector didn't match, and step through content
2516 : // while testing the same selector
2517 :
2518 : // This approach is slightly strange in that when it recurs
2519 : // it tests from the top of the content tree, down. This
2520 : // doesn't matter much for performance since most selectors
2521 : // don't match. (If most did, it might be faster...)
2522 0 : Element* styleScope = aTreeMatchContext.mCurrentStyleScope;
2523 0 : if (SelectorMatchesTree(element, selector, aTreeMatchContext, aFlags)) {
2524 0 : return true;
2525 : }
2526 : // We want to reset mCurrentStyleScope on aTreeMatchContext
2527 : // back to its state before the SelectorMatchesTree call, in
2528 : // case that call happens to traverse past the style scope element
2529 : // and sets it to null.
2530 0 : aTreeMatchContext.mCurrentStyleScope = styleScope;
2531 : }
2532 2479 : selector = selector->mNext;
2533 : }
2534 : else {
2535 : // for adjacent sibling and child combinators, if we didn't find
2536 : // a match, we're done
2537 11411 : if (!NS_IS_GREEDY_OPERATOR(selector->mOperator)) {
2538 4489 : return false; // parent was required to match
2539 : }
2540 : }
2541 9401 : prevElement = element;
2542 : }
2543 1642 : return true; // all the selectors matched.
2544 : }
2545 :
2546 : static inline
2547 187198 : void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
2548 : ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
2549 : AncestorFilter *ancestorFilter)
2550 : {
2551 187198 : if (nodeContext.mIsRelevantLink) {
2552 0 : data->mTreeMatchContext.SetHaveRelevantLink();
2553 : }
2554 354989 : if (ancestorFilter &&
2555 167791 : !ancestorFilter->MightHaveMatchingAncestor<RuleValue::eMaxAncestorHashes>(
2556 : value.mAncestorSelectorHashes)) {
2557 : // We won't match; nothing else to do here
2558 130500 : return;
2559 : }
2560 121948 : if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
2561 : data->mScope)) {
2562 : // The selector is for a rule in a scoped style sheet, and the subject
2563 : // of the selector matching is not in its scope.
2564 0 : return;
2565 : }
2566 121948 : nsCSSSelector* selector = aSelector;
2567 121948 : if (selector->IsPseudoElement()) {
2568 : PseudoElementRuleProcessorData* pdata =
2569 256 : static_cast<PseudoElementRuleProcessorData*>(data);
2570 256 : if (!pdata->mPseudoElement && selector->mPseudoClassList) {
2571 : // We can get here when calling getComputedStyle(aElt, aPseudo) if:
2572 : //
2573 : // * aPseudo is a pseudo-element that supports a user action
2574 : // pseudo-class, like "::placeholder";
2575 : // * there is a style rule that uses a pseudo-class on this
2576 : // pseudo-element in the document, like ::placeholder:hover; and
2577 : // * aElt does not have such a pseudo-element.
2578 : //
2579 : // We know that the selector can't match, since there is no element for
2580 : // the user action pseudo-class to match against.
2581 0 : return;
2582 : }
2583 256 : if (!StateSelectorMatches(pdata->mPseudoElement, aSelector, nodeContext,
2584 : data->mTreeMatchContext,
2585 : SelectorMatchesFlags::NONE)) {
2586 0 : return;
2587 : }
2588 256 : selector = selector->mNext;
2589 : }
2590 :
2591 121948 : SelectorMatchesFlags selectorFlags = SelectorMatchesFlags::NONE;
2592 121948 : if (aSelector->IsPseudoElement()) {
2593 256 : selectorFlags |= SelectorMatchesFlags::HAS_PSEUDO_ELEMENT;
2594 : }
2595 121948 : if (SelectorMatches(data->mElement, selector, nodeContext,
2596 : data->mTreeMatchContext, selectorFlags)) {
2597 16201 : nsCSSSelector *next = selector->mNext;
2598 23368 : if (!next ||
2599 7167 : SelectorMatchesTree(data->mElement, next,
2600 : data->mTreeMatchContext,
2601 7167 : nodeContext.mIsRelevantLink ?
2602 : SelectorMatchesTreeFlags(0) :
2603 : eLookForRelevantLink)) {
2604 10475 : css::Declaration* declaration = value.mRule->GetDeclaration();
2605 10475 : declaration->SetImmutable();
2606 10475 : data->mRuleWalker->Forward(declaration);
2607 : // nsStyleSet will deal with the !important rule
2608 : }
2609 : }
2610 : }
2611 :
2612 : /* virtual */ void
2613 6605 : nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData *aData)
2614 : {
2615 6605 : RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
2616 :
2617 6605 : if (cascade) {
2618 : NodeMatchContext nodeContext(EventStates(),
2619 6605 : nsCSSRuleProcessor::IsLink(aData->mElement));
2620 6605 : cascade->mRuleHash.EnumerateAllRules(aData->mElement, aData, nodeContext);
2621 : }
2622 6605 : }
2623 :
2624 : /* virtual */ void
2625 3570 : nsCSSRuleProcessor::RulesMatching(PseudoElementRuleProcessorData* aData)
2626 : {
2627 3570 : RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
2628 :
2629 3570 : if (cascade) {
2630 : RuleHash* ruleHash = cascade->mPseudoElementRuleHashes[
2631 3570 : static_cast<CSSPseudoElementTypeBase>(aData->mPseudoType)];
2632 3570 : if (ruleHash) {
2633 : NodeMatchContext nodeContext(EventStates(),
2634 2163 : nsCSSRuleProcessor::IsLink(aData->mElement));
2635 2163 : ruleHash->EnumerateAllRules(aData->mElement, aData, nodeContext);
2636 : }
2637 : }
2638 3570 : }
2639 :
2640 : /* virtual */ void
2641 311 : nsCSSRuleProcessor::RulesMatching(AnonBoxRuleProcessorData* aData)
2642 : {
2643 311 : RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
2644 :
2645 311 : if (cascade && cascade->mAnonBoxRules.EntryCount()) {
2646 : auto entry = static_cast<RuleHashTagTableEntry*>
2647 210 : (cascade->mAnonBoxRules.Search(aData->mPseudoTag));
2648 210 : if (entry) {
2649 180 : nsTArray<RuleValue>& rules = entry->mRules;
2650 369 : for (RuleValue *value = rules.Elements(), *end = value + rules.Length();
2651 369 : value != end; ++value) {
2652 189 : css::Declaration* declaration = value->mRule->GetDeclaration();
2653 189 : declaration->SetImmutable();
2654 189 : aData->mRuleWalker->Forward(declaration);
2655 : }
2656 : }
2657 : }
2658 311 : }
2659 :
2660 : #ifdef MOZ_XUL
2661 : /* virtual */ void
2662 0 : nsCSSRuleProcessor::RulesMatching(XULTreeRuleProcessorData* aData)
2663 : {
2664 0 : RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
2665 :
2666 0 : if (cascade && cascade->mXULTreeRules.EntryCount()) {
2667 : auto entry = static_cast<RuleHashTagTableEntry*>
2668 0 : (cascade->mXULTreeRules.Search(aData->mPseudoTag));
2669 0 : if (entry) {
2670 : NodeMatchContext nodeContext(EventStates(),
2671 0 : nsCSSRuleProcessor::IsLink(aData->mElement));
2672 0 : nsTArray<RuleValue>& rules = entry->mRules;
2673 0 : for (RuleValue *value = rules.Elements(), *end = value + rules.Length();
2674 0 : value != end; ++value) {
2675 0 : if (aData->mComparator->PseudoMatches(value->mSelector)) {
2676 0 : ContentEnumFunc(*value, value->mSelector->mNext, aData, nodeContext,
2677 0 : nullptr);
2678 : }
2679 : }
2680 : }
2681 : }
2682 0 : }
2683 : #endif
2684 :
2685 18462 : static inline nsRestyleHint RestyleHintForOp(char16_t oper)
2686 : {
2687 18462 : if (oper == char16_t('+') || oper == char16_t('~')) {
2688 0 : return eRestyle_LaterSiblings;
2689 : }
2690 :
2691 18462 : if (oper != char16_t(0)) {
2692 3704 : return eRestyle_Subtree;
2693 : }
2694 :
2695 14758 : return eRestyle_Self;
2696 : }
2697 :
2698 : nsRestyleHint
2699 137 : nsCSSRuleProcessor::HasStateDependentStyle(ElementDependentRuleProcessorData* aData,
2700 : Element* aStatefulElement,
2701 : CSSPseudoElementType aPseudoType,
2702 : EventStates aStateMask)
2703 : {
2704 137 : MOZ_ASSERT(!aData->mTreeMatchContext.mForScopedStyle,
2705 : "mCurrentStyleScope will need to be saved and restored after the "
2706 : "SelectorMatchesTree call");
2707 :
2708 : bool isPseudoElement =
2709 137 : aPseudoType != CSSPseudoElementType::NotPseudo;
2710 :
2711 137 : RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
2712 :
2713 : // Look up the content node in the state rule list, which points to
2714 : // any (CSS2 definition) simple selector (whether or not it is the
2715 : // subject) that has a state pseudo-class on it. This means that this
2716 : // code will be matching selectors that aren't real selectors in any
2717 : // stylesheet (e.g., if there is a selector "body > p:hover > a", then
2718 : // "body > p:hover" will be in |cascade->mStateSelectors|). Note that
2719 : // |ComputeSelectorStateDependence| below determines which selectors are in
2720 : // |cascade->mStateSelectors|.
2721 137 : nsRestyleHint hint = nsRestyleHint(0);
2722 137 : if (cascade) {
2723 137 : StateSelector *iter = cascade->mStateSelectors.Elements(),
2724 137 : *end = iter + cascade->mStateSelectors.Length();
2725 137 : NodeMatchContext nodeContext(aStateMask, false);
2726 37061 : for(; iter != end; ++iter) {
2727 18462 : nsCSSSelector* selector = iter->mSelector;
2728 18462 : EventStates states = iter->mStates;
2729 :
2730 18462 : if (selector->IsPseudoElement() != isPseudoElement) {
2731 0 : continue;
2732 : }
2733 :
2734 : nsCSSSelector* selectorForPseudo;
2735 18462 : if (isPseudoElement) {
2736 0 : if (selector->PseudoType() != aPseudoType) {
2737 0 : continue;
2738 : }
2739 0 : selectorForPseudo = selector;
2740 0 : selector = selector->mNext;
2741 : }
2742 :
2743 18462 : nsRestyleHint possibleChange = RestyleHintForOp(selector->mOperator);
2744 18462 : SelectorMatchesFlags selectorFlags = SelectorMatchesFlags::UNKNOWN;
2745 :
2746 : // If hint already includes all the bits of possibleChange,
2747 : // don't bother calling SelectorMatches, since even if it returns false
2748 : // hint won't change.
2749 : // Also don't bother calling SelectorMatches if none of the
2750 : // states passed in are relevant here.
2751 86733 : if ((possibleChange & ~hint) &&
2752 29388 : states.HasAtLeastOneOfStates(aStateMask) &&
2753 : // We can optimize away testing selectors that only involve :hover, a
2754 : // namespace, and a tag name against nodes that don't have the
2755 : // NodeHasRelevantHoverRules flag: such a selector didn't match
2756 : // the tag name or namespace the first time around (since the :hover
2757 : // didn't set the NodeHasRelevantHoverRules flag), so it won't
2758 : // match it now. Check for our selector only having :hover states, or
2759 : // the element having the hover rules flag, or the selector having
2760 : // some sort of non-namespace, non-tagname data in it.
2761 38537 : (states != NS_EVENT_STATE_HOVER ||
2762 7144 : aStatefulElement->HasRelevantHoverRules() ||
2763 6612 : selector->mIDList || selector->mClassList ||
2764 : // We generally expect an mPseudoClassList, since we have a :hover.
2765 : // The question is whether we have anything else in there.
2766 1008 : (selector->mPseudoClassList &&
2767 980 : (selector->mPseudoClassList->mNext ||
2768 476 : selector->mPseudoClassList->mType !=
2769 448 : CSSPseudoClassType::hover)) ||
2770 5977 : selector->mAttrList || selector->mNegations) &&
2771 5333 : (!isPseudoElement ||
2772 0 : StateSelectorMatches(aStatefulElement, selectorForPseudo,
2773 : nodeContext, aData->mTreeMatchContext,
2774 5333 : selectorFlags, nullptr, aStateMask)) &&
2775 5333 : SelectorMatches(aData->mElement, selector, nodeContext,
2776 36930 : aData->mTreeMatchContext, selectorFlags) &&
2777 6 : SelectorMatchesTree(aData->mElement, selector->mNext,
2778 : aData->mTreeMatchContext,
2779 : eMatchOnConditionalRestyleAncestor))
2780 : {
2781 6 : hint = nsRestyleHint(hint | possibleChange);
2782 : }
2783 : }
2784 : }
2785 137 : return hint;
2786 : }
2787 :
2788 : nsRestyleHint
2789 137 : nsCSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData)
2790 : {
2791 : return HasStateDependentStyle(aData,
2792 137 : aData->mElement,
2793 : CSSPseudoElementType::NotPseudo,
2794 137 : aData->mStateMask);
2795 : }
2796 :
2797 : nsRestyleHint
2798 0 : nsCSSRuleProcessor::HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData)
2799 : {
2800 0 : return HasStateDependentStyle(aData,
2801 0 : aData->mPseudoElement,
2802 : aData->mPseudoType,
2803 0 : aData->mStateMask);
2804 : }
2805 :
2806 : bool
2807 27 : nsCSSRuleProcessor::HasDocumentStateDependentStyle(StateRuleProcessorData* aData)
2808 : {
2809 27 : RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
2810 :
2811 27 : return cascade && cascade->mSelectorDocumentStates.HasAtLeastOneOfStates(aData->mStateMask);
2812 : }
2813 :
2814 : struct AttributeEnumData {
2815 1525 : AttributeEnumData(AttributeRuleProcessorData *aData,
2816 : RestyleHintData& aRestyleHintData)
2817 1525 : : data(aData), change(nsRestyleHint(0)), hintData(aRestyleHintData) {}
2818 :
2819 : AttributeRuleProcessorData *data;
2820 : nsRestyleHint change;
2821 : RestyleHintData& hintData;
2822 : };
2823 :
2824 :
2825 : static inline nsRestyleHint
2826 14066 : RestyleHintForSelectorWithAttributeChange(nsRestyleHint aCurrentHint,
2827 : nsCSSSelector* aSelector,
2828 : nsCSSSelector* aRightmostSelector)
2829 : {
2830 14066 : MOZ_ASSERT(aSelector);
2831 :
2832 14066 : char16_t oper = aSelector->mOperator;
2833 :
2834 14066 : if (oper == char16_t('+') || oper == char16_t('~')) {
2835 82 : return eRestyle_LaterSiblings;
2836 : }
2837 :
2838 13984 : if (oper == char16_t(':')) {
2839 282 : return eRestyle_Subtree;
2840 : }
2841 :
2842 13702 : if (oper != char16_t(0)) {
2843 : // Check whether the selector is in a form that supports
2844 : // eRestyle_SomeDescendants. If it isn't, return eRestyle_Subtree.
2845 :
2846 5135 : if (aCurrentHint & eRestyle_Subtree) {
2847 : // No point checking, since we'll end up restyling the whole
2848 : // subtree anyway.
2849 277 : return eRestyle_Subtree;
2850 : }
2851 :
2852 4858 : if (!aRightmostSelector) {
2853 : // aSelector wasn't a top-level selector, which means we were inside
2854 : // a :not() or :-moz-any(). We don't support that.
2855 499 : return eRestyle_Subtree;
2856 : }
2857 :
2858 4359 : MOZ_ASSERT(aSelector != aRightmostSelector,
2859 : "if aSelector == aRightmostSelector then we should have "
2860 : "no operator");
2861 :
2862 : // Check that aRightmostSelector can be passed to RestrictedSelectorMatches.
2863 4359 : if (!aRightmostSelector->IsRestrictedSelector()) {
2864 48 : return eRestyle_Subtree;
2865 : }
2866 :
2867 : // We also don't support pseudo-elements on any of the selectors
2868 : // between aRightmostSelector and aSelector.
2869 : // XXX Can we lift this restriction, so that we don't have to loop
2870 : // over all the selectors?
2871 5539 : for (nsCSSSelector* sel = aRightmostSelector->mNext;
2872 5539 : sel != aSelector;
2873 1228 : sel = sel->mNext) {
2874 1228 : MOZ_ASSERT(sel, "aSelector must be reachable from aRightmostSelector");
2875 1228 : if (sel->PseudoType() != CSSPseudoElementType::NotPseudo) {
2876 0 : return eRestyle_Subtree;
2877 : }
2878 : }
2879 :
2880 4311 : return eRestyle_SomeDescendants;
2881 : }
2882 :
2883 8567 : return eRestyle_Self;
2884 : }
2885 :
2886 : static void
2887 14066 : AttributeEnumFunc(nsCSSSelector* aSelector,
2888 : nsCSSSelector* aRightmostSelector,
2889 : AttributeEnumData* aData)
2890 : {
2891 14066 : AttributeRuleProcessorData *data = aData->data;
2892 :
2893 14066 : if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
2894 : data->mScope)) {
2895 : // The selector is for a rule in a scoped style sheet, and the subject
2896 : // of the selector matching is not in its scope.
2897 0 : return;
2898 : }
2899 :
2900 : nsRestyleHint possibleChange =
2901 14066 : RestyleHintForSelectorWithAttributeChange(aData->change,
2902 14066 : aSelector, aRightmostSelector);
2903 :
2904 : // If, ignoring eRestyle_SomeDescendants, enumData->change already includes
2905 : // all the bits of possibleChange, don't bother calling SelectorMatches, since
2906 : // even if it returns false enumData->change won't change. If possibleChange
2907 : // has eRestyle_SomeDescendants, we need to call SelectorMatches(Tree)
2908 : // regardless as it might give us new selectors to append to
2909 : // mSelectorsForDescendants.
2910 14066 : NodeMatchContext nodeContext(EventStates(), false);
2911 41004 : if (((possibleChange & (~(aData->change) | eRestyle_SomeDescendants))) &&
2912 12872 : SelectorMatches(data->mElement, aSelector, nodeContext,
2913 14329 : data->mTreeMatchContext, SelectorMatchesFlags::UNKNOWN) &&
2914 263 : SelectorMatchesTree(data->mElement, aSelector->mNext,
2915 : data->mTreeMatchContext,
2916 : eMatchOnConditionalRestyleAncestor)) {
2917 206 : aData->change = nsRestyleHint(aData->change | possibleChange);
2918 206 : if (possibleChange & eRestyle_SomeDescendants) {
2919 104 : aData->hintData.mSelectorsForDescendants.AppendElement(aRightmostSelector);
2920 : }
2921 : }
2922 : }
2923 :
2924 : static MOZ_ALWAYS_INLINE void
2925 508 : EnumerateSelectors(nsTArray<SelectorPair>& aSelectors, AttributeEnumData* aData)
2926 : {
2927 508 : SelectorPair *iter = aSelectors.Elements(),
2928 508 : *end = iter + aSelectors.Length();
2929 26016 : for (; iter != end; ++iter) {
2930 12754 : AttributeEnumFunc(iter->mSelector, iter->mRightmostSelector, aData);
2931 : }
2932 508 : }
2933 :
2934 : static MOZ_ALWAYS_INLINE void
2935 52 : EnumerateSelectors(nsTArray<nsCSSSelector*>& aSelectors, AttributeEnumData* aData)
2936 : {
2937 52 : nsCSSSelector **iter = aSelectors.Elements(),
2938 52 : **end = iter + aSelectors.Length();
2939 2676 : for (; iter != end; ++iter) {
2940 1312 : AttributeEnumFunc(*iter, nullptr, aData);
2941 : }
2942 52 : }
2943 :
2944 : nsRestyleHint
2945 1525 : nsCSSRuleProcessor::HasAttributeDependentStyle(
2946 : AttributeRuleProcessorData* aData,
2947 : RestyleHintData& aRestyleHintDataResult)
2948 : {
2949 : // We could try making use of aData->mModType, but :not rules make it a bit
2950 : // of a pain to do so... So just ignore it for now.
2951 :
2952 1525 : AttributeEnumData data(aData, aRestyleHintDataResult);
2953 :
2954 : // Don't do our special handling of certain attributes if the attr
2955 : // hasn't changed yet.
2956 1525 : if (aData->mAttrHasChanged) {
2957 : // check for the lwtheme and lwthemetextcolor attribute on root XUL elements
2958 2274 : if ((aData->mAttribute == nsGkAtoms::lwtheme ||
2959 758 : aData->mAttribute == nsGkAtoms::lwthemetextcolor) &&
2960 758 : aData->mElement->GetNameSpaceID() == kNameSpaceID_XUL &&
2961 0 : aData->mElement == aData->mElement->OwnerDoc()->GetRootElement())
2962 : {
2963 0 : data.change = nsRestyleHint(data.change | eRestyle_Subtree);
2964 : }
2965 :
2966 : // We don't know the namespace of the attribute, and xml:lang applies to
2967 : // all elements. If the lang attribute changes, we need to restyle our
2968 : // whole subtree, since the :lang selector on our descendants can examine
2969 : // our lang attribute.
2970 758 : if (aData->mAttribute == nsGkAtoms::lang) {
2971 0 : data.change = nsRestyleHint(data.change | eRestyle_Subtree);
2972 : }
2973 : }
2974 :
2975 1525 : RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
2976 :
2977 : // Since we get both before and after notifications for attributes, we
2978 : // don't have to ignore aData->mAttribute while matching. Just check
2979 : // whether we have selectors relevant to aData->mAttribute that we
2980 : // match. If this is the before change notification, that will catch
2981 : // rules we might stop matching; if the after change notification, the
2982 : // ones we might have started matching.
2983 1525 : if (cascade) {
2984 1525 : if (aData->mAttribute == nsGkAtoms::id) {
2985 6 : nsIAtom* id = aData->mElement->GetID();
2986 6 : if (id) {
2987 : auto entry =
2988 3 : static_cast<AtomSelectorEntry*>(cascade->mIdSelectors.Search(id));
2989 3 : if (entry) {
2990 0 : EnumerateSelectors(entry->mSelectors, &data);
2991 : }
2992 : }
2993 :
2994 6 : EnumerateSelectors(cascade->mPossiblyNegatedIDSelectors, &data);
2995 : }
2996 :
2997 1571 : if (aData->mAttribute == nsGkAtoms::_class &&
2998 46 : aData->mNameSpaceID == kNameSpaceID_None) {
2999 46 : const nsAttrValue* otherClasses = aData->mOtherValue;
3000 46 : NS_ASSERTION(otherClasses ||
3001 : aData->mModType == nsIDOMMutationEvent::REMOVAL,
3002 : "All class values should be StoresOwnData and parsed"
3003 : "via Element::BeforeSetAttr, so available here");
3004 : // For WillChange, enumerate classes that will be removed to see which
3005 : // rules apply before the change.
3006 : // For Changed, enumerate classes that have been added to see which rules
3007 : // apply after the change.
3008 : // In both cases we're interested in the classes that are currently on
3009 : // the element but not in mOtherValue.
3010 46 : const nsAttrValue* elementClasses = aData->mElement->GetClasses();
3011 46 : if (elementClasses) {
3012 35 : int32_t atomCount = elementClasses->GetAtomCount();
3013 35 : if (atomCount > 0) {
3014 70 : nsTHashtable<nsPtrHashKey<nsIAtom>> otherClassesTable;
3015 35 : if (otherClasses) {
3016 35 : int32_t otherClassesCount = otherClasses->GetAtomCount();
3017 71 : for (int32_t i = 0; i < otherClassesCount; ++i) {
3018 36 : otherClassesTable.PutEntry(otherClasses->AtomAt(i));
3019 : }
3020 : }
3021 82 : for (int32_t i = 0; i < atomCount; ++i) {
3022 47 : nsIAtom* curClass = elementClasses->AtomAt(i);
3023 47 : if (!otherClassesTable.Contains(curClass)) {
3024 : auto entry =
3025 : static_cast<AtomSelectorEntry*>
3026 23 : (cascade->mClassSelectors.Search(curClass));
3027 23 : if (entry) {
3028 8 : EnumerateSelectors(entry->mSelectors, &data);
3029 : }
3030 : }
3031 : }
3032 : }
3033 : }
3034 :
3035 46 : EnumerateSelectors(cascade->mPossiblyNegatedClassSelectors, &data);
3036 : }
3037 :
3038 : auto entry =
3039 : static_cast<AtomSelectorEntry*>
3040 1525 : (cascade->mAttributeSelectors.Search(aData->mAttribute));
3041 1525 : if (entry) {
3042 500 : EnumerateSelectors(entry->mSelectors, &data);
3043 : }
3044 : }
3045 :
3046 1525 : return data.change;
3047 : }
3048 :
3049 : /* virtual */ bool
3050 97 : nsCSSRuleProcessor::MediumFeaturesChanged(nsPresContext* aPresContext)
3051 : {
3052 : // We don't want to do anything if there aren't any sets of rules
3053 : // cached yet, since we should not build the rule cascade too early
3054 : // (e.g., before we know whether the quirk style sheet should be
3055 : // enabled). And if there's nothing cached, it doesn't matter if
3056 : // anything changed. But in the cases where it does matter, we've
3057 : // cached a previous cache key to test against, instead of our current
3058 : // rule cascades. See bug 448281 and bug 1089417.
3059 97 : MOZ_ASSERT(!(mRuleCascades && mPreviousCacheKey));
3060 97 : RuleCascadeData *old = mRuleCascades;
3061 97 : if (old) {
3062 97 : RefreshRuleCascade(aPresContext);
3063 97 : return (old != mRuleCascades);
3064 : }
3065 :
3066 0 : if (mPreviousCacheKey) {
3067 : // RefreshRuleCascade will get rid of mPreviousCacheKey anyway to
3068 : // maintain the invariant that we can't have both an mRuleCascades
3069 : // and an mPreviousCacheKey. But we need to hold it a little
3070 : // longer.
3071 : UniquePtr<nsMediaQueryResultCacheKey> previousCacheKey(
3072 0 : Move(mPreviousCacheKey));
3073 0 : RefreshRuleCascade(aPresContext);
3074 :
3075 : // This test is a bit pessimistic since the cache key's operator==
3076 : // just does list comparison rather than set comparison, but it
3077 : // should catch all the cases we care about (i.e., where the cascade
3078 : // order hasn't changed). Other cases will do a restyle anyway, so
3079 : // we shouldn't need to worry about posting a second.
3080 0 : return !mRuleCascades || // all sheets gone, but we had sheets before
3081 0 : mRuleCascades->mCacheKey != *previousCacheKey;
3082 : }
3083 :
3084 0 : return false;
3085 : }
3086 :
3087 : UniquePtr<nsMediaQueryResultCacheKey>
3088 33 : nsCSSRuleProcessor::CloneMQCacheKey()
3089 : {
3090 33 : MOZ_ASSERT(!(mRuleCascades && mPreviousCacheKey));
3091 :
3092 33 : RuleCascadeData* c = mRuleCascades;
3093 33 : if (!c) {
3094 : // We might have an mPreviousCacheKey. It already comes from a call
3095 : // to CloneMQCacheKey, so don't bother checking
3096 : // HasFeatureConditions().
3097 28 : if (mPreviousCacheKey) {
3098 0 : NS_ASSERTION(mPreviousCacheKey->HasFeatureConditions(),
3099 : "we shouldn't have a previous cache key unless it has "
3100 : "feature conditions");
3101 0 : return MakeUnique<nsMediaQueryResultCacheKey>(*mPreviousCacheKey);
3102 : }
3103 :
3104 28 : return UniquePtr<nsMediaQueryResultCacheKey>();
3105 : }
3106 :
3107 5 : if (!c->mCacheKey.HasFeatureConditions()) {
3108 0 : return UniquePtr<nsMediaQueryResultCacheKey>();
3109 : }
3110 :
3111 5 : return MakeUnique<nsMediaQueryResultCacheKey>(c->mCacheKey);
3112 : }
3113 :
3114 : /* virtual */ size_t
3115 5 : nsCSSRuleProcessor::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
3116 : {
3117 5 : size_t n = 0;
3118 5 : n += mSheets.ShallowSizeOfExcludingThis(aMallocSizeOf);
3119 7 : for (RuleCascadeData* cascade = mRuleCascades; cascade;
3120 2 : cascade = cascade->mNext) {
3121 2 : n += cascade->SizeOfIncludingThis(aMallocSizeOf);
3122 : }
3123 :
3124 5 : return n;
3125 : }
3126 :
3127 : /* virtual */ size_t
3128 5 : nsCSSRuleProcessor::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
3129 : {
3130 5 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
3131 : }
3132 :
3133 : // Append all the currently-active font face rules to aArray. Return
3134 : // true for success and false for failure.
3135 : bool
3136 16 : nsCSSRuleProcessor::AppendFontFaceRules(
3137 : nsPresContext *aPresContext,
3138 : nsTArray<nsFontFaceRuleContainer>& aArray)
3139 : {
3140 16 : RuleCascadeData* cascade = GetRuleCascade(aPresContext);
3141 :
3142 16 : if (cascade) {
3143 16 : if (!aArray.AppendElements(cascade->mFontFaceRules))
3144 0 : return false;
3145 : }
3146 :
3147 16 : return true;
3148 : }
3149 :
3150 : nsCSSKeyframesRule*
3151 0 : nsCSSRuleProcessor::KeyframesRuleForName(nsPresContext* aPresContext,
3152 : const nsString& aName)
3153 : {
3154 0 : RuleCascadeData* cascade = GetRuleCascade(aPresContext);
3155 :
3156 0 : if (cascade) {
3157 0 : return cascade->mKeyframesRuleTable.Get(aName);
3158 : }
3159 :
3160 0 : return nullptr;
3161 : }
3162 :
3163 : nsCSSCounterStyleRule*
3164 0 : nsCSSRuleProcessor::CounterStyleRuleForName(nsPresContext* aPresContext,
3165 : nsIAtom* aName)
3166 : {
3167 0 : RuleCascadeData* cascade = GetRuleCascade(aPresContext);
3168 :
3169 0 : if (cascade) {
3170 0 : return cascade->mCounterStyleRuleTable.Get(aName);
3171 : }
3172 :
3173 0 : return nullptr;
3174 : }
3175 :
3176 : // Append all the currently-active page rules to aArray. Return
3177 : // true for success and false for failure.
3178 : bool
3179 0 : nsCSSRuleProcessor::AppendPageRules(
3180 : nsPresContext* aPresContext,
3181 : nsTArray<nsCSSPageRule*>& aArray)
3182 : {
3183 0 : RuleCascadeData* cascade = GetRuleCascade(aPresContext);
3184 :
3185 0 : if (cascade) {
3186 0 : if (!aArray.AppendElements(cascade->mPageRules)) {
3187 0 : return false;
3188 : }
3189 : }
3190 :
3191 0 : return true;
3192 : }
3193 :
3194 : bool
3195 0 : nsCSSRuleProcessor::AppendFontFeatureValuesRules(
3196 : nsPresContext *aPresContext,
3197 : nsTArray<nsCSSFontFeatureValuesRule*>& aArray)
3198 : {
3199 0 : RuleCascadeData* cascade = GetRuleCascade(aPresContext);
3200 :
3201 0 : if (cascade) {
3202 0 : if (!aArray.AppendElements(cascade->mFontFeatureValuesRules))
3203 0 : return false;
3204 : }
3205 :
3206 0 : return true;
3207 : }
3208 :
3209 : nsresult
3210 15 : nsCSSRuleProcessor::ClearRuleCascades()
3211 : {
3212 15 : if (!mPreviousCacheKey) {
3213 15 : mPreviousCacheKey = CloneMQCacheKey();
3214 : }
3215 :
3216 : // No need to remove the rule processor from the RuleProcessorCache here,
3217 : // since CSSStyleSheet::ClearRuleCascades will have called
3218 : // RuleProcessorCache::RemoveSheet() passing itself, which will catch
3219 : // this rule processor (and any others for different @-moz-document
3220 : // cache key results).
3221 15 : MOZ_ASSERT(!RuleProcessorCache::HasRuleProcessor(this));
3222 :
3223 : #ifdef DEBUG
3224 : // For shared rule processors, if we've already gathered document
3225 : // rules, then they will now be out of date. We don't actually need
3226 : // them to be up-to-date (see the comment in RefreshRuleCascade), so
3227 : // record their invalidity so we can assert if we try to use them.
3228 15 : if (!mMustGatherDocumentRules) {
3229 15 : mDocumentRulesAndCacheKeyValid = false;
3230 : }
3231 : #endif
3232 :
3233 : // We rely on our caller (perhaps indirectly) to do something that
3234 : // will rebuild style data and the user font set (either
3235 : // nsIPresShell::RestyleForCSSRuleChanges or
3236 : // nsPresContext::RebuildAllStyleData).
3237 15 : RuleCascadeData *data = mRuleCascades;
3238 15 : mRuleCascades = nullptr;
3239 17 : while (data) {
3240 1 : RuleCascadeData *next = data->mNext;
3241 1 : delete data;
3242 1 : data = next;
3243 : }
3244 15 : return NS_OK;
3245 : }
3246 :
3247 :
3248 : // This function should return the set of states that this selector
3249 : // depends on; this is used to implement HasStateDependentStyle. It
3250 : // does NOT recur down into things like :not and :-moz-any.
3251 : inline
3252 26598 : EventStates ComputeSelectorStateDependence(nsCSSSelector& aSelector)
3253 : {
3254 26598 : EventStates states;
3255 32174 : for (nsPseudoClassList* pseudoClass = aSelector.mPseudoClassList;
3256 37750 : pseudoClass; pseudoClass = pseudoClass->mNext) {
3257 : // Tree pseudo-elements overload mPseudoClassList for things that
3258 : // aren't pseudo-classes.
3259 5576 : if (pseudoClass->mType >= CSSPseudoClassType::Count) {
3260 0 : continue;
3261 : }
3262 :
3263 5576 : auto idx = static_cast<CSSPseudoClassTypeBase>(pseudoClass->mType);
3264 5576 : states |= sPseudoClassStateDependences[idx];
3265 : }
3266 26598 : return states;
3267 : }
3268 :
3269 : static bool
3270 24905 : AddSelector(RuleCascadeData* aCascade,
3271 : // The part between combinators at the top level of the selector
3272 : nsCSSSelector* aSelectorInTopLevel,
3273 : // The part we should look through (might be in :not or :-moz-any())
3274 : nsCSSSelector* aSelectorPart,
3275 : // The right-most selector at the top level
3276 : nsCSSSelector* aRightmostSelector)
3277 : {
3278 : // It's worth noting that this loop over negations isn't quite
3279 : // optimal for two reasons. One, we could add something to one of
3280 : // these lists twice, which means we'll check it twice, but I don't
3281 : // think that's worth worrying about. (We do the same for multiple
3282 : // attribute selectors on the same attribute.) Two, we don't really
3283 : // need to check negations past the first in the current
3284 : // implementation (and they're rare as well), but that might change
3285 : // in the future if :not() is extended.
3286 51503 : for (nsCSSSelector* negation = aSelectorPart; negation;
3287 26598 : negation = negation->mNegations) {
3288 : // Track both document states and attribute dependence in pseudo-classes.
3289 32174 : for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList;
3290 37750 : pseudoClass; pseudoClass = pseudoClass->mNext) {
3291 5576 : switch (pseudoClass->mType) {
3292 : case CSSPseudoClassType::mozLocaleDir: {
3293 313 : aCascade->mSelectorDocumentStates |= NS_DOCUMENT_STATE_RTL_LOCALE;
3294 313 : break;
3295 : }
3296 : case CSSPseudoClassType::mozWindowInactive: {
3297 15 : aCascade->mSelectorDocumentStates |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
3298 15 : break;
3299 : }
3300 : case CSSPseudoClassType::mozTableBorderNonzero: {
3301 : nsTArray<SelectorPair> *array =
3302 56 : aCascade->AttributeListFor(nsGkAtoms::border);
3303 56 : if (!array) {
3304 0 : return false;
3305 : }
3306 112 : array->AppendElement(SelectorPair(aSelectorInTopLevel,
3307 56 : aRightmostSelector));
3308 56 : break;
3309 : }
3310 : default: {
3311 5192 : break;
3312 : }
3313 : }
3314 : }
3315 :
3316 : // Build mStateSelectors.
3317 26598 : EventStates dependentStates = ComputeSelectorStateDependence(*negation);
3318 26598 : if (!dependentStates.IsEmpty()) {
3319 1912 : aCascade->mStateSelectors.AppendElement(
3320 3824 : nsCSSRuleProcessor::StateSelector(dependentStates,
3321 1912 : aSelectorInTopLevel));
3322 : }
3323 :
3324 : // Build mIDSelectors
3325 26598 : if (negation == aSelectorInTopLevel) {
3326 27887 : for (nsAtomList* curID = negation->mIDList; curID;
3327 5197 : curID = curID->mNext) {
3328 : auto entry = static_cast<AtomSelectorEntry*>
3329 5197 : (aCascade->mIdSelectors.Add(curID->mAtom, fallible));
3330 5197 : if (entry) {
3331 10394 : entry->mSelectors.AppendElement(SelectorPair(aSelectorInTopLevel,
3332 5197 : aRightmostSelector));
3333 : }
3334 : }
3335 3908 : } else if (negation->mIDList) {
3336 650 : aCascade->mPossiblyNegatedIDSelectors.AppendElement(aSelectorInTopLevel);
3337 : }
3338 :
3339 : // Build mClassSelectors
3340 26598 : if (negation == aSelectorInTopLevel) {
3341 29712 : for (nsAtomList* curClass = negation->mClassList; curClass;
3342 7022 : curClass = curClass->mNext) {
3343 : auto entry = static_cast<AtomSelectorEntry*>
3344 7022 : (aCascade->mClassSelectors.Add(curClass->mAtom, fallible));
3345 7022 : if (entry) {
3346 14044 : entry->mSelectors.AppendElement(SelectorPair(aSelectorInTopLevel,
3347 7022 : aRightmostSelector));
3348 : }
3349 : }
3350 3908 : } else if (negation->mClassList) {
3351 169 : aCascade->mPossiblyNegatedClassSelectors.AppendElement(aSelectorInTopLevel);
3352 : }
3353 :
3354 : // Build mAttributeSelectors.
3355 33752 : for (nsAttrSelector *attr = negation->mAttrList; attr;
3356 7154 : attr = attr->mNext) {
3357 : nsTArray<SelectorPair> *array =
3358 7154 : aCascade->AttributeListFor(attr->mCasedAttr);
3359 7154 : if (!array) {
3360 0 : return false;
3361 : }
3362 14308 : array->AppendElement(SelectorPair(aSelectorInTopLevel,
3363 7154 : aRightmostSelector));
3364 7154 : if (attr->mLowercaseAttr != attr->mCasedAttr) {
3365 250 : array = aCascade->AttributeListFor(attr->mLowercaseAttr);
3366 250 : if (!array) {
3367 0 : return false;
3368 : }
3369 500 : array->AppendElement(SelectorPair(aSelectorInTopLevel,
3370 250 : aRightmostSelector));
3371 : }
3372 : }
3373 :
3374 : // Recur through any :-moz-any selectors
3375 32174 : for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList;
3376 37750 : pseudoClass; pseudoClass = pseudoClass->mNext) {
3377 5576 : if (pseudoClass->mType == CSSPseudoClassType::any) {
3378 2760 : for (nsCSSSelectorList *l = pseudoClass->u.mSelectors; l; l = l->mNext) {
3379 2215 : nsCSSSelector *s = l->mSelectors;
3380 2215 : if (!AddSelector(aCascade, aSelectorInTopLevel, s,
3381 : aRightmostSelector)) {
3382 0 : return false;
3383 : }
3384 : }
3385 : }
3386 : }
3387 : }
3388 :
3389 24905 : return true;
3390 : }
3391 :
3392 : static bool
3393 14586 : AddRule(RuleSelectorPair* aRuleInfo, RuleCascadeData* aCascade)
3394 : {
3395 14586 : RuleCascadeData * const cascade = aCascade;
3396 :
3397 : // Build the rule hash.
3398 14586 : CSSPseudoElementType pseudoType = aRuleInfo->mSelector->PseudoType();
3399 14586 : if (MOZ_LIKELY(pseudoType == CSSPseudoElementType::NotPseudo)) {
3400 13444 : cascade->mRuleHash.AppendRule(*aRuleInfo);
3401 1142 : } else if (pseudoType < CSSPseudoElementType::Count) {
3402 : RuleHash*& ruleHash = cascade->mPseudoElementRuleHashes[
3403 696 : static_cast<CSSPseudoElementTypeBase>(pseudoType)];
3404 696 : if (!ruleHash) {
3405 169 : ruleHash = new RuleHash(cascade->mQuirksMode);
3406 169 : if (!ruleHash) {
3407 : // Out of memory; give up
3408 0 : return false;
3409 : }
3410 : }
3411 696 : NS_ASSERTION(aRuleInfo->mSelector->mNext,
3412 : "Must have mNext; parser screwed up");
3413 696 : NS_ASSERTION(aRuleInfo->mSelector->mNext->mOperator == ':',
3414 : "Unexpected mNext combinator");
3415 696 : ruleHash->AppendRule(*aRuleInfo);
3416 446 : } else if (pseudoType == CSSPseudoElementType::InheritingAnonBox ||
3417 : pseudoType == CSSPseudoElementType::NonInheritingAnonBox) {
3418 340 : NS_ASSERTION(!aRuleInfo->mSelector->mCasedTag &&
3419 : !aRuleInfo->mSelector->mIDList &&
3420 : !aRuleInfo->mSelector->mClassList &&
3421 : !aRuleInfo->mSelector->mPseudoClassList &&
3422 : !aRuleInfo->mSelector->mAttrList &&
3423 : !aRuleInfo->mSelector->mNegations &&
3424 : !aRuleInfo->mSelector->mNext &&
3425 : aRuleInfo->mSelector->mNameSpace == kNameSpaceID_Unknown,
3426 : "Parser messed up with anon box selector");
3427 :
3428 : // Index doesn't matter here, since we'll just be walking these
3429 : // rules in order; just pass 0.
3430 340 : AppendRuleToTagTable(&cascade->mAnonBoxRules,
3431 340 : aRuleInfo->mSelector->mLowercaseTag,
3432 680 : RuleValue(*aRuleInfo, 0, aCascade->mQuirksMode));
3433 : } else {
3434 : #ifdef MOZ_XUL
3435 106 : NS_ASSERTION(pseudoType == CSSPseudoElementType::XULTree,
3436 : "Unexpected pseudo type");
3437 : // Index doesn't matter here, since we'll just be walking these
3438 : // rules in order; just pass 0.
3439 106 : AppendRuleToTagTable(&cascade->mXULTreeRules,
3440 106 : aRuleInfo->mSelector->mLowercaseTag,
3441 212 : RuleValue(*aRuleInfo, 0, aCascade->mQuirksMode));
3442 : #else
3443 : NS_NOTREACHED("Unexpected pseudo type");
3444 : #endif
3445 : }
3446 :
3447 38194 : for (nsCSSSelector* selector = aRuleInfo->mSelector;
3448 61802 : selector; selector = selector->mNext) {
3449 23608 : if (selector->IsPseudoElement()) {
3450 1142 : CSSPseudoElementType pseudo = selector->PseudoType();
3451 1838 : if (pseudo >= CSSPseudoElementType::Count ||
3452 696 : !nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudo)) {
3453 918 : NS_ASSERTION(!selector->mNegations, "Shouldn't have negations");
3454 : // We do store selectors ending with pseudo-elements that allow :hover
3455 : // and :active after them in the hashtables corresponding to that
3456 : // selector's mNext (i.e. the thing that matches against the element),
3457 : // but we want to make sure that selectors for any other kinds of
3458 : // pseudo-elements don't end up in the hashtables. In particular, tree
3459 : // pseudos store strange things in mPseudoClassList that we don't want
3460 : // to try to match elements against.
3461 918 : continue;
3462 : }
3463 : }
3464 22690 : if (!AddSelector(cascade, selector, selector, aRuleInfo->mSelector)) {
3465 0 : return false;
3466 : }
3467 : }
3468 :
3469 14586 : return true;
3470 : }
3471 :
3472 : struct PerWeightDataListItem : public RuleSelectorPair {
3473 14586 : PerWeightDataListItem(css::StyleRule* aRule, nsCSSSelector* aSelector)
3474 14586 : : RuleSelectorPair(aRule, aSelector)
3475 14586 : , mNext(nullptr)
3476 14586 : {}
3477 : // No destructor; these are arena-allocated
3478 :
3479 :
3480 : // Placement new to arena allocate the PerWeightDataListItem
3481 14586 : void *operator new(size_t aSize, CascadeAllocator &aArena) CPP_THROW_NEW {
3482 14586 : return aArena.Allocate(aSize, fallible);
3483 : }
3484 :
3485 : PerWeightDataListItem *mNext;
3486 : };
3487 :
3488 : struct PerWeightData {
3489 1116 : PerWeightData()
3490 1116 : : mRuleSelectorPairs(nullptr)
3491 1116 : , mTail(&mRuleSelectorPairs)
3492 1116 : {}
3493 :
3494 : int32_t mWeight;
3495 : PerWeightDataListItem *mRuleSelectorPairs;
3496 : PerWeightDataListItem **mTail;
3497 : };
3498 :
3499 558 : struct RuleByWeightEntry : public PLDHashEntryHdr {
3500 : PerWeightData data; // mWeight is key, mRuleSelectorPairs are value
3501 : };
3502 :
3503 : static PLDHashNumber
3504 14586 : HashIntKey(const void *key)
3505 : {
3506 14586 : return HashGeneric(key);
3507 : }
3508 :
3509 : static bool
3510 14028 : MatchWeightEntry(const PLDHashEntryHdr *hdr, const void *key)
3511 : {
3512 14028 : const RuleByWeightEntry *entry = (const RuleByWeightEntry *)hdr;
3513 14028 : return entry->data.mWeight == NS_PTR_TO_INT32(key);
3514 : }
3515 :
3516 : static void
3517 558 : InitWeightEntry(PLDHashEntryHdr *hdr, const void *key)
3518 : {
3519 558 : RuleByWeightEntry* entry = static_cast<RuleByWeightEntry*>(hdr);
3520 558 : new (KnownNotNull, entry) RuleByWeightEntry();
3521 558 : }
3522 :
3523 : static const PLDHashTableOps gRulesByWeightOps = {
3524 : HashIntKey,
3525 : MatchWeightEntry,
3526 : PLDHashTable::MoveEntryStub,
3527 : PLDHashTable::ClearEntryStub,
3528 : InitWeightEntry
3529 : };
3530 :
3531 : struct CascadeEnumData {
3532 42 : CascadeEnumData(nsPresContext* aPresContext,
3533 : nsTArray<nsFontFaceRuleContainer>& aFontFaceRules,
3534 : nsTArray<nsCSSKeyframesRule*>& aKeyframesRules,
3535 : nsTArray<nsCSSFontFeatureValuesRule*>& aFontFeatureValuesRules,
3536 : nsTArray<nsCSSPageRule*>& aPageRules,
3537 : nsTArray<nsCSSCounterStyleRule*>& aCounterStyleRules,
3538 : nsTArray<css::DocumentRule*>& aDocumentRules,
3539 : nsMediaQueryResultCacheKey& aKey,
3540 : nsDocumentRuleResultCacheKey& aDocumentKey,
3541 : SheetType aSheetType,
3542 : bool aMustGatherDocumentRules)
3543 42 : : mPresContext(aPresContext),
3544 : mFontFaceRules(aFontFaceRules),
3545 : mKeyframesRules(aKeyframesRules),
3546 : mFontFeatureValuesRules(aFontFeatureValuesRules),
3547 : mPageRules(aPageRules),
3548 : mCounterStyleRules(aCounterStyleRules),
3549 : mDocumentRules(aDocumentRules),
3550 : mCacheKey(aKey),
3551 : mDocumentCacheKey(aDocumentKey),
3552 : mRulesByWeight(&gRulesByWeightOps, sizeof(RuleByWeightEntry), 32),
3553 : mSheetType(aSheetType),
3554 42 : mMustGatherDocumentRules(aMustGatherDocumentRules)
3555 : {
3556 42 : }
3557 :
3558 42 : ~CascadeEnumData()
3559 42 : {
3560 42 : }
3561 :
3562 : nsPresContext* mPresContext;
3563 : nsTArray<nsFontFaceRuleContainer>& mFontFaceRules;
3564 : nsTArray<nsCSSKeyframesRule*>& mKeyframesRules;
3565 : nsTArray<nsCSSFontFeatureValuesRule*>& mFontFeatureValuesRules;
3566 : nsTArray<nsCSSPageRule*>& mPageRules;
3567 : nsTArray<nsCSSCounterStyleRule*>& mCounterStyleRules;
3568 : nsTArray<css::DocumentRule*>& mDocumentRules;
3569 : nsMediaQueryResultCacheKey& mCacheKey;
3570 : nsDocumentRuleResultCacheKey& mDocumentCacheKey;
3571 : // We want page-sized arenas so there's no fragmentation involved.
3572 : CascadeAllocator mArena;
3573 : // Hooray, a manual PLDHashTable since nsClassHashtable doesn't
3574 : // provide a getter that gives me a *reference* to the value.
3575 : PLDHashTable mRulesByWeight; // of PerWeightDataListItem linked lists
3576 : SheetType mSheetType;
3577 : bool mMustGatherDocumentRules;
3578 : };
3579 :
3580 : /**
3581 : * Recursively traverses rules in order to:
3582 : * (1) add any @-moz-document rules into data->mDocumentRules.
3583 : * (2) record any @-moz-document rules whose conditions evaluate to true
3584 : * on data->mDocumentCacheKey.
3585 : *
3586 : * See also CascadeRuleEnumFunc below, which calls us via
3587 : * EnumerateRulesForwards. If modifying this function you may need to
3588 : * update CascadeRuleEnumFunc too.
3589 : */
3590 : static bool
3591 79 : GatherDocRuleEnumFunc(css::Rule* aRule, void* aData)
3592 : {
3593 79 : CascadeEnumData* data = (CascadeEnumData*)aData;
3594 79 : int32_t type = aRule->GetType();
3595 :
3596 79 : MOZ_ASSERT(data->mMustGatherDocumentRules,
3597 : "should only call GatherDocRuleEnumFunc if "
3598 : "mMustGatherDocumentRules is true");
3599 :
3600 79 : if (css::Rule::MEDIA_RULE == type ||
3601 : css::Rule::SUPPORTS_RULE == type) {
3602 0 : css::GroupRule* groupRule = static_cast<css::GroupRule*>(aRule);
3603 0 : if (!groupRule->EnumerateRulesForwards(GatherDocRuleEnumFunc, aData)) {
3604 0 : return false;
3605 0 : }
3606 : }
3607 79 : else if (css::Rule::DOCUMENT_RULE == type) {
3608 0 : css::DocumentRule* docRule = static_cast<css::DocumentRule*>(aRule);
3609 0 : if (!data->mDocumentRules.AppendElement(docRule)) {
3610 0 : return false;
3611 : }
3612 0 : if (docRule->UseForPresentation(data->mPresContext)) {
3613 0 : if (!data->mDocumentCacheKey.AddMatchingRule(docRule)) {
3614 0 : return false;
3615 : }
3616 : }
3617 0 : if (!docRule->EnumerateRulesForwards(GatherDocRuleEnumFunc, aData)) {
3618 0 : return false;
3619 : }
3620 : }
3621 79 : return true;
3622 : }
3623 :
3624 : /*
3625 : * This enumerates style rules in a sheet (and recursively into any
3626 : * grouping rules) in order to:
3627 : * (1) add any style rules, in order, into data->mRulesByWeight (for
3628 : * the primary CSS cascade), where they are separated by weight
3629 : * but kept in order per-weight, and
3630 : * (2) add any @font-face rules, in order, into data->mFontFaceRules.
3631 : * (3) add any @keyframes rules, in order, into data->mKeyframesRules.
3632 : * (4) add any @font-feature-value rules, in order,
3633 : * into data->mFontFeatureValuesRules.
3634 : * (5) add any @page rules, in order, into data->mPageRules.
3635 : * (6) add any @counter-style rules, in order, into data->mCounterStyleRules.
3636 : * (7) add any @-moz-document rules into data->mDocumentRules.
3637 : * (8) record any @-moz-document rules whose conditions evaluate to true
3638 : * on data->mDocumentCacheKey.
3639 : *
3640 : * See also GatherDocRuleEnumFunc above, which we call to traverse into
3641 : * @-moz-document rules even if their (or an ancestor's) condition
3642 : * fails. This means we might look at the result of some @-moz-document
3643 : * rules that don't actually affect whether a RuleProcessorCache lookup
3644 : * is a hit or a miss. The presence of @-moz-document rules inside
3645 : * @media etc. rules should be rare, and looking at all of them in the
3646 : * sheets lets us avoid the complication of having different document
3647 : * cache key results for different media.
3648 : *
3649 : * If modifying this function you may need to update
3650 : * GatherDocRuleEnumFunc too.
3651 : */
3652 : static bool
3653 9629 : CascadeRuleEnumFunc(css::Rule* aRule, void* aData)
3654 : {
3655 9629 : CascadeEnumData* data = (CascadeEnumData*)aData;
3656 9629 : int32_t type = aRule->GetType();
3657 9629 : MOZ_ASSERT(type != css::Rule::IMPORT_RULE,
3658 : "@import rule must not be handled here");
3659 :
3660 9629 : if (css::Rule::STYLE_RULE == type) {
3661 8667 : css::StyleRule* styleRule = static_cast<css::StyleRule*>(aRule);
3662 :
3663 23253 : for (nsCSSSelectorList *sel = styleRule->Selector();
3664 37839 : sel; sel = sel->mNext) {
3665 14586 : int32_t weight = sel->mWeight;
3666 : auto entry = static_cast<RuleByWeightEntry*>
3667 14586 : (data->mRulesByWeight.Add(NS_INT32_TO_PTR(weight), fallible));
3668 14586 : if (!entry)
3669 0 : return false;
3670 14586 : entry->data.mWeight = weight;
3671 : // entry->data.mRuleSelectorPairs should be linked in forward order;
3672 : // entry->data.mTail is the slot to write to.
3673 : auto* newItem =
3674 14586 : new (data->mArena) PerWeightDataListItem(styleRule, sel->mSelectors);
3675 14586 : if (newItem) {
3676 14586 : *(entry->data.mTail) = newItem;
3677 14586 : entry->data.mTail = &newItem->mNext;
3678 : }
3679 : }
3680 : }
3681 962 : else if (css::Rule::MEDIA_RULE == type ||
3682 : css::Rule::SUPPORTS_RULE == type) {
3683 149 : css::GroupRule* groupRule = static_cast<css::GroupRule*>(aRule);
3684 : const bool use =
3685 149 : groupRule->UseForPresentation(data->mPresContext, data->mCacheKey);
3686 149 : if (use || data->mMustGatherDocumentRules) {
3687 133 : if (!groupRule->EnumerateRulesForwards(use ? CascadeRuleEnumFunc :
3688 : GatherDocRuleEnumFunc,
3689 : aData)) {
3690 0 : return false;
3691 : }
3692 149 : }
3693 : }
3694 813 : else if (css::Rule::DOCUMENT_RULE == type) {
3695 8 : css::DocumentRule* docRule = static_cast<css::DocumentRule*>(aRule);
3696 8 : if (data->mMustGatherDocumentRules) {
3697 7 : if (!data->mDocumentRules.AppendElement(docRule)) {
3698 0 : return false;
3699 : }
3700 : }
3701 8 : const bool use = docRule->UseForPresentation(data->mPresContext);
3702 8 : if (use && data->mMustGatherDocumentRules) {
3703 4 : if (!data->mDocumentCacheKey.AddMatchingRule(docRule)) {
3704 0 : return false;
3705 : }
3706 : }
3707 8 : if (use || data->mMustGatherDocumentRules) {
3708 7 : if (!docRule->EnumerateRulesForwards(use ? CascadeRuleEnumFunc
3709 : : GatherDocRuleEnumFunc,
3710 : aData)) {
3711 0 : return false;
3712 : }
3713 : }
3714 : }
3715 805 : else if (css::Rule::FONT_FACE_RULE == type) {
3716 0 : nsCSSFontFaceRule *fontFaceRule = static_cast<nsCSSFontFaceRule*>(aRule);
3717 0 : nsFontFaceRuleContainer *ptr = data->mFontFaceRules.AppendElement();
3718 0 : if (!ptr)
3719 0 : return false;
3720 0 : ptr->mRule = fontFaceRule;
3721 0 : ptr->mSheetType = data->mSheetType;
3722 : }
3723 805 : else if (css::Rule::KEYFRAMES_RULE == type) {
3724 : nsCSSKeyframesRule *keyframesRule =
3725 93 : static_cast<nsCSSKeyframesRule*>(aRule);
3726 93 : if (!data->mKeyframesRules.AppendElement(keyframesRule)) {
3727 0 : return false;
3728 : }
3729 : }
3730 712 : else if (css::Rule::FONT_FEATURE_VALUES_RULE == type) {
3731 : nsCSSFontFeatureValuesRule *fontFeatureValuesRule =
3732 0 : static_cast<nsCSSFontFeatureValuesRule*>(aRule);
3733 0 : if (!data->mFontFeatureValuesRules.AppendElement(fontFeatureValuesRule)) {
3734 0 : return false;
3735 : }
3736 : }
3737 712 : else if (css::Rule::PAGE_RULE == type) {
3738 0 : nsCSSPageRule* pageRule = static_cast<nsCSSPageRule*>(aRule);
3739 0 : if (!data->mPageRules.AppendElement(pageRule)) {
3740 0 : return false;
3741 : }
3742 : }
3743 712 : else if (css::Rule::COUNTER_STYLE_RULE == type) {
3744 : nsCSSCounterStyleRule* counterStyleRule =
3745 552 : static_cast<nsCSSCounterStyleRule*>(aRule);
3746 552 : if (!data->mCounterStyleRules.AppendElement(counterStyleRule)) {
3747 0 : return false;
3748 : }
3749 : }
3750 9629 : return true;
3751 : }
3752 :
3753 : /* static */ bool
3754 144 : nsCSSRuleProcessor::CascadeSheet(CSSStyleSheet* aSheet, CascadeEnumData* aData)
3755 : {
3756 432 : if (aSheet->IsApplicable() &&
3757 288 : aSheet->UseForPresentation(aData->mPresContext, aData->mCacheKey) &&
3758 144 : aSheet->mInner) {
3759 144 : auto& rules = aSheet->Inner()->mOrderedRules;
3760 144 : uint32_t i = 0, len = rules.Length();
3761 160 : for (; i < len; i++) {
3762 152 : if (rules[i]->GetType() != css::Rule::IMPORT_RULE) {
3763 144 : break;
3764 : }
3765 : }
3766 :
3767 144 : if (i > 0) {
3768 : // Collect stylesheets from @import rules. It is done in reverse
3769 : // order so that we can avoid cascading duplicate sheets.
3770 16 : nsTArray<StyleSheet*> childSheets(i);
3771 16 : nsTHashtable<nsPtrHashKey<StyleSheet>> childSheetSet(i);
3772 16 : for (uint32_t j = i; j > 0; j--) {
3773 8 : auto importRule = static_cast<css::ImportRule*>(rules[j - 1]);
3774 8 : StyleSheet* sheet = importRule->GetStyleSheet();
3775 : // There are two cases we want to ignore an import rule:
3776 : // 1. the import rule does not have stylesheet connected because
3777 : // it fails in security check or there is a loop involved.
3778 : // 2. the sheet has been referenced by another import rule at a
3779 : // later position.
3780 8 : if (sheet && !childSheetSet.Contains(sheet)) {
3781 8 : childSheets.AppendElement(sheet);
3782 8 : childSheetSet.PutEntry(sheet);
3783 : }
3784 : }
3785 : // Now cascade all sheets listed.
3786 16 : for (StyleSheet* child : Reversed(childSheets)) {
3787 8 : CascadeSheet(child->AsGecko(), aData);
3788 : }
3789 : }
3790 :
3791 18844 : for (; i < len; i++) {
3792 9350 : if (!CascadeRuleEnumFunc(rules[i], aData)) {
3793 0 : return false;
3794 : }
3795 : }
3796 : }
3797 144 : return true;
3798 : }
3799 :
3800 2194 : static int CompareWeightData(const void* aArg1, const void* aArg2,
3801 : void* closure)
3802 : {
3803 2194 : const PerWeightData* arg1 = static_cast<const PerWeightData*>(aArg1);
3804 2194 : const PerWeightData* arg2 = static_cast<const PerWeightData*>(aArg2);
3805 2194 : return arg1->mWeight - arg2->mWeight; // put lower weight first
3806 : }
3807 :
3808 : RuleCascadeData*
3809 12201 : nsCSSRuleProcessor::GetRuleCascade(nsPresContext* aPresContext)
3810 : {
3811 : // FIXME: Make this infallible!
3812 :
3813 : // If anything changes about the presentation context, we will be
3814 : // notified. Otherwise, our cache is valid if mLastPresContext
3815 : // matches aPresContext. (The only rule processors used for multiple
3816 : // pres contexts are for XBL. These rule processors are probably less
3817 : // likely to have @media rules, and thus the cache is pretty likely to
3818 : // hit instantly even when we're switching between pres contexts.)
3819 :
3820 12201 : if (!mRuleCascades || aPresContext != mLastPresContext) {
3821 79 : RefreshRuleCascade(aPresContext);
3822 : }
3823 12201 : mLastPresContext = aPresContext;
3824 :
3825 12201 : return mRuleCascades;
3826 : }
3827 :
3828 : void
3829 176 : nsCSSRuleProcessor::RefreshRuleCascade(nsPresContext* aPresContext)
3830 : {
3831 : // Having RuleCascadeData objects be per-medium (over all variation
3832 : // caused by media queries, handled through mCacheKey) works for now
3833 : // since nsCSSRuleProcessor objects are per-document. (For a given
3834 : // set of stylesheets they can vary based on medium (@media) or
3835 : // document (@-moz-document).)
3836 :
3837 356 : for (RuleCascadeData **cascadep = &mRuleCascades, *cascade;
3838 180 : (cascade = *cascadep); cascadep = &cascade->mNext) {
3839 136 : if (cascade->mCacheKey.Matches(aPresContext)) {
3840 : // Ensure that the current one is always mRuleCascades.
3841 134 : *cascadep = cascade->mNext;
3842 134 : cascade->mNext = mRuleCascades;
3843 134 : mRuleCascades = cascade;
3844 :
3845 134 : return;
3846 : }
3847 : }
3848 :
3849 : // We're going to make a new rule cascade; this means that we should
3850 : // now stop using the previous cache key that we're holding on to from
3851 : // the last time we had rule cascades.
3852 42 : mPreviousCacheKey = nullptr;
3853 :
3854 42 : if (mSheets.Length() != 0) {
3855 : nsAutoPtr<RuleCascadeData> newCascade(
3856 42 : new RuleCascadeData(aPresContext->Medium(),
3857 126 : eCompatibility_NavQuirks == aPresContext->CompatibilityMode()));
3858 42 : if (newCascade) {
3859 42 : CascadeEnumData data(aPresContext, newCascade->mFontFaceRules,
3860 42 : newCascade->mKeyframesRules,
3861 42 : newCascade->mFontFeatureValuesRules,
3862 42 : newCascade->mPageRules,
3863 42 : newCascade->mCounterStyleRules,
3864 : mDocumentRules,
3865 42 : newCascade->mCacheKey,
3866 : mDocumentCacheKey,
3867 : mSheetType,
3868 336 : mMustGatherDocumentRules);
3869 :
3870 178 : for (uint32_t i = 0; i < mSheets.Length(); ++i) {
3871 136 : if (!CascadeSheet(mSheets.ElementAt(i), &data))
3872 0 : return; /* out of memory */
3873 : }
3874 :
3875 : // Sort the hash table of per-weight linked lists by weight.
3876 42 : uint32_t weightCount = data.mRulesByWeight.EntryCount();
3877 84 : auto weightArray = MakeUnique<PerWeightData[]>(weightCount);
3878 42 : int32_t j = 0;
3879 600 : for (auto iter = data.mRulesByWeight.Iter(); !iter.Done(); iter.Next()) {
3880 558 : auto entry = static_cast<const RuleByWeightEntry*>(iter.Get());
3881 558 : weightArray[j++] = entry->data;
3882 : }
3883 42 : NS_QuickSort(weightArray.get(), weightCount, sizeof(PerWeightData),
3884 42 : CompareWeightData, nullptr);
3885 :
3886 : // Put things into the rule hash.
3887 : // The primary sort is by weight...
3888 600 : for (uint32_t i = 0; i < weightCount; ++i) {
3889 : // and the secondary sort is by order. mRuleSelectorPairs is already in
3890 : // the right order..
3891 15144 : for (PerWeightDataListItem *cur = weightArray[i].mRuleSelectorPairs;
3892 15144 : cur;
3893 14586 : cur = cur->mNext) {
3894 14586 : if (!AddRule(cur, newCascade))
3895 0 : return; /* out of memory */
3896 : }
3897 : }
3898 :
3899 : // Build mKeyframesRuleTable.
3900 135 : for (nsTArray<nsCSSKeyframesRule*>::size_type i = 0,
3901 42 : iEnd = newCascade->mKeyframesRules.Length(); i < iEnd; ++i) {
3902 93 : nsCSSKeyframesRule* rule = newCascade->mKeyframesRules[i];
3903 93 : newCascade->mKeyframesRuleTable.Put(rule->GetName(), rule);
3904 : }
3905 :
3906 : // Build mCounterStyleRuleTable
3907 594 : for (nsTArray<nsCSSCounterStyleRule*>::size_type i = 0,
3908 42 : iEnd = newCascade->mCounterStyleRules.Length(); i < iEnd; ++i) {
3909 552 : nsCSSCounterStyleRule* rule = newCascade->mCounterStyleRules[i];
3910 552 : newCascade->mCounterStyleRuleTable.Put(rule->Name(), rule);
3911 : }
3912 :
3913 : // mMustGatherDocumentRules controls whether we build mDocumentRules
3914 : // and mDocumentCacheKey so that they can be used as keys by the
3915 : // RuleProcessorCache, as obtained by TakeDocumentRulesAndCacheKey
3916 : // later. We set it to false just below so that we only do this
3917 : // the first time we build a RuleProcessorCache for a shared rule
3918 : // processor.
3919 : //
3920 : // An up-to-date mDocumentCacheKey is only needed if we
3921 : // are still in the RuleProcessorCache (as we store a copy of the
3922 : // cache key in the RuleProcessorCache), and an up-to-date
3923 : // mDocumentRules is only needed at the time TakeDocumentRulesAndCacheKey
3924 : // is called, which is immediately after the rule processor is created
3925 : // (by nsStyleSet).
3926 : //
3927 : // Note that when nsCSSRuleProcessor::ClearRuleCascades is called,
3928 : // by CSSStyleSheet::ClearRuleCascades, we will have called
3929 : // RuleProcessorCache::RemoveSheet, which will remove the rule
3930 : // processor from the cache. (This is because the list of document
3931 : // rules now may not match the one used as they key in the
3932 : // RuleProcessorCache.)
3933 : //
3934 : // Thus, as we'll no longer be in the RuleProcessorCache, and we won't
3935 : // have TakeDocumentRulesAndCacheKey called on us, we don't need to ensure
3936 : // mDocumentCacheKey and mDocumentRules are up-to-date after the
3937 : // first time GetRuleCascade is called.
3938 42 : if (mMustGatherDocumentRules) {
3939 10 : mDocumentRules.Sort();
3940 10 : mDocumentCacheKey.Finalize();
3941 10 : mMustGatherDocumentRules = false;
3942 : #ifdef DEBUG
3943 10 : mDocumentRulesAndCacheKeyValid = true;
3944 : #endif
3945 : }
3946 :
3947 : // Ensure that the current one is always mRuleCascades.
3948 42 : newCascade->mNext = mRuleCascades;
3949 42 : mRuleCascades = newCascade.forget();
3950 : }
3951 : }
3952 42 : return;
3953 : }
3954 :
3955 : /* static */ bool
3956 1332 : nsCSSRuleProcessor::SelectorListMatches(Element* aElement,
3957 : TreeMatchContext& aTreeMatchContext,
3958 : nsCSSSelectorList* aSelectorList)
3959 : {
3960 1332 : MOZ_ASSERT(!aTreeMatchContext.mForScopedStyle,
3961 : "mCurrentStyleScope will need to be saved and restored after the "
3962 : "SelectorMatchesTree call");
3963 :
3964 3992 : while (aSelectorList) {
3965 1341 : nsCSSSelector* sel = aSelectorList->mSelectors;
3966 1341 : NS_ASSERTION(sel, "Should have *some* selectors");
3967 1341 : NS_ASSERTION(!sel->IsPseudoElement(), "Shouldn't have been called");
3968 1341 : NodeMatchContext nodeContext(EventStates(), false);
3969 1341 : if (SelectorMatches(aElement, sel, nodeContext, aTreeMatchContext,
3970 : SelectorMatchesFlags::NONE)) {
3971 11 : nsCSSSelector* next = sel->mNext;
3972 14 : if (!next ||
3973 3 : SelectorMatchesTree(aElement, next, aTreeMatchContext,
3974 : SelectorMatchesTreeFlags(0))) {
3975 11 : return true;
3976 : }
3977 : }
3978 :
3979 1330 : aSelectorList = aSelectorList->mNext;
3980 : }
3981 :
3982 1321 : return false;
3983 : }
3984 :
3985 : void
3986 10 : nsCSSRuleProcessor::TakeDocumentRulesAndCacheKey(
3987 : nsPresContext* aPresContext,
3988 : nsTArray<css::DocumentRule*>& aDocumentRules,
3989 : nsDocumentRuleResultCacheKey& aCacheKey)
3990 : {
3991 10 : MOZ_ASSERT(mIsShared);
3992 :
3993 10 : GetRuleCascade(aPresContext);
3994 10 : MOZ_ASSERT(mDocumentRulesAndCacheKeyValid);
3995 :
3996 10 : aDocumentRules.Clear();
3997 10 : aDocumentRules.SwapElements(mDocumentRules);
3998 10 : aCacheKey.Swap(mDocumentCacheKey);
3999 :
4000 : #ifdef DEBUG
4001 10 : mDocumentRulesAndCacheKeyValid = false;
4002 : #endif
4003 10 : }
4004 :
4005 : void
4006 34 : nsCSSRuleProcessor::AddStyleSetRef()
4007 : {
4008 34 : MOZ_ASSERT(mIsShared);
4009 34 : if (++mStyleSetRefCnt == 1) {
4010 10 : RuleProcessorCache::StopTracking(this);
4011 : }
4012 34 : }
4013 :
4014 : void
4015 9 : nsCSSRuleProcessor::ReleaseStyleSetRef()
4016 : {
4017 9 : MOZ_ASSERT(mIsShared);
4018 9 : MOZ_ASSERT(mStyleSetRefCnt > 0);
4019 9 : if (--mStyleSetRefCnt == 0 && mInRuleProcessorCache) {
4020 3 : RuleProcessorCache::StartTracking(this);
4021 : }
4022 9 : }
4023 :
4024 : // TreeMatchContext and AncestorFilter out of line methods
4025 : void
4026 141 : TreeMatchContext::InitAncestors(Element *aElement)
4027 : {
4028 141 : MOZ_ASSERT(!mAncestorFilter.mFilter);
4029 141 : MOZ_ASSERT(mAncestorFilter.mHashes.IsEmpty());
4030 141 : MOZ_ASSERT(mStyleScopes.IsEmpty());
4031 :
4032 141 : mAncestorFilter.mFilter = new AncestorFilter::Filter();
4033 :
4034 141 : if (MOZ_LIKELY(aElement)) {
4035 107 : MOZ_ASSERT(aElement->GetUncomposedDoc() ||
4036 : aElement->HasFlag(NODE_IS_IN_SHADOW_TREE),
4037 : "aElement must be in the document or in shadow tree "
4038 : "for the assumption that GetParentNode() is non-null "
4039 : "on all element ancestors of aElement to be true");
4040 : // Collect up the ancestors
4041 214 : AutoTArray<Element*, 50> ancestors;
4042 107 : Element* cur = aElement;
4043 559 : do {
4044 666 : ancestors.AppendElement(cur);
4045 666 : cur = cur->GetParentElementCrossingShadowRoot();
4046 666 : } while (cur);
4047 :
4048 : // Now push them in reverse order.
4049 773 : for (uint32_t i = ancestors.Length(); i-- != 0; ) {
4050 666 : mAncestorFilter.PushAncestor(ancestors[i]);
4051 666 : PushStyleScope(ancestors[i]);
4052 : }
4053 : }
4054 141 : }
4055 :
4056 : void
4057 0 : TreeMatchContext::InitStyleScopes(Element* aElement)
4058 : {
4059 0 : MOZ_ASSERT(mStyleScopes.IsEmpty());
4060 :
4061 0 : if (MOZ_LIKELY(aElement)) {
4062 : // Collect up the ancestors
4063 0 : AutoTArray<Element*, 50> ancestors;
4064 0 : Element* cur = aElement;
4065 0 : do {
4066 0 : ancestors.AppendElement(cur);
4067 0 : cur = cur->GetParentElementCrossingShadowRoot();
4068 0 : } while (cur);
4069 :
4070 : // Now push them in reverse order.
4071 0 : for (uint32_t i = ancestors.Length(); i-- != 0; ) {
4072 0 : PushStyleScope(ancestors[i]);
4073 : }
4074 : }
4075 0 : }
4076 :
4077 : void
4078 1873 : AncestorFilter::PushAncestor(Element *aElement)
4079 : {
4080 1873 : MOZ_ASSERT(mFilter);
4081 :
4082 1873 : uint32_t oldLength = mHashes.Length();
4083 :
4084 1873 : mPopTargets.AppendElement(oldLength);
4085 : #ifdef DEBUG
4086 1873 : mElements.AppendElement(aElement);
4087 : #endif
4088 1873 : mHashes.AppendElement(aElement->NodeInfo()->NameAtom()->hash());
4089 1873 : nsIAtom *id = aElement->GetID();
4090 1873 : if (id) {
4091 1085 : mHashes.AppendElement(id->hash());
4092 : }
4093 1873 : const nsAttrValue *classes = aElement->GetClasses();
4094 1873 : if (classes) {
4095 823 : uint32_t classCount = classes->GetAtomCount();
4096 1854 : for (uint32_t i = 0; i < classCount; ++i) {
4097 1031 : mHashes.AppendElement(classes->AtomAt(i)->hash());
4098 : }
4099 : }
4100 :
4101 1873 : uint32_t newLength = mHashes.Length();
4102 5862 : for (uint32_t i = oldLength; i < newLength; ++i) {
4103 3989 : mFilter->add(mHashes[i]);
4104 : }
4105 1873 : }
4106 :
4107 : void
4108 1207 : AncestorFilter::PopAncestor()
4109 : {
4110 1207 : MOZ_ASSERT(!mPopTargets.IsEmpty());
4111 1207 : MOZ_ASSERT(mPopTargets.Length() == mElements.Length());
4112 :
4113 1207 : uint32_t popTargetLength = mPopTargets.Length();
4114 1207 : uint32_t newLength = mPopTargets[popTargetLength-1];
4115 :
4116 1207 : mPopTargets.TruncateLength(popTargetLength-1);
4117 : #ifdef DEBUG
4118 1207 : mElements.TruncateLength(popTargetLength-1);
4119 : #endif
4120 :
4121 1207 : uint32_t oldLength = mHashes.Length();
4122 3753 : for (uint32_t i = newLength; i < oldLength; ++i) {
4123 2546 : mFilter->remove(mHashes[i]);
4124 : }
4125 1207 : mHashes.TruncateLength(newLength);
4126 1207 : }
4127 :
4128 : #ifdef DEBUG
4129 : void
4130 5002 : AncestorFilter::AssertHasAllAncestors(Element *aElement) const
4131 : {
4132 5002 : Element* cur = aElement->GetParentElementCrossingShadowRoot();
4133 65170 : while (cur) {
4134 30084 : MOZ_ASSERT(mElements.Contains(cur));
4135 30084 : cur = cur->GetParentElementCrossingShadowRoot();
4136 : }
4137 5002 : }
4138 :
4139 : void
4140 136014 : TreeMatchContext::AssertHasAllStyleScopes(Element* aElement) const
4141 : {
4142 136014 : if (aElement->IsInNativeAnonymousSubtree()) {
4143 : // Document style sheets are never applied to native anonymous content,
4144 : // so it's not possible for them to be in a <style scoped> scope.
4145 1242 : return;
4146 : }
4147 134772 : Element* cur = aElement->GetParentElementCrossingShadowRoot();
4148 1699704 : while (cur) {
4149 782466 : if (cur->IsScopedStyleRoot()) {
4150 0 : MOZ_ASSERT(mStyleScopes.Contains(cur));
4151 : }
4152 782466 : cur = cur->GetParentElementCrossingShadowRoot();
4153 : }
4154 : }
4155 : #endif
|