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