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
23 #if ENABLE(WML)
24 #include "WMLSelectElement.h"
25 #include "CString.h"
26 #include "HTMLNames.h"
27 #include "MappedAttribute.h"
28 #include "OptionElement.h"
29 #include "RenderListBox.h"
30 #include "RenderMenuList.h"
31 #include "WMLDocument.h"
32 #include "WMLNames.h"
33 #include "WMLVariables.h"
34 #include <wtf/StdLibExtras.h>
35
36 namespace WebCore {
37
38 using namespace WMLNames;
39
WMLSelectElement(const QualifiedName & tagName,Document * document)40 WMLSelectElement::WMLSelectElement(const QualifiedName& tagName, Document* document)
41 : WMLFormControlElement(tagName, document)
42 {
43 }
44
~WMLSelectElement()45 WMLSelectElement::~WMLSelectElement()
46 {
47 }
48
formControlName() const49 const AtomicString& WMLSelectElement::formControlName() const
50 {
51 AtomicString name = this->name();
52 return name.isNull() ? emptyAtom : name;
53 }
54
formControlType() const55 const AtomicString& WMLSelectElement::formControlType() const
56 {
57 DEFINE_STATIC_LOCAL(const AtomicString, selectMultiple, ("select-multiple"));
58 DEFINE_STATIC_LOCAL(const AtomicString, selectOne, ("select-one"));
59 return m_data.multiple() ? selectMultiple : selectOne;
60 }
61
isKeyboardFocusable(KeyboardEvent * event) const62 bool WMLSelectElement::isKeyboardFocusable(KeyboardEvent* event) const
63 {
64 if (renderer())
65 return isFocusable();
66
67 return WMLFormControlElement::isKeyboardFocusable(event);
68 }
69
isMouseFocusable() const70 bool WMLSelectElement::isMouseFocusable() const
71 {
72 if (renderer())
73 return isFocusable();
74
75 return WMLFormControlElement::isMouseFocusable();
76 }
77
selectAll()78 void WMLSelectElement::selectAll()
79 {
80 SelectElement::selectAll(m_data, this);
81 }
82
recalcStyle(StyleChange change)83 void WMLSelectElement::recalcStyle(StyleChange change)
84 {
85 SelectElement::recalcStyle(m_data, this);
86 WMLFormControlElement::recalcStyle(change);
87 }
88
dispatchFocusEvent()89 void WMLSelectElement::dispatchFocusEvent()
90 {
91 SelectElement::dispatchFocusEvent(m_data, this);
92 WMLFormControlElement::dispatchFocusEvent();
93 }
94
dispatchBlurEvent()95 void WMLSelectElement::dispatchBlurEvent()
96 {
97 SelectElement::dispatchBlurEvent(m_data, this);
98 WMLFormControlElement::dispatchBlurEvent();
99 }
100
selectedIndex() const101 int WMLSelectElement::selectedIndex() const
102 {
103 return SelectElement::selectedIndex(m_data, this);
104 }
105
setSelectedIndex(int optionIndex,bool deselect)106 void WMLSelectElement::setSelectedIndex(int optionIndex, bool deselect)
107 {
108 SelectElement::setSelectedIndex(m_data, this, optionIndex, deselect, false, false);
109 }
110
setSelectedIndexByUser(int optionIndex,bool deselect,bool fireOnChangeNow)111 void WMLSelectElement::setSelectedIndexByUser(int optionIndex, bool deselect, bool fireOnChangeNow)
112 {
113 SelectElement::setSelectedIndex(m_data, this, optionIndex, deselect, fireOnChangeNow, true);
114 }
115
saveFormControlState(String & value) const116 bool WMLSelectElement::saveFormControlState(String& value) const
117 {
118 return SelectElement::saveFormControlState(m_data, this, value);
119 }
120
restoreFormControlState(const String & state)121 void WMLSelectElement::restoreFormControlState(const String& state)
122 {
123 SelectElement::restoreFormControlState(m_data, this, state);
124 }
125
childrenChanged(bool changedByParser,Node * beforeChange,Node * afterChange,int childCountDelta)126 void WMLSelectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
127 {
128 SelectElement::setRecalcListItems(m_data, this);
129 WMLFormControlElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
130 }
131
parseMappedAttribute(MappedAttribute * attr)132 void WMLSelectElement::parseMappedAttribute(MappedAttribute* attr)
133 {
134 if (attr->name() == HTMLNames::multipleAttr)
135 SelectElement::parseMultipleAttribute(m_data, this, attr);
136 else
137 WMLFormControlElement::parseMappedAttribute(attr);
138 }
139
createRenderer(RenderArena * arena,RenderStyle *)140 RenderObject* WMLSelectElement::createRenderer(RenderArena* arena, RenderStyle*)
141 {
142 if (m_data.usesMenuList())
143 return new (arena) RenderMenuList(this);
144 return new (arena) RenderListBox(this);
145 }
146
appendFormData(FormDataList & list,bool)147 bool WMLSelectElement::appendFormData(FormDataList& list, bool)
148 {
149 return SelectElement::appendFormData(m_data, this, list);
150 }
151
optionToListIndex(int optionIndex) const152 int WMLSelectElement::optionToListIndex(int optionIndex) const
153 {
154 return SelectElement::optionToListIndex(m_data, this, optionIndex);
155 }
156
listToOptionIndex(int listIndex) const157 int WMLSelectElement::listToOptionIndex(int listIndex) const
158 {
159 return SelectElement::listToOptionIndex(m_data, this, listIndex);
160 }
161
reset()162 void WMLSelectElement::reset()
163 {
164 SelectElement::reset(m_data, this);
165 }
166
defaultEventHandler(Event * event)167 void WMLSelectElement::defaultEventHandler(Event* event)
168 {
169 SelectElement::defaultEventHandler(m_data, this, event);
170
171 // FIXME: There must be a better place to update the page variable state. Investigate.
172 updateVariables();
173
174 if (event->defaultHandled())
175 return;
176
177 WMLFormControlElement::defaultEventHandler(event);
178 }
179
accessKeyAction(bool sendToAnyElement)180 void WMLSelectElement::accessKeyAction(bool sendToAnyElement)
181 {
182 focus();
183 dispatchSimulatedClick(0, sendToAnyElement);
184 }
185
setActiveSelectionAnchorIndex(int index)186 void WMLSelectElement::setActiveSelectionAnchorIndex(int index)
187 {
188 SelectElement::setActiveSelectionAnchorIndex(m_data, this, index);
189 }
190
setActiveSelectionEndIndex(int index)191 void WMLSelectElement::setActiveSelectionEndIndex(int index)
192 {
193 SelectElement::setActiveSelectionEndIndex(m_data, index);
194 }
195
updateListBoxSelection(bool deselectOtherOptions)196 void WMLSelectElement::updateListBoxSelection(bool deselectOtherOptions)
197 {
198 SelectElement::updateListBoxSelection(m_data, this, deselectOtherOptions);
199 }
200
listBoxOnChange()201 void WMLSelectElement::listBoxOnChange()
202 {
203 SelectElement::listBoxOnChange(m_data, this);
204 }
205
menuListOnChange()206 void WMLSelectElement::menuListOnChange()
207 {
208 SelectElement::menuListOnChange(m_data, this);
209 }
210
activeSelectionStartListIndex() const211 int WMLSelectElement::activeSelectionStartListIndex() const
212 {
213 if (m_data.activeSelectionAnchorIndex() >= 0)
214 return m_data.activeSelectionAnchorIndex();
215 return optionToListIndex(selectedIndex());
216 }
217
activeSelectionEndListIndex() const218 int WMLSelectElement::activeSelectionEndListIndex() const
219 {
220 if (m_data.activeSelectionEndIndex() >= 0)
221 return m_data.activeSelectionEndIndex();
222 return SelectElement::lastSelectedListIndex(m_data, this);
223 }
224
accessKeySetSelectedIndex(int index)225 void WMLSelectElement::accessKeySetSelectedIndex(int index)
226 {
227 SelectElement::accessKeySetSelectedIndex(m_data, this, index);
228 }
229
setRecalcListItems()230 void WMLSelectElement::setRecalcListItems()
231 {
232 SelectElement::setRecalcListItems(m_data, this);
233 }
234
scrollToSelection()235 void WMLSelectElement::scrollToSelection()
236 {
237 SelectElement::scrollToSelection(m_data, this);
238 }
239
selectInitialOptions()240 void WMLSelectElement::selectInitialOptions()
241 {
242 // Spec: Step 1 - the default option index is determined using iname and ivalue
243 calculateDefaultOptionIndices();
244
245 if (m_defaultOptionIndices.isEmpty())
246 return;
247
248 // Spec: Step 2 – initialise variables
249 initializeVariables();
250
251 // Spec: Step 3 – pre-select option(s) specified by the default option index
252 selectDefaultOptions();
253 }
254
insertedIntoTree(bool deep)255 void WMLSelectElement::insertedIntoTree(bool deep)
256 {
257 SelectElement::insertedIntoTree(m_data, this);
258 WMLFormControlElement::insertedIntoTree(deep);
259 }
260
calculateDefaultOptionIndices()261 void WMLSelectElement::calculateDefaultOptionIndices()
262 {
263 WMLPageState* pageState = wmlPageStateForDocument(document());
264 if (!pageState)
265 return;
266
267 String variable;
268
269 // Spec: If the 'iname' attribute is specified and names a variable that is set,
270 // then the default option index is the validated value of that variable.
271 String iname = this->iname();
272 if (!iname.isEmpty()) {
273 variable = pageState->getVariable(iname);
274 if (!variable.isEmpty())
275 m_defaultOptionIndices = parseIndexValueString(variable);
276 }
277
278 // Spec: If the default option index is empty and the 'ivalue' attribute is specified,
279 // then the default option index is the validated attribute value.
280 String ivalue = this->ivalue();
281 if (m_defaultOptionIndices.isEmpty() && !ivalue.isEmpty())
282 m_defaultOptionIndices = parseIndexValueString(ivalue);
283
284 // Spec: If the default option index is empty, and the 'name' attribute is specified
285 // and the 'name' ttribute names a variable that is set, then for each value in the 'name'
286 // variable that is present as a value in the select's option elements, the index of the
287 // first option element containing that value is added to the default index if that
288 // index has not been previously added.
289 String name = this->name();
290 if (m_defaultOptionIndices.isEmpty() && !name.isEmpty()) {
291 variable = pageState->getVariable(name);
292 if (!variable.isEmpty())
293 m_defaultOptionIndices = valueStringToOptionIndices(variable);
294 }
295
296 String value = parseValueSubstitutingVariableReferences(getAttribute(HTMLNames::valueAttr));
297
298 // Spec: If the default option index is empty and the 'value' attribute is specified then
299 // for each value in the 'value' attribute that is present as a value in the select's
300 // option elements, the index of the first option element containing that value is added
301 // to the default index if that index has not been previously added.
302 if (m_defaultOptionIndices.isEmpty() && !value.isEmpty())
303 m_defaultOptionIndices = valueStringToOptionIndices(value);
304
305 // Spec: If the default option index is empty and the select is a multi-choice, then the
306 // default option index is set to zero. If the select is single-choice, then the default
307 // option index is set to one.
308 if (m_defaultOptionIndices.isEmpty())
309 m_defaultOptionIndices.append((unsigned) !m_data.multiple());
310 }
311
selectDefaultOptions()312 void WMLSelectElement::selectDefaultOptions()
313 {
314 ASSERT(!m_defaultOptionIndices.isEmpty());
315
316 if (!m_data.multiple()) {
317 setSelectedIndex(m_defaultOptionIndices.first() - 1, false);
318 return;
319 }
320
321 Vector<unsigned>::const_iterator end = m_defaultOptionIndices.end();
322 for (Vector<unsigned>::const_iterator it = m_defaultOptionIndices.begin(); it != end; ++it)
323 setSelectedIndex((*it) - 1, false);
324 }
325
initializeVariables()326 void WMLSelectElement::initializeVariables()
327 {
328 ASSERT(!m_defaultOptionIndices.isEmpty());
329
330 WMLPageState* pageState = wmlPageStateForDocument(document());
331 if (!pageState)
332 return;
333
334 const Vector<Element*>& items = m_data.listItems(this);
335 if (items.isEmpty())
336 return;
337
338 // Spec: If the 'iname' attribute is specified, then the named variable is set with the default option index.
339 String iname = this->iname();
340 if (!iname.isEmpty())
341 pageState->storeVariable(iname, optionIndicesToString());
342
343 String name = this->name();
344 if (name.isEmpty())
345 return;
346
347 if (m_data.multiple()) {
348 // Spec: If the 'name' attribute is specified and the select is a multiple-choice element,
349 // then for each index greater than zero, the value of the 'value' attribute on the option
350 // element at the index is added to the name variable.
351 pageState->storeVariable(name, optionIndicesToValueString());
352 return;
353 }
354
355 // Spec: If the 'name' attribute is specified and the select is a single-choice element,
356 // then the named variable is set with the value of the 'value' attribute on the option
357 // element at the default option index.
358 unsigned optionIndex = m_defaultOptionIndices.first();
359 ASSERT(optionIndex >= 1);
360
361 int listIndex = optionToListIndex(optionIndex - 1);
362 ASSERT(listIndex >= 0);
363 ASSERT(listIndex < (int) items.size());
364
365 if (OptionElement* optionElement = toOptionElement(items[listIndex]))
366 pageState->storeVariable(name, optionElement->value());
367 }
368
updateVariables()369 void WMLSelectElement::updateVariables()
370 {
371 WMLPageState* pageState = wmlPageStateForDocument(document());
372 if (!pageState)
373 return;
374
375 String name = this->name();
376 String iname = this->iname();
377 if (iname.isEmpty() && name.isEmpty())
378 return;
379
380 String nameString;
381 String inameString;
382
383 unsigned optionIndex = 0;
384 const Vector<Element*>& items = m_data.listItems(this);
385
386 for (unsigned i = 0; i < items.size(); ++i) {
387 OptionElement* optionElement = toOptionElement(items[i]);
388 if (!optionElement)
389 continue;
390
391 ++optionIndex;
392 if (!optionElement->selected())
393 continue;
394
395 if (!nameString.isEmpty())
396 nameString += ";";
397
398 if (!inameString.isEmpty())
399 inameString += ";";
400
401 nameString += optionElement->value();
402 inameString += String::number(optionIndex);
403 }
404
405 if (!name.isEmpty())
406 pageState->storeVariable(name, nameString);
407
408 if (!iname.isEmpty())
409 pageState->storeVariable(iname, inameString);
410 }
411
parseIndexValueString(const String & indexValue) const412 Vector<unsigned> WMLSelectElement::parseIndexValueString(const String& indexValue) const
413 {
414 Vector<unsigned> indices;
415 if (indexValue.isEmpty())
416 return indices;
417
418 Vector<String> indexStrings;
419 indexValue.split(';', indexStrings);
420
421 bool ok = false;
422 unsigned optionCount = SelectElement::optionCount(m_data, this);
423
424 Vector<String>::const_iterator end = indexStrings.end();
425 for (Vector<String>::const_iterator it = indexStrings.begin(); it != end; ++it) {
426 unsigned parsedValue = (*it).toUIntStrict(&ok);
427 // Spec: Remove all non-integer indices from the value. Remove all out-of-range indices
428 // from the value, where out-of-range is defined as any index with a value greater than
429 // the number of options in the select or with a value less than one.
430 if (!ok || parsedValue < 1 || parsedValue > optionCount)
431 continue;
432
433 // Spec: Remove duplicate indices.
434 if (indices.find(parsedValue) == notFound)
435 indices.append(parsedValue);
436 }
437
438 return indices;
439 }
440
valueStringToOptionIndices(const String & value) const441 Vector<unsigned> WMLSelectElement::valueStringToOptionIndices(const String& value) const
442 {
443 Vector<unsigned> indices;
444 if (value.isEmpty())
445 return indices;
446
447 const Vector<Element*>& items = m_data.listItems(this);
448 if (items.isEmpty())
449 return indices;
450
451 Vector<String> indexStrings;
452 value.split(';', indexStrings);
453
454 unsigned optionIndex = 0;
455
456 Vector<String>::const_iterator end = indexStrings.end();
457 for (Vector<String>::const_iterator it = indexStrings.begin(); it != end; ++it) {
458 String value = *it;
459
460 for (unsigned i = 0; i < items.size(); ++i) {
461 if (!isOptionElement(items[i]))
462 continue;
463
464 ++optionIndex;
465 if (OptionElement* optionElement = toOptionElement(items[i])) {
466 if (optionElement->value() == value) {
467 indices.append(optionIndex);
468 break;
469 }
470 }
471 }
472 }
473
474 return indices;
475 }
476
optionIndicesToValueString() const477 String WMLSelectElement::optionIndicesToValueString() const
478 {
479 String valueString;
480 if (m_defaultOptionIndices.isEmpty())
481 return valueString;
482
483 const Vector<Element*>& items = m_data.listItems(this);
484 if (items.isEmpty())
485 return valueString;
486
487 Vector<unsigned>::const_iterator end = m_defaultOptionIndices.end();
488 for (Vector<unsigned>::const_iterator it = m_defaultOptionIndices.begin(); it != end; ++it) {
489 unsigned optionIndex = (*it);
490 if (optionIndex < 1 || optionIndex > items.size())
491 continue;
492
493 int listIndex = optionToListIndex((*it) - 1);
494 ASSERT(listIndex >= 0);
495 ASSERT(listIndex < (int) items.size());
496
497 if (OptionElement* optionElement = toOptionElement(items[listIndex])) {
498 if (!valueString.isEmpty())
499 valueString += ";";
500
501 valueString += optionElement->value();
502 }
503 }
504
505 return valueString;
506 }
507
optionIndicesToString() const508 String WMLSelectElement::optionIndicesToString() const
509 {
510 String valueString;
511 if (m_defaultOptionIndices.isEmpty())
512 return valueString;
513
514 Vector<unsigned>::const_iterator end = m_defaultOptionIndices.end();
515 for (Vector<unsigned>::const_iterator it = m_defaultOptionIndices.begin(); it != end; ++it) {
516 if (!valueString.isEmpty())
517 valueString += ";";
518
519 valueString += String::number(*it);
520 }
521
522 return valueString;
523 }
524
name() const525 String WMLSelectElement::name() const
526 {
527 return parseValueForbiddingVariableReferences(getAttribute(HTMLNames::nameAttr));
528 }
529
value() const530 String WMLSelectElement::value() const
531 {
532 return parseValueSubstitutingVariableReferences(getAttribute(HTMLNames::valueAttr));
533 }
534
iname() const535 String WMLSelectElement::iname() const
536 {
537 return parseValueForbiddingVariableReferences(getAttribute(inameAttr));
538 }
539
ivalue() const540 String WMLSelectElement::ivalue() const
541 {
542 return parseValueSubstitutingVariableReferences(getAttribute(ivalueAttr));
543 }
544
545 }
546
547 #endif
548