• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, 2009 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., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24 
25 #include "config.h"
26 #include "HTMLSelectElement.h"
27 
28 #include "AXObjectCache.h"
29 #include "EventNames.h"
30 #include "HTMLNames.h"
31 #include "HTMLOptionElement.h"
32 #include "HTMLOptionsCollection.h"
33 #include "MappedAttribute.h"
34 #include "RenderListBox.h"
35 #include "RenderMenuList.h"
36 #include "ScriptEventListener.h"
37 
38 using namespace std;
39 
40 namespace WebCore {
41 
42 using namespace HTMLNames;
43 
44 // Upper limit agreed upon with representatives of Opera and Mozilla.
45 static const unsigned maxSelectItems = 10000;
46 
HTMLSelectElement(const QualifiedName & tagName,Document * document,HTMLFormElement * form)47 HTMLSelectElement::HTMLSelectElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form)
48     : HTMLFormControlElementWithState(tagName, document, form)
49 {
50     ASSERT(hasTagName(selectTag) || hasTagName(keygenTag));
51 }
52 
checkDTD(const Node * newChild)53 bool HTMLSelectElement::checkDTD(const Node* newChild)
54 {
55     // Make sure to keep <optgroup> in sync with this.
56     return newChild->isTextNode() || newChild->hasTagName(optionTag) || newChild->hasTagName(optgroupTag) || newChild->hasTagName(hrTag) ||
57            newChild->hasTagName(scriptTag);
58 }
59 
recalcStyle(StyleChange change)60 void HTMLSelectElement::recalcStyle(StyleChange change)
61 {
62     SelectElement::recalcStyle(m_data, this);
63     HTMLFormControlElementWithState::recalcStyle(change);
64 }
65 
formControlType() const66 const AtomicString& HTMLSelectElement::formControlType() const
67 {
68     DEFINE_STATIC_LOCAL(const AtomicString, selectMultiple, ("select-multiple"));
69     DEFINE_STATIC_LOCAL(const AtomicString, selectOne, ("select-one"));
70     return m_data.multiple() ? selectMultiple : selectOne;
71 }
72 
selectedIndex() const73 int HTMLSelectElement::selectedIndex() const
74 {
75     return SelectElement::selectedIndex(m_data, this);
76 }
77 
deselectItems(HTMLOptionElement * excludeElement)78 void HTMLSelectElement::deselectItems(HTMLOptionElement* excludeElement)
79 {
80     SelectElement::deselectItems(m_data, this, excludeElement);
81 }
82 
setSelectedIndex(int optionIndex,bool deselect)83 void HTMLSelectElement::setSelectedIndex(int optionIndex, bool deselect)
84 {
85     SelectElement::setSelectedIndex(m_data, this, optionIndex, deselect, false, false);
86 }
87 
setSelectedIndexByUser(int optionIndex,bool deselect,bool fireOnChangeNow)88 void HTMLSelectElement::setSelectedIndexByUser(int optionIndex, bool deselect, bool fireOnChangeNow)
89 {
90     SelectElement::setSelectedIndex(m_data, this, optionIndex, deselect, fireOnChangeNow, true);
91 }
92 
activeSelectionStartListIndex() const93 int HTMLSelectElement::activeSelectionStartListIndex() const
94 {
95     if (m_data.activeSelectionAnchorIndex() >= 0)
96         return m_data.activeSelectionAnchorIndex();
97     return optionToListIndex(selectedIndex());
98 }
99 
activeSelectionEndListIndex() const100 int HTMLSelectElement::activeSelectionEndListIndex() const
101 {
102     if (m_data.activeSelectionEndIndex() >= 0)
103         return m_data.activeSelectionEndIndex();
104     return SelectElement::lastSelectedListIndex(m_data, this);
105 }
106 
length() const107 unsigned HTMLSelectElement::length() const
108 {
109     return SelectElement::optionCount(m_data, this);
110 }
111 
add(HTMLElement * element,HTMLElement * before,ExceptionCode & ec)112 void HTMLSelectElement::add(HTMLElement *element, HTMLElement *before, ExceptionCode& ec)
113 {
114     RefPtr<HTMLElement> protectNewChild(element); // make sure the element is ref'd and deref'd so we don't leak it
115 
116     if (!element || !(element->hasLocalName(optionTag) || element->hasLocalName(hrTag)))
117         return;
118 
119     insertBefore(element, before, ec);
120 }
121 
remove(int index)122 void HTMLSelectElement::remove(int index)
123 {
124     int listIndex = optionToListIndex(index);
125     if (listIndex < 0)
126         return;
127 
128     Element* item = listItems()[listIndex];
129     ASSERT(item->parentNode());
130     ExceptionCode ec;
131     item->parentNode()->removeChild(item, ec);
132 }
133 
value()134 String HTMLSelectElement::value()
135 {
136     const Vector<Element*>& items = listItems();
137     for (unsigned i = 0; i < items.size(); i++) {
138         if (items[i]->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(items[i])->selected())
139             return static_cast<HTMLOptionElement*>(items[i])->value();
140     }
141     return "";
142 }
143 
setValue(const String & value)144 void HTMLSelectElement::setValue(const String &value)
145 {
146     if (value.isNull())
147         return;
148     // find the option with value() matching the given parameter
149     // and make it the current selection.
150     const Vector<Element*>& items = listItems();
151     unsigned optionIndex = 0;
152     for (unsigned i = 0; i < items.size(); i++) {
153         if (items[i]->hasLocalName(optionTag)) {
154             if (static_cast<HTMLOptionElement*>(items[i])->value() == value) {
155                 setSelectedIndex(optionIndex, true);
156                 return;
157             }
158             optionIndex++;
159         }
160     }
161 }
162 
saveFormControlState(String & value) const163 bool HTMLSelectElement::saveFormControlState(String& value) const
164 {
165     return SelectElement::saveFormControlState(m_data, this, value);
166 }
167 
restoreFormControlState(const String & state)168 void HTMLSelectElement::restoreFormControlState(const String& state)
169 {
170     SelectElement::restoreFormControlState(m_data, this, state);
171 }
172 
parseMappedAttribute(MappedAttribute * attr)173 void HTMLSelectElement::parseMappedAttribute(MappedAttribute* attr)
174 {
175     bool oldUsesMenuList = m_data.usesMenuList();
176     if (attr->name() == sizeAttr) {
177         int oldSize = m_data.size();
178         // Set the attribute value to a number.
179         // This is important since the style rules for this attribute can determine the appearance property.
180         int size = attr->value().toInt();
181         String attrSize = String::number(size);
182         if (attrSize != attr->value())
183             attr->setValue(attrSize);
184 
185         m_data.setSize(max(size, 1));
186         if ((oldUsesMenuList != m_data.usesMenuList() || (!oldUsesMenuList && m_data.size() != oldSize)) && attached()) {
187             detach();
188             attach();
189             setRecalcListItems();
190         }
191     } else if (attr->name() == multipleAttr)
192         SelectElement::parseMultipleAttribute(m_data, this, attr);
193     else if (attr->name() == accesskeyAttr) {
194         // FIXME: ignore for the moment
195     } else if (attr->name() == alignAttr) {
196         // Don't map 'align' attribute.  This matches what Firefox, Opera and IE do.
197         // See http://bugs.webkit.org/show_bug.cgi?id=12072
198     } else if (attr->name() == onfocusAttr) {
199         setAttributeEventListener(eventNames().focusEvent, createAttributeEventListener(this, attr));
200     } else if (attr->name() == onblurAttr) {
201         setAttributeEventListener(eventNames().blurEvent, createAttributeEventListener(this, attr));
202     } else if (attr->name() == onchangeAttr) {
203         setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, attr));
204     } else
205         HTMLFormControlElementWithState::parseMappedAttribute(attr);
206 }
207 
isKeyboardFocusable(KeyboardEvent * event) const208 bool HTMLSelectElement::isKeyboardFocusable(KeyboardEvent* event) const
209 {
210     if (renderer())
211         return isFocusable();
212     return HTMLFormControlElementWithState::isKeyboardFocusable(event);
213 }
214 
isMouseFocusable() const215 bool HTMLSelectElement::isMouseFocusable() const
216 {
217     if (renderer())
218         return isFocusable();
219     return HTMLFormControlElementWithState::isMouseFocusable();
220 }
221 
canSelectAll() const222 bool HTMLSelectElement::canSelectAll() const
223 {
224     return !m_data.usesMenuList();
225 }
226 
selectAll()227 void HTMLSelectElement::selectAll()
228 {
229     SelectElement::selectAll(m_data, this);
230 }
231 
createRenderer(RenderArena * arena,RenderStyle *)232 RenderObject* HTMLSelectElement::createRenderer(RenderArena* arena, RenderStyle*)
233 {
234     if (m_data.usesMenuList())
235         return new (arena) RenderMenuList(this);
236     return new (arena) RenderListBox(this);
237 }
238 
appendFormData(FormDataList & list,bool)239 bool HTMLSelectElement::appendFormData(FormDataList& list, bool)
240 {
241     return SelectElement::appendFormData(m_data, this, list);
242 }
243 
optionToListIndex(int optionIndex) const244 int HTMLSelectElement::optionToListIndex(int optionIndex) const
245 {
246     return SelectElement::optionToListIndex(m_data, this, optionIndex);
247 }
248 
listToOptionIndex(int listIndex) const249 int HTMLSelectElement::listToOptionIndex(int listIndex) const
250 {
251     return SelectElement::listToOptionIndex(m_data, this, listIndex);
252 }
253 
options()254 PassRefPtr<HTMLOptionsCollection> HTMLSelectElement::options()
255 {
256     return HTMLOptionsCollection::create(this);
257 }
258 
recalcListItems(bool updateSelectedStates) const259 void HTMLSelectElement::recalcListItems(bool updateSelectedStates) const
260 {
261     SelectElement::recalcListItems(const_cast<SelectElementData&>(m_data), this, updateSelectedStates);
262 }
263 
childrenChanged(bool changedByParser,Node * beforeChange,Node * afterChange,int childCountDelta)264 void HTMLSelectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
265 {
266     setRecalcListItems();
267     HTMLFormControlElementWithState::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
268 
269     if (AXObjectCache::accessibilityEnabled() && renderer())
270         renderer()->document()->axObjectCache()->childrenChanged(renderer());
271 }
272 
setRecalcListItems()273 void HTMLSelectElement::setRecalcListItems()
274 {
275     SelectElement::setRecalcListItems(m_data, this);
276 
277     if (!inDocument())
278         m_collectionInfo.reset();
279 }
280 
reset()281 void HTMLSelectElement::reset()
282 {
283     SelectElement::reset(m_data, this);
284 }
285 
dispatchFocusEvent()286 void HTMLSelectElement::dispatchFocusEvent()
287 {
288     SelectElement::dispatchFocusEvent(m_data, this);
289     HTMLFormControlElementWithState::dispatchFocusEvent();
290 }
291 
dispatchBlurEvent()292 void HTMLSelectElement::dispatchBlurEvent()
293 {
294     SelectElement::dispatchBlurEvent(m_data, this);
295     HTMLFormControlElementWithState::dispatchBlurEvent();
296 }
297 
defaultEventHandler(Event * event)298 void HTMLSelectElement::defaultEventHandler(Event* event)
299 {
300     SelectElement::defaultEventHandler(m_data, this, event, form());
301     if (event->defaultHandled())
302         return;
303     HTMLFormControlElementWithState::defaultEventHandler(event);
304 }
305 
setActiveSelectionAnchorIndex(int index)306 void HTMLSelectElement::setActiveSelectionAnchorIndex(int index)
307 {
308     SelectElement::setActiveSelectionAnchorIndex(m_data, this, index);
309 }
310 
setActiveSelectionEndIndex(int index)311 void HTMLSelectElement::setActiveSelectionEndIndex(int index)
312 {
313     SelectElement::setActiveSelectionEndIndex(m_data, index);
314 }
315 
updateListBoxSelection(bool deselectOtherOptions)316 void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions)
317 {
318     SelectElement::updateListBoxSelection(m_data, this, deselectOtherOptions);
319 }
320 
menuListOnChange()321 void HTMLSelectElement::menuListOnChange()
322 {
323     SelectElement::menuListOnChange(m_data, this);
324 }
325 
listBoxOnChange()326 void HTMLSelectElement::listBoxOnChange()
327 {
328     SelectElement::listBoxOnChange(m_data, this);
329 }
330 
saveLastSelection()331 void HTMLSelectElement::saveLastSelection()
332 {
333     SelectElement::saveLastSelection(m_data, this);
334 }
335 
accessKeyAction(bool sendToAnyElement)336 void HTMLSelectElement::accessKeyAction(bool sendToAnyElement)
337 {
338     focus();
339     dispatchSimulatedClick(0, sendToAnyElement);
340 }
341 
accessKeySetSelectedIndex(int index)342 void HTMLSelectElement::accessKeySetSelectedIndex(int index)
343 {
344     SelectElement::accessKeySetSelectedIndex(m_data, this, index);
345 }
346 
setMultiple(bool multiple)347 void HTMLSelectElement::setMultiple(bool multiple)
348 {
349     setAttribute(multipleAttr, multiple ? "" : 0);
350 }
351 
setSize(int size)352 void HTMLSelectElement::setSize(int size)
353 {
354     setAttribute(sizeAttr, String::number(size));
355 }
356 
namedItem(const AtomicString & name)357 Node* HTMLSelectElement::namedItem(const AtomicString& name)
358 {
359     return options()->namedItem(name);
360 }
361 
item(unsigned index)362 Node* HTMLSelectElement::item(unsigned index)
363 {
364     return options()->item(index);
365 }
366 
setOption(unsigned index,HTMLOptionElement * option,ExceptionCode & ec)367 void HTMLSelectElement::setOption(unsigned index, HTMLOptionElement* option, ExceptionCode& ec)
368 {
369     ec = 0;
370     if (index > maxSelectItems - 1)
371         index = maxSelectItems - 1;
372     int diff = index  - length();
373     HTMLElement* before = 0;
374     // out of array bounds ? first insert empty dummies
375     if (diff > 0) {
376         setLength(index, ec);
377         // replace an existing entry ?
378     } else if (diff < 0) {
379         before = static_cast<HTMLElement*>(options()->item(index+1));
380         remove(index);
381     }
382     // finally add the new element
383     if (!ec) {
384         add(option, before, ec);
385         if (diff >= 0 && option->selected())
386             setSelectedIndex(index, !m_data.multiple());
387     }
388 }
389 
setLength(unsigned newLen,ExceptionCode & ec)390 void HTMLSelectElement::setLength(unsigned newLen, ExceptionCode& ec)
391 {
392     ec = 0;
393     if (newLen > maxSelectItems)
394         newLen = maxSelectItems;
395     int diff = length() - newLen;
396 
397     if (diff < 0) { // add dummy elements
398         do {
399             RefPtr<Element> option = document()->createElement(optionTag, false);
400             ASSERT(option);
401             add(static_cast<HTMLElement*>(option.get()), 0, ec);
402             if (ec)
403                 break;
404         } while (++diff);
405     } else {
406         const Vector<Element*>& items = listItems();
407 
408         size_t optionIndex = 0;
409         for (size_t listIndex = 0; listIndex < items.size(); listIndex++) {
410             if (items[listIndex]->hasLocalName(optionTag) && optionIndex++ >= newLen) {
411                 Element *item = items[listIndex];
412                 ASSERT(item->parentNode());
413                 item->parentNode()->removeChild(item, ec);
414             }
415         }
416     }
417 }
418 
scrollToSelection()419 void HTMLSelectElement::scrollToSelection()
420 {
421     SelectElement::scrollToSelection(m_data, this);
422 }
423 
insertedIntoTree(bool deep)424 void HTMLSelectElement::insertedIntoTree(bool deep)
425 {
426     SelectElement::insertedIntoTree(m_data, this);
427     HTMLFormControlElementWithState::insertedIntoTree(deep);
428 }
429 
430 } // namespace
431