Bug 1398483 - Implement column and row gap for flexbox. draft
authorMihir Iyer <miyer@mozilla.com>
Tue, 19 Jun 2018 10:28:31 -0700
changeset 811135 9a8f841ec346ee4cef5e81e557ee9288cf02a54c
parent 810823 348090c6b5c421c67b9dccc48742b54a854d6d0e
push id114200
push userbmo:miyer@mozilla.com
push dateWed, 27 Jun 2018 00:23:02 +0000
bugs1398483
milestone63.0a1
Bug 1398483 - Implement column and row gap for flexbox. MozReview-Commit-ID: 2EmaG3G0HRI
layout/generic/nsFlexContainerFrame.cpp
layout/generic/nsFlexContainerFrame.h
layout/reftests/w3c-css/submitted/flexbox/flexbox-column-row-gap-001-ref.html
layout/reftests/w3c-css/submitted/flexbox/flexbox-column-row-gap-001.html
layout/reftests/w3c-css/submitted/flexbox/flexbox-column-row-gap-002-ref.html
layout/reftests/w3c-css/submitted/flexbox/flexbox-column-row-gap-002.html
layout/reftests/w3c-css/submitted/flexbox/reftest.list
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -882,27 +882,33 @@ protected:
 
 /**
  * Represents a single flex line in a flex container.
  * Manages a linked list of the FlexItems that are in the line.
  */
 class nsFlexContainerFrame::FlexLine : public LinkedListElement<FlexLine>
 {
 public:
-  FlexLine()
+  explicit FlexLine(nscoord aMainGapSize)
   : mNumItems(0),
     mNumFrozenItems(0),
     mTotalItemMBP(0),
     mTotalOuterHypotheticalMainSize(0),
     mLineCrossSize(0),
     mFirstBaselineOffset(nscoord_MIN),
-    mLastBaselineOffset(nscoord_MIN)
+    mLastBaselineOffset(nscoord_MIN),
+    mMainGapSize(aMainGapSize)
   {}
 
-  // Returns the sum of our FlexItems' outer hypothetical main sizes.
+  nscoord GetSumOfGaps() const {
+    return mNumItems > 0 ? (mNumItems - 1) * mMainGapSize : 0;
+  }
+
+  // Returns the sum of our FlexItems' outer hypothetical main sizes plus the
+  // sum of main axis {row,column}-gaps between items.
   // ("outer" = margin-box, and "hypothetical" = before flexing)
   nscoord GetTotalOuterHypotheticalMainSize() const {
     return mTotalOuterHypotheticalMainSize;
   }
 
   // Accessors for our FlexItems & information about them:
   FlexItem* GetFirstItem()
   {
@@ -978,17 +984,25 @@ public:
     // don't want that to make mTotalOuterHypotheticalMainSize overflow to a
     // negative value), because that'd make us incorrectly think that we should
     // grow our flex items rather than shrink them when it comes time to
     // resolve flexible items. Hence, we sum up the hypothetical sizes using a
     // helper function AddChecked() to avoid overflow.
     mTotalItemMBP = AddChecked(mTotalItemMBP, itemMBP);
 
     mTotalOuterHypotheticalMainSize =
-      AddChecked(mTotalOuterHypotheticalMainSize, aItemOuterHypotheticalMainSize);
+      AddChecked(mTotalOuterHypotheticalMainSize,
+                 aItemOuterHypotheticalMainSize);
+
+    // If the item added was not the first item in the line, we add in any gap
+    // space as needed.
+    if (mNumItems >= 2) {
+      mTotalOuterHypotheticalMainSize =
+        AddChecked(mTotalOuterHypotheticalMainSize, mMainGapSize);
+    }
   }
 
   // Computes the cross-size and baseline position of this FlexLine, based on
   // its FlexItems.
   void ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker);
 
   // Returns the cross-size of this line.
   nscoord GetLineCrossSize() const { return mLineCrossSize; }
@@ -1022,16 +1036,32 @@ public:
    * distance from the line's cross-start edge.
    *
    * If there are no last baseline-aligned FlexItems, returns nscoord_MIN.
    */
   nscoord GetLastBaselineOffset() const {
     return mLastBaselineOffset;
   }
 
+  /**
+   * Returns the number of items held in this line. Used for total gap
+   * calculations.
+   */
+  uint32_t GetNumItems() const {
+    return mNumItems;
+  }
+
+  /**
+   * Returns the gap size in the main axis for this line. Used for gap
+   * calculations.
+   */
+  nscoord GetMainGapSize() const {
+    return mMainGapSize;
+  }
+
   // Runs the "Resolving Flexible Lengths" algorithm from section 9.7 of the
   // CSS flexbox spec to distribute aFlexContainerMainSize among our flex items.
   void ResolveFlexibleLengths(nscoord aFlexContainerMainSize,
                               ComputedFlexLineInfo* aLineInfo);
 
   void PositionItemsInMainAxis(uint8_t aJustifyContent,
                                nscoord aContentBoxMainSize,
                                const FlexboxAxisTracker& aAxisTracker);
@@ -1059,24 +1089,28 @@ private:
   // Number of *frozen* FlexItems in this line, based on FlexItem::IsFrozen().
   // Mostly used for optimization purposes, e.g. to bail out early from loops
   // when we can tell they have nothing left to do.
   uint32_t mNumFrozenItems;
 
   // Sum of margin/border/padding for the FlexItems in this FlexLine.
   nscoord mTotalItemMBP;
 
-  // Sum of FlexItems' outer hypothetical main sizes.
+  // Sum of FlexItems' outer hypothetical main sizes and all main-axis
+  // {row,columnm}-gaps between items.
   // (i.e. their flex base sizes, clamped via their min/max-size properties,
-  // plus their main-axis margin/border/padding.)
+  // plus their main-axis margin/border/padding, plus the sum of the gaps.)
   nscoord mTotalOuterHypotheticalMainSize;
 
   nscoord mLineCrossSize;
   nscoord mFirstBaselineOffset;
   nscoord mLastBaselineOffset;
+
+  // Maintain size of each {row,column}-gap in the main axis
+  const nscoord mMainGapSize;
 };
 
 // Information about a strut left behind by a FlexItem that's been collapsed
 // using "visibility:collapse".
 struct nsFlexContainerFrame::StrutInfo {
   StrutInfo(uint32_t aItemIdx, nscoord aStrutCrossSize)
     : mItemIdx(aItemIdx),
       mStrutCrossSize(aStrutCrossSize)
@@ -2227,16 +2261,19 @@ public:
 
   ~MainAxisPositionTracker() {
     MOZ_ASSERT(mNumPackingSpacesRemaining == 0,
                "miscounted the number of packing spaces");
     MOZ_ASSERT(mNumAutoMarginsInMainAxis == 0,
                "miscounted the number of auto margins");
   }
 
+  // Advances past the gap space (if any) between two flex items
+  void TraverseGap(nscoord aGapSize) { mPosition += aGapSize; }
+
   // Advances past the packing space (if any) between two flex items
   void TraversePackingSpace();
 
   // If aItem has any 'auto' margins in the main axis, this method updates the
   // corresponding values in its margin.
   void ResolveAutoMarginsInMainAxis(FlexItem& aItem);
 
 private:
@@ -2252,17 +2289,21 @@ private:
 // The "0" position represents the cross-start edge of the flex container's
 // content-box.
 class MOZ_STACK_CLASS CrossAxisPositionTracker : public PositionTracker {
 public:
   CrossAxisPositionTracker(FlexLine* aFirstLine,
                            const ReflowInput& aReflowInput,
                            nscoord aContentBoxCrossSize,
                            bool aIsCrossSizeDefinite,
-                           const FlexboxAxisTracker& aAxisTracker);
+                           const FlexboxAxisTracker& aAxisTracker,
+                           const nscoord aCrossGapSize);
+
+  // Advances past the gap (if any) between two flex lines
+  void TraverseGap() { mPosition += mCrossGapSize; }
 
   // Advances past the packing space (if any) between two flex lines
   void TraversePackingSpace();
 
   // Advances past the given FlexLine
   void TraverseLine(FlexLine& aLine) { mPosition += aLine.GetLineCrossSize(); }
 
 private:
@@ -2274,16 +2315,18 @@ private:
   void ExitMargin(const nsMargin& aMargin) = delete;
   void EnterChildFrame(nscoord aChildFrameSize) = delete;
   void ExitChildFrame(nscoord aChildFrameSize) = delete;
 
   nscoord  mPackingSpaceRemaining;
   uint32_t mNumPackingSpacesRemaining;
   // XXX this should be uint16_t when we add explicit fallback handling
   uint8_t  mAlignContent;
+
+  nscoord mCrossGapSize = 0;
 };
 
 // Utility class for managing our position along the cross axis, *within* a
 // single flex line.
 class MOZ_STACK_CLASS SingleLineCrossAxisPositionTracker : public PositionTracker {
 public:
   explicit SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker);
 
@@ -2554,21 +2597,21 @@ FlexLine::ResolveFlexibleLengths(nscoord
   if ((mNumFrozenItems == mNumItems) && !aLineInfo) {
     // All our items are frozen, so we have no flexible lengths to resolve,
     // and we aren't being asked to generate computed line info.
     return;
   }
   MOZ_ASSERT(!IsEmpty() || aLineInfo,
              "empty lines should take the early-return above");
 
-  // Subtract space occupied by our items' margins/borders/padding, so we can
-  // just be dealing with the space available for our flex items' content
+  // Subtract space occupied by our items' margins/borders/padding/gaps, so
+  // we can just be dealing with the space available for our flex items' content
   // boxes.
   nscoord spaceAvailableForFlexItemsContentBoxes =
-    aFlexContainerMainSize - mTotalItemMBP;
+    aFlexContainerMainSize - (mTotalItemMBP + GetSumOfGaps());
 
   nscoord origAvailableFreeSpace;
   bool isOrigAvailFreeSpaceInitialized = false;
 
   // NOTE: I claim that this chunk of the algorithm (the looping part) needs to
   // run the loop at MOST mNumItems times.  This claim should hold up
   // because we'll freeze at least one item on each loop iteration, and once
   // we've run out of items to freeze, there's nothing left to do.  However,
@@ -2895,16 +2938,19 @@ MainAxisPositionTracker::
   // we'll subtract out the main sizes of our flex items, so that it ends up
   // with the *actual* amount of packing space.
   for (const FlexItem* item = aLine->GetFirstItem(); item;
        item = item->getNext()) {
     mPackingSpaceRemaining -= item->GetOuterMainSize(mAxis);
     mNumAutoMarginsInMainAxis += item->GetNumAutoMarginsInAxis(mAxis);
   }
 
+  // Subtract space required for row/col gap from the remaining packing space
+  mPackingSpaceRemaining -= aLine->GetSumOfGaps();
+
   if (mPackingSpaceRemaining <= 0) {
     // No available packing space to use for resolving auto margins.
     mNumAutoMarginsInMainAxis = 0;
   }
 
   // If packing space is negative or we only have one item, 'space-between'
   // falls back to 'flex-start', and 'space-around' & 'space-evenly' fall back
   // to 'center'. In those cases, it's simplest to just pretend we have a
@@ -3042,22 +3088,24 @@ MainAxisPositionTracker::TraversePacking
   }
 }
 
 CrossAxisPositionTracker::
   CrossAxisPositionTracker(FlexLine* aFirstLine,
                            const ReflowInput& aReflowInput,
                            nscoord aContentBoxCrossSize,
                            bool aIsCrossSizeDefinite,
-                           const FlexboxAxisTracker& aAxisTracker)
+                           const FlexboxAxisTracker& aAxisTracker,
+                           const nscoord aCrossGapSize)
   : PositionTracker(aAxisTracker.GetCrossAxis(),
                     aAxisTracker.IsCrossAxisReversed()),
     mPackingSpaceRemaining(0),
     mNumPackingSpacesRemaining(0),
-    mAlignContent(aReflowInput.mStylePosition->mAlignContent)
+    mAlignContent(aReflowInput.mStylePosition->mAlignContent),
+    mCrossGapSize(aCrossGapSize)
 {
   MOZ_ASSERT(aFirstLine, "null first line pointer");
 
   // 'normal' behaves as 'stretch'
   if (mAlignContent == NS_STYLE_ALIGN_NORMAL) {
     mAlignContent = NS_STYLE_ALIGN_STRETCH;
   }
 
@@ -3100,16 +3148,21 @@ CrossAxisPositionTracker::
   // lines we have. (We need that count in some cases below.)
   mPackingSpaceRemaining = aContentBoxCrossSize;
   uint32_t numLines = 0;
   for (FlexLine* line = aFirstLine; line; line = line->getNext()) {
     mPackingSpaceRemaining -= line->GetLineCrossSize();
     numLines++;
   }
 
+  // Subtract space required for row/col gap from the remaining packing space
+  MOZ_ASSERT(numLines >= 1,
+             "GenerateFlexLines should've produced at least 1 line");
+  mPackingSpaceRemaining -= aCrossGapSize * (numLines - 1);
+
   // If packing space is negative, 'space-between' and 'stretch' behave like
   // 'flex-start', and 'space-around' and 'space-evenly' behave like 'center'.
   // In those cases, it's simplest to just pretend we have a different
   // 'align-content' value and share code. (If we only have one line, all of
   // the 'space-*' keywords fall back as well, but 'stretch' doesn't because
   // even a single line can still stretch.)
   if (mPackingSpaceRemaining < 0 && mAlignContent == NS_STYLE_ALIGN_STRETCH) {
       mAlignContent = NS_STYLE_ALIGN_FLEX_START;
@@ -3704,19 +3757,20 @@ FlexboxAxisTracker::InitAxesFromModernPr
     mIsCrossAxisReversed = false;
   }
 }
 
 // Allocates a new FlexLine, adds it to the given LinkedList (at the front or
 // back depending on aShouldInsertAtFront), and returns a pointer to it.
 static FlexLine*
 AddNewFlexLineToList(LinkedList<FlexLine>& aLines,
-                     bool aShouldInsertAtFront)
+                     bool aShouldInsertAtFront,
+                     nscoord aMainGapSize)
 {
-  FlexLine* newLine = new FlexLine();
+  FlexLine* newLine = new FlexLine(aMainGapSize);
   if (aShouldInsertAtFront) {
     aLines.insertFront(newLine);
   } else {
     aLines.insertBack(newLine);
   }
   return newLine;
 }
 
@@ -3756,16 +3810,17 @@ nsFlexContainerFrame::ShouldUseMozBoxCol
 void
 nsFlexContainerFrame::GenerateFlexLines(
   nsPresContext* aPresContext,
   const ReflowInput& aReflowInput,
   nscoord aContentBoxMainSize,
   nscoord aAvailableBSizeForContent,
   const nsTArray<StrutInfo>& aStruts,
   const FlexboxAxisTracker& aAxisTracker,
+  nscoord aMainGapSize,
   nsTArray<nsIFrame*>& aPlaceholders, /* out */
   LinkedList<FlexLine>& aLines /* out */)
 {
   MOZ_ASSERT(aLines.isEmpty(), "Expecting outparam to start out empty");
 
   const bool isSingleLine =
     NS_STYLE_FLEX_WRAP_NOWRAP == aReflowInput.mStylePosition->mFlexWrap;
 
@@ -3774,17 +3829,18 @@ nsFlexContainerFrame::GenerateFlexLines(
   // layout (with flipped axes) will still produce the correct result.
   // Here, we declare a convenience bool that we'll pass when adding a new
   // FlexLine or FlexItem, to make us insert it at the beginning of its list
   // (so the list ends up reversed).
   const bool shouldInsertAtFront = aAxisTracker.AreAxesInternallyReversed();
 
   // We have at least one FlexLine. Even an empty flex container has a single
   // (empty) flex line.
-  FlexLine* curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
+  FlexLine* curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront,
+                                           aMainGapSize);
 
   nscoord wrapThreshold;
   if (isSingleLine) {
     // Not wrapping. Set threshold to sentinel value that tells us not to wrap.
     wrapThreshold = NS_UNCONSTRAINEDSIZE;
   } else {
     // Wrapping! Set wrap threshold to flex container's content-box main-size.
     wrapThreshold = aContentBoxMainSize;
@@ -3838,17 +3894,17 @@ nsFlexContainerFrame::GenerateFlexLines(
     if (childFrame->IsPlaceholderFrame()) {
       aPlaceholders.AppendElement(childFrame);
       continue;
     }
 
     // Honor "page-break-before", if we're multi-line and this line isn't empty:
     if (!isSingleLine && !curLine->IsEmpty() &&
         childFrame->StyleDisplay()->mBreakBefore) {
-      curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
+      curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront, aMainGapSize);
     }
 
     UniquePtr<FlexItem> item;
     if (useMozBoxCollapseBehavior &&
         (NS_STYLE_VISIBILITY_COLLAPSE ==
          childFrame->StyleVisibility()->mVisible)) {
       // Legacy visibility:collapse behavior: make a 0-sized strut. (No need to
       // bother with aStruts and remembering cross size.)
@@ -3878,31 +3934,35 @@ nsFlexContainerFrame::GenerateFlexLines(
       // NOTE: We have to account for the fact that
       // itemOuterHypotheticalMainSize might be huge, if our item is (or
       // contains) a table with "table-layout:fixed". So we use AddChecked()
       // rather than (possibly-overflowing) normal addition, to be sure we don't
       // make the wrong judgement about whether the item fits on this line.
       nscoord newOuterSize =
         AddChecked(curLine->GetTotalOuterHypotheticalMainSize(),
                    itemOuterHypotheticalMainSize);
+      // Account for gap between this line's previous item and this item
+      newOuterSize = AddChecked(newOuterSize, aMainGapSize);
       if (newOuterSize == nscoord_MAX || newOuterSize > wrapThreshold) {
-        curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
+        curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront,
+                                       aMainGapSize);
       }
     }
 
     // Add item to current flex line (and update the line's bookkeeping about
     // how large its items collectively are).
-    curLine->AddItem(item.release(), shouldInsertAtFront,
+    curLine->AddItem(item.release(),
+                     shouldInsertAtFront,
                      itemInnerHypotheticalMainSize,
                      itemOuterHypotheticalMainSize);
 
     // Honor "page-break-after", if we're multi-line and have more children:
     if (!isSingleLine && childFrame->GetNextSibling() &&
         childFrame->StyleDisplay()->mBreakAfter) {
-      curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
+      curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront, aMainGapSize);
     }
     itemIdxInContainer++;
   }
 }
 
 // Retrieves the content-box main-size of our flex container from the
 // reflow state (specifically, the main-size of *this continuation* of the
 // flex container).
@@ -4084,16 +4144,19 @@ FlexLine::PositionItemsInMainAxis(uint8_
     mainAxisPosnTracker.EnterMargin(item->GetMargin());
     mainAxisPosnTracker.EnterChildFrame(itemMainBorderBoxSize);
 
     item->SetMainPosition(mainAxisPosnTracker.GetPosition());
 
     mainAxisPosnTracker.ExitChildFrame(itemMainBorderBoxSize);
     mainAxisPosnTracker.ExitMargin(item->GetMargin());
     mainAxisPosnTracker.TraversePackingSpace();
+    if (item->getNext()) {
+      mainAxisPosnTracker.TraverseGap(mMainGapSize);
+    }
   }
 }
 
 /**
  * Given the flex container's "flex-relative ascent" (i.e. distance from the
  * flex container's content-box cross-start edge to its baseline), returns
  * its actual physical ascent value (the distance from the *border-box* top
  * edge to its baseline).
@@ -4265,28 +4328,48 @@ nsFlexContainerFrame::Reflow(nsPresConte
     availableBSizeForContent -=
       aReflowInput.ComputedLogicalBorderPadding().BStart(wm);
     // (Don't let that push availableBSizeForContent below zero, though):
     availableBSizeForContent = std::max(availableBSizeForContent, 0);
   }
 
   nscoord contentBoxMainSize = GetMainSizeFromReflowInput(aReflowInput,
                                                           axisTracker);
+  // Calculate gap size for main and cross axis
+  nscoord mainGapSize;
+  nscoord crossGapSize;
+  if (axisTracker.IsRowOriented()) {
+    mainGapSize = nsLayoutUtils::ResolveGapToLength(stylePos->mColumnGap,
+                                                    contentBoxMainSize);
+    crossGapSize =
+      nsLayoutUtils::ResolveGapToLength(stylePos->mRowGap,
+                                        GetEffectiveComputedBSize(aReflowInput));
+  } else {
+    mainGapSize = nsLayoutUtils::ResolveGapToLength(stylePos->mRowGap,
+                                                    contentBoxMainSize);
+    NS_WARNING_ASSERTION(aReflowInput.ComputedISize() != NS_UNCONSTRAINEDSIZE,
+                         "Unconstrained inline size; this should only result "
+                         "from huge sizes (not intrinsic sizing w/ orthogonal "
+                         "flows)");
+    crossGapSize =
+      nsLayoutUtils::ResolveGapToLength(stylePos->mColumnGap,
+                                        aReflowInput.ComputedISize());
+  }
 
   AutoTArray<StrutInfo, 1> struts;
   DoFlexLayout(aPresContext, aDesiredSize, aReflowInput, aStatus,
                contentBoxMainSize, availableBSizeForContent,
-               struts, axisTracker);
+               struts, axisTracker, mainGapSize, crossGapSize);
 
   if (!struts.IsEmpty()) {
     // We're restarting flex layout, with new knowledge of collapsed items.
     aStatus.Reset();
     DoFlexLayout(aPresContext, aDesiredSize, aReflowInput, aStatus,
                  contentBoxMainSize, availableBSizeForContent,
-                 struts, axisTracker);
+                 struts, axisTracker, mainGapSize, crossGapSize);
   }
 }
 
 // RAII class to clean up a list of FlexLines.
 // Specifically, this removes each line from the list, deletes all the
 // FlexItems in its list, and deletes the FlexLine.
 class MOZ_RAII AutoFlexLineListClearer
 {
@@ -4510,28 +4593,31 @@ nsFlexContainerFrame::IsUsedFlexBasisCon
 void
 nsFlexContainerFrame::DoFlexLayout(nsPresContext*           aPresContext,
                                    ReflowOutput&     aDesiredSize,
                                    const ReflowInput& aReflowInput,
                                    nsReflowStatus&          aStatus,
                                    nscoord aContentBoxMainSize,
                                    nscoord aAvailableBSizeForContent,
                                    nsTArray<StrutInfo>& aStruts,
-                                   const FlexboxAxisTracker& aAxisTracker)
+                                   const FlexboxAxisTracker& aAxisTracker,
+                                   nscoord aMainGapSize,
+                                   nscoord aCrossGapSize)
 {
   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
 
   LinkedList<FlexLine> lines;
   nsTArray<nsIFrame*> placeholderKids;
   AutoFlexLineListClearer cleanupLines(lines);
 
   GenerateFlexLines(aPresContext, aReflowInput,
                     aContentBoxMainSize,
                     aAvailableBSizeForContent,
                     aStruts, aAxisTracker,
+                    aMainGapSize,
                     placeholderKids, lines);
 
   if (lines.getFirst()->IsEmpty() &&
       !lines.getFirst()->getNext()) {
     // We have no flex items, our parent should synthesize a baseline if needed.
     AddStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE);
   } else {
     RemoveStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE);
@@ -4674,17 +4760,18 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
     ComputeCrossSize(aReflowInput, aAxisTracker, sumLineCrossSizes,
                      aAvailableBSizeForContent, &isCrossSizeDefinite, aStatus);
 
   // Set up state for cross-axis alignment, at a high level (outside the
   // scope of a particular flex line)
   CrossAxisPositionTracker
     crossAxisPosnTracker(lines.getFirst(),
                          aReflowInput, contentBoxCrossSize,
-                         isCrossSizeDefinite, aAxisTracker);
+                         isCrossSizeDefinite, aAxisTracker,
+                         aCrossGapSize);
 
   // Now that we know the cross size of each line (including
   // "align-content:stretch" adjustments, from the CrossAxisPositionTracker
   // constructor), we can create struts for any flex items with
   // "visibility: collapse" (and restart flex layout).
   if (aStruts.IsEmpty() && // (Don't make struts if we already did)
       !ShouldUseMozBoxCollapseBehavior(aReflowInput.mStyleDisplay)) {
     BuildStrutInfoFromCollapsedItems(lines.getFirst(), aStruts);
@@ -4712,16 +4799,17 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
           contentBoxCrossSize, aReflowInput, aAxisTracker);
     }
   }
 
   const auto justifyContent = IsLegacyBox(aReflowInput.mFrame) ?
     ConvertLegacyStyleToJustifyContent(StyleXUL()) :
     aReflowInput.mStylePosition->mJustifyContent;
 
+
   lineIndex = 0;
   for (FlexLine* line = lines.getFirst(); line; line = line->getNext(),
                                                 ++lineIndex) {
     // Main-Axis Alignment - Flexbox spec section 9.5
     // ==============================================
     line->PositionItemsInMainAxis(justifyContent,
                                   aContentBoxMainSize,
                                   aAxisTracker);
@@ -4733,16 +4821,20 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
     }
 
     // Cross-Axis Alignment - Flexbox spec section 9.6
     // ===============================================
     line->PositionItemsInCrossAxis(crossAxisPosnTracker.GetPosition(),
                                    aAxisTracker);
     crossAxisPosnTracker.TraverseLine(*line);
     crossAxisPosnTracker.TraversePackingSpace();
+
+    if (line->getNext()) {
+      crossAxisPosnTracker.TraverseGap();
+    }
   }
 
   // If the container should derive its baseline from the last FlexLine,
   // do that here (while crossAxisPosnTracker is conveniently pointing
   // at the cross-end edge of that line, which the line's baseline offset is
   // measured from):
   if (aAxisTracker.AreAxesInternallyReversed()) {
     nscoord lastLineBaselineOffset = lines.getLast()->GetFirstBaselineOffset();
@@ -5140,38 +5232,56 @@ nsFlexContainerFrame::IntrinsicISize(gfx
                                      IntrinsicISizeType aType)
 {
   nscoord containerISize = 0;
   RenumberList();
 
   const nsStylePosition* stylePos = StylePosition();
   const FlexboxAxisTracker axisTracker(this, GetWritingMode());
 
+  nscoord mainGapSize;
+  if (axisTracker.IsRowOriented()) {
+    mainGapSize = nsLayoutUtils::ResolveGapToLength(stylePos->mColumnGap,
+                                                    NS_UNCONSTRAINEDSIZE);
+  } else {
+    mainGapSize = nsLayoutUtils::ResolveGapToLength(stylePos->mRowGap,
+                                                    NS_UNCONSTRAINEDSIZE);
+  }
+
   const bool useMozBoxCollapseBehavior =
     ShouldUseMozBoxCollapseBehavior(StyleDisplay());
 
+  // The loop below sets aside space for a gap before each item besides the
+  // first. This bool helps us handle that special-case.
+  bool onFirstChild = true;
+
   for (nsIFrame* childFrame : mFrames) {
     // If we're using legacy "visibility:collapse" behavior, then we don't
     // care about the sizes of any collapsed children.
     if (!useMozBoxCollapseBehavior ||
         (NS_STYLE_VISIBILITY_COLLAPSE !=
          childFrame->StyleVisibility()->mVisible)) {
       nscoord childISize =
         nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
                                              aType);
       // * For a row-oriented single-line flex container, the intrinsic
-      // {min/pref}-isize is the sum of its items' {min/pref}-isizes.
+      // {min/pref}-isize is the sum of its items' {min/pref}-isizes and
+      // (n-1) column gaps.
       // * For a column-oriented flex container, the intrinsic min isize
       // is the max of its items' min isizes.
       // * For a row-oriented multi-line flex container, the intrinsic
       // pref isize is former (sum), and its min isize is the latter (max).
       bool isSingleLine = (NS_STYLE_FLEX_WRAP_NOWRAP == stylePos->mFlexWrap);
       if (axisTracker.IsRowOriented() &&
           (isSingleLine || aType == nsLayoutUtils::PREF_ISIZE)) {
         containerISize += childISize;
+        if (!onFirstChild) {
+          containerISize += mainGapSize;
+        }
+        onFirstChild = false;
       } else { // (col-oriented, or MIN_ISIZE for multi-line row flex container)
         containerISize = std::max(containerISize, childISize);
       }
     }
   }
 
   return containerISize;
 }
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -253,17 +253,19 @@ protected:
    */
   void DoFlexLayout(nsPresContext*           aPresContext,
                     ReflowOutput&     aDesiredSize,
                     const ReflowInput& aReflowInput,
                     nsReflowStatus&          aStatus,
                     nscoord aContentBoxMainSize,
                     nscoord aAvailableBSizeForContent,
                     nsTArray<StrutInfo>& aStruts,
-                    const FlexboxAxisTracker& aAxisTracker);
+                    const FlexboxAxisTracker& aAxisTracker,
+                    nscoord aMainGapSize,
+                    nscoord aCrossGapSize);
 
   /**
    * Checks whether our child-frame list "mFrames" is sorted, using the given
    * IsLessThanOrEqual function, and sorts it if it's not already sorted.
    *
    * XXXdholbert Once we support pagination, we need to make this function
    * check our continuations as well (or wrap it in a function that does).
    *
@@ -348,16 +350,17 @@ protected:
    * (Absolutely positioned children of a flex container are *not* flex items.)
    */
   void GenerateFlexLines(nsPresContext* aPresContext,
                          const ReflowInput& aReflowInput,
                          nscoord aContentBoxMainSize,
                          nscoord aAvailableBSizeForContent,
                          const nsTArray<StrutInfo>& aStruts,
                          const FlexboxAxisTracker& aAxisTracker,
+                         nscoord aMainGapSize,
                          nsTArray<nsIFrame*>& aPlaceholders,
                          mozilla::LinkedList<FlexLine>& aLines);
 
   nscoord GetMainSizeFromReflowInput(const ReflowInput& aReflowInput,
                                      const FlexboxAxisTracker& aAxisTracker);
 
   nscoord ComputeCrossSize(const ReflowInput& aReflowInput,
                            const FlexboxAxisTracker& aAxisTracker,
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-column-row-gap-001-ref.html
@@ -0,0 +1,97 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0
+-->
+<!--
+     Reference for the correctness of gaps in horizontal and vertical multi-line
+     flex containers.
+-->
+<html>
+<head>
+  <title>Reference: Testing row and column gaps in horizontal and vertical multi-line flex containers with the space-around property and auto margins</title>
+  <link rel="author" title="Mihir Iyer" href="mailto:miyer@mozilla.com">
+  <meta charset="utf-8">
+  <style>
+    .outerBox {
+      width: 200px;
+      height: 220px;
+      border: 1px solid black;
+      float: left;
+    }
+    .flexContainer {
+      display: flex;
+      align-content: space-around;
+      justify-content: space-around;
+    }
+    .rowNoWrap {
+      flex-flow: row nowrap;
+    }
+    .columnNoWrap {
+      flex-flow: column nowrap;
+    }
+    .item {
+      border: 1px solid blue;
+      background: lightblue;
+      width: 28px;
+      height: 28px;
+    }
+    .autoLBMargins {
+      margin-left: auto;
+      margin-bottom: auto;
+    }
+    .autoBMargin {
+      margin-bottom: auto;
+    }
+    .right20 {
+      margin-right: 20px;
+    }
+    .bottom40 {
+      margin-bottom: 40px;
+    }
+    .tb30100 {
+      margin-top: 30px;
+      margin-bottom: 100px;
+    }
+    .lr3080 {
+      margin-left: 30px;
+      margin-right: 80px;
+    }
+    .w200 {
+      width: 200px;
+    }
+    .h220 {
+      height: 220px;
+    }
+    .fleft {
+      float: left;
+    }
+  </style>
+</head>
+<body>
+  <div class="outerBox">
+    <div class="flexContainer w200 rowNoWrap tb30100">
+      <div class="item right20"></div>
+      <div class="item right20"></div>
+      <div class="item right20"></div>
+      <div class="item"></div>
+    </div>
+    <div class="flexContainer w200 rowNoWrap">
+      <div class="item autoLBMargins right20"></div>
+      <div class="item"></div>
+    </div>
+  </div>
+  <div class="outerBox">
+    <div class="flexContainer fleft h220 columnNoWrap lr3080">
+      <div class="item bottom40"></div>
+      <div class="item bottom40"></div>
+      <div class="item"></div>
+    </div>
+    <div class="flexContainer fleft  h220 columnNoWrap">
+      <div class="item bottom40"></div>
+      <div class="item bottom40"></div>
+      <div class="item autoBMargin"></div>
+    </div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-column-row-gap-001.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0
+-->
+<!--
+     Testcase for the correctness of gaps in horizontal and vertical multi-line
+     flex containers.
+-->
+<html>
+<head>
+  <title>CSS Test: Testing row and column gaps in horizontal and vertical multi-line flex containers with the space-around property and auto margins</title>
+  <link rel="author" title="Mihir Iyer" href="mailto:miyer@mozilla.com">
+  <link rel="help" href="https://drafts.csswg.org/css-align/#gap-flex">
+  <link rel="match" href="flexbox-column-row-gap-001-ref.html">
+  <meta charset="utf-8">
+  <style>
+    .flexContainer {
+      display: flex;
+      width: 200px;
+      height: 220px;
+      border: 1px solid black;
+      column-gap: 10%;
+      row-gap: 40px;
+      align-content: space-around;
+      justify-content: space-around;
+      float: left;
+    }
+    .rowWrap {
+      flex-flow: row wrap;
+    }
+    .columnWrap {
+      flex-flow: column wrap;
+    }
+    .item {
+      border: 1px solid blue;
+      background: lightblue;
+      width: 28px;
+      height: 28px;
+    }
+    .autoLBMargins {
+      margin-left: auto;
+      margin-bottom: auto;
+    }
+  </style>
+</head>
+<body>
+  <div class="flexContainer rowWrap">
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item autoLBMargins"></div>
+    <div class="item"></div>
+  </div>
+  <div class="flexContainer columnWrap">
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item autoLBMargins"></div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-column-row-gap-002-ref.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0
+-->
+<!--
+     Testcase for the correctness of gaps in vertical writing mode
+     multi-line flex containers.
+-->
+<html>
+<head>
+  <title>Reference: Testing row and column gaps in vertical writing mode multi-line flex containers</title>
+  <link rel="author" title="Mihir Iyer" href="mailto:miyer@mozilla.com">
+  <meta charset="utf-8">
+  <style>
+    .flexContainer {
+      display: flex;
+      width: 220px;
+      height: 200px;
+      border: 1px solid black;
+      align-content: flex-start;
+      justify-content: flex-end;
+      float: left;
+      writing-mode: vertical-lr;
+    }
+    .rowWrap {
+      flex-flow: row wrap;
+    }
+    .columnWrap {
+      flex-flow: column wrap;
+    }
+    .item {
+      border: 1px solid blue;
+      background: lightblue;
+      width: 28px;
+      height: 28px;
+    }
+    .b20 {
+      margin-bottom: 20px;
+    }
+    .t20 {
+      margin-top: 20px;
+    }
+    .l10 {
+      margin-left: 10px;
+    }
+  </style>
+</head>
+<body>
+  <div class="flexContainer rowWrap">
+    <div class="item b20"></div>
+    <div class="item b20"></div>
+    <div class="item b20"></div>
+    <div class="item"></div>
+    <div class="item l10 b20"></div>
+    <div class="item l10"></div>
+  </div>
+  <div class="flexContainer columnWrap">
+    <div class="item"></div>
+    <div class="item l10"></div>
+    <div class="item l10"></div>
+    <div class="item l10"></div>
+    <div class="item l10"></div>
+    <div class="item l10 t20"></div>
+  </div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-column-row-gap-002.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0
+-->
+<!--
+     Testcase for the correctness of gaps in vertical writing mode multi-line
+     flex containers.
+-->
+<html>
+<head>
+  <title>CSS Test: Testing row and column gaps in vertical writing mode multi-line flex containers</title>
+  <link rel="author" title="Mihir Iyer" href="mailto:miyer@mozilla.com">
+  <link rel="help" href="https://drafts.csswg.org/css-align/#gap-flex">
+  <link rel="match" href="flexbox-column-row-gap-002-ref.html">
+  <meta charset="utf-8">
+  <style>
+    .flexContainer {
+      display: flex;
+      width: 220px;
+      height: 200px;
+      border: 1px solid black;
+      column-gap: 10%;
+      row-gap: 10px;
+      align-content: flex-start;
+      justify-content: flex-end;
+      float: left;
+      writing-mode: vertical-lr;
+    }
+    .rowWrap {
+      flex-flow: row wrap;
+    }
+    .columnWrap {
+      flex-flow: column wrap;
+    }
+    .item {
+      border: 1px solid blue;
+      background: lightblue;
+      width: 28px;
+      height: 28px;
+    }
+  </style>
+</head>
+<body>
+  <div class="flexContainer rowWrap">
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+  </div>
+  <div class="flexContainer columnWrap">
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+  </div>
+</body>
+</html>
--- a/layout/reftests/w3c-css/submitted/flexbox/reftest.list
+++ b/layout/reftests/w3c-css/submitted/flexbox/reftest.list
@@ -93,16 +93,20 @@ fuzzy-if(Android,158,32) == flexbox-alig
 == flexbox-break-request-vert-002b.html  flexbox-break-request-vert-002-ref.html
 
 # Tests for flex items with "visibility:collapse"
 == flexbox-collapsed-item-baseline-001.html flexbox-collapsed-item-baseline-001-ref.html
 == flexbox-collapsed-item-horiz-001.html flexbox-collapsed-item-horiz-001-ref.html
 == flexbox-collapsed-item-horiz-002.html flexbox-collapsed-item-horiz-002-ref.html
 == flexbox-collapsed-item-horiz-003.html flexbox-collapsed-item-horiz-003-ref.html
 
+# Tests for "row gap" and "column gap"
+== flexbox-column-row-gap-001.html flexbox-column-row-gap-001-ref.html
+== flexbox-column-row-gap-002.html flexbox-column-row-gap-002-ref.html
+
 # Tests for "flex-basis: content"
 == flexbox-flex-basis-content-001a.html flexbox-flex-basis-content-001-ref.html
 == flexbox-flex-basis-content-001b.html flexbox-flex-basis-content-001-ref.html
 == flexbox-flex-basis-content-002a.html flexbox-flex-basis-content-002-ref.html
 == flexbox-flex-basis-content-002b.html flexbox-flex-basis-content-002-ref.html
 == flexbox-flex-basis-content-003a.html flexbox-flex-basis-content-003-ref.html
 == flexbox-flex-basis-content-003b.html flexbox-flex-basis-content-003-ref.html
 == flexbox-flex-basis-content-004a.html flexbox-flex-basis-content-004-ref.html