layout/base/nsBidiPresUtils.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsBidiPresUtils.h"
     7 #include "nsGkAtoms.h"
     8 #include "nsPresContext.h"
     9 #include "nsRenderingContext.h"
    10 #include "nsBidiUtils.h"
    11 #include "nsCSSFrameConstructor.h"
    12 #include "nsContainerFrame.h"
    13 #include "nsInlineFrame.h"
    14 #include "nsPlaceholderFrame.h"
    15 #include "nsFirstLetterFrame.h"
    16 #include "nsUnicodeProperties.h"
    17 #include "nsTextFrame.h"
    18 #include "nsBlockFrame.h"
    19 #include "nsIFrameInlines.h"
    20 #include <algorithm>
    22 #undef NOISY_BIDI
    23 #undef REALLY_NOISY_BIDI
    25 using namespace mozilla;
    27 static const char16_t kSpace            = 0x0020;
    28 static const char16_t kZWSP             = 0x200B;
    29 static const char16_t kLineSeparator    = 0x2028;
    30 static const char16_t kObjectSubstitute = 0xFFFC;
    31 static const char16_t kLRE              = 0x202A;
    32 static const char16_t kRLE              = 0x202B;
    33 static const char16_t kLRO              = 0x202D;
    34 static const char16_t kRLO              = 0x202E;
    35 static const char16_t kPDF              = 0x202C;
    36 static const char16_t kSeparators[] = {
    37   // All characters with Bidi type Segment Separator or Block Separator
    38   char16_t('\t'),
    39   char16_t('\r'),
    40   char16_t('\n'),
    41   char16_t(0xb),
    42   char16_t(0x1c),
    43   char16_t(0x1d),
    44   char16_t(0x1e),
    45   char16_t(0x1f),
    46   char16_t(0x85),
    47   char16_t(0x2029),
    48   char16_t(0)
    49 };
    51 #define NS_BIDI_CONTROL_FRAME ((nsIFrame*)0xfffb1d1)
    53 struct BidiParagraphData {
    54   nsString            mBuffer;
    55   nsAutoTArray<char16_t, 16> mEmbeddingStack;
    56   nsTArray<nsIFrame*> mLogicalFrames;
    57   nsTArray<nsLineBox*> mLinePerFrame;
    58   nsDataHashtable<nsISupportsHashKey, int32_t> mContentToFrameIndex;
    59   bool                mIsVisual;
    60   bool                mReset;
    61   nsBidiLevel         mParaLevel;
    62   nsIContent*         mPrevContent;
    63   nsAutoPtr<nsBidi>   mBidiEngine;
    64   nsIFrame*           mPrevFrame;
    65   nsAutoPtr<BidiParagraphData> mSubParagraph;
    66   uint8_t             mParagraphDepth;
    68   void Init(nsBlockFrame *aBlockFrame)
    69   {
    70     mBidiEngine = new nsBidi();
    71     mPrevContent = nullptr;
    72     mParagraphDepth = 0;
    74     mParaLevel = nsBidiPresUtils::BidiLevelFromStyle(aBlockFrame->StyleContext());
    76     mIsVisual = aBlockFrame->PresContext()->IsVisualMode();
    77     if (mIsVisual) {
    78       /**
    79        * Drill up in content to detect whether this is an element that needs to
    80        * be rendered with logical order even on visual pages.
    81        *
    82        * We always use logical order on form controls, firstly so that text
    83        * entry will be in logical order, but also because visual pages were
    84        * written with the assumption that even if the browser had no support
    85        * for right-to-left text rendering, it would use native widgets with
    86        * bidi support to display form controls.
    87        *
    88        * We also use logical order in XUL elements, since we expect that if a
    89        * XUL element appears in a visual page, it will be generated by an XBL
    90        * binding and contain localized text which will be in logical order.
    91        */
    92       for (nsIContent* content = aBlockFrame->GetContent() ; content; 
    93            content = content->GetParent()) {
    94         if (content->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) ||
    95             content->IsXUL()) {
    96           mIsVisual = false;
    97           break;
    98         }
    99       }
   100     }
   101   }
   103   BidiParagraphData* GetSubParagraph()
   104   {
   105     if (!mSubParagraph) {
   106       mSubParagraph = new BidiParagraphData();
   107       mSubParagraph->Init(this);
   108     }
   110     return mSubParagraph;
   111   }
   113   // Initialise a sub-paragraph from its containing paragraph
   114   void Init(BidiParagraphData *aBpd)
   115   {
   116     mBidiEngine = new nsBidi();
   117     mPrevContent = nullptr;
   118     mIsVisual = aBpd->mIsVisual;
   119     mReset = false;
   120   }
   122   void Reset(nsIFrame* aBDIFrame, BidiParagraphData *aBpd)
   123   {
   124     mReset = true;
   125     mLogicalFrames.Clear();
   126     mLinePerFrame.Clear();
   127     mContentToFrameIndex.Clear();
   128     mBuffer.SetLength(0);
   129     mPrevFrame = aBpd->mPrevFrame;
   130     mParagraphDepth = aBpd->mParagraphDepth + 1;
   132     const nsStyleTextReset* text = aBDIFrame->StyleTextReset();
   133     bool isRTL = (NS_STYLE_DIRECTION_RTL ==
   134                   aBDIFrame->StyleVisibility()->mDirection);
   136     if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
   137       mParaLevel = NSBIDI_DEFAULT_LTR;
   138     } else {
   139       mParaLevel = mParagraphDepth * 2;
   140       if (isRTL) ++mParaLevel;
   141     }
   143     if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_OVERRIDE) {
   144       PushBidiControl(isRTL ? kRLO : kLRO);
   145     }
   146   }
   148   void EmptyBuffer()
   149   {
   150     mBuffer.SetLength(0);
   151   }
   153   nsresult SetPara()
   154   {
   155     return mBidiEngine->SetPara(mBuffer.get(), BufferLength(),
   156                                 mParaLevel, nullptr);
   157   }
   159   /**
   160    * mParaLevel can be NSBIDI_DEFAULT_LTR as well as NSBIDI_LTR or NSBIDI_RTL.
   161    * GetParaLevel() returns the actual (resolved) paragraph level which is
   162    * always either NSBIDI_LTR or NSBIDI_RTL
   163    */
   164   nsBidiLevel GetParaLevel()
   165   {
   166     nsBidiLevel paraLevel = mParaLevel;
   167     if (IS_DEFAULT_LEVEL(paraLevel)) {
   168       mBidiEngine->GetParaLevel(&paraLevel);
   169     }
   170     return paraLevel;
   171   }
   173   nsBidiDirection GetDirection()
   174   {
   175     nsBidiDirection dir;
   176     mBidiEngine->GetDirection(&dir);
   177     return dir;
   178   }
   180   nsresult CountRuns(int32_t *runCount){ return mBidiEngine->CountRuns(runCount); }
   182   nsresult GetLogicalRun(int32_t aLogicalStart, 
   183                          int32_t* aLogicalLimit,
   184                          nsBidiLevel* aLevel)
   185   {
   186     nsresult rv = mBidiEngine->GetLogicalRun(aLogicalStart,
   187                                              aLogicalLimit, aLevel);
   188     if (mIsVisual || NS_FAILED(rv))
   189       *aLevel = GetParaLevel();
   190     return rv;
   191   }
   193   void ResetData()
   194   {
   195     mLogicalFrames.Clear();
   196     mLinePerFrame.Clear();
   197     mContentToFrameIndex.Clear();
   198     mBuffer.SetLength(0);
   199     mPrevContent = nullptr;
   200     for (uint32_t i = 0; i < mEmbeddingStack.Length(); ++i) {
   201       mBuffer.Append(mEmbeddingStack[i]);
   202       mLogicalFrames.AppendElement(NS_BIDI_CONTROL_FRAME);
   203       mLinePerFrame.AppendElement((nsLineBox*)nullptr);
   204     }
   205   }
   207   void ResetForNewBlock()
   208   {
   209     for (BidiParagraphData* bpd = this; bpd; bpd = bpd->mSubParagraph) {
   210       bpd->mPrevFrame = nullptr;
   211     }
   212   }
   214   void AppendFrame(nsIFrame* aFrame,
   215                    nsBlockInFlowLineIterator* aLineIter,
   216                    nsIContent* aContent = nullptr)
   217   {
   218     if (aContent) {
   219       mContentToFrameIndex.Put(aContent, FrameCount());
   220     }
   221     mLogicalFrames.AppendElement(aFrame);
   223     AdvanceLineIteratorToFrame(aFrame, aLineIter, mPrevFrame);
   224     mLinePerFrame.AppendElement(aLineIter->GetLine().get());
   225   }
   227   void AdvanceAndAppendFrame(nsIFrame** aFrame,
   228                              nsBlockInFlowLineIterator* aLineIter,
   229                              nsIFrame** aNextSibling)
   230   {
   231     nsIFrame* frame = *aFrame;
   232     nsIFrame* nextSibling = *aNextSibling;
   234     frame = frame->GetNextContinuation();
   235     if (frame) {
   236       AppendFrame(frame, aLineIter, nullptr);
   238       /*
   239        * If we have already overshot the saved next-sibling while
   240        * scanning the frame's continuations, advance it.
   241        */
   242       if (frame == nextSibling) {
   243         nextSibling = frame->GetNextSibling();
   244       }
   245     }
   247     *aFrame = frame;
   248     *aNextSibling = nextSibling;
   249   }
   251   int32_t GetLastFrameForContent(nsIContent *aContent)
   252   {
   253     int32_t index = 0;
   254     mContentToFrameIndex.Get(aContent, &index);
   255     return index;
   256   }
   258   int32_t FrameCount(){ return mLogicalFrames.Length(); }
   260   int32_t BufferLength(){ return mBuffer.Length(); }
   262   nsIFrame* FrameAt(int32_t aIndex){ return mLogicalFrames[aIndex]; }
   264   nsLineBox* GetLineForFrameAt(int32_t aIndex){ return mLinePerFrame[aIndex]; }
   266   void AppendUnichar(char16_t aCh){ mBuffer.Append(aCh); }
   268   void AppendString(const nsDependentSubstring& aString){ mBuffer.Append(aString); }
   270   void AppendControlChar(char16_t aCh)
   271   {
   272     mLogicalFrames.AppendElement(NS_BIDI_CONTROL_FRAME);
   273     mLinePerFrame.AppendElement((nsLineBox*)nullptr);
   274     AppendUnichar(aCh);
   275   }
   277   void PushBidiControl(char16_t aCh)
   278   {
   279     AppendControlChar(aCh);
   280     mEmbeddingStack.AppendElement(aCh);
   281   }
   283   void PopBidiControl()
   284   {
   285     AppendControlChar(kPDF);
   286     NS_ASSERTION(mEmbeddingStack.Length(), "embedding/override underflow");
   287     mEmbeddingStack.TruncateLength(mEmbeddingStack.Length() - 1);
   288   }
   290   void ClearBidiControls()
   291   {
   292     for (uint32_t i = 0; i < mEmbeddingStack.Length(); ++i) {
   293       AppendControlChar(kPDF);
   294     }
   295   }
   297   static bool
   298   IsFrameInCurrentLine(nsBlockInFlowLineIterator* aLineIter,
   299                        nsIFrame* aPrevFrame, nsIFrame* aFrame)
   300   {
   301     nsIFrame* endFrame = aLineIter->IsLastLineInList() ? nullptr :
   302       aLineIter->GetLine().next()->mFirstChild;
   303     nsIFrame* startFrame = aPrevFrame ? aPrevFrame : aLineIter->GetLine()->mFirstChild;
   304     for (nsIFrame* frame = startFrame; frame && frame != endFrame;
   305          frame = frame->GetNextSibling()) {
   306       if (frame == aFrame)
   307         return true;
   308     }
   309     return false;
   310   }
   312   static void
   313   AdvanceLineIteratorToFrame(nsIFrame* aFrame,
   314                              nsBlockInFlowLineIterator* aLineIter,
   315                              nsIFrame*& aPrevFrame)
   316   {
   317     // Advance aLine to the line containing aFrame
   318     nsIFrame* child = aFrame;
   319     nsIFrame* parent = nsLayoutUtils::GetParentOrPlaceholderFor(child);
   320     while (parent && !nsLayoutUtils::GetAsBlock(parent)) {
   321       child = parent;
   322       parent = nsLayoutUtils::GetParentOrPlaceholderFor(child);
   323     }
   324     NS_ASSERTION (parent, "aFrame is not a descendent of aBlockFrame");
   325     while (!IsFrameInCurrentLine(aLineIter, aPrevFrame, child)) {
   326 #ifdef DEBUG
   327       bool hasNext =
   328 #endif
   329         aLineIter->Next();
   330       NS_ASSERTION(hasNext, "Can't find frame in lines!");
   331       aPrevFrame = nullptr;
   332     }
   333     aPrevFrame = child;
   334   }
   336 };
   338 struct BidiLineData {
   339   nsTArray<nsIFrame*> mLogicalFrames;
   340   nsTArray<nsIFrame*> mVisualFrames;
   341   nsTArray<int32_t> mIndexMap;
   342   nsAutoTArray<uint8_t, 18> mLevels;
   343   bool mIsReordered;
   345   BidiLineData(nsIFrame* aFirstFrameOnLine, int32_t   aNumFramesOnLine)
   346   {
   347     /**
   348      * Initialize the logically-ordered array of frames using the top-level
   349      * frames of a single line
   350      */
   351     mLogicalFrames.Clear();
   353     bool isReordered = false;
   354     bool hasRTLFrames = false;
   356     for (nsIFrame* frame = aFirstFrameOnLine;
   357          frame && aNumFramesOnLine--;
   358          frame = frame->GetNextSibling()) {
   359       AppendFrame(frame);
   360       uint8_t level = nsBidiPresUtils::GetFrameEmbeddingLevel(frame);
   361       mLevels.AppendElement(level);
   362       mIndexMap.AppendElement(0);
   363       if (level & 1) {
   364         hasRTLFrames = true;
   365       }
   366     }
   368     // Reorder the line
   369     nsBidi::ReorderVisual(mLevels.Elements(), FrameCount(),
   370                           mIndexMap.Elements());
   372     for (int32_t i = 0; i < FrameCount(); i++) {
   373       mVisualFrames.AppendElement(LogicalFrameAt(mIndexMap[i]));
   374       if (i != mIndexMap[i]) {
   375         isReordered = true;
   376       }
   377     }
   379     // If there's an RTL frame, assume the line is reordered
   380     mIsReordered = isReordered || hasRTLFrames;
   381   }
   383   void AppendFrame(nsIFrame* aFrame)
   384   {
   385     mLogicalFrames.AppendElement(aFrame); 
   386   }
   388   int32_t FrameCount(){ return mLogicalFrames.Length(); }
   390   nsIFrame* LogicalFrameAt(int32_t aIndex){ return mLogicalFrames[aIndex]; }
   392   nsIFrame* VisualFrameAt(int32_t aIndex){ return mVisualFrames[aIndex]; }
   393 };
   395 /* Some helper methods for Resolve() */
   397 // Should this frame be split between text runs?
   398 static bool
   399 IsBidiSplittable(nsIFrame* aFrame)
   400 {
   401   // Bidi inline containers should be split, unless they're line frames.
   402   nsIAtom* frameType = aFrame->GetType();
   403   return (aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer) &&
   404           frameType != nsGkAtoms::lineFrame) ||
   405          frameType == nsGkAtoms::textFrame;
   406 }
   408 // Should this frame be treated as a leaf (e.g. when building mLogicalFrames)?
   409 static bool
   410 IsBidiLeaf(nsIFrame* aFrame)
   411 {
   412   nsIFrame* kid = aFrame->GetFirstPrincipalChild();
   413   return !kid || !aFrame->IsFrameOfType(nsIFrame::eBidiInlineContainer);
   414 }
   416 /**
   417  * Create non-fluid continuations for the ancestors of a given frame all the way
   418  * up the frame tree until we hit a non-splittable frame (a line or a block).
   419  *
   420  * @param aParent the first parent frame to be split
   421  * @param aFrame the child frames after this frame are reparented to the
   422  *        newly-created continuation of aParent.
   423  *        If aFrame is null, all the children of aParent are reparented.
   424  */
   425 static nsresult
   426 SplitInlineAncestors(nsIFrame* aParent,
   427                      nsIFrame* aFrame)
   428 {
   429   nsPresContext *presContext = aParent->PresContext();
   430   nsIPresShell *presShell = presContext->PresShell();
   431   nsIFrame* frame = aFrame;
   432   nsIFrame* parent = aParent;
   433   nsIFrame* newParent;
   435   while (IsBidiSplittable(parent)) {
   436     nsIFrame* grandparent = parent->GetParent();
   437     NS_ASSERTION(grandparent, "Couldn't get parent's parent in nsBidiPresUtils::SplitInlineAncestors");
   439     // Split the child list after |frame|, unless it is the last child.
   440     if (!frame || frame->GetNextSibling()) {
   442       newParent = presShell->FrameConstructor()->
   443         CreateContinuingFrame(presContext, parent, grandparent, false);
   445       nsContainerFrame* container = do_QueryFrame(parent);
   446       nsFrameList tail = container->StealFramesAfter(frame);
   448       // Reparent views as necessary
   449       nsresult rv;
   450       rv = nsContainerFrame::ReparentFrameViewList(tail, parent, newParent);
   451       if (NS_FAILED(rv)) {
   452         return rv;
   453       }
   455       // The parent's continuation adopts the siblings after the split.
   456       rv = newParent->InsertFrames(nsIFrame::kNoReflowPrincipalList, nullptr, tail);
   457       if (NS_FAILED(rv)) {
   458         return rv;
   459       }
   461       // The list name kNoReflowPrincipalList would indicate we don't want reflow
   462       nsFrameList temp(newParent, newParent);
   463       rv = grandparent->InsertFrames(nsIFrame::kNoReflowPrincipalList, parent, temp);
   464       if (NS_FAILED(rv)) {
   465         return rv;
   466       }
   467     }
   469     frame = parent;
   470     parent = grandparent;
   471   }
   473   return NS_OK;
   474 }
   476 static void
   477 MakeContinuationFluid(nsIFrame* aFrame, nsIFrame* aNext)
   478 {
   479   NS_ASSERTION (!aFrame->GetNextInFlow() || aFrame->GetNextInFlow() == aNext, 
   480                 "next-in-flow is not next continuation!");
   481   aFrame->SetNextInFlow(aNext);
   483   NS_ASSERTION (!aNext->GetPrevInFlow() || aNext->GetPrevInFlow() == aFrame,
   484                 "prev-in-flow is not prev continuation!");
   485   aNext->SetPrevInFlow(aFrame);
   486 }
   488 static void
   489 MakeContinuationsNonFluidUpParentChain(nsIFrame* aFrame, nsIFrame* aNext)
   490 {
   491   nsIFrame* frame;
   492   nsIFrame* next;
   494   for (frame = aFrame, next = aNext;
   495        frame && next &&
   496          next != frame && next == frame->GetNextInFlow() &&
   497          IsBidiSplittable(frame);
   498        frame = frame->GetParent(), next = next->GetParent()) {
   500     frame->SetNextContinuation(next);
   501     next->SetPrevContinuation(frame);
   502   }
   503 }
   505 // If aFrame is the last child of its parent, convert bidi continuations to
   506 // fluid continuations for all of its inline ancestors.
   507 // If it isn't the last child, make sure that its continuation is fluid.
   508 static void
   509 JoinInlineAncestors(nsIFrame* aFrame)
   510 {
   511   nsIFrame* frame = aFrame;
   512   do {
   513     nsIFrame* next = frame->GetNextContinuation();
   514     if (next) {
   515       // Don't join frames if they come from different paragraph depths (i.e.
   516       // one is bidi isolated relative to the other
   517       if (nsBidiPresUtils::GetParagraphDepth(frame) ==
   518           nsBidiPresUtils::GetParagraphDepth(next)) {
   519         MakeContinuationFluid(frame, next);
   520       }
   521     }
   522     // Join the parent only as long as we're its last child.
   523     if (frame->GetNextSibling())
   524       break;
   525     frame = frame->GetParent();
   526   } while (frame && IsBidiSplittable(frame));
   527 }
   529 static nsresult
   530 CreateContinuation(nsIFrame*  aFrame,
   531                    nsIFrame** aNewFrame,
   532                    bool       aIsFluid)
   533 {
   534   NS_PRECONDITION(aNewFrame, "null OUT ptr");
   535   NS_PRECONDITION(aFrame, "null ptr");
   537   *aNewFrame = nullptr;
   539   nsPresContext *presContext = aFrame->PresContext();
   540   nsIPresShell *presShell = presContext->PresShell();
   541   NS_ASSERTION(presShell, "PresShell must be set on PresContext before calling nsBidiPresUtils::CreateContinuation");
   543   nsIFrame* parent = aFrame->GetParent();
   544   NS_ASSERTION(parent, "Couldn't get frame parent in nsBidiPresUtils::CreateContinuation");
   546   nsresult rv = NS_OK;
   548   // Have to special case floating first letter frames because the continuation
   549   // doesn't go in the first letter frame. The continuation goes with the rest
   550   // of the text that the first letter frame was made out of.
   551   if (parent->GetType() == nsGkAtoms::letterFrame &&
   552       parent->IsFloating()) {
   553     nsFirstLetterFrame* letterFrame = do_QueryFrame(parent);
   554     rv = letterFrame->CreateContinuationForFloatingParent(presContext, aFrame,
   555                                                           aNewFrame, aIsFluid);
   556     return rv;
   557   }
   559   *aNewFrame = presShell->FrameConstructor()->
   560     CreateContinuingFrame(presContext, aFrame, parent, aIsFluid);
   562   // The list name kNoReflowPrincipalList would indicate we don't want reflow
   563   // XXXbz this needs higher-level framelist love
   564   nsFrameList temp(*aNewFrame, *aNewFrame);
   565   rv = parent->InsertFrames(nsIFrame::kNoReflowPrincipalList, aFrame, temp);
   566   if (NS_FAILED(rv)) {
   567     return rv;
   568   }
   570   if (!aIsFluid) {  
   571     // Split inline ancestor frames
   572     rv = SplitInlineAncestors(parent, aFrame);
   573     if (NS_FAILED(rv)) {
   574       return rv;
   575     }
   576   }
   578   return NS_OK;
   579 }
   581 /*
   582  * Overview of the implementation of Resolve():
   583  *
   584  *  Walk through the descendants of aBlockFrame and build:
   585  *   * mLogicalFrames: an nsTArray of nsIFrame* pointers in logical order
   586  *   * mBuffer: an nsString containing a representation of
   587  *     the content of the frames.
   588  *     In the case of text frames, this is the actual text context of the
   589  *     frames, but some other elements are represented in a symbolic form which
   590  *     will make the Unicode Bidi Algorithm give the correct results.
   591  *     Bidi embeddings and overrides set by CSS or <bdo> elements are
   592  *     represented by the corresponding Unicode control characters.
   593  *     <br> elements are represented by U+2028 LINE SEPARATOR
   594  *     Other inline elements are represented by U+FFFC OBJECT REPLACEMENT
   595  *     CHARACTER
   596  *
   597  *  Then pass mBuffer to the Bidi engine for resolving of embedding levels
   598  *  by nsBidi::SetPara() and division into directional runs by
   599  *  nsBidi::CountRuns().
   600  *
   601  *  Finally, walk these runs in logical order using nsBidi::GetLogicalRun() and
   602  *  correlate them with the frames indexed in mLogicalFrames, setting the
   603  *  baseLevel and embeddingLevel properties according to the results returned
   604  *  by the Bidi engine.
   605  *
   606  *  The rendering layer requires each text frame to contain text in only one
   607  *  direction, so we may need to call EnsureBidiContinuation() to split frames.
   608  *  We may also need to call RemoveBidiContinuation() to convert frames created
   609  *  by EnsureBidiContinuation() in previous reflows into fluid continuations.
   610  */
   611 nsresult
   612 nsBidiPresUtils::Resolve(nsBlockFrame* aBlockFrame)
   613 {
   614   BidiParagraphData bpd;
   615   bpd.Init(aBlockFrame);
   617   // Handle bidi-override being set on the block itself before calling
   618   // TraverseFrames.
   619   const nsStyleTextReset* text = aBlockFrame->StyleTextReset();
   620   char16_t ch = 0;
   621   if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_OVERRIDE) {
   622     const nsStyleVisibility* vis = aBlockFrame->StyleVisibility();
   623     if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
   624       ch = kRLO;
   625     }
   626     else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
   627       ch = kLRO;
   628     }
   629     if (ch != 0) {
   630       bpd.PushBidiControl(ch);
   631     }
   632   }
   633   for (nsBlockFrame* block = aBlockFrame; block;
   634        block = static_cast<nsBlockFrame*>(block->GetNextContinuation())) {
   635     block->RemoveStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
   636     nsBlockInFlowLineIterator lineIter(block, block->begin_lines());
   637     bpd.ResetForNewBlock();
   638     TraverseFrames(aBlockFrame, &lineIter, block->GetFirstPrincipalChild(), &bpd);
   639     // XXX what about overflow lines?
   640   }
   642   if (ch != 0) {
   643     bpd.PopBidiControl();
   644   }
   646   BidiParagraphData* subParagraph = bpd.GetSubParagraph();
   647   if (subParagraph->BufferLength()) {
   648     ResolveParagraph(aBlockFrame, subParagraph);
   649     subParagraph->EmptyBuffer();
   650   }
   651   return ResolveParagraph(aBlockFrame, &bpd);
   652 }
   654 nsresult
   655 nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame,
   656                                   BidiParagraphData* aBpd)
   657 {
   658   nsPresContext *presContext = aBlockFrame->PresContext();
   660   if (aBpd->BufferLength() < 1) {
   661     return NS_OK;
   662   }
   663   aBpd->mBuffer.ReplaceChar(kSeparators, kSpace);
   665   int32_t runCount;
   667   nsresult rv = aBpd->SetPara();
   668   NS_ENSURE_SUCCESS(rv, rv);
   670   uint8_t embeddingLevel = aBpd->GetParaLevel();
   672   rv = aBpd->CountRuns(&runCount);
   673   NS_ENSURE_SUCCESS(rv, rv);
   675   int32_t     runLength      = 0;   // the length of the current run of text
   676   int32_t     lineOffset     = 0;   // the start of the current run
   677   int32_t     logicalLimit   = 0;   // the end of the current run + 1
   678   int32_t     numRun         = -1;
   679   int32_t     fragmentLength = 0;   // the length of the current text frame
   680   int32_t     frameIndex     = -1;  // index to the frames in mLogicalFrames
   681   int32_t     frameCount     = aBpd->FrameCount();
   682   int32_t     contentOffset  = 0;   // offset of current frame in its content node
   683   bool        isTextFrame    = false;
   684   nsIFrame*   frame = nullptr;
   685   nsIContent* content = nullptr;
   686   int32_t     contentTextLength = 0;
   688   FramePropertyTable *propTable = presContext->PropertyTable();
   689   nsLineBox* currentLine = nullptr;
   691 #ifdef DEBUG
   692 #ifdef NOISY_BIDI
   693   printf("Before Resolve(), aBlockFrame=0x%p, mBuffer='%s', frameCount=%d, runCount=%d\n",
   694          (void*)aBlockFrame, NS_ConvertUTF16toUTF8(aBpd->mBuffer).get(), frameCount, runCount);
   695 #ifdef REALLY_NOISY_BIDI
   696   printf(" block frame tree=:\n");
   697   aBlockFrame->List(stdout, 0);
   698 #endif
   699 #endif
   700 #endif
   702   if (runCount == 1 && frameCount == 1 &&
   703       aBpd->mParagraphDepth == 0 && aBpd->GetDirection() == NSBIDI_LTR &&
   704       aBpd->GetParaLevel() == 0) {
   705     // We have a single left-to-right frame in a left-to-right paragraph,
   706     // without bidi isolation from the surrounding text.
   707     // Make sure that the embedding level and base level frame properties aren't
   708     // set (because if they are this frame used to have some other direction,
   709     // so we can't do this optimization), and we're done.
   710     nsIFrame* frame = aBpd->FrameAt(0);
   711     if (frame != NS_BIDI_CONTROL_FRAME &&
   712         !frame->Properties().Get(nsIFrame::EmbeddingLevelProperty()) &&
   713         !frame->Properties().Get(nsIFrame::BaseLevelProperty())) {
   714 #ifdef DEBUG
   715 #ifdef NOISY_BIDI
   716       printf("early return for single direction frame %p\n", (void*)frame);
   717 #endif
   718 #endif
   719       frame->AddStateBits(NS_FRAME_IS_BIDI);
   720       return NS_OK;
   721     }
   722   }
   724   nsIFrame* firstFrame = nullptr;
   725   nsIFrame* lastFrame = nullptr;
   727   for (; ;) {
   728     if (fragmentLength <= 0) {
   729       // Get the next frame from mLogicalFrames
   730       if (++frameIndex >= frameCount) {
   731         break;
   732       }
   733       frame = aBpd->FrameAt(frameIndex);
   734       if (frame == NS_BIDI_CONTROL_FRAME ||
   735           nsGkAtoms::textFrame != frame->GetType()) {
   736         /*
   737          * Any non-text frame corresponds to a single character in the text buffer
   738          * (a bidi control character, LINE SEPARATOR, or OBJECT SUBSTITUTE)
   739          */
   740         isTextFrame = false;
   741         fragmentLength = 1;
   742       }
   743       else {
   744         if (!firstFrame) {
   745           firstFrame = frame;
   746         }
   747         lastFrame = frame;
   748         currentLine = aBpd->GetLineForFrameAt(frameIndex);
   749         content = frame->GetContent();
   750         if (!content) {
   751           rv = NS_OK;
   752           break;
   753         }
   754         contentTextLength = content->TextLength();
   755         if (contentTextLength == 0) {
   756           frame->AdjustOffsetsForBidi(0, 0);
   757           // Set the base level and embedding level of the current run even
   758           // on an empty frame. Otherwise frame reordering will not be correct.
   759           propTable->Set(frame, nsIFrame::EmbeddingLevelProperty(),
   760                          NS_INT32_TO_PTR(embeddingLevel));
   761           propTable->Set(frame, nsIFrame::BaseLevelProperty(),
   762                          NS_INT32_TO_PTR(aBpd->GetParaLevel()));
   763           propTable->Set(frame, nsIFrame::ParagraphDepthProperty(),
   764                          NS_INT32_TO_PTR(aBpd->mParagraphDepth));
   765           continue;
   766         }
   767         int32_t start, end;
   768         frame->GetOffsets(start, end);
   769         NS_ASSERTION(!(contentTextLength < end - start),
   770                      "Frame offsets don't fit in content");
   771         fragmentLength = std::min(contentTextLength, end - start);
   772         contentOffset = start;
   773         isTextFrame = true;
   774       }
   775     } // if (fragmentLength <= 0)
   777     if (runLength <= 0) {
   778       // Get the next run of text from the Bidi engine
   779       if (++numRun >= runCount) {
   780         break;
   781       }
   782       lineOffset = logicalLimit;
   783       if (NS_FAILED(aBpd->GetLogicalRun(
   784               lineOffset, &logicalLimit, &embeddingLevel) ) ) {
   785         break;
   786       }
   787       runLength = logicalLimit - lineOffset;
   788     } // if (runLength <= 0)
   790     if (frame == NS_BIDI_CONTROL_FRAME) {
   791       frame = nullptr;
   792       ++lineOffset;
   793     }
   794     else {
   795       propTable->Set(frame, nsIFrame::EmbeddingLevelProperty(),
   796                      NS_INT32_TO_PTR(embeddingLevel));
   797       propTable->Set(frame, nsIFrame::BaseLevelProperty(),
   798                      NS_INT32_TO_PTR(aBpd->GetParaLevel()));
   799       propTable->Set(frame, nsIFrame::ParagraphDepthProperty(),
   800                      NS_INT32_TO_PTR(aBpd->mParagraphDepth));
   801       if (isTextFrame) {
   802         if ( (runLength > 0) && (runLength < fragmentLength) ) {
   803           /*
   804            * The text in this frame continues beyond the end of this directional run.
   805            * Create a non-fluid continuation frame for the next directional run.
   806            */
   807           currentLine->MarkDirty();
   808           nsIFrame* nextBidi;
   809           int32_t runEnd = contentOffset + runLength;
   810           rv = EnsureBidiContinuation(frame, &nextBidi, frameIndex,
   811                                       contentOffset,
   812                                       runEnd);
   813           if (NS_FAILED(rv)) {
   814             break;
   815           }
   816           nextBidi->AdjustOffsetsForBidi(runEnd,
   817                                          contentOffset + fragmentLength);
   818           lastFrame = frame = nextBidi;
   819           contentOffset = runEnd;
   820         } // if (runLength < fragmentLength)
   821         else {
   822           if (contentOffset + fragmentLength == contentTextLength) {
   823             /* 
   824              * We have finished all the text in this content node. Convert any
   825              * further non-fluid continuations to fluid continuations and advance
   826              * frameIndex to the last frame in the content node
   827              */
   828             int32_t newIndex = aBpd->GetLastFrameForContent(content);
   829             if (newIndex > frameIndex) {
   830               currentLine->MarkDirty();
   831               RemoveBidiContinuation(aBpd, frame,
   832                                      frameIndex, newIndex, lineOffset);
   833               frameIndex = newIndex;
   834               lastFrame = frame = aBpd->FrameAt(frameIndex);
   835             }
   836           } else if (fragmentLength > 0 && runLength > fragmentLength) {
   837             /*
   838              * There is more text that belongs to this directional run in the next
   839              * text frame: make sure it is a fluid continuation of the current frame.
   840              * Do not advance frameIndex, because the next frame may contain
   841              * multi-directional text and need to be split
   842              */
   843             int32_t newIndex = frameIndex;
   844             do {
   845             } while (++newIndex < frameCount &&
   846                      aBpd->FrameAt(newIndex) == NS_BIDI_CONTROL_FRAME);
   847             if (newIndex < frameCount) {
   848               currentLine->MarkDirty();
   849               RemoveBidiContinuation(aBpd, frame,
   850                                      frameIndex, newIndex, lineOffset);
   851             }
   852           } else if (runLength == fragmentLength) {
   853             /*
   854              * If the directional run ends at the end of the frame, make sure
   855              * that any continuation is non-fluid, and do the same up the
   856              * parent chain
   857              */
   858             nsIFrame* next = frame->GetNextInFlow();
   859             if (next) {
   860               currentLine->MarkDirty();
   861               MakeContinuationsNonFluidUpParentChain(frame, next);
   862             }
   863           }
   864           frame->AdjustOffsetsForBidi(contentOffset, contentOffset + fragmentLength);
   865           currentLine->MarkDirty();
   866         }
   867       } // isTextFrame
   868       else {
   869         ++lineOffset;
   870       }
   871     } // not bidi control frame
   872     int32_t temp = runLength;
   873     runLength -= fragmentLength;
   874     fragmentLength -= temp;
   876     if (frame && fragmentLength <= 0) {
   877       // If the frame is at the end of a run, and this is not the end of our
   878       // paragrah, split all ancestor inlines that need splitting.
   879       // To determine whether we're at the end of the run, we check that we've
   880       // finished processing the current run, and that the current frame
   881       // doesn't have a fluid continuation (it could have a fluid continuation
   882       // of zero length, so testing runLength alone is not sufficient).
   883       if (runLength <= 0 && !frame->GetNextInFlow()) {
   884         if (numRun + 1 < runCount) {
   885           nsIFrame* child = frame;
   886           nsIFrame* parent = frame->GetParent();
   887           // As long as we're on the last sibling, the parent doesn't have to
   888           // be split.
   889           // However, if the parent has a fluid continuation, we do have to make
   890           // it non-fluid. This can happen e.g. when we have a first-letter
   891           // frame and the end of the first-letter coincides with the end of a
   892           // directional run.
   893           while (parent &&
   894                  IsBidiSplittable(parent) &&
   895                  !child->GetNextSibling()) {
   896             nsIFrame* next = parent->GetNextInFlow();
   897             if (next) {
   898               parent->SetNextContinuation(next);
   899               next->SetPrevContinuation(parent);
   900             }
   901             child = parent;
   902             parent = child->GetParent();
   903           }
   904           if (parent && IsBidiSplittable(parent)) {
   905             SplitInlineAncestors(parent, child);
   906           }
   907         }
   908       }
   909       else {
   910         // We're not at an end of a run. If |frame| is the last child of its
   911         // parent, and its ancestors happen to have bidi continuations, convert
   912         // them into fluid continuations.
   913         JoinInlineAncestors(frame);
   914       }
   915     }
   916   } // for
   918   if (aBpd->mParagraphDepth > 0) {
   919     nsIFrame* child;
   920     nsIFrame* parent;
   921     if (firstFrame) {
   922       child = firstFrame->GetParent();
   923       if (child) {
   924         parent = child->GetParent();
   925         if (parent && IsBidiSplittable(parent)) {
   926           nsIFrame* prev = child->GetPrevSibling();
   927           if (prev) {
   928             SplitInlineAncestors(parent, prev);
   929           }
   930         }
   931       }
   932     }
   933     if (lastFrame) {
   934       child = lastFrame->GetParent();
   935       if (child) {
   936         parent = child->GetParent();
   937         if (parent && IsBidiSplittable(parent)) {
   938           SplitInlineAncestors(parent, child);
   939         }
   940       }
   941     }
   942   }
   944 #ifdef DEBUG
   945 #ifdef REALLY_NOISY_BIDI
   946   printf("---\nAfter Resolve(), frameTree =:\n");
   947   aBlockFrame->List(stdout, 0);
   948   printf("===\n");
   949 #endif
   950 #endif
   952   return rv;
   953 }
   955 void
   956 nsBidiPresUtils::TraverseFrames(nsBlockFrame*              aBlockFrame,
   957                                 nsBlockInFlowLineIterator* aLineIter,
   958                                 nsIFrame*                  aCurrentFrame,
   959                                 BidiParagraphData*         aBpd)
   960 {
   961   if (!aCurrentFrame)
   962     return;
   964 #ifdef DEBUG
   965   nsBlockFrame* initialLineContainer = aLineIter->GetContainer();
   966 #endif
   968   nsIFrame* childFrame = aCurrentFrame;
   969   do {
   970     /*
   971      * It's important to get the next sibling and next continuation *before*
   972      * handling the frame: If we encounter a forced paragraph break and call
   973      * ResolveParagraph within this loop, doing GetNextSibling and
   974      * GetNextContinuation after that could return a bidi continuation that had
   975      * just been split from the original childFrame and we would process it
   976      * twice.
   977      */
   978     nsIFrame* nextSibling = childFrame->GetNextSibling();
   979     bool isLastFrame = !childFrame->GetNextContinuation();
   980     bool isFirstFrame = !childFrame->GetPrevContinuation();
   982     // If the real frame for a placeholder is a first letter frame, we need to
   983     // drill down into it and include its contents in Bidi resolution.
   984     // If not, we just use the placeholder.
   985     nsIFrame* frame = childFrame;
   986     if (nsGkAtoms::placeholderFrame == childFrame->GetType()) {
   987       nsIFrame* realFrame =
   988         nsPlaceholderFrame::GetRealFrameForPlaceholder(childFrame);
   989       if (realFrame->GetType() == nsGkAtoms::letterFrame) {
   990         frame = realFrame;
   991       }
   992     }
   994     char16_t ch = 0;
   995     if (frame->IsFrameOfType(nsIFrame::eBidiInlineContainer)) {
   996       if (!(frame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
   997         nsContainerFrame* c = static_cast<nsContainerFrame*>(frame);
   998         MOZ_ASSERT(c = do_QueryFrame(frame),
   999                    "eBidiInlineContainer must be a nsContainerFrame subclass");
  1000         c->DrainSelfOverflowList();
  1003       const nsStyleVisibility* vis = frame->StyleVisibility();
  1004       const nsStyleTextReset* text = frame->StyleTextReset();
  1005       if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_OVERRIDE) {
  1006         if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
  1007           ch = kRLO;
  1009         else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
  1010           ch = kLRO;
  1012       } else if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_EMBED) {
  1013         if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
  1014           ch = kRLE;
  1016         else if (NS_STYLE_DIRECTION_LTR == vis->mDirection) {
  1017           ch = kLRE;
  1021       // Add a dummy frame pointer representing a bidi control code before the
  1022       // first frame of an element specifying embedding or override
  1023       if (ch != 0 && isFirstFrame) {
  1024         aBpd->PushBidiControl(ch);
  1028     if (IsBidiLeaf(frame)) {
  1029       /* Bidi leaf frame: add the frame to the mLogicalFrames array,
  1030        * and add its index to the mContentToFrameIndex hashtable. This
  1031        * will be used in RemoveBidiContinuation() to identify the last
  1032        * frame in the array with a given content.
  1033        */
  1034       nsIContent* content = frame->GetContent();
  1035       aBpd->AppendFrame(frame, aLineIter, content);
  1037       // Append the content of the frame to the paragraph buffer
  1038       nsIAtom* frameType = frame->GetType();
  1039       if (nsGkAtoms::textFrame == frameType) {
  1040         if (content != aBpd->mPrevContent) {
  1041           aBpd->mPrevContent = content;
  1042           if (!frame->StyleText()->NewlineIsSignificant()) {
  1043             content->AppendTextTo(aBpd->mBuffer);
  1044           } else {
  1045             /*
  1046              * For preformatted text we have to do bidi resolution on each line
  1047              * separately. 
  1048              */
  1049             nsAutoString text;
  1050             content->AppendTextTo(text);
  1051             nsIFrame* next;
  1052             do {
  1053               next = nullptr;
  1055               int32_t start, end;
  1056               frame->GetOffsets(start, end);
  1057               int32_t endLine = text.FindChar('\n', start);
  1058               if (endLine == -1) {
  1059                 /*
  1060                  * If there is no newline in the text content, just save the
  1061                  * text from this frame and its continuations, and do bidi
  1062                  * resolution later
  1063                  */
  1064                 aBpd->AppendString(Substring(text, start));
  1065                 while (frame && nextSibling) {
  1066                   aBpd->AdvanceAndAppendFrame(&frame, aLineIter, &nextSibling);
  1068                 break;
  1071               /*
  1072                * If there is a newline in the frame, break the frame after the
  1073                * newline, do bidi resolution and repeat until the last sibling
  1074                */
  1075               ++endLine;
  1077               /*
  1078                * If the frame ends before the new line, save the text and move
  1079                * into the next continuation
  1080                */
  1081               aBpd->AppendString(Substring(text, start,
  1082                                            std::min(end, endLine) - start));
  1083               while (end < endLine && nextSibling) { 
  1084                 aBpd->AdvanceAndAppendFrame(&frame, aLineIter, &nextSibling);
  1085                 NS_ASSERTION(frame, "Premature end of continuation chain");
  1086                 frame->GetOffsets(start, end);
  1087                 aBpd->AppendString(Substring(text, start,
  1088                                              std::min(end, endLine) - start));
  1091               if (end < endLine) {
  1092                 aBpd->mPrevContent = nullptr;
  1093                 break;
  1096               bool createdContinuation = false;
  1097               if (uint32_t(endLine) < text.Length()) {
  1098                 /*
  1099                  * Timing is everything here: if the frame already has a bidi
  1100                  * continuation, we need to make the continuation fluid *before*
  1101                  * resetting the length of the current frame. Otherwise
  1102                  * nsTextFrame::SetLength won't set the continuation frame's
  1103                  * text offsets correctly.
  1105                  * On the other hand, if the frame doesn't have a continuation,
  1106                  * we need to create one *after* resetting the length, or
  1107                  * CreateContinuingFrame will complain that there is no more
  1108                  * content for the continuation.               
  1109                  */
  1110                 next = frame->GetNextInFlow();
  1111                 if (!next) {
  1112                   // If the frame already has a bidi continuation, make it fluid
  1113                   next = frame->GetNextContinuation();
  1114                   if (next) {
  1115                     MakeContinuationFluid(frame, next);
  1116                     JoinInlineAncestors(frame);
  1120                 nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
  1121                 textFrame->SetLength(endLine - start, nullptr);
  1123                 if (!next) {
  1124                   // If the frame has no next in flow, create one.
  1125                   CreateContinuation(frame, &next, true);
  1126                   createdContinuation = true;
  1128                 aBpd->GetLineForFrameAt(aBpd->FrameCount() - 1)->MarkDirty();
  1130               ResolveParagraphWithinBlock(aBlockFrame, aBpd);
  1132               if (!nextSibling && !createdContinuation) {
  1133                 break;
  1134               } else if (next) {
  1135                 frame = next;
  1136                 aBpd->AppendFrame(frame, aLineIter);
  1139               /*
  1140                * If we have already overshot the saved next-sibling while
  1141                * scanning the frame's continuations, advance it.
  1142                */
  1143               if (frame && frame == nextSibling) {
  1144                 nextSibling = frame->GetNextSibling();
  1147             } while (next);
  1150       } else if (nsGkAtoms::brFrame == frameType) {
  1151         // break frame -- append line separator
  1152         aBpd->AppendUnichar(kLineSeparator);
  1153         ResolveParagraphWithinBlock(aBlockFrame, aBpd);
  1154       } else { 
  1155         // other frame type -- see the Unicode Bidi Algorithm:
  1156         // "...inline objects (such as graphics) are treated as if they are ...
  1157         // U+FFFC"
  1158         // <wbr>, however, is treated as U+200B ZERO WIDTH SPACE. See
  1159         // http://dev.w3.org/html5/spec/Overview.html#phrasing-content-1
  1160         aBpd->AppendUnichar(content->IsHTML(nsGkAtoms::wbr) ?
  1161                             kZWSP : kObjectSubstitute);
  1162         if (!frame->IsInlineOutside()) {
  1163           // if it is not inline, end the paragraph
  1164           ResolveParagraphWithinBlock(aBlockFrame, aBpd);
  1167     } else {
  1168       // For a non-leaf frame, recurse into TraverseFrames
  1169       nsIFrame* kid = frame->GetFirstPrincipalChild();
  1170       MOZ_ASSERT(!frame->GetFirstChild(nsIFrame::kOverflowList),
  1171                  "should have drained the overflow list above");
  1172       if (kid) {
  1173         const nsStyleTextReset* text = frame->StyleTextReset();
  1174         if (text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_ISOLATE ||
  1175             text->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
  1176           // css "unicode-bidi: isolate" and html5 bdi: 
  1177           //  resolve the element as a separate paragraph
  1178           BidiParagraphData* subParagraph = aBpd->GetSubParagraph();
  1180           /*
  1181            * As at the beginning of the loop, it's important to check for
  1182            * next-continuations before handling the frame. If we do
  1183            * TraverseFrames and *then* do GetNextContinuation on the original
  1184            * first frame, it could return a bidi continuation that had only
  1185            * just been created, and we would skip doing bidi resolution on the
  1186            * last part of the sub-paragraph.
  1187            */
  1188           bool isLastContinuation = !frame->GetNextContinuation();
  1189           if (!frame->GetPrevContinuation() || !subParagraph->mReset) {
  1190             if (subParagraph->BufferLength()) {
  1191               ResolveParagraph(aBlockFrame, subParagraph);
  1193             subParagraph->Reset(frame, aBpd);
  1195           TraverseFrames(aBlockFrame, aLineIter, kid, subParagraph);
  1196           if (isLastContinuation) {
  1197             ResolveParagraph(aBlockFrame, subParagraph);
  1198             subParagraph->EmptyBuffer();
  1201           // Treat the element as a neutral character within its containing
  1202           //  paragraph.
  1203           aBpd->AppendControlChar(kObjectSubstitute);
  1204         } else {
  1205           TraverseFrames(aBlockFrame, aLineIter, kid, aBpd);
  1210     // If the element is attributed by dir, indicate direction pop (add PDF frame)
  1211     if (isLastFrame) {
  1212       if (ch) {
  1213         // Add a dummy frame pointer representing a bidi control code after the
  1214         // last frame of an element specifying embedding or override
  1215         aBpd->PopBidiControl();
  1218     childFrame = nextSibling;
  1219   } while (childFrame);
  1221   MOZ_ASSERT(initialLineContainer == aLineIter->GetContainer());
  1224 void
  1225 nsBidiPresUtils::ResolveParagraphWithinBlock(nsBlockFrame* aBlockFrame,
  1226                                              BidiParagraphData* aBpd)
  1228   aBpd->ClearBidiControls();
  1229   ResolveParagraph(aBlockFrame, aBpd);
  1230   aBpd->ResetData();
  1233 void
  1234 nsBidiPresUtils::ReorderFrames(nsIFrame*   aFirstFrameOnLine,
  1235                                int32_t     aNumFramesOnLine,
  1236                                WritingMode aLineWM,
  1237                                nscoord&    aLineWidth)
  1239   // If this line consists of a line frame, reorder the line frame's children.
  1240   if (aFirstFrameOnLine->GetType() == nsGkAtoms::lineFrame) {
  1241     aFirstFrameOnLine = aFirstFrameOnLine->GetFirstPrincipalChild();
  1242     if (!aFirstFrameOnLine)
  1243       return;
  1244     // All children of the line frame are on the first line. Setting aNumFramesOnLine
  1245     // to -1 makes InitLogicalArrayFromLine look at all of them.
  1246     aNumFramesOnLine = -1;
  1249   BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
  1250   RepositionInlineFrames(&bld, aFirstFrameOnLine, aLineWM, aLineWidth);
  1253 nsIFrame*
  1254 nsBidiPresUtils::GetFirstLeaf(nsIFrame* aFrame)
  1256   nsIFrame* firstLeaf = aFrame;
  1257   while (!IsBidiLeaf(firstLeaf)) {
  1258     nsIFrame* firstChild = firstLeaf->GetFirstPrincipalChild();
  1259     nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(firstChild);
  1260     firstLeaf = (realFrame->GetType() == nsGkAtoms::letterFrame) ?
  1261                  realFrame : firstChild;
  1263   return firstLeaf;
  1266 nsBidiLevel
  1267 nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame* aFrame)
  1269   return NS_GET_EMBEDDING_LEVEL(nsBidiPresUtils::GetFirstLeaf(aFrame));
  1272 uint8_t
  1273 nsBidiPresUtils::GetParagraphDepth(nsIFrame* aFrame)
  1275   return NS_GET_PARAGRAPH_DEPTH(nsBidiPresUtils::GetFirstLeaf(aFrame));
  1279 nsBidiLevel
  1280 nsBidiPresUtils::GetFrameBaseLevel(nsIFrame* aFrame)
  1282   nsIFrame* firstLeaf = aFrame;
  1283   while (!IsBidiLeaf(firstLeaf)) {
  1284     firstLeaf = firstLeaf->GetFirstPrincipalChild();
  1286   return NS_GET_BASE_LEVEL(firstLeaf);
  1289 void
  1290 nsBidiPresUtils::IsFirstOrLast(nsIFrame*             aFrame,
  1291                                nsContinuationStates* aContinuationStates,
  1292                                bool&                 aIsFirst /* out */,
  1293                                bool&                 aIsLast /* out */)
  1295   /*
  1296    * Since we lay out frames in the line's direction, visiting a frame with
  1297    * 'mFirstVisualFrame == nullptr', means it's the first appearance of one
  1298    * of its continuation chain frames on the line.
  1299    * To determine if it's the last visual frame of its continuation chain on
  1300    * the line or not, we count the number of frames of the chain on the line,
  1301    * and then reduce it when we lay out a frame of the chain. If this value
  1302    * becomes 1 it means that it's the last visual frame of its continuation
  1303    * chain on this line.
  1304    */
  1306   nsFrameContinuationState* frameState = aContinuationStates->GetEntry(aFrame);
  1307   nsFrameContinuationState* firstFrameState;
  1309   if (!frameState->mFirstVisualFrame) {
  1310     // aFrame is the first visual frame of its continuation chain
  1311     nsFrameContinuationState* contState;
  1312     nsIFrame* frame;
  1314     frameState->mFrameCount = 1;
  1315     frameState->mFirstVisualFrame = aFrame;
  1317     /**
  1318      * Traverse continuation chain of aFrame in both backward and forward
  1319      * directions while the frames are on this line. Count the frames and
  1320      * set their mFirstVisualFrame to aFrame.
  1321      */
  1322     // Traverse continuation chain backward
  1323     for (frame = aFrame->GetPrevContinuation();
  1324          frame && (contState = aContinuationStates->GetEntry(frame));
  1325          frame = frame->GetPrevContinuation()) {
  1326       frameState->mFrameCount++;
  1327       contState->mFirstVisualFrame = aFrame;
  1329     frameState->mHasContOnPrevLines = (frame != nullptr);
  1331     // Traverse continuation chain forward
  1332     for (frame = aFrame->GetNextContinuation();
  1333          frame && (contState = aContinuationStates->GetEntry(frame));
  1334          frame = frame->GetNextContinuation()) {
  1335       frameState->mFrameCount++;
  1336       contState->mFirstVisualFrame = aFrame;
  1338     frameState->mHasContOnNextLines = (frame != nullptr);
  1340     aIsFirst = !frameState->mHasContOnPrevLines;
  1341     firstFrameState = frameState;
  1342   } else {
  1343     // aFrame is not the first visual frame of its continuation chain
  1344     aIsFirst = false;
  1345     firstFrameState = aContinuationStates->GetEntry(frameState->mFirstVisualFrame);
  1348   aIsLast = (firstFrameState->mFrameCount == 1 &&
  1349              !firstFrameState->mHasContOnNextLines);
  1351   if ((aIsFirst || aIsLast) &&
  1352       (aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
  1353     // For ib splits, don't treat anything except the last part as
  1354     // endmost or anything except the first part as startmost.
  1355     // As an optimization, only get the first continuation once.
  1356     nsIFrame* firstContinuation = aFrame->FirstContinuation();
  1357     if (firstContinuation->FrameIsNonLastInIBSplit()) {
  1358       // We are not endmost
  1359       aIsLast = false;
  1361     if (firstContinuation->FrameIsNonFirstInIBSplit()) {
  1362       // We are not startmost
  1363       aIsFirst = false;
  1367   // Reduce number of remaining frames of the continuation chain on the line.
  1368   firstFrameState->mFrameCount--;
  1371 void
  1372 nsBidiPresUtils::RepositionFrame(nsIFrame*             aFrame,
  1373                                  bool                  aIsEvenLevel,
  1374                                  nscoord&              aStart,
  1375                                  nsContinuationStates* aContinuationStates,
  1376                                  WritingMode           aLineWM,
  1377                                  nscoord&              aLineWidth)
  1379   if (!aFrame)
  1380     return;
  1382   bool isFirst, isLast;
  1383   IsFirstOrLast(aFrame,
  1384                 aContinuationStates,
  1385                 isFirst /* out */,
  1386                 isLast /* out */);
  1388   WritingMode frameWM = aFrame->GetWritingMode();
  1389   nsInlineFrame* testFrame = do_QueryFrame(aFrame);
  1391   if (testFrame) {
  1392     aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET);
  1394     if (isFirst) {
  1395       aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_FIRST);
  1396     } else {
  1397       aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_FIRST);
  1400     if (isLast) {
  1401       aFrame->AddStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST);
  1402     } else {
  1403       aFrame->RemoveStateBits(NS_INLINE_FRAME_BIDI_VISUAL_IS_LAST);
  1406   // This method is called from nsBlockFrame::PlaceLine via the call to
  1407   // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
  1408   // have been reflowed, which is required for GetUsedMargin/Border/Padding
  1409   LogicalMargin margin(frameWM, aFrame->GetUsedMargin());
  1410   if (isFirst) {
  1411     aStart += margin.IStart(frameWM);
  1414   nscoord start = aStart;
  1415   nscoord frameWidth = aFrame->GetSize().width;
  1417   if (!IsBidiLeaf(aFrame))
  1419     nscoord iCoord = 0;
  1420     LogicalMargin borderPadding(frameWM, aFrame->GetUsedBorderAndPadding());
  1421     if (isFirst) {
  1422       iCoord += borderPadding.IStart(frameWM);
  1425     // If the resolved direction of the container is different from the
  1426     // direction of the frame, we need to traverse the child list in reverse
  1427     // order, to make it O(n) we store the list locally and iterate the list
  1428     // in reverse
  1429     bool reverseOrder = aIsEvenLevel != frameWM.IsBidiLTR();
  1430     nsTArray<nsIFrame*> childList;
  1431     nsIFrame *frame = aFrame->GetFirstPrincipalChild();
  1432     if (frame && reverseOrder) {
  1433       childList.AppendElement((nsIFrame*)nullptr);
  1434       while (frame) {
  1435         childList.AppendElement(frame);
  1436         frame = frame->GetNextSibling();
  1438       frame = childList[childList.Length() - 1];
  1441     // Reposition the child frames
  1442     int32_t index = 0;
  1443     while (frame) {
  1444       RepositionFrame(frame,
  1445                       aIsEvenLevel,
  1446                       iCoord,
  1447                       aContinuationStates,
  1448                       frameWM,
  1449                       frameWidth);
  1450       index++;
  1451       frame = reverseOrder ?
  1452                 childList[childList.Length() - index - 1] :
  1453                 frame->GetNextSibling();
  1456     if (isLast) {
  1457       iCoord += borderPadding.IEnd(frameWM);
  1459     aStart += iCoord;
  1460   } else {
  1461     aStart += frameWidth;
  1464   LogicalRect logicalRect(aLineWM, aFrame->GetRect(), aLineWidth);
  1465   logicalRect.IStart(aLineWM) = start;
  1466   logicalRect.ISize(aLineWM) = aStart - start;
  1467   aFrame->SetRect(aLineWM, logicalRect, aLineWidth);
  1469   if (isLast) {
  1470     aStart += margin.IEnd(frameWM);
  1474 void
  1475 nsBidiPresUtils::InitContinuationStates(nsIFrame*              aFrame,
  1476                                         nsContinuationStates*  aContinuationStates)
  1478   nsFrameContinuationState* state = aContinuationStates->PutEntry(aFrame);
  1479   state->mFirstVisualFrame = nullptr;
  1480   state->mFrameCount = 0;
  1482   if (!IsBidiLeaf(aFrame)) {
  1483     // Continue for child frames
  1484     nsIFrame* frame;
  1485     for (frame = aFrame->GetFirstPrincipalChild();
  1486          frame;
  1487          frame = frame->GetNextSibling()) {
  1488       InitContinuationStates(frame,
  1489                              aContinuationStates);
  1494 void
  1495 nsBidiPresUtils::RepositionInlineFrames(BidiLineData *aBld,
  1496                                         nsIFrame* aFirstChild,
  1497                                         WritingMode aLineWM,
  1498                                         nscoord& aLineWidth)
  1500   nscoord startSpace = 0;
  1502   // This method is called from nsBlockFrame::PlaceLine via the call to
  1503   // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
  1504   // have been reflowed, which is required for GetUsedMargin/Border/Padding
  1505   WritingMode frameWM = aFirstChild->GetWritingMode();
  1506   LogicalMargin margin(frameWM, aFirstChild->GetUsedMargin());
  1507   if (!aFirstChild->GetPrevContinuation() &&
  1508       !aFirstChild->FrameIsNonFirstInIBSplit())
  1509     startSpace = margin.IStart(frameWM);
  1511   nscoord start = LogicalRect(aLineWM, aFirstChild->GetRect(),
  1512                               aLineWidth).IStart(aLineWM) - startSpace;
  1513   nsIFrame* frame;
  1514   int32_t count = aBld->mVisualFrames.Length();
  1515   int32_t index;
  1516   nsContinuationStates continuationStates;
  1518   // Initialize continuation states to (nullptr, 0) for
  1519   // each frame on the line.
  1520   for (index = 0; index < count; index++) {
  1521     InitContinuationStates(aBld->VisualFrameAt(index), &continuationStates);
  1524   // Reposition frames in visual order
  1525   int32_t step, limit;
  1526   if (aLineWM.IsBidiLTR()) {
  1527     index = 0;
  1528     step = 1;
  1529     limit = count;
  1530   } else {
  1531     index = count - 1;
  1532     step = -1;
  1533     limit = -1;
  1535   for (; index != limit; index += step) {
  1536     frame = aBld->VisualFrameAt(index);
  1537     RepositionFrame(frame,
  1538                     !(aBld->mLevels[aBld->mIndexMap[index]] & 1),
  1539                     start,
  1540                     &continuationStates,
  1541                     aLineWM,
  1542                     aLineWidth);
  1546 bool
  1547 nsBidiPresUtils::CheckLineOrder(nsIFrame*  aFirstFrameOnLine,
  1548                                 int32_t    aNumFramesOnLine,
  1549                                 nsIFrame** aFirstVisual,
  1550                                 nsIFrame** aLastVisual)
  1552   BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
  1553   int32_t count = bld.FrameCount();
  1555   if (aFirstVisual) {
  1556     *aFirstVisual = bld.VisualFrameAt(0);
  1558   if (aLastVisual) {
  1559     *aLastVisual = bld.VisualFrameAt(count-1);
  1562   return bld.mIsReordered;
  1565 nsIFrame*
  1566 nsBidiPresUtils::GetFrameToRightOf(const nsIFrame*  aFrame,
  1567                                    nsIFrame*        aFirstFrameOnLine,
  1568                                    int32_t          aNumFramesOnLine)
  1570   BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
  1572   int32_t count = bld.mVisualFrames.Length();
  1574   if (aFrame == nullptr && count)
  1575     return bld.VisualFrameAt(0);
  1577   for (int32_t i = 0; i < count - 1; i++) {
  1578     if (bld.VisualFrameAt(i) == aFrame) {
  1579       return bld.VisualFrameAt(i+1);
  1583   return nullptr;
  1586 nsIFrame*
  1587 nsBidiPresUtils::GetFrameToLeftOf(const nsIFrame*  aFrame,
  1588                                   nsIFrame*        aFirstFrameOnLine,
  1589                                   int32_t          aNumFramesOnLine)
  1591   BidiLineData bld(aFirstFrameOnLine, aNumFramesOnLine);
  1593   int32_t count = bld.mVisualFrames.Length();
  1595   if (aFrame == nullptr && count)
  1596     return bld.VisualFrameAt(count-1);
  1598   for (int32_t i = 1; i < count; i++) {
  1599     if (bld.VisualFrameAt(i) == aFrame) {
  1600       return bld.VisualFrameAt(i-1);
  1604   return nullptr;
  1607 inline nsresult
  1608 nsBidiPresUtils::EnsureBidiContinuation(nsIFrame*       aFrame,
  1609                                         nsIFrame**      aNewFrame,
  1610                                         int32_t&        aFrameIndex,
  1611                                         int32_t         aStart,
  1612                                         int32_t         aEnd)
  1614   NS_PRECONDITION(aNewFrame, "null OUT ptr");
  1615   NS_PRECONDITION(aFrame, "aFrame is null");
  1617   aFrame->AdjustOffsetsForBidi(aStart, aEnd);
  1618   return CreateContinuation(aFrame, aNewFrame, false);
  1621 void
  1622 nsBidiPresUtils::RemoveBidiContinuation(BidiParagraphData *aBpd,
  1623                                         nsIFrame*       aFrame,
  1624                                         int32_t         aFirstIndex,
  1625                                         int32_t         aLastIndex,
  1626                                         int32_t&        aOffset)
  1628   FrameProperties props = aFrame->Properties();
  1629   nsBidiLevel embeddingLevel =
  1630     (nsBidiLevel)NS_PTR_TO_INT32(props.Get(nsIFrame::EmbeddingLevelProperty()));
  1631   nsBidiLevel baseLevel =
  1632     (nsBidiLevel)NS_PTR_TO_INT32(props.Get(nsIFrame::BaseLevelProperty()));
  1633   uint8_t paragraphDepth = 
  1634     NS_PTR_TO_INT32(props.Get(nsIFrame::ParagraphDepthProperty()));
  1636   for (int32_t index = aFirstIndex + 1; index <= aLastIndex; index++) {
  1637     nsIFrame* frame = aBpd->FrameAt(index);
  1638     if (frame == NS_BIDI_CONTROL_FRAME) {
  1639       ++aOffset;
  1641     else {
  1642       // Make the frame and its continuation ancestors fluid,
  1643       // so they can be reused or deleted by normal reflow code
  1644       FrameProperties frameProps = frame->Properties();
  1645       frameProps.Set(nsIFrame::EmbeddingLevelProperty(),
  1646                      NS_INT32_TO_PTR(embeddingLevel));
  1647       frameProps.Set(nsIFrame::BaseLevelProperty(),
  1648                      NS_INT32_TO_PTR(baseLevel));
  1649       frameProps.Set(nsIFrame::ParagraphDepthProperty(),
  1650                      NS_INT32_TO_PTR(paragraphDepth));
  1651       frame->AddStateBits(NS_FRAME_IS_BIDI);
  1652       while (frame) {
  1653         nsIFrame* prev = frame->GetPrevContinuation();
  1654         if (prev) {
  1655           MakeContinuationFluid(prev, frame);
  1656           frame = frame->GetParent();
  1657         } else {
  1658           break;
  1664   // Make sure that the last continuation we made fluid does not itself have a
  1665   // fluid continuation (this can happen when re-resolving after dynamic changes
  1666   // to content)
  1667   nsIFrame* lastFrame = aBpd->FrameAt(aLastIndex);
  1668   MakeContinuationsNonFluidUpParentChain(lastFrame, lastFrame->GetNextInFlow());
  1671 nsresult
  1672 nsBidiPresUtils::FormatUnicodeText(nsPresContext*  aPresContext,
  1673                                    char16_t*       aText,
  1674                                    int32_t&         aTextLength,
  1675                                    nsCharType       aCharType,
  1676                                    bool             aIsOddLevel)
  1678   nsresult rv = NS_OK;
  1679   // ahmed 
  1680   //adjusted for correct numeral shaping  
  1681   uint32_t bidiOptions = aPresContext->GetBidi();
  1682   switch (GET_BIDI_OPTION_NUMERAL(bidiOptions)) {
  1684     case IBMBIDI_NUMERAL_HINDI:
  1685       HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
  1686       break;
  1688     case IBMBIDI_NUMERAL_ARABIC:
  1689       HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
  1690       break;
  1692     case IBMBIDI_NUMERAL_PERSIAN:
  1693       HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_PERSIAN);
  1694       break;
  1696     case IBMBIDI_NUMERAL_REGULAR:
  1698       switch (aCharType) {
  1700         case eCharType_EuropeanNumber:
  1701           HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
  1702           break;
  1704         case eCharType_ArabicNumber:
  1705           HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
  1706           break;
  1708         default:
  1709           break;
  1711       break;
  1713     case IBMBIDI_NUMERAL_HINDICONTEXT:
  1714       if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions)==IBMBIDI_TEXTDIRECTION_RTL) && (IS_ARABIC_DIGIT (aText[0])) ) || (eCharType_ArabicNumber == aCharType) )
  1715         HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_HINDI);
  1716       else if (eCharType_EuropeanNumber == aCharType)
  1717         HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
  1718       break;
  1720     case IBMBIDI_NUMERAL_PERSIANCONTEXT:
  1721       if ( ( (GET_BIDI_OPTION_DIRECTION(bidiOptions)==IBMBIDI_TEXTDIRECTION_RTL) && (IS_ARABIC_DIGIT (aText[0])) ) || (eCharType_ArabicNumber == aCharType) )
  1722         HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_PERSIAN);
  1723       else if (eCharType_EuropeanNumber == aCharType)
  1724         HandleNumbers(aText,aTextLength,IBMBIDI_NUMERAL_ARABIC);
  1725       break;
  1727     case IBMBIDI_NUMERAL_NOMINAL:
  1728     default:
  1729       break;
  1732   StripBidiControlCharacters(aText, aTextLength);
  1733   return rv;
  1736 void
  1737 nsBidiPresUtils::StripBidiControlCharacters(char16_t* aText,
  1738                                             int32_t&   aTextLength)
  1740   if ( (nullptr == aText) || (aTextLength < 1) ) {
  1741     return;
  1744   int32_t stripLen = 0;
  1746   for (int32_t i = 0; i < aTextLength; i++) {
  1747     // XXX: This silently ignores surrogate characters.
  1748     //      As of Unicode 4.0, all Bidi control characters are within the BMP.
  1749     if (IsBidiControl((uint32_t)aText[i])) {
  1750       ++stripLen;
  1752     else {
  1753       aText[i - stripLen] = aText[i];
  1756   aTextLength -= stripLen;
  1759 #if 0 // XXX: for the future use ???
  1760 void
  1761 RemoveDiacritics(char16_t* aText,
  1762                  int32_t&   aTextLength)
  1764   if (aText && (aTextLength > 0) ) {
  1765     int32_t offset = 0;
  1767     for (int32_t i = 0; i < aTextLength && aText[i]; i++) {
  1768       if (IS_BIDI_DIACRITIC(aText[i]) ) {
  1769         ++offset;
  1770         continue;
  1772       aText[i - offset] = aText[i];
  1774     aTextLength = i - offset;
  1775     aText[aTextLength] = 0;
  1778 #endif
  1780 void
  1781 nsBidiPresUtils::CalculateCharType(nsBidi* aBidiEngine,
  1782                                    const char16_t* aText,
  1783                                    int32_t& aOffset,
  1784                                    int32_t  aCharTypeLimit,
  1785                                    int32_t& aRunLimit,
  1786                                    int32_t& aRunLength,
  1787                                    int32_t& aRunCount,
  1788                                    uint8_t& aCharType,
  1789                                    uint8_t& aPrevCharType)
  1792   bool       strongTypeFound = false;
  1793   int32_t    offset;
  1794   nsCharType charType;
  1796   aCharType = eCharType_OtherNeutral;
  1798   for (offset = aOffset; offset < aCharTypeLimit; offset++) {
  1799     // Make sure we give RTL chartype to all characters that would be classified
  1800     // as Right-To-Left by a bidi platform.
  1801     // (May differ from the UnicodeData, eg we set RTL chartype to some NSMs.)
  1802     if (IS_HEBREW_CHAR(aText[offset]) ) {
  1803       charType = eCharType_RightToLeft;
  1805     else if (IS_ARABIC_ALPHABETIC(aText[offset]) ) {
  1806       charType = eCharType_RightToLeftArabic;
  1808     else {
  1809       aBidiEngine->GetCharTypeAt(offset, &charType);
  1812     if (!CHARTYPE_IS_WEAK(charType) ) {
  1814       if (strongTypeFound
  1815           && (charType != aPrevCharType)
  1816           && (CHARTYPE_IS_RTL(charType) || CHARTYPE_IS_RTL(aPrevCharType) ) ) {
  1817         // Stop at this point to ensure uni-directionality of the text
  1818         // (from platform's point of view).
  1819         // Also, don't mix Arabic and Hebrew content (since platform may
  1820         // provide BIDI support to one of them only).
  1821         aRunLength = offset - aOffset;
  1822         aRunLimit = offset;
  1823         ++aRunCount;
  1824         break;
  1827       if ( (eCharType_RightToLeftArabic == aPrevCharType
  1828             || eCharType_ArabicNumber == aPrevCharType)
  1829           && eCharType_EuropeanNumber == charType) {
  1830         charType = eCharType_ArabicNumber;
  1833       // Set PrevCharType to the last strong type in this frame
  1834       // (for correct numeric shaping)
  1835       aPrevCharType = charType;
  1837       strongTypeFound = true;
  1838       aCharType = charType;
  1841   aOffset = offset;
  1844 nsresult nsBidiPresUtils::ProcessText(const char16_t*       aText,
  1845                                       int32_t                aLength,
  1846                                       nsBidiLevel            aBaseLevel,
  1847                                       nsPresContext*         aPresContext,
  1848                                       BidiProcessor&         aprocessor,
  1849                                       Mode                   aMode,
  1850                                       nsBidiPositionResolve* aPosResolve,
  1851                                       int32_t                aPosResolveCount,
  1852                                       nscoord*               aWidth,
  1853                                       nsBidi*                aBidiEngine)
  1855   NS_ASSERTION((aPosResolve == nullptr) != (aPosResolveCount > 0), "Incorrect aPosResolve / aPosResolveCount arguments");
  1857   int32_t runCount;
  1859   nsAutoString textBuffer(aText, aLength);
  1861   nsresult rv = aBidiEngine->SetPara(aText, aLength, aBaseLevel, nullptr);
  1862   if (NS_FAILED(rv))
  1863     return rv;
  1865   rv = aBidiEngine->CountRuns(&runCount);
  1866   if (NS_FAILED(rv))
  1867     return rv;
  1869   nscoord xOffset = 0;
  1870   nscoord width, xEndRun = 0;
  1871   nscoord totalWidth = 0;
  1872   int32_t i, start, limit, length;
  1873   uint32_t visualStart = 0;
  1874   uint8_t charType;
  1875   uint8_t prevType = eCharType_LeftToRight;
  1876   nsBidiLevel level;
  1878   for(int nPosResolve=0; nPosResolve < aPosResolveCount; ++nPosResolve)
  1880     aPosResolve[nPosResolve].visualIndex = kNotFound;
  1881     aPosResolve[nPosResolve].visualLeftTwips = kNotFound;
  1882     aPosResolve[nPosResolve].visualWidth = kNotFound;
  1885   for (i = 0; i < runCount; i++) {
  1886     nsBidiDirection dir;
  1887     rv = aBidiEngine->GetVisualRun(i, &start, &length, &dir);
  1888     if (NS_FAILED(rv))
  1889       return rv;
  1891     rv = aBidiEngine->GetLogicalRun(start, &limit, &level);
  1892     if (NS_FAILED(rv))
  1893       return rv;
  1895     int32_t subRunLength = limit - start;
  1896     int32_t lineOffset = start;
  1897     int32_t typeLimit = std::min(limit, aLength);
  1898     int32_t subRunCount = 1;
  1899     int32_t subRunLimit = typeLimit;
  1901     /*
  1902      * If |level| is even, i.e. the direction of the run is left-to-right, we
  1903      * render the subruns from left to right and increment the x-coordinate
  1904      * |xOffset| by the width of each subrun after rendering.
  1906      * If |level| is odd, i.e. the direction of the run is right-to-left, we
  1907      * render the subruns from right to left. We begin by incrementing |xOffset| by
  1908      * the width of the whole run, and then decrement it by the width of each
  1909      * subrun before rendering. After rendering all the subruns, we restore the
  1910      * x-coordinate of the end of the run for the start of the next run.
  1911      */
  1913     if (level & 1) {
  1914       aprocessor.SetText(aText + start, subRunLength, nsBidiDirection(level & 1));
  1915       width = aprocessor.GetWidth();
  1916       xOffset += width;
  1917       xEndRun = xOffset;
  1920     while (subRunCount > 0) {
  1921       // CalculateCharType can increment subRunCount if the run
  1922       // contains mixed character types
  1923       CalculateCharType(aBidiEngine, aText, lineOffset, typeLimit, subRunLimit, subRunLength, subRunCount, charType, prevType);
  1925       nsAutoString runVisualText;
  1926       runVisualText.Assign(aText + start, subRunLength);
  1927       if (int32_t(runVisualText.Length()) < subRunLength)
  1928         return NS_ERROR_OUT_OF_MEMORY;
  1929       FormatUnicodeText(aPresContext, runVisualText.BeginWriting(), subRunLength,
  1930                         (nsCharType)charType, level & 1);
  1932       aprocessor.SetText(runVisualText.get(), subRunLength, nsBidiDirection(level & 1));
  1933       width = aprocessor.GetWidth();
  1934       totalWidth += width;
  1935       if (level & 1) {
  1936         xOffset -= width;
  1938       if (aMode == MODE_DRAW) {
  1939         aprocessor.DrawText(xOffset, width);
  1942       /*
  1943        * The caller may request to calculate the visual position of one
  1944        * or more characters.
  1945        */
  1946       for(int nPosResolve=0; nPosResolve<aPosResolveCount; ++nPosResolve)
  1948         nsBidiPositionResolve* posResolve = &aPosResolve[nPosResolve];
  1949         /*
  1950          * Did we already resolve this position's visual metric? If so, skip.
  1951          */
  1952         if (posResolve->visualLeftTwips != kNotFound)
  1953            continue;
  1955         /*
  1956          * First find out if the logical position is within this run.
  1957          */
  1958         if (start <= posResolve->logicalIndex &&
  1959             start + subRunLength > posResolve->logicalIndex) {
  1960           /*
  1961            * If this run is only one character long, we have an easy case:
  1962            * the visual position is the x-coord of the start of the run
  1963            * less the x-coord of the start of the whole text.
  1964            */
  1965           if (subRunLength == 1) {
  1966             posResolve->visualIndex = visualStart;
  1967             posResolve->visualLeftTwips = xOffset;
  1968             posResolve->visualWidth = width;
  1970           /*
  1971            * Otherwise, we need to measure the width of the run's part
  1972            * which is to the visual left of the index.
  1973            * In other words, the run is broken in two, around the logical index,
  1974            * and we measure the part which is visually left.
  1975            * If the run is right-to-left, this part will span from after the index
  1976            * up to the end of the run; if it is left-to-right, this part will span
  1977            * from the start of the run up to (and inclduing) the character before the index.
  1978            */
  1979           else {
  1980             /*
  1981              * Here is a description of how the width of the current character
  1982              * (posResolve->visualWidth) is calculated:
  1984              * LTR (current char: "P"):
  1985              *    S A M P L E          (logical index: 3, visual index: 3)
  1986              *    ^ (visualLeftPart)
  1987              *    ^ (visualRightSide)
  1988              *    visualLeftLength == 3
  1989              *    ^^^^^^ (subWidth)
  1990              *    ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
  1991              *          ^^ (posResolve->visualWidth)
  1993              * RTL (current char: "M"):
  1994              *    E L P M A S          (logical index: 2, visual index: 3)
  1995              *        ^ (visualLeftPart)
  1996              *          ^ (visualRightSide)
  1997              *    visualLeftLength == 3
  1998              *    ^^^^^^ (subWidth)
  1999              *    ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
  2000              *          ^^ (posResolve->visualWidth)
  2001              */
  2002             nscoord subWidth;
  2003             // The position in the text where this run's "left part" begins.
  2004             const char16_t* visualLeftPart, *visualRightSide;
  2005             if (level & 1) {
  2006               // One day, son, this could all be replaced with mBidiEngine.GetVisualIndex ...
  2007               posResolve->visualIndex = visualStart + (subRunLength - (posResolve->logicalIndex + 1 - start));
  2008               // Skipping to the "left part".
  2009               visualLeftPart = aText + posResolve->logicalIndex + 1;
  2010               // Skipping to the right side of the current character
  2011               visualRightSide = visualLeftPart - 1;
  2013             else {
  2014               posResolve->visualIndex = visualStart + (posResolve->logicalIndex - start);
  2015               // Skipping to the "left part".
  2016               visualLeftPart = aText + start;
  2017               // In LTR mode this is the same as visualLeftPart
  2018               visualRightSide = visualLeftPart;
  2020             // The delta between the start of the run and the left part's end.
  2021             int32_t visualLeftLength = posResolve->visualIndex - visualStart;
  2022             aprocessor.SetText(visualLeftPart, visualLeftLength, nsBidiDirection(level & 1));
  2023             subWidth = aprocessor.GetWidth();
  2024             aprocessor.SetText(visualRightSide, visualLeftLength + 1, nsBidiDirection(level & 1));
  2025             posResolve->visualLeftTwips = xOffset + subWidth;
  2026             posResolve->visualWidth = aprocessor.GetWidth() - subWidth;
  2031       if (!(level & 1)) {
  2032         xOffset += width;
  2035       --subRunCount;
  2036       start = lineOffset;
  2037       subRunLimit = typeLimit;
  2038       subRunLength = typeLimit - lineOffset;
  2039     } // while
  2040     if (level & 1) {
  2041       xOffset = xEndRun;
  2044     visualStart += length;
  2045   } // for
  2047   if (aWidth) {
  2048     *aWidth = totalWidth;
  2050   return NS_OK;
  2053 class MOZ_STACK_CLASS nsIRenderingContextBidiProcessor : public nsBidiPresUtils::BidiProcessor {
  2054 public:
  2055   nsIRenderingContextBidiProcessor(nsRenderingContext* aCtx,
  2056                                    nsRenderingContext* aTextRunConstructionContext,
  2057                                    const nsPoint&       aPt)
  2058     : mCtx(aCtx), mTextRunConstructionContext(aTextRunConstructionContext), mPt(aPt) { }
  2060   ~nsIRenderingContextBidiProcessor()
  2062     mCtx->SetTextRunRTL(false);
  2065   virtual void SetText(const char16_t* aText,
  2066                        int32_t          aLength,
  2067                        nsBidiDirection  aDirection) MOZ_OVERRIDE
  2069     mTextRunConstructionContext->SetTextRunRTL(aDirection==NSBIDI_RTL);
  2070     mText = aText;
  2071     mLength = aLength;
  2074   virtual nscoord GetWidth() MOZ_OVERRIDE
  2076     return mTextRunConstructionContext->GetWidth(mText, mLength);
  2079   virtual void DrawText(nscoord aXOffset,
  2080                         nscoord) MOZ_OVERRIDE
  2082     mCtx->FontMetrics()->DrawString(mText, mLength, mPt.x + aXOffset, mPt.y,
  2083                                     mCtx, mTextRunConstructionContext);
  2086 private:
  2087   nsRenderingContext* mCtx;
  2088   nsRenderingContext* mTextRunConstructionContext;
  2089   nsPoint mPt;
  2090   const char16_t* mText;
  2091   int32_t mLength;
  2092 };
  2094 nsresult nsBidiPresUtils::ProcessTextForRenderingContext(const char16_t*       aText,
  2095                                                          int32_t                aLength,
  2096                                                          nsBidiLevel            aBaseLevel,
  2097                                                          nsPresContext*         aPresContext,
  2098                                                          nsRenderingContext&   aRenderingContext,
  2099                                                          nsRenderingContext&   aTextRunConstructionContext,
  2100                                                          Mode                   aMode,
  2101                                                          nscoord                aX,
  2102                                                          nscoord                aY,
  2103                                                          nsBidiPositionResolve* aPosResolve,
  2104                                                          int32_t                aPosResolveCount,
  2105                                                          nscoord*               aWidth)
  2107   nsIRenderingContextBidiProcessor processor(&aRenderingContext, &aTextRunConstructionContext, nsPoint(aX, aY));
  2108   nsBidi bidiEngine;
  2109   return ProcessText(aText, aLength, aBaseLevel, aPresContext, processor,
  2110                      aMode, aPosResolve, aPosResolveCount, aWidth, &bidiEngine);
  2113 /* static */
  2114 void nsBidiPresUtils::WriteReverse(const char16_t* aSrc,
  2115                                    uint32_t aSrcLength,
  2116                                    char16_t* aDest)
  2118   char16_t* dest = aDest + aSrcLength;
  2119   mozilla::unicode::ClusterIterator iter(aSrc, aSrcLength);
  2121   while (!iter.AtEnd()) {
  2122     iter.Next();
  2123     for (const char16_t *cp = iter; cp > aSrc; ) {
  2124       // Here we rely on the fact that there are no non-BMP mirrored pairs
  2125       // currently in Unicode, so we don't need to look for surrogates
  2126       *--dest = mozilla::unicode::GetMirroredChar(*--cp);
  2128     aSrc = iter;
  2131   NS_ASSERTION(dest == aDest, "Whole string not copied");
  2134 /* static */
  2135 bool nsBidiPresUtils::WriteLogicalToVisual(const char16_t* aSrc,
  2136                                            uint32_t aSrcLength,
  2137                                            char16_t* aDest,
  2138                                            nsBidiLevel aBaseDirection,
  2139                                            nsBidi* aBidiEngine)
  2141   const char16_t* src = aSrc;
  2142   nsresult rv = aBidiEngine->SetPara(src, aSrcLength, aBaseDirection, nullptr);
  2143   if (NS_FAILED(rv)) {
  2144     return false;
  2147   nsBidiDirection dir;
  2148   rv = aBidiEngine->GetDirection(&dir);
  2149   // NSBIDI_LTR returned from GetDirection means the whole text is LTR
  2150   if (NS_FAILED(rv) || dir == NSBIDI_LTR) {
  2151     return false;
  2154   int32_t runCount;
  2155   rv = aBidiEngine->CountRuns(&runCount);
  2156   if (NS_FAILED(rv)) {
  2157     return false;
  2160   int32_t runIndex, start, length;
  2161   char16_t* dest = aDest;
  2163   for (runIndex = 0; runIndex < runCount; ++runIndex) {
  2164     rv = aBidiEngine->GetVisualRun(runIndex, &start, &length, &dir);
  2165     if (NS_FAILED(rv)) {
  2166       return false;
  2169     src = aSrc + start;
  2171     if (dir == NSBIDI_RTL) {
  2172       WriteReverse(src, length, dest);
  2173       dest += length;
  2174     } else {
  2175       do {
  2176         NS_ASSERTION(src >= aSrc && src < aSrc + aSrcLength,
  2177                      "logical index out of range");
  2178         NS_ASSERTION(dest < aDest + aSrcLength, "visual index out of range");
  2179         *(dest++) = *(src++);
  2180       } while (--length);
  2184   NS_ASSERTION(static_cast<uint32_t>(dest - aDest) == aSrcLength,
  2185                "whole string not copied");
  2186   return true;
  2189 void nsBidiPresUtils::CopyLogicalToVisual(const nsAString& aSource,
  2190                                           nsAString& aDest,
  2191                                           nsBidiLevel aBaseDirection,
  2192                                           bool aOverride)
  2194   aDest.SetLength(0);
  2195   uint32_t srcLength = aSource.Length();
  2196   if (srcLength == 0)
  2197     return;
  2198   if (!aDest.SetLength(srcLength, fallible_t())) {
  2199     return;
  2201   nsAString::const_iterator fromBegin, fromEnd;
  2202   nsAString::iterator toBegin;
  2203   aSource.BeginReading(fromBegin);
  2204   aSource.EndReading(fromEnd);
  2205   aDest.BeginWriting(toBegin);
  2207   if (aOverride) {
  2208     if (aBaseDirection == NSBIDI_RTL) {
  2209       // no need to use the converter -- just copy the string in reverse order
  2210       WriteReverse(fromBegin.get(), srcLength, toBegin.get());
  2211     } else {
  2212       // if aOverride && aBaseDirection == NSBIDI_LTR, fall through to the
  2213       // simple copy
  2214       aDest.SetLength(0);
  2216   } else {
  2217     nsBidi bidiEngine;
  2218     if (!WriteLogicalToVisual(fromBegin.get(), srcLength, toBegin.get(),
  2219                              aBaseDirection, &bidiEngine)) {
  2220       aDest.SetLength(0);
  2224   if (aDest.IsEmpty()) {
  2225     // Either there was an error or the source is unidirectional
  2226     //  left-to-right. In either case, just copy source to dest.
  2227     CopyUnicodeTo(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
  2228                   aDest);
  2232 /* static */
  2233 nsBidiLevel
  2234 nsBidiPresUtils::BidiLevelFromStyle(nsStyleContext* aStyleContext)
  2236   if (aStyleContext->StyleTextReset()->mUnicodeBidi &
  2237       NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
  2238     return NSBIDI_DEFAULT_LTR;
  2241   if (aStyleContext->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
  2242     return NSBIDI_RTL;
  2245   return NSBIDI_LTR;

mercurial