Bug 1107783 - part 1, [css-grid] Implement abs.pos. grid item placement and reflow. r=dholbert
authorMats Palmgren <matspal@gmail.com>
Thu, 26 Mar 2015 18:57:39 +0000
changeset 264819 791f8195e4dd341022ae79f0f8f264ef26a33050
parent 264818 23edaf5ea79a6c0df12a54541426ce34e4ad532d
child 264820 a55d25db977892b42460a17ce3a305a385ba3d86
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs1107783
milestone39.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 1107783 - part 1, [css-grid] Implement abs.pos. grid item placement and reflow. r=dholbert
layout/generic/nsAbsoluteContainingBlock.cpp
layout/generic/nsGridContainerFrame.cpp
layout/generic/nsGridContainerFrame.h
--- a/layout/generic/nsAbsoluteContainingBlock.cpp
+++ b/layout/generic/nsAbsoluteContainingBlock.cpp
@@ -11,16 +11,17 @@
 #include "nsAbsoluteContainingBlock.h"
 
 #include "nsContainerFrame.h"
 #include "nsGkAtoms.h"
 #include "nsIPresShell.h"
 #include "nsHTMLReflowState.h"
 #include "nsPresContext.h"
 #include "nsCSSFrameConstructor.h"
+#include "nsGridContainerFrame.h"
 
 #ifdef DEBUG
 #include "nsBlockFrame.h"
 
 static void PrettyUC(nscoord aSize, char* aBuf)
 {
   if (NS_UNCONSTRAINEDSIZE == aSize) {
     strcpy(aBuf, "UC");
@@ -114,26 +115,35 @@ nsAbsoluteContainingBlock::Reflow(nsCont
                                   bool                     aCBWidthChanged,
                                   bool                     aCBHeightChanged,
                                   nsOverflowAreas*         aOverflowAreas)
 {
   nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
 
   bool reflowAll = aReflowState.ShouldReflowAllKids();
 
+  // The 'width' check below is an optimization to avoid the virtual GetType()
+  // call in most cases.  'aContainingBlock' isn't used for grid items,
+  // each item has its own CB on a frame property instead.
+  // @see nsGridContainerFrame::ReflowChildren
+  const bool isGrid =
+    aContainingBlock.width == nsGridContainerFrame::VERY_LIKELY_A_GRID_CONTAINER &&
+    aDelegatingFrame->GetType() == nsGkAtoms::gridContainerFrame;
+
   nsIFrame* kidFrame;
   nsOverflowContinuationTracker tracker(aDelegatingFrame, true);
   for (kidFrame = mAbsoluteFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) {
     bool kidNeedsReflow = reflowAll || NS_SUBTREE_DIRTY(kidFrame) ||
       FrameDependsOnContainer(kidFrame, aCBWidthChanged, aCBHeightChanged);
     if (kidNeedsReflow && !aPresContext->HasPendingInterrupt()) {
       // Reflow the frame
       nsReflowStatus  kidStatus = NS_FRAME_COMPLETE;
-      ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowState,
-                          aContainingBlock,
+      const nsRect& cb = isGrid ? nsGridContainerFrame::GridItemCB(kidFrame)
+                                : aContainingBlock;
+      ReflowAbsoluteFrame(aDelegatingFrame, aPresContext, aReflowState, cb,
                           aConstrainHeight, kidFrame, kidStatus,
                           aOverflowAreas);
       nsIFrame* nextFrame = kidFrame->GetNextInFlow();
       if (!NS_FRAME_IS_FULLY_COMPLETE(kidStatus) &&
           aDelegatingFrame->IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) {
         // Need a continuation
         if (!nextFrame) {
           nextFrame =
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -4,19 +4,22 @@
  * 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: grid | inline-grid" */
 
 #include "nsGridContainerFrame.h"
 
 #include "mozilla/Maybe.h"
+#include "nsAbsoluteContainingBlock.h"
+#include "nsAlgorithm.h" // for clamped()
 #include "nsCSSAnonBoxes.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
+#include "nsIFrameInlines.h"
 #include "nsPresContext.h"
 #include "nsReadableUtils.h"
 #include "nsRuleNode.h"
 #include "nsStyleContext.h"
 
 using namespace mozilla;
 typedef nsGridContainerFrame::TrackSize TrackSize;
 
@@ -141,16 +144,29 @@ IsNameWithEndSuffix(const nsString& aStr
 }
 
 static bool
 IsNameWithStartSuffix(const nsString& aString, uint32_t* aIndex)
 {
   return IsNameWithSuffix(aString, NS_LITERAL_STRING("-start"), aIndex);
 }
 
+static nscoord
+GridLinePosition(uint32_t aLine, const nsTArray<TrackSize>& aTrackSizes)
+{
+  MOZ_ASSERT(aLine != 0, "expected a 1-based line number");
+  const uint32_t endIndex = aLine - 1;
+  MOZ_ASSERT(endIndex <= aTrackSizes.Length(), "aTrackSizes is too small");
+  nscoord pos = 0;
+  for (uint32_t i = 0; i < endIndex; ++i) {
+    pos += aTrackSizes[i].mBase;
+  }
+  return pos;
+}
+
 //----------------------------------------------------------------------
 
 // Frame class boilerplate
 // =======================
 
 NS_QUERYFRAME_HEAD(nsGridContainerFrame)
   NS_QUERYFRAME_ENTRY(nsGridContainerFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
@@ -165,16 +181,28 @@ NS_NewGridContainerFrame(nsIPresShell* a
 }
 
 
 //----------------------------------------------------------------------
 
 // nsGridContainerFrame Method Implementations
 // ===========================================
 
+/*static*/ const nsRect&
+nsGridContainerFrame::GridItemCB(nsIFrame* aChild)
+{
+  MOZ_ASSERT((aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
+             aChild->IsAbsolutelyPositioned());
+  nsRect* cb = static_cast<nsRect*>(aChild->Properties().Get(
+                 GridItemContainingBlockRect()));
+  MOZ_ASSERT(cb, "this method must only be called on grid items, and the grid "
+                 "container should've reflowed this item by now and set up cb");
+  return *cb;
+}
+
 void
 nsGridContainerFrame::AddImplicitNamedAreas(
   const nsTArray<nsTArray<nsString>>& aLineNameLists)
 {
   // http://dev.w3.org/csswg/css-grid/#implicit-named-areas
   // XXX this just checks x-start .. x-end in one dimension and there's
   // no other error checking.  A few wrong cases (maybe):
   // (x-start x-end)
@@ -415,16 +443,63 @@ nsGridContainerFrame::PlaceDefinite(nsIF
                      &GridNamedArea::mColumnStart, &GridNamedArea::mColumnEnd,
                      mExplicitGridColEnd, aStyle),
     ResolveLineRange(itemStyle->mGridRowStart, itemStyle->mGridRowEnd,
                      aStyle->mGridTemplateRows.mLineNameLists,
                      &GridNamedArea::mRowStart, &GridNamedArea::mRowEnd,
                      mExplicitGridRowEnd, aStyle));
 }
 
+nsGridContainerFrame::LineRange
+nsGridContainerFrame::ResolveAbsPosLineRange(
+  const nsStyleGridLine& aStart,
+  const nsStyleGridLine& aEnd,
+  const nsTArray<nsTArray<nsString>>& aLineNameList,
+  uint32_t GridNamedArea::* aAreaStart,
+  uint32_t GridNamedArea::* aAreaEnd,
+  uint32_t aExplicitGridEnd,
+  uint32_t aGridEnd,
+  const nsStylePosition* aStyle)
+{
+  if (aStart.IsAuto()) {
+    if (aEnd.IsAuto()) {
+      return LineRange(0, 0);
+    }
+    uint32_t end = ResolveLine(aEnd, aEnd.mInteger, 0, aLineNameList, aAreaStart,
+                               aAreaEnd, aExplicitGridEnd, eLineRangeSideEnd,
+                               aStyle);
+    MOZ_ASSERT(end != 0, "resolving non-auto line shouldn't result in auto");
+    if (aEnd.mHasSpan) {
+      ++end;
+    }
+    return LineRange(0, clamped(end, 1U, aGridEnd));
+  }
+
+  if (aEnd.IsAuto()) {
+    uint32_t start =
+      ResolveLine(aStart, aStart.mInteger, 0, aLineNameList, aAreaStart,
+                  aAreaEnd, aExplicitGridEnd, eLineRangeSideStart, aStyle);
+    MOZ_ASSERT(start != 0, "resolving non-auto line shouldn't result in auto");
+    if (aStart.mHasSpan) {
+      start = std::max(int32_t(aGridEnd) - int32_t(start), 1);
+    }
+    return LineRange(clamped(start, 1U, aGridEnd), 0);
+  }
+
+  LineRange r = ResolveLineRange(aStart, aEnd, aLineNameList, aAreaStart,
+                                 aAreaEnd, aExplicitGridEnd, aStyle);
+  MOZ_ASSERT(!r.IsAuto(), "resolving definite lines shouldn't result in auto");
+  // Clamp definite lines to be within the implicit grid.
+  // Note that this implies mStart may be equal to mEnd.
+  r.mStart = clamped(r.mStart, 1U, aGridEnd);
+  r.mEnd = clamped(r.mEnd, 1U, aGridEnd);
+  MOZ_ASSERT(r.mStart <= r.mEnd);
+  return r;
+}
+
 uint32_t
 nsGridContainerFrame::FindAutoCol(uint32_t aStartCol, uint32_t aLockedRow,
                                   const GridArea* aArea) const
 {
   MOZ_ASSERT(aStartCol > 0, "expected a 1-based track number");
   MOZ_ASSERT(aLockedRow > 0, "expected a 1-based track number");
   const uint32_t extent = aArea->mCols.Extent();
   const uint32_t iStart = aLockedRow - 1;
@@ -456,16 +531,36 @@ nsGridContainerFrame::FindAutoCol(uint32
       i = iStart;
     } else {
       ++i;
     }
   }
   return candidate + 1; // return a 1-based column number
 }
 
+nsGridContainerFrame::GridArea
+nsGridContainerFrame::PlaceAbsPos(nsIFrame* aChild,
+                                  const nsStylePosition* aStyle)
+{
+  const nsStylePosition* itemStyle = aChild->StylePosition();
+  return GridArea(
+    ResolveAbsPosLineRange(itemStyle->mGridColumnStart,
+                           itemStyle->mGridColumnEnd,
+                           aStyle->mGridTemplateColumns.mLineNameLists,
+                           &GridNamedArea::mColumnStart,
+                           &GridNamedArea::mColumnEnd,
+                           mExplicitGridColEnd, mGridColEnd, aStyle),
+    ResolveAbsPosLineRange(itemStyle->mGridRowStart,
+                           itemStyle->mGridRowEnd,
+                           aStyle->mGridTemplateRows.mLineNameLists,
+                           &GridNamedArea::mRowStart,
+                           &GridNamedArea::mRowEnd,
+                           mExplicitGridRowEnd, mGridRowEnd, aStyle));
+}
+
 void
 nsGridContainerFrame::PlaceAutoCol(uint32_t aStartCol, GridArea* aArea) const
 {
   MOZ_ASSERT(aArea->mRows.IsDefinite() && aArea->mCols.IsAuto());
   uint32_t col = FindAutoCol(aStartCol, aArea->mRows.mStart, aArea);
   aArea->mCols.ResolveAutoPosition(col);
   MOZ_ASSERT(aArea->IsDefinite());
 }
@@ -682,16 +777,34 @@ nsGridContainerFrame::PlaceGridItems(con
                      "we shouldn't add implicit minor tracks for auto/auto");
 #endif
         }
       }
       mCellMap.Fill(*area);
       InflateGridFor(*area);
     }
   }
+
+  if (IsAbsoluteContainer()) {
+    // 9.4 Absolutely-positioned Grid Items
+    // http://dev.w3.org/csswg/css-grid/#abspos-items
+    // We only resolve definite lines here; we'll align auto positions to the
+    // grid container later during reflow.
+    nsFrameList children(GetChildList(GetAbsoluteListID()));
+    for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
+      nsIFrame* child = e.get();
+      GridArea area(PlaceAbsPos(child, aStyle));
+      GridArea* prop = GetGridAreaForChild(child);
+      if (prop) {
+        *prop = area;
+      } else {
+        child->Properties().Set(GridAreaProperty(), new GridArea(area));
+      }
+    }
+  }
 }
 
 static void
 InitializeTrackSize(nscoord aPercentageBasis,
                     const nsStyleCoord& aMinCoord,
                     const nsStyleCoord& aMaxCoord,
                     TrackSize* aTrackSize)
 {
@@ -767,17 +880,19 @@ nsGridContainerFrame::CalculateTrackSize
                        aStyle->mGridAutoRowsMax,
                        aRowSizes);
 }
 
 void
 nsGridContainerFrame::LineRange::ToPositionAndLength(
   const nsTArray<TrackSize>& aTrackSizes, nscoord* aPos, nscoord* aLength) const
 {
-  MOZ_ASSERT(mStart != 0 && Extent() > 0, "expected a definite LineRange");
+  //MOZ_ASSERT(mStart != 0 && Extent() > 0, "expected a definite LineRange");
+  // XXX relaxed it a bit for abs.pos. items for now:
+  MOZ_ASSERT(mStart != 0 && mEnd != 0, "expected a definite LineRange");
   nscoord pos = 0;
   const uint32_t start = mStart - 1;
   uint32_t i = 0;
   for (; i < start; ++i) {
     pos += aTrackSizes[i].mBase;
   }
   *aPos = pos;
 
@@ -785,29 +900,77 @@ nsGridContainerFrame::LineRange::ToPosit
   const uint32_t end = mEnd - 1;
   MOZ_ASSERT(end <= aTrackSizes.Length(), "aTrackSizes isn't large enough");
   for (; i < end; ++i) {
     length += aTrackSizes[i].mBase;
   }
   *aLength = length;
 }
 
+void
+nsGridContainerFrame::LineRange::ToPositionAndLengthForAbsPos(
+  const nsTArray<TrackSize>& aTrackSizes, nscoord aGridOrigin,
+  nscoord* aPos, nscoord* aLength) const
+{
+  // A "0" line represents "auto", which for abspos children, contributes
+  // the corresponding edge of the grid's padding-box.
+  if (mEnd == 0) {
+    if (mStart == 0) {
+      // done
+    } else {
+      const nscoord endPos = *aPos + *aLength;
+      nscoord startPos = ::GridLinePosition(mStart, aTrackSizes);
+      *aPos = aGridOrigin + startPos;
+      *aLength = std::max(endPos - *aPos, 0);
+    }
+  } else {
+    if (mStart == 0) {
+      nscoord endPos = ::GridLinePosition(mEnd, aTrackSizes);
+      *aLength = std::max(aGridOrigin + endPos, 0);
+    } else {
+      nscoord pos;
+      ToPositionAndLength(aTrackSizes, &pos, aLength);
+      *aPos = aGridOrigin + pos;
+    }
+  }
+}
+
 LogicalRect
 nsGridContainerFrame::ContainingBlockFor(
   const WritingMode& aWM,
   const GridArea& aArea,
   const nsTArray<TrackSize>& aColSizes,
   const nsTArray<TrackSize>& aRowSizes) const
 {
   nscoord i, b, iSize, bSize;
   aArea.mCols.ToPositionAndLength(aColSizes, &i, &iSize);
   aArea.mRows.ToPositionAndLength(aRowSizes, &b, &bSize);
   return LogicalRect(aWM, i, b, iSize, bSize);
 }
 
+LogicalRect
+nsGridContainerFrame::ContainingBlockForAbsPos(
+  const WritingMode& aWM,
+  const GridArea& aArea,
+  const nsTArray<TrackSize>& aColSizes,
+  const nsTArray<TrackSize>& aRowSizes,
+  const LogicalPoint& aGridOrigin,
+  const LogicalRect& aGridCB) const
+{
+  nscoord i = aGridCB.IStart(aWM);
+  nscoord b = aGridCB.BStart(aWM);
+  nscoord iSize = aGridCB.ISize(aWM);
+  nscoord bSize = aGridCB.BSize(aWM);
+  aArea.mCols.ToPositionAndLengthForAbsPos(aColSizes, aGridOrigin.I(aWM),
+                                           &i, &iSize);
+  aArea.mRows.ToPositionAndLengthForAbsPos(aRowSizes, aGridOrigin.B(aWM),
+                                           &b, &bSize);
+  return LogicalRect(aWM, i, b, iSize, bSize);
+}
+
 void
 nsGridContainerFrame::ReflowChildren(const LogicalRect&         aContentArea,
                                      const nsTArray<TrackSize>& aColSizes,
                                      const nsTArray<TrackSize>& aRowSizes,
                                      nsHTMLReflowMetrics&       aDesiredSize,
                                      const nsHTMLReflowState&   aReflowState,
                                      nsReflowStatus&            aStatus)
 {
@@ -838,16 +1001,54 @@ nsGridContainerFrame::ReflowChildren(con
     nsReflowStatus childStatus;
     ReflowChild(child, pc, childSize, childRS, wm, childPos,
                 gridWidth, 0, childStatus);
     FinishReflowChild(child, pc, childSize, &childRS, wm, childPos,
                       gridWidth, 0);
     ConsiderChildOverflow(aDesiredSize.mOverflowAreas, child);
     // XXX deal with 'childStatus' not being COMPLETE
   }
+
+  if (IsAbsoluteContainer()) {
+    nsFrameList children(GetChildList(GetAbsoluteListID()));
+    if (!children.IsEmpty()) {
+      LogicalMargin pad(aReflowState.ComputedLogicalPadding());
+      pad.ApplySkipSides(GetLogicalSkipSides(&aReflowState));
+      // 'gridOrigin' is the origin of the grid (the start of the first track),
+      // with respect to the grid container's padding-box (CB).
+      const LogicalPoint gridOrigin(wm, pad.IStart(wm), pad.BStart(wm));
+      const LogicalRect gridCB(wm, 0, 0,
+                               aContentArea.ISize(wm) + pad.IStartEnd(wm),
+                               aContentArea.BSize(wm) + pad.BStartEnd(wm));
+      for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
+        nsIFrame* child = e.get();
+        GridArea* area = GetGridAreaForChild(child);
+        MOZ_ASSERT(area);
+        LogicalRect itemCB(ContainingBlockForAbsPos(wm, *area,
+                                                    aColSizes, aRowSizes,
+                                                    gridOrigin, gridCB));
+        // nsAbsoluteContainingBlock::Reflow uses physical coordinates.
+        nsRect* cb = static_cast<nsRect*>(child->Properties().Get(
+                       GridItemContainingBlockRect()));
+        if (!cb) {
+          cb = new nsRect;
+          child->Properties().Set(GridItemContainingBlockRect(), cb);
+        }
+        *cb = itemCB.GetPhysicalRect(wm, gridWidth);
+      }
+      // This rect isn't used at all for layout so we use it to optimize
+      // away the virtual GetType() call in the callee in most cases.
+      // @see nsAbsoluteContainingBlock::Reflow
+      nsRect dummyRect(0, 0, VERY_LIKELY_A_GRID_CONTAINER, 0);
+      GetAbsoluteContainingBlock()->Reflow(this, pc, aReflowState, aStatus,
+                                           dummyRect, true,
+                                           true, true, // XXX could be optimized
+                                           &aDesiredSize.mOverflowAreas);
+    }
+  }
 }
 
 void
 nsGridContainerFrame::Reflow(nsPresContext*           aPresContext,
                              nsHTMLReflowMetrics&     aDesiredSize,
                              const nsHTMLReflowState& aReflowState,
                              nsReflowStatus&          aStatus)
 {
--- a/layout/generic/nsGridContainerFrame.h
+++ b/layout/generic/nsGridContainerFrame.h
@@ -33,36 +33,54 @@ public:
               const nsHTMLReflowState& aReflowState,
               nsReflowStatus&          aStatus) override;
   virtual nsIAtom* GetType() const override;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
+  /**
+   * Return the containing block for aChild which MUST be an abs.pos. child
+   * of a grid container.  This is just a helper method for
+   * nsAbsoluteContainingBlock::Reflow - it's not meant to be used elsewhere.
+   */
+  static const nsRect& GridItemCB(nsIFrame* aChild);
+
   struct TrackSize {
     nscoord mBase;
     nscoord mLimit;
   };
 
+  // @see nsAbsoluteContainingBlock::Reflow about this magic number
+  static const nscoord VERY_LIKELY_A_GRID_CONTAINER = -123456789;
+
+  NS_DECLARE_FRAME_PROPERTY(GridItemContainingBlockRect, DeleteValue<nsRect>)
+
 protected:
+  typedef mozilla::LogicalPoint LogicalPoint;
   typedef mozilla::LogicalRect LogicalRect;
   typedef mozilla::WritingMode WritingMode;
   typedef mozilla::css::GridNamedArea GridNamedArea;
   friend nsContainerFrame* NS_NewGridContainerFrame(nsIPresShell* aPresShell,
                                                     nsStyleContext* aContext);
   explicit nsGridContainerFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) {}
 
   /**
    * A LineRange can be definite or auto - when it's definite it represents
    * a consecutive set of tracks between a starting line and an ending line
    * (both 1-based) where mStart < mEnd.  Before it's definite it can also
    * represent an auto position with a span, where mStart == 0 and mEnd is
    * the (non-zero positive) span.
-   * In both states the invariant mEnd > mStart holds.
+   * In both states the invariant mEnd > mStart holds (for normal flow items).
+   *
+   * For abs.pos. grid items, mStart and mEnd may both be zero, meaning
+   * "attach this side to the grid container containing block edge".
+   * Additionally, mEnd >= mStart holds when both are definite (non-zero),
+   * i.e. the invariant is slightly relaxed compared to normal flow items.
    */
   struct LineRange {
    LineRange(uint32_t aStart, uint32_t aEnd)
       : mStart(aStart), mEnd(aEnd) {}
     bool IsAuto() const { return mStart == 0; }
     bool IsDefinite() const { return mStart != 0; }
     uint32_t Extent() const { return mEnd - mStart; }
     /**
@@ -83,16 +101,25 @@ protected:
      */
     uint32_t HypotheticalEnd() const { return IsAuto() ? mEnd + 1 : mEnd; }
     /**
      * Given an array of track sizes, return the starting position and length
      * of the tracks in this line range.
      */
     void ToPositionAndLength(const nsTArray<TrackSize>& aTrackSizes,
                              nscoord* aPos, nscoord* aLength) const;
+    /**
+     * Given an array of track sizes and a grid origin coordinate, adjust the
+     * abs.pos. containing block along an axis given by aPos and aLength.
+     * aPos and aLength should already be initialized to the grid container
+     * containing block for this axis before calling this method.
+     */
+    void ToPositionAndLengthForAbsPos(const nsTArray<TrackSize>& aTrackSizes,
+                                      nscoord aGridOrigin,
+                                      nscoord* aPos, nscoord* aLength) const;
 
     uint32_t mStart;  // the start line, or zero for 'auto'
     uint32_t mEnd;    // the end line, or the span length for 'auto'
   };
 
   /**
    * A GridArea is the area in the grid for a grid item.
    * The area is represented by two LineRanges, both of which can be auto
@@ -177,16 +204,31 @@ protected:
                              const nsStyleGridLine& aEnd,
                              const nsTArray<nsTArray<nsString>>& aLineNameList,
                              uint32_t GridNamedArea::* aAreaStart,
                              uint32_t GridNamedArea::* aAreaEnd,
                              uint32_t aExplicitGridEnd,
                              const nsStylePosition* aStyle);
 
   /**
+   * As above but for an abs.pos. child.  Any 'auto' lines will be represented
+   * by zero in the LineRange result.
+   * @param aGridEnd the last line in the (final) implicit grid
+   */
+  LineRange
+  ResolveAbsPosLineRange(const nsStyleGridLine& aStart,
+                         const nsStyleGridLine& aEnd,
+                         const nsTArray<nsTArray<nsString>>& aLineNameList,
+                         uint32_t GridNamedArea::* aAreaStart,
+                         uint32_t GridNamedArea::* aAreaEnd,
+                         uint32_t aExplicitGridEnd,
+                         uint32_t aGridEnd,
+                         const nsStylePosition* aStyle);
+
+  /**
    * Return a GridArea with non-auto lines placed at a definite line number
    * and with placement errors resolved.  One or both positions may still be
    * 'auto'.
    * @param aChild the grid item
    * @param aStyle the StylePosition() for the grid container
    */
   GridArea PlaceDefinite(nsIFrame* aChild, const nsStylePosition* aStyle);
 
@@ -240,16 +282,25 @@ protected:
    * If there's no such row in aStartCol, continue in position aStartCol+1,1.
    * Pre-condition: aArea->mCols.IsAuto() && aArea->mRows.IsAuto() is true.
    * Post-condition: aArea->IsDefinite() is true.
    */
   void PlaceAutoAutoInColOrder(uint32_t aStartCol, uint32_t aStartRow,
                                GridArea* aArea) const;
 
   /**
+   * Place an abs.pos. child and return its grid area.
+   * @note the resulting area may still have 'auto' lines in one or both
+   * dimensions (represented as zero).
+   * @param aChild the abs.pos. grid item to place
+   * @param aStyle the StylePosition() for the grid container
+   */
+  GridArea PlaceAbsPos(nsIFrame* aChild, const nsStylePosition* aStyle);
+
+  /**
    * Place all child frames into the grid and expand the (implicit) grid as
    * needed.  The allocated GridAreas are stored in the GridAreaProperty
    * frame property on the child frame.
    * @param aStyle the StylePosition() for the grid container
    */
   void PlaceGridItems(const nsStylePosition* aStyle);
 
   /**
@@ -327,16 +378,32 @@ protected:
    * @param aRowSizes row track sizes
    */
   LogicalRect ContainingBlockFor(const WritingMode& aWM,
                                  const GridArea& aArea,
                                  const nsTArray<TrackSize>& aColSizes,
                                  const nsTArray<TrackSize>& aRowSizes) const;
 
   /**
+   * Return the containing block for an abs.pos. grid item occupying aArea.
+   * Any 'auto' lines in the grid area will be aligned with grid container
+   * containing block on that side.
+   * @param aColSizes column track sizes
+   * @param aRowSizes row track sizes
+   * @param aGridOrigin the origin of the grid
+   * @param aGridCB the grid container containing block (its padding area)
+   */
+  LogicalRect ContainingBlockForAbsPos(const WritingMode& aWM,
+                                       const GridArea& aArea,
+                                       const nsTArray<TrackSize>& aColSizes,
+                                       const nsTArray<TrackSize>& aRowSizes,
+                                       const LogicalPoint& aGridOrigin,
+                                       const LogicalRect& aGridCB) const;
+
+  /**
    * Reflow and place our children.
    */
   void ReflowChildren(const LogicalRect&          aContentArea,
                       const nsTArray<TrackSize>&  aColSizes,
                       const nsTArray<TrackSize>&  aRowSizes,
                       nsHTMLReflowMetrics&        aDesiredSize,
                       const nsHTMLReflowState&    aReflowState,
                       nsReflowStatus&             aStatus);