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