1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "nsBidiPresUtils.h"
8 
9 #include "mozilla/IntegerRange.h"
10 
11 #include "gfxContext.h"
12 #include "nsAutoPtr.h"
13 #include "nsFontMetrics.h"
14 #include "nsGkAtoms.h"
15 #include "nsPresContext.h"
16 #include "nsBidiUtils.h"
17 #include "nsCSSFrameConstructor.h"
18 #include "nsContainerFrame.h"
19 #include "nsInlineFrame.h"
20 #include "nsPlaceholderFrame.h"
21 #include "nsPointerHashKeys.h"
22 #include "nsFirstLetterFrame.h"
23 #include "nsUnicodeProperties.h"
24 #include "nsTextFrame.h"
25 #include "nsBlockFrame.h"
26 #include "nsIFrameInlines.h"
27 #include "nsStyleStructInlines.h"
28 #include "RubyUtils.h"
29 #include "nsRubyFrame.h"
30 #include "nsRubyBaseFrame.h"
31 #include "nsRubyTextFrame.h"
32 #include "nsRubyBaseContainerFrame.h"
33 #include "nsRubyTextContainerFrame.h"
34 #include <algorithm>
35 
36 #undef NOISY_BIDI
37 #undef REALLY_NOISY_BIDI
38 
39 using namespace mozilla;
40 
41 static const char16_t kSpace = 0x0020;
42 static const char16_t kZWSP = 0x200B;
43 static const char16_t kLineSeparator = 0x2028;
44 static const char16_t kObjectSubstitute = 0xFFFC;
45 static const char16_t kLRE = 0x202A;
46 static const char16_t kRLE = 0x202B;
47 static const char16_t kLRO = 0x202D;
48 static const char16_t kRLO = 0x202E;
49 static const char16_t kPDF = 0x202C;
50 static const char16_t kLRI = 0x2066;
51 static const char16_t kRLI = 0x2067;
52 static const char16_t kFSI = 0x2068;
53 static const char16_t kPDI = 0x2069;
54 static const char16_t kSeparators[] = {
55     // All characters with Bidi type Segment Separator or Block Separator
56     char16_t('\t'), char16_t('\r'),   char16_t('\n'), char16_t(0xb),
57     char16_t(0x1c), char16_t(0x1d),   char16_t(0x1e), char16_t(0x1f),
58     char16_t(0x85), char16_t(0x2029), char16_t(0)};
59 
60 #define NS_BIDI_CONTROL_FRAME ((nsIFrame*)0xfffb1d1)
61 
IsIsolateControl(char16_t aChar)62 static bool IsIsolateControl(char16_t aChar) {
63   return aChar == kLRI || aChar == kRLI || aChar == kFSI;
64 }
65 
66 // Given a style context, return any bidi control character necessary to
67 // implement style properties that override directionality (i.e. if it has
68 // unicode-bidi:bidi-override, or text-orientation:upright in vertical
69 // writing mode) when applying the bidi algorithm.
70 //
71 // Returns 0 if no override control character is implied by this style.
GetBidiOverride(nsStyleContext * aStyleContext)72 static char16_t GetBidiOverride(nsStyleContext* aStyleContext) {
73   const nsStyleVisibility* vis = aStyleContext->StyleVisibility();
74   if ((vis->mWritingMode == NS_STYLE_WRITING_MODE_VERTICAL_RL ||
75        vis->mWritingMode == NS_STYLE_WRITING_MODE_VERTICAL_LR) &&
76       vis->mTextOrientation == NS_STYLE_TEXT_ORIENTATION_UPRIGHT) {
77     return kLRO;
78   }
79   const nsStyleTextReset* text = aStyleContext->StyleTextReset();
80   if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_BIDI_OVERRIDE) {
81     return NS_STYLE_DIRECTION_RTL == vis->mDirection ? kRLO : kLRO;
82   }
83   return 0;
84 }
85 
86 // Given a style context, return any bidi control character necessary to
87 // implement style properties that affect bidi resolution (i.e. if it
88 // has unicode-bidiembed, isolate, or plaintext) when applying the bidi
89 // algorithm.
90 //
91 // Returns 0 if no control character is implied by the style.
92 //
93 // Note that GetBidiOverride and GetBidiControl need to be separate
94 // because in the case of unicode-bidi:isolate-override we need both
95 // FSI and LRO/RLO.
GetBidiControl(nsStyleContext * aStyleContext)96 static char16_t GetBidiControl(nsStyleContext* aStyleContext) {
97   const nsStyleVisibility* vis = aStyleContext->StyleVisibility();
98   const nsStyleTextReset* text = aStyleContext->StyleTextReset();
99   if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_EMBED) {
100     return NS_STYLE_DIRECTION_RTL == vis->mDirection ? kRLE : kLRE;
101   }
102   if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_ISOLATE) {
103     if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_BIDI_OVERRIDE) {
104       // isolate-override
105       return kFSI;
106     }
107     // <bdi> element already has its directionality set from content so
108     // we never need to return kFSI.
109     return NS_STYLE_DIRECTION_RTL == vis->mDirection ? kRLI : kLRI;
110   }
111   if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
112     return kFSI;
113   }
114   return 0;
115 }
116 
117 struct MOZ_STACK_CLASS BidiParagraphData {
118   nsAutoString mBuffer;
119   AutoTArray<char16_t, 16> mEmbeddingStack;
120   AutoTArray<nsIFrame*, 16> mLogicalFrames;
121   AutoTArray<nsLineBox*, 16> mLinePerFrame;
122   nsDataHashtable<nsPtrHashKey<const nsIContent>, int32_t> mContentToFrameIndex;
123   // Cached presentation context for the frames we're processing.
124   nsPresContext* mPresContext;
125   bool mIsVisual;
126   bool mRequiresBidi;
127   nsBidiLevel mParaLevel;
128   nsIContent* mPrevContent;
129   nsIFrame* mPrevFrame;
130 #ifdef DEBUG
131   // Only used for NOISY debug output.
132   nsBlockFrame* mCurrentBlock;
133 #endif
134 
BidiParagraphDataBidiParagraphData135   explicit BidiParagraphData(nsBlockFrame* aBlockFrame)
136       : mPresContext(aBlockFrame->PresContext()),
137         mIsVisual(mPresContext->IsVisualMode()),
138         mRequiresBidi(false),
139         mParaLevel(
140             nsBidiPresUtils::BidiLevelFromStyle(aBlockFrame->StyleContext())),
141         mPrevContent(nullptr)
142 #ifdef DEBUG
143         ,
144         mCurrentBlock(aBlockFrame)
145 #endif
146   {
147     if (mParaLevel > 0) {
148       mRequiresBidi = true;
149     }
150 
151     if (mIsVisual) {
152       /**
153        * Drill up in content to detect whether this is an element that needs to
154        * be rendered with logical order even on visual pages.
155        *
156        * We always use logical order on form controls, firstly so that text
157        * entry will be in logical order, but also because visual pages were
158        * written with the assumption that even if the browser had no support
159        * for right-to-left text rendering, it would use native widgets with
160        * bidi support to display form controls.
161        *
162        * We also use logical order in XUL elements, since we expect that if a
163        * XUL element appears in a visual page, it will be generated by an XBL
164        * binding and contain localized text which will be in logical order.
165        */
166       for (nsIContent* content = aBlockFrame->GetContent(); content;
167            content = content->GetParent()) {
168         if (content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) ||
169             content->IsXULElement()) {
170           mIsVisual = false;
171           break;
172         }
173       }
174     }
175   }
176 
SetParaBidiParagraphData177   nsresult SetPara() {
178     return mPresContext->GetBidiEngine().SetPara(mBuffer.get(), BufferLength(),
179                                                  mParaLevel);
180   }
181 
182   /**
183    * mParaLevel can be NSBIDI_DEFAULT_LTR as well as NSBIDI_LTR or NSBIDI_RTL.
184    * GetParaLevel() returns the actual (resolved) paragraph level which is
185    * always either NSBIDI_LTR or NSBIDI_RTL
186    */
GetParaLevelBidiParagraphData187   nsBidiLevel GetParaLevel() {
188     nsBidiLevel paraLevel = mParaLevel;
189     if (paraLevel == NSBIDI_DEFAULT_LTR || paraLevel == NSBIDI_DEFAULT_RTL) {
190       paraLevel = mPresContext->GetBidiEngine().GetParaLevel();
191     }
192     return paraLevel;
193   }
194 
GetDirectionBidiParagraphData195   nsBidiDirection GetDirection() {
196     return mPresContext->GetBidiEngine().GetDirection();
197   }
198 
CountRunsBidiParagraphData199   nsresult CountRuns(int32_t* runCount) {
200     return mPresContext->GetBidiEngine().CountRuns(runCount);
201   }
202 
GetLogicalRunBidiParagraphData203   void GetLogicalRun(int32_t aLogicalStart, int32_t* aLogicalLimit,
204                      nsBidiLevel* aLevel) {
205     mPresContext->GetBidiEngine().GetLogicalRun(aLogicalStart, aLogicalLimit,
206                                                 aLevel);
207     if (mIsVisual) {
208       *aLevel = GetParaLevel();
209     }
210   }
211 
ResetDataBidiParagraphData212   void ResetData() {
213     mLogicalFrames.Clear();
214     mLinePerFrame.Clear();
215     mContentToFrameIndex.Clear();
216     mBuffer.SetLength(0);
217     mPrevContent = nullptr;
218     for (uint32_t i = 0; i < mEmbeddingStack.Length(); ++i) {
219       mBuffer.Append(mEmbeddingStack[i]);
220       mLogicalFrames.AppendElement(NS_BIDI_CONTROL_FRAME);
221       mLinePerFrame.AppendElement((nsLineBox*)nullptr);
222     }
223   }
224 
AppendFrameBidiParagraphData225   void AppendFrame(nsIFrame* aFrame, nsBlockInFlowLineIterator* aLineIter,
226                    nsIContent* aContent = nullptr) {
227     if (aContent) {
228       mContentToFrameIndex.Put(aContent, FrameCount());
229     }
230     mLogicalFrames.AppendElement(aFrame);
231 
232     AdvanceLineIteratorToFrame(aFrame, aLineIter, mPrevFrame);
233     mLinePerFrame.AppendElement(aLineIter->GetLine().get());
234   }
235 
AdvanceAndAppendFrameBidiParagraphData236   void AdvanceAndAppendFrame(nsIFrame** aFrame,
237                              nsBlockInFlowLineIterator* aLineIter,
238                              nsIFrame** aNextSibling) {
239     nsIFrame* frame = *aFrame;
240     nsIFrame* nextSibling = *aNextSibling;
241 
242     frame = frame->GetNextContinuation();
243     if (frame) {
244       AppendFrame(frame, aLineIter, nullptr);
245 
246       /*
247        * If we have already overshot the saved next-sibling while
248        * scanning the frame's continuations, advance it.
249        */
250       if (frame == nextSibling) {
251         nextSibling = frame->GetNextSibling();
252       }
253     }
254 
255     *aFrame = frame;
256     *aNextSibling = nextSibling;
257   }
258 
GetLastFrameForContentBidiParagraphData259   int32_t GetLastFrameForContent(nsIContent* aContent) {
260     int32_t index = 0;
261     mContentToFrameIndex.Get(aContent, &index);
262     return index;
263   }
264 
FrameCountBidiParagraphData265   int32_t FrameCount() { return mLogicalFrames.Length(); }
266 
BufferLengthBidiParagraphData267   int32_t BufferLength() { return mBuffer.Length(); }
268 
FrameAtBidiParagraphData269   nsIFrame* FrameAt(int32_t aIndex) { return mLogicalFrames[aIndex]; }
270 
GetLineForFrameAtBidiParagraphData271   nsLineBox* GetLineForFrameAt(int32_t aIndex) { return mLinePerFrame[aIndex]; }
272 
AppendUnicharBidiParagraphData273   void AppendUnichar(char16_t aCh) { mBuffer.Append(aCh); }
274 
AppendStringBidiParagraphData275   void AppendString(const nsDependentSubstring& aString) {
276     mBuffer.Append(aString);
277   }
278 
AppendControlCharBidiParagraphData279   void AppendControlChar(char16_t aCh) {
280     mLogicalFrames.AppendElement(NS_BIDI_CONTROL_FRAME);
281     mLinePerFrame.AppendElement((nsLineBox*)nullptr);
282     AppendUnichar(aCh);
283   }
284 
PushBidiControlBidiParagraphData285   void PushBidiControl(char16_t aCh) {
286     AppendControlChar(aCh);
287     mEmbeddingStack.AppendElement(aCh);
288   }
289 
AppendPopCharBidiParagraphData290   void AppendPopChar(char16_t aCh) {
291     AppendControlChar(IsIsolateControl(aCh) ? kPDI : kPDF);
292   }
293 
PopBidiControlBidiParagraphData294   void PopBidiControl(char16_t aCh) {
295     MOZ_ASSERT(mEmbeddingStack.Length(), "embedding/override underflow");
296     MOZ_ASSERT(aCh == mEmbeddingStack.LastElement());
297     AppendPopChar(aCh);
298     mEmbeddingStack.TruncateLength(mEmbeddingStack.Length() - 1);
299   }
300 
ClearBidiControlsBidiParagraphData301   void ClearBidiControls() {
302     for (char16_t c : Reversed(mEmbeddingStack)) {
303       AppendPopChar(c);
304     }
305   }
306 
IsFrameInCurrentLineBidiParagraphData307   static bool IsFrameInCurrentLine(nsBlockInFlowLineIterator* aLineIter,
308                                    nsIFrame* aPrevFrame, nsIFrame* aFrame) {
309     nsIFrame* endFrame = aLineIter->IsLastLineInList()
310                              ? nullptr
311                              : aLineIter->GetLine().next()->mFirstChild;
312     nsIFrame* startFrame =
313         aPrevFrame ? aPrevFrame : aLineIter->GetLine()->mFirstChild;
314     for (nsIFrame* frame = startFrame; frame && frame != endFrame;
315          frame = frame->GetNextSibling()) {
316       if (frame == aFrame) return true;
317     }
318     return false;
319   }
320 
AdvanceLineIteratorToFrameBidiParagraphData321   static void AdvanceLineIteratorToFrame(nsIFrame* aFrame,
322                                          nsBlockInFlowLineIterator* aLineIter,
323                                          nsIFrame*& aPrevFrame) {
324     // Advance aLine to the line containing aFrame
325     nsIFrame* child = aFrame;
326     nsIFrame* parent = nsLayoutUtils::GetParentOrPlaceholderFor(child);
327     while (parent && !nsLayoutUtils::GetAsBlock(parent)) {
328       child = parent;
329       parent = nsLayoutUtils::GetParentOrPlaceholderFor(child);
330     }
331     NS_ASSERTION(parent, "aFrame is not a descendent of a block frame");
332     while (!IsFrameInCurrentLine(aLineIter, aPrevFrame, child)) {
333 #ifdef DEBUG
334       bool hasNext =
335 #endif
336           aLineIter->Next();
337       NS_ASSERTION(hasNext, "Can't find frame in lines!");
338       aPrevFrame = nullptr;
339     }
340     aPrevFrame = child;
341   }
342 };
343 
344 struct MOZ_STACK_CLASS BidiLineData {
345   AutoTArray<nsIFrame*, 16> mLogicalFrames;
346   AutoTArray<nsIFrame*, 16> mVisualFrames;
347   AutoTArray<int32_t, 16> mIndexMap;
348   AutoTArray<uint8_t, 16> mLevels;
349   bool mIsReordered;
350 
BidiLineDataBidiLineData351   BidiLineData(nsIFrame* aFirstFrameOnLine, int32_t aNumFramesOnLine) {
352     /**
353      * Initialize the logically-ordered array of frames using the top-level
354      * frames of a single line
355      */
356     bool isReordered = false;
357     bool hasRTLFrames = false;
358     bool hasVirtualControls = false;
359 
360     auto appendFrame = [&](nsIFrame* frame, nsBidiLevel level) {
361       mLogicalFrames.AppendElement(frame);
362       mLevels.AppendElement(level);
363       mIndexMap.AppendElement(0);
364       if (IS_LEVEL_RTL(level)) {
365         hasRTLFrames = true;
366       }
367     };
368 
369     bool firstFrame = true;
370     for (nsIFrame* frame = aFirstFrameOnLine; frame && aNumFramesOnLine--;
371          frame = frame->GetNextSibling()) {
372       FrameBidiData bidiData = nsBidiPresUtils::GetFrameBidiData(frame);
373       // Ignore virtual control before the first frame. Doing so should
374       // not affect the visual result, but could avoid running into the
375       // stripping code below for many cases.
376       if (!firstFrame && bidiData.precedingControl != kBidiLevelNone) {
377         appendFrame(NS_BIDI_CONTROL_FRAME, bidiData.precedingControl);
378         hasVirtualControls = true;
379       }
380       appendFrame(frame, bidiData.embeddingLevel);
381       firstFrame = false;
382     }
383 
384     // Reorder the line
385     nsBidi::ReorderVisual(mLevels.Elements(), FrameCount(),
386                           mIndexMap.Elements());
387 
388     // Strip virtual frames
389     if (hasVirtualControls) {
390       auto originalCount = mLogicalFrames.Length();
391       AutoTArray<int32_t, 16> realFrameMap;
392       realFrameMap.SetCapacity(originalCount);
393       size_t count = 0;
394       for (auto i : IntegerRange(originalCount)) {
395         if (mLogicalFrames[i] == NS_BIDI_CONTROL_FRAME) {
396           realFrameMap.AppendElement(-1);
397         } else {
398           mLogicalFrames[count] = mLogicalFrames[i];
399           mLevels[count] = mLevels[i];
400           realFrameMap.AppendElement(count);
401           count++;
402         }
403       }
404       // Only keep index map for real frames.
405       for (size_t i = 0, j = 0; i < originalCount; ++i) {
406         auto newIndex = realFrameMap[mIndexMap[i]];
407         if (newIndex != -1) {
408           mIndexMap[j] = newIndex;
409           j++;
410         }
411       }
412       mLogicalFrames.TruncateLength(count);
413       mLevels.TruncateLength(count);
414       mIndexMap.TruncateLength(count);
415     }
416 
417     for (int32_t i = 0; i < FrameCount(); i++) {
418       mVisualFrames.AppendElement(LogicalFrameAt(mIndexMap[i]));
419       if (i != mIndexMap[i]) {
420         isReordered = true;
421       }
422     }
423 
424     // If there's an RTL frame, assume the line is reordered
425     mIsReordered = isReordered || hasRTLFrames;
426   }
427 
FrameCountBidiLineData428   int32_t FrameCount() const { return mLogicalFrames.Length(); }
429 
LogicalFrameAtBidiLineData430   nsIFrame* LogicalFrameAt(int32_t aIndex) const {
431     return mLogicalFrames[aIndex];
432   }
433 
VisualFrameAtBidiLineData434   nsIFrame* VisualFrameAt(int32_t aIndex) const {
435     return mVisualFrames[aIndex];
436   }
437 };
438 
439 #ifdef DEBUG
440 extern "C" {
DumpFrameArray(const nsTArray<nsIFrame * > & aFrames)441 void MOZ_EXPORT DumpFrameArray(const nsTArray<nsIFrame*>& aFrames) {
442   for (nsIFrame* frame : aFrames) {
443     if (frame == NS_BIDI_CONTROL_FRAME) {
444       fprintf_stderr(stderr, "(Bidi control frame)\n");
445     } else {
446       frame->List();
447     }
448   }
449 }
450 
DumpBidiLine(BidiLineData * aData,bool aVisualOrder)451 void MOZ_EXPORT DumpBidiLine(BidiLineData* aData, bool aVisualOrder) {
452   DumpFrameArray(aVisualOrder ? aData->mVisualFrames : aData->mLogicalFrames);
453 }
454 }
455 #endif
456 
457 /* Some helper methods for Resolve() */
458 
459 // Should this frame be split between text runs?
IsBidiSplittable(nsIFrame * aFrame)460 static bool IsBidiSplittable(nsIFrame* aFrame) {
461   // Bidi inline containers should be split, unless they're line frames.
462   LayoutFrameType frameType = aFrame->Type();
463   return (aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer) &&
464           frameType != LayoutFrameType::Line) ||
465          frameType == LayoutFrameType::Text;
466 }
467 
468 // Should this frame be treated as a leaf (e.g. when building mLogicalFrames)?
IsBidiLeaf(nsIFrame * aFrame)469 static bool IsBidiLeaf(nsIFrame* aFrame) {
470   nsIFrame* kid = aFrame->PrincipalChildList().FirstChild();
471   return !kid || !aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer);
472 }
473 
474 /**
475  * Create non-fluid continuations for the ancestors of a given frame all the way
476  * up the frame tree until we hit a non-splittable frame (a line or a block).
477  *
478  * @param aParent the first parent frame to be split
479  * @param aFrame the child frames after this frame are reparented to the
480  *        newly-created continuation of aParent.
481  *        If aFrame is null, all the children of aParent are reparented.
482  */
SplitInlineAncestors(nsContainerFrame * aParent,nsIFrame * aFrame)483 static nsresult SplitInlineAncestors(nsContainerFrame* aParent,
484                                      nsIFrame* aFrame) {
485   nsPresContext* presContext = aParent->PresContext();
486   nsIPresShell* presShell = presContext->PresShell();
487   nsIFrame* frame = aFrame;
488   nsContainerFrame* parent = aParent;
489   nsContainerFrame* newParent;
490 
491   while (IsBidiSplittable(parent)) {
492     nsContainerFrame* grandparent = parent->GetParent();
493     NS_ASSERTION(grandparent,
494                  "Couldn't get parent's parent in "
495                  "nsBidiPresUtils::SplitInlineAncestors");
496 
497     // Split the child list after |frame|, unless it is the last child.
498     if (!frame || frame->GetNextSibling()) {
499       newParent = static_cast<nsContainerFrame*>(
500           presShell->FrameConstructor()->CreateContinuingFrame(
501               presContext, parent, grandparent, false));
502 
503       nsFrameList tail = parent->StealFramesAfter(frame);
504 
505       // Reparent views as necessary
506       nsresult rv;
507       rv = nsContainerFrame::ReparentFrameViewList(tail, parent, newParent);
508       if (NS_FAILED(rv)) {
509         return rv;
510       }
511 
512       // The parent's continuation adopts the siblings after the split.
513       newParent->InsertFrames(nsIFrame::kNoReflowPrincipalList, nullptr, tail);
514 
515       // The list name kNoReflowPrincipalList would indicate we don't want
516       // reflow
517       nsFrameList temp(newParent, newParent);
518       grandparent->InsertFrames(nsIFrame::kNoReflowPrincipalList, parent, temp);
519     }
520 
521     frame = parent;
522     parent = grandparent;
523   }
524 
525   return NS_OK;
526 }
527 
MakeContinuationFluid(nsIFrame * aFrame,nsIFrame * aNext)528 static void MakeContinuationFluid(nsIFrame* aFrame, nsIFrame* aNext) {
529   NS_ASSERTION(!aFrame->GetNextInFlow() || aFrame->GetNextInFlow() == aNext,
530                "next-in-flow is not next continuation!");
531   aFrame->SetNextInFlow(aNext);
532 
533   NS_ASSERTION(!aNext->GetPrevInFlow() || aNext->GetPrevInFlow() == aFrame,
534                "prev-in-flow is not prev continuation!");
535   aNext->SetPrevInFlow(aFrame);
536 }
537 
MakeContinuationsNonFluidUpParentChain(nsIFrame * aFrame,nsIFrame * aNext)538 static void MakeContinuationsNonFluidUpParentChain(nsIFrame* aFrame,
539                                                    nsIFrame* aNext) {
540   nsIFrame* frame;
541   nsIFrame* next;
542 
543   for (frame = aFrame, next = aNext;
544        frame && next && next != frame && next == frame->GetNextInFlow() &&
545        IsBidiSplittable(frame);
546        frame = frame->GetParent(), next = next->GetParent()) {
547     frame->SetNextContinuation(next);
548     next->SetPrevContinuation(frame);
549   }
550 }
551 
552 // If aFrame is the last child of its parent, convert bidi continuations to
553 // fluid continuations for all of its inline ancestors.
554 // If it isn't the last child, make sure that its continuation is fluid.
JoinInlineAncestors(nsIFrame * aFrame)555 static void JoinInlineAncestors(nsIFrame* aFrame) {
556   nsIFrame* frame = aFrame;
557   do {
558     nsIFrame* next = frame->GetNextContinuation();
559     if (next) {
560       MakeContinuationFluid(frame, next);
561     }
562     // Join the parent only as long as we're its last child.
563     if (frame->GetNextSibling()) break;
564     frame = frame->GetParent();
565   } while (frame && IsBidiSplittable(frame));
566 }
567 
CreateContinuation(nsIFrame * aFrame,nsIFrame ** aNewFrame,bool aIsFluid)568 static nsresult CreateContinuation(nsIFrame* aFrame, nsIFrame** aNewFrame,
569                                    bool aIsFluid) {
570   NS_PRECONDITION(aNewFrame, "null OUT ptr");
571   NS_PRECONDITION(aFrame, "null ptr");
572 
573   *aNewFrame = nullptr;
574 
575   nsPresContext* presContext = aFrame->PresContext();
576   nsIPresShell* presShell = presContext->PresShell();
577   NS_ASSERTION(presShell,
578                "PresShell must be set on PresContext before calling "
579                "nsBidiPresUtils::CreateContinuation");
580 
581   nsContainerFrame* parent = aFrame->GetParent();
582   NS_ASSERTION(
583       parent,
584       "Couldn't get frame parent in nsBidiPresUtils::CreateContinuation");
585 
586   nsresult rv = NS_OK;
587 
588   // Have to special case floating first letter frames because the continuation
589   // doesn't go in the first letter frame. The continuation goes with the rest
590   // of the text that the first letter frame was made out of.
591   if (parent->IsLetterFrame() && parent->IsFloating()) {
592     nsFirstLetterFrame* letterFrame = do_QueryFrame(parent);
593     rv = letterFrame->CreateContinuationForFloatingParent(presContext, aFrame,
594                                                           aNewFrame, aIsFluid);
595     return rv;
596   }
597 
598   *aNewFrame = presShell->FrameConstructor()->CreateContinuingFrame(
599       presContext, aFrame, parent, aIsFluid);
600 
601   // The list name kNoReflowPrincipalList would indicate we don't want reflow
602   // XXXbz this needs higher-level framelist love
603   nsFrameList temp(*aNewFrame, *aNewFrame);
604   parent->InsertFrames(nsIFrame::kNoReflowPrincipalList, aFrame, temp);
605 
606   if (!aIsFluid) {
607     // Split inline ancestor frames
608     rv = SplitInlineAncestors(parent, aFrame);
609     if (NS_FAILED(rv)) {
610       return rv;
611     }
612   }
613 
614   return NS_OK;
615 }
616 
617 /*
618  * Overview of the implementation of Resolve():
619  *
620  *  Walk through the descendants of aBlockFrame and build:
621  *   * mLogicalFrames: an nsTArray of nsIFrame* pointers in logical order
622  *   * mBuffer: an nsString containing a representation of
623  *     the content of the frames.
624  *     In the case of text frames, this is the actual text context of the
625  *     frames, but some other elements are represented in a symbolic form which
626  *     will make the Unicode Bidi Algorithm give the correct results.
627  *     Bidi isolates, embeddings, and overrides set by CSS, <bdi>, or <bdo>
628  *     elements are represented by the corresponding Unicode control characters.
629  *     <br> elements are represented by U+2028 LINE SEPARATOR
630  *     Other inline elements are represented by U+FFFC OBJECT REPLACEMENT
631  *     CHARACTER
632  *
633  *  Then pass mBuffer to the Bidi engine for resolving of embedding levels
634  *  by nsBidi::SetPara() and division into directional runs by
635  *  nsBidi::CountRuns().
636  *
637  *  Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and
638  *  correlate them with the frames indexed in mLogicalFrames, setting the
639  *  baseLevel and embeddingLevel properties according to the results returned
640  *  by the Bidi engine.
641  *
642  *  The rendering layer requires each text frame to contain text in only one
643  *  direction, so we may need to call EnsureBidiContinuation() to split frames.
644  *  We may also need to call RemoveBidiContinuation() to convert frames created
645  *  by EnsureBidiContinuation() in previous reflows into fluid continuations.
646  */
Resolve(nsBlockFrame * aBlockFrame)647 nsresult nsBidiPresUtils::Resolve(nsBlockFrame* aBlockFrame) {
648   BidiParagraphData bpd(aBlockFrame);
649 
650   // Handle bidi-override being set on the block itself before calling
651   // TraverseFrames.
652   // No need to call GetBidiControl as well, because isolate and embed
653   // values of unicode-bidi property are redundant on block elements.
654   // unicode-bidi:plaintext on a block element is handled by block frame
655   // via using nsIFrame::GetWritingMode(nsIFrame*).
656   char16_t ch = GetBidiOverride(aBlockFrame->StyleContext());
657   if (ch != 0) {
658     bpd.PushBidiControl(ch);
659     bpd.mRequiresBidi = true;
660   } else {
661     // If there are no unicode-bidi properties and no RTL characters in the
662     // block's content, then it is pure LTR and we can skip the rest of bidi
663     // resolution.
664     nsIContent* currContent = nullptr;
665     for (nsBlockFrame* block = aBlockFrame; block;
666          block = static_cast<nsBlockFrame*>(block->GetNextContinuation())) {
667       block->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
668       if (!bpd.mRequiresBidi &&
669           ChildListMayRequireBidi(block->PrincipalChildList().FirstChild(),
670                                   &currContent)) {
671         bpd.mRequiresBidi = true;
672       }
673       if (!bpd.mRequiresBidi) {
674         nsBlockFrame::FrameLines* overflowLines = block->GetOverflowLines();
675         if (overflowLines) {
676           if (ChildListMayRequireBidi(overflowLines->mFrames.FirstChild(),
677                                       &currContent)) {
678             bpd.mRequiresBidi = true;
679           }
680         }
681       }
682     }
683     if (!bpd.mRequiresBidi) {
684       return NS_OK;
685     }
686   }
687 
688   for (nsBlockFrame* block = aBlockFrame; block;
689        block = static_cast<nsBlockFrame*>(block->GetNextContinuation())) {
690 #ifdef DEBUG
691     bpd.mCurrentBlock = block;
692 #endif
693     block->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
694     nsBlockInFlowLineIterator it(block, block->LinesBegin());
695     bpd.mPrevFrame = nullptr;
696     TraverseFrames(&it, block->PrincipalChildList().FirstChild(), &bpd);
697     nsBlockFrame::FrameLines* overflowLines = block->GetOverflowLines();
698     if (overflowLines) {
699       nsBlockInFlowLineIterator it(block, overflowLines->mLines.begin(), true);
700       bpd.mPrevFrame = nullptr;
701       TraverseFrames(&it, overflowLines->mFrames.FirstChild(), &bpd);
702     }
703   }
704 
705   if (ch != 0) {
706     bpd.PopBidiControl(ch);
707   }
708 
709   return ResolveParagraph(&bpd);
710 }
711 
ResolveParagraph(BidiParagraphData * aBpd)712 nsresult nsBidiPresUtils::ResolveParagraph(BidiParagraphData* aBpd) {
713   if (aBpd->BufferLength() < 1) {
714     return NS_OK;
715   }
716   aBpd->mBuffer.ReplaceChar(kSeparators, kSpace);
717 
718   int32_t runCount;
719 
720   nsresult rv = aBpd->SetPara();
721   NS_ENSURE_SUCCESS(rv, rv);
722 
723   nsBidiLevel embeddingLevel = aBpd->GetParaLevel();
724 
725   rv = aBpd->CountRuns(&runCount);
726   NS_ENSURE_SUCCESS(rv, rv);
727 
728   int32_t runLength = 0;     // the length of the current run of text
729   int32_t logicalLimit = 0;  // the end of the current run + 1
730   int32_t numRun = -1;
731   int32_t fragmentLength = 0;  // the length of the current text frame
732   int32_t frameIndex = -1;     // index to the frames in mLogicalFrames
733   int32_t frameCount = aBpd->FrameCount();
734   int32_t contentOffset = 0;  // offset of current frame in its content node
735   bool isTextFrame = false;
736   nsIFrame* frame = nullptr;
737   nsIContent* content = nullptr;
738   int32_t contentTextLength = 0;
739 
740   nsLineBox* currentLine = nullptr;
741 
742 #ifdef DEBUG
743 #ifdef NOISY_BIDI
744   printf(
745       "Before Resolve(), mCurrentBlock=%p, mBuffer='%s', frameCount=%d, "
746       "runCount=%d\n",
747       (void*)aBpd->mCurrentBlock, NS_ConvertUTF16toUTF8(aBpd->mBuffer).get(),
748       frameCount, runCount);
749 #ifdef REALLY_NOISY_BIDI
750   printf(" block frame tree=:\n");
751   aBpd->mCurrentBlock->List(stdout, 0);
752 #endif
753 #endif
754 #endif
755 
756   if (runCount == 1 && frameCount == 1 && aBpd->GetDirection() == NSBIDI_LTR &&
757       aBpd->GetParaLevel() == 0) {
758     // We have a single left-to-right frame in a left-to-right paragraph,
759     // without bidi isolation from the surrounding text.
760     // Make sure that the embedding level and base level frame properties aren't
761     // set (because if they are this frame used to have some other direction,
762     // so we can't do this optimization), and we're done.
763     nsIFrame* frame = aBpd->FrameAt(0);
764     if (frame != NS_BIDI_CONTROL_FRAME) {
765       FrameBidiData bidiData = frame->GetBidiData();
766       if (!bidiData.embeddingLevel && !bidiData.baseLevel) {
767 #ifdef DEBUG
768 #ifdef NOISY_BIDI
769         printf("early return for single direction frame %p\n", (void*)frame);
770 #endif
771 #endif
772         frame->AddStateBits(NS_FRAME_IS_BIDI);
773         return NS_OK;
774       }
775     }
776   }
777 
778   nsIFrame* lastRealFrame = nullptr;
779   nsBidiLevel lastEmbeddingLevel = kBidiLevelNone;
780   nsBidiLevel precedingControl = kBidiLevelNone;
781 
782   auto storeBidiDataToFrame = [&]() {
783     FrameBidiData bidiData;
784     bidiData.embeddingLevel = embeddingLevel;
785     bidiData.baseLevel = aBpd->GetParaLevel();
786     // If a control character doesn't have a lower embedding level than
787     // both the preceding and the following frame, it isn't something
788     // needed for getting the correct result. This optimization should
789     // remove almost all of embeds and overrides, and some of isolates.
790     if (precedingControl >= embeddingLevel ||
791         precedingControl >= lastEmbeddingLevel) {
792       bidiData.precedingControl = kBidiLevelNone;
793     } else {
794       bidiData.precedingControl = precedingControl;
795     }
796     precedingControl = kBidiLevelNone;
797     lastEmbeddingLevel = embeddingLevel;
798     frame->SetProperty(nsIFrame::BidiDataProperty(), bidiData);
799   };
800 
801   for (;;) {
802     if (fragmentLength <= 0) {
803       // Get the next frame from mLogicalFrames
804       if (++frameIndex >= frameCount) {
805         break;
806       }
807       frame = aBpd->FrameAt(frameIndex);
808       if (frame == NS_BIDI_CONTROL_FRAME || !frame->IsTextFrame()) {
809         /*
810          * Any non-text frame corresponds to a single character in the text
811          * buffer (a bidi control character, LINE SEPARATOR, or OBJECT
812          * SUBSTITUTE)
813          */
814         isTextFrame = false;
815         fragmentLength = 1;
816       } else {
817         currentLine = aBpd->GetLineForFrameAt(frameIndex);
818         content = frame->GetContent();
819         if (!content) {
820           rv = NS_OK;
821           break;
822         }
823         contentTextLength = content->TextLength();
824         int32_t start, end;
825         frame->GetOffsets(start, end);
826         NS_ASSERTION(!(contentTextLength < end - start),
827                      "Frame offsets don't fit in content");
828         fragmentLength = std::min(contentTextLength, end - start);
829         contentOffset = start;
830         isTextFrame = true;
831       }
832     }  // if (fragmentLength <= 0)
833 
834     if (runLength <= 0) {
835       // Get the next run of text from the Bidi engine
836       if (++numRun >= runCount) {
837         // We've run out of runs of text; but don't forget to store bidi data
838         // to the frame before breaking out of the loop (bug 1426042).
839         storeBidiDataToFrame();
840         if (isTextFrame) {
841           frame->AdjustOffsetsForBidi(contentOffset,
842                                       contentOffset + fragmentLength);
843         }
844         break;
845       }
846       int32_t lineOffset = logicalLimit;
847       aBpd->GetLogicalRun(lineOffset, &logicalLimit, &embeddingLevel);
848       runLength = logicalLimit - lineOffset;
849     }  // if (runLength <= 0)
850 
851     if (frame == NS_BIDI_CONTROL_FRAME) {
852       // In theory, we only need to do this for isolates. However, it is
853       // easier to do this for all here because we do not maintain the
854       // index to get corresponding character from buffer. Since we do
855       // have proper embedding level for all those characters, including
856       // them wouldn't affect the final result.
857       precedingControl = std::min(precedingControl, embeddingLevel);
858     } else {
859       storeBidiDataToFrame();
860       if (isTextFrame) {
861         if (contentTextLength == 0) {
862           // Set the base level and embedding level of the current run even
863           // on an empty frame. Otherwise frame reordering will not be correct.
864           frame->AdjustOffsetsForBidi(0, 0);
865           // Nothing more to do for an empty frame.
866           continue;
867         }
868         if ((runLength > 0) && (runLength < fragmentLength)) {
869           /*
870            * The text in this frame continues beyond the end of this directional
871            * run. Create a non-fluid continuation frame for the next directional
872            * run.
873            */
874           currentLine->MarkDirty();
875           nsIFrame* nextBidi;
876           int32_t runEnd = contentOffset + runLength;
877           rv = EnsureBidiContinuation(frame, &nextBidi, contentOffset, runEnd);
878           if (NS_FAILED(rv)) {
879             break;
880           }
881           nextBidi->AdjustOffsetsForBidi(runEnd,
882                                          contentOffset + fragmentLength);
883           frame = nextBidi;
884           contentOffset = runEnd;
885         }  // if (runLength < fragmentLength)
886         else {
887           if (contentOffset + fragmentLength == contentTextLength) {
888             /*
889              * We have finished all the text in this content node. Convert any
890              * further non-fluid continuations to fluid continuations and
891              * advance frameIndex to the last frame in the content node
892              */
893             int32_t newIndex = aBpd->GetLastFrameForContent(content);
894             if (newIndex > frameIndex) {
895               currentLine->MarkDirty();
896               RemoveBidiContinuation(aBpd, frame, frameIndex, newIndex);
897               frameIndex = newIndex;
898               frame = aBpd->FrameAt(frameIndex);
899             }
900           } else if (fragmentLength > 0 && runLength > fragmentLength) {
901             /*
902              * There is more text that belongs to this directional run in the
903              * next text frame: make sure it is a fluid continuation of the
904              * current frame. Do not advance frameIndex, because the next frame
905              * may contain multi-directional text and need to be split
906              */
907             int32_t newIndex = frameIndex;
908             do {
909             } while (++newIndex < frameCount &&
910                      aBpd->FrameAt(newIndex) == NS_BIDI_CONTROL_FRAME);
911             if (newIndex < frameCount) {
912               currentLine->MarkDirty();
913               RemoveBidiContinuation(aBpd, frame, frameIndex, newIndex);
914             }
915           } else if (runLength == fragmentLength) {
916             /*
917              * If the directional run ends at the end of the frame, make sure
918              * that any continuation is non-fluid, and do the same up the
919              * parent chain
920              */
921             nsIFrame* next = frame->GetNextInFlow();
922             if (next) {
923               currentLine->MarkDirty();
924               MakeContinuationsNonFluidUpParentChain(frame, next);
925             }
926           }
927           frame->AdjustOffsetsForBidi(contentOffset,
928                                       contentOffset + fragmentLength);
929         }
930       }  // isTextFrame
931     }    // not bidi control frame
932     int32_t temp = runLength;
933     runLength -= fragmentLength;
934     fragmentLength -= temp;
935 
936     // Record last real frame so that we can do splitting properly even
937     // if a run ends after a virtual bidi control frame.
938     if (frame != NS_BIDI_CONTROL_FRAME) {
939       lastRealFrame = frame;
940     }
941     if (lastRealFrame && fragmentLength <= 0) {
942       // If the frame is at the end of a run, and this is not the end of our
943       // paragraph, split all ancestor inlines that need splitting.
944       // To determine whether we're at the end of the run, we check that we've
945       // finished processing the current run, and that the current frame
946       // doesn't have a fluid continuation (it could have a fluid continuation
947       // of zero length, so testing runLength alone is not sufficient).
948       if (runLength <= 0 && !lastRealFrame->GetNextInFlow()) {
949         if (numRun + 1 < runCount) {
950           nsIFrame* child = lastRealFrame;
951           nsContainerFrame* parent = child->GetParent();
952           // As long as we're on the last sibling, the parent doesn't have to
953           // be split.
954           // However, if the parent has a fluid continuation, we do have to make
955           // it non-fluid. This can happen e.g. when we have a first-letter
956           // frame and the end of the first-letter coincides with the end of a
957           // directional run.
958           while (parent && IsBidiSplittable(parent) &&
959                  !child->GetNextSibling()) {
960             nsIFrame* next = parent->GetNextInFlow();
961             if (next) {
962               parent->SetNextContinuation(next);
963               next->SetPrevContinuation(parent);
964             }
965             child = parent;
966             parent = child->GetParent();
967           }
968           if (parent && IsBidiSplittable(parent)) {
969             SplitInlineAncestors(parent, child);
970           }
971         }
972       } else if (frame != NS_BIDI_CONTROL_FRAME) {
973         // We're not at an end of a run. If |frame| is the last child of its
974         // parent, and its ancestors happen to have bidi continuations, convert
975         // them into fluid continuations.
976         JoinInlineAncestors(frame);
977       }
978     }
979   }  // for
980 
981 #ifdef DEBUG
982 #ifdef REALLY_NOISY_BIDI
983   printf("---\nAfter Resolve(), frameTree =:\n");
984   aBpd->mCurrentBlock->List(stdout, 0);
985   printf("===\n");
986 #endif
987 #endif
988 
989   return rv;
990 }
991 
TraverseFrames(nsBlockInFlowLineIterator * aLineIter,nsIFrame * aCurrentFrame,BidiParagraphData * aBpd)992 void nsBidiPresUtils::TraverseFrames(nsBlockInFlowLineIterator* aLineIter,
993                                      nsIFrame* aCurrentFrame,
994                                      BidiParagraphData* aBpd) {
995   if (!aCurrentFrame) return;
996 
997 #ifdef DEBUG
998   nsBlockFrame* initialLineContainer = aLineIter->GetContainer();
999 #endif
1000 
1001   nsIFrame* childFrame = aCurrentFrame;
1002   do {
1003     /*
1004      * It's important to get the next sibling and next continuation *before*
1005      * handling the frame: If we encounter a forced paragraph break and call
1006      * ResolveParagraph within this loop, doing GetNextSibling and
1007      * GetNextContinuation after that could return a bidi continuation that had
1008      * just been split from the original childFrame and we would process it
1009      * twice.
1010      */
1011     nsIFrame* nextSibling = childFrame->GetNextSibling();
1012 
1013     // If the real frame for a placeholder is a first letter frame, we need to
1014     // drill down into it and include its contents in Bidi resolution.
1015     // If not, we just use the placeholder.
1016     nsIFrame* frame = childFrame;
1017     if (childFrame->IsPlaceholderFrame()) {
1018       nsIFrame* realFrame =
1019           nsPlaceholderFrame::GetRealFrameForPlaceholder(childFrame);
1020       if (realFrame->IsLetterFrame()) {
1021         frame = realFrame;
1022       }
1023     }
1024 
1025     auto DifferentBidiValues = [](nsStyleContext* aSC1, nsIFrame* aFrame2) {
1026       nsStyleContext* sc2 = aFrame2->StyleContext();
1027       return GetBidiControl(aSC1) != GetBidiControl(sc2) ||
1028              GetBidiOverride(aSC1) != GetBidiOverride(sc2);
1029     };
1030 
1031     nsStyleContext* sc = frame->StyleContext();
1032     nsIFrame* nextContinuation = frame->GetNextContinuation();
1033     nsIFrame* prevContinuation = frame->GetPrevContinuation();
1034     bool isLastFrame =
1035         !nextContinuation || DifferentBidiValues(sc, nextContinuation);
1036     bool isFirstFrame =
1037         !prevContinuation || DifferentBidiValues(sc, prevContinuation);
1038 
1039     char16_t controlChar = 0;
1040     char16_t overrideChar = 0;
1041     if (frame->IsFrameOfType(nsIFrame::eBidiInlineContainer)) {
1042       if (!(frame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1043         nsContainerFrame* c = static_cast<nsContainerFrame*>(frame);
1044         MOZ_ASSERT(c == do_QueryFrame(frame),
1045                    "eBidiInlineContainer must be a nsContainerFrame subclass");
1046         c->DrainSelfOverflowList();
1047       }
1048 
1049       controlChar = GetBidiControl(sc);
1050       overrideChar = GetBidiOverride(sc);
1051 
1052       // Add dummy frame pointers representing bidi control codes before
1053       // the first frames of elements specifying override, isolation, or
1054       // plaintext.
1055       if (isFirstFrame) {
1056         if (controlChar != 0) {
1057           aBpd->PushBidiControl(controlChar);
1058         }
1059         if (overrideChar != 0) {
1060           aBpd->PushBidiControl(overrideChar);
1061         }
1062       }
1063     }
1064 
1065     if (IsBidiLeaf(frame)) {
1066       /* Bidi leaf frame: add the frame to the mLogicalFrames array,
1067        * and add its index to the mContentToFrameIndex hashtable. This
1068        * will be used in RemoveBidiContinuation() to identify the last
1069        * frame in the array with a given content.
1070        */
1071       nsIContent* content = frame->GetContent();
1072       aBpd->AppendFrame(frame, aLineIter, content);
1073 
1074       // Append the content of the frame to the paragraph buffer
1075       LayoutFrameType frameType = frame->Type();
1076       if (LayoutFrameType::Text == frameType) {
1077         if (content != aBpd->mPrevContent) {
1078           aBpd->mPrevContent = content;
1079           if (!frame->StyleText()->NewlineIsSignificant(
1080                   static_cast<nsTextFrame*>(frame))) {
1081             content->AppendTextTo(aBpd->mBuffer);
1082           } else {
1083             /*
1084              * For preformatted text we have to do bidi resolution on each line
1085              * separately.
1086              */
1087             nsAutoString text;
1088             content->AppendTextTo(text);
1089             nsIFrame* next;
1090             do {
1091               next = nullptr;
1092 
1093               int32_t start, end;
1094               frame->GetOffsets(start, end);
1095               int32_t endLine = text.FindChar('\n', start);
1096               if (endLine == -1) {
1097                 /*
1098                  * If there is no newline in the text content, just save the
1099                  * text from this frame and its continuations, and do bidi
1100                  * resolution later
1101                  */
1102                 aBpd->AppendString(Substring(text, start));
1103                 while (frame && nextSibling) {
1104                   aBpd->AdvanceAndAppendFrame(&frame, aLineIter, &nextSibling);
1105                 }
1106                 break;
1107               }
1108 
1109               /*
1110                * If there is a newline in the frame, break the frame after the
1111                * newline, do bidi resolution and repeat until the last sibling
1112                */
1113               ++endLine;
1114 
1115               /*
1116                * If the frame ends before the new line, save the text and move
1117                * into the next continuation
1118                */
1119               aBpd->AppendString(
1120                   Substring(text, start, std::min(end, endLine) - start));
1121               while (end < endLine && nextSibling) {
1122                 aBpd->AdvanceAndAppendFrame(&frame, aLineIter, &nextSibling);
1123                 NS_ASSERTION(frame, "Premature end of continuation chain");
1124                 frame->GetOffsets(start, end);
1125                 aBpd->AppendString(
1126                     Substring(text, start, std::min(end, endLine) - start));
1127               }
1128 
1129               if (end < endLine) {
1130                 aBpd->mPrevContent = nullptr;
1131                 break;
1132               }
1133 
1134               bool createdContinuation = false;
1135               if (uint32_t(endLine) < text.Length()) {
1136                 /*
1137                  * Timing is everything here: if the frame already has a bidi
1138                  * continuation, we need to make the continuation fluid *before*
1139                  * resetting the length of the current frame. Otherwise
1140                  * nsTextFrame::SetLength won't set the continuation frame's
1141                  * text offsets correctly.
1142                  *
1143                  * On the other hand, if the frame doesn't have a continuation,
1144                  * we need to create one *after* resetting the length, or
1145                  * CreateContinuingFrame will complain that there is no more
1146                  * content for the continuation.
1147                  */
1148                 next = frame->GetNextInFlow();
1149                 if (!next) {
1150                   // If the frame already has a bidi continuation, make it fluid
1151                   next = frame->GetNextContinuation();
1152                   if (next) {
1153                     MakeContinuationFluid(frame, next);
1154                     JoinInlineAncestors(frame);
1155                   }
1156                 }
1157 
1158                 nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
1159                 textFrame->SetLength(endLine - start, nullptr);
1160 
1161                 if (!next) {
1162                   // If the frame has no next in flow, create one.
1163                   CreateContinuation(frame, &next, true);
1164                   createdContinuation = true;
1165                 }
1166                 // Mark the line before the newline as dirty.
1167                 aBpd->GetLineForFrameAt(aBpd->FrameCount() - 1)->MarkDirty();
1168               }
1169               ResolveParagraphWithinBlock(aBpd);
1170 
1171               if (!nextSibling && !createdContinuation) {
1172                 break;
1173               } else if (next) {
1174                 frame = next;
1175                 aBpd->AppendFrame(frame, aLineIter);
1176                 // Mark the line after the newline as dirty.
1177                 aBpd->GetLineForFrameAt(aBpd->FrameCount() - 1)->MarkDirty();
1178               }
1179 
1180               /*
1181                * If we have already overshot the saved next-sibling while
1182                * scanning the frame's continuations, advance it.
1183                */
1184               if (frame && frame == nextSibling) {
1185                 nextSibling = frame->GetNextSibling();
1186               }
1187 
1188             } while (next);
1189           }
1190         }
1191       } else if (LayoutFrameType::Br == frameType) {
1192         // break frame -- append line separator
1193         aBpd->AppendUnichar(kLineSeparator);
1194         ResolveParagraphWithinBlock(aBpd);
1195       } else {
1196         // other frame type -- see the Unicode Bidi Algorithm:
1197         // "...inline objects (such as graphics) are treated as if they are ...
1198         // U+FFFC"
1199         // <wbr>, however, is treated as U+200B ZERO WIDTH SPACE. See
1200         // http://dev.w3.org/html5/spec/Overview.html#phrasing-content-1
1201         aBpd->AppendUnichar(
1202             content->IsHTMLElement(nsGkAtoms::wbr) ? kZWSP : kObjectSubstitute);
1203         if (!frame->IsInlineOutside()) {
1204           // if it is not inline, end the paragraph
1205           ResolveParagraphWithinBlock(aBpd);
1206         }
1207       }
1208     } else {
1209       // For a non-leaf frame, recurse into TraverseFrames
1210       nsIFrame* kid = frame->PrincipalChildList().FirstChild();
1211       MOZ_ASSERT(!frame->GetChildList(nsIFrame::kOverflowList).FirstChild(),
1212                  "should have drained the overflow list above");
1213       if (kid) {
1214         TraverseFrames(aLineIter, kid, aBpd);
1215       }
1216     }
1217 
1218     // If the element is attributed by dir, indicate direction pop (add PDF
1219     // frame)
1220     if (isLastFrame) {
1221       // Add a dummy frame pointer representing a bidi control code after the
1222       // last frame of an element specifying embedding or override
1223       if (overrideChar != 0) {
1224         aBpd->PopBidiControl(overrideChar);
1225       }
1226       if (controlChar != 0) {
1227         aBpd->PopBidiControl(controlChar);
1228       }
1229     }
1230     childFrame = nextSibling;
1231   } while (childFrame);
1232 
1233   MOZ_ASSERT(initialLineContainer == aLineIter->GetContainer());
1234 }
1235 
ChildListMayRequireBidi(nsIFrame * aFirstChild,nsIContent ** aCurrContent)1236 bool nsBidiPresUtils::ChildListMayRequireBidi(nsIFrame* aFirstChild,
1237                                               nsIContent** aCurrContent) {
1238   MOZ_ASSERT(!aFirstChild || !aFirstChild->GetPrevSibling(),
1239              "Expecting to traverse from the start of a child list");
1240 
1241   for (nsIFrame* childFrame = aFirstChild; childFrame;
1242        childFrame = childFrame->GetNextSibling()) {
1243     nsIFrame* frame = childFrame;
1244 
1245     // If the real frame for a placeholder is a first-letter frame, we need to
1246     // consider its contents for potential Bidi resolution.
1247     if (childFrame->IsPlaceholderFrame()) {
1248       nsIFrame* realFrame =
1249           nsPlaceholderFrame::GetRealFrameForPlaceholder(childFrame);
1250       if (realFrame->IsLetterFrame()) {
1251         frame = realFrame;
1252       }
1253     }
1254 
1255     // If unicode-bidi properties are present, we should do bidi resolution.
1256     nsStyleContext* sc = frame->StyleContext();
1257     if (GetBidiControl(sc) || GetBidiOverride(sc)) {
1258       return true;
1259     }
1260 
1261     if (IsBidiLeaf(frame)) {
1262       if (frame->IsTextFrame()) {
1263         // If the frame already has a BidiDataProperty, we know we need to
1264         // perform bidi resolution (even if no bidi content is NOW present --
1265         // we might need to remove the property set by a previous reflow, if
1266         // content has changed; see bug 1366623).
1267         if (frame->HasProperty(nsIFrame::BidiDataProperty())) {
1268           return true;
1269         }
1270 
1271         // Check whether the text frame has any RTL characters; if so, bidi
1272         // resolution will be needed.
1273         nsIContent* content = frame->GetContent();
1274         if (content != *aCurrContent) {
1275           *aCurrContent = content;
1276           const nsTextFragment* txt = content->GetText();
1277           if (txt->Is2b() &&
1278               HasRTLChars(MakeSpan(txt->Get2b(), txt->GetLength()))) {
1279             return true;
1280           }
1281         }
1282       }
1283     } else if (ChildListMayRequireBidi(frame->PrincipalChildList().FirstChild(),
1284                                        aCurrContent)) {
1285       return true;
1286     }
1287   }
1288 
1289   return false;
1290 }
1291 
ResolveParagraphWithinBlock(BidiParagraphData * aBpd)1292 void nsBidiPresUtils::ResolveParagraphWithinBlock(BidiParagraphData* aBpd) {
1293   aBpd->ClearBidiControls();
1294   ResolveParagraph(aBpd);
1295   aBpd->ResetData();
1296 }
1297 
ReorderFrames(nsIFrame * aFirstFrameOnLine,int32_t aNumFramesOnLine,WritingMode aLineWM,const nsSize & aContainerSize,nscoord aStart)1298 /* static */ nscoord nsBidiPresUtils::ReorderFrames(
1299     nsIFrame* aFirstFrameOnLine, int32_t aNumFramesOnLine, WritingMode aLineWM,
1300     const nsSize& aContainerSize, nscoord aStart) {
1301   nsSize containerSize(aContainerSize);
1302 
1303   // If this line consists of a line frame, reorder the line frame's children.
1304   if (aFirstFrameOnLine->IsLineFrame()) {
1305     // The line frame is positioned at the start-edge, so use its size
1306     // as the container size.
1307     containerSize = aFirstFrameOnLine->GetSize();
1308 
1309     aFirstFrameOnLine = aFirstFrameOnLine->PrincipalChildList().FirstChild();
1310     if (!aFirstFrameOnLine) {
1311       return 0;
1312     }
1313     // All children of the line frame are on the first line. Setting
1314     // aNumFramesOnLine to -1 makes InitLogicalArrayFromLine look at all of
1315     // them.
1316     aNumFramesOnLine = -1;
1317   }
1318 
1319   BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1320   return RepositionInlineFrames(&bld, aLineWM, containerSize, aStart);
1321 }
1322 
GetFirstLeaf(nsIFrame * aFrame)1323 nsIFrame* nsBidiPresUtils::GetFirstLeaf(nsIFrame* aFrame) {
1324   nsIFrame* firstLeaf = aFrame;
1325   while (!IsBidiLeaf(firstLeaf)) {
1326     nsIFrame* firstChild = firstLeaf->PrincipalChildList().FirstChild();
1327     nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(firstChild);
1328     firstLeaf = (realFrame->IsLetterFrame()) ? realFrame : firstChild;
1329   }
1330   return firstLeaf;
1331 }
1332 
GetFrameBidiData(nsIFrame * aFrame)1333 FrameBidiData nsBidiPresUtils::GetFrameBidiData(nsIFrame* aFrame) {
1334   return GetFirstLeaf(aFrame)->GetBidiData();
1335 }
1336 
GetFrameEmbeddingLevel(nsIFrame * aFrame)1337 nsBidiLevel nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame* aFrame) {
1338   return GetFirstLeaf(aFrame)->GetEmbeddingLevel();
1339 }
1340 
GetFrameBaseLevel(nsIFrame * aFrame)1341 nsBidiLevel nsBidiPresUtils::GetFrameBaseLevel(nsIFrame* aFrame) {
1342   nsIFrame* firstLeaf = aFrame;
1343   while (!IsBidiLeaf(firstLeaf)) {
1344     firstLeaf = firstLeaf->PrincipalChildList().FirstChild();
1345   }
1346   return firstLeaf->GetBaseLevel();
1347 }
1348 
IsFirstOrLast(nsIFrame * aFrame,const nsContinuationStates * aContinuationStates,bool aSpanDirMatchesLineDir,bool & aIsFirst,bool & aIsLast)1349 void nsBidiPresUtils::IsFirstOrLast(
1350     nsIFrame* aFrame, const nsContinuationStates* aContinuationStates,
1351     bool aSpanDirMatchesLineDir, bool& aIsFirst /* out */,
1352     bool& aIsLast /* out */) {
1353   /*
1354    * Since we lay out frames in the line's direction, visiting a frame with
1355    * 'mFirstVisualFrame == nullptr', means it's the first appearance of one
1356    * of its continuation chain frames on the line.
1357    * To determine if it's the last visual frame of its continuation chain on
1358    * the line or not, we count the number of frames of the chain on the line,
1359    * and then reduce it when we lay out a frame of the chain. If this value
1360    * becomes 1 it means that it's the last visual frame of its continuation
1361    * chain on this line.
1362    */
1363 
1364   bool firstInLineOrder, lastInLineOrder;
1365   nsFrameContinuationState* frameState = aContinuationStates->GetEntry(aFrame);
1366   nsFrameContinuationState* firstFrameState;
1367 
1368   if (!frameState->mFirstVisualFrame) {
1369     // aFrame is the first visual frame of its continuation chain
1370     nsFrameContinuationState* contState;
1371     nsIFrame* frame;
1372 
1373     frameState->mFrameCount = 1;
1374     frameState->mFirstVisualFrame = aFrame;
1375 
1376     /**
1377      * Traverse continuation chain of aFrame in both backward and forward
1378      * directions while the frames are on this line. Count the frames and
1379      * set their mFirstVisualFrame to aFrame.
1380      */
1381     // Traverse continuation chain backward
1382     for (frame = aFrame->GetPrevContinuation();
1383          frame && (contState = aContinuationStates->GetEntry(frame));
1384          frame = frame->GetPrevContinuation()) {
1385       frameState->mFrameCount++;
1386       contState->mFirstVisualFrame = aFrame;
1387     }
1388     frameState->mHasContOnPrevLines = (frame != nullptr);
1389 
1390     // Traverse continuation chain forward
1391     for (frame = aFrame->GetNextContinuation();
1392          frame && (contState = aContinuationStates->GetEntry(frame));
1393          frame = frame->GetNextContinuation()) {
1394       frameState->mFrameCount++;
1395       contState->mFirstVisualFrame = aFrame;
1396     }
1397     frameState->mHasContOnNextLines = (frame != nullptr);
1398 
1399     firstInLineOrder = true;
1400     firstFrameState = frameState;
1401   } else {
1402     // aFrame is not the first visual frame of its continuation chain
1403     firstInLineOrder = false;
1404     firstFrameState =
1405         aContinuationStates->GetEntry(frameState->mFirstVisualFrame);
1406   }
1407 
1408   lastInLineOrder = (firstFrameState->mFrameCount == 1);
1409 
1410   if (aSpanDirMatchesLineDir) {
1411     aIsFirst = firstInLineOrder;
1412     aIsLast = lastInLineOrder;
1413   } else {
1414     aIsFirst = lastInLineOrder;
1415     aIsLast = firstInLineOrder;
1416   }
1417 
1418   if (frameState->mHasContOnPrevLines) {
1419     aIsFirst = false;
1420   }
1421   if (firstFrameState->mHasContOnNextLines) {
1422     aIsLast = false;
1423   }
1424 
1425   if ((aIsFirst || aIsLast) &&
1426       (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
1427     // For ib splits, don't treat anything except the last part as
1428     // endmost or anything except the first part as startmost.
1429     // As an optimization, only get the first continuation once.
1430     nsIFrame* firstContinuation = aFrame->FirstContinuation();
1431     if (firstContinuation->FrameIsNonLastInIBSplit()) {
1432       // We are not endmost
1433       aIsLast = false;
1434     }
1435     if (firstContinuation->FrameIsNonFirstInIBSplit()) {
1436       // We are not startmost
1437       aIsFirst = false;
1438     }
1439   }
1440 
1441   // Reduce number of remaining frames of the continuation chain on the line.
1442   firstFrameState->mFrameCount--;
1443 
1444   nsInlineFrame* testFrame = do_QueryFrame(aFrame);
1445 
1446   if (testFrame) {
1447     aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET);
1448 
1449     if (aIsFirst) {
1450       aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_FIRST);
1451     } else {
1452       aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_FIRST);
1453     }
1454 
1455     if (aIsLast) {
1456       aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST);
1457     } else {
1458       aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST);
1459     }
1460   }
1461 }
1462 
RepositionRubyContentFrame(nsIFrame * aFrame,WritingMode aFrameWM,const LogicalMargin & aBorderPadding)1463 /* static */ void nsBidiPresUtils::RepositionRubyContentFrame(
1464     nsIFrame* aFrame, WritingMode aFrameWM,
1465     const LogicalMargin& aBorderPadding) {
1466   const nsFrameList& childList = aFrame->PrincipalChildList();
1467   if (childList.IsEmpty()) {
1468     return;
1469   }
1470 
1471   // Reorder the children.
1472   // XXX It currently doesn't work properly because we do not
1473   // resolve frames inside ruby content frames.
1474   nscoord isize =
1475       ReorderFrames(childList.FirstChild(), childList.GetLength(), aFrameWM,
1476                     aFrame->GetSize(), aBorderPadding.IStart(aFrameWM));
1477   isize += aBorderPadding.IEnd(aFrameWM);
1478 
1479   if (aFrame->StyleText()->mRubyAlign == NS_STYLE_RUBY_ALIGN_START) {
1480     return;
1481   }
1482   nscoord residualISize = aFrame->ISize(aFrameWM) - isize;
1483   if (residualISize <= 0) {
1484     return;
1485   }
1486 
1487   // When ruby-align is not "start", if the content does not fill this
1488   // frame, we need to center the children.
1489   const nsSize dummyContainerSize;
1490   for (nsIFrame* child : childList) {
1491     LogicalRect rect = child->GetLogicalRect(aFrameWM, dummyContainerSize);
1492     rect.IStart(aFrameWM) += residualISize / 2;
1493     child->SetRect(aFrameWM, rect, dummyContainerSize);
1494   }
1495 }
1496 
RepositionRubyFrame(nsIFrame * aFrame,const nsContinuationStates * aContinuationStates,const WritingMode aContainerWM,const LogicalMargin & aBorderPadding)1497 /* static */ nscoord nsBidiPresUtils::RepositionRubyFrame(
1498     nsIFrame* aFrame, const nsContinuationStates* aContinuationStates,
1499     const WritingMode aContainerWM, const LogicalMargin& aBorderPadding) {
1500   LayoutFrameType frameType = aFrame->Type();
1501   MOZ_ASSERT(RubyUtils::IsRubyBox(frameType));
1502 
1503   nscoord icoord = 0;
1504   WritingMode frameWM = aFrame->GetWritingMode();
1505   bool isLTR = frameWM.IsBidiLTR();
1506   nsSize frameSize = aFrame->GetSize();
1507   if (frameType == LayoutFrameType::Ruby) {
1508     icoord += aBorderPadding.IStart(frameWM);
1509     // Reposition ruby segments in a ruby container
1510     for (RubySegmentEnumerator e(static_cast<nsRubyFrame*>(aFrame)); !e.AtEnd();
1511          e.Next()) {
1512       nsRubyBaseContainerFrame* rbc = e.GetBaseContainer();
1513       AutoRubyTextContainerArray textContainers(rbc);
1514 
1515       nscoord segmentISize = RepositionFrame(
1516           rbc, isLTR, icoord, aContinuationStates, frameWM, false, frameSize);
1517       for (nsRubyTextContainerFrame* rtc : textContainers) {
1518         nscoord isize = RepositionFrame(rtc, isLTR, icoord, aContinuationStates,
1519                                         frameWM, false, frameSize);
1520         segmentISize = std::max(segmentISize, isize);
1521       }
1522       icoord += segmentISize;
1523     }
1524     icoord += aBorderPadding.IEnd(frameWM);
1525   } else if (frameType == LayoutFrameType::RubyBaseContainer) {
1526     // Reposition ruby columns in a ruby segment
1527     auto rbc = static_cast<nsRubyBaseContainerFrame*>(aFrame);
1528     AutoRubyTextContainerArray textContainers(rbc);
1529 
1530     for (RubyColumnEnumerator e(rbc, textContainers); !e.AtEnd(); e.Next()) {
1531       RubyColumn column;
1532       e.GetColumn(column);
1533 
1534       nscoord columnISize =
1535           RepositionFrame(column.mBaseFrame, isLTR, icoord, aContinuationStates,
1536                           frameWM, false, frameSize);
1537       for (nsRubyTextFrame* rt : column.mTextFrames) {
1538         nscoord isize = RepositionFrame(rt, isLTR, icoord, aContinuationStates,
1539                                         frameWM, false, frameSize);
1540         columnISize = std::max(columnISize, isize);
1541       }
1542       icoord += columnISize;
1543     }
1544   } else {
1545     if (frameType == LayoutFrameType::RubyBase ||
1546         frameType == LayoutFrameType::RubyText) {
1547       RepositionRubyContentFrame(aFrame, frameWM, aBorderPadding);
1548     }
1549     // Note that, ruby text container is not present in all conditions
1550     // above. It is intended, because the children of rtc are reordered
1551     // with the children of ruby base container simultaneously. We only
1552     // need to return its isize here, as it should not be changed.
1553     icoord += aFrame->ISize(aContainerWM);
1554   }
1555   return icoord;
1556 }
1557 
RepositionFrame(nsIFrame * aFrame,bool aIsEvenLevel,nscoord aStartOrEnd,const nsContinuationStates * aContinuationStates,WritingMode aContainerWM,bool aContainerReverseDir,const nsSize & aContainerSize)1558 /* static */ nscoord nsBidiPresUtils::RepositionFrame(
1559     nsIFrame* aFrame, bool aIsEvenLevel, nscoord aStartOrEnd,
1560     const nsContinuationStates* aContinuationStates, WritingMode aContainerWM,
1561     bool aContainerReverseDir, const nsSize& aContainerSize) {
1562   nscoord lineSize =
1563       aContainerWM.IsVertical() ? aContainerSize.height : aContainerSize.width;
1564   NS_ASSERTION(lineSize != NS_UNCONSTRAINEDSIZE,
1565                "Unconstrained inline line size in bidi frame reordering");
1566   if (!aFrame) return 0;
1567 
1568   bool isFirst, isLast;
1569   WritingMode frameWM = aFrame->GetWritingMode();
1570   IsFirstOrLast(aFrame, aContinuationStates,
1571                 aContainerWM.IsBidiLTR() == frameWM.IsBidiLTR(),
1572                 isFirst /* out */, isLast /* out */);
1573 
1574   // We only need the margin if the frame is first or last in its own
1575   // writing mode, but we're traversing the frames in the order of the
1576   // container's writing mode. To get the right values, we set start and
1577   // end margins on a logical margin in the frame's writing mode, and
1578   // then convert the margin to the container's writing mode to set the
1579   // coordinates.
1580 
1581   // This method is called from nsBlockFrame::PlaceLine via the call to
1582   // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
1583   // have been reflowed, which is required for GetUsedMargin/Border/Padding
1584   nscoord frameISize = aFrame->ISize();
1585   LogicalMargin frameMargin = aFrame->GetLogicalUsedMargin(frameWM);
1586   LogicalMargin borderPadding = aFrame->GetLogicalUsedBorderAndPadding(frameWM);
1587   // Since the visual order of frame could be different from the continuation
1588   // order, we need to remove any inline border/padding [that is already applied
1589   // based on continuation order] and then add it back based on the visual order
1590   // (i.e. isFirst/isLast) to get the correct isize for the current frame.
1591   // We don't need to do that for 'box-decoration-break:clone' because then all
1592   // continuations have border/padding/margin applied.
1593   if (aFrame->StyleBorder()->mBoxDecorationBreak ==
1594       StyleBoxDecorationBreak::Slice) {
1595     // First remove the border/padding that was applied based on logical order.
1596     if (!aFrame->GetPrevContinuation()) {
1597       frameISize -= borderPadding.IStart(frameWM);
1598     }
1599     if (!aFrame->GetNextContinuation()) {
1600       frameISize -= borderPadding.IEnd(frameWM);
1601     }
1602     // Set margin/border/padding based on visual order.
1603     if (!isFirst) {
1604       frameMargin.IStart(frameWM) = 0;
1605       borderPadding.IStart(frameWM) = 0;
1606     }
1607     if (!isLast) {
1608       frameMargin.IEnd(frameWM) = 0;
1609       borderPadding.IEnd(frameWM) = 0;
1610     }
1611     // Add the border/padding which is now based on visual order.
1612     frameISize += borderPadding.IStartEnd(frameWM);
1613   }
1614 
1615   nscoord icoord = 0;
1616   if (!IsBidiLeaf(aFrame)) {
1617     bool reverseDir = aIsEvenLevel != frameWM.IsBidiLTR();
1618     icoord += reverseDir ? borderPadding.IEnd(frameWM)
1619                          : borderPadding.IStart(frameWM);
1620     LogicalSize logicalSize(frameWM, frameISize, aFrame->BSize());
1621     nsSize frameSize = logicalSize.GetPhysicalSize(frameWM);
1622     // Reposition the child frames
1623     for (nsFrameList::Enumerator e(aFrame->PrincipalChildList()); !e.AtEnd();
1624          e.Next()) {
1625       icoord +=
1626           RepositionFrame(e.get(), aIsEvenLevel, icoord, aContinuationStates,
1627                           frameWM, reverseDir, frameSize);
1628     }
1629     icoord += reverseDir ? borderPadding.IStart(frameWM)
1630                          : borderPadding.IEnd(frameWM);
1631   } else if (RubyUtils::IsRubyBox(aFrame->Type())) {
1632     icoord += RepositionRubyFrame(aFrame, aContinuationStates, aContainerWM,
1633                                   borderPadding);
1634   } else {
1635     icoord +=
1636         frameWM.IsOrthogonalTo(aContainerWM) ? aFrame->BSize() : frameISize;
1637   }
1638 
1639   // In the following variables, if aContainerReverseDir is true, i.e.
1640   // the container is positioning its children in reverse of its logical
1641   // direction, the "StartOrEnd" refers to the distance from the frame
1642   // to the inline end edge of the container, elsewise, it refers to the
1643   // distance to the inline start edge.
1644   const LogicalMargin margin = frameMargin.ConvertTo(aContainerWM, frameWM);
1645   nscoord marginStartOrEnd = aContainerReverseDir ? margin.IEnd(aContainerWM)
1646                                                   : margin.IStart(aContainerWM);
1647   nscoord frameStartOrEnd = aStartOrEnd + marginStartOrEnd;
1648 
1649   LogicalRect rect = aFrame->GetLogicalRect(aContainerWM, aContainerSize);
1650   rect.ISize(aContainerWM) = icoord;
1651   rect.IStart(aContainerWM) = aContainerReverseDir
1652                                   ? lineSize - frameStartOrEnd - icoord
1653                                   : frameStartOrEnd;
1654   aFrame->SetRect(aContainerWM, rect, aContainerSize);
1655 
1656   return icoord + margin.IStartEnd(aContainerWM);
1657 }
1658 
InitContinuationStates(nsIFrame * aFrame,nsContinuationStates * aContinuationStates)1659 void nsBidiPresUtils::InitContinuationStates(
1660     nsIFrame* aFrame, nsContinuationStates* aContinuationStates) {
1661   aContinuationStates->PutEntry(aFrame);
1662   if (!IsBidiLeaf(aFrame) || RubyUtils::IsRubyBox(aFrame->Type())) {
1663     // Continue for child frames
1664     for (nsIFrame* frame : aFrame->PrincipalChildList()) {
1665       InitContinuationStates(frame, aContinuationStates);
1666     }
1667   }
1668 }
1669 
RepositionInlineFrames(BidiLineData * aBld,WritingMode aLineWM,const nsSize & aContainerSize,nscoord aStart)1670 /* static */ nscoord nsBidiPresUtils::RepositionInlineFrames(
1671     BidiLineData* aBld, WritingMode aLineWM, const nsSize& aContainerSize,
1672     nscoord aStart) {
1673   nscoord start = aStart;
1674   nsIFrame* frame;
1675   int32_t count = aBld->mVisualFrames.Length();
1676   int32_t index;
1677   nsContinuationStates continuationStates;
1678 
1679   // Initialize continuation states to (nullptr, 0) for
1680   // each frame on the line.
1681   for (index = 0; index < count; index++) {
1682     InitContinuationStates(aBld->VisualFrameAt(index), &continuationStates);
1683   }
1684 
1685   // Reposition frames in visual order
1686   int32_t step, limit;
1687   if (aLineWM.IsBidiLTR()) {
1688     index = 0;
1689     step = 1;
1690     limit = count;
1691   } else {
1692     index = count - 1;
1693     step = -1;
1694     limit = -1;
1695   }
1696   for (; index != limit; index += step) {
1697     frame = aBld->VisualFrameAt(index);
1698     start += RepositionFrame(
1699         frame, !(IS_LEVEL_RTL(aBld->mLevels[aBld->mIndexMap[index]])), start,
1700         &continuationStates, aLineWM, false, aContainerSize);
1701   }
1702   return start;
1703 }
1704 
CheckLineOrder(nsIFrame * aFirstFrameOnLine,int32_t aNumFramesOnLine,nsIFrame ** aFirstVisual,nsIFrame ** aLastVisual)1705 bool nsBidiPresUtils::CheckLineOrder(nsIFrame* aFirstFrameOnLine,
1706                                      int32_t aNumFramesOnLine,
1707                                      nsIFrame** aFirstVisual,
1708                                      nsIFrame** aLastVisual) {
1709   BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1710   int32_t count = bld.FrameCount();
1711 
1712   if (aFirstVisual) {
1713     *aFirstVisual = bld.VisualFrameAt(0);
1714   }
1715   if (aLastVisual) {
1716     *aLastVisual = bld.VisualFrameAt(count - 1);
1717   }
1718 
1719   return bld.mIsReordered;
1720 }
1721 
GetFrameToRightOf(const nsIFrame * aFrame,nsIFrame * aFirstFrameOnLine,int32_t aNumFramesOnLine)1722 nsIFrame* nsBidiPresUtils::GetFrameToRightOf(const nsIFrame* aFrame,
1723                                              nsIFrame* aFirstFrameOnLine,
1724                                              int32_t aNumFramesOnLine) {
1725   BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1726 
1727   int32_t count = bld.mVisualFrames.Length();
1728 
1729   if (aFrame == nullptr && count) return bld.VisualFrameAt(0);
1730 
1731   for (int32_t i = 0; i < count - 1; i++) {
1732     if (bld.VisualFrameAt(i) == aFrame) {
1733       return bld.VisualFrameAt(i + 1);
1734     }
1735   }
1736 
1737   return nullptr;
1738 }
1739 
GetFrameToLeftOf(const nsIFrame * aFrame,nsIFrame * aFirstFrameOnLine,int32_t aNumFramesOnLine)1740 nsIFrame* nsBidiPresUtils::GetFrameToLeftOf(const nsIFrame* aFrame,
1741                                             nsIFrame* aFirstFrameOnLine,
1742                                             int32_t aNumFramesOnLine) {
1743   BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
1744 
1745   int32_t count = bld.mVisualFrames.Length();
1746 
1747   if (aFrame == nullptr && count) return bld.VisualFrameAt(count - 1);
1748 
1749   for (int32_t i = 1; i < count; i++) {
1750     if (bld.VisualFrameAt(i) == aFrame) {
1751       return bld.VisualFrameAt(i - 1);
1752     }
1753   }
1754 
1755   return nullptr;
1756 }
1757 
EnsureBidiContinuation(nsIFrame * aFrame,nsIFrame ** aNewFrame,int32_t aStart,int32_t aEnd)1758 inline nsresult nsBidiPresUtils::EnsureBidiContinuation(nsIFrame* aFrame,
1759                                                         nsIFrame** aNewFrame,
1760                                                         int32_t aStart,
1761                                                         int32_t aEnd) {
1762   NS_PRECONDITION(aNewFrame, "null OUT ptr");
1763   NS_PRECONDITION(aFrame, "aFrame is null");
1764 
1765   aFrame->AdjustOffsetsForBidi(aStart, aEnd);
1766   return CreateContinuation(aFrame, aNewFrame, false);
1767 }
1768 
RemoveBidiContinuation(BidiParagraphData * aBpd,nsIFrame * aFrame,int32_t aFirstIndex,int32_t aLastIndex)1769 void nsBidiPresUtils::RemoveBidiContinuation(BidiParagraphData* aBpd,
1770                                              nsIFrame* aFrame,
1771                                              int32_t aFirstIndex,
1772                                              int32_t aLastIndex) {
1773   FrameBidiData bidiData = aFrame->GetBidiData();
1774   bidiData.precedingControl = kBidiLevelNone;
1775   for (int32_t index = aFirstIndex + 1; index <= aLastIndex; index++) {
1776     nsIFrame* frame = aBpd->FrameAt(index);
1777     if (frame != NS_BIDI_CONTROL_FRAME) {
1778       // Make the frame and its continuation ancestors fluid,
1779       // so they can be reused or deleted by normal reflow code
1780       frame->SetProperty(nsIFrame::BidiDataProperty(), bidiData);
1781       frame->AddStateBits(NS_FRAME_IS_BIDI);
1782       while (frame) {
1783         nsIFrame* prev = frame->GetPrevContinuation();
1784         if (prev) {
1785           MakeContinuationFluid(prev, frame);
1786           frame = frame->GetParent();
1787         } else {
1788           break;
1789         }
1790       }
1791     }
1792   }
1793 
1794   // Make sure that the last continuation we made fluid does not itself have a
1795   // fluid continuation (this can happen when re-resolving after dynamic changes
1796   // to content)
1797   nsIFrame* lastFrame = aBpd->FrameAt(aLastIndex);
1798   MakeContinuationsNonFluidUpParentChain(lastFrame, lastFrame->GetNextInFlow());
1799 }
1800 
FormatUnicodeText(nsPresContext * aPresContext,char16_t * aText,int32_t & aTextLength,nsCharType aCharType)1801 nsresult nsBidiPresUtils::FormatUnicodeText(nsPresContext* aPresContext,
1802                                             char16_t* aText,
1803                                             int32_t& aTextLength,
1804                                             nsCharType aCharType) {
1805   nsresult rv = NS_OK;
1806   // ahmed
1807   // adjusted for correct numeral shaping
1808   uint32_t bidiOptions = aPresContext->GetBidi();
1809   switch (GET_BIDI_OPTION_NUMERAL(bidiOptions)) {
1810     case IBMBIDI_NUMERAL_HINDI:
1811       HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_HINDI);
1812       break;
1813 
1814     case IBMBIDI_NUMERAL_ARABIC:
1815       HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_ARABIC);
1816       break;
1817 
1818     case IBMBIDI_NUMERAL_PERSIAN:
1819       HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_PERSIAN);
1820       break;
1821 
1822     case IBMBIDI_NUMERAL_REGULAR:
1823 
1824       switch (aCharType) {
1825         case eCharType_EuropeanNumber:
1826           HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_ARABIC);
1827           break;
1828 
1829         case eCharType_ArabicNumber:
1830           HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_HINDI);
1831           break;
1832 
1833         default:
1834           break;
1835       }
1836       break;
1837 
1838     case IBMBIDI_NUMERAL_HINDICONTEXT:
1839       if (((GET_BIDI_OPTION_DIRECTION(bidiOptions) ==
1840             IBMBIDI_TEXTDIRECTION_RTL) &&
1841            (IS_ARABIC_DIGIT(aText[0]))) ||
1842           (eCharType_ArabicNumber == aCharType))
1843         HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_HINDI);
1844       else if (eCharType_EuropeanNumber == aCharType)
1845         HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_ARABIC);
1846       break;
1847 
1848     case IBMBIDI_NUMERAL_PERSIANCONTEXT:
1849       if (((GET_BIDI_OPTION_DIRECTION(bidiOptions) ==
1850             IBMBIDI_TEXTDIRECTION_RTL) &&
1851            (IS_ARABIC_DIGIT(aText[0]))) ||
1852           (eCharType_ArabicNumber == aCharType))
1853         HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_PERSIAN);
1854       else if (eCharType_EuropeanNumber == aCharType)
1855         HandleNumbers(aText, aTextLength, IBMBIDI_NUMERAL_ARABIC);
1856       break;
1857 
1858     case IBMBIDI_NUMERAL_NOMINAL:
1859     default:
1860       break;
1861   }
1862 
1863   StripBidiControlCharacters(aText, aTextLength);
1864   return rv;
1865 }
1866 
StripBidiControlCharacters(char16_t * aText,int32_t & aTextLength)1867 void nsBidiPresUtils::StripBidiControlCharacters(char16_t* aText,
1868                                                  int32_t& aTextLength) {
1869   if ((nullptr == aText) || (aTextLength < 1)) {
1870     return;
1871   }
1872 
1873   int32_t stripLen = 0;
1874 
1875   for (int32_t i = 0; i < aTextLength; i++) {
1876     // XXX: This silently ignores surrogate characters.
1877     //      As of Unicode 4.0, all Bidi control characters are within the BMP.
1878     if (IsBidiControl((uint32_t)aText[i])) {
1879       ++stripLen;
1880     } else {
1881       aText[i - stripLen] = aText[i];
1882     }
1883   }
1884   aTextLength -= stripLen;
1885 }
1886 
1887 #if 0  // XXX: for the future use ???
1888 void
1889 RemoveDiacritics(char16_t* aText,
1890                  int32_t&   aTextLength)
1891 {
1892   if (aText && (aTextLength > 0) ) {
1893     int32_t offset = 0;
1894 
1895     for (int32_t i = 0; i < aTextLength && aText[i]; i++) {
1896       if (IS_BIDI_DIACRITIC(aText[i]) ) {
1897         ++offset;
1898         continue;
1899       }
1900       aText[i - offset] = aText[i];
1901     }
1902     aTextLength = i - offset;
1903     aText[aTextLength] = 0;
1904   }
1905 }
1906 #endif
1907 
CalculateCharType(nsBidi * aBidiEngine,const char16_t * aText,int32_t & aOffset,int32_t aCharTypeLimit,int32_t & aRunLimit,int32_t & aRunLength,int32_t & aRunCount,uint8_t & aCharType,uint8_t & aPrevCharType)1908 void nsBidiPresUtils::CalculateCharType(nsBidi* aBidiEngine,
1909                                         const char16_t* aText, int32_t& aOffset,
1910                                         int32_t aCharTypeLimit,
1911                                         int32_t& aRunLimit, int32_t& aRunLength,
1912                                         int32_t& aRunCount, uint8_t& aCharType,
1913                                         uint8_t& aPrevCharType)
1914 
1915 {
1916   bool strongTypeFound = false;
1917   int32_t offset;
1918   nsCharType charType;
1919 
1920   aCharType = eCharType_OtherNeutral;
1921 
1922   int32_t charLen;
1923   for (offset = aOffset; offset < aCharTypeLimit; offset += charLen) {
1924     // Make sure we give RTL chartype to all characters that would be classified
1925     // as Right-To-Left by a bidi platform.
1926     // (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.)
1927     charLen = 1;
1928     uint32_t ch = aText[offset];
1929     if (IS_HEBREW_CHAR(ch)) {
1930       charType = eCharType_RightToLeft;
1931     } else if (IS_ARABIC_ALPHABETIC(ch)) {
1932       charType = eCharType_RightToLeftArabic;
1933     } else {
1934       if (NS_IS_HIGH_SURROGATE(ch) && offset + 1 < aCharTypeLimit &&
1935           NS_IS_LOW_SURROGATE(aText[offset + 1])) {
1936         ch = SURROGATE_TO_UCS4(ch, aText[offset + 1]);
1937         charLen = 2;
1938       }
1939       charType = unicode::GetBidiCat(ch);
1940     }
1941 
1942     if (!CHARTYPE_IS_WEAK(charType)) {
1943       if (strongTypeFound && (charType != aPrevCharType) &&
1944           (CHARTYPE_IS_RTL(charType) || CHARTYPE_IS_RTL(aPrevCharType))) {
1945         // Stop at this point to ensure uni-directionality of the text
1946         // (from platform's point of view).
1947         // Also, don't mix Arabic and Hebrew content (since platform may
1948         // provide BIDI support to one of them only).
1949         aRunLength = offset - aOffset;
1950         aRunLimit = offset;
1951         ++aRunCount;
1952         break;
1953       }
1954 
1955       if ((eCharType_RightToLeftArabic == aPrevCharType ||
1956            eCharType_ArabicNumber == aPrevCharType) &&
1957           eCharType_EuropeanNumber == charType) {
1958         charType = eCharType_ArabicNumber;
1959       }
1960 
1961       // Set PrevCharType to the last strong type in this frame
1962       // (for correct numeric shaping)
1963       aPrevCharType = charType;
1964 
1965       strongTypeFound = true;
1966       aCharType = charType;
1967     }
1968   }
1969   aOffset = offset;
1970 }
1971 
ProcessText(const char16_t * aText,int32_t aLength,nsBidiLevel aBaseLevel,nsPresContext * aPresContext,BidiProcessor & aprocessor,Mode aMode,nsBidiPositionResolve * aPosResolve,int32_t aPosResolveCount,nscoord * aWidth,nsBidi * aBidiEngine)1972 nsresult nsBidiPresUtils::ProcessText(const char16_t* aText, int32_t aLength,
1973                                       nsBidiLevel aBaseLevel,
1974                                       nsPresContext* aPresContext,
1975                                       BidiProcessor& aprocessor, Mode aMode,
1976                                       nsBidiPositionResolve* aPosResolve,
1977                                       int32_t aPosResolveCount, nscoord* aWidth,
1978                                       nsBidi* aBidiEngine) {
1979   NS_ASSERTION((aPosResolve == nullptr) != (aPosResolveCount > 0),
1980                "Incorrect aPosResolve / aPosResolveCount arguments");
1981 
1982   int32_t runCount;
1983 
1984   nsAutoString textBuffer(aText, aLength);
1985   textBuffer.ReplaceChar(kSeparators, kSpace);
1986   const char16_t* text = textBuffer.get();
1987 
1988   nsresult rv = aBidiEngine->SetPara(text, aLength, aBaseLevel);
1989   if (NS_FAILED(rv)) return rv;
1990 
1991   rv = aBidiEngine->CountRuns(&runCount);
1992   if (NS_FAILED(rv)) return rv;
1993 
1994   nscoord xOffset = 0;
1995   nscoord width, xEndRun = 0;
1996   nscoord totalWidth = 0;
1997   int32_t i, start, limit, length;
1998   uint32_t visualStart = 0;
1999   uint8_t charType;
2000   uint8_t prevType = eCharType_LeftToRight;
2001 
2002   for (int nPosResolve = 0; nPosResolve < aPosResolveCount; ++nPosResolve) {
2003     aPosResolve[nPosResolve].visualIndex = kNotFound;
2004     aPosResolve[nPosResolve].visualLeftTwips = kNotFound;
2005     aPosResolve[nPosResolve].visualWidth = kNotFound;
2006   }
2007 
2008   for (i = 0; i < runCount; i++) {
2009     nsBidiDirection dir = aBidiEngine->GetVisualRun(i, &start, &length);
2010 
2011     nsBidiLevel level;
2012     aBidiEngine->GetLogicalRun(start, &limit, &level);
2013 
2014     dir = DIRECTION_FROM_LEVEL(level);
2015     int32_t subRunLength = limit - start;
2016     int32_t lineOffset = start;
2017     int32_t typeLimit = std::min(limit, aLength);
2018     int32_t subRunCount = 1;
2019     int32_t subRunLimit = typeLimit;
2020 
2021     /*
2022      * If |level| is even, i.e. the direction of the run is left-to-right, we
2023      * render the subruns from left to right and increment the x-coordinate
2024      * |xOffset| by the width of each subrun after rendering.
2025      *
2026      * If |level| is odd, i.e. the direction of the run is right-to-left, we
2027      * render the subruns from right to left. We begin by incrementing |xOffset|
2028      * by the width of the whole run, and then decrement it by the width of each
2029      * subrun before rendering. After rendering all the subruns, we restore the
2030      * x-coordinate of the end of the run for the start of the next run.
2031      */
2032 
2033     if (dir == NSBIDI_RTL) {
2034       aprocessor.SetText(text + start, subRunLength, dir);
2035       width = aprocessor.GetWidth();
2036       xOffset += width;
2037       xEndRun = xOffset;
2038     }
2039 
2040     while (subRunCount > 0) {
2041       // CalculateCharType can increment subRunCount if the run
2042       // contains mixed character types
2043       CalculateCharType(aBidiEngine, text, lineOffset, typeLimit, subRunLimit,
2044                         subRunLength, subRunCount, charType, prevType);
2045 
2046       nsAutoString runVisualText;
2047       runVisualText.Assign(text + start, subRunLength);
2048       if (int32_t(runVisualText.Length()) < subRunLength)
2049         return NS_ERROR_OUT_OF_MEMORY;
2050       FormatUnicodeText(aPresContext, runVisualText.BeginWriting(),
2051                         subRunLength, (nsCharType)charType);
2052 
2053       aprocessor.SetText(runVisualText.get(), subRunLength, dir);
2054       width = aprocessor.GetWidth();
2055       totalWidth += width;
2056       if (dir == NSBIDI_RTL) {
2057         xOffset -= width;
2058       }
2059       if (aMode == MODE_DRAW) {
2060         aprocessor.DrawText(xOffset, width);
2061       }
2062 
2063       /*
2064        * The caller may request to calculate the visual position of one
2065        * or more characters.
2066        */
2067       for (int nPosResolve = 0; nPosResolve < aPosResolveCount; ++nPosResolve) {
2068         nsBidiPositionResolve* posResolve = &aPosResolve[nPosResolve];
2069         /*
2070          * Did we already resolve this position's visual metric? If so, skip.
2071          */
2072         if (posResolve->visualLeftTwips != kNotFound) continue;
2073 
2074         /*
2075          * First find out if the logical position is within this run.
2076          */
2077         if (start <= posResolve->logicalIndex &&
2078             start + subRunLength > posResolve->logicalIndex) {
2079           /*
2080            * If this run is only one character long, we have an easy case:
2081            * the visual position is the x-coord of the start of the run
2082            * less the x-coord of the start of the whole text.
2083            */
2084           if (subRunLength == 1) {
2085             posResolve->visualIndex = visualStart;
2086             posResolve->visualLeftTwips = xOffset;
2087             posResolve->visualWidth = width;
2088           }
2089           /*
2090            * Otherwise, we need to measure the width of the run's part
2091            * which is to the visual left of the index.
2092            * In other words, the run is broken in two, around the logical index,
2093            * and we measure the part which is visually left.
2094            * If the run is right-to-left, this part will span from after the
2095            * index up to the end of the run; if it is left-to-right, this part
2096            * will span from the start of the run up to (and inclduing) the
2097            * character before the index.
2098            */
2099           else {
2100             /*
2101              * Here is a description of how the width of the current character
2102              * (posResolve->visualWidth) is calculated:
2103              *
2104              * LTR (current char: "P"):
2105              *    S A M P L E          (logical index: 3, visual index: 3)
2106              *    ^ (visualLeftPart)
2107              *    ^ (visualRightSide)
2108              *    visualLeftLength == 3
2109              *    ^^^^^^ (subWidth)
2110              *    ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
2111              *          ^^ (posResolve->visualWidth)
2112              *
2113              * RTL (current char: "M"):
2114              *    E L P M A S          (logical index: 2, visual index: 3)
2115              *        ^ (visualLeftPart)
2116              *          ^ (visualRightSide)
2117              *    visualLeftLength == 3
2118              *    ^^^^^^ (subWidth)
2119              *    ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
2120              *          ^^ (posResolve->visualWidth)
2121              */
2122             nscoord subWidth;
2123             // The position in the text where this run's "left part" begins.
2124             const char16_t* visualLeftPart;
2125             const char16_t* visualRightSide;
2126             if (dir == NSBIDI_RTL) {
2127               // One day, son, this could all be replaced with
2128               // mPresContext->GetBidiEngine().GetVisualIndex() ...
2129               posResolve->visualIndex =
2130                   visualStart +
2131                   (subRunLength - (posResolve->logicalIndex + 1 - start));
2132               // Skipping to the "left part".
2133               visualLeftPart = text + posResolve->logicalIndex + 1;
2134               // Skipping to the right side of the current character
2135               visualRightSide = visualLeftPart - 1;
2136             } else {
2137               posResolve->visualIndex =
2138                   visualStart + (posResolve->logicalIndex - start);
2139               // Skipping to the "left part".
2140               visualLeftPart = text + start;
2141               // In LTR mode this is the same as visualLeftPart
2142               visualRightSide = visualLeftPart;
2143             }
2144             // The delta between the start of the run and the left part's end.
2145             int32_t visualLeftLength = posResolve->visualIndex - visualStart;
2146             aprocessor.SetText(visualLeftPart, visualLeftLength, dir);
2147             subWidth = aprocessor.GetWidth();
2148             aprocessor.SetText(visualRightSide, visualLeftLength + 1, dir);
2149             posResolve->visualLeftTwips = xOffset + subWidth;
2150             posResolve->visualWidth = aprocessor.GetWidth() - subWidth;
2151           }
2152         }
2153       }
2154 
2155       if (dir == NSBIDI_LTR) {
2156         xOffset += width;
2157       }
2158 
2159       --subRunCount;
2160       start = lineOffset;
2161       subRunLimit = typeLimit;
2162       subRunLength = typeLimit - lineOffset;
2163     }  // while
2164     if (dir == NSBIDI_RTL) {
2165       xOffset = xEndRun;
2166     }
2167 
2168     visualStart += length;
2169   }  // for
2170 
2171   if (aWidth) {
2172     *aWidth = totalWidth;
2173   }
2174   return NS_OK;
2175 }
2176 
2177 class MOZ_STACK_CLASS nsIRenderingContextBidiProcessor final
2178     : public nsBidiPresUtils::BidiProcessor {
2179  public:
2180   typedef mozilla::gfx::DrawTarget DrawTarget;
2181 
nsIRenderingContextBidiProcessor(gfxContext * aCtx,DrawTarget * aTextRunConstructionDrawTarget,nsFontMetrics * aFontMetrics,const nsPoint & aPt)2182   nsIRenderingContextBidiProcessor(gfxContext* aCtx,
2183                                    DrawTarget* aTextRunConstructionDrawTarget,
2184                                    nsFontMetrics* aFontMetrics,
2185                                    const nsPoint& aPt)
2186       : mCtx(aCtx),
2187         mTextRunConstructionDrawTarget(aTextRunConstructionDrawTarget),
2188         mFontMetrics(aFontMetrics),
2189         mPt(aPt) {}
2190 
~nsIRenderingContextBidiProcessor()2191   ~nsIRenderingContextBidiProcessor() { mFontMetrics->SetTextRunRTL(false); }
2192 
SetText(const char16_t * aText,int32_t aLength,nsBidiDirection aDirection)2193   virtual void SetText(const char16_t* aText, int32_t aLength,
2194                        nsBidiDirection aDirection) override {
2195     mFontMetrics->SetTextRunRTL(aDirection == NSBIDI_RTL);
2196     mText = aText;
2197     mLength = aLength;
2198   }
2199 
GetWidth()2200   virtual nscoord GetWidth() override {
2201     return nsLayoutUtils::AppUnitWidthOfString(mText, mLength, *mFontMetrics,
2202                                                mTextRunConstructionDrawTarget);
2203   }
2204 
DrawText(nscoord aIOffset,nscoord)2205   virtual void DrawText(nscoord aIOffset, nscoord) override {
2206     nsPoint pt(mPt);
2207     if (mFontMetrics->GetVertical()) {
2208       pt.y += aIOffset;
2209     } else {
2210       pt.x += aIOffset;
2211     }
2212     mFontMetrics->DrawString(mText, mLength, pt.x, pt.y, mCtx,
2213                              mTextRunConstructionDrawTarget);
2214   }
2215 
2216  private:
2217   gfxContext* mCtx;
2218   DrawTarget* mTextRunConstructionDrawTarget;
2219   nsFontMetrics* mFontMetrics;
2220   nsPoint mPt;
2221   const char16_t* mText;
2222   int32_t mLength;
2223 };
2224 
ProcessTextForRenderingContext(const char16_t * aText,int32_t aLength,nsBidiLevel aBaseLevel,nsPresContext * aPresContext,gfxContext & aRenderingContext,DrawTarget * aTextRunConstructionDrawTarget,nsFontMetrics & aFontMetrics,Mode aMode,nscoord aX,nscoord aY,nsBidiPositionResolve * aPosResolve,int32_t aPosResolveCount,nscoord * aWidth)2225 nsresult nsBidiPresUtils::ProcessTextForRenderingContext(
2226     const char16_t* aText, int32_t aLength, nsBidiLevel aBaseLevel,
2227     nsPresContext* aPresContext, gfxContext& aRenderingContext,
2228     DrawTarget* aTextRunConstructionDrawTarget, nsFontMetrics& aFontMetrics,
2229     Mode aMode, nscoord aX, nscoord aY, nsBidiPositionResolve* aPosResolve,
2230     int32_t aPosResolveCount, nscoord* aWidth) {
2231   nsIRenderingContextBidiProcessor processor(&aRenderingContext,
2232                                              aTextRunConstructionDrawTarget,
2233                                              &aFontMetrics, nsPoint(aX, aY));
2234   return ProcessText(aText, aLength, aBaseLevel, aPresContext, processor, aMode,
2235                      aPosResolve, aPosResolveCount, aWidth,
2236                      &aPresContext->GetBidiEngine());
2237 }
2238 
2239 /* static */
BidiLevelFromStyle(nsStyleContext * aStyleContext)2240 nsBidiLevel nsBidiPresUtils::BidiLevelFromStyle(nsStyleContext* aStyleContext) {
2241   if (aStyleContext->StyleTextReset()->mUnicodeBidi &
2242       NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
2243     return NSBIDI_DEFAULT_LTR;
2244   }
2245 
2246   if (aStyleContext->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
2247     return NSBIDI_RTL;
2248   }
2249 
2250   return NSBIDI_LTR;
2251 }
2252