Title: [104668] trunk
Revision
104668
Author
tk...@chromium.org
Date
2012-01-10 21:03:23 -0800 (Tue, 10 Jan 2012)

Log Message

Source/WebCore: A radio button not in a document should not join a radio button group
https://bugs.webkit.org/show_bug.cgi?id=45719

Reviewed by Darin Adler.

As per the standard and other browser behaviors, we should not add a
radio button not in a document to a radio button group.

So, CheckedRadioButton member functions should be called in
insertedIntoDocument() and removedFromDocument(), and they should do
nothing if the specified radio button is not in a document.

This change also fixes a bug that form owner change by 'form' attribute
didn't update radio button groups correctly.

Test: fast/forms/radio/radio-group.html

* dom/CheckedRadioButtons.cpp:
(WebCore::shouldMakeRadioGroup): A helper function to check <input> state.
(WebCore::CheckedRadioButtons::addButton):
- Change the argument type: HTMLFormControlElement* -> HTMLInputElement*.
- Use shouldMakeRadioGroup().
(WebCore::CheckedRadioButtons::removeButton): ditto.
* dom/CheckedRadioButtons.h:
 Change the argument types of addButton() and removeButton():
 HTMLFormControlElement* -> HTMLInputElement*.
* html/FormAssociatedElement.cpp:
 Do not update m_form except in setForm() and formWillBeDestroyed().
 This helps us to call registerFormElement() and removeFromElement()
 correctly, and call willChangeForm()/didChangeForm() hooks correctly.
(WebCore::FormAssociatedElement::FormAssociatedElement):
(WebCore::FormAssociatedElement::~FormAssociatedElement):
(WebCore::FormAssociatedElement::insertedIntoTree):
(WebCore::FormAssociatedElement::removedFromTree):
(WebCore::FormAssociatedElement::setForm):
(WebCore::FormAssociatedElement::willChangeForm):
This virtual function is called before the owner form is updated.
(WebCore::FormAssociatedElement::didChangeForm):
This virtual function is called after the owner form was updated.
(WebCore::FormAssociatedElement::formWillBeDestroyed):
- Renamaed from formDestroyed()
- Add calls to willChangeForm() and didChangeForm().
(WebCore::FormAssociatedElement::resetFormOwner): Use setForm().
(WebCore::FormAssociatedElement::formAttributeChanged): ditto.
* html/FormAssociatedElement.h:
* html/HTMLFormControlElement.cpp:
(WebCore::HTMLFormControlElement::HTMLFormControlElement): Use setForm().
(WebCore::HTMLFormControlElement::~HTMLFormControlElement):
removeFormElement() is not needed. ~FormAssociatedElement() treats it.
(WebCore::HTMLFormControlElement::parseMappedAttribute):
No need to handle CheckedRadioButtons here. It is handled by HTMLInputElement.
(WebCore::HTMLFormControlElement::insertedIntoTree): ditto.
* html/HTMLFormElement.cpp:
(WebCore::HTMLFormElement::~HTMLFormElement):
 Rename formDestroyed() with willDestroyForm().
(WebCore::HTMLFormElement::registerFormElement):
We don't need to handle radio button groups here.
(WebCore::HTMLFormElement::removeFormElement): ditto.
* html/HTMLInputElement.cpp:
(WebCore::HTMLInputElement::~HTMLInputElement):
Calls setForm(0) so that it calls correct virtual functions of
HTMLInputElement.
(WebCore::HTMLInputElement::updateCheckedRadioButtons):
Checking attached() was wrong.
(WebCore::HTMLInputElement::willChangeForm):
Remove this radio button from a radio button group for the old form.
(WebCore::HTMLInputElement::didChangeForm):
Add this radio button to a radio button group for the new form.
(WebCore::HTMLInputElement::insertedIntoDocument):
Add CheckedRadioButtons::addButton() call.
(WebCore::HTMLInputElement::removedFromDocument):
Add CheckedRadioButtons::removeButton() call.
(WebCore::HTMLInputElement::didMoveToNewDocument):
We don't need to handle CheckedRadioButton() here because
removedFromDocument() handles it.
* html/HTMLInputElement.h:
* html/HTMLObjectElement.cpp:
(WebCore::HTMLObjectElement::HTMLObjectElement): Use setForm().
(WebCore::HTMLObjectElement::~HTMLObjectElement):
removeFormElement() is not needed. ~FormAssociatedElement() treats it.

LayoutTests: A radio button not in a Document should not join a radio button group
https://bugs.webkit.org/show_bug.cgi?id=45719

Reviewed by Darin Adler.

* fast/forms/radio/radio-group-expected.txt: Added.
* fast/forms/radio/radio-group.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (104667 => 104668)


--- trunk/LayoutTests/ChangeLog	2012-01-11 05:02:48 UTC (rev 104667)
+++ trunk/LayoutTests/ChangeLog	2012-01-11 05:03:23 UTC (rev 104668)
@@ -1,3 +1,13 @@
+2012-01-04  Kent Tamura  <tk...@chromium.org>
+
+        A radio button not in a Document should not join a radio button group
+        https://bugs.webkit.org/show_bug.cgi?id=45719
+
+        Reviewed by Darin Adler.
+
+        * fast/forms/radio/radio-group-expected.txt: Added.
+        * fast/forms/radio/radio-group.html: Added.
+
 2012-01-10  João Paulo Rechi Vita  <jprv...@openbossa.org>
 
         [Qt] Unskiping more passing tests

Added: trunk/LayoutTests/fast/forms/radio/radio-group-expected.txt (0 => 104668)


--- trunk/LayoutTests/fast/forms/radio/radio-group-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/forms/radio/radio-group-expected.txt	2012-01-11 05:03:23 UTC (rev 104668)
@@ -0,0 +1,63 @@
+Various tests about radio button group.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Changing the name attribute of a radio button:
+PASS $("radio1-1").checked is true
+PASS $("radio1-2").checked is true
+PASS $("radio1-1").checked is false
+PASS $("radio1-2").checked is true
+
+Changing the type of an input element to radio:
+PASS $("radio1-1").checked is true
+PASS $("radio1-1").checked is false
+PASS $("text1-2").checked is true
+
+Moving a checked radio button to another form:
+PASS $("outside").checked is true
+PASS $("inside").checked is true
+PASS $("inside2").checked is true
+PASS $("outside").checked is true
+PASS $("inside").checked is false
+PASS $("inside2").checked is true
+When a radio in a form is removed, it should not affect Document-level groups:
+PASS $("outside").checked is true
+
+Removing an ancestor owner form:
+PASS $("radio0-1").checked is true
+
+Changing form attribute
+PASS $("radio1-1").checked is true
+PASS $("radio1-2").checked is false
+PASS $("radio1-3").checked is true
+Removing a non-ancestor owner form:
+PASS $("radio1-3").checked is true
+PASS $("radio1-4").checked is true
+(The following test depends on gc(). It might fail on a real browser.)
+PASS $("radio1-1").checked is false
+PASS $("radio1-3").checked is true
+
+Adding a radio button to an orphan tree:
+PASS orphanDiv.getElementsByTagName("input")[0].checked is true
+PASS orphanDiv.getElementsByTagName("input")[1].checked is true
+Adding the orphan tree to a document:
+PASS orphanDiv.getElementsByTagName("input")[0].checked is false
+PASS orphanDiv.getElementsByTagName("input")[1].checked is true
+Parsing an orphan form:
+PASS orphanDiv.getElementsByTagName("input")[0].checked is true
+PASS orphanDiv.getElementsByTagName("input")[1].checked is true
+
+Moving a radio button to another Document:
+PASS doc.getElementById("radio4-2").checked is false
+PASS doc.getElementById("radio4-1").checked is true
+PASS doc.getElementById("radio4-1").checked is true
+
+Cloning a radio button:
+PASS original.checked is true
+PASS clonedRadio.checked is true
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Property changes on: trunk/LayoutTests/fast/forms/radio/radio-group-expected.txt
___________________________________________________________________

Added: svn:eol-style

Added: trunk/LayoutTests/fast/forms/radio/radio-group.html (0 => 104668)


--- trunk/LayoutTests/fast/forms/radio/radio-group.html	                        (rev 0)
+++ trunk/LayoutTests/fast/forms/radio/radio-group.html	2012-01-11 05:03:23 UTC (rev 104668)
@@ -0,0 +1,145 @@
+<!DOCTYPE html>
+<body>
+<script src=""
+
+<div id="parent"></div>
+
+<script>
+description('Various tests about radio button group.');
+
+const Checked = true;
+const Unchecked = false;
+
+function createRadio(name, checked, id) {
+    var input = document.createElement('input');
+    input.type = 'radio';
+    input.name = name;
+    input.checked = !!checked;
+    input.id = id;
+    return input;
+}
+
+function $(id) {
+    return document.getElementById(id);
+}
+
+var parent = $('parent');
+
+debug('Changing the name attribute of a radio button:');
+parent.innerHTML = '<form>' +
+    '<input type=radio name=name1 checked id=radio1-1>' +
+    '<input type=radio name=name2 checked id=radio1-2>' +
+    '</form>';
+shouldBeTrue('$("radio1-1").checked');
+shouldBeTrue('$("radio1-2").checked');
+$('radio1-2').name = 'name1';
+shouldBeFalse('$("radio1-1").checked');
+shouldBeTrue('$("radio1-2").checked');
+
+debug('');
+debug('Changing the type of an input element to radio:');
+parent.innerHTML = '<form>' +
+    '<input type=radio name=name1 checked id=radio1-1>' +
+    '<input type=text name=name1 checked id=text1-2>' +
+    '</form>';
+shouldBeTrue('$("radio1-1").checked');
+$('text1-2').type = 'radio';
+shouldBeFalse('$("radio1-1").checked');
+shouldBeTrue('$("text1-2").checked');
+
+debug('');
+debug('Moving a checked radio button to another form:');
+parent.innerHTML = '<input type="radio" name="name1" checked id="outside">' +
+    '<form id="form1">' +
+    '<input type="radio" name="name1" checked id="inside">' +
+    '</form>' +
+    '<form id="form2">' +
+    '<input type="radio" name="name1" checked id="inside2">' +
+    '</form>'
+shouldBeTrue('$("outside").checked');
+shouldBeTrue('$("inside").checked');
+shouldBeTrue('$("inside2").checked');
+$('form1').appendChild($('inside2'));
+shouldBeTrue('$("outside").checked');
+shouldBeFalse('$("inside").checked');
+shouldBeTrue('$("inside2").checked');
+debug('When a radio in a form is removed, it should not affect Document-level groups:');
+parent.removeChild($('form1'));
+shouldBeTrue('$("outside").checked');
+
+debug('');
+debug('Removing an ancestor owner form:');
+parent.innerHTML = '<input type=radio name=name0 checked id=radio0-1>' +
+    '<form id=form1><input type=radio name=name0 checked id=radio0-2></form>';
+parent.removeChild($('form1'));
+gc();
+shouldBeTrue('$("radio0-1").checked');
+
+debug('');
+debug('Changing form attribute');
+parent.innerHTML = '<input type=radio name=name1 checked id=radio1-1>' +
+    '<form id=form1><input type=radio name=name1 checked id=radio1-2></form>' +
+    '<form id=form2><input type=radio name=name1 checked id=radio1-3></form>';
+$('radio1-3').setAttribute('form', 'form1');
+shouldBeTrue('$("radio1-1").checked');
+shouldBeFalse('$("radio1-2").checked');
+shouldBeTrue('$("radio1-3").checked');
+
+debug('Removing a non-ancestor owner form:');
+$('form2').appendChild(createRadio('name1', Checked, 'radio1-4'));
+shouldBeTrue('$("radio1-3").checked');
+// If there is no elements with ID specified by form= attribute, the form
+// control is not associated to any forms.
+parent.removeChild($('form1'));
+// FIXME: We need to call gc() twice on Apple Mac.
+gc();
+gc();
+shouldBeTrue('$("radio1-4").checked');
+debug('(The following test depends on gc(). It might fail on a real browser.)');
+shouldBeFalse('$("radio1-1").checked');
+shouldBeTrue('$("radio1-3").checked');
+
+debug('');
+debug('Adding a radio button to an orphan tree:');
+var orphanDiv = document.createElement('div');
+orphanDiv.appendChild(createRadio('name2', Checked, ''));
+orphanDiv.appendChild(createRadio('name2', Checked, ''));
+shouldBeTrue('orphanDiv.getElementsByTagName("input")[0].checked');
+shouldBeTrue('orphanDiv.getElementsByTagName("input")[1].checked');
+debug('Adding the orphan tree to a document:');
+document.body.appendChild(orphanDiv);
+shouldBeFalse('orphanDiv.getElementsByTagName("input")[0].checked');
+shouldBeTrue('orphanDiv.getElementsByTagName("input")[1].checked');
+
+debug('Parsing an orphan form:');
+document.body.removeChild(orphanDiv);
+orphanDiv.innerHTML = '<form><input type=radio name=name3 checked><input type=radio name=name3 checked></form>';
+shouldBeTrue('orphanDiv.getElementsByTagName("input")[0].checked');
+shouldBeTrue('orphanDiv.getElementsByTagName("input")[1].checked');
+
+debug('');
+debug('Moving a radio button to another Document:');
+parent.innerHTML = '<input type=radio name=name4 checked id=radio4-1>';
+var doc = document.implementation.createHTMLDocument();
+doc.body.innerHTML = '<input type=radio name=name4 checked id=radio4-2>';
+doc.body.appendChild(doc.adoptNode($('radio4-1')));
+shouldBeFalse('doc.getElementById("radio4-2").checked');
+shouldBeTrue('doc.getElementById("radio4-1").checked');
+parent.appendChild(createRadio('name4', Checked, ''));
+shouldBeTrue('doc.getElementById("radio4-1").checked');
+
+debug('');
+debug('Cloning a radio button:');
+var original = createRadio('name5', Checked, '');
+parent.innerHTML = '';
+parent.appendChild(original);
+var clonedRadio = original.cloneNode(true);
+shouldBeTrue('original.checked');
+shouldBeTrue('clonedRadio.checked');
+
+parent.innerHTML = '';
+debug('');
+</script>
+
+<script src=""
+</body>
Property changes on: trunk/LayoutTests/fast/forms/radio/radio-group.html
___________________________________________________________________

Added: svn:eol-style

Modified: trunk/Source/WebCore/ChangeLog (104667 => 104668)


--- trunk/Source/WebCore/ChangeLog	2012-01-11 05:02:48 UTC (rev 104667)
+++ trunk/Source/WebCore/ChangeLog	2012-01-11 05:03:23 UTC (rev 104668)
@@ -1,3 +1,86 @@
+2012-01-04  Kent Tamura  <tk...@chromium.org>
+
+        A radio button not in a document should not join a radio button group
+        https://bugs.webkit.org/show_bug.cgi?id=45719
+
+        Reviewed by Darin Adler.
+
+        As per the standard and other browser behaviors, we should not add a
+        radio button not in a document to a radio button group.
+
+        So, CheckedRadioButton member functions should be called in
+        insertedIntoDocument() and removedFromDocument(), and they should do
+        nothing if the specified radio button is not in a document.
+
+        This change also fixes a bug that form owner change by 'form' attribute
+        didn't update radio button groups correctly.
+
+        Test: fast/forms/radio/radio-group.html
+
+        * dom/CheckedRadioButtons.cpp:
+        (WebCore::shouldMakeRadioGroup): A helper function to check <input> state.
+        (WebCore::CheckedRadioButtons::addButton):
+        - Change the argument type: HTMLFormControlElement* -> HTMLInputElement*.
+        - Use shouldMakeRadioGroup().
+        (WebCore::CheckedRadioButtons::removeButton): ditto.
+        * dom/CheckedRadioButtons.h:
+         Change the argument types of addButton() and removeButton():
+         HTMLFormControlElement* -> HTMLInputElement*.
+        * html/FormAssociatedElement.cpp:
+         Do not update m_form except in setForm() and formWillBeDestroyed().
+         This helps us to call registerFormElement() and removeFromElement()
+         correctly, and call willChangeForm()/didChangeForm() hooks correctly.
+        (WebCore::FormAssociatedElement::FormAssociatedElement):
+        (WebCore::FormAssociatedElement::~FormAssociatedElement):
+        (WebCore::FormAssociatedElement::insertedIntoTree):
+        (WebCore::FormAssociatedElement::removedFromTree):
+        (WebCore::FormAssociatedElement::setForm):
+        (WebCore::FormAssociatedElement::willChangeForm):
+        This virtual function is called before the owner form is updated.
+        (WebCore::FormAssociatedElement::didChangeForm):
+        This virtual function is called after the owner form was updated.
+        (WebCore::FormAssociatedElement::formWillBeDestroyed):
+        - Renamaed from formDestroyed()
+        - Add calls to willChangeForm() and didChangeForm().
+        (WebCore::FormAssociatedElement::resetFormOwner): Use setForm().
+        (WebCore::FormAssociatedElement::formAttributeChanged): ditto.
+        * html/FormAssociatedElement.h:
+        * html/HTMLFormControlElement.cpp:
+        (WebCore::HTMLFormControlElement::HTMLFormControlElement): Use setForm().
+        (WebCore::HTMLFormControlElement::~HTMLFormControlElement):
+        removeFormElement() is not needed. ~FormAssociatedElement() treats it.
+        (WebCore::HTMLFormControlElement::parseMappedAttribute):
+        No need to handle CheckedRadioButtons here. It is handled by HTMLInputElement.
+        (WebCore::HTMLFormControlElement::insertedIntoTree): ditto.
+        * html/HTMLFormElement.cpp:
+        (WebCore::HTMLFormElement::~HTMLFormElement):
+         Rename formDestroyed() with willDestroyForm().
+        (WebCore::HTMLFormElement::registerFormElement):
+        We don't need to handle radio button groups here.
+        (WebCore::HTMLFormElement::removeFormElement): ditto.
+        * html/HTMLInputElement.cpp:
+        (WebCore::HTMLInputElement::~HTMLInputElement):
+        Calls setForm(0) so that it calls correct virtual functions of
+        HTMLInputElement.
+        (WebCore::HTMLInputElement::updateCheckedRadioButtons):
+        Checking attached() was wrong.
+        (WebCore::HTMLInputElement::willChangeForm):
+        Remove this radio button from a radio button group for the old form.
+        (WebCore::HTMLInputElement::didChangeForm):
+        Add this radio button to a radio button group for the new form.
+        (WebCore::HTMLInputElement::insertedIntoDocument):
+        Add CheckedRadioButtons::addButton() call.
+        (WebCore::HTMLInputElement::removedFromDocument):
+        Add CheckedRadioButtons::removeButton() call.
+        (WebCore::HTMLInputElement::didMoveToNewDocument):
+        We don't need to handle CheckedRadioButton() here because
+        removedFromDocument() handles it.
+        * html/HTMLInputElement.h:
+        * html/HTMLObjectElement.cpp:
+        (WebCore::HTMLObjectElement::HTMLObjectElement): Use setForm().
+        (WebCore::HTMLObjectElement::~HTMLObjectElement):
+        removeFormElement() is not needed. ~FormAssociatedElement() treats it.
+
 2012-01-10  Kentaro Hara  <hara...@chromium.org>
 
         Remove redundant code from DOMWindowSQLDatabase.cpp

Modified: trunk/Source/WebCore/dom/CheckedRadioButtons.cpp (104667 => 104668)


--- trunk/Source/WebCore/dom/CheckedRadioButtons.cpp	2012-01-11 05:02:48 UTC (rev 104667)
+++ trunk/Source/WebCore/dom/CheckedRadioButtons.cpp	2012-01-11 05:03:23 UTC (rev 104668)
@@ -25,34 +25,32 @@
 
 namespace WebCore {
 
-void CheckedRadioButtons::addButton(HTMLFormControlElement* element)
+static inline bool shouldMakeRadioGroup(HTMLInputElement* element)
 {
-    // We only want to add radio buttons.
-    if (!element->isRadioButton())
-        return;
+    return element->isRadioButton() && !element->name().isEmpty() && element->inDocument();
+}
 
-    // Without a name, there is no group.
-    if (element->name().isEmpty())
+void CheckedRadioButtons::addButton(HTMLInputElement* element)
+{
+    if (!shouldMakeRadioGroup(element))
         return;
 
-    HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(element);
-
     // We only track checked buttons.
-    if (!inputElement->checked())
+    if (!element->checked())
         return;
 
     if (!m_nameToCheckedRadioButtonMap)
         m_nameToCheckedRadioButtonMap = adoptPtr(new NameToInputMap);
 
-    pair<NameToInputMap::iterator, bool> result = m_nameToCheckedRadioButtonMap->add(element->name().impl(), inputElement);
+    pair<NameToInputMap::iterator, bool> result = m_nameToCheckedRadioButtonMap->add(element->name().impl(), element);
     if (result.second)
         return;
     
     HTMLInputElement* oldCheckedButton = result.first->second;
-    if (oldCheckedButton == inputElement)
+    if (oldCheckedButton == element)
         return;
 
-    result.first->second = inputElement;
+    result.first->second = element;
     oldCheckedButton->setChecked(false);
 }
 
@@ -66,7 +64,7 @@
     return m_nameToCheckedRadioButtonMap->get(name.impl());
 }
 
-void CheckedRadioButtons::removeButton(HTMLFormControlElement* element)
+void CheckedRadioButtons::removeButton(HTMLInputElement* element)
 {
     if (element->name().isEmpty() || !m_nameToCheckedRadioButtonMap)
         return;
@@ -77,9 +75,7 @@
     if (it == m_nameToCheckedRadioButtonMap->end() || it->second != element)
         return;
     
-    HTMLInputElement* inputElement = element->toInputElement();
-    ASSERT_UNUSED(inputElement, inputElement);
-    ASSERT(inputElement->shouldAppearChecked());
+    ASSERT(element->shouldAppearChecked());
     ASSERT(element->isRadioButton());
 
     m_nameToCheckedRadioButtonMap->remove(it);

Modified: trunk/Source/WebCore/dom/CheckedRadioButtons.h (104667 => 104668)


--- trunk/Source/WebCore/dom/CheckedRadioButtons.h	2012-01-11 05:02:48 UTC (rev 104667)
+++ trunk/Source/WebCore/dom/CheckedRadioButtons.h	2012-01-11 05:03:23 UTC (rev 104668)
@@ -27,13 +27,12 @@
 
 namespace WebCore {
 
-class HTMLFormControlElement;
 class HTMLInputElement;
 
 class CheckedRadioButtons {
 public:
-    void addButton(HTMLFormControlElement*);
-    void removeButton(HTMLFormControlElement*);
+    void addButton(HTMLInputElement*);
+    void removeButton(HTMLInputElement*);
     HTMLInputElement* checkedButtonForGroup(const AtomicString& groupName) const;
 
 private:

Modified: trunk/Source/WebCore/html/FormAssociatedElement.cpp (104667 => 104668)


--- trunk/Source/WebCore/html/FormAssociatedElement.cpp	2012-01-11 05:02:48 UTC (rev 104667)
+++ trunk/Source/WebCore/html/FormAssociatedElement.cpp	2012-01-11 05:03:23 UTC (rev 104668)
@@ -35,13 +35,14 @@
 
 using namespace HTMLNames;
 
-FormAssociatedElement::FormAssociatedElement(HTMLFormElement* form)
-    : m_form(form)
+FormAssociatedElement::FormAssociatedElement()
+    : m_form(0)
 {
 }
 
 FormAssociatedElement::~FormAssociatedElement()
 {
+    setForm(0);
 }
 
 ValidityState* FormAssociatedElement::validity()
@@ -76,19 +77,13 @@
 void FormAssociatedElement::insertedIntoTree()
 {
     HTMLElement* element = toHTMLElement(this);
-    if (element->fastHasAttribute(formAttr)) {
-        // Resets the form owner at first to make sure the element don't
-        // associate any form elements when there is no element which has
-        // the given ID.
-        if (m_form) {
-            m_form->removeFormElement(this);
-            m_form = 0;
-        }
-        Element* formElement = element->treeScope()->getElementById(element->fastGetAttribute(formAttr));
-        if (formElement && formElement->hasTagName(formTag)) {
-            m_form = static_cast<HTMLFormElement*>(formElement);
-            m_form->registerFormElement(this);
-        }
+    const AtomicString& formId = element->fastGetAttribute(formAttr);
+    if (!formId.isNull()) {
+        HTMLFormElement* newForm = 0;
+        Element* newFormCandidate = element->treeScope()->getElementById(formId);
+        if (newFormCandidate && newFormCandidate->hasTagName(formTag))
+            newForm = static_cast<HTMLFormElement*>(newFormCandidate);
+        setForm(newForm);
         return;
     }
     if (!m_form) {
@@ -96,9 +91,7 @@
         // _javascript_ and inserted inside a form.  In the case of the parser
         // setting a form, we will already have a non-null value for m_form,
         // and so we don't need to do anything.
-        m_form = element->findFormAncestor();
-        if (m_form)
-            m_form->registerFormElement(this);
+        setForm(element->findFormAncestor());
     }
 }
 
@@ -117,15 +110,38 @@
     // 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(element) != findRoot(m_form))
-        removeFromForm();
+        setForm(0);
 }
 
-void FormAssociatedElement::removeFromForm()
+void FormAssociatedElement::setForm(HTMLFormElement* newForm)
 {
+    if (m_form == newForm)
+        return;
+    willChangeForm();
+    if (m_form)
+        m_form->removeFormElement(this);
+    m_form = newForm;
+    if (m_form)
+        m_form->registerFormElement(this);
+    didChangeForm();
+}
+
+void FormAssociatedElement::willChangeForm()
+{
+}
+
+void FormAssociatedElement::didChangeForm()
+{
+}
+
+void FormAssociatedElement::formWillBeDestroyed()
+{
+    ASSERT(m_form);
     if (!m_form)
         return;
-    m_form->removeFormElement(this);
+    willChangeForm();
     m_form = 0;
+    didChangeForm();
 }
 
 void FormAssociatedElement::resetFormOwner()
@@ -135,9 +151,8 @@
     if (m_form) {
         if (formId.isNull())
             return;
-        m_form->removeFormElement(this);
     }
-    m_form = 0;
+    HTMLFormElement* newForm = 0;
     if (!formId.isNull() && element->inDocument()) {
         // The HTML5 spec says that the element should be associated with
         // the first element in the document to have an ID that equal to
@@ -145,11 +160,10 @@
         // treeScope()->getElementById() over the given element.
         Element* firstElement = element->treeScope()->getElementById(formId);
         if (firstElement && firstElement->hasTagName(formTag))
-            m_form = static_cast<HTMLFormElement*>(firstElement);
+            newForm = static_cast<HTMLFormElement*>(firstElement);
     } else
-        m_form = element->findFormAncestor();
-    if (m_form)
-        m_form->registerFormElement(this);
+        newForm = element->findFormAncestor();
+    setForm(newForm);
 }
 
 void FormAssociatedElement::formAttributeChanged()
@@ -157,11 +171,7 @@
     HTMLElement* element = toHTMLElement(this);
     if (!element->fastHasAttribute(formAttr)) {
         // The form attribute removed. We need to reset form owner here.
-        if (m_form)
-            m_form->removeFormElement(this);
-        m_form = element->findFormAncestor();
-        if (m_form)
-            form()->registerFormElement(this);
+        setForm(element->findFormAncestor());
         element->document()->unregisterFormElementWithFormAttribute(this);
     } else
         resetFormOwner();

Modified: trunk/Source/WebCore/html/FormAssociatedElement.h (104667 => 104668)


--- trunk/Source/WebCore/html/FormAssociatedElement.h	2012-01-11 05:02:48 UTC (rev 104667)
+++ trunk/Source/WebCore/html/FormAssociatedElement.h	2012-01-11 05:03:23 UTC (rev 104668)
@@ -53,12 +53,12 @@
     // Return true for a successful control (see HTML4-17.13.2).
     virtual bool appendFormData(FormDataList&, bool) { return false; }
 
-    virtual void formDestroyed() { m_form = 0; }
+    void formWillBeDestroyed();
 
     void resetFormOwner();
 
 protected:
-    FormAssociatedElement(HTMLFormElement*);
+    FormAssociatedElement();
 
     void insertedIntoTree();
     void removedFromTree();
@@ -66,10 +66,15 @@
     void removedFromDocument();
     void didMoveToNewDocument(Document* oldDocument);
 
-    void setForm(HTMLFormElement* form) { m_form = form; }
-    void removeFromForm();
+    void setForm(HTMLFormElement*);
     void formAttributeChanged();
 
+    // If you add an override of willChangeForm() or didChangeForm() to a class
+    // derived from this one, you will need to add a call to setForm(0) to the
+    // destructor of that class.
+    virtual void willChangeForm();
+    virtual void didChangeForm();
+
 private:
     virtual const AtomicString& formControlName() const = 0;
 

Modified: trunk/Source/WebCore/html/HTMLFormControlElement.cpp (104667 => 104668)


--- trunk/Source/WebCore/html/HTMLFormControlElement.cpp	2012-01-11 05:02:48 UTC (rev 104667)
+++ trunk/Source/WebCore/html/HTMLFormControlElement.cpp	2012-01-11 05:03:23 UTC (rev 104668)
@@ -50,7 +50,6 @@
 
 HTMLFormControlElement::HTMLFormControlElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form)
     : HTMLElement(tagName, document)
-    , FormAssociatedElement(form)
     , m_disabled(false)
     , m_readOnly(false)
     , m_required(false)
@@ -61,18 +60,12 @@
     , m_wasChangedSinceLastFormControlChangeEvent(false)
     , m_hasAutofocused(false)
 {
-    if (!this->form())
-        setForm(findFormAncestor());
-    if (this->form())
-        this->form()->registerFormElement(this);
-
+    setForm(form ? form : findFormAncestor());
     setHasCustomWillOrDidRecalcStyle();
 }
 
 HTMLFormControlElement::~HTMLFormControlElement()
 {
-    if (form())
-        form()->removeFormElement(this);
 }
 
 void HTMLFormControlElement::detach()
@@ -108,11 +101,9 @@
 
 void HTMLFormControlElement::parseMappedAttribute(Attribute* attr)
 {
-    if (attr->name() == formAttr) {
+    if (attr->name() == formAttr)
         formAttributeChanged();
-        if (!form())
-            document()->checkedRadioButtons().addButton(this);
-    } else if (attr->name() == disabledAttr) {
+    else if (attr->name() == disabledAttr) {
         bool oldDisabled = m_disabled;
         m_disabled = !attr->isNull();
         if (oldDisabled != m_disabled) {
@@ -205,9 +196,6 @@
 void HTMLFormControlElement::insertedIntoTree(bool deep)
 {
     FormAssociatedElement::insertedIntoTree();
-    if (!form())
-        document()->checkedRadioButtons().addButton(this);
-
     HTMLElement::insertedIntoTree(deep);
 }
 

Modified: trunk/Source/WebCore/html/HTMLFormElement.cpp (104667 => 104668)


--- trunk/Source/WebCore/html/HTMLFormElement.cpp	2012-01-11 05:02:48 UTC (rev 104667)
+++ trunk/Source/WebCore/html/HTMLFormElement.cpp	2012-01-11 05:03:23 UTC (rev 104668)
@@ -95,7 +95,7 @@
         document()->unregisterForPageCacheSuspensionCallbacks(this);
 
     for (unsigned i = 0; i < m_associatedElements.size(); ++i)
-        m_associatedElements[i]->formDestroyed();
+        m_associatedElements[i]->formWillBeDestroyed();
     for (unsigned i = 0; i < m_imageElements.size(); ++i)
         m_imageElements[i]->m_form = 0;
 }
@@ -456,18 +456,11 @@
 
 void HTMLFormElement::registerFormElement(FormAssociatedElement* e)
 {
-    if (e->isFormControlElement()) {
-        HTMLFormControlElement* element = static_cast<HTMLFormControlElement*>(e);
-        document()->checkedRadioButtons().removeButton(element);
-        m_checkedRadioButtons.addButton(element);
-    }
     m_associatedElements.insert(formElementIndex(e), e);
 }
 
 void HTMLFormElement::removeFormElement(FormAssociatedElement* e)
 {
-    if (e->isFormControlElement())
-        m_checkedRadioButtons.removeButton(static_cast<HTMLFormControlElement*>(e));
     unsigned index;
     for (index = 0; index < m_associatedElements.size(); ++index) {
         if (m_associatedElements[index] == e)

Modified: trunk/Source/WebCore/html/HTMLInputElement.cpp (104667 => 104668)


--- trunk/Source/WebCore/html/HTMLInputElement.cpp	2012-01-11 05:02:48 UTC (rev 104667)
+++ trunk/Source/WebCore/html/HTMLInputElement.cpp	2012-01-11 05:03:23 UTC (rev 104668)
@@ -116,11 +116,9 @@
     if (needsSuspensionCallback())
         document()->unregisterForPageCacheSuspensionCallbacks(this);
 
-    document()->checkedRadioButtons().removeButton(this);
-
-    // Need to remove this from the form while it is still an HTMLInputElement,
-    // so can't wait for the base class's destructor to do it.
-    removeFromForm();
+    // Need to remove form association while this is still an HTMLInputElement
+    // so that virtual functions are called correctly.
+    setForm(0);
 }
 
 const AtomicString& HTMLInputElement::formControlName() const
@@ -179,8 +177,7 @@
 
 void HTMLInputElement::updateCheckedRadioButtons()
 {
-    if (attached() && checked())
-        checkedRadioButtons().addButton(this);
+    checkedRadioButtons().addButton(this);
 
     if (form()) {
         const Vector<FormAssociatedElement*>& controls = form()->associatedElements();
@@ -1502,6 +1499,32 @@
     reset();
 }
 
+void HTMLInputElement::willChangeForm()
+{
+    checkedRadioButtons().removeButton(this);
+    HTMLTextFormControlElement::willChangeForm();
+}
+
+void HTMLInputElement::didChangeForm()
+{
+    HTMLTextFormControlElement::didChangeForm();
+    checkedRadioButtons().addButton(this);
+}
+
+void HTMLInputElement::insertedIntoDocument()
+{
+    HTMLTextFormControlElement::insertedIntoDocument();
+    ASSERT(inDocument());
+    checkedRadioButtons().addButton(this);
+}
+
+void HTMLInputElement::removedFromDocument()
+{
+    ASSERT(inDocument());
+    checkedRadioButtons().removeButton(this);
+    HTMLTextFormControlElement::removedFromDocument();
+}
+
 void HTMLInputElement::didMoveToNewDocument(Document* oldDocument)
 {
     m_inputType->willMoveToNewOwnerDocument();

Modified: trunk/Source/WebCore/html/HTMLInputElement.h (104667 => 104668)


--- trunk/Source/WebCore/html/HTMLInputElement.h	2012-01-11 05:02:48 UTC (rev 104667)
+++ trunk/Source/WebCore/html/HTMLInputElement.h	2012-01-11 05:03:23 UTC (rev 104668)
@@ -249,6 +249,10 @@
     enum AutoCompleteSetting { Uninitialized, On, Off };
     enum AnyStepHandling { RejectAny, AnyIsDefaultStep };
 
+    virtual void willChangeForm() OVERRIDE;
+    virtual void didChangeForm() OVERRIDE;
+    virtual void insertedIntoDocument() OVERRIDE;
+    virtual void removedFromDocument() OVERRIDE;
     virtual void didMoveToNewDocument(Document* oldDocument) OVERRIDE;
 
     virtual bool isKeyboardFocusable(KeyboardEvent*) const;

Modified: trunk/Source/WebCore/html/HTMLObjectElement.cpp (104667 => 104668)


--- trunk/Source/WebCore/html/HTMLObjectElement.cpp	2012-01-11 05:02:48 UTC (rev 104667)
+++ trunk/Source/WebCore/html/HTMLObjectElement.cpp	2012-01-11 05:03:23 UTC (rev 104668)
@@ -55,21 +55,15 @@
 
 inline HTMLObjectElement::HTMLObjectElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form, bool createdByParser) 
     : HTMLPlugInImageElement(tagName, document, createdByParser, ShouldNotPreferPlugInsForImages)
-    , FormAssociatedElement(form)
     , m_docNamedItem(true)
     , m_useFallbackContent(false)
 {
     ASSERT(hasTagName(objectTag));
-    if (!this->form())
-        setForm(findFormAncestor());
-    if (this->form())
-        this->form()->registerFormElement(this);
+    setForm(form ? form : findFormAncestor());
 }
 
 inline HTMLObjectElement::~HTMLObjectElement()
 {
-    if (form())
-        form()->removeFormElement(this);
 }
 
 PassRefPtr<HTMLObjectElement> HTMLObjectElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form, bool createdByParser)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to