Bug 263359 part 4: resolve paragraph on encountering line breaks in preformatted elements. r=roc

This commit is contained in:
Simon Montagu 2011-04-11 11:00:28 +03:00
Родитель ef694e6aea
Коммит 7f6b363f1e
5 изменённых файлов: 240 добавлений и 44 удалений

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

@ -0,0 +1,14 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
function boom()
{
document.body.offsetHeight;
document.body.appendChild(document.createTextNode('Y'));
}
</script>
</head>
<body style="white-space: pre;" onload="boom();">&#x000D;&#x064C;</body>
</html>

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

@ -329,5 +329,6 @@ load 629908-1.html
load 635329.html
== 640272.html 640272-ref.html
load 645193.html
load 650475.xhtml
load 650489.xhtml
load 653133-1.html

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

@ -55,6 +55,7 @@
#include "nsContainerFrame.h"
#include "nsFirstLetterFrame.h"
#include "gfxUnicodeProperties.h"
#include "nsTextFrame.h"
#undef NOISY_BIDI
#undef REALLY_NOISY_BIDI
@ -164,22 +165,31 @@ SplitInlineAncestors(nsIFrame* aFrame)
return NS_OK;
}
// Convert bidi continuations to fluid continuations for a frame and all of its
// inline ancestors.
static void
MakeContinuationFluid(nsIFrame* aFrame, nsIFrame* aNext)
{
NS_ASSERTION (!aFrame->GetNextInFlow() || aFrame->GetNextInFlow() == aNext,
"next-in-flow is not next continuation!");
aFrame->SetNextInFlow(aNext);
NS_ASSERTION (!aNext->GetPrevInFlow() || aNext->GetPrevInFlow() == aFrame,
"prev-in-flow is not prev continuation!");
aNext->SetPrevInFlow(aFrame);
}
// If aFrame is the last child of its parent, convert bidi continuations to
// fluid continuations for all of its inline ancestors.
static void
JoinInlineAncestors(nsIFrame* aFrame)
{
nsIFrame* frame = aFrame;
if (aFrame->GetNextSibling()) {
return;
}
nsIFrame* frame = aFrame->GetParent();
while (frame && IsBidiSplittable(frame)) {
nsIFrame* next = frame->GetNextContinuation();
if (next) {
NS_ASSERTION (!frame->GetNextInFlow() || frame->GetNextInFlow() == next,
"next-in-flow is not next continuation!");
frame->SetNextInFlow(next);
NS_ASSERTION (!next->GetPrevInFlow() || next->GetPrevInFlow() == frame,
"prev-in-flow is not prev continuation!");
next->SetPrevInFlow(frame);
MakeContinuationFluid(frame, next);
}
// Join the parent only as long as we're its last child.
if (frame->GetNextSibling())
@ -189,8 +199,9 @@ JoinInlineAncestors(nsIFrame* aFrame)
}
static nsresult
CreateBidiContinuation(nsIFrame* aFrame,
nsIFrame** aNewFrame)
CreateContinuation(nsIFrame* aFrame,
nsIFrame** aNewFrame,
PRBool aIsFluid)
{
NS_PRECONDITION(aNewFrame, "null OUT ptr");
NS_PRECONDITION(aFrame, "null ptr");
@ -199,10 +210,10 @@ CreateBidiContinuation(nsIFrame* aFrame,
nsPresContext *presContext = aFrame->PresContext();
nsIPresShell *presShell = presContext->PresShell();
NS_ASSERTION(presShell, "PresShell must be set on PresContext before calling nsBidiPresUtils::CreateBidiContinuation");
NS_ASSERTION(presShell, "PresShell must be set on PresContext before calling nsBidiPresUtils::CreateContinuation");
nsIFrame* parent = aFrame->GetParent();
NS_ASSERTION(parent, "Couldn't get frame parent in nsBidiPresUtils::CreateBidiContinuation");
NS_ASSERTION(parent, "Couldn't get frame parent in nsBidiPresUtils::CreateContinuation");
nsresult rv = NS_OK;
@ -213,12 +224,12 @@ CreateBidiContinuation(nsIFrame* aFrame,
parent->GetStyleDisplay()->IsFloating()) {
nsFirstLetterFrame* letterFrame = do_QueryFrame(parent);
rv = letterFrame->CreateContinuationForFloatingParent(presContext, aFrame,
aNewFrame, PR_FALSE);
aNewFrame, aIsFluid);
return rv;
}
rv = presShell->FrameConstructor()->
CreateContinuingFrame(presContext, aFrame, parent, aNewFrame, PR_FALSE);
CreateContinuingFrame(presContext, aFrame, parent, aNewFrame, aIsFluid);
if (NS_FAILED(rv)) {
return rv;
}
@ -230,11 +241,13 @@ CreateBidiContinuation(nsIFrame* aFrame,
if (NS_FAILED(rv)) {
return rv;
}
// Split inline ancestor frames
rv = SplitInlineAncestors(aFrame);
if (NS_FAILED(rv)) {
return rv;
if (!aIsFluid) {
// Split inline ancestor frames
rv = SplitInlineAncestors(aFrame);
if (NS_FAILED(rv)) {
return rv;
}
}
return NS_OK;
@ -285,7 +298,7 @@ AdvanceLineIteratorToFrame(nsIFrame* aFrame,
*
* Walk through the descendants of aBlockFrame and build:
* * mLogicalFrames: an nsTArray of nsIFrame* pointers in logical order
* * mBuffer: an nsAutoString containing a representation of
* * mBuffer: an nsString containing a representation of
* the content of the frames.
* In the case of text frames, this is the actual text context of the
* frames, but some other elements are represented in a symbolic form which
@ -383,6 +396,7 @@ nsBidiPresUtils::Resolve(nsBlockFrame* aBlockFrame)
mLogicalFrames.AppendElement(NS_BIDI_CONTROL_FRAME);
mLinePerFrame.AppendElement((nsLineBox*)nsnull);
mBuffer.Append(kPDF);
NS_ASSERTION(mEmbeddingStack.Length(), "embedding/override underflow");
mEmbeddingStack.TruncateLength(mEmbeddingStack.Length() - 1);
}
@ -400,7 +414,6 @@ nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame)
mSuccess = NS_OK;
return;
}
// XXX: TODO: Handle preformatted text ('\n')
mBuffer.ReplaceChar("\t\r\n", kSpace);
PRInt32 runCount;
@ -553,12 +566,17 @@ nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame)
*/
PRInt32 newIndex = frameIndex;
do {
} while (mLogicalFrames[++newIndex] == NS_BIDI_CONTROL_FRAME);
RemoveBidiContinuation(frame, frameIndex, newIndex, lineOffset);
} else if (runLength == fragmentLength) {
} while (++newIndex < frameCount &&
mLogicalFrames[newIndex] == NS_BIDI_CONTROL_FRAME);
if (newIndex < frameCount) {
RemoveBidiContinuation(frame, frameIndex, newIndex, lineOffset);
}
} else if (runLength == fragmentLength &&
numRun + 1 < runCount) {
/*
* The directional run ends at the end of the frame. Make sure that
* the next frame is a non-fluid continuation
* If the directional run ends at the end of the frame, and this is
* not the end of our paragraph, make sure that the next frame is a
* non-fluid continuation
*/
nsIFrame* next = frame->GetNextInFlow();
if (next) {
@ -607,12 +625,11 @@ nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame)
if (parent && IsBidiSplittable(parent))
SplitInlineAncestors(child);
}
else if (!frame->GetNextSibling()) {
// We're not at an end of a run, and |frame| is the last child of its parent.
// If its ancestors happen to have bidi continuations, convert them into
// fluid continuations.
nsIFrame* parent = frame->GetParent();
JoinInlineAncestors(parent);
else {
// We're not at an end of a run. If |frame| is the last child of its
// parent, and its ancestors happen to have bidi continuations, convert
// them into fluid continuations.
JoinInlineAncestors(frame);
}
}
} // for
@ -721,7 +738,117 @@ nsBidiPresUtils::TraverseFrames(nsBlockFrame* aBlockFrame,
if (nsGkAtoms::textFrame == frameType) {
if (content != mPrevContent) {
mPrevContent = content;
content->AppendTextTo(mBuffer);
if (!frame->GetStyleContext()->GetStyleText()->NewlineIsSignificant()) {
content->AppendTextTo(mBuffer);
} else {
/*
* For preformatted text we have to do bidi resolution on each line
* separately.
*/
nsAutoString text;
content->AppendTextTo(text);
nsIFrame* next;
do {
next = nsnull;
PRInt32 start, end;
frame->GetOffsets(start, end);
PRInt32 endLine = text.FindCharInSet(NS_LITERAL_STRING("\n\r"),
start);
if (endLine == -1) {
/*
* If there is no newline in the frame, just save the text and
* do bidi resolution later
*/
mBuffer.Append(Substring(text, start));
break;
}
/*
* If there is a newline in the frame, break the frame after the
* newline, do bidi resolution and repeat until the end of the
* element.
*/
++endLine;
/*
* If the frame ends before the new line, save the text and move
* into the next continuation
*/
while (end < endLine) {
mBuffer.Append(Substring(text, start, end - start));
frame = frame->GetNextContinuation();
NS_ASSERTION(frame, "Premature end of continuation chain");
frame->GetOffsets(start, end);
mLogicalFrames.AppendElement(frame);
AdvanceLineIteratorToFrame(frame, aLineIter, mPrevFrame);
mLinePerFrame.AppendElement(aLineIter->GetLine().get());
/*
* If we have already overshot the saved next-sibling while
* scanning the frame's continuations, advance it.
*/
if (frame == nextSibling) {
nextSibling = frame->GetNextSibling();
}
}
mBuffer.Append(Substring(text, start, endLine - start));
PRBool createdContinuation = PR_FALSE;
if (PRUint32(endLine) < text.Length()) {
/*
* Timing is everything here: if the frame already has a bidi
* continuation, we need to make the continuation fluid *before*
* resetting the length of the current frame. Otherwise
* nsTextFrame::SetLength won't set the continuation frame's
* text offsets correctly.
*
* On the other hand, if the frame doesn't have a continuation,
* we need to create one *after* resetting the length, or
* CreateContinuingFrame will complain that there is no more
* content for the continuation.
*/
next = frame->GetNextInFlow();
if (!next) {
// If the frame already has a bidi continuation, make it fluid
next = frame->GetNextContinuation();
if (next) {
MakeContinuationFluid(frame, next);
JoinInlineAncestors(frame);
}
}
nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
textFrame->SetLength(endLine - start, nsnull);
if (!next) {
// If the frame has no next in flow, create one.
CreateContinuation(frame, &next, PR_TRUE);
createdContinuation = PR_TRUE;
}
}
ResolveParagraphWithinBlock(aBlockFrame);
if (!nextSibling && !createdContinuation) {
break;
} else if (next) {
frame = next;
mLogicalFrames.AppendElement(frame);
AdvanceLineIteratorToFrame(frame, aLineIter, mPrevFrame);
mLinePerFrame.AppendElement(aLineIter->GetLine().get());
}
/*
* If we have already overshot the saved next-sibling while
* scanning the frame's continuations, advance it.
*/
if (frame && frame == nextSibling) {
nextSibling = frame->GetNextSibling();
}
} while (next);
}
}
} else if (nsGkAtoms::brFrame == frameType) {
// break frame -- append line separator
@ -751,6 +878,7 @@ nsBidiPresUtils::TraverseFrames(nsBlockFrame* aBlockFrame,
mLogicalFrames.AppendElement(NS_BIDI_CONTROL_FRAME);
mLinePerFrame.AppendElement((nsLineBox*)nsnull);
mBuffer.Append(kPDF);
NS_ASSERTION(mEmbeddingStack.Length(), "embedding/override underflow");
mEmbeddingStack.TruncateLength(mEmbeddingStack.Length() - 1);
}
childFrame = nextSibling;
@ -1237,7 +1365,7 @@ nsBidiPresUtils::EnsureBidiContinuation(nsIFrame* aFrame,
NS_PRECONDITION(aFrame, "aFrame is null");
aFrame->AdjustOffsetsForBidi(aStart, aEnd);
mSuccess = CreateBidiContinuation(aFrame, aNewFrame);
mSuccess = CreateContinuation(aFrame, aNewFrame, PR_FALSE);
}
void
@ -1269,14 +1397,7 @@ nsBidiPresUtils::RemoveBidiContinuation(nsIFrame* aFrame,
while (frame) {
nsIFrame* prev = frame->GetPrevContinuation();
if (prev) {
NS_ASSERTION (!frame->GetPrevInFlow() || frame->GetPrevInFlow() == prev,
"prev-in-flow is not prev continuation!");
frame->SetPrevInFlow(prev);
NS_ASSERTION (!prev->GetNextInFlow() || prev->GetNextInFlow() == frame,
"next-in-flow is not next continuation!");
prev->SetNextInFlow(frame);
MakeContinuationFluid(prev, frame);
frame = frame->GetParent();
} else {
break;

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

@ -332,6 +332,7 @@ _TEST_FILES += \
test_bug588174.html \
test_bug607529.html \
file_bug607529.html \
test_bug644768.html \
$(NULL)
endif

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

@ -0,0 +1,59 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=644768
-->
<head>
<title>Test for Bug 644768</title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="test()">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=644768">Mozilla Bug 644768</a>
<p id="display"></p>
<div id="content">
<!-- test text is
== زادروزها ==
* [[۱۳۰۷]]
-->
<textarea id="testInput" dir="rtl" cols="80" rows="25">
== &#x0632;&#x0627;&#x062F;&#x0631;&#x0648;&#x0632;&#x0647;&#x0627; ==
* [[&#x06F1;&#x06F3;&#x06F0;&#x06F7;]]</textarea>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 644768 **/
SimpleTest.waitForExplicitFinish();
function test() {
var textInput = $("testInput");
var s1, s2, equal, str1, str2;
textInput.focus();
s1 = snapshotWindow(window);
synthesizeKey("VK_UP", { });
synthesizeKey("VK_UP", { });
synthesizeKey("VK_UP", { });
synthesizeKey("VK_DELETE", { });
synthesizeKey("VK_ENTER", { });
s2 = snapshotWindow(window);
[equal, str1, str2] = compareSnapshots(s1, s2, true);
ok(equal, "newline before bidi text shouldn't change direction: expected " +
str1 + " but got " + str2);
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>