Changeset 158536 in webkit
- Timestamp:
- Nov 3, 2013 11:52:40 AM (10 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r158533 r158536 1 2013-11-03 Antti Koivisto <antti@apple.com> 2 3 ChildNodeList should not be LiveNodeList 4 https://bugs.webkit.org/show_bug.cgi?id=123708 5 6 Reviewed by Sam Weinig. 7 8 ChildNodeList is a poor fit to be a LiveNodeList. It is heavily special-cased. It is also 9 the only subtype that returns non-Elements thus preventing tightening. 10 11 * bindings/js/JSNodeListCustom.cpp: 12 (WebCore::JSNodeListOwner::isReachableFromOpaqueRoots): 13 14 Support new types. 15 16 * dom/ChildNodeList.cpp: 17 (WebCore::EmptyNodeList::~EmptyNodeList): 18 (WebCore::ChildNodeList::ChildNodeList): 19 (WebCore::ChildNodeList::~ChildNodeList): 20 (WebCore::ChildNodeList::length): 21 (WebCore::childFromFirst): 22 (WebCore::childFromLast): 23 (WebCore::ChildNodeList::nodeBeforeCached): 24 (WebCore::ChildNodeList::nodeAfterCached): 25 (WebCore::ChildNodeList::item): 26 (WebCore::ChildNodeList::namedItem): 27 (WebCore::ChildNodeList::invalidateCache): 28 29 Implement the same caching optimizations as LiveNodeList with tighter, less generic code. 30 31 * dom/ChildNodeList.h: 32 33 Inherit ChildNodeList directly from NodeList. 34 35 Add new EmptyNodeList type. This is only ever used if NodeList is requested for a non-container node. 36 It allows tighter typing in ChildNodeList. 37 38 * dom/LiveNodeList.cpp: 39 (WebCore::LiveNodeList::namedItem): 40 * dom/LiveNodeList.h: 41 (WebCore::LiveNodeListBase::LiveNodeListBase): 42 (WebCore::LiveNodeListBase::~LiveNodeListBase): 43 (WebCore::LiveNodeList::LiveNodeList): 44 45 Remove ChildNodeList specific code and branches. 46 47 * dom/Node.cpp: 48 (WebCore::Node::childNodes): 49 50 Return EmptyNodeList for non-containers. 51 52 * dom/NodeList.h: 53 (WebCore::NodeList::~NodeList): 54 (WebCore::NodeList::isLiveNodeList): 55 (WebCore::NodeList::isChildNodeList): 56 (WebCore::NodeList::isEmptyNodeList): 57 58 For isReachableFromOpaqueRoots. 59 60 * dom/NodeRareData.h: 61 (WebCore::NodeListsNodeData::ensureChildNodeList): 62 (WebCore::NodeListsNodeData::removeChildNodeList): 63 (WebCore::NodeListsNodeData::ensureEmptyChildNodeList): 64 (WebCore::NodeListsNodeData::removeEmptyChildNodeList): 65 (WebCore::NodeListsNodeData::NodeListsNodeData): 66 (WebCore::NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList): 67 68 EmptyNodeList support. 69 70 * html/CollectionType.h: 71 * html/HTMLCollection.cpp: 72 (WebCore::shouldOnlyIncludeDirectChildren): 73 (WebCore::rootTypeFromCollectionType): 74 (WebCore::invalidationTypeExcludingIdAndNameAttributes): 75 (WebCore::isMatchingElement): 76 (WebCore::LiveNodeListBase::itemBefore): 77 (WebCore::LiveNodeListBase::traverseLiveNodeListFirstElement): 78 (WebCore::LiveNodeListBase::traverseLiveNodeListForwardToOffset): 79 (WebCore::LiveNodeListBase::item): 80 (WebCore::LiveNodeListBase::itemBeforeOrAfterCachedItem): 81 82 Remove ChildNodeList specific code and branches. 83 1 84 2013-11-03 Patrick Gansterer <paroga@webkit.org> 2 85 -
trunk/Source/WebCore/bindings/js/JSNodeListCustom.cpp
r157215 r158536 27 27 #include "JSNodeList.h" 28 28 29 #include "ChildNodeList.h" 29 30 #include "JSNode.h" 30 31 #include "LiveNodeList.h" … … 42 43 if (!jsNodeList->hasCustomProperties()) 43 44 return false; 44 if (!jsNodeList->impl().isLiveNodeList()) 45 return false; 46 return visitor.containsOpaqueRoot(root(static_cast<LiveNodeList&>(jsNodeList->impl()).ownerNode())); 45 if (jsNodeList->impl().isLiveNodeList()) 46 return visitor.containsOpaqueRoot(root(static_cast<LiveNodeList&>(jsNodeList->impl()).ownerNode())); 47 if (jsNodeList->impl().isChildNodeList()) 48 return visitor.containsOpaqueRoot(root(static_cast<ChildNodeList&>(jsNodeList->impl()).ownerNode())); 49 if (jsNodeList->impl().isEmptyNodeList()) 50 return visitor.containsOpaqueRoot(root(static_cast<EmptyNodeList&>(jsNodeList->impl()).ownerNode())); 51 return false; 47 52 } 48 53 -
trunk/Source/WebCore/dom/ChildNodeList.cpp
r157365 r158536 25 25 26 26 #include "Element.h" 27 #include "ElementIterator.h" 27 28 #include "NodeRareData.h" 28 29 29 30 namespace WebCore { 30 31 31 ChildNodeList::ChildNodeList(Node& node) 32 : LiveNodeList(node, ChildNodeListType, DoNotInvalidateOnAttributeChanges) 32 EmptyNodeList::~EmptyNodeList() 33 { 34 m_owner.get().nodeLists()->removeEmptyChildNodeList(this); 35 } 36 37 ChildNodeList::ChildNodeList(ContainerNode& parent) 38 : m_parent(parent) 39 , m_cachedLength(0) 40 , m_cachedLengthValid(false) 41 , m_cachedCurrentPosition(0) 42 , m_cachedCurrentNode(nullptr) 33 43 { 34 44 } … … 36 46 ChildNodeList::~ChildNodeList() 37 47 { 38 ownerNode().nodeLists()->removeChildNodeList(this);48 m_parent.get().nodeLists()->removeChildNodeList(this); 39 49 } 40 50 41 bool ChildNodeList::nodeMatches(Element* testNode) const51 unsigned ChildNodeList::length() const 42 52 { 43 // This function will be called only by LiveNodeList::namedItem, 44 // for an element that was located with getElementById. 45 return testNode->parentNode() == &rootNode(); 53 if (!m_cachedLengthValid) { 54 m_cachedLength = m_parent->childNodeCount(); 55 m_cachedLengthValid = true; 56 } 57 return m_cachedLength; 58 } 59 60 static Node* childFromFirst(const ContainerNode& parent, unsigned delta) 61 { 62 Node* child = parent.firstChild(); 63 for (; delta && child; --delta) 64 child = child->nextSibling(); 65 return child; 66 } 67 68 static Node* childFromLast(const ContainerNode& parent, unsigned delta) 69 { 70 Node* child = parent.lastChild(); 71 for (; delta; --delta) 72 child = child->previousSibling(); 73 ASSERT(child); 74 return child; 75 } 76 77 Node* ChildNodeList::nodeBeforeCached(unsigned index) const 78 { 79 ASSERT(m_cachedCurrentNode); 80 ASSERT(index < m_cachedCurrentPosition); 81 if (index < m_cachedCurrentPosition - index) { 82 m_cachedCurrentNode = childFromFirst(m_parent.get(), index); 83 m_cachedCurrentPosition = index; 84 return m_cachedCurrentNode; 85 } 86 while (m_cachedCurrentNode && m_cachedCurrentPosition > index) { 87 m_cachedCurrentNode = m_cachedCurrentNode->previousSibling(); 88 --m_cachedCurrentPosition; 89 } 90 return m_cachedCurrentNode; 91 } 92 93 Node* ChildNodeList::nodeAfterCached(unsigned index) const 94 { 95 ASSERT(m_cachedCurrentNode); 96 ASSERT(index > m_cachedCurrentPosition); 97 ASSERT(!m_cachedLengthValid || index < m_cachedLength); 98 if (m_cachedLengthValid && m_cachedLength - index < index - m_cachedCurrentPosition) { 99 m_cachedCurrentNode = childFromLast(m_parent.get(), m_cachedLength - index - 1); 100 m_cachedCurrentPosition = index; 101 return m_cachedCurrentNode; 102 } 103 while (m_cachedCurrentNode && m_cachedCurrentPosition < index) { 104 m_cachedCurrentNode = m_cachedCurrentNode->nextSibling(); 105 ++m_cachedCurrentPosition; 106 } 107 if (!m_cachedCurrentNode && !m_cachedLengthValid) { 108 m_cachedLength = m_cachedCurrentPosition; 109 m_cachedLengthValid = true; 110 } 111 return m_cachedCurrentNode; 112 } 113 114 Node* ChildNodeList::item(unsigned index) const 115 { 116 if (m_cachedLengthValid && index >= m_cachedLength) 117 return nullptr; 118 if (m_cachedCurrentNode) { 119 if (index > m_cachedCurrentPosition) 120 return nodeAfterCached(index); 121 if (index < m_cachedCurrentPosition) 122 return nodeBeforeCached(index); 123 return m_cachedCurrentNode; 124 } 125 if (m_cachedLengthValid && m_cachedLength - index < index) 126 m_cachedCurrentNode = childFromLast(m_parent.get(), m_cachedLength - index - 1); 127 else 128 m_cachedCurrentNode = childFromFirst(m_parent.get(), index); 129 m_cachedCurrentPosition = index; 130 return m_cachedCurrentNode; 131 } 132 133 Node* ChildNodeList::namedItem(const AtomicString& name) const 134 { 135 // FIXME: According to the spec child node list should not have namedItem(). 136 if (m_parent.get().inDocument()) { 137 Element* element = m_parent.get().treeScope().getElementById(name); 138 if (element && element->parentNode() == &m_parent.get()) 139 return element; 140 if (!element || !m_parent.get().treeScope().containsMultipleElementsWithId(name)) 141 return nullptr; 142 } 143 auto children = elementChildren(m_parent.get()); 144 for (auto it = children.begin(), end = children.end(); it != end; ++it) { 145 auto& element = *it; 146 if (element.hasID() && element.idForStyleResolution() == name) 147 return const_cast<Element*>(&element); 148 } 149 return nullptr; 150 } 151 152 void ChildNodeList::invalidateCache() 153 { 154 m_cachedCurrentNode = nullptr; 155 m_cachedLengthValid = false; 46 156 } 47 157 -
trunk/Source/WebCore/dom/ChildNodeList.h
r157065 r158536 25 25 #define ChildNodeList_h 26 26 27 #include "LiveNodeList.h" 28 #include <wtf/PassRefPtr.h> 27 #include "NodeList.h" 28 #include <wtf/Ref.h> 29 #include <wtf/RefPtr.h> 29 30 30 31 namespace WebCore { 31 32 32 class ChildNodeList : public LiveNodeList { 33 public: 34 static PassRefPtr<ChildNodeList> create(Node& rootNode) 35 { 36 return adoptRef(new ChildNodeList(rootNode)); 37 } 33 class ContainerNode; 38 34 39 virtual ~ChildNodeList(); 35 class EmptyNodeList FINAL : public NodeList { 36 public: 37 static PassRefPtr<EmptyNodeList> create(Node& owner) 38 { 39 return adoptRef(new EmptyNodeList(owner)); 40 } 41 virtual ~EmptyNodeList(); 40 42 41 protected: 42 explicit ChildNodeList(Node& rootNode); 43 Node& ownerNode() { return m_owner.get(); } 43 44 44 virtual bool nodeMatches(Element*) const OVERRIDE; 45 }; 45 private: 46 explicit EmptyNodeList(Node& owner) : m_owner(owner) { } 47 48 virtual unsigned length() const OVERRIDE { return 0; } 49 virtual Node* item(unsigned) const OVERRIDE { return nullptr; } 50 virtual Node* namedItem(const AtomicString&) const OVERRIDE { return nullptr; } 51 52 virtual bool isEmptyNodeList() const OVERRIDE { return true; } 53 54 Ref<Node> m_owner; 55 }; 56 57 class ChildNodeList FINAL : public NodeList { 58 public: 59 static PassRefPtr<ChildNodeList> create(ContainerNode& parent) 60 { 61 return adoptRef(new ChildNodeList(parent)); 62 } 63 64 virtual ~ChildNodeList(); 65 66 ContainerNode& ownerNode() { return m_parent.get(); } 67 68 void invalidateCache(); 69 70 private: 71 explicit ChildNodeList(ContainerNode& parent); 72 73 virtual unsigned length() const OVERRIDE; 74 virtual Node* item(unsigned index) const OVERRIDE; 75 virtual Node* namedItem(const AtomicString&) const OVERRIDE; 76 77 virtual bool isChildNodeList() const OVERRIDE { return true; } 78 79 Node* nodeBeforeCached(unsigned) const; 80 Node* nodeAfterCached(unsigned) const; 81 82 Ref<ContainerNode> m_parent; 83 mutable unsigned m_cachedLength : 31; 84 mutable unsigned m_cachedLengthValid : 1; 85 mutable unsigned m_cachedCurrentPosition; 86 mutable Node* m_cachedCurrentNode; 87 }; 46 88 47 89 } // namespace WebCore -
trunk/Source/WebCore/dom/LiveNodeList.cpp
r157365 r158536 72 72 Node* LiveNodeList::namedItem(const AtomicString& elementId) const 73 73 { 74 // FIXME: Why doesn't this look into the name attribute like HTMLCollection::namedItem does? 74 75 Node& rootNode = this->rootNode(); 75 76 -
trunk/Source/WebCore/dom/LiveNodeList.h
r157365 r158536 68 68 ASSERT(!m_overridesItemAfter || !isNodeList(collectionType)); 69 69 70 if (collectionType != ChildNodeListType) 71 document().registerNodeList(this); 70 document().registerNodeList(this); 72 71 } 73 72 74 73 virtual ~LiveNodeListBase() 75 74 { 76 if (type() != ChildNodeListType) 77 document().unregisterNodeList(this); 75 document().unregisterNodeList(this); 78 76 } 79 77 … … 137 135 private: 138 136 Node* itemBeforeOrAfterCachedItem(unsigned offset, ContainerNode* root) const; 139 Node* traverseChildNodeListForwardToOffset(unsigned offset, Node* currentNode, unsigned& currentOffset) const;140 137 Element* traverseLiveNodeListFirstElement(ContainerNode* root) const; 141 138 Element* traverseLiveNodeListForwardToOffset(unsigned offset, Element* currentElement, unsigned& currentOffset, ContainerNode* root) const; … … 189 186 public: 190 187 LiveNodeList(Node& ownerNode, CollectionType collectionType, NodeListInvalidationType invalidationType, NodeListRootType rootType = NodeListIsRootedAtNode) 191 : LiveNodeListBase(ownerNode, rootType, invalidationType, collectionType == ChildNodeListType, 192 collectionType, DoesNotOverrideItemAfter) 188 : LiveNodeListBase(ownerNode, rootType, invalidationType, /*shouldOnlyIncludeDirectChildren*/ false, collectionType, DoesNotOverrideItemAfter) 193 189 { } 194 190 -
trunk/Source/WebCore/dom/Node.cpp
r158163 r158536 427 427 PassRefPtr<NodeList> Node::childNodes() 428 428 { 429 return ensureRareData().ensureNodeLists().ensureChildNodeList(*this); 429 if (isContainerNode()) 430 return ensureRareData().ensureNodeLists().ensureChildNodeList(toContainerNode(*this)); 431 return ensureRareData().ensureNodeLists().ensureEmptyChildNodeList(*this); 430 432 } 431 433 -
trunk/Source/WebCore/dom/NodeList.h
r135671 r158536 31 31 namespace WebCore { 32 32 33 33 class Node; 34 34 35 36 37 35 class NodeList : public ScriptWrappable, public RefCounted<NodeList> { 36 public: 37 virtual ~NodeList() { } 38 38 39 40 41 42 39 // DOM methods & attributes for NodeList 40 virtual unsigned length() const = 0; 41 virtual Node* item(unsigned index) const = 0; 42 virtual Node* namedItem(const AtomicString&) const = 0; 43 43 44 // Other methods (not part of DOM) 45 virtual bool isLiveNodeList() const { return false; } 46 }; 44 // Other methods (not part of DOM) 45 virtual bool isLiveNodeList() const { return false; } 46 virtual bool isChildNodeList() const { return false; } 47 virtual bool isEmptyNodeList() const { return false; } 48 }; 47 49 48 50 } // namespace WebCore -
trunk/Source/WebCore/dom/NodeRareData.h
r157653 r158536 56 56 } 57 57 58 PassRefPtr<ChildNodeList> ensureChildNodeList(Node& node) 59 { 58 PassRefPtr<ChildNodeList> ensureChildNodeList(ContainerNode& node) 59 { 60 ASSERT(!m_emptyChildNodeList); 60 61 if (m_childNodeList) 61 62 return m_childNodeList; … … 67 68 void removeChildNodeList(ChildNodeList* list) 68 69 { 69 ASSERT(m_childNodeList = list); 70 if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) 71 return; 72 m_childNodeList = 0; 70 ASSERT(m_childNodeList == list); 71 if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) 72 return; 73 m_childNodeList = nullptr; 74 } 75 76 PassRefPtr<EmptyNodeList> ensureEmptyChildNodeList(Node& node) 77 { 78 ASSERT(!m_childNodeList); 79 if (m_emptyChildNodeList) 80 return m_emptyChildNodeList; 81 RefPtr<EmptyNodeList> list = EmptyNodeList::create(node); 82 m_emptyChildNodeList = list.get(); 83 return list.release(); 84 } 85 86 void removeEmptyChildNodeList(EmptyNodeList* list) 87 { 88 ASSERT(m_emptyChildNodeList == list); 89 if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) 90 return; 91 m_emptyChildNodeList = nullptr; 73 92 } 74 93 … … 214 233 private: 215 234 NodeListsNodeData() 216 : m_childNodeList(0) 235 : m_childNodeList(nullptr) 236 , m_emptyChildNodeList(nullptr) 217 237 { } 218 238 … … 229 249 bool deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node&); 230 250 231 // FIXME: m_childNodeList should be merged into m_atomicNameCaches or at least be shared with HTMLCollection returned by Element::children 232 // but it's tricky because invalidateCaches shouldn't invalidate this cache and adoptTreeScope shouldn't call registerNodeList or unregisterNodeList. 251 // These two are currently mutually exclusive and could be unioned. Not very important as this class is large anyway. 233 252 ChildNodeList* m_childNodeList; 253 EmptyNodeList* m_emptyChildNodeList; 254 234 255 NodeListAtomicNameCacheMap m_atomicNameCaches; 235 256 NodeListNameCacheMap m_nameCaches; … … 299 320 { 300 321 ASSERT(ownerNode.nodeLists() == this); 301 if ((m_childNodeList ? 1 : 0) + m_atomicNameCaches.size() + m_nameCaches.size() + m_tagNodeListCacheNS.size() != 1)322 if ((m_childNodeList ? 1 : 0) + (m_emptyChildNodeList ? 1 : 0) + m_atomicNameCaches.size() + m_nameCaches.size() + m_tagNodeListCacheNS.size() != 1) 302 323 return false; 303 324 ownerNode.clearNodeLists(); -
trunk/Source/WebCore/html/CollectionType.h
r153772 r158536 54 54 55 55 // Live NodeList. 56 ChildNodeListType,57 56 ClassNodeListType, 58 57 NameNodeListType, … … 63 62 }; 64 63 65 static const CollectionType FirstNodeListType = C hildNodeListType;64 static const CollectionType FirstNodeListType = ClassNodeListType; 66 65 67 66 inline bool isNodeList(CollectionType type) -
trunk/Source/WebCore/html/HTMLCollection.cpp
r157653 r158536 64 64 case TableTBodies: 65 65 return true; 66 case ChildNodeListType:67 66 case ClassNodeListType: 68 67 case NameNodeListType: … … 102 101 case MapAreas: 103 102 return NodeListIsRootedAtNode; 104 case ChildNodeListType:105 103 case ClassNodeListType: 106 104 case NameNodeListType: … … 146 144 case FormControls: 147 145 return InvalidateForFormControls; 148 case ChildNodeListType:149 146 case ClassNodeListType: 150 147 case NameNodeListType: … … 229 226 case FormControls: 230 227 case TableRows: 231 case ChildNodeListType:232 228 case ClassNodeListType: 233 229 case NameNodeListType: … … 291 287 current = lastNode(rootNode(), shouldOnlyIncludeDirectChildren()); 292 288 293 if (type() == ChildNodeListType)294 return current;295 289 return iterateForPreviousNode(current); 296 290 } … … 325 319 } 326 320 327 // FIXME: This should be in ChildNodeList328 inline Node* LiveNodeListBase::traverseChildNodeListForwardToOffset(unsigned offset, Node* currentNode, unsigned& currentOffset) const329 {330 ASSERT(type() == ChildNodeListType);331 ASSERT_WITH_SECURITY_IMPLICATION(currentOffset < offset);332 while ((currentNode = currentNode->nextSibling())) {333 if (++currentOffset == offset)334 return currentNode;335 }336 return 0;337 }338 339 321 // FIXME: This should be in LiveNodeList 340 322 inline Element* LiveNodeListBase::traverseLiveNodeListFirstElement(ContainerNode* root) const 341 323 { 342 324 ASSERT(isNodeList(type())); 343 ASSERT(type() != ChildNodeListType);344 325 if (type() == HTMLTagNodeListType) 345 326 return firstMatchingElement(static_cast<const HTMLTagNodeList*>(this), root); … … 353 334 { 354 335 ASSERT(isNodeList(type())); 355 ASSERT(type() != ChildNodeListType);356 336 if (type() == HTMLTagNodeListType) 357 337 return traverseMatchingElementsForwardToOffset(static_cast<const HTMLTagNodeList*>(this), offset, currentElement, currentOffset, root); … … 425 405 unsigned offsetInArray = 0; 426 406 Node* firstItem; 427 if (type() == ChildNodeListType) 428 firstItem = root->firstChild(); 429 else if (isNodeList(type())) 407 if (isNodeList(type())) 430 408 firstItem = traverseLiveNodeListFirstElement(root); 431 409 else … … 468 446 469 447 unsigned offsetInArray = 0; 470 if (type() == ChildNodeListType) 471 currentItem = traverseChildNodeListForwardToOffset(offset, currentItem, currentOffset); 472 else if (isNodeList(type())) 448 if (isNodeList(type())) 473 449 currentItem = traverseLiveNodeListForwardToOffset(offset, toElement(currentItem), currentOffset, root); 474 450 else
Note: See TracChangeset
for help on using the changeset viewer.