Bug 1052924 - Rewrite reflow code. r=dbaron
authorXidorn Quan <quanxunzhen@gmail.com>
Wed, 26 Nov 2014 15:52:49 +1100
changeset 217538 d52caef15e10b32a7e9c48ab7ef5a79c8a1e2dec
parent 217537 fab3d1117a6a539611813a4050a692bdbdae5af3
child 217539 002f76d56d0f1d7d81d5f88ac3b690ca62de7876
push id52308
push userxquan@mozilla.com
push dateWed, 26 Nov 2014 04:53:51 +0000
treeherdermozilla-inbound@b657497010c2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs1052924, 1055676
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1052924 - Rewrite reflow code. r=dbaron Important changes: * Avoid using GetPrefISize on the ruby texts in nsRubyBaseContainerFrame::Reflow, since the size it produces might not match the size produced by Reflow. The old code calls that on all the ruby texts to determine how big they are, then reflows all the ruby bases, and then reflows all the ruby texts. The new code instead processes one pair at a time, and for each pair reflows the ruby texts and then the ruby base. * Change the base class of nsRubyTextContainerFrame from nsBlockFrame to nsContainerFrame, and stop constructing an nsBlockReflowState for its reflow. * Move the code for reflowing ruby texts from nsRubyTextContainerFrame and to nsRubyBaseContainerFrame. * Fix the regression that ruby text containers contain span are not reflowed properly. It is the regression introduced in patch 0. Known regression: * This patch drops centering ruby base and annotation in pairs. This should be fixed in bug 1055676 (ruby-align).
layout/generic/nsRubyBaseContainerFrame.cpp
layout/generic/nsRubyBaseContainerFrame.h
layout/generic/nsRubyTextContainerFrame.cpp
layout/generic/nsRubyTextContainerFrame.h
--- a/layout/generic/nsRubyBaseContainerFrame.cpp
+++ b/layout/generic/nsRubyBaseContainerFrame.cpp
@@ -215,102 +215,144 @@ nsRubyBaseContainerFrame::Reflow(nsPresC
     NS_ASSERTION(
       aReflowState.mLineLayout,
       "No line layout provided to RubyBaseContainerFrame reflow method.");
     aStatus = NS_FRAME_COMPLETE;
     return;
   }
 
   aStatus = NS_FRAME_COMPLETE;
-  nscoord isize = 0;
-  nscoord leftoverSpace = 0;
-  nscoord spaceApart = 0;
   WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
   WritingMode frameWM = aReflowState.GetWritingMode();
-  LogicalMargin borderPadding =
-    aReflowState.ComputedLogicalBorderPadding();
-  nscoord baseStart = 0;
 
   LogicalSize availSize(lineWM, aReflowState.AvailableWidth(),
                         aReflowState.AvailableHeight());
 
   const uint32_t rtcCount = mTextContainers.Length();
+  const uint32_t spanCount = mSpanContainers.Length();
+  const uint32_t totalCount = rtcCount + spanCount;
+  // We have a reflow state and a line layout for each RTC.
+  // They are conceptually the state of the RTCs, but we don't actually
+  // reflow those RTCs in this code. These two arrays are holders of
+  // the reflow states and line layouts.
+  nsAutoTArray<UniquePtr<nsHTMLReflowState>, RTC_ARRAY_SIZE> reflowStates;
+  nsAutoTArray<UniquePtr<nsLineLayout>, RTC_ARRAY_SIZE> lineLayouts;
+  reflowStates.SetCapacity(totalCount);
+  lineLayouts.SetCapacity(totalCount);
+
+  nsAutoTArray<nsHTMLReflowState*, RTC_ARRAY_SIZE> rtcReflowStates;
+  nsAutoTArray<nsHTMLReflowState*, RTC_ARRAY_SIZE> spanReflowStates;
+  rtcReflowStates.SetCapacity(rtcCount);
+  spanReflowStates.SetCapacity(spanCount);
+
   // Begin the line layout for each ruby text container in advance.
-  for (uint32_t i = 0; i < rtcCount; i++) {
-    nsRubyTextContainerFrame* rtcFrame = mTextContainers.ElementAt(i);
-    nsHTMLReflowState rtcReflowState(aPresContext,
-                                     *aReflowState.parentReflowState,
-                                     rtcFrame, availSize);
-    rtcReflowState.mLineLayout = aReflowState.mLineLayout;
-    // FIXME: Avoid using/needing the rtcReflowState argument
-    rtcFrame->BeginRTCLineLayout(aPresContext, rtcReflowState);
+  for (uint32_t i = 0; i < totalCount; i++) {
+    nsIFrame* textContainer;
+    nsTArray<nsHTMLReflowState*>* reflowStateArray;
+    if (i < rtcCount) {
+      textContainer = mTextContainers[i];
+      reflowStateArray = &rtcReflowStates;
+    } else {
+      textContainer = mSpanContainers[i - rtcCount];
+      reflowStateArray = &spanReflowStates;
+    }
+    nsHTMLReflowState* reflowState = new nsHTMLReflowState(
+      aPresContext, *aReflowState.parentReflowState, textContainer, availSize);
+    reflowStates.AppendElement(reflowState);
+    reflowStateArray->AppendElement(reflowState);
+    nsLineLayout* lineLayout = new nsLineLayout(
+      aPresContext, reflowState->mFloatManager, reflowState, nullptr);
+    lineLayouts.AppendElement(lineLayout);
+
+    // Line number is useless for ruby text
+    // XXX nullptr here may cause problem, see comments for
+    //     nsLineLayout::mBlockRS and nsLineLayout::AddFloat
+    lineLayout->Init(nullptr, reflowState->CalcLineHeight(), -1);
+    reflowState->mLineLayout = lineLayout;
+
+    LogicalMargin borderPadding = reflowState->ComputedLogicalBorderPadding();
+    nscoord containerWidth =
+      reflowState->ComputedWidth() + borderPadding.LeftRight(lineWM);
+
+    lineLayout->BeginLineReflow(borderPadding.IStart(lineWM),
+                                borderPadding.BStart(lineWM),
+                                reflowState->ComputedISize(),
+                                NS_UNCONSTRAINEDSIZE,
+                                false, false, lineWM, containerWidth);
   }
 
+  nscoord istart = aReflowState.mLineLayout->GetCurrentICoord();
+  nscoord icoord = istart;
+
+  // Reflow non-span annotations and bases
   for (PairEnumerator e(this, mTextContainers); !e.AtEnd(); e.Next()) {
-    // Determine if we need more spacing between bases in the inline direction
-    // depending on the inline size of the corresponding annotations
-    // FIXME: The use of GetPrefISize here and below is easier but not ideal. It
-    // would be better to use metrics from reflow.
-    nscoord textWidth = 0;
+    nscoord pairISize = 0;
+
     for (uint32_t i = 0; i < rtcCount; i++) {
       nsRubyTextFrame* rtFrame = do_QueryFrame(e.GetTextFrame(i));
       if (rtFrame) {
-        int newWidth = rtFrame->GetPrefISize(aReflowState.rendContext);
-        if (newWidth > textWidth) {
-          textWidth = newWidth;
-        }
+        nsReflowStatus reflowStatus;
+        nsHTMLReflowMetrics metrics(*rtcReflowStates[i]);
+
+        bool pushedFrame;
+        rtcReflowStates[i]->mLineLayout->ReflowFrame(rtFrame, reflowStatus,
+                                                     &metrics, pushedFrame);
+        NS_ASSERTION(!pushedFrame, "Ruby line breaking is not yet implemented");
+        pairISize = std::max(pairISize, metrics.ISize(lineWM));
       }
     }
 
     nsIFrame* rbFrame = e.GetBaseFrame();
-    NS_ASSERTION(!rbFrame || rbFrame->GetType() == nsGkAtoms::rubyBaseFrame,
-                 "Unrecognized child type for ruby base container");
     if (rbFrame) {
-      nsReflowStatus frameReflowStatus;
-      nsHTMLReflowMetrics metrics(aReflowState, aDesiredSize.mFlags);
-      nscoord prefWidth = rbFrame->GetPrefISize(aReflowState.rendContext);
-
-      if (textWidth > prefWidth) {
-        spaceApart = std::max((textWidth - prefWidth) / 2, spaceApart);
-        leftoverSpace = spaceApart;
-      } else {
-        spaceApart = leftoverSpace;
-        leftoverSpace = 0;
-      }
-      if (spaceApart > 0) {
-        aReflowState.mLineLayout->AdvanceICoord(spaceApart);
-      }
-      baseStart = aReflowState.mLineLayout->GetCurrentICoord();
+      MOZ_ASSERT(rbFrame->GetType() == nsGkAtoms::rubyBaseFrame);
+      nsReflowStatus reflowStatus;
+      nsHTMLReflowMetrics metrics(aReflowState);
 
       bool pushedFrame;
-      aReflowState.mLineLayout->ReflowFrame(rbFrame, frameReflowStatus,
+      aReflowState.mLineLayout->ReflowFrame(rbFrame, reflowStatus,
                                             &metrics, pushedFrame);
       NS_ASSERTION(!pushedFrame, "Ruby line breaking is not yet implemented");
-
-      isize += metrics.ISize(lineWM);
-      rbFrame->SetSize(LogicalSize(lineWM, metrics.ISize(lineWM),
-                                  metrics.BSize(lineWM)));
-      FinishReflowChild(rbFrame, aPresContext, metrics, &aReflowState, 0, 0,
-                        NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_MOVE_VIEW);
+      pairISize = std::max(pairISize, metrics.ISize(lineWM));
     }
 
-    // Now reflow the ruby text boxes that correspond to this ruby base box.
+    // Align all the line layout to the new coordinate.
+    icoord += pairISize;
+    aReflowState.mLineLayout->AdvanceICoord(
+      icoord - aReflowState.mLineLayout->GetCurrentICoord());
     for (uint32_t i = 0; i < rtcCount; i++) {
-      nsRubyTextFrame* rtFrame = do_QueryFrame(e.GetTextFrame(i));
-      nsRubyTextContainerFrame* rtcFrame = mTextContainers[i];
-      if (rtFrame) {
-        nsHTMLReflowMetrics rtcMetrics(*aReflowState.parentReflowState,
-                                       aDesiredSize.mFlags);
-        nsHTMLReflowState rtcReflowState(aPresContext,
-                                         *aReflowState.parentReflowState,
-                                         rtcFrame, availSize);
-        rtcReflowState.mLineLayout = rtcFrame->GetLineLayout();
-        rtcFrame->ReflowRubyTextFrame(rtFrame, rbFrame, baseStart,
-                                      aPresContext, rtcMetrics,
-                                      rtcReflowState);
-      }
+      nsLineLayout* lineLayout = rtcReflowStates[i]->mLineLayout;
+      lineLayout->AdvanceICoord(icoord - lineLayout->GetCurrentICoord());
     }
   }
 
+  // Reflow spans
+  nscoord spanISize = 0;
+  for (uint32_t i = 0; i < spanCount; i++) {
+    nsRubyTextContainerFrame* container = mSpanContainers[i];
+    nsIFrame* rtFrame = container->GetFirstPrincipalChild();
+    nsReflowStatus reflowStatus;
+    nsHTMLReflowMetrics metrics(*spanReflowStates[i]);
+    bool pushedFrame;
+    spanReflowStates[i]->mLineLayout->ReflowFrame(rtFrame, reflowStatus,
+                                                  &metrics, pushedFrame);
+    NS_ASSERTION(!pushedFrame, "Ruby line breaking is not yet implemented");
+    spanISize = std::max(spanISize, metrics.ISize(lineWM));
+  }
+
+  nscoord isize = icoord - istart;
+  if (isize < spanISize) {
+    aReflowState.mLineLayout->AdvanceICoord(spanISize - isize);
+    isize = spanISize;
+  }
+  for (uint32_t i = 0; i < totalCount; i++) {
+    // It happens before the ruby text container is reflowed, and that
+    // when it is reflowed, it will just use this size.
+    nsRubyTextContainerFrame* textContainer = i < rtcCount ?
+      mTextContainers[i] : mSpanContainers[i - rtcCount];
+    textContainer->SetISize(isize);
+    lineLayouts[i]->EndLineReflow();
+  }
+
+  LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding();
   aDesiredSize.ISize(lineWM) = isize;
   nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState,
                                          borderPadding, lineWM, frameWM);
 }
--- a/layout/generic/nsRubyBaseContainerFrame.h
+++ b/layout/generic/nsRubyBaseContainerFrame.h
@@ -4,16 +4,17 @@
  * version 2.0 (the "License"). You can obtain a copy of the License at
  * http://mozilla.org/MPL/2.0/. */
 
 /* rendering object for CSS "display: ruby-base-container" */
 
 #ifndef nsRubyBaseContainerFrame_h___
 #define nsRubyBaseContainerFrame_h___
 
+#include "mozilla/UniquePtr.h"
 #include "nsContainerFrame.h"
 #include "nsRubyTextContainerFrame.h"
 #include "nsRubyBaseFrame.h"
 #include "nsRubyTextFrame.h"
 
 /**
  * Factory function.
  * @return a newly allocated nsRubyBaseContainerFrame (infallible)
--- a/layout/generic/nsRubyTextContainerFrame.cpp
+++ b/layout/generic/nsRubyTextContainerFrame.cpp
@@ -7,24 +7,26 @@
 /* rendering object for CSS "display: ruby-text-container" */
 
 #include "nsRubyTextContainerFrame.h"
 #include "nsPresContext.h"
 #include "nsStyleContext.h"
 #include "WritingModes.h"
 #include "mozilla/UniquePtr.h"
 
+using namespace mozilla;
+
 //----------------------------------------------------------------------
 
 // Frame class boilerplate
 // =======================
 
 NS_QUERYFRAME_HEAD(nsRubyTextContainerFrame)
   NS_QUERYFRAME_ENTRY(nsRubyTextContainerFrame)
-NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
 
 NS_IMPL_FRAMEARENA_HELPERS(nsRubyTextContainerFrame)
 
 nsContainerFrame*
 NS_NewRubyTextContainerFrame(nsIPresShell* aPresShell,
                              nsStyleContext* aContext)
 {
   return new (aPresShell) nsRubyTextContainerFrame(aContext);
@@ -45,135 +47,29 @@ nsRubyTextContainerFrame::GetType() cons
 #ifdef DEBUG_FRAME_DUMP
 nsresult
 nsRubyTextContainerFrame::GetFrameName(nsAString& aResult) const
 {
   return MakeFrameName(NS_LITERAL_STRING("RubyTextContainer"), aResult);
 }
 #endif
 
-void
-nsRubyTextContainerFrame::BeginRTCLineLayout(nsPresContext* aPresContext,
-                                             const nsHTMLReflowState& aReflowState)
-{
-  // Construct block reflow state and line layout
-  nscoord consumedBSize = GetConsumedBSize();
-
-  ClearLineCursor();
-
-  mISize = 0;
-
-  nsBlockReflowState state(aReflowState, aPresContext, this, true, true,
-                           false, consumedBSize);
-
-  NS_ASSERTION(!mLines.empty(),
-    "There should be at least one line in the ruby text container");
-  line_iterator firstLine = begin_lines();
-  mLineLayout = mozilla::MakeUnique<nsLineLayout>(
-                           state.mPresContext,
-                           state.mReflowState.mFloatManager,
-                           &state.mReflowState, &firstLine);
-  mLineLayout->Init(&state, state.mMinLineHeight, state.mLineNumber);
-
-  mozilla::WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
-  mozilla::LogicalRect lineRect(state.mContentArea);
-  nscoord iStart = lineRect.IStart(lineWM);
-  nscoord availISize = lineRect.ISize(lineWM);
-  nscoord availBSize = NS_UNCONSTRAINEDSIZE;
-
-  mLineLayout->BeginLineReflow(iStart, state.mBCoord,
-                              availISize, availBSize,
-                              false,
-                              false,
-                              lineWM, state.mContainerWidth);
-}
-
-void
-nsRubyTextContainerFrame::ReflowRubyTextFrame(
-                            nsRubyTextFrame* rtFrame,
-                            nsIFrame* rbFrame,
-                            nscoord baseStart,
-                            nsPresContext* aPresContext,
-                            nsHTMLReflowMetrics& aDesiredSize,
-                            const nsHTMLReflowState& aReflowState)
-{
-  nsReflowStatus frameReflowStatus;
-  nsHTMLReflowMetrics metrics(aReflowState, aDesiredSize.mFlags);
-  mozilla::WritingMode lineWM = mLineLayout->GetWritingMode();
-  mozilla::LogicalSize availSize(lineWM, aReflowState.AvailableWidth(),
-                   aReflowState.AvailableHeight());
-  nsHTMLReflowState childReflowState(aPresContext, aReflowState, rtFrame, availSize);
-
-  // Determine the inline coordinate for the text frame by centering over
-  // the corresponding base frame
-  int baseWidth;
-  if (rbFrame) {
-    baseWidth = rbFrame->ISize();
-
-    // If this is the last ruby annotation, it gets paired with ALL remaining
-    // ruby bases
-    if (!rtFrame->GetNextSibling()) {
-      rbFrame = rbFrame->GetNextSibling();
-      while (rbFrame) {
-        baseWidth += rbFrame->ISize();
-        rbFrame = rbFrame->GetNextSibling();
-      }
-    }
-  } else {
-    baseWidth = 0;
-  }
-  
-  int baseCenter = baseStart + baseWidth / 2;
-  // FIXME: Find a way to avoid using GetPrefISize here, potentially by moving
-  // the frame after it has reflowed.
-  nscoord ICoord = baseCenter - rtFrame->GetPrefISize(aReflowState.rendContext) / 2;
-  if (ICoord > mLineLayout->GetCurrentICoord()) {
-    mLineLayout->AdvanceICoord(ICoord - mLineLayout->GetCurrentICoord());
-  } 
-
-  bool pushedFrame;
-  mLineLayout->ReflowFrame(rtFrame, frameReflowStatus,
-                           &metrics, pushedFrame);
-
-  NS_ASSERTION(!pushedFrame, "Ruby line breaking is not yet implemented");
-
-  mISize += metrics.ISize(lineWM);
-  rtFrame->SetSize(nsSize(metrics.ISize(lineWM), metrics.BSize(lineWM)));
-  FinishReflowChild(rtFrame, aPresContext, metrics, &childReflowState, 0, 0,
-                    NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_MOVE_VIEW);
-} 
-
 /* virtual */ void
 nsRubyTextContainerFrame::Reflow(nsPresContext* aPresContext,
                                  nsHTMLReflowMetrics& aDesiredSize,
                                  const nsHTMLReflowState& aReflowState,
                                  nsReflowStatus& aStatus)
 {
   DO_GLOBAL_REFLOW_COUNT("nsRubyTextContainerFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   // All rt children have already been reflowed. All we need to do is clean up
   // the line layout.
 
   aStatus = NS_FRAME_COMPLETE;
-  mozilla::WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
-  mozilla::WritingMode frameWM = aReflowState.GetWritingMode();
-  mozilla::LogicalMargin borderPadding =
-    aReflowState.ComputedLogicalBorderPadding();
+  WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
+  WritingMode frameWM = aReflowState.GetWritingMode();
+  LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding();
 
   aDesiredSize.ISize(lineWM) = mISize;
   nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState,
                                          borderPadding, lineWM, frameWM);
-
-  nscoord bsize = aDesiredSize.BSize(lineWM);
-  if (!mLines.empty()) {
-    // Okay to use BlockStartAscent because it has just been correctly set by
-    // nsLayoutUtils::SetBSizeFromFontMetrics.
-    mLines.begin()->SetLogicalAscent(aDesiredSize.BlockStartAscent());
-    mLines.begin()->SetBounds(aReflowState.GetWritingMode(), 0, 0, mISize,
-                              bsize, mISize);
-  }
-
-  if (mLineLayout) {
-    mLineLayout->EndLineReflow();
-    mLineLayout = nullptr;
-  }
 }
--- a/layout/generic/nsRubyTextContainerFrame.h
+++ b/layout/generic/nsRubyTextContainerFrame.h
@@ -16,53 +16,41 @@
 
 /**
  * Factory function.
  * @return a newly allocated nsRubyTextContainerFrame (infallible)
  */
 nsContainerFrame* NS_NewRubyTextContainerFrame(nsIPresShell* aPresShell,
                                                nsStyleContext* aContext);
 
-// If this is ever changed to be inline again, the code in
-// nsFrame::IsFontSizeInflationContainer should be updated to stop excluding
-// this from being considered inline.
-class nsRubyTextContainerFrame MOZ_FINAL : public nsBlockFrame
+class nsRubyTextContainerFrame MOZ_FINAL : public nsContainerFrame
 {
 public:
   NS_DECL_FRAMEARENA_HELPERS
   NS_DECL_QUERYFRAME_TARGET(nsRubyTextContainerFrame)
   NS_DECL_QUERYFRAME
 
   // nsIFrame overrides
   virtual nsIAtom* GetType() const MOZ_OVERRIDE;
   virtual void Reflow(nsPresContext* aPresContext,
                       nsHTMLReflowMetrics& aDesiredSize,
                       const nsHTMLReflowState& aReflowState,
                       nsReflowStatus& aStatus) MOZ_OVERRIDE;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE;
 #endif
-
-  void ReflowRubyTextFrame(nsRubyTextFrame* rtFrame, nsIFrame* rbFrame,
-                           nscoord baseStart, nsPresContext* aPresContext,
-                           nsHTMLReflowMetrics& aDesiredSize,
-                           const nsHTMLReflowState& aReflowState);
-  void BeginRTCLineLayout(nsPresContext* aPresContext,
-                          const nsHTMLReflowState& aReflowState);
-  nsLineLayout* GetLineLayout() { return mLineLayout.get(); };
+  void SetISize(nscoord aISize) { mISize = aISize; }
 
 protected:
   friend nsContainerFrame*
     NS_NewRubyTextContainerFrame(nsIPresShell* aPresShell,
                                  nsStyleContext* aContext);
-  explicit nsRubyTextContainerFrame(nsStyleContext* aContext) : nsBlockFrame(aContext) {}
-  // This pointer is active only during reflow of the ruby structure. It gets
-  // created when the corresponding ruby base container is reflowed, and it is
-  // destroyed when the ruby text container itself is reflowed.
-  mozilla::UniquePtr<nsLineLayout> mLineLayout;
+  explicit nsRubyTextContainerFrame(nsStyleContext* aContext)
+    : nsContainerFrame(aContext) {}
+
   // The intended dimensions of the ruby text container. These are modified
   // whenever a ruby text box is reflowed and used when the ruby text container
   // is reflowed.
   nscoord mISize;
 };
 
 #endif /* nsRubyTextContainerFrame_h___ */