Home | History | Annotate | Download | only in html
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2010, 2011, 2012 Apple Inc. All rights reserved.
      5  * Copyright (C) 2014 Samsung Electronics. All rights reserved.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  *
     22  */
     23 
     24 #include "config.h"
     25 #include "core/html/HTMLFormControlsCollection.h"
     26 
     27 #include "core/HTMLNames.h"
     28 #include "core/frame/UseCounter.h"
     29 #include "core/html/HTMLFieldSetElement.h"
     30 #include "core/html/HTMLFormElement.h"
     31 #include "core/html/HTMLImageElement.h"
     32 #include "wtf/HashSet.h"
     33 
     34 namespace blink {
     35 
     36 using namespace HTMLNames;
     37 
     38 // Since the collections are to be "live", we have to do the
     39 // calculation every time if anything has changed.
     40 
     41 HTMLFormControlsCollection::HTMLFormControlsCollection(ContainerNode& ownerNode)
     42     : HTMLCollection(ownerNode, FormControls, OverridesItemAfter)
     43     , m_cachedElement(nullptr)
     44     , m_cachedElementOffsetInArray(0)
     45 {
     46     ASSERT(isHTMLFormElement(ownerNode) || isHTMLFieldSetElement(ownerNode));
     47 }
     48 
     49 PassRefPtrWillBeRawPtr<HTMLFormControlsCollection> HTMLFormControlsCollection::create(ContainerNode& ownerNode, CollectionType type)
     50 {
     51     ASSERT_UNUSED(type, type == FormControls);
     52     return adoptRefWillBeNoop(new HTMLFormControlsCollection(ownerNode));
     53 }
     54 
     55 HTMLFormControlsCollection::~HTMLFormControlsCollection()
     56 {
     57 }
     58 
     59 const FormAssociatedElement::List& HTMLFormControlsCollection::formControlElements() const
     60 {
     61     ASSERT(isHTMLFormElement(ownerNode()) || isHTMLFieldSetElement(ownerNode()));
     62     if (isHTMLFormElement(ownerNode()))
     63         return toHTMLFormElement(ownerNode()).associatedElements();
     64     return toHTMLFieldSetElement(ownerNode()).associatedElements();
     65 }
     66 
     67 const WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> >& HTMLFormControlsCollection::formImageElements() const
     68 {
     69     return toHTMLFormElement(ownerNode()).imageElements();
     70 }
     71 
     72 static unsigned findFormAssociatedElement(const FormAssociatedElement::List& associatedElements, Element* element)
     73 {
     74     unsigned i = 0;
     75     for (; i < associatedElements.size(); ++i) {
     76         FormAssociatedElement* associatedElement = associatedElements[i];
     77         if (associatedElement->isEnumeratable() && toHTMLElement(associatedElement) == element)
     78             break;
     79     }
     80     return i;
     81 }
     82 
     83 HTMLElement* HTMLFormControlsCollection::virtualItemAfter(Element* previous) const
     84 {
     85     const FormAssociatedElement::List& associatedElements = formControlElements();
     86     unsigned offset;
     87     if (!previous)
     88         offset = 0;
     89     else if (m_cachedElement == previous)
     90         offset = m_cachedElementOffsetInArray + 1;
     91     else
     92         offset = findFormAssociatedElement(associatedElements, previous) + 1;
     93 
     94     for (unsigned i = offset; i < associatedElements.size(); ++i) {
     95         FormAssociatedElement* associatedElement = associatedElements[i];
     96         if (associatedElement->isEnumeratable()) {
     97             m_cachedElement = toHTMLElement(associatedElement);
     98             m_cachedElementOffsetInArray = i;
     99             return m_cachedElement;
    100         }
    101     }
    102     return 0;
    103 }
    104 
    105 void HTMLFormControlsCollection::invalidateCache(Document* oldDocument) const
    106 {
    107     HTMLCollection::invalidateCache(oldDocument);
    108     m_cachedElement = nullptr;
    109     m_cachedElementOffsetInArray = 0;
    110 }
    111 
    112 static HTMLElement* firstNamedItem(const FormAssociatedElement::List& elementsArray,
    113     const WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> >* imageElementsArray, const QualifiedName& attrName, const String& name)
    114 {
    115     ASSERT(attrName == idAttr || attrName == nameAttr);
    116 
    117     for (unsigned i = 0; i < elementsArray.size(); ++i) {
    118         HTMLElement* element = toHTMLElement(elementsArray[i]);
    119         if (elementsArray[i]->isEnumeratable() && element->fastGetAttribute(attrName) == name)
    120             return element;
    121     }
    122 
    123     if (!imageElementsArray)
    124         return 0;
    125 
    126     for (unsigned i = 0; i < imageElementsArray->size(); ++i) {
    127         HTMLImageElement* element = (*imageElementsArray)[i];
    128         if (element->fastGetAttribute(attrName) == name) {
    129             UseCounter::count(element->document(), UseCounter::FormNameAccessForImageElement);
    130             return element;
    131         }
    132     }
    133 
    134     return 0;
    135 }
    136 
    137 HTMLElement* HTMLFormControlsCollection::namedItem(const AtomicString& name) const
    138 {
    139     // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp
    140     // This method first searches for an object with a matching id
    141     // attribute. If a match is not found, the method then searches for an
    142     // object with a matching name attribute, but only on those elements
    143     // that are allowed a name attribute.
    144     const WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> >* imagesElements = isHTMLFieldSetElement(ownerNode()) ? 0 : &formImageElements();
    145     if (HTMLElement* item = firstNamedItem(formControlElements(), imagesElements, idAttr, name))
    146         return item;
    147 
    148     return firstNamedItem(formControlElements(), imagesElements, nameAttr, name);
    149 }
    150 
    151 void HTMLFormControlsCollection::updateIdNameCache() const
    152 {
    153     if (hasValidIdNameCache())
    154         return;
    155 
    156     OwnPtrWillBeRawPtr<NamedItemCache> cache = NamedItemCache::create();
    157     HashSet<StringImpl*> foundInputElements;
    158 
    159     const FormAssociatedElement::List& elementsArray = formControlElements();
    160 
    161     for (unsigned i = 0; i < elementsArray.size(); ++i) {
    162         FormAssociatedElement* associatedElement = elementsArray[i];
    163         if (associatedElement->isEnumeratable()) {
    164             HTMLElement* element = toHTMLElement(associatedElement);
    165             const AtomicString& idAttrVal = element->getIdAttribute();
    166             const AtomicString& nameAttrVal = element->getNameAttribute();
    167             if (!idAttrVal.isEmpty()) {
    168                 cache->addElementWithId(idAttrVal, element);
    169                 foundInputElements.add(idAttrVal.impl());
    170             }
    171             if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal) {
    172                 cache->addElementWithName(nameAttrVal, element);
    173                 foundInputElements.add(nameAttrVal.impl());
    174             }
    175         }
    176     }
    177 
    178     if (isHTMLFormElement(ownerNode())) {
    179         const WillBeHeapVector<RawPtrWillBeMember<HTMLImageElement> >& imageElementsArray = formImageElements();
    180         for (unsigned i = 0; i < imageElementsArray.size(); ++i) {
    181             HTMLImageElement* element = imageElementsArray[i];
    182             const AtomicString& idAttrVal = element->getIdAttribute();
    183             const AtomicString& nameAttrVal = element->getNameAttribute();
    184             if (!idAttrVal.isEmpty() && !foundInputElements.contains(idAttrVal.impl()))
    185                 cache->addElementWithId(idAttrVal, element);
    186             if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && !foundInputElements.contains(nameAttrVal.impl()))
    187                 cache->addElementWithName(nameAttrVal, element);
    188         }
    189     }
    190 
    191     // Set the named item cache last as traversing the tree may cause cache invalidation.
    192     setNamedItemCache(cache.release());
    193 }
    194 
    195 void HTMLFormControlsCollection::namedGetter(const AtomicString& name, RefPtrWillBeRawPtr<RadioNodeList>& radioNodeList, RefPtrWillBeRawPtr<Element>& element)
    196 {
    197     WillBeHeapVector<RefPtrWillBeMember<Element> > namedItems;
    198     this->namedItems(name, namedItems);
    199 
    200     if (namedItems.isEmpty())
    201         return;
    202 
    203     if (namedItems.size() == 1) {
    204         element = namedItems.at(0);
    205         return;
    206     }
    207 
    208     radioNodeList = ownerNode().radioNodeList(name);
    209 }
    210 
    211 void HTMLFormControlsCollection::supportedPropertyNames(Vector<String>& names)
    212 {
    213     // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#htmlformcontrolscollection-0:
    214     // The supported property names consist of the non-empty values of all the id and name attributes
    215     // of all the elements represented by the collection, in tree order, ignoring later duplicates,
    216     // with the id of an element preceding its name if it contributes both, they differ from each
    217     // other, and neither is the duplicate of an earlier entry.
    218     HashSet<AtomicString> existingNames;
    219     unsigned length = this->length();
    220     for (unsigned i = 0; i < length; ++i) {
    221         HTMLElement* element = item(i);
    222         ASSERT(element);
    223         const AtomicString& idAttribute = element->getIdAttribute();
    224         if (!idAttribute.isEmpty()) {
    225             HashSet<AtomicString>::AddResult addResult = existingNames.add(idAttribute);
    226             if (addResult.isNewEntry)
    227                 names.append(idAttribute);
    228         }
    229         const AtomicString& nameAttribute = element->getNameAttribute();
    230         if (!nameAttribute.isEmpty()) {
    231             HashSet<AtomicString>::AddResult addResult = existingNames.add(nameAttribute);
    232             if (addResult.isNewEntry)
    233                 names.append(nameAttribute);
    234         }
    235     }
    236 }
    237 
    238 void HTMLFormControlsCollection::trace(Visitor* visitor)
    239 {
    240     visitor->trace(m_cachedElement);
    241     HTMLCollection::trace(visitor);
    242 }
    243 
    244 }
    245