Reviewed by Adam Treat.
[WebKit-https.git] / WebCore / wml / WMLSelectElement.cpp
index 5b5aed1..5e70098 100644 (file)
 
 #if ENABLE(WML)
 #include "WMLSelectElement.h"
-
+#include "CString.h"
 #include "HTMLNames.h"
 #include "MappedAttribute.h"
+#include "OptionElement.h"
 #include "RenderListBox.h"
 #include "RenderMenuList.h"
-
+#include "WMLDocument.h"
+#include "WMLNames.h"
+#include "WMLVariables.h"
 #include <wtf/StdLibExtras.h>
 
 namespace WebCore {
 
+using namespace WMLNames;
+
 WMLSelectElement::WMLSelectElement(const QualifiedName& tagName, Document* document)
     : WMLFormControlElement(tagName, document)
 {
@@ -41,6 +46,17 @@ WMLSelectElement::~WMLSelectElement()
 {
 }
 
+String WMLSelectElement::title() const
+{
+    return substituteVariableReferences(getAttribute(HTMLNames::titleAttr), document());
+}
+
+const AtomicString& WMLSelectElement::formControlName() const
+{
+    AtomicString name = this->name();
+    return name.isNull() ? emptyAtom : name;
+}
+
 const AtomicString& WMLSelectElement::formControlType() const
 {
     DEFINE_STATIC_LOCAL(const AtomicString, selectMultiple, ("select-multiple"));
@@ -92,9 +108,14 @@ int WMLSelectElement::selectedIndex() const
     return SelectElement::selectedIndex(m_data, this);
 }
     
-void WMLSelectElement::setSelectedIndex(int index, bool deselect, bool fireOnChange)
+void WMLSelectElement::setSelectedIndex(int optionIndex, bool deselect)
+{
+    SelectElement::setSelectedIndex(m_data, this, optionIndex, deselect, false, false);
+}
+
+void WMLSelectElement::setSelectedIndexByUser(int optionIndex, bool deselect, bool fireOnChangeNow)
 {
-    SelectElement::setSelectedIndex(m_data, this, index, deselect, fireOnChange);
+    SelectElement::setSelectedIndex(m_data, this, optionIndex, deselect, fireOnChangeNow, true);
 }
 
 bool WMLSelectElement::saveFormControlState(String& value) const
@@ -151,6 +172,14 @@ void WMLSelectElement::reset()
 void WMLSelectElement::defaultEventHandler(Event* event)
 {
     SelectElement::defaultEventHandler(m_data, this, event);
+
+    // FIXME: There must be a better place to update the page variable state. Investigate.
+    updateVariables();
+
+    if (event->defaultHandled())
+        return;
+
+    WMLFormControlElement::defaultEventHandler(event);
 }
 
 void WMLSelectElement::accessKeyAction(bool sendToAnyElement)
@@ -213,12 +242,311 @@ void WMLSelectElement::scrollToSelection()
     SelectElement::scrollToSelection(m_data, this);
 }
 
+void WMLSelectElement::selectInitialOptions()
+{
+    // Spec: Step 1 - the default option index is determined using iname and ivalue
+    calculateDefaultOptionIndices();
+
+    if (m_defaultOptionIndices.isEmpty())
+        return;
+
+    // Spec: Step 2 – initialise variables
+    initializeVariables();
+
+    // Spec: Step 3 – pre-select option(s) specified by the default option index 
+    selectDefaultOptions();
+}
+
 void WMLSelectElement::insertedIntoTree(bool deep)
 {
     SelectElement::insertedIntoTree(m_data, this);
     WMLFormControlElement::insertedIntoTree(deep);
 }
 
+void WMLSelectElement::calculateDefaultOptionIndices()
+{
+    WMLPageState* pageState = wmlPageStateForDocument(document());
+    if (!pageState)
+        return;
+
+    String variable;
+
+    // Spec: If the 'iname' attribute is specified and names a variable that is set,
+    // then the default option index is the validated value of that variable. 
+    String iname = this->iname();
+    if (!iname.isEmpty()) {
+        variable = pageState->getVariable(iname);
+        if (!variable.isEmpty())
+            m_defaultOptionIndices = parseIndexValueString(variable);
+    }
+
+    // Spec: If the default option index is empty and the 'ivalue' attribute is specified,
+    // then the default option index is the validated attribute value.
+    String ivalue = this->ivalue();
+    if (m_defaultOptionIndices.isEmpty() && !ivalue.isEmpty())
+        m_defaultOptionIndices = parseIndexValueString(ivalue);
+
+    // Spec: If the default option index is empty, and the 'name' attribute is specified
+    // and the 'name' ttribute names a variable that is set, then for each value in the 'name'
+    // variable that is present as a value in the select's option elements, the index of the
+    // first option element containing that value is added to the default index if that
+    // index has not been previously added. 
+    String name = this->name();
+    if (m_defaultOptionIndices.isEmpty() && !name.isEmpty()) {
+        variable = pageState->getVariable(name);
+        if (!variable.isEmpty())
+            m_defaultOptionIndices = valueStringToOptionIndices(variable);
+    }
+
+    String value = parseValueSubstitutingVariableReferences(getAttribute(HTMLNames::valueAttr));
+
+    // Spec: If the default option index is empty and the 'value' attribute is specified then
+    // for each  value in the 'value' attribute that is present as a value in the select's
+    // option elements, the index of the first option element containing that value is added
+    // to the default index if that index has not been previously added. 
+    if (m_defaultOptionIndices.isEmpty() && !value.isEmpty())
+        m_defaultOptionIndices = valueStringToOptionIndices(value);
+
+    // Spec: If the default option index is empty and the select is a multi-choice, then the
+    // default option index is set to zero. If the select is single-choice, then the default
+    // option index is set to one.
+    if (m_defaultOptionIndices.isEmpty())
+        m_defaultOptionIndices.append((unsigned) !m_data.multiple());
+}
+
+void WMLSelectElement::selectDefaultOptions()
+{
+    ASSERT(!m_defaultOptionIndices.isEmpty());
+
+    if (!m_data.multiple()) {
+        setSelectedIndex(m_defaultOptionIndices.first() - 1, false);
+        return;
+    }
+
+    Vector<unsigned>::const_iterator end = m_defaultOptionIndices.end();
+    for (Vector<unsigned>::const_iterator it = m_defaultOptionIndices.begin(); it != end; ++it)
+        setSelectedIndex((*it) - 1, false);
+}
+
+void WMLSelectElement::initializeVariables()
+{
+    ASSERT(!m_defaultOptionIndices.isEmpty());
+
+    WMLPageState* pageState = wmlPageStateForDocument(document());
+    if (!pageState)
+        return;
+
+    const Vector<Element*>& items = m_data.listItems(this);
+    if (items.isEmpty())
+        return;
+
+    // Spec: If the 'iname' attribute is specified, then the named variable is set with the default option index. 
+    String iname = this->iname();
+    if (!iname.isEmpty())
+        pageState->storeVariable(iname, optionIndicesToString());
+
+    String name = this->name();
+    if (name.isEmpty())
+        return;
+
+    if (m_data.multiple()) {
+        // Spec: If the 'name' attribute is specified and the select is a multiple-choice element,
+        // then for each index greater than zero, the value of the 'value' attribute on the option
+        // element at the index is added to the name variable. 
+        pageState->storeVariable(name, optionIndicesToValueString());
+        return;
+    }
+
+    // Spec: If the 'name' attribute is specified and the select is a single-choice element,
+    // then the named variable is set with the value of the 'value' attribute on the option
+    // element at the default option index. 
+    unsigned optionIndex = m_defaultOptionIndices.first();
+    ASSERT(optionIndex >= 1);
+
+    int listIndex = optionToListIndex(optionIndex - 1);
+    ASSERT(listIndex >= 0);
+    ASSERT(listIndex < (int) items.size());
+
+    if (OptionElement* optionElement = toOptionElement(items[listIndex]))
+        pageState->storeVariable(name, optionElement->value());
+}
+
+void WMLSelectElement::updateVariables()
+{
+    WMLPageState* pageState = wmlPageStateForDocument(document());
+    if (!pageState)
+        return;
+
+    String name = this->name();
+    String iname = this->iname();
+    if (iname.isEmpty() && name.isEmpty())
+        return;
+
+    String nameString;
+    String inameString;
+
+    unsigned optionIndex = 0;
+    const Vector<Element*>& items = m_data.listItems(this);
+
+    for (unsigned i = 0; i < items.size(); ++i) {
+        OptionElement* optionElement = toOptionElement(items[i]);
+        if (!optionElement)
+            continue;
+
+        ++optionIndex;
+        if (!optionElement->selected())
+            continue;
+
+        if (!nameString.isEmpty())
+            nameString += ";";
+
+        if (!inameString.isEmpty())
+            inameString += ";";
+
+        nameString += optionElement->value();
+        inameString += String::number(optionIndex);
+    }
+
+    if (!name.isEmpty())
+        pageState->storeVariable(name, nameString);
+
+    if (!iname.isEmpty())
+        pageState->storeVariable(iname, inameString);
+}
+
+Vector<unsigned> WMLSelectElement::parseIndexValueString(const String& indexValue) const
+{
+    Vector<unsigned> indices;
+    if (indexValue.isEmpty())
+        return indices;
+
+    Vector<String> indexStrings;
+    indexValue.split(';', indexStrings);
+
+    bool ok = false;
+    unsigned optionCount = SelectElement::optionCount(m_data, this);
+
+    Vector<String>::const_iterator end = indexStrings.end();
+    for (Vector<String>::const_iterator it = indexStrings.begin(); it != end; ++it) {
+        unsigned parsedValue = (*it).toUIntStrict(&ok);
+        // Spec: Remove all non-integer indices from the value. Remove all out-of-range indices
+        // from the value, where out-of-range is defined as any index with a value greater than
+        // the number of options in the select or with a value less than one. 
+        if (!ok || parsedValue < 1 || parsedValue > optionCount)
+            continue;
+
+        // Spec: Remove duplicate indices.
+        if (indices.find(parsedValue) == notFound)
+            indices.append(parsedValue);
+    }
+
+    return indices;
+}
+
+Vector<unsigned> WMLSelectElement::valueStringToOptionIndices(const String& value) const
+{
+    Vector<unsigned> indices;
+    if (value.isEmpty())
+        return indices;
+
+    const Vector<Element*>& items = m_data.listItems(this);
+    if (items.isEmpty())
+        return indices;
+
+    Vector<String> indexStrings;
+    value.split(';', indexStrings);
+
+    unsigned optionIndex = 0;
+
+    Vector<String>::const_iterator end = indexStrings.end();
+    for (Vector<String>::const_iterator it = indexStrings.begin(); it != end; ++it) {
+        String value = *it;
+
+        for (unsigned i = 0; i < items.size(); ++i) {
+            if (!isOptionElement(items[i]))
+                continue;
+
+            ++optionIndex;
+            if (OptionElement* optionElement = toOptionElement(items[i])) {
+                if (optionElement->value() == value) {
+                    indices.append(optionIndex);
+                    break;
+                }
+            }
+        }
+    }
+
+    return indices;
+}
+
+String WMLSelectElement::optionIndicesToValueString() const
+{
+    String valueString;
+    if (m_defaultOptionIndices.isEmpty())
+        return valueString;
+
+    const Vector<Element*>& items = m_data.listItems(this);
+    if (items.isEmpty())
+        return valueString;
+
+    Vector<unsigned>::const_iterator end = m_defaultOptionIndices.end();
+    for (Vector<unsigned>::const_iterator it = m_defaultOptionIndices.begin(); it != end; ++it) {
+        unsigned optionIndex = (*it);
+        if (optionIndex < 1 || optionIndex > items.size())
+            continue;
+
+        int listIndex = optionToListIndex((*it) - 1);
+        ASSERT(listIndex >= 0);
+        ASSERT(listIndex < (int) items.size());
+
+        if (OptionElement* optionElement = toOptionElement(items[listIndex])) {
+            if (!valueString.isEmpty())
+                valueString += ";";
+
+            valueString += optionElement->value();
+        }
+    }
+
+    return valueString;
+}
+
+String WMLSelectElement::optionIndicesToString() const
+{
+    String valueString;
+    if (m_defaultOptionIndices.isEmpty())
+        return valueString;
+
+    Vector<unsigned>::const_iterator end = m_defaultOptionIndices.end();
+    for (Vector<unsigned>::const_iterator it = m_defaultOptionIndices.begin(); it != end; ++it) {
+        if (!valueString.isEmpty())
+            valueString += ";";
+
+        valueString += String::number(*it);
+    }
+
+    return valueString;
+}
+
+String WMLSelectElement::name() const
+{
+    return parseValueForbiddingVariableReferences(getAttribute(HTMLNames::nameAttr));
+}
+
+String WMLSelectElement::value() const
+{
+    return parseValueSubstitutingVariableReferences(getAttribute(HTMLNames::valueAttr));
+}
+
+String WMLSelectElement::iname() const
+{
+    return parseValueForbiddingVariableReferences(getAttribute(inameAttr));
+}
+
+String WMLSelectElement::ivalue() const
+{
+    return parseValueSubstitutingVariableReferences(getAttribute(ivalueAttr));
+}
+
 }
 
 #endif