Index: third_party/WebKit/Source/core/dom/NthIndexCache.cpp |
diff --git a/third_party/WebKit/Source/core/dom/NthIndexCache.cpp b/third_party/WebKit/Source/core/dom/NthIndexCache.cpp |
index a039019abecbb93d6e2a5b6d1fcaf045d0d1549d..de8c37d0d3c90b9aee8cb24fa768942f4e76328b 100644 |
--- a/third_party/WebKit/Source/core/dom/NthIndexCache.cpp |
+++ b/third_party/WebKit/Source/core/dom/NthIndexCache.cpp |
@@ -24,20 +24,146 @@ NthIndexCache::~NthIndexCache() |
m_document->setNthIndexCache(nullptr); |
} |
-NthIndexData& NthIndexCache::ensureNthIndexDataFor(Node& parent) |
+namespace { |
+ |
+// Generating the cached nth-index counts when the number of children |
+// exceeds this count. This number is picked based on testing |
+// querySelectorAll for :nth-child(3n+2) and :nth-of-type(3n+2) on an |
+// increasing number of children. |
+ |
+const unsigned kCachedSiblingCountLimit = 32; |
+ |
+unsigned uncachedNthChildIndex(Element& element) |
+{ |
+ int index = 1; |
+ for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) |
+ index++; |
+ |
+ return index; |
+} |
+ |
+unsigned uncachedNthLastChildIndex(Element& element) |
+{ |
+ int index = 1; |
+ for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling)) |
+ ++index; |
+ return index; |
+} |
+ |
+unsigned uncachedNthOfTypeIndex(Element& element, unsigned& siblingCount) |
+{ |
+ int index = 1; |
+ const QualifiedName& tag = element.tagQName(); |
+ for (const Element* sibling = ElementTraversal::previousSibling(element); sibling; sibling = ElementTraversal::previousSibling(*sibling)) { |
+ if (sibling->tagQName() == tag) |
+ ++index; |
+ ++siblingCount; |
+ } |
+ return index; |
+} |
+ |
+unsigned uncachedNthLastOfTypeIndex(Element& element, unsigned& siblingCount) |
+{ |
+ int index = 1; |
+ const QualifiedName& tag = element.tagQName(); |
+ for (const Element* sibling = ElementTraversal::nextSibling(element); sibling; sibling = ElementTraversal::nextSibling(*sibling)) { |
+ if (sibling->tagQName() == tag) |
+ ++index; |
+ ++siblingCount; |
+ } |
+ return index; |
+} |
+ |
+} // namespace |
+ |
+unsigned NthIndexCache::nthChildIndex(Element& element) |
+{ |
+ if (element.isPseudoElement()) |
+ return 1; |
+ ASSERT(element.parentNode()); |
+ NthIndexCache* nthIndexCache = element.document().nthIndexCache(); |
+ NthIndexData* nthIndexData = nullptr; |
+ if (nthIndexCache && nthIndexCache->m_parentMap) |
+ nthIndexData = nthIndexCache->m_parentMap->get(element.parentNode()); |
+ if (nthIndexData) |
+ return nthIndexData->nthIndex(element); |
+ unsigned index = uncachedNthChildIndex(element); |
+ if (nthIndexCache && index > kCachedSiblingCountLimit) |
+ nthIndexCache->cacheNthIndexDataForParent(element); |
+ return index; |
+} |
+ |
+unsigned NthIndexCache::nthLastChildIndex(Element& element) |
+{ |
+ if (element.isPseudoElement()) |
+ return 1; |
+ ASSERT(element.parentNode()); |
+ NthIndexCache* nthIndexCache = element.document().nthIndexCache(); |
+ NthIndexData* nthIndexData = nullptr; |
+ if (nthIndexCache && nthIndexCache->m_parentMap) |
+ nthIndexData = nthIndexCache->m_parentMap->get(element.parentNode()); |
+ if (nthIndexData) |
+ return nthIndexData->nthLastIndex(element); |
+ unsigned index = uncachedNthLastChildIndex(element); |
+ if (nthIndexCache && index > kCachedSiblingCountLimit) |
+ nthIndexCache->cacheNthIndexDataForParent(element); |
+ return index; |
+} |
+ |
+NthIndexData* NthIndexCache::nthTypeIndexDataForParent(Element& element) const |
+{ |
+ ASSERT(element.parentNode()); |
+ if (!m_parentMapForType) |
+ return nullptr; |
+ if (const IndexByType* map = m_parentMapForType->get(element.parentNode())) |
+ return map->get(element.tagName()); |
+ return nullptr; |
+} |
+ |
+unsigned NthIndexCache::nthOfTypeIndex(Element& element) |
+{ |
+ if (element.isPseudoElement()) |
+ return 1; |
+ NthIndexCache* nthIndexCache = element.document().nthIndexCache(); |
+ if (nthIndexCache) { |
+ if (NthIndexData* nthIndexData = nthIndexCache->nthTypeIndexDataForParent(element)) |
+ return nthIndexData->nthOfTypeIndex(element); |
+ } |
+ unsigned siblingCount = 0; |
+ unsigned index = uncachedNthOfTypeIndex(element, siblingCount); |
+ if (nthIndexCache && siblingCount > kCachedSiblingCountLimit) |
+ nthIndexCache->cacheNthOfTypeIndexDataForParent(element); |
+ return index; |
+} |
+ |
+unsigned NthIndexCache::nthLastOfTypeIndex(Element& element) |
{ |
+ if (element.isPseudoElement()) |
+ return 1; |
+ NthIndexCache* nthIndexCache = element.document().nthIndexCache(); |
+ if (nthIndexCache) { |
+ if (NthIndexData* nthIndexData = nthIndexCache->nthTypeIndexDataForParent(element)) |
+ return nthIndexData->nthLastOfTypeIndex(element); |
+ } |
+ unsigned siblingCount = 0; |
+ unsigned index = uncachedNthLastOfTypeIndex(element, siblingCount); |
+ if (nthIndexCache && siblingCount > kCachedSiblingCountLimit) |
+ nthIndexCache->cacheNthOfTypeIndexDataForParent(element); |
+ return index; |
+} |
+ |
+void NthIndexCache::cacheNthIndexDataForParent(Element& element) |
+{ |
+ ASSERT(element.parentNode()); |
if (!m_parentMap) |
m_parentMap = adoptPtrWillBeNoop(new ParentMap()); |
- ParentMap::AddResult addResult = m_parentMap->add(&parent, nullptr); |
- if (addResult.isNewEntry) |
- addResult.storedValue->value = adoptPtrWillBeNoop(new NthIndexData()); |
- |
- ASSERT(addResult.storedValue->value); |
- return *addResult.storedValue->value; |
+ ParentMap::AddResult addResult = m_parentMap->add(element.parentNode(), nullptr); |
+ ASSERT(addResult.isNewEntry); |
+ addResult.storedValue->value = adoptPtrWillBeNoop(new NthIndexData(*element.parentNode())); |
} |
-NthIndexCache::IndexByType& NthIndexCache::ensureTypeIndexMap(Node& parent) |
+NthIndexCache::IndexByType& NthIndexCache::ensureTypeIndexMap(ContainerNode& parent) |
{ |
if (!m_parentMapForType) |
m_parentMapForType = adoptPtrWillBeNoop(new ParentMapForType()); |
@@ -50,20 +176,17 @@ NthIndexCache::IndexByType& NthIndexCache::ensureTypeIndexMap(Node& parent) |
return *addResult.storedValue->value; |
} |
-NthIndexData& NthIndexCache::nthIndexDataWithTagName(Element& element) |
+void NthIndexCache::cacheNthOfTypeIndexDataForParent(Element& element) |
{ |
+ ASSERT(element.parentNode()); |
IndexByType::AddResult addResult = ensureTypeIndexMap(*element.parentNode()).add(element.tagName(), nullptr); |
- if (addResult.isNewEntry) |
- addResult.storedValue->value = adoptPtrWillBeNoop(new NthIndexData()); |
- return *addResult.storedValue->value; |
+ ASSERT(addResult.isNewEntry); |
+ addResult.storedValue->value = adoptPtrWillBeNoop(new NthIndexData(*element.parentNode(), element.tagQName())); |
} |
-unsigned NthIndexData::nthIndex(Element& element) |
+unsigned NthIndexData::nthIndex(Element& element) const |
{ |
- if (element.isPseudoElement()) |
- return 1; |
- if (!m_count) |
- return cacheNthIndices(element); |
+ ASSERT(!element.isPseudoElement()); |
unsigned index = 0; |
for (Element* sibling = &element; sibling; sibling = ElementTraversal::previousSibling(*sibling), index++) { |
@@ -74,14 +197,12 @@ unsigned NthIndexData::nthIndex(Element& element) |
return index; |
} |
-unsigned NthIndexData::nthIndexOfType(Element& element, const QualifiedName& type) |
+unsigned NthIndexData::nthOfTypeIndex(Element& element) const |
{ |
- if (element.isPseudoElement()) |
- return 1; |
- if (!m_count) |
- return cacheNthIndicesOfType(element, type); |
+ ASSERT(!element.isPseudoElement()); |
+ |
unsigned index = 0; |
- for (Element* sibling = &element; sibling; sibling = ElementTraversal::previousSibling(*sibling, HasTagName(type)), index++) { |
+ for (Element* sibling = &element; sibling; sibling = ElementTraversal::previousSibling(*sibling, HasTagName(element.tagQName())), index++) { |
auto it = m_elementIndexMap.find(sibling); |
if (it != m_elementIndexMap.end()) |
return it->value + index; |
@@ -89,27 +210,18 @@ unsigned NthIndexData::nthIndexOfType(Element& element, const QualifiedName& typ |
return index; |
} |
-unsigned NthIndexData::nthLastIndex(Element& element) |
+unsigned NthIndexData::nthLastIndex(Element& element) const |
{ |
- if (element.isPseudoElement()) |
- return 1; |
- unsigned index = nthIndex(element); |
- return m_count - index + 1; |
+ return m_count - nthIndex(element) + 1; |
} |
-unsigned NthIndexData::nthLastIndexOfType(Element& element, const QualifiedName& type) |
+unsigned NthIndexData::nthLastOfTypeIndex(Element& element) const |
{ |
- if (element.isPseudoElement()) |
- return 1; |
- unsigned index = nthIndexOfType(element, type); |
- return m_count - index + 1; |
+ return m_count - nthOfTypeIndex(element) + 1; |
} |
-unsigned NthIndexData::cacheNthIndices(Element& element) |
+NthIndexData::NthIndexData(ContainerNode& parent) |
{ |
- ASSERT(!element.isPseudoElement()); |
- ASSERT(m_elementIndexMap.isEmpty()); |
- unsigned index = 0; |
// The frequency at which we cache the nth-index for a set of siblings. |
// A spread value of 3 means every third Element will have its nth-index cached. |
// Using a spread value > 1 is done to save memory. Looking up the nth-index will |
@@ -117,22 +229,16 @@ unsigned NthIndexData::cacheNthIndices(Element& element) |
// elements will be traversed. |
const unsigned spread = 3; |
unsigned count = 0; |
- for (Element* sibling = ElementTraversal::firstChild(*element.parentNode()); sibling; sibling = ElementTraversal::nextSibling(*sibling)) { |
+ for (Element* sibling = ElementTraversal::firstChild(parent); sibling; sibling = ElementTraversal::nextSibling(*sibling)) { |
if (!(++count % spread)) |
m_elementIndexMap.add(sibling, count); |
- if (sibling == &element) |
- index = count; |
} |
- ASSERT(count && index); |
+ ASSERT(count); |
m_count = count; |
- return index; |
} |
-unsigned NthIndexData::cacheNthIndicesOfType(Element& element, const QualifiedName& type) |
+NthIndexData::NthIndexData(ContainerNode& parent, const QualifiedName& type) |
{ |
- ASSERT(!element.isPseudoElement()); |
- ASSERT(m_elementIndexMap.isEmpty()); |
- unsigned index = 0; |
// The frequency at which we cache the nth-index of type for a set of siblings. |
// A spread value of 3 means every third Element of its type will have its nth-index cached. |
// Using a spread value > 1 is done to save memory. Looking up the nth-index of its type will |
@@ -140,15 +246,12 @@ unsigned NthIndexData::cacheNthIndicesOfType(Element& element, const QualifiedNa |
// will be equal to find 'spread' elements in the sibling set. |
const unsigned spread = 3; |
unsigned count = 0; |
- for (Element* sibling = ElementTraversal::firstChild(*element.parentNode(), HasTagName(type)); sibling; sibling = ElementTraversal::nextSibling(*sibling, HasTagName(type))) { |
+ for (Element* sibling = ElementTraversal::firstChild(parent, HasTagName(type)); sibling; sibling = ElementTraversal::nextSibling(*sibling, HasTagName(type))) { |
if (!(++count % spread)) |
m_elementIndexMap.add(sibling, count); |
- if (sibling == &element) |
- index = count; |
} |
- ASSERT(count && index); |
+ ASSERT(count); |
m_count = count; |
- return index; |
} |
DEFINE_TRACE(NthIndexData) |