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