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 "OptionElement.h"
23 
24 #include "Document.h"
25 #include "Element.h"
26 #include "HTMLNames.h"
27 #include "HTMLOptionElement.h"
28 #include "OptionGroupElement.h"
29 #include "ScriptElement.h"
30 #include "SelectElement.h"
31 #include <wtf/Assertions.h>
32 
33 namespace WebCore {
34 
setSelectedState(OptionElementData & data,Element * element,bool selected)35 void OptionElement::setSelectedState(OptionElementData& data, Element* element, bool selected)
36 {
37     if (data.selected() == selected)
38         return;
39 
40     data.setSelected(selected);
41     element->setNeedsStyleRecalc();
42 }
43 
optionIndex(SelectElement * selectElement,const Element * element)44 int OptionElement::optionIndex(SelectElement* selectElement, const Element* element)
45 {
46     if (!selectElement)
47         return 0;
48 
49     // Let's do this dynamically. Might be a bit slow, but we're sure
50     // we won't forget to update a member variable in some cases...
51     const Vector<Element*>& items = selectElement->listItems();
52     int length = items.size();
53     int optionIndex = 0;
54     for (int i = 0; i < length; ++i) {
55         if (!isOptionElement(items[i]))
56             continue;
57         if (items[i] == element)
58             return optionIndex;
59         ++optionIndex;
60     }
61 
62     return 0;
63 }
64 
collectOptionLabelOrText(const OptionElementData & data,const Element * element)65 String OptionElement::collectOptionLabelOrText(const OptionElementData& data, const Element* element)
66 {
67     Document* document = element->document();
68     String text;
69 
70     // WinIE does not use the label attribute, so as a quirk, we ignore it.
71     if (!document->inQuirksMode())
72         text = data.label();
73     if (text.isEmpty())
74         text = collectOptionInnerText(element);
75     return normalizeText(document, text);
76 }
77 
collectOptionInnerText(const Element * element)78 String OptionElement::collectOptionInnerText(const Element* element)
79 {
80     String text;
81     Node* n = element->firstChild();
82     while (n) {
83         if (n->nodeType() == Node::TEXT_NODE || n->nodeType() == Node::CDATA_SECTION_NODE)
84             text += n->nodeValue();
85 
86         // skip script content
87         if (n->isElementNode() && toScriptElement(static_cast<Element*>(n)))
88             n = n->traverseNextSibling(element);
89         else
90             n = n->traverseNextNode(element);
91     }
92     return text;
93 }
94 
normalizeText(const Document * document,const String & src)95 String OptionElement::normalizeText(const Document* document, const String& src)
96 {
97     String text = document->displayStringModifiedByEncoding(src);
98 
99     // In WinIE, leading and trailing whitespace is ignored in options and optgroups. We match this behavior.
100     text = text.stripWhiteSpace();
101 
102     // We want to collapse our whitespace too.  This will match other browsers.
103     text = text.simplifyWhiteSpace();
104     return text;
105 }
106 
collectOptionTextRespectingGroupLabel(const OptionElementData & data,const Element * element)107 String OptionElement::collectOptionTextRespectingGroupLabel(const OptionElementData& data, const Element* element)
108 {
109     Element* parentElement = static_cast<Element*>(element->parentNode());
110     if (parentElement && toOptionGroupElement(parentElement))
111         return "    " + collectOptionLabelOrText(data, element);
112 
113     return collectOptionLabelOrText(data, element);
114 }
115 
collectOptionValue(const OptionElementData & data,const Element * element)116 String OptionElement::collectOptionValue(const OptionElementData& data, const Element* element)
117 {
118     String value = data.value();
119     if (!value.isNull())
120         return value;
121 
122     // Use the text if the value wasn't set.
123     return collectOptionInnerText(element).stripWhiteSpace();
124 }
125 
126 // OptionElementData
OptionElementData()127 OptionElementData::OptionElementData()
128     : m_selected(false)
129 {
130 }
131 
~OptionElementData()132 OptionElementData::~OptionElementData()
133 {
134 }
135 
toOptionElement(Element * element)136 OptionElement* toOptionElement(Element* element)
137 {
138     if (element->isHTMLElement() && element->hasTagName(HTMLNames::optionTag))
139         return static_cast<HTMLOptionElement*>(element);
140     return 0;
141 }
142 
isOptionElement(Element * element)143 bool isOptionElement(Element* element)
144 {
145     return element->hasLocalName(HTMLNames::optionTag);
146 }
147 
148 }
149