Fix bug 390975: when a form that is not an ancestor of a form control is
removed, clear the control's mForm pointer if it points to the now-removed form. r+sr=jst, a=damons git-svn-id: svn://10.0.0.236/trunk@238905 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -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<nsIDOMHTMLFormElement> form = FindForm(mForm);
|
||||
if (!form) {
|
||||
SetForm(nsnull, PR_TRUE, PR_TRUE);
|
||||
} else {
|
||||
UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
|
||||
@@ -711,13 +711,91 @@ nsHTMLFormElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void
|
||||
MarkOrphans(const nsTArray<nsIFormControl*> aArray)
|
||||
{
|
||||
PRUint32 length = aArray.Length();
|
||||
for (PRUint32 i = 0; i < length; ++i) {
|
||||
nsCOMPtr<nsINode> 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<nsIFormControl*> 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<nsINode> 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<nsIDOMHTMLFormElement> form;
|
||||
control->GetForm(getter_AddRefs(form));
|
||||
NS_ASSERTION(form == aThisForm, "How did that happen?");
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHTMLFormElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
|
||||
{
|
||||
nsCOMPtr<nsIHTMLDocument> 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();
|
||||
}
|
||||
|
||||
@@ -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 \
|
||||
|
||||
62
mozilla/content/html/content/test/test_bug390975.html
Normal file
62
mozilla/content/html/content/test/test_bug390975.html
Normal file
@@ -0,0 +1,62 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=390975
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 390975</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=390975">Mozilla Bug 390975</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<table id="table1">
|
||||
<form id="form1">
|
||||
<input>
|
||||
<input>
|
||||
<tr><td>
|
||||
<input>
|
||||
<input>
|
||||
<input>
|
||||
</td></tr>
|
||||
</form>
|
||||
</table>
|
||||
|
||||
<table id="table2">
|
||||
<form id="form2">
|
||||
<input>
|
||||
<input>
|
||||
<tr id="row2"><td>
|
||||
<input>
|
||||
<input>
|
||||
<input>
|
||||
</td></tr>
|
||||
</form>
|
||||
</table>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug 390975 **/
|
||||
var form = $("form1");
|
||||
is(form.elements.length, 5, "Unexpected elements length");
|
||||
|
||||
$("table1").parentNode.removeChild($("table1"));
|
||||
is(form.elements.length, 3, "Should have lost control outside table");
|
||||
|
||||
form.parentNode.removeChild(form);
|
||||
is(form.elements.length, 0, "Should have lost control outside form");
|
||||
|
||||
form = $("form2");
|
||||
is(form.elements.length, 5, "Unexpected elements length");
|
||||
|
||||
$("row2").parentNode.removeChild($("row2"));
|
||||
is(form.elements.length, 2, "Should have lost controls inside table row");
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user