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