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