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/. */
7 * A class that computes and caches the indices used for :nth-* pseudo-class
11 #include "nsNthIndexCache.h"
12 #include "mozilla/dom/Element.h"
13 #include "mozilla/dom/NodeInfoInlines.h"
15 nsNthIndexCache::nsNthIndexCache()
19 nsNthIndexCache::~nsNthIndexCache()
24 nsNthIndexCache::Reset()
26 mCaches
[0][0].clear();
27 mCaches
[0][1].clear();
28 mCaches
[1][0].clear();
29 mCaches
[1][1].clear();
33 nsNthIndexCache::SiblingMatchesElement(nsIContent
* aSibling
, Element
* aElement
,
36 return aSibling
->IsElement() &&
38 aSibling
->NodeInfo()->NameAndNamespaceEquals(aElement
->NodeInfo()));
42 nsNthIndexCache::IndexDeterminedFromPreviousSibling(nsIContent
* aSibling
,
49 if (SiblingMatchesElement(aSibling
, aChild
, aIsOfType
)) {
50 Cache::Ptr siblingEntry
= aCache
.lookup(aSibling
);
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
);
73 nsNthIndexCache::GetNthIndex(Element
* aChild
, bool aIsOfType
,
74 bool aIsFromEnd
, bool aCheckEdgeOnly
)
76 NS_ASSERTION(aChild
->GetParent(), "caller should check GetParent()");
78 if (aChild
->IsRootOfAnonymousSubtree()) {
82 Cache
&cache
= mCaches
[aIsOfType
][aIsFromEnd
];
84 if (!cache
.initialized() && !cache
.init()) {
85 // Give up and just don't match.
89 Cache::AddPtr entry
= cache
.lookupForAdd(aChild
);
91 // Default the value to -2 when adding
92 if (!entry
&& !cache
.add(entry
, aChild
, -2)) {
93 // No good; don't match.
97 int32_t &slot
= entry
->value();
98 if (slot
!= -2 && (slot
!= -1 || aCheckEdgeOnly
)) {
103 if (aCheckEdgeOnly
) {
104 // The caller only cares whether or not the result is 1, so we can
105 // stop as soon as we see any other elements that match us.
107 for (nsIContent
*cur
= aChild
->GetNextSibling();
109 cur
= cur
->GetNextSibling()) {
110 if (SiblingMatchesElement(cur
, aChild
, aIsOfType
)) {
116 for (nsIContent
*cur
= aChild
->GetPreviousSibling();
118 cur
= cur
->GetPreviousSibling()) {
119 if (SiblingMatchesElement(cur
, aChild
, aIsOfType
)) {
126 // In the common case, we already have a cached index for one of
127 // our previous siblings, so check that first.
128 for (nsIContent
*cur
= aChild
->GetPreviousSibling();
130 cur
= cur
->GetPreviousSibling()) {
131 if (IndexDeterminedFromPreviousSibling(cur
, aChild
, aIsOfType
,
132 aIsFromEnd
, cache
, result
)) {
138 // Now if aIsFromEnd we lose: need to actually compute our index,
139 // since looking at previous siblings wouldn't have told us
140 // anything about it. Note that it doesn't make sense to do cache
141 // lookups on our following siblings, since chances are the cache
142 // is not primed for them.
145 for (nsIContent
*cur
= aChild
->GetNextSibling();
147 cur
= cur
->GetNextSibling()) {
148 if (SiblingMatchesElement(cur
, aChild
, aIsOfType
)) {