Line data Source code
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 :
15 2029 : nsNthIndexCache::nsNthIndexCache()
16 : {
17 2029 : }
18 :
19 2029 : nsNthIndexCache::~nsNthIndexCache()
20 : {
21 2029 : }
22 :
23 : void
24 0 : nsNthIndexCache::Reset()
25 : {
26 0 : mCaches[0][0].clear();
27 0 : mCaches[0][1].clear();
28 0 : mCaches[1][0].clear();
29 0 : mCaches[1][1].clear();
30 0 : }
31 :
32 : inline bool
33 6 : nsNthIndexCache::SiblingMatchesElement(nsIContent* aSibling, Element* aElement,
34 : bool aIsOfType)
35 : {
36 18 : return aSibling->IsElement() &&
37 6 : (!aIsOfType ||
38 6 : aSibling->NodeInfo()->NameAndNamespaceEquals(aElement->NodeInfo()));
39 : }
40 :
41 : inline bool
42 0 : nsNthIndexCache::IndexDeterminedFromPreviousSibling(nsIContent* aSibling,
43 : Element* aChild,
44 : bool aIsOfType,
45 : bool aIsFromEnd,
46 : const Cache& aCache,
47 : int32_t& aResult)
48 : {
49 0 : if (SiblingMatchesElement(aSibling, aChild, aIsOfType)) {
50 0 : Cache::Ptr siblingEntry = aCache.lookup(aSibling);
51 0 : if (siblingEntry) {
52 0 : int32_t siblingIndex = siblingEntry->value();
53 0 : NS_ASSERTION(siblingIndex != 0,
54 : "How can a non-anonymous node have an anonymous sibling?");
55 0 : 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 0 : aResult = siblingIndex + aResult * (1 - 2 * aIsFromEnd);
62 0 : return true;
63 : }
64 : }
65 :
66 0 : ++aResult;
67 : }
68 :
69 0 : return false;
70 : }
71 :
72 : int32_t
73 6 : nsNthIndexCache::GetNthIndex(Element* aChild, bool aIsOfType,
74 : bool aIsFromEnd, bool aCheckEdgeOnly)
75 : {
76 6 : if (aChild->IsRootOfAnonymousSubtree()) {
77 0 : return 0;
78 : }
79 :
80 6 : Cache& cache = mCaches[aIsOfType][aIsFromEnd];
81 :
82 6 : if (!cache.initialized() && !cache.init()) {
83 : // Give up and just don't match.
84 0 : return 0;
85 : }
86 :
87 6 : Cache::AddPtr entry = cache.lookupForAdd(aChild);
88 :
89 : // Default the value to -2 when adding
90 6 : if (!entry && !cache.add(entry, aChild, -2)) {
91 : // No good; don't match.
92 0 : return 0;
93 : }
94 :
95 6 : int32_t& slot = entry->value();
96 6 : if (slot != -2 && (slot != -1 || aCheckEdgeOnly)) {
97 0 : return slot;
98 : }
99 :
100 6 : int32_t result = 1;
101 6 : 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 6 : if (aIsFromEnd) {
105 0 : for (nsIContent *cur = aChild->GetNextSibling();
106 0 : cur;
107 0 : cur = cur->GetNextSibling()) {
108 0 : if (SiblingMatchesElement(cur, aChild, aIsOfType)) {
109 0 : result = -1;
110 0 : break;
111 : }
112 : }
113 : } else {
114 6 : for (nsIContent *cur = aChild->GetPreviousSibling();
115 6 : cur;
116 0 : cur = cur->GetPreviousSibling()) {
117 6 : if (SiblingMatchesElement(cur, aChild, aIsOfType)) {
118 6 : result = -1;
119 6 : 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 0 : for (nsIContent *cur = aChild->GetPreviousSibling();
127 0 : cur;
128 0 : cur = cur->GetPreviousSibling()) {
129 0 : if (IndexDeterminedFromPreviousSibling(cur, aChild, aIsOfType,
130 : aIsFromEnd, cache, result)) {
131 0 : slot = result;
132 0 : 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 0 : if (aIsFromEnd) {
142 0 : result = 1;
143 0 : for (nsIContent *cur = aChild->GetNextSibling();
144 0 : cur;
145 0 : cur = cur->GetNextSibling()) {
146 0 : if (SiblingMatchesElement(cur, aChild, aIsOfType)) {
147 0 : ++result;
148 : }
149 : }
150 : }
151 : }
152 :
153 6 : slot = result;
154 6 : return result;
155 : }
|