# HG changeset patch # User Simon Montagu # Date 1326309962 -7200 # Node ID c21aafb8603b7ed092c04977f900f62f54c645ae # Parent 4f5c4cf931b97f25abd04d45e17b54e2031092de [mq]: 712600.3 diff --git a/layout/base/nsBidiPresUtils.cpp b/layout/base/nsBidiPresUtils.cpp --- a/layout/base/nsBidiPresUtils.cpp +++ b/layout/base/nsBidiPresUtils.cpp @@ -81,22 +81,24 @@ struct BidiParagraphData { nsDataHashtable mContentToFrameIndex; bool mIsVisual; bool mReset; nsBidiLevel mParaLevel; nsIContent* mPrevContent; nsAutoPtr mBidiEngine; nsIFrame* mPrevFrame; nsAutoPtr 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); if (aBlockFrame->GetStyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) { // unicode-bidi: plaintext: the Bidi algorithm will determine the // directionality of the paragraph according to the first strong // directional character. @@ -144,67 +146,36 @@ struct BidiParagraphData { // Initialise a sub-paragraph from its containing paragraph void Init(BidiParagraphData *aBpd) { mContentToFrameIndex.Init(); mBidiEngine = new nsBidi(); mPrevContent = nsnull; mIsVisual = aBpd->mIsVisual; - mParaLevel = aBpd->mParaLevel; - - // If the containing paragraph has a level of NSBIDI_DEFAULT_LTR/RTL, set - // the sub-paragraph to the corresponding non-default level (We can't use - // GetParaLevel, because the containing paragraph hasn't yet been through - // bidi resolution - if (IS_DEFAULT_LEVEL(mParaLevel)) { - mParaLevel = (mParaLevel == NSBIDI_DEFAULT_RTL) ? NSBIDI_RTL : NSBIDI_LTR; - } mReset = false; } - void Reset(nsIFrame* aFrame, BidiParagraphData *aBpd) + void Reset(nsIFrame* aBDIFrame, BidiParagraphData *aBpd) { mReset = true; mLogicalFrames.Clear(); mLinePerFrame.Clear(); 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; + bool isRTL = (NS_STYLE_DIRECTION_RTL == + aBDIFrame->GetStyleVisibility()->mDirection); + mParaLevel = mParagraphDepth * 2; + if (isRTL) ++mParaLevel; - 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); - } - - 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); } } nsresult SetPara() { return mBidiEngine->SetPara(mBuffer.get(), BufferLength(), mParaLevel, nsnull); } @@ -328,26 +299,16 @@ struct BidiParagraphData { void ClearBidiControls() { for (PRUint32 i = 0; i < mEmbeddingStack.Length(); ++i) { AppendControlChar(kPDF); } } - 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) { nsIFrame* endFrame = aLineIter->IsLastLineInList() ? nsnull : aLineIter->GetLine().next()->mFirstChild; nsIFrame* startFrame = aPrevFrame ? aPrevFrame : aLineIter->GetLine()->mFirstChild; for (nsIFrame* frame = startFrame; frame && frame != endFrame; @@ -449,17 +410,18 @@ bool IsBidiSplittable(nsIFrame* aFrame) { nsIAtom* frameType = aFrame->GetType(); // Bidi inline containers should be split, unless they're line frames. return aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer) && frameType != nsGkAtoms::lineFrame; } static nsresult -SplitInlineAncestors(nsIFrame* aFrame) +SplitInlineAncestors(nsIFrame* aFrame, + bool splitBefore) { nsPresContext *presContext = aFrame->PresContext(); nsIPresShell *presShell = presContext->PresShell(); nsIFrame* frame = aFrame; nsIFrame* parent = aFrame->GetParent(); nsIFrame* newParent; while (IsBidiSplittable(parent)) { @@ -469,43 +431,60 @@ SplitInlineAncestors(nsIFrame* aFram nsresult rv = presShell->FrameConstructor()-> CreateContinuingFrame(presContext, parent, grandparent, &newParent, false); if (NS_FAILED(rv)) { return rv; } // Split the child list after |frame|. nsContainerFrame* container = do_QueryFrame(parent); - nsFrameList tail = container->StealFramesAfter(frame); + nsIFrame* splitAfterFrame = ((frame == aFrame) && splitBefore) ? + frame->GetPrevSibling() : frame; + if (splitAfterFrame && splitAfterFrame->GetNextSibling()) { + nsFrameList tail = container->StealFramesAfter(splitAfterFrame); - // 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); if (NS_FAILED(rv)) { return rv; } frame = parent; parent = grandparent; } return NS_OK; } +static nsresult +SplitInlineAncestorsBefore(nsIFrame* aFrame) +{ + return SplitInlineAncestors(aFrame, true); +} + +static nsresult +SplitInlineAncestorsAfter(nsIFrame* aFrame) +{ + return SplitInlineAncestors(aFrame, false); +} + static void MakeContinuationFluid(nsIFrame* aFrame, nsIFrame* aNext) { NS_ASSERTION (!aFrame->GetNextInFlow() || aFrame->GetNextInFlow() == aNext, "next-in-flow is not next continuation!"); aFrame->SetNextInFlow(aNext); NS_ASSERTION (!aNext->GetPrevInFlow() || aNext->GetPrevInFlow() == aFrame, @@ -520,17 +499,22 @@ JoinInlineAncestors(nsIFrame* aFrame) { if (aFrame->GetNextSibling()) { return; } nsIFrame* frame = aFrame->GetParent(); 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()) break; frame = frame->GetParent(); } } @@ -575,17 +559,17 @@ CreateContinuation(nsIFrame* aFrame, nsFrameList temp(*aNewFrame, *aNewFrame); rv = parent->InsertFrames(nsIFrame::kNoReflowPrincipalList, aFrame, temp); if (NS_FAILED(rv)) { return rv; } if (!aIsFluid) { // Split inline ancestor frames - rv = SplitInlineAncestors(aFrame); + rv = SplitInlineAncestorsAfter(aFrame); if (NS_FAILED(rv)) { return rv; } } return NS_OK; } @@ -700,16 +684,19 @@ nsBidiPresUtils::ResolveParagraph(nsBloc (void*)aBlockFrame, NS_ConvertUTF16toUTF8(aBpd->mBuffer).get(), frameCount, runCount); #ifdef REALLY_NOISY_BIDI printf(" block frame tree=:\n"); aBlockFrame->List(stdout, 0); #endif #endif #endif + nsIFrame* firstFrame = nsnull; + nsIFrame* lastFrame = nsnull; + for (; ;) { if (fragmentLength <= 0) { // Get the next frame from mLogicalFrames if (++frameIndex >= frameCount) { break; } frame = aBpd->FrameAt(frameIndex); if (frame == NS_BIDI_CONTROL_FRAME || @@ -717,31 +704,37 @@ nsBidiPresUtils::ResolveParagraph(nsBloc /* * Any non-text frame corresponds to a single character in the text buffer * (a bidi control character, LINE SEPARATOR, or OBJECT SUBSTITUTE) */ isTextFrame = false; fragmentLength = 1; } else { + if (!firstFrame) { + firstFrame = frame; + } + lastFrame = frame; currentLine = aBpd->GetLineForFrameAt(frameIndex); content = frame->GetContent(); if (!content) { rv = NS_OK; break; } contentTextLength = content->TextLength(); if (contentTextLength == 0) { frame->AdjustOffsetsForBidi(0, 0); // Set the base level and embedding level of the current run even // on an empty frame. Otherwise frame reordering will not be correct. propTable->Set(frame, nsIFrame::EmbeddingLevelProperty(), 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; frame->GetOffsets(start, end); NS_ASSERTION(!(contentTextLength < end - start), "Frame offsets don't fit in content"); fragmentLength = NS_MIN(contentTextLength, end - start); contentOffset = start; @@ -766,16 +759,18 @@ nsBidiPresUtils::ResolveParagraph(nsBloc frame = nsnull; ++lineOffset; } else { propTable->Set(frame, nsIFrame::EmbeddingLevelProperty(), 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) ) { /* * The text in this frame continues beyond the end of this directional run. * Create a non-fluid continuation frame for the next directional run. */ currentLine->MarkDirty(); nsIFrame* nextBidi; @@ -783,17 +778,17 @@ nsBidiPresUtils::ResolveParagraph(nsBloc rv = EnsureBidiContinuation(frame, &nextBidi, frameIndex, contentOffset, runEnd); if (NS_FAILED(rv)) { break; } nextBidi->AdjustOffsetsForBidi(runEnd, contentOffset + fragmentLength); - frame = nextBidi; + lastFrame = frame = nextBidi; contentOffset = runEnd; } // if (runLength < fragmentLength) else { if (contentOffset + fragmentLength == contentTextLength) { /* * We have finished all the text in this content node. Convert any * further non-fluid continuations to fluid continuations and advance * frameIndex to the last frame in the content node @@ -868,28 +863,51 @@ nsBidiPresUtils::ResolveParagraph(nsBloc if (next) { parent->SetNextContinuation(next); next->SetPrevContinuation(parent); } child = parent; parent = child->GetParent(); } if (parent && IsBidiSplittable(parent)) - SplitInlineAncestors(child); + SplitInlineAncestorsAfter(child); } } else { // We're not at an end of a run. If |frame| is the last child of its // parent, and its ancestors happen to have bidi continuations, convert // them into fluid continuations. JoinInlineAncestors(frame); } } } // for + if (aBpd->mParagraphDepth > 1) { + nsIFrame* child; + nsIFrame* parent; + if (firstFrame) { + child = firstFrame->GetParent(); + if (child) { + parent = child->GetParent(); + if (parent && IsBidiSplittable(parent)) { + SplitInlineAncestorsBefore(child); + } + } + } + if (lastFrame) { + child = lastFrame->GetParent(); + if (child) { + parent = child->GetParent(); + if (parent && IsBidiSplittable(parent)) { + SplitInlineAncestorsAfter(child); + } + } + } + } + #ifdef DEBUG #ifdef REALLY_NOISY_BIDI printf("---\nAfter Resolve(), frameTree =:\n"); aBlockFrame->List(stdout, 0); printf("===\n"); #endif #endif @@ -1116,17 +1134,17 @@ nsBidiPresUtils::TraverseFrames(nsBlockF * next-continuations before handling the frame. If we do * TraverseFrames and *then* do GetNextContinuation on the original * first frame, it could return a bidi continuation that had only * just been created, and we would skip doing bidi resolution on the * last part of the sub-paragraph. */ bool isLastContinuation = !frame->GetNextContinuation(); if (!frame->GetPrevContinuation() || !subParagraph->mReset) { - subParagraph->Reset(kid, aBpd); + subParagraph->Reset(frame, aBpd); } TraverseFrames(aBlockFrame, aLineIter, kid, subParagraph); if (isLastContinuation) { ResolveParagraph(aBlockFrame, subParagraph); } // Treat the element as a neutral character within its containing // paragraph. @@ -1171,28 +1189,40 @@ nsBidiPresUtils::ReorderFrames(nsIFrame* // to -1 makes InitLogicalArrayFromLine look at all of them. aNumFramesOnLine = -1; } BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine); RepositionInlineFrames(&bld, aFirstFrameOnLine); } -nsBidiLevel -nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame* aFrame) +nsIFrame* +nsBidiPresUtils::GetFirstLeaf(nsIFrame* aFrame) { nsIFrame* firstLeaf = aFrame; while (!IsBidiLeaf(firstLeaf)) { nsIFrame* firstChild = firstLeaf->GetFirstPrincipalChild(); nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(firstChild); 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) { nsIFrame* firstLeaf = aFrame; while (!IsBidiLeaf(firstLeaf)) { firstLeaf = firstLeaf->GetFirstPrincipalChild(); } @@ -1522,30 +1552,34 @@ nsBidiPresUtils::RemoveBidiContinuation( PRInt32 aLastIndex, PRInt32& aOffset) { FrameProperties props = aFrame->Properties(); nsBidiLevel embeddingLevel = (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); if (frame == NS_BIDI_CONTROL_FRAME) { ++aOffset; } else { // Make the frame and its continuation ancestors fluid, // so they can be reused or deleted by normal reflow code FrameProperties frameProps = frame->Properties(); frameProps.Set(nsIFrame::EmbeddingLevelProperty(), 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(); if (prev) { MakeContinuationFluid(prev, frame); frame = frame->GetParent(); } else { break; diff --git a/layout/base/nsBidiPresUtils.h b/layout/base/nsBidiPresUtils.h --- a/layout/base/nsBidiPresUtils.h +++ b/layout/base/nsBidiPresUtils.h @@ -278,21 +278,28 @@ public: * @param aFrame : We're looking for the frame to the left of this frame. * If null, return the rightmost frame on the line. * @param aFirstFrameOnLine : first frame of the line to be tested * @param aNumFramesOnLine : number of frames on this line */ 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. */ static nsBidiLevel GetFrameBaseLevel(nsIFrame* aFrame); enum Mode { MODE_DRAW, MODE_MEASURE }; diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -2501,23 +2501,27 @@ public: } FrameProperties Properties() const { return FrameProperties(PresContext()->PropertyTable(), this); } 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())) #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 * this means children can't be made visible again. */ virtual bool SupportsVisibilityHidden() { return true; } /** diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -1482,17 +1482,18 @@ BuildTextRunsScanner::ContinueTextRunAcr { // We don't need to check font size inflation, since // |FindLineContainer| above (via |nsIFrame::CanContinueTextRun|) // ensures that text runs never cross block boundaries. This means // that the font size inflation on all text frames in the text run is // 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(); const nsStyleText* textStyle1 = sc1->GetStyleText(); // If the first frame ends in a preformatted newline, then we end the textrun // here. This avoids creating giant textruns for an entire plain text file. // Note that we create a single text frame for a preformatted text node, // even if it has newlines in it, so typically we won't see trailing newlines @@ -3932,28 +3933,31 @@ nsContinuingTextFrame::Init(nsIContent* } #ifdef IBMBIDI if (aPrevInFlow->GetStateBits() & NS_FRAME_IS_BIDI) { FramePropertyTable *propTable = PresContext()->PropertyTable(); // Get all the properties from the prev-in-flow first to take // 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); nextContinuation->SetPrevContinuation(this); // Adjust next-continuations' content offset as needed. while (nextContinuation && 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(nextContinuation->GetNextContinuation()); } } mState |= NS_FRAME_IS_BIDI; } // prev frame is bidi #endif // IBMBIDI