зеркало из https://github.com/mozilla/pjs.git
Bug 448564. Better handling of sink context switches. r=mrbkap, sr=bzbarsky
This commit is contained in:
Родитель
8d271ae64d
Коммит
986e6eec49
|
@ -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.
|
||||
|
|
Загрузка…
Ссылка в новой задаче