LCOV - code coverage report
Current view: top level - layout/base - nsBidiPresUtils.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1038 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 77 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13