Changeset 122660 in webkit
- Timestamp:
- Jul 14, 2012 12:08:55 AM (12 years ago)
- Location:
- trunk
- Files:
-
- 2 added
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r122656 r122660 1 2012-07-13 Ryosuke Niwa <rniwa@webkit.org> 2 3 Iterating backwards over HTMLCollection is O(n^2) 4 https://bugs.webkit.org/show_bug.cgi?id=91306 5 6 Reviewed by Anders Carlsson. 7 8 Add an asymptotic time complexity test. 9 10 * perf/htmlcollection-backwards-iteration-expected.txt: Added. 11 * perf/htmlcollection-backwards-iteration.html: Added. 12 1 13 2012-07-13 Kent Tamura <tkent@chromium.org> 2 14 -
trunk/Source/WebCore/ChangeLog
r122658 r122660 1 2012-07-13 Ryosuke Niwa <rniwa@webkit.org> 2 3 Iterating backwards over HTMLCollection is O(n^2) 4 https://bugs.webkit.org/show_bug.cgi?id=91306 5 6 Reviewed by Anders Carlsson. 7 8 Fixed the bug by introducing itemBefore that iterates nodes backwards to complement itemAfter. 9 Unfortunately, some HTML collections such as HTMLFormCollection and HTMLTableRowsCollection have 10 its own itemAfter function and writing an equivalent itemBefore is somewhat tricky. For now, 11 added a new boolean flag indicating whether a given HTML collection supports itemBefore or not, 12 and left those HTML collections that override itemAfter alone. 13 14 This also paves our way to share more code between DynamicNodeList and HTMLCollection. 15 16 Test: perf/htmlcollection-backwards-iteration.html 17 18 * dom/DynamicNodeList.h: 19 (WebCore::DynamicNodeListCacheBase::DynamicNodeListCacheBase): Takes ItemBeforeSupportType. 20 (WebCore::DynamicNodeListCacheBase::supportsItemBefore): Added. 21 (DynamicNodeListCacheBase): 22 (WebCore::DynamicNodeListCacheBase::setItemCache): Replaced a FIXME by an assertion now that 23 we can. 24 * html/HTMLAllCollection.cpp: 25 (WebCore::HTMLAllCollection::HTMLAllCollection): Supports itemBefore since it doesn't override 26 itemAfter. 27 * html/HTMLCollection.cpp: 28 (WebCore::HTMLCollection::HTMLCollection): 29 (WebCore::HTMLCollection::create): 30 (WebCore::isAcceptableElement): Made it a static local function instead of a static member. 31 (WebCore::nextNode): Templatized. 32 (WebCore::itemBeforeOrAfter): Extracted from itemAfter and templatized. 33 (WebCore::HTMLCollection::itemBefore): Added. 34 (WebCore::HTMLCollection::itemAfter): 35 (WebCore::HTMLCollection::shouldSearchFromFirstItem): Added. Determines whether we should reset 36 the item cache to the first item. We obviously do if the cache is invalid. If the target offset 37 is after the cached offset, then we shouldn't go back regardless of availability of itemBefore. 38 Otherwise, we go back to the first item iff itemBefore is not available or the distance from 39 the cached offset to the target offset is greater than the target offset itself. 40 (WebCore::HTMLCollection::length): 41 (WebCore::HTMLCollection::item): Use the term "offset" to match the terminology elsewhere. 42 (WebCore::HTMLCollection::itemBeforeOrAfterCachedItem): Ditto. Also added the logic to iterate 43 nodes backwards using itemBefore. Once we're in this branch, we should always find a matching 44 item since the target offset was less than the cached offset, and offsets are non-negative. 45 If we had ever reached the end of the loop without finding an item, it indicates that the cache 46 has been invalid and we have some serious bug elsewhere. 47 * html/HTMLCollection.h: 48 (WebCore::HTMLCollectionCacheBase::HTMLCollectionCacheBase): 49 (HTMLCollection): 50 * html/HTMLOptionsCollection.cpp: 51 (WebCore::HTMLOptionsCollection::HTMLOptionsCollection): Supports itemBefore since it doesn't 52 override itemAfter. 53 * html/HTMLFormCollection.cpp: 54 (WebCore::HTMLFormCollection::HTMLFormCollection): Doesn't support itemBefore as it overrides 55 itemAfter. 56 * html/HTMLNameCollection.cpp: 57 (WebCore::HTMLNameCollection::HTMLNameCollection): Ditto. 58 * html/HTMLPropertiesCollection.cpp: 59 (WebCore::HTMLPropertiesCollection::HTMLPropertiesCollection): 60 * html/HTMLTableRowsCollection.cpp: 61 (WebCore::HTMLTableRowsCollection::HTMLTableRowsCollection): 62 1 63 2012-07-13 Eric Penner <epenner@google.com> 2 64 -
trunk/Source/WebCore/dom/DynamicNodeList.h
r122649 r122660 44 44 class DynamicNodeListCacheBase { 45 45 public: 46 DynamicNodeListCacheBase(NodeListRootType rootType, NodeListInvalidationType invalidationType, CollectionType collectionType = InvalidCollectionType) 46 enum ItemBeforeSupportType { 47 DoNotSupportItemBefore, 48 SupportItemBefore, 49 }; 50 51 DynamicNodeListCacheBase(NodeListRootType rootType, NodeListInvalidationType invalidationType, 52 CollectionType collectionType = InvalidCollectionType, ItemBeforeSupportType itemBeforeSupportType = DoNotSupportItemBefore) 47 53 : m_cachedItem(0) 48 54 , m_isLengthCacheValid(false) … … 52 58 , m_isNameCacheValid(false) 53 59 , m_collectionType(collectionType) 60 , m_supportsItemBefore(itemBeforeSupportType == SupportItemBefore) 54 61 { 55 62 ASSERT(m_invalidationType == static_cast<unsigned>(invalidationType)); … … 67 74 68 75 protected: 76 bool supportsItemBefore() const { return m_supportsItemBefore; } 77 69 78 ALWAYS_INLINE bool isItemCacheValid() const { return m_isItemCacheValid; } 70 79 ALWAYS_INLINE Node* cachedItem() const { return m_cachedItem; } … … 80 89 ALWAYS_INLINE void setItemCache(Node* item, unsigned offset) const 81 90 { 82 // FIXME: Assert that item is not null once we've updated DynamicNodeList.91 ASSERT(item); 83 92 m_cachedItem = item; 84 93 m_cachedItemOffset = offset; … … 101 110 mutable unsigned m_isNameCacheValid : 1; 102 111 const unsigned m_collectionType : 5; 112 const unsigned m_supportsItemBefore : 1; 103 113 }; 104 114 -
trunk/Source/WebCore/html/HTMLAllCollection.cpp
r122621 r122660 37 37 38 38 HTMLAllCollection::HTMLAllCollection(Document* document) 39 : HTMLCollection(document, DocAll )39 : HTMLCollection(document, DocAll, SupportItemBefore) 40 40 { 41 41 } -
trunk/Source/WebCore/html/HTMLCollection.cpp
r122621 r122660 153 153 154 154 155 HTMLCollection::HTMLCollection(Node* base, CollectionType type )156 : HTMLCollectionCacheBase(rootTypeFromCollectionType(type), invalidationTypeExcludingIdAndNameAttributes(type), type )155 HTMLCollection::HTMLCollection(Node* base, CollectionType type, ItemBeforeSupportType itemBeforeSupportType) 156 : HTMLCollectionCacheBase(rootTypeFromCollectionType(type), invalidationTypeExcludingIdAndNameAttributes(type), type, itemBeforeSupportType) 157 157 , m_base(base) 158 158 { … … 163 163 PassRefPtr<HTMLCollection> HTMLCollection::create(Node* base, CollectionType type) 164 164 { 165 return adoptRef(new HTMLCollection(base, type ));165 return adoptRef(new HTMLCollection(base, type, SupportItemBefore)); 166 166 } 167 167 … … 180 180 } 181 181 182 inline bool HTMLCollection::isAcceptableElement(Element* element) const 183 { 184 if (!element->isHTMLElement() && !(type () == DocAll || type()== NodeChildren))185 return false; 186 187 switch (type ()) {182 static inline bool isAcceptableElement(CollectionType type, Element* element) 183 { 184 if (!element->isHTMLElement() && !(type == DocAll || type == NodeChildren)) 185 return false; 186 187 switch (type) { 188 188 case DocImages: 189 189 return element->hasLocalName(imgTag); … … 238 238 } 239 239 240 static ALWAYS_INLINE Node* nextNode(Node* base, Node* previous, bool onlyIncludeDirectChildren) 241 { 242 return onlyIncludeDirectChildren ? previous->traverseNextSibling(base) : previous->traverseNextNode(base); 240 template<bool forward> 241 static Node* nextNode(Node* base, Node* previous, bool onlyIncludeDirectChildren) 242 { 243 if (forward) 244 return onlyIncludeDirectChildren ? previous->nextSibling() : previous->traverseNextNode(base); 245 else 246 return onlyIncludeDirectChildren ? previous->previousSibling() : previous->traversePreviousNode(base); 247 } 248 249 template<bool forward> 250 static Element* itemBeforeOrAfter(CollectionType type, Node* base, unsigned& offsetInArray, Node* previous) 251 { 252 ASSERT_UNUSED(offsetInArray, !offsetInArray); 253 bool onlyIncludeDirectChildren = shouldOnlyIncludeDirectChildren(type); 254 Node* rootNode = base; 255 Node* current = previous ? nextNode<forward>(rootNode, previous, onlyIncludeDirectChildren) : (forward ? base->firstChild() : base->lastChild()); 256 257 for (; current; current = nextNode<forward>(rootNode, current, onlyIncludeDirectChildren)) { 258 if (current->isElementNode() && isAcceptableElement(type, toElement(current))) 259 return toElement(current); 260 } 261 262 return 0; 263 } 264 265 Element* HTMLCollection::itemBefore(unsigned& offsetInArray, Element* previous) const 266 { 267 return itemBeforeOrAfter<false>(type(), base(), offsetInArray, previous); 243 268 } 244 269 245 270 Element* HTMLCollection::itemAfter(unsigned& offsetInArray, Element* previous) const 246 271 { 247 ASSERT_UNUSED(offsetInArray, !offsetInArray); 248 bool onlyIncludeDirectChildren = shouldOnlyIncludeDirectChildren(type()); 249 Node* rootNode = base(); 250 Node* current = previous ? nextNode(rootNode, previous, onlyIncludeDirectChildren) : m_base->firstChild(); 251 252 for (; current; current = nextNode(rootNode, current, onlyIncludeDirectChildren)) { 253 if (current->isElementNode() && isAcceptableElement(toElement(current))) 254 return toElement(current); 255 } 256 257 return 0; 272 return itemBeforeOrAfter<true>(type(), base(), offsetInArray, previous); 273 } 274 275 bool ALWAYS_INLINE HTMLCollection::shouldSearchFromFirstItem(unsigned offset) const 276 { 277 if (!isItemCacheValid()) 278 return true; 279 280 ASSERT(offset != cachedItemOffset()); 281 if (offset > cachedItemOffset()) 282 return false; 283 284 unsigned distanceFromCachedItem = cachedItemOffset() - offset; 285 return !supportsItemBefore() || offset < distanceFromCachedItem; 258 286 } 259 287 … … 273 301 do { 274 302 offset++; 275 } while (item AfterCachedItem(offset));303 } while (itemBeforeOrAfterCachedItem(offset)); 276 304 277 305 setLengthCache(offset); … … 279 307 } 280 308 281 Node* HTMLCollection::item(unsigned index) const282 { 283 if (isItemCacheValid() && cachedItemOffset() == index)309 Node* HTMLCollection::item(unsigned offset) const 310 { 311 if (isItemCacheValid() && cachedItemOffset() == offset) 284 312 return cachedItem(); 285 313 286 if (isLengthCacheValid() && cachedLength() <= index)314 if (isLengthCacheValid() && cachedLength() <= offset) 287 315 return 0; 288 316 … … 292 320 #endif 293 321 294 if ( !isItemCacheValid() || cachedItemOffset() > index) {322 if (shouldSearchFromFirstItem(offset)) { 295 323 unsigned offsetInArray = 0; 296 324 Node* firstItem = itemAfter(offsetInArray, 0); … … 301 329 setItemCache(firstItem, 0, offsetInArray); 302 330 ASSERT(!cachedItemOffset()); 303 if (! index)331 if (!offset) 304 332 return cachedItem(); 305 333 } 306 334 307 return item AfterCachedItem(index);308 } 309 310 Element* HTMLCollection::item AfterCachedItem(unsigned index) const311 { 312 unsigned current Index= cachedItemOffset();335 return itemBeforeOrAfterCachedItem(offset); 336 } 337 338 Element* HTMLCollection::itemBeforeOrAfterCachedItem(unsigned offset) const 339 { 340 unsigned currentOffset = cachedItemOffset(); 313 341 ASSERT(cachedItem()->isElementNode()); 314 342 Element* currentItem = toElement(cachedItem()); 315 ASSERT(current Index < index);343 ASSERT(currentOffset != offset); 316 344 317 345 unsigned offsetInArray = cachedElementsArrayOffset(); 346 347 if (offset < cachedItemOffset()) { 348 ASSERT(supportsItemBefore()); 349 while ((currentItem = itemBefore(offsetInArray, currentItem))) { 350 ASSERT(currentOffset); 351 currentOffset--; 352 if (currentOffset == offset) { 353 setItemCache(currentItem, currentOffset, offsetInArray); 354 return currentItem; 355 } 356 } 357 ASSERT_NOT_REACHED(); 358 return 0; 359 } 360 318 361 while ((currentItem = itemAfter(offsetInArray, currentItem))) { 319 current Index++;320 if (current Index == index) {321 setItemCache(currentItem, current Index, offsetInArray);362 currentOffset++; 363 if (currentOffset == offset) { 364 setItemCache(currentItem, currentOffset, offsetInArray); 322 365 return currentItem; 323 366 } 324 367 } 325 368 326 setLengthCache(current Index);369 setLengthCache(currentOffset); 327 370 328 371 return 0; -
trunk/Source/WebCore/html/HTMLCollection.h
r122637 r122660 40 40 class HTMLCollectionCacheBase : public DynamicNodeListCacheBase { 41 41 public: 42 HTMLCollectionCacheBase(NodeListRootType rootType, NodeListInvalidationType invalidationType, CollectionType collectionType )43 : DynamicNodeListCacheBase(rootType, invalidationType, collectionType )42 HTMLCollectionCacheBase(NodeListRootType rootType, NodeListInvalidationType invalidationType, CollectionType collectionType, ItemBeforeSupportType itemBeforeSupportType) 43 : DynamicNodeListCacheBase(rootType, invalidationType, collectionType, itemBeforeSupportType) 44 44 , m_cachedElementsArrayOffset(0) 45 45 { … … 107 107 108 108 protected: 109 HTMLCollection(Node* base, CollectionType );109 HTMLCollection(Node* base, CollectionType, ItemBeforeSupportType); 110 110 111 111 virtual void updateNameCache() const; … … 115 115 bool checkForNameMatch(Element*, bool checkName, const AtomicString& name) const; 116 116 117 Element* itemAfterCachedItem(unsigned) const; 118 bool isAcceptableElement(Element*) const; 117 Element* itemBefore(unsigned& offsetInArray, Element*) const; 118 bool shouldSearchFromFirstItem(unsigned offset) const; 119 Element* itemBeforeOrAfterCachedItem(unsigned offset) const; 119 120 120 121 RefPtr<Node> m_base; -
trunk/Source/WebCore/html/HTMLFormCollection.cpp
r122621 r122660 38 38 39 39 HTMLFormCollection::HTMLFormCollection(Element* base) 40 : HTMLCollection(base, FormControls )40 : HTMLCollection(base, FormControls, DoNotSupportItemBefore) 41 41 { 42 42 ASSERT(base->hasTagName(formTag) || base->hasTagName(fieldsetTag)); -
trunk/Source/WebCore/html/HTMLNameCollection.cpp
r122518 r122660 34 34 35 35 HTMLNameCollection::HTMLNameCollection(Document* document, CollectionType type, const AtomicString& name) 36 : HTMLCollection(document, type )36 : HTMLCollection(document, type, DoNotSupportItemBefore) 37 37 , m_name(name) 38 38 { -
trunk/Source/WebCore/html/HTMLOptionsCollection.cpp
r122115 r122660 29 29 30 30 HTMLOptionsCollection::HTMLOptionsCollection(Element* select) 31 : HTMLCollection(select, SelectOptions )31 : HTMLCollection(select, SelectOptions, SupportItemBefore) 32 32 { 33 33 ASSERT(select->hasTagName(HTMLNames::selectTag)); -
trunk/Source/WebCore/html/HTMLPropertiesCollection.cpp
r122621 r122660 52 52 53 53 HTMLPropertiesCollection::HTMLPropertiesCollection(Node* itemNode) 54 : HTMLCollection(itemNode, ItemProperties )54 : HTMLCollection(itemNode, ItemProperties, DoNotSupportItemBefore) 55 55 , m_hasPropertyNameCache(false) 56 56 , m_hasItemRefElements(false) -
trunk/Source/WebCore/html/HTMLTableRowsCollection.cpp
r122518 r122660 153 153 // differ between compilers. 154 154 HTMLTableRowsCollection::HTMLTableRowsCollection(Element* table) 155 : HTMLCollection(table, TableRows )155 : HTMLCollection(table, TableRows, DoNotSupportItemBefore) 156 156 { 157 157 ASSERT(table->hasTagName(tableTag));
Note: See TracChangeset
for help on using the changeset viewer.