Bug 343445. Change inline reflow strategy to avoid looking ahead through words while measuring text. r+sr=dbaron

This commit is contained in:
roc+@cs.cmu.edu
2006-10-19 01:47:47 +00:00
parent d0946cb6eb
commit d9807fd270
18 changed files with 716 additions and 892 deletions

View File

@@ -249,6 +249,16 @@ nsTextFragment::AppendTo(nsAString& aString) const
}
}
void
nsTextFragment::AppendTo(nsAString& aString, PRInt32 aOffset, PRInt32 aLength) const
{
if (mState.mIs2b) {
aString.Append(m2b + aOffset, aLength);
} else {
AppendASCIItoUTF16(Substring(m1b + aOffset, m1b + aOffset + aLength), aString);
}
}
void
nsTextFragment::CopyTo(PRUnichar *aDest, PRInt32 aOffset, PRInt32 aCount)
{

View File

@@ -160,6 +160,13 @@ public:
*/
void AppendTo(nsAString& aString) const;
/**
* Append a substring of the contents of this string fragment to aString.
* @param aOffset where to start the substring in this text fragment
* @param aLength the length of the substring
*/
void AppendTo(nsAString& aString, PRInt32 aOffset, PRInt32 aLength) const;
/**
* Make a copy of the fragments contents starting at offset for
* count characters. The offset and count will be adjusted to

View File

@@ -95,18 +95,11 @@ DestroyRectFunc(void* aFrame,
delete NS_STATIC_CAST(nsRect*, aPropertyValue);
}
static nsIFrame* GetParentOrPlaceholderFor(nsFrameManager* aFrameManager,
nsIFrame* aFrame) {
if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
return aFrameManager->GetPlaceholderFrameFor(aFrame);
}
return aFrame->GetParent();
}
static void MarkFrameForDisplay(nsIFrame* aFrame, nsIFrame* aStopAtFrame) {
nsFrameManager* frameManager = aFrame->GetPresContext()->PresShell()->FrameManager();
for (nsIFrame* f = aFrame; f; f = GetParentOrPlaceholderFor(frameManager, f)) {
for (nsIFrame* f = aFrame; f;
f = nsLayoutUtils::GetParentOrPlaceholderFor(frameManager, f)) {
if (f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)
return;
f->AddStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
@@ -135,7 +128,8 @@ static void UnmarkFrameForDisplay(nsIFrame* aFrame) {
nsFrameManager* frameManager = aFrame->GetPresContext()->PresShell()->FrameManager();
for (nsIFrame* f = aFrame; f; f = GetParentOrPlaceholderFor(frameManager, f)) {
for (nsIFrame* f = aFrame; f;
f = nsLayoutUtils::GetParentOrPlaceholderFor(frameManager, f)) {
if (!(f->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO))
return;
f->RemoveStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);

View File

@@ -56,6 +56,7 @@
#include "nsGUIEvent.h"
#include "nsDisplayList.h"
#include "nsRegion.h"
#include "nsFrameManager.h"
#ifdef MOZ_SVG_FOREIGNOBJECT
#include "nsSVGForeignObjectFrame.h"
@@ -995,3 +996,69 @@ nsLayoutUtils::GetFontMetricsForFrame(nsIFrame* aFrame,
sc->GetStyleVisibility()->mLangGroup,
*aFontMetrics);
}
nsIFrame*
nsLayoutUtils::GetParentOrPlaceholderFor(nsFrameManager* aFrameManager,
nsIFrame* aFrame)
{
if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
return aFrameManager->GetPlaceholderFrameFor(aFrame);
return aFrame->GetParent();
}
nsIFrame*
nsLayoutUtils::GetClosestCommonAncestorViaPlaceholders(nsIFrame* aFrame1,
nsIFrame* aFrame2,
nsIFrame* aKnownCommonAncestorHint)
{
NS_PRECONDITION(aFrame1, "aFrame1 must not be null");
NS_PRECONDITION(aFrame2, "aFrame2 must not be null");
nsPresContext* presContext = aFrame1->GetPresContext();
if (presContext != aFrame2->GetPresContext()) {
// different documents, no common ancestor
return nsnull;
}
nsFrameManager* frameManager = presContext->PresShell()->FrameManager();
nsAutoVoidArray frame1Ancestors;
nsIFrame* f1;
for (f1 = aFrame1; f1 && f1 != aKnownCommonAncestorHint;
f1 = GetParentOrPlaceholderFor(frameManager, f1)) {
frame1Ancestors.AppendElement(f1);
}
if (!f1 && aKnownCommonAncestorHint) {
// So, it turns out aKnownCommonAncestorHint was not an ancestor of f1. Oops.
// Never mind. We can continue as if aKnownCommonAncestorHint was null.
aKnownCommonAncestorHint = nsnull;
}
nsAutoVoidArray frame2Ancestors;
nsIFrame* f2;
for (f2 = aFrame2; f2 && f2 != aKnownCommonAncestorHint;
f2 = GetParentOrPlaceholderFor(frameManager, f2)) {
frame2Ancestors.AppendElement(f2);
}
if (!f2 && aKnownCommonAncestorHint) {
// So, it turns out aKnownCommonAncestorHint was not an ancestor of f2.
// We need to retry with no common ancestor hint.
return GetClosestCommonAncestorViaPlaceholders(aFrame1, aFrame2, nsnull);
}
// now frame1Ancestors and frame2Ancestors give us the parent frame chain
// up to aKnownCommonAncestorHint, or if that is null, up to and including
// the root frame. We need to walk from the end (i.e., the top of the
// frame (sub)tree) down to aFrame1/aFrame2 looking for the first difference.
nsIFrame* lastCommonFrame = aKnownCommonAncestorHint;
PRInt32 last1 = frame1Ancestors.Count() - 1;
PRInt32 last2 = frame2Ancestors.Count() - 1;
while (last1 >= 0 && last2 >= 0) {
nsIFrame* frame1 = NS_STATIC_CAST(nsIFrame*, frame1Ancestors.ElementAt(last1));
if (frame1 != frame2Ancestors.ElementAt(last2))
break;
lastCommonFrame = frame1;
last1--;
last2--;
}
return lastCommonFrame;
}

View File

@@ -431,6 +431,27 @@ public:
*/
static nsresult GetFontMetricsForFrame(nsIFrame* aFrame,
nsIFontMetrics** aFontMetrics);
/**
* If aFrame is an out of flow frame, return its placeholder, otherwise
* return its parent.
*/
static nsIFrame* GetParentOrPlaceholderFor(nsFrameManager* aFrameManager,
nsIFrame* aFrame);
/**
* Find the closest common ancestor of aFrame1 and aFrame2, following
* out of flow frames to their placeholders instead of their parents. Returns
* nsnull if the frames are in different frame trees.
*
* @param aKnownCommonAncestorHint a frame that is believed to be on the
* ancestor chain of both aFrame1 and aFrame2. If null, or a frame that is
* not in fact on both ancestor chains, then this function will still return
* the correct result, but it will be slower.
*/
static nsIFrame*
GetClosestCommonAncestorViaPlaceholders(nsIFrame* aFrame1, nsIFrame* aFrame2,
nsIFrame* aKnownCommonAncestorHint);
};
#endif // nsLayoutUtils_h__

View File

@@ -3811,6 +3811,8 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
PRBool movedPastFloat = PR_FALSE;
do {
PRBool allowPullUp = aTryPull;
nsIContent* forceBreakInContent = nsnull;
PRInt32 forceBreakOffset = -1;
do {
nsSpaceManager::SavedState spaceManagerState;
aState.mReflowState.mSpaceManager->PushState(&spaceManagerState);
@@ -3827,6 +3829,9 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
&aState.mReflowState,
aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH));
lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
if (forceBreakInContent) {
lineLayout.ForceBreakAtPosition(forceBreakInContent, forceBreakOffset);
}
rv = DoReflowInlineFrames(aState, lineLayout, aLine,
aKeepReflowGoing, &lineReflowStatus,
aUpdateMaximumWidth, aDamageDirtyArea,
@@ -3835,9 +3840,19 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
if (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus ||
LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
if (lineLayout.NeedsBackup()) {
NS_ASSERTION(!forceBreakInContent, "Backuping up twice; this should never be necessary");
// If there is no saved break position, then this will set
// set forceBreakInContent to null and we won't back up, which is
// correct.
forceBreakInContent = lineLayout.GetLastOptionalBreakPosition(&forceBreakOffset);
} else {
forceBreakInContent = nsnull;
}
// restore the space manager state
aState.mReflowState.mSpaceManager->PopState(&spaceManagerState);
// Clear out below-current-line-floats
// Clear out float lists
aState.mCurrentLineFloats.DeleteAll();
aState.mBelowCurrentLineFloats.DeleteAll();
}
@@ -3887,6 +3902,13 @@ nsBlockFrame::PushTruncatedPlaceholderLine(nsBlockReflowState& aState,
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
}
#ifdef DEBUG
static const char* LineReflowStatusNames[] = {
"LINE_REFLOW_OK", "LINE_REFLOW_STOP", "LINE_REFLOW_REDO_NO_PULL",
"LINE_REFLOW_REDO_NEXT_BAND", "LINE_REFLOW_TRUNCATED"
};
#endif
nsresult
nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
@@ -3953,9 +3975,18 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
// continuations
PRBool isContinuingPlaceholders = PR_FALSE;
if (impactedByFloats) {
// There is a soft break opportunity at the start of the line, because
// we can always move this line down below float(s).
if (aLineLayout.NotifyOptionalBreakPosition(frame->GetContent(), 0)) {
lineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND;
}
}
// need to repeatedly call GetChildCount here, because the child
// count can change during the loop!
for (i = 0; i < aLine->GetChildCount(); i++) {
for (i = 0; LINE_REFLOW_OK == lineReflowStatus && i < aLine->GetChildCount();
i++, frame = frame->GetNextSibling()) {
if (IsContinuationPlaceholder(frame)) {
isContinuingPlaceholders = PR_TRUE;
}
@@ -3986,9 +4017,7 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing);
}
}
break;
}
frame = frame->GetNextSibling();
}
// Don't pull up new frames into lines with continuation placeholders
@@ -4024,6 +4053,20 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
}
}
if ((lineReflowStatus == LINE_REFLOW_STOP || lineReflowStatus == LINE_REFLOW_OK) &&
!aLineLayout.HaveForcedBreakPosition() && aLineLayout.NeedsBackup()) {
// We need to try backing up to before a text run
PRInt32 offset;
nsIContent* breakContent = aLineLayout.GetLastOptionalBreakPosition(&offset);
if (breakContent) {
// We can back up!
lineReflowStatus = LINE_REFLOW_REDO_NO_PULL;
}
} else {
// Don't try to force any breaking if we are going to retry
aLineLayout.ClearOptionalBreakPosition();
}
if (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
// This happens only when we have a line that is impacted by
// floats and the first element in the line doesn't fit with
@@ -4084,6 +4127,11 @@ nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
}
}
}
#ifdef DEBUG
if (gNoisyReflow) {
printf("Line reflow status = %s\n", LineReflowStatusNames[lineReflowStatus]);
}
#endif
*aLineReflowStatus = lineReflowStatus;
return rv;

View File

@@ -218,20 +218,6 @@ nsHTMLCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
nsISelectionDisplay::DISPLAY_IMAGES);
}
NS_IMETHODIMP
nsHTMLCanvasFrame::CanContinueTextRun(PRBool& aContinueTextRun) const
{
// stolen from nsImageFrame.cpp
// images really CAN continue text runs, but the textFrame needs to be
// educated before we can indicate that it can. For now, we handle the fixing up
// of max element widths in nsLineLayout::VerticalAlignFrames, but hopefully
// this can be eliminated and the textFrame can be convinced to handle inlines
// that take up space in text runs.
aContinueTextRun = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsHTMLCanvasFrame::GetContentForEvent(nsPresContext* aPresContext,
nsEvent* aEvent,

View File

@@ -65,9 +65,6 @@ public:
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus);
NS_IMETHOD CanContinueTextRun(PRBool& aContinueTextRun) const;
NS_IMETHOD GetContentForEvent(nsPresContext* aPresContext,
nsEvent* aEvent,
nsIContent** aContent);

View File

@@ -1628,20 +1628,6 @@ nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget,
return status;
}
NS_IMETHODIMP
nsImageFrame::CanContinueTextRun(PRBool& aContinueTextRun) const
{
// images really CAN continue text runs, but the textFrame needs to be
// educated before we can indicate that it can. For now, we handle the fixing up
// of max element widths in nsLineLayout::VerticalAlignFrames, but hopefully
// this can be eliminated and the textFrame can be convinced to handle inlines
// that take up space in text runs.
aContinueTextRun = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsImageFrame::GetContentForEvent(nsPresContext* aPresContext,
nsEvent* aEvent,

View File

@@ -110,9 +110,6 @@ public:
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus);
NS_IMETHOD CanContinueTextRun(PRBool& aContinueTextRun) const;
NS_IMETHOD GetContentForEvent(nsPresContext* aPresContext,
nsEvent* aEvent,
nsIContent** aContent);

View File

@@ -680,7 +680,9 @@ nsInlineFrame::ReflowInlineFrame(nsPresContext* aPresContext,
nsLineLayout* lineLayout = aReflowState.mLineLayout;
PRBool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK();
PRBool pushedFrame;
nsresult rv = lineLayout->ReflowFrame(aFrame, aStatus, nsnull, pushedFrame);
nsresult rv =
lineLayout->ReflowFrame(aFrame, aStatus, nsnull, pushedFrame);
/* This next block is for bug 28811
Test the child frame for %-awareness,
and mark this frame with a bit if it is %-aware.

View File

@@ -156,11 +156,15 @@ nsLineLayout::nsLineLayout(nsPresContext* aPresContext,
: mPresContext(aPresContext),
mSpaceManager(aSpaceManager),
mBlockReflowState(aOuterReflowState),
mLastOptionalBreakContent(nsnull),
mForceBreakContent(nsnull),
mLastOptionalBreakContentOffset(-1),
mForceBreakContentOffset(-1),
mTrailingTextFrame(nsnull),
mBlockRS(nsnull),/* XXX temporary */
mMinLineHeight(0),
mComputeMaxElementWidth(aComputeMaxElementWidth),
mTextIndent(0),
mWordFrames(0)
mTextIndent(0)
{
MOZ_COUNT_CTOR(nsLineLayout);
@@ -195,8 +199,6 @@ nsLineLayout::~nsLineLayout()
NS_ASSERTION(nsnull == mRootSpan, "bad line-layout user");
delete mWordFrames; // operator delete for this class just returns
// PL_FreeArenaPool takes our memory and puts in on a global free list so
// that the next time an arena makes an allocation it will not have to go
// all the way down to malloc. This is desirable as this class is created
@@ -210,22 +212,6 @@ nsLineLayout::~nsLineLayout()
PL_FinishArenaPool(&mArena);
}
void*
nsLineLayout::ArenaDeque::operator new(size_t aSize, PLArenaPool &aPool)
{
void *mem;
PL_ARENA_ALLOCATE(mem, &aPool, aSize);
return mem;
}
PRBool nsLineLayout::AllocateDeque()
{
mWordFrames = new(mArena) ArenaDeque;
return mWordFrames != nsnull;
}
// Find out if the frame has a non-null prev-in-flow, i.e., whether it
// is a continuation.
inline PRBool
@@ -282,8 +268,6 @@ nsLineLayout::BeginLineReflow(nscoord aX, nscoord aY,
mSpanDepth = 0;
mMaxTopBoxHeight = mMaxBottomBoxHeight = 0;
ForgetWordFrames();
PerSpanData* psd;
NewPerSpanData(&psd);
mCurrentSpan = mRootSpan = psd;
@@ -936,16 +920,15 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
// We want to guarantee that we always make progress when
// formatting. Therefore, if the object being placed on the line is
// too big for the line, but it is the only thing on the line
// (including counting floats) then we go ahead and place it
// anyway. Its also true that if the object is a part of a larger
// object (a multiple frame word) then we will place it on the line
// too.
// too big for the line, but it is the only thing on the line and is not
// impacted by a float, then we go ahead and place it anyway. (If the line
// is impacted by one or more floats, then it is safe to break because
// we can move the line down below float(s).)
//
// Capture this state *before* we reflow the frame in case it clears
// the state out. We need to know how to treat the current frame
// when breaking.
PRBool notSafeToBreak = CanPlaceFloatNow() || InWord();
PRBool notSafeToBreak = CanPlaceFloatNow() && !GetFlag(LL_IMPACTEDBYFLOATS);
// Apply start margins (as appropriate) to the frame computing the
// new starting x,y coordinates for the frame.
@@ -992,6 +975,10 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
nsIAtom* frameType = aFrame->GetType();
PRInt32 savedOptionalBreakOffset;
nsIContent* savedOptionalBreakContent =
GetLastOptionalBreakPosition(&savedOptionalBreakOffset);
rv = aFrame->Reflow(mPresContext, metrics, reflowState, aReflowStatus);
if (NS_FAILED(rv)) {
NS_WARNING( "Reflow of frame failed in nsLineLayout" );
@@ -1003,20 +990,20 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
// XXX See if the frame is a placeholderFrame and if it is process
// the float.
PRBool placedFloat = PR_FALSE;
if (frameType) {
if (nsLayoutAtoms::placeholderFrame == frameType) {
pfd->SetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE, PR_TRUE);
nsIFrame* outOfFlowFrame = nsLayoutUtils::GetFloatFromPlaceholder(aFrame);
if (outOfFlowFrame) {
nsPlaceholderFrame* placeholder = NS_STATIC_CAST(nsPlaceholderFrame*, aFrame);
PRBool didPlace;
if (eReflowReason_Incremental == reason) {
didPlace = InitFloat(placeholder, aReflowStatus);
placedFloat = InitFloat(placeholder, aReflowStatus);
}
else {
didPlace = AddFloat(placeholder, aReflowStatus);
placedFloat = AddFloat(placeholder, aReflowStatus);
}
if (!didPlace) {
if (!placedFloat) {
aReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
}
if (outOfFlowFrame->GetType() == nsLayoutAtoms::letterFrame) {
@@ -1164,9 +1151,15 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
}
}
// Check whether this frame breaks up text runs. All frames break up text
// runs (hence return false here) except for text frames and inline containers.
PRBool continuingTextRun;
aFrame->CanContinueTextRun(continuingTextRun);
// See if we can place the frame. If we can't fit it, then we
// return now.
if (CanPlaceFrame(pfd, reflowState, notSafeToBreak, metrics, aReflowStatus)) {
if (CanPlaceFrame(pfd, reflowState, notSafeToBreak, continuingTextRun,
metrics, aReflowStatus)) {
// Place the frame, updating aBounds with the final size and
// location. Then apply the bottom+right margins (as
// appropriate) to the frame.
@@ -1178,10 +1171,28 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
// so do most of it now.
VerticalAlignFrames(span);
}
if (!continuingTextRun) {
SetFlag(LL_INWORD, PR_FALSE);
mTrailingTextFrame = nsnull;
if (!psd->mNoWrap && (!CanPlaceFloatNow() || placedFloat)) {
// record soft break opportunity after this content that can't be
// part of a text run. This is not a text frame so we know
// that offset PR_INT32_MAX means "after the content".
if (NotifyOptionalBreakPosition(aFrame->GetContent(), PR_INT32_MAX)) {
// If this returns true then we are being told to actually break here.
aReflowStatus = NS_INLINE_LINE_BREAK_AFTER(aReflowStatus);
}
}
}
}
else {
PushFrame(aFrame);
aPushedFrame = PR_TRUE;
// Undo any saved break positions that the frame might have told us about,
// since we didn't end up placing it
RestoreSavedBreakPosition(savedOptionalBreakContent,
savedOptionalBreakOffset);
}
}
else {
@@ -1262,6 +1273,7 @@ PRBool
nsLineLayout::CanPlaceFrame(PerFrameData* pfd,
const nsHTMLReflowState& aReflowState,
PRBool aNotSafeToBreak,
PRBool aFrameCanContinueTextRun,
nsHTMLReflowMetrics& aMetrics,
nsReflowStatus& aStatus)
{
@@ -1297,6 +1309,9 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd,
return PR_TRUE;
}
PRBool ltr = NS_STYLE_DIRECTION_LTR == aReflowState.mStyleVisibility->mDirection;
nscoord endMargin = ltr ? pfd->mMargin.right : pfd->mMargin.left;
#ifdef NOISY_CAN_PLACE_FRAME
if (nsnull != psd->mFrame) {
nsFrame::ListTag(stdout, psd->mFrame->mFrame);
@@ -1306,13 +1321,12 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd,
}
printf(": aNotSafeToBreak=%s frame=", aNotSafeToBreak ? "true" : "false");
nsFrame::ListTag(stdout, pfd->mFrame);
printf(" frameWidth=%d\n", pfd->mBounds.XMost() + rightMargin - psd->mX);
printf(" frameWidth=%d\n", pfd->mBounds.XMost() + endMargin - psd->mX);
#endif
// Set outside to PR_TRUE if the result of the reflow leads to the
// frame sticking outside of our available area.
PRBool ltr = (NS_STYLE_DIRECTION_LTR == aReflowState.mStyleVisibility->mDirection);
PRBool outside = pfd->mBounds.XMost() + (ltr ? pfd->mMargin.right : pfd->mMargin.left) > psd->mRightEdge;
PRBool outside = pfd->mBounds.XMost() + endMargin > psd->mRightEdge;
if (!outside) {
// If it fits, it fits
#ifdef NOISY_CAN_PLACE_FRAME
@@ -1342,10 +1356,9 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd,
#endif
if (aNotSafeToBreak) {
// There are no frames on the line or we are in the first word on
// the line. If the line isn't impacted by a float then the
// current frame fits.
if (!GetFlag(LL_IMPACTEDBYFLOATS)) {
// There are no frames on the line that take up width and the line is
// not impacted by floats, so we must allow the current frame to be
// placed on the line
#ifdef NOISY_CAN_PLACE_FRAME
printf(" ==> not-safe and not-impacted fits: ");
while (nsnull != psd) {
@@ -1356,60 +1369,6 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd,
#endif
return PR_TRUE;
}
else if (GetFlag(LL_LASTFLOATWASLETTERFRAME)) {
// Another special case: see if the float is a letter
// frame. If it is, then allow the frame next to it to fit.
if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME)) {
// This must be the first piece of non-empty text (because
// aNotSafeToBreak is true) or it's a piece of text that is
// part of a larger word.
pfd->SetFlag(PFD_ISSTICKY, PR_TRUE);
}
else if (pfd->mSpan) {
PerFrameData* pf = pfd->mSpan->mFirstFrame;
while (pf) {
if (pf->GetFlag(PFD_ISSTICKY)) {
// If one of the spans children was sticky then the span
// itself is sticky.
pfd->SetFlag(PFD_ISSTICKY, PR_TRUE);
}
pf = pf->mNext;
}
}
if (pfd->GetFlag(PFD_ISSTICKY)) {
#ifdef NOISY_CAN_PLACE_FRAME
printf(" ==> last float was letter frame && frame is sticky\n");
#endif
return PR_TRUE;
}
}
}
// If this is a piece of text inside a letter frame...
if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME)) {
if (psd->mFrame && psd->mFrame->GetFlag(PFD_ISLETTERFRAME)) {
nsIFrame* prevInFlow = psd->mFrame->mFrame->GetPrevInFlow();
if (prevInFlow) {
nsIFrame* prevPrevInFlow = prevInFlow->GetPrevInFlow();
if (!prevPrevInFlow) {
// And it's the first continuation of the letter frame...
// Then make sure that the text fits
return PR_TRUE;
}
}
}
}
else if (pfd->GetFlag(PFD_ISLETTERFRAME)) {
// If this is the first continuation of the letter frame...
nsIFrame* prevInFlow = pfd->mFrame->GetPrevInFlow();
if (prevInFlow) {
nsIFrame* prevPrevInFlow = prevInFlow->GetPrevInFlow();
if (!prevPrevInFlow) {
return PR_TRUE;
}
}
}
// Special check for span frames
if (pfd->mSpan && pfd->mSpan->mContainsFloat) {
@@ -1439,6 +1398,24 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd,
return PR_TRUE;
}
if (aFrameCanContinueTextRun) {
// Let it fit, but we reserve the right to roll back
// to before the text run! Note that we usually won't get here because
// a text frame will break itself to avoid exceeding the available width.
// We'll only get here for text frames that couldn't break early enough.
#ifdef NOISY_CAN_PLACE_FRAME
printf(" ==> placing overflowing textrun, requesting backup\n");
#endif
if (!mLastOptionalBreakContent) {
// Nowhere to roll back to, so make this fit
return PR_TRUE;
}
// We have something to roll back to. So, signal that we will to roll back,
// and fall through to not place this frame.
SetFlag(LL_NEEDBACKUP, PR_TRUE);
}
#ifdef NOISY_CAN_PLACE_FRAME
printf(" ==> didn't fit\n");
#endif
@@ -3062,107 +3039,6 @@ nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsRect& aCombinedArea)
aCombinedArea = combinedAreaResult;
}
void
nsLineLayout::ForgetWordFrame(nsIFrame* aFrame)
{
if (mWordFrames && 0 != mWordFrames->GetSize()) {
NS_ASSERTION((void*)aFrame == mWordFrames->PeekFront(), "forget-word-frame");
mWordFrames->PopFront();
}
}
nsIFrame*
nsLineLayout::FindNextText(nsPresContext* aPresContext, nsIFrame* aFrame)
{
// Grovel through the frame hierarchy to find a text frame that is
// "adjacent" to aFrame.
// So this is kind of funky. During reflow, overflow frames will
// have their parent pointers set up lazily. We assume that, on
// entry, aFrame has its parent pointer set correctly (as do all of
// its ancestors). Starting from that, we need to make sure that as
// we traverse through frames trying to find the next text frame, we
// leave the frames with their parent pointers set correctly, so the
// *next* time we come through here, we're good to go.
// Build a path from the enclosing block frame down to aFrame. We'll
// use this to walk the frame tree. (XXXwaterson if I was clever, I
// wouldn't need to build this up before hand, and could incorporate
// this logic into the walking code directly.)
nsAutoVoidArray stack;
for (;;) {
stack.InsertElementAt(aFrame, 0);
aFrame = aFrame->GetParent();
NS_ASSERTION(aFrame, "wow, no block frame found");
if (! aFrame)
break;
if (NS_STYLE_DISPLAY_INLINE != aFrame->GetStyleDisplay()->mDisplay)
break;
}
// Using the path we've built up, walk the frame tree looking for
// the text frame that follows aFrame.
PRInt32 count;
while ((count = stack.Count()) != 0) {
PRInt32 lastIndex = count - 1;
nsIFrame* top = NS_STATIC_CAST(nsIFrame*, stack.ElementAt(lastIndex));
// If this is a frame that'll break a word, then bail.
PRBool canContinue;
top->CanContinueTextRun(canContinue);
if (! canContinue)
return nsnull;
// Advance to top's next sibling
nsIFrame* next = top->GetNextSibling();
if (! next) {
// No more siblings. Pop the top element to walk back up the
// frame tree.
stack.RemoveElementAt(lastIndex);
continue;
}
// We know top's parent is good, but next's might not be. So let's
// set it to be sure.
next->SetParent(top->GetParent());
// Save next at the top of the stack...
stack.ReplaceElementAt(next, lastIndex);
// ...and prowl down to next's deepest child. We'll need to check
// for potential run-busters "on the way down", too.
for (;;) {
next->CanContinueTextRun(canContinue);
if (! canContinue)
return nsnull;
nsIFrame* child = next->GetFirstChild(nsnull);
if (! child)
break;
stack.AppendElement(child);
next = child;
}
// Ignore continuing frames
if (HasPrevInFlow(next))
continue;
// If this is a text frame, return it.
if (nsLayoutAtoms::textFrame == next->GetType())
return next;
}
// If we get here, then there are no more text frames in this block.
return nsnull;
}
PRBool
nsLineLayout::TreatFrameAsBlock(nsIFrame* aFrame)
{

View File

@@ -56,6 +56,8 @@
#include "nsBlockReflowState.h"
#include "plarena.h"
class nsBlockFrame;
class nsSpaceManager;
class nsPlaceholderFrame;
struct nsStyleText;
@@ -68,14 +70,6 @@ public:
PRBool aComputeMaxElementWidth);
~nsLineLayout();
class ArenaDeque : public nsDeque
{
public:
ArenaDeque() : nsDeque(nsnull) {}
void *operator new(size_t, PLArenaPool &pool);
void operator delete(void *) {} // Dont do anything. Its Arena memory
};
void Init(nsBlockReflowState* aState, nscoord aMinLineHeight,
PRInt32 aLineNumber) {
mBlockRS = aState;
@@ -156,6 +150,7 @@ public:
protected:
#define LL_ENDSINWHITESPACE 0x00000001
#define LL_UNDERSTANDSNWHITESPACE 0x00000002
#define LL_INWORD 0x00000004
#define LL_FIRSTLETTERSTYLEOK 0x00000008
#define LL_ISTOPOFPAGE 0x00000010
#define LL_UPDATEDBAND 0x00000020
@@ -169,7 +164,9 @@ protected:
// large, e.g., if a large word-spacing is set). LL should not be misled into
// placing something where the whitespace was trimmed. See bug 329987.
#define LL_LINEENDSINSOFTBR 0x00000400
#define LL_LASTFLAG LL_LINEENDSINSOFTBR
#define LL_NEEDBACKUP 0x00000800
#define LL_LASTTEXTFRAME_WRAPPINGENABLED 0x00001000
#define LL_LASTFLAG LL_LASTTEXTFRAME_WRAPPINGENABLED
PRUint16 mFlags;
@@ -215,25 +212,6 @@ public:
mTextJustificationNumLetters = aNumLetters;
}
void RecordWordFrame(nsIFrame* aWordFrame) {
if(mWordFrames || AllocateDeque())
mWordFrames->Push(aWordFrame);
}
PRBool InWord() const {
return mWordFrames && 0 != mWordFrames->GetSize();
}
void ForgetWordFrame(nsIFrame* aFrame);
void ForgetWordFrames() {
if(mWordFrames) {
mWordFrames->Empty();
}
}
nsIFrame* FindNextText(nsPresContext* aPresContext, nsIFrame* aFrame);
PRBool CanPlaceFloatNow() const;
PRBool LineIsBreakable() const;
@@ -279,6 +257,44 @@ public:
return mBlockRS->AddFloat(*this, aFrame, PR_FALSE, aReflowStatus);
}
/**
* InWord is true when the last text frame reflowed ended in non-whitespace
* (so it has content that might form a word with subsequent text). The word width
* is the width of contiguous text up to the end of that last word, possibly
* including words from previous frames.
*
* If GetTrailingTextFrame is null then InWord will be false.
*/
PRBool InWord(nscoord* aWordWidth) {
if (!GetFlag(LL_INWORD))
return PR_FALSE;
*aWordWidth = mWordWidth;
return PR_TRUE;
}
void SetInWord(PRBool aInWord, nscoord aWordWidth) {
SetFlag(LL_INWORD, aInWord);
mWordWidth = aWordWidth;
}
/**
* If the last content placed on the line (not counting inline containers)
* was text, and can form a contiguous text flow with the next content to be
* placed, and is not just a frame of all-skipped whitespace, this is the
* frame for that last content ... otherwise it's null.
*
* @param aWrappingEnabled whether that text had word-wrapping enabled
* (white-space:normal or -moz-pre-wrap)
*/
nsIFrame* GetTrailingTextFrame(PRBool* aWrappingEnabled) {
*aWrappingEnabled = GetFlag(LL_LASTTEXTFRAME_WRAPPINGENABLED);
return mTrailingTextFrame;
}
void SetTrailingTextFrame(nsIFrame* aFrame, PRBool aWrappingEnabled)
{
mTrailingTextFrame = aFrame;
SetFlag(LL_LASTTEXTFRAME_WRAPPINGENABLED, aWrappingEnabled);
}
//----------------------------------------
PRBool GetFirstLetterStyleOK() const {
@@ -307,12 +323,99 @@ public:
nsPresContext* mPresContext;
/**
* Record where an optional break could have been placed. During line reflow,
* frames containing optional break points (e.g., whitespace in text frames)
* can call SetLastOptionalBreakPosition to record where a break could
* have been made, but wasn't because there appeared to be enough room
* to place more content on the line. For non-text frames, offset 0 means
* before the content, offset PR_INT32_MAX means after the content.
*
* Currently this is used to handle cases where a single word comprises
* multiple frames, and the first frame fits on the line but the whole word
* doesn't. We look back to the last optional break position and
* reflow the whole line again, forcing a break at that position. The last
* optional break position could be in a text frame or else after a frame
* that cannot be part of a text run, so those are the positions we record.
*
* It is imperative that this only gets called for break points that
* are within the available width.
*
* @return PR_TRUE if we are actually reflowing with forced break position and we
* should break here
*/
PRBool NotifyOptionalBreakPosition(nsIContent* aContent, PRInt32 aOffset) {
NS_ASSERTION(!GetFlag(LL_NEEDBACKUP),
"Shouldn't be updating the break position after we've already flagged an overrun");
mLastOptionalBreakContent = aContent;
mLastOptionalBreakContentOffset = aOffset;
return aContent && mForceBreakContent == aContent &&
mForceBreakContentOffset == aOffset;
}
/**
* Like NotifyOptionalBreakPosition, but here it's OK for LL_NEEDBACKUP
* to be set, because the caller is merely pruning some saved break position(s)
* that are actually not feasible.
*/
void RestoreSavedBreakPosition(nsIContent* aContent, PRInt32 aOffset) {
mLastOptionalBreakContent = aContent;
mLastOptionalBreakContentOffset = aOffset;
}
void ClearOptionalBreakPosition() {
mLastOptionalBreakContent = nsnull;
mLastOptionalBreakContentOffset = -1;
}
// Retrieve last set optional break position. When this returns null, no
// optional break has been recorded (which means that the line can't break yet).
nsIContent* GetLastOptionalBreakPosition(PRInt32* aOffset) {
*aOffset = mLastOptionalBreakContentOffset;
return mLastOptionalBreakContent;
}
/**
* Check whether frames overflowed the available width and CanPlaceFrame
* requested backing up to a saved break position.
*/
PRBool NeedsBackup() { return GetFlag(LL_NEEDBACKUP); }
// Line layout may place too much content on a line, overflowing its available
// width. When that happens, if SetLastOptionalBreakPosition has been
// used to record an optional break that wasn't taken, we can reflow the line
// again and force the break to happen at that point (i.e., backtracking
// to the last choice point).
// Record that we want to break at the given content+offset (which
// should have been previously returned by GetLastOptionalBreakPosition
// from another nsLineLayout).
void ForceBreakAtPosition(nsIContent* aContent, PRInt32 aOffset) {
mForceBreakContent = aContent;
mForceBreakContentOffset = aOffset;
}
PRBool HaveForcedBreakPosition() { return mForceBreakContent != nsnull; }
PRInt32 GetForcedBreakPosition(nsIContent* aContent) {
return mForceBreakContent == aContent ? mForceBreakContentOffset : -1;
}
/**
* 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).
*/
nsIFrame* GetLineContainerFrame() { return mBlockReflowState->frame; }
protected:
// This state is constant for a given block frame doing line layout
nsSpaceManager* mSpaceManager;
const nsStyleText* mStyleText; // for the block
const nsHTMLReflowState* mBlockReflowState;
nsIContent* mLastOptionalBreakContent;
nsIContent* mForceBreakContent;
PRInt32 mLastOptionalBreakContentOffset;
PRInt32 mForceBreakContentOffset;
nsIFrame* mTrailingTextFrame;
// XXX remove this when landing bug 154892 (splitting absolute positioned frames)
friend class nsInlineFrame;
@@ -333,14 +436,13 @@ protected:
nsIFrame* mFirstLetterFrame;
PRInt32 mLineNumber;
PRInt32 mColumn;
nscoord mWordWidth;
PRInt32 mTextJustificationNumSpaces;
PRInt32 mTextJustificationNumLetters;
nsLineBox* mLineBox;
PRInt32 mTotalPlacedFrames;
ArenaDeque *mWordFrames;
PRBool AllocateDeque();
nscoord mTopEdge;
nscoord mMaxTopBoxHeight;
@@ -395,7 +497,6 @@ protected:
#define PFD_ISNONEMPTYTEXTFRAME 0x00000004
#define PFD_ISNONWHITESPACETEXTFRAME 0x00000008
#define PFD_ISLETTERFRAME 0x00000010
#define PFD_ISSTICKY 0x00000020
#define PFD_ISBULLET 0x00000040
#define PFD_SKIPWHENTRIMMINGWHITESPACE 0x00000080
#define PFD_LASTFLAG PFD_SKIPWHENTRIMMINGWHITESPACE
@@ -497,6 +598,7 @@ protected:
PRBool CanPlaceFrame(PerFrameData* pfd,
const nsHTMLReflowState& aReflowState,
PRBool aNotSafeToBreak,
PRBool aFrameCanContinueTextRun,
nsHTMLReflowMetrics& aMetrics,
nsReflowStatus& aStatus);

View File

@@ -105,6 +105,18 @@ nsPlaceholderFrame::GetType() const
return nsLayoutAtoms::placeholderFrame;
}
NS_IMETHODIMP
nsPlaceholderFrame::CanContinueTextRun(PRBool& aContinueTextRun) const
{
if (!mOutOfFlowFrame) {
aContinueTextRun = PR_FALSE;
return NS_OK;
}
// first-letter frames can continue text runs, and placeholders for floated
// first-letter frames can too
return mOutOfFlowFrame->CanContinueTextRun(aContinueTextRun);
}
#ifdef DEBUG
static void
PaintDebugPlaceholder(nsIFrame* aFrame, nsIRenderingContext* aCtx,

View File

@@ -99,6 +99,8 @@ public:
virtual PRBool IsEmpty() { return PR_TRUE; }
virtual PRBool IsSelfEmpty() { return PR_TRUE; }
NS_IMETHOD CanContinueTextRun(PRBool& aContinueTextRun) const;
#ifdef ACCESSIBILITY
NS_IMETHOD GetAccessible(nsIAccessible** aAccessible)
{

View File

@@ -540,61 +540,13 @@ public:
nsTextPaintStyle& aStyle,
nscoord dx, nscoord dy);
/**
* ComputeTotalWordDimensions and ComputeWordFragmentDimensions work
* together to measure a text that spans multiple frames, e.g., as in
* "baseText<b>moreText<i>moreDeepText</i></b>moreAlsoHere"
* where the total text shoudn't be broken (or the joined pieces should be
* passed to the linebreaker for examination, especially in i18n cases).
*
* ComputeTotalWordDimensions will loop over ComputeWordFragmentDimensions
* to look-ahead and accumulate the joining fragments.
*
* @param aNextFrame is the first textFrame after the baseText's textFrame.
*
* @param aBaseDimensions is the dimension of baseText.
*
* @param aCanBreakBefore is false when it is not possible to break before
* the baseText (e.g., when this is the first word on the line).
*/
nsTextDimensions ComputeTotalWordDimensions(nsPresContext* aPresContext,
nsLineLayout& aLineLayout,
const nsHTMLReflowState& aReflowState,
nsIFrame* aNextFrame,
const nsTextDimensions& aBaseDimensions,
PRUnichar* aWordBuf,
PRUint32 aWordBufLen,
PRUint32 aWordBufSize,
PRBool aCanBreakBefore);
/**
* @param aNextFrame is the textFrame following the current fragment.
*
* @param aMoreSize plays a double role. The process should continue
* normally when it is zero. But when it returns -1, it means that there is
* no more fragment of interest and the look-ahead should be stopped. When
* it returns a positive value, it means that the current buffer (aWordBuf
* of size aWordBufSize) is not big enough to accumulate the current fragment.
* The returned positive value is the shortfall.
*
* @param aWordBufLen is the accumulated length of the fragments that have
* been accounted for so far.
*/
nsTextDimensions ComputeWordFragmentDimensions(nsPresContext* aPresContext,
nsLineLayout& aLineLayout,
const nsHTMLReflowState& aReflowState,
nsIFrame* aNextFrame,
nsIContent* aContent,
PRInt32* aMoreSize,
const PRUnichar* aWordBuf,
PRUint32 &aWordBufLen,
PRUint32 aWordBufSize,
PRBool aCanBreakBefore);
#ifdef DEBUG
void ToCString(nsString& aBuf, PRInt32* aTotalContentLength) const;
#endif
PRInt32 GetContentOffset() { return mContentOffset; }
PRInt32 GetContentLength() { return mContentLength; }
protected:
virtual ~nsTextFrame();
@@ -4907,21 +4859,6 @@ struct TextRun {
}
};
// Transforms characters in place from ascii to Unicode
static void
TransformTextToUnicode(char* aText, PRInt32 aNumChars)
{
// Go backwards over the characters and convert them.
unsigned char* cp1 = (unsigned char*)aText + aNumChars - 1;
PRUnichar* cp2 = (PRUnichar*)aText + (aNumChars - 1);
while (aNumChars-- > 0) {
// XXX: If you crash here then you may see the issue described
// in http://bugzilla.mozilla.org/show_bug.cgi?id=36146#c44
*cp2-- = PRUnichar(*cp1--);
}
}
PRUint32
nsTextFrame::EstimateNumChars(PRUint32 aAvailableWidth,
PRUint32 aAverageCharWidth)
@@ -4951,6 +4888,85 @@ DEFINE_CCMAP(gPuncCharsCCMap, const);
#define IsPunctuationMark(ch) (CCMAP_HAS_CHAR(gPuncCharsCCMap, ch))
static PRBool CanBreakBetween(nsTextFrame* aBefore,
PRBool aBreakWhitespaceBefore,
nsTextFrame* aAfter,
PRBool aBreakWhitespaceAfter,
PRBool aSkipLeadingWhitespaceAfter,
nsIFrame* aLineContainer)
{
// This assumes text transforms don't change text breaking properties
const nsTextFragment* fragBefore = aBefore->GetContent()->GetText();
const nsTextFragment* fragAfter = aAfter->GetContent()->GetText();
NS_ASSERTION(fragBefore && fragAfter, "text frames with no text!");
// The end of the before-content
PRInt32 beforeOffset = aBefore->GetContentOffset() + aBefore->GetContentLength();
// The start of the after-content
PRInt32 afterOffset = aAfter->GetContentOffset();
PRInt32 afterLength = fragAfter->GetLength() - afterOffset;
if (beforeOffset <= 0 || afterLength <= 0) {
// This shouldn't really happen, text frames shouldn't map no content
NS_WARNING("Textframe maps no content");
return PR_FALSE;
}
PRUnichar lastBefore = fragBefore->CharAt(beforeOffset - 1);
PRUnichar firstAfter = fragAfter->CharAt(afterOffset);
// If we're skipping leading whitespace in the after-frame, and we actually
// have leading whitespace in the after-frame, then we can't break before
// it. We will rely on a saved break opportunity from the end of the last frame
// (if any). The problem is that we can't accurately figure out here whether
// a break is allowed.
if (aSkipLeadingWhitespaceAfter && XP_IS_SPACE(firstAfter))
return PR_FALSE;
while (IS_DISCARDED(firstAfter)) {
++afterOffset;
--afterLength;
if (afterLength == 0) {
// aAfter will be entirely skipped. No breaking allowed here.
return PR_FALSE;
}
firstAfter = fragAfter->CharAt(afterOffset);
}
while (IS_DISCARDED(lastBefore)) {
NS_ASSERTION(beforeOffset > 0,
"Before-textframe maps no content, should not have called SetTrailingTextFrame");
--beforeOffset;
lastBefore = fragBefore->CharAt(beforeOffset - 1);
}
// If the character before or after the boundary is a breakable whitespace
// character that's not skipped, we're good to break.
if ((XP_IS_SPACE(lastBefore) && aBreakWhitespaceBefore) ||
(XP_IS_SPACE(firstAfter) && aBreakWhitespaceAfter))
return PR_TRUE;
// See nsJISx4051LineBreaker::BreakInBetween ... characters 0-255 don't
// trigger complex line breaking behaviour. This is an approximation since
// currently nsJISx4051LineBreaker can look far along a line, but it'll do
// until the line breaker is sorted out.
if (!fragBefore->Is2b() && !fragAfter->Is2b())
return PR_FALSE;
nsIFrame* f =
nsLayoutUtils::GetClosestCommonAncestorViaPlaceholders(aBefore, aAfter, aLineContainer);
NS_ASSERTION(f, "Frames to check not in the same document???");
// Check if our nearest common ancestor allows wrapping between its children
if (f->GetStyleText()->WhiteSpaceCanWrap())
return PR_FALSE;
nsAutoString beforeStr;
nsAutoString afterStr;
fragBefore->AppendTo(beforeStr, 0, beforeOffset);
fragAfter->AppendTo(afterStr, afterOffset, afterLength);
return nsContentUtils::LineBreaker()->BreakInBetween(
beforeStr.get(), beforeStr.Length(), afterStr.get(), afterStr.Length());
}
nsReflowStatus
nsTextFrame::MeasureText(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
@@ -4963,9 +4979,7 @@ nsTextFrame::MeasureText(nsPresContext* aPresContext,
nsLineLayout& lineLayout = *aReflowState.mLineLayout;
PRInt32 contentLength = aTx.GetContentLength();
PRInt32 startingOffset = aTextData.mOffset;
PRInt32 prevOffset = -1;
PRInt32 column = mColumn;
PRInt32 prevColumn = column;
nscoord prevMaxWordWidth = 0, prevAscent = 0, prevDescent = 0;
PRInt32 lastWordLen = 0;
PRUnichar* lastWordPtr = nsnull;
@@ -4974,6 +4988,21 @@ nsTextFrame::MeasureText(nsPresContext* aPresContext,
PRBool justDidFirstLetter = PR_FALSE;
nsTextDimensions dimensions, lastWordDimensions;
PRBool measureTextRuns = PR_FALSE;
nscoord lastWordWidth = NS_UNCONSTRAINEDSIZE;
// Check whether we can break between the last text frame (if any) and this one
PRBool trailingTextFrameCanWrap;
nsIFrame* lastTextFrame = lineLayout.GetTrailingTextFrame(&trailingTextFrameCanWrap);
PRBool canBreakBetweenTextFrames = PR_FALSE;
if (lastTextFrame) {
NS_ASSERTION(lastTextFrame->GetType() == nsGkAtoms::textFrame,
"Trailing text frame isn't text!");
canBreakBetweenTextFrames =
CanBreakBetween(NS_STATIC_CAST(nsTextFrame*, lastTextFrame),
trailingTextFrameCanWrap,
this, aTextData.mWrapping, aTextData.mSkipWhitespace,
lineLayout.GetLineContainerFrame());
}
if (contentLength == 0) {
aTextData.mX = 0;
@@ -5108,7 +5137,6 @@ nsTextFrame::MeasureText(nsPresContext* aPresContext,
if ('\n' == firstChar) {
// We hit a newline. Stop looping.
NS_ASSERTION(aTs.mPreformatted, "newline w/o ts.mPreformatted");
prevOffset = aTextData.mOffset;
aTextData.mOffset++;
endsInWhitespace = PR_TRUE;
endsInNewline = PR_TRUE;
@@ -5159,10 +5187,8 @@ nsTextFrame::MeasureText(nsPresContext* aPresContext,
//Even if there is not enough space for this "space", we still put it
//here instead of next line
prevColumn = column;
column += wordLen;
endsInWhitespace = PR_TRUE;
prevOffset = aTextData.mOffset;
aTextData.mOffset += contentLen;
if (aTextData.mMeasureText) {
@@ -5193,7 +5219,9 @@ nsTextFrame::MeasureText(nsPresContext* aPresContext,
} //(aTextData.mMeasureText)
}
else {
PRBool currentWordIsFirstThing = firstThing;
firstThing = PR_FALSE;
aTextData.mSkipWhitespace = PR_FALSE;
if (aTextData.mFirstLetterOK) {
@@ -5269,20 +5297,47 @@ nsTextFrame::MeasureText(nsPresContext* aPresContext,
}
}
}
lastWordDimensions = dimensions;
PRBool canBreak;
if (0 == aTextData.mX) {
canBreak = canBreakBetweenTextFrames;
// Allow breaking between text frames even if mWrapping is false
// (e.g., we're white-space:pre). If canBreakBetweenTextFrames is
// true, then the previous text frame's mWrapping must have been
// true, and we allow breaking between text frames if at least
// one of them allows breaking.
} else {
canBreak = aTextData.mWrapping;
}
if (canBreak) {
// Remember that we *could* have broken here, even if we choose not to
PRBool forceBreak =
lineLayout.NotifyOptionalBreakPosition(GetContent(), aTextData.mOffset);
// See if there is room for the text
if ((0 != aTextData.mX) && aTextData.mWrapping && (aTextData.mX + dimensions.width > maxWidth)) {
// The text will not fit.
if (forceBreak || (aTextData.mX + dimensions.width > maxWidth)) {
// The text will not fit, or a break was forced.
break;
}
}
prevMaxWordWidth = aTextData.mMaxWordWidth;
prevAscent = aTextData.mAscent;
prevDescent = aTextData.mDescent;
aTextData.mX += dimensions.width;
if (dimensions.width > aTextData.mMaxWordWidth) {
aTextData.mMaxWordWidth = dimensions.width;
if (aTextData.mComputeMaxWordWidth) {
lastWordWidth = dimensions.width;
if (currentWordIsFirstThing) {
nscoord incomingWordWidth;
if (lineLayout.InWord(&incomingWordWidth)) {
lastWordWidth += incomingWordWidth;
}
}
if (lastWordWidth > aTextData.mMaxWordWidth) {
aTextData.mMaxWordWidth = lastWordWidth;
}
}
if (aTextData.mAscent < dimensions.ascent) {
aTextData.mAscent = dimensions.ascent;
@@ -5291,10 +5346,8 @@ nsTextFrame::MeasureText(nsPresContext* aPresContext,
aTextData.mDescent = dimensions.descent;
}
prevColumn = column;
column += wordLen;
endsInWhitespace = PR_FALSE;
prevOffset = aTextData.mOffset;
aTextData.mOffset += contentLen;
if (justDidFirstLetter) {
// Time to stop
@@ -5303,11 +5356,25 @@ nsTextFrame::MeasureText(nsPresContext* aPresContext,
}
}
else {
// Remember that we *could* have broken before this chunk of text.
PRBool canBreak;
if (aTextData.mOffset == startingOffset) {
canBreak = canBreakBetweenTextFrames;
} else {
canBreak = aTextData.mWrapping;
}
if (canBreak) {
#ifdef DEBUG
PRBool forceBreak =
#endif
lineLayout.NotifyOptionalBreakPosition(GetContent(), aTextData.mOffset);
NS_ASSERTION(!forceBreak, "If we're supposed to break, we should be "
"really measuring");
}
// We didn't measure the text, but we need to update our state
prevColumn = column;
column += wordLen;
endsInWhitespace = PR_FALSE;
prevOffset = aTextData.mOffset;
aTextData.mOffset += contentLen;
if (justDidFirstLetter) {
// Time to stop
@@ -5322,20 +5389,43 @@ nsTextFrame::MeasureText(nsPresContext* aPresContext,
// see if we have implementation for GetTextDimensions()
if (hints & NS_RENDERING_HINT_FAST_MEASURE) {
PRInt32 numCharsFit;
PRInt32 forcedOffset = lineLayout.GetForcedBreakPosition(GetContent());
PRInt32 measureChars = textRun.mTotalNumChars;
if (forcedOffset == -1) {
forcedOffset -= aTextData.mOffset;
NS_ASSERTION(forcedOffset >= 0,
"Overshot forced offset, we should have already exited");
if (forcedOffset >= 0 && forcedOffset < textRun.mTotalNumChars) {
// Only measure up to forcedOffset characters. We still need to measure
// to make sure we get the right text dimensions, even though we know
// where we're going to break. This will reduce the number of chars that
// fit, which we then detect as a required break.
measureChars = forcedOffset;
}
}
// These calls can return numCharsFit not positioned at a break in the textRun. Beware.
if (aTx.TransformedTextIsAscii()) {
aReflowState.rendContext->GetTextDimensions((char*)aTx.GetWordBuffer(), textRun.mTotalNumChars,
aReflowState.rendContext->GetTextDimensions((char*)aTx.GetWordBuffer(), measureChars,
maxWidth - aTextData.mX,
textRun.mBreaks, textRun.mNumSegments,
dimensions, numCharsFit, lastWordDimensions);
} else {
aReflowState.rendContext->GetTextDimensions(aTx.GetWordBuffer(), textRun.mTotalNumChars,
aReflowState.rendContext->GetTextDimensions(aTx.GetWordBuffer(), measureChars,
maxWidth - aTextData.mX,
textRun.mBreaks, textRun.mNumSegments,
dimensions, numCharsFit, lastWordDimensions);
}
// See how much of the text fit
if ((0 != aTextData.mX) && aTextData.mWrapping && (aTextData.mX + dimensions.width > maxWidth)) {
PRBool canBreak;
if (0 == aTextData.mX) {
canBreak = canBreakBetweenTextFrames;
} else {
canBreak = aTextData.mWrapping;
}
if (canBreak && aTextData.mX + dimensions.width > maxWidth) {
// None of the text fits
#ifdef IBMBIDI
nextBidi = nsnull;
@@ -5368,17 +5458,7 @@ nsTextFrame::MeasureText(nsPresContext* aPresContext,
if (lastSegment < 0) {
// no segments fit
break;
} else */
if (lastSegment == 0) {
// Only one segment fit
prevColumn = column;
prevOffset = aTextData.mOffset;
} else {
// The previous state is for the next to last word
// NOTE: The textRun data are relative to the last updated column and offset!
prevColumn = column + textRun.mBreaks[lastSegment - 1];
prevOffset = aTextData.mOffset + textRun.mSegments[lastSegment - 1].ContentLen();
}
} */
aTextData.mX += dimensions.width;
if (aTextData.mAscent < dimensions.ascent) {
@@ -5398,9 +5478,21 @@ nsTextFrame::MeasureText(nsPresContext* aPresContext,
aTextData.mDescent = lastWordDimensions.descent;
}
endsInWhitespace = textRun.mSegments[lastSegment].IsWhitespace();
// Save last possible backup position. Whitespace segments are always
// breakable since nsTextTransformer reports preformatted spaces as
// "not whitespace". Note that each segment is either entirely whitespace
// or entirely non-whitespace.
PRInt32 lastWhitespaceSegment =
endsInWhitespace ? lastSegment : lastSegment - 1;
if (lastWhitespaceSegment >= 0) {
lineLayout.NotifyOptionalBreakPosition(GetContent(),
aTextData.mOffset + textRun.mSegments[lastWhitespaceSegment].ContentLen());
}
column += numCharsFit;
aTextData.mOffset += textRun.mSegments[lastSegment].ContentLen();
endsInWhitespace = textRun.mSegments[lastSegment].IsWhitespace();
// If all the text didn't fit, then we're done
if (numCharsFit != textRun.mTotalNumChars) {
@@ -5430,8 +5522,6 @@ nsTextFrame::MeasureText(nsPresContext* aPresContext,
estimatedNumChars = EstimateNumChars(maxWidth - aTextData.mX,
aTs.mAveCharWidth);
}
#else /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) */
int unused = -1;
#endif /* defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) */
}
@@ -5448,160 +5538,24 @@ nsTextFrame::MeasureText(nsPresContext* aPresContext,
}
}
// Post processing logic to deal with word-breaking that spans
// multiple frames.
if (lineLayout.InWord()) {
// We are already in a word. This means a text frame prior to this
// one had a fragment of a nbword that is joined with this
// frame. It also means that the prior frame already found this
// frame and recorded it as part of the word.
#ifdef DEBUG_WORD_WRAPPING
ListTag(stdout);
printf(": in word; skipping\n");
#endif
lineLayout.ForgetWordFrame(this);
}
if (!lineLayout.InWord()) {
// There is no currently active word. This frame may contain the
// start of one.
if (endsInWhitespace) {
// Nope, this frame doesn't start a word.
lineLayout.ForgetWordFrames();
}
else if ((aTextData.mOffset == contentLength) && (prevOffset >= 0)) {
// Force breakable to false when we aren't wrapping (this
// guarantees that the combined word will stay together)
if (!aTextData.mWrapping) {
aTextData.mCanBreakBefore = PR_FALSE;
}
// This frame does start a word. However, there is no point
// messing around with it if we are already out of room. We
// always have room if we are not breakable.
if (!aTextData.mCanBreakBefore || (aTextData.mX <= maxWidth)) {
// There is room for this word fragment. It's possible that
// this word fragment is the end of the text-run. If it's not
// then we continue with the look-ahead processing.
nsIFrame* next = lineLayout.FindNextText(aPresContext, this);
if (nsnull != next) {
#ifdef DEBUG_WORD_WRAPPING
nsAutoString tmp(aTx.GetWordBuffer(), lastWordLen);
ListTag(stdout);
printf(": start='");
fputs(NS_LossyConvertUTF16toASCII(tmp).get(), stdout);
printf("' lastWordLen=%d baseWidth=%d prevOffset=%d offset=%d next=",
lastWordLen, lastWordDimensions.width, prevOffset, aTextData.mOffset);
ListTag(stdout, next);
printf("\n");
#endif
PRUnichar* pWordBuf = lastWordPtr;
PRUint32 wordBufLen = aTx.GetWordBufferLength() -
(lastWordPtr - aTx.GetWordBuffer());
if (aTx.TransformedTextIsAscii()) {
// The text transform buffer contains ascii characters, so
// transform it to Unicode
NS_ASSERTION(wordBufLen >= PRUint32(lastWordLen), "no room to transform in place");
TransformTextToUnicode((char*)lastWordPtr, lastWordLen);
}
// Look ahead in the text-run and compute the final word
// width, taking into account any style changes and stopping
// at the first breakable point.
if (!aTextData.mMeasureText || (lastWordDimensions.width == -1)) {
// We either didn't measure any text or we measured multiple words
// at once so either way we don't know lastWordDimensions. We'll have to
// compute it now
if (prevOffset == startingOffset) {
// There's only one word, so we don't have to measure after all
lastWordDimensions.width = aTextData.mX;
}
else if (aTs.mSmallCaps) {
MeasureSmallCapsText(aReflowState, aTs, pWordBuf,
lastWordLen, PR_FALSE, &lastWordDimensions);
}
else {
aReflowState.rendContext->GetTextDimensions(pWordBuf, lastWordLen, lastWordDimensions);
if (aTs.mLetterSpacing) {
lastWordDimensions.width += aTs.mLetterSpacing * lastWordLen;
}
if (aTs.mWordSpacing) {
for (PRUnichar* bp = pWordBuf;
bp < pWordBuf + lastWordLen; bp++) {
if (*bp == ' ') // || *bp == CH_CJKSP)
lastWordDimensions.width += aTs.mWordSpacing;
}
}
}
}
nsTextDimensions wordDimensions = ComputeTotalWordDimensions(aPresContext,
lineLayout,
aReflowState, next,
lastWordDimensions,
pWordBuf,
lastWordLen,
wordBufLen,
aTextData.mCanBreakBefore);
if (!aTextData.mCanBreakBefore || (aTextData.mX - lastWordDimensions.width + wordDimensions.width <= maxWidth)) {
// The fully joined word has fit. Account for the joined
// word's affect on the max-element-size here (since the
// joined word is large than it's pieces, the right effect
// will occur from the perspective of the container
// reflowing this frame)
if (wordDimensions.width > aTextData.mMaxWordWidth) {
aTextData.mMaxWordWidth = wordDimensions.width;
}
// Now that we now that we will retain the last word, we should
// account for its ascent and descent
if (aTextData.mAscent < lastWordDimensions.ascent) {
aTextData.mAscent = lastWordDimensions.ascent;
}
if (aTextData.mDescent < lastWordDimensions.descent) {
aTextData.mDescent = lastWordDimensions.descent;
}
}
else {
#ifdef NOISY_REFLOW
ListTag(stdout);
printf(": look-ahead (didn't fit) x=%d wordWidth=%d lastWordWidth=%d\n",
aTextData.mX, wordDimensions.width, lastWordDimensions.width);
#endif
// The fully joined word won't fit. We need to reduce our
// size by lastWordDimensions
aTextData.mX -= lastWordDimensions.width;
aTextData.mMaxWordWidth = prevMaxWordWidth;
aTextData.mOffset = prevOffset;
column = prevColumn;
if (aTextData.mMeasureText) {
aTextData.mAscent = prevAscent;
aTextData.mDescent = prevDescent;
}
// else {
// XXX we didn't measure the text, and so we don't know where to back up,
// we will retain our current height. However, there is a possible
// edge case that is not handled: since we just chopped the last word,
// our remaining text could have got shorter.
// }
#ifdef DEBUG_WORD_WRAPPING
printf(" x=%d maxWordWidth=%d len=%d\n", aTextData.mX, aTextData.mMaxWordWidth,
aTextData.mOffset - startingOffset);
#endif
lineLayout.ForgetWordFrames();
}
}
}
}
}
// Inform line layout of how this piece of text ends in whitespace
// (only text objects do this). Note that if x is zero then this
// text object collapsed into nothingness which means it shouldn't
// effect the current setting of the ends-in-whitespace flag.
// effect the current setting of the ends-in-whitespace flag, nor should it
// be setting InWord state, and it should be ignored when subsequent text
// considers whether it can break-before.
lineLayout.SetColumn(column);
lineLayout.SetUnderstandsWhiteSpace(PR_TRUE);
if (0 != aTextData.mX) {
lineLayout.SetTrailingTextFrame(this, aTextData.mWrapping);
lineLayout.SetEndsInWhiteSpace(endsInWhitespace);
lineLayout.SetInWord(!endsInWhitespace, lastWordWidth);
} else {
// Don't allow subsequent text frame to break-before. All our text is
// being skipped (usually whitespace, could be discarded Unicode control
// characters).
lineLayout.SetTrailingTextFrame(nsnull, PR_FALSE);
lineLayout.SetInWord(PR_FALSE, 0);
}
if (justDidFirstLetter) {
lineLayout.SetFirstLetterFrame(this);
@@ -5616,21 +5570,35 @@ nsTextFrame::MeasureText(nsPresContext* aPresContext,
#endif // IBMBIDI
? NS_FRAME_COMPLETE
: NS_FRAME_NOT_COMPLETE;
if (endsInNewline) {
rs = NS_INLINE_LINE_BREAK_AFTER(rs);
lineLayout.SetLineEndsInBR(PR_TRUE);
if (canBreakBetweenTextFrames && aTextData.mOffset == startingOffset) {
// Couldn't place any text but we can break between text frames, so do that.
return NS_INLINE_LINE_BREAK_BEFORE();
}
else if (aTextData.mTrailingSpaceTrimmed && rs == NS_FRAME_COMPLETE) {
if (endsInNewline) {
lineLayout.SetLineEndsInBR(PR_TRUE);
return NS_INLINE_LINE_BREAK_AFTER(rs);
}
if (aTextData.mTrailingSpaceTrimmed && rs == NS_FRAME_COMPLETE) {
// Flag a soft-break that we can check (below) if we come back here
lineLayout.SetLineEndsInSoftBR(PR_TRUE);
}
else if (lineLayout.GetLineEndsInSoftBR() && !lineLayout.GetEndsInWhiteSpace()) {
} else if (lineLayout.GetLineEndsInSoftBR() && !lineLayout.GetEndsInWhiteSpace()) {
// Break-before a word that follows the soft-break flagged earlier
rs = NS_INLINE_LINE_BREAK_BEFORE();
return NS_INLINE_LINE_BREAK_BEFORE();
}
else if ((aTextData.mOffset != contentLength) && (aTextData.mOffset == startingOffset)) {
if (rs == NS_FRAME_COMPLETE && 0 != aTextData.mX && endsInWhitespace &&
aTextData.mWrapping && aTextData.mX <= maxWidth) {
// Remember the break opportunity at the end of this frame
if (lineLayout.NotifyOptionalBreakPosition(GetContent(), aTextData.mOffset))
return NS_INLINE_LINE_BREAK_AFTER(rs);
}
if ((aTextData.mOffset != contentLength) && (aTextData.mOffset == startingOffset)) {
// Break-before a long-word that doesn't fit here
rs = NS_INLINE_LINE_BREAK_BEFORE();
return NS_INLINE_LINE_BREAK_BEFORE();
}
return rs;
@@ -5722,8 +5690,7 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
}
}
PRBool wrapping = (NS_STYLE_WHITESPACE_NORMAL == ts.mText->mWhiteSpace) ||
(NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == ts.mText->mWhiteSpace);
PRBool wrapping = ts.mText->WhiteSpaceCanWrap();
// Set whitespace skip flag
PRBool skipWhitespace = PR_FALSE;
@@ -5757,7 +5724,8 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
// Set inWord to true if we are part of a previous piece of text's word. This
// is only valid for one pass through the measuring loop.
PRBool inWord = lineLayout.InWord() || ((nsnull != prevInFlow) && (NS_STATIC_CAST(nsTextFrame*, prevInFlow)->mState & TEXT_FIRST_LETTER));
nscoord currentWordWidth;
PRBool inWord = lineLayout.InWord(&currentWordWidth);
if (inWord) {
mState |= TEXT_IN_WORD;
}
@@ -5793,6 +5761,7 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
if (!GetNextInFlow() &&
(mState & TEXT_OPTIMIZE_RESIZE) &&
!aMetrics.mComputeMEW &&
lineLayout.GetForcedBreakPosition(GetContent()) == -1 &&
(lastTimeWeSkippedLeadingWS == skipWhitespace) &&
((wrapping && (maxWidth >= realWidth)) ||
(!wrapping && (prevColumn == column))) &&
@@ -6032,259 +6001,6 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
return NS_OK;
}
static void
RevertSpacesToNBSP(PRUnichar* aBuffer, PRInt32 aWordLen)
{
PRUnichar* end = aBuffer + aWordLen;
for (; aBuffer < end; aBuffer++) {
PRUnichar ch = *aBuffer;
if (ch == ' ') {
*aBuffer = CH_NBSP;
}
}
}
nsTextDimensions
nsTextFrame::ComputeTotalWordDimensions(nsPresContext* aPresContext,
nsLineLayout& aLineLayout,
const nsHTMLReflowState& aReflowState,
nsIFrame* aNextFrame,
const nsTextDimensions& aBaseDimensions,
PRUnichar* aWordBuf,
PRUint32 aWordLen,
PRUint32 aWordBufSize,
PRBool aCanBreakBefore)
{
// Before we get going, convert any spaces in the current word back
// to nbsp's. This keeps the breaking logic happy.
RevertSpacesToNBSP(aWordBuf, (PRInt32) aWordLen);
nsTextDimensions addedDimensions;
PRUnichar *newWordBuf = aWordBuf;
PRUint32 newWordBufSize = aWordBufSize;
while (aNextFrame) {
nsIContent* content = aNextFrame->GetContent();
#ifdef DEBUG_WORD_WRAPPING
printf(" next textRun=");
nsFrame::ListTag(stdout, aNextFrame);
printf("\n");
#endif
if (content->IsNodeOfType(nsINode::eTEXT)) {
PRInt32 moreSize = 0;
nsTextDimensions moreDimensions;
moreDimensions = ComputeWordFragmentDimensions(aPresContext,
aLineLayout,
aReflowState,
aNextFrame, content,
&moreSize,
newWordBuf,
aWordLen,
newWordBufSize,
aCanBreakBefore);
if (moreSize > 0) {
//Oh, wordBuf is too small, we have to grow it
newWordBufSize += moreSize;
if (newWordBuf != aWordBuf) {
newWordBuf = (PRUnichar*)nsMemory::Realloc(newWordBuf, sizeof(PRUnichar)*newWordBufSize);
NS_ASSERTION(newWordBuf, "not enough memory");
} else {
newWordBuf = (PRUnichar*)nsMemory::Alloc(sizeof(PRUnichar)*newWordBufSize);
NS_ASSERTION(newWordBuf, "not enough memory");
if(newWordBuf) {
memcpy((void*)newWordBuf, aWordBuf, sizeof(PRUnichar)*(newWordBufSize-moreSize));
}
}
if(newWordBuf) {
moreDimensions =
ComputeWordFragmentDimensions(aPresContext,
aLineLayout, aReflowState,
aNextFrame, content, &moreSize,
newWordBuf, aWordLen, newWordBufSize,
aCanBreakBefore);
NS_ASSERTION((moreSize <= 0),
"ComputeWordFragmentDimensions is asking more buffer");
} else {
moreSize = -1;
moreDimensions.Clear();
}
}
addedDimensions.Combine(moreDimensions);
#ifdef DEBUG_WORD_WRAPPING
printf(" moreWidth=%d (addedWidth=%d) stop=%c\n", moreDimensions.width,
addedDimensions.width, stop?'T':'F');
#endif
if (moreSize == -1) {
goto done;
}
}
else {
// It claimed it was text but it doesn't contain a textfragment.
// Therefore I don't know what to do with it and can't look inside
// it. Oh well.
goto done;
}
// Move on to the next frame in the text-run
aNextFrame = aLineLayout.FindNextText(aPresContext, aNextFrame);
}
done:;
#ifdef DEBUG_WORD_WRAPPING
printf(" total word width=%d\n", aBaseDimensions.width + addedDimensions.width);
#endif
if (newWordBuf && (newWordBuf != aWordBuf)) {
nsMemory::Free(newWordBuf);
}
addedDimensions.Combine(aBaseDimensions);
return addedDimensions;
}
nsTextDimensions
nsTextFrame::ComputeWordFragmentDimensions(nsPresContext* aPresContext,
nsLineLayout& aLineLayout,
const nsHTMLReflowState& aReflowState,
nsIFrame* aNextFrame,
nsIContent* aContent,
PRInt32* aMoreSize,
const PRUnichar* aWordBuf,
PRUint32& aRunningWordLen,
PRUint32 aWordBufSize,
PRBool aCanBreakBefore)
{
nsTextTransformer tx(aPresContext);
PRInt32 nextFrameStart, nextFrameEnd;
aNextFrame->GetOffsets(nextFrameStart, nextFrameEnd);
tx.Init(aNextFrame, aContent, nextFrameStart);
if (nextFrameEnd == 0) // uninitialized
nextFrameEnd = tx.GetContentLength();
PRBool isWhitespace, wasTransformed;
PRInt32 wordLen, contentLen;
nsTextDimensions dimensions;
#ifdef IBMBIDI
if (aNextFrame->GetStateBits() & NS_FRAME_IS_BIDI) {
wordLen = nextFrameEnd;
} else {
wordLen = -1;
}
#endif // IBMBIDI
*aMoreSize = 0;
PRUnichar* bp = tx.GetNextWord(PR_TRUE, &wordLen, &contentLen, &isWhitespace, &wasTransformed);
if (!bp) {
//empty text node, but we need to continue lookahead measurement
// AND we need to remember the text frame for later so that we don't
// bother doing the word look ahead.
aLineLayout.RecordWordFrame(aNextFrame);
return dimensions; // 0
}
if (isWhitespace) {
// Don't bother measuring nothing
*aMoreSize = -1; // flag that we should stop now
return dimensions; // 0
}
// We need to adjust the length by looking at the two pieces together. But if
// we have to grow aWordBuf, ask the caller to do it by returning the shortfall
if ((wordLen + aRunningWordLen) > aWordBufSize) {
*aMoreSize = wordLen + aRunningWordLen - aWordBufSize;
return dimensions; // 0
}
if (nextFrameStart + contentLen < nextFrameEnd)
*aMoreSize = -1;
// Convert any spaces in the current word back to nbsp's. This keeps
// the breaking logic happy.
RevertSpacesToNBSP(bp, wordLen);
if (aCanBreakBefore) {
if(wordLen > 0)
{
memcpy((void*)&(aWordBuf[aRunningWordLen]), bp, sizeof(PRUnichar)*wordLen);
PRInt32 breakP=0;
breakP = nsContentUtils::LineBreaker()->Next(aWordBuf,
aRunningWordLen+wordLen, 0);
// when we look at two pieces text together, we might decide to break
// eariler than if we only look at the 2nd pieces of text
if (breakP != NS_LINEBREAKER_NEED_MORE_TEXT &&
(breakP < (aRunningWordLen + wordLen)))
{
wordLen = breakP - aRunningWordLen;
if(wordLen < 0)
wordLen = 0;
*aMoreSize = -1;
}
// if we don't stop, we need to extend the buf so the next one can
// see this part otherwise, it does not matter since we will stop
// anyway
if (*aMoreSize != -1)
aRunningWordLen += wordLen;
}
}
else {
// Even if the previous text fragment is not breakable, the connected pieces
// can be breakable in between. This especially true for CJK.
PRBool canBreak;
canBreak = nsContentUtils::LineBreaker()->BreakInBetween(aWordBuf,
aRunningWordLen, bp, wordLen);
if (canBreak) {
wordLen = 0;
*aMoreSize = -1;
}
}
if ((*aMoreSize == -1) && (wordLen == 0))
return dimensions; // 0;
nsStyleContext* sc = aNextFrame->GetStyleContext();
if (sc) {
// Measure the piece of text. Note that we have to select the
// appropriate font into the text first because the rendering
// context has our font in it, not the font that aText is using.
nsIRenderingContext& rc = *aReflowState.rendContext;
nsCOMPtr<nsIFontMetrics> oldfm;
rc.GetFontMetrics(*getter_AddRefs(oldfm));
nsTextStyle ts(aLineLayout.mPresContext, rc, sc);
if (ts.mSmallCaps) {
MeasureSmallCapsText(aReflowState, ts, bp, wordLen, PR_FALSE, &dimensions);
}
else {
rc.GetTextDimensions(bp, wordLen, dimensions);
// NOTE: Don't forget to add letter spacing for the word fragment!
dimensions.width += wordLen*ts.mLetterSpacing;
if (ts.mWordSpacing) {
for (PRUnichar* bp2 = bp; bp2 < bp + wordLen; bp2++) {
if (*bp2 == CH_NBSP) // || *bp2 == CH_CJKSP)
dimensions.width += ts.mWordSpacing;
}
}
}
rc.SetFont(oldfm);
#ifdef DEBUG_WORD_WRAPPING
nsAutoString tmp(bp, wordLen);
printf(" fragment='");
fputs(NS_LossyConvertUTF16toASCII(tmp).get(), stdout);
printf("' width=%d [wordLen=%d contentLen=%d ContentLength=%d]\n",
dimensions.width, wordLen, contentLen, tx.GetContentLength());
#endif
// Remember the text frame for later so that we don't bother doing
// the word look ahead.
aLineLayout.RecordWordFrame(aNextFrame);
return dimensions;
}
*aMoreSize = -1;
return dimensions; // 0
}
#ifdef DEBUG
// Translate the mapped content into a string that's printable
void

View File

@@ -1005,10 +1005,6 @@ nsMathMLContainerFrame::ReflowForeignChild(nsIFrame* aChildFrame,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
// don't bother trying to span words as if they were non-breaking beyond this point
if (aReflowState.mLineLayout)
aReflowState.mLineLayout->ForgetWordFrames();
nsAutoSpaceManager autoSpaceManager(NS_CONST_CAST(nsHTMLReflowState &, aReflowState));
nsresult rv = autoSpaceManager.CreateSpaceManagerFor(aPresContext, this);
NS_ENSURE_SUCCESS(rv, rv);

View File

@@ -748,6 +748,11 @@ struct nsStyleText : public nsStyleStruct {
return mWhiteSpace == NS_STYLE_WHITESPACE_PRE ||
mWhiteSpace == NS_STYLE_WHITESPACE_MOZ_PRE_WRAP;
}
PRBool WhiteSpaceCanWrap() const {
return mWhiteSpace == NS_STYLE_WHITESPACE_NORMAL ||
mWhiteSpace == NS_STYLE_WHITESPACE_MOZ_PRE_WRAP;
}
};
struct nsStyleVisibility : public nsStyleStruct {