Reviewed by Hyatt.
[WebKit-https.git] / WebCore / html / HTMLSelectElement.cpp
1 /*
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  *           (C) 2001 Dirk Mueller (mueller@kde.org)
7  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
8  *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  *
25  */
26  
27 #include "config.h"
28 #include "HTMLSelectElement.h"
29
30 #include "CSSPropertyNames.h"
31 #include "Document.h"
32 #include "DeprecatedRenderSelect.h"
33 #include "Event.h"
34 #include "EventNames.h"
35 #include "FormDataList.h"
36 #include "Frame.h"
37 #include "HTMLFormElement.h"
38 #include "HTMLNames.h"
39 #include "HTMLOptionElement.h"
40 #include "HTMLOptionsCollection.h"
41 #include "KeyboardEvent.h"
42 #include "MouseEvent.h"
43 #include "RenderListBox.h"
44 #include "RenderMenuList.h"
45 #include "RenderPopupMenu.h"
46 #include "cssstyleselector.h"
47 #include <wtf/Vector.h>
48 #include <math.h>
49
50 #if PLATFORM(MAC)
51 #define ARROW_KEYS_POP_MENU 1
52 #else
53 #define ARROW_KEYS_POP_MENU 0
54 #endif
55
56 using namespace std;
57
58 namespace WebCore {
59
60 using namespace EventNames;
61 using namespace HTMLNames;
62
63 HTMLSelectElement::HTMLSelectElement(Document* doc, HTMLFormElement* f)
64     : HTMLGenericFormElement(selectTag, doc, f)
65     , m_minwidth(0)
66     , m_size(0)
67     , m_multiple(false)
68     , m_recalcListItems(false)
69 {
70     document()->registerFormElementWithState(this);
71 }
72
73 HTMLSelectElement::HTMLSelectElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* f)
74     : HTMLGenericFormElement(tagName, doc, f), m_minwidth(0), m_size(0), m_multiple(false), m_recalcListItems(false)
75 {
76     document()->registerFormElementWithState(this);
77 }
78
79 HTMLSelectElement::~HTMLSelectElement()
80 {
81     document()->deregisterFormElementWithState(this);
82 }
83
84 bool HTMLSelectElement::checkDTD(const Node* newChild)
85 {
86     return newChild->isTextNode() || newChild->hasTagName(optionTag) || newChild->hasTagName(optgroupTag) || newChild->hasTagName(hrTag) ||
87            newChild->hasTagName(scriptTag);
88 }
89
90 void HTMLSelectElement::recalcStyle( StyleChange ch )
91 {
92     if (hasChangedChild() && renderer()) {
93         if (usesMenuList())
94             static_cast<RenderMenuList*>(renderer())->setOptionsChanged(true);
95         else if (renderer() && renderer()->style()->appearance() == ListboxAppearance)
96             static_cast<RenderListBox*>(renderer())->setOptionsChanged(true);
97         else
98             static_cast<DeprecatedRenderSelect*>(renderer())->setOptionsChanged(true);
99     }
100
101     HTMLGenericFormElement::recalcStyle( ch );
102 }
103
104 const AtomicString& HTMLSelectElement::type() const
105 {
106     static const AtomicString selectMultiple("select-multiple");
107     static const AtomicString selectOne("select-one");
108     return m_multiple ? selectMultiple : selectOne;
109 }
110
111 int HTMLSelectElement::selectedIndex() const
112 {
113     // return the number of the first option selected
114     unsigned index = 0;
115     const Vector<HTMLElement*>& items = listItems();
116     for (unsigned int i = 0; i < items.size(); i++) {
117         if (items[i]->hasLocalName(optionTag)) {
118             if (static_cast<HTMLOptionElement*>(items[i])->selected())
119                 return index;
120             index++;
121         }
122     }
123     return -1;
124 }
125
126 int HTMLSelectElement::lastSelectedListIndex() const
127 {
128     // return the number of the last option selected
129     unsigned index = 0;
130     bool found = false;
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                 index = i;
136                 found = true;
137             }
138         }
139     }
140     return found ? (int) index : -1;
141 }
142
143 void HTMLSelectElement::deselectItems(HTMLOptionElement* excludeElement)
144 {
145     const Vector<HTMLElement*>& items = listItems();
146     unsigned i;
147     for (i = 0; i < items.size(); i++) {
148         if (items[i]->hasLocalName(optionTag) && (items[i] != excludeElement)) {
149             HTMLOptionElement* element = static_cast<HTMLOptionElement*>(items[i]);
150             element->m_selected = false;
151             element->setChanged();
152         }
153     }
154 }
155
156 void HTMLSelectElement::setSelectedIndex(int index, bool deselect)
157 {
158     const Vector<HTMLElement*>& items = listItems();
159     int listIndex = optionToListIndex(index);
160     HTMLOptionElement* element = 0;
161     if (listIndex >= 0) {
162         element = static_cast<HTMLOptionElement*>(items[listIndex]);
163         element->setSelected(true);
164     }
165     if (deselect)
166         deselectItems(element);
167 }
168
169 int HTMLSelectElement::length() const
170 {
171     int len = 0;
172     unsigned i;
173     const Vector<HTMLElement*>& items = listItems();
174     for (i = 0; i < items.size(); i++) {
175         if (items[i]->hasLocalName(optionTag))
176             len++;
177     }
178     return len;
179 }
180
181 void HTMLSelectElement::add( HTMLElement *element, HTMLElement *before, ExceptionCode& ec)
182 {
183     RefPtr<HTMLElement> protectNewChild(element); // make sure the element is ref'd and deref'd so we don't leak it
184
185     if (!element || !(element->hasLocalName(optionTag) || element->hasLocalName(hrTag)))
186         return;
187
188     insertBefore(element, before, ec);
189     if (!ec)
190         setRecalcListItems();
191 }
192
193 void HTMLSelectElement::remove(int index)
194 {
195     ExceptionCode ec = 0;
196     int listIndex = optionToListIndex(index);
197
198     const Vector<HTMLElement*>& items = listItems();
199     if (listIndex < 0 || index >= int(items.size()))
200         return; // ### what should we do ? remove the last item?
201
202     removeChild(items[listIndex], ec);
203     if (!ec)
204         setRecalcListItems();
205 }
206
207 String HTMLSelectElement::value()
208 {
209     unsigned i;
210     const Vector<HTMLElement*>& items = listItems();
211     for (i = 0; i < items.size(); i++) {
212         if (items[i]->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(items[i])->selected())
213             return static_cast<HTMLOptionElement*>(items[i])->value();
214     }
215     return String("");
216 }
217
218 void HTMLSelectElement::setValue(const String &value)
219 {
220     if (value.isNull())
221         return;
222     // find the option with value() matching the given parameter
223     // and make it the current selection.
224     const Vector<HTMLElement*>& items = listItems();
225     for (unsigned i = 0; i < items.size(); i++)
226         if (items[i]->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(items[i])->value() == value) {
227             static_cast<HTMLOptionElement*>(items[i])->setSelected(true);
228             return;
229         }
230 }
231
232 String HTMLSelectElement::stateValue() const
233 {
234     const Vector<HTMLElement*>& items = listItems();
235     int l = items.size();
236     Vector<char, 1024> characters(l);
237     for (int i = 0; i < l; ++i) {
238         HTMLElement* e = items[i];
239         bool selected = e->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(e)->selected();
240         characters[i] = selected ? 'X' : '.';
241     }
242     return String(characters, l);
243 }
244
245 void HTMLSelectElement::restoreState(const String& state)
246 {
247     recalcListItems();
248
249     const Vector<HTMLElement*>& items = listItems();
250     int l = items.size();
251     for (int i = 0; i < l; i++)
252         if (items[i]->hasLocalName(optionTag))
253             static_cast<HTMLOptionElement*>(items[i])->setSelected(state[i] == 'X');
254     setChanged(true);
255 }
256
257 bool HTMLSelectElement::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec)
258 {
259     bool result = HTMLGenericFormElement::insertBefore(newChild, refChild, ec);
260     if (result)
261         setRecalcListItems();
262     return result;
263 }
264
265 bool HTMLSelectElement::replaceChild(PassRefPtr<Node> newChild, Node *oldChild, ExceptionCode& ec)
266 {
267     bool result = HTMLGenericFormElement::replaceChild(newChild, oldChild, ec);
268     if (result)
269         setRecalcListItems();
270     return result;
271 }
272
273 bool HTMLSelectElement::removeChild(Node* oldChild, ExceptionCode& ec)
274 {
275     bool result = HTMLGenericFormElement::removeChild(oldChild, ec);
276     if (result)
277         setRecalcListItems();
278     return result;
279 }
280
281 bool HTMLSelectElement::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec)
282 {
283     bool result = HTMLGenericFormElement::appendChild(newChild, ec);
284     if (result)
285         setRecalcListItems();
286     return result;
287 }
288
289 ContainerNode* HTMLSelectElement::addChild(PassRefPtr<Node> newChild)
290 {
291     ContainerNode* result = HTMLGenericFormElement::addChild(newChild);
292     if (result)
293         setRecalcListItems();
294     return result;
295 }
296
297 void HTMLSelectElement::parseMappedAttribute(MappedAttribute *attr)
298 {
299     bool oldUsesMenuList = usesMenuList();
300     if (attr->name() == sizeAttr) {
301         m_size = max(attr->value().toInt(), 1);
302         if (oldUsesMenuList != usesMenuList() && attached()) {
303             detach();
304             attach();
305         }
306     } else if (attr->name() == widthAttr) {
307         m_minwidth = max(attr->value().toInt(), 0);
308     } else if (attr->name() == multipleAttr) {
309         m_multiple = (!attr->isNull());
310         if (oldUsesMenuList != usesMenuList() && attached()) {
311             detach();
312             attach();
313         }
314     } else if (attr->name() == accesskeyAttr) {
315         // FIXME: ignore for the moment
316     } else if (attr->name() == onfocusAttr) {
317         setHTMLEventListener(focusEvent, attr);
318     } else if (attr->name() == onblurAttr) {
319         setHTMLEventListener(blurEvent, attr);
320     } else if (attr->name() == onchangeAttr) {
321         setHTMLEventListener(changeEvent, attr);
322     } else
323         HTMLGenericFormElement::parseMappedAttribute(attr);
324 }
325
326 bool HTMLSelectElement::isKeyboardFocusable() const
327 {
328     if (renderer() && (usesMenuList() || renderer()->style()->appearance() == ListboxAppearance))
329         return isFocusable();
330     return HTMLGenericFormElement::isKeyboardFocusable();
331 }
332
333 bool HTMLSelectElement::isMouseFocusable() const
334 {
335     if (renderer() && (usesMenuList() || renderer()->style()->appearance() == ListboxAppearance))
336         return isFocusable();
337     return HTMLGenericFormElement::isMouseFocusable();
338 }
339
340 RenderObject *HTMLSelectElement::createRenderer(RenderArena *arena, RenderStyle *style)
341 {
342     if (usesMenuList())
343         return new (arena) RenderMenuList(this);
344     if (style->appearance() == ListboxAppearance)
345         return new (arena) RenderListBox(this);
346     return new (arena) DeprecatedRenderSelect(this);
347 }
348
349 bool HTMLSelectElement::appendFormData(FormDataList& list, bool)
350 {
351     bool successful = false;
352     const Vector<HTMLElement*>& items = listItems();
353
354     unsigned i;
355     for (i = 0; i < items.size(); i++) {
356         if (items[i]->hasLocalName(optionTag)) {
357             HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[i]);
358             if (option->selected()) {
359                 list.appendData(name(), option->value());
360                 successful = true;
361             }
362         }
363     }
364
365     // ### this case should not happen. make sure that we select the first option
366     // in any case. otherwise we have no consistency with the DOM interface. FIXME!
367     // we return the first one if it was a combobox select
368     if (!successful && !m_multiple && m_size <= 1 && items.size() &&
369         (items[0]->hasLocalName(optionTag))) {
370         HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[0]);
371         if (option->value().isNull())
372             list.appendData(name(), option->text().stripWhiteSpace());
373         else
374             list.appendData(name(), option->value());
375         successful = true;
376     }
377
378     return successful;
379 }
380
381 int HTMLSelectElement::optionToListIndex(int optionIndex) const
382 {
383     const Vector<HTMLElement*>& items = listItems();
384     if (optionIndex < 0 || optionIndex >= int(items.size()))
385         return -1;
386
387     int listIndex = 0;
388     int optionIndex2 = 0;
389     for (;
390          optionIndex2 < int(items.size()) && optionIndex2 <= optionIndex;
391          listIndex++) { // not a typo!
392         if (items[listIndex]->hasLocalName(optionTag))
393             optionIndex2++;
394     }
395     listIndex--;
396     return listIndex;
397 }
398
399 int HTMLSelectElement::listToOptionIndex(int listIndex) const
400 {
401     const Vector<HTMLElement*>& items = listItems();
402     if (listIndex < 0 || listIndex >= int(items.size()) ||
403         !items[listIndex]->hasLocalName(optionTag))
404         return -1;
405
406     int optionIndex = 0; // actual index of option not counting OPTGROUP entries that may be in list
407     for (int i = 0; i < listIndex; i++)
408         if (items[i]->hasLocalName(optionTag))
409             optionIndex++;
410     return optionIndex;
411 }
412
413 PassRefPtr<HTMLOptionsCollection> HTMLSelectElement::options()
414 {
415     return new HTMLOptionsCollection(this);
416 }
417
418 void HTMLSelectElement::recalcListItems() const
419 {
420     Node* current = firstChild();
421     m_listItems.clear();
422     HTMLOptionElement* foundSelected = 0;
423     while (current) {
424         if (current->hasTagName(optgroupTag) && current->firstChild()) {
425             // ### what if optgroup contains just comments? don't want one of no options in it...
426             m_listItems.append(static_cast<HTMLElement*>(current));
427             current = current->firstChild();
428         }
429         if (current->hasTagName(optionTag)) {
430             m_listItems.append(static_cast<HTMLElement*>(current));
431             if (!foundSelected && (usesMenuList() || (!m_multiple && static_cast<HTMLOptionElement*>(current)->selected()))) {
432                 foundSelected = static_cast<HTMLOptionElement*>(current);
433                 foundSelected->m_selected = true;
434             } else if (foundSelected && !m_multiple && static_cast<HTMLOptionElement*>(current)->selected()) {
435                 foundSelected->m_selected = false;
436                 foundSelected = static_cast<HTMLOptionElement*>(current);
437             }
438         }
439         if (current->hasTagName(hrTag))
440             m_listItems.append(static_cast<HTMLElement*>(current));
441
442         Node* parent = current->parentNode();
443         current = current->nextSibling();
444         if (!current) {
445             if (parent != this)
446                 current = parent->nextSibling();
447         }
448     }
449     m_recalcListItems = false;
450 }
451
452 void HTMLSelectElement::childrenChanged()
453 {
454     setRecalcListItems();
455
456     HTMLGenericFormElement::childrenChanged();
457 }
458
459 void HTMLSelectElement::setRecalcListItems()
460 {
461     m_recalcListItems = true;
462     if (renderer()) {
463         if (usesMenuList())
464             static_cast<RenderMenuList*>(renderer())->setOptionsChanged(true);
465         else if (renderer() && renderer()->style()->appearance() == ListboxAppearance)
466             static_cast<RenderListBox*>(renderer())->setOptionsChanged(true);
467         else
468             static_cast<DeprecatedRenderSelect*>(renderer())->setOptionsChanged(true);
469     }
470     setChanged();
471 }
472
473 void HTMLSelectElement::reset()
474 {
475     bool optionSelected = false;
476     HTMLOptionElement* firstOption = 0;
477     const Vector<HTMLElement*>& items = listItems();
478     unsigned i;
479     for (i = 0; i < items.size(); i++) {
480         if (items[i]->hasLocalName(optionTag)) {
481             HTMLOptionElement *option = static_cast<HTMLOptionElement*>(items[i]);
482             if (!option->getAttribute(selectedAttr).isNull()) {
483                 option->setSelected(true);
484                 optionSelected = true;
485             } else
486                 option->setSelected(false);
487             if (!firstOption)
488                 firstOption = option;
489         }
490     }
491     if (!optionSelected && firstOption)
492         firstOption->setSelected(true);
493     setChanged(true);
494 }
495
496 void HTMLSelectElement::notifyOptionSelected(HTMLOptionElement *selectedOption, bool selected)
497  {
498     if (selected && !m_multiple)
499         deselectItems(selectedOption);
500
501     if (renderer() && !usesMenuList()) {
502         if (renderer()->style()->appearance() == ListboxAppearance)
503             static_cast<RenderListBox*>(renderer())->setSelectionChanged(true);
504         else
505             static_cast<DeprecatedRenderSelect*>(renderer())->setSelectionChanged(true);
506     }
507  
508     setChanged(true);
509 }
510
511 void HTMLSelectElement::defaultEventHandler(Event* evt)
512 {
513     if (usesMenuList())
514         menuListDefaultEventHandler(evt);
515     else if (renderer() && renderer()->isListBox() && renderer()->style()->appearance() == ListboxAppearance) 
516         listBoxDefaultEventHandler(evt);
517     HTMLGenericFormElement::defaultEventHandler(evt);
518 }
519
520 void HTMLSelectElement::menuListDefaultEventHandler(Event* evt)
521 {
522     RenderMenuList* menuList = static_cast<RenderMenuList*>(renderer());
523
524     // Use key press event here since sending simulated mouse events
525     // on key down blocks the proper sending of the key press event.
526     if (evt->type() == keypressEvent) {
527         if (!renderer() || !evt->isKeyboardEvent())
528             return;
529         String keyIdentifier = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
530         bool handled = false;
531 #if ARROW_KEYS_POP_MENU
532         if (form() && keyIdentifier == "Enter") {
533             blur();
534             // Make sure the form hasn't been destroyed during the blur.
535             if (form())
536                 form()->submitClick();
537             handled = true;
538         }
539         if ((keyIdentifier == "Down" || keyIdentifier == "Up" || keyIdentifier == "U+000020") && renderer() && usesMenuList()) {
540             focus();
541             menuList->showPopup();
542             handled = true;
543         }
544 #else
545         int index = optionToListIndex(selectedIndex());
546         if (keyIdentifier == "Down" || keyIdentifier == "Right") {
547             if (index < listItems().size() - 1)
548                 setSelectedIndex(++index);
549             handled = true;
550         } else if (keyIdentifier == "Up" || keyIdentifier == "Left") {
551             if (index > 0)
552                 setSelectedIndex(--index);
553             handled = true;
554         }
555 #endif
556         if (handled)
557             evt->setDefaultHandled();
558
559     }
560     if (evt->type() == mousedownEvent) {
561          focus();
562         if (menuList->popupIsVisible())
563             menuList->hidePopup();
564         else
565             menuList->showPopup();
566         evt->setDefaultHandled();
567     }
568 }
569
570 void HTMLSelectElement::listBoxDefaultEventHandler(Event* evt)
571 {
572     if (evt->type() == mousedownEvent) {
573         MouseEvent* mEvt = static_cast<MouseEvent*>(evt);
574         if (HTMLOptionElement* element = static_cast<RenderListBox*>(renderer())->optionAtPoint(mEvt->x(), mEvt->y())) {
575             bool deselectOtherOptions = true;
576             bool shouldSelect = true;
577             
578             bool multiSelectKeyPressed = false;
579 #if PLATFORM(MAC)
580             multiSelectKeyPressed = mEvt->metaKey();
581 #else
582             multiSelectKeyPressed = mEvt->ctrlKey();
583 #endif
584             if (multiple() && multiSelectKeyPressed)
585                 deselectOtherOptions = false;
586             if (element->selected() && multiSelectKeyPressed)
587                 shouldSelect = false;
588             
589             int optionIndex = element->index();
590             if (!shouldSelect) {
591                 optionIndex = -1;
592                 element->m_selected = false;
593             }
594             setSelectedIndex(optionIndex, deselectOtherOptions);
595         }
596     }
597     
598     if (evt->type() == keypressEvent) {
599         if (!evt->isKeyboardEvent())
600             return;
601         String keyIdentifier = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
602         
603         int index;
604         const Vector<HTMLElement*>& items = listItems();
605
606         if (keyIdentifier == "Down") {
607             index = nextSelectableListIndex(lastSelectedListIndex());
608             
609         } else if (keyIdentifier == "Up") {
610             index = previousSelectableListIndex(optionToListIndex(selectedIndex()));
611         }
612         if (keyIdentifier == "Down" || keyIdentifier == "Up") {
613             ASSERT(index >= 0 && (unsigned)index < items.size()); 
614             HTMLOptionElement* element = static_cast<HTMLOptionElement*>(items[index]);
615             
616             setSelectedIndex(element->index(), !multiple() || !static_cast<KeyboardEvent*>(evt)->shiftKey());
617             static_cast<RenderListBox*>(renderer())->scrollToRevealElementAtListIndex(index);
618             
619             evt->setDefaultHandled();
620             setChanged();
621             renderer()->repaint();
622         }
623     }
624 }
625
626 int HTMLSelectElement::nextSelectableListIndex(int startIndex)
627 {
628     const Vector<HTMLElement*>& items = listItems();
629     int index = startIndex + 1;
630     while (index >= 0 && (unsigned)index < items.size() && (!items[index]->hasLocalName(optionTag) || items[index]->disabled()))
631         index++;
632     if ((unsigned) index == items.size())
633         return startIndex;
634     return index;
635 }
636
637 int HTMLSelectElement::previousSelectableListIndex(int startIndex)
638 {
639     const Vector<HTMLElement*>& items = listItems();
640     if (startIndex == -1)
641         startIndex = items.size();
642     int index = startIndex - 1;
643     while (index >= 0 && (unsigned)index < items.size() && (!items[index]->hasLocalName(optionTag) || items[index]->disabled()))
644         index--;
645     if (index == -1)
646         return startIndex;
647     return index;
648 }
649
650 void HTMLSelectElement::accessKeyAction(bool sendToAnyElement)
651 {
652     // send the mouse button events iff the
653     // caller specified sendToAnyElement
654     click(sendToAnyElement);
655 }
656
657 void HTMLSelectElement::setMultiple(bool multiple)
658 {
659     setAttribute(multipleAttr, multiple ? "" : 0);
660 }
661
662 void HTMLSelectElement::setSize(int size)
663 {
664     setAttribute(sizeAttr, String::number(size));
665 }
666
667 Node* HTMLSelectElement::namedItem(const String &name, bool caseSensitive)
668 {
669     return (options()->namedItem(name, caseSensitive));
670 }
671
672 void HTMLSelectElement::setOption(unsigned index, HTMLOptionElement* option, ExceptionCode& ec)
673 {
674     ec = 0;
675     if (index > INT_MAX)
676         index = INT_MAX;
677     int diff = index  - length();
678     HTMLElement* before = 0;
679     // out of array bounds ? first insert empty dummies
680     if (diff > 0) {
681         setLength(index, ec);
682         // replace an existing entry ?
683     } else if (diff < 0) {
684         before = static_cast<HTMLElement*>(options()->item(index+1));
685         remove(index);
686     }
687     // finally add the new element
688     if (ec == 0) {
689         add(option, before, ec);
690         if (diff >= 0 && option->selected())
691             setSelectedIndex(index, !m_multiple);
692     }
693 }
694
695 void HTMLSelectElement::setLength(unsigned newLen, ExceptionCode& ec)
696 {
697     ec = 0;
698     if (newLen > INT_MAX)
699         newLen = INT_MAX;
700     int diff = length() - newLen;
701
702     if (diff < 0) { // add dummy elements
703         do {
704             RefPtr<Element> option = ownerDocument()->createElement("option", ec);
705             if (!option)
706                 break;
707             add(static_cast<HTMLElement*>(option.get()), 0, ec);
708             if (ec)
709                 break;
710         } while (++diff);
711     }
712     else // remove elements
713         while (diff-- > 0)
714             remove(newLen);
715 }
716
717 } // namespace