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
Родитель 4e71dcf716
Коммит 899128e82a
18 изменённых файлов: 716 добавлений и 892 удалений

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

@ -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)
{

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

@ -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

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

@ -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);

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

@ -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;
}

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

@ -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__

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

@ -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,17 +3829,30 @@ 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,
allowPullUp);
lineLayout.EndLineReflow();
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;

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

@ -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,

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

@ -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);

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

@ -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,

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

@ -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);

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

@ -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.

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

@ -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;
@ -313,7 +297,7 @@ nsLineLayout::BeginLineReflow(nscoord aX, nscoord aY,
// If this is the first line of a block then see if the text-indent
// property amounts to anything.
if (0 == mLineNumber && !HasPrevInFlow(mBlockReflowState->frame)) {
nscoord indent = 0;
nsStyleUnit unit = mStyleText->mTextIndent.GetUnit();
@ -936,17 +920,16 @@ 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.
ApplyStartMargin(pfd, reflowState);
@ -991,32 +974,36 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
#endif // IBMBIDI
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" );
return rv;
}
pfd->mJustificationNumSpaces = mTextJustificationNumSpaces;
pfd->mJustificationNumLetters = mTextJustificationNumLetters;
// 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,16 +1171,34 @@ 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 {
PushFrame(aFrame);
}
#ifdef REALLY_NOISY_REFLOW
nsFrame::IndentBy(stdout, mSpanDepth);
printf("End ReflowFrame ");
@ -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,75 +1356,20 @@ 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) {
printf("<psd=%p x=%d left=%d> ", psd, psd->mX, psd->mLeftEdge);
psd = psd->mParent;
}
printf("\n");
printf(" ==> not-safe and not-impacted fits: ");
while (nsnull != psd) {
printf("<psd=%p x=%d left=%d> ", psd, psd->mX, psd->mLeftEdge);
psd = psd->mParent;
}
printf("\n");
#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;
}
}
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) {
// If the span either directly or indirectly contains a float then
@ -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)
{

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

@ -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);

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

@ -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,

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

@ -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)
{

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -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);

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

@ -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 {