8d8442be3bd432c2487e83ccf38627dc4e3e39e8
[WebKit-https.git] / WebCore / html / HTMLOptionElement.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 "HTMLOptionElement.h"
29
30 #include "Document.h"
31 #include "ExceptionCode.h"
32 #include "EventNames.h"
33 #include "HTMLNames.h"
34 #include "HTMLSelectElement.h"
35 #include "RenderMenuList.h"
36 #include "Text.h"
37 #include "cssstyleselector.h"
38 #include <wtf/Vector.h>
39
40 namespace WebCore {
41
42 using namespace HTMLNames;
43 using namespace EventNames;
44
45 HTMLOptionElement::HTMLOptionElement(Document* doc, HTMLFormElement* f)
46     : HTMLGenericFormElement(optionTag, doc, f)
47     , m_selected(false)
48     , m_style(0)
49 {
50 }
51
52 bool HTMLOptionElement::checkDTD(const Node* newChild)
53 {
54     return newChild->isTextNode() || newChild->hasTagName(scriptTag);
55 }
56
57 void HTMLOptionElement::attach()
58 {
59     RenderStyle* style = styleForRenderer(0);
60     setRenderStyle(style);
61     style->deref(document()->renderArena());
62     HTMLGenericFormElement::attach();
63 }
64
65 void HTMLOptionElement::detach()
66 {
67     if (m_style) {
68         m_style->deref(document()->renderArena());
69         m_style = 0;
70     }
71     HTMLGenericFormElement::detach();
72 }
73
74 bool HTMLOptionElement::isFocusable() const
75 {
76     return false;
77 }
78
79 const AtomicString& HTMLOptionElement::type() const
80 {
81     static const AtomicString option("option");
82     return option;
83 }
84
85 String HTMLOptionElement::text() const
86 {
87     String text;
88
89     // WinIE does not use the label attribute, so as a quirk, we ignore it.
90     if (!document()->inCompatMode()) {
91         String text = getAttribute(labelAttr);
92         if (!text.isEmpty())
93             return text;
94     }
95
96     const Node* n = firstChild();
97     while (n) {
98         if (n->nodeType() == TEXT_NODE || n->nodeType() == CDATA_SECTION_NODE)
99             text += n->nodeValue();
100         // skip script content
101         if (n->isElementNode() && n->hasTagName(HTMLNames::scriptTag))
102             n = n->traverseNextSibling(this);
103         else
104             n = n->traverseNextNode(this);
105     }
106
107     return text;
108 }
109
110 void HTMLOptionElement::setText(const String &text, ExceptionCode& ec)
111 {
112     // Handle the common special case where there's exactly 1 child node, and it's a text node.
113     Node* child = firstChild();
114     if (child && child->isTextNode() && !child->nextSibling()) {
115         static_cast<Text *>(child)->setData(text, ec);
116         return;
117     }
118
119     removeChildren();
120     appendChild(new Text(document(), text), ec);
121 }
122
123 int HTMLOptionElement::index() const
124 {
125     // Let's do this dynamically. Might be a bit slow, but we're sure
126     // we won't forget to update a member variable in some cases...
127     HTMLSelectElement *select = getSelect();
128     if (select) {
129         const Vector<HTMLElement*>& items = select->listItems();
130         int l = items.size();
131         int optionIndex = 0;
132         for(int i = 0; i < l; i++) {
133             if (items[i]->hasLocalName(optionTag)) {
134                 if (static_cast<HTMLOptionElement*>(items[i]) == this)
135                     return optionIndex;
136                 optionIndex++;
137             }
138         }
139     }
140     return 0;
141 }
142
143 void HTMLOptionElement::setIndex(int, ExceptionCode& ec)
144 {
145     ec = NO_MODIFICATION_ALLOWED_ERR;
146     // ###
147 }
148
149 void HTMLOptionElement::parseMappedAttribute(MappedAttribute *attr)
150 {
151     if (attr->name() == selectedAttr)
152         m_selected = (!attr->isNull());
153     else if (attr->name() == valueAttr)
154         m_value = attr->value();
155     else
156         HTMLGenericFormElement::parseMappedAttribute(attr);
157 }
158
159 String HTMLOptionElement::value() const
160 {
161     if ( !m_value.isNull() )
162         return m_value;
163     // Use the text if the value wasn't set.
164     return text().stripWhiteSpace();
165 }
166
167 void HTMLOptionElement::setValue(const String& value)
168 {
169     setAttribute(valueAttr, value);
170 }
171
172 void HTMLOptionElement::setSelected(bool selected)
173 {
174     if (m_selected == selected)
175         return;
176     m_selected = selected;
177     if (HTMLSelectElement* select = getSelect())
178         select->notifyOptionSelected(this, selected);
179 }
180
181 void HTMLOptionElement::childrenChanged()
182 {
183    HTMLSelectElement *select = getSelect();
184    if (select)
185        select->childrenChanged();
186 }
187
188 HTMLSelectElement* HTMLOptionElement::getSelect() const
189 {
190     Node* select = parentNode();
191     while (select && !select->hasTagName(selectTag))
192         select = select->parentNode();
193     return static_cast<HTMLSelectElement*>(select);
194 }
195
196 bool HTMLOptionElement::defaultSelected() const
197 {
198     return !getAttribute(selectedAttr).isNull();
199 }
200
201 void HTMLOptionElement::setDefaultSelected(bool b)
202 {
203     setAttribute(selectedAttr, b ? "" : 0);
204 }
205
206 String HTMLOptionElement::label() const
207 {
208     return getAttribute(labelAttr);
209 }
210
211 void HTMLOptionElement::setLabel(const String& value)
212 {
213     setAttribute(labelAttr, value);
214 }
215
216 void HTMLOptionElement::setRenderStyle(RenderStyle* newStyle)
217 {
218     RenderStyle* oldStyle = m_style;
219     m_style = newStyle;
220     if (newStyle)
221         newStyle->ref();
222     if (oldStyle)
223         oldStyle->deref(document()->renderArena());
224 }
225
226 String HTMLOptionElement::optionText()
227 {
228     DeprecatedString itemText = text().deprecatedString();
229     if (itemText.isEmpty())
230         itemText = getAttribute(labelAttr).deprecatedString();
231     
232     itemText.replace('\\', document()->backslashAsCurrencySymbol());
233     // In WinIE, leading and trailing whitespace is ignored in options and optgroups. We match this behavior.
234     itemText = itemText.stripWhiteSpace();
235     // We want to collapse our whitespace too.  This will match other browsers.
236     itemText = itemText.simplifyWhiteSpace();
237     if (parentNode() && parentNode()->hasTagName(optgroupTag))
238         itemText.prepend("    ");
239         
240     return itemText;
241 }
242
243 bool HTMLOptionElement::disabled() const
244
245     return HTMLGenericFormElement::disabled() || (parentNode() && static_cast<HTMLGenericFormElement*>(parentNode())->disabled()); 
246 }
247
248 } // namespace