Bug 1300293 - Make nsBidiPresUtils track line boxes separately for the progress of TraverseFrames and ResolveParagraph. r=jfkthame

TraverseFrames gathers a bunch of frames into an array, and then at
forced breaks or the end of the block flushes its array by running
ResolveParagraph.  ResolveParagraph can sometimes cause lines to break,
so lines recorded during TraverseFrames may no longer be accurate during
ResolveParagraph.

It may look like the lines are only needed during ResolveParagraph and
we could handle the one issue in TraverseFrames that deals with
preformatted newlines by restructuring the code a tad.  However, in the
next patch, there will be an additional caller that needs the current
line during TraverseFrames to pass to CreateContinuation, which makes
such a structure less plausible.

This will also make it easier to produce a current line for the call to
SplitInlineAncestors within ResolveParagraph, which will be done in the
patch after the next.

Differential Revision: https://phabricator.services.mozilla.com/D38797

--HG--
extra : moz-landing-system : lando
This commit is contained in:
L. David Baron 2019-07-22 18:55:56 +00:00
Родитель d81370ebeb
Коммит 64f57e5d7f
3 изменённых файлов: 174 добавлений и 103 удалений

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

@ -7,6 +7,7 @@
#include "nsBidiPresUtils.h"
#include "mozilla/IntegerRange.h"
#include "mozilla/Maybe.h"
#include "mozilla/PresShell.h"
#include "mozilla/dom/Text.h"
@ -118,6 +119,17 @@ static char16_t GetBidiControl(ComputedStyle* aComputedStyle) {
return 0;
}
#ifdef DEBUG
static inline bool AreContinuationsInOrder(nsIFrame* aFrame1,
nsIFrame* aFrame2) {
nsIFrame* f = aFrame1;
do {
f = f->GetNextContinuation();
} while (f && f != aFrame2);
return !!f;
}
#endif
struct MOZ_STACK_CLASS BidiParagraphData {
struct FrameInfo {
FrameInfo(nsIFrame* aFrame, nsBlockInFlowLineIterator& aLineIter)
@ -146,7 +158,6 @@ struct MOZ_STACK_CLASS BidiParagraphData {
nsAutoString mBuffer;
AutoTArray<char16_t, 16> mEmbeddingStack;
AutoTArray<FrameInfo, 16> mLogicalFrames;
AutoTArray<nsLineBox*, 16> mLinePerFrame;
nsDataHashtable<nsPtrHashKey<const nsIContent>, int32_t> mContentToFrameIndex;
// Cached presentation context for the frames we're processing.
nsPresContext* mPresContext;
@ -154,9 +165,107 @@ struct MOZ_STACK_CLASS BidiParagraphData {
bool mRequiresBidi;
nsBidiLevel mParaLevel;
nsIContent* mPrevContent;
nsIFrame* mPrevFrame;
/**
* This class is designed to manage the process of mapping a frame to
* the line that it's in, when we know that (a) the frames we ask it
* about are always in the block's lines and (b) each successive frame
* we ask it about is the same as or after (in depth-first search
* order) the previous.
*
* Since we move through the lines at a different pace in Traverse and
* ResolveParagraph, we use one of these for each.
*
* The state of the mapping is also different between TraverseFrames
* and ResolveParagraph since since resolving can call functions
* (EnsureBidiContinuation or SplitInlineAncestors) that can create
* new frames and thus break lines.
*
* The TraverseFrames iterator is only used in some edge cases.
*/
struct FastLineIterator {
FastLineIterator() : mPrevFrame(nullptr) {}
// These iterators *and* mPrevFrame track the line list that we're
// iterating over.
//
// mPrevFrame, if non-null, is guaranteed to be either the frame we're
// currently handling in Resolve or a frame before it, and is also
// guaranteed to be in mCurrentLine.
nsBlockInFlowLineIterator mLineIterator;
nsIFrame* mPrevFrame;
nsLineList::iterator GetLine() { return mLineIterator.GetLine(); }
static bool IsFrameInCurrentLine(nsBlockInFlowLineIterator* aLineIter,
nsIFrame* aPrevFrame, nsIFrame* aFrame) {
MOZ_ASSERT(!aPrevFrame || aLineIter->GetLine()->Contains(aPrevFrame),
"aPrevFrame must be in aLineIter's current line");
nsIFrame* endFrame = aLineIter->IsLastLineInList()
? nullptr
: aLineIter->GetLine().next()->mFirstChild;
nsIFrame* startFrame =
aPrevFrame ? aPrevFrame : aLineIter->GetLine()->mFirstChild;
for (nsIFrame* frame = startFrame; frame && frame != endFrame;
frame = frame->GetNextSibling()) {
if (frame == aFrame) return true;
}
return false;
}
// Advance line iterator to the line containing aFrame, assuming
// that aFrame is already in the line list our iterator is iterating
// over.
void AdvanceToFrame(nsIFrame* aFrame) {
nsIFrame* child = aFrame;
nsIFrame* parent = nsLayoutUtils::GetParentOrPlaceholderFor(child);
while (parent && !parent->IsBlockFrameOrSubclass()) {
child = parent;
parent = nsLayoutUtils::GetParentOrPlaceholderFor(child);
}
MOZ_ASSERT(parent, "aFrame is not a descendent of a block frame");
while (!IsFrameInCurrentLine(&mLineIterator, mPrevFrame, child)) {
#ifdef DEBUG
bool hasNext =
#endif
mLineIterator.Next();
MOZ_ASSERT(hasNext, "Can't find frame in lines!");
mPrevFrame = nullptr;
}
mPrevFrame = child;
}
// Advance line iterator to the line containing aFrame, which may
// require moving forward into overflow lines or into a later
// continuation (or both).
void AdvanceToLinesAndFrame(const FrameInfo& aFrameInfo) {
if (mLineIterator.GetContainer() != aFrameInfo.mBlockContainer ||
mLineIterator.GetInOverflow() != aFrameInfo.mInOverflow) {
MOZ_ASSERT(
mLineIterator.GetContainer() == aFrameInfo.mBlockContainer
? (!mLineIterator.GetInOverflow() && aFrameInfo.mInOverflow)
: (!mLineIterator.GetContainer() ||
AreContinuationsInOrder(mLineIterator.GetContainer(),
aFrameInfo.mBlockContainer)),
"must move forwards");
nsBlockFrame* block = aFrameInfo.mBlockContainer;
nsLineList::iterator lines =
aFrameInfo.mInOverflow ? block->GetOverflowLines()->mLines.begin()
: block->LinesBegin();
mLineIterator =
nsBlockInFlowLineIterator(block, lines, aFrameInfo.mInOverflow);
mPrevFrame = nullptr;
}
AdvanceToFrame(aFrameInfo.mFrame);
}
};
FastLineIterator mCurrentTraverseLine, mCurrentResolveLine;
#ifdef DEBUG
// Only used for NOISY debug output.
// Matches the current TraverseFrames state, not the ResolveParagraph
// state.
nsBlockFrame* mCurrentBlock;
#endif
@ -165,8 +274,7 @@ struct MOZ_STACK_CLASS BidiParagraphData {
mIsVisual(mPresContext->IsVisualMode()),
mRequiresBidi(false),
mParaLevel(nsBidiPresUtils::BidiLevelFromStyle(aBlockFrame->Style())),
mPrevContent(nullptr),
mPrevFrame(nullptr)
mPrevContent(nullptr)
#ifdef DEBUG
,
mCurrentBlock(aBlockFrame)
@ -239,30 +347,27 @@ struct MOZ_STACK_CLASS BidiParagraphData {
void ResetData() {
mLogicalFrames.Clear();
mLinePerFrame.Clear();
mContentToFrameIndex.Clear();
mBuffer.SetLength(0);
mPrevContent = nullptr;
for (uint32_t i = 0; i < mEmbeddingStack.Length(); ++i) {
mBuffer.Append(mEmbeddingStack[i]);
mLogicalFrames.AppendElement(FrameInfo(BidiControlFrameType::Value));
mLinePerFrame.AppendElement((nsLineBox*)nullptr);
}
}
void AppendFrame(nsIFrame* aFrame, nsBlockInFlowLineIterator* aLineIter,
void AppendFrame(nsIFrame* aFrame, FastLineIterator& aLineIter,
nsIContent* aContent = nullptr) {
if (aContent) {
mContentToFrameIndex.Put(aContent, FrameCount());
}
AdvanceLineIteratorToFrame(aFrame, aLineIter, mPrevFrame);
mLogicalFrames.AppendElement(FrameInfo(aFrame, aLineIter));
mLinePerFrame.AppendElement(aLineIter->GetLine().get());
// We don't actually need to advance aLineIter to aFrame, since all we use
// from it is the block and is-overflow state, which are correct already.
mLogicalFrames.AppendElement(FrameInfo(aFrame, aLineIter.mLineIterator));
}
void AdvanceAndAppendFrame(nsIFrame** aFrame,
nsBlockInFlowLineIterator* aLineIter,
void AdvanceAndAppendFrame(nsIFrame** aFrame, FastLineIterator& aLineIter,
nsIFrame** aNextSibling) {
nsIFrame* frame = *aFrame;
nsIFrame* nextSibling = *aNextSibling;
@ -300,8 +405,6 @@ struct MOZ_STACK_CLASS BidiParagraphData {
return mLogicalFrames[aIndex];
}
nsLineBox* GetLineForFrameAt(int32_t aIndex) { return mLinePerFrame[aIndex]; }
void AppendUnichar(char16_t aCh) { mBuffer.Append(aCh); }
void AppendString(const nsDependentSubstring& aString) {
@ -310,7 +413,6 @@ struct MOZ_STACK_CLASS BidiParagraphData {
void AppendControlChar(char16_t aCh) {
mLogicalFrames.AppendElement(FrameInfo(BidiControlFrameType::Value));
mLinePerFrame.AppendElement((nsLineBox*)nullptr);
AppendUnichar(aCh);
}
@ -335,44 +437,6 @@ struct MOZ_STACK_CLASS BidiParagraphData {
AppendPopChar(c);
}
}
static bool IsFrameInCurrentLine(nsBlockInFlowLineIterator* aLineIter,
nsIFrame* aPrevFrame, nsIFrame* aFrame) {
MOZ_ASSERT(!aPrevFrame || aLineIter->GetLine()->Contains(aPrevFrame),
"aPrevFrame must be in aLineIter's current line");
nsIFrame* endFrame = aLineIter->IsLastLineInList()
? nullptr
: aLineIter->GetLine().next()->mFirstChild;
nsIFrame* startFrame =
aPrevFrame ? aPrevFrame : aLineIter->GetLine()->mFirstChild;
for (nsIFrame* frame = startFrame; frame && frame != endFrame;
frame = frame->GetNextSibling()) {
if (frame == aFrame) return true;
}
return false;
}
static void AdvanceLineIteratorToFrame(nsIFrame* aFrame,
nsBlockInFlowLineIterator* aLineIter,
nsIFrame*& aPrevFrame) {
// Advance aLine to the line containing aFrame
nsIFrame* child = aFrame;
nsIFrame* parent = nsLayoutUtils::GetParentOrPlaceholderFor(child);
while (parent && !parent->IsBlockFrameOrSubclass()) {
child = parent;
parent = nsLayoutUtils::GetParentOrPlaceholderFor(child);
}
NS_ASSERTION(parent, "aFrame is not a descendent of a block frame");
while (!IsFrameInCurrentLine(aLineIter, aPrevFrame, child)) {
#ifdef DEBUG
bool hasNext =
#endif
aLineIter->Next();
NS_ASSERTION(hasNext, "Can't find frame in lines!");
aPrevFrame = nullptr;
}
aPrevFrame = child;
}
};
struct MOZ_STACK_CLASS BidiLineData {
@ -728,14 +792,16 @@ nsresult nsBidiPresUtils::Resolve(nsBlockFrame* aBlockFrame) {
bpd.mCurrentBlock = block;
#endif
block->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
nsBlockInFlowLineIterator it(block, block->LinesBegin());
bpd.mPrevFrame = nullptr;
TraverseFrames(&it, block->PrincipalChildList().FirstChild(), &bpd);
bpd.mCurrentTraverseLine.mLineIterator =
nsBlockInFlowLineIterator(block, block->LinesBegin());
bpd.mCurrentTraverseLine.mPrevFrame = nullptr;
TraverseFrames(block->PrincipalChildList().FirstChild(), &bpd);
nsBlockFrame::FrameLines* overflowLines = block->GetOverflowLines();
if (overflowLines) {
nsBlockInFlowLineIterator it(block, overflowLines->mLines.begin(), true);
bpd.mPrevFrame = nullptr;
TraverseFrames(&it, overflowLines->mFrames.FirstChild(), &bpd);
bpd.mCurrentTraverseLine.mLineIterator =
nsBlockInFlowLineIterator(block, overflowLines->mLines.begin(), true);
bpd.mCurrentTraverseLine.mPrevFrame = nullptr;
TraverseFrames(overflowLines->mFrames.FirstChild(), &bpd);
}
}
@ -771,11 +837,10 @@ nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
int32_t contentOffset = 0; // offset of current frame in its content node
bool isTextFrame = false;
nsIFrame* frame = nullptr;
BidiParagraphData::FrameInfo frameInfo;
nsIContent* content = nullptr;
int32_t contentTextLength = 0;
nsLineBox* currentLine = nullptr;
#ifdef DEBUG
# ifdef NOISY_BIDI
printf(
@ -812,7 +877,7 @@ nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
}
}
nsIFrame* lastRealFrame = nullptr;
BidiParagraphData::FrameInfo lastRealFrame;
nsBidiLevel lastEmbeddingLevel = kBidiLevelNone;
nsBidiLevel precedingControl = kBidiLevelNone;
@ -841,7 +906,8 @@ nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
if (++frameIndex >= frameCount) {
break;
}
frame = aBpd->FrameAt(frameIndex);
frameInfo = aBpd->FrameInfoAt(frameIndex);
frame = frameInfo.mFrame;
if (frame == NS_BIDI_CONTROL_FRAME || !frame->IsTextFrame()) {
/*
* Any non-text frame corresponds to a single character in the text
@ -851,7 +917,7 @@ nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
isTextFrame = false;
fragmentLength = 1;
} else {
currentLine = aBpd->GetLineForFrameAt(frameIndex);
aBpd->mCurrentResolveLine.AdvanceToLinesAndFrame(frameInfo);
content = frame->GetContent();
if (!content) {
rv = NS_OK;
@ -902,6 +968,7 @@ nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
// Nothing more to do for an empty frame.
continue;
}
nsLineList::iterator currentLine = aBpd->mCurrentResolveLine.GetLine();
if ((runLength > 0) && (runLength < fragmentLength)) {
/*
* The text in this frame continues beyond the end of this directional
@ -918,18 +985,9 @@ nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
nextBidi->AdjustOffsetsForBidi(runEnd,
contentOffset + fragmentLength);
frame = nextBidi;
frameInfo.mFrame = frame;
contentOffset = runEnd;
// We might have gotten here because we called
// ResolveParagraphWithinBlock in the middle of TraverseFrames. If
// so, once that's done, we're going to continue on with
// TraverseFrames.
//
// TraverseFrames uses aBpd->mPrevFrame as an optimization to help
// advance its line iterator correctly. The way this optimization
// works relies on the invariant that mPrevFrame is within the line
// pointed to by the line iterator.
//
// Calling EnsureBidiContinuation above may have caused a line to
// split when we added a continuation (since nsBlockFrame, by
// default, creates new lines for frame insertions). This means
@ -940,12 +998,11 @@ nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
// mPrevFrame that is *ahead* of the line iterator. If that
// happens, we'll never advance the line again until something nulls
// out mPrevFrame.
//
// On the other hand, if this wasn't called from
// ResolveParagraphWithinBlock, then nothing will care about
// mPrevFrame in the future, so nulling it out doesn't hurt
// anything.
aBpd->mPrevFrame = nullptr;
// FIXME: Are these both (or either) still needed?
aBpd->mCurrentResolveLine.mPrevFrame = nullptr;
aBpd->mCurrentTraverseLine.mPrevFrame = nullptr;
aBpd->mCurrentResolveLine.AdvanceToFrame(frame);
} // if (runLength < fragmentLength)
else {
if (contentOffset + fragmentLength == contentTextLength) {
@ -959,7 +1016,8 @@ nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
currentLine->MarkDirty();
RemoveBidiContinuation(aBpd, frame, frameIndex, newIndex);
frameIndex = newIndex;
frame = aBpd->FrameAt(frameIndex);
frameInfo = aBpd->FrameInfoAt(frameIndex);
frame = frameInfo.mFrame;
}
} else if (fragmentLength > 0 && runLength > fragmentLength) {
/*
@ -1000,18 +1058,18 @@ nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
// Record last real frame so that we can do splitting properly even
// if a run ends after a virtual bidi control frame.
if (frame != NS_BIDI_CONTROL_FRAME) {
lastRealFrame = frame;
lastRealFrame = frameInfo;
}
if (lastRealFrame && fragmentLength <= 0) {
if (lastRealFrame.mFrame && fragmentLength <= 0) {
// If the frame is at the end of a run, and this is not the end of our
// paragraph, split all ancestor inlines that need splitting.
// To determine whether we're at the end of the run, we check that we've
// finished processing the current run, and that the current frame
// doesn't have a fluid continuation (it could have a fluid continuation
// of zero length, so testing runLength alone is not sufficient).
if (runLength <= 0 && !lastRealFrame->GetNextInFlow()) {
if (runLength <= 0 && !lastRealFrame.mFrame->GetNextInFlow()) {
if (numRun + 1 < runCount) {
nsIFrame* child = lastRealFrame;
nsIFrame* child = lastRealFrame.mFrame;
nsContainerFrame* parent = child->GetParent();
// As long as we're on the last sibling, the parent doesn't have to
// be split.
@ -1035,7 +1093,11 @@ nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
// See above comment about the call to EnsureBidiContinuation.
// The SplitInlineAncestors call here might do the same thing, so
// again we need to set aBpd->mPrevFrame to null.
aBpd->mPrevFrame = nullptr;
// FIXME: Are these both (or either) still needed?
aBpd->mCurrentResolveLine.mPrevFrame = nullptr;
aBpd->mCurrentTraverseLine.mPrevFrame = nullptr;
aBpd->mCurrentResolveLine.AdvanceToLinesAndFrame(lastRealFrame);
}
}
} else if (frame != NS_BIDI_CONTROL_FRAME) {
@ -1058,13 +1120,13 @@ nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
return rv;
}
void nsBidiPresUtils::TraverseFrames(nsBlockInFlowLineIterator* aLineIter,
nsIFrame* aCurrentFrame,
void nsBidiPresUtils::TraverseFrames(nsIFrame* aCurrentFrame,
BidiParagraphData* aBpd) {
if (!aCurrentFrame) return;
#ifdef DEBUG
nsBlockFrame* initialLineContainer = aLineIter->GetContainer();
nsBlockFrame* initialLineContainer =
aBpd->mCurrentTraverseLine.mLineIterator.GetContainer();
#endif
nsIFrame* childFrame = aCurrentFrame;
@ -1138,7 +1200,7 @@ void nsBidiPresUtils::TraverseFrames(nsBlockInFlowLineIterator* aLineIter,
* frame in the array with a given content.
*/
nsIContent* content = frame->GetContent();
aBpd->AppendFrame(frame, aLineIter, content);
aBpd->AppendFrame(frame, aBpd->mCurrentTraverseLine, content);
// Append the content of the frame to the paragraph buffer
LayoutFrameType frameType = frame->Type();
@ -1170,7 +1232,8 @@ void nsBidiPresUtils::TraverseFrames(nsBlockInFlowLineIterator* aLineIter,
*/
aBpd->AppendString(Substring(text, start));
while (frame && nextSibling) {
aBpd->AdvanceAndAppendFrame(&frame, aLineIter, &nextSibling);
aBpd->AdvanceAndAppendFrame(
&frame, aBpd->mCurrentTraverseLine, &nextSibling);
}
break;
}
@ -1188,7 +1251,8 @@ void nsBidiPresUtils::TraverseFrames(nsBlockInFlowLineIterator* aLineIter,
aBpd->AppendString(
Substring(text, start, std::min(end, endLine) - start));
while (end < endLine && nextSibling) {
aBpd->AdvanceAndAppendFrame(&frame, aLineIter, &nextSibling);
aBpd->AdvanceAndAppendFrame(&frame, aBpd->mCurrentTraverseLine,
&nextSibling);
NS_ASSERTION(frame, "Premature end of continuation chain");
frame->GetOffsets(start, end);
aBpd->AppendString(
@ -1227,13 +1291,19 @@ void nsBidiPresUtils::TraverseFrames(nsBlockInFlowLineIterator* aLineIter,
nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
textFrame->SetLength(endLine - start, nullptr);
// If it weren't for CreateContinuation needing this to
// be current, we could restructure the marking dirty
// below to use mCurrentResolveLine and eliminate
// mCurrentTraverseLine entirely.
aBpd->mCurrentTraverseLine.AdvanceToFrame(frame);
if (!next) {
// If the frame has no next in flow, create one.
CreateContinuation(frame, &next, true);
createdContinuation = true;
}
// Mark the line before the newline as dirty.
aBpd->GetLineForFrameAt(aBpd->FrameCount() - 1)->MarkDirty();
aBpd->mCurrentTraverseLine.GetLine()->MarkDirty();
}
ResolveParagraphWithinBlock(aBpd);
@ -1241,9 +1311,10 @@ void nsBidiPresUtils::TraverseFrames(nsBlockInFlowLineIterator* aLineIter,
break;
} else if (next) {
frame = next;
aBpd->AppendFrame(frame, aLineIter);
aBpd->AppendFrame(frame, aBpd->mCurrentTraverseLine);
// Mark the line after the newline as dirty.
aBpd->GetLineForFrameAt(aBpd->FrameCount() - 1)->MarkDirty();
aBpd->mCurrentTraverseLine.AdvanceToFrame(frame);
aBpd->mCurrentTraverseLine.GetLine()->MarkDirty();
}
/*
@ -1280,7 +1351,7 @@ void nsBidiPresUtils::TraverseFrames(nsBlockInFlowLineIterator* aLineIter,
MOZ_ASSERT(!frame->GetChildList(nsIFrame::kOverflowList).FirstChild(),
"should have drained the overflow list above");
if (kid) {
TraverseFrames(aLineIter, kid, aBpd);
TraverseFrames(kid, aBpd);
}
}
@ -1299,7 +1370,8 @@ void nsBidiPresUtils::TraverseFrames(nsBlockInFlowLineIterator* aLineIter,
childFrame = nextSibling;
} while (childFrame);
MOZ_ASSERT(initialLineContainer == aLineIter->GetContainer());
MOZ_ASSERT(initialLineContainer ==
aBpd->mCurrentTraverseLine.mLineIterator.GetContainer());
}
bool nsBidiPresUtils::ChildListMayRequireBidi(nsIFrame* aFirstChild,

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

@ -25,7 +25,6 @@ class nsFontMetrics;
class nsIFrame;
class nsBlockFrame;
class nsPresContext;
class nsBlockInFlowLineIterator;
struct nsSize;
template <class T>
class nsTHashtable;
@ -418,8 +417,7 @@ class nsBidiPresUtils {
* than one paragraph for bidi resolution, resolve the paragraph up to that
* point.
*/
static void TraverseFrames(nsBlockInFlowLineIterator* aLineIter,
nsIFrame* aCurrentFrame, BidiParagraphData* aBpd);
static void TraverseFrames(nsIFrame* aCurrentFrame, BidiParagraphData* aBpd);
/**
* Perform a recursive "pre-traversal" of the child frames of a block or

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

@ -998,6 +998,9 @@ class nsBlockInFlowLineIterator {
nsBlockInFlowLineIterator(nsBlockFrame* aFrame, nsIFrame* aFindFrame,
bool* aFoundValidLine);
// Allow to be uninitialized (and then assigned from another object).
nsBlockInFlowLineIterator() : mFrame(nullptr) {}
LineIterator GetLine() { return mLine; }
bool IsLastLineInList();
nsBlockFrame* GetContainer() { return mFrame; }
@ -1025,14 +1028,12 @@ class nsBlockInFlowLineIterator {
*/
bool Prev();
private:
friend class nsBlockFrame;
friend class nsBidiPresUtils;
// XXX nsBlockFrame uses this internally in one place. Try to remove it.
// XXX uhm, and nsBidiPresUtils::Resolve too.
nsBlockInFlowLineIterator(nsBlockFrame* aFrame, LineIterator aLine,
bool aInOverflow);
private:
nsBlockFrame* mFrame;
LineIterator mLine;
nsLineList* mLineList; // the line list mLine is in