Add a mParagraphDepth member to BidiParagraphData and cache it on frames to track nested bidi-isolated paragraphs. Use mParagraphDepth (* 2 + isRTL in order to keep LTR even and RTL odd) as the initial paragraph embedding level instead of pushing a stack of embedding codes. Pass the <bdi> frame instead of its first-child to BidiParagraphData::Reset and save a call to GetParent(). Split inline ancestors before and after bidi-isolated paragraphs, in the same way that we currently do between runs in different directions. This required also adding code to SplitInlineAncestors to handle situations where the parent frame is being split before its first child or after its last child. In JoinInlineAncestors, prevent frames that belong to paragraphs isolated from one another from being rejoined. Bug 712600, r=ehsan, roc

This commit is contained in:
Simon Montagu 2012-01-11 21:26:02 +02:00
parent 0c126e1db1
commit a2edafefab
4 changed files with 121 additions and 73 deletions

View File

@ -86,12 +86,14 @@ struct BidiParagraphData {
nsAutoPtr<nsBidi> mBidiEngine;
nsIFrame* mPrevFrame;
nsAutoPtr<BidiParagraphData> mSubParagraph;
PRUint8 mParagraphDepth;
void Init(nsBlockFrame *aBlockFrame)
{
mContentToFrameIndex.Init();
mBidiEngine = new nsBidi();
mPrevContent = nsnull;
mParagraphDepth = 0;
bool styleDirectionIsRTL =
(NS_STYLE_DIRECTION_RTL == aBlockFrame->GetStyleVisibility()->mDirection);
@ -149,19 +151,10 @@ struct BidiParagraphData {
mBidiEngine = new nsBidi();
mPrevContent = nsnull;
mIsVisual = aBpd->mIsVisual;
mParaLevel = aBpd->mParaLevel;
// If the containing paragraph has a level of NSBIDI_DEFAULT_LTR, set
// the sub-paragraph to NSBIDI_LTR (we can't use GetParaLevel to find the
// resolved paragraph level, because the containing paragraph hasn't yet
// been through bidi resolution
if (mParaLevel == NSBIDI_DEFAULT_LTR) {
mParaLevel = NSBIDI_LTR;
}
mReset = false;
}
void Reset(nsIFrame* aFrame, BidiParagraphData *aBpd)
void Reset(nsIFrame* aBDIFrame, BidiParagraphData *aBpd)
{
mReset = true;
mLogicalFrames.Clear();
@ -169,37 +162,15 @@ struct BidiParagraphData {
mContentToFrameIndex.Clear();
mBuffer.SetLength(0);
mPrevFrame = aBpd->mPrevFrame;
// We need to copy in embeddings (but not overrides!) from the containing
// paragraph so that the line(s) including this sub-paragraph will be
// correctly reordered.
for (PRUint32 i = 0; i < aBpd->mEmbeddingStack.Length(); ++i) {
switch(aBpd->mEmbeddingStack[i]) {
case kRLE:
case kRLO:
mParaLevel = NextOddLevel(mParaLevel);
break;
mParagraphDepth = aBpd->mParagraphDepth + 1;
case kLRE:
case kLRO:
mParaLevel = NextEvenLevel(mParaLevel);
break;
default:
break;
}
}
nsIFrame* container = aFrame->GetParent();
bool isRTL = (NS_STYLE_DIRECTION_RTL ==
container->GetStyleVisibility()->mDirection);
if ((isRTL & 1) != (mParaLevel & 1)) {
mParaLevel = isRTL ? NextOddLevel(mParaLevel) : NextEvenLevel(mParaLevel);
}
aBDIFrame->GetStyleVisibility()->mDirection);
mParaLevel = mParagraphDepth * 2;
if (isRTL) ++mParaLevel;
if (container->GetStyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_OVERRIDE) {
if (aBDIFrame->GetStyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_OVERRIDE) {
PushBidiControl(isRTL ? kRLO : kLRO);
} else {
PushBidiControl(isRTL ? kRLE : kLRE);
}
}
@ -333,16 +304,6 @@ struct BidiParagraphData {
}
}
nsBidiLevel NextOddLevel(nsBidiLevel aLevel)
{
return (aLevel + 1) | 1;
}
nsBidiLevel NextEvenLevel(nsBidiLevel aLevel)
{
return (aLevel + 2) & ~1;
}
static bool
IsFrameInCurrentLine(nsBlockInFlowLineIterator* aLineIter,
nsIFrame* aPrevFrame, nsIFrame* aFrame)
@ -453,13 +414,23 @@ IsBidiSplittable(nsIFrame* aFrame) {
&& frameType != nsGkAtoms::lineFrame;
}
/**
* Create non-fluid continuations for the ancestors of a given frame all the way
* up the frame tree until we hit a non-splittable frame (a line or a block).
*
* @param aParent the first parent frame to be split
* @param aFrame the child frames after this frame are reparented to the
* newly-created continuation of aParent.
* If aFrame is null, all the children of aParent are reparented.
*/
static nsresult
SplitInlineAncestors(nsIFrame* aFrame)
SplitInlineAncestors(nsIFrame* aParent,
nsIFrame* aFrame)
{
nsPresContext *presContext = aFrame->PresContext();
nsPresContext *presContext = aParent->PresContext();
nsIPresShell *presShell = presContext->PresShell();
nsIFrame* frame = aFrame;
nsIFrame* parent = aFrame->GetParent();
nsIFrame* parent = aParent;
nsIFrame* newParent;
while (IsBidiSplittable(parent)) {
@ -472,21 +443,24 @@ SplitInlineAncestors(nsIFrame* aFrame)
return rv;
}
// Split the child list after |frame|.
nsContainerFrame* container = do_QueryFrame(parent);
nsFrameList tail = container->StealFramesAfter(frame);
// Split the child list after |frame|, unless it is the last child.
if (!frame || frame->GetNextSibling()) {
nsContainerFrame* container = do_QueryFrame(parent);
nsFrameList tail = container->StealFramesAfter(frame);
// Reparent views as necessary
rv = nsContainerFrame::ReparentFrameViewList(presContext, tail, parent, newParent);
if (NS_FAILED(rv)) {
return rv;
// Reparent views as necessary
rv = nsContainerFrame::ReparentFrameViewList(presContext, tail, parent, newParent);
if (NS_FAILED(rv)) {
return rv;
}
// The parent's continuation adopts the siblings after the split.
rv = newParent->InsertFrames(nsIFrame::kNoReflowPrincipalList, nsnull, tail);
if (NS_FAILED(rv)) {
return rv;
}
}
// The parent's continuation adopts the siblings after the split.
rv = newParent->InsertFrames(nsIFrame::kNoReflowPrincipalList, nsnull, tail);
if (NS_FAILED(rv)) {
return rv;
}
// The list name kNoReflowPrincipalList would indicate we don't want reflow
nsFrameList temp(newParent, newParent);
rv = grandparent->InsertFrames(nsIFrame::kNoReflowPrincipalList, parent, temp);
@ -525,7 +499,12 @@ JoinInlineAncestors(nsIFrame* aFrame)
while (frame && IsBidiSplittable(frame)) {
nsIFrame* next = frame->GetNextContinuation();
if (next) {
MakeContinuationFluid(frame, next);
// Don't join frames if they come from different paragraph depths (i.e.
// one is bidi isolated relative to the other
if (nsBidiPresUtils::GetParagraphDepth(frame) ==
nsBidiPresUtils::GetParagraphDepth(next)) {
MakeContinuationFluid(frame, next);
}
}
// Join the parent only as long as we're its last child.
if (frame->GetNextSibling())
@ -580,7 +559,7 @@ CreateContinuation(nsIFrame* aFrame,
if (!aIsFluid) {
// Split inline ancestor frames
rv = SplitInlineAncestors(aFrame);
rv = SplitInlineAncestors(parent, aFrame);
if (NS_FAILED(rv)) {
return rv;
}
@ -705,6 +684,9 @@ nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame,
#endif
#endif
nsIFrame* firstFrame = nsnull;
nsIFrame* lastFrame = nsnull;
for (; ;) {
if (fragmentLength <= 0) {
// Get the next frame from mLogicalFrames
@ -722,6 +704,10 @@ nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame,
fragmentLength = 1;
}
else {
if (!firstFrame) {
firstFrame = frame;
}
lastFrame = frame;
currentLine = aBpd->GetLineForFrameAt(frameIndex);
content = frame->GetContent();
if (!content) {
@ -737,6 +723,8 @@ nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame,
NS_INT32_TO_PTR(embeddingLevel));
propTable->Set(frame, nsIFrame::BaseLevelProperty(),
NS_INT32_TO_PTR(aBpd->GetParaLevel()));
propTable->Set(frame, nsIFrame::ParagraphDepthProperty(),
NS_INT32_TO_PTR(aBpd->mParagraphDepth));
continue;
}
PRInt32 start, end;
@ -771,6 +759,8 @@ nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame,
NS_INT32_TO_PTR(embeddingLevel));
propTable->Set(frame, nsIFrame::BaseLevelProperty(),
NS_INT32_TO_PTR(aBpd->GetParaLevel()));
propTable->Set(frame, nsIFrame::ParagraphDepthProperty(),
NS_INT32_TO_PTR(aBpd->mParagraphDepth));
if (isTextFrame) {
if ( (runLength > 0) && (runLength < fragmentLength) ) {
/*
@ -788,7 +778,7 @@ nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame,
}
nextBidi->AdjustOffsetsForBidi(runEnd,
contentOffset + fragmentLength);
frame = nextBidi;
lastFrame = frame = nextBidi;
contentOffset = runEnd;
} // if (runLength < fragmentLength)
else {
@ -870,8 +860,9 @@ nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame,
child = parent;
parent = child->GetParent();
}
if (parent && IsBidiSplittable(parent))
SplitInlineAncestors(child);
if (parent && IsBidiSplittable(parent)) {
SplitInlineAncestors(parent, child);
}
}
}
else {
@ -883,6 +874,31 @@ nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame,
}
} // for
if (aBpd->mParagraphDepth > 1) {
nsIFrame* child;
nsIFrame* parent;
if (firstFrame) {
child = firstFrame->GetParent();
if (child) {
parent = child->GetParent();
if (parent && IsBidiSplittable(parent)) {
// no need to null-check the result of GetPrevSibling, because
// SplitInlineAncestors accepts a null parameter
SplitInlineAncestors(parent, child->GetPrevSibling());
}
}
}
if (lastFrame) {
child = lastFrame->GetParent();
if (child) {
parent = child->GetParent();
if (parent && IsBidiSplittable(parent)) {
SplitInlineAncestors(parent, child);
}
}
}
}
#ifdef DEBUG
#ifdef REALLY_NOISY_BIDI
printf("---\nAfter Resolve(), frameTree =:\n");
@ -1122,7 +1138,7 @@ nsBidiPresUtils::TraverseFrames(nsBlockFrame* aBlockFrame,
*/
bool isLastContinuation = !frame->GetNextContinuation();
if (!frame->GetPrevContinuation() || !subParagraph->mReset) {
subParagraph->Reset(kid, aBpd);
subParagraph->Reset(frame, aBpd);
}
TraverseFrames(aBlockFrame, aLineIter, kid, subParagraph);
if (isLastContinuation) {
@ -1177,8 +1193,8 @@ nsBidiPresUtils::ReorderFrames(nsIFrame* aFirstFrameOnLine,
RepositionInlineFrames(&bld, aFirstFrameOnLine);
}
nsBidiLevel
nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame* aFrame)
nsIFrame*
nsBidiPresUtils::GetFirstLeaf(nsIFrame* aFrame)
{
nsIFrame* firstLeaf = aFrame;
while (!IsBidiLeaf(firstLeaf)) {
@ -1187,9 +1203,22 @@ nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame* aFrame)
firstLeaf = (realFrame->GetType() == nsGkAtoms::letterFrame) ?
realFrame : firstChild;
}
return NS_GET_EMBEDDING_LEVEL(firstLeaf);
return firstLeaf;
}
nsBidiLevel
nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame* aFrame)
{
return NS_GET_EMBEDDING_LEVEL(nsBidiPresUtils::GetFirstLeaf(aFrame));
}
PRUint8
nsBidiPresUtils::GetParagraphDepth(nsIFrame* aFrame)
{
return NS_GET_PARAGRAPH_DEPTH(nsBidiPresUtils::GetFirstLeaf(aFrame));
}
nsBidiLevel
nsBidiPresUtils::GetFrameBaseLevel(nsIFrame* aFrame)
{
@ -1528,6 +1557,8 @@ nsBidiPresUtils::RemoveBidiContinuation(BidiParagraphData *aBpd,
(nsBidiLevel)NS_PTR_TO_INT32(props.Get(nsIFrame::EmbeddingLevelProperty()));
nsBidiLevel baseLevel =
(nsBidiLevel)NS_PTR_TO_INT32(props.Get(nsIFrame::BaseLevelProperty()));
PRUint8 paragraphDepth =
NS_PTR_TO_INT32(props.Get(nsIFrame::ParagraphDepthProperty()));
for (PRInt32 index = aFirstIndex + 1; index <= aLastIndex; index++) {
nsIFrame* frame = aBpd->FrameAt(index);
@ -1542,6 +1573,8 @@ nsBidiPresUtils::RemoveBidiContinuation(BidiParagraphData *aBpd,
NS_INT32_TO_PTR(embeddingLevel));
frameProps.Set(nsIFrame::BaseLevelProperty(),
NS_INT32_TO_PTR(baseLevel));
frameProps.Set(nsIFrame::ParagraphDepthProperty(),
NS_INT32_TO_PTR(paragraphDepth));
frame->AddStateBits(NS_FRAME_IS_BIDI);
while (frame) {
nsIFrame* prev = frame->GetPrevContinuation();

View File

@ -283,11 +283,18 @@ public:
static nsIFrame* GetFrameToLeftOf(const nsIFrame* aFrame,
nsIFrame* aFirstFrameOnLine,
PRInt32 aNumFramesOnLine);
static nsIFrame* GetFirstLeaf(nsIFrame* aFrame);
/**
* Get the bidi embedding level of the given (inline) frame.
*/
static nsBidiLevel GetFrameEmbeddingLevel(nsIFrame* aFrame);
/**
* Get the paragraph depth of the given (inline) frame.
*/
static PRUint8 GetParagraphDepth(nsIFrame* aFrame);
/**
* Get the bidi base level of the given (inline) frame.

View File

@ -2529,6 +2529,7 @@ public:
NS_DECLARE_FRAME_PROPERTY(BaseLevelProperty, nsnull)
NS_DECLARE_FRAME_PROPERTY(EmbeddingLevelProperty, nsnull)
NS_DECLARE_FRAME_PROPERTY(ParagraphDepthProperty, nsnull)
#define NS_GET_BASE_LEVEL(frame) \
NS_PTR_TO_INT32(frame->Properties().Get(nsIFrame::BaseLevelProperty()))
@ -2536,6 +2537,9 @@ NS_PTR_TO_INT32(frame->Properties().Get(nsIFrame::BaseLevelProperty()))
#define NS_GET_EMBEDDING_LEVEL(frame) \
NS_PTR_TO_INT32(frame->Properties().Get(nsIFrame::EmbeddingLevelProperty()))
#define NS_GET_PARAGRAPH_DEPTH(frame) \
NS_PTR_TO_INT32(frame->Properties().Get(nsIFrame::ParagraphDepthProperty()))
/**
* Return true if and only if this frame obeys visibility:hidden.
* if it does not, then nsContainerFrame will hide its view even though

View File

@ -1492,7 +1492,8 @@ BuildTextRunsScanner::ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, nsTextFr
// already guaranteed to be the same as each other (and for the line
// container).
if (mBidiEnabled &&
NS_GET_EMBEDDING_LEVEL(aFrame1) != NS_GET_EMBEDDING_LEVEL(aFrame2))
(NS_GET_EMBEDDING_LEVEL(aFrame1) != NS_GET_EMBEDDING_LEVEL(aFrame2) ||
NS_GET_PARAGRAPH_DEPTH(aFrame1) != NS_GET_PARAGRAPH_DEPTH(aFrame2)))
return false;
nsStyleContext* sc1 = aFrame1->GetStyleContext();
@ -3943,8 +3944,10 @@ nsContinuingTextFrame::Init(nsIContent* aContent,
// advantage of the propTable's cache and simplify the assertion below
void* embeddingLevel = propTable->Get(aPrevInFlow, EmbeddingLevelProperty());
void* baseLevel = propTable->Get(aPrevInFlow, BaseLevelProperty());
void* paragraphDepth = propTable->Get(aPrevInFlow, ParagraphDepthProperty());
propTable->Set(this, EmbeddingLevelProperty(), embeddingLevel);
propTable->Set(this, BaseLevelProperty(), baseLevel);
propTable->Set(this, ParagraphDepthProperty(), paragraphDepth);
if (nextContinuation) {
SetNextContinuation(nextContinuation);
@ -3954,7 +3957,8 @@ nsContinuingTextFrame::Init(nsIContent* aContent,
nextContinuation->GetContentOffset() < mContentOffset) {
NS_ASSERTION(
embeddingLevel == propTable->Get(nextContinuation, EmbeddingLevelProperty()) &&
baseLevel == propTable->Get(nextContinuation, BaseLevelProperty()),
baseLevel == propTable->Get(nextContinuation, BaseLevelProperty()) &&
paragraphDepth == propTable->Get(nextContinuation, ParagraphDepthProperty()),
"stealing text from different type of BIDI continuation");
nextContinuation->mContentOffset = mContentOffset;
nextContinuation = static_cast<nsTextFrame*>(nextContinuation->GetNextContinuation());