Bug 1312379 part 1 - [css-align][css-flexbox][css-grid] Introduce nsIFrame methods for calculating baselines per CSS Alignment and CSS2 'vertical-align'. r=dholbert a=cbook
☠☠ backed out by 3f796290f90e ☠ ☠
authorMats Palmgren <matspal@gmail.com>
Thu, 22 Dec 2016 16:08:42 +0100
changeset 455688 b4f1387cf20b8817ef2fac7014deb77d16d8ab79
parent 455687 99babeef86597888291d0e24cabb836555a2b4e0
child 455689 2973e5aa9d5cf9e6e147aea4cec33a2a80a5f124
push id40304
push userbmo:wpan@mozilla.com
push dateWed, 04 Jan 2017 10:03:07 +0000
reviewersdholbert, cbook
bugs1312379
milestone52.0a2
Bug 1312379 part 1 - [css-align][css-flexbox][css-grid] Introduce nsIFrame methods for calculating baselines per CSS Alignment and CSS2 'vertical-align'. r=dholbert a=cbook
layout/base/nsLayoutUtils.cpp
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsFlexContainerFrame.cpp
layout/generic/nsFlexContainerFrame.h
layout/generic/nsGfxScrollFrame.h
layout/generic/nsGridContainerFrame.cpp
layout/generic/nsGridContainerFrame.h
layout/generic/nsIFrame.h
layout/generic/nsIFrameInlines.h
layout/reftests/css-grid/grid-fragmentation-015-ref.html
layout/reftests/flexbox/flexbox-align-self-baseline-horiz-3-ref.xhtml
layout/reftests/flexbox/flexbox-align-self-baseline-horiz-3.xhtml
layout/reftests/flexbox/reftest.list
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -5881,17 +5881,18 @@ nsLayoutUtils::GetFirstLinePosition(Writ
       if ((fType == nsGkAtoms::gridContainerFrame &&
            aFrame->HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) ||
           (fType == nsGkAtoms::flexContainerFrame &&
            aFrame->HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) ||
           (fType == nsGkAtoms::tableWrapperFrame &&
            static_cast<const nsTableWrapperFrame*>(aFrame)->GetRowCount() == 0)) {
         // empty grid/flex/table container
         aResult->mBStart = 0;
-        aResult->mBaseline = aFrame->SynthesizeBaselineFromBorderBox(aWM);
+        aResult->mBaseline = aFrame->SynthesizeBaselineBOffsetFromBorderBox(aWM,
+                                       BaselineSharingGroup::eFirst);
         aResult->mBEnd = aFrame->BSize(aWM);
         return true;
       }
       aResult->mBStart = 0;
       aResult->mBaseline = aFrame->GetLogicalBaseline(aWM);
       // This is what we want for the list bullet caller; not sure if
       // other future callers will want the same.
       aResult->mBEnd = aFrame->BSize(aWM);
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -484,22 +484,54 @@ nsBlockFrame::InvalidateFrameWithRect(co
                  "unexpected block frame in SVG text");
     GetParent()->InvalidateFrame();
     return;
   }
   nsContainerFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
 }
 
 nscoord
-nsBlockFrame::GetLogicalBaseline(WritingMode aWritingMode) const
-{
-  nscoord result;
-  if (nsLayoutUtils::GetLastLineBaseline(aWritingMode, this, &result))
-    return result;
-  return nsFrame::GetLogicalBaseline(aWritingMode);
+nsBlockFrame::GetLogicalBaseline(WritingMode aWM) const
+{
+  auto lastBaseline =
+    GetBaseline(aWM, BaselineSharingGroup::eLast, AlignmentContext::eInline);
+  return BSize(aWM) - lastBaseline;
+}
+
+bool
+nsBlockFrame::GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+                                        BaselineSharingGroup aBaselineGroup,
+                                        nscoord*             aBaseline) const
+{
+  if (aBaselineGroup == BaselineSharingGroup::eFirst) {
+    return nsLayoutUtils::GetFirstLineBaseline(aWM, this, aBaseline);
+  }
+
+  for (ConstReverseLineIterator line = LinesRBegin(), line_end = LinesREnd();
+       line != line_end; ++line) {
+    if (line->IsBlock()) {
+      nscoord offset;
+      nsIFrame* kid = line->mFirstChild;
+      if (kid->GetVerticalAlignBaseline(aWM, &offset)) {
+        // Ignore relative positioning for baseline calculations.
+        const nsSize& sz = line->mContainerSize;
+        offset += kid->GetLogicalNormalPosition(aWM, sz).B(aWM);
+        *aBaseline = BSize(aWM) - offset;
+        return true;
+      }
+    } else {
+      // XXX Is this the right test?  We have some bogus empty lines
+      // floating around, but IsEmpty is perhaps too weak.
+      if (line->BSize() != 0 || !line->IsEmpty()) {
+        *aBaseline = BSize(aWM) - (line->BStart() + line->GetLogicalAscent());
+        return true;
+      }
+    }
+  }
+  return false;
 }
 
 nscoord
 nsBlockFrame::GetCaretBaseline() const
 {
   nsRect contentRect = GetContentRect();
   nsMargin bp = GetUsedBorderAndPadding();
 
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -116,16 +116,29 @@ public:
   virtual void InsertFrames(ChildListID aListID,
                             nsIFrame* aPrevFrame,
                             nsFrameList& aFrameList) override;
   virtual void RemoveFrame(ChildListID aListID,
                            nsIFrame* aOldFrame) override;
   virtual const nsFrameList& GetChildList(ChildListID aListID) const override;
   virtual void GetChildLists(nsTArray<ChildList>* aLists) const override;
   virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
+  bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+                                nscoord* aBaseline) const override
+  {
+    nscoord lastBaseline;
+    if (GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eLast, &lastBaseline)) {
+      *aBaseline = BSize() - lastBaseline;
+      return true;
+    }
+    return false;
+  }
+  bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+                                 BaselineSharingGroup aBaselineGroup,
+                                 nscoord*             aBaseline) const override;
   virtual nscoord GetCaretBaseline() const override;
   virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
   virtual nsSplittableType GetSplittableType() const override;
   virtual bool IsFloatContainingBlock() const override;
   virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
                                 const nsRect& aDirtyRect,
                                 const nsDisplayListSet& aLists) override;
   virtual nsIAtom* GetType() const override;
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -467,17 +467,18 @@ public:
       // or a dependency.
       // Use GetFirstLineBaseline() or GetLastLineBaseline() as appropriate,
       // or just GetLogicalBaseline() if that fails.
       bool found = aUseFirstBaseline ?
         nsLayoutUtils::GetFirstLineBaseline(mWM, mFrame, &mAscent) :
         nsLayoutUtils::GetLastLineBaseline(mWM, mFrame, &mAscent);
 
       if (!found) {
-        mAscent = mFrame->SynthesizeBaselineFromBorderBox(mWM);
+        mAscent = mFrame->SynthesizeBaselineBOffsetFromBorderBox(mWM,
+                            BaselineSharingGroup::eFirst);
       }
     }
     return mAscent;
   }
 
   // Convenience methods to compute the main & cross size of our *margin-box*.
   // The caller is responsible for telling us the right axis, so that we can
   // pull out the appropriate components of our margin/border/padding structs.
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -74,16 +74,33 @@ public:
 
   virtual nsIAtom* GetType() const override;
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
   nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const override;
 
+  bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+                                nscoord* aBaseline) const override
+  {
+    return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, aBaseline);
+  }
+
+  bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+                                 BaselineSharingGroup aBaselineGroup,
+                                 nscoord*             aBaseline) const override
+  {
+    if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) {
+      return false;
+    }
+    *aBaseline = GetLogicalBaseline(aWM);
+    return true;
+  }
+
   // nsContainerFrame overrides
   uint16_t CSSAlignmentForAbsPosChild(
             const ReflowInput& aChildRI,
             mozilla::LogicalAxis aLogicalAxis) const override;
 
   // Flexbox-specific public methods
   bool IsHorizontal();
 
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -719,16 +719,22 @@ public:
                       ReflowOutput&     aDesiredSize,
                       const ReflowInput& aReflowInput,
                       nsReflowStatus&          aStatus) override;
 
   virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override {
     return mHelper.ComputeCustomOverflow(aOverflowAreas);
   }
 
+  bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+                                nscoord* aBaseline) const override {
+    *aBaseline = GetLogicalBaseline(aWM);
+    return true;
+  }
+
   // Recomputes the scrollable overflow area we store in the helper to take children
   // that are affected by perpsective set on the outer frame and scroll at different
   // rates.
   void AdjustForPerspective(nsRect& aScrollableOverflow);
 
   // Called to set the child frames. We typically have three: the scroll area,
   // the vertical scrollbar, and the horizontal scrollbar.
   virtual void SetInitialChildList(ChildListID     aListID,
@@ -1105,16 +1111,22 @@ public:
 #if 0
   virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
 #endif
 
   virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override {
     return mHelper.ComputeCustomOverflow(aOverflowAreas);
   }
 
+  bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+                                nscoord* aBaseline) const override {
+    *aBaseline = GetLogicalBaseline(aWM);
+    return true;
+  }
+
   // Called to set the child frames. We typically have three: the scroll area,
   // the vertical scrollbar, and the horizontal scrollbar.
   virtual void SetInitialChildList(ChildListID     aListID,
                                    nsFrameList&    aChildList) override;
   virtual void AppendFrames(ChildListID     aListID,
                             nsFrameList&    aFrameList) override;
   virtual void InsertFrames(ChildListID     aListID,
                             nsIFrame*       aPrevFrame,
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -6717,21 +6717,18 @@ nsGridContainerFrame::SynthesizeBaseline
   nscoord start;
   nscoord size;
   if (aAxis == eLogicalAxisBlock) {
     start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).B(aCBWM);
     size = child->BSize(aCBWM);
     if (grid && aGridOrderItem.mIsInEdgeTrack) {
       isOrthogonal ? grid->GetIBaseline(aGroup, &baseline) :
                      grid->GetBBaseline(aGroup, &baseline);
-    } else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack &&
-               GetBBaseline(aGroup, childWM, child, &baseline)) {
-      if (aGroup == BaselineSharingGroup::eLast) {
-        baseline = size - baseline; // convert to distance from border-box end
-      }
+    } else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack) {
+      baseline = child->GetBaseline(childWM, aGroup, AlignmentContext::eGrid);
     } else {
       baseline = ::SynthesizeBaselineFromBorderBox(aGroup, childWM, size);
     }
   } else {
     start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).I(aCBWM);
     size = child->ISize(aCBWM);
     if (grid && aGridOrderItem.mIsInEdgeTrack) {
       isOrthogonal ? grid->GetBBaseline(aGroup, &baseline) :
--- a/layout/generic/nsGridContainerFrame.h
+++ b/layout/generic/nsGridContainerFrame.h
@@ -10,24 +10,16 @@
 #define nsGridContainerFrame_h___
 
 #include "mozilla/Maybe.h"
 #include "mozilla/TypeTraits.h"
 #include "nsContainerFrame.h"
 #include "nsHashKeys.h"
 #include "nsTHashtable.h"
 
-// https://drafts.csswg.org/css-align-3/#baseline-sharing-group
-enum BaselineSharingGroup
-{
-  // NOTE Used as an array index so must be 0 and 1.
-  eFirst = 0,
-  eLast = 1,
-};
-
 /**
  * Factory function.
  * @return a newly allocated nsGridContainerFrame (infallible)
  */
 nsContainerFrame* NS_NewGridContainerFrame(nsIPresShell* aPresShell,
                                            nsStyleContext* aContext);
 
 namespace mozilla {
@@ -115,16 +107,32 @@ public:
       // Return a baseline synthesized from our margin-box.
       return nsContainerFrame::GetLogicalBaseline(aWM);
     }
     nscoord b;
     GetBBaseline(BaselineSharingGroup::eFirst, &b);
     return b;
   }
 
+  bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+                                nscoord* aBaseline) const override
+  {
+    return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, aBaseline);
+  }
+
+  bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+                                 BaselineSharingGroup aBaselineGroup,
+                                 nscoord*             aBaseline) const override
+  {
+    if (HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) {
+      return false;
+    }
+    return GetBBaseline(aBaselineGroup, aBaseline);
+  }
+
 #ifdef DEBUG_FRAME_DUMP
   nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
   // nsContainerFrame overrides
   bool DrainSelfOverflowList() override;
   void AppendFrames(ChildListID aListID, nsFrameList& aFrameList) override;
   void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -412,16 +412,34 @@ enum nsBidiDirection {
   NSBIDI_LTR,
   /** All right-to-left text This is a 1 value. */
   NSBIDI_RTL,
   /** Mixed-directional text. */
   NSBIDI_MIXED
 };
 
 namespace mozilla {
+
+// https://drafts.csswg.org/css-align-3/#baseline-sharing-group
+enum BaselineSharingGroup
+{
+  // NOTE Used as an array index so must be 0 and 1.
+  eFirst = 0,
+  eLast = 1,
+};
+
+// Loosely: https://drafts.csswg.org/css-align-3/#shared-alignment-context
+enum class AlignmentContext
+{
+  eInline,
+  eTable,
+  eFlexbox,
+  eGrid,
+};
+
 /*
  * For replaced elements only. Gets the intrinsic dimensions of this element.
  * The dimensions may only be one of the following two types:
  *
  *   eStyleUnit_Coord   - a length in app units
  *   eStyleUnit_None    - the element has no intrinsic size in this dimension
  */
 struct IntrinsicSize {
@@ -494,16 +512,18 @@ static void ReleaseValue(T* aPropertyVal
  * link to many of the functions defined here. Too bad.
  *
  * If you're not in layout but you must call functions in here, at least
  * restrict yourself to calling virtual methods, which won't hurt you as badly.
  */
 class nsIFrame : public nsQueryFrame
 {
 public:
+  using AlignmentContext = mozilla::AlignmentContext;
+  using BaselineSharingGroup = mozilla::BaselineSharingGroup;
   template <typename T> using Maybe = mozilla::Maybe<T>;
   using Nothing = mozilla::Nothing;
   using OnNonvisible = mozilla::OnNonvisible;
   template<typename T=void>
   using PropertyDescriptor = const mozilla::FramePropertyDescriptor<T>*;
   using ReflowInput = mozilla::ReflowInput;
   using ReflowOutput = mozilla::ReflowOutput;
   using Visibility = mozilla::Visibility;
@@ -1178,44 +1198,114 @@ public:
                               Sides aSkipSides,
                               nscoord aRadii[8]) const;
   bool GetBorderRadii(nscoord aRadii[8]) const;
 
   bool GetPaddingBoxBorderRadii(nscoord aRadii[8]) const;
   bool GetContentBoxBorderRadii(nscoord aRadii[8]) const;
 
   /**
+   * XXX: this method will likely be replaced by GetVerticalAlignBaseline
    * Get the position of the frame's baseline, relative to the top of
    * the frame (its top border edge).  Only valid when Reflow is not
    * needed.
+   * @note You should only call this on frames with a WM that's parallel to aWM.
+   * @param aWM the writing-mode of the alignment context, with the ltr/rtl
+   * direction tweak done by nsIFrame::GetWritingMode(nsIFrame*) in inline
+   * contexts (see that method).
    */
-  virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const = 0;
+  virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const = 0;
+
+  /**
+   * Synthesize a first(last) inline-axis baseline from our margin-box.
+   * An alphabetical baseline is at the start(end) edge and a central baseline
+   * is at the center of our block-axis margin-box (aWM tells which to use).
+   * https://drafts.csswg.org/css-align-3/#synthesize-baselines
+   * @note You should only call this on frames with a WM that's parallel to aWM.
+   * @param aWM the writing-mode of the alignment context
+   * @return an offset from our border-box block-axis start(end) edge for
+   * a first(last) baseline respectively
+   * (implemented in nsIFrameInlines.h)
+   */
+  inline nscoord SynthesizeBaselineBOffsetFromMarginBox(
+                   mozilla::WritingMode aWM,
+                   BaselineSharingGroup aGroup) const;
+
+  /**
+   * Synthesize a first(last) inline-axis baseline from our border-box.
+   * An alphabetical baseline is at the start(end) edge and a central baseline
+   * is at the center of our block-axis border-box (aWM tells which to use).
+   * https://drafts.csswg.org/css-align-3/#synthesize-baselines
+   * @note The returned value is only valid when reflow is not needed.
+   * @note You should only call this on frames with a WM that's parallel to aWM.
+   * @param aWM the writing-mode of the alignment context
+   * @return an offset from our border-box block-axis start(end) edge for
+   * a first(last) baseline respectively
+   * (implemented in nsIFrameInlines.h)
+   */
+  inline nscoord SynthesizeBaselineBOffsetFromBorderBox(
+                   mozilla::WritingMode aWM,
+                   BaselineSharingGroup aGroup) const;
+
+  /**
+   * Return the position of the frame's inline-axis baseline, or synthesize one
+   * for the given alignment context. The returned baseline is the distance from
+   * the block-axis border-box start(end) edge for aBaselineGroup eFirst(eLast).
+   * @note The returned value is only valid when reflow is not needed.
+   * @note You should only call this on frames with a WM that's parallel to aWM.
+   * @param aWM the writing-mode of the alignment context
+   * @param aBaselineOffset out-param, only valid if the method returns true
+   * (implemented in nsIFrameInlines.h)
+   */
+  inline nscoord GetBaseline(mozilla::WritingMode aWM,
+                             BaselineSharingGroup aBaselineGroup,
+                             AlignmentContext     aAlignmentContext) const;
+
+  /**
+   * XXX: this method is taking over the role that GetLogicalBaseline has.
+   * Return true if the frame has a CSS2 'vertical-align' baseline.
+   * If it has, then the returned baseline is the distance from the block-
+   * axis border-box start edge.
+   * @note This method should only be used in AlignmentContext::eInline contexts.
+   * @note The returned value is only valid when reflow is not needed.
+   * @note You should only call this on frames with a WM that's parallel to aWM.
+   * @param aWM the writing-mode of the alignment context
+   * @param aBaseline the baseline offset, only valid if the method returns true
+   */
+  virtual bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+                                        nscoord* aBaseline) const {
+    return false;
+  }
+
+  /**
+   * Return true if the frame has a first(last) inline-axis natural baseline per
+   * CSS Box Alignment.  If so, then the returned baseline is the distance from
+   * the block-axis border-box start(end) edge for aBaselineGroup eFirst(eLast).
+   * https://drafts.csswg.org/css-align-3/#natural-baseline
+   * @note The returned value is only valid when reflow is not needed.
+   * @note You should only call this on frames with a WM that's parallel to aWM.
+   * @param aWM the writing-mode of the alignment context
+   * @param aBaseline the baseline offset, only valid if the method returns true
+   */
+  virtual bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+                                         BaselineSharingGroup aBaselineGroup,
+                                         nscoord*             aBaseline) const {
+    return false;
+  }
 
   /**
    * Get the position of the baseline on which the caret needs to be placed,
    * relative to the top of the frame.  This is mostly needed for frames
    * which return a baseline from GetBaseline which is not useful for
    * caret positioning.
    */
   virtual nscoord GetCaretBaseline() const {
     return GetLogicalBaseline(GetWritingMode());
   }
 
-  /**
-   * Synthesize a baseline from our border box.  For an alphabetical baseline
-   * this is the end edge of the border box.  For a central baseline it's
-   * the center of the border box.
-   * https://drafts.csswg.org/css-align-3/#synthesize-baselines
-   */
-  nscoord SynthesizeBaselineFromBorderBox(mozilla::WritingMode aWM) const
-  {
-    nscoord borderBoxSize = BSize(aWM);
-    return aWM.IsAlphabeticalBaseline() ? borderBoxSize : borderBoxSize / 2;
-  }
-
   ///////////////////////////////////////////////////////////////////////////////
   // The public visibility API.
   ///////////////////////////////////////////////////////////////////////////////
 
   /// @return true if we're tracking visibility for this frame.
   bool TrackingVisibility() const
   {
     return bool(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
--- a/layout/generic/nsIFrameInlines.h
+++ b/layout/generic/nsIFrameInlines.h
@@ -91,9 +91,73 @@ nsIFrame::IsInlineOutside() const
 }
 
 mozilla::StyleDisplay
 nsIFrame::GetDisplay() const
 {
   return StyleDisplay()->GetDisplay(this);
 }
 
+nscoord
+nsIFrame::SynthesizeBaselineBOffsetFromMarginBox(
+            mozilla::WritingMode aWM,
+            BaselineSharingGroup aGroup) const
+{
+  MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode()));
+  auto margin = GetLogicalUsedMargin(aWM);
+  if (aGroup == BaselineSharingGroup::eFirst) {
+    if (aWM.IsAlphabeticalBaseline()) {
+      // First baseline for inverted-line content is the block-start margin edge,
+      // as the frame is in effect "flipped" for alignment purposes.
+      return MOZ_UNLIKELY(aWM.IsLineInverted()) ? -margin.BStart(aWM)
+                                                : BSize(aWM) + margin.BEnd(aWM);
+    }
+    nscoord marginBoxCenter = (BSize(aWM) + margin.BStartEnd(aWM)) / 2;
+    return marginBoxCenter - margin.BStart(aWM);
+  }
+  MOZ_ASSERT(aGroup == BaselineSharingGroup::eLast);
+  if (aWM.IsAlphabeticalBaseline()) {
+    // Last baseline for inverted-line content is the block-start margin edge,
+    // as the frame is in effect "flipped" for alignment purposes.
+    return MOZ_UNLIKELY(aWM.IsLineInverted()) ? BSize(aWM) + margin.BStart(aWM)
+                                              : -margin.BEnd(aWM);
+  }
+  // Round up for central baseline offset, to be consistent with eFirst.
+  nscoord marginBoxSize = BSize(aWM) + margin.BStartEnd(aWM);
+  nscoord marginBoxCenter = (marginBoxSize / 2) + (marginBoxSize % 2);
+  return marginBoxCenter - margin.BEnd(aWM);
+}
+
+nscoord
+nsIFrame::SynthesizeBaselineBOffsetFromBorderBox(
+            mozilla::WritingMode aWM,
+            BaselineSharingGroup aGroup) const
+{
+  MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode()));
+  nscoord borderBoxSize = BSize(aWM);
+  if (aGroup == BaselineSharingGroup::eFirst) {
+    return MOZ_LIKELY(aWM.IsAlphabeticalBaseline()) ? borderBoxSize
+                                                    : borderBoxSize / 2;
+  }
+  MOZ_ASSERT(aGroup == BaselineSharingGroup::eLast);
+  // Round up for central baseline offset, to be consistent with eFirst.
+  auto borderBoxCenter = (borderBoxSize / 2) + (borderBoxSize % 2);
+  return MOZ_LIKELY(aWM.IsAlphabeticalBaseline()) ? 0 : borderBoxCenter;
+}
+
+nscoord
+nsIFrame::GetBaseline(mozilla::WritingMode aWM,
+                      BaselineSharingGroup aBaselineGroup,
+                      AlignmentContext     aAlignmentContext) const
+{
+  MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode()));
+  nscoord baseline;
+  if (GetNaturalBaselineBOffset(aWM, aBaselineGroup, &baseline)) {
+    return baseline;
+  }
+  if (aAlignmentContext == AlignmentContext::eInline) {
+    return SynthesizeBaselineBOffsetFromMarginBox(aWM, aBaselineGroup);
+  }
+  // XXX AlignmentContext::eTable should use content box?
+  return SynthesizeBaselineBOffsetFromBorderBox(aWM, aBaselineGroup);
+}
+
 #endif
--- a/layout/reftests/css-grid/grid-fragmentation-015-ref.html
+++ b/layout/reftests/css-grid/grid-fragmentation-015-ref.html
@@ -79,17 +79,17 @@ x { display:block; height:20px; }
 <span style="grid-row:span 2"><x></x></span>
 <span><x></x></span>
 <span><x></x></span>
 </div></div></div></div>
 
 <!-- grid wrapped in FIELDSET inline -->
 <div class="columns" style="height: 40px; margin-left:200px">
 <div style="padding-top:2px; background:grey">
-<div style="display:inline-block; overflow:hidden; border:none; padding:0; margin:0">
+<div style="display:inline-block; border:none; padding:0; margin:0">
 <div class="grid">
 <span style="grid-row:span 2"><x></x></span>
 <span><x></x></span>
 <span><x></x></span>
 </div></div></div></div>
 
 <!-- grid wrapped in FIELDSET overflow:hidden block -->
 <div class="columns" style="height: 40px; margin-left:400px">
--- a/layout/reftests/flexbox/flexbox-align-self-baseline-horiz-3-ref.xhtml
+++ b/layout/reftests/flexbox/flexbox-align-self-baseline-horiz-3-ref.xhtml
@@ -18,41 +18,49 @@
         display: block;
         border: 1px dashed blue;
         font: 14px sans-serif;
       }
 
       div   { display: inline-block; }
       table { display: inline-table; }
 
-      .big {
-        height: 100px;
-        font: 24px sans-serif;
-        margin-top: 20px;
-      }
-
       .lime   { background: lime;   }
       .pink   { background: pink;   }
       .aqua   { background: aqua;   }
+
+      i { display:inline-block; width:20px; height:2px; background:black; }
+      .ref {
+            -moz-appearance:none;
+             -ms-appearance:none;
+         -webkit-appearance:none;
+                 appearance:none;
+         border:none;
+         margin:0;
+         padding:0;
+         border-bottom:2px solid black;
+         width:20px;
+      }
    </style>
   </head>
   <body>
     <div class="flexbox">
       <div class="lime">text</div
       ><button>btn</button
-      ><input type="radio"
-     /><input type="checkbox"
-     /><label class="pink">label</label
+      ><label class="pink">label</label
       ><table cellspacing="0" cellpadding="0"  class="aqua">
         <label>lab<br/>el</label>
       </table
       ><table cellspacing="0" cellpadding="0">
         <fieldset>field<br/>set</fieldset>
       </table
       ><table cellspacing="0" cellpadding="0">
         <fieldset><legend>leg</legend>field<br/>set</fieldset>
       </table
       ><table cellspacing="0" cellpadding="0">
         <fieldset><legend>leg<br/>end</legend>field<br/>set</fieldset>
       </table>
     </div>
+    <div class="flexbox" style="font-size:0"><input type="radio"
+     /><input type="checkbox"
+     /><input type="checkbox" class="ref"/></div>
   </body>
 </html>
--- a/layout/reftests/flexbox/flexbox-align-self-baseline-horiz-3.xhtml
+++ b/layout/reftests/flexbox/flexbox-align-self-baseline-horiz-3.xhtml
@@ -25,24 +25,29 @@
         height: 100px;
         font: 24px sans-serif;
         margin-top: 20px;
       }
 
       .lime   { background: lime;   }
       .pink   { background: pink;   }
       .aqua   { background: aqua;   }
+
+      i { display:inline-block; width:20px; height:2px; background:black; }
    </style>
   </head>
   <body>
     <div class="flexbox">
       <div class="lime">text</div>
       <button>btn</button>
-      <input type="radio"/>
-      <input type="checkbox"/>
       <label class="pink">label</label>
       <label class="aqua">lab<br/>el</label>
       <fieldset>field<br/>set</fieldset>
       <fieldset><legend>leg</legend>field<br/>set</fieldset>
       <fieldset><legend>leg<br/>end</legend>field<br/>set</fieldset>
     </div>
+    <div class="flexbox" style="font-size:0">
+      <input type="radio"/>
+      <input type="checkbox"/>
+      <i></i>
+    </div>
   </body>
 </html>
--- a/layout/reftests/flexbox/reftest.list
+++ b/layout/reftests/flexbox/reftest.list
@@ -12,17 +12,17 @@
 # SUBDIRECTORY: Reftests for paginated flex containers
 include pagination/reftest.list
 
 # Tests for cross-axis alignment (align-self / align-items properties)
 fails == flexbox-align-self-baseline-horiz-2.xhtml  flexbox-align-self-baseline-horiz-2-ref.xhtml # bug 793456, and possibly others
 # This one fails on windows R (but not Ru, strangely) and GTK.
 # On Windows R and GTK, the single-line <label> flex item has a different
 # background size in test vs. ref
-fuzzy-if(cocoaWidget,1,2) random-if(winWidget||gtkWidget) == flexbox-align-self-baseline-horiz-3.xhtml  flexbox-align-self-baseline-horiz-3-ref.xhtml # XXXdholbert investigate
+fuzzy-if(cocoaWidget,1,2) random-if(winWidget||gtkWidget) skip-if(Android) == flexbox-align-self-baseline-horiz-3.xhtml  flexbox-align-self-baseline-horiz-3-ref.xhtml # XXXdholbert investigate the random-if. The skip-if(Android) is because checkbox/radio appearance:none doesn't work as expected.
 == flexbox-align-self-baseline-horiz-4.xhtml flexbox-align-self-baseline-horiz-4-ref.xhtml
 
 # Tests for box-sizing on flex containers and flex items.
 == flexbox-box-sizing-on-container-horiz-1.html flexbox-box-sizing-on-container-horiz-1-ref.html
 == flexbox-box-sizing-on-container-vert-1.html flexbox-box-sizing-on-container-vert-1-ref.html
 == flexbox-box-sizing-on-items-horiz-1a.html flexbox-box-sizing-on-items-horiz-1-ref.html
 == flexbox-box-sizing-on-items-horiz-1b.html flexbox-box-sizing-on-items-horiz-1-ref.html
 == flexbox-box-sizing-on-items-vert-1a.html flexbox-box-sizing-on-items-vert-1-ref.html