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