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  *           (C) 2001 Dirk Mueller (mueller (at) kde.org)
      5  * Copyright (C) 2004, 2005, 2006, 2007, 2009 Apple Inc. All rights reserved.
      6  *           (C) 2006 Alexey Proskuryakov (ap (at) nypop.com)
      7  * Copyright (C) 2010 Google Inc. All rights reserved.
      8  *
      9  * This library is free software; you can redistribute it and/or
     10  * modify it under the terms of the GNU Library General Public
     11  * License as published by the Free Software Foundation; either
     12  * version 2 of the License, or (at your option) any later version.
     13  *
     14  * This library is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     17  * Library General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU Library General Public License
     20  * along with this library; see the file COPYING.LIB.  If not, write to
     21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     22  * Boston, MA 02110-1301, USA.
     23  *
     24  */
     25 
     26 #include "config.h"
     27 #include "HTMLSelectElement.h"
     28 
     29 #include "AXObjectCache.h"
     30 #include "EventNames.h"
     31 #include "HTMLNames.h"
     32 #include "HTMLOptionElement.h"
     33 #include "HTMLOptionsCollection.h"
     34 #include "MappedAttribute.h"
     35 #include "RenderListBox.h"
     36 #include "RenderMenuList.h"
     37 #include "ScriptEventListener.h"
     38 
     39 using namespace std;
     40 
     41 namespace WebCore {
     42 
     43 using namespace HTMLNames;
     44 
     45 // Upper limit agreed upon with representatives of Opera and Mozilla.
     46 static const unsigned maxSelectItems = 10000;
     47 
     48 HTMLSelectElement::HTMLSelectElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form)
     49     : HTMLFormControlElementWithState(tagName, document, form)
     50 {
     51     ASSERT(hasTagName(selectTag) || hasTagName(keygenTag));
     52 }
     53 
     54 bool HTMLSelectElement::checkDTD(const Node* newChild)
     55 {
     56     // Make sure to keep <optgroup> in sync with this.
     57     return newChild->isTextNode() || newChild->hasTagName(optionTag) || newChild->hasTagName(optgroupTag) || newChild->hasTagName(hrTag) ||
     58            newChild->hasTagName(scriptTag);
     59 }
     60 
     61 void HTMLSelectElement::recalcStyle(StyleChange change)
     62 {
     63     HTMLFormControlElementWithState::recalcStyle(change);
     64 }
     65 
     66 const AtomicString& HTMLSelectElement::formControlType() const
     67 {
     68     DEFINE_STATIC_LOCAL(const AtomicString, selectMultiple, ("select-multiple"));
     69     DEFINE_STATIC_LOCAL(const AtomicString, selectOne, ("select-one"));
     70     return m_data.multiple() ? selectMultiple : selectOne;
     71 }
     72 
     73 int HTMLSelectElement::selectedIndex() const
     74 {
     75     return SelectElement::selectedIndex(m_data, this);
     76 }
     77 
     78 void HTMLSelectElement::deselectItems(HTMLOptionElement* excludeElement)
     79 {
     80     SelectElement::deselectItems(m_data, this, excludeElement);
     81 }
     82 
     83 void HTMLSelectElement::setSelectedIndex(int optionIndex, bool deselect)
     84 {
     85     SelectElement::setSelectedIndex(m_data, this, optionIndex, deselect, false, false);
     86 }
     87 
     88 void HTMLSelectElement::setSelectedIndexByUser(int optionIndex, bool deselect, bool fireOnChangeNow)
     89 {
     90     SelectElement::setSelectedIndex(m_data, this, optionIndex, deselect, fireOnChangeNow, true);
     91 }
     92 
     93 int HTMLSelectElement::activeSelectionStartListIndex() const
     94 {
     95     if (m_data.activeSelectionAnchorIndex() >= 0)
     96         return m_data.activeSelectionAnchorIndex();
     97     return optionToListIndex(selectedIndex());
     98 }
     99 
    100 int HTMLSelectElement::activeSelectionEndListIndex() const
    101 {
    102     if (m_data.activeSelectionEndIndex() >= 0)
    103         return m_data.activeSelectionEndIndex();
    104     return SelectElement::lastSelectedListIndex(m_data, this);
    105 }
    106 
    107 unsigned HTMLSelectElement::length() const
    108 {
    109     return SelectElement::optionCount(m_data, this);
    110 }
    111 
    112 void HTMLSelectElement::add(HTMLElement *element, HTMLElement *before, ExceptionCode& ec)
    113 {
    114     RefPtr<HTMLElement> protectNewChild(element); // make sure the element is ref'd and deref'd so we don't leak it
    115 
    116     if (!element || !(element->hasLocalName(optionTag) || element->hasLocalName(hrTag)))
    117         return;
    118 
    119     insertBefore(element, before, ec);
    120 }
    121 
    122 void HTMLSelectElement::remove(int index)
    123 {
    124     int listIndex = optionToListIndex(index);
    125     if (listIndex < 0)
    126         return;
    127 
    128     Element* item = listItems()[listIndex];
    129     ASSERT(item->parentNode());
    130     ExceptionCode ec;
    131     item->parentNode()->removeChild(item, ec);
    132 }
    133 
    134 String HTMLSelectElement::value()
    135 {
    136     const Vector<Element*>& items = listItems();
    137     for (unsigned i = 0; i < items.size(); i++) {
    138         if (items[i]->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(items[i])->selected())
    139             return static_cast<HTMLOptionElement*>(items[i])->value();
    140     }
    141     return "";
    142 }
    143 
    144 void HTMLSelectElement::setValue(const String &value)
    145 {
    146     if (value.isNull())
    147         return;
    148     // find the option with value() matching the given parameter
    149     // and make it the current selection.
    150     const Vector<Element*>& items = listItems();
    151     unsigned optionIndex = 0;
    152     for (unsigned i = 0; i < items.size(); i++) {
    153         if (items[i]->hasLocalName(optionTag)) {
    154             if (static_cast<HTMLOptionElement*>(items[i])->value() == value) {
    155                 setSelectedIndex(optionIndex, true);
    156                 return;
    157             }
    158             optionIndex++;
    159         }
    160     }
    161 }
    162 
    163 bool HTMLSelectElement::saveFormControlState(String& value) const
    164 {
    165     return SelectElement::saveFormControlState(m_data, this, value);
    166 }
    167 
    168 void HTMLSelectElement::restoreFormControlState(const String& state)
    169 {
    170     SelectElement::restoreFormControlState(m_data, this, state);
    171 }
    172 
    173 void HTMLSelectElement::parseMappedAttribute(MappedAttribute* attr)
    174 {
    175     bool oldUsesMenuList = m_data.usesMenuList();
    176     if (attr->name() == sizeAttr) {
    177         int oldSize = m_data.size();
    178         // Set the attribute value to a number.
    179         // This is important since the style rules for this attribute can determine the appearance property.
    180         int size = attr->value().toInt();
    181         String attrSize = String::number(size);
    182         if (attrSize != attr->value())
    183             attr->setValue(attrSize);
    184 
    185         m_data.setSize(max(size, 1));
    186         if ((oldUsesMenuList != m_data.usesMenuList() || (!oldUsesMenuList && m_data.size() != oldSize)) && attached()) {
    187             detach();
    188             attach();
    189             setRecalcListItems();
    190         }
    191     } else if (attr->name() == multipleAttr)
    192         SelectElement::parseMultipleAttribute(m_data, this, attr);
    193     else if (attr->name() == accesskeyAttr) {
    194         // FIXME: ignore for the moment
    195     } else if (attr->name() == alignAttr) {
    196         // Don't map 'align' attribute.  This matches what Firefox, Opera and IE do.
    197         // See http://bugs.webkit.org/show_bug.cgi?id=12072
    198     } else if (attr->name() == onfocusAttr) {
    199         setAttributeEventListener(eventNames().focusEvent, createAttributeEventListener(this, attr));
    200     } else if (attr->name() == onblurAttr) {
    201         setAttributeEventListener(eventNames().blurEvent, createAttributeEventListener(this, attr));
    202     } else if (attr->name() == onchangeAttr) {
    203         setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, attr));
    204     } else
    205         HTMLFormControlElementWithState::parseMappedAttribute(attr);
    206 }
    207 
    208 bool HTMLSelectElement::isKeyboardFocusable(KeyboardEvent* event) const
    209 {
    210     if (renderer())
    211         return isFocusable();
    212     return HTMLFormControlElementWithState::isKeyboardFocusable(event);
    213 }
    214 
    215 bool HTMLSelectElement::isMouseFocusable() const
    216 {
    217     if (renderer())
    218         return isFocusable();
    219     return HTMLFormControlElementWithState::isMouseFocusable();
    220 }
    221 
    222 bool HTMLSelectElement::canSelectAll() const
    223 {
    224     return !m_data.usesMenuList();
    225 }
    226 
    227 void HTMLSelectElement::selectAll()
    228 {
    229     SelectElement::selectAll(m_data, this);
    230 }
    231 
    232 RenderObject* HTMLSelectElement::createRenderer(RenderArena* arena, RenderStyle*)
    233 {
    234     if (m_data.usesMenuList())
    235         return new (arena) RenderMenuList(this);
    236     return new (arena) RenderListBox(this);
    237 }
    238 
    239 bool HTMLSelectElement::appendFormData(FormDataList& list, bool)
    240 {
    241     return SelectElement::appendFormData(m_data, this, list);
    242 }
    243 
    244 int HTMLSelectElement::optionToListIndex(int optionIndex) const
    245 {
    246     return SelectElement::optionToListIndex(m_data, this, optionIndex);
    247 }
    248 
    249 int HTMLSelectElement::listToOptionIndex(int listIndex) const
    250 {
    251     return SelectElement::listToOptionIndex(m_data, this, listIndex);
    252 }
    253 
    254 PassRefPtr<HTMLOptionsCollection> HTMLSelectElement::options()
    255 {
    256     return HTMLOptionsCollection::create(this);
    257 }
    258 
    259 void HTMLSelectElement::recalcListItems(bool updateSelectedStates) const
    260 {
    261     SelectElement::recalcListItems(const_cast<SelectElementData&>(m_data), this, updateSelectedStates);
    262 }
    263 
    264 void HTMLSelectElement::recalcListItemsIfNeeded()
    265 {
    266     if (m_data.shouldRecalcListItems())
    267         recalcListItems();
    268 }
    269 
    270 void HTMLSelectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
    271 {
    272     setRecalcListItems();
    273     HTMLFormControlElementWithState::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
    274 
    275     if (AXObjectCache::accessibilityEnabled() && renderer())
    276         renderer()->document()->axObjectCache()->childrenChanged(renderer());
    277 }
    278 
    279 void HTMLSelectElement::setRecalcListItems()
    280 {
    281     SelectElement::setRecalcListItems(m_data, this);
    282 
    283     if (!inDocument())
    284         m_collectionInfo.reset();
    285 }
    286 
    287 void HTMLSelectElement::reset()
    288 {
    289     SelectElement::reset(m_data, this);
    290 }
    291 
    292 void HTMLSelectElement::dispatchFocusEvent()
    293 {
    294     SelectElement::dispatchFocusEvent(m_data, this);
    295     HTMLFormControlElementWithState::dispatchFocusEvent();
    296 }
    297 
    298 void HTMLSelectElement::dispatchBlurEvent()
    299 {
    300     SelectElement::dispatchBlurEvent(m_data, this);
    301     HTMLFormControlElementWithState::dispatchBlurEvent();
    302 }
    303 
    304 void HTMLSelectElement::defaultEventHandler(Event* event)
    305 {
    306     SelectElement::defaultEventHandler(m_data, this, event, form());
    307     if (event->defaultHandled())
    308         return;
    309     HTMLFormControlElementWithState::defaultEventHandler(event);
    310 }
    311 
    312 void HTMLSelectElement::setActiveSelectionAnchorIndex(int index)
    313 {
    314     SelectElement::setActiveSelectionAnchorIndex(m_data, this, index);
    315 }
    316 
    317 void HTMLSelectElement::setActiveSelectionEndIndex(int index)
    318 {
    319     SelectElement::setActiveSelectionEndIndex(m_data, index);
    320 }
    321 
    322 void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions)
    323 {
    324     SelectElement::updateListBoxSelection(m_data, this, deselectOtherOptions);
    325 }
    326 
    327 void HTMLSelectElement::menuListOnChange()
    328 {
    329     SelectElement::menuListOnChange(m_data, this);
    330 }
    331 
    332 void HTMLSelectElement::listBoxOnChange()
    333 {
    334     SelectElement::listBoxOnChange(m_data, this);
    335 }
    336 
    337 void HTMLSelectElement::saveLastSelection()
    338 {
    339     SelectElement::saveLastSelection(m_data, this);
    340 }
    341 
    342 void HTMLSelectElement::accessKeyAction(bool sendToAnyElement)
    343 {
    344     focus();
    345     dispatchSimulatedClick(0, sendToAnyElement);
    346 }
    347 
    348 void HTMLSelectElement::accessKeySetSelectedIndex(int index)
    349 {
    350     SelectElement::accessKeySetSelectedIndex(m_data, this, index);
    351 }
    352 
    353 void HTMLSelectElement::setMultiple(bool multiple)
    354 {
    355     setAttribute(multipleAttr, multiple ? "" : 0);
    356 }
    357 
    358 void HTMLSelectElement::setSize(int size)
    359 {
    360     setAttribute(sizeAttr, String::number(size));
    361 }
    362 
    363 Node* HTMLSelectElement::namedItem(const AtomicString& name)
    364 {
    365     return options()->namedItem(name);
    366 }
    367 
    368 Node* HTMLSelectElement::item(unsigned index)
    369 {
    370     return options()->item(index);
    371 }
    372 
    373 void HTMLSelectElement::setOption(unsigned index, HTMLOptionElement* option, ExceptionCode& ec)
    374 {
    375     ec = 0;
    376     if (index > maxSelectItems - 1)
    377         index = maxSelectItems - 1;
    378     int diff = index  - length();
    379     HTMLElement* before = 0;
    380     // out of array bounds ? first insert empty dummies
    381     if (diff > 0) {
    382         setLength(index, ec);
    383         // replace an existing entry ?
    384     } else if (diff < 0) {
    385         before = static_cast<HTMLElement*>(options()->item(index+1));
    386         remove(index);
    387     }
    388     // finally add the new element
    389     if (!ec) {
    390         add(option, before, ec);
    391         if (diff >= 0 && option->selected())
    392             setSelectedIndex(index, !m_data.multiple());
    393     }
    394 }
    395 
    396 void HTMLSelectElement::setLength(unsigned newLen, ExceptionCode& ec)
    397 {
    398     ec = 0;
    399     if (newLen > maxSelectItems)
    400         newLen = maxSelectItems;
    401     int diff = length() - newLen;
    402 
    403     if (diff < 0) { // add dummy elements
    404         do {
    405             RefPtr<Element> option = document()->createElement(optionTag, false);
    406             ASSERT(option);
    407             add(static_cast<HTMLElement*>(option.get()), 0, ec);
    408             if (ec)
    409                 break;
    410         } while (++diff);
    411     } else {
    412         const Vector<Element*>& items = listItems();
    413 
    414         size_t optionIndex = 0;
    415         for (size_t listIndex = 0; listIndex < items.size(); listIndex++) {
    416             if (items[listIndex]->hasLocalName(optionTag) && optionIndex++ >= newLen) {
    417                 Element *item = items[listIndex];
    418                 ASSERT(item->parentNode());
    419                 item->parentNode()->removeChild(item, ec);
    420             }
    421         }
    422     }
    423 }
    424 
    425 void HTMLSelectElement::scrollToSelection()
    426 {
    427     SelectElement::scrollToSelection(m_data, this);
    428 }
    429 
    430 void HTMLSelectElement::insertedIntoTree(bool deep)
    431 {
    432     SelectElement::insertedIntoTree(m_data, this);
    433     HTMLFormControlElementWithState::insertedIntoTree(deep);
    434 }
    435 
    436 } // namespace
    437