Bug 1281099 part 2 - Merge three bidi frame properties into one. r?jfkthame draft
authorXidorn Quan <me@upsuper.org>
Tue, 21 Jun 2016 17:53:10 +1000
changeset 380244 642ddf84cb080eaec8706e8d33b23b1758cd552f
parent 380225 1ff7feb782285aef743e3d4632037fad089f995d
child 523685 05f945cbde4137224160c3c49a38c7605eafa2de
push id21178
push userxquan@mozilla.com
push dateTue, 21 Jun 2016 09:19:24 +0000
reviewersjfkthame
bugs1281099
milestone50.0a1
Bug 1281099 part 2 - Merge three bidi frame properties into one. r?jfkthame MozReview-Commit-ID: CEJhM3c21KO
layout/base/nsBidi.h
layout/base/nsBidiPresUtils.cpp
layout/generic/nsFrame.cpp
layout/generic/nsSelection.cpp
layout/generic/nsTextFrame.cpp
--- a/layout/base/nsBidi.h
+++ b/layout/base/nsBidi.h
@@ -404,16 +404,25 @@ struct LevState {
     PImpTab pImpTab;                    /* level table pointer          */
     PImpAct pImpAct;                    /* action map array             */
     int32_t startON;                    /* start of ON sequence         */
     int32_t state;                      /* current state                */
     int32_t runStart;                   /* start position of the run    */
     nsBidiLevel runLevel;               /* run level before implicit solving */
 };
 
+namespace mozilla {
+struct FrameBidiData
+{
+  nsBidiLevel baseLevel;
+  nsBidiLevel embeddingLevel;
+  uint8_t paragraphDepth;
+};
+} // namespace mozilla
+
 /**
  * This class holds information about a paragraph of text
  * with Bidi-algorithm-related details, or about one line of
  * such a paragraph.<p>
  * Reordering can be done on a line, or on a paragraph which is
  * then interpreted as one single line.<p>
  *
  * On construction, the class is initially empty. It is assigned
@@ -653,33 +662,36 @@ public:
    *
    * @param aOptions A bit set of options for the reordering that control
    *                how the reordered text is written.
    *
    * @param aDestSize will receive the number of characters that were written to <code>aDest</code>.
    */
   nsresult WriteReverse(const char16_t *aSrc, int32_t aSrcLength, char16_t *aDest, uint16_t aOptions, int32_t *aDestSize);
 
-  NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BaseLevelProperty, nsBidiLevel)
-  NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(EmbeddingLevelProperty, nsBidiLevel)
-  NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ParagraphDepthProperty, uint8_t)
+  NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BidiDataProperty, mozilla::FrameBidiData)
+
+  static mozilla::FrameBidiData GetBidiData(nsIFrame* aFrame)
+  {
+    return aFrame->Properties().Get(BidiDataProperty());
+  }
 
   static nsBidiLevel GetBaseLevel(nsIFrame* aFrame)
   {
-    return aFrame->Properties().Get(BaseLevelProperty());
+    return GetBidiData(aFrame).baseLevel;
   }
 
   static nsBidiLevel GetEmbeddingLevel(nsIFrame* aFrame)
   {
-    return aFrame->Properties().Get(EmbeddingLevelProperty());
+    return GetBidiData(aFrame).embeddingLevel;
   }
 
   static uint8_t GetParagraphDepth(nsIFrame* aFrame)
   {
-    return aFrame->Properties().Get(ParagraphDepthProperty());
+    return GetBidiData(aFrame).paragraphDepth;
   }
 
 protected:
   friend class nsBidiPresUtils;
 
   class BracketData {
   public:
     explicit BracketData(const nsBidi* aBidi);
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -739,26 +739,27 @@ nsBidiPresUtils::ResolveParagraph(nsBloc
       aBpd->mParagraphDepth == 0 && aBpd->GetDirection() == NSBIDI_LTR &&
       aBpd->GetParaLevel() == 0) {
     // We have a single left-to-right frame in a left-to-right paragraph,
     // without bidi isolation from the surrounding text.
     // Make sure that the embedding level and base level frame properties aren't
     // set (because if they are this frame used to have some other direction,
     // so we can't do this optimization), and we're done.
     nsIFrame* frame = aBpd->FrameAt(0);
-    if (frame != NS_BIDI_CONTROL_FRAME &&
-        !frame->Properties().Get(nsBidi::EmbeddingLevelProperty()) &&
-        !frame->Properties().Get(nsBidi::BaseLevelProperty())) {
+    if (frame != NS_BIDI_CONTROL_FRAME) {
+      FrameBidiData bidiData = nsBidi::GetBidiData(frame);
+      if (!bidiData.embeddingLevel && !bidiData.baseLevel) {
 #ifdef DEBUG
 #ifdef NOISY_BIDI
-      printf("early return for single direction frame %p\n", (void*)frame);
+        printf("early return for single direction frame %p\n", (void*)frame);
 #endif
 #endif
-      frame->AddStateBits(NS_FRAME_IS_BIDI);
-      return NS_OK;
+        frame->AddStateBits(NS_FRAME_IS_BIDI);
+        return NS_OK;
+      }
     }
   }
 
   nsIFrame* firstFrame = nullptr;
   nsIFrame* lastFrame = nullptr;
 
   for (; ;) {
     if (fragmentLength <= 0) {
@@ -787,22 +788,21 @@ nsBidiPresUtils::ResolveParagraph(nsBloc
           rv = NS_OK;
           break;
         }
         contentTextLength = content->TextLength();
         if (contentTextLength == 0) {
           frame->AdjustOffsetsForBidi(0, 0);
           // Set the base level and embedding level of the current run even
           // on an empty frame. Otherwise frame reordering will not be correct.
-          propTable->Set(frame, nsBidi::EmbeddingLevelProperty(),
-                         embeddingLevel);
-          propTable->Set(frame, nsBidi::BaseLevelProperty(),
-                         aBpd->GetParaLevel());
-          propTable->Set(frame, nsBidi::ParagraphDepthProperty(),
-                         aBpd->mParagraphDepth);
+          FrameBidiData bidiData;
+          bidiData.embeddingLevel = embeddingLevel;
+          bidiData.baseLevel = aBpd->GetParaLevel();
+          bidiData.paragraphDepth = aBpd->mParagraphDepth;
+          propTable->Set(frame, nsBidi::BidiDataProperty(), bidiData);
           continue;
         }
         int32_t start, end;
         frame->GetOffsets(start, end);
         NS_ASSERTION(!(contentTextLength < end - start),
                      "Frame offsets don't fit in content");
         fragmentLength = std::min(contentTextLength, end - start);
         contentOffset = start;
@@ -823,22 +823,21 @@ nsBidiPresUtils::ResolveParagraph(nsBloc
       runLength = logicalLimit - lineOffset;
     } // if (runLength <= 0)
 
     if (frame == NS_BIDI_CONTROL_FRAME) {
       frame = nullptr;
       ++lineOffset;
     }
     else {
-      propTable->Set(frame, nsBidi::EmbeddingLevelProperty(),
-                     embeddingLevel);
-      propTable->Set(frame, nsBidi::BaseLevelProperty(),
-                     aBpd->GetParaLevel());
-      propTable->Set(frame, nsBidi::ParagraphDepthProperty(),
-                     aBpd->mParagraphDepth);
+      FrameBidiData bidiData;
+      bidiData.embeddingLevel = embeddingLevel;
+      bidiData.baseLevel = aBpd->GetParaLevel();
+      bidiData.paragraphDepth = aBpd->mParagraphDepth;
+      propTable->Set(frame, nsBidi::BidiDataProperty(), bidiData);
       if (isTextFrame) {
         if ( (runLength > 0) && (runLength < fragmentLength) ) {
           /*
            * The text in this frame continues beyond the end of this directional run.
            * Create a non-fluid continuation frame for the next directional run.
            */
           currentLine->MarkDirty();
           nsIFrame* nextBidi;
@@ -1773,33 +1772,26 @@ nsBidiPresUtils::EnsureBidiContinuation(
 
 void
 nsBidiPresUtils::RemoveBidiContinuation(BidiParagraphData *aBpd,
                                         nsIFrame*       aFrame,
                                         int32_t         aFirstIndex,
                                         int32_t         aLastIndex,
                                         int32_t&        aOffset)
 {
-  FrameProperties props = aFrame->Properties();
-  nsBidiLevel embeddingLevel = props.Get(nsBidi::EmbeddingLevelProperty());
-  nsBidiLevel baseLevel = props.Get(nsBidi::BaseLevelProperty());
-  uint8_t paragraphDepth = props.Get(nsBidi::ParagraphDepthProperty());
-
+  FrameBidiData bidiData = nsBidi::GetBidiData(aFrame);
   for (int32_t index = aFirstIndex + 1; index <= aLastIndex; index++) {
     nsIFrame* frame = aBpd->FrameAt(index);
     if (frame == NS_BIDI_CONTROL_FRAME) {
       ++aOffset;
     }
     else {
       // Make the frame and its continuation ancestors fluid,
       // so they can be reused or deleted by normal reflow code
-      FrameProperties frameProps = frame->Properties();
-      frameProps.Set(nsBidi::EmbeddingLevelProperty(), embeddingLevel);
-      frameProps.Set(nsBidi::BaseLevelProperty(), baseLevel);
-      frameProps.Set(nsBidi::ParagraphDepthProperty(), paragraphDepth);
+      frame->Properties().Set(nsBidi::BidiDataProperty(), bidiData);
       frame->AddStateBits(NS_FRAME_IS_BIDI);
       while (frame) {
         nsIFrame* prev = frame->GetPrevContinuation();
         if (prev) {
           MakeContinuationFluid(prev, frame);
           frame = frame->GetParent();
         } else {
           break;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -149,18 +149,18 @@ struct nsContentAndOffset
 #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.
 static bool
 IsReversedDirectionFrame(nsIFrame* aFrame)
 {
-  return !IS_SAME_DIRECTION(nsBidi::GetEmbeddingLevel(aFrame),
-                            nsBidi::GetBaseLevel(aFrame));
+  FrameBidiData bidiData = nsBidi::GetBidiData(aFrame);
+  return !IS_SAME_DIRECTION(bidiData.embeddingLevel, bidiData.baseLevel);
 }
 
 #include "nsILineIterator.h"
 
 //non Hack prototypes
 #if 0
 static void RefreshContentFrames(nsPresContext* aPresContext, nsIContent * aStartContent, nsIContent * aEndContent);
 #endif
@@ -6585,21 +6585,21 @@ nsFrame::GetPointFromOffset(int32_t inOf
     if (newContent){
       int32_t newOffset = newContent->IndexOf(mContent);
 
       // Find the direction of the frame from the EmbeddingLevelProperty,
       // which is the resolved bidi level set in
       // nsBidiPresUtils::ResolveParagraph (odd levels = right-to-left).
       // If the embedding level isn't set, just use the CSS direction
       // property.
-      bool hasEmbeddingLevel;
-      nsBidiLevel embeddingLevel = Properties().Get(
-        nsBidi::EmbeddingLevelProperty(), &hasEmbeddingLevel);
-      bool isRTL = hasEmbeddingLevel
-        ? IS_LEVEL_RTL(embeddingLevel)
+      bool hasBidiData;
+      FrameBidiData bidiData =
+        Properties().Get(nsBidi::BidiDataProperty(), &hasBidiData);
+      bool isRTL = hasBidiData
+        ? IS_LEVEL_RTL(bidiData.embeddingLevel)
         : StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
       if ((!isRTL && inOffset > newOffset) ||
           (isRTL && inOffset <= newOffset)) {
         pt = contentRect.TopRight();
       }
     }
   }
   *outPoint = pt;
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -1112,26 +1112,27 @@ nsFrameSelection::MoveCaret(nsDirection 
 
       theFrame->GetOffsets(frameStart, frameEnd);
     }
 
     if (context->BidiEnabled())
     {
       switch (aAmount) {
         case eSelectBeginLine:
-        case eSelectEndLine:
+        case eSelectEndLine: {
           // In Bidi contexts, PeekOffset calculates pos.mContentOffset
           // differently depending on whether the movement is visual or logical.
           // For visual movement, pos.mContentOffset depends on the direction-
           // ality of the first/last frame on the line (theFrame), and the caret
           // directionality must correspond.
-          SetCaretBidiLevel(visualMovement ? nsBidi::GetEmbeddingLevel(theFrame)
-                                           : nsBidi::GetBaseLevel(theFrame));
+          FrameBidiData bidiData = nsBidi::GetBidiData(theFrame);
+          SetCaretBidiLevel(visualMovement ? bidiData.embeddingLevel
+                                           : bidiData.baseLevel);
           break;
-
+        }
         default:
           // If the current position is not a frame boundary, it's enough just
           // to take the Bidi level of the current frame
           if ((pos.mContentOffset != frameStart &&
                pos.mContentOffset != frameEnd) ||
               eSelectLine == aAmount) {
             SetCaretBidiLevel(nsBidi::GetEmbeddingLevel(theFrame));
           }
@@ -1373,31 +1374,31 @@ nsFrameSelection::GetPrevNextBidiLevels(
   bool jumpedLine, movedOverNonSelectableText;
   nsresult rv = currentFrame->GetFrameFromDirection(direction, false,
                                                     aJumpLines, true,
                                                     &newFrame, &offset, &jumpedLine,
                                                     &movedOverNonSelectableText);
   if (NS_FAILED(rv))
     newFrame = nullptr;
 
-  nsBidiLevel baseLevel = nsBidi::GetBaseLevel(currentFrame);
-  nsBidiLevel currentLevel = nsBidi::GetEmbeddingLevel(currentFrame);
+  FrameBidiData currentBidi = nsBidi::GetBidiData(currentFrame);
+  nsBidiLevel currentLevel = currentBidi.embeddingLevel;
   nsBidiLevel newLevel = newFrame ? nsBidi::GetEmbeddingLevel(newFrame)
-                                  : baseLevel;
+                                  : currentBidi.baseLevel;
   
   // If not jumping lines, disregard br frames, since they might be positioned incorrectly.
   // XXX This could be removed once bug 339786 is fixed.
   if (!aJumpLines) {
     if (currentFrame->GetType() == nsGkAtoms::brFrame) {
       currentFrame = nullptr;
-      currentLevel = baseLevel;
+      currentLevel = currentBidi.baseLevel;
     }
     if (newFrame && newFrame->GetType() == nsGkAtoms::brFrame) {
       newFrame = nullptr;
-      newLevel = baseLevel;
+      newLevel = currentBidi.baseLevel;
     }
   }
   
   if (direction == eDirNext)
     levels.SetData(currentFrame, newFrame, currentLevel, newLevel);
   else
     levels.SetData(newFrame, currentFrame, newLevel, currentLevel);
 
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -1601,22 +1601,24 @@ bool
 BuildTextRunsScanner::ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, nsTextFrame* aFrame2)
 {
   // We don't need to check font size inflation, since
   // |FindLineContainer| above (via |nsIFrame::CanContinueTextRun|)
   // ensures that text runs never cross block boundaries.  This means
   // that the font size inflation on all text frames in the text run is
   // already guaranteed to be the same as each other (and for the line
   // container).
-  if (mBidiEnabled &&
-      (nsBidi::GetEmbeddingLevel(aFrame1) !=
-       nsBidi::GetEmbeddingLevel(aFrame2) ||
-       nsBidi::GetParagraphDepth(aFrame1) !=
-       nsBidi::GetParagraphDepth(aFrame2)))
-    return false;
+  if (mBidiEnabled) {
+    FrameBidiData data1 = nsBidi::GetBidiData(aFrame1);
+    FrameBidiData data2 = nsBidi::GetBidiData(aFrame2);
+    if (data1.embeddingLevel != data2.embeddingLevel ||
+        data1.paragraphDepth != data2.paragraphDepth) {
+      return false;
+    }
+  }
 
   nsStyleContext* sc1 = aFrame1->StyleContext();
   const nsStyleText* textStyle1 = sc1->StyleText();
   // If the first frame ends in a preformatted newline, then we end the textrun
   // here. This avoids creating giant textruns for an entire plain text file.
   // Note that we create a single text frame for a preformatted text node,
   // even if it has newlines in it, so typically we won't see trailing newlines
   // until after reflow has broken up the frame into one (or more) frames per
@@ -4144,43 +4146,32 @@ nsContinuingTextFrame::Init(nsIContent* 
       gfxTextRun *uninflatedTextRun =
         prev->GetTextRun(nsTextFrame::eNotInflated);
       if (uninflatedTextRun) {
         SetTextRun(uninflatedTextRun, nsTextFrame::eNotInflated, 1.0f);
       }
     }
   }
   if (aPrevInFlow->GetStateBits() & NS_FRAME_IS_BIDI) {
-    FramePropertyTable *propTable = PresContext()->PropertyTable();
-    // Get all the properties from the prev-in-flow first to take
-    // advantage of the propTable's cache and simplify the assertion below
-    auto embeddingLevel =
-      propTable->Get(aPrevInFlow, nsBidi::EmbeddingLevelProperty());
-    auto baseLevel =
-      propTable->Get(aPrevInFlow, nsBidi::BaseLevelProperty());
-    auto paragraphDepth =
-      propTable->Get(aPrevInFlow, nsBidi::ParagraphDepthProperty());
-    propTable->Set(this, nsBidi::EmbeddingLevelProperty(), embeddingLevel);
-    propTable->Set(this, nsBidi::BaseLevelProperty(), baseLevel);
-    propTable->Set(this, nsBidi::ParagraphDepthProperty(), paragraphDepth);
+    FrameBidiData bidiData = nsBidi::GetBidiData(aPrevInFlow);
+    Properties().Set(nsBidi::BidiDataProperty(), bidiData);
 
     if (nextContinuation) {
       SetNextContinuation(nextContinuation);
       nextContinuation->SetPrevContinuation(this);
       // Adjust next-continuations' content offset as needed.
       while (nextContinuation &&
              nextContinuation->GetContentOffset() < mContentOffset) {
-        NS_ASSERTION(
-          embeddingLevel == propTable->Get(
-            nextContinuation, nsBidi::EmbeddingLevelProperty()) &&
-          baseLevel == propTable->Get(
-            nextContinuation, nsBidi::BaseLevelProperty()) &&
-          paragraphDepth == propTable->Get(
-            nextContinuation, nsBidi::ParagraphDepthProperty()),
-          "stealing text from different type of BIDI continuation");
+#ifdef DEBUG
+        FrameBidiData nextBidiData = nsBidi::GetBidiData(nextContinuation);
+        NS_ASSERTION(bidiData.embeddingLevel == nextBidiData.embeddingLevel &&
+                     bidiData.baseLevel == nextBidiData.baseLevel &&
+                     bidiData.paragraphDepth == nextBidiData.paragraphDepth,
+                     "stealing text from different type of BIDI continuation");
+#endif
         nextContinuation->mContentOffset = mContentOffset;
         nextContinuation = static_cast<nsTextFrame*>(nextContinuation->GetNextContinuation());
       }
     }
     mState |= NS_FRAME_IS_BIDI;
   } // prev frame is bidi
 }