HTMLCollection's caches should be owned by either ElementRareData or Document
[WebKit-https.git] / Source / WebCore / html / HTMLSelectElement.cpp
1 /*
2  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
3  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
4  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
5  *           (C) 2001 Dirk Mueller (mueller@kde.org)
6  * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights reserved.
7  *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
8  * Copyright (C) 2010 Google Inc. All rights reserved.
9  * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public License
22  * along with this library; see the file COPYING.LIB.  If not, write to
23  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24  * Boston, MA 02110-1301, USA.
25  *
26  */
27
28 #include "config.h"
29 #include "HTMLSelectElement.h"
30
31 #include "AXObjectCache.h"
32 #include "Attribute.h"
33 #include "Chrome.h"
34 #include "ChromeClient.h"
35 #include "EventNames.h"
36 #include "FormController.h"
37 #include "FormDataList.h"
38 #include "Frame.h"
39 #include "HTMLFormElement.h"
40 #include "HTMLNames.h"
41 #include "HTMLOptGroupElement.h"
42 #include "HTMLOptionElement.h"
43 #include "HTMLOptionsCollection.h"
44 #include "KeyboardEvent.h"
45 #include "LocalizedStrings.h"
46 #include "MouseEvent.h"
47 #include "NodeRenderingContext.h"
48 #include "Page.h"
49 #include "RenderListBox.h"
50 #include "RenderMenuList.h"
51 #include "RenderTheme.h"
52 #include "ScriptEventListener.h"
53 #include "SpatialNavigation.h"
54 #include <wtf/text/StringBuilder.h>
55 #include <wtf/unicode/Unicode.h>
56
57 using namespace std;
58 using namespace WTF::Unicode;
59
60 namespace WebCore {
61
62 using namespace HTMLNames;
63
64 // Upper limit agreed upon with representatives of Opera and Mozilla.
65 static const unsigned maxSelectItems = 10000;
66
67 static const DOMTimeStamp typeAheadTimeout = 1000;
68
69 HTMLSelectElement::HTMLSelectElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form)
70     : HTMLFormControlElementWithState(tagName, document, form)
71     , m_lastCharTime(0)
72     , m_size(0)
73     , m_lastOnChangeIndex(-1)
74     , m_activeSelectionAnchorIndex(-1)
75     , m_activeSelectionEndIndex(-1)
76     , m_repeatingChar(0)
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"));
94     DEFINE_STATIC_LOCAL(const AtomicString, selectOne, ("select-one"));
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(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 = static_cast<HTMLOptionElement*>(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 (!isRequiredFormControl())
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     ExceptionCode ec;
230     listItems()[listIndex]->remove(ec);
231 }
232
233 void HTMLSelectElement::remove(HTMLOptionElement* option)
234 {
235     if (option->ownerSelectElement() != this)
236         return;
237
238     ExceptionCode ec;
239     option->remove(ec);
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 Attribute& attribute)
288 {
289     if (attribute.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 = attribute.value().toInt();
294         String attrSize = String::number(size);
295         if (attrSize != attribute.value()) {
296             // FIXME: This is horribly factored.
297             if (Attribute* sizeAttribute = 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 (attribute.name() == multipleAttr)
313         parseMultipleAttribute(attribute);
314     else if (attribute.name() == accesskeyAttr) {
315         // FIXME: ignore for the moment.
316     } else if (attribute.name() == onchangeAttr)
317         setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, attribute));
318     else
319         HTMLFormControlElementWithState::parseAttribute(attribute);
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     return childContext.isOnUpperEncapsulationBoundary() && HTMLFormControlElementWithState::childShouldCreateRenderer(childContext);
351 }
352
353 HTMLCollection* HTMLSelectElement::selectedOptions()
354 {
355     return ensureCachedHTMLCollection(SelectedOptions);
356 }
357
358 HTMLOptionsCollection* HTMLSelectElement::options()
359 {
360     return static_cast<HTMLOptionsCollection*>(ensureCachedHTMLCollection(SelectOptions));
361 }
362
363 void HTMLSelectElement::updateListItemSelectedStates()
364 {
365     if (m_shouldRecalcListItems)
366         recalcListItems();
367 }
368
369 void HTMLSelectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
370 {
371     setRecalcListItems();
372     setNeedsValidityCheck();
373
374     HTMLFormControlElementWithState::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
375     
376     if (AXObjectCache::accessibilityEnabled() && renderer())
377         renderer()->document()->axObjectCache()->childrenChanged(renderer());
378 }
379
380 void HTMLSelectElement::optionElementChildrenChanged()
381 {
382     setRecalcListItems();
383     setNeedsValidityCheck();
384
385     if (AXObjectCache::accessibilityEnabled() && renderer())
386         renderer()->document()->axObjectCache()->childrenChanged(renderer());
387 }
388
389 void HTMLSelectElement::accessKeyAction(bool sendMouseEvents)
390 {
391     focus();
392     dispatchSimulatedClick(0, sendMouseEvents);
393 }
394
395 void HTMLSelectElement::setMultiple(bool multiple)
396 {
397     bool oldMultiple = this->multiple();
398     int oldSelectedIndex = selectedIndex();
399     setAttribute(multipleAttr, multiple ? "" : 0);
400
401     // Restore selectedIndex after changing the multiple flag to preserve
402     // selection as single-line and multi-line has different defaults.
403     if (oldMultiple != this->multiple())
404         setSelectedIndex(oldSelectedIndex);
405 }
406
407 void HTMLSelectElement::setSize(int size)
408 {
409     setAttribute(sizeAttr, String::number(size));
410 }
411
412 Node* HTMLSelectElement::namedItem(const AtomicString& name)
413 {
414     return options()->namedItem(name);
415 }
416
417 Node* HTMLSelectElement::item(unsigned index)
418 {
419     return options()->item(index);
420 }
421
422 void HTMLSelectElement::setOption(unsigned index, HTMLOptionElement* option, ExceptionCode& ec)
423 {
424     ec = 0;
425     if (index > maxSelectItems - 1)
426         index = maxSelectItems - 1;
427     int diff = index - length();
428     RefPtr<HTMLElement> before = 0;
429     // Out of array bounds? First insert empty dummies.
430     if (diff > 0) {
431         setLength(index, ec);
432         // Replace an existing entry?
433     } else if (diff < 0) {
434         before = toHTMLElement(options()->item(index+1));
435         remove(index);
436     }
437     // Finally add the new element.
438     if (!ec) {
439         add(option, before.get(), ec);
440         if (diff >= 0 && option->selected())
441             optionSelectionStateChanged(option, true);
442     }
443 }
444
445 void HTMLSelectElement::setLength(unsigned newLen, ExceptionCode& ec)
446 {
447     ec = 0;
448     if (newLen > maxSelectItems)
449         newLen = maxSelectItems;
450     int diff = length() - newLen;
451
452     if (diff < 0) { // Add dummy elements.
453         do {
454             RefPtr<Element> option = document()->createElement(optionTag, false);
455             ASSERT(option);
456             add(toHTMLElement(option.get()), 0, ec);
457             if (ec)
458                 break;
459         } while (++diff);
460     } else {
461         const Vector<HTMLElement*>& items = listItems();
462
463         // Removing children fires mutation events, which might mutate the DOM further, so we first copy out a list
464         // of elements that we intend to remove then attempt to remove them one at a time.
465         Vector<RefPtr<Element> > itemsToRemove;
466         size_t optionIndex = 0;
467         for (size_t i = 0; i < items.size(); ++i) {
468             Element* item = items[i];
469             if (item->hasLocalName(optionTag) && optionIndex++ >= newLen) {
470                 ASSERT(item->parentNode());
471                 itemsToRemove.append(item);
472             }
473         }
474
475         for (size_t i = 0; i < itemsToRemove.size(); ++i) {
476             Element* item = itemsToRemove[i].get();
477             if (item->parentNode())
478                 item->parentNode()->removeChild(item, ec);
479         }
480     }
481     setNeedsValidityCheck();
482 }
483
484 bool HTMLSelectElement::isRequiredFormControl() const
485 {
486     return required();
487 }
488
489 // Returns the 1st valid item |skip| items from |listIndex| in direction |direction| if there is one.
490 // Otherwise, it returns the valid item closest to that boundary which is past |listIndex| if there is one.
491 // Otherwise, it returns |listIndex|.
492 // Valid means that it is enabled and an option element.
493 int HTMLSelectElement::nextValidIndex(int listIndex, SkipDirection direction, int skip) const
494 {
495     ASSERT(direction == -1 || direction == 1);
496     const Vector<HTMLElement*>& listItems = this->listItems();
497     int lastGoodIndex = listIndex;
498     int size = listItems.size();
499     for (listIndex += direction; listIndex >= 0 && listIndex < size; listIndex += direction) {
500         --skip;
501         if (!listItems[listIndex]->disabled() && listItems[listIndex]->hasTagName(optionTag)) {
502             lastGoodIndex = listIndex;
503             if (skip <= 0)
504                 break;
505         }
506     }
507     return lastGoodIndex;
508 }
509
510 int HTMLSelectElement::nextSelectableListIndex(int startIndex) const
511 {
512     return nextValidIndex(startIndex, SkipForwards, 1);
513 }
514
515 int HTMLSelectElement::previousSelectableListIndex(int startIndex) const
516 {
517     if (startIndex == -1)
518         startIndex = listItems().size();
519     return nextValidIndex(startIndex, SkipBackwards, 1);
520 }
521
522 int HTMLSelectElement::firstSelectableListIndex() const
523 {
524     const Vector<HTMLElement*>& items = listItems();
525     int index = nextValidIndex(items.size(), SkipBackwards, INT_MAX);
526     if (static_cast<size_t>(index) == items.size())
527         return -1;
528     return index;
529 }
530
531 int HTMLSelectElement::lastSelectableListIndex() const
532 {
533     return nextValidIndex(-1, SkipForwards, INT_MAX);
534 }
535
536 // Returns the index of the next valid item one page away from |startIndex| in direction |direction|.
537 int HTMLSelectElement::nextSelectableListIndexPageAway(int startIndex, SkipDirection direction) const
538 {
539     const Vector<HTMLElement*>& items = listItems();
540     // Can't use m_size because renderer forces a minimum size.
541     int pageSize = 0;
542     if (renderer()->isListBox())
543         pageSize = toRenderListBox(renderer())->size() - 1; // -1 so we still show context.
544
545     // One page away, but not outside valid bounds.
546     // If there is a valid option item one page away, the index is chosen.
547     // If there is no exact one page away valid option, returns startIndex or the most far index.
548     int edgeIndex = (direction == SkipForwards) ? 0 : (items.size() - 1);
549     int skipAmount = pageSize + ((direction == SkipForwards) ? startIndex : (edgeIndex - startIndex));
550     return nextValidIndex(edgeIndex, direction, skipAmount);
551 }
552
553 void HTMLSelectElement::selectAll()
554 {
555     ASSERT(!usesMenuList());
556     if (!renderer() || !m_multiple)
557         return;
558
559     // Save the selection so it can be compared to the new selectAll selection
560     // when dispatching change events.
561     saveLastSelection();
562
563     m_activeSelectionState = true;
564     setActiveSelectionAnchorIndex(nextSelectableListIndex(-1));
565     setActiveSelectionEndIndex(previousSelectableListIndex(-1));
566
567     updateListBoxSelection(false);
568     listBoxOnChange();
569     setNeedsValidityCheck();
570 }
571
572 void HTMLSelectElement::saveLastSelection()
573 {
574     if (usesMenuList()) {
575         m_lastOnChangeIndex = selectedIndex();
576         return;
577     }
578
579     m_lastOnChangeSelection.clear();
580     const Vector<HTMLElement*>& items = listItems();
581     for (unsigned i = 0; i < items.size(); ++i) {
582         HTMLElement* element = items[i];
583         m_lastOnChangeSelection.append(element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected());
584     }
585 }
586
587 void HTMLSelectElement::setActiveSelectionAnchorIndex(int index)
588 {
589     m_activeSelectionAnchorIndex = index;
590
591     // Cache the selection state so we can restore the old selection as the new
592     // selection pivots around this anchor index.
593     m_cachedStateForActiveSelection.clear();
594
595     const Vector<HTMLElement*>& items = listItems();
596     for (unsigned i = 0; i < items.size(); ++i) {
597         HTMLElement* element = items[i];
598         m_cachedStateForActiveSelection.append(element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected());
599     }
600 }
601
602 void HTMLSelectElement::setActiveSelectionEndIndex(int index)
603 {
604     m_activeSelectionEndIndex = index;
605 }
606
607 void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions)
608 {
609     ASSERT(renderer() && (renderer()->isListBox() || m_multiple));
610     ASSERT(!listItems().size() || m_activeSelectionAnchorIndex >= 0);
611
612     unsigned start = min(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex);
613     unsigned end = max(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex);
614
615     const Vector<HTMLElement*>& items = listItems();
616     for (unsigned i = 0; i < items.size(); ++i) {
617         HTMLElement* element = items[i];
618         if (!element->hasTagName(optionTag) || toHTMLOptionElement(element)->disabled())
619             continue;
620
621         if (i >= start && i <= end)
622             toHTMLOptionElement(element)->setSelectedState(m_activeSelectionState);
623         else if (deselectOtherOptions || i >= m_cachedStateForActiveSelection.size())
624             toHTMLOptionElement(element)->setSelectedState(false);
625         else
626             toHTMLOptionElement(element)->setSelectedState(m_cachedStateForActiveSelection[i]);
627     }
628
629     scrollToSelection();
630     setNeedsValidityCheck();
631 }
632
633 void HTMLSelectElement::listBoxOnChange()
634 {
635     ASSERT(!usesMenuList() || m_multiple);
636
637     const Vector<HTMLElement*>& items = listItems();
638
639     // If the cached selection list is empty, or the size has changed, then fire
640     // dispatchFormControlChangeEvent, and return early.
641     if (m_lastOnChangeSelection.isEmpty() || m_lastOnChangeSelection.size() != items.size()) {
642         dispatchFormControlChangeEvent();
643         return;
644     }
645
646     // Update m_lastOnChangeSelection and fire dispatchFormControlChangeEvent.
647     bool fireOnChange = false;
648     for (unsigned i = 0; i < items.size(); ++i) {
649         HTMLElement* element = items[i];
650         bool selected = element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected();
651         if (selected != m_lastOnChangeSelection[i])
652             fireOnChange = true;
653         m_lastOnChangeSelection[i] = selected;
654     }
655
656     if (fireOnChange)
657         dispatchFormControlChangeEvent();
658 }
659
660 void HTMLSelectElement::dispatchChangeEventForMenuList()
661 {
662     ASSERT(usesMenuList());
663
664     int selected = selectedIndex();
665     if (m_lastOnChangeIndex != selected && m_isProcessingUserDrivenChange) {
666         m_lastOnChangeIndex = selected;
667         m_isProcessingUserDrivenChange = false;
668         dispatchFormControlChangeEvent();
669     }
670 }
671
672 void HTMLSelectElement::scrollToSelection()
673 {
674     if (usesMenuList())
675         return;
676
677     if (RenderObject* renderer = this->renderer())
678         toRenderListBox(renderer)->selectionChanged();
679 }
680
681 void HTMLSelectElement::setOptionsChangedOnRenderer()
682 {
683     if (RenderObject* renderer = this->renderer()) {
684         if (usesMenuList())
685             toRenderMenuList(renderer)->setOptionsChanged(true);
686         else
687             toRenderListBox(renderer)->setOptionsChanged(true);
688     }
689 }
690
691 const Vector<HTMLElement*>& HTMLSelectElement::listItems() const
692 {
693     if (m_shouldRecalcListItems)
694         recalcListItems();
695     else {
696 #if !ASSERT_DISABLED
697         Vector<HTMLElement*> items = m_listItems;
698         recalcListItems(false);
699         ASSERT(items == m_listItems);
700 #endif
701     }
702
703     return m_listItems;
704 }
705
706 void HTMLSelectElement::invalidateSelectedItems()
707 {
708     if (HTMLCollection* collection = cachedHTMLCollection(SelectedOptions))
709         collection->invalidateCache();
710 }
711
712 void HTMLSelectElement::setRecalcListItems()
713 {
714     m_shouldRecalcListItems = true;
715     // Manual selection anchor is reset when manipulating the select programmatically.
716     m_activeSelectionAnchorIndex = -1;
717     setOptionsChangedOnRenderer();
718     setNeedsStyleRecalc();
719     if (!inDocument()) {
720         if (HTMLCollection* collection = cachedHTMLCollection(SelectOptions))
721             collection->invalidateCache();
722     }
723     if (!inDocument())
724         invalidateSelectedItems();
725 }
726
727 void HTMLSelectElement::recalcListItems(bool updateSelectedStates) const
728 {
729     m_listItems.clear();
730
731     m_shouldRecalcListItems = false;
732
733     HTMLOptionElement* foundSelected = 0;
734     HTMLOptionElement* firstOption = 0;
735     for (Node* currentNode = this->firstChild(); currentNode;) {
736         if (!currentNode->isHTMLElement()) {
737             currentNode = currentNode->traverseNextSibling(this);
738             continue;
739         }
740
741         HTMLElement* current = toHTMLElement(currentNode);
742
743         // optgroup tags may not nest. However, both FireFox and IE will
744         // flatten the tree automatically, so we follow suit.
745         // (http://www.w3.org/TR/html401/interact/forms.html#h-17.6)
746         if (current->hasTagName(optgroupTag)) {
747             m_listItems.append(current);
748             if (current->firstChild()) {
749                 currentNode = current->firstChild();
750                 continue;
751             }
752         }
753
754         if (current->hasTagName(optionTag)) {
755             m_listItems.append(current);
756
757             if (updateSelectedStates && !m_multiple) {
758                 HTMLOptionElement* option = toHTMLOptionElement(current);
759                 if (!firstOption)
760                     firstOption = option;
761                 if (option->selected()) {
762                     if (foundSelected)
763                         foundSelected->setSelectedState(false);
764                     foundSelected = option;
765                 } else if (m_size <= 1 && !foundSelected && !option->disabled()) {
766                     foundSelected = option;
767                     foundSelected->setSelectedState(true);
768                 }
769             }
770         }
771
772         if (current->hasTagName(hrTag))
773             m_listItems.append(current);
774
775         // In conforming HTML code, only <optgroup> and <option> will be found
776         // within a <select>. We call traverseNextSibling so that we only step
777         // into those tags that we choose to. For web-compat, we should cope
778         // with the case where odd tags like a <div> have been added but we
779         // handle this because such tags have already been removed from the
780         // <select>'s subtree at this point.
781         currentNode = currentNode->traverseNextSibling(this);
782     }
783
784     if (!foundSelected && m_size <= 1 && firstOption && !firstOption->selected())
785         firstOption->setSelectedState(true);
786 }
787
788 int HTMLSelectElement::selectedIndex() const
789 {
790     unsigned index = 0;
791
792     // Return the number of the first option selected.
793     const Vector<HTMLElement*>& items = listItems();
794     for (size_t i = 0; i < items.size(); ++i) {
795         HTMLElement* element = items[i];
796         if (element->hasTagName(optionTag)) {
797             if (toHTMLOptionElement(element)->selected())
798                 return index;
799             ++index;
800         }
801     }
802
803     return -1;
804 }
805
806 void HTMLSelectElement::setSelectedIndex(int index)
807 {
808     selectOption(index, DeselectOtherOptions);
809 }
810
811 void HTMLSelectElement::optionSelectionStateChanged(HTMLOptionElement* option, bool optionIsSelected)
812 {
813     ASSERT(option->ownerSelectElement() == this);
814     if (optionIsSelected)
815         selectOption(option->index());
816     else if (!usesMenuList())
817         selectOption(-1);
818     else
819         selectOption(nextSelectableListIndex(-1));
820 }
821
822 void HTMLSelectElement::selectOption(int optionIndex, SelectOptionFlags flags)
823 {
824     bool shouldDeselect = !m_multiple || (flags & DeselectOtherOptions);
825
826     const Vector<HTMLElement*>& items = listItems();
827     int listIndex = optionToListIndex(optionIndex);
828
829     HTMLElement* element = 0;
830     if (listIndex >= 0) {
831         element = items[listIndex];
832         if (element->hasTagName(optionTag)) {
833             if (m_activeSelectionAnchorIndex < 0 || shouldDeselect)
834                 setActiveSelectionAnchorIndex(listIndex);
835             if (m_activeSelectionEndIndex < 0 || shouldDeselect)
836                 setActiveSelectionEndIndex(listIndex);
837             toHTMLOptionElement(element)->setSelectedState(true);
838         }
839     }
840
841     if (shouldDeselect)
842         deselectItemsWithoutValidation(element);
843
844     // For the menu list case, this is what makes the selected element appear.
845     if (RenderObject* renderer = this->renderer())
846         renderer->updateFromElement();
847
848     scrollToSelection();
849
850     if (usesMenuList()) {
851         m_isProcessingUserDrivenChange = flags & UserDriven;
852         if (flags & DispatchChangeEvent)
853             dispatchChangeEventForMenuList();
854         if (RenderObject* renderer = this->renderer()) {
855             if (usesMenuList())
856                 toRenderMenuList(renderer)->didSetSelectedIndex(listIndex);
857             else if (renderer->isListBox())
858                 toRenderListBox(renderer)->selectionChanged();
859         }
860     }
861
862     setNeedsValidityCheck();
863     if (Frame* frame = document()->frame())
864         frame->page()->chrome()->client()->formStateDidChange(this);
865 }
866
867 int HTMLSelectElement::optionToListIndex(int optionIndex) const
868 {
869     const Vector<HTMLElement*>& items = listItems();
870     int listSize = static_cast<int>(items.size());
871     if (optionIndex < 0 || optionIndex >= listSize)
872         return -1;
873
874     int optionIndex2 = -1;
875     for (int listIndex = 0; listIndex < listSize; ++listIndex) {
876         if (items[listIndex]->hasTagName(optionTag)) {
877             ++optionIndex2;
878             if (optionIndex2 == optionIndex)
879                 return listIndex;
880         }
881     }
882
883     return -1;
884 }
885
886 int HTMLSelectElement::listToOptionIndex(int listIndex) const
887 {
888     const Vector<HTMLElement*>& items = listItems();
889     if (listIndex < 0 || listIndex >= static_cast<int>(items.size()) || !items[listIndex]->hasTagName(optionTag))
890         return -1;
891
892     // Actual index of option not counting OPTGROUP entries that may be in list.
893     int optionIndex = 0;
894     for (int i = 0; i < listIndex; ++i) {
895         if (items[i]->hasTagName(optionTag))
896             ++optionIndex;
897     }
898
899     return optionIndex;
900 }
901
902 void HTMLSelectElement::dispatchFocusEvent(PassRefPtr<Node> oldFocusedNode)
903 {
904     // Save the selection so it can be compared to the new selection when
905     // dispatching change events during blur event dispatch.
906     if (usesMenuList())
907         saveLastSelection();
908     HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedNode);
909 }
910
911 void HTMLSelectElement::dispatchBlurEvent(PassRefPtr<Node> newFocusedNode)
912 {
913     // We only need to fire change events here for menu lists, because we fire
914     // change events for list boxes whenever the selection change is actually made.
915     // This matches other browsers' behavior.
916     if (usesMenuList())
917         dispatchChangeEventForMenuList();
918     HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedNode);
919 }
920
921 void HTMLSelectElement::deselectItemsWithoutValidation(HTMLElement* excludeElement)
922 {
923     const Vector<HTMLElement*>& items = listItems();
924     for (unsigned i = 0; i < items.size(); ++i) {
925         HTMLElement* element = items[i];
926         if (element != excludeElement && element->hasTagName(optionTag))
927             toHTMLOptionElement(element)->setSelectedState(false);
928     }
929 }
930
931 FormControlState HTMLSelectElement::saveFormControlState() const
932 {
933     const Vector<HTMLElement*>& items = listItems();
934     size_t length = items.size();
935     FormControlState state;
936     for (unsigned i = 0; i < length; ++i) {
937         if (!items[i]->hasTagName(optionTag))
938             continue;
939         HTMLOptionElement* option = toHTMLOptionElement(items[i]);
940         if (!option->selected())
941             continue;
942         state.append(option->value());
943         if (!multiple())
944             break;
945     }
946     return state;
947 }
948
949 size_t HTMLSelectElement::searchOptionsForValue(const String& value, size_t listIndexStart, size_t listIndexEnd) const
950 {
951     const Vector<HTMLElement*>& items = listItems();
952     size_t loopEndIndex = std::min(items.size(), listIndexEnd);
953     for (size_t i = listIndexStart; i < loopEndIndex; ++i) {
954         if (!items[i]->hasLocalName(optionTag))
955             continue;
956         if (static_cast<HTMLOptionElement*>(items[i])->value() == value)
957             return i;
958     }
959     return notFound;
960 }
961
962 void HTMLSelectElement::restoreFormControlState(const FormControlState& state)
963 {
964     recalcListItems();
965
966     const Vector<HTMLElement*>& items = listItems();
967     size_t itemsSize = items.size();
968     if (!itemsSize)
969         return;
970
971     for (size_t i = 0; i < itemsSize; ++i) {
972         if (!items[i]->hasLocalName(optionTag))
973             continue;
974         static_cast<HTMLOptionElement*>(items[i])->setSelectedState(false);
975     }
976
977     if (!multiple()) {
978         size_t foundIndex = searchOptionsForValue(state[0], 0, itemsSize);
979         if (foundIndex != notFound)
980             toHTMLOptionElement(items[foundIndex])->setSelectedState(true);
981     } else {
982         size_t startIndex = 0;
983         for (size_t i = 0; i < state.valueSize(); ++i) {
984             const String& value = state[i];
985             size_t foundIndex = searchOptionsForValue(value, startIndex, itemsSize);
986             if (foundIndex == notFound)
987                 foundIndex = searchOptionsForValue(value, 0, startIndex);
988             if (foundIndex == notFound)
989                 continue;
990             toHTMLOptionElement(items[foundIndex])->setSelectedState(true);
991             startIndex = foundIndex + 1;
992         }
993     }
994
995     setOptionsChangedOnRenderer();
996     setNeedsValidityCheck();
997 }
998
999 void HTMLSelectElement::parseMultipleAttribute(const Attribute& attribute)
1000 {
1001     bool oldUsesMenuList = usesMenuList();
1002     m_multiple = !attribute.isNull();
1003     setNeedsValidityCheck();
1004     if (oldUsesMenuList != usesMenuList())
1005         reattachIfAttached();
1006 }
1007
1008 bool HTMLSelectElement::appendFormData(FormDataList& list, bool)
1009 {
1010     const AtomicString& name = this->name();
1011     if (name.isEmpty())
1012         return false;
1013
1014     bool successful = false;
1015     const Vector<HTMLElement*>& items = listItems();
1016
1017     for (unsigned i = 0; i < items.size(); ++i) {
1018         HTMLElement* element = items[i];
1019         if (element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected() && !toHTMLOptionElement(element)->disabled()) {
1020             list.appendData(name, toHTMLOptionElement(element)->value());
1021             successful = true;
1022         }
1023     }
1024
1025     // It's possible that this is a menulist with multiple options and nothing
1026     // will be submitted (!successful). We won't send a unselected non-disabled
1027     // option as fallback. This behavior matches to other browsers.
1028     return successful;
1029
1030
1031 void HTMLSelectElement::reset()
1032 {
1033     HTMLOptionElement* firstOption = 0;
1034     HTMLOptionElement* selectedOption = 0;
1035
1036     const Vector<HTMLElement*>& items = listItems();
1037     for (unsigned i = 0; i < items.size(); ++i) {
1038         HTMLElement* element = items[i];
1039         if (!element->hasTagName(optionTag))
1040             continue;
1041
1042         if (items[i]->fastHasAttribute(selectedAttr)) {
1043             if (selectedOption && !m_multiple)
1044                 selectedOption->setSelectedState(false);
1045             toHTMLOptionElement(element)->setSelectedState(true);
1046             selectedOption = toHTMLOptionElement(element);
1047         } else
1048             toHTMLOptionElement(element)->setSelectedState(false);
1049
1050         if (!firstOption)
1051             firstOption = toHTMLOptionElement(element);
1052     }
1053
1054     if (!selectedOption && firstOption && !m_multiple && m_size <= 1)
1055         firstOption->setSelectedState(true);
1056
1057     setOptionsChangedOnRenderer();
1058     setNeedsStyleRecalc();
1059     setNeedsValidityCheck();
1060 }
1061
1062 #if !PLATFORM(WIN) || OS(WINCE)
1063 bool HTMLSelectElement::platformHandleKeydownEvent(KeyboardEvent* event)
1064 {
1065     const Page* page = document()->page();
1066     RefPtr<RenderTheme> renderTheme = page ? page->theme() : RenderTheme::defaultTheme();
1067
1068     if (!renderTheme->popsMenuByArrowKeys())
1069         return false;
1070
1071     if (!isSpatialNavigationEnabled(document()->frame())) {
1072         if (event->keyIdentifier() == "Down" || event->keyIdentifier() == "Up") {
1073             focus();
1074             // Calling focus() may cause us to lose our renderer. Return true so
1075             // that our caller doesn't process the event further, but don't set
1076             // the event as handled.
1077             if (!renderer())
1078                 return true;
1079
1080             // Save the selection so it can be compared to the new selection
1081             // when dispatching change events during selectOption, which
1082             // gets called from RenderMenuList::valueChanged, which gets called
1083             // after the user makes a selection from the menu.
1084             saveLastSelection();
1085             if (RenderMenuList* menuList = toRenderMenuList(renderer()))
1086                 menuList->showPopup();
1087             event->setDefaultHandled();
1088         }
1089         return true;
1090     }
1091
1092     return false;
1093 }
1094 #endif
1095
1096 void HTMLSelectElement::menuListDefaultEventHandler(Event* event)
1097 {
1098     const Page* page = document()->page();
1099     RefPtr<RenderTheme> renderTheme = page ? page->theme() : RenderTheme::defaultTheme();
1100
1101     if (event->type() == eventNames().keydownEvent) {
1102         if (!renderer() || !event->isKeyboardEvent())
1103             return;
1104
1105         if (platformHandleKeydownEvent(static_cast<KeyboardEvent*>(event)))
1106             return;
1107
1108         // When using spatial navigation, we want to be able to navigate away
1109         // from the select element when the user hits any of the arrow keys,
1110         // instead of changing the selection.
1111         if (isSpatialNavigationEnabled(document()->frame())) {
1112             if (!m_activeSelectionState)
1113                 return;
1114         }
1115
1116         const String& keyIdentifier = static_cast<KeyboardEvent*>(event)->keyIdentifier();
1117         bool handled = true;
1118         const Vector<HTMLElement*>& listItems = this->listItems();
1119         int listIndex = optionToListIndex(selectedIndex());
1120
1121         if (keyIdentifier == "Down" || keyIdentifier == "Right")
1122             listIndex = nextValidIndex(listIndex, SkipForwards, 1);
1123         else if (keyIdentifier == "Up" || keyIdentifier == "Left")
1124             listIndex = nextValidIndex(listIndex, SkipBackwards, 1);
1125         else if (keyIdentifier == "PageDown")
1126             listIndex = nextValidIndex(listIndex, SkipForwards, 3);
1127         else if (keyIdentifier == "PageUp")
1128             listIndex = nextValidIndex(listIndex, SkipBackwards, 3);
1129         else if (keyIdentifier == "Home")
1130             listIndex = nextValidIndex(-1, SkipForwards, 1);
1131         else if (keyIdentifier == "End")
1132             listIndex = nextValidIndex(listItems.size(), SkipBackwards, 1);
1133         else
1134             handled = false;
1135
1136         if (handled && static_cast<size_t>(listIndex) < listItems.size())
1137             selectOption(listToOptionIndex(listIndex), DeselectOtherOptions | DispatchChangeEvent | UserDriven);
1138
1139         if (handled)
1140             event->setDefaultHandled();
1141     }
1142
1143     // Use key press event here since sending simulated mouse events
1144     // on key down blocks the proper sending of the key press event.
1145     if (event->type() == eventNames().keypressEvent) {
1146         if (!renderer() || !event->isKeyboardEvent())
1147             return;
1148
1149         int keyCode = static_cast<KeyboardEvent*>(event)->keyCode();
1150         bool handled = false;
1151
1152         if (keyCode == ' ' && isSpatialNavigationEnabled(document()->frame())) {
1153             // Use space to toggle arrow key handling for selection change or spatial navigation.
1154             m_activeSelectionState = !m_activeSelectionState;
1155             event->setDefaultHandled();
1156             return;
1157         }
1158
1159         if (renderTheme->popsMenuBySpaceOrReturn()) {
1160             if (keyCode == ' ' || keyCode == '\r') {
1161                 focus();
1162
1163                 // Calling focus() may cause us to lose our renderer, in which case
1164                 // do not want to handle the event.
1165                 if (!renderer())
1166                     return;
1167
1168                 // Save the selection so it can be compared to the new selection
1169                 // when dispatching change events during selectOption, which
1170                 // gets called from RenderMenuList::valueChanged, which gets called
1171                 // after the user makes a selection from the menu.
1172                 saveLastSelection();
1173                 if (RenderMenuList* menuList = toRenderMenuList(renderer()))
1174                     menuList->showPopup();
1175                 handled = true;
1176             }
1177         } else if (renderTheme->popsMenuByArrowKeys()) {
1178             if (keyCode == ' ') {
1179                 focus();
1180
1181                 // Calling focus() may cause us to lose our renderer, in which case
1182                 // do not want to handle the event.
1183                 if (!renderer())
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             } else if (keyCode == '\r') {
1195                 if (form())
1196                     form()->submitImplicitly(event, false);
1197                 dispatchChangeEventForMenuList();
1198                 handled = true;
1199             }
1200         }
1201
1202         if (handled)
1203             event->setDefaultHandled();
1204     }
1205
1206     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
1207         focus();
1208         if (renderer() && renderer()->isMenuList()) {
1209             if (RenderMenuList* menuList = toRenderMenuList(renderer())) {
1210                 if (menuList->popupIsVisible())
1211                     menuList->hidePopup();
1212                 else {
1213                     // Save the selection so it can be compared to the new
1214                     // selection when we call onChange during selectOption,
1215                     // which gets called from RenderMenuList::valueChanged,
1216                     // which gets called after the user makes a selection from
1217                     // the menu.
1218                     saveLastSelection();
1219                     menuList->showPopup();
1220                 }
1221             }
1222         }
1223         event->setDefaultHandled();
1224     }
1225 }
1226
1227 void HTMLSelectElement::updateSelectedState(int listIndex, bool multi, bool shift)
1228 {
1229     ASSERT(listIndex >= 0);
1230
1231     // Save the selection so it can be compared to the new selection when
1232     // dispatching change events during mouseup, or after autoscroll finishes.
1233     saveLastSelection();
1234
1235     m_activeSelectionState = true;
1236
1237     bool shiftSelect = m_multiple && shift;
1238     bool multiSelect = m_multiple && multi && !shift;
1239
1240     HTMLElement* clickedElement = listItems()[listIndex];
1241     if (clickedElement->hasTagName(optionTag)) {
1242         // Keep track of whether an active selection (like during drag
1243         // selection), should select or deselect.
1244         if (toHTMLOptionElement(clickedElement)->selected() && multiSelect)
1245             m_activeSelectionState = false;
1246         if (!m_activeSelectionState)
1247             toHTMLOptionElement(clickedElement)->setSelectedState(false);
1248     }
1249
1250     // If we're not in any special multiple selection mode, then deselect all
1251     // other items, excluding the clicked option. If no option was clicked, then
1252     // this will deselect all items in the list.
1253     if (!shiftSelect && !multiSelect)
1254         deselectItemsWithoutValidation(clickedElement);
1255
1256     // If the anchor hasn't been set, and we're doing a single selection or a
1257     // shift selection, then initialize the anchor to the first selected index.
1258     if (m_activeSelectionAnchorIndex < 0 && !multiSelect)
1259         setActiveSelectionAnchorIndex(selectedIndex());
1260
1261     // Set the selection state of the clicked option.
1262     if (clickedElement->hasTagName(optionTag) && !toHTMLOptionElement(clickedElement)->disabled())
1263         toHTMLOptionElement(clickedElement)->setSelectedState(true);
1264
1265     // If there was no selectedIndex() for the previous initialization, or If
1266     // we're doing a single selection, or a multiple selection (using cmd or
1267     // ctrl), then initialize the anchor index to the listIndex that just got
1268     // clicked.
1269     if (m_activeSelectionAnchorIndex < 0 || !shiftSelect)
1270         setActiveSelectionAnchorIndex(listIndex);
1271
1272     setActiveSelectionEndIndex(listIndex);
1273     updateListBoxSelection(!multiSelect);
1274 }
1275
1276 void HTMLSelectElement::listBoxDefaultEventHandler(Event* event)
1277 {
1278     const Vector<HTMLElement*>& listItems = this->listItems();
1279
1280     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
1281         focus();
1282         // Calling focus() may cause us to lose our renderer, in which case do not want to handle the event.
1283         if (!renderer())
1284             return;
1285
1286         // Convert to coords relative to the list box if needed.
1287         MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
1288         IntPoint localOffset = roundedIntPoint(renderer()->absoluteToLocal(mouseEvent->absoluteLocation(), false, true));
1289         int listIndex = toRenderListBox(renderer())->listIndexAtOffset(toSize(localOffset));
1290         if (listIndex >= 0) {
1291 #if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN))
1292             updateSelectedState(listIndex, mouseEvent->metaKey(), mouseEvent->shiftKey());
1293 #else
1294             updateSelectedState(listIndex, mouseEvent->ctrlKey(), mouseEvent->shiftKey());
1295 #endif
1296             if (Frame* frame = document()->frame())
1297                 frame->eventHandler()->setMouseDownMayStartAutoscroll();
1298
1299             event->setDefaultHandled();
1300         }
1301     } else if (event->type() == eventNames().mousemoveEvent && event->isMouseEvent() && !toRenderBox(renderer())->canBeScrolledAndHasScrollableArea()) {
1302         MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
1303         if (mouseEvent->button() != LeftButton || !mouseEvent->buttonDown())
1304             return;
1305
1306         IntPoint localOffset = roundedIntPoint(renderer()->absoluteToLocal(mouseEvent->absoluteLocation(), false, true));
1307         int listIndex = toRenderListBox(renderer())->listIndexAtOffset(toSize(localOffset));
1308         if (listIndex >= 0) {
1309             if (m_multiple) {
1310                 setActiveSelectionEndIndex(listIndex);
1311                 updateListBoxSelection(false);
1312             } else {
1313                 setActiveSelectionAnchorIndex(listIndex);
1314                 setActiveSelectionEndIndex(listIndex);
1315                 updateListBoxSelection(true);
1316             }
1317             event->setDefaultHandled();
1318         }
1319     } else if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton && document()->frame()->eventHandler()->autoscrollRenderer() != renderer()) {
1320         // This makes sure we fire dispatchFormControlChangeEvent for a single
1321         // click. For drag selection, onChange will fire when the autoscroll
1322         // timer stops.
1323         listBoxOnChange();
1324     } else if (event->type() == eventNames().keydownEvent) {
1325         if (!event->isKeyboardEvent())
1326             return;
1327         const String& keyIdentifier = static_cast<KeyboardEvent*>(event)->keyIdentifier();
1328
1329         bool handled = false;
1330         int endIndex = 0;
1331         if (m_activeSelectionEndIndex < 0) {
1332             // Initialize the end index
1333             if (keyIdentifier == "Down" || keyIdentifier == "PageDown") {
1334                 int startIndex = lastSelectedListIndex();
1335                 handled = true;
1336                 if (keyIdentifier == "Down")
1337                     endIndex = nextSelectableListIndex(startIndex);
1338                 else
1339                     endIndex = nextSelectableListIndexPageAway(startIndex, SkipForwards);
1340             } else if (keyIdentifier == "Up" || keyIdentifier == "PageUp") {
1341                 int startIndex = optionToListIndex(selectedIndex());
1342                 handled = true;
1343                 if (keyIdentifier == "Up")
1344                     endIndex = previousSelectableListIndex(startIndex);
1345                 else
1346                     endIndex = nextSelectableListIndexPageAway(startIndex, SkipBackwards);
1347             }
1348         } else {
1349             // Set the end index based on the current end index.
1350             if (keyIdentifier == "Down") {
1351                 endIndex = nextSelectableListIndex(m_activeSelectionEndIndex);
1352                 handled = true;
1353             } else if (keyIdentifier == "Up") {
1354                 endIndex = previousSelectableListIndex(m_activeSelectionEndIndex);
1355                 handled = true;
1356             } else if (keyIdentifier == "PageDown") {
1357                 endIndex = nextSelectableListIndexPageAway(m_activeSelectionEndIndex, SkipForwards);
1358                 handled = true;
1359             } else if (keyIdentifier == "PageUp") {
1360                 endIndex = nextSelectableListIndexPageAway(m_activeSelectionEndIndex, SkipBackwards);
1361                 handled = true;
1362             }
1363         }
1364         if (keyIdentifier == "Home") {
1365             endIndex = firstSelectableListIndex();
1366             handled = true;
1367         } else if (keyIdentifier == "End") {
1368             endIndex = lastSelectableListIndex();
1369             handled = true;
1370         }
1371
1372         if (isSpatialNavigationEnabled(document()->frame()))
1373             // Check if the selection moves to the boundary.
1374             if (keyIdentifier == "Left" || keyIdentifier == "Right" || ((keyIdentifier == "Down" || keyIdentifier == "Up") && endIndex == m_activeSelectionEndIndex))
1375                 return;
1376
1377         if (endIndex >= 0 && handled) {
1378             // Save the selection so it can be compared to the new selection
1379             // when dispatching change events immediately after making the new
1380             // selection.
1381             saveLastSelection();
1382
1383             ASSERT_UNUSED(listItems, !listItems.size() || static_cast<size_t>(endIndex) < listItems.size());
1384             setActiveSelectionEndIndex(endIndex);
1385
1386             bool selectNewItem = !m_multiple || static_cast<KeyboardEvent*>(event)->shiftKey() || !isSpatialNavigationEnabled(document()->frame());
1387             if (selectNewItem)
1388                 m_activeSelectionState = true;
1389             // If the anchor is unitialized, or if we're going to deselect all
1390             // other options, then set the anchor index equal to the end index.
1391             bool deselectOthers = !m_multiple || (!static_cast<KeyboardEvent*>(event)->shiftKey() && selectNewItem);
1392             if (m_activeSelectionAnchorIndex < 0 || deselectOthers) {
1393                 if (deselectOthers)
1394                     deselectItemsWithoutValidation();
1395                 setActiveSelectionAnchorIndex(m_activeSelectionEndIndex);
1396             }
1397
1398             toRenderListBox(renderer())->scrollToRevealElementAtListIndex(endIndex);
1399             if (selectNewItem) {
1400                 updateListBoxSelection(deselectOthers);
1401                 listBoxOnChange();
1402             } else
1403                 scrollToSelection();
1404
1405             event->setDefaultHandled();
1406         }
1407     } else if (event->type() == eventNames().keypressEvent) {
1408         if (!event->isKeyboardEvent())
1409             return;
1410         int keyCode = static_cast<KeyboardEvent*>(event)->keyCode();
1411
1412         if (keyCode == '\r') {
1413             if (form())
1414                 form()->submitImplicitly(event, false);
1415             event->setDefaultHandled();
1416         } else if (m_multiple && keyCode == ' ' && isSpatialNavigationEnabled(document()->frame())) {
1417             // Use space to toggle selection change.
1418             m_activeSelectionState = !m_activeSelectionState;
1419             updateSelectedState(listToOptionIndex(m_activeSelectionEndIndex), true /*multi*/, false /*shift*/);
1420             listBoxOnChange();
1421             event->setDefaultHandled();
1422         }
1423     }
1424 }
1425
1426 void HTMLSelectElement::defaultEventHandler(Event* event)
1427 {
1428     if (!renderer())
1429         return;
1430
1431     if (usesMenuList())
1432         menuListDefaultEventHandler(event);
1433     else 
1434         listBoxDefaultEventHandler(event);
1435     if (event->defaultHandled())
1436         return;
1437
1438     if (event->type() == eventNames().keypressEvent && event->isKeyboardEvent()) {
1439         KeyboardEvent* keyboardEvent = static_cast<KeyboardEvent*>(event);
1440         if (!keyboardEvent->ctrlKey() && !keyboardEvent->altKey() && !keyboardEvent->metaKey() && isPrintableChar(keyboardEvent->charCode())) {
1441             typeAheadFind(keyboardEvent);
1442             event->setDefaultHandled();
1443             return;
1444         }
1445     }
1446     HTMLFormControlElementWithState::defaultEventHandler(event);
1447 }
1448
1449 int HTMLSelectElement::lastSelectedListIndex() const
1450 {
1451     const Vector<HTMLElement*>& items = listItems();
1452     for (size_t i = items.size(); i;) {
1453         HTMLElement* element = items[--i];
1454         if (element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected())
1455             return i;
1456     }
1457     return -1;
1458 }
1459
1460 static String stripLeadingWhiteSpace(const String& string)
1461 {
1462     int length = string.length();
1463
1464     int i;
1465     for (i = 0; i < length; ++i) {
1466         if (string[i] != noBreakSpace && (string[i] <= 0x7F ? !isASCIISpace(string[i]) : (direction(string[i]) != WhiteSpaceNeutral)))
1467             break;
1468     }
1469
1470     return string.substring(i, length - i);
1471 }
1472
1473 void HTMLSelectElement::typeAheadFind(KeyboardEvent* event)
1474 {
1475     if (event->timeStamp() < m_lastCharTime)
1476         return;
1477
1478     DOMTimeStamp delta = event->timeStamp() - m_lastCharTime;
1479     m_lastCharTime = event->timeStamp();
1480
1481     UChar c = event->charCode();
1482
1483     String prefix;
1484     int searchStartOffset = 1;
1485     if (delta > typeAheadTimeout) {
1486         prefix = String(&c, 1);
1487         m_typedString = prefix;
1488         m_repeatingChar = c;
1489     } else {
1490         m_typedString.append(c);
1491
1492         if (c == m_repeatingChar) {
1493             // The user is likely trying to cycle through all the items starting
1494             // with this character, so just search on the character.
1495             prefix = String(&c, 1);
1496         } else {
1497             m_repeatingChar = 0;
1498             prefix = m_typedString;
1499             searchStartOffset = 0;
1500         }
1501     }
1502
1503     const Vector<HTMLElement*>& items = listItems();
1504     int itemCount = items.size();
1505     if (itemCount < 1)
1506         return;
1507
1508     int selected = selectedIndex();
1509     int index = (optionToListIndex(selected >= 0 ? selected : 0) + searchStartOffset) % itemCount;
1510     ASSERT(index >= 0);
1511
1512     // Compute a case-folded copy of the prefix string before beginning the search for
1513     // a matching element. This code uses foldCase to work around the fact that
1514     // String::startWith does not fold non-ASCII characters. This code can be changed
1515     // to use startWith once that is fixed.
1516     String prefixWithCaseFolded(prefix.foldCase());
1517     for (int i = 0; i < itemCount; ++i, index = (index + 1) % itemCount) {
1518         HTMLElement* element = items[index];
1519         if (!element->hasTagName(optionTag) || toHTMLOptionElement(element)->disabled())
1520             continue;
1521
1522         // Fold the option string and check if its prefix is equal to the folded prefix.
1523         String text = toHTMLOptionElement(element)->textIndentedToRespectGroupLabel();
1524         if (stripLeadingWhiteSpace(text).foldCase().startsWith(prefixWithCaseFolded)) {
1525             selectOption(listToOptionIndex(index), DeselectOtherOptions | DispatchChangeEvent | UserDriven);
1526             if (!usesMenuList())
1527                 listBoxOnChange();
1528
1529             setOptionsChangedOnRenderer();
1530             setNeedsStyleRecalc();
1531             return;
1532         }
1533     }
1534 }
1535
1536 Node::InsertionNotificationRequest HTMLSelectElement::insertedInto(ContainerNode* insertionPoint)
1537 {
1538     // When the element is created during document parsing, it won't have any
1539     // items yet - but for innerHTML and related methods, this method is called
1540     // after the whole subtree is constructed.
1541     recalcListItems();
1542     HTMLFormControlElementWithState::insertedInto(insertionPoint);
1543     return InsertionDone;
1544 }
1545
1546 void HTMLSelectElement::accessKeySetSelectedIndex(int index)
1547 {    
1548     // First bring into focus the list box.
1549     if (!focused())
1550         accessKeyAction(false);
1551     
1552     // If this index is already selected, unselect. otherwise update the selected index.
1553     const Vector<HTMLElement*>& items = listItems();
1554     int listIndex = optionToListIndex(index);
1555     if (listIndex >= 0) {
1556         HTMLElement* element = items[listIndex];
1557         if (element->hasTagName(optionTag)) {
1558             if (toHTMLOptionElement(element)->selected())
1559                 toHTMLOptionElement(element)->setSelectedState(false);
1560             else
1561                 selectOption(index, DispatchChangeEvent | UserDriven);
1562         }
1563     }
1564
1565     if (usesMenuList())
1566         dispatchChangeEventForMenuList();
1567     else
1568         listBoxOnChange();
1569
1570     scrollToSelection();
1571 }
1572
1573 unsigned HTMLSelectElement::length() const
1574 {
1575     unsigned options = 0;
1576
1577     const Vector<HTMLElement*>& items = listItems();
1578     for (unsigned i = 0; i < items.size(); ++i) {
1579         if (items[i]->hasTagName(optionTag))
1580             ++options;
1581     }
1582
1583     return options;
1584 }
1585
1586 #ifndef NDEBUG
1587
1588 HTMLSelectElement* toHTMLSelectElement(Node* node)
1589 {
1590     ASSERT(!node || node->hasTagName(selectTag));
1591     return static_cast<HTMLSelectElement*>(node);
1592 }
1593
1594 const HTMLSelectElement* toHTMLSelectElement(const Node* node)
1595 {
1596     ASSERT(!node || node->hasTagName(selectTag));
1597     return static_cast<const HTMLSelectElement*>(node);
1598 }
1599
1600 #endif
1601
1602 } // namespace