Reviewed by Adele.
[WebKit-https.git] / WebCore / html / HTMLSelectElement.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Dirk Mueller (mueller@kde.org)
5  * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
6  *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  */
24  
25 #include "config.h"
26 #include "HTMLSelectElement.h"
27
28 #include "CharacterNames.h"
29 #include "CSSPropertyNames.h"
30 #include "Document.h"
31 #include "Event.h"
32 #include "EventHandler.h"
33 #include "EventNames.h"
34 #include "FormDataList.h"
35 #include "Frame.h"
36 #include "HTMLFormElement.h"
37 #include "HTMLNames.h"
38 #include "HTMLOptionElement.h"
39 #include "HTMLOptionsCollection.h"
40 #include "KeyboardEvent.h"
41 #include "MouseEvent.h"
42 #include "RenderListBox.h"
43 #include "RenderMenuList.h"
44 #include "cssstyleselector.h"
45 #include <wtf/Vector.h>
46 #include <math.h>
47
48 #if PLATFORM(MAC)
49 #define ARROW_KEYS_POP_MENU 1
50 #else
51 #define ARROW_KEYS_POP_MENU 0
52 #endif
53
54 using namespace std;
55 using namespace WTF;
56 using namespace Unicode;
57
58 namespace WebCore {
59
60 using namespace EventNames;
61 using namespace HTMLNames;
62
63 static const DOMTimeStamp typeAheadTimeout = 1000;
64
65 HTMLSelectElement::HTMLSelectElement(Document* doc, HTMLFormElement* f)
66     : HTMLGenericFormElement(selectTag, doc, f)
67     , m_minwidth(0)
68     , m_size(0)
69     , m_multiple(false)
70     , m_recalcListItems(false)
71     , m_lastOnChangeIndex(-1)
72     , m_activeSelectionAnchorIndex(-1)
73     , m_activeSelectionEndIndex(-1)
74     , m_activeSelectionState(false)
75     , m_repeatingChar(0)
76     , m_lastCharTime(0)
77 {
78     document()->registerFormElementWithState(this);
79 }
80
81 HTMLSelectElement::HTMLSelectElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* f)
82     : HTMLGenericFormElement(tagName, doc, f)
83     , m_minwidth(0)
84     , m_size(0)
85     , m_multiple(false)
86     , m_recalcListItems(false)
87     , m_lastOnChangeIndex(-1)
88     , m_activeSelectionAnchorIndex(-1)
89     , m_activeSelectionEndIndex(-1)
90     , m_activeSelectionState(false)
91     , m_repeatingChar(0)
92     , m_lastCharTime(0)
93 {
94     document()->registerFormElementWithState(this);
95 }
96
97 HTMLSelectElement::~HTMLSelectElement()
98 {
99     document()->deregisterFormElementWithState(this);
100 }
101
102 bool HTMLSelectElement::checkDTD(const Node* newChild)
103 {
104     return newChild->isTextNode() || newChild->hasTagName(optionTag) || newChild->hasTagName(optgroupTag) || newChild->hasTagName(hrTag) ||
105            newChild->hasTagName(scriptTag);
106 }
107
108 void HTMLSelectElement::recalcStyle( StyleChange ch )
109 {
110     if (hasChangedChild() && renderer()) {
111         if (usesMenuList())
112             static_cast<RenderMenuList*>(renderer())->setOptionsChanged(true);
113         else
114             static_cast<RenderListBox*>(renderer())->setOptionsChanged(true);
115     }
116
117     HTMLGenericFormElement::recalcStyle( ch );
118 }
119
120 const AtomicString& HTMLSelectElement::type() const
121 {
122     static const AtomicString selectMultiple("select-multiple");
123     static const AtomicString selectOne("select-one");
124     return m_multiple ? selectMultiple : selectOne;
125 }
126
127 int HTMLSelectElement::selectedIndex() const
128 {
129     // return the number of the first option selected
130     unsigned index = 0;
131     const Vector<HTMLElement*>& items = listItems();
132     for (unsigned int i = 0; i < items.size(); i++) {
133         if (items[i]->hasLocalName(optionTag)) {
134             if (static_cast<HTMLOptionElement*>(items[i])->selected())
135                 return index;
136             index++;
137         }
138     }
139     return -1;
140 }
141
142 int HTMLSelectElement::lastSelectedListIndex() const
143 {
144     // return the number of the last option selected
145     unsigned index = 0;
146     bool found = false;
147     const Vector<HTMLElement*>& items = listItems();
148     for (unsigned int i = 0; i < items.size(); i++) {
149         if (items[i]->hasLocalName(optionTag)) {
150             if (static_cast<HTMLOptionElement*>(items[i])->selected()) {
151                 index = i;
152                 found = true;
153             }
154         }
155     }
156     return found ? (int) index : -1;
157 }
158
159 void HTMLSelectElement::deselectItems(HTMLOptionElement* excludeElement)
160 {
161     const Vector<HTMLElement*>& items = listItems();
162     unsigned i;
163     for (i = 0; i < items.size(); i++) {
164         if (items[i]->hasLocalName(optionTag) && (items[i] != excludeElement)) {
165             HTMLOptionElement* element = static_cast<HTMLOptionElement*>(items[i]);
166             element->m_selected = false;
167             element->setChanged();
168         }
169     }
170 }
171
172 void HTMLSelectElement::setSelectedIndex(int optionIndex, bool deselect, bool fireOnChange)
173 {
174     const Vector<HTMLElement*>& items = listItems();
175     int listIndex = optionToListIndex(optionIndex);
176     HTMLOptionElement* element = 0;
177
178     // Make a local copy, since setSelected() can update m_lastOnChangeIndex.
179     int lastOnChangeIndex = m_lastOnChangeIndex;
180
181     if (listIndex >= 0) {
182         element = static_cast<HTMLOptionElement*>(items[listIndex]);
183         element->setSelected(true);
184     } else if (deselect)
185         m_lastOnChangeIndex = -1;
186
187     if (deselect)
188         deselectItems(element);
189
190     if (listIndex >= 0) {
191         if (m_activeSelectionAnchorIndex < 0 || deselect)
192             setActiveSelectionAnchorIndex(listIndex);
193         if (m_activeSelectionEndIndex < 0 || deselect)
194             setActiveSelectionEndIndex(listIndex);
195     }
196
197     ASSERT(m_lastOnChangeIndex == -1 || m_lastOnChangeIndex == optionIndex);
198     if (fireOnChange && usesMenuList() && lastOnChangeIndex != optionIndex)
199         onChange();
200 }
201
202 int HTMLSelectElement::activeSelectionStartListIndex() const
203 {
204     if (m_activeSelectionAnchorIndex >= 0)
205         return m_activeSelectionAnchorIndex;
206     return optionToListIndex(selectedIndex());
207 }
208
209 int HTMLSelectElement::activeSelectionEndListIndex() const
210 {
211     if (m_activeSelectionEndIndex >= 0)
212         return m_activeSelectionEndIndex;
213     return lastSelectedListIndex();
214 }
215
216 unsigned HTMLSelectElement::length() const
217 {
218     unsigned len = 0;
219     const Vector<HTMLElement*>& items = listItems();
220     for (unsigned i = 0; i < items.size(); ++i) {
221         if (items[i]->hasLocalName(optionTag))
222             ++len;
223     }
224     return len;
225 }
226
227 void HTMLSelectElement::add( HTMLElement *element, HTMLElement *before, ExceptionCode& ec)
228 {
229     RefPtr<HTMLElement> protectNewChild(element); // make sure the element is ref'd and deref'd so we don't leak it
230
231     if (!element || !(element->hasLocalName(optionTag) || element->hasLocalName(hrTag)))
232         return;
233
234     insertBefore(element, before, ec);
235     if (!ec)
236         setRecalcListItems();
237 }
238
239 void HTMLSelectElement::remove(int index)
240 {
241     ExceptionCode ec = 0;
242     int listIndex = optionToListIndex(index);
243
244     const Vector<HTMLElement*>& items = listItems();
245     if (listIndex < 0 || index >= int(items.size()))
246         return; // ### what should we do ? remove the last item?
247
248     removeChild(items[listIndex], ec);
249     if (!ec)
250         setRecalcListItems();
251 }
252
253 String HTMLSelectElement::value()
254 {
255     unsigned i;
256     const Vector<HTMLElement*>& items = listItems();
257     for (i = 0; i < items.size(); i++) {
258         if (items[i]->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(items[i])->selected())
259             return static_cast<HTMLOptionElement*>(items[i])->value();
260     }
261     return String("");
262 }
263
264 void HTMLSelectElement::setValue(const String &value)
265 {
266     if (value.isNull())
267         return;
268     // find the option with value() matching the given parameter
269     // and make it the current selection.
270     const Vector<HTMLElement*>& items = listItems();
271     for (unsigned i = 0; i < items.size(); i++)
272         if (items[i]->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(items[i])->value() == value) {
273             static_cast<HTMLOptionElement*>(items[i])->setSelected(true);
274             return;
275         }
276 }
277
278 String HTMLSelectElement::stateValue() const
279 {
280     const Vector<HTMLElement*>& items = listItems();
281     int l = items.size();
282     Vector<char, 1024> characters(l);
283     for (int i = 0; i < l; ++i) {
284         HTMLElement* e = items[i];
285         bool selected = e->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(e)->selected();
286         characters[i] = selected ? 'X' : '.';
287     }
288     return String(characters, l);
289 }
290
291 void HTMLSelectElement::restoreState(const String& state)
292 {
293     recalcListItems();
294
295     const Vector<HTMLElement*>& items = listItems();
296     int l = items.size();
297     for (int i = 0; i < l; i++)
298         if (items[i]->hasLocalName(optionTag))
299             static_cast<HTMLOptionElement*>(items[i])->setSelected(state[i] == 'X');
300     setChanged(true);
301 }
302
303 bool HTMLSelectElement::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec)
304 {
305     bool result = HTMLGenericFormElement::insertBefore(newChild, refChild, ec);
306     if (result)
307         setRecalcListItems();
308     return result;
309 }
310
311 bool HTMLSelectElement::replaceChild(PassRefPtr<Node> newChild, Node *oldChild, ExceptionCode& ec)
312 {
313     bool result = HTMLGenericFormElement::replaceChild(newChild, oldChild, ec);
314     if (result)
315         setRecalcListItems();
316     return result;
317 }
318
319 bool HTMLSelectElement::removeChild(Node* oldChild, ExceptionCode& ec)
320 {
321     bool result = HTMLGenericFormElement::removeChild(oldChild, ec);
322     if (result)
323         setRecalcListItems();
324     return result;
325 }
326
327 bool HTMLSelectElement::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec)
328 {
329     bool result = HTMLGenericFormElement::appendChild(newChild, ec);
330     if (result)
331         setRecalcListItems();
332     return result;
333 }
334
335 ContainerNode* HTMLSelectElement::addChild(PassRefPtr<Node> newChild)
336 {
337     ContainerNode* result = HTMLGenericFormElement::addChild(newChild);
338     if (result)
339         setRecalcListItems();
340     return result;
341 }
342
343 void HTMLSelectElement::parseMappedAttribute(MappedAttribute *attr)
344 {
345     bool oldUsesMenuList = usesMenuList();
346     if (attr->name() == sizeAttr) {
347         // Set the attribute value to a number.
348         // This is important since the style rules for this attribute can determine the appearance property.
349         int size = attr->value().toInt();
350         String attrSize = String::number(size);
351         if (attrSize != attr->value())
352             attr->setValue(attrSize);
353
354         m_size = max(size, 1);
355         if (oldUsesMenuList != usesMenuList() && attached()) {
356             detach();
357             attach();
358         }
359     } else if (attr->name() == widthAttr) {
360         m_minwidth = max(attr->value().toInt(), 0);
361     } else if (attr->name() == multipleAttr) {
362         m_multiple = (!attr->isNull());
363         if (oldUsesMenuList != usesMenuList() && attached()) {
364             detach();
365             attach();
366         }
367     } else if (attr->name() == accesskeyAttr) {
368         // FIXME: ignore for the moment
369     } else if (attr->name() == alignAttr) {
370         // Don't map 'align' attribute.  This matches what Firefox, Opera and IE do.
371         // See http://bugs.webkit.org/show_bug.cgi?id=12072
372     } else if (attr->name() == onfocusAttr) {
373         setHTMLEventListener(focusEvent, attr);
374     } else if (attr->name() == onblurAttr) {
375         setHTMLEventListener(blurEvent, attr);
376     } else if (attr->name() == onchangeAttr) {
377         setHTMLEventListener(changeEvent, attr);
378     } else
379         HTMLGenericFormElement::parseMappedAttribute(attr);
380 }
381
382 bool HTMLSelectElement::isKeyboardFocusable(KeyboardEvent* event) const
383 {
384     if (renderer())
385         return isFocusable();
386     return HTMLGenericFormElement::isKeyboardFocusable(event);
387 }
388
389 bool HTMLSelectElement::isMouseFocusable() const
390 {
391     if (renderer())
392         return isFocusable();
393     return HTMLGenericFormElement::isMouseFocusable();
394 }
395
396 bool HTMLSelectElement::canSelectAll() const
397 {
398     return !usesMenuList() && renderer() && renderer()->canSelect(); 
399 }
400
401 void HTMLSelectElement::selectAll()
402 {
403     ASSERT(!usesMenuList());
404     if (!renderer() || !multiple())
405         return;
406     
407     m_activeSelectionState = true;
408     setActiveSelectionAnchorIndex(nextSelectableListIndex(-1));
409     setActiveSelectionEndIndex(previousSelectableListIndex(-1));
410     
411     updateListBoxSelection(false);
412     listBoxOnChange();
413 }
414
415 RenderObject *HTMLSelectElement::createRenderer(RenderArena *arena, RenderStyle *style)
416 {
417     if (usesMenuList())
418         return new (arena) RenderMenuList(this);
419     return new (arena) RenderListBox(this);
420 }
421
422 bool HTMLSelectElement::appendFormData(FormDataList& list, bool)
423 {
424     bool successful = false;
425     const Vector<HTMLElement*>& items = listItems();
426
427     unsigned i;
428     for (i = 0; i < items.size(); i++) {
429         if (items[i]->hasLocalName(optionTag)) {
430             HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[i]);
431             if (option->selected()) {
432                 list.appendData(name(), option->value());
433                 successful = true;
434             }
435         }
436     }
437
438     // ### this case should not happen. make sure that we select the first option
439     // in any case. otherwise we have no consistency with the DOM interface. FIXME!
440     // we return the first one if it was a combobox select
441     if (!successful && !m_multiple && m_size <= 1 && items.size() &&
442         (items[0]->hasLocalName(optionTag))) {
443         HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[0]);
444         if (option->value().isNull())
445             list.appendData(name(), option->text().stripWhiteSpace());
446         else
447             list.appendData(name(), option->value());
448         successful = true;
449     }
450
451     return successful;
452 }
453
454 int HTMLSelectElement::optionToListIndex(int optionIndex) const
455 {
456     const Vector<HTMLElement*>& items = listItems();
457     int listSize = (int)items.size();
458     if (optionIndex < 0 || optionIndex >= listSize)
459         return -1;
460
461     int optionIndex2 = -1;
462     for (int listIndex = 0; listIndex < listSize; listIndex++) {
463         if (items[listIndex]->hasLocalName(optionTag)) {
464             optionIndex2++;
465             if (optionIndex2 == optionIndex)
466                 return listIndex;
467         }
468     }
469     return -1;
470 }
471
472 int HTMLSelectElement::listToOptionIndex(int listIndex) const
473 {
474     const Vector<HTMLElement*>& items = listItems();
475     if (listIndex < 0 || listIndex >= int(items.size()) ||
476         !items[listIndex]->hasLocalName(optionTag))
477         return -1;
478
479     int optionIndex = 0; // actual index of option not counting OPTGROUP entries that may be in list
480     for (int i = 0; i < listIndex; i++)
481         if (items[i]->hasLocalName(optionTag))
482             optionIndex++;
483     return optionIndex;
484 }
485
486 PassRefPtr<HTMLOptionsCollection> HTMLSelectElement::options()
487 {
488     return new HTMLOptionsCollection(this);
489 }
490
491 void HTMLSelectElement::recalcListItems() const
492 {
493     Node* current = firstChild();
494     m_listItems.clear();
495     HTMLOptionElement* foundSelected = 0;
496     while (current) {
497         if (current->hasTagName(optgroupTag) && current->firstChild()) {
498             // ### what if optgroup contains just comments? don't want one of no options in it...
499             m_listItems.append(static_cast<HTMLElement*>(current));
500             current = current->firstChild();
501         }
502         if (current->hasTagName(optionTag)) {
503             m_listItems.append(static_cast<HTMLElement*>(current));
504             if (!foundSelected && (usesMenuList() || (!m_multiple && static_cast<HTMLOptionElement*>(current)->selected()))) {
505                 foundSelected = static_cast<HTMLOptionElement*>(current);
506                 foundSelected->m_selected = true;
507             } else if (foundSelected && !m_multiple && static_cast<HTMLOptionElement*>(current)->selected()) {
508                 foundSelected->m_selected = false;
509                 foundSelected = static_cast<HTMLOptionElement*>(current);
510             }
511         }
512         if (current->hasTagName(hrTag))
513             m_listItems.append(static_cast<HTMLElement*>(current));
514
515         Node* parent = current->parentNode();
516         current = current->nextSibling();
517         if (!current) {
518             if (parent != this)
519                 current = parent->nextSibling();
520         }
521     }
522     m_recalcListItems = false;
523 }
524
525 void HTMLSelectElement::childrenChanged()
526 {
527     setRecalcListItems();
528
529     HTMLGenericFormElement::childrenChanged();
530 }
531
532 void HTMLSelectElement::setRecalcListItems()
533 {
534     m_recalcListItems = true;
535     if (renderer()) {
536         if (usesMenuList())
537             static_cast<RenderMenuList*>(renderer())->setOptionsChanged(true);
538         else
539             static_cast<RenderListBox*>(renderer())->setOptionsChanged(true);
540     }
541     if (!inDocument())
542         m_collectionInfo.reset();
543     setChanged();
544 }
545
546 void HTMLSelectElement::reset()
547 {
548     bool optionSelected = false;
549     HTMLOptionElement* firstOption = 0;
550     const Vector<HTMLElement*>& items = listItems();
551     unsigned i;
552     for (i = 0; i < items.size(); i++) {
553         if (items[i]->hasLocalName(optionTag)) {
554             HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[i]);
555             if (!option->getAttribute(selectedAttr).isNull()) {
556                 option->setSelected(true);
557                 optionSelected = true;
558             } else
559                 option->setSelected(false);
560             if (!firstOption)
561                 firstOption = option;
562         }
563     }
564     if (!optionSelected && firstOption && usesMenuList())
565         firstOption->setSelected(true);
566     setChanged(true);
567     m_lastOnChangeIndex = -1;
568 }
569
570 void HTMLSelectElement::notifyOptionSelected(HTMLOptionElement* selectedOption, bool selected)
571 {
572     if (selected && !m_multiple)
573         deselectItems(selectedOption);
574
575     scrollToSelection();
576
577     m_lastOnChangeIndex = selectedOption->index();
578     setChanged(true);
579 }
580
581 void HTMLSelectElement::dispatchBlurEvent()
582 {
583 #if !ARROW_KEYS_POP_MENU
584     if (usesMenuList() && selectedIndex() != m_lastOnChangeIndex) {
585         m_lastOnChangeIndex = selectedIndex();
586         onChange();
587     }
588 #endif
589     HTMLGenericFormElement::dispatchBlurEvent();
590 }
591
592 void HTMLSelectElement::defaultEventHandler(Event* evt)
593 {
594     if (usesMenuList())
595         menuListDefaultEventHandler(evt);
596     else 
597         listBoxDefaultEventHandler(evt);
598     
599     if (evt->defaultHandled())
600         return;
601
602     if (!evt->defaultHandled() && evt->type() == keypressEvent && evt->isKeyboardEvent()) {
603         KeyboardEvent* keyboardEvent = static_cast<KeyboardEvent*>(evt);
604     
605         if (!keyboardEvent->ctrlKey() && !keyboardEvent->altKey() && !keyboardEvent->metaKey() &&
606             isPrintableChar(keyboardEvent->charCode())) {
607             typeAheadFind(keyboardEvent);
608             evt->setDefaultHandled();
609             return;
610         }
611     }
612
613     HTMLGenericFormElement::defaultEventHandler(evt);
614 }
615
616 void HTMLSelectElement::menuListDefaultEventHandler(Event* evt)
617 {
618     RenderMenuList* menuList = static_cast<RenderMenuList*>(renderer());
619
620     // Use key press event here since sending simulated mouse events
621     // on key down blocks the proper sending of the key press event.
622     if (evt->type() == keypressEvent) {
623         if (!renderer() || !evt->isKeyboardEvent())
624             return;
625         String keyIdentifier = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
626         bool handled = false;
627 #if ARROW_KEYS_POP_MENU
628         if (form() && keyIdentifier == "Enter") {
629             blur();
630             // Make sure the form hasn't been destroyed during the blur.
631             if (form())
632                 form()->submitClick(evt);
633             handled = true;
634         }
635         if ((keyIdentifier == "Down" || keyIdentifier == "Up" || keyIdentifier == "U+000020") && renderer() && usesMenuList()) {
636             focus();
637             menuList->showPopup();
638             handled = true;
639         }
640 #else
641         int listIndex = optionToListIndex(selectedIndex());
642         if (keyIdentifier == "Down" || keyIdentifier == "Right") {
643             size_t size = listItems().size();
644             for (listIndex += 1;
645                  listIndex >= 0 && listIndex < size && (listItems()[listIndex]->disabled() || !listItems()[listIndex]->hasTagName(optionTag));
646                  ++listIndex);
647             
648             if (listIndex >= 0 && listIndex < size)
649                 setSelectedIndex(listToOptionIndex(listIndex));
650             handled = true;
651         } else if (keyIdentifier == "Up" || keyIdentifier == "Left") {
652             size_t size = listItems().size();
653             for (listIndex -= 1;
654                  listIndex >= 0 && listIndex < size && (listItems()[listIndex]->disabled() || !listItems()[listIndex]->hasTagName(optionTag));
655                  --listIndex);
656             
657             if (listIndex >= 0 && listIndex < size)
658                 setSelectedIndex(listToOptionIndex(listIndex));
659             handled = true;
660         } else if (keyIdentifier == "Enter") {
661             setSelectedIndex(listToOptionIndex(listIndex), true, true);
662         }
663 #endif
664         if (handled)
665             evt->setDefaultHandled();
666
667     }
668     if (evt->type() == mousedownEvent) {
669         focus();
670         if (menuList->popupIsVisible())
671             menuList->hidePopup();
672         else
673             menuList->showPopup();
674         evt->setDefaultHandled();
675     }
676 }
677
678 void HTMLSelectElement::listBoxDefaultEventHandler(Event* evt)
679 {
680     if (!renderer() || !renderer()->canSelect())
681         return;
682
683     if (evt->type() == mousedownEvent) {
684         focus();
685         MouseEvent* mEvt = static_cast<MouseEvent*>(evt);
686         int listIndex = static_cast<RenderListBox*>(renderer())->listIndexAtOffset(mEvt->offsetX(), mEvt->offsetY());
687         if (listIndex >= 0) {
688             m_activeSelectionState = true;
689             
690             bool multiSelectKeyPressed = false;
691 #if PLATFORM(MAC)
692             multiSelectKeyPressed = mEvt->metaKey();
693 #else
694             multiSelectKeyPressed = mEvt->ctrlKey();
695 #endif
696
697             bool shiftSelect = multiple() && mEvt->shiftKey();
698             bool multiSelect = multiple() && multiSelectKeyPressed && !mEvt->shiftKey();
699             
700             HTMLElement* clickedElement = listItems()[listIndex];            
701             HTMLOptionElement* option = 0;
702             if (clickedElement->hasLocalName(optionTag)) {
703                 option = static_cast<HTMLOptionElement*>(clickedElement);
704                 
705                 // Keep track of whether an active selection (like during drag selection), should select or deselect
706                 if (option->selected() && multiSelectKeyPressed)
707                     m_activeSelectionState = false;
708
709                 if (!m_activeSelectionState)
710                     option->m_selected = false;
711             }
712             
713             // If we're not in any special multiple selection mode, then deselect all other items, excluding the clicked option.
714             // If no option was clicked, then this will deselect all items in the list.
715             if (!shiftSelect && !multiSelect)
716                 deselectItems(option);
717
718             // 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.
719             if (m_activeSelectionAnchorIndex < 0 && !multiSelect)
720                 setActiveSelectionAnchorIndex(selectedIndex());
721
722             // Set the selection state of the clicked option
723             if (option && !option->disabled())
724                 option->m_selected = true;
725             
726             // If there was no selectedIndex() for the previous initialization, or
727             // 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.
728             if (listIndex >= 0 && (m_activeSelectionAnchorIndex < 0 || !shiftSelect))
729                 setActiveSelectionAnchorIndex(listIndex);
730             
731             setActiveSelectionEndIndex(listIndex);
732             updateListBoxSelection(!multiSelect);
733
734             if (Frame* frame = document()->frame())
735                 frame->eventHandler()->setMouseDownMayStartAutoscroll();
736
737             evt->setDefaultHandled();
738         }
739     } else if (evt->type() == mouseupEvent && document()->frame()->eventHandler()->autoscrollRenderer() != renderer())
740         // This makes sure we fire onChange for a single click.  For drag selection, onChange will fire when the autoscroll timer stops.
741         listBoxOnChange();
742     else if (evt->type() == keypressEvent) {
743         if (!evt->isKeyboardEvent())
744             return;
745         String keyIdentifier = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
746         
747         if (form() && keyIdentifier == "Enter") {
748             blur();
749             // Make sure the form hasn't been destroyed during the blur.
750             if (form())
751                 form()->submitClick(evt);
752             evt->setDefaultHandled();
753             return;
754         }
755
756         int endIndex = 0;        
757         if (m_activeSelectionEndIndex < 0) {
758             // Initialize the end index
759             if (keyIdentifier == "Down")
760                 endIndex = nextSelectableListIndex(lastSelectedListIndex());
761             else if (keyIdentifier == "Up")
762                 endIndex = previousSelectableListIndex(optionToListIndex(selectedIndex()));
763         } else {
764             // Set the end index based on the current end index
765             if (keyIdentifier == "Down")
766                 endIndex = nextSelectableListIndex(m_activeSelectionEndIndex);
767             else if (keyIdentifier == "Up")
768                 endIndex = previousSelectableListIndex(m_activeSelectionEndIndex);    
769         }
770         
771         if (keyIdentifier == "Down" || keyIdentifier == "Up") {
772             ASSERT(endIndex >= 0 && (unsigned)endIndex < listItems().size()); 
773             setActiveSelectionEndIndex(endIndex);
774             
775             // 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.
776             bool deselectOthers = !multiple() || !static_cast<KeyboardEvent*>(evt)->shiftKey();
777             if (m_activeSelectionAnchorIndex < 0 || deselectOthers) {
778                 m_activeSelectionState = true;
779                 if (deselectOthers)
780                     deselectItems();
781                 setActiveSelectionAnchorIndex(m_activeSelectionEndIndex);
782             }
783
784             static_cast<RenderListBox*>(renderer())->scrollToRevealElementAtListIndex(endIndex);
785             updateListBoxSelection(deselectOthers);
786             listBoxOnChange();
787             evt->setDefaultHandled();
788         }
789     }
790 }
791
792 void HTMLSelectElement::setActiveSelectionAnchorIndex(int index)
793 {
794     m_activeSelectionAnchorIndex = index;
795     
796     // Cache the selection state so we can restore the old selection as the new selection pivots around this anchor index
797     const Vector<HTMLElement*>& items = listItems();
798     m_cachedStateForActiveSelection.clear();
799     for (unsigned i = 0; i < items.size(); i++) {
800         if (items[i]->hasLocalName(optionTag)) {
801             HTMLOptionElement* option = static_cast<HTMLOptionElement*>(items[i]);
802             m_cachedStateForActiveSelection.append(option->selected());
803         } else
804             m_cachedStateForActiveSelection.append(false);
805     }
806 }
807
808 void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions)
809 {
810     ASSERT(renderer() && renderer()->isListBox());
811     
812     unsigned start;
813     unsigned end;
814     ASSERT(m_activeSelectionAnchorIndex >= 0);
815     start = min(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex);
816     end = max(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex);
817
818     const Vector<HTMLElement*>& items = listItems();
819     for (unsigned i = 0; i < items.size(); i++) {
820         if (items[i]->hasLocalName(optionTag)) {
821             HTMLOptionElement* option = static_cast<HTMLOptionElement*>(items[i]);
822             if (!option->disabled()) {
823                 if (i >= start && i <= end)
824                     option->m_selected = m_activeSelectionState;
825                 else if (deselectOtherOptions)
826                     option->m_selected = false;
827                 else
828                     option->m_selected = m_cachedStateForActiveSelection[i];
829             }
830         }
831     }
832
833     scrollToSelection();
834 }
835
836 void HTMLSelectElement::listBoxOnChange()
837 {
838     const Vector<HTMLElement*>& items = listItems();
839     
840     // If the cached selection list is empty, or the size has changed, then rebuild the list, fire onChange, and return early.
841     if (m_lastOnChangeSelection.isEmpty() || m_lastOnChangeSelection.size() != items.size()) {
842         m_lastOnChangeSelection.clear();
843         for (unsigned i = 0; i < items.size(); i++) {
844             if (items[i]->hasLocalName(optionTag)) {
845                 HTMLOptionElement* option = static_cast<HTMLOptionElement*>(items[i]);
846                 m_lastOnChangeSelection.append(option->selected());
847             } else
848                 m_lastOnChangeSelection.append(false);
849         }
850         onChange();
851         return;
852     }
853     
854     // Update m_lastOnChangeSelection and fire onChange
855     bool fireOnChange = false;
856     for (unsigned i = 0; i < items.size(); i++) {
857         bool selected = false;
858         if (items[i]->hasLocalName(optionTag))
859             selected = static_cast<HTMLOptionElement*>(items[i])->selected();
860         if (selected != m_lastOnChangeSelection[i])      
861             fireOnChange = true;
862         m_lastOnChangeSelection[i] = selected;
863     }
864     if (fireOnChange)
865         onChange();
866 }
867
868 static String stripLeadingWhiteSpace(const String& string)
869 {
870     int length = string.length();
871     int i;
872     for (i = 0; i < length; ++i)
873         if (string[i] != noBreakSpace &&
874             (string[i] <= 0x7F ? !isspace(string[i]) : (direction(string[i]) != WhiteSpaceNeutral)))
875             break;
876
877     return string.substring(i, length - i);
878 }
879
880 void HTMLSelectElement::typeAheadFind(KeyboardEvent* event)
881 {
882     if (event->timeStamp() < m_lastCharTime)
883         return;
884
885     DOMTimeStamp delta = event->timeStamp() - m_lastCharTime;
886
887     m_lastCharTime = event->timeStamp();
888
889     UChar c = event->charCode();
890
891     String prefix;
892     int searchStartOffset = 1;
893     if (delta > typeAheadTimeout) {
894         m_typedString = prefix = String(&c, 1);
895         m_repeatingChar = c;
896     } else {
897         m_typedString.append(c);
898
899         if (c == m_repeatingChar)
900             // The user is likely trying to cycle through all the items starting with this character, so just search on the character
901             prefix = String(&c, 1);
902         else {
903             m_repeatingChar = 0;
904             prefix = m_typedString;
905             searchStartOffset = 0;
906         }
907     }
908
909     const Vector<HTMLElement*>& items = listItems();
910     int itemCount = items.size();
911
912     int index = (optionToListIndex(selectedIndex()) + searchStartOffset) % itemCount;
913     for (int i = 0; i < itemCount; i++, index = (index + 1) % itemCount) {
914         if (!items[index]->hasTagName(optionTag) || items[index]->disabled())
915             continue;
916
917         if (stripLeadingWhiteSpace(static_cast<HTMLOptionElement*>(items[index])->optionText()).startsWith(prefix, false)) {
918             setSelectedIndex(listToOptionIndex(index));
919             setChanged();
920             return;
921         }
922     }
923 }
924
925 int HTMLSelectElement::nextSelectableListIndex(int startIndex)
926 {
927     const Vector<HTMLElement*>& items = listItems();
928     int index = startIndex + 1;
929     while (index >= 0 && (unsigned)index < items.size() && (!items[index]->hasLocalName(optionTag) || items[index]->disabled()))
930         index++;
931     if ((unsigned) index == items.size())
932         return startIndex;
933     return index;
934 }
935
936 int HTMLSelectElement::previousSelectableListIndex(int startIndex)
937 {
938     const Vector<HTMLElement*>& items = listItems();
939     if (startIndex == -1)
940         startIndex = items.size();
941     int index = startIndex - 1;
942     while (index >= 0 && (unsigned)index < items.size() && (!items[index]->hasLocalName(optionTag) || items[index]->disabled()))
943         index--;
944     if (index == -1)
945         return startIndex;
946     return index;
947 }
948
949 void HTMLSelectElement::accessKeyAction(bool sendToAnyElement)
950 {
951     focus();
952     dispatchSimulatedClick(0, sendToAnyElement);
953 }
954
955 void HTMLSelectElement::setMultiple(bool multiple)
956 {
957     setAttribute(multipleAttr, multiple ? "" : 0);
958 }
959
960 void HTMLSelectElement::setSize(int size)
961 {
962     setAttribute(sizeAttr, String::number(size));
963 }
964
965 Node* HTMLSelectElement::namedItem(const String &name, bool caseSensitive)
966 {
967     return options()->namedItem(name, caseSensitive);
968 }
969
970 Node* HTMLSelectElement::item(unsigned index)
971 {
972     return options()->item(index);
973 }
974
975 void HTMLSelectElement::setOption(unsigned index, HTMLOptionElement* option, ExceptionCode& ec)
976 {
977     ec = 0;
978     if (index > INT_MAX)
979         index = INT_MAX;
980     int diff = index  - length();
981     HTMLElement* before = 0;
982     // out of array bounds ? first insert empty dummies
983     if (diff > 0) {
984         setLength(index, ec);
985         // replace an existing entry ?
986     } else if (diff < 0) {
987         before = static_cast<HTMLElement*>(options()->item(index+1));
988         remove(index);
989     }
990     // finally add the new element
991     if (ec == 0) {
992         add(option, before, ec);
993         if (diff >= 0 && option->selected())
994             setSelectedIndex(index, !m_multiple);
995     }
996 }
997
998 void HTMLSelectElement::setLength(unsigned newLen, ExceptionCode& ec)
999 {
1000     ec = 0;
1001     if (newLen > INT_MAX)
1002         newLen = INT_MAX;
1003     int diff = length() - newLen;
1004
1005     if (diff < 0) { // add dummy elements
1006         do {
1007             RefPtr<Element> option = ownerDocument()->createElement("option", ec);
1008             if (!option)
1009                 break;
1010             add(static_cast<HTMLElement*>(option.get()), 0, ec);
1011             if (ec)
1012                 break;
1013         } while (++diff);
1014     }
1015     else // remove elements
1016         while (diff-- > 0)
1017             remove(newLen);
1018 }
1019
1020 void HTMLSelectElement::scrollToSelection()
1021 {
1022     if (renderer() && !usesMenuList())
1023         static_cast<RenderListBox*>(renderer())->selectionChanged();
1024 }
1025
1026 } // namespace