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