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
+
+