(Patch #1 to fix Bug 431260) Patch for Bug 431341 - Include floating first-letter text when we build textruns for a paragraph, because we want nsLineBreaker to see the text for capitalization analysis. Make sure that textrun construction for floating first-letter text uses the block as its scope. And make sure we reconstruct textruns after determining the first-letter length, so that ligatures are broken as necessary. Patch by Robert O'Callahan <robert@ocallahan.org> r=smontagu

git-svn-id: svn://10.0.0.236/trunk@256331 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
dholbert%cs.stanford.edu
2009-02-26 20:12:49 +00:00
parent 906d65ea56
commit 2fa4de5805
7 changed files with 165 additions and 61 deletions

View File

@@ -319,7 +319,7 @@ public:
/**
* This can't be null. It usually returns a block frame but may return
* some other kind of frame when inline frames are reflowed in a non-block
* context (e.g. MathML).
* context (e.g. MathML or floating first-letter).
*/
nsIFrame* GetLineContainerFrame() const { return mBlockReflowState->frame; }
const nsLineList::iterator* GetLine() const {

View File

@@ -569,7 +569,8 @@ public:
mContext(aContext),
mLineContainer(aLineContainer),
mBidiEnabled(aPresContext->BidiEnabled()),
mTrimNextRunLeadingWhitespace(PR_FALSE), mSkipIncompleteTextRuns(PR_FALSE) {
mTrimNextRunLeadingWhitespace(PR_FALSE),
mSkipIncompleteTextRuns(PR_FALSE) {
ResetRunInfo();
}
@@ -717,7 +718,7 @@ private:
static nsIFrame*
FindLineContainer(nsIFrame* aFrame)
{
while (aFrame && aFrame->IsFrameOfType(nsIFrame::eLineParticipant)) {
while (aFrame && aFrame->CanContinueTextRun()) {
aFrame = aFrame->GetParent();
}
return aFrame;
@@ -745,20 +746,59 @@ TextContainsLineBreakerWhiteSpace(const void* aText, PRUint32 aLength,
}
}
static PRBool
CanTextRunCrossFrameBoundary(nsIFrame* aFrame)
struct FrameTextTraversal {
nsIFrame* mFrameToDescendInto;
PRPackedBool mDescendIntoFrameSiblings;
PRPackedBool mLineBreakerCanCrossFrameBoundary;
PRPackedBool mTextRunCanCrossFrameBoundary;
};
static FrameTextTraversal
CanTextCrossFrameBoundary(nsIFrame* aFrame, nsIAtom* aType)
{
// placeholders are "invisible", so a text run should be able to span
// across one. The text in the out-of-flow, if any, will not be included
// in this textrun of course.
return aFrame->CanContinueTextRun() ||
aFrame->GetType() == nsGkAtoms::placeholderFrame;
NS_ASSERTION(aType == aFrame->GetType(), "Wrong type");
FrameTextTraversal result;
PRBool continuesTextRun = aFrame->CanContinueTextRun();
if (aType == nsGkAtoms::placeholderFrame) {
// placeholders are "invisible", so a text run should be able to span
// across one. But don't descend into the out-of-flow.
result.mLineBreakerCanCrossFrameBoundary = PR_TRUE;
if (continuesTextRun) {
// ... Except for first-letter floats, which are really in-flow
// from the point of view of capitalization etc, so we'd better
// descend into them. But we actually need to break the textrun for
// first-letter floats since things look bad if, say, we try to make a
// ligature across the float boundary.
result.mFrameToDescendInto =
(static_cast<nsPlaceholderFrame*>(aFrame))->GetOutOfFlowFrame();
result.mDescendIntoFrameSiblings = PR_FALSE;
result.mTextRunCanCrossFrameBoundary = PR_FALSE;
} else {
result.mFrameToDescendInto = nsnull;
result.mTextRunCanCrossFrameBoundary = PR_TRUE;
}
} else {
if (continuesTextRun) {
result.mFrameToDescendInto = aFrame->GetFirstChild(nsnull);
result.mDescendIntoFrameSiblings = PR_TRUE;
result.mTextRunCanCrossFrameBoundary = PR_TRUE;
result.mLineBreakerCanCrossFrameBoundary = PR_TRUE;
} else {
result.mFrameToDescendInto = nsnull;
result.mTextRunCanCrossFrameBoundary = PR_FALSE;
result.mLineBreakerCanCrossFrameBoundary = PR_FALSE;
}
}
return result;
}
BuildTextRunsScanner::FindBoundaryResult
BuildTextRunsScanner::FindBoundaries(nsIFrame* aFrame, FindBoundaryState* aState)
{
nsTextFrame* textFrame = aFrame->GetType() == nsGkAtoms::textFrame
nsIAtom* frameType = aFrame->GetType();
nsTextFrame* textFrame = frameType == nsGkAtoms::textFrame
? static_cast<nsTextFrame*>(aFrame) : nsnull;
if (textFrame) {
if (aState->mLastTextFrame &&
@@ -794,28 +834,24 @@ BuildTextRunsScanner::FindBoundaries(nsIFrame* aFrame, FindBoundaryState* aState
return FB_CONTINUE;
}
PRBool continueTextRun = CanTextRunCrossFrameBoundary(aFrame);
PRBool descendInto = PR_TRUE;
if (!continueTextRun) {
// XXX do we need this? are there frames we need to descend into that aren't
// float-containing-blocks?
descendInto = !aFrame->IsFloatContainingBlock();
FrameTextTraversal traversal =
CanTextCrossFrameBoundary(aFrame, frameType);
if (!traversal.mTextRunCanCrossFrameBoundary) {
aState->mSeenTextRunBoundaryOnThisLine = PR_TRUE;
if (aState->mSeenSpaceForLineBreakingOnThisLine)
return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
}
if (descendInto) {
nsIFrame* child = aFrame->GetFirstChild(nsnull);
while (child) {
FindBoundaryResult result = FindBoundaries(child, aState);
if (result != FB_CONTINUE)
return result;
child = child->GetNextSibling();
}
for (nsIFrame* f = traversal.mFrameToDescendInto; f;
f = f->GetNextSibling()) {
FindBoundaryResult result = FindBoundaries(f, aState);
if (result != FB_CONTINUE)
return result;
if (!traversal.mDescendIntoFrameSiblings)
break;
}
if (!continueTextRun) {
if (!traversal.mTextRunCanCrossFrameBoundary) {
aState->mSeenTextRunBoundaryOnThisLine = PR_TRUE;
if (aState->mSeenSpaceForLineBreakingOnThisLine)
return FB_FOUND_VALID_TEXTRUN_BOUNDARY;
@@ -841,10 +877,10 @@ static void
BuildTextRuns(gfxContext* aContext, nsTextFrame* aForFrame,
nsIFrame* aLineContainer, const nsLineList::iterator* aForFrameLine)
{
NS_ASSERTION(aForFrame || aForFrameLine,
"One of aForFrame or aForFrameLine must be set!");
NS_ASSERTION(aForFrame || (aForFrameLine && aLineContainer),
"One of aForFrame or aForFrameLine+aLineContainer must be set!");
if (!aLineContainer) {
if (!aLineContainer || !aForFrameLine) {
aLineContainer = FindLineContainer(aForFrame);
} else {
NS_ASSERTION(!aForFrame || aLineContainer == FindLineContainer(aForFrame), "Wrong line container hint");
@@ -1207,34 +1243,35 @@ void BuildTextRunsScanner::ScanFrame(nsIFrame* aFrame)
return;
}
PRBool continueTextRun = CanTextRunCrossFrameBoundary(aFrame);
PRBool descendInto = PR_TRUE;
FrameTextTraversal traversal =
CanTextCrossFrameBoundary(aFrame, frameType);
PRBool isBR = frameType == nsGkAtoms::brFrame;
if (!continueTextRun) {
if (!traversal.mLineBreakerCanCrossFrameBoundary) {
// BR frames are special. We do not need or want to record a break opportunity
// before a BR frame.
FlushFrames(PR_TRUE, isBR);
mCommonAncestorWithLastFrame = aFrame;
mTrimNextRunLeadingWhitespace = PR_FALSE;
// XXX do we need this? are there frames we need to descend into that aren't
// float-containing-blocks?
descendInto = !aFrame->IsFloatContainingBlock();
mStartOfLine = PR_FALSE;
} else if (!traversal.mTextRunCanCrossFrameBoundary) {
FlushFrames(PR_FALSE, PR_FALSE);
}
if (descendInto) {
nsIFrame* f;
for (f = aFrame->GetFirstChild(nsnull); f; f = f->GetNextSibling()) {
ScanFrame(f);
}
for (nsIFrame* f = traversal.mFrameToDescendInto; f;
f = f->GetNextSibling()) {
ScanFrame(f);
if (!traversal.mDescendIntoFrameSiblings)
break;
}
if (!continueTextRun) {
if (!traversal.mLineBreakerCanCrossFrameBoundary) {
// Really if we're a BR frame this is unnecessary since descendInto will be
// false. In fact this whole "if" statement should move into the descendInto.
FlushFrames(PR_TRUE, isBR);
mCommonAncestorWithLastFrame = aFrame;
mTrimNextRunLeadingWhitespace = PR_FALSE;
} else if (!traversal.mTextRunCanCrossFrameBoundary) {
FlushFrames(PR_FALSE, PR_FALSE);
}
LiftCommonAncestorWithLastFrameToParent(aFrame->GetParent());
@@ -3192,7 +3229,7 @@ nsContinuingTextFrame::Init(nsIContent* aContent,
aPrevInFlow->SetNextInFlow(this);
nsTextFrame* prev = static_cast<nsTextFrame*>(aPrevInFlow);
mContentOffset = prev->GetContentOffset() + prev->GetContentLengthHint();
NS_ASSERTION(mContentOffset < aContent->GetText()->GetLength(),
NS_ASSERTION(mContentOffset < PRInt32(aContent->GetText()->GetLength()),
"Creating ContinuingTextFrame, but there is no more content");
if (prev->GetStyleContext() != GetStyleContext()) {
// We're taking part of prev's text, and its style may be different
@@ -5382,15 +5419,11 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
AddStateBits(TEXT_START_OF_LINE);
}
// Layout dependent styles are a problem because we need to reconstruct
// the gfxTextRun based on our layout.
PRBool layoutDependentTextRun =
lineLayout.GetFirstLetterStyleOK() || lineLayout.GetInFirstLine();
if (layoutDependentTextRun) {
SetLength(maxContentLength);
}
PRUint32 flowEndInTextRun;
nsIFrame* lineContainer = lineLayout.GetLineContainerFrame();
gfxContext* ctx = aReflowState.rendContext->ThebesContext();
const nsTextFragment* frag = mContent->GetText();
// DOM offsets of the text range we need to measure, after trimming
// whitespace, restricting to first-letter, and restricting preformatted text
// to nearest newline
@@ -5413,9 +5446,36 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
}
}
PRUint32 flowEndInTextRun;
nsIFrame* lineContainer = lineLayout.GetLineContainerFrame();
gfxContext* ctx = aReflowState.rendContext->ThebesContext();
PRBool completedFirstLetter = PR_FALSE;
// Layout dependent styles are a problem because we need to reconstruct
// the gfxTextRun based on our layout.
if (lineLayout.GetFirstLetterStyleOK() || lineLayout.GetInFirstLine()) {
SetLength(maxContentLength);
if (lineLayout.GetFirstLetterStyleOK()) {
// floating first-letter boundaries are significant in textrun
// construction, so clear the textrun out every time we hit a first-letter
// and have changed our length (which controls the first-letter boundary)
ClearTextRun();
// Find the length of the first-letter. We need a textrun for this.
gfxSkipCharsIterator iter =
EnsureTextRun(ctx, lineContainer, lineLayout.GetLine(), &flowEndInTextRun);
if (mTextRun) {
completedFirstLetter = FindFirstLetterRange(frag, mTextRun, offset, iter, &length);
if (length) {
AddStateBits(TEXT_FIRST_LETTER);
}
// Change this frame's length to the first-letter length right now
// so that when we rebuild the textrun it will be built with the
// right first-letter boundary
SetLength(offset + length - GetContentOffset());
// Ensure that the textrun will be rebuilt
ClearTextRun();
}
}
}
gfxSkipCharsIterator iter =
EnsureTextRun(ctx, lineContainer, lineLayout.GetLine(), &flowEndInTextRun);
@@ -5431,7 +5491,7 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
iter = EnsureTextRun(ctx, lineContainer,
lineLayout.GetLine(), &flowEndInTextRun);
}
if (!mTextRun) {
ClearMetrics(aMetrics);
aStatus = NS_FRAME_COMPLETE;
@@ -5442,13 +5502,6 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
<= mTextRun->GetLength(),
"Text run does not map enough text for our reflow");
// Restrict to just the first-letter if necessary
PRBool completedFirstLetter = PR_FALSE;
if (lineLayout.GetFirstLetterStyleOK()) {
AddStateBits(TEXT_FIRST_LETTER);
completedFirstLetter = FindFirstLetterRange(frag, mTextRun, offset, iter, &length);
}
/////////////////////////////////////////////////////////////////////
// See how much text should belong to this text frame, and measure it
/////////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,12 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
p::first-letter { font-size:200%; float:left; }
</style>
</head>
<body>
<p>Hello Kitty
<p>"Hello Kitty"
</body>
</html>

View File

@@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
p { text-transform:capitalize; }
p::first-letter { font-size:200%; float:left; }
</style>
</head>
<body>
<p>hello kitty
<p>"hello kitty"
</body>
</html>

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family:DejaVu Sans; font-size:100px; }
p::first-letter { float:left; }
</style>
</head>
<body>
<p>f&#8204;ish fish f&#8204;ish
</body>
</html>

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family:DejaVu Sans; font-size:100px; }
p::first-letter { float:left; }
</style>
</head>
<body>
<p>fish fish f&#8204;ish
</body>
</html>

View File

@@ -827,6 +827,8 @@ fails-if(MOZ_WIDGET_TOOLKIT=="gtk2") == 424074-1-ref2.xul 424074-1-ref3.xul
== 430813-1.html 430813-1-ref.html
== 430813-2.html 430813-2-ref.html
== 430813-3.html 430813-3-ref.html
== 431341-1.html 431341-1-ref.html
== 431341-2.html 431341-2-ref.html
== 433640-1.html 433640-1-ref.html
== 433700.html 433700-ref.html
== 436356-1.html 436356-1-ref.html