Reviewed by Adam Treat.
authorzimmermann@webkit.org <zimmermann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 29 Jun 2009 19:11:12 +0000 (19:11 +0000)
committerzimmermann@webkit.org <zimmermann@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 29 Jun 2009 19:11:12 +0000 (19:11 +0000)
Fixes: https://bugs.webkit.org/show_bug.cgi?id=26246

Implement most WML specific <select> element features.
Add 'iname' / 'ivalue' support and support variable references.

Add two tests covering most select/variable related functionality.
Needs more tests when adding full 'iname' / 'ivalue' support (used in conjuction with onpick).

Tests: http/tests/wml/post-data-to-server.html
       wml/select-element-variables.html

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@45342 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/wml/post-data-to-server-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/wml/post-data-to-server.html [new file with mode: 0644]
LayoutTests/http/tests/wml/resources/dumpVariables.cgi [new file with mode: 0755]
LayoutTests/http/tests/wml/resources/post-data-to-server.js [new file with mode: 0644]
LayoutTests/wml/resources/WMLTestCase.js
LayoutTests/wml/resources/select-element-variables.js [new file with mode: 0644]
LayoutTests/wml/select-element-variables-expected.txt [new file with mode: 0644]
LayoutTests/wml/select-element-variables.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/dom/SelectElement.cpp
WebCore/dom/SelectElement.h
WebCore/html/HTMLSelectElement.cpp
WebCore/wml/WMLCardElement.cpp
WebCore/wml/WMLSelectElement.cpp
WebCore/wml/WMLSelectElement.h

index 6826101..39b2dd0 100644 (file)
@@ -1,3 +1,31 @@
+2009-06-29  Nikolas Zimmermann  <nikolas.zimmermann@torchmobile.com>
+
+        Reviewed by Adam Treat.
+
+        Fixes: https://bugs.webkit.org/show_bug.cgi?id=26246
+
+        Implement most WML specific <select> element features.
+        Add two tests covering most select/variable related functionality.
+
+        * http/tests/wml/post-data-to-server-expected.txt: Added.
+        * http/tests/wml/post-data-to-server.html: Added.
+        * http/tests/wml/resources/dumpVariables.cgi: Added.
+        * http/tests/wml/resources/post-data-to-server.js: Added.
+        (setupTestDocument):
+        (sendTextToControl):
+        (prepareTest):
+        (executeTest):
+        * wml/resources/WMLTestCase.js:
+        (createWMLTestCase.iframeElement.onload):
+        (createWMLTestCase):
+        (delayExecuteTest):
+        * wml/resources/select-element-variables.js: Added.
+        (setupTestDocument):
+        (prepareTest):
+        (executeTest):
+        * wml/select-element-variables-expected.txt: Added.
+        * wml/select-element-variables.html: Added.
+
 2009-06-28  Dan Bernstein  <mitz@apple.com>
 
         Reviewed by Simon Fraser.
diff --git a/LayoutTests/http/tests/wml/post-data-to-server-expected.txt b/LayoutTests/http/tests/wml/post-data-to-server-expected.txt
new file mode 100644 (file)
index 0000000..15670cf
--- /dev/null
@@ -0,0 +1,24 @@
+WML http navigation layout tests - using XHTML scripting
+
+
+Complex data posting example
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+This page was requested with an HTTP POST
+
+The query parameters are: category=wap;title=%5Btitle%5D%20TEST%20PASSED;url=%5Burl%5D%20TEST%20PASSED;desc=%5Bdesc%5D%20TEST%20PASSED
+Form content category: 'wap'
+Form content title : '[title] TEST PASSED'
+Form content url : '[url] TEST PASSED'
+Form content desc : '[desc] TEST PASSED'
+
diff --git a/LayoutTests/http/tests/wml/post-data-to-server.html b/LayoutTests/http/tests/wml/post-data-to-server.html
new file mode 100644 (file)
index 0000000..7c647dc
--- /dev/null
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../../../fast/js/resources/js-test-style.css">
+<script>var relativePathToLayoutTests = "../../..";</script>
+<script src="../../../wml/resources/WMLTestCase.js"></script>
+<script src="../../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<h1>WML http navigation layout tests - using XHTML scripting</h1>
+<p id="description"></p>
+<div id="console"></div>
+<script src="resources/post-data-to-server.js" charset="utf8"></script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/wml/resources/dumpVariables.cgi b/LayoutTests/http/tests/wml/resources/dumpVariables.cgi
new file mode 100755 (executable)
index 0000000..85b4e87
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/perl -w
+
+use CGI;
+
+$query = new CGI;
+$method = $query->request_method();
+$queryString = $query->query_string;
+$cat = $query->param("category");
+$title = $query->param("title");
+$url = $query->param("url");
+$desc = $query->param("desc");
+
+print "Content-type: text/html\r\n";
+print "Cache-Control: no-store, no-cache, must-revalidate\r\n";
+print "\r\n";
+
+print <<HERE_DOC_END;
+<html>
+<body>
+This page was requested with an HTTP $method<br/><br/>
+The query parameters are: $queryString<br/>
+Form content category: '$cat'<br/>
+Form content title   : '$title'<br/>
+Form content url     : '$url'<br/>
+Form content desc    : '$desc'<br/>
+</body>
+</html>
+HERE_DOC_END
diff --git a/LayoutTests/http/tests/wml/resources/post-data-to-server.js b/LayoutTests/http/tests/wml/resources/post-data-to-server.js
new file mode 100644 (file)
index 0000000..c479a38
--- /dev/null
@@ -0,0 +1,126 @@
+// [Name] post-data-to-server.js
+
+createWMLTestCase("Complex data posting example", false);
+    
+var titleInput;
+var urlInput;
+var descInput;
+
+function setupTestDocument() {
+    var cardElement = testDocument.documentElement.firstChild;
+
+    var anchorElement = createWMLElement("anchor");
+    anchorElement.setAttribute("id", "top");
+    anchorElement.appendChild(testDocument.createTextNode("Start test"));
+    cardElement.appendChild(anchorElement);
+
+    // Pure WML based form data posting using variable substitution
+    var goElement = createWMLElement("go");
+    goElement.setAttribute("method", "POST");
+    goElement.setAttribute("href", "http://127.0.0.1:8000/wml/resources/dumpVariables.cgi");
+
+    var postfieldElement1 = createWMLElement("postfield");
+    postfieldElement1.setAttribute("name", "category");
+    postfieldElement1.setAttribute("value", "$category");
+    goElement.appendChild(postfieldElement1);
+
+    var postfieldElement2 = createWMLElement("postfield");
+    postfieldElement2.setAttribute("name", "title");
+    postfieldElement2.setAttribute("value", "$title");
+    goElement.appendChild(postfieldElement2);
+
+    var postfieldElement3 = createWMLElement("postfield");
+    postfieldElement3.setAttribute("name", "url");
+    postfieldElement3.setAttribute("value", "$url");
+    goElement.appendChild(postfieldElement3);
+
+    var postfieldElement4 = createWMLElement("postfield");
+    postfieldElement4.setAttribute("name", "desc");
+    postfieldElement4.setAttribute("value", "$desc");
+    goElement.appendChild(postfieldElement4);
+
+    anchorElement.appendChild(goElement);
+
+    // Category
+    var fieldsetElement1 = createWMLElement("fieldset");
+    fieldsetElement1.textContent = "Category:";
+
+    var selectElement = createWMLElement("select");
+    selectElement.setAttribute("name", "category");
+    selectElement.setAttribute("value", "wap");
+    selectElement.setAttribute("title", "Section?");
+
+    var optionElement1 = createWMLElement("option");
+    optionElement1.textContent = "Computers";
+    optionElement1.setAttribute("value", "computers");
+    selectElement.appendChild(optionElement1);
+
+    var optionElement2 = createWMLElement("option");
+    optionElement2.textContent = "WAP";
+    optionElement2.setAttribute("value", "wap");
+    selectElement.appendChild(optionElement2);
+
+    var optionElement3 = createWMLElement("option");
+    optionElement3.textContent = "Gateways";
+    optionElement3.setAttribute("value", "gateways");
+    selectElement.appendChild(optionElement3);
+
+    fieldsetElement1.appendChild(selectElement);
+    cardElement.appendChild(fieldsetElement1);
+
+    // Title
+    var fieldsetElement2 = createWMLElement("fieldset");
+    fieldsetElement2.textContent = "Title:";
+
+    titleInput = createWMLElement("input");
+    titleInput.setAttribute("type", "text");
+    titleInput.setAttribute("name", "title");
+    titleInput.setAttribute("size", "20");
+    titleInput.setAttribute("emptyok", "false");
+    fieldsetElement2.appendChild(titleInput);
+    cardElement.appendChild(fieldsetElement2);
+
+    // URL
+    var fieldsetElement3 = createWMLElement("fieldset");
+    fieldsetElement3.textContent = "URL:";
+
+    urlInput = createWMLElement("input");
+    urlInput.setAttribute("type", "text");
+    urlInput.setAttribute("name", "url");
+    urlInput.setAttribute("size", "100");
+    urlInput.setAttribute("emptyok", "false");
+    fieldsetElement3.appendChild(urlInput);
+    cardElement.appendChild(fieldsetElement3);
+
+    // Description
+    var fieldsetElement4 = createWMLElement("fieldset");
+    fieldsetElement4.textContent = "Description:";
+
+    descInput = createWMLElement("input");
+    descInput.setAttribute("type", "text");
+    descInput.setAttribute("name", "desc");
+    descInput.setAttribute("size", "800");
+    descInput.setAttribute("emptyok", "true");
+    fieldsetElement4.appendChild(descInput);
+    cardElement.appendChild(fieldsetElement4);
+}
+
+function sendTextToControl(element, text) {
+    element.focus();
+    testDocument.execCommand("InsertText", false, text);
+}
+
+function prepareTest() {
+    sendTextToControl(titleInput, "[title] TEST PASSED");
+    sendTextToControl(urlInput, "[url] TEST PASSED");
+    sendTextToControl(descInput, "[desc] TEST PASSED");
+
+    testDocument.getElementById("top").focus();
+    startTest(25, 15);
+}
+
+function executeTest() {
+    completeTest();
+}
+
+var successfullyParsed = true;
index 926f183..845bbba 100644 (file)
@@ -42,7 +42,7 @@ function createWMLTestCase(desc, substituteVariables, testName) {
 
         // External deck jumps
         if (testDocument != null && !substituteVariables) {
-            executeTest();
+            delayExecuteTest();
             return;
         }
 
@@ -51,7 +51,7 @@ function createWMLTestCase(desc, substituteVariables, testName) {
 
         // Variable refresh
         if (loaded && substituteVariables) {
-            executeTest();
+            delayExecuteTest();
             return;
         }
 
@@ -61,7 +61,7 @@ function createWMLTestCase(desc, substituteVariables, testName) {
         // Internal deck jumps
         if (!substituteVariables) {
             executed = true;
-            executeTest();
+            delayExecuteTest();
         }
     }
 
@@ -90,6 +90,11 @@ function startTest(x, y) {
     window.setTimeout("triggerUpdate(" + x + ", " + y + ")", 0);
 }
 
+function delayExecuteTest() {
+    // Assure first layout finished, after making changes
+    window.setTimeout("executeTest()", 0);
+}
+
 function completeTest() {
     var script = document.createElementNS(xhtmlNS, "script");
 
diff --git a/LayoutTests/wml/resources/select-element-variables.js b/LayoutTests/wml/resources/select-element-variables.js
new file mode 100644 (file)
index 0000000..753c2b7
--- /dev/null
@@ -0,0 +1,51 @@
+// [Name] select-element-variables.js
+
+createWMLTestCase("Tests variable references in conjuction with select elements");
+
+var resultElement;
+
+function setupTestDocument() {
+    var cardElement = testDocument.documentElement.firstChild;
+    var card2Element = testDocument.getElementById("card2");
+
+    var anchorElement = createWMLElement("anchor");
+    anchorElement.textContent = "Start test";
+    cardElement.appendChild(anchorElement);
+
+    var goElement = createWMLElement("go");
+    goElement.setAttribute("href", "#card2");
+    anchorElement.appendChild(goElement);
+
+    var pElement = createWMLElement("p");
+    pElement.textContent = "Select an option";
+    cardElement.appendChild(pElement);
+
+    var selectElement = createWMLElement("select");
+    selectElement.setAttribute("name", "result");
+    cardElement.appendChild(selectElement);
+
+    resultElement = card2Element.firstChild;
+    resultElement.textContent = "Selection result: '$result'";
+
+    var optionElement1 = createWMLElement("option");
+    optionElement1.setAttribute("value", "doggy");
+    optionElement1.textContent = "Dog";
+    selectElement.appendChild(optionElement1);
+
+    var optionElement2 = createWMLElement("option");
+    optionElement2.setAttribute("value", "kitten");
+    optionElement2.textContent = "Cat";
+    selectElement.appendChild(optionElement2);
+}
+
+function prepareTest() {
+    shouldBeEqualToString("resultElement.textContent", "Selection result: '$result'");
+    startTest(25, 15);
+}
+
+function executeTest() {
+    shouldBeEqualToString("resultElement.textContent", "Selection result: 'doggy'");
+    completeTest();
+}
+
+var successfullyParsed = true;
diff --git a/LayoutTests/wml/select-element-variables-expected.txt b/LayoutTests/wml/select-element-variables-expected.txt
new file mode 100644 (file)
index 0000000..6a7af27
--- /dev/null
@@ -0,0 +1,19 @@
+WML layout tests - using XHTML scripting
+
+
+Tests variable references in conjuction with select elements
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS resultElement.textContent is "Selection result: '$result'"
+PASS resultElement.textContent is "Selection result: 'doggy'"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+Selection result: 'doggy'
diff --git a/LayoutTests/wml/select-element-variables.html b/LayoutTests/wml/select-element-variables.html
new file mode 100644 (file)
index 0000000..e0ed6af
--- /dev/null
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../fast/js/resources/js-test-style.css">
+<script>var relativePathToLayoutTests = "..";</script>
+<script src="resources/WMLTestCase.js"></script>
+<script src="../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<h1>WML layout tests - using XHTML scripting</h1>
+<p id="description"></p>
+<div id="console"></div>
+<script src="resources/select-element-variables.js"></script>
+</body>
+</html>
index eca65fc..3c2c5e8 100644 (file)
@@ -1,3 +1,44 @@
+2009-06-29  Nikolas Zimmermann  <nikolas.zimmermann@torchmobile.com>
+
+        Reviewed by Adam Treat.
+
+        Fixes: https://bugs.webkit.org/show_bug.cgi?id=26246
+
+        Implement most WML specific <select> element features.
+        Add 'iname' / 'ivalue' support and support variable references.
+
+        Add two tests covering most select/variable related functionality.
+        Needs more tests when adding full 'iname' / 'ivalue' support (used in conjuction with onpick).
+
+        Tests: http/tests/wml/post-data-to-server.html
+               wml/select-element-variables.html
+
+        * dom/SelectElement.cpp:
+        (WebCore::SelectElement::optionCount): Refactored from HTMLSelectElement::length() for use within HTML & WML.
+        * dom/SelectElement.h:
+        * html/HTMLSelectElement.cpp:
+        (WebCore::HTMLSelectElement::length): Use new SelectElement::optionCount() function. (no functional changes for HTML).
+        * wml/WMLCardElement.cpp:
+        (WebCore::WMLCardElement::handleIntrinsicEventIfNeeded): Activated commented code taking care of WMLSelectElement initialization.
+        * wml/WMLSelectElement.cpp: Add a bunch of new code handling WML specific feature processing.
+        (WebCore::WMLSelectElement::title):
+        (WebCore::WMLSelectElement::formControlName):
+        (WebCore::WMLSelectElement::defaultEventHandler):
+        (WebCore::WMLSelectElement::selectInitialOptions):
+        (WebCore::WMLSelectElement::calculateDefaultOptionIndices):
+        (WebCore::WMLSelectElement::selectDefaultOptions):
+        (WebCore::WMLSelectElement::initializeVariables):
+        (WebCore::WMLSelectElement::updateVariables):
+        (WebCore::WMLSelectElement::parseIndexValueString):
+        (WebCore::WMLSelectElement::valueStringToOptionIndices):
+        (WebCore::WMLSelectElement::optionIndicesToValueString):
+        (WebCore::WMLSelectElement::optionIndicesToString):
+        (WebCore::WMLSelectElement::name):
+        (WebCore::WMLSelectElement::value):
+        (WebCore::WMLSelectElement::iname):
+        (WebCore::WMLSelectElement::ivalue):
+        * wml/WMLSelectElement.h:
+
 2009-06-29  David Hyatt  <hyatt@apple.com>
 
         Reviewed by Adam Roben.
index 1831f3a..7552c56 100644 (file)
@@ -867,6 +867,19 @@ void SelectElement::accessKeySetSelectedIndex(SelectElementData& data, Element*
     scrollToSelection(data, element);
 }
 
+unsigned SelectElement::optionCount(const SelectElementData& data, const Element* element)
+{
+    unsigned options = 0;
+
+    const Vector<Element*>& items = data.listItems(element);
+    for (unsigned i = 0; i < items.size(); ++i) {
+        if (isOptionElement(items[i]))
+            ++options;
+    }
+
+    return options;
+}
+
 // SelectElementData
 SelectElementData::SelectElementData()
     : m_multiple(false) 
index 29187ae..4cc76a0 100644 (file)
@@ -95,7 +95,8 @@ protected:
     static void typeAheadFind(SelectElementData&, Element*, KeyboardEvent*);
     static void insertedIntoTree(SelectElementData&, Element*);
     static void accessKeySetSelectedIndex(SelectElementData&, Element*, int index);
-
+    static unsigned optionCount(const SelectElementData&, const Element*);
 private:
     static void menuListDefaultEventHandler(SelectElementData&, Element*, Event*, HTMLFormElement*);
     static void listBoxDefaultEventHandler(SelectElementData&, Element*, Event*, HTMLFormElement*);
index c47bb70..2409f31 100644 (file)
@@ -106,13 +106,7 @@ int HTMLSelectElement::activeSelectionEndListIndex() const
 
 unsigned HTMLSelectElement::length() const
 {
-    unsigned len = 0;
-    const Vector<Element*>& items = listItems();
-    for (unsigned i = 0; i < items.size(); ++i) {
-        if (items[i]->hasLocalName(optionTag))
-            ++len;
-    }
-    return len;
+    return SelectElement::optionCount(m_data, this);
 }
 
 void HTMLSelectElement::add(HTMLElement *element, HTMLElement *before, ExceptionCode& ec)
index 3713c59..0f49bd7 100644 (file)
@@ -34,6 +34,7 @@
 #include "WMLInputElement.h"
 #include "WMLIntrinsicEventHandler.h"
 #include "WMLNames.h"
+#include "WMLSelectElement.h"
 #include "WMLTemplateElement.h"
 #include "WMLTimerElement.h"
 #include "WMLVariables.h"
@@ -165,17 +166,14 @@ void WMLCardElement::handleIntrinsicEventIfNeeded()
     if (m_eventTimer)
         m_eventTimer->start();
 
-    // FIXME: Initialize select elements in this card
     for (Node* node = traverseNextNode(); node != 0; node = node->traverseNextNode()) {
         if (!node->isElementNode())
             continue;
 
         if (node->hasTagName(inputTag))
             static_cast<WMLInputElement*>(node)->initialize();
-        /*
         else if (node->hasTagName(selectTag))
             static_cast<WMLSelectElement*>(node)->selectInitialOptions();
-        */
     }
 }
 
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
index 8d3e0f9..6cd3bcb 100644 (file)
@@ -32,6 +32,9 @@ public:
     WMLSelectElement(const QualifiedName&, Document*);
     virtual ~WMLSelectElement();
 
+    virtual String title() const;
+
+    virtual const AtomicString& formControlName() const;
     virtual const AtomicString& formControlType() const;
  
     virtual bool isKeyboardFocusable(KeyboardEvent*) const;
@@ -47,7 +50,8 @@ public:
     virtual bool canStartSelection() const { return false; }
 
     virtual int selectedIndex() const;
-    virtual void setSelectedIndex(int index, bool deselect = true, bool fireOnChange = false);
+    virtual void setSelectedIndex(int index, bool deselect = true);
+    virtual void setSelectedIndexByUser(int index, bool deselect = true, bool fireOnChangeNow = false);
 
     virtual int size() const { return m_data.size(); }
     virtual bool multiple() const { return m_data.multiple(); }
@@ -81,11 +85,28 @@ public:
     void accessKeySetSelectedIndex(int);
     void setRecalcListItems();
     void scrollToSelection();
+    void selectInitialOptions();
 
 private:
     virtual void insertedIntoTree(bool);
 
+    void calculateDefaultOptionIndices();
+    void selectDefaultOptions();
+    void initializeVariables();
+    void updateVariables();
+
+    Vector<unsigned> parseIndexValueString(const String&) const;
+    Vector<unsigned> valueStringToOptionIndices(const String&) const;
+    String optionIndicesToValueString() const;
+    String optionIndicesToString() const;
+
+    String name() const;
+    String value() const;
+    String iname() const;
+    String ivalue() const;
+
     SelectElementData m_data;
+    Vector<unsigned> m_defaultOptionIndices;
 };
 
 }