2011-01-29 Patrick Gansterer <paroga@webkit.org>
[WebKit-https.git] / Source / WebCore / dom / SelectElement.cpp
1 /*
2  * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  *
19  */
20
21 #include "config.h"
22 #include "SelectElement.h"
23
24 #include "Attribute.h"
25 #include "Chrome.h"
26 #include "ChromeClient.h"
27 #include "Element.h"
28 #include "EventHandler.h"
29 #include "EventNames.h"
30 #include "FormDataList.h"
31 #include "Frame.h"
32 #include "HTMLFormElement.h"
33 #include "HTMLKeygenElement.h"
34 #include "HTMLNames.h"
35 #include "HTMLSelectElement.h"
36 #include "KeyboardEvent.h"
37 #include "MouseEvent.h"
38 #include "OptionElement.h"
39 #include "OptionGroupElement.h"
40 #include "Page.h"
41 #include "RenderListBox.h"
42 #include "RenderMenuList.h"
43 #include "SpatialNavigation.h"
44 #include <wtf/Assertions.h>
45 #include <wtf/unicode/CharacterNames.h>
46
47 #if ENABLE(WML)
48 #include "WMLNames.h"
49 #include "WMLSelectElement.h"
50 #endif
51
52 // Configure platform-specific behavior when focused pop-up receives arrow/space/return keystroke.
53 // (PLATFORM(MAC) and PLATFORM(GTK) are always false in Chromium, hence the extra tests.)
54 #if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN))
55 #define ARROW_KEYS_POP_MENU 1
56 #define SPACE_OR_RETURN_POP_MENU 0
57 #elif PLATFORM(GTK) || (PLATFORM(CHROMIUM) && (OS(LINUX) || OS(FREEBSD)))
58 #define ARROW_KEYS_POP_MENU 0
59 #define SPACE_OR_RETURN_POP_MENU 1
60 #else
61 #define ARROW_KEYS_POP_MENU 0
62 #define SPACE_OR_RETURN_POP_MENU 0
63 #endif
64
65 using std::min;
66 using std::max;
67 using namespace WTF;
68 using namespace Unicode;
69
70 namespace WebCore {
71
72 static const DOMTimeStamp typeAheadTimeout = 1000;
73
74 void SelectElement::selectAll(SelectElementData& data, Element* element)
75 {
76     ASSERT(!data.usesMenuList());
77     if (!element->renderer() || !data.multiple())
78         return;
79
80     // Save the selection so it can be compared to the new selectAll selection when dispatching change events
81     saveLastSelection(data, element);
82
83     data.setActiveSelectionState(true);
84     setActiveSelectionAnchorIndex(data, element, nextSelectableListIndex(data, element, -1));
85     setActiveSelectionEndIndex(data, previousSelectableListIndex(data, element, -1));
86
87     updateListBoxSelection(data, element, false);
88     listBoxOnChange(data, element);
89 }
90
91 void SelectElement::saveLastSelection(SelectElementData& data, Element* element)
92 {
93     if (data.usesMenuList()) {
94         data.setLastOnChangeIndex(selectedIndex(data, element));
95         return;
96     }
97
98     Vector<bool>& lastOnChangeSelection = data.lastOnChangeSelection(); 
99     lastOnChangeSelection.clear();
100
101     const Vector<Element*>& items = data.listItems(element);
102     for (unsigned i = 0; i < items.size(); ++i) {
103         OptionElement* optionElement = toOptionElement(items[i]);
104         lastOnChangeSelection.append(optionElement && optionElement->selected());
105     }
106 }
107
108 int SelectElement::nextSelectableListIndex(SelectElementData& data, Element* element, int startIndex)
109 {
110     const Vector<Element*>& items = data.listItems(element);
111     int index = startIndex + 1;
112     while (index >= 0 && (unsigned) index < items.size() && (!isOptionElement(items[index]) || items[index]->disabled()))
113         ++index;
114     if ((unsigned) index == items.size())
115         return startIndex;
116     return index;
117 }
118
119 int SelectElement::previousSelectableListIndex(SelectElementData& data, Element* element, int startIndex)
120 {
121     const Vector<Element*>& items = data.listItems(element);
122     if (startIndex == -1)
123         startIndex = items.size();
124     int index = startIndex - 1;
125     while (index >= 0 && (unsigned) index < items.size() && (!isOptionElement(items[index]) || items[index]->disabled()))
126         --index;
127     if (index == -1)
128         return startIndex;
129     return index;
130 }
131
132 void SelectElement::setActiveSelectionAnchorIndex(SelectElementData& data, Element* element, int index)
133 {
134     data.setActiveSelectionAnchorIndex(index);
135
136     // Cache the selection state so we can restore the old selection as the new selection pivots around this anchor index
137     Vector<bool>& cachedStateForActiveSelection = data.cachedStateForActiveSelection(); 
138     cachedStateForActiveSelection.clear();
139
140     const Vector<Element*>& items = data.listItems(element);
141     for (unsigned i = 0; i < items.size(); ++i) {
142         OptionElement* optionElement = toOptionElement(items[i]);
143         cachedStateForActiveSelection.append(optionElement && optionElement->selected());
144     }
145 }
146
147 void SelectElement::setActiveSelectionEndIndex(SelectElementData& data, int index)
148 {
149     data.setActiveSelectionEndIndex(index);
150 }
151
152 void SelectElement::updateListBoxSelection(SelectElementData& data, Element* element, bool deselectOtherOptions)
153 {
154     ASSERT(element->renderer() && (element->renderer()->isListBox() || data.multiple()));
155     ASSERT(!data.listItems(element).size() || data.activeSelectionAnchorIndex() >= 0);
156
157     unsigned start = min(data.activeSelectionAnchorIndex(), data.activeSelectionEndIndex());
158     unsigned end = max(data.activeSelectionAnchorIndex(), data.activeSelectionEndIndex());
159     Vector<bool>& cachedStateForActiveSelection = data.cachedStateForActiveSelection();
160
161     const Vector<Element*>& items = data.listItems(element);
162     for (unsigned i = 0; i < items.size(); ++i) {
163         OptionElement* optionElement = toOptionElement(items[i]);
164         if (!optionElement || items[i]->disabled())
165             continue;
166
167         if (i >= start && i <= end)
168             optionElement->setSelectedState(data.activeSelectionState());
169         else if (deselectOtherOptions || i >= cachedStateForActiveSelection.size())
170             optionElement->setSelectedState(false);
171         else
172             optionElement->setSelectedState(cachedStateForActiveSelection[i]);
173     }
174
175     toSelectElement(element)->updateValidity();
176     scrollToSelection(data, element);
177 }
178
179 void SelectElement::listBoxOnChange(SelectElementData& data, Element* element)
180 {
181     ASSERT(!data.usesMenuList() || data.multiple());
182
183     Vector<bool>& lastOnChangeSelection = data.lastOnChangeSelection(); 
184     const Vector<Element*>& items = data.listItems(element);
185
186     // If the cached selection list is empty, or the size has changed, then fire dispatchFormControlChangeEvent, and return early.
187     if (lastOnChangeSelection.isEmpty() || lastOnChangeSelection.size() != items.size()) {
188         element->dispatchFormControlChangeEvent();
189         return;
190     }
191
192     // Update lastOnChangeSelection and fire dispatchFormControlChangeEvent
193     bool fireOnChange = false;
194     for (unsigned i = 0; i < items.size(); ++i) {
195         OptionElement* optionElement = toOptionElement(items[i]);
196         bool selected = optionElement &&  optionElement->selected();
197         if (selected != lastOnChangeSelection[i])
198             fireOnChange = true;
199         lastOnChangeSelection[i] = selected;
200     }
201
202     if (fireOnChange)
203         element->dispatchFormControlChangeEvent();
204 }
205
206 void SelectElement::menuListOnChange(SelectElementData& data, Element* element)
207 {
208     ASSERT(data.usesMenuList());
209
210     int selected = selectedIndex(data, element);
211     if (data.lastOnChangeIndex() != selected && data.userDrivenChange()) {
212         data.setLastOnChangeIndex(selected);
213         data.setUserDrivenChange(false);
214         element->dispatchFormControlChangeEvent();
215     }
216 }
217
218 void SelectElement::scrollToSelection(SelectElementData& data, Element* element)
219 {
220     if (data.usesMenuList())
221         return;
222
223     if (RenderObject* renderer = element->renderer())
224         toRenderListBox(renderer)->selectionChanged();
225 }
226
227 void SelectElement::setOptionsChangedOnRenderer(SelectElementData& data, Element* element)
228 {
229     if (RenderObject* renderer = element->renderer()) {
230         if (data.usesMenuList())
231             toRenderMenuList(renderer)->setOptionsChanged(true);
232         else
233             toRenderListBox(renderer)->setOptionsChanged(true);
234     }
235 }
236
237 void SelectElement::setRecalcListItems(SelectElementData& data, Element* element)
238 {
239     data.setShouldRecalcListItems(true);
240     data.setActiveSelectionAnchorIndex(-1); // Manual selection anchor is reset when manipulating the select programmatically.
241     setOptionsChangedOnRenderer(data, element);
242     element->setNeedsStyleRecalc();
243 }
244
245 void SelectElement::recalcListItems(SelectElementData& data, const Element* element, bool updateSelectedStates)
246 {
247     Vector<Element*>& listItems = data.rawListItems();
248     listItems.clear();
249
250     data.setShouldRecalcListItems(false);
251
252     OptionElement* foundSelected = 0;
253     for (Node* currentNode = element->firstChild(); currentNode;) {
254         if (!currentNode->isElementNode()) {
255             currentNode = currentNode->traverseNextSibling(element);
256             continue;
257         }
258
259         Element* current = static_cast<Element*>(currentNode);
260
261         // optgroup tags may not nest. However, both FireFox and IE will
262         // flatten the tree automatically, so we follow suit.
263         // (http://www.w3.org/TR/html401/interact/forms.html#h-17.6)
264         if (isOptionGroupElement(current)) {
265             listItems.append(current);
266             if (current->firstChild()) {
267                 currentNode = current->firstChild();
268                 continue;
269             }
270         }
271
272         if (OptionElement* optionElement = toOptionElement(current)) {
273             listItems.append(current);
274
275             if (updateSelectedStates && !data.multiple()) {
276                 if (!foundSelected && (data.size() <= 1 || optionElement->selected())) {
277                     foundSelected = optionElement;
278                     foundSelected->setSelectedState(true);
279                 } else if (foundSelected && optionElement->selected()) {
280                     foundSelected->setSelectedState(false);
281                     foundSelected = optionElement;
282                 }
283             }
284         }
285
286         if (current->hasTagName(HTMLNames::hrTag))
287             listItems.append(current);
288
289         // In conforming HTML code, only <optgroup> and <option> will be found
290         // within a <select>. We call traverseNextSibling so that we only step
291         // into those tags that we choose to. For web-compat, we should cope
292         // with the case where odd tags like a <div> have been added but we
293         // handle this because such tags have already been removed from the
294         // <select>'s subtree at this point.
295         currentNode = currentNode->traverseNextSibling(element);
296     }
297 }
298
299 int SelectElement::selectedIndex(const SelectElementData& data, const Element* element)
300 {
301     unsigned index = 0;
302
303     // return the number of the first option selected
304     const Vector<Element*>& items = data.listItems(element);
305     for (size_t i = 0; i < items.size(); ++i) {
306         if (OptionElement* optionElement = toOptionElement(items[i])) {
307             if (optionElement->selected())
308                 return index;
309             ++index;
310         }
311     }
312
313     return -1;
314 }
315
316 void SelectElement::setSelectedIndex(SelectElementData& data, Element* element, int optionIndex, bool deselect, bool fireOnChangeNow, bool userDrivenChange)
317 {
318     const Vector<Element*>& items = data.listItems(element);
319     int listIndex = optionToListIndex(data, element, optionIndex);
320     if (!data.multiple())
321         deselect = true;
322
323     Element* excludeElement = 0;
324     if (OptionElement* optionElement = (listIndex >= 0 ? toOptionElement(items[listIndex]) : 0)) {
325         excludeElement = items[listIndex];
326         if (data.activeSelectionAnchorIndex() < 0 || deselect)
327             setActiveSelectionAnchorIndex(data, element, listIndex);
328         if (data.activeSelectionEndIndex() < 0 || deselect)
329             setActiveSelectionEndIndex(data, listIndex);
330         optionElement->setSelectedState(true);
331     }
332
333     if (deselect)
334         deselectItems(data, element, excludeElement);
335
336     // For the menu list case, this is what makes the selected element appear.
337     if (RenderObject* renderer = element->renderer())
338         renderer->updateFromElement();
339
340     scrollToSelection(data, element);
341
342     // This only gets called with fireOnChangeNow for menu lists. 
343     if (data.usesMenuList()) {
344         data.setUserDrivenChange(userDrivenChange);
345         if (fireOnChangeNow)
346             menuListOnChange(data, element);
347         RenderObject* renderer = element->renderer();
348         if (renderer) {
349             if (data.usesMenuList())
350                 toRenderMenuList(renderer)->didSetSelectedIndex();
351             else if (renderer->isListBox())
352                 toRenderListBox(renderer)->selectionChanged();
353         }
354     }
355
356     if (Frame* frame = element->document()->frame())
357         frame->page()->chrome()->client()->formStateDidChange(element);
358 }
359
360 int SelectElement::optionToListIndex(const SelectElementData& data, const Element* element, int optionIndex)
361 {
362     const Vector<Element*>& items = data.listItems(element);
363     int listSize = (int) items.size();
364     if (optionIndex < 0 || optionIndex >= listSize)
365         return -1;
366
367     int optionIndex2 = -1;
368     for (int listIndex = 0; listIndex < listSize; ++listIndex) {
369         if (isOptionElement(items[listIndex])) {
370             ++optionIndex2;
371             if (optionIndex2 == optionIndex)
372                 return listIndex;
373         }
374     }
375
376     return -1;
377 }
378
379 int SelectElement::listToOptionIndex(const SelectElementData& data, const Element* element, int listIndex)
380 {
381     const Vector<Element*>& items = data.listItems(element);
382     if (listIndex < 0 || listIndex >= int(items.size()) ||
383         !isOptionElement(items[listIndex]))
384         return -1;
385
386     int optionIndex = 0; // actual index of option not counting OPTGROUP entries that may be in list
387     for (int i = 0; i < listIndex; ++i)
388         if (isOptionElement(items[i]))
389             ++optionIndex;
390
391     return optionIndex;
392 }
393
394 void SelectElement::dispatchFocusEvent(SelectElementData& data, Element* element)
395 {
396     // Save the selection so it can be compared to the new selection when dispatching change events during blur event dispatchal
397     if (data.usesMenuList())
398         saveLastSelection(data, element);
399 }
400
401 void SelectElement::dispatchBlurEvent(SelectElementData& data, Element* element)
402 {
403     // We only need to fire change events here for menu lists, because we fire change events for list boxes whenever the selection change is actually made.
404     // This matches other browsers' behavior.
405     if (data.usesMenuList())
406         menuListOnChange(data, element);
407 }
408
409 void SelectElement::deselectItems(SelectElementData& data, Element* element, Element* excludeElement)
410 {
411     const Vector<Element*>& items = data.listItems(element);
412     for (unsigned i = 0; i < items.size(); ++i) {
413         if (items[i] == excludeElement)
414             continue;
415
416         if (OptionElement* optionElement = toOptionElement(items[i]))
417             optionElement->setSelectedState(false);
418     }
419 }
420
421 bool SelectElement::saveFormControlState(const SelectElementData& data, const Element* element, String& value)
422 {
423     const Vector<Element*>& items = data.listItems(element);
424     int length = items.size();
425
426     // FIXME: Change this code to use the new StringImpl::createUninitialized code path.
427     Vector<char, 1024> characters(length);
428     for (int i = 0; i < length; ++i) {
429         OptionElement* optionElement = toOptionElement(items[i]);
430         bool selected = optionElement && optionElement->selected();
431         characters[i] = selected ? 'X' : '.';
432     }
433
434     value = String(characters.data(), length);
435     return true;
436 }
437
438 void SelectElement::restoreFormControlState(SelectElementData& data, Element* element, const String& state)
439 {
440     recalcListItems(data, element);
441
442     const Vector<Element*>& items = data.listItems(element);
443     int length = items.size();
444
445     for (int i = 0; i < length; ++i) {
446         if (OptionElement* optionElement = toOptionElement(items[i]))
447             optionElement->setSelectedState(state[i] == 'X');
448     }
449
450     setOptionsChangedOnRenderer(data, element);
451 }
452
453 void SelectElement::parseMultipleAttribute(SelectElementData& data, Element* element, Attribute* attribute)
454 {
455     bool oldUsesMenuList = data.usesMenuList();
456     data.setMultiple(!attribute->isNull());
457     toSelectElement(element)->updateValidity();
458     if (oldUsesMenuList != data.usesMenuList() && element->attached()) {
459         element->detach();
460         element->attach();
461     }
462 }
463
464 bool SelectElement::appendFormData(SelectElementData& data, Element* element, FormDataList& list)
465 {
466     const AtomicString& name = element->formControlName();
467     if (name.isEmpty())
468         return false;
469
470     bool successful = false;
471     const Vector<Element*>& items = data.listItems(element);
472
473     for (unsigned i = 0; i < items.size(); ++i) {
474         OptionElement* optionElement = toOptionElement(items[i]);
475         if (optionElement && optionElement->selected() && !optionElement->disabled()) {
476             list.appendData(name, optionElement->value());
477             successful = true;
478         }
479     }
480
481     // It's possible that this is a menulist with multiple options and nothing
482     // will be submitted (!successful). We won't send a unselected non-disabled
483     // option as fallback. This behavior matches to other browsers.
484     return successful;
485
486
487 void SelectElement::reset(SelectElementData& data, Element* element)
488 {
489     OptionElement* firstOption = 0;
490     OptionElement* selectedOption = 0;
491
492     const Vector<Element*>& items = data.listItems(element);
493     for (unsigned i = 0; i < items.size(); ++i) {
494         OptionElement* optionElement = toOptionElement(items[i]);
495         if (!optionElement)
496             continue;
497
498         if (items[i]->fastHasAttribute(HTMLNames::selectedAttr)) {
499             if (selectedOption && !data.multiple())
500                 selectedOption->setSelectedState(false);
501             optionElement->setSelectedState(true);
502             selectedOption = optionElement;
503         } else
504             optionElement->setSelectedState(false);
505
506         if (!firstOption)
507             firstOption = optionElement;
508     }
509
510     if (!selectedOption && firstOption && !data.multiple() && data.size() <= 1)
511         firstOption->setSelectedState(true);
512
513     setOptionsChangedOnRenderer(data, element);
514     element->setNeedsStyleRecalc();
515 }
516     
517 enum SkipDirection {
518     SkipBackwards = -1,
519     SkipForwards = 1
520 };
521
522 // Returns the index of the next valid list item |skip| items past |listIndex| in direction |direction|.
523 static int nextValidIndex(const Vector<Element*>& listItems, int listIndex, SkipDirection direction, int skip)
524 {
525     int lastGoodIndex = listIndex;
526     int size = listItems.size();
527     for (listIndex += direction; listIndex >= 0 && listIndex < size; listIndex += direction) {
528         --skip;
529         if (!listItems[listIndex]->disabled() && isOptionElement(listItems[listIndex])) {
530             lastGoodIndex = listIndex;
531             if (skip <= 0)
532                 break;
533         }
534     }
535     return lastGoodIndex;
536 }
537
538 void SelectElement::menuListDefaultEventHandler(SelectElementData& data, Element* element, Event* event, HTMLFormElement* htmlForm)
539 {
540     if (event->type() == eventNames().keydownEvent) {
541         if (!element->renderer() || !event->isKeyboardEvent())
542             return;
543
544         const String& keyIdentifier = static_cast<KeyboardEvent*>(event)->keyIdentifier();
545         bool handled = false;
546
547 #if ARROW_KEYS_POP_MENU
548         if (!isSpatialNavigationEnabled(element->document()->frame())) {
549             if (keyIdentifier == "Down" || keyIdentifier == "Up") {
550                 element->focus();
551
552                 if (!element->renderer()) // Calling focus() may cause us to lose our renderer, in which case do not want to handle the event.
553                     return;
554
555                 // Save the selection so it can be compared to the new selection when dispatching change events during setSelectedIndex,
556                 // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu.
557                 saveLastSelection(data, element);
558                 if (RenderMenuList* menuList = toRenderMenuList(element->renderer()))
559                     menuList->showPopup();
560
561                 event->setDefaultHandled();
562             }
563             return;
564         }
565 #endif
566         // When using spatial navigation, we want to be able to navigate away from the select element
567         // when the user hits any of the arrow keys, instead of changing the selection.
568         if (isSpatialNavigationEnabled(element->document()->frame()))
569             if (!data.activeSelectionState())
570                 return;
571
572         UNUSED_PARAM(htmlForm);
573         const Vector<Element*>& listItems = data.listItems(element);
574
575         int listIndex = optionToListIndex(data, element, selectedIndex(data, element));
576         if (keyIdentifier == "Down" || keyIdentifier == "Right") {
577             listIndex = nextValidIndex(listItems, listIndex, SkipForwards, 1);
578             handled = true;
579         } else if (keyIdentifier == "Up" || keyIdentifier == "Left") {
580             listIndex = nextValidIndex(listItems, listIndex, SkipBackwards, 1);
581             handled = true;
582         } else if (keyIdentifier == "PageDown") {
583             listIndex = nextValidIndex(listItems, listIndex, SkipForwards, 3);
584             handled = true;
585         } else if (keyIdentifier == "PageUp") {
586             listIndex = nextValidIndex(listItems, listIndex, SkipBackwards, 3);
587             handled = true;
588         } else if (keyIdentifier == "Home") {
589             listIndex = nextValidIndex(listItems, -1, SkipForwards, 1);
590             handled = true;
591         } else if (keyIdentifier == "End") {
592             listIndex = nextValidIndex(listItems, listItems.size(), SkipBackwards, 1);
593             handled = true;
594         }
595         
596         if (handled && listIndex >= 0 && (unsigned)listIndex < listItems.size())
597             setSelectedIndex(data, element, listToOptionIndex(data, element, listIndex));
598
599         if (handled)
600             event->setDefaultHandled();
601     }
602
603     // Use key press event here since sending simulated mouse events
604     // on key down blocks the proper sending of the key press event.
605     if (event->type() == eventNames().keypressEvent) {
606         if (!element->renderer() || !event->isKeyboardEvent())
607             return;
608
609         int keyCode = static_cast<KeyboardEvent*>(event)->keyCode();
610         bool handled = false;
611
612         if (keyCode == ' ' && isSpatialNavigationEnabled(element->document()->frame())) {
613             // Use space to toggle arrow key handling for selection change or spatial navigation.
614             data.setActiveSelectionState(!data.activeSelectionState());
615             event->setDefaultHandled();
616             return;
617         }
618
619 #if SPACE_OR_RETURN_POP_MENU
620         if (keyCode == ' ' || keyCode == '\r') {
621             element->focus();
622
623             if (!element->renderer()) // Calling focus() may cause us to lose our renderer, in which case do not want to handle the event.
624                 return;
625
626             // Save the selection so it can be compared to the new selection when dispatching change events during setSelectedIndex,
627             // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu.
628             saveLastSelection(data, element);
629             if (RenderMenuList* menuList = toRenderMenuList(element->renderer()))
630                 menuList->showPopup();
631             handled = true;
632         }
633 #elif ARROW_KEYS_POP_MENU
634         if (keyCode == ' ') {
635             element->focus();
636
637             if (!element->renderer()) // Calling focus() may cause us to lose our renderer, in which case do not want to handle the event.
638                 return;
639
640             // Save the selection so it can be compared to the new selection when dispatching change events during setSelectedIndex,
641             // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu.
642             saveLastSelection(data, element);
643             if (RenderMenuList* menuList = toRenderMenuList(element->renderer()))
644                 menuList->showPopup();
645             handled = true;
646         } else if (keyCode == '\r') {
647             if (htmlForm)
648                 htmlForm->submitImplicitly(event, false);
649             menuListOnChange(data, element);
650             handled = true;
651         }
652 #else
653         int listIndex = optionToListIndex(data, element, selectedIndex(data, element));
654         if (keyCode == '\r') {
655             // listIndex should already be selected, but this will fire the onchange handler.
656             setSelectedIndex(data, element, listToOptionIndex(data, element, listIndex), true, true);
657             handled = true;
658         }
659 #endif
660         if (handled)
661             event->setDefaultHandled();
662     }
663
664     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
665         element->focus();
666         if (element->renderer() && element->renderer()->isMenuList()) {
667             if (RenderMenuList* menuList = toRenderMenuList(element->renderer())) {
668                 if (menuList->popupIsVisible())
669                     menuList->hidePopup();
670                 else {
671                     // Save the selection so it can be compared to the new selection when we call onChange during setSelectedIndex,
672                     // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu.
673                     saveLastSelection(data, element);
674                     menuList->showPopup();
675                 }
676             }
677         }
678         event->setDefaultHandled();
679     }
680 }
681
682 void SelectElement::updateSelectedState(SelectElementData& data, Element* element, int listIndex,
683                                         bool multi, bool shift)
684 {
685     ASSERT(listIndex >= 0);
686
687     // Save the selection so it can be compared to the new selection when dispatching change events during mouseup, or after autoscroll finishes.
688     saveLastSelection(data, element);
689
690     data.setActiveSelectionState(true);
691
692     bool shiftSelect = data.multiple() && shift;
693     bool multiSelect = data.multiple() && multi && !shift;
694
695     Element* clickedElement = data.listItems(element)[listIndex];
696     OptionElement* option = toOptionElement(clickedElement);
697     if (option) {
698         // Keep track of whether an active selection (like during drag selection), should select or deselect
699         if (option->selected() && multi)
700             data.setActiveSelectionState(false);
701
702         if (!data.activeSelectionState())
703             option->setSelectedState(false);
704     }
705
706     // If we're not in any special multiple selection mode, then deselect all other items, excluding the clicked option.
707     // If no option was clicked, then this will deselect all items in the list.
708     if (!shiftSelect && !multiSelect)
709         deselectItems(data, element, clickedElement);
710
711     // If the anchor hasn't been set, and we're doing a single selection or a shift selection, then initialize the anchor to the first selected index.
712     if (data.activeSelectionAnchorIndex() < 0 && !multiSelect)
713         setActiveSelectionAnchorIndex(data, element, selectedIndex(data, element));
714
715     // Set the selection state of the clicked option
716     if (option && !clickedElement->disabled())
717         option->setSelectedState(true);
718
719     // If there was no selectedIndex() for the previous initialization, or
720     // If we're doing a single selection, or a multiple selection (using cmd or ctrl), then initialize the anchor index to the listIndex that just got clicked.
721     if (data.activeSelectionAnchorIndex() < 0 || !shiftSelect)
722         setActiveSelectionAnchorIndex(data, element, listIndex);
723
724     setActiveSelectionEndIndex(data, listIndex);
725     updateListBoxSelection(data, element, !multiSelect);
726 }
727
728 void SelectElement::listBoxDefaultEventHandler(SelectElementData& data, Element* element, Event* event, HTMLFormElement* htmlForm)
729 {
730     const Vector<Element*>& listItems = data.listItems(element);
731
732     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
733         element->focus();
734
735         if (!element->renderer()) // Calling focus() may cause us to lose our renderer, in which case do not want to handle the event.
736             return;
737
738         // Convert to coords relative to the list box if needed.
739         MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
740         IntPoint localOffset = roundedIntPoint(element->renderer()->absoluteToLocal(mouseEvent->absoluteLocation(), false, true));
741         int listIndex = toRenderListBox(element->renderer())->listIndexAtOffset(localOffset.x(), localOffset.y());
742         if (listIndex >= 0) {
743 #if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN))
744             updateSelectedState(data, element, listIndex, mouseEvent->metaKey(), mouseEvent->shiftKey());
745 #else
746             updateSelectedState(data, element, listIndex, mouseEvent->ctrlKey(), mouseEvent->shiftKey());
747 #endif
748             if (Frame* frame = element->document()->frame())
749                 frame->eventHandler()->setMouseDownMayStartAutoscroll();
750
751             event->setDefaultHandled();
752         }
753     } else if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton && element->document()->frame()->eventHandler()->autoscrollRenderer() != element->renderer()) {
754         // This makes sure we fire dispatchFormControlChangeEvent for a single click.  For drag selection, onChange will fire when the autoscroll timer stops.
755         listBoxOnChange(data, element);
756     } else if (event->type() == eventNames().keydownEvent) {
757         if (!event->isKeyboardEvent())
758             return;
759         const String& keyIdentifier = static_cast<KeyboardEvent*>(event)->keyIdentifier();
760
761         int endIndex = 0;        
762         if (data.activeSelectionEndIndex() < 0) {
763             // Initialize the end index
764             if (keyIdentifier == "Down")
765                 endIndex = nextSelectableListIndex(data, element, lastSelectedListIndex(data, element));
766             else if (keyIdentifier == "Up")
767                 endIndex = previousSelectableListIndex(data, element, optionToListIndex(data, element, selectedIndex(data, element)));
768         } else {
769             // Set the end index based on the current end index
770             if (keyIdentifier == "Down")
771                 endIndex = nextSelectableListIndex(data, element, data.activeSelectionEndIndex());
772             else if (keyIdentifier == "Up")
773                 endIndex = previousSelectableListIndex(data, element, data.activeSelectionEndIndex());    
774         }
775
776         if (isSpatialNavigationEnabled(element->document()->frame()))
777             // Check if the selection moves to the boundary.
778             if (keyIdentifier == "Left" || keyIdentifier == "Right" || ((keyIdentifier == "Down" || keyIdentifier == "Up") && endIndex == data.activeSelectionEndIndex()))
779                 return;
780
781         if (keyIdentifier == "Down" || keyIdentifier == "Up") {
782             // Save the selection so it can be compared to the new selection when dispatching change events immediately after making the new selection.
783             saveLastSelection(data, element);
784
785             ASSERT_UNUSED(listItems, !listItems.size() || (endIndex >= 0 && (unsigned) endIndex < listItems.size()));
786             setActiveSelectionEndIndex(data, endIndex);
787             
788             // If the anchor is unitialized, or if we're going to deselect all other options, then set the anchor index equal to the end index.
789             bool deselectOthers = !data.multiple() || !static_cast<KeyboardEvent*>(event)->shiftKey();
790             if (data.activeSelectionAnchorIndex() < 0 || deselectOthers) {
791                 data.setActiveSelectionState(true);
792                 if (deselectOthers)
793                     deselectItems(data, element);
794                 setActiveSelectionAnchorIndex(data, element, data.activeSelectionEndIndex());
795             }
796
797             toRenderListBox(element->renderer())->scrollToRevealElementAtListIndex(endIndex);
798             updateListBoxSelection(data, element, deselectOthers);
799             listBoxOnChange(data, element);
800             event->setDefaultHandled();
801         }
802     } else if (event->type() == eventNames().keypressEvent) {
803         if (!event->isKeyboardEvent())
804             return;
805         int keyCode = static_cast<KeyboardEvent*>(event)->keyCode();
806
807         if (keyCode == '\r') {
808             if (htmlForm)
809                 htmlForm->submitImplicitly(event, false);
810             event->setDefaultHandled();
811             return;
812         }
813     }
814 }
815
816 void SelectElement::defaultEventHandler(SelectElementData& data, Element* element, Event* event, HTMLFormElement* htmlForm)
817 {
818     if (!element->renderer())
819         return;
820
821     if (data.usesMenuList())
822         menuListDefaultEventHandler(data, element, event, htmlForm);
823     else 
824         listBoxDefaultEventHandler(data, element, event, htmlForm);
825
826     if (event->defaultHandled())
827         return;
828
829     if (event->type() == eventNames().keypressEvent && event->isKeyboardEvent()) {
830         KeyboardEvent* keyboardEvent = static_cast<KeyboardEvent*>(event);
831         if (!keyboardEvent->ctrlKey() && !keyboardEvent->altKey() && !keyboardEvent->metaKey() && isPrintableChar(keyboardEvent->charCode())) {
832             typeAheadFind(data, element, keyboardEvent);
833             event->setDefaultHandled();
834             return;
835         }
836     }
837 }
838
839 int SelectElement::lastSelectedListIndex(const SelectElementData& data, const Element* element)
840 {
841     // return the number of the last option selected
842     unsigned index = 0;
843     bool found = false;
844     const Vector<Element*>& items = data.listItems(element);
845     for (size_t i = 0; i < items.size(); ++i) {
846         if (OptionElement* optionElement = toOptionElement(items[i])) {
847             if (optionElement->selected()) {
848                 index = i;
849                 found = true;
850             }
851         }
852     }
853
854     return found ? (int) index : -1;
855 }
856
857 static String stripLeadingWhiteSpace(const String& string)
858 {
859     int length = string.length();
860
861     int i;
862     for (i = 0; i < length; ++i) {
863         if (string[i] != noBreakSpace && (string[i] <= 0x7F ? !isASCIISpace(string[i]) : (direction(string[i]) != WhiteSpaceNeutral)))
864             break;
865     }
866
867     return string.substring(i, length - i);
868 }
869
870 void SelectElement::typeAheadFind(SelectElementData& data, Element* element, KeyboardEvent* event)
871 {
872     if (event->timeStamp() < data.lastCharTime())
873         return;
874
875     DOMTimeStamp delta = event->timeStamp() - data.lastCharTime();
876     data.setLastCharTime(event->timeStamp());
877
878     UChar c = event->charCode();
879
880     String prefix;
881     int searchStartOffset = 1;
882     if (delta > typeAheadTimeout) {
883         prefix = String(&c, 1);
884         data.setTypedString(prefix);
885         data.setRepeatingChar(c);
886     } else {
887         data.typedString().append(c);
888
889         if (c == data.repeatingChar())
890             // The user is likely trying to cycle through all the items starting with this character, so just search on the character
891             prefix = String(&c, 1);
892         else {
893             data.setRepeatingChar(0);
894             prefix = data.typedString();
895             searchStartOffset = 0;
896         }
897     }
898
899     const Vector<Element*>& items = data.listItems(element);
900     int itemCount = items.size();
901     if (itemCount < 1)
902         return;
903
904     int selected = selectedIndex(data, element);
905     int index = (optionToListIndex(data, element, selected >= 0 ? selected : 0) + searchStartOffset) % itemCount;
906     ASSERT(index >= 0);
907
908     // Compute a case-folded copy of the prefix string before beginning the search for
909     // a matching element. This code uses foldCase to work around the fact that
910     // String::startWith does not fold non-ASCII characters. This code can be changed
911     // to use startWith once that is fixed.
912     String prefixWithCaseFolded(prefix.foldCase());
913     for (int i = 0; i < itemCount; ++i, index = (index + 1) % itemCount) {
914         OptionElement* optionElement = toOptionElement(items[index]);
915         if (!optionElement || items[index]->disabled())
916             continue;
917
918         // Fold the option string and check if its prefix is equal to the folded prefix.
919         String text = optionElement->textIndentedToRespectGroupLabel();
920         if (stripLeadingWhiteSpace(text).foldCase().startsWith(prefixWithCaseFolded)) {
921             setSelectedIndex(data, element, listToOptionIndex(data, element, index));
922             if (!data.usesMenuList())
923                 listBoxOnChange(data, element);
924
925             setOptionsChangedOnRenderer(data, element);
926             element->setNeedsStyleRecalc();
927             return;
928         }
929     }
930 }
931
932 void SelectElement::insertedIntoTree(SelectElementData& data, Element* element)
933 {
934     // When the element is created during document parsing, it won't have any items yet - but for innerHTML
935     // and related methods, this method is called after the whole subtree is constructed.
936     recalcListItems(data, element, true);
937 }
938
939 void SelectElement::accessKeySetSelectedIndex(SelectElementData& data, Element* element, int index)
940 {    
941     // first bring into focus the list box
942     if (!element->focused())
943         element->accessKeyAction(false);
944     
945     // if this index is already selected, unselect. otherwise update the selected index
946     const Vector<Element*>& items = data.listItems(element);
947     int listIndex = optionToListIndex(data, element, index);
948     if (OptionElement* optionElement = (listIndex >= 0 ? toOptionElement(items[listIndex]) : 0)) {
949         if (optionElement->selected())
950             optionElement->setSelectedState(false);
951         else
952             setSelectedIndex(data, element, index, false, true);
953     }
954
955     if (data.usesMenuList())
956         menuListOnChange(data, element);
957     else
958         listBoxOnChange(data, element);
959
960     scrollToSelection(data, element);
961 }
962
963 unsigned SelectElement::optionCount(const SelectElementData& data, const Element* element)
964 {
965     unsigned options = 0;
966
967     const Vector<Element*>& items = data.listItems(element);
968     for (unsigned i = 0; i < items.size(); ++i) {
969         if (isOptionElement(items[i]))
970             ++options;
971     }
972
973     return options;
974 }
975
976 // SelectElementData
977 SelectElementData::SelectElementData()
978     : m_multiple(false) 
979     , m_size(0)
980     , m_lastOnChangeIndex(-1)
981     , m_activeSelectionState(false)
982     , m_activeSelectionAnchorIndex(-1)
983     , m_activeSelectionEndIndex(-1)
984     , m_recalcListItems(false)
985     , m_repeatingChar(0)
986     , m_lastCharTime(0)
987 {
988 }
989
990 SelectElementData::~SelectElementData()
991 {
992 }
993
994 void SelectElementData::checkListItems(const Element* element) const
995 {
996 #if !ASSERT_DISABLED
997     Vector<Element*> items = m_listItems;
998     SelectElement::recalcListItems(*const_cast<SelectElementData*>(this), element, false);
999     ASSERT(items == m_listItems);
1000 #else
1001     UNUSED_PARAM(element);
1002 #endif
1003 }
1004
1005 Vector<Element*>& SelectElementData::listItems(const Element* element)
1006 {
1007     if (m_recalcListItems)
1008         SelectElement::recalcListItems(*this, element);
1009     else
1010         checkListItems(element);
1011
1012     return m_listItems;
1013 }
1014
1015 const Vector<Element*>& SelectElementData::listItems(const Element* element) const
1016 {
1017     if (m_recalcListItems)
1018         SelectElement::recalcListItems(*const_cast<SelectElementData*>(this), element);
1019     else
1020         checkListItems(element);
1021
1022     return m_listItems;
1023 }
1024
1025 SelectElement* toSelectElement(Element* element)
1026 {
1027     if (element->isHTMLElement()) {
1028         if (element->hasTagName(HTMLNames::selectTag))
1029             return static_cast<HTMLSelectElement*>(element);
1030         if (element->hasTagName(HTMLNames::keygenTag))
1031             return static_cast<HTMLKeygenElement*>(element);
1032     }
1033
1034 #if ENABLE(WML)
1035     if (element->isWMLElement() && element->hasTagName(WMLNames::selectTag))
1036         return static_cast<WMLSelectElement*>(element);
1037 #endif
1038
1039     return 0;
1040 }
1041
1042 }