2010-11-14 Kenichi Ishibashi <bashi@google.com>
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 15 Nov 2010 04:57:04 +0000 (04:57 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 15 Nov 2010 04:57:04 +0000 (04:57 +0000)
        Reviewed by Kent Tamura.

        [HTML5] "form" attribute support for form control elements
        https://bugs.webkit.org/show_bug.cgi?id=47813

        Adds a test file for "form" attribute of form-associated elements.
        The test might need to be revised because <label> and <object> don't
        support "form" attribute for now, in spite of the HTML5 spec says that
        they should support.

        * fast/forms/form-attribute-elements-expected.txt: Added.
        * fast/forms/form-attribute-elements-order-expected.txt: Added.
        * fast/forms/form-attribute-elements-order.html: Added.
        * fast/forms/form-attribute-elements-order2-expected.txt: Added.
        * fast/forms/form-attribute-elements-order2.html: Added.
        * fast/forms/form-attribute-elements.html: Added.
        * fast/forms/form-attribute-expected.txt: Added.
        * fast/forms/form-attribute.html: Added.
        * fast/forms/script-tests/form-attribute-elements-order.js: Added.
        * fast/forms/script-tests/form-attribute-elements-order2.js: Added.
        * fast/forms/script-tests/form-attribute-elements.js: Added.
        * fast/forms/script-tests/form-attribute.js: Added.
2010-11-14  Kenichi Ishibashi  <bashi@google.com>

        Reviewed by Kent Tamura.

        [HTML5] "form" attribute support for form control elements
        https://bugs.webkit.org/show_bug.cgi?id=47813

        Adds a list of form-associated elements with form attribute into
        the Document class to support form attribute.
        Adds a function to determine the right place to locate
        form-associated elements with form attribute into
        m_associatedElements of HTMLFormElement class.

        Tests: fast/forms/form-attribute-elements-order.html
               fast/forms/form-attribute-elements-order2.html
               fast/forms/form-attribute-elements.html
               fast/forms/form-attribute.html

        * dom/Document.cpp:
        (WebCore::Document::registerFormElementWithFormAttribute): Added.
        (WebCore::Document::unregisterFormElementWithFormAttribute): Added.
        (WebCore::Document::resetFormElementsOwner): Added.
        * dom/Document.h: Added the list for elements with form attribute.
        * html/HTMLAttributeNames.in: Added form attribute.
        * html/HTMLFormControlElement.cpp:
        (WebCore::HTMLFormControlElement::insertedIntoTree): Modified to handle
        form attribute.
        (WebCore::HTMLFormControlElement::removedFromTree): Ditto.
        (WebCore::HTMLFormControlElement::resetFormOwner): Added.
        (WebCore::HTMLFormControlElement::attributeChanged): Added.
        * html/HTMLFormControlElement.h:
        * html/HTMLFormElement.cpp:
        (WebCore::HTMLFormElement::HTMLFormElement): Modified to initialize
        newly-added variables.
        (WebCore::HTMLFormElement::insertedIntoDocument): Modified to reset
        form owner of form-associated elements.
        (WebCore::HTMLFormElement::removedFromDocument): Ditto.
        (WebCore::HTMLFormElement::formElementIndexWithFormAttribute): Added.
        (WebCore::HTMLFormElement::formElementIndex): Modified to treat
        form-associated elements with form attribute separately.
        (WebCore::HTMLFormElement::removeFormElement): Modified to handle
        form-associated elements with form attribute.
        * html/HTMLFormElement.h: Added three variables to handle form attribute.
        * html/HTMLOutputElement.cpp: Removed "FIXME" comment.
        (WebCore::HTMLOutputElement::parseMappedAttribute):
        * html/HTMLOutputElement.h: Removed setForm().

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

23 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/forms/form-attribute-elements-expected.txt [new file with mode: 0644]
LayoutTests/fast/forms/form-attribute-elements-order-expected.txt [new file with mode: 0644]
LayoutTests/fast/forms/form-attribute-elements-order.html [new file with mode: 0644]
LayoutTests/fast/forms/form-attribute-elements-order2-expected.txt [new file with mode: 0644]
LayoutTests/fast/forms/form-attribute-elements-order2.html [new file with mode: 0644]
LayoutTests/fast/forms/form-attribute-elements.html [new file with mode: 0644]
LayoutTests/fast/forms/form-attribute-expected.txt [new file with mode: 0644]
LayoutTests/fast/forms/form-attribute.html [new file with mode: 0644]
LayoutTests/fast/forms/script-tests/form-attribute-elements-order.js [new file with mode: 0644]
LayoutTests/fast/forms/script-tests/form-attribute-elements-order2.js [new file with mode: 0644]
LayoutTests/fast/forms/script-tests/form-attribute-elements.js [new file with mode: 0644]
LayoutTests/fast/forms/script-tests/form-attribute.js [new file with mode: 0644]
WebCore/ChangeLog
WebCore/dom/Document.cpp
WebCore/dom/Document.h
WebCore/html/HTMLAttributeNames.in
WebCore/html/HTMLFormControlElement.cpp
WebCore/html/HTMLFormControlElement.h
WebCore/html/HTMLFormElement.cpp
WebCore/html/HTMLFormElement.h
WebCore/html/HTMLOutputElement.cpp
WebCore/html/HTMLOutputElement.h

index a907c43..adb7a63 100644 (file)
@@ -1,3 +1,28 @@
+2010-11-14  Kenichi Ishibashi  <bashi@google.com>
+
+        Reviewed by Kent Tamura.
+
+        [HTML5] "form" attribute support for form control elements
+        https://bugs.webkit.org/show_bug.cgi?id=47813
+
+        Adds a test file for "form" attribute of form-associated elements.
+        The test might need to be revised because <label> and <object> don't
+        support "form" attribute for now, in spite of the HTML5 spec says that
+        they should support.
+
+        * fast/forms/form-attribute-elements-expected.txt: Added.
+        * fast/forms/form-attribute-elements-order-expected.txt: Added.
+        * fast/forms/form-attribute-elements-order.html: Added.
+        * fast/forms/form-attribute-elements-order2-expected.txt: Added.
+        * fast/forms/form-attribute-elements-order2.html: Added.
+        * fast/forms/form-attribute-elements.html: Added.
+        * fast/forms/form-attribute-expected.txt: Added.
+        * fast/forms/form-attribute.html: Added.
+        * fast/forms/script-tests/form-attribute-elements-order.js: Added.
+        * fast/forms/script-tests/form-attribute-elements-order2.js: Added.
+        * fast/forms/script-tests/form-attribute-elements.js: Added.
+        * fast/forms/script-tests/form-attribute.js: Added.
+
 2010-11-14  Mihai Parparita  <mihaip@chromium.org>
 
         Unreviewed Chromium expectations update.
diff --git a/LayoutTests/fast/forms/form-attribute-elements-expected.txt b/LayoutTests/fast/forms/form-attribute-elements-expected.txt
new file mode 100644 (file)
index 0000000..dc61c4c
--- /dev/null
@@ -0,0 +1,20 @@
+This test checks a form element can handle elements of which form attribute points the form element even if elements are outside of the form.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+- Ensures that elements attribute of the form contains elements which are outside of the form.
+PASS owner.elements.length is 3
+PASS owner.elements[0] is outerBefore
+PASS owner.elements[1] is inner
+PASS owner.elements[2] is outerAfter
+
+- Ensures that form submission contains name and value pairs for such elements.
+PASS pairs.length is 3
+PASS pairs[0] is "key1=value1"
+PASS pairs[1] is "key2=value2"
+PASS pairs[2] is "submitted=true"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/forms/form-attribute-elements-order-expected.txt b/LayoutTests/fast/forms/form-attribute-elements-order-expected.txt
new file mode 100644 (file)
index 0000000..0f6048c
--- /dev/null
@@ -0,0 +1,95 @@
+This test examines the order of the elements attribute of a form element.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+- Test for the case where some elements are outside of the form.
+PASS owner.elements.length is 6
+PASS owner.elements[0] is before1
+PASS owner.elements[1] is before2
+PASS owner.elements[2] is inner1
+PASS owner.elements[3] is inner2
+PASS owner.elements[4] is after1
+PASS owner.elements[5] is after2
+
+- Test for changing the value of the form attribute of a element which is located before the form owner.
+PASS owner.elements.length is 5
+PASS owner.elements[0] is before1
+PASS owner.elements[1] is inner1
+PASS owner.elements[2] is inner2
+PASS owner.elements[3] is after1
+PASS owner.elements[4] is after2
+PASS owner.elements.length is 6
+PASS owner.elements[0] is before1
+PASS owner.elements[1] is before2
+PASS owner.elements[2] is inner1
+PASS owner.elements[3] is inner2
+PASS owner.elements[4] is after1
+PASS owner.elements[5] is after2
+
+- Test for changing the value of the form attribute of a element which is located inside of the form owner.
+PASS owner.elements.length is 5
+PASS owner.elements[0] is before1
+PASS owner.elements[1] is before2
+PASS owner.elements[2] is inner1
+PASS owner.elements[3] is after1
+PASS owner.elements[4] is after2
+PASS owner.elements.length is 6
+PASS owner.elements[0] is before1
+PASS owner.elements[1] is before2
+PASS owner.elements[2] is inner1
+PASS owner.elements[3] is inner2
+PASS owner.elements[4] is after1
+PASS owner.elements[5] is after2
+
+- Test for changing the value of the form attribute of a element which is located after the form owner.
+PASS owner.elements.length is 5
+PASS owner.elements[0] is before1
+PASS owner.elements[1] is before2
+PASS owner.elements[2] is inner1
+PASS owner.elements[3] is inner2
+PASS owner.elements[4] is after2
+PASS owner.elements.length is 6
+PASS owner.elements[0] is before1
+PASS owner.elements[1] is before2
+PASS owner.elements[2] is inner1
+PASS owner.elements[3] is inner2
+PASS owner.elements[4] is after1
+PASS owner.elements[5] is after2
+
+- Test for setting form attribute of elements in reverse order.
+PASS owner.elements.length is 6
+PASS owner.elements[0] is before1
+PASS owner.elements[1] is before2
+PASS owner.elements[2] is inner1
+PASS owner.elements[3] is inner2
+PASS owner.elements[4] is after1
+PASS owner.elements[5] is after2
+
+- Test for setting form attribute of elements in random order.
+PASS owner.elements.length is 6
+PASS owner.elements[0] is before1
+PASS owner.elements[1] is before2
+PASS owner.elements[2] is inner1
+PASS owner.elements[3] is inner2
+PASS owner.elements[4] is after1
+PASS owner.elements[5] is after2
+
+- Test for removing/adding elements
+PASS owner.elements.length is 3
+PASS owner.elements[0] is before1
+PASS owner.elements[1] is inner1
+PASS owner.elements[2] is after2
+PASS owner.elements.length is 6
+PASS owner.elements[0] is before1
+PASS owner.elements[1] is before2
+PASS owner.elements[2] is inner1
+PASS owner.elements[3] is inner2
+PASS owner.elements[4] is after1
+PASS owner.elements[5] is after2
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+  
+
diff --git a/LayoutTests/fast/forms/form-attribute-elements-order.html b/LayoutTests/fast/forms/form-attribute-elements-order.html
new file mode 100644 (file)
index 0000000..2e1721d
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/form-attribute-elements-order.js"></script>
+<script src="../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/forms/form-attribute-elements-order2-expected.txt b/LayoutTests/fast/forms/form-attribute-elements-order2-expected.txt
new file mode 100644 (file)
index 0000000..25460ed
--- /dev/null
@@ -0,0 +1,49 @@
+This test examines the order of the elements attribute of a form element with form-associated elements with form attribute or witout form attibute.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+- Test for the case where some elements have form attribute but some others not.
+PASS owner.elements.length is 3
+PASS owner.elements[0] is input1
+PASS owner.elements[1] is input2
+PASS owner.elements[2] is input3
+PASS owner.elements.length is 3
+PASS owner.elements[0] is input1
+PASS owner.elements[1] is input2
+PASS owner.elements[2] is input3
+
+- Test for inserting/removing a form-associated element without form attribute.
+PASS owner.elements.length is 4
+PASS owner.elements[0] is before
+PASS owner.elements[1] is inner
+PASS owner.elements[2] is inner2
+PASS owner.elements[3] is after
+PASS owner.elements.length is 4
+PASS owner.elements[0] is before
+PASS owner.elements[1] is inner
+PASS owner.elements[2] is inner2
+PASS owner.elements[3] is after
+
+- Test for inserting/removing a form-associated element with form attribute.
+PASS owner.elements.length is 6
+PASS owner.elements[0] is before
+PASS owner.elements[1] is before2
+PASS owner.elements[2] is inner
+PASS owner.elements[3] is inner2
+PASS owner.elements[4] is after
+PASS owner.elements[5] is after2
+PASS owner.elements.length is 6
+PASS owner.elements[0] is before
+PASS owner.elements[1] is before2
+PASS owner.elements[2] is inner
+PASS owner.elements[3] is inner2
+PASS owner.elements[4] is after
+PASS owner.elements[5] is after2
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
+
diff --git a/LayoutTests/fast/forms/form-attribute-elements-order2.html b/LayoutTests/fast/forms/form-attribute-elements-order2.html
new file mode 100644 (file)
index 0000000..493a7db
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/form-attribute-elements-order2.js"></script>
+<script src="../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/forms/form-attribute-elements.html b/LayoutTests/fast/forms/form-attribute-elements.html
new file mode 100644 (file)
index 0000000..3389bce
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/form-attribute-elements.js"></script>
+<script src="../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/forms/form-attribute-expected.txt b/LayoutTests/fast/forms/form-attribute-expected.txt
new file mode 100644 (file)
index 0000000..7f20967
--- /dev/null
@@ -0,0 +1,53 @@
+This test checks the form attribute of the form-associated elements.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+- Checks the existence of the form attribute for each form-associated elements.
+FIXME: <label> and <object> don't support the form attribute for now.
+PASS document.getElementsByTagName("button")[0].form is owner
+PASS document.getElementsByTagName("fieldset")[0].form is owner
+PASS document.getElementsByTagName("input")[0].form is owner
+PASS document.getElementsByTagName("keygen")[0].form is owner
+FAIL document.getElementsByTagName("label")[0].form should be [object HTMLFormElement]. Was null.
+PASS document.getElementsByTagName("meter")[0].form is owner
+FAIL document.getElementsByTagName("object")[0].form should be [object HTMLFormElement]. Was null.
+PASS document.getElementsByTagName("output")[0].form is owner
+PASS document.getElementsByTagName("progress")[0].form is owner
+PASS document.getElementsByTagName("select")[0].form is owner
+PASS document.getElementsByTagName("textarea")[0].form is owner
+
+- Ensures that the form attribute points the form owner even if the element is within another form element.
+PASS inputElement.form is owner
+
+- Ensures that the form attribute of all form-associated element with or witout form attribute points the form owner.
+PASS inputElement1.form is owner
+PASS inputElement2.form is owner
+PASS inputElement3.form is owner
+
+- Ensures that the form attribute points the form owner even if the form element is nested another form element.
+NOTE: It seems that nesting form elements is not allowed so we ensure each form-associated elements associate with the outmost form element.
+PASS inputElement1.form is owner
+PASS inputElement2.form is owner
+PASS inputElement3.form is owner
+
+- Ensures whether the form owner is set correctly when the value of form attribute of a form-associated element changed.
+PASS inputElement.form is form1
+PASS inputElement.form is form2
+
+- Ensures whether the form owner is set correctly when the value of form attribute is added/removed.
+PASS inputElement.form is null
+PASS inputElement.form is owner
+PASS inputElement.form is null
+
+- Ensures whether the form owner is set correctly when the form owner is added/removed.
+PASS owner.name is "firstOwner"
+PASS owner.name is "secondOwner"
+PASS inputElement.form is owner
+PASS inputElement.form is null
+PASS inputElement.form is owner
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
diff --git a/LayoutTests/fast/forms/form-attribute.html b/LayoutTests/fast/forms/form-attribute.html
new file mode 100644 (file)
index 0000000..3069b0c
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="../../fast/js/resources/js-test-style.css">
+<script src="../../fast/js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="script-tests/form-attribute.js"></script>
+<script src="../../fast/js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/forms/script-tests/form-attribute-elements-order.js b/LayoutTests/fast/forms/script-tests/form-attribute-elements-order.js
new file mode 100644 (file)
index 0000000..6dcdae3
--- /dev/null
@@ -0,0 +1,153 @@
+description("This test examines the order of the elements attribute of a form element.");
+
+var container = document.createElement('div');
+document.body.appendChild(container);
+container.innerHTML = '<input name=victim id=before1 form=owner />' +
+    '<input name=victim id=before2 form=owner />' +
+    '<form id=owner action= method=GET>' +
+    '    <input name=victim id=inner1 form=owner />' +
+    '    <input name=victim id=inner2 form=owner />' +
+    '</form>' +
+    '<input name=victim id=after1 form=owner />' +
+    '<input name=victim id=after2 form=owner />';
+
+var owner = document.getElementById('owner');
+var before1 = document.getElementById('before1');
+var before2 = document.getElementById('before2');
+var inner1 = document.getElementById('inner1');
+var inner2 = document.getElementById('inner2');
+var after1 = document.getElementById('after1');
+var after2 = document.getElementById('after2');
+
+debug('- Test for the case where some elements are outside of the form.');
+shouldBe('owner.elements.length', '6');
+shouldBe('owner.elements[0]', 'before1');
+shouldBe('owner.elements[1]', 'before2');
+shouldBe('owner.elements[2]', 'inner1');
+shouldBe('owner.elements[3]', 'inner2');
+shouldBe('owner.elements[4]', 'after1');
+shouldBe('owner.elements[5]', 'after2');
+
+debug('');
+debug('- Test for changing the value of the form attribute of a element which is located before the form owner.');
+before2.attributes['form'].value = '';
+shouldBe('owner.elements.length', '5');
+shouldBe('owner.elements[0]', 'before1');
+shouldBe('owner.elements[1]', 'inner1');
+shouldBe('owner.elements[2]', 'inner2');
+shouldBe('owner.elements[3]', 'after1');
+shouldBe('owner.elements[4]', 'after2');
+before2.attributes['form'].value = 'owner';
+shouldBe('owner.elements.length', '6');
+shouldBe('owner.elements[0]', 'before1');
+shouldBe('owner.elements[1]', 'before2');
+shouldBe('owner.elements[2]', 'inner1');
+shouldBe('owner.elements[3]', 'inner2');
+shouldBe('owner.elements[4]', 'after1');
+shouldBe('owner.elements[5]', 'after2');
+
+debug('');
+debug('- Test for changing the value of the form attribute of a element which is located inside of the form owner.');
+inner2.attributes['form'].value = '';
+shouldBe('owner.elements.length', '5');
+shouldBe('owner.elements[0]', 'before1');
+shouldBe('owner.elements[1]', 'before2');
+shouldBe('owner.elements[2]', 'inner1');
+shouldBe('owner.elements[3]', 'after1');
+shouldBe('owner.elements[4]', 'after2');
+inner2.attributes['form'].value = 'owner';
+shouldBe('owner.elements.length', '6');
+shouldBe('owner.elements[0]', 'before1');
+shouldBe('owner.elements[1]', 'before2');
+shouldBe('owner.elements[2]', 'inner1');
+shouldBe('owner.elements[3]', 'inner2');
+shouldBe('owner.elements[4]', 'after1');
+shouldBe('owner.elements[5]', 'after2');
+
+debug('');
+debug('- Test for changing the value of the form attribute of a element which is located after the form owner.');
+after1.attributes['form'].value = '';
+shouldBe('owner.elements.length', '5');
+shouldBe('owner.elements[0]', 'before1');
+shouldBe('owner.elements[1]', 'before2');
+shouldBe('owner.elements[2]', 'inner1');
+shouldBe('owner.elements[3]', 'inner2');
+shouldBe('owner.elements[4]', 'after2');
+after1.attributes['form'].value = 'owner';
+shouldBe('owner.elements.length', '6');
+shouldBe('owner.elements[0]', 'before1');
+shouldBe('owner.elements[1]', 'before2');
+shouldBe('owner.elements[2]', 'inner1');
+shouldBe('owner.elements[3]', 'inner2');
+shouldBe('owner.elements[4]', 'after1');
+shouldBe('owner.elements[5]', 'after2');
+
+debug('');
+debug('- Test for setting form attribute of elements in reverse order.');
+before1.attributes['form'].value = '';
+before2.attributes['form'].value = '';
+inner1.attributes['form'].value = '';
+inner2.attributes['form'].value = '';
+after1.attributes['form'].value = '';
+after2.attributes['form'].value = '';
+
+after2.attributes['form'].value = 'owner';
+after1.attributes['form'].value = 'owner';
+inner2.attributes['form'].value = 'owner';
+inner1.attributes['form'].value = 'owner';
+before2.attributes['form'].value = 'owner';
+before1.attributes['form'].value = 'owner';
+
+shouldBe('owner.elements.length', '6');
+shouldBe('owner.elements[0]', 'before1');
+shouldBe('owner.elements[1]', 'before2');
+shouldBe('owner.elements[2]', 'inner1');
+shouldBe('owner.elements[3]', 'inner2');
+shouldBe('owner.elements[4]', 'after1');
+shouldBe('owner.elements[5]', 'after2');
+
+debug('');
+debug('- Test for setting form attribute of elements in random order.');
+before1.attributes['form'].value = '';
+before2.attributes['form'].value = '';
+inner1.attributes['form'].value = '';
+inner2.attributes['form'].value = '';
+after1.attributes['form'].value = '';
+after2.attributes['form'].value = '';
+
+after1.attributes['form'].value = 'owner';
+before1.attributes['form'].value = 'owner';
+inner2.attributes['form'].value = 'owner';
+before2.attributes['form'].value = 'owner';
+after2.attributes['form'].value = 'owner';
+inner1.attributes['form'].value = 'owner';
+
+shouldBe('owner.elements.length', '6');
+shouldBe('owner.elements[0]', 'before1');
+shouldBe('owner.elements[1]', 'before2');
+shouldBe('owner.elements[2]', 'inner1');
+shouldBe('owner.elements[3]', 'inner2');
+shouldBe('owner.elements[4]', 'after1');
+shouldBe('owner.elements[5]', 'after2');
+
+debug('');
+debug('- Test for removing/adding elements');
+container.removeChild(before2);
+owner.removeChild(inner2);
+container.removeChild(after1);
+shouldBe('owner.elements.length', '3');
+shouldBe('owner.elements[0]', 'before1');
+shouldBe('owner.elements[1]', 'inner1');
+shouldBe('owner.elements[2]', 'after2');
+container.insertBefore(before2, owner);
+owner.appendChild(inner2);
+container.insertBefore(after1, after2);
+shouldBe('owner.elements.length', '6');
+shouldBe('owner.elements[0]', 'before1');
+shouldBe('owner.elements[1]', 'before2');
+shouldBe('owner.elements[2]', 'inner1');
+shouldBe('owner.elements[3]', 'inner2');
+shouldBe('owner.elements[4]', 'after1');
+shouldBe('owner.elements[5]', 'after2');
+
+var successfullyParsed = true;
diff --git a/LayoutTests/fast/forms/script-tests/form-attribute-elements-order2.js b/LayoutTests/fast/forms/script-tests/form-attribute-elements-order2.js
new file mode 100644 (file)
index 0000000..6d7f799
--- /dev/null
@@ -0,0 +1,98 @@
+description("This test examines the order of the elements attribute of a form element with form-associated elements with form attribute or witout form attibute.");
+
+var container = document.createElement('div');
+document.body.appendChild(container);
+
+debug('');
+debug('- Test for the case where some elements have form attribute but some others not.');
+container.innerHTML = '<form id=owner>' +
+    '    <input id=input1 name=victim />' +
+    '    <input id=input2 name=victim form=owner />' +
+    '    <input id=input3 name=victim />' +
+    '</form>';
+var owner = document.getElementById('owner');
+var input1 = document.getElementById('input1');
+var input2 = document.getElementById('input2');
+var input3 = document.getElementById('input3');
+shouldBe('owner.elements.length', '3');
+shouldBe('owner.elements[0]', 'input1');
+shouldBe('owner.elements[1]', 'input2');
+shouldBe('owner.elements[2]', 'input3');
+
+container.innerHTML = '<form id=owner>' +
+    '    <input id=input1 name=victim form=owner />' +
+    '    <input id=input2 name=victim />' +
+    '    <input id=input3 name=victim form=owner />' +
+    '</form>';
+owner = document.getElementById('owner');
+input1 = document.getElementById('input1');
+input2 = document.getElementById('input2');
+input3 = document.getElementById('input3');
+shouldBe('owner.elements.length', '3');
+shouldBe('owner.elements[0]', 'input1');
+shouldBe('owner.elements[1]', 'input2');
+shouldBe('owner.elements[2]', 'input3');
+
+debug('');
+debug('- Test for inserting/removing a form-associated element without form attribute.');
+container.innerHTML = '<input name=victim id=before form=owner />' +
+    '<form id=owner action= method=GET>' +
+    '    <input name=victim id=inner />' +
+    '</form>' +
+    '<input name=victim id=after form=owner />';
+owner = document.getElementById('owner');
+var before = document.getElementById('before');
+var inner = document.getElementById('inner');
+var after = document.getElementById('after');
+
+var inner2 = document.createElement('input');
+inner2.name = 'victim';
+inner2.id = 'inner2';
+owner.appendChild(inner2);
+shouldBe('owner.elements.length', '4');
+shouldBe('owner.elements[0]', 'before');
+shouldBe('owner.elements[1]', 'inner');
+shouldBe('owner.elements[2]', 'inner2');
+shouldBe('owner.elements[3]', 'after');
+
+owner.removeChild(inner);
+owner.insertBefore(inner, inner2);
+shouldBe('owner.elements.length', '4');
+shouldBe('owner.elements[0]', 'before');
+shouldBe('owner.elements[1]', 'inner');
+shouldBe('owner.elements[2]', 'inner2');
+shouldBe('owner.elements[3]', 'after');
+
+debug('');
+debug('- Test for inserting/removing a form-associated element with form attribute.');
+var before2 = document.createElement('input');
+before2.name = 'victim';
+before2.id = 'before2';
+before2.setAttribute('form', 'owner');
+container.insertBefore(before2, owner);
+var after2 = document.createElement('input');
+after2.name = 'victim';
+after2.id = 'after2';
+after2.setAttribute('form', 'owner');
+container.appendChild(after2);
+shouldBe('owner.elements.length', '6');
+shouldBe('owner.elements[0]', 'before');
+shouldBe('owner.elements[1]', 'before2');
+shouldBe('owner.elements[2]', 'inner');
+shouldBe('owner.elements[3]', 'inner2');
+shouldBe('owner.elements[4]', 'after');
+shouldBe('owner.elements[5]', 'after2');
+
+container.removeChild(before);
+container.removeChild(after);
+container.insertBefore(before, before2);
+container.insertBefore(after, after2);
+shouldBe('owner.elements.length', '6');
+shouldBe('owner.elements[0]', 'before');
+shouldBe('owner.elements[1]', 'before2');
+shouldBe('owner.elements[2]', 'inner');
+shouldBe('owner.elements[3]', 'inner2');
+shouldBe('owner.elements[4]', 'after');
+shouldBe('owner.elements[5]', 'after2');
+
+var successfullyParsed = true;
diff --git a/LayoutTests/fast/forms/script-tests/form-attribute-elements.js b/LayoutTests/fast/forms/script-tests/form-attribute-elements.js
new file mode 100644 (file)
index 0000000..3826447
--- /dev/null
@@ -0,0 +1,42 @@
+description("This test checks a form element can handle elements of which form attribute points the form element even if elements are outside of the form.");
+
+if (window.layoutTestController)
+    layoutTestController.waitUntilDone();
+
+var container = document.createElement('div');
+document.body.appendChild(container);
+
+container.innerHTML = '<input type=hidden name=key1 value=value1 id=outer-before form=owner />' +
+    '<form id=owner action="" method="GET">' +
+    '    <input type=hidden name=key2 value=value2 id=inner form=owner />' +
+    '</form>' +
+    '<input type=hidden name=submitted value=true id=outer-after form=owner />';
+
+var owner = document.getElementById('owner');
+var outerBefore = document.getElementById('outer-before');
+var inner = document.getElementById('inner');
+var outerAfter = document.getElementById('outer-after');
+var query = window.location.search;
+
+if (query.indexOf('submitted=true') == -1) {
+    owner.submit();
+} else {
+    debug('- Ensures that elements attribute of the form contains elements which are outside of the form.');
+
+    shouldBe('owner.elements.length', '3');
+    shouldBe('owner.elements[0]', 'outerBefore');
+    shouldBe('owner.elements[1]', 'inner');
+    shouldBe('owner.elements[2]', 'outerAfter');
+
+    debug('');
+    debug('- Ensures that form submission contains name and value pairs for such elements.');
+    var pairs = query.substr(1).split('&');
+    shouldBe('pairs.length', '3');
+    shouldBeEqualToString('pairs[0]', 'key1=value1');
+    shouldBeEqualToString('pairs[1]', 'key2=value2');
+    shouldBeEqualToString('pairs[2]', 'submitted=true');
+    if (window.layoutTestController)
+        layoutTestController.notifyDone();
+}
+
+var successfullyParsed = true;
diff --git a/LayoutTests/fast/forms/script-tests/form-attribute.js b/LayoutTests/fast/forms/script-tests/form-attribute.js
new file mode 100644 (file)
index 0000000..082afe6
--- /dev/null
@@ -0,0 +1,119 @@
+description("This test checks the form attribute of the form-associated elements.");
+
+var container = document.createElement('div');
+document.body.appendChild(container);
+
+debug('- Checks the existence of the form attribute for each form-associated elements.');
+debug('FIXME: &lt;label&gt; and &lt;object&gt; don\'t support the form attribute for now.');
+container.innerHTML = '<form id=owner></form>' +
+    '<button name=victim form=owner />' +
+    '<fieldset name=victim form=owner />' +
+    '<input name=victim form=owner />' +
+    '<keygen name=victim form=owner />' +
+    '<label name=victim form=owner />' +
+    '<meter name=victim form=owner />' +
+    '<object name=victim form=owner />' +
+    '<output name=victim form=owner />' +
+    '<progress name=victim form=owner />' +
+    '<select name=victim form=owner />' +
+    '<textarea name=victim form=owner />';
+
+var owner = document.getElementById('owner');
+shouldBe('document.getElementsByTagName("button")[0].form', 'owner');
+shouldBe('document.getElementsByTagName("fieldset")[0].form', 'owner');
+shouldBe('document.getElementsByTagName("input")[0].form', 'owner');
+shouldBe('document.getElementsByTagName("keygen")[0].form', 'owner');
+shouldBe('document.getElementsByTagName("label")[0].form', 'owner');
+shouldBe('document.getElementsByTagName("meter")[0].form', 'owner');
+shouldBe('document.getElementsByTagName("object")[0].form', 'owner');
+shouldBe('document.getElementsByTagName("output")[0].form', 'owner');
+shouldBe('document.getElementsByTagName("progress")[0].form', 'owner');
+shouldBe('document.getElementsByTagName("select")[0].form', 'owner');
+shouldBe('document.getElementsByTagName("textarea")[0].form', 'owner');
+
+debug('');
+debug('- Ensures that the form attribute points the form owner even if the element is within another form element.');
+container.innerHTML = '<form id=owner></form>' +
+    '<form id=shouldNotBeOwner>' +
+    '    <input id=inputElement name=victim form=owner />' +
+    '</form>';
+owner = document.getElementById('owner');
+var inputElement = document.getElementById('inputElement');
+shouldBe('inputElement.form', 'owner');
+
+debug('');
+debug('- Ensures that the form attribute of all form-associated element with or witout form attribute points the form owner.');
+container.innerHTML = '<form id=owner>' +
+    '    <input id=inputElement1 name=victim />' +
+    '    <input id=inputElement2 name=victim form=owner />' +
+    '    <input id=inputElement3 name=victim />' +
+    '</form>';
+owner = document.getElementById('owner');
+var inputElement1 = document.getElementById('inputElement1');
+var inputElement2 = document.getElementById('inputElement2');
+var inputElement3 = document.getElementById('inputElement3');
+shouldBe('inputElement1.form', 'owner');
+shouldBe('inputElement2.form', 'owner');
+shouldBe('inputElement3.form', 'owner');
+
+debug('');
+debug('- Ensures that the form attribute points the form owner even if the form element is nested another form element.');
+debug('NOTE: It seems that nesting form elements is not allowed so we ensure each form-associated elements associate with the outmost form element.');
+container.innerHTML = '<form id=owner>' +
+    '    <form>' +
+    '        <input id=inputElement1 name=victim form=owner/>' +
+    '        <input id=inputElement2 name=victim />' +
+    '        <input id=inputElement3 name=victim form=owner/>' +
+    '    </form>' +
+    '</form>';
+owner = document.getElementById('owner');
+inputElement1 = document.getElementById('inputElement1');
+inputElement2 = document.getElementById('inputElement2');
+inputElement3 = document.getElementById('inputElement3');
+shouldBe('inputElement1.form', 'owner');
+shouldBe('inputElement2.form', 'owner');
+shouldBe('inputElement3.form', 'owner');
+
+debug('');
+debug('- Ensures whether the form owner is set correctly when the value of form attribute of a form-associated element changed.');
+container.innerHTML = '<form id=form1></form>' +
+    '<form id=form2></form>' +
+    '<input id=inputElement name=victim form=form1 />';
+var form1 = document.getElementById('form1');
+var form2 = document.getElementById('form2');
+inputElement = document.getElementById('inputElement');
+shouldBe('inputElement.form', 'form1');
+inputElement.attributes['form'].value = 'form2';
+shouldBe('inputElement.form', 'form2');
+
+debug('');
+debug('- Ensures whether the form owner is set correctly when the value of form attribute is added/removed.');
+container.innerHTML = '<form id=owner name=firstOwner></form>' +
+    '<input id=inputElement name=victim />';
+owner = document.getElementById('owner');
+inputElement = document.getElementById('inputElement');
+shouldBe('inputElement.form', 'null');
+var formAttribute = document.createAttribute('form');
+inputElement.setAttribute('form', 'owner');
+shouldBe('inputElement.form', 'owner');
+inputElement.removeAttribute('form');
+shouldBe('inputElement.form', 'null');
+
+debug('');
+debug('- Ensures whether the form owner is set correctly when the form owner is added/removed.');
+container.innerHTML = '<form id=owner name=firstOwner></form>' +
+    '<form id=owner name=secondOwner></form>' +
+    '<input id=inputElement name=victim form=owner />';
+owner = document.getElementById('owner');
+shouldBeEqualToString('owner.name', 'firstOwner');
+inputElement = document.getElementById('inputElement');
+container.removeChild(owner);
+owner = document.getElementById('owner');
+shouldBeEqualToString('owner.name', 'secondOwner');
+shouldBe('inputElement.form', 'owner');
+container.removeChild(owner);
+shouldBe('inputElement.form', 'null');
+container.appendChild(owner);
+shouldBe('inputElement.form', 'owner');
+
+var successfullyParsed = true;
index 5bccc86..2ca2eda 100644 (file)
@@ -1,3 +1,50 @@
+2010-11-14  Kenichi Ishibashi  <bashi@google.com>
+
+        Reviewed by Kent Tamura.
+
+        [HTML5] "form" attribute support for form control elements
+        https://bugs.webkit.org/show_bug.cgi?id=47813
+
+        Adds a list of form-associated elements with form attribute into
+        the Document class to support form attribute.
+        Adds a function to determine the right place to locate
+        form-associated elements with form attribute into
+        m_associatedElements of HTMLFormElement class.
+
+        Tests: fast/forms/form-attribute-elements-order.html
+               fast/forms/form-attribute-elements-order2.html
+               fast/forms/form-attribute-elements.html
+               fast/forms/form-attribute.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::registerFormElementWithFormAttribute): Added.
+        (WebCore::Document::unregisterFormElementWithFormAttribute): Added.
+        (WebCore::Document::resetFormElementsOwner): Added.
+        * dom/Document.h: Added the list for elements with form attribute.
+        * html/HTMLAttributeNames.in: Added form attribute.
+        * html/HTMLFormControlElement.cpp:
+        (WebCore::HTMLFormControlElement::insertedIntoTree): Modified to handle
+        form attribute.
+        (WebCore::HTMLFormControlElement::removedFromTree): Ditto.
+        (WebCore::HTMLFormControlElement::resetFormOwner): Added.
+        (WebCore::HTMLFormControlElement::attributeChanged): Added.
+        * html/HTMLFormControlElement.h:
+        * html/HTMLFormElement.cpp:
+        (WebCore::HTMLFormElement::HTMLFormElement): Modified to initialize
+        newly-added variables.
+        (WebCore::HTMLFormElement::insertedIntoDocument): Modified to reset
+        form owner of form-associated elements.
+        (WebCore::HTMLFormElement::removedFromDocument): Ditto.
+        (WebCore::HTMLFormElement::formElementIndexWithFormAttribute): Added.
+        (WebCore::HTMLFormElement::formElementIndex): Modified to treat
+        form-associated elements with form attribute separately.
+        (WebCore::HTMLFormElement::removeFormElement): Modified to handle
+        form-associated elements with form attribute.
+        * html/HTMLFormElement.h: Added three variables to handle form attribute.
+        * html/HTMLOutputElement.cpp: Removed "FIXME" comment.
+        (WebCore::HTMLOutputElement::parseMappedAttribute):
+        * html/HTMLOutputElement.h: Removed setForm().
+
 2010-11-14  David Hyatt  <hyatt@apple.com>
 
         Reviewed by Dan Bernstein.
index 7f77b3e..4c3425a 100644 (file)
@@ -4365,6 +4365,25 @@ void Document::setIconURL(const String& iconURL, const String& type)
         f->loader()->setIconURL(m_iconURL);
 }
 
+void Document::registerFormElementWithFormAttribute(Element* control)
+{
+    ASSERT(control->fastHasAttribute(formAttr));
+    m_formElementsWithFormAttribute.add(control);
+}
+
+void Document::unregisterFormElementWithFormAttribute(Element* control)
+{
+    m_formElementsWithFormAttribute.remove(control);
+}
+
+void Document::resetFormElementsOwner(HTMLFormElement* form)
+{
+    typedef FormElementListHashSet::iterator Iterator;
+    Iterator end = m_formElementsWithFormAttribute.end();
+    for (Iterator it = m_formElementsWithFormAttribute.begin(); it != end; ++it)
+        static_cast<HTMLFormControlElement*>(*it)->resetFormOwner(form);
+}
+
 void Document::setUseSecureKeyboardEntryWhenActive(bool usesSecureKeyboard)
 {
     if (m_useSecureKeyboardEntryWhenActive == usesSecureKeyboard)
index 8cc6272..ba48d6f 100644 (file)
@@ -483,6 +483,10 @@ public:
     bool hasStateForNewFormElements() const;
     bool takeStateForFormElement(AtomicStringImpl* name, AtomicStringImpl* type, String& state);
 
+    void registerFormElementWithFormAttribute(Element*);
+    void unregisterFormElementWithFormAttribute(Element*);
+    void resetFormElementsOwner(HTMLFormElement*);
+
     FrameView* view() const; // can be NULL
     Frame* frame() const { return m_frame; } // can be NULL
     Page* page() const; // can be NULL
@@ -1197,6 +1201,7 @@ private:
 
     typedef ListHashSet<Element*, 64> FormElementListHashSet;
     FormElementListHashSet m_formElementsWithState;
+    FormElementListHashSet m_formElementsWithFormAttribute;
 
     typedef HashMap<FormElementKey, Vector<String>, FormElementKeyHash, FormElementKeyHashTraits> FormElementStateMap;
     FormElementStateMap m_stateForNewFormElements;
index d9759fc..1881b6f 100644 (file)
@@ -94,6 +94,7 @@ expanded
 face
 focused
 for
+form
 formnovalidate
 frame
 frameborder
index 8fa000f..2f5d414 100644 (file)
@@ -154,6 +154,16 @@ void HTMLFormControlElement::attach()
 
 void HTMLFormControlElement::insertedIntoTree(bool deep)
 {
+    if (fastHasAttribute(formAttr)) {
+        document()->registerFormElementWithFormAttribute(this);
+        Element* element = document()->getElementById(fastGetAttribute(formAttr));
+        if (element && element->hasTagName(formTag)) {
+            if (m_form)
+                m_form->removeFormElement(this);
+            m_form = static_cast<HTMLFormElement*>(element);
+            m_form->registerFormElement(this);
+        }
+    }
     if (!m_form) {
         // This handles the case of a new form element being created by
         // JavaScript and inserted inside a form.  In the case of the parser
@@ -179,6 +189,9 @@ static inline Node* findRoot(Node* n)
 
 void HTMLFormControlElement::removedFromTree(bool deep)
 {
+    if (fastHasAttribute(formAttr))
+        document()->unregisterFormElementWithFormAttribute(this);
+
     // If the form and element are both in the same tree, preserve the connection to the form.
     // Otherwise, null out our form and remove ourselves from the form's list of elements.
     if (m_form && findRoot(this) != findRoot(m_form)) {
@@ -431,6 +444,50 @@ void HTMLFormControlElement::removeFromForm()
     m_form = 0;
 }
 
+void HTMLFormControlElement::resetFormOwner(HTMLFormElement* form)
+{
+    if (m_form) {
+        if (!fastHasAttribute(formAttr))
+            return;
+        m_form->removeFormElement(this);
+    }
+    m_form = 0;
+    if (fastHasAttribute(formAttr)) {
+        // The HTML5 spec says that the element should be associated with
+        // the first element in the document to have an ID that equal to
+        // the value of form attribute, so we put the result of
+        // document()->getElementById() over the given element.
+        Element* firstElement = document()->getElementById(fastGetAttribute(formAttr));
+        if (firstElement && firstElement->hasTagName(formTag))
+            m_form = static_cast<HTMLFormElement*>(firstElement);
+        else
+            m_form = form;
+    } else
+        m_form = findFormAncestor();
+    if (m_form)
+        m_form->registerFormElement(this);
+    else
+        document()->checkedRadioButtons().addButton(this);
+}
+
+void HTMLFormControlElement::attributeChanged(Attribute* attr, bool preserveDecls)
+{
+    if (attr->name() == formAttr) {
+        if (!fastHasAttribute(formAttr)) {
+            // The form attribute removed. We need to reset form owner here.
+            if (m_form)
+                m_form->removeFormElement(this);
+            m_form = findFormAncestor();
+            if (m_form)
+                m_form->registerFormElement(this);
+            else
+                document()->checkedRadioButtons().addButton(this);
+        } else
+            resetFormOwner(0);
+    }
+    HTMLElement::attributeChanged(attr, preserveDecls);
+}
+
 bool HTMLFormControlElement::isLabelable() const
 {
     // FIXME: Add meterTag and outputTag to the list once we support them.
index 1960fc3..8c98d91 100644 (file)
@@ -99,6 +99,9 @@ public:
     
     bool readOnly() const { return m_readOnly; }
 
+    void resetFormOwner(HTMLFormElement*);
+    virtual void attributeChanged(Attribute*, bool preserveDecls = false);
+
 protected:
     HTMLFormControlElement(const QualifiedName& tagName, Document*, HTMLFormElement*);
 
index da388d5..1aeb360 100644 (file)
@@ -65,6 +65,8 @@ using namespace HTMLNames;
 
 HTMLFormElement::HTMLFormElement(const QualifiedName& tagName, Document* document)
     : HTMLElement(tagName, document)
+    , m_associatedElementsBeforeIndex(0)
+    , m_associatedElementsAfterIndex(0)
     , m_wasUserSubmitted(false)
     , m_autocomplete(true)
     , m_insubmit(false)
@@ -133,6 +135,9 @@ void HTMLFormElement::insertedIntoDocument()
         static_cast<HTMLDocument*>(document())->addNamedItem(m_name);
 
     HTMLElement::insertedIntoDocument();
+
+    if (hasID())
+        document()->resetFormElementsOwner(this);
 }
 
 void HTMLFormElement::removedFromDocument()
@@ -141,6 +146,9 @@ void HTMLFormElement::removedFromDocument()
         static_cast<HTMLDocument*>(document())->removeNamedItem(m_name);
 
     HTMLElement::removedFromDocument();
+
+    if (hasID())
+        document()->resetFormElementsOwner(0);
 }
 
 void HTMLFormElement::handleLocalEvents(Event* event)
@@ -388,24 +396,64 @@ template<class T, size_t n> static void removeFromVector(Vector<T*, n> & vec, T*
         }
 }
 
+unsigned HTMLFormElement::formElementIndexWithFormAttribute(HTMLFormControlElement* element)
+{
+    // Compares the position of the form element and the inserted element.
+    // Updates the indeces in order to the relation of the position:
+    unsigned short position = compareDocumentPosition(element);
+    if (position & DOCUMENT_POSITION_CONTAINS)
+        ++m_associatedElementsAfterIndex;
+    else if (position & DOCUMENT_POSITION_PRECEDING) {
+        ++m_associatedElementsBeforeIndex;
+        ++m_associatedElementsAfterIndex;
+    }
+
+    if (m_associatedElements.isEmpty())
+        return 0;
+
+    // Does binary search on m_associatedElements in order to find the index
+    // to be inserted.
+    unsigned left = 0, right = m_associatedElements.size() - 1;
+    while (left != right) {
+        unsigned middle = left + ((right - left) / 2);
+        position = element->compareDocumentPosition(m_associatedElements[middle]);
+        if (position & DOCUMENT_POSITION_FOLLOWING)
+            right = middle;
+        else
+            left = middle + 1;
+    }
+
+    position = element->compareDocumentPosition(m_associatedElements[left]);
+    if (position & DOCUMENT_POSITION_FOLLOWING)
+        return left;
+    return left + 1;
+}
+
 unsigned HTMLFormElement::formElementIndex(HTMLFormControlElement* e)
 {
+    // Treats separately the case where this element has the form attribute
+    // for performance consideration.
+    if (e->fastHasAttribute(formAttr))
+        return formElementIndexWithFormAttribute(e);
+
     // Check for the special case where this element is the very last thing in
     // the form's tree of children; we don't want to walk the entire tree in that
     // common case that occurs during parsing; instead we'll just return a value
     // that says "add this form element to the end of the array".
     if (e->traverseNextNode(this)) {
-        unsigned i = 0;
+        unsigned i = m_associatedElementsBeforeIndex;
         for (Node* node = this; node; node = node->traverseNextNode(this)) {
-            if (node == e)
+            if (node == e) {
+                ++m_associatedElementsAfterIndex;
                 return i;
+            }
             if (node->isHTMLElement()
                     && static_cast<Element*>(node)->isFormControlElement()
                     && static_cast<HTMLFormControlElement*>(node)->form() == this)
                 ++i;
         }
     }
-    return m_associatedElements.size();
+    return m_associatedElementsAfterIndex++;
 }
 
 void HTMLFormElement::registerFormElement(HTMLFormControlElement* e)
@@ -418,6 +466,18 @@ void HTMLFormElement::registerFormElement(HTMLFormControlElement* e)
 void HTMLFormElement::removeFormElement(HTMLFormControlElement* e)
 {
     m_checkedRadioButtons.removeButton(e);
+    if (e->fastHasAttribute(formAttr)) {
+        unsigned index;
+        for (index = 0; index < m_associatedElements.size(); ++index)
+            if (m_associatedElements[index] == e)
+                break;
+        ASSERT(index < m_associatedElements.size());
+        if (index < m_associatedElementsBeforeIndex)
+            --m_associatedElementsBeforeIndex;
+        if (index < m_associatedElementsAfterIndex)
+            --m_associatedElementsAfterIndex;
+    } else
+        --m_associatedElementsAfterIndex;
     removeFromVector(m_associatedElements, e);
 }
 
index b996d9d..4e06bec 100644 (file)
@@ -131,6 +131,7 @@ private:
 
     void submit(Event*, bool activateSubmitButton, bool processingUserGesture, FormSubmissionTrigger);
 
+    unsigned formElementIndexWithFormAttribute(HTMLFormControlElement*);
     unsigned formElementIndex(HTMLFormControlElement*);
     // Returns true if the submission should be proceeded.
     bool validateInteractively(Event*);
@@ -148,6 +149,8 @@ private:
 
     CheckedRadioButtons m_checkedRadioButtons;
 
+    unsigned m_associatedElementsBeforeIndex;
+    unsigned m_associatedElementsAfterIndex;
     Vector<HTMLFormControlElement*> m_associatedElements;
     Vector<HTMLImageElement*> m_imageElements;
 
index dee21ae..b83263e 100644 (file)
@@ -58,7 +58,6 @@ const AtomicString& HTMLOutputElement::formControlType() const
 
 void HTMLOutputElement::parseMappedAttribute(Attribute* attr)
 {
-    // FIXME: Should handle the 'form' attribute here.
     if (attr->name() == HTMLNames::forAttr)
         setFor(attr->value());
     else
@@ -75,11 +74,6 @@ void HTMLOutputElement::setFor(const String& value)
     m_tokens->setValue(value);
 }
 
-void HTMLOutputElement::setForm(const String& /*id*/)
-{
-    // FIXME: Implement this function.
-}
-
 void HTMLOutputElement::childrenChanged(bool createdByParser, Node*, Node*, int)
 {
     if (createdByParser || m_isSetTextContentInProgress) {
index df807fb..83ecee2 100644 (file)
@@ -43,7 +43,6 @@ public:
 
     virtual bool willValidate() const { return false; }
 
-    void setForm(const String&);
     String value() const;
     void setValue(const String&);
     String defaultValue() const;