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