diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/html/HTMLCollection.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/html/HTMLCollection.cpp')
-rw-r--r-- | Source/WebCore/html/HTMLCollection.cpp | 424 |
1 files changed, 84 insertions, 340 deletions
diff --git a/Source/WebCore/html/HTMLCollection.cpp b/Source/WebCore/html/HTMLCollection.cpp index 58a9eeca2..8c1738ae3 100644 --- a/Source/WebCore/html/HTMLCollection.cpp +++ b/Source/WebCore/html/HTMLCollection.cpp @@ -23,49 +23,15 @@ #include "config.h" #include "HTMLCollection.h" -#include "ElementTraversal.h" -#include "HTMLDocument.h" -#include "HTMLNameCollection.h" +#include "CachedHTMLCollection.h" #include "HTMLNames.h" -#include "HTMLObjectElement.h" -#include "HTMLOptionElement.h" #include "NodeRareData.h" namespace WebCore { using namespace HTMLNames; -static bool shouldOnlyIncludeDirectChildren(CollectionType type) -{ - switch (type) { - case DocAll: - case DocAnchors: - case DocApplets: - case DocEmbeds: - case DocForms: - case DocImages: - case DocLinks: - case DocScripts: - case DocumentNamedItems: - case MapAreas: - case TableRows: - case SelectOptions: - case SelectedOptions: - case DataListOptions: - case WindowNamedItems: - case FormControls: - return false; - case NodeChildren: - case TRCells: - case TSectionRows: - case TableTBodies: - return true; - } - ASSERT_NOT_REACHED(); - return false; -} - -static NodeListRootType rootTypeFromCollectionType(CollectionType type) +inline auto HTMLCollection::rootTypeFromCollectionType(CollectionType type) -> RootType { switch (type) { case DocImages: @@ -79,7 +45,11 @@ static NodeListRootType rootTypeFromCollectionType(CollectionType type) case WindowNamedItems: case DocumentNamedItems: case FormControls: - return NodeListIsRootedAtDocument; + return HTMLCollection::IsRootedAtDocument; + case AllDescendants: + case ByClass: + case ByTag: + case ByHTMLTag: case NodeChildren: case TableTBodies: case TSectionRows: @@ -89,15 +59,18 @@ static NodeListRootType rootTypeFromCollectionType(CollectionType type) case SelectedOptions: case DataListOptions: case MapAreas: - return NodeListIsRootedAtNode; + return HTMLCollection::IsRootedAtNode; } ASSERT_NOT_REACHED(); - return NodeListIsRootedAtNode; + return HTMLCollection::IsRootedAtNode; } static NodeListInvalidationType invalidationTypeExcludingIdAndNameAttributes(CollectionType type) { switch (type) { + case ByTag: + case ByHTMLTag: + case AllDescendants: case DocImages: case DocEmbeds: case DocForms: @@ -116,6 +89,8 @@ static NodeListInvalidationType invalidationTypeExcludingIdAndNameAttributes(Col case DataListOptions: // FIXME: We can do better some day. return InvalidateOnAnyAttrChange; + case ByClass: + return InvalidateOnClassAttrChange; case DocAnchors: return InvalidateOnNameAttrChange; case DocLinks: @@ -131,359 +106,128 @@ static NodeListInvalidationType invalidationTypeExcludingIdAndNameAttributes(Col return DoNotInvalidateOnAttributeChanges; } -HTMLCollection::HTMLCollection(ContainerNode& ownerNode, CollectionType type, ElementTraversalType traversalType) +HTMLCollection::HTMLCollection(ContainerNode& ownerNode, CollectionType type) : m_ownerNode(ownerNode) - , m_rootType(rootTypeFromCollectionType(type)) - , m_invalidationType(invalidationTypeExcludingIdAndNameAttributes(type)) - , m_shouldOnlyIncludeDirectChildren(shouldOnlyIncludeDirectChildren(type)) - , m_isNameCacheValid(false) , m_collectionType(type) - , m_usesCustomForwardOnlyTraversal(traversalType == CustomForwardOnlyTraversal) - , m_isItemRefElementsCacheValid(false) + , m_invalidationType(invalidationTypeExcludingIdAndNameAttributes(type)) + , m_rootType(rootTypeFromCollectionType(type)) { ASSERT(m_rootType == static_cast<unsigned>(rootTypeFromCollectionType(type))); ASSERT(m_invalidationType == static_cast<unsigned>(invalidationTypeExcludingIdAndNameAttributes(type))); ASSERT(m_collectionType == static_cast<unsigned>(type)); - - document().registerCollection(*this); -} - -PassRefPtr<HTMLCollection> HTMLCollection::create(ContainerNode& base, CollectionType type) -{ - return adoptRef(new HTMLCollection(base, type)); } HTMLCollection::~HTMLCollection() { - document().unregisterCollection(*this); - // HTMLNameCollection removes cache by itself. - if (type() != WindowNamedItems && type() != DocumentNamedItems) - ownerNode().nodeLists()->removeCachedCollection(this); -} - -ContainerNode& HTMLCollection::rootNode() const -{ - if (isRootedAtDocument() && ownerNode().inDocument()) - return ownerNode().document(); - - return ownerNode(); -} + if (hasNamedElementCache()) + document().collectionWillClearIdNameMap(*this); -inline bool isMatchingElement(const HTMLCollection& htmlCollection, Element& element) -{ - CollectionType type = htmlCollection.type(); - if (!element.isHTMLElement() && !(type == DocAll || type == NodeChildren || type == WindowNamedItems)) - return false; - - switch (type) { - case DocImages: - return element.hasLocalName(imgTag); - case DocScripts: - return element.hasLocalName(scriptTag); - case DocForms: - return element.hasLocalName(formTag); - case TableTBodies: - return element.hasLocalName(tbodyTag); - case TRCells: - return element.hasLocalName(tdTag) || element.hasLocalName(thTag); - case TSectionRows: - return element.hasLocalName(trTag); - case SelectOptions: - return element.hasLocalName(optionTag); - case SelectedOptions: - return element.hasLocalName(optionTag) && toHTMLOptionElement(element).selected(); - case DataListOptions: - if (element.hasLocalName(optionTag)) { - HTMLOptionElement& option = toHTMLOptionElement(element); - if (!option.isDisabledFormControl() && !option.value().isEmpty()) - return true; - } - return false; - case MapAreas: - return element.hasLocalName(areaTag); - case DocApplets: - return element.hasLocalName(appletTag) || (element.hasLocalName(objectTag) && toHTMLObjectElement(element).containsJavaApplet()); - case DocEmbeds: - return element.hasLocalName(embedTag); - case DocLinks: - return (element.hasLocalName(aTag) || element.hasLocalName(areaTag)) && element.fastHasAttribute(hrefAttr); - case DocAnchors: - return element.hasLocalName(aTag) && element.fastHasAttribute(nameAttr); - case DocAll: - case NodeChildren: - return true; - case DocumentNamedItems: - return static_cast<const DocumentNameCollection&>(htmlCollection).nodeMatches(&element); + // HTMLNameCollection & ClassCollection remove cache by themselves. + // FIXME: We need a cleaner way to handle this. + switch (type()) { + case ByClass: + case ByTag: + case ByHTMLTag: case WindowNamedItems: - return static_cast<const WindowNameCollection&>(htmlCollection).nodeMatches(&element); - case FormControls: - case TableRows: + case DocumentNamedItems: break; + default: + ownerNode().nodeLists()->removeCachedCollection(this); } - ASSERT_NOT_REACHED(); - return false; -} - -static Element* previousElement(ContainerNode& base, Element* previous, bool onlyIncludeDirectChildren) -{ - return onlyIncludeDirectChildren ? ElementTraversal::previousSibling(previous) : ElementTraversal::previous(previous, &base); -} - -ALWAYS_INLINE Element* HTMLCollection::iterateForPreviousElement(Element* current) const -{ - bool onlyIncludeDirectChildren = m_shouldOnlyIncludeDirectChildren; - ContainerNode& rootNode = this->rootNode(); - for (; current; current = previousElement(rootNode, current, onlyIncludeDirectChildren)) { - if (isMatchingElement(*this, *current)) - return current; - } - return nullptr; -} - -inline Element* firstMatchingElement(const HTMLCollection& collection, ContainerNode& root) -{ - Element* element = ElementTraversal::firstWithin(&root); - while (element && !isMatchingElement(collection, *element)) - element = ElementTraversal::next(element, &root); - return element; -} - -inline Element* nextMatchingElement(const HTMLCollection& collection, Element* current, ContainerNode& root) -{ - do { - current = ElementTraversal::next(current, &root); - } while (current && !isMatchingElement(collection, *current)); - return current; -} - -unsigned HTMLCollection::length() const -{ - return m_indexCache.nodeCount(*this); -} - -Node* HTMLCollection::item(unsigned offset) const -{ - return m_indexCache.nodeAt(*this, offset); -} - -static inline bool nameShouldBeVisibleInDocumentAll(HTMLElement& element) -{ - // The document.all collection returns only certain types of elements by name, - // although it returns any type of element by id. - return element.hasLocalName(appletTag) - || element.hasLocalName(embedTag) - || element.hasLocalName(formTag) - || element.hasLocalName(imgTag) - || element.hasLocalName(inputTag) - || element.hasLocalName(objectTag) - || element.hasLocalName(selectTag); -} - -inline Element* firstMatchingChildElement(const HTMLCollection& nodeList, ContainerNode& root) -{ - Element* element = ElementTraversal::firstWithin(&root); - while (element && !isMatchingElement(nodeList, *element)) - element = ElementTraversal::nextSibling(element); - return element; -} - -inline Element* nextMatchingSiblingElement(const HTMLCollection& nodeList, Element* current) -{ - do { - current = ElementTraversal::nextSibling(current); - } while (current && !isMatchingElement(nodeList, *current)); - return current; -} - -inline Element* HTMLCollection::firstElement(ContainerNode& root) const -{ - if (usesCustomForwardOnlyTraversal()) - return customElementAfter(nullptr); - if (m_shouldOnlyIncludeDirectChildren) - return firstMatchingChildElement(*this, root); - return firstMatchingElement(*this, root); -} - -inline Element* HTMLCollection::traverseForward(Element& current, unsigned count, unsigned& traversedCount, ContainerNode& root) const -{ - Element* element = ¤t; - if (usesCustomForwardOnlyTraversal()) { - for (traversedCount = 0; traversedCount < count; ++traversedCount) { - element = customElementAfter(element); - if (!element) - return nullptr; - } - return element; - } - if (m_shouldOnlyIncludeDirectChildren) { - for (traversedCount = 0; traversedCount < count; ++traversedCount) { - element = nextMatchingSiblingElement(*this, element); - if (!element) - return nullptr; - } - return element; - } - for (traversedCount = 0; traversedCount < count; ++traversedCount) { - element = nextMatchingElement(*this, element, root); - if (!element) - return nullptr; - } - return element; -} - -Element* HTMLCollection::collectionFirst() const -{ - return firstElement(rootNode()); -} - -Element* HTMLCollection::collectionLast() const -{ - // FIXME: This should be optimized similarly to the forward case. - auto& root = rootNode(); - Element* last = m_shouldOnlyIncludeDirectChildren ? ElementTraversal::lastChild(&root) : ElementTraversal::lastWithin(&root); - return iterateForPreviousElement(last); -} - -Element* HTMLCollection::collectionTraverseForward(Element& current, unsigned count, unsigned& traversedCount) const -{ - return traverseForward(current, count, traversedCount, rootNode()); -} - -Element* HTMLCollection::collectionTraverseBackward(Element& current, unsigned count) const -{ - // FIXME: This should be optimized similarly to the forward case. - auto& root = rootNode(); - Element* element = ¤t; - if (m_shouldOnlyIncludeDirectChildren) { - for (; count && element ; --count) - element = iterateForPreviousElement(ElementTraversal::previousSibling(element)); - return element; - } - for (; count && element ; --count) - element = iterateForPreviousElement(ElementTraversal::previous(element, &root)); - return element; } -void HTMLCollection::invalidateCache() const +void HTMLCollection::invalidateCache(Document& document) { - m_indexCache.invalidate(); - m_isNameCacheValid = false; - m_isItemRefElementsCacheValid = false; - m_idCache.clear(); - m_nameCache.clear(); + if (hasNamedElementCache()) + invalidateNamedElementCache(document); } -void HTMLCollection::invalidateIdNameCacheMaps() const +void HTMLCollection::invalidateNamedElementCache(Document& document) const { - m_idCache.clear(); - m_nameCache.clear(); + ASSERT(hasNamedElementCache()); + document.collectionWillClearIdNameMap(*this); + m_namedElementCache = nullptr; } -Node* HTMLCollection::namedItem(const AtomicString& name) const +Element* HTMLCollection::namedItemSlow(const AtomicString& name) const { - // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp - // This method first searches for an object with a matching id - // attribute. If a match is not found, the method then searches for an - // object with a matching name attribute, but only on those elements - // that are allowed a name attribute. - - if (name.isEmpty()) - return 0; - - ContainerNode& root = rootNode(); - if (!usesCustomForwardOnlyTraversal() && root.isInTreeScope()) { - TreeScope& treeScope = root.treeScope(); - Element* candidate = 0; - if (treeScope.hasElementWithId(*name.impl())) { - if (!treeScope.containsMultipleElementsWithId(name)) - candidate = treeScope.getElementById(name); - } else if (treeScope.hasElementWithName(*name.impl())) { - if (!treeScope.containsMultipleElementsWithName(name)) { - candidate = treeScope.getElementByName(name); - if (candidate && type() == DocAll && (!candidate->isHTMLElement() || !nameShouldBeVisibleInDocumentAll(toHTMLElement(*candidate)))) - candidate = 0; - } - } else - return 0; - - if (candidate && isMatchingElement(*this, *candidate) - && (m_shouldOnlyIncludeDirectChildren ? candidate->parentNode() == &root : candidate->isDescendantOf(&root))) - return candidate; - } - // The pathological case. We need to walk the entire subtree. - updateNameCache(); + updateNamedElementCache(); + ASSERT(m_namedElementCache); - if (Vector<Element*>* idResults = idCache(name)) { + if (const Vector<Element*>* idResults = m_namedElementCache->findElementsWithId(name)) { if (idResults->size()) return idResults->at(0); } - if (Vector<Element*>* nameResults = nameCache(name)) { + if (const Vector<Element*>* nameResults = m_namedElementCache->findElementsWithName(name)) { if (nameResults->size()) return nameResults->at(0); } - return 0; + return nullptr; } -void HTMLCollection::updateNameCache() const +// Documented in https://dom.spec.whatwg.org/#interface-htmlcollection. +const Vector<AtomicString>& HTMLCollection::supportedPropertyNames() { - if (hasNameCache()) + updateNamedElementCache(); + ASSERT(m_namedElementCache); + + return m_namedElementCache->propertyNames(); +} + +void HTMLCollection::updateNamedElementCache() const +{ + if (hasNamedElementCache()) return; - ContainerNode& root = rootNode(); + auto cache = std::make_unique<CollectionNamedElementCache>(); - unsigned count; - for (Element* element = firstElement(root); element; element = traverseForward(*element, 1, count, root)) { - const AtomicString& idAttrVal = element->getIdAttribute(); - if (!idAttrVal.isEmpty()) - appendIdCache(idAttrVal, element); - if (!element->isHTMLElement()) + unsigned size = length(); + for (unsigned i = 0; i < size; ++i) { + Element& element = *item(i); + const AtomicString& id = element.getIdAttribute(); + if (!id.isEmpty()) + cache->appendToIdCache(id, element); + if (!is<HTMLElement>(element)) continue; - const AtomicString& nameAttrVal = element->getNameAttribute(); - if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && (type() != DocAll || nameShouldBeVisibleInDocumentAll(toHTMLElement(*element)))) - appendNameCache(nameAttrVal, element); + const AtomicString& name = element.getNameAttribute(); + if (!name.isEmpty() && id != name && (type() != DocAll || nameShouldBeVisibleInDocumentAll(downcast<HTMLElement>(element)))) + cache->appendToNameCache(name, element); } - setHasNameCache(); + setNamedItemCache(WTFMove(cache)); } -bool HTMLCollection::hasNamedItem(const AtomicString& name) const +Vector<Ref<Element>> HTMLCollection::namedItems(const AtomicString& name) const { - // FIXME: We can do better when there are multiple elements of the same name. - return namedItem(name); -} + // FIXME: This non-virtual function can't possibly be doing the correct thing for + // any derived class that overrides the virtual namedItem function. -void HTMLCollection::namedItems(const AtomicString& name, Vector<Ref<Element>>& result) const -{ - ASSERT(result.isEmpty()); - if (name.isEmpty()) - return; + Vector<Ref<Element>> elements; - updateNameCache(); + if (name.isEmpty()) + return elements; - Vector<Element*>* idResults = idCache(name); - Vector<Element*>* nameResults = nameCache(name); + updateNamedElementCache(); + ASSERT(m_namedElementCache); - for (unsigned i = 0; idResults && i < idResults->size(); ++i) - result.append(*idResults->at(i)); + auto* elementsWithId = m_namedElementCache->findElementsWithId(name); + auto* elementsWithName = m_namedElementCache->findElementsWithName(name); - for (unsigned i = 0; nameResults && i < nameResults->size(); ++i) - result.append(*nameResults->at(i)); -} + elements.reserveInitialCapacity((elementsWithId ? elementsWithId->size() : 0) + (elementsWithName ? elementsWithName->size() : 0)); -PassRefPtr<NodeList> HTMLCollection::tags(const String& name) -{ - return ownerNode().getElementsByTagName(name); -} + if (elementsWithId) { + for (auto& element : *elementsWithId) + elements.uncheckedAppend(*element); + } + if (elementsWithName) { + for (auto& element : *elementsWithName) + elements.uncheckedAppend(*element); + } -void HTMLCollection::append(NodeCacheMap& map, const AtomicString& key, Element* element) -{ - OwnPtr<Vector<Element*>>& vector = map.add(key.impl(), nullptr).iterator->value; - if (!vector) - vector = adoptPtr(new Vector<Element*>); - vector->append(element); + return elements; } } // namespace WebCore |