1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /*
7 * A class that computes and caches the indices used for :nth-* pseudo-class
8 * matching.
9 */
10
11 #include "nsNthIndexCache.h"
12 #include "mozilla/dom/Element.h"
13 #include "mozilla/dom/NodeInfoInlines.h"
14
nsNthIndexCache()15 nsNthIndexCache::nsNthIndexCache()
16 {
17 }
18
~nsNthIndexCache()19 nsNthIndexCache::~nsNthIndexCache()
20 {
21 }
22
23 void
Reset()24 nsNthIndexCache::Reset()
25 {
26 mCaches[0][0].clear();
27 mCaches[0][1].clear();
28 mCaches[1][0].clear();
29 mCaches[1][1].clear();
30 }
31
32 inline bool
SiblingMatchesElement(nsIContent * aSibling,Element * aElement,bool aIsOfType)33 nsNthIndexCache::SiblingMatchesElement(nsIContent* aSibling, Element* aElement,
34 bool aIsOfType)
35 {
36 return aSibling->IsElement() &&
37 (!aIsOfType ||
38 aSibling->NodeInfo()->NameAndNamespaceEquals(aElement->NodeInfo()));
39 }
40
41 inline bool
IndexDeterminedFromPreviousSibling(nsIContent * aSibling,Element * aChild,bool aIsOfType,bool aIsFromEnd,const Cache & aCache,int32_t & aResult)42 nsNthIndexCache::IndexDeterminedFromPreviousSibling(nsIContent* aSibling,
43 Element* aChild,
44 bool aIsOfType,
45 bool aIsFromEnd,
46 const Cache& aCache,
47 int32_t& aResult)
48 {
49 if (SiblingMatchesElement(aSibling, aChild, aIsOfType)) {
50 Cache::Ptr siblingEntry = aCache.lookup(aSibling);
51 if (siblingEntry) {
52 int32_t siblingIndex = siblingEntry->value();
53 NS_ASSERTION(siblingIndex != 0,
54 "How can a non-anonymous node have an anonymous sibling?");
55 if (siblingIndex > 0) {
56 // At this point, aResult is a count of how many elements matching
57 // aChild we have seen after aSibling, including aChild itself.
58 // |siblingIndex| is the index of aSibling.
59 // So if aIsFromEnd, we want |aResult = siblingIndex - aResult| and
60 // otherwise we want |aResult = siblingIndex + aResult|.
61 aResult = siblingIndex + aResult * (1 - 2 * aIsFromEnd);
62 return true;
63 }
64 }
65
66 ++aResult;
67 }
68
69 return false;
70 }
71
72 int32_t
GetNthIndex(Element * aChild,bool aIsOfType,bool aIsFromEnd,bool aCheckEdgeOnly)73 nsNthIndexCache::GetNthIndex(Element* aChild, bool aIsOfType,
74 bool aIsFromEnd, bool aCheckEdgeOnly)
75 {
76 if (aChild->IsRootOfAnonymousSubtree()) {
77 return 0;
78 }
79
80 Cache& cache = mCaches[aIsOfType][aIsFromEnd];
81
82 if (!cache.initialized() && !cache.init()) {
83 // Give up and just don't match.
84 return 0;
85 }
86
87 Cache::AddPtr entry = cache.lookupForAdd(aChild);
88
89 // Default the value to -2 when adding
90 if (!entry && !cache.add(entry, aChild, -2)) {
91 // No good; don't match.
92 return 0;
93 }
94
95 int32_t& slot = entry->value();
96 if (slot != -2 && (slot != -1 || aCheckEdgeOnly)) {
97 return slot;
98 }
99
100 int32_t result = 1;
101 if (aCheckEdgeOnly) {
102 // The caller only cares whether or not the result is 1, so we can
103 // stop as soon as we see any other elements that match us.
104 if (aIsFromEnd) {
105 for (nsIContent *cur = aChild->GetNextSibling();
106 cur;
107 cur = cur->GetNextSibling()) {
108 if (SiblingMatchesElement(cur, aChild, aIsOfType)) {
109 result = -1;
110 break;
111 }
112 }
113 } else {
114 for (nsIContent *cur = aChild->GetPreviousSibling();
115 cur;
116 cur = cur->GetPreviousSibling()) {
117 if (SiblingMatchesElement(cur, aChild, aIsOfType)) {
118 result = -1;
119 break;
120 }
121 }
122 }
123 } else {
124 // In the common case, we already have a cached index for one of
125 // our previous siblings, so check that first.
126 for (nsIContent *cur = aChild->GetPreviousSibling();
127 cur;
128 cur = cur->GetPreviousSibling()) {
129 if (IndexDeterminedFromPreviousSibling(cur, aChild, aIsOfType,
130 aIsFromEnd, cache, result)) {
131 slot = result;
132 return result;
133 }
134 }
135
136 // Now if aIsFromEnd we lose: need to actually compute our index,
137 // since looking at previous siblings wouldn't have told us
138 // anything about it. Note that it doesn't make sense to do cache
139 // lookups on our following siblings, since chances are the cache
140 // is not primed for them.
141 if (aIsFromEnd) {
142 result = 1;
143 for (nsIContent *cur = aChild->GetNextSibling();
144 cur;
145 cur = cur->GetNextSibling()) {
146 if (SiblingMatchesElement(cur, aChild, aIsOfType)) {
147 ++result;
148 }
149 }
150 }
151 }
152
153 slot = result;
154 return result;
155 }
156