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