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