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