layout/style/nsNthIndexCache.cpp

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

mercurial