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