Bug 343445. Change inline reflow strategy to avoid looking ahead through words while measuring text. r+sr=dbaron
This commit is contained in:
@@ -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,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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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(¤tWordWidth);
|
||||
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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user