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