Bug 1358448 - Add radio back to a radio group after moving out of a form. r=smaug

The container of a radio group is the form, if it belongs to a form, or the
document object otherwise.
When moving a radio out of a form, we should add it back to a radio group.
Similary, before moving the radio to a form, we should remove it from the
original radio group.

MozReview-Commit-ID: 22WsEhz2SXH

--HG--
extra : rebase_source : 68dbd50da523b734086d8e5de6e6ae179b0b4af0
This commit is contained in:
Jessica Jong 2017-05-01 23:10:00 +02:00
parent fbc92ea6ed
commit 57c042eeb9
8 changed files with 57 additions and 13 deletions

View File

@ -332,7 +332,7 @@ nsNodeUtils::LastRelease(nsINode* aNode)
aNode->HasFlag(ADDED_TO_FORM)) { aNode->HasFlag(ADDED_TO_FORM)) {
// Tell the form (if any) this node is going away. Don't // Tell the form (if any) this node is going away. Don't
// notify, since we're being destroyed in any case. // notify, since we're being destroyed in any case.
static_cast<nsGenericHTMLFormElement*>(aNode)->ClearForm(true); static_cast<nsGenericHTMLFormElement*>(aNode)->ClearForm(true, true);
} }
if (aNode->IsHTMLElement(nsGkAtoms::img) && if (aNode->IsHTMLElement(nsGkAtoms::img) &&

View File

@ -100,12 +100,12 @@ HTMLFormControlsCollection::Clear()
{ {
// Null out childrens' pointer to me. No refcounting here // Null out childrens' pointer to me. No refcounting here
for (int32_t i = mElements.Length() - 1; i >= 0; i--) { for (int32_t i = mElements.Length() - 1; i >= 0; i--) {
mElements[i]->ClearForm(false); mElements[i]->ClearForm(false, false);
} }
mElements.Clear(); mElements.Clear();
for (int32_t i = mNotInElements.Length() - 1; i >= 0; i--) { for (int32_t i = mNotInElements.Length() - 1; i >= 0; i--) {
mNotInElements[i]->ClearForm(false); mNotInElements[i]->ClearForm(false, false);
} }
mNotInElements.Clear(); mNotInElements.Clear();

View File

@ -386,7 +386,7 @@ CollectOrphans(nsINode* aRemovalRoot,
if (node->HasFlag(MAYBE_ORPHAN_FORM_ELEMENT)) { if (node->HasFlag(MAYBE_ORPHAN_FORM_ELEMENT)) {
node->UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT); node->UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
if (!nsContentUtils::ContentIsDescendantOf(node, aRemovalRoot)) { if (!nsContentUtils::ContentIsDescendantOf(node, aRemovalRoot)) {
node->ClearForm(true); node->ClearForm(true, false);
// When a form control loses its form owner, its state can change. // When a form control loses its form owner, its state can change.
node->UpdateState(true); node->UpdateState(true);

View File

@ -1550,6 +1550,27 @@ HTMLInputElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
aValue, aNotify); aValue, aNotify);
} }
void
HTMLInputElement::BeforeSetForm(bool aBindToTree)
{
// No need to remove from radio group if we are just binding to tree.
if (mType == NS_FORM_INPUT_RADIO && !aBindToTree) {
WillRemoveFromRadioGroup();
}
}
void
HTMLInputElement::AfterClearForm(bool aUnbindOrDelete)
{
MOZ_ASSERT(!mForm);
// Do not add back to radio group if we are releasing or unbinding from tree.
if (mType == NS_FORM_INPUT_RADIO && !aUnbindOrDelete) {
AddedToRadioGroup();
UpdateValueMissingValidityStateForRadio(false);
}
}
// nsIDOMHTMLInputElement // nsIDOMHTMLInputElement
NS_IMETHODIMP NS_IMETHODIMP

View File

@ -984,6 +984,10 @@ protected:
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify) override; const nsAttrValue* aValue, bool aNotify) override;
virtual void BeforeSetForm(bool aBindToTree) override;
virtual void AfterClearForm(bool aUnbindOrDelete) override;
/** /**
* Dispatch a select event. Returns true if the event was not cancelled. * Dispatch a select event. Returns true if the event was not cancelled.
*/ */

View File

@ -1760,12 +1760,21 @@ nsGenericHTMLFormElement::SetForm(nsIDOMHTMLFormElement* aForm)
NS_ASSERTION(!mForm, NS_ASSERTION(!mForm,
"We don't support switching from one non-null form to another."); "We don't support switching from one non-null form to another.");
SetForm(static_cast<HTMLFormElement*>(aForm), false);
}
void nsGenericHTMLFormElement::SetForm(HTMLFormElement* aForm, bool aBindToTree)
{
if (aForm) {
BeforeSetForm(aBindToTree);
}
// keep a *weak* ref to the form here // keep a *weak* ref to the form here
mForm = static_cast<HTMLFormElement*>(aForm); mForm = aForm;
} }
void void
nsGenericHTMLFormElement::ClearForm(bool aRemoveFromForm) nsGenericHTMLFormElement::ClearForm(bool aRemoveFromForm, bool aUnbindOrDelete)
{ {
NS_ASSERTION((mForm != nullptr) == HasFlag(ADDED_TO_FORM), NS_ASSERTION((mForm != nullptr) == HasFlag(ADDED_TO_FORM),
"Form control should have had flag set correctly"); "Form control should have had flag set correctly");
@ -1792,6 +1801,8 @@ nsGenericHTMLFormElement::ClearForm(bool aRemoveFromForm)
UnsetFlags(ADDED_TO_FORM); UnsetFlags(ADDED_TO_FORM);
mForm = nullptr; mForm = nullptr;
AfterClearForm(aUnbindOrDelete);
} }
Element* Element*
@ -1876,12 +1887,12 @@ nsGenericHTMLFormElement::UnbindFromTree(bool aDeep, bool aNullParent)
// Might need to unset mForm // Might need to unset mForm
if (aNullParent) { if (aNullParent) {
// No more parent means no more form // No more parent means no more form
ClearForm(true); ClearForm(true, true);
} else { } else {
// Recheck whether we should still have an mForm. // Recheck whether we should still have an mForm.
if (HasAttr(kNameSpaceID_None, nsGkAtoms::form) || if (HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
!FindAncestorForm(mForm)) { !FindAncestorForm(mForm)) {
ClearForm(true); ClearForm(true, true);
} else { } else {
UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT); UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
} }
@ -2269,7 +2280,7 @@ nsGenericHTMLFormElement::UpdateFormOwner(bool aBindToTree,
bool needStateUpdate = false; bool needStateUpdate = false;
if (!aBindToTree) { if (!aBindToTree) {
needStateUpdate = mForm && mForm->IsDefaultSubmitElement(this); needStateUpdate = mForm && mForm->IsDefaultSubmitElement(this);
ClearForm(true); ClearForm(true, false);
} }
HTMLFormElement *oldForm = mForm; HTMLFormElement *oldForm = mForm;
@ -2295,7 +2306,7 @@ nsGenericHTMLFormElement::UpdateFormOwner(bool aBindToTree,
"associated with the id in @form!"); "associated with the id in @form!");
if (element && element->IsHTMLElement(nsGkAtoms::form)) { if (element && element->IsHTMLElement(nsGkAtoms::form)) {
mForm = static_cast<HTMLFormElement*>(element); SetForm(static_cast<HTMLFormElement*>(element), aBindToTree);
} }
} }
} else { } else {
@ -2305,7 +2316,7 @@ nsGenericHTMLFormElement::UpdateFormOwner(bool aBindToTree,
// it to the right value. Also note that even if being bound here didn't // it to the right value. Also note that even if being bound here didn't
// change our parent, we still need to search, since our parent chain // change our parent, we still need to search, since our parent chain
// probably changed _somewhere_. // probably changed _somewhere_.
mForm = FindAncestorForm(); SetForm(FindAncestorForm(), aBindToTree);
} }
} }

View File

@ -1226,7 +1226,7 @@ public:
return mForm; return mForm;
} }
virtual void SetForm(nsIDOMHTMLFormElement* aForm) override; virtual void SetForm(nsIDOMHTMLFormElement* aForm) override;
virtual void ClearForm(bool aRemoveFromForm) override; virtual void ClearForm(bool aRemoveFromForm, bool aUnbindOrDelete) override;
nsresult GetForm(nsIDOMHTMLFormElement** aForm); nsresult GetForm(nsIDOMHTMLFormElement** aForm);
@ -1303,6 +1303,12 @@ protected:
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify) override; const nsAttrValue* aValue, bool aNotify) override;
virtual void BeforeSetForm(bool aBindToTree) {}
virtual void AfterClearForm(bool aUnbindOrDelete) {}
void SetForm(mozilla::dom::HTMLFormElement* aForm, bool aBindToTree);
/** /**
* This method will update the form owner, using @form or looking to a parent. * This method will update the form owner, using @form or looking to a parent.
* *

View File

@ -127,8 +127,10 @@ public:
* *
* @param aRemoveFromForm set false if you do not want this element removed * @param aRemoveFromForm set false if you do not want this element removed
* from the form. (Used by nsFormControlList::Clear()) * from the form. (Used by nsFormControlList::Clear())
* @param aUnbindOrDelete set true if the element is being deleted or unbound
* from tree.
*/ */
virtual void ClearForm(bool aRemoveFromForm) = 0; virtual void ClearForm(bool aRemoveFromForm, bool aUnbindOrDelete) = 0;
/** /**
* Get the type of this control as an int (see NS_FORM_* above) * Get the type of this control as an int (see NS_FORM_* above)