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