Home | History | Annotate | Download | only in html
      1 /*
      2  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
      3  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      4  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      5  *           (C) 2001 Dirk Mueller (mueller (at) kde.org)
      6  * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights reserved.
      7  *           (C) 2006 Alexey Proskuryakov (ap (at) nypop.com)
      8  * Copyright (C) 2010 Google Inc. All rights reserved.
      9  * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
     10  *
     11  * This library is free software; you can redistribute it and/or
     12  * modify it under the terms of the GNU Library General Public
     13  * License as published by the Free Software Foundation; either
     14  * version 2 of the License, or (at your option) any later version.
     15  *
     16  * This library is distributed in the hope that it will be useful,
     17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     19  * Library General Public License for more details.
     20  *
     21  * You should have received a copy of the GNU Library General Public License
     22  * along with this library; see the file COPYING.LIB.  If not, write to
     23  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     24  * Boston, MA 02110-1301, USA.
     25  *
     26  */
     27 
     28 #include "config.h"
     29 #include "core/html/HTMLSelectElement.h"
     30 
     31 #include "HTMLNames.h"
     32 #include "bindings/v8/ExceptionState.h"
     33 #include "bindings/v8/ExceptionStatePlaceholder.h"
     34 #include "core/accessibility/AXObjectCache.h"
     35 #include "core/dom/Attribute.h"
     36 #include "core/dom/EventNames.h"
     37 #include "core/dom/KeyboardEvent.h"
     38 #include "core/dom/MouseEvent.h"
     39 #include "core/dom/NodeRenderingContext.h"
     40 #include "core/dom/NodeTraversal.h"
     41 #include "core/html/FormController.h"
     42 #include "core/html/FormDataList.h"
     43 #include "core/html/HTMLFormElement.h"
     44 #include "core/html/HTMLOptGroupElement.h"
     45 #include "core/html/HTMLOptionElement.h"
     46 #include "core/html/HTMLOptionsCollection.h"
     47 #include "core/page/EventHandler.h"
     48 #include "core/page/Frame.h"
     49 #include "core/page/Page.h"
     50 #include "core/page/SpatialNavigation.h"
     51 #include "core/platform/LocalizedStrings.h"
     52 #include "core/platform/PlatformMouseEvent.h"
     53 #include "core/rendering/RenderListBox.h"
     54 #include "core/rendering/RenderMenuList.h"
     55 #include "core/rendering/RenderTheme.h"
     56 
     57 using namespace std;
     58 using namespace WTF::Unicode;
     59 
     60 namespace WebCore {
     61 
     62 using namespace HTMLNames;
     63 
     64 // Upper limit agreed upon with representatives of Opera and Mozilla.
     65 static const unsigned maxSelectItems = 10000;
     66 
     67 HTMLSelectElement::HTMLSelectElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form, bool createdByParser)
     68     : HTMLFormControlElementWithState(tagName, document, form)
     69     , m_typeAhead(this)
     70     , m_size(0)
     71     , m_lastOnChangeIndex(-1)
     72     , m_activeSelectionAnchorIndex(-1)
     73     , m_activeSelectionEndIndex(-1)
     74     , m_isProcessingUserDrivenChange(false)
     75     , m_multiple(false)
     76     , m_activeSelectionState(false)
     77     , m_shouldRecalcListItems(false)
     78     , m_isParsingInProgress(createdByParser)
     79 {
     80     ASSERT(hasTagName(selectTag));
     81     ScriptWrappable::init(this);
     82 }
     83 
     84 PassRefPtr<HTMLSelectElement> HTMLSelectElement::create(Document* document)
     85 {
     86     return adoptRef(new HTMLSelectElement(selectTag, document, 0, false));
     87 }
     88 
     89 PassRefPtr<HTMLSelectElement> HTMLSelectElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form, bool createdByParser)
     90 {
     91     ASSERT(tagName.matches(selectTag));
     92     return adoptRef(new HTMLSelectElement(tagName, document, form, createdByParser));
     93 }
     94 
     95 const AtomicString& HTMLSelectElement::formControlType() const
     96 {
     97     DEFINE_STATIC_LOCAL(const AtomicString, selectMultiple, ("select-multiple", AtomicString::ConstructFromLiteral));
     98     DEFINE_STATIC_LOCAL(const AtomicString, selectOne, ("select-one", AtomicString::ConstructFromLiteral));
     99     return m_multiple ? selectMultiple : selectOne;
    100 }
    101 
    102 void HTMLSelectElement::deselectItems(HTMLOptionElement* excludeElement)
    103 {
    104     deselectItemsWithoutValidation(excludeElement);
    105     setNeedsValidityCheck();
    106 }
    107 
    108 void HTMLSelectElement::optionSelectedByUser(int optionIndex, bool fireOnChangeNow, bool allowMultipleSelection)
    109 {
    110     // User interaction such as mousedown events can cause list box select elements to send change events.
    111     // This produces that same behavior for changes triggered by other code running on behalf of the user.
    112     if (!usesMenuList()) {
    113         updateSelectedState(optionToListIndex(optionIndex), allowMultipleSelection, false);
    114         setNeedsValidityCheck();
    115         if (fireOnChangeNow)
    116             listBoxOnChange();
    117         return;
    118     }
    119 
    120     // Bail out if this index is already the selected one, to avoid running unnecessary JavaScript that can mess up
    121     // autofill when there is no actual change (see https://bugs.webkit.org/show_bug.cgi?id=35256 and <rdar://7467917>).
    122     // The selectOption function does not behave this way, possibly because other callers need a change event even
    123     // in cases where the selected option is not change.
    124     if (optionIndex == selectedIndex())
    125         return;
    126 
    127     selectOption(optionIndex, DeselectOtherOptions | (fireOnChangeNow ? DispatchChangeEvent : 0) | UserDriven);
    128 }
    129 
    130 bool HTMLSelectElement::hasPlaceholderLabelOption() const
    131 {
    132     // The select element has no placeholder label option if it has an attribute "multiple" specified or a display size of non-1.
    133     //
    134     // The condition "size() > 1" is not compliant with the HTML5 spec as of Dec 3, 2010. "size() != 1" is correct.
    135     // Using "size() > 1" here because size() may be 0 in WebKit.
    136     // See the discussion at https://bugs.webkit.org/show_bug.cgi?id=43887
    137     //
    138     // "0 size()" happens when an attribute "size" is absent or an invalid size attribute is specified.
    139     // In this case, the display size should be assumed as the default.
    140     // The default display size is 1 for non-multiple select elements, and 4 for multiple select elements.
    141     //
    142     // Finally, if size() == 0 and non-multiple, the display size can be assumed as 1.
    143     if (multiple() || size() > 1)
    144         return false;
    145 
    146     int listIndex = optionToListIndex(0);
    147     ASSERT(listIndex >= 0);
    148     if (listIndex < 0)
    149         return false;
    150     return !listIndex && toHTMLOptionElement(listItems()[listIndex])->value().isEmpty();
    151 }
    152 
    153 String HTMLSelectElement::validationMessage() const
    154 {
    155     if (!willValidate())
    156         return String();
    157 
    158     if (customError())
    159         return customValidationMessage();
    160 
    161     return valueMissing() ? validationMessageValueMissingForSelectText() : String();
    162 }
    163 
    164 bool HTMLSelectElement::valueMissing() const
    165 {
    166     if (!willValidate())
    167         return false;
    168 
    169     if (!isRequired())
    170         return false;
    171 
    172     int firstSelectionIndex = selectedIndex();
    173 
    174     // If a non-placeholer label option is selected (firstSelectionIndex > 0), it's not value-missing.
    175     return firstSelectionIndex < 0 || (!firstSelectionIndex && hasPlaceholderLabelOption());
    176 }
    177 
    178 void HTMLSelectElement::listBoxSelectItem(int listIndex, bool allowMultiplySelections, bool shift, bool fireOnChangeNow)
    179 {
    180     if (!multiple())
    181         optionSelectedByUser(listToOptionIndex(listIndex), fireOnChangeNow, false);
    182     else {
    183         updateSelectedState(listIndex, allowMultiplySelections, shift);
    184         setNeedsValidityCheck();
    185         if (fireOnChangeNow)
    186             listBoxOnChange();
    187     }
    188 }
    189 
    190 bool HTMLSelectElement::usesMenuList() const
    191 {
    192     const Page* page = document()->page();
    193     RefPtr<RenderTheme> renderTheme = page ? page->theme() : RenderTheme::defaultTheme();
    194     if (renderTheme->delegatesMenuListRendering())
    195         return true;
    196 
    197     return !m_multiple && m_size <= 1;
    198 }
    199 
    200 int HTMLSelectElement::activeSelectionStartListIndex() const
    201 {
    202     if (m_activeSelectionAnchorIndex >= 0)
    203         return m_activeSelectionAnchorIndex;
    204     return optionToListIndex(selectedIndex());
    205 }
    206 
    207 int HTMLSelectElement::activeSelectionEndListIndex() const
    208 {
    209     if (m_activeSelectionEndIndex >= 0)
    210         return m_activeSelectionEndIndex;
    211     return lastSelectedListIndex();
    212 }
    213 
    214 void HTMLSelectElement::add(HTMLElement* element, HTMLElement* before, ExceptionState& es)
    215 {
    216     // Make sure the element is ref'd and deref'd so we don't leak it.
    217     RefPtr<HTMLElement> protectNewChild(element);
    218 
    219     if (!element || !(element->hasLocalName(optionTag) || element->hasLocalName(hrTag)))
    220         return;
    221 
    222     insertBefore(element, before, es, AttachLazily);
    223     setNeedsValidityCheck();
    224 }
    225 
    226 void HTMLSelectElement::remove(int optionIndex)
    227 {
    228     int listIndex = optionToListIndex(optionIndex);
    229     if (listIndex < 0)
    230         return;
    231 
    232     listItems()[listIndex]->remove(IGNORE_EXCEPTION);
    233 }
    234 
    235 void HTMLSelectElement::remove(HTMLOptionElement* option)
    236 {
    237     if (option->ownerSelectElement() != this)
    238         return;
    239 
    240     option->remove(IGNORE_EXCEPTION);
    241 }
    242 
    243 String HTMLSelectElement::value() const
    244 {
    245     const Vector<HTMLElement*>& items = listItems();
    246     for (unsigned i = 0; i < items.size(); i++) {
    247         if (items[i]->hasLocalName(optionTag) && toHTMLOptionElement(items[i])->selected())
    248             return toHTMLOptionElement(items[i])->value();
    249     }
    250     return "";
    251 }
    252 
    253 void HTMLSelectElement::setValue(const String &value)
    254 {
    255     // We clear the previously selected option(s) when needed, to guarantee calling setSelectedIndex() only once.
    256     if (value.isNull()) {
    257         setSelectedIndex(-1);
    258         return;
    259     }
    260 
    261     // Find the option with value() matching the given parameter and make it the current selection.
    262     const Vector<HTMLElement*>& items = listItems();
    263     unsigned optionIndex = 0;
    264     for (unsigned i = 0; i < items.size(); i++) {
    265         if (items[i]->hasLocalName(optionTag)) {
    266             if (toHTMLOptionElement(items[i])->value() == value) {
    267                 setSelectedIndex(optionIndex);
    268                 return;
    269             }
    270             optionIndex++;
    271         }
    272     }
    273 
    274     setSelectedIndex(-1);
    275 }
    276 
    277 bool HTMLSelectElement::isPresentationAttribute(const QualifiedName& name) const
    278 {
    279     if (name == alignAttr) {
    280         // Don't map 'align' attribute. This matches what Firefox, Opera and IE do.
    281         // See http://bugs.webkit.org/show_bug.cgi?id=12072
    282         return false;
    283     }
    284 
    285     return HTMLFormControlElementWithState::isPresentationAttribute(name);
    286 }
    287 
    288 void HTMLSelectElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
    289 {
    290     if (name == sizeAttr) {
    291         int oldSize = m_size;
    292         // Set the attribute value to a number.
    293         // This is important since the style rules for this attribute can determine the appearance property.
    294         int size = value.toInt();
    295         String attrSize = String::number(size);
    296         if (attrSize != value) {
    297             // FIXME: This is horribly factored.
    298             if (Attribute* sizeAttribute = ensureUniqueElementData()->getAttributeItem(sizeAttr))
    299                 sizeAttribute->setValue(attrSize);
    300         }
    301         size = max(size, 1);
    302 
    303         // Ensure that we've determined selectedness of the items at least once prior to changing the size.
    304         if (oldSize != size)
    305             updateListItemSelectedStates();
    306 
    307         m_size = size;
    308         setNeedsValidityCheck();
    309         if (m_size != oldSize && attached()) {
    310             lazyReattach();
    311             setRecalcListItems();
    312         }
    313     } else if (name == multipleAttr)
    314         parseMultipleAttribute(value);
    315     else if (name == accesskeyAttr) {
    316         // FIXME: ignore for the moment.
    317         //
    318     } else
    319         HTMLFormControlElementWithState::parseAttribute(name, value);
    320 }
    321 
    322 bool HTMLSelectElement::shouldShowFocusRingOnMouseFocus() const
    323 {
    324     return true;
    325 }
    326 
    327 bool HTMLSelectElement::canSelectAll() const
    328 {
    329     return !usesMenuList();
    330 }
    331 
    332 RenderObject* HTMLSelectElement::createRenderer(RenderStyle*)
    333 {
    334     if (usesMenuList())
    335         return new RenderMenuList(this);
    336     return new RenderListBox(this);
    337 }
    338 
    339 bool HTMLSelectElement::childShouldCreateRenderer(const NodeRenderingContext& childContext) const
    340 {
    341     if (!HTMLFormControlElementWithState::childShouldCreateRenderer(childContext))
    342         return false;
    343     if (!usesMenuList())
    344         return childContext.node()->hasTagName(HTMLNames::optionTag) || isHTMLOptGroupElement(childContext.node());
    345     return false;
    346 }
    347 
    348 PassRefPtr<HTMLCollection> HTMLSelectElement::selectedOptions()
    349 {
    350     updateListItemSelectedStates();
    351     return ensureCachedHTMLCollection(SelectedOptions);
    352 }
    353 
    354 PassRefPtr<HTMLOptionsCollection> HTMLSelectElement::options()
    355 {
    356     return static_cast<HTMLOptionsCollection*>(ensureCachedHTMLCollection(SelectOptions).get());
    357 }
    358 
    359 void HTMLSelectElement::updateListItemSelectedStates()
    360 {
    361     if (!m_shouldRecalcListItems)
    362         return;
    363     recalcListItems();
    364     setNeedsValidityCheck();
    365 }
    366 
    367 void HTMLSelectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
    368 {
    369     setRecalcListItems();
    370     setNeedsValidityCheck();
    371     m_lastOnChangeSelection.clear();
    372 
    373     HTMLFormControlElementWithState::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
    374 }
    375 
    376 void HTMLSelectElement::optionElementChildrenChanged()
    377 {
    378     setRecalcListItems();
    379     setNeedsValidityCheck();
    380 
    381     if (renderer()) {
    382         if (AXObjectCache* cache = renderer()->document()->existingAXObjectCache())
    383             cache->childrenChanged(this);
    384     }
    385 }
    386 
    387 void HTMLSelectElement::accessKeyAction(bool sendMouseEvents)
    388 {
    389     focus();
    390     dispatchSimulatedClick(0, sendMouseEvents ? SendMouseUpDownEvents : SendNoEvents);
    391 }
    392 
    393 void HTMLSelectElement::setMultiple(bool multiple)
    394 {
    395     bool oldMultiple = this->multiple();
    396     int oldSelectedIndex = selectedIndex();
    397     setAttribute(multipleAttr, multiple ? "" : 0);
    398 
    399     // Restore selectedIndex after changing the multiple flag to preserve
    400     // selection as single-line and multi-line has different defaults.
    401     if (oldMultiple != this->multiple())
    402         setSelectedIndex(oldSelectedIndex);
    403 }
    404 
    405 void HTMLSelectElement::setSize(int size)
    406 {
    407     setAttribute(sizeAttr, String::number(size));
    408 }
    409 
    410 Node* HTMLSelectElement::namedItem(const AtomicString& name)
    411 {
    412     return options()->namedItem(name);
    413 }
    414 
    415 Node* HTMLSelectElement::item(unsigned index)
    416 {
    417     return options()->item(index);
    418 }
    419 
    420 void HTMLSelectElement::setOption(unsigned index, HTMLOptionElement* option, ExceptionState& es)
    421 {
    422     if (index > maxSelectItems - 1)
    423         index = maxSelectItems - 1;
    424     int diff = index - length();
    425     RefPtr<HTMLElement> before = 0;
    426     // Out of array bounds? First insert empty dummies.
    427     if (diff > 0) {
    428         setLength(index, es);
    429         // Replace an existing entry?
    430     } else if (diff < 0) {
    431         before = toHTMLElement(options()->item(index+1));
    432         remove(index);
    433     }
    434     // Finally add the new element.
    435     if (!es.hadException()) {
    436         add(option, before.get(), es);
    437         if (diff >= 0 && option->selected())
    438             optionSelectionStateChanged(option, true);
    439     }
    440 }
    441 
    442 void HTMLSelectElement::setLength(unsigned newLen, ExceptionState& es)
    443 {
    444     if (newLen > maxSelectItems)
    445         newLen = maxSelectItems;
    446     int diff = length() - newLen;
    447 
    448     if (diff < 0) { // Add dummy elements.
    449         do {
    450             RefPtr<Element> option = document()->createElement(optionTag, false);
    451             ASSERT(option);
    452             add(toHTMLElement(option.get()), 0, es);
    453             if (es.hadException())
    454                 break;
    455         } while (++diff);
    456     } else {
    457         const Vector<HTMLElement*>& items = listItems();
    458 
    459         // Removing children fires mutation events, which might mutate the DOM further, so we first copy out a list
    460         // of elements that we intend to remove then attempt to remove them one at a time.
    461         Vector<RefPtr<Element> > itemsToRemove;
    462         size_t optionIndex = 0;
    463         for (size_t i = 0; i < items.size(); ++i) {
    464             Element* item = items[i];
    465             if (item->hasLocalName(optionTag) && optionIndex++ >= newLen) {
    466                 ASSERT(item->parentNode());
    467                 itemsToRemove.append(item);
    468             }
    469         }
    470 
    471         for (size_t i = 0; i < itemsToRemove.size(); ++i) {
    472             Element* item = itemsToRemove[i].get();
    473             if (item->parentNode())
    474                 item->parentNode()->removeChild(item, es);
    475         }
    476     }
    477     setNeedsValidityCheck();
    478 }
    479 
    480 bool HTMLSelectElement::isRequiredFormControl() const
    481 {
    482     return isRequired();
    483 }
    484 
    485 // Returns the 1st valid item |skip| items from |listIndex| in direction |direction| if there is one.
    486 // Otherwise, it returns the valid item closest to that boundary which is past |listIndex| if there is one.
    487 // Otherwise, it returns |listIndex|.
    488 // Valid means that it is enabled and an option element.
    489 int HTMLSelectElement::nextValidIndex(int listIndex, SkipDirection direction, int skip) const
    490 {
    491     ASSERT(direction == -1 || direction == 1);
    492     const Vector<HTMLElement*>& listItems = this->listItems();
    493     int lastGoodIndex = listIndex;
    494     int size = listItems.size();
    495     for (listIndex += direction; listIndex >= 0 && listIndex < size; listIndex += direction) {
    496         --skip;
    497         if (!listItems[listIndex]->isDisabledFormControl() && listItems[listIndex]->hasTagName(optionTag)) {
    498             lastGoodIndex = listIndex;
    499             if (skip <= 0)
    500                 break;
    501         }
    502     }
    503     return lastGoodIndex;
    504 }
    505 
    506 int HTMLSelectElement::nextSelectableListIndex(int startIndex) const
    507 {
    508     return nextValidIndex(startIndex, SkipForwards, 1);
    509 }
    510 
    511 int HTMLSelectElement::previousSelectableListIndex(int startIndex) const
    512 {
    513     if (startIndex == -1)
    514         startIndex = listItems().size();
    515     return nextValidIndex(startIndex, SkipBackwards, 1);
    516 }
    517 
    518 int HTMLSelectElement::firstSelectableListIndex() const
    519 {
    520     const Vector<HTMLElement*>& items = listItems();
    521     int index = nextValidIndex(items.size(), SkipBackwards, INT_MAX);
    522     if (static_cast<size_t>(index) == items.size())
    523         return -1;
    524     return index;
    525 }
    526 
    527 int HTMLSelectElement::lastSelectableListIndex() const
    528 {
    529     return nextValidIndex(-1, SkipForwards, INT_MAX);
    530 }
    531 
    532 // Returns the index of the next valid item one page away from |startIndex| in direction |direction|.
    533 int HTMLSelectElement::nextSelectableListIndexPageAway(int startIndex, SkipDirection direction) const
    534 {
    535     const Vector<HTMLElement*>& items = listItems();
    536     // Can't use m_size because renderer forces a minimum size.
    537     int pageSize = 0;
    538     if (renderer()->isListBox())
    539         pageSize = toRenderListBox(renderer())->size() - 1; // -1 so we still show context.
    540 
    541     // One page away, but not outside valid bounds.
    542     // If there is a valid option item one page away, the index is chosen.
    543     // If there is no exact one page away valid option, returns startIndex or the most far index.
    544     int edgeIndex = (direction == SkipForwards) ? 0 : (items.size() - 1);
    545     int skipAmount = pageSize + ((direction == SkipForwards) ? startIndex : (edgeIndex - startIndex));
    546     return nextValidIndex(edgeIndex, direction, skipAmount);
    547 }
    548 
    549 void HTMLSelectElement::selectAll()
    550 {
    551     ASSERT(!usesMenuList());
    552     if (!renderer() || !m_multiple)
    553         return;
    554 
    555     // Save the selection so it can be compared to the new selectAll selection
    556     // when dispatching change events.
    557     saveLastSelection();
    558 
    559     m_activeSelectionState = true;
    560     setActiveSelectionAnchorIndex(nextSelectableListIndex(-1));
    561     setActiveSelectionEndIndex(previousSelectableListIndex(-1));
    562 
    563     updateListBoxSelection(false);
    564     listBoxOnChange();
    565     setNeedsValidityCheck();
    566 }
    567 
    568 void HTMLSelectElement::saveLastSelection()
    569 {
    570     if (usesMenuList()) {
    571         m_lastOnChangeIndex = selectedIndex();
    572         return;
    573     }
    574 
    575     m_lastOnChangeSelection.clear();
    576     const Vector<HTMLElement*>& items = listItems();
    577     for (unsigned i = 0; i < items.size(); ++i) {
    578         HTMLElement* element = items[i];
    579         m_lastOnChangeSelection.append(element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected());
    580     }
    581 }
    582 
    583 void HTMLSelectElement::setActiveSelectionAnchorIndex(int index)
    584 {
    585     m_activeSelectionAnchorIndex = index;
    586 
    587     // Cache the selection state so we can restore the old selection as the new
    588     // selection pivots around this anchor index.
    589     m_cachedStateForActiveSelection.clear();
    590 
    591     const Vector<HTMLElement*>& items = listItems();
    592     for (unsigned i = 0; i < items.size(); ++i) {
    593         HTMLElement* element = items[i];
    594         m_cachedStateForActiveSelection.append(element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected());
    595     }
    596 }
    597 
    598 void HTMLSelectElement::setActiveSelectionEndIndex(int index)
    599 {
    600     m_activeSelectionEndIndex = index;
    601 }
    602 
    603 void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions)
    604 {
    605     ASSERT(renderer() && (renderer()->isListBox() || m_multiple));
    606     ASSERT(!listItems().size() || m_activeSelectionAnchorIndex >= 0);
    607 
    608     unsigned start = min(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex);
    609     unsigned end = max(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex);
    610 
    611     const Vector<HTMLElement*>& items = listItems();
    612     for (unsigned i = 0; i < items.size(); ++i) {
    613         HTMLElement* element = items[i];
    614         if (!element->hasTagName(optionTag) || toHTMLOptionElement(element)->isDisabledFormControl())
    615             continue;
    616 
    617         if (i >= start && i <= end)
    618             toHTMLOptionElement(element)->setSelectedState(m_activeSelectionState);
    619         else if (deselectOtherOptions || i >= m_cachedStateForActiveSelection.size())
    620             toHTMLOptionElement(element)->setSelectedState(false);
    621         else
    622             toHTMLOptionElement(element)->setSelectedState(m_cachedStateForActiveSelection[i]);
    623     }
    624 
    625     scrollToSelection();
    626     setNeedsValidityCheck();
    627     notifyFormStateChanged();
    628 }
    629 
    630 void HTMLSelectElement::listBoxOnChange()
    631 {
    632     ASSERT(!usesMenuList() || m_multiple);
    633 
    634     const Vector<HTMLElement*>& items = listItems();
    635 
    636     // If the cached selection list is empty, or the size has changed, then fire
    637     // dispatchFormControlChangeEvent, and return early.
    638     // FIXME: Why? This looks unreasonable.
    639     if (m_lastOnChangeSelection.isEmpty() || m_lastOnChangeSelection.size() != items.size()) {
    640         dispatchFormControlChangeEvent();
    641         return;
    642     }
    643 
    644     // Update m_lastOnChangeSelection and fire dispatchFormControlChangeEvent.
    645     bool fireOnChange = false;
    646     for (unsigned i = 0; i < items.size(); ++i) {
    647         HTMLElement* element = items[i];
    648         bool selected = element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected();
    649         if (selected != m_lastOnChangeSelection[i])
    650             fireOnChange = true;
    651         m_lastOnChangeSelection[i] = selected;
    652     }
    653 
    654     if (fireOnChange)
    655         dispatchFormControlChangeEvent();
    656 }
    657 
    658 void HTMLSelectElement::dispatchChangeEventForMenuList()
    659 {
    660     ASSERT(usesMenuList());
    661 
    662     int selected = selectedIndex();
    663     if (m_lastOnChangeIndex != selected && m_isProcessingUserDrivenChange) {
    664         m_lastOnChangeIndex = selected;
    665         m_isProcessingUserDrivenChange = false;
    666         dispatchFormControlChangeEvent();
    667     }
    668 }
    669 
    670 void HTMLSelectElement::scrollToSelection()
    671 {
    672     if (usesMenuList())
    673         return;
    674 
    675     if (RenderObject* renderer = this->renderer())
    676         toRenderListBox(renderer)->selectionChanged();
    677 }
    678 
    679 void HTMLSelectElement::setOptionsChangedOnRenderer()
    680 {
    681     if (RenderObject* renderer = this->renderer()) {
    682         if (usesMenuList())
    683             toRenderMenuList(renderer)->setOptionsChanged(true);
    684         else
    685             toRenderListBox(renderer)->setOptionsChanged(true);
    686     }
    687 }
    688 
    689 const Vector<HTMLElement*>& HTMLSelectElement::listItems() const
    690 {
    691     if (m_shouldRecalcListItems)
    692         recalcListItems();
    693     else {
    694 #if !ASSERT_DISABLED
    695         Vector<HTMLElement*> items = m_listItems;
    696         recalcListItems(false);
    697         ASSERT(items == m_listItems);
    698 #endif
    699     }
    700 
    701     return m_listItems;
    702 }
    703 
    704 void HTMLSelectElement::invalidateSelectedItems()
    705 {
    706     if (HTMLCollection* collection = cachedHTMLCollection(SelectedOptions))
    707         collection->invalidateCache();
    708 }
    709 
    710 void HTMLSelectElement::setRecalcListItems()
    711 {
    712     m_shouldRecalcListItems = true;
    713     // Manual selection anchor is reset when manipulating the select programmatically.
    714     m_activeSelectionAnchorIndex = -1;
    715     setOptionsChangedOnRenderer();
    716     setNeedsStyleRecalc();
    717     if (!inDocument()) {
    718         if (HTMLCollection* collection = cachedHTMLCollection(SelectOptions))
    719             collection->invalidateCache();
    720     }
    721     if (!inDocument())
    722         invalidateSelectedItems();
    723 
    724     if (renderer()) {
    725         if (AXObjectCache* cache = renderer()->document()->existingAXObjectCache())
    726             cache->childrenChanged(this);
    727     }
    728 }
    729 
    730 void HTMLSelectElement::recalcListItems(bool updateSelectedStates) const
    731 {
    732     m_listItems.clear();
    733 
    734     m_shouldRecalcListItems = false;
    735 
    736     HTMLOptionElement* foundSelected = 0;
    737     HTMLOptionElement* firstOption = 0;
    738     for (Element* currentElement = ElementTraversal::firstWithin(this); currentElement; ) {
    739         if (!currentElement->isHTMLElement()) {
    740             currentElement = ElementTraversal::nextSkippingChildren(currentElement, this);
    741             continue;
    742         }
    743         HTMLElement* current = toHTMLElement(currentElement);
    744 
    745         // optgroup tags may not nest. However, both FireFox and IE will
    746         // flatten the tree automatically, so we follow suit.
    747         // (http://www.w3.org/TR/html401/interact/forms.html#h-17.6)
    748         if (isHTMLOptGroupElement(current)) {
    749             m_listItems.append(current);
    750             if (Element* nextElement = ElementTraversal::firstWithin(current)) {
    751                 currentElement = nextElement;
    752                 continue;
    753             }
    754         }
    755 
    756         if (current->hasTagName(optionTag)) {
    757             m_listItems.append(current);
    758 
    759             if (updateSelectedStates && !m_multiple) {
    760                 HTMLOptionElement* option = toHTMLOptionElement(current);
    761                 if (!firstOption)
    762                     firstOption = option;
    763                 if (option->selected()) {
    764                     if (foundSelected)
    765                         foundSelected->setSelectedState(false);
    766                     foundSelected = option;
    767                 } else if (m_size <= 1 && !foundSelected && !option->isDisabledFormControl()) {
    768                     foundSelected = option;
    769                     foundSelected->setSelectedState(true);
    770                 }
    771             }
    772         }
    773 
    774         if (current->hasTagName(hrTag))
    775             m_listItems.append(current);
    776 
    777         // In conforming HTML code, only <optgroup> and <option> will be found
    778         // within a <select>. We call NodeTraversal::nextSkippingChildren so that we only step
    779         // into those tags that we choose to. For web-compat, we should cope
    780         // with the case where odd tags like a <div> have been added but we
    781         // handle this because such tags have already been removed from the
    782         // <select>'s subtree at this point.
    783         currentElement = ElementTraversal::nextSkippingChildren(currentElement, this);
    784     }
    785 
    786     if (!foundSelected && m_size <= 1 && firstOption && !firstOption->selected())
    787         firstOption->setSelectedState(true);
    788 }
    789 
    790 int HTMLSelectElement::selectedIndex() const
    791 {
    792     unsigned index = 0;
    793 
    794     // Return the number of the first option selected.
    795     const Vector<HTMLElement*>& items = listItems();
    796     for (size_t i = 0; i < items.size(); ++i) {
    797         HTMLElement* element = items[i];
    798         if (element->hasTagName(optionTag)) {
    799             if (toHTMLOptionElement(element)->selected())
    800                 return index;
    801             ++index;
    802         }
    803     }
    804 
    805     return -1;
    806 }
    807 
    808 void HTMLSelectElement::setSelectedIndex(int index)
    809 {
    810     selectOption(index, DeselectOtherOptions);
    811 }
    812 
    813 void HTMLSelectElement::optionSelectionStateChanged(HTMLOptionElement* option, bool optionIsSelected)
    814 {
    815     ASSERT(option->ownerSelectElement() == this);
    816     if (optionIsSelected)
    817         selectOption(option->index());
    818     else if (!usesMenuList())
    819         selectOption(-1);
    820     else
    821         selectOption(nextSelectableListIndex(-1));
    822 }
    823 
    824 void HTMLSelectElement::selectOption(int optionIndex, SelectOptionFlags flags)
    825 {
    826     bool shouldDeselect = !m_multiple || (flags & DeselectOtherOptions);
    827 
    828     const Vector<HTMLElement*>& items = listItems();
    829     int listIndex = optionToListIndex(optionIndex);
    830 
    831     HTMLElement* element = 0;
    832     if (listIndex >= 0) {
    833         element = items[listIndex];
    834         if (element->hasTagName(optionTag)) {
    835             if (m_activeSelectionAnchorIndex < 0 || shouldDeselect)
    836                 setActiveSelectionAnchorIndex(listIndex);
    837             if (m_activeSelectionEndIndex < 0 || shouldDeselect)
    838                 setActiveSelectionEndIndex(listIndex);
    839             toHTMLOptionElement(element)->setSelectedState(true);
    840         }
    841     }
    842 
    843     if (shouldDeselect)
    844         deselectItemsWithoutValidation(element);
    845 
    846     // For the menu list case, this is what makes the selected element appear.
    847     if (RenderObject* renderer = this->renderer())
    848         renderer->updateFromElement();
    849 
    850     scrollToSelection();
    851 
    852     if (usesMenuList()) {
    853         m_isProcessingUserDrivenChange = flags & UserDriven;
    854         if (flags & DispatchChangeEvent)
    855             dispatchChangeEventForMenuList();
    856         if (RenderObject* renderer = this->renderer()) {
    857             if (usesMenuList())
    858                 toRenderMenuList(renderer)->didSetSelectedIndex(listIndex);
    859             else if (renderer->isListBox())
    860                 toRenderListBox(renderer)->selectionChanged();
    861         }
    862     }
    863 
    864     setNeedsValidityCheck();
    865     notifyFormStateChanged();
    866 }
    867 
    868 int HTMLSelectElement::optionToListIndex(int optionIndex) const
    869 {
    870     const Vector<HTMLElement*>& items = listItems();
    871     int listSize = static_cast<int>(items.size());
    872     if (optionIndex < 0 || optionIndex >= listSize)
    873         return -1;
    874 
    875     int optionIndex2 = -1;
    876     for (int listIndex = 0; listIndex < listSize; ++listIndex) {
    877         if (items[listIndex]->hasTagName(optionTag)) {
    878             ++optionIndex2;
    879             if (optionIndex2 == optionIndex)
    880                 return listIndex;
    881         }
    882     }
    883 
    884     return -1;
    885 }
    886 
    887 int HTMLSelectElement::listToOptionIndex(int listIndex) const
    888 {
    889     const Vector<HTMLElement*>& items = listItems();
    890     if (listIndex < 0 || listIndex >= static_cast<int>(items.size()) || !items[listIndex]->hasTagName(optionTag))
    891         return -1;
    892 
    893     // Actual index of option not counting OPTGROUP entries that may be in list.
    894     int optionIndex = 0;
    895     for (int i = 0; i < listIndex; ++i) {
    896         if (items[i]->hasTagName(optionTag))
    897             ++optionIndex;
    898     }
    899 
    900     return optionIndex;
    901 }
    902 
    903 void HTMLSelectElement::dispatchFocusEvent(Element* oldFocusedElement, FocusDirection direction)
    904 {
    905     // Save the selection so it can be compared to the new selection when
    906     // dispatching change events during blur event dispatch.
    907     if (usesMenuList())
    908         saveLastSelection();
    909     HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedElement, direction);
    910 }
    911 
    912 void HTMLSelectElement::dispatchBlurEvent(Element* newFocusedElement)
    913 {
    914     // We only need to fire change events here for menu lists, because we fire
    915     // change events for list boxes whenever the selection change is actually made.
    916     // This matches other browsers' behavior.
    917     if (usesMenuList())
    918         dispatchChangeEventForMenuList();
    919     HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedElement);
    920 }
    921 
    922 void HTMLSelectElement::deselectItemsWithoutValidation(HTMLElement* excludeElement)
    923 {
    924     const Vector<HTMLElement*>& items = listItems();
    925     for (unsigned i = 0; i < items.size(); ++i) {
    926         HTMLElement* element = items[i];
    927         if (element != excludeElement && element->hasTagName(optionTag))
    928             toHTMLOptionElement(element)->setSelectedState(false);
    929     }
    930 }
    931 
    932 FormControlState HTMLSelectElement::saveFormControlState() const
    933 {
    934     const Vector<HTMLElement*>& items = listItems();
    935     size_t length = items.size();
    936     FormControlState state;
    937     for (unsigned i = 0; i < length; ++i) {
    938         if (!items[i]->hasTagName(optionTag))
    939             continue;
    940         HTMLOptionElement* option = toHTMLOptionElement(items[i]);
    941         if (!option->selected())
    942             continue;
    943         state.append(option->value());
    944         if (!multiple())
    945             break;
    946     }
    947     return state;
    948 }
    949 
    950 size_t HTMLSelectElement::searchOptionsForValue(const String& value, size_t listIndexStart, size_t listIndexEnd) const
    951 {
    952     const Vector<HTMLElement*>& items = listItems();
    953     size_t loopEndIndex = std::min(items.size(), listIndexEnd);
    954     for (size_t i = listIndexStart; i < loopEndIndex; ++i) {
    955         if (!items[i]->hasLocalName(optionTag))
    956             continue;
    957         if (toHTMLOptionElement(items[i])->value() == value)
    958             return i;
    959     }
    960     return notFound;
    961 }
    962 
    963 void HTMLSelectElement::restoreFormControlState(const FormControlState& state)
    964 {
    965     recalcListItems();
    966 
    967     const Vector<HTMLElement*>& items = listItems();
    968     size_t itemsSize = items.size();
    969     if (!itemsSize)
    970         return;
    971 
    972     for (size_t i = 0; i < itemsSize; ++i) {
    973         if (!items[i]->hasLocalName(optionTag))
    974             continue;
    975         toHTMLOptionElement(items[i])->setSelectedState(false);
    976     }
    977 
    978     if (!multiple()) {
    979         size_t foundIndex = searchOptionsForValue(state[0], 0, itemsSize);
    980         if (foundIndex != notFound)
    981             toHTMLOptionElement(items[foundIndex])->setSelectedState(true);
    982     } else {
    983         size_t startIndex = 0;
    984         for (size_t i = 0; i < state.valueSize(); ++i) {
    985             const String& value = state[i];
    986             size_t foundIndex = searchOptionsForValue(value, startIndex, itemsSize);
    987             if (foundIndex == notFound)
    988                 foundIndex = searchOptionsForValue(value, 0, startIndex);
    989             if (foundIndex == notFound)
    990                 continue;
    991             toHTMLOptionElement(items[foundIndex])->setSelectedState(true);
    992             startIndex = foundIndex + 1;
    993         }
    994     }
    995 
    996     setOptionsChangedOnRenderer();
    997     setNeedsValidityCheck();
    998 }
    999 
   1000 void HTMLSelectElement::parseMultipleAttribute(const AtomicString& value)
   1001 {
   1002     bool oldUsesMenuList = usesMenuList();
   1003     m_multiple = !value.isNull();
   1004     setNeedsValidityCheck();
   1005     if (oldUsesMenuList != usesMenuList())
   1006         lazyReattachIfAttached();
   1007 }
   1008 
   1009 bool HTMLSelectElement::appendFormData(FormDataList& list, bool)
   1010 {
   1011     const AtomicString& name = this->name();
   1012     if (name.isEmpty())
   1013         return false;
   1014 
   1015     bool successful = false;
   1016     const Vector<HTMLElement*>& items = listItems();
   1017 
   1018     for (unsigned i = 0; i < items.size(); ++i) {
   1019         HTMLElement* element = items[i];
   1020         if (element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected() && !toHTMLOptionElement(element)->isDisabledFormControl()) {
   1021             list.appendData(name, toHTMLOptionElement(element)->value());
   1022             successful = true;
   1023         }
   1024     }
   1025 
   1026     // It's possible that this is a menulist with multiple options and nothing
   1027     // will be submitted (!successful). We won't send a unselected non-disabled
   1028     // option as fallback. This behavior matches to other browsers.
   1029     return successful;
   1030 }
   1031 
   1032 void HTMLSelectElement::reset()
   1033 {
   1034     HTMLOptionElement* firstOption = 0;
   1035     HTMLOptionElement* selectedOption = 0;
   1036 
   1037     const Vector<HTMLElement*>& items = listItems();
   1038     for (unsigned i = 0; i < items.size(); ++i) {
   1039         HTMLElement* element = items[i];
   1040         if (!element->hasTagName(optionTag))
   1041             continue;
   1042 
   1043         if (items[i]->fastHasAttribute(selectedAttr)) {
   1044             if (selectedOption && !m_multiple)
   1045                 selectedOption->setSelectedState(false);
   1046             toHTMLOptionElement(element)->setSelectedState(true);
   1047             selectedOption = toHTMLOptionElement(element);
   1048         } else
   1049             toHTMLOptionElement(element)->setSelectedState(false);
   1050 
   1051         if (!firstOption)
   1052             firstOption = toHTMLOptionElement(element);
   1053     }
   1054 
   1055     if (!selectedOption && firstOption && !m_multiple && m_size <= 1)
   1056         firstOption->setSelectedState(true);
   1057 
   1058     setOptionsChangedOnRenderer();
   1059     setNeedsStyleRecalc();
   1060     setNeedsValidityCheck();
   1061 }
   1062 
   1063 #if !OS(WINDOWS)
   1064 bool HTMLSelectElement::platformHandleKeydownEvent(KeyboardEvent* event)
   1065 {
   1066     const Page* page = document()->page();
   1067     RefPtr<RenderTheme> renderTheme = page ? page->theme() : RenderTheme::defaultTheme();
   1068 
   1069     if (!renderTheme->popsMenuByArrowKeys())
   1070         return false;
   1071 
   1072     if (!isSpatialNavigationEnabled(document()->frame())) {
   1073         if (event->keyIdentifier() == "Down" || event->keyIdentifier() == "Up") {
   1074             focus();
   1075             // Calling focus() may cause us to lose our renderer. Return true so
   1076             // that our caller doesn't process the event further, but don't set
   1077             // the event as handled.
   1078             if (!renderer())
   1079                 return true;
   1080 
   1081             // Save the selection so it can be compared to the new selection
   1082             // when dispatching change events during selectOption, which
   1083             // gets called from RenderMenuList::valueChanged, which gets called
   1084             // after the user makes a selection from the menu.
   1085             saveLastSelection();
   1086             if (RenderMenuList* menuList = toRenderMenuList(renderer()))
   1087                 menuList->showPopup();
   1088             event->setDefaultHandled();
   1089         }
   1090         return true;
   1091     }
   1092 
   1093     return false;
   1094 }
   1095 #endif
   1096 
   1097 void HTMLSelectElement::menuListDefaultEventHandler(Event* event)
   1098 {
   1099     const Page* page = document()->page();
   1100     RefPtr<RenderTheme> renderTheme = page ? page->theme() : RenderTheme::defaultTheme();
   1101 
   1102     if (event->type() == eventNames().keydownEvent) {
   1103         if (!renderer() || !event->isKeyboardEvent())
   1104             return;
   1105 
   1106         if (platformHandleKeydownEvent(toKeyboardEvent(event)))
   1107             return;
   1108 
   1109         // When using spatial navigation, we want to be able to navigate away
   1110         // from the select element when the user hits any of the arrow keys,
   1111         // instead of changing the selection.
   1112         if (isSpatialNavigationEnabled(document()->frame())) {
   1113             if (!m_activeSelectionState)
   1114                 return;
   1115         }
   1116 
   1117         const String& keyIdentifier = toKeyboardEvent(event)->keyIdentifier();
   1118         bool handled = true;
   1119         const Vector<HTMLElement*>& listItems = this->listItems();
   1120         int listIndex = optionToListIndex(selectedIndex());
   1121 
   1122         if (keyIdentifier == "Down" || keyIdentifier == "Right")
   1123             listIndex = nextValidIndex(listIndex, SkipForwards, 1);
   1124         else if (keyIdentifier == "Up" || keyIdentifier == "Left")
   1125             listIndex = nextValidIndex(listIndex, SkipBackwards, 1);
   1126         else if (keyIdentifier == "PageDown")
   1127             listIndex = nextValidIndex(listIndex, SkipForwards, 3);
   1128         else if (keyIdentifier == "PageUp")
   1129             listIndex = nextValidIndex(listIndex, SkipBackwards, 3);
   1130         else if (keyIdentifier == "Home")
   1131             listIndex = nextValidIndex(-1, SkipForwards, 1);
   1132         else if (keyIdentifier == "End")
   1133             listIndex = nextValidIndex(listItems.size(), SkipBackwards, 1);
   1134         else
   1135             handled = false;
   1136 
   1137         if (handled && static_cast<size_t>(listIndex) < listItems.size())
   1138             selectOption(listToOptionIndex(listIndex), DeselectOtherOptions | DispatchChangeEvent | UserDriven);
   1139 
   1140         if (handled)
   1141             event->setDefaultHandled();
   1142     }
   1143 
   1144     // Use key press event here since sending simulated mouse events
   1145     // on key down blocks the proper sending of the key press event.
   1146     if (event->type() == eventNames().keypressEvent) {
   1147         if (!renderer() || !event->isKeyboardEvent())
   1148             return;
   1149 
   1150         int keyCode = toKeyboardEvent(event)->keyCode();
   1151         bool handled = false;
   1152 
   1153         if (keyCode == ' ' && isSpatialNavigationEnabled(document()->frame())) {
   1154             // Use space to toggle arrow key handling for selection change or spatial navigation.
   1155             m_activeSelectionState = !m_activeSelectionState;
   1156             event->setDefaultHandled();
   1157             return;
   1158         }
   1159 
   1160         if (renderTheme->popsMenuBySpaceOrReturn()) {
   1161             if (keyCode == ' ' || keyCode == '\r') {
   1162                 focus();
   1163 
   1164                 // Calling focus() may remove the renderer or change the
   1165                 // renderer type.
   1166                 if (!renderer() || !renderer()->isMenuList())
   1167                     return;
   1168 
   1169                 // Save the selection so it can be compared to the new selection
   1170                 // when dispatching change events during selectOption, which
   1171                 // gets called from RenderMenuList::valueChanged, which gets called
   1172                 // after the user makes a selection from the menu.
   1173                 saveLastSelection();
   1174                 if (RenderMenuList* menuList = toRenderMenuList(renderer()))
   1175                     menuList->showPopup();
   1176                 handled = true;
   1177             }
   1178         } else if (renderTheme->popsMenuByArrowKeys()) {
   1179             if (keyCode == ' ') {
   1180                 focus();
   1181 
   1182                 // Calling focus() may remove the renderer or change the
   1183                 // renderer type.
   1184                 if (!renderer() || !renderer()->isMenuList())
   1185                     return;
   1186 
   1187                 // Save the selection so it can be compared to the new selection
   1188                 // when dispatching change events during selectOption, which
   1189                 // gets called from RenderMenuList::valueChanged, which gets called
   1190                 // after the user makes a selection from the menu.
   1191                 saveLastSelection();
   1192                 if (RenderMenuList* menuList = toRenderMenuList(renderer()))
   1193                     menuList->showPopup();
   1194                 handled = true;
   1195             } else if (keyCode == '\r') {
   1196                 if (form())
   1197                     form()->submitImplicitly(event, false);
   1198                 dispatchChangeEventForMenuList();
   1199                 handled = true;
   1200             }
   1201         }
   1202 
   1203         if (handled)
   1204             event->setDefaultHandled();
   1205     }
   1206 
   1207     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
   1208         focus();
   1209         if (renderer() && renderer()->isMenuList()) {
   1210             if (RenderMenuList* menuList = toRenderMenuList(renderer())) {
   1211                 if (menuList->popupIsVisible())
   1212                     menuList->hidePopup();
   1213                 else {
   1214                     // Save the selection so it can be compared to the new
   1215                     // selection when we call onChange during selectOption,
   1216                     // which gets called from RenderMenuList::valueChanged,
   1217                     // which gets called after the user makes a selection from
   1218                     // the menu.
   1219                     saveLastSelection();
   1220                     menuList->showPopup();
   1221                 }
   1222             }
   1223         }
   1224         event->setDefaultHandled();
   1225     }
   1226 
   1227     if (event->type() == eventNames().blurEvent) {
   1228         if (RenderMenuList* menuList = toRenderMenuList(renderer())) {
   1229             if (menuList->popupIsVisible())
   1230                 menuList->hidePopup();
   1231         }
   1232     }
   1233 }
   1234 
   1235 void HTMLSelectElement::updateSelectedState(int listIndex, bool multi, bool shift)
   1236 {
   1237     ASSERT(listIndex >= 0);
   1238 
   1239     // Save the selection so it can be compared to the new selection when
   1240     // dispatching change events during mouseup, or after autoscroll finishes.
   1241     saveLastSelection();
   1242 
   1243     m_activeSelectionState = true;
   1244 
   1245     bool shiftSelect = m_multiple && shift;
   1246     bool multiSelect = m_multiple && multi && !shift;
   1247 
   1248     HTMLElement* clickedElement = listItems()[listIndex];
   1249     if (clickedElement->hasTagName(optionTag)) {
   1250         // Keep track of whether an active selection (like during drag
   1251         // selection), should select or deselect.
   1252         if (toHTMLOptionElement(clickedElement)->selected() && multiSelect)
   1253             m_activeSelectionState = false;
   1254         if (!m_activeSelectionState)
   1255             toHTMLOptionElement(clickedElement)->setSelectedState(false);
   1256     }
   1257 
   1258     // If we're not in any special multiple selection mode, then deselect all
   1259     // other items, excluding the clicked option. If no option was clicked, then
   1260     // this will deselect all items in the list.
   1261     if (!shiftSelect && !multiSelect)
   1262         deselectItemsWithoutValidation(clickedElement);
   1263 
   1264     // If the anchor hasn't been set, and we're doing a single selection or a
   1265     // shift selection, then initialize the anchor to the first selected index.
   1266     if (m_activeSelectionAnchorIndex < 0 && !multiSelect)
   1267         setActiveSelectionAnchorIndex(selectedIndex());
   1268 
   1269     // Set the selection state of the clicked option.
   1270     if (clickedElement->hasTagName(optionTag) && !toHTMLOptionElement(clickedElement)->isDisabledFormControl())
   1271         toHTMLOptionElement(clickedElement)->setSelectedState(true);
   1272 
   1273     // If there was no selectedIndex() for the previous initialization, or If
   1274     // we're doing a single selection, or a multiple selection (using cmd or
   1275     // ctrl), then initialize the anchor index to the listIndex that just got
   1276     // clicked.
   1277     if (m_activeSelectionAnchorIndex < 0 || !shiftSelect)
   1278         setActiveSelectionAnchorIndex(listIndex);
   1279 
   1280     setActiveSelectionEndIndex(listIndex);
   1281     updateListBoxSelection(!multiSelect);
   1282 }
   1283 
   1284 void HTMLSelectElement::listBoxDefaultEventHandler(Event* event)
   1285 {
   1286     const Vector<HTMLElement*>& listItems = this->listItems();
   1287 
   1288     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
   1289         focus();
   1290         // Calling focus() may cause us to lose our renderer, in which case do not want to handle the event.
   1291         if (!renderer())
   1292             return;
   1293 
   1294         // Convert to coords relative to the list box if needed.
   1295         MouseEvent* mouseEvent = toMouseEvent(event);
   1296         IntPoint localOffset = roundedIntPoint(renderer()->absoluteToLocal(mouseEvent->absoluteLocation(), UseTransforms));
   1297         int listIndex = toRenderListBox(renderer())->listIndexAtOffset(toIntSize(localOffset));
   1298         if (listIndex >= 0) {
   1299             if (!isDisabledFormControl()) {
   1300 #if OS(DARWIN)
   1301                 updateSelectedState(listIndex, mouseEvent->metaKey(), mouseEvent->shiftKey());
   1302 #else
   1303                 updateSelectedState(listIndex, mouseEvent->ctrlKey(), mouseEvent->shiftKey());
   1304 #endif
   1305             }
   1306             if (Frame* frame = document()->frame())
   1307                 frame->eventHandler()->setMouseDownMayStartAutoscroll();
   1308 
   1309             event->setDefaultHandled();
   1310         }
   1311     } else if (event->type() == eventNames().mousemoveEvent && event->isMouseEvent() && !toRenderBox(renderer())->canBeScrolledAndHasScrollableArea()) {
   1312         MouseEvent* mouseEvent = toMouseEvent(event);
   1313         if (mouseEvent->button() != LeftButton || !mouseEvent->buttonDown())
   1314             return;
   1315 
   1316         IntPoint localOffset = roundedIntPoint(renderer()->absoluteToLocal(mouseEvent->absoluteLocation(), UseTransforms));
   1317         int listIndex = toRenderListBox(renderer())->listIndexAtOffset(toIntSize(localOffset));
   1318         if (listIndex >= 0) {
   1319             if (!isDisabledFormControl()) {
   1320                 if (m_multiple) {
   1321                     // Only extend selection if there is something selected.
   1322                     if (m_activeSelectionAnchorIndex < 0)
   1323                         return;
   1324 
   1325                     setActiveSelectionEndIndex(listIndex);
   1326                     updateListBoxSelection(false);
   1327                 } else {
   1328                     setActiveSelectionAnchorIndex(listIndex);
   1329                     setActiveSelectionEndIndex(listIndex);
   1330                     updateListBoxSelection(true);
   1331                 }
   1332             }
   1333             event->setDefaultHandled();
   1334         }
   1335     } else if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton && renderer() && !toRenderBox(renderer())->autoscrollInProgress()) {
   1336         // We didn't start this click/drag on any options.
   1337         if (m_lastOnChangeSelection.isEmpty())
   1338             return;
   1339         // This makes sure we fire dispatchFormControlChangeEvent for a single
   1340         // click. For drag selection, onChange will fire when the autoscroll
   1341         // timer stops.
   1342         listBoxOnChange();
   1343     } else if (event->type() == eventNames().keydownEvent) {
   1344         if (!event->isKeyboardEvent())
   1345             return;
   1346         const String& keyIdentifier = toKeyboardEvent(event)->keyIdentifier();
   1347 
   1348         bool handled = false;
   1349         int endIndex = 0;
   1350         if (m_activeSelectionEndIndex < 0) {
   1351             // Initialize the end index
   1352             if (keyIdentifier == "Down" || keyIdentifier == "PageDown") {
   1353                 int startIndex = lastSelectedListIndex();
   1354                 handled = true;
   1355                 if (keyIdentifier == "Down")
   1356                     endIndex = nextSelectableListIndex(startIndex);
   1357                 else
   1358                     endIndex = nextSelectableListIndexPageAway(startIndex, SkipForwards);
   1359             } else if (keyIdentifier == "Up" || keyIdentifier == "PageUp") {
   1360                 int startIndex = optionToListIndex(selectedIndex());
   1361                 handled = true;
   1362                 if (keyIdentifier == "Up")
   1363                     endIndex = previousSelectableListIndex(startIndex);
   1364                 else
   1365                     endIndex = nextSelectableListIndexPageAway(startIndex, SkipBackwards);
   1366             }
   1367         } else {
   1368             // Set the end index based on the current end index.
   1369             if (keyIdentifier == "Down") {
   1370                 endIndex = nextSelectableListIndex(m_activeSelectionEndIndex);
   1371                 handled = true;
   1372             } else if (keyIdentifier == "Up") {
   1373                 endIndex = previousSelectableListIndex(m_activeSelectionEndIndex);
   1374                 handled = true;
   1375             } else if (keyIdentifier == "PageDown") {
   1376                 endIndex = nextSelectableListIndexPageAway(m_activeSelectionEndIndex, SkipForwards);
   1377                 handled = true;
   1378             } else if (keyIdentifier == "PageUp") {
   1379                 endIndex = nextSelectableListIndexPageAway(m_activeSelectionEndIndex, SkipBackwards);
   1380                 handled = true;
   1381             }
   1382         }
   1383         if (keyIdentifier == "Home") {
   1384             endIndex = firstSelectableListIndex();
   1385             handled = true;
   1386         } else if (keyIdentifier == "End") {
   1387             endIndex = lastSelectableListIndex();
   1388             handled = true;
   1389         }
   1390 
   1391         if (isSpatialNavigationEnabled(document()->frame()))
   1392             // Check if the selection moves to the boundary.
   1393             if (keyIdentifier == "Left" || keyIdentifier == "Right" || ((keyIdentifier == "Down" || keyIdentifier == "Up") && endIndex == m_activeSelectionEndIndex))
   1394                 return;
   1395 
   1396         if (endIndex >= 0 && handled) {
   1397             // Save the selection so it can be compared to the new selection
   1398             // when dispatching change events immediately after making the new
   1399             // selection.
   1400             saveLastSelection();
   1401 
   1402             ASSERT_UNUSED(listItems, !listItems.size() || static_cast<size_t>(endIndex) < listItems.size());
   1403             setActiveSelectionEndIndex(endIndex);
   1404 
   1405             bool selectNewItem = !m_multiple || toKeyboardEvent(event)->shiftKey() || !isSpatialNavigationEnabled(document()->frame());
   1406             if (selectNewItem)
   1407                 m_activeSelectionState = true;
   1408             // If the anchor is unitialized, or if we're going to deselect all
   1409             // other options, then set the anchor index equal to the end index.
   1410             bool deselectOthers = !m_multiple || (!toKeyboardEvent(event)->shiftKey() && selectNewItem);
   1411             if (m_activeSelectionAnchorIndex < 0 || deselectOthers) {
   1412                 if (deselectOthers)
   1413                     deselectItemsWithoutValidation();
   1414                 setActiveSelectionAnchorIndex(m_activeSelectionEndIndex);
   1415             }
   1416 
   1417             toRenderListBox(renderer())->scrollToRevealElementAtListIndex(endIndex);
   1418             if (selectNewItem) {
   1419                 updateListBoxSelection(deselectOthers);
   1420                 listBoxOnChange();
   1421             } else
   1422                 scrollToSelection();
   1423 
   1424             event->setDefaultHandled();
   1425         }
   1426     } else if (event->type() == eventNames().keypressEvent) {
   1427         if (!event->isKeyboardEvent())
   1428             return;
   1429         int keyCode = toKeyboardEvent(event)->keyCode();
   1430 
   1431         if (keyCode == '\r') {
   1432             if (form())
   1433                 form()->submitImplicitly(event, false);
   1434             event->setDefaultHandled();
   1435         } else if (m_multiple && keyCode == ' ' && isSpatialNavigationEnabled(document()->frame())) {
   1436             // Use space to toggle selection change.
   1437             m_activeSelectionState = !m_activeSelectionState;
   1438             updateSelectedState(listToOptionIndex(m_activeSelectionEndIndex), true /*multi*/, false /*shift*/);
   1439             listBoxOnChange();
   1440             event->setDefaultHandled();
   1441         }
   1442     }
   1443 }
   1444 
   1445 void HTMLSelectElement::defaultEventHandler(Event* event)
   1446 {
   1447     if (!renderer())
   1448         return;
   1449 
   1450     if (isDisabledFormControl()) {
   1451         HTMLFormControlElementWithState::defaultEventHandler(event);
   1452         return;
   1453     }
   1454 
   1455     if (usesMenuList())
   1456         menuListDefaultEventHandler(event);
   1457     else
   1458         listBoxDefaultEventHandler(event);
   1459     if (event->defaultHandled())
   1460         return;
   1461 
   1462     if (event->type() == eventNames().keypressEvent && event->isKeyboardEvent()) {
   1463         KeyboardEvent* keyboardEvent = toKeyboardEvent(event);
   1464         if (!keyboardEvent->ctrlKey() && !keyboardEvent->altKey() && !keyboardEvent->metaKey() && isPrintableChar(keyboardEvent->charCode())) {
   1465             typeAheadFind(keyboardEvent);
   1466             event->setDefaultHandled();
   1467             return;
   1468         }
   1469     }
   1470     HTMLFormControlElementWithState::defaultEventHandler(event);
   1471 }
   1472 
   1473 int HTMLSelectElement::lastSelectedListIndex() const
   1474 {
   1475     const Vector<HTMLElement*>& items = listItems();
   1476     for (size_t i = items.size(); i;) {
   1477         HTMLElement* element = items[--i];
   1478         if (element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected())
   1479             return i;
   1480     }
   1481     return -1;
   1482 }
   1483 
   1484 int HTMLSelectElement::indexOfSelectedOption() const
   1485 {
   1486     return optionToListIndex(selectedIndex());
   1487 }
   1488 
   1489 int HTMLSelectElement::optionCount() const
   1490 {
   1491     return listItems().size();
   1492 }
   1493 
   1494 String HTMLSelectElement::optionAtIndex(int index) const
   1495 {
   1496     const Vector<HTMLElement*>& items = listItems();
   1497 
   1498     HTMLElement* element = items[index];
   1499     if (!element->hasTagName(optionTag) || toHTMLOptionElement(element)->isDisabledFormControl())
   1500         return String();
   1501     return toHTMLOptionElement(element)->textIndentedToRespectGroupLabel();
   1502 }
   1503 
   1504 void HTMLSelectElement::typeAheadFind(KeyboardEvent* event)
   1505 {
   1506     int index = m_typeAhead.handleEvent(event, TypeAhead::MatchPrefix | TypeAhead::CycleFirstChar);
   1507     if (index < 0)
   1508         return;
   1509     selectOption(listToOptionIndex(index), DeselectOtherOptions | DispatchChangeEvent | UserDriven);
   1510     if (!usesMenuList())
   1511         listBoxOnChange();
   1512 }
   1513 
   1514 Node::InsertionNotificationRequest HTMLSelectElement::insertedInto(ContainerNode* insertionPoint)
   1515 {
   1516     // When the element is created during document parsing, it won't have any
   1517     // items yet - but for innerHTML and related methods, this method is called
   1518     // after the whole subtree is constructed.
   1519     recalcListItems();
   1520     HTMLFormControlElementWithState::insertedInto(insertionPoint);
   1521     return InsertionDone;
   1522 }
   1523 
   1524 void HTMLSelectElement::accessKeySetSelectedIndex(int index)
   1525 {
   1526     // First bring into focus the list box.
   1527     if (!focused())
   1528         accessKeyAction(false);
   1529 
   1530     // If this index is already selected, unselect. otherwise update the selected index.
   1531     const Vector<HTMLElement*>& items = listItems();
   1532     int listIndex = optionToListIndex(index);
   1533     if (listIndex >= 0) {
   1534         HTMLElement* element = items[listIndex];
   1535         if (element->hasTagName(optionTag)) {
   1536             if (toHTMLOptionElement(element)->selected())
   1537                 toHTMLOptionElement(element)->setSelectedState(false);
   1538             else
   1539                 selectOption(index, DispatchChangeEvent | UserDriven);
   1540         }
   1541     }
   1542 
   1543     if (usesMenuList())
   1544         dispatchChangeEventForMenuList();
   1545     else
   1546         listBoxOnChange();
   1547 
   1548     scrollToSelection();
   1549 }
   1550 
   1551 unsigned HTMLSelectElement::length() const
   1552 {
   1553     unsigned options = 0;
   1554 
   1555     const Vector<HTMLElement*>& items = listItems();
   1556     for (unsigned i = 0; i < items.size(); ++i) {
   1557         if (items[i]->hasTagName(optionTag))
   1558             ++options;
   1559     }
   1560 
   1561     return options;
   1562 }
   1563 
   1564 void HTMLSelectElement::finishParsingChildren()
   1565 {
   1566     HTMLFormControlElementWithState::finishParsingChildren();
   1567     m_isParsingInProgress = false;
   1568     updateListItemSelectedStates();
   1569 }
   1570 
   1571 bool HTMLSelectElement::anonymousIndexedSetter(unsigned index, PassRefPtr<HTMLOptionElement> value, ExceptionState& es)
   1572 {
   1573     if (!value) {
   1574         es.throwDOMException(TypeMismatchError);
   1575         return false;
   1576     }
   1577     setOption(index, value.get(), es);
   1578     return true;
   1579 }
   1580 
   1581 bool HTMLSelectElement::anonymousIndexedSetterRemove(unsigned index, ExceptionState& es)
   1582 {
   1583     remove(index);
   1584     return true;
   1585 }
   1586 
   1587 } // namespace
   1588