Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / HTMLCollection.cpp
index 42f5c65..c373595 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
- * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2011, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2003-2008, 2011, 2012, 2014 Apple Inc. All rights reserved.
  * Copyright (C) 2014 Samsung Electronics. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
 #include "config.h"
 #include "core/html/HTMLCollection.h"
 
-#include "HTMLNames.h"
+#include "core/HTMLNames.h"
 #include "core/dom/ClassCollection.h"
 #include "core/dom/ElementTraversal.h"
 #include "core/dom/NodeRareData.h"
+#include "core/html/DocumentNameCollection.h"
+#include "core/html/HTMLDataListOptionsCollection.h"
 #include "core/html/HTMLElement.h"
 #include "core/html/HTMLObjectElement.h"
 #include "core/html/HTMLOptionElement.h"
+#include "core/html/HTMLOptionsCollection.h"
+#include "core/html/HTMLTagCollection.h"
+#include "core/html/WindowNameCollection.h"
 #include "wtf/HashSet.h"
 
-namespace WebCore {
+namespace blink {
 
 using namespace HTMLNames;
 
@@ -163,96 +168,79 @@ HTMLCollection::HTMLCollection(ContainerNode& ownerNode, CollectionType type, It
     , m_overridesItemAfter(itemAfterOverrideType == OverridesItemAfter)
     , m_shouldOnlyIncludeDirectChildren(shouldTypeOnlyIncludeDirectChildren(type))
 {
-    ScriptWrappable::init(this);
 }
 
-PassRefPtr<HTMLCollection> HTMLCollection::create(ContainerNode& base, CollectionType type)
+PassRefPtrWillBeRawPtr<HTMLCollection> HTMLCollection::create(ContainerNode& base, CollectionType type)
 {
-    return adoptRef(new HTMLCollection(base, type, DoesNotOverrideItemAfter));
+    return adoptRefWillBeNoop(new HTMLCollection(base, type, DoesNotOverrideItemAfter));
 }
 
 HTMLCollection::~HTMLCollection()
 {
+#if !ENABLE(OILPAN)
     if (hasValidIdNameCache())
         unregisterIdNameCacheFromDocument(document());
     // Named HTMLCollection types remove cache by themselves.
     if (isUnnamedHTMLCollectionType(type()))
         ownerNode().nodeLists()->removeCache(this, type());
+#endif
 }
 
 void HTMLCollection::invalidateCache(Document* oldDocument) const
 {
-    m_collectionIndexCache.invalidate();
+    m_collectionItemsCache.invalidate();
     invalidateIdNameCacheMaps(oldDocument);
 }
 
-template <class NodeListType>
-inline bool isMatchingElement(const NodeListType&, const Element&);
-
-template <> inline bool isMatchingElement(const HTMLCollection& htmlCollection, const Element& element)
+unsigned HTMLCollection::length() const
 {
-    CollectionType type = htmlCollection.type();
-
-    // These collections apply to any kind of Elements, not just HTMLElements.
-    switch (type) {
-    case DocAll:
-    case NodeChildren:
-        return true;
-    case ClassCollectionType:
-        return static_cast<const ClassCollection&>(htmlCollection).elementMatches(element);
-    case TagCollectionType:
-        return static_cast<const TagCollection&>(htmlCollection).elementMatches(element);
-    case HTMLTagCollectionType:
-        return static_cast<const HTMLTagCollection&>(htmlCollection).elementMatches(element);
-    default:
-        break;
-    }
+    return m_collectionItemsCache.nodeCount(*this);
+}
 
-    // The following only applies to HTMLElements.
-    if (!element.isHTMLElement())
-        return false;
+Element* HTMLCollection::item(unsigned offset) const
+{
+    return m_collectionItemsCache.nodeAt(*this, offset);
+}
 
-    switch (type) {
+static inline bool isMatchingHTMLElement(const HTMLCollection& htmlCollection, const HTMLElement& element)
+{
+    switch (htmlCollection.type()) {
     case DocImages:
-        return element.hasLocalName(imgTag);
+        return element.hasTagName(imgTag);
     case DocScripts:
-        return element.hasLocalName(scriptTag);
+        return element.hasTagName(scriptTag);
     case DocForms:
-        return element.hasLocalName(formTag);
+        return element.hasTagName(formTag);
+    case DocumentNamedItems:
+        return toDocumentNameCollection(htmlCollection).elementMatches(element);
     case TableTBodies:
-        return element.hasLocalName(tbodyTag);
+        return element.hasTagName(tbodyTag);
     case TRCells:
-        return element.hasLocalName(tdTag) || element.hasLocalName(thTag);
+        return element.hasTagName(tdTag) || element.hasTagName(thTag);
     case TSectionRows:
-        return element.hasLocalName(trTag);
+        return element.hasTagName(trTag);
     case SelectOptions:
-        return element.hasLocalName(optionTag);
+        return toHTMLOptionsCollection(htmlCollection).elementMatches(element);
     case SelectedOptions:
-        return element.hasLocalName(optionTag) && toHTMLOptionElement(element).selected();
+        return isHTMLOptionElement(element) && toHTMLOptionElement(element).selected();
     case DataListOptions:
-        if (element.hasLocalName(optionTag)) {
-            const HTMLOptionElement& option = toHTMLOptionElement(element);
-            if (!option.isDisabledFormControl() && !option.value().isEmpty())
-                return true;
-        }
-        return false;
+        return toHTMLDataListOptionsCollection(htmlCollection).elementMatches(element);
     case MapAreas:
-        return element.hasLocalName(areaTag);
+        return element.hasTagName(areaTag);
     case DocApplets:
-        return element.hasLocalName(appletTag) || (element.hasLocalName(objectTag) && toHTMLObjectElement(element).containsJavaApplet());
+        return element.hasTagName(appletTag) || (isHTMLObjectElement(element) && toHTMLObjectElement(element).containsJavaApplet());
     case DocEmbeds:
-        return element.hasLocalName(embedTag);
+        return element.hasTagName(embedTag);
     case DocLinks:
-        return (element.hasLocalName(aTag) || element.hasLocalName(areaTag)) && element.fastHasAttribute(hrefAttr);
+        return (element.hasTagName(aTag) || element.hasTagName(areaTag)) && element.fastHasAttribute(hrefAttr);
     case DocAnchors:
-        return element.hasLocalName(aTag) && element.fastHasAttribute(nameAttr);
+        return element.hasTagName(aTag) && element.fastHasAttribute(nameAttr);
     case ClassCollectionType:
     case TagCollectionType:
     case HTMLTagCollectionType:
     case DocAll:
     case NodeChildren:
     case FormControls:
-    case DocumentNamedItems:
     case TableRows:
     case WindowNamedItems:
     case NameNodeListType:
@@ -264,20 +252,56 @@ template <> inline bool isMatchingElement(const HTMLCollection& htmlCollection,
     return false;
 }
 
-template <> inline bool isMatchingElement(const ClassCollection& collection, const Element& element)
+inline bool HTMLCollection::elementMatches(const Element& element) const
 {
-    return collection.elementMatches(element);
-}
+    // These collections apply to any kind of Elements, not just HTMLElements.
+    switch (type()) {
+    case DocAll:
+    case NodeChildren:
+        return true;
+    case ClassCollectionType:
+        return toClassCollection(*this).elementMatches(element);
+    case TagCollectionType:
+        return toTagCollection(*this).elementMatches(element);
+    case HTMLTagCollectionType:
+        return toHTMLTagCollection(*this).elementMatches(element);
+    case WindowNamedItems:
+        return toWindowNameCollection(*this).elementMatches(element);
+    default:
+        break;
+    }
 
-template <> inline bool isMatchingElement(const HTMLTagCollection& collection, const Element& element)
-{
-    return collection.elementMatches(element);
+    // The following only applies to HTMLElements.
+    return element.isHTMLElement() && isMatchingHTMLElement(*this, toHTMLElement(element));
 }
 
+namespace {
+
+template <class HTMLCollectionType>
+class IsMatch {
+public:
+    IsMatch(const HTMLCollectionType& list)
+        : m_list(list)
+    { }
+
+    bool operator() (const Element& element) const
+    {
+        return m_list.elementMatches(element);
+    }
+
+private:
+    const HTMLCollectionType& m_list;
+};
+
+} // namespace
+
+template <class HTMLCollectionType>
+static inline IsMatch<HTMLCollectionType> makeIsMatch(const HTMLCollectionType& list) { return IsMatch<HTMLCollectionType>(list); }
+
 Element* HTMLCollection::virtualItemAfter(Element*) const
 {
     ASSERT_NOT_REACHED();
-    return 0;
+    return nullptr;
 }
 
 static inline bool nameShouldBeVisibleInDocumentAll(const HTMLElement& element)
@@ -285,85 +309,42 @@ static inline bool nameShouldBeVisibleInDocumentAll(const HTMLElement& element)
     // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#dom-htmlallcollection-nameditem:
     // The document.all collection returns only certain types of elements by name,
     // although it returns any type of element by id.
-    return element.hasLocalName(aTag)
-        || element.hasLocalName(appletTag)
-        || element.hasLocalName(areaTag)
-        || element.hasLocalName(embedTag)
-        || element.hasLocalName(formTag)
-        || element.hasLocalName(frameTag)
-        || element.hasLocalName(framesetTag)
-        || element.hasLocalName(iframeTag)
-        || element.hasLocalName(imgTag)
-        || element.hasLocalName(inputTag)
-        || element.hasLocalName(objectTag)
-        || element.hasLocalName(selectTag);
-}
-
-inline Element* firstMatchingChildElement(const HTMLCollection& nodeList)
-{
-    Element* element = ElementTraversal::firstChild(nodeList.rootNode());
-    while (element && !isMatchingElement(nodeList, *element))
-        element = ElementTraversal::nextSibling(*element);
-    return element;
-}
-
-inline Element* lastMatchingChildElement(const HTMLCollection& nodeList)
-{
-    Element* element = ElementTraversal::lastChild(nodeList.rootNode());
-    while (element && !isMatchingElement(nodeList, *element))
-        element = ElementTraversal::previousSibling(*element);
-    return element;
+    return element.hasTagName(aTag)
+        || element.hasTagName(appletTag)
+        || element.hasTagName(areaTag)
+        || element.hasTagName(embedTag)
+        || element.hasTagName(formTag)
+        || element.hasTagName(frameTag)
+        || element.hasTagName(framesetTag)
+        || element.hasTagName(iframeTag)
+        || element.hasTagName(imgTag)
+        || element.hasTagName(inputTag)
+        || element.hasTagName(objectTag)
+        || element.hasTagName(selectTag);
 }
 
-inline Element* nextMatchingChildElement(const HTMLCollection& nodeList, Element& current)
-{
-    Element* next = &current;
-    do {
-        next = ElementTraversal::nextSibling(*next);
-    } while (next && !isMatchingElement(nodeList, *next));
-    return next;
-}
-
-inline Element* previousMatchingChildElement(const HTMLCollection& nodeList, Element& current)
-{
-    Element* previous = &current;
-    do {
-        previous = ElementTraversal::previousSibling(*previous);
-    } while (previous && !isMatchingElement(nodeList, *previous));
-    return previous;
-}
-
-Element* HTMLCollection::traverseToFirstElement() const
+Element* HTMLCollection::traverseToFirst() const
 {
     switch (type()) {
     case HTMLTagCollectionType:
-        return firstMatchingElement(static_cast<const HTMLTagCollection&>(*this));
+        return ElementTraversal::firstWithin(rootNode(), makeIsMatch(toHTMLTagCollection(*this)));
     case ClassCollectionType:
-        return firstMatchingElement(static_cast<const ClassCollection&>(*this));
+        return ElementTraversal::firstWithin(rootNode(), makeIsMatch(toClassCollection(*this)));
     default:
         if (overridesItemAfter())
             return virtualItemAfter(0);
         if (shouldOnlyIncludeDirectChildren())
-            return firstMatchingChildElement(*this);
-        return firstMatchingElement(*this);
+            return ElementTraversal::firstChild(rootNode(), makeIsMatch(*this));
+        return ElementTraversal::firstWithin(rootNode(), makeIsMatch(*this));
     }
 }
 
-Element* HTMLCollection::traverseToLastElement() const
+Element* HTMLCollection::traverseToLast() const
 {
     ASSERT(canTraverseBackward());
     if (shouldOnlyIncludeDirectChildren())
-        return lastMatchingChildElement(*this);
-    return lastMatchingElement(*this);
-}
-
-inline Element* HTMLCollection::traverseNextElement(Element& previous) const
-{
-    if (overridesItemAfter())
-        return virtualItemAfter(&previous);
-    if (shouldOnlyIncludeDirectChildren())
-        return nextMatchingChildElement(*this, previous);
-    return nextMatchingElement(*this, previous);
+        return ElementTraversal::lastChild(rootNode(), makeIsMatch(*this));
+    return ElementTraversal::lastWithin(rootNode(), makeIsMatch(*this));
 }
 
 Element* HTMLCollection::traverseForwardToOffset(unsigned offset, Element& currentElement, unsigned& currentOffset) const
@@ -371,27 +352,26 @@ Element* HTMLCollection::traverseForwardToOffset(unsigned offset, Element& curre
     ASSERT(currentOffset < offset);
     switch (type()) {
     case HTMLTagCollectionType:
-        return traverseMatchingElementsForwardToOffset(static_cast<const HTMLTagCollection&>(*this), offset, currentElement, currentOffset);
+        return traverseMatchingElementsForwardToOffset(currentElement, &rootNode(), offset, currentOffset, makeIsMatch(toHTMLTagCollection(*this)));
     case ClassCollectionType:
-        return traverseMatchingElementsForwardToOffset(static_cast<const ClassCollection&>(*this), offset, currentElement, currentOffset);
+        return traverseMatchingElementsForwardToOffset(currentElement, &rootNode(), offset, currentOffset, makeIsMatch(toClassCollection(*this)));
     default:
         if (overridesItemAfter()) {
-            Element* next = &currentElement;
-            while ((next = virtualItemAfter(next))) {
+            for (Element* next = virtualItemAfter(&currentElement); next; next = virtualItemAfter(next)) {
                 if (++currentOffset == offset)
                     return next;
             }
-            return 0;
+            return nullptr;
         }
         if (shouldOnlyIncludeDirectChildren()) {
-            Element* next = &currentElement;
-            while ((next = nextMatchingChildElement(*this, *next))) {
+            IsMatch<HTMLCollection> isMatch(*this);
+            for (Element* next = ElementTraversal::nextSibling(currentElement, isMatch); next; next = ElementTraversal::nextSibling(*next, isMatch)) {
                 if (++currentOffset == offset)
                     return next;
             }
-            return 0;
+            return nullptr;
         }
-        return traverseMatchingElementsForwardToOffset(*this, offset, currentElement, currentOffset);
+        return traverseMatchingElementsForwardToOffset(currentElement, &rootNode(), offset, currentOffset, makeIsMatch(*this));
     }
 }
 
@@ -400,14 +380,14 @@ Element* HTMLCollection::traverseBackwardToOffset(unsigned offset, Element& curr
     ASSERT(currentOffset > offset);
     ASSERT(canTraverseBackward());
     if (shouldOnlyIncludeDirectChildren()) {
-        Element* previous = &currentElement;
-        while ((previous = previousMatchingChildElement(*this, *previous))) {
+        IsMatch<HTMLCollection> isMatch(*this);
+        for (Element* previous = ElementTraversal::previousSibling(currentElement, isMatch); previous; previous = ElementTraversal::previousSibling(*previous, isMatch)) {
             if (--currentOffset == offset)
                 return previous;
         }
-        return 0;
+        return nullptr;
     }
-    return traverseMatchingElementsBackwardToOffset(*this, offset, currentElement, currentOffset);
+    return traverseMatchingElementsBackwardToOffset(currentElement, &rootNode(), offset, currentOffset, makeIsMatch(*this));
 }
 
 Element* HTMLCollection::namedItem(const AtomicString& name) const
@@ -420,15 +400,15 @@ Element* HTMLCollection::namedItem(const AtomicString& name) const
     updateIdNameCache();
 
     const NamedItemCache& cache = namedItemCache();
-    Vector<Element*>* idResults = cache.getElementsById(name);
+    WillBeHeapVector<RawPtrWillBeMember<Element>>* idResults = cache.getElementsById(name);
     if (idResults && !idResults->isEmpty())
         return idResults->first();
 
-    Vector<Element*>* nameResults = cache.getElementsByName(name);
+    WillBeHeapVector<RawPtrWillBeMember<Element>>* nameResults = cache.getElementsByName(name);
     if (nameResults && !nameResults->isEmpty())
         return nameResults->first();
 
-    return 0;
+    return nullptr;
 }
 
 bool HTMLCollection::namedPropertyQuery(const AtomicString& name, ExceptionState&)
@@ -447,7 +427,9 @@ void HTMLCollection::supportedPropertyNames(Vector<String>& names)
     //      nor is in result, append element's name attribute value to result.
     // 3. Return result.
     HashSet<AtomicString> existingNames;
-    for (Element* element = traverseToFirstElement(); element; element = traverseNextElement(*element)) {
+    unsigned length = this->length();
+    for (unsigned i = 0; i < length; ++i) {
+        Element* element = item(i);
         const AtomicString& idAttribute = element->getIdAttribute();
         if (!idAttribute.isEmpty()) {
             HashSet<AtomicString>::AddResult addResult = existingNames.add(idAttribute);
@@ -475,8 +457,10 @@ void HTMLCollection::updateIdNameCache() const
     if (hasValidIdNameCache())
         return;
 
-    OwnPtr<NamedItemCache> cache = NamedItemCache::create();
-    for (Element* element = traverseToFirstElement(); element; element = traverseNextElement(*element)) {
+    OwnPtrWillBeRawPtr<NamedItemCache> cache = NamedItemCache::create();
+    unsigned length = this->length();
+    for (unsigned i = 0; i < length; ++i) {
+        Element* element = item(i);
         const AtomicString& idAttrVal = element->getIdAttribute();
         if (!idAttrVal.isEmpty())
             cache->addElementWithId(idAttrVal, element);
@@ -490,7 +474,7 @@ void HTMLCollection::updateIdNameCache() const
     setNamedItemCache(cache.release());
 }
 
-void HTMLCollection::namedItems(const AtomicString& name, Vector<RefPtr<Element> >& result) const
+void HTMLCollection::namedItems(const AtomicString& name, WillBeHeapVector<RefPtrWillBeMember<Element>>& result) const
 {
     ASSERT(result.isEmpty());
     if (name.isEmpty())
@@ -499,18 +483,25 @@ void HTMLCollection::namedItems(const AtomicString& name, Vector<RefPtr<Element>
     updateIdNameCache();
 
     const NamedItemCache& cache = namedItemCache();
-    Vector<Element*>* idResults = cache.getElementsById(name);
-    Vector<Element*>* nameResults = cache.getElementsByName(name);
-
-    for (unsigned i = 0; idResults && i < idResults->size(); ++i)
-        result.append(idResults->at(i));
-
-    for (unsigned i = 0; nameResults && i < nameResults->size(); ++i)
-        result.append(nameResults->at(i));
+    if (WillBeHeapVector<RawPtrWillBeMember<Element>>* idResults = cache.getElementsById(name)) {
+        for (unsigned i = 0; i < idResults->size(); ++i)
+            result.append(idResults->at(i));
+    }
+    if (WillBeHeapVector<RawPtrWillBeMember<Element>>* nameResults = cache.getElementsByName(name)) {
+        for (unsigned i = 0; i < nameResults->size(); ++i)
+            result.append(nameResults->at(i));
+    }
 }
 
 HTMLCollection::NamedItemCache::NamedItemCache()
 {
 }
 
-} // namespace WebCore
+void HTMLCollection::trace(Visitor* visitor)
+{
+    visitor->trace(m_namedItemCache);
+    visitor->trace(m_collectionItemsCache);
+    LiveNodeListBase::trace(visitor);
+}
+
+} // namespace blink