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