Bug 1030993 - Basic reflow implementation for ruby frame classes. r=dbaron
authorSusanna Bowen <sgbowen8@gmail.com>
Fri, 15 Aug 2014 10:34:20 -0700
changeset 199797 6978c9d538d4a41ea385cc1ac3abb08a87605195
parent 199796 9e184a027905e4031fbd6368aaec499f31014909
child 199798 b2e1f5bb5e0714d7ca65aa5662ed5c3a09a7919c
push id47732
push userdholbert@mozilla.com
push dateFri, 15 Aug 2014 17:34:32 +0000
treeherdermozilla-inbound@8372c105c01c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs1030993
milestone34.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 1030993 - Basic reflow implementation for ruby frame classes. r=dbaron To account for spacing between bases or text boxes during reflow, the line layout which manages the bases updates its inline direction coordinate based on the preferred inline size for the corresponding text boxes. Next, the base is reflowed at the correct inline coordinate. Each paired text box is then also reflowed at the proper inline position determined by (1) the current position of its corresponding base and (2) its own preferred width. In computing intrinsic widths, accounting for spacing is less complicated. The minimum intrinsic width is the width of the widest ruby column, and the preferred intrinsic width is the sum of all the ruby column widths. Each ruby column width is the maximum width of its base box and text boxes. These individual widths are determined using GetPrefISize on the base and text boxes. Ruby base container frames store a list of pointers to the ruby text container frames in the segment they denote. This list of pointers is created in the ruby frame reflow method before calling the reflow method for the ruby base container. The list exists and is used only during reflow of the main ruby frame and is cleared before returning from reflow.
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsInlineFrame.cpp
layout/generic/nsLineLayout.cpp
layout/generic/nsLineLayout.h
layout/generic/nsRubyBaseContainerFrame.cpp
layout/generic/nsRubyBaseContainerFrame.h
layout/generic/nsRubyBaseFrame.cpp
layout/generic/nsRubyBaseFrame.h
layout/generic/nsRubyFrame.cpp
layout/generic/nsRubyFrame.h
layout/generic/nsRubyTextContainerFrame.cpp
layout/generic/nsRubyTextContainerFrame.h
layout/generic/nsRubyTextFrame.cpp
layout/generic/nsRubyTextFrame.h
layout/generic/nsTextFrame.cpp
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -4568,23 +4568,25 @@ nsCSSFrameConstructor::FindDisplayData(c
     { NS_STYLE_DISPLAY_GRID,
       FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame) },
     { NS_STYLE_DISPLAY_INLINE_GRID,
       FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame) },
     { NS_STYLE_DISPLAY_RUBY,
       FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT,
                   NS_NewRubyFrame) },
     { NS_STYLE_DISPLAY_RUBY_BASE,
-      FCDATA_DECL(FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer),
+      FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
+                  FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer),
                   NS_NewRubyBaseFrame) },
     { NS_STYLE_DISPLAY_RUBY_BASE_CONTAINER,
       FCDATA_DECL(FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby),
                   NS_NewRubyBaseContainerFrame) },
     { NS_STYLE_DISPLAY_RUBY_TEXT,
-      FCDATA_DECL(FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer),
+      FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
+                  FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer),
                   NS_NewRubyTextFrame) },
     { NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER,
       FCDATA_DECL(FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby),
                   NS_NewRubyTextContainerFrame) },
     { NS_STYLE_DISPLAY_TABLE,
       FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable) },
     { NS_STYLE_DISPLAY_INLINE_TABLE,
       FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable) },
@@ -9091,30 +9093,32 @@ nsCSSFrameConstructor::sPseudoParentData
     FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
                 FCDATA_USE_CHILD_ITEMS |
                 FCDATA_SKIP_FRAMESET,
                 NS_NewRubyFrame),
     &nsCSSAnonBoxes::ruby
   },
   { // Ruby Base
     FCDATA_DECL(FCDATA_USE_CHILD_ITEMS | 
+                FCDATA_IS_LINE_PARTICIPANT |
                 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer) |
                 FCDATA_SKIP_FRAMESET,
                 NS_NewRubyBaseFrame),
     &nsCSSAnonBoxes::rubyBase
   },
   { // Ruby Base Container
     FCDATA_DECL(FCDATA_USE_CHILD_ITEMS | 
                 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) |
                 FCDATA_SKIP_FRAMESET,
                 NS_NewRubyBaseContainerFrame),
     &nsCSSAnonBoxes::rubyBaseContainer
   },
   { // Ruby Text
     FCDATA_DECL(FCDATA_USE_CHILD_ITEMS |
+                FCDATA_IS_LINE_PARTICIPANT |
                 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer) |
                 FCDATA_SKIP_FRAMESET,
                 NS_NewRubyTextFrame),
     &nsCSSAnonBoxes::rubyText
   },
   { // Ruby Text Container
     FCDATA_DECL(FCDATA_USE_CHILD_ITEMS |
                 FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby) |
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -6927,8 +6927,44 @@ nsLayoutUtils::IsOutlineStyleAutoEnabled
   if (!sOutlineStyleAutoPrefCached) {
     sOutlineStyleAutoPrefCached = true;
     Preferences::AddBoolVarCache(&sOutlineStyleAutoEnabled,
                                  "layout.css.outline-style-auto.enabled",
                                  false);
   }
   return sOutlineStyleAutoEnabled;
 }
+
+/* static */ void
+nsLayoutUtils::SetBSizeFromFontMetrics(const nsIFrame* aFrame,
+                                       nsHTMLReflowMetrics& aMetrics,
+                                       const nsHTMLReflowState& aReflowState,
+                                       LogicalMargin aFramePadding, 
+                                       WritingMode aLineWM,
+                                       WritingMode aFrameWM)
+{
+  nsRefPtr<nsFontMetrics> fm;
+  float inflation = nsLayoutUtils::FontSizeInflationFor(aFrame);
+  nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm), inflation);
+  aReflowState.rendContext->SetFont(fm);
+
+  if (fm) {
+    // Compute final height of the frame.
+    //
+    // Do things the standard css2 way -- though it's hard to find it
+    // in the css2 spec! It's actually found in the css1 spec section
+    // 4.4 (you will have to read between the lines to really see
+    // it).
+    //
+    // The height of our box is the sum of our font size plus the top
+    // and bottom border and padding. The height of children do not
+    // affect our height.
+    aMetrics.SetBlockStartAscent(fm->MaxAscent());
+    aMetrics.BSize(aLineWM) = fm->MaxHeight();
+  } else {
+    NS_WARNING("Cannot get font metrics - defaulting sizes to 0");
+    aMetrics.SetBlockStartAscent(aMetrics.BSize(aLineWM) = 0);
+  }
+  aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent() +
+                               aFramePadding.BStart(aFrameWM));
+  aMetrics.BSize(aLineWM) +=
+    aReflowState.ComputedLogicalBorderPadding().BStartEnd(aFrameWM);
+}
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -22,16 +22,17 @@
 #include "nsStyleCoord.h"
 #include "nsStyleConsts.h"
 #include "nsGkAtoms.h"
 #include "nsRuleNode.h"
 #include "imgIContainer.h"
 #include "mozilla/gfx/2D.h"
 #include "Units.h"
 #include "mozilla/ToString.h"
+#include "nsHTMLReflowMetrics.h"
 
 #include <limits>
 #include <algorithm>
 
 class nsIFormControlFrame;
 class nsPresContext;
 class nsIContent;
 class nsIAtom;
@@ -2264,16 +2265,23 @@ public:
    */
   static bool GetOrMaybeCreateDisplayPort(nsDisplayListBuilder& aBuilder,
                                           nsIFrame* aScrollFrame,
                                           nsRect aDisplayPortBase,
                                           nsRect* aOutDisplayport);
 
   static bool IsOutlineStyleAutoEnabled();
 
+  static void SetBSizeFromFontMetrics(const nsIFrame* aFrame,
+                                      nsHTMLReflowMetrics& aMetrics,
+                                      const nsHTMLReflowState& aReflowState,
+                                      mozilla::LogicalMargin aFramePadding, 
+                                      mozilla::WritingMode aLineWM,
+                                      mozilla::WritingMode aFrameWM);
+
 private:
   static uint32_t sFontSizeInflationEmPerLine;
   static uint32_t sFontSizeInflationMinTwips;
   static uint32_t sFontSizeInflationLineThreshold;
   static int32_t  sFontSizeInflationMappingIntercept;
   static uint32_t sFontSizeInflationMaxRatio;
   static bool sFontSizeInflationForceEnabled;
   static bool sFontSizeInflationDisabledInMasterProcess;
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -700,42 +700,18 @@ nsInlineFrame::ReflowFrames(nsPresContex
    */
   if ((NS_FRAME_IS_COMPLETE(aStatus) &&
        !LastInFlow()->GetNextContinuation() &&
        !FrameIsNonLastInIBSplit()) ||
       boxDecorationBreakClone) {
     aMetrics.ISize(lineWM) += framePadding.IEnd(frameWM);
   }
 
-  nsRefPtr<nsFontMetrics> fm;
-  float inflation = nsLayoutUtils::FontSizeInflationFor(this);
-  nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), inflation);
-  aReflowState.rendContext->SetFont(fm);
-
-  if (fm) {
-    // Compute final height of the frame.
-    //
-    // Do things the standard css2 way -- though it's hard to find it
-    // in the css2 spec! It's actually found in the css1 spec section
-    // 4.4 (you will have to read between the lines to really see
-    // it).
-    //
-    // The height of our box is the sum of our font size plus the top
-    // and bottom border and padding. The height of children do not
-    // affect our height.
-    aMetrics.SetBlockStartAscent(fm->MaxAscent());
-    aMetrics.BSize(lineWM) = fm->MaxHeight();
-  } else {
-    NS_WARNING("Cannot get font metrics - defaulting sizes to 0");
-    aMetrics.SetBlockStartAscent(aMetrics.BSize(lineWM) = 0);
-  }
-  aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent() +
-                               framePadding.BStart(frameWM));
-  aMetrics.BSize(lineWM) +=
-    aReflowState.ComputedLogicalBorderPadding().BStartEnd(frameWM);
+  nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, aReflowState,
+                                         framePadding, lineWM, frameWM);
 
   // For now our overflow area is zero. The real value will be
   // computed in |nsLineLayout::RelativePositionFrames|.
   aMetrics.mOverflowAreas.Clear();
 
 #ifdef NOISY_FINAL_SIZE
   ListTag(stdout);
   printf(": metrics=%d,%d ascent=%d\n",
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -2729,8 +2729,26 @@ nsLineLayout::RelativePositionFrames(Per
   // overflow rect...
   if (psd->mFrame) {
     PerFrameData* spanPFD = psd->mFrame;
     nsIFrame* frame = spanPFD->mFrame;
     frame->FinishAndStoreOverflow(overflowAreas, frame->GetSize());
   }
   aOverflowAreas = overflowAreas;
 }
+
+void
+nsLineLayout::AdvanceICoord(nscoord aAmount)
+{
+  mCurrentSpan->mICoord += aAmount;
+}
+
+WritingMode
+nsLineLayout::GetWritingMode()
+{
+  return mRootSpan->mWritingMode;
+}
+
+nscoord
+nsLineLayout::GetCurrentICoord()
+{
+  return mCurrentSpan->mICoord;
+}
--- a/layout/generic/nsLineLayout.h
+++ b/layout/generic/nsLineLayout.h
@@ -311,16 +311,30 @@ public:
    * the right edge for RTL blocks and from the left edge for LTR blocks.
    * In other words, the current frame's distance from the line container's
    * start content edge is:
    * <code>GetCurrentFrameInlineDistanceFromBlock() - lineContainer->GetUsedBorderAndPadding().left</code>
    * Note the use of <code>.left</code> for both LTR and RTL line containers.
    */
   nscoord GetCurrentFrameInlineDistanceFromBlock();
 
+  /**
+   * Move the inline position where the next frame will be reflowed forward by
+   * aAmount.
+   */
+  void AdvanceICoord(nscoord aAmount);
+  /**
+   * Returns the writing mode for the root span.
+   */
+  mozilla::WritingMode GetWritingMode();
+  /**
+   * Returns the inline position where the next frame will be reflowed.
+   */
+  nscoord GetCurrentICoord();
+
 protected:
   // This state is constant for a given block frame doing line layout
   nsFloatManager* mFloatManager;
   const nsStyleText* mStyleText; // for the block
   const nsHTMLReflowState* mBlockReflowState;
 
   nsIContent* mLastOptionalBreakContent;
   nsIContent* mForceBreakContent;
--- a/layout/generic/nsRubyBaseContainerFrame.cpp
+++ b/layout/generic/nsRubyBaseContainerFrame.cpp
@@ -2,18 +2,22 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code is subject to the terms of the Mozilla Public License
  * 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" */
 
 #include "nsRubyBaseContainerFrame.h"
+#include "nsLineLayout.h"
 #include "nsPresContext.h"
 #include "nsStyleContext.h"
+#include "WritingModes.h"
+
+using namespace mozilla;
 
 //----------------------------------------------------------------------
 
 // Frame class boilerplate
 // =======================
 
 NS_QUERYFRAME_HEAD(nsRubyBaseContainerFrame)
   NS_QUERYFRAME_ENTRY(nsRubyBaseContainerFrame)
@@ -42,8 +46,167 @@ nsRubyBaseContainerFrame::GetType() cons
 
 #ifdef DEBUG_FRAME_DUMP
 nsresult
 nsRubyBaseContainerFrame::GetFrameName(nsAString& aResult) const
 {
   return MakeFrameName(NS_LITERAL_STRING("RubyBaseContainer"), aResult);
 }
 #endif
+
+
+void nsRubyBaseContainerFrame::AppendTextContainer(nsIFrame* aFrame)
+{
+  nsRubyTextContainerFrame* rtcFrame = do_QueryFrame(aFrame);
+  if (rtcFrame) {
+    mTextContainers.AppendElement(rtcFrame);
+  }
+}
+
+void nsRubyBaseContainerFrame::ClearTextContainers() {
+  mTextContainers.Clear();
+}
+
+/* virtual */ void
+nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext,
+                                 nsHTMLReflowMetrics& aDesiredSize,
+                                 const nsHTMLReflowState& aReflowState,
+                                 nsReflowStatus& aStatus)
+{
+  DO_GLOBAL_REFLOW_COUNT("nsRubyBaseContainerFrame");
+  DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
+
+  if (!aReflowState.mLineLayout) {
+    NS_ASSERTION(
+      aReflowState.mLineLayout,
+      "No line layout provided to RubyBaseContainerFrame reflow method.");
+    aStatus = NS_FRAME_COMPLETE;
+    return;
+  }
+
+  aStatus = NS_FRAME_COMPLETE;
+  nscoord isize = 0;
+  int baseNum = 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());
+
+  // Begin the line layout for each ruby text container in advance.
+  for (uint32_t i = 0; i < mTextContainers.Length(); 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 (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
+    nsIFrame* rbFrame = e.get();
+    if (rbFrame->GetType() != nsGkAtoms::rubyBaseFrame) {
+      NS_ASSERTION(false, "Unrecognized child type for ruby base container");
+      continue;
+    }
+
+    nsReflowStatus frameReflowStatus;
+    nsHTMLReflowMetrics metrics(aReflowState, aDesiredSize.mFlags);
+
+    // 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 prefWidth = rbFrame->GetPrefISize(aReflowState.rendContext);
+    nscoord textWidth = 0;
+
+    for (uint32_t i = 0; i < mTextContainers.Length(); i++) {
+      nsRubyTextFrame* rtFrame = do_QueryFrame(mTextContainers.ElementAt(i)->
+                          PrincipalChildList().FrameAt(baseNum));
+      if (rtFrame) {
+        int newWidth = rtFrame->GetPrefISize(aReflowState.rendContext);
+        if (newWidth > textWidth) {
+          textWidth = newWidth;
+        }
+      }
+    }
+    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();
+
+    bool pushedFrame;
+    aReflowState.mLineLayout->ReflowFrame(rbFrame, frameReflowStatus,
+                                          &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);
+
+    // Now reflow the ruby text boxes that correspond to this ruby base box.
+    for (uint32_t i = 0; i < mTextContainers.Length(); i++) {
+      nsRubyTextFrame* rtFrame = do_QueryFrame(mTextContainers.ElementAt(i)->
+                          PrincipalChildList().FrameAt(baseNum));
+      nsRubyTextContainerFrame* rtcFrame = mTextContainers.ElementAt(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);
+      }
+    }
+    baseNum++;
+  }
+
+  // Reflow ruby annotations which do not have a corresponding ruby base box due
+  // to a ruby base shortage. According to the spec, an empty ruby base is
+  // assumed to exist for each of these annotations.
+  bool continueReflow = true;
+  while (continueReflow) {
+    continueReflow = false;
+    for (uint32_t i = 0; i < mTextContainers.Length(); i++) {
+      nsRubyTextFrame* rtFrame = do_QueryFrame(mTextContainers.ElementAt(i)->
+                          PrincipalChildList().FrameAt(baseNum));
+      nsRubyTextContainerFrame* rtcFrame = mTextContainers.ElementAt(i);
+      if (rtFrame) {
+        continueReflow = true;
+        nsHTMLReflowMetrics rtcMetrics(*aReflowState.parentReflowState,
+                                       aDesiredSize.mFlags);
+        nsHTMLReflowState rtcReflowState(aPresContext,
+                                         *aReflowState.parentReflowState,
+                                         rtcFrame, availSize);
+        rtcReflowState.mLineLayout = rtcFrame->GetLineLayout();
+        rtcFrame->ReflowRubyTextFrame(rtFrame, nullptr, baseStart,
+                                      aPresContext, rtcMetrics,
+                                      rtcReflowState);
+        // Update the inline coord to make space for subsequent ruby annotations
+        // (since there is no corresponding base inline size to use).
+        baseStart += rtcMetrics.ISize(lineWM);
+      }
+    }
+    baseNum++;
+  }
+
+  aDesiredSize.ISize(lineWM) = isize;
+  nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState,
+                                         borderPadding, lineWM, frameWM);
+}
--- a/layout/generic/nsRubyBaseContainerFrame.h
+++ b/layout/generic/nsRubyBaseContainerFrame.h
@@ -5,16 +5,19 @@
  * http://mozilla.org/MPL/2.0/. */
 
 /* rendering object for CSS "display: ruby-base-container" */
 
 #ifndef nsRubyBaseContainerFrame_h___
 #define nsRubyBaseContainerFrame_h___
 
 #include "nsContainerFrame.h"
+#include "nsRubyTextContainerFrame.h"
+#include "nsRubyBaseFrame.h"
+#include "nsRubyTextFrame.h"
 
 /**
  * Factory function.
  * @return a newly allocated nsRubyBaseContainerFrame (infallible)
  */
 nsContainerFrame* NS_NewRubyBaseContainerFrame(nsIPresShell* aPresShell,
                                                nsStyleContext* aContext);
 
@@ -22,21 +25,35 @@ class nsRubyBaseContainerFrame MOZ_FINAL
 {
 public:
   NS_DECL_FRAMEARENA_HELPERS
   NS_DECL_QUERYFRAME_TARGET(nsRubyBaseContainerFrame)
   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 AppendTextContainer(nsIFrame* aFrame);
+  void ClearTextContainers();
+
 protected:
   friend nsContainerFrame*
     NS_NewRubyBaseContainerFrame(nsIPresShell* aPresShell,
                                  nsStyleContext* aContext);
   nsRubyBaseContainerFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) {}
+  /*
+   * The ruby text containers that belong to the ruby segment defined by
+   * this ruby base container. These text containers are located at the start
+   * of reflow for the ruby frame (parent) and cleared at the end of that
+   * reflow.
+   */
+  nsTArray<nsRubyTextContainerFrame*> mTextContainers;
 };
 
 #endif /* nsRubyBaseContainerFrame_h___ */
--- a/layout/generic/nsRubyBaseFrame.cpp
+++ b/layout/generic/nsRubyBaseFrame.cpp
@@ -2,18 +2,22 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code is subject to the terms of the Mozilla Public License
  * 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" */
 
 #include "nsRubyBaseFrame.h"
+#include "nsLineLayout.h"
 #include "nsPresContext.h"
 #include "nsStyleContext.h"
+#include "WritingModes.h"
+
+using namespace mozilla;
 
 //----------------------------------------------------------------------
 
 // Frame class boilerplate
 // =======================
 
 NS_QUERYFRAME_HEAD(nsRubyBaseFrame)
   NS_QUERYFRAME_ENTRY(nsRubyBaseFrame)
@@ -42,8 +46,103 @@ nsRubyBaseFrame::GetType() const
 
 #ifdef DEBUG_FRAME_DUMP
 nsresult
 nsRubyBaseFrame::GetFrameName(nsAString& aResult) const
 {
   return MakeFrameName(NS_LITERAL_STRING("RubyBase"), aResult);
 }
 #endif
+
+nscoord
+nsRubyBaseFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+  return nsLayoutUtils::MinISizeFromInline(this, aRenderingContext);
+}
+
+nscoord
+nsRubyBaseFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+  return nsLayoutUtils::PrefISizeFromInline(this, aRenderingContext);
+}
+
+/* virtual */ void
+nsRubyBaseFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext,
+                                   nsIFrame::InlineMinISizeData *aData)
+{
+  for (nsFrameList::Enumerator e(PrincipalChildList()); !e.AtEnd(); e.Next()) {
+    e.get()->AddInlineMinISize(aRenderingContext, aData);
+  }
+}
+
+/* virtual */ void
+nsRubyBaseFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+                                    nsIFrame::InlinePrefISizeData *aData)
+{
+  for (nsFrameList::Enumerator e(PrincipalChildList()); !e.AtEnd(); e.Next()) {
+    e.get()->AddInlinePrefISize(aRenderingContext, aData);
+  }
+}
+
+/* virtual */ bool 
+nsRubyBaseFrame::IsFrameOfType(uint32_t aFlags) const 
+{
+  return nsContainerFrame::IsFrameOfType(aFlags & 
+         ~(nsIFrame::eLineParticipant));
+}
+
+/* virtual */ nscoord
+nsRubyBaseFrame::GetLogicalBaseline(WritingMode aWritingMode) const
+{
+  return mBaseline;
+}
+
+/* virtual */ void
+nsRubyBaseFrame::Reflow(nsPresContext* aPresContext,
+                        nsHTMLReflowMetrics& aDesiredSize,
+                        const nsHTMLReflowState& aReflowState,
+                        nsReflowStatus& aStatus)
+{
+  DO_GLOBAL_REFLOW_COUNT("nsRubyBaseFrame");
+  DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
+  
+  if (!aReflowState.mLineLayout) {
+    NS_ASSERTION(aReflowState.mLineLayout,
+                 "No line layout provided to RubyBaseFrame reflow method.");
+    aStatus = NS_FRAME_COMPLETE;
+    return;
+  }
+
+  WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
+  WritingMode frameWM = aReflowState.GetWritingMode();
+  aStatus = NS_FRAME_COMPLETE;
+  LogicalSize availSize(lineWM, aReflowState.AvailableWidth(),
+                        aReflowState.AvailableHeight());
+  LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding();
+
+  // Begin the span for the ruby base frame
+  nscoord availableISize = aReflowState.AvailableISize();
+  NS_ASSERTION(availableISize != NS_UNCONSTRAINEDSIZE,
+               "should no longer use available widths");
+  // Subtract off inline axis border+padding from availableISize
+  availableISize -= borderPadding.IStartEnd(frameWM);
+  aReflowState.mLineLayout->BeginSpan(this, &aReflowState,
+                                      borderPadding.IStart(frameWM),
+                                      availableISize, &mBaseline);
+
+  for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
+    nsReflowStatus frameReflowStatus;
+    nsHTMLReflowMetrics metrics(aReflowState, aDesiredSize.mFlags);
+
+    bool pushedFrame;
+    aReflowState.mLineLayout->ReflowFrame(e.get(), frameReflowStatus,
+                                          &metrics, pushedFrame);
+    NS_ASSERTION(!pushedFrame,
+                 "Ruby line breaking is not yet implemented");
+
+    e.get()->SetSize(LogicalSize(lineWM, metrics.ISize(lineWM),
+                                 metrics.BSize(lineWM)));
+  }
+
+  aDesiredSize.ISize(lineWM) = aReflowState.mLineLayout->EndSpan(this);
+  nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState,
+                                         borderPadding, lineWM, frameWM);
+}
--- a/layout/generic/nsRubyBaseFrame.h
+++ b/layout/generic/nsRubyBaseFrame.h
@@ -22,20 +22,34 @@ class nsRubyBaseFrame MOZ_FINAL : public
 {
 public:
   NS_DECL_FRAMEARENA_HELPERS
   NS_DECL_QUERYFRAME_TARGET(nsRubyBaseFrame)
   NS_DECL_QUERYFRAME
 
   // nsIFrame overrides
   virtual nsIAtom* GetType() const MOZ_OVERRIDE;
+  virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext,
+                                 InlineMinISizeData *aData) MOZ_OVERRIDE;
+  virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+                                  InlinePrefISizeData *aData) MOZ_OVERRIDE;
+  virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
+  virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext);
+  virtual void Reflow(nsPresContext* aPresContext,
+                      nsHTMLReflowMetrics& aDesiredSize,
+                      const nsHTMLReflowState& aReflowState,
+                      nsReflowStatus& aStatus) MOZ_OVERRIDE;
+  virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE;
+  virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode)
+    const MOZ_OVERRIDE;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE;
 #endif
 
 protected:
   friend nsContainerFrame* NS_NewRubyBaseFrame(nsIPresShell* aPresShell,
                                                nsStyleContext* aContext);
   nsRubyBaseFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) {}
+  nscoord mBaseline;
 };
 
 #endif /* nsRubyBaseFrame_h___ */
--- a/layout/generic/nsRubyFrame.cpp
+++ b/layout/generic/nsRubyFrame.cpp
@@ -1,18 +1,24 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code is subject to the terms of the Mozilla Public License
  * 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" */
 #include "nsRubyFrame.h"
+#include "nsLineLayout.h"
 #include "nsPresContext.h"
 #include "nsStyleContext.h"
+#include "WritingModes.h"
+#include "nsRubyBaseContainerFrame.h"
+#include "nsRubyTextContainerFrame.h"
+
+using namespace mozilla;
 
 //----------------------------------------------------------------------
 
 // Frame class boilerplate
 // =======================
 
 NS_QUERYFRAME_HEAD(nsRubyFrame)
   NS_QUERYFRAME_ENTRY(nsRubyFrame)
@@ -22,34 +28,257 @@ NS_IMPL_FRAMEARENA_HELPERS(nsRubyFrame)
 
 nsContainerFrame*
 NS_NewRubyFrame(nsIPresShell* aPresShell,
                 nsStyleContext* aContext)
 {
   return new (aPresShell) nsRubyFrame(aContext);
 }
 
-
 //----------------------------------------------------------------------
 
 // nsRubyFrame Method Implementations
 // ==================================
 
 nsIAtom*
 nsRubyFrame::GetType() const
 {
   return nsGkAtoms::rubyFrame;
 }
 
-bool
+/* virtual */ bool
 nsRubyFrame::IsFrameOfType(uint32_t aFlags) const
 {
   return nsContainerFrame::IsFrameOfType(aFlags &
-    ~(nsIFrame::eBidiInlineContainer | nsIFrame::eLineParticipant));
+    ~(nsIFrame::eLineParticipant));
 }
 
 #ifdef DEBUG_FRAME_DUMP
 nsresult
 nsRubyFrame::GetFrameName(nsAString& aResult) const
 {
   return MakeFrameName(NS_LITERAL_STRING("Ruby"), aResult);
 }
 #endif
+
+void
+nsRubyFrame::CalculateColSizes(nsRenderingContext* aRenderingContext,
+                               nsTArray<nscoord>& aColSizes)
+{
+  nsFrameList::Enumerator e(this->PrincipalChildList());
+  uint32_t annotationNum = 0;
+  int segmentNum = -1;
+
+  nsTArray<int> segmentBaseCounts;
+
+  for(; !e.AtEnd(); e.Next()) {
+    nsIFrame* childFrame = e.get();
+    if (childFrame->GetType() == nsGkAtoms::rubyBaseContainerFrame) {
+      segmentNum++;
+      segmentBaseCounts.AppendElement(0);
+      nsFrameList::Enumerator bases(childFrame->PrincipalChildList());
+      for(; !bases.AtEnd(); bases.Next()) {
+        aColSizes.AppendElement(bases.get()->GetPrefISize(aRenderingContext));
+        segmentBaseCounts.ElementAt(segmentNum)++;
+      }
+    } else if (childFrame->GetType() == nsGkAtoms::rubyTextContainerFrame) {
+      if (segmentNum == -1) {
+        // No rbc exists for first segment, so act as if there is one
+        segmentNum++;
+        segmentBaseCounts.AppendElement(1);
+        aColSizes.AppendElement(0);
+      }
+      nsFrameList::Enumerator annotations(childFrame->PrincipalChildList());
+      uint32_t baseCount = segmentBaseCounts.ElementAt(segmentNum);
+      for(; !annotations.AtEnd(); annotations.Next()) {
+        nsIFrame* annotationFrame = annotations.get();
+        if (annotationNum > baseCount) {
+          aColSizes.AppendElement(annotationFrame->
+            GetPrefISize(aRenderingContext));
+          baseCount++;
+          segmentBaseCounts.ElementAt(segmentNum) = baseCount;
+          annotationNum++;
+        } else if (annotationNum < baseCount - 1) {
+          //there are fewer annotations than bases, so the last annotation is
+          //associated with (spans) the remaining bases. This means these
+          //columns can't be broken up, so gather their entire ISize in one
+          //entry of aColSizes and clear the other entries.
+          int baseSum = 0;
+          for (uint32_t i = annotationNum; i < annotationNum + baseCount; i++) {
+            baseSum += aColSizes.ElementAt(i);
+            if (i > annotationNum) {
+              aColSizes.ElementAt(i) = 0;
+            }
+          }
+          aColSizes.ElementAt(annotationNum) =
+            std::max(baseSum, annotationFrame->GetPrefISize(aRenderingContext));
+          annotationNum = baseCount;
+        } else {
+          aColSizes.ElementAt(annotationNum) =
+            std::max(aColSizes.ElementAt(annotationNum),
+                     annotationFrame->GetPrefISize(aRenderingContext));
+          annotationNum++;
+        }
+      }
+    } else {
+      NS_ASSERTION(false, "Unrecognized child type for ruby frame.");
+    }
+  }
+}
+
+/* virtual */ void
+nsRubyFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext,
+                               nsIFrame::InlineMinISizeData *aData)
+{
+  //FIXME: This needs to handle the cases where it's possible for a ruby base to
+  //break, as well as forced breaks.
+  nsTArray<int> colSizes;
+  CalculateColSizes(aRenderingContext, colSizes);
+
+  nscoord max = 0;
+  for (uint32_t i = 0; i < colSizes.Length(); i++) {
+    if (colSizes.ElementAt(i) > max) {
+      max = colSizes.ElementAt(i);
+    }
+  }
+
+  aData->currentLine += max;
+}
+
+/* virtual */ void
+nsRubyFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+                                nsIFrame::InlinePrefISizeData *aData)
+{
+  nsTArray<int> colSizes;
+  CalculateColSizes(aRenderingContext, colSizes);
+
+  nscoord sum = 0;
+  for (uint32_t i = 0; i < colSizes.Length(); i++) {
+    sum += colSizes.ElementAt(i);
+  }
+
+  aData->currentLine += sum;
+}
+
+/* virtual */ nscoord
+nsRubyFrame::GetLogicalBaseline(WritingMode aWritingMode) const
+{
+  return mBaseline;
+}
+
+/* virtual */ void
+nsRubyFrame::Reflow(nsPresContext* aPresContext,
+                    nsHTMLReflowMetrics& aDesiredSize,
+                    const nsHTMLReflowState& aReflowState,
+                    nsReflowStatus& aStatus)
+{
+  DO_GLOBAL_REFLOW_COUNT("nsRubyFrame");
+  DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
+  
+  if (!aReflowState.mLineLayout) {
+    NS_ASSERTION(aReflowState.mLineLayout,
+                 "No line layout provided to RubyFrame reflow method.");
+    aStatus = NS_FRAME_COMPLETE;
+    return;
+  }
+
+  // Begin the span for the ruby frame
+  WritingMode frameWM = aReflowState.GetWritingMode();
+  WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
+  LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding();
+  nscoord availableISize = aReflowState.AvailableISize();
+  NS_ASSERTION(availableISize != NS_UNCONSTRAINEDSIZE,
+               "should no longer use available widths");
+  // Subtract off inline axis border+padding from availableISize
+  availableISize -= borderPadding.IStartEnd(frameWM);
+  aReflowState.mLineLayout->BeginSpan(this, &aReflowState,
+                                      borderPadding.IStart(frameWM),
+                                      availableISize, &mBaseline);
+
+  // FIXME: line breaking / continuations not yet implemented
+  aStatus = NS_FRAME_COMPLETE;
+  LogicalSize availSize(lineWM, aReflowState.AvailableISize(),
+                        aReflowState.AvailableBSize());
+  // The ruby base container for the current segment
+  nsRubyBaseContainerFrame* segmentRBC = nullptr;
+  nscoord annotationBSize = 0;
+  for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
+    nsIFrame* childFrame = e.get();
+    if (e.get()->GetType() == nsGkAtoms::rubyBaseContainerFrame) {
+      if (segmentRBC) {
+        annotationBSize = 0;
+      }
+
+      // Figure out what all the text containers are for this segment (necessary
+      // for reflow calculations)
+      segmentRBC = do_QueryFrame(childFrame);
+      nsFrameList::Enumerator segment(e);
+      segment.Next();
+      while (!segment.AtEnd() && (segment.get()->GetType() !=
+             nsGkAtoms::rubyBaseContainerFrame)) {
+        if (segment.get()->GetType() == nsGkAtoms::rubyTextContainerFrame) {
+          segmentRBC->AppendTextContainer(segment.get());
+        }
+        segment.Next();
+      }
+
+      nsReflowStatus frameReflowStatus;
+      nsHTMLReflowMetrics metrics(aReflowState, aDesiredSize.mFlags);
+      nsHTMLReflowState childReflowState(aPresContext, aReflowState,
+                                         childFrame, availSize);
+      childReflowState.mLineLayout = aReflowState.mLineLayout;
+      childFrame->Reflow(aPresContext, metrics, childReflowState,
+                         frameReflowStatus);
+      NS_ASSERTION(frameReflowStatus == NS_FRAME_COMPLETE,
+                 "Ruby line breaking is not yet implemented");
+      childFrame->SetSize(LogicalSize(lineWM, metrics.ISize(lineWM),
+                                      metrics.BSize(lineWM)));
+      FinishReflowChild(childFrame, aPresContext, metrics, &childReflowState, 0,
+                        0, NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_MOVE_VIEW);
+
+    } else if (childFrame->GetType() == nsGkAtoms::rubyTextContainerFrame) {
+      nsReflowStatus frameReflowStatus;
+      nsHTMLReflowMetrics metrics(aReflowState, aDesiredSize.mFlags);
+      nsHTMLReflowState childReflowState(aPresContext, aReflowState, childFrame,
+                                         availSize);
+      childReflowState.mLineLayout = aReflowState.mLineLayout;
+      childFrame->Reflow(aPresContext, metrics, childReflowState,
+                      frameReflowStatus);
+      NS_ASSERTION(frameReflowStatus == NS_FRAME_COMPLETE,
+                 "Ruby line breaking is not yet implemented");
+      annotationBSize += metrics.BSize(lineWM);
+      childFrame->SetSize(LogicalSize(lineWM, metrics.ISize(lineWM),
+                                      metrics.BSize(lineWM)));
+      // FIXME: This is a temporary calculation for finding the block coordinate
+      // of the ruby text container. A better one replace it once it's been
+      // spec'ed.
+      nscoord baseContainerBCoord; 
+      if (segmentRBC) {
+        // Find the starting block coordinate of the ruby base container for
+        // this segment. For now, the ruby text container will be placed so that
+        // its bottom edge touches this coordinate.
+        baseContainerBCoord = segmentRBC->
+          GetLogicalPosition(this->GetParent()->GetLogicalSize().ISize(lineWM)).
+          B(lineWM);
+      } else {
+        baseContainerBCoord = 0;
+      }
+      FinishReflowChild(childFrame, aPresContext, metrics, &childReflowState, 0,
+                        baseContainerBCoord - metrics.BSize(lineWM), 0);
+    } else {
+      NS_NOTREACHED(
+        "Unrecognized child type for ruby frame will not be reflowed."); 
+    }
+  }
+
+  // Null the pointers between child frames.
+  for (nsFrameList::Enumerator children(mFrames); !children.AtEnd();
+       children.Next()) {
+    nsRubyBaseContainerFrame* rbcFrame = do_QueryFrame(children.get());
+    if (rbcFrame) {
+      rbcFrame->ClearTextContainers();
+    }
+  }
+
+  aDesiredSize.ISize(lineWM) = aReflowState.mLineLayout->EndSpan(this);
+  nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState,
+                                         borderPadding, lineWM, frameWM);
+}
--- a/layout/generic/nsRubyFrame.h
+++ b/layout/generic/nsRubyFrame.h
@@ -23,20 +23,33 @@ class nsRubyFrame MOZ_FINAL : public nsC
 public:
   NS_DECL_FRAMEARENA_HELPERS
   NS_DECL_QUERYFRAME_TARGET(nsRubyFrame)
   NS_DECL_QUERYFRAME
 
   // nsIFrame overrides
   virtual nsIAtom* GetType() const MOZ_OVERRIDE;
   virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE;
+  virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext,
+                                 InlineMinISizeData *aData) MOZ_OVERRIDE;
+  virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+                                  InlinePrefISizeData *aData) MOZ_OVERRIDE;
+  virtual void Reflow(nsPresContext* aPresContext,
+                      nsHTMLReflowMetrics& aDesiredSize,
+                      const nsHTMLReflowState& aReflowState,
+                      nsReflowStatus& aStatus) MOZ_OVERRIDE;
+  virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode)
+    const MOZ_OVERRIDE;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE;
 #endif
 
 protected:
   friend nsContainerFrame* NS_NewRubyFrame(nsIPresShell* aPresShell,
                                            nsStyleContext* aContext);
   nsRubyFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) {}
+  void CalculateColSizes(nsRenderingContext* aRenderingContext,
+                         nsTArray<nscoord>& aColSizes);
+  nscoord mBaseline;
 };
 
 #endif /* nsRubyFrame_h___ */
--- a/layout/generic/nsRubyTextContainerFrame.cpp
+++ b/layout/generic/nsRubyTextContainerFrame.cpp
@@ -4,16 +4,18 @@
  * 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-text-container" */
 
 #include "nsRubyTextContainerFrame.h"
 #include "nsPresContext.h"
 #include "nsStyleContext.h"
+#include "WritingModes.h"
+#include "mozilla/UniquePtr.h"
 
 //----------------------------------------------------------------------
 
 // Frame class boilerplate
 // =======================
 
 NS_QUERYFRAME_HEAD(nsRubyTextContainerFrame)
   NS_QUERYFRAME_ENTRY(nsRubyTextContainerFrame)
@@ -42,8 +44,136 @@ 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();
+
+  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
@@ -5,16 +5,19 @@
  * http://mozilla.org/MPL/2.0/. */
 
 /* rendering object for CSS "display: ruby-text-container" */
 
 #ifndef nsRubyTextContainerFrame_h___
 #define nsRubyTextContainerFrame_h___
 
 #include "nsBlockFrame.h"
+#include "nsRubyBaseFrame.h"
+#include "nsRubyTextFrame.h"
+#include "nsLineLayout.h"
 
 /**
  * Factory function.
  * @return a newly allocated nsRubyTextContainerFrame (infallible)
  */
 nsContainerFrame* NS_NewRubyTextContainerFrame(nsIPresShell* aPresShell,
                                                nsStyleContext* aContext);
 
@@ -25,21 +28,41 @@ class nsRubyTextContainerFrame MOZ_FINAL
 {
 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(); };
+
 protected:
   friend nsContainerFrame*
     NS_NewRubyTextContainerFrame(nsIPresShell* aPresShell,
                                  nsStyleContext* aContext);
   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;
+  // 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___ */
--- a/layout/generic/nsRubyTextFrame.cpp
+++ b/layout/generic/nsRubyTextFrame.cpp
@@ -4,16 +4,20 @@
  * 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-text" */
 
 #include "nsRubyTextFrame.h"
 #include "nsPresContext.h"
 #include "nsStyleContext.h"
+#include "WritingModes.h"
+#include "nsLineLayout.h"
+
+using namespace mozilla;
 
 //----------------------------------------------------------------------
 
 // Frame class boilerplate
 // =======================
 
 NS_QUERYFRAME_HEAD(nsRubyTextFrame)
   NS_QUERYFRAME_ENTRY(nsRubyTextFrame)
@@ -42,8 +46,113 @@ nsRubyTextFrame::GetType() const
 
 #ifdef DEBUG_FRAME_DUMP
 nsresult
 nsRubyTextFrame::GetFrameName(nsAString& aResult) const
 {
   return MakeFrameName(NS_LITERAL_STRING("RubyText"), aResult);
 }
 #endif
+
+/* virtual */ bool 
+nsRubyTextFrame::IsFrameOfType(uint32_t aFlags) const 
+{
+  return nsContainerFrame::IsFrameOfType(aFlags & 
+         ~(nsIFrame::eLineParticipant));
+}
+
+/* virtual */ nscoord
+nsRubyTextFrame::GetMinISize(nsRenderingContext *aRenderingContext)
+{
+  return nsLayoutUtils::MinISizeFromInline(this, aRenderingContext);
+}
+
+/* virtual */ nscoord
+nsRubyTextFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
+{
+  return nsLayoutUtils::PrefISizeFromInline(this, aRenderingContext);
+}
+
+/* virtual */ void
+nsRubyTextFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext,
+                                   nsIFrame::InlineMinISizeData *aData)
+{
+  // FIXME: See the fixme in AddInlinePrefISize.
+  aData->lineContainer = this;
+
+  for (nsFrameList::Enumerator e(PrincipalChildList()); !e.AtEnd(); e.Next()) {
+    e.get()->AddInlineMinISize(aRenderingContext, aData);
+  }
+}
+
+/* virtual */ void
+nsRubyTextFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+                                    nsIFrame::InlinePrefISizeData *aData)
+{
+  // FIXME: We shouldn't need to set this, but it prevents us from tripping an
+  // assertion in nsTextFrame.cpp because FindLineContainer on a child frame will
+  // return the ruby text box (us) instead of the ruby text container (our
+  // parent). A fix would need to be made to FindLineContainer and/or
+  // CanContinueTextRun so that this line can be removed.
+  aData->lineContainer = this;
+
+  for (nsFrameList::Enumerator e(PrincipalChildList()); !e.AtEnd(); e.Next()) {
+    e.get()->AddInlinePrefISize(aRenderingContext, aData);
+  }
+}
+
+/* virtual */ nscoord
+nsRubyTextFrame::GetLogicalBaseline(WritingMode aWritingMode) const
+{
+  return mBaseline;
+}
+
+/* virtual */ void
+nsRubyTextFrame::Reflow(nsPresContext* aPresContext,
+                        nsHTMLReflowMetrics& aDesiredSize,
+                        const nsHTMLReflowState& aReflowState,
+                        nsReflowStatus& aStatus)
+{
+  DO_GLOBAL_REFLOW_COUNT("nsRubyBaseFrame");
+  DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
+  
+  if (!aReflowState.mLineLayout) {
+    NS_ASSERTION(aReflowState.mLineLayout,
+                 "No line layout provided to RubyTextFrame reflow method.");
+    aStatus = NS_FRAME_COMPLETE;
+    return;
+  }
+
+  WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode();
+  WritingMode frameWM = aReflowState.GetWritingMode();
+  LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding();
+  aStatus = NS_FRAME_COMPLETE;
+  LogicalSize availSize(lineWM, aReflowState.AvailableWidth(),
+                        aReflowState.AvailableHeight());
+
+  // Begin the span for the ruby text frame
+  nscoord availableISize = aReflowState.AvailableISize();
+  NS_ASSERTION(availableISize != NS_UNCONSTRAINEDSIZE,
+               "should no longer use available widths");
+  // Subtract off inline axis border+padding from availableISize
+  availableISize -= borderPadding.IStartEnd(frameWM);
+  aReflowState.mLineLayout->BeginSpan(this, &aReflowState,
+                                      borderPadding.IStart(frameWM),
+                                      availableISize, &mBaseline);
+
+  for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
+    nsReflowStatus frameReflowStatus;
+    nsHTMLReflowMetrics metrics(aReflowState, aDesiredSize.mFlags);
+
+    bool pushedFrame;
+    aReflowState.mLineLayout->ReflowFrame(e.get(), frameReflowStatus,
+                                          &metrics, pushedFrame);
+    NS_ASSERTION(!pushedFrame,
+                 "Ruby line breaking is not yet implemented");
+    e.get()->SetSize(LogicalSize(lineWM, metrics.ISize(lineWM),
+                                 metrics.BSize(lineWM)));
+  }
+
+  aDesiredSize.ISize(lineWM) = aReflowState.mLineLayout->EndSpan(this);
+  nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState,
+                                         borderPadding, lineWM, frameWM);
+
+}
--- a/layout/generic/nsRubyTextFrame.h
+++ b/layout/generic/nsRubyTextFrame.h
@@ -22,20 +22,34 @@ class nsRubyTextFrame MOZ_FINAL : public
 {
 public:
   NS_DECL_FRAMEARENA_HELPERS
   NS_DECL_QUERYFRAME_TARGET(nsRubyTextFrame)
   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;
+  virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE;
+  virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext,
+                                 InlineMinISizeData *aData) MOZ_OVERRIDE;
+  virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
+                                  InlinePrefISizeData *aData) MOZ_OVERRIDE;
+  virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
+  virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
+  virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode)
+    const MOZ_OVERRIDE;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE;
 #endif
 
 protected:
   friend nsContainerFrame* NS_NewRubyTextFrame(nsIPresShell* aPresShell,
                                                nsStyleContext* aContext);
   nsRubyTextFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) {}
+  nscoord mBaseline;
 };
 
 #endif /* nsRubyTextFrame_h___ */
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -1126,16 +1126,25 @@ CanTextCrossFrameBoundary(nsIFrame* aFra
       result.mFrameToScan = aFrame->GetFirstPrincipalChild();
       result.mOverflowFrameToScan =
         aFrame->GetFirstChild(nsIFrame::kOverflowList);
       NS_WARN_IF_FALSE(!result.mOverflowFrameToScan,
                        "Scanning overflow inline frames is something we should avoid");
       result.mScanSiblings = true;
       result.mTextRunCanCrossFrameBoundary = true;
       result.mLineBreakerCanCrossFrameBoundary = true;
+    } else if (aFrame->GetType() == nsGkAtoms::rubyTextFrame) {
+      result.mFrameToScan = aFrame->GetFirstPrincipalChild();
+      result.mOverflowFrameToScan =
+        aFrame->GetFirstChild(nsIFrame::kOverflowList);
+      NS_WARN_IF_FALSE(!result.mOverflowFrameToScan,
+                       "Scanning overflow inline frames is something we should avoid");
+      result.mScanSiblings = true;
+      result.mTextRunCanCrossFrameBoundary = false;
+      result.mLineBreakerCanCrossFrameBoundary = false;
     } else {
       result.mFrameToScan = nullptr;
       result.mOverflowFrameToScan = nullptr;
       result.mTextRunCanCrossFrameBoundary = false;
       result.mLineBreakerCanCrossFrameBoundary = false;
     }
   }    
   return result;
@@ -1239,16 +1248,17 @@ BuildTextRuns(gfxContext* aContext, nsTe
     if (aForFrame->IsFloatingFirstLetterChild()) {
       lineContainerChild = aForFrame->PresContext()->PresShell()->
         GetPlaceholderFrameFor(aForFrame->GetParent());
     }
     aLineContainer = FindLineContainer(lineContainerChild);
   } else {
     NS_ASSERTION(!aForFrame ||
                  (aLineContainer == FindLineContainer(aForFrame) ||
+                  aLineContainer->GetType() == nsGkAtoms::rubyTextContainerFrame ||
                   (aLineContainer->GetType() == nsGkAtoms::letterFrame &&
                    aLineContainer->IsFloating())),
                  "Wrong line container hint");
   }
 
   if (aForFrame) {
     if (aForFrame->HasAnyStateBits(TEXT_IS_IN_TOKEN_MATHML)) {
       aLineContainer->AddStateBits(TEXT_IS_IN_TOKEN_MATHML);