diff --git a/mozilla/content/html/content/src/nsGenericHTMLElement.cpp b/mozilla/content/html/content/src/nsGenericHTMLElement.cpp index ad13dbab1e7..63e89690a16 100644 --- a/mozilla/content/html/content/src/nsGenericHTMLElement.cpp +++ b/mozilla/content/html/content/src/nsGenericHTMLElement.cpp @@ -111,12 +111,6 @@ #include "nsLayoutUtils.h" #include "nsContentCreatorFunctions.h" -// If this flag is set on an nsGenericHTMLFormElement, that means that we have -// added ourselves to our mForm. It's possible to have a non-null mForm, but -// not have this flag set. That happens when the form is set via the content -// sink. -#define ADDED_TO_FORM (1 << NODE_TYPE_SPECIFIC_BITS_OFFSET) - class nsINodeInfo; class nsIDOMNodeList; class nsRuleWalker; @@ -2712,6 +2706,8 @@ nsGenericHTMLFormElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent) nsCOMPtr form = FindForm(mForm); if (!form) { SetForm(nsnull, PR_TRUE, PR_TRUE); + } else { + UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT); } } } diff --git a/mozilla/content/html/content/src/nsGenericHTMLElement.h b/mozilla/content/html/content/src/nsGenericHTMLElement.h index 97d0657d4bd..01dc70b9391 100644 --- a/mozilla/content/html/content/src/nsGenericHTMLElement.h +++ b/mozilla/content/html/content/src/nsGenericHTMLElement.h @@ -877,6 +877,21 @@ protected: nsIForm* mForm; }; +// If this flag is set on an nsGenericHTMLFormElement, that means that we have +// added ourselves to our mForm. It's possible to have a non-null mForm, but +// not have this flag set. That happens when the form is set via the content +// sink. +#define ADDED_TO_FORM (1 << NODE_TYPE_SPECIFIC_BITS_OFFSET) + +// If this flag is set on an nsGenericHTMLFormElement, that means that its form +// is in the process of being unbound from the tree, and this form element +// hasn't re-found its form in nsGenericHTMLFormElement::UnbindFromTree yet. +#define MAYBE_ORPHAN_FORM_ELEMENT (1 << (NODE_TYPE_SPECIFIC_BITS_OFFSET+1)) + +// NOTE: I don't think it's possible to have the above two flags set at the +// same time, so if it becomes an issue we can probably merge them into the +// same bit. --bz + //---------------------------------------------------------------------- /** diff --git a/mozilla/content/html/content/src/nsHTMLFormElement.cpp b/mozilla/content/html/content/src/nsHTMLFormElement.cpp index abd825de9c6..a109a77fc93 100644 --- a/mozilla/content/html/content/src/nsHTMLFormElement.cpp +++ b/mozilla/content/html/content/src/nsHTMLFormElement.cpp @@ -711,13 +711,91 @@ nsHTMLFormElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, return rv; } +static void +MarkOrphans(const nsTArray aArray) +{ + PRUint32 length = aArray.Length(); + for (PRUint32 i = 0; i < length; ++i) { + nsCOMPtr node = do_QueryInterface(aArray[i]); + NS_ASSERTION(node, "Form control must be nsINode"); + node->SetFlags(MAYBE_ORPHAN_FORM_ELEMENT); + } +} + +static void +CollectOrphans(nsINode* aRemovalRoot, nsTArray aArray +#ifdef DEBUG + , nsIDOMHTMLFormElement* aThisForm +#endif + ) +{ + // Walk backwards so that if we remove elements we can just keep iterating + PRUint32 length = aArray.Length(); + for (PRUint32 i = length; i > 0; --i) { + nsIFormControl* control = aArray[i-1]; + nsCOMPtr node = do_QueryInterface(control); + NS_ASSERTION(node, "Form control must be nsINode"); + + // Now if MAYBE_ORPHAN_FORM_ELEMENT is not set, that would mean that the + // node is in fact a descendant of the form and hence should stay in the + // form. If it _is_ set, then we need to check whether the node is a + // descendant of aRemovalRoot. If it is, we leave it in the form. See + // also the code in nsGenericHTMLFormElement::FindForm. +#ifdef DEBUG + PRBool removed = PR_FALSE; +#endif + if (node->HasFlag(MAYBE_ORPHAN_FORM_ELEMENT)) { + node->UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT); + if (!nsContentUtils::ContentIsDescendantOf(node, aRemovalRoot)) { + control->SetForm(nsnull, PR_TRUE, PR_TRUE); +#ifdef DEBUG + removed = PR_TRUE; +#endif + } + } + +#ifdef DEBUG + if (!removed) { + nsCOMPtr form; + control->GetForm(getter_AddRefs(form)); + NS_ASSERTION(form == aThisForm, "How did that happen?"); + } +#endif /* DEBUG */ + } +} + void nsHTMLFormElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent) { nsCOMPtr oldDocument = do_QueryInterface(GetCurrentDoc()); + // Mark all of our controls as maybe being orphans + MarkOrphans(mControls->mElements); + MarkOrphans(mControls->mNotInElements); + nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); + nsINode* ancestor = this; + nsINode* cur; + do { + cur = ancestor->GetNodeParent(); + if (!cur) { + break; + } + ancestor = cur; + } while (1); + + CollectOrphans(ancestor, mControls->mElements +#ifdef DEBUG + , this +#endif + ); + CollectOrphans(ancestor, mControls->mNotInElements +#ifdef DEBUG + , this +#endif + ); + if (oldDocument) { oldDocument->RemovedForm(); } diff --git a/mozilla/content/html/content/test/Makefile.in b/mozilla/content/html/content/test/Makefile.in index 16740b80c3f..bc0f8bd7b37 100644 --- a/mozilla/content/html/content/test/Makefile.in +++ b/mozilla/content/html/content/test/Makefile.in @@ -96,6 +96,7 @@ _TEST_FILES = test_bug589.html \ test_bug386996.html \ test_bug388746.html \ test_bug389797.html \ + test_bug390975.html \ test_bug391994.html \ test_bug392567.html \ test_bug394700.html \ diff --git a/mozilla/content/html/content/test/test_bug390975.html b/mozilla/content/html/content/test/test_bug390975.html new file mode 100644 index 00000000000..3a8c649c0d1 --- /dev/null +++ b/mozilla/content/html/content/test/test_bug390975.html @@ -0,0 +1,62 @@ + + + + + Test for Bug 390975 + + + + + +Mozilla Bug 390975 +

+ +
+
+
+ + +