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