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