Bug 448564. Better handling of sink context switches. r=mrbkap, sr=bzbarsky

This commit is contained in:
Ben Newman 2008-10-12 13:44:23 -04:00
Родитель 8d271ae64d
Коммит 986e6eec49
26 изменённых файлов: 362 добавлений и 58 удалений

Просмотреть файл

@ -0,0 +1,7 @@
<s>
<form>a</form>
<iframe></iframe>
<script src=a></script>
<form></form>
<table>
<optgroup>

Просмотреть файл

@ -1,3 +1,4 @@
load 388183-1.html
load 395340-1.html
load 407053.html
load 448564.html

Просмотреть файл

@ -0,0 +1,13 @@
<html>
<head>
<link rel="stylesheet" type="text/css"
href="bug448564_forms.css">
</link>
</head>
<body>
<i><b>
<form>a</form>
<form>b</form>
</b></i>
</body>
</html>

Просмотреть файл

@ -0,0 +1,19 @@
<html>
<head>
<link rel="stylesheet" type="text/css"
href="bug448564_forms.css">
</link>
</head>
<body>
<i><b>
<form>a</form> <!-- These forms should not end up nested! -->
<form>b</form>
<!-- Why does it matter whether we explicitly close this tag? -->
<!-- It matters because nsHTMLTokenizer::ScanDocStructure checks
whether there are any malformed tags before parsing begins,
and, if there are any, residual style tags (<i>, <b>, &c.)
must be pushed inside block elements (e.g., <form>). -->
<div><!-- </div> -->
</b></i>
</body>
</html>

Просмотреть файл

@ -0,0 +1,11 @@
<html>
<head>
<link rel="stylesheet" type="text/css"
href="bug448564_forms.css">
</link>
</head>
<body>
<form><i><b>a</b></i></form>
<form><i><b>b</b></i></form>
</body>
</html>

Просмотреть файл

@ -0,0 +1,11 @@
<html>
<body>
<s>
<form>a</form>
<iframe></iframe>
<script src=a></script>
<form></form>
<table>
<optgroup>
</body>
</html>

Просмотреть файл

@ -0,0 +1,14 @@
<html>
<body>
<s>
<form>a</form>
<iframe></iframe>
</s>
<form></form>
<form>
<select>
<optgroup></optgroup>
</select>
</form>
</body>
</html>

Просмотреть файл

@ -0,0 +1,4 @@
<table>
<th>head</th>
<optgroup></optgroup>
</table>

Просмотреть файл

@ -0,0 +1,8 @@
<form>
<select>
<optgroup></optgroup>
</select>
</form>
<table>
<th>head</th>
</table>

Просмотреть файл

@ -0,0 +1,10 @@
<html>
<body>
<b><i>
<!-- Closing a form causes any open residual style tags to be closed
as well. This test ensures that these tags get reopened. -->
<form>form contents</form>
bold text
</i></b>
</body>
</html>

Просмотреть файл

@ -0,0 +1,6 @@
<html>
<body>
<form><b><i>form contents</i></b></form>
<b><i>bold text</i></b>
</body>
</html>

Просмотреть файл

@ -0,0 +1,16 @@
<html>
<head>
<link rel="stylesheet" type="text/css"
href="bug448564_forms.css">
</link>
</head>
<body>
<form>
<table>
<optgroup></optgroup>
</table>
<input type="button" value="button"></input>
</form>
<b>asdf</b>
</body>
</html>

Просмотреть файл

@ -0,0 +1,17 @@
<html>
<head>
<link rel="stylesheet" type="text/css"
href="bug448564_forms.css">
</link>
</head>
<body>
<form>
<select>
<optgroup></optgroup>
</select>
<table></table>
<input type="button" value="button"></input>
</form>
<b>asdf</b>
</body>
</html>

Просмотреть файл

@ -0,0 +1,2 @@
/* make nesting obvious */
form { border: 1px solid black; }

Просмотреть файл

@ -0,0 +1,10 @@
== bug448564-1_malformed.html bug448564-1_well-formed.html
== bug448564-1_malformed.html bug448564-1_ideal.html
== bug448564-2_malformed.html bug448564-2_well-formed.html
== bug448564-3_malformed.html bug448564-3_well-formed.html
== bug448564-4a.html bug448564-4b.html
== bug448564-5_malformed.html bug448564-5_well-formed.html

Просмотреть файл

@ -373,6 +373,8 @@ public:
nsGenericHTMLElement* mContent;
PRUint32 mNumFlushed;
PRInt32 mInsertionPoint;
nsIContent *Add(nsIContent *child);
};
Node* mStack;
@ -728,8 +730,10 @@ SinkContext::DidAddContent(nsIContent* aContent)
}
#endif
mSink->NotifyInsert(parent, aContent,
mStack[mStackPos - 1].mInsertionPoint - 1);
PRInt32 childIndex = mStack[mStackPos - 1].mInsertionPoint - 1;
NS_ASSERTION(parent->GetChildAt(childIndex) == aContent,
"Flushing the wrong child.");
mSink->NotifyInsert(parent, aContent, childIndex);
mStack[mStackPos - 1].mNumFlushed = parent->GetChildCount();
} else if (mSink->IsTimeToNotify()) {
SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
@ -833,15 +837,7 @@ SinkContext::OpenContainer(const nsIParserNode& aNode)
rv = mSink->AddAttributes(aNode, content);
MaybeSetForm(content, nodeType, mSink);
nsGenericHTMLElement* parent = mStack[mStackPos - 2].mContent;
if (mStack[mStackPos - 2].mInsertionPoint != -1) {
parent->InsertChildAt(content,
mStack[mStackPos - 2].mInsertionPoint++,
PR_FALSE);
} else {
parent->AppendChildTo(content, PR_FALSE);
}
mStack[mStackPos - 2].Add(content);
NS_ENSURE_SUCCESS(rv, rv);
@ -900,6 +896,19 @@ SinkContext::HaveNotifiedForCurrentContent() const
return PR_TRUE;
}
nsIContent *
SinkContext::Node::Add(nsIContent *child)
{
if (mInsertionPoint != -1) {
NS_ASSERTION(mNumFlushed == mContent->GetChildCount(),
"Inserting multiple children without flushing.");
mContent->InsertChildAt(child, mInsertionPoint++, PR_FALSE);
} else {
mContent->AppendChildTo(child, PR_FALSE);
}
return child;
}
nsresult
SinkContext::CloseContainer(const nsHTMLTag aTag, PRBool aMalformed)
{
@ -1151,19 +1160,8 @@ SinkContext::AddLeaf(nsGenericHTMLElement* aContent)
if (mStackPos <= 0) {
return NS_ERROR_FAILURE;
}
nsGenericHTMLElement* parent = mStack[mStackPos - 1].mContent;
// If the parent has an insertion point, insert rather than append.
if (mStack[mStackPos - 1].mInsertionPoint != -1) {
parent->InsertChildAt(aContent,
mStack[mStackPos - 1].mInsertionPoint++,
PR_FALSE);
} else {
parent->AppendChildTo(aContent, PR_FALSE);
}
DidAddContent(aContent);
DidAddContent(mStack[mStackPos - 1].Add(aContent));
#ifdef DEBUG
if (SINK_LOG_TEST(gSinkLogModuleInfo, SINK_ALWAYS_REFLOW)) {
@ -1199,26 +1197,17 @@ SinkContext::AddComment(const nsIParserNode& aNode)
return NS_ERROR_FAILURE;
}
nsGenericHTMLElement* parent;
if (!mSink->mBody && !mSink->mFrameset && mSink->mHead) {
// XXXbz but this will make DidAddContent use the wrong parent for
// the notification! That seems so bogus it's not even funny.
parent = mSink->mHead;
} else {
parent = mStack[mStackPos - 1].mContent;
{
Node &parentNode = mStack[mStackPos - 1];
nsGenericHTMLElement *parent = parentNode.mContent;
if (!mSink->mBody && !mSink->mFrameset && mSink->mHead)
// XXXbz but this will make DidAddContent use the wrong parent for
// the notification! That seems so bogus it's not even funny.
parentNode.mContent = mSink->mHead;
DidAddContent(parentNode.Add(comment));
parentNode.mContent = parent;
}
// If the parent has an insertion point, insert rather than append.
if (mStack[mStackPos - 1].mInsertionPoint != -1) {
parent->InsertChildAt(comment,
mStack[mStackPos - 1].mInsertionPoint++,
PR_FALSE);
} else {
parent->AppendChildTo(comment, PR_FALSE);
}
DidAddContent(comment);
#ifdef DEBUG
if (SINK_LOG_TEST(gSinkLogModuleInfo, SINK_ALWAYS_REFLOW)) {
mSink->ForceReflow();
@ -1383,10 +1372,11 @@ SinkContext::FlushTags()
#endif
if ((mStack[stackPos].mInsertionPoint != -1) &&
(mStackPos > (stackPos + 1))) {
PRInt32 childIndex = mStack[stackPos].mInsertionPoint - 1;
nsIContent* child = mStack[stackPos + 1].mContent;
mSink->NotifyInsert(content,
child,
mStack[stackPos].mInsertionPoint - 1);
NS_ASSERTION(content->GetChildAt(childIndex) == child,
"Flushing the wrong child.");
mSink->NotifyInsert(content, child, childIndex);
} else {
mSink->NotifyAppend(content, mStack[stackPos].mNumFlushed);
}
@ -1487,18 +1477,9 @@ SinkContext::FlushText(PRBool* aDidFlush, PRBool aReleaseLast)
return NS_ERROR_FAILURE;
}
nsGenericHTMLElement* parent = mStack[mStackPos - 1].mContent;
if (mStack[mStackPos - 1].mInsertionPoint != -1) {
parent->InsertChildAt(mLastTextNode,
mStack[mStackPos - 1].mInsertionPoint++,
PR_FALSE);
} else {
parent->AppendChildTo(mLastTextNode, PR_FALSE);
}
DidAddContent(mStack[mStackPos - 1].Add(mLastTextNode));
didFlush = PR_TRUE;
DidAddContent(mLastTextNode);
}
}
@ -1936,12 +1917,24 @@ HTMLContentSink::EndContext(PRInt32 aPosition)
PRInt32 n = mContextStack.Count() - 1;
SinkContext* sc = (SinkContext*) mContextStack.ElementAt(n);
NS_ASSERTION(sc->mStack[aPosition].mType == mCurrentContext->mStack[0].mType,
const SinkContext::Node &bottom = mCurrentContext->mStack[0];
NS_ASSERTION(sc->mStack[aPosition].mType == bottom.mType,
"ending a wrong context");
mCurrentContext->FlushTextAndRelease();
NS_ASSERTION(bottom.mContent->GetChildCount() == bottom.mNumFlushed,
"Node at base of context stack not fully flushed.");
sc->mStack[aPosition].mNumFlushed = mCurrentContext->mStack[0].mNumFlushed;
// Flushing tags before the assertion on the previous line would
// undoubtedly prevent the assertion from failing, but it shouldn't
// be failing anyway, FlushTags or no. Flushing here is nevertheless
// a worthwhile precaution, since we lose some information (e.g.,
// mInsertionPoints) when we end the current context.
mCurrentContext->FlushTags();
sc->mStack[aPosition].mNumFlushed = bottom.mNumFlushed;
for (PRInt32 i = 0; i<mCurrentContext->mStackPos; i++) {
NS_IF_RELEASE(mCurrentContext->mStack[i].mContent);

Просмотреть файл

@ -73,6 +73,12 @@ _TEST_FILES = test_bug1682.html \
test_form-parsing.html \
test_viewport.html \
test_documentAll.html \
test_bug448564.html \
bug448564-iframe-1.html \
bug448564-iframe-2.html \
bug448564-iframe-3.html \
bug448564-echo.sjs \
bug448564-submit.js \
$(NULL)
libs:: $(_TEST_FILES)

Просмотреть файл

@ -0,0 +1,8 @@
function handleRequest(request, response) {
response.setHeader("Cache-Control", "no-cache", false);
response.setStatusLine(request.httpVersion, 200, "OK");
response.write("<script>");
response.write(" parent.checkQueryString('" + request.queryString + "');");
response.write("</script>");
}

Просмотреть файл

@ -0,0 +1,16 @@
<html>
<body>
<table>
<form action="bug448564-echo.sjs" method="GET">
<tr><td><input name="a" value="aval"></td></tr>
<input type="hidden" name="b" value="bval">
<input name="c" value="cval">
<tr><td><input name="d" value="dval" type="submit"></td></tr>
</form>
</table>
<script src="bug448564-submit.js"></script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,16 @@
<html>
<body>
<form action="bug448564-echo.sjs" method="GET">
<table>
<tr><td><input name="a" value="aval"></td></tr>
<input type="hidden" name="b" value="bval">
<input name="c" value="cval">
<tr><td><input name="d" value="dval" type="submit"></td></tr>
</table>
</form>
<script src="bug448564-submit.js"></script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,16 @@
<html>
<body>
<table>
<span><form action="bug448564-echo.sjs" method="GET">
<tr><td><input name="a" value="aval"></td></tr>
<input type="hidden" name="b" value="bval">
<input name="c" value="cval">
<tr><td><input name="d" value="dval" type="submit"></td></tr>
</form></span>
</table>
<script src="bug448564-submit.js"></script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,4 @@
var inputs = document.getElementsByTagName("input");
for (var input, i = 0; input = inputs[i]; ++i)
if ("submit" == input.type)
input.click();

Просмотреть файл

@ -0,0 +1,63 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=448564
-->
<head>
<title>Test for Bug 448564</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" />
<script type="text/javascript;version=1.8">
var toCheck = [];
function flush() {
if (!check)
return;
for each (let qs in toCheck)
check(qs);
toCheck.length = 0;
}
function checkQueryString(queryString) {
toCheck.push(queryString);
flush();
}
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=448564">Mozilla Bug 448564</a>
<p id="display">
<iframe src="bug448564-iframe-1.html"></iframe>
<iframe src="bug448564-iframe-2.html"></iframe>
<iframe src="bug448564-iframe-3.html"></iframe>
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 448564 **/
var checksToDo = document.getElementsByTagName("iframe").length;
function check(queryString) {
is(queryString.split("&").sort().join("&"),
"a=aval&b=bval&c=cval&d=dval",
"Not all form fields were properly submitted.");
if (--checksToDo == 0)
SimpleTest.finish();
}
flush();
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -125,3 +125,6 @@ include ../xul/base/src/grid/reftests/reftest.list
# z-index/
include z-index/reftest.list
# reftest(s) to verify content bugfixes
include ../../content/html/document/reftests/reftests.list

Просмотреть файл

@ -1806,8 +1806,9 @@ CNavDTD::HandleSavedTokens(PRInt32 anIndex)
PRInt32 attrCount;
PRInt32 theTopIndex = anIndex + 1;
PRInt32 theTagCount = mBodyContext->GetCount();
PRBool formWasOnStack = mSink->IsFormOnStack();
if (mSink->IsFormOnStack()) {
if (formWasOnStack) {
// Do this to synchronize dtd stack and the sink stack.
// Note: FORM is never on the dtd stack because its always
// considered as a leaf. However, in the sink FORM can either
@ -1875,9 +1876,19 @@ CNavDTD::HandleSavedTokens(PRInt32 anIndex)
result = HandleToken(theToken, mParser);
}
}
if (theTopIndex != mBodyContext->GetCount()) {
// CloseContainersTo does not close any forms we might have opened while
// handling saved tokens, because the parser does not track forms on its
// mBodyContext stack.
CloseContainersTo(theTopIndex, mBodyContext->TagAt(theTopIndex),
PR_TRUE);
}
if (!formWasOnStack && mSink->IsFormOnStack()) {
// If a form has appeared on the sink context stack since the beginning of
// HandleSavedTokens, have the sink close it:
mSink->CloseContainer(eHTMLTag_form);
}
// Bad-contents were successfully processed. Now, itz time to get
@ -2707,6 +2718,19 @@ CNavDTD::OpenContainer(const nsCParserNode *aNode,
return result;
}
nsresult
CNavDTD::CloseResidualStyleTags(const eHTMLTags aTag,
PRBool aClosedByStartTag)
{
const PRInt32 count = mBodyContext->GetCount();
PRInt32 pos = count;
while (nsHTMLElement::IsResidualStyleTag(mBodyContext->TagAt(pos - 1)))
--pos;
if (pos < count)
return CloseContainersTo(pos, aTag, aClosedByStartTag);
return NS_OK;
}
/**
* This method does two things: 1st, help construct
* our own internal model of the content-stack; and
@ -2749,6 +2773,10 @@ CNavDTD::CloseContainer(const eHTMLTags aTag, PRBool aMalformed)
if (mFlags & NS_DTD_FLAG_HAS_OPEN_FORM) {
mFlags &= ~NS_DTD_FLAG_HAS_OPEN_FORM;
done = PR_FALSE;
// If we neglect to close these tags, the sink will refuse to close the
// form because the form will not be on the top of the SinkContext stack.
// See HTMLContentSink::CloseForm. (XXX do this in other cases?)
CloseResidualStyleTags(eHTMLTag_form, PR_FALSE);
}
break;

Просмотреть файл

@ -310,6 +310,8 @@ private:
nsresult CloseContainersTo(eHTMLTags aTag, PRBool aClosedByStartTag);
nsresult CloseContainersTo(PRInt32 anIndex, eHTMLTags aTag,
PRBool aClosedByStartTag);
nsresult CloseResidualStyleTags(const eHTMLTags aTag,
PRBool aClosedByStartTag);
/**
* Causes leaf to be added to sink at current vector pos.