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