Bug 1100071 patch 2: add macros for common tests whether bidi level is odd and whether two bidi levels have the same parity, r=dholbert
authorSimon Montagu <smontagu@smontagu.org>
Thu, 20 Nov 2014 12:45:22 +0200
changeset 216629 53fb431ea9433f4c31517760502114dcea6fbc23
parent 216628 086c21f9b16fe8a497a64eb7b0c6813e56070a07
child 216630 1bee60dc14ec7a2563de9a8c9e412143948e5cdf
push idunknown
push userunknown
push dateunknown
reviewersdholbert
bugs1100071
milestone36.0a1
Bug 1100071 patch 2: add macros for common tests whether bidi level is odd and whether two bidi levels have the same parity, r=dholbert
dom/canvas/CanvasRenderingContext2D.cpp
intl/unicharutil/util/nsBidiUtils.h
layout/base/nsBidiPresUtils.cpp
layout/base/nsBidiPresUtils.h
layout/base/nsCaret.cpp
layout/generic/WritingModes.h
layout/generic/nsFrame.cpp
layout/generic/nsFrameList.cpp
layout/generic/nsSelection.cpp
layout/generic/nsTextFrame.cpp
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -3248,17 +3248,17 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
 {
   typedef CanvasRenderingContext2D::ContextState ContextState;
 
   virtual void SetText(const char16_t* text, int32_t length, nsBidiDirection direction)
   {
     mFontgrp->UpdateUserFonts(); // ensure user font generation is current
     // adjust flags for current direction run
     uint32_t flags = mTextRunFlags;
-    if (direction & 1) {
+    if (direction == NSBIDI_RTL) {
       flags |= gfxTextRunFactory::TEXT_IS_RTL;
     } else {
       flags &= ~gfxTextRunFactory::TEXT_IS_RTL;
     }
     mTextRun = mFontgrp->MakeTextRun(text,
                                      length,
                                      mThebes,
                                      mAppUnitsPerDevPixel,
--- a/intl/unicharutil/util/nsBidiUtils.h
+++ b/intl/unicharutil/util/nsBidiUtils.h
@@ -41,16 +41,35 @@ enum nsCharType   {
 };
 
 /**
  * This specifies the language directional property of a character set.
  */
 typedef enum nsCharType nsCharType;
 
 /**
+ * Find the direction of an embedding level or paragraph level set by
+ * the Unicode Bidi Algorithm. (Even levels are left-to-right, odd
+ * levels right-to-left.
+ */
+#define IS_LEVEL_RTL(level) (((level) & 1) == 1)
+
+/**
+ * Check whether two bidi levels have the same parity and thus the same
+ * directionality
+ */
+#define IS_SAME_DIRECTION(level1, level2) (((level1 ^ level2) & 1) == 0)
+
+/**
+ * Convert from nsBidiLevel to nsBidiDirection
+ */
+#define DIRECTION_FROM_LEVEL(level) ((IS_LEVEL_RTL(level)) \
+   ? NSBIDI_RTL : NSBIDI_LTR)
+
+/**
  * definitions of bidirection character types by category
  */
 
 #define CHARTYPE_IS_RTL(val) ( ( (val) == eCharType_RightToLeft) || ( (val) == eCharType_RightToLeftArabic) )
 
 #define CHARTYPE_IS_WEAK(val) ( ( (val) == eCharType_EuropeanNumberSeparator)    \
                            || ( (val) == eCharType_EuropeanNumberTerminator) \
                            || ( ( (val) > eCharType_ArabicNumber) && ( (val) != eCharType_RightToLeftArabic) ) )
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -356,17 +356,17 @@ struct BidiLineData {
 
     for (nsIFrame* frame = aFirstFrameOnLine;
          frame && aNumFramesOnLine--;
          frame = frame->GetNextSibling()) {
       AppendFrame(frame);
       nsBidiLevel level = nsBidiPresUtils::GetFrameEmbeddingLevel(frame);
       mLevels.AppendElement(level);
       mIndexMap.AppendElement(0);
-      if (level & 1) {
+      if (IS_LEVEL_RTL(level)) {
         hasRTLFrames = true;
       }
     }
 
     // Reorder the line
     nsBidi::ReorderVisual(mLevels.Elements(), FrameCount(),
                           mIndexMap.Elements());
 
@@ -1534,17 +1534,17 @@ nsBidiPresUtils::RepositionInlineFrames(
   } else {
     index = count - 1;
     step = -1;
     limit = -1;
   }
   for (; index != limit; index += step) {
     frame = aBld->VisualFrameAt(index);
     RepositionFrame(frame,
-                    !(aBld->mLevels[aBld->mIndexMap[index]] & 1),
+                    !(IS_LEVEL_RTL(aBld->mLevels[aBld->mIndexMap[index]])),
                     start,
                     &continuationStates,
                     aLineWM,
                     aLineWidth);
   }
 }
 
 bool
@@ -1670,19 +1670,19 @@ nsBidiPresUtils::RemoveBidiContinuation(
   // to content)
   nsIFrame* lastFrame = aBpd->FrameAt(aLastIndex);
   MakeContinuationsNonFluidUpParentChain(lastFrame, lastFrame->GetNextInFlow());
 }
 
 nsresult
 nsBidiPresUtils::FormatUnicodeText(nsPresContext*  aPresContext,
                                    char16_t*       aText,
-                                   int32_t&         aTextLength,
-                                   nsCharType       aCharType,
-                                   bool             aIsOddLevel)
+                                   int32_t&        aTextLength,
+                                   nsCharType      aCharType,
+                                   nsBidiDirection aDir)
 {
   nsresult rv = NS_OK;
   // ahmed 
   //adjusted for correct numeral shaping  
   uint32_t bidiOptions = aPresContext->GetBidi();
   switch (GET_BIDI_OPTION_NUMERAL(bidiOptions)) {
 
     case IBMBIDI_NUMERAL_HINDI:
@@ -1872,35 +1872,36 @@ nsresult nsBidiPresUtils::ProcessText(co
 
   nscoord xOffset = 0;
   nscoord width, xEndRun = 0;
   nscoord totalWidth = 0;
   int32_t i, start, limit, length;
   uint32_t visualStart = 0;
   uint8_t charType;
   uint8_t prevType = eCharType_LeftToRight;
-  nsBidiLevel level;
-      
+
   for(int nPosResolve=0; nPosResolve < aPosResolveCount; ++nPosResolve)
   {
     aPosResolve[nPosResolve].visualIndex = kNotFound;
     aPosResolve[nPosResolve].visualLeftTwips = kNotFound;
     aPosResolve[nPosResolve].visualWidth = kNotFound;
   }
 
   for (i = 0; i < runCount; i++) {
     nsBidiDirection dir;
     rv = aBidiEngine->GetVisualRun(i, &start, &length, &dir);
     if (NS_FAILED(rv))
       return rv;
 
+    nsBidiLevel level;
     rv = aBidiEngine->GetLogicalRun(start, &limit, &level);
     if (NS_FAILED(rv))
       return rv;
 
+    dir = DIRECTION_FROM_LEVEL(level);
     int32_t subRunLength = limit - start;
     int32_t lineOffset = start;
     int32_t typeLimit = std::min(limit, aLength);
     int32_t subRunCount = 1;
     int32_t subRunLimit = typeLimit;
 
     /*
      * If |level| is even, i.e. the direction of the run is left-to-right, we
@@ -1909,39 +1910,39 @@ nsresult nsBidiPresUtils::ProcessText(co
      *
      * If |level| is odd, i.e. the direction of the run is right-to-left, we
      * render the subruns from right to left. We begin by incrementing |xOffset| by
      * the width of the whole run, and then decrement it by the width of each
      * subrun before rendering. After rendering all the subruns, we restore the
      * x-coordinate of the end of the run for the start of the next run.
      */
 
-    if (level & 1) {
-      aprocessor.SetText(aText + start, subRunLength, nsBidiDirection(level & 1));
+    if (dir == NSBIDI_RTL) {
+      aprocessor.SetText(aText + start, subRunLength, dir);
       width = aprocessor.GetWidth();
       xOffset += width;
       xEndRun = xOffset;
     }
 
     while (subRunCount > 0) {
       // CalculateCharType can increment subRunCount if the run
       // contains mixed character types
       CalculateCharType(aBidiEngine, aText, lineOffset, typeLimit, subRunLimit, subRunLength, subRunCount, charType, prevType);
-      
+
       nsAutoString runVisualText;
       runVisualText.Assign(aText + start, subRunLength);
       if (int32_t(runVisualText.Length()) < subRunLength)
         return NS_ERROR_OUT_OF_MEMORY;
-      FormatUnicodeText(aPresContext, runVisualText.BeginWriting(), subRunLength,
-                        (nsCharType)charType, level & 1);
+      FormatUnicodeText(aPresContext, runVisualText.BeginWriting(),
+                        subRunLength, (nsCharType)charType, dir);
 
-      aprocessor.SetText(runVisualText.get(), subRunLength, nsBidiDirection(level & 1));
+      aprocessor.SetText(runVisualText.get(), subRunLength, dir);
       width = aprocessor.GetWidth();
       totalWidth += width;
-      if (level & 1) {
+      if (dir == NSBIDI_RTL) {
         xOffset -= width;
       }
       if (aMode == MODE_DRAW) {
         aprocessor.DrawText(xOffset, width);
       }
 
       /*
        * The caller may request to calculate the visual position of one
@@ -2002,55 +2003,55 @@ nsresult nsBidiPresUtils::ProcessText(co
              *    ^^^^^^ (subWidth)
              *    ^^^^^^^^ (aprocessor.GetWidth() -- with visualRightSide)
              *          ^^ (posResolve->visualWidth)
              */
             nscoord subWidth;
             // The position in the text where this run's "left part" begins.
             const char16_t* visualLeftPart;
             const char16_t* visualRightSide;
-            if (level & 1) {
+            if (dir == NSBIDI_RTL) {
               // One day, son, this could all be replaced with mBidiEngine.GetVisualIndex ...
               posResolve->visualIndex = visualStart + (subRunLength - (posResolve->logicalIndex + 1 - start));
               // Skipping to the "left part".
               visualLeftPart = aText + posResolve->logicalIndex + 1;
               // Skipping to the right side of the current character
               visualRightSide = visualLeftPart - 1;
             }
             else {
               posResolve->visualIndex = visualStart + (posResolve->logicalIndex - start);
               // Skipping to the "left part".
               visualLeftPart = aText + start;
               // In LTR mode this is the same as visualLeftPart
               visualRightSide = visualLeftPart;
             }
             // The delta between the start of the run and the left part's end.
             int32_t visualLeftLength = posResolve->visualIndex - visualStart;
-            aprocessor.SetText(visualLeftPart, visualLeftLength, nsBidiDirection(level & 1));
+            aprocessor.SetText(visualLeftPart, visualLeftLength, dir);
             subWidth = aprocessor.GetWidth();
-            aprocessor.SetText(visualRightSide, visualLeftLength + 1, nsBidiDirection(level & 1));
+            aprocessor.SetText(visualRightSide, visualLeftLength + 1, dir);
             posResolve->visualLeftTwips = xOffset + subWidth;
             posResolve->visualWidth = aprocessor.GetWidth() - subWidth;
           }
         }
       }
 
-      if (!(level & 1)) {
+      if (dir == NSBIDI_LTR) {
         xOffset += width;
       }
 
       --subRunCount;
       start = lineOffset;
       subRunLimit = typeLimit;
       subRunLength = typeLimit - lineOffset;
     } // while
-    if (level & 1) {
+    if (dir == NSBIDI_RTL) {
       xOffset = xEndRun;
     }
-    
+
     visualStart += length;
   } // for
 
   if (aWidth) {
     *aWidth = totalWidth;
   }
   return NS_OK;
 }
@@ -2070,18 +2071,18 @@ public:
   {}
 
   ~nsIRenderingContextBidiProcessor()
   {
     mFontMetrics->SetTextRunRTL(false);
   }
 
   virtual void SetText(const char16_t* aText,
-                       int32_t          aLength,
-                       nsBidiDirection  aDirection) MOZ_OVERRIDE
+                       int32_t         aLength,
+                       nsBidiDirection aDirection) MOZ_OVERRIDE
   {
     mFontMetrics->SetTextRunRTL(aDirection==NSBIDI_RTL);
     mText = aText;
     mLength = aLength;
   }
 
   virtual nscoord GetWidth() MOZ_OVERRIDE
   {
--- a/layout/base/nsBidiPresUtils.h
+++ b/layout/base/nsBidiPresUtils.h
@@ -102,25 +102,25 @@ public:
     virtual ~BidiProcessor() { }
 
     /**
      * Sets the current text with the given length and the given direction.
      *
      * @remark The reason that the function gives a string instead of an index
      *  is that ProcessText copies and modifies the string passed to it, so
      *  passing an index would be impossible.
-     * 
+     *
      * @param aText The string of text.
      * @param aLength The length of the string of text.
      * @param aDirection The direction of the text. The string will never have
      *  mixed direction.
      */
     virtual void SetText(const char16_t*   aText,
-                         int32_t            aLength,
-                         nsBidiDirection    aDirection) = 0;
+                         int32_t           aLength,
+                         nsBidiDirection   aDirection) = 0;
 
     /**
      * Returns the measured width of the text given in SetText. If SetText was
      * not called with valid parameters, the result of this call is undefined.
      * This call is guaranteed to only be called once between SetText calls.
      * Will be invoked before DrawText.
      */
     virtual nscoord GetWidth() = 0;
@@ -166,21 +166,21 @@ public:
 
   /**
    * Format Unicode text, taking into account bidi capabilities
    * of the platform. The formatting includes: reordering, Arabic shaping,
    * symmetric and numeric swapping, removing control characters.
    *
    * @lina 06/18/2000 
    */
-  static nsresult FormatUnicodeText(nsPresContext* aPresContext,
-                                    char16_t*      aText,
+  static nsresult FormatUnicodeText(nsPresContext*  aPresContext,
+                                    char16_t*       aText,
                                     int32_t&        aTextLength,
                                     nsCharType      aCharType,
-                                    bool            aIsOddLevel);
+                                    nsBidiDirection aDir);
 
   /**
    * Reorder plain text using the Unicode Bidi algorithm and send it to
    * a rendering context for rendering.
    *
    * @param[in] aText  the string to be rendered (in logical order)
    * @param aLength the number of characters in the string
    * @param aBaseLevel the base embedding level of the string
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -673,18 +673,20 @@ nsCaret::GetCaretFrameForNodeOffset(nsFr
         levelBefore = levels.mLevelBefore;
         levelAfter = levels.mLevelAfter;
 
         if ((levelBefore != levelAfter) || (aBidiLevel != levelBefore))
         {
           aBidiLevel = std::max(aBidiLevel, std::min(levelBefore, levelAfter));                                  // rule c3
           aBidiLevel = std::min(aBidiLevel, std::max(levelBefore, levelAfter));                                  // rule c4
           if (aBidiLevel == levelBefore                                                                      // rule c1
-              || (aBidiLevel > levelBefore && aBidiLevel < levelAfter && !((aBidiLevel ^ levelBefore) & 1))    // rule c5
-              || (aBidiLevel < levelBefore && aBidiLevel > levelAfter && !((aBidiLevel ^ levelBefore) & 1)))  // rule c9
+              || (aBidiLevel > levelBefore && aBidiLevel < levelAfter &&
+                  IS_SAME_DIRECTION(aBidiLevel, levelBefore))   // rule c5
+              || (aBidiLevel < levelBefore && aBidiLevel > levelAfter &&
+                  IS_SAME_DIRECTION(aBidiLevel, levelBefore)))  // rule c9
           {
             if (theFrame != frameBefore)
             {
               if (frameBefore) // if there is a frameBefore, move into it
               {
                 theFrame = frameBefore;
                 theFrame->GetOffsets(start, end);
                 theFrameOffset = end;
@@ -703,18 +705,20 @@ nsCaret::GetCaretFrameForNodeOffset(nsFr
                     theFrame = pos.mResultFrame;
                     theFrameOffset = pos.mContentOffset;
                   }
                 }
               }
             }
           }
           else if (aBidiLevel == levelAfter                                                                     // rule c2
-                   || (aBidiLevel > levelBefore && aBidiLevel < levelAfter && !((aBidiLevel ^ levelAfter) & 1))   // rule c6
-                   || (aBidiLevel < levelBefore && aBidiLevel > levelAfter && !((aBidiLevel ^ levelAfter) & 1)))  // rule c10
+                   || (aBidiLevel > levelBefore && aBidiLevel < levelAfter &&
+                       IS_SAME_DIRECTION(aBidiLevel, levelAfter))   // rule c6
+                   || (aBidiLevel < levelBefore && aBidiLevel > levelAfter &&
+                       IS_SAME_DIRECTION(aBidiLevel, levelAfter)))  // rule c10
           {
             if (theFrame != frameAfter)
             {
               if (frameAfter)
               {
                 // if there is a frameAfter, move into it
                 theFrame = frameAfter;
                 theFrame->GetOffsets(start, end);
@@ -734,43 +738,43 @@ nsCaret::GetCaretFrameForNodeOffset(nsFr
                     theFrame = pos.mResultFrame;
                     theFrameOffset = pos.mContentOffset;
                   }
                 }
               }
             }
           }
           else if (aBidiLevel > levelBefore && aBidiLevel < levelAfter  // rule c7/8
-                   && !((levelBefore ^ levelAfter) & 1)                 // before and after have the same parity
-                   && ((aBidiLevel ^ levelAfter) & 1))                  // caret has different parity
+                   && IS_SAME_DIRECTION(levelBefore, levelAfter)        // before and after have the same parity
+                   && !IS_SAME_DIRECTION(aBidiLevel, levelAfter))       // caret has different parity
           {
             if (NS_SUCCEEDED(aFrameSelection->GetFrameFromLevel(frameAfter, eDirNext, aBidiLevel, &theFrame)))
             {
               theFrame->GetOffsets(start, end);
               levelAfter = NS_GET_EMBEDDING_LEVEL(theFrame);
-              if (aBidiLevel & 1) // c8: caret to the right of the rightmost character
-                theFrameOffset = (levelAfter & 1) ? start : end;
+              if (IS_LEVEL_RTL(aBidiLevel)) // c8: caret to the right of the rightmost character
+                theFrameOffset = IS_LEVEL_RTL(levelAfter) ? start : end;
               else               // c7: caret to the left of the leftmost character
-                theFrameOffset = (levelAfter & 1) ? end : start;
+                theFrameOffset = IS_LEVEL_RTL(levelAfter) ? end : start;
             }
           }
           else if (aBidiLevel < levelBefore && aBidiLevel > levelAfter  // rule c11/12
-                   && !((levelBefore ^ levelAfter) & 1)                 // before and after have the same parity
-                   && ((aBidiLevel ^ levelAfter) & 1))                  // caret has different parity
+                   && IS_SAME_DIRECTION(levelBefore, levelAfter)        // before and after have the same parity
+                   && !IS_SAME_DIRECTION(aBidiLevel, levelAfter))       // caret has different parity
           {
             if (NS_SUCCEEDED(aFrameSelection->GetFrameFromLevel(frameBefore, eDirPrevious, aBidiLevel, &theFrame)))
             {
               theFrame->GetOffsets(start, end);
               levelBefore = NS_GET_EMBEDDING_LEVEL(theFrame);
-              if (aBidiLevel & 1) // c12: caret to the left of the leftmost character
-                theFrameOffset = (levelBefore & 1) ? end : start;
+              if (IS_LEVEL_RTL(aBidiLevel)) // c12: caret to the left of the leftmost character
+                theFrameOffset = IS_LEVEL_RTL(levelBefore) ? end : start;
               else               // c11: caret to the right of the rightmost character
-                theFrameOffset = (levelBefore & 1) ? start : end;
+                theFrameOffset = IS_LEVEL_RTL(levelBefore) ? start : end;
             }
-          }   
+          }
         }
       }
     }
   }
 
   *aReturnFrame = theFrame;
   *aReturnOffset = theFrameOffset;
   return NS_OK;
--- a/layout/generic/WritingModes.h
+++ b/layout/generic/WritingModes.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WritingModes_h_
 #define WritingModes_h_
 
 #include "nsRect.h"
 #include "nsStyleContext.h"
+#include "nsBidiUtils.h"
 
 // If WRITING_MODE_VERTICAL_ENABLED is defined, we will attempt to support
 // the vertical writing-mode values; if it is not defined, then
 // WritingMode.IsVertical() will be hard-coded to return false, allowing
 // many conditional branches to be optimized away while we're in the process
 // of transitioning layout to use writing-mode and logical directions, but
 // not yet ready to ship vertical support.
 
@@ -304,21 +305,21 @@ public:
   }
 
   // For unicode-bidi: plaintext, reset the direction of the writing mode from
   // the bidi paragraph level of the content
 
   //XXX change uint8_t to UBiDiLevel after bug 924851
   void SetDirectionFromBidiLevel(uint8_t level)
   {
-    if (level & 1) {
-      // odd level, set RTL
+    if (IS_LEVEL_RTL(level)) {
+      // set RTL
       mWritingMode |= eBidiMask;
     } else {
-      // even level, set LTR
+      // set LTR
       mWritingMode &= ~eBidiMask;
     }
   }
 
   /**
    * Compare two WritingModes for equality.
    */
   bool operator==(const WritingMode& aOther) const
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -125,16 +125,21 @@ struct nsContentAndOffset
   int32_t mOffset;
 };
 
 // Some Misc #defines
 #define SELECTION_DEBUG        0
 #define FORCE_SELECTION_UPDATE 1
 #define CALC_DEBUG             0
 
+// This is faster than nsBidiPresUtils::IsFrameInParagraphDirection,
+// because it uses the frame pointer passed in without drilling down to
+// the leaf frame.
+#define REVERSED_DIRECTION_FRAME(frame) \
+  (!IS_SAME_DIRECTION(NS_GET_EMBEDDING_LEVEL(frame), NS_GET_BASE_LEVEL(frame)))
 
 #include "nsILineIterator.h"
 
 //non Hack prototypes
 #if 0
 static void RefreshContentFrames(nsPresContext* aPresContext, nsIContent * aStartContent, nsIContent * aEndContent);
 #endif
 
@@ -5905,17 +5910,17 @@ nsFrame::GetPointFromOffset(int32_t inOf
       // nsBidiPresUtils::ResolveParagraph (odd levels = right-to-left).
       // If the embedding level isn't set, just use the CSS direction
       // property.
       bool hasEmbeddingLevel;
       nsBidiLevel embeddingLevel =
         NS_PTR_TO_INT32(Properties().Get(nsIFrame::EmbeddingLevelProperty(),
                                          &hasEmbeddingLevel));
       bool isRTL = hasEmbeddingLevel
-        ? (embeddingLevel & 1) == 1
+        ? IS_LEVEL_RTL(embeddingLevel)
         : StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
       if ((!isRTL && inOffset > newOffset) ||
           (isRTL && inOffset <= newOffset)) {
         pt = contentRect.TopRight();
       }
     }
   }
   *outPoint = pt;
@@ -6349,18 +6354,17 @@ nsIFrame::PeekOffsetParagraph(nsPeekOffs
     }
   }
   return NS_OK;
 }
 
 // Determine movement direction relative to frame
 static bool IsMovingInFrameDirection(nsIFrame* frame, nsDirection aDirection, bool aVisual)
 {
-  bool isReverseDirection = aVisual ?
-    (NS_GET_EMBEDDING_LEVEL(frame) & 1) != (NS_GET_BASE_LEVEL(frame) & 1) : false;
+  bool isReverseDirection = aVisual && REVERSED_DIRECTION_FRAME(frame);
   return aDirection == (isReverseDirection ? eDirPrevious : eDirNext);
 }
 
 nsresult
 nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos)
 {
   if (!aPos)
     return NS_ERROR_NULL_POINTER;
@@ -6647,17 +6651,17 @@ nsIFrame::PeekOffset(nsPeekOffsetStruct*
         bool isReordered;
         nsIFrame *lastFrame;
         result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
         baseFrame = endOfLine ? lastFrame : firstFrame;
         if (baseFrame) {
           nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(baseFrame);
           // If the direction of the frame on the edge is opposite to that of the line,
           // we'll need to drill down to its opposite end, so reverse endOfLine.
-          if ((embeddingLevel & 1) == !lineIsRTL)
+          if (IS_LEVEL_RTL(embeddingLevel) == !lineIsRTL)
             endOfLine = !endOfLine;
         }
       } else {
         it->GetLine(thisLine, &firstFrame, &lineFrameCount, usedRect, &lineFlags);
 
         nsIFrame* frame = firstFrame;
         for (int32_t count = lineFrameCount; count;
              --count, frame = frame->GetNextSibling()) {
@@ -6870,18 +6874,18 @@ nsIFrame::GetFrameFromDirection(nsDirect
     nsIFrame *lastFrame;
     if (aVisual && presContext->BidiEnabled()) {
       bool lineIsRTL = it->GetDirection();
       bool isReordered;
       result = it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
       nsIFrame** framePtr = aDirection == eDirPrevious ? &firstFrame : &lastFrame;
       if (*framePtr) {
         nsBidiLevel embeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(*framePtr);
-        if ((((embeddingLevel & 1) && lineIsRTL) || (!(embeddingLevel & 1) && !lineIsRTL)) ==
-            (aDirection == eDirPrevious)) {
+        bool frameIsRTL = IS_LEVEL_RTL(embeddingLevel);
+        if ((frameIsRTL == lineIsRTL) == (aDirection == eDirPrevious)) {
           nsFrame::GetFirstLeaf(presContext, framePtr);
         } else {
           nsFrame::GetLastLeaf(presContext, framePtr);
         }
         atLineEdge = *framePtr == traversedFrame;
       } else {
         atLineEdge = true;
       }
@@ -6940,21 +6944,19 @@ nsIFrame::GetFrameFromDirection(nsDirect
         traversedFrame->GetContent()->IsRootOfNativeAnonymousSubtree())
       return NS_ERROR_FAILURE;
 
     traversedFrame->IsSelectable(&selectable, nullptr);
   } // while (!selectable)
 
   *aOutOffset = (aDirection == eDirNext) ? 0 : -1;
 
-  if (aVisual) {
-    uint8_t newLevel = NS_GET_EMBEDDING_LEVEL(traversedFrame);
-    uint8_t newBaseLevel = NS_GET_BASE_LEVEL(traversedFrame);
-    if ((newLevel & 1) != (newBaseLevel & 1)) // The new frame is reverse-direction, go to the other end
-      *aOutOffset = -1 - *aOutOffset;
+  if (aVisual && REVERSED_DIRECTION_FRAME(traversedFrame)) {
+    // The new frame is reverse-direction, go to the other end
+    *aOutOffset = -1 - *aOutOffset;
   }
   *aOutFrame = traversedFrame;
   return NS_OK;
 }
 
 nsView* nsIFrame::GetClosestView(nsPoint* aOffset) const
 {
   nsPoint offset(0,0);
--- a/layout/generic/nsFrameList.cpp
+++ b/layout/generic/nsFrameList.cpp
@@ -362,21 +362,21 @@ nsFrameList::GetPrevVisualFor(nsIFrame* 
       if (baseLevel == NSBIDI_LTR) {
         return nsBidiPresUtils::GetFrameToLeftOf(aFrame, mFirstChild, -1);
       } else { // RTL
         return nsBidiPresUtils::GetFrameToRightOf(aFrame, mFirstChild, -1);
       }
     } else {
       // Just get the next or prev sibling, depending on block and frame direction.
       nsBidiLevel frameEmbeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(mFirstChild);
-      if ((frameEmbeddingLevel & 1) == (baseLevel & 1)) {
+      if (IS_SAME_DIRECTION(frameEmbeddingLevel, baseLevel)) {
         return aFrame ? aFrame->GetPrevSibling() : LastChild();
       } else {
         return aFrame ? aFrame->GetNextSibling() : mFirstChild;
-      }    
+      }
     }
   }
 
   // Parent is a block frame, so use the LineIterator to find the previous visual 
   // sibling on this line, or the last one on the previous line.
 
   int32_t thisLine;
   if (aFrame) {
@@ -436,17 +436,17 @@ nsFrameList::GetNextVisualFor(nsIFrame* 
       if (baseLevel == NSBIDI_LTR) {
         return nsBidiPresUtils::GetFrameToRightOf(aFrame, mFirstChild, -1);
       } else { // RTL
         return nsBidiPresUtils::GetFrameToLeftOf(aFrame, mFirstChild, -1);
       }
     } else {
       // Just get the next or prev sibling, depending on block and frame direction.
       nsBidiLevel frameEmbeddingLevel = nsBidiPresUtils::GetFrameEmbeddingLevel(mFirstChild);
-      if ((frameEmbeddingLevel & 1) == (baseLevel & 1)) {
+      if (IS_SAME_DIRECTION(frameEmbeddingLevel, baseLevel)) {
         return aFrame ? aFrame->GetNextSibling() : mFirstChild;
       } else {
         return aFrame ? aFrame->GetPrevSibling() : LastChild();
       }
     }
   }
 
   // Parent is a block frame, so use the LineIterator to find the next visual 
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -871,21 +871,21 @@ nsFrameSelection::MoveCaret(uint32_t    
                          true, mLimiter != nullptr, true, aVisualMovement);
 
   nsBidiLevel baseLevel = nsBidiPresUtils::GetFrameBaseLevel(frame);
   
   CaretAssociateHint tHint(mHint); //temporary variable so we dont set mHint until it is necessary
   switch (aKeycode){
     case nsIDOMKeyEvent::DOM_VK_RIGHT : 
         InvalidateDesiredX();
-        pos.mDirection = (baseLevel & 1) ? eDirPrevious : eDirNext;
+        pos.mDirection = IS_LEVEL_RTL(baseLevel) ? eDirPrevious : eDirNext;
       break;
     case nsIDOMKeyEvent::DOM_VK_LEFT :
         InvalidateDesiredX();
-        pos.mDirection = (baseLevel & 1) ? eDirNext : eDirPrevious;
+        pos.mDirection = IS_LEVEL_RTL(baseLevel) ? eDirNext : eDirPrevious;
       break;
     case nsIDOMKeyEvent::DOM_VK_DELETE :
         InvalidateDesiredX();
         pos.mDirection = eDirNext;
       break;
     case nsIDOMKeyEvent::DOM_VK_BACK_SPACE : 
         InvalidateDesiredX();
         pos.mDirection = eDirPrevious;
@@ -5828,17 +5828,17 @@ Selection::Modify(const nsAString& aAlte
   // If the base level of the focused frame is odd, we may have to swap the
   // direction of the keycode.
   nsIFrame *frame;
   int32_t offset;
   rv = GetPrimaryFrameForFocusNode(&frame, &offset, visual);
   if (NS_SUCCEEDED(rv) && frame) {
     nsBidiLevel baseLevel = nsBidiPresUtils::GetFrameBaseLevel(frame);
 
-    if (baseLevel & 1) {
+    if (IS_LEVEL_RTL(baseLevel)) {
       if (!visual && keycode == nsIDOMKeyEvent::DOM_VK_RIGHT) {
         keycode = nsIDOMKeyEvent::DOM_VK_LEFT;
       }
       else if (!visual && keycode == nsIDOMKeyEvent::DOM_VK_LEFT) {
         keycode = nsIDOMKeyEvent::DOM_VK_RIGHT;
       }
       else if (visual && keycode == nsIDOMKeyEvent::DOM_VK_HOME) {
         keycode = nsIDOMKeyEvent::DOM_VK_END;
@@ -5903,32 +5903,32 @@ Selection::SelectionLanguageChange(bool 
     nsCOMPtr<nsIContent> focusContent = do_QueryInterface(GetFocusNode());
     nsPrevNextBidiLevels levels = mFrameSelection->
       GetPrevNextBidiLevels(focusContent, focusOffset, false);
       
     levelBefore = levels.mLevelBefore;
     levelAfter = levels.mLevelAfter;
   }
 
-  if ((levelBefore & 1) == (levelAfter & 1)) {
+  if (IS_SAME_DIRECTION(levelBefore, levelAfter)) {
     // if cursor is between two characters with the same orientation, changing the keyboard language
     //  must toggle the cursor level between the level of the character with the lowest level
     //  (if the new language corresponds to the orientation of that character) and this level plus 1
     //  (if the new language corresponds to the opposite orientation)
     if ((level != levelBefore) && (level != levelAfter))
       level = std::min(levelBefore, levelAfter);
-    if ((level & 1) == aLangRTL)
+    if (IS_LEVEL_RTL(level) == aLangRTL)
       mFrameSelection->SetCaretBidiLevel(level);
     else
       mFrameSelection->SetCaretBidiLevel(level + 1);
   }
   else {
     // if cursor is between characters with opposite orientations, changing the keyboard language must change
     //  the cursor level to that of the adjacent character with the orientation corresponding to the new language.
-    if ((levelBefore & 1) == aLangRTL)
+    if (IS_LEVEL_RTL(levelBefore) == aLangRTL)
       mFrameSelection->SetCaretBidiLevel(levelBefore);
     else
       mFrameSelection->SetCaretBidiLevel(levelAfter);
   }
   
   // The caret might have moved, so invalidate the desired X position
   // for future usages of up-arrow or down-arrow
   mFrameSelection->InvalidateDesiredX();
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -2054,17 +2054,17 @@ BuildTextRunsScanner::BuildTextRunForFra
   }
 
   if (textFlags & nsTextFrameUtils::TEXT_HAS_TAB) {
     textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING;
   }
   if (textFlags & nsTextFrameUtils::TEXT_HAS_SHY) {
     textFlags |= gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS;
   }
-  if (mBidiEnabled && (NS_GET_EMBEDDING_LEVEL(firstFrame) & 1)) {
+  if (mBidiEnabled && (IS_LEVEL_RTL(NS_GET_EMBEDDING_LEVEL(firstFrame)))) {
     textFlags |= gfxTextRunFactory::TEXT_IS_RTL;
   }
   if (mNextRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) {
     textFlags |= nsTextFrameUtils::TEXT_TRAILING_WHITESPACE;
   }
   if (mNextRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) {
     textFlags |= gfxTextRunFactory::TEXT_TRAILING_ARABICCHAR;
   }