1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsBidiPresUtils.h"
7 #include "nsFontMetrics.h"
9 #include "nsPresContext.h"
10 #include "nsRenderingContext.h"
11 #include "nsBidiUtils.h"
12 #include "nsCSSFrameConstructor.h"
13 #include "nsContainerFrame.h"
14 #include "nsInlineFrame.h"
15 #include "nsPlaceholderFrame.h"
16 #include "nsFirstLetterFrame.h"
17 #include "nsUnicodeProperties.h"
18 #include "nsTextFrame.h"
19 #include "nsBlockFrame.h"
20 #include "nsIFrameInlines.h"
24 #undef REALLY_NOISY_BIDI
26 using namespace mozilla
;
28 static const char16_t kSpace
= 0x0020;
29 static const char16_t kZWSP
= 0x200B;
30 static const char16_t kLineSeparator
= 0x2028;
31 static const char16_t kObjectSubstitute
= 0xFFFC;
32 static const char16_t kLRE
= 0x202A;
33 static const char16_t kRLE
= 0x202B;
34 static const char16_t kLRO
= 0x202D;
35 static const char16_t kRLO
= 0x202E;
36 static const char16_t kPDF
= 0x202C;
37 static const char16_t kSeparators
[] = {
38 // All characters with Bidi type Segment Separator or Block Separator
52 #define NS_BIDI_CONTROL_FRAME ((nsIFrame*)0xfffb1d1)
54 struct BidiParagraphData
{
56 nsAutoTArray
<char16_t
, 16> mEmbeddingStack
;
57 nsTArray
<nsIFrame
*> mLogicalFrames
;
58 nsTArray
<nsLineBox
*> mLinePerFrame
;
59 nsDataHashtable
<nsISupportsHashKey
, int32_t> mContentToFrameIndex
;
62 nsBidiLevel mParaLevel
;
63 nsIContent
* mPrevContent
;
64 nsAutoPtr
<nsBidi
> mBidiEngine
;
66 nsAutoPtr
<BidiParagraphData
> mSubParagraph
;
67 uint8_t mParagraphDepth
;
69 void Init(nsBlockFrame
*aBlockFrame
)
71 mBidiEngine
= new nsBidi();
72 mPrevContent
= nullptr;
75 mParaLevel
= nsBidiPresUtils::BidiLevelFromStyle(aBlockFrame
->StyleContext());
77 mIsVisual
= aBlockFrame
->PresContext()->IsVisualMode();
80 * Drill up in content to detect whether this is an element that needs to
81 * be rendered with logical order even on visual pages.
83 * We always use logical order on form controls, firstly so that text
84 * entry will be in logical order, but also because visual pages were
85 * written with the assumption that even if the browser had no support
86 * for right-to-left text rendering, it would use native widgets with
87 * bidi support to display form controls.
89 * We also use logical order in XUL elements, since we expect that if a
90 * XUL element appears in a visual page, it will be generated by an XBL
91 * binding and contain localized text which will be in logical order.
93 for (nsIContent
* content
= aBlockFrame
->GetContent() ; content
;
94 content
= content
->GetParent()) {
95 if (content
->IsNodeOfType(nsINode::eHTML_FORM_CONTROL
) ||
104 BidiParagraphData
* GetSubParagraph()
106 if (!mSubParagraph
) {
107 mSubParagraph
= new BidiParagraphData();
108 mSubParagraph
->Init(this);
111 return mSubParagraph
;
114 // Initialise a sub-paragraph from its containing paragraph
115 void Init(BidiParagraphData
*aBpd
)
117 mBidiEngine
= new nsBidi();
118 mPrevContent
= nullptr;
119 mIsVisual
= aBpd
->mIsVisual
;
123 void Reset(nsIFrame
* aBDIFrame
, BidiParagraphData
*aBpd
)
126 mLogicalFrames
.Clear();
127 mLinePerFrame
.Clear();
128 mContentToFrameIndex
.Clear();
129 mBuffer
.SetLength(0);
130 mPrevFrame
= aBpd
->mPrevFrame
;
131 mParagraphDepth
= aBpd
->mParagraphDepth
+ 1;
133 const nsStyleTextReset
* text
= aBDIFrame
->StyleTextReset();
134 bool isRTL
= (NS_STYLE_DIRECTION_RTL
==
135 aBDIFrame
->StyleVisibility()->mDirection
);
137 if (text
->mUnicodeBidi
& NS_STYLE_UNICODE_BIDI_PLAINTEXT
) {
138 mParaLevel
= NSBIDI_DEFAULT_LTR
;
140 mParaLevel
= mParagraphDepth
* 2;
141 if (isRTL
) ++mParaLevel
;
144 if (text
->mUnicodeBidi
& NS_STYLE_UNICODE_BIDI_OVERRIDE
) {
145 PushBidiControl(isRTL
? kRLO
: kLRO
);
151 mBuffer
.SetLength(0);
156 return mBidiEngine
->SetPara(mBuffer
.get(), BufferLength(),
157 mParaLevel
, nullptr);
161 * mParaLevel can be NSBIDI_DEFAULT_LTR as well as NSBIDI_LTR or NSBIDI_RTL.
162 * GetParaLevel() returns the actual (resolved) paragraph level which is
163 * always either NSBIDI_LTR or NSBIDI_RTL
165 nsBidiLevel
GetParaLevel()
167 nsBidiLevel paraLevel
= mParaLevel
;
168 if (IS_DEFAULT_LEVEL(paraLevel
)) {
169 mBidiEngine
->GetParaLevel(¶Level
);
174 nsBidiDirection
GetDirection()
177 mBidiEngine
->GetDirection(&dir
);
181 nsresult
CountRuns(int32_t *runCount
){ return mBidiEngine
->CountRuns(runCount
); }
183 nsresult
GetLogicalRun(int32_t aLogicalStart
,
184 int32_t* aLogicalLimit
,
187 nsresult rv
= mBidiEngine
->GetLogicalRun(aLogicalStart
,
188 aLogicalLimit
, aLevel
);
189 if (mIsVisual
|| NS_FAILED(rv
))
190 *aLevel
= GetParaLevel();
196 mLogicalFrames
.Clear();
197 mLinePerFrame
.Clear();
198 mContentToFrameIndex
.Clear();
199 mBuffer
.SetLength(0);
200 mPrevContent
= nullptr;
201 for (uint32_t i
= 0; i
< mEmbeddingStack
.Length(); ++i
) {
202 mBuffer
.Append(mEmbeddingStack
[i
]);
203 mLogicalFrames
.AppendElement(NS_BIDI_CONTROL_FRAME
);
204 mLinePerFrame
.AppendElement((nsLineBox
*)nullptr);
208 void ResetForNewBlock()
210 for (BidiParagraphData
* bpd
= this; bpd
; bpd
= bpd
->mSubParagraph
) {
211 bpd
->mPrevFrame
= nullptr;
215 void AppendFrame(nsIFrame
* aFrame
,
216 nsBlockInFlowLineIterator
* aLineIter
,
217 nsIContent
* aContent
= nullptr)
220 mContentToFrameIndex
.Put(aContent
, FrameCount());
222 mLogicalFrames
.AppendElement(aFrame
);
224 AdvanceLineIteratorToFrame(aFrame
, aLineIter
, mPrevFrame
);
225 mLinePerFrame
.AppendElement(aLineIter
->GetLine().get());
228 void AdvanceAndAppendFrame(nsIFrame
** aFrame
,
229 nsBlockInFlowLineIterator
* aLineIter
,
230 nsIFrame
** aNextSibling
)
232 nsIFrame
* frame
= *aFrame
;
233 nsIFrame
* nextSibling
= *aNextSibling
;
235 frame
= frame
->GetNextContinuation();
237 AppendFrame(frame
, aLineIter
, nullptr);
240 * If we have already overshot the saved next-sibling while
241 * scanning the frame's continuations, advance it.
243 if (frame
== nextSibling
) {
244 nextSibling
= frame
->GetNextSibling();
249 *aNextSibling
= nextSibling
;
252 int32_t GetLastFrameForContent(nsIContent
*aContent
)
255 mContentToFrameIndex
.Get(aContent
, &index
);
259 int32_t FrameCount(){ return mLogicalFrames
.Length(); }
261 int32_t BufferLength(){ return mBuffer
.Length(); }
263 nsIFrame
* FrameAt(int32_t aIndex
){ return mLogicalFrames
[aIndex
]; }
265 nsLineBox
* GetLineForFrameAt(int32_t aIndex
){ return mLinePerFrame
[aIndex
]; }
267 void AppendUnichar(char16_t aCh
){ mBuffer
.Append(aCh
); }
269 void AppendString(const nsDependentSubstring
& aString
){ mBuffer
.Append(aString
); }
271 void AppendControlChar(char16_t aCh
)
273 mLogicalFrames
.AppendElement(NS_BIDI_CONTROL_FRAME
);
274 mLinePerFrame
.AppendElement((nsLineBox
*)nullptr);
278 void PushBidiControl(char16_t aCh
)
280 AppendControlChar(aCh
);
281 mEmbeddingStack
.AppendElement(aCh
);
284 void PopBidiControl()
286 AppendControlChar(kPDF
);
287 NS_ASSERTION(mEmbeddingStack
.Length(), "embedding/override underflow");
288 mEmbeddingStack
.TruncateLength(mEmbeddingStack
.Length() - 1);
291 void ClearBidiControls()
293 for (uint32_t i
= 0; i
< mEmbeddingStack
.Length(); ++i
) {
294 AppendControlChar(kPDF
);
299 IsFrameInCurrentLine(nsBlockInFlowLineIterator
* aLineIter
,
300 nsIFrame
* aPrevFrame
, nsIFrame
* aFrame
)
302 nsIFrame
* endFrame
= aLineIter
->IsLastLineInList() ? nullptr :
303 aLineIter
->GetLine().next()->mFirstChild
;
304 nsIFrame
* startFrame
= aPrevFrame
? aPrevFrame
: aLineIter
->GetLine()->mFirstChild
;
305 for (nsIFrame
* frame
= startFrame
; frame
&& frame
!= endFrame
;
306 frame
= frame
->GetNextSibling()) {
314 AdvanceLineIteratorToFrame(nsIFrame
* aFrame
,
315 nsBlockInFlowLineIterator
* aLineIter
,
316 nsIFrame
*& aPrevFrame
)
318 // Advance aLine to the line containing aFrame
319 nsIFrame
* child
= aFrame
;
320 nsIFrame
* parent
= nsLayoutUtils::GetParentOrPlaceholderFor(child
);
321 while (parent
&& !nsLayoutUtils::GetAsBlock(parent
)) {
323 parent
= nsLayoutUtils::GetParentOrPlaceholderFor(child
);
325 NS_ASSERTION (parent
, "aFrame is not a descendent of aBlockFrame");
326 while (!IsFrameInCurrentLine(aLineIter
, aPrevFrame
, child
)) {
331 NS_ASSERTION(hasNext
, "Can't find frame in lines!");
332 aPrevFrame
= nullptr;
339 struct BidiLineData
{
340 nsTArray
<nsIFrame
*> mLogicalFrames
;
341 nsTArray
<nsIFrame
*> mVisualFrames
;
342 nsTArray
<int32_t> mIndexMap
;
343 nsAutoTArray
<uint8_t, 18> mLevels
;
346 BidiLineData(nsIFrame
* aFirstFrameOnLine
, int32_t aNumFramesOnLine
)
349 * Initialize the logically-ordered array of frames using the top-level
350 * frames of a single line
352 mLogicalFrames
.Clear();
354 bool isReordered
= false;
355 bool hasRTLFrames
= false;
357 for (nsIFrame
* frame
= aFirstFrameOnLine
;
358 frame
&& aNumFramesOnLine
--;
359 frame
= frame
->GetNextSibling()) {
361 nsBidiLevel level
= nsBidiPresUtils::GetFrameEmbeddingLevel(frame
);
362 mLevels
.AppendElement(level
);
363 mIndexMap
.AppendElement(0);
364 if (IS_LEVEL_RTL(level
)) {
370 nsBidi::ReorderVisual(mLevels
.Elements(), FrameCount(),
371 mIndexMap
.Elements());
373 for (int32_t i
= 0; i
< FrameCount(); i
++) {
374 mVisualFrames
.AppendElement(LogicalFrameAt(mIndexMap
[i
]));
375 if (i
!= mIndexMap
[i
]) {
380 // If there's an RTL frame, assume the line is reordered
381 mIsReordered
= isReordered
|| hasRTLFrames
;
384 void AppendFrame(nsIFrame
* aFrame
)
386 mLogicalFrames
.AppendElement(aFrame
);
389 int32_t FrameCount(){ return mLogicalFrames
.Length(); }
391 nsIFrame
* LogicalFrameAt(int32_t aIndex
){ return mLogicalFrames
[aIndex
]; }
393 nsIFrame
* VisualFrameAt(int32_t aIndex
){ return mVisualFrames
[aIndex
]; }
396 /* Some helper methods for Resolve() */
398 // Should this frame be split between text runs?
400 IsBidiSplittable(nsIFrame
* aFrame
)
402 // Bidi inline containers should be split, unless they're line frames.
403 nsIAtom
* frameType
= aFrame
->GetType();
404 return (aFrame
->IsFrameOfType(nsIFrame::eBidiInlineContainer
) &&
405 frameType
!= nsGkAtoms::lineFrame
) ||
406 frameType
== nsGkAtoms::textFrame
;
409 // Should this frame be treated as a leaf (e.g. when building mLogicalFrames)?
411 IsBidiLeaf(nsIFrame
* aFrame
)
413 nsIFrame
* kid
= aFrame
->GetFirstPrincipalChild();
414 return !kid
|| !aFrame
->IsFrameOfType(nsIFrame::eBidiInlineContainer
);
418 * Create non-fluid continuations for the ancestors of a given frame all the way
419 * up the frame tree until we hit a non-splittable frame (a line or a block).
421 * @param aParent the first parent frame to be split
422 * @param aFrame the child frames after this frame are reparented to the
423 * newly-created continuation of aParent.
424 * If aFrame is null, all the children of aParent are reparented.
427 SplitInlineAncestors(nsContainerFrame
* aParent
,
430 nsPresContext
* presContext
= aParent
->PresContext();
431 nsIPresShell
* presShell
= presContext
->PresShell();
432 nsIFrame
* frame
= aFrame
;
433 nsContainerFrame
* parent
= aParent
;
434 nsContainerFrame
* newParent
;
436 while (IsBidiSplittable(parent
)) {
437 nsContainerFrame
* grandparent
= parent
->GetParent();
438 NS_ASSERTION(grandparent
, "Couldn't get parent's parent in nsBidiPresUtils::SplitInlineAncestors");
440 // Split the child list after |frame|, unless it is the last child.
441 if (!frame
|| frame
->GetNextSibling()) {
443 newParent
= static_cast<nsContainerFrame
*>(presShell
->FrameConstructor()->
444 CreateContinuingFrame(presContext
, parent
, grandparent
, false));
446 nsFrameList tail
= parent
->StealFramesAfter(frame
);
448 // Reparent views as necessary
450 rv
= nsContainerFrame::ReparentFrameViewList(tail
, parent
, newParent
);
455 // The parent's continuation adopts the siblings after the split.
456 newParent
->InsertFrames(nsIFrame::kNoReflowPrincipalList
, nullptr, tail
);
458 // The list name kNoReflowPrincipalList would indicate we don't want reflow
459 nsFrameList
temp(newParent
, newParent
);
460 grandparent
->InsertFrames(nsIFrame::kNoReflowPrincipalList
, parent
, temp
);
464 parent
= grandparent
;
471 MakeContinuationFluid(nsIFrame
* aFrame
, nsIFrame
* aNext
)
473 NS_ASSERTION (!aFrame
->GetNextInFlow() || aFrame
->GetNextInFlow() == aNext
,
474 "next-in-flow is not next continuation!");
475 aFrame
->SetNextInFlow(aNext
);
477 NS_ASSERTION (!aNext
->GetPrevInFlow() || aNext
->GetPrevInFlow() == aFrame
,
478 "prev-in-flow is not prev continuation!");
479 aNext
->SetPrevInFlow(aFrame
);
483 MakeContinuationsNonFluidUpParentChain(nsIFrame
* aFrame
, nsIFrame
* aNext
)
488 for (frame
= aFrame
, next
= aNext
;
490 next
!= frame
&& next
== frame
->GetNextInFlow() &&
491 IsBidiSplittable(frame
);
492 frame
= frame
->GetParent(), next
= next
->GetParent()) {
494 frame
->SetNextContinuation(next
);
495 next
->SetPrevContinuation(frame
);
499 // If aFrame is the last child of its parent, convert bidi continuations to
500 // fluid continuations for all of its inline ancestors.
501 // If it isn't the last child, make sure that its continuation is fluid.
503 JoinInlineAncestors(nsIFrame
* aFrame
)
505 nsIFrame
* frame
= aFrame
;
507 nsIFrame
* next
= frame
->GetNextContinuation();
509 // Don't join frames if they come from different paragraph depths (i.e.
510 // one is bidi isolated relative to the other
511 if (nsBidiPresUtils::GetParagraphDepth(frame
) ==
512 nsBidiPresUtils::GetParagraphDepth(next
)) {
513 MakeContinuationFluid(frame
, next
);
516 // Join the parent only as long as we're its last child.
517 if (frame
->GetNextSibling())
519 frame
= frame
->GetParent();
520 } while (frame
&& IsBidiSplittable(frame
));
524 CreateContinuation(nsIFrame
* aFrame
,
525 nsIFrame
** aNewFrame
,
528 NS_PRECONDITION(aNewFrame
, "null OUT ptr");
529 NS_PRECONDITION(aFrame
, "null ptr");
531 *aNewFrame
= nullptr;
533 nsPresContext
*presContext
= aFrame
->PresContext();
534 nsIPresShell
*presShell
= presContext
->PresShell();
535 NS_ASSERTION(presShell
, "PresShell must be set on PresContext before calling nsBidiPresUtils::CreateContinuation");
537 nsContainerFrame
* parent
= aFrame
->GetParent();
538 NS_ASSERTION(parent
, "Couldn't get frame parent in nsBidiPresUtils::CreateContinuation");
542 // Have to special case floating first letter frames because the continuation
543 // doesn't go in the first letter frame. The continuation goes with the rest
544 // of the text that the first letter frame was made out of.
545 if (parent
->GetType() == nsGkAtoms::letterFrame
&&
546 parent
->IsFloating()) {
547 nsFirstLetterFrame
* letterFrame
= do_QueryFrame(parent
);
548 rv
= letterFrame
->CreateContinuationForFloatingParent(presContext
, aFrame
,
549 aNewFrame
, aIsFluid
);
553 *aNewFrame
= presShell
->FrameConstructor()->
554 CreateContinuingFrame(presContext
, aFrame
, parent
, aIsFluid
);
556 // The list name kNoReflowPrincipalList would indicate we don't want reflow
557 // XXXbz this needs higher-level framelist love
558 nsFrameList
temp(*aNewFrame
, *aNewFrame
);
559 parent
->InsertFrames(nsIFrame::kNoReflowPrincipalList
, aFrame
, temp
);
562 // Split inline ancestor frames
563 rv
= SplitInlineAncestors(parent
, aFrame
);
573 * Overview of the implementation of Resolve():
575 * Walk through the descendants of aBlockFrame and build:
576 * * mLogicalFrames: an nsTArray of nsIFrame* pointers in logical order
577 * * mBuffer: an nsString containing a representation of
578 * the content of the frames.
579 * In the case of text frames, this is the actual text context of the
580 * frames, but some other elements are represented in a symbolic form which
581 * will make the Unicode Bidi Algorithm give the correct results.
582 * Bidi embeddings and overrides set by CSS or <bdo> elements are
583 * represented by the corresponding Unicode control characters.
584 * <br> elements are represented by U+2028 LINE SEPARATOR
585 * Other inline elements are represented by U+FFFC OBJECT REPLACEMENT
588 * Then pass mBuffer to the Bidi engine for resolving of embedding levels
589 * by nsBidi::SetPara() and division into directional runs by
590 * nsBidi::CountRuns().
592 * Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and
593 * correlate them with the frames indexed in mLogicalFrames, setting the
594 * baseLevel and embeddingLevel properties according to the results returned
595 * by the Bidi engine.
597 * The rendering layer requires each text frame to contain text in only one
598 * direction, so we may need to call EnsureBidiContinuation() to split frames.
599 * We may also need to call RemoveBidiContinuation() to convert frames created
600 * by EnsureBidiContinuation() in previous reflows into fluid continuations.
603 nsBidiPresUtils::Resolve(nsBlockFrame
* aBlockFrame
)
605 BidiParagraphData bpd
;
606 bpd
.Init(aBlockFrame
);
608 // Handle bidi-override being set on the block itself before calling
610 const nsStyleTextReset
* text
= aBlockFrame
->StyleTextReset();
612 if (text
->mUnicodeBidi
& NS_STYLE_UNICODE_BIDI_OVERRIDE
) {
613 const nsStyleVisibility
* vis
= aBlockFrame
->StyleVisibility();
614 if (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
) {
617 else if (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
) {
621 bpd
.PushBidiControl(ch
);
624 for (nsBlockFrame
* block
= aBlockFrame
; block
;
625 block
= static_cast<nsBlockFrame
*>(block
->GetNextContinuation())) {
626 block
->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION
);
627 nsBlockInFlowLineIterator
lineIter(block
, block
->begin_lines());
628 bpd
.ResetForNewBlock();
629 TraverseFrames(aBlockFrame
, &lineIter
, block
->GetFirstPrincipalChild(), &bpd
);
630 // XXX what about overflow lines?
634 bpd
.PopBidiControl();
637 BidiParagraphData
* subParagraph
= bpd
.GetSubParagraph();
638 if (subParagraph
->BufferLength()) {
639 ResolveParagraph(aBlockFrame
, subParagraph
);
640 subParagraph
->EmptyBuffer();
642 return ResolveParagraph(aBlockFrame
, &bpd
);
646 nsBidiPresUtils::ResolveParagraph(nsBlockFrame
* aBlockFrame
,
647 BidiParagraphData
* aBpd
)
649 nsPresContext
*presContext
= aBlockFrame
->PresContext();
651 if (aBpd
->BufferLength() < 1) {
654 aBpd
->mBuffer
.ReplaceChar(kSeparators
, kSpace
);
658 nsresult rv
= aBpd
->SetPara();
659 NS_ENSURE_SUCCESS(rv
, rv
);
661 nsBidiLevel embeddingLevel
= aBpd
->GetParaLevel();
663 rv
= aBpd
->CountRuns(&runCount
);
664 NS_ENSURE_SUCCESS(rv
, rv
);
666 int32_t runLength
= 0; // the length of the current run of text
667 int32_t lineOffset
= 0; // the start of the current run
668 int32_t logicalLimit
= 0; // the end of the current run + 1
670 int32_t fragmentLength
= 0; // the length of the current text frame
671 int32_t frameIndex
= -1; // index to the frames in mLogicalFrames
672 int32_t frameCount
= aBpd
->FrameCount();
673 int32_t contentOffset
= 0; // offset of current frame in its content node
674 bool isTextFrame
= false;
675 nsIFrame
* frame
= nullptr;
676 nsIContent
* content
= nullptr;
677 int32_t contentTextLength
= 0;
679 FramePropertyTable
*propTable
= presContext
->PropertyTable();
680 nsLineBox
* currentLine
= nullptr;
684 printf("Before Resolve(), aBlockFrame=0x%p, mBuffer='%s', frameCount=%d, runCount=%d\n",
685 (void*)aBlockFrame
, NS_ConvertUTF16toUTF8(aBpd
->mBuffer
).get(), frameCount
, runCount
);
686 #ifdef REALLY_NOISY_BIDI
687 printf(" block frame tree=:\n");
688 aBlockFrame
->List(stdout
, 0);
693 if (runCount
== 1 && frameCount
== 1 &&
694 aBpd
->mParagraphDepth
== 0 && aBpd
->GetDirection() == NSBIDI_LTR
&&
695 aBpd
->GetParaLevel() == 0) {
696 // We have a single left-to-right frame in a left-to-right paragraph,
697 // without bidi isolation from the surrounding text.
698 // Make sure that the embedding level and base level frame properties aren't
699 // set (because if they are this frame used to have some other direction,
700 // so we can't do this optimization), and we're done.
701 nsIFrame
* frame
= aBpd
->FrameAt(0);
702 if (frame
!= NS_BIDI_CONTROL_FRAME
&&
703 !frame
->Properties().Get(nsIFrame::EmbeddingLevelProperty()) &&
704 !frame
->Properties().Get(nsIFrame::BaseLevelProperty())) {
707 printf("early return for single direction frame %p\n", (void*)frame
);
710 frame
->AddStateBits(NS_FRAME_IS_BIDI
);
715 nsIFrame
* firstFrame
= nullptr;
716 nsIFrame
* lastFrame
= nullptr;
719 if (fragmentLength
<= 0) {
720 // Get the next frame from mLogicalFrames
721 if (++frameIndex
>= frameCount
) {
724 frame
= aBpd
->FrameAt(frameIndex
);
725 if (frame
== NS_BIDI_CONTROL_FRAME
||
726 nsGkAtoms::textFrame
!= frame
->GetType()) {
728 * Any non-text frame corresponds to a single character in the text buffer
729 * (a bidi control character, LINE SEPARATOR, or OBJECT SUBSTITUTE)
739 currentLine
= aBpd
->GetLineForFrameAt(frameIndex
);
740 content
= frame
->GetContent();
745 contentTextLength
= content
->TextLength();
746 if (contentTextLength
== 0) {
747 frame
->AdjustOffsetsForBidi(0, 0);
748 // Set the base level and embedding level of the current run even
749 // on an empty frame. Otherwise frame reordering will not be correct.
750 propTable
->Set(frame
, nsIFrame::EmbeddingLevelProperty(),
751 NS_INT32_TO_PTR(embeddingLevel
));
752 propTable
->Set(frame
, nsIFrame::BaseLevelProperty(),
753 NS_INT32_TO_PTR(aBpd
->GetParaLevel()));
754 propTable
->Set(frame
, nsIFrame::ParagraphDepthProperty(),
755 NS_INT32_TO_PTR(aBpd
->mParagraphDepth
));
759 frame
->GetOffsets(start
, end
);
760 NS_ASSERTION(!(contentTextLength
< end
- start
),
761 "Frame offsets don't fit in content");
762 fragmentLength
= std::min(contentTextLength
, end
- start
);
763 contentOffset
= start
;
766 } // if (fragmentLength <= 0)
768 if (runLength
<= 0) {
769 // Get the next run of text from the Bidi engine
770 if (++numRun
>= runCount
) {
773 lineOffset
= logicalLimit
;
774 if (NS_FAILED(aBpd
->GetLogicalRun(
775 lineOffset
, &logicalLimit
, &embeddingLevel
) ) ) {
778 runLength
= logicalLimit
- lineOffset
;
779 } // if (runLength <= 0)
781 if (frame
== NS_BIDI_CONTROL_FRAME
) {
786 propTable
->Set(frame
, nsIFrame::EmbeddingLevelProperty(),
787 NS_INT32_TO_PTR(embeddingLevel
));
788 propTable
->Set(frame
, nsIFrame::BaseLevelProperty(),
789 NS_INT32_TO_PTR(aBpd
->GetParaLevel()));
790 propTable
->Set(frame
, nsIFrame::ParagraphDepthProperty(),
791 NS_INT32_TO_PTR(aBpd
->mParagraphDepth
));
793 if ( (runLength
> 0) && (runLength
< fragmentLength
) ) {
795 * The text in this frame continues beyond the end of this directional run.
796 * Create a non-fluid continuation frame for the next directional run.
798 currentLine
->MarkDirty();
800 int32_t runEnd
= contentOffset
+ runLength
;
801 rv
= EnsureBidiContinuation(frame
, &nextBidi
, frameIndex
,
807 nextBidi
->AdjustOffsetsForBidi(runEnd
,
808 contentOffset
+ fragmentLength
);
809 lastFrame
= frame
= nextBidi
;
810 contentOffset
= runEnd
;
811 } // if (runLength < fragmentLength)
813 if (contentOffset
+ fragmentLength
== contentTextLength
) {
815 * We have finished all the text in this content node. Convert any
816 * further non-fluid continuations to fluid continuations and advance
817 * frameIndex to the last frame in the content node
819 int32_t newIndex
= aBpd
->GetLastFrameForContent(content
);
820 if (newIndex
> frameIndex
) {
821 currentLine
->MarkDirty();
822 RemoveBidiContinuation(aBpd
, frame
,
823 frameIndex
, newIndex
, lineOffset
);
824 frameIndex
= newIndex
;
825 lastFrame
= frame
= aBpd
->FrameAt(frameIndex
);
827 } else if (fragmentLength
> 0 && runLength
> fragmentLength
) {
829 * There is more text that belongs to this directional run in the next
830 * text frame: make sure it is a fluid continuation of the current frame.
831 * Do not advance frameIndex, because the next frame may contain
832 * multi-directional text and need to be split
834 int32_t newIndex
= frameIndex
;
836 } while (++newIndex
< frameCount
&&
837 aBpd
->FrameAt(newIndex
) == NS_BIDI_CONTROL_FRAME
);
838 if (newIndex
< frameCount
) {
839 currentLine
->MarkDirty();
840 RemoveBidiContinuation(aBpd
, frame
,
841 frameIndex
, newIndex
, lineOffset
);
843 } else if (runLength
== fragmentLength
) {
845 * If the directional run ends at the end of the frame, make sure
846 * that any continuation is non-fluid, and do the same up the
849 nsIFrame
* next
= frame
->GetNextInFlow();
851 currentLine
->MarkDirty();
852 MakeContinuationsNonFluidUpParentChain(frame
, next
);
855 frame
->AdjustOffsetsForBidi(contentOffset
, contentOffset
+ fragmentLength
);
861 } // not bidi control frame
862 int32_t temp
= runLength
;
863 runLength
-= fragmentLength
;
864 fragmentLength
-= temp
;
866 if (frame
&& fragmentLength
<= 0) {
867 // If the frame is at the end of a run, and this is not the end of our
868 // paragrah, split all ancestor inlines that need splitting.
869 // To determine whether we're at the end of the run, we check that we've
870 // finished processing the current run, and that the current frame
871 // doesn't have a fluid continuation (it could have a fluid continuation
872 // of zero length, so testing runLength alone is not sufficient).
873 if (runLength
<= 0 && !frame
->GetNextInFlow()) {
874 if (numRun
+ 1 < runCount
) {
875 nsIFrame
* child
= frame
;
876 nsContainerFrame
* parent
= frame
->GetParent();
877 // As long as we're on the last sibling, the parent doesn't have to
879 // However, if the parent has a fluid continuation, we do have to make
880 // it non-fluid. This can happen e.g. when we have a first-letter
881 // frame and the end of the first-letter coincides with the end of a
884 IsBidiSplittable(parent
) &&
885 !child
->GetNextSibling()) {
886 nsIFrame
* next
= parent
->GetNextInFlow();
888 parent
->SetNextContinuation(next
);
889 next
->SetPrevContinuation(parent
);
892 parent
= child
->GetParent();
894 if (parent
&& IsBidiSplittable(parent
)) {
895 SplitInlineAncestors(parent
, child
);
900 // We're not at an end of a run. If |frame| is the last child of its
901 // parent, and its ancestors happen to have bidi continuations, convert
902 // them into fluid continuations.
903 JoinInlineAncestors(frame
);
908 if (aBpd
->mParagraphDepth
> 0) {
910 nsContainerFrame
* child
= firstFrame
->GetParent();
912 nsContainerFrame
* parent
= child
->GetParent();
913 if (parent
&& IsBidiSplittable(parent
)) {
914 nsIFrame
* prev
= child
->GetPrevSibling();
916 SplitInlineAncestors(parent
, prev
);
922 nsContainerFrame
* child
= lastFrame
->GetParent();
924 nsContainerFrame
* parent
= child
->GetParent();
925 if (parent
&& IsBidiSplittable(parent
)) {
926 SplitInlineAncestors(parent
, child
);
933 #ifdef REALLY_NOISY_BIDI
934 printf("---\nAfter Resolve(), frameTree =:\n");
935 aBlockFrame
->List(stdout
, 0);
944 nsBidiPresUtils::TraverseFrames(nsBlockFrame
* aBlockFrame
,
945 nsBlockInFlowLineIterator
* aLineIter
,
946 nsIFrame
* aCurrentFrame
,
947 BidiParagraphData
* aBpd
)
953 nsBlockFrame
* initialLineContainer
= aLineIter
->GetContainer();
956 nsIFrame
* childFrame
= aCurrentFrame
;
959 * It's important to get the next sibling and next continuation *before*
960 * handling the frame: If we encounter a forced paragraph break and call
961 * ResolveParagraph within this loop, doing GetNextSibling and
962 * GetNextContinuation after that could return a bidi continuation that had
963 * just been split from the original childFrame and we would process it
966 nsIFrame
* nextSibling
= childFrame
->GetNextSibling();
967 bool isLastFrame
= !childFrame
->GetNextContinuation();
968 bool isFirstFrame
= !childFrame
->GetPrevContinuation();
970 // If the real frame for a placeholder is a first letter frame, we need to
971 // drill down into it and include its contents in Bidi resolution.
972 // If not, we just use the placeholder.
973 nsIFrame
* frame
= childFrame
;
974 if (nsGkAtoms::placeholderFrame
== childFrame
->GetType()) {
975 nsIFrame
* realFrame
=
976 nsPlaceholderFrame::GetRealFrameForPlaceholder(childFrame
);
977 if (realFrame
->GetType() == nsGkAtoms::letterFrame
) {
983 if (frame
->IsFrameOfType(nsIFrame::eBidiInlineContainer
)) {
984 if (!(frame
->GetStateBits() & NS_FRAME_FIRST_REFLOW
)) {
985 nsContainerFrame
* c
= static_cast<nsContainerFrame
*>(frame
);
986 MOZ_ASSERT(c
= do_QueryFrame(frame
),
987 "eBidiInlineContainer must be a nsContainerFrame subclass");
988 c
->DrainSelfOverflowList();
991 const nsStyleVisibility
* vis
= frame
->StyleVisibility();
992 const nsStyleTextReset
* text
= frame
->StyleTextReset();
993 if (text
->mUnicodeBidi
& NS_STYLE_UNICODE_BIDI_OVERRIDE
) {
994 if (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
) {
997 else if (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
) {
1000 } else if (text
->mUnicodeBidi
& NS_STYLE_UNICODE_BIDI_EMBED
) {
1001 if (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
) {
1004 else if (NS_STYLE_DIRECTION_LTR
== vis
->mDirection
) {
1009 // Add a dummy frame pointer representing a bidi control code before the
1010 // first frame of an element specifying embedding or override
1011 if (ch
!= 0 && isFirstFrame
) {
1012 aBpd
->PushBidiControl(ch
);
1016 if (IsBidiLeaf(frame
)) {
1017 /* Bidi leaf frame: add the frame to the mLogicalFrames array,
1018 * and add its index to the mContentToFrameIndex hashtable. This
1019 * will be used in RemoveBidiContinuation() to identify the last
1020 * frame in the array with a given content.
1022 nsIContent
* content
= frame
->GetContent();
1023 aBpd
->AppendFrame(frame
, aLineIter
, content
);
1025 // Append the content of the frame to the paragraph buffer
1026 nsIAtom
* frameType
= frame
->GetType();
1027 if (nsGkAtoms::textFrame
== frameType
) {
1028 if (content
!= aBpd
->mPrevContent
) {
1029 aBpd
->mPrevContent
= content
;
1030 if (!frame
->StyleText()->NewlineIsSignificant()) {
1031 content
->AppendTextTo(aBpd
->mBuffer
);
1034 * For preformatted text we have to do bidi resolution on each line
1038 content
->AppendTextTo(text
);
1044 frame
->GetOffsets(start
, end
);
1045 int32_t endLine
= text
.FindChar('\n', start
);
1046 if (endLine
== -1) {
1048 * If there is no newline in the text content, just save the
1049 * text from this frame and its continuations, and do bidi
1052 aBpd
->AppendString(Substring(text
, start
));
1053 while (frame
&& nextSibling
) {
1054 aBpd
->AdvanceAndAppendFrame(&frame
, aLineIter
, &nextSibling
);
1060 * If there is a newline in the frame, break the frame after the
1061 * newline, do bidi resolution and repeat until the last sibling
1066 * If the frame ends before the new line, save the text and move
1067 * into the next continuation
1069 aBpd
->AppendString(Substring(text
, start
,
1070 std::min(end
, endLine
) - start
));
1071 while (end
< endLine
&& nextSibling
) {
1072 aBpd
->AdvanceAndAppendFrame(&frame
, aLineIter
, &nextSibling
);
1073 NS_ASSERTION(frame
, "Premature end of continuation chain");
1074 frame
->GetOffsets(start
, end
);
1075 aBpd
->AppendString(Substring(text
, start
,
1076 std::min(end
, endLine
) - start
));
1079 if (end
< endLine
) {
1080 aBpd
->mPrevContent
= nullptr;
1084 bool createdContinuation
= false;
1085 if (uint32_t(endLine
) < text
.Length()) {
1087 * Timing is everything here: if the frame already has a bidi
1088 * continuation, we need to make the continuation fluid *before*
1089 * resetting the length of the current frame. Otherwise
1090 * nsTextFrame::SetLength won't set the continuation frame's
1091 * text offsets correctly.
1093 * On the other hand, if the frame doesn't have a continuation,
1094 * we need to create one *after* resetting the length, or
1095 * CreateContinuingFrame will complain that there is no more
1096 * content for the continuation.
1098 next
= frame
->GetNextInFlow();
1100 // If the frame already has a bidi continuation, make it fluid
1101 next
= frame
->GetNextContinuation();
1103 MakeContinuationFluid(frame
, next
);
1104 JoinInlineAncestors(frame
);
1108 nsTextFrame
* textFrame
= static_cast<nsTextFrame
*>(frame
);
1109 textFrame
->SetLength(endLine
- start
, nullptr);
1112 // If the frame has no next in flow, create one.
1113 CreateContinuation(frame
, &next
, true);
1114 createdContinuation
= true;
1116 // Mark the line before the newline as dirty.
1117 aBpd
->GetLineForFrameAt(aBpd
->FrameCount() - 1)->MarkDirty();
1119 ResolveParagraphWithinBlock(aBlockFrame
, aBpd
);
1121 if (!nextSibling
&& !createdContinuation
) {
1125 aBpd
->AppendFrame(frame
, aLineIter
);
1126 // Mark the line after the newline as dirty.
1127 aBpd
->GetLineForFrameAt(aBpd
->FrameCount() - 1)->MarkDirty();
1131 * If we have already overshot the saved next-sibling while
1132 * scanning the frame's continuations, advance it.
1134 if (frame
&& frame
== nextSibling
) {
1135 nextSibling
= frame
->GetNextSibling();
1141 } else if (nsGkAtoms::brFrame
== frameType
) {
1142 // break frame -- append line separator
1143 aBpd
->AppendUnichar(kLineSeparator
);
1144 ResolveParagraphWithinBlock(aBlockFrame
, aBpd
);
1146 // other frame type -- see the Unicode Bidi Algorithm:
1147 // "...inline objects (such as graphics) are treated as if they are ...
1149 // <wbr>, however, is treated as U+200B ZERO WIDTH SPACE. See
1150 // http://dev.w3.org/html5/spec/Overview.html#phrasing-content-1
1151 aBpd
->AppendUnichar(content
->IsHTML(nsGkAtoms::wbr
) ?
1152 kZWSP
: kObjectSubstitute
);
1153 if (!frame
->IsInlineOutside()) {
1154 // if it is not inline, end the paragraph
1155 ResolveParagraphWithinBlock(aBlockFrame
, aBpd
);
1159 // For a non-leaf frame, recurse into TraverseFrames
1160 nsIFrame
* kid
= frame
->GetFirstPrincipalChild();
1161 MOZ_ASSERT(!frame
->GetFirstChild(nsIFrame::kOverflowList
),
1162 "should have drained the overflow list above");
1164 const nsStyleTextReset
* text
= frame
->StyleTextReset();
1165 if (text
->mUnicodeBidi
& NS_STYLE_UNICODE_BIDI_ISOLATE
||
1166 text
->mUnicodeBidi
& NS_STYLE_UNICODE_BIDI_PLAINTEXT
) {
1167 // css "unicode-bidi: isolate" and html5 bdi:
1168 // resolve the element as a separate paragraph
1169 BidiParagraphData
* subParagraph
= aBpd
->GetSubParagraph();
1172 * As at the beginning of the loop, it's important to check for
1173 * next-continuations before handling the frame. If we do
1174 * TraverseFrames and *then* do GetNextContinuation on the original
1175 * first frame, it could return a bidi continuation that had only
1176 * just been created, and we would skip doing bidi resolution on the
1177 * last part of the sub-paragraph.
1179 bool isLastContinuation
= !frame
->GetNextContinuation();
1180 if (!frame
->GetPrevContinuation() || !subParagraph
->mReset
) {
1181 if (subParagraph
->BufferLength()) {
1182 ResolveParagraph(aBlockFrame
, subParagraph
);
1184 subParagraph
->Reset(frame
, aBpd
);
1186 TraverseFrames(aBlockFrame
, aLineIter
, kid
, subParagraph
);
1187 if (isLastContinuation
) {
1188 ResolveParagraph(aBlockFrame
, subParagraph
);
1189 subParagraph
->EmptyBuffer();
1192 // Treat the element as a neutral character within its containing
1194 aBpd
->AppendControlChar(kObjectSubstitute
);
1196 TraverseFrames(aBlockFrame
, aLineIter
, kid
, aBpd
);
1201 // If the element is attributed by dir, indicate direction pop (add PDF frame)
1204 // Add a dummy frame pointer representing a bidi control code after the
1205 // last frame of an element specifying embedding or override
1206 aBpd
->PopBidiControl();
1209 childFrame
= nextSibling
;
1210 } while (childFrame
);
1212 MOZ_ASSERT(initialLineContainer
== aLineIter
->GetContainer());
1216 nsBidiPresUtils::ResolveParagraphWithinBlock(nsBlockFrame
* aBlockFrame
,
1217 BidiParagraphData
* aBpd
)
1219 aBpd
->ClearBidiControls();
1220 ResolveParagraph(aBlockFrame
, aBpd
);
1225 nsBidiPresUtils::ReorderFrames(nsIFrame
* aFirstFrameOnLine
,
1226 int32_t aNumFramesOnLine
,
1227 WritingMode aLineWM
,
1231 // If this line consists of a line frame, reorder the line frame's children.
1232 if (aFirstFrameOnLine
->GetType() == nsGkAtoms::lineFrame
) {
1233 aFirstFrameOnLine
= aFirstFrameOnLine
->GetFirstPrincipalChild();
1234 if (!aFirstFrameOnLine
)
1236 // All children of the line frame are on the first line. Setting aNumFramesOnLine
1237 // to -1 makes InitLogicalArrayFromLine look at all of them.
1238 aNumFramesOnLine
= -1;
1241 BidiLineData
bld(aFirstFrameOnLine
, aNumFramesOnLine
);
1242 RepositionInlineFrames(&bld
, aFirstFrameOnLine
, aLineWM
, aLineWidth
, aStart
);
1246 nsBidiPresUtils::GetFirstLeaf(nsIFrame
* aFrame
)
1248 nsIFrame
* firstLeaf
= aFrame
;
1249 while (!IsBidiLeaf(firstLeaf
)) {
1250 nsIFrame
* firstChild
= firstLeaf
->GetFirstPrincipalChild();
1251 nsIFrame
* realFrame
= nsPlaceholderFrame::GetRealFrameFor(firstChild
);
1252 firstLeaf
= (realFrame
->GetType() == nsGkAtoms::letterFrame
) ?
1253 realFrame
: firstChild
;
1259 nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame
* aFrame
)
1261 return NS_GET_EMBEDDING_LEVEL(nsBidiPresUtils::GetFirstLeaf(aFrame
));
1265 nsBidiPresUtils::GetParagraphDepth(nsIFrame
* aFrame
)
1267 return NS_GET_PARAGRAPH_DEPTH(nsBidiPresUtils::GetFirstLeaf(aFrame
));
1272 nsBidiPresUtils::GetFrameBaseLevel(nsIFrame
* aFrame
)
1274 nsIFrame
* firstLeaf
= aFrame
;
1275 while (!IsBidiLeaf(firstLeaf
)) {
1276 firstLeaf
= firstLeaf
->GetFirstPrincipalChild();
1278 return NS_GET_BASE_LEVEL(firstLeaf
);
1282 nsBidiPresUtils::IsFirstOrLast(nsIFrame
* aFrame
,
1283 nsContinuationStates
* aContinuationStates
,
1284 bool aSpanDirMatchesLineDir
,
1285 bool& aIsFirst
/* out */,
1286 bool& aIsLast
/* out */)
1289 * Since we lay out frames in the line's direction, visiting a frame with
1290 * 'mFirstVisualFrame == nullptr', means it's the first appearance of one
1291 * of its continuation chain frames on the line.
1292 * To determine if it's the last visual frame of its continuation chain on
1293 * the line or not, we count the number of frames of the chain on the line,
1294 * and then reduce it when we lay out a frame of the chain. If this value
1295 * becomes 1 it means that it's the last visual frame of its continuation
1296 * chain on this line.
1299 bool firstInLineOrder
, lastInLineOrder
;
1300 nsFrameContinuationState
* frameState
= aContinuationStates
->GetEntry(aFrame
);
1301 nsFrameContinuationState
* firstFrameState
;
1303 if (!frameState
->mFirstVisualFrame
) {
1304 // aFrame is the first visual frame of its continuation chain
1305 nsFrameContinuationState
* contState
;
1308 frameState
->mFrameCount
= 1;
1309 frameState
->mFirstVisualFrame
= aFrame
;
1312 * Traverse continuation chain of aFrame in both backward and forward
1313 * directions while the frames are on this line. Count the frames and
1314 * set their mFirstVisualFrame to aFrame.
1316 // Traverse continuation chain backward
1317 for (frame
= aFrame
->GetPrevContinuation();
1318 frame
&& (contState
= aContinuationStates
->GetEntry(frame
));
1319 frame
= frame
->GetPrevContinuation()) {
1320 frameState
->mFrameCount
++;
1321 contState
->mFirstVisualFrame
= aFrame
;
1323 frameState
->mHasContOnPrevLines
= (frame
!= nullptr);
1325 // Traverse continuation chain forward
1326 for (frame
= aFrame
->GetNextContinuation();
1327 frame
&& (contState
= aContinuationStates
->GetEntry(frame
));
1328 frame
= frame
->GetNextContinuation()) {
1329 frameState
->mFrameCount
++;
1330 contState
->mFirstVisualFrame
= aFrame
;
1332 frameState
->mHasContOnNextLines
= (frame
!= nullptr);
1334 firstInLineOrder
= true;
1335 firstFrameState
= frameState
;
1337 // aFrame is not the first visual frame of its continuation chain
1338 firstInLineOrder
= false;
1339 firstFrameState
= aContinuationStates
->GetEntry(frameState
->mFirstVisualFrame
);
1342 lastInLineOrder
= (firstFrameState
->mFrameCount
== 1);
1344 if (aSpanDirMatchesLineDir
) {
1345 aIsFirst
= firstInLineOrder
;
1346 aIsLast
= lastInLineOrder
;
1348 aIsFirst
= lastInLineOrder
;
1349 aIsLast
= firstInLineOrder
;
1352 if (frameState
->mHasContOnPrevLines
) {
1355 if (firstFrameState
->mHasContOnNextLines
) {
1359 if ((aIsFirst
|| aIsLast
) &&
1360 (aFrame
->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT
)) {
1361 // For ib splits, don't treat anything except the last part as
1362 // endmost or anything except the first part as startmost.
1363 // As an optimization, only get the first continuation once.
1364 nsIFrame
* firstContinuation
= aFrame
->FirstContinuation();
1365 if (firstContinuation
->FrameIsNonLastInIBSplit()) {
1366 // We are not endmost
1369 if (firstContinuation
->FrameIsNonFirstInIBSplit()) {
1370 // We are not startmost
1375 // Reduce number of remaining frames of the continuation chain on the line.
1376 firstFrameState
->mFrameCount
--;
1378 nsInlineFrame
* testFrame
= do_QueryFrame(aFrame
);
1381 aFrame
->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET
);
1384 aFrame
->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_FIRST
);
1386 aFrame
->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_FIRST
);
1390 aFrame
->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST
);
1392 aFrame
->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST
);
1398 nsBidiPresUtils::RepositionFrame(nsIFrame
* aFrame
,
1401 nsContinuationStates
* aContinuationStates
,
1402 WritingMode aContainerWM
,
1403 nscoord aContainerWidth
)
1408 bool isFirst
, isLast
;
1409 WritingMode frameWM
= aFrame
->GetWritingMode();
1410 IsFirstOrLast(aFrame
,
1411 aContinuationStates
,
1412 aContainerWM
.IsBidiLTR() == frameWM
.IsBidiLTR(),
1416 // We only need the margin if the frame is first or last in its own
1417 // writing mode, but we're traversing the frames in the order of the
1418 // container's writing mode. To get the right values, we set start and
1419 // end margins on a logical margin in the frame's writing mode, and
1420 // then convert the margin to the container's writing mode to set the
1423 // This method is called from nsBlockFrame::PlaceLine via the call to
1424 // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
1425 // have been reflowed, which is required for GetUsedMargin/Border/Padding
1426 LogicalMargin frameMargin
= aFrame
->GetLogicalUsedMargin(frameWM
);
1427 LogicalMargin borderPadding
= aFrame
->GetLogicalUsedBorderAndPadding(frameWM
);
1429 frameMargin
.IStart(frameWM
) = 0;
1430 borderPadding
.IStart(frameWM
) = 0;
1433 frameMargin
.IEnd(frameWM
) = 0;
1434 borderPadding
.IEnd(frameWM
) = 0;
1436 LogicalMargin margin
= frameMargin
.ConvertTo(aContainerWM
, frameWM
);
1437 aStart
+= margin
.IStart(aContainerWM
);
1439 nscoord start
= aStart
;
1441 if (!IsBidiLeaf(aFrame
)) {
1442 // If the resolved direction of the container is different from the
1443 // direction of the frame, we need to traverse the child list in reverse
1444 // order, to make it O(n) we store the list locally and iterate the list
1446 bool reverseOrder
= aIsEvenLevel
!= frameWM
.IsBidiLTR();
1447 nsTArray
<nsIFrame
*> childList
;
1448 nsIFrame
*frame
= aFrame
->GetFirstPrincipalChild();
1449 if (frame
&& reverseOrder
) {
1450 childList
.AppendElement((nsIFrame
*)nullptr);
1452 childList
.AppendElement(frame
);
1453 frame
= frame
->GetNextSibling();
1455 frame
= childList
[childList
.Length() - 1];
1458 // Reposition the child frames
1460 nscoord iCoord
= borderPadding
.IStart(frameWM
);
1463 RepositionFrame(frame
,
1466 aContinuationStates
,
1468 aFrame
->GetLogicalSize(aContainerWM
).Width(aContainerWM
));
1470 frame
= reverseOrder
?
1471 childList
[childList
.Length() - index
- 1] :
1472 frame
->GetNextSibling();
1475 aStart
+= iCoord
+ borderPadding
.IEnd(frameWM
);
1477 aStart
+= aFrame
->ISize(aContainerWM
);
1480 LogicalRect logicalRect
= aFrame
->GetLogicalRect(aContainerWM
,
1482 logicalRect
.IStart(aContainerWM
) = start
;
1483 logicalRect
.ISize(aContainerWM
) = aStart
- start
;
1484 aFrame
->SetRect(aContainerWM
, logicalRect
, aContainerWidth
);
1486 aStart
+= margin
.IEnd(aContainerWM
);
1490 nsBidiPresUtils::InitContinuationStates(nsIFrame
* aFrame
,
1491 nsContinuationStates
* aContinuationStates
)
1493 nsFrameContinuationState
* state
= aContinuationStates
->PutEntry(aFrame
);
1494 state
->mFirstVisualFrame
= nullptr;
1495 state
->mFrameCount
= 0;
1497 if (!IsBidiLeaf(aFrame
)) {
1498 // Continue for child frames
1500 for (frame
= aFrame
->GetFirstPrincipalChild();
1502 frame
= frame
->GetNextSibling()) {
1503 InitContinuationStates(frame
,
1504 aContinuationStates
);
1510 nsBidiPresUtils::RepositionInlineFrames(BidiLineData
*aBld
,
1511 nsIFrame
* aFirstChild
,
1512 WritingMode aLineWM
,
1516 nscoord start
= aStart
;
1518 int32_t count
= aBld
->mVisualFrames
.Length();
1520 nsContinuationStates continuationStates
;
1522 // Initialize continuation states to (nullptr, 0) for
1523 // each frame on the line.
1524 for (index
= 0; index
< count
; index
++) {
1525 InitContinuationStates(aBld
->VisualFrameAt(index
), &continuationStates
);
1528 // Reposition frames in visual order
1529 int32_t step
, limit
;
1530 if (aLineWM
.IsBidiLTR()) {
1539 for (; index
!= limit
; index
+= step
) {
1540 frame
= aBld
->VisualFrameAt(index
);
1541 RepositionFrame(frame
,
1542 !(IS_LEVEL_RTL(aBld
->mLevels
[aBld
->mIndexMap
[index
]])),
1544 &continuationStates
,
1551 nsBidiPresUtils::CheckLineOrder(nsIFrame
* aFirstFrameOnLine
,
1552 int32_t aNumFramesOnLine
,
1553 nsIFrame
** aFirstVisual
,
1554 nsIFrame
** aLastVisual
)
1556 BidiLineData
bld(aFirstFrameOnLine
, aNumFramesOnLine
);
1557 int32_t count
= bld
.FrameCount();
1560 *aFirstVisual
= bld
.VisualFrameAt(0);
1563 *aLastVisual
= bld
.VisualFrameAt(count
-1);
1566 return bld
.mIsReordered
;
1570 nsBidiPresUtils::GetFrameToRightOf(const nsIFrame
* aFrame
,
1571 nsIFrame
* aFirstFrameOnLine
,
1572 int32_t aNumFramesOnLine
)
1574 BidiLineData
bld(aFirstFrameOnLine
, aNumFramesOnLine
);
1576 int32_t count
= bld
.mVisualFrames
.Length();
1578 if (aFrame
== nullptr && count
)
1579 return bld
.VisualFrameAt(0);
1581 for (int32_t i
= 0; i
< count
- 1; i
++) {
1582 if (bld
.VisualFrameAt(i
) == aFrame
) {
1583 return bld
.VisualFrameAt(i
+1);
1591 nsBidiPresUtils::GetFrameToLeftOf(const nsIFrame
* aFrame
,
1592 nsIFrame
* aFirstFrameOnLine
,
1593 int32_t aNumFramesOnLine
)
1595 BidiLineData
bld(aFirstFrameOnLine
, aNumFramesOnLine
);
1597 int32_t count
= bld
.mVisualFrames
.Length();
1599 if (aFrame
== nullptr && count
)
1600 return bld
.VisualFrameAt(count
-1);
1602 for (int32_t i
= 1; i
< count
; i
++) {
1603 if (bld
.VisualFrameAt(i
) == aFrame
) {
1604 return bld
.VisualFrameAt(i
-1);
1612 nsBidiPresUtils::EnsureBidiContinuation(nsIFrame
* aFrame
,
1613 nsIFrame
** aNewFrame
,
1614 int32_t& aFrameIndex
,
1618 NS_PRECONDITION(aNewFrame
, "null OUT ptr");
1619 NS_PRECONDITION(aFrame
, "aFrame is null");
1621 aFrame
->AdjustOffsetsForBidi(aStart
, aEnd
);
1622 return CreateContinuation(aFrame
, aNewFrame
, false);
1626 nsBidiPresUtils::RemoveBidiContinuation(BidiParagraphData
*aBpd
,
1628 int32_t aFirstIndex
,
1632 FrameProperties props
= aFrame
->Properties();
1633 nsBidiLevel embeddingLevel
=
1634 (nsBidiLevel
)NS_PTR_TO_INT32(props
.Get(nsIFrame::EmbeddingLevelProperty()));
1635 nsBidiLevel baseLevel
=
1636 (nsBidiLevel
)NS_PTR_TO_INT32(props
.Get(nsIFrame::BaseLevelProperty()));
1637 uint8_t paragraphDepth
=
1638 NS_PTR_TO_INT32(props
.Get(nsIFrame::ParagraphDepthProperty()));
1640 for (int32_t index
= aFirstIndex
+ 1; index
<= aLastIndex
; index
++) {
1641 nsIFrame
* frame
= aBpd
->FrameAt(index
);
1642 if (frame
== NS_BIDI_CONTROL_FRAME
) {
1646 // Make the frame and its continuation ancestors fluid,
1647 // so they can be reused or deleted by normal reflow code
1648 FrameProperties frameProps
= frame
->Properties();
1649 frameProps
.Set(nsIFrame::EmbeddingLevelProperty(),
1650 NS_INT32_TO_PTR(embeddingLevel
));
1651 frameProps
.Set(nsIFrame::BaseLevelProperty(),
1652 NS_INT32_TO_PTR(baseLevel
));
1653 frameProps
.Set(nsIFrame::ParagraphDepthProperty(),
1654 NS_INT32_TO_PTR(paragraphDepth
));
1655 frame
->AddStateBits(NS_FRAME_IS_BIDI
);
1657 nsIFrame
* prev
= frame
->GetPrevContinuation();
1659 MakeContinuationFluid(prev
, frame
);
1660 frame
= frame
->GetParent();
1668 // Make sure that the last continuation we made fluid does not itself have a
1669 // fluid continuation (this can happen when re-resolving after dynamic changes
1671 nsIFrame
* lastFrame
= aBpd
->FrameAt(aLastIndex
);
1672 MakeContinuationsNonFluidUpParentChain(lastFrame
, lastFrame
->GetNextInFlow());
1676 nsBidiPresUtils::FormatUnicodeText(nsPresContext
* aPresContext
,
1678 int32_t& aTextLength
,
1679 nsCharType aCharType
,
1680 nsBidiDirection aDir
)
1682 nsresult rv
= NS_OK
;
1684 //adjusted for correct numeral shaping
1685 uint32_t bidiOptions
= aPresContext
->GetBidi();
1686 switch (GET_BIDI_OPTION_NUMERAL(bidiOptions
)) {
1688 case IBMBIDI_NUMERAL_HINDI
:
1689 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_HINDI
);
1692 case IBMBIDI_NUMERAL_ARABIC
:
1693 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1696 case IBMBIDI_NUMERAL_PERSIAN
:
1697 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_PERSIAN
);
1700 case IBMBIDI_NUMERAL_REGULAR
:
1702 switch (aCharType
) {
1704 case eCharType_EuropeanNumber
:
1705 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1708 case eCharType_ArabicNumber
:
1709 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_HINDI
);
1717 case IBMBIDI_NUMERAL_HINDICONTEXT
:
1718 if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions
)==IBMBIDI_TEXTDIRECTION_RTL
) && (IS_ARABIC_DIGIT (aText
[0])) ) || (eCharType_ArabicNumber
== aCharType
) )
1719 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_HINDI
);
1720 else if (eCharType_EuropeanNumber
== aCharType
)
1721 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1724 case IBMBIDI_NUMERAL_PERSIANCONTEXT
:
1725 if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions
)==IBMBIDI_TEXTDIRECTION_RTL
) && (IS_ARABIC_DIGIT (aText
[0])) ) || (eCharType_ArabicNumber
== aCharType
) )
1726 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_PERSIAN
);
1727 else if (eCharType_EuropeanNumber
== aCharType
)
1728 HandleNumbers(aText
,aTextLength
,IBMBIDI_NUMERAL_ARABIC
);
1731 case IBMBIDI_NUMERAL_NOMINAL
:
1736 StripBidiControlCharacters(aText
, aTextLength
);
1741 nsBidiPresUtils::StripBidiControlCharacters(char16_t
* aText
,
1742 int32_t& aTextLength
)
1744 if ( (nullptr == aText
) || (aTextLength
< 1) ) {
1748 int32_t stripLen
= 0;
1750 for (int32_t i
= 0; i
< aTextLength
; i
++) {
1751 // XXX: This silently ignores surrogate characters.
1752 // As of Unicode 4.0, all Bidi control characters are within the BMP.
1753 if (IsBidiControl((uint32_t)aText
[i
])) {
1757 aText
[i
- stripLen
] = aText
[i
];
1760 aTextLength
-= stripLen
;
1763 #if 0 // XXX: for the future use ???
1765 RemoveDiacritics(char16_t
* aText
,
1766 int32_t& aTextLength
)
1768 if (aText
&& (aTextLength
> 0) ) {
1771 for (int32_t i
= 0; i
< aTextLength
&& aText
[i
]; i
++) {
1772 if (IS_BIDI_DIACRITIC(aText
[i
]) ) {
1776 aText
[i
- offset
] = aText
[i
];
1778 aTextLength
= i
- offset
;
1779 aText
[aTextLength
] = 0;
1785 nsBidiPresUtils::CalculateCharType(nsBidi
* aBidiEngine
,
1786 const char16_t
* aText
,
1788 int32_t aCharTypeLimit
,
1790 int32_t& aRunLength
,
1793 uint8_t& aPrevCharType
)
1796 bool strongTypeFound
= false;
1798 nsCharType charType
;
1800 aCharType
= eCharType_OtherNeutral
;
1802 for (offset
= aOffset
; offset
< aCharTypeLimit
; offset
++) {
1803 // Make sure we give RTL chartype to all characters that would be classified
1804 // as Right-To-Left by a bidi platform.
1805 // (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.)
1806 if (IS_HEBREW_CHAR(aText
[offset
]) ) {
1807 charType
= eCharType_RightToLeft
;
1809 else if (IS_ARABIC_ALPHABETIC(aText
[offset
]) ) {
1810 charType
= eCharType_RightToLeftArabic
;
1813 aBidiEngine
->GetCharTypeAt(offset
, &charType
);
1816 if (!CHARTYPE_IS_WEAK(charType
) ) {
1819 && (charType
!= aPrevCharType
)
1820 && (CHARTYPE_IS_RTL(charType
) || CHARTYPE_IS_RTL(aPrevCharType
) ) ) {
1821 // Stop at this point to ensure uni-directionality of the text
1822 // (from platform's point of view).
1823 // Also, don't mix Arabic and Hebrew content (since platform may
1824 // provide BIDI support to one of them only).
1825 aRunLength
= offset
- aOffset
;
1831 if ( (eCharType_RightToLeftArabic
== aPrevCharType
1832 || eCharType_ArabicNumber
== aPrevCharType
)
1833 && eCharType_EuropeanNumber
== charType
) {
1834 charType
= eCharType_ArabicNumber
;
1837 // Set PrevCharType to the last strong type in this frame
1838 // (for correct numeric shaping)
1839 aPrevCharType
= charType
;
1841 strongTypeFound
= true;
1842 aCharType
= charType
;
1848 nsresult
nsBidiPresUtils::ProcessText(const char16_t
* aText
,
1850 nsBidiLevel aBaseLevel
,
1851 nsPresContext
* aPresContext
,
1852 BidiProcessor
& aprocessor
,
1854 nsBidiPositionResolve
* aPosResolve
,
1855 int32_t aPosResolveCount
,
1857 nsBidi
* aBidiEngine
)
1859 NS_ASSERTION((aPosResolve
== nullptr) != (aPosResolveCount
> 0), "Incorrect aPosResolve / aPosResolveCount arguments");
1863 nsAutoString
textBuffer(aText
, aLength
);
1865 nsresult rv
= aBidiEngine
->SetPara(aText
, aLength
, aBaseLevel
, nullptr);
1869 rv
= aBidiEngine
->CountRuns(&runCount
);
1873 nscoord xOffset
= 0;
1874 nscoord width
, xEndRun
= 0;
1875 nscoord totalWidth
= 0;
1876 int32_t i
, start
, limit
, length
;
1877 uint32_t visualStart
= 0;
1879 uint8_t prevType
= eCharType_LeftToRight
;
1881 for(int nPosResolve
=0; nPosResolve
< aPosResolveCount
; ++nPosResolve
)
1883 aPosResolve
[nPosResolve
].visualIndex
= kNotFound
;
1884 aPosResolve
[nPosResolve
].visualLeftTwips
= kNotFound
;
1885 aPosResolve
[nPosResolve
].visualWidth
= kNotFound
;
1888 for (i
= 0; i
< runCount
; i
++) {
1889 nsBidiDirection dir
;
1890 rv
= aBidiEngine
->GetVisualRun(i
, &start
, &length
, &dir
);
1895 rv
= aBidiEngine
->GetLogicalRun(start
, &limit
, &level
);
1899 dir
= DIRECTION_FROM_LEVEL(level
);
1900 int32_t subRunLength
= limit
- start
;
1901 int32_t lineOffset
= start
;
1902 int32_t typeLimit
= std::min(limit
, aLength
);
1903 int32_t subRunCount
= 1;
1904 int32_t subRunLimit
= typeLimit
;
1907 * If |level| is even, i.e. the direction of the run is left-to-right, we
1908 * render the subruns from left to right and increment the x-coordinate
1909 * |xOffset| by the width of each subrun after rendering.
1911 * If |level| is odd, i.e. the direction of the run is right-to-left, we
1912 * render the subruns from right to left. We begin by incrementing |xOffset| by
1913 * the width of the whole run, and then decrement it by the width of each
1914 * subrun before rendering. After rendering all the subruns, we restore the
1915 * x-coordinate of the end of the run for the start of the next run.
1918 if (dir
== NSBIDI_RTL
) {
1919 aprocessor
.SetText(aText
+ start
, subRunLength
, dir
);
1920 width
= aprocessor
.GetWidth();
1925 while (subRunCount
> 0) {
1926 // CalculateCharType can increment subRunCount if the run
1927 // contains mixed character types
1928 CalculateCharType(aBidiEngine
, aText
, lineOffset
, typeLimit
, subRunLimit
, subRunLength
, subRunCount
, charType
, prevType
);
1930 nsAutoString runVisualText
;
1931 runVisualText
.Assign(aText
+ start
, subRunLength
);
1932 if (int32_t(runVisualText
.Length()) < subRunLength
)
1933 return NS_ERROR_OUT_OF_MEMORY
;
1934 FormatUnicodeText(aPresContext
, runVisualText
.BeginWriting(),
1935 subRunLength
, (nsCharType
)charType
, dir
);
1937 aprocessor
.SetText(runVisualText
.get(), subRunLength
, dir
);
1938 width
= aprocessor
.GetWidth();
1939 totalWidth
+= width
;
1940 if (dir
== NSBIDI_RTL
) {
1943 if (aMode
== MODE_DRAW
) {
1944 aprocessor
.DrawText(xOffset
, width
);
1948 * The caller may request to calculate the visual position of one
1949 * or more characters.
1951 for(int nPosResolve
=0; nPosResolve
<aPosResolveCount
; ++nPosResolve
)
1953 nsBidiPositionResolve
* posResolve
= &aPosResolve
[nPosResolve
];
1955 * Did we already resolve this position's visual metric? If so, skip.
1957 if (posResolve
->visualLeftTwips
!= kNotFound
)
1961 * First find out if the logical position is within this run.
1963 if (start
<= posResolve
->logicalIndex
&&
1964 start
+ subRunLength
> posResolve
->logicalIndex
) {
1966 * If this run is only one character long, we have an easy case:
1967 * the visual position is the x-coord of the start of the run
1968 * less the x-coord of the start of the whole text.
1970 if (subRunLength
== 1) {
1971 posResolve
->visualIndex
= visualStart
;
1972 posResolve
->visualLeftTwips
= xOffset
;
1973 posResolve
->visualWidth
= width
;
1976 * Otherwise, we need to measure the width of the run's part
1977 * which is to the visual left of the index.
1978 * In other words, the run is broken in two, around the logical index,
1979 * and we measure the part which is visually left.
1980 * If the run is right-to-left, this part will span from after the index
1981 * up to the end of the run; if it is left-to-right, this part will span
1982 * from the start of the run up to (and inclduing) the character before the index.
1986 * Here is a description of how the width of the current character
1987 * (posResolve->visualWidth) is calculated:
1989 * LTR (current char: "P"):
1990 * S A M P L E (logical index: 3, visual index: 3)
1991 * ^ (visualLeftPart)
1992 * ^ (visualRightSide)
1993 * visualLeftLength == 3
1995 * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
1996 * ^^ (posResolve->visualWidth)
1998 * RTL (current char: "M"):
1999 * E L P M A S (logical index: 2, visual index: 3)
2000 * ^ (visualLeftPart)
2001 * ^ (visualRightSide)
2002 * visualLeftLength == 3
2004 * ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
2005 * ^^ (posResolve->visualWidth)
2008 // The position in the text where this run's "left part" begins.
2009 const char16_t
* visualLeftPart
;
2010 const char16_t
* visualRightSide
;
2011 if (dir
== NSBIDI_RTL
) {
2012 // One day, son, this could all be replaced with mBidiEngine.GetVisualIndex ...
2013 posResolve
->visualIndex
= visualStart
+ (subRunLength
- (posResolve
->logicalIndex
+ 1 - start
));
2014 // Skipping to the "left part".
2015 visualLeftPart
= aText
+ posResolve
->logicalIndex
+ 1;
2016 // Skipping to the right side of the current character
2017 visualRightSide
= visualLeftPart
- 1;
2020 posResolve
->visualIndex
= visualStart
+ (posResolve
->logicalIndex
- start
);
2021 // Skipping to the "left part".
2022 visualLeftPart
= aText
+ start
;
2023 // In LTR mode this is the same as visualLeftPart
2024 visualRightSide
= visualLeftPart
;
2026 // The delta between the start of the run and the left part's end.
2027 int32_t visualLeftLength
= posResolve
->visualIndex
- visualStart
;
2028 aprocessor
.SetText(visualLeftPart
, visualLeftLength
, dir
);
2029 subWidth
= aprocessor
.GetWidth();
2030 aprocessor
.SetText(visualRightSide
, visualLeftLength
+ 1, dir
);
2031 posResolve
->visualLeftTwips
= xOffset
+ subWidth
;
2032 posResolve
->visualWidth
= aprocessor
.GetWidth() - subWidth
;
2037 if (dir
== NSBIDI_LTR
) {
2043 subRunLimit
= typeLimit
;
2044 subRunLength
= typeLimit
- lineOffset
;
2046 if (dir
== NSBIDI_RTL
) {
2050 visualStart
+= length
;
2054 *aWidth
= totalWidth
;
2059 class MOZ_STACK_CLASS nsIRenderingContextBidiProcessor MOZ_FINAL
2060 : public nsBidiPresUtils::BidiProcessor
2063 nsIRenderingContextBidiProcessor(nsRenderingContext
* aCtx
,
2064 nsRenderingContext
* aTextRunConstructionContext
,
2065 nsFontMetrics
* aFontMetrics
,
2068 , mTextRunConstructionContext(aTextRunConstructionContext
)
2069 , mFontMetrics(aFontMetrics
)
2073 ~nsIRenderingContextBidiProcessor()
2075 mFontMetrics
->SetTextRunRTL(false);
2078 virtual void SetText(const char16_t
* aText
,
2080 nsBidiDirection aDirection
) MOZ_OVERRIDE
2082 mFontMetrics
->SetTextRunRTL(aDirection
==NSBIDI_RTL
);
2087 virtual nscoord
GetWidth() MOZ_OVERRIDE
2089 return nsLayoutUtils::AppUnitWidthOfString(mText
, mLength
, *mFontMetrics
,
2090 *mTextRunConstructionContext
);
2093 virtual void DrawText(nscoord aXOffset
,
2094 nscoord
) MOZ_OVERRIDE
2096 mFontMetrics
->DrawString(mText
, mLength
, mPt
.x
+ aXOffset
, mPt
.y
,
2097 mCtx
, mTextRunConstructionContext
);
2101 nsRenderingContext
* mCtx
;
2102 nsRenderingContext
* mTextRunConstructionContext
;
2103 nsFontMetrics
* mFontMetrics
;
2105 const char16_t
* mText
;
2109 nsresult
nsBidiPresUtils::ProcessTextForRenderingContext(const char16_t
* aText
,
2111 nsBidiLevel aBaseLevel
,
2112 nsPresContext
* aPresContext
,
2113 nsRenderingContext
& aRenderingContext
,
2114 nsRenderingContext
& aTextRunConstructionContext
,
2115 nsFontMetrics
& aFontMetrics
,
2119 nsBidiPositionResolve
* aPosResolve
,
2120 int32_t aPosResolveCount
,
2123 nsIRenderingContextBidiProcessor
processor(&aRenderingContext
,
2124 &aTextRunConstructionContext
,
2128 return ProcessText(aText
, aLength
, aBaseLevel
, aPresContext
, processor
,
2129 aMode
, aPosResolve
, aPosResolveCount
, aWidth
, &bidiEngine
);
2133 void nsBidiPresUtils::WriteReverse(const char16_t
* aSrc
,
2134 uint32_t aSrcLength
,
2137 char16_t
* dest
= aDest
+ aSrcLength
;
2138 mozilla::unicode::ClusterIterator
iter(aSrc
, aSrcLength
);
2140 while (!iter
.AtEnd()) {
2142 for (const char16_t
*cp
= iter
; cp
> aSrc
; ) {
2143 // Here we rely on the fact that there are no non-BMP mirrored pairs
2144 // currently in Unicode, so we don't need to look for surrogates
2145 *--dest
= mozilla::unicode::GetMirroredChar(*--cp
);
2150 NS_ASSERTION(dest
== aDest
, "Whole string not copied");
2154 bool nsBidiPresUtils::WriteLogicalToVisual(const char16_t
* aSrc
,
2155 uint32_t aSrcLength
,
2157 nsBidiLevel aBaseDirection
,
2158 nsBidi
* aBidiEngine
)
2160 const char16_t
* src
= aSrc
;
2161 nsresult rv
= aBidiEngine
->SetPara(src
, aSrcLength
, aBaseDirection
, nullptr);
2162 if (NS_FAILED(rv
)) {
2166 nsBidiDirection dir
;
2167 rv
= aBidiEngine
->GetDirection(&dir
);
2168 // NSBIDI_LTR returned from GetDirection means the whole text is LTR
2169 if (NS_FAILED(rv
) || dir
== NSBIDI_LTR
) {
2174 rv
= aBidiEngine
->CountRuns(&runCount
);
2175 if (NS_FAILED(rv
)) {
2179 int32_t runIndex
, start
, length
;
2180 char16_t
* dest
= aDest
;
2182 for (runIndex
= 0; runIndex
< runCount
; ++runIndex
) {
2183 rv
= aBidiEngine
->GetVisualRun(runIndex
, &start
, &length
, &dir
);
2184 if (NS_FAILED(rv
)) {
2190 if (dir
== NSBIDI_RTL
) {
2191 WriteReverse(src
, length
, dest
);
2195 NS_ASSERTION(src
>= aSrc
&& src
< aSrc
+ aSrcLength
,
2196 "logical index out of range");
2197 NS_ASSERTION(dest
< aDest
+ aSrcLength
, "visual index out of range");
2198 *(dest
++) = *(src
++);
2203 NS_ASSERTION(static_cast<uint32_t>(dest
- aDest
) == aSrcLength
,
2204 "whole string not copied");
2208 void nsBidiPresUtils::CopyLogicalToVisual(const nsAString
& aSource
,
2210 nsBidiLevel aBaseDirection
,
2214 uint32_t srcLength
= aSource
.Length();
2217 if (!aDest
.SetLength(srcLength
, fallible_t())) {
2220 nsAString::const_iterator fromBegin
, fromEnd
;
2221 nsAString::iterator toBegin
;
2222 aSource
.BeginReading(fromBegin
);
2223 aSource
.EndReading(fromEnd
);
2224 aDest
.BeginWriting(toBegin
);
2227 if (aBaseDirection
== NSBIDI_RTL
) {
2228 // no need to use the converter -- just copy the string in reverse order
2229 WriteReverse(fromBegin
.get(), srcLength
, toBegin
.get());
2231 // if aOverride && aBaseDirection == NSBIDI_LTR, fall through to the
2237 if (!WriteLogicalToVisual(fromBegin
.get(), srcLength
, toBegin
.get(),
2238 aBaseDirection
, &bidiEngine
)) {
2243 if (aDest
.IsEmpty()) {
2244 // Either there was an error or the source is unidirectional
2245 // left-to-right. In either case, just copy source to dest.
2246 CopyUnicodeTo(aSource
.BeginReading(fromBegin
), aSource
.EndReading(fromEnd
),
2253 nsBidiPresUtils::BidiLevelFromStyle(nsStyleContext
* aStyleContext
)
2255 if (aStyleContext
->StyleTextReset()->mUnicodeBidi
&
2256 NS_STYLE_UNICODE_BIDI_PLAINTEXT
) {
2257 return NSBIDI_DEFAULT_LTR
;
2260 if (aStyleContext
->StyleVisibility()->mDirection
== NS_STYLE_DIRECTION_RTL
) {