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