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