Bug 1297774 - Implement safe/unsafe for flexbox 'justify-content' and 'align-{content,self,items}' draft
authorMihir Iyer <miyer@mozilla.com>
Tue, 26 Jun 2018 14:33:02 -0700
changeset 819352 34798edc91f875015d45deb9541a45be6f3914de
parent 819215 547144f5596c1a146b208d68d93950a6313080ca
push id116527
push userbmo:miyer@mozilla.com
push dateTue, 17 Jul 2018 18:07:48 +0000
bugs1297774
milestone63.0a1
Bug 1297774 - Implement safe/unsafe for flexbox 'justify-content' and 'align-{content,self,items}' MozReview-Commit-ID: BHpaSvj5EOW
layout/generic/nsFlexContainerFrame.cpp
layout/reftests/w3c-css/submitted/flexbox/flexbox-safe-overflow-position-001-ref.html
layout/reftests/w3c-css/submitted/flexbox/flexbox-safe-overflow-position-001.html
layout/reftests/w3c-css/submitted/flexbox/reftest.list
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -579,17 +579,18 @@ public:
   // on which axes those callsites are reasoning about).
   bool IsInlineAxisMainAxis() const  { return mIsInlineAxisMainAxis;  }
   bool IsInlineAxisCrossAxis() const { return !mIsInlineAxisMainAxis; }
   bool IsBlockAxisMainAxis() const   { return !mIsInlineAxisMainAxis; }
   bool IsBlockAxisCrossAxis() const  { return mIsInlineAxisMainAxis;  }
 
 
   WritingMode GetWritingMode() const { return mWM; }
-  uint8_t GetAlignSelf() const     { return mAlignSelf; }
+  uint8_t GetAlignSelf() const { return mAlignSelf; }
+  uint8_t GetAlignSelfFlags() const { return mAlignSelfFlags; }
 
   // Returns the flex factor (flex-grow or flex-shrink), depending on
   // 'aIsUsingFlexGrow'.
   //
   // Asserts fatally if called on a frozen item (since frozen items are not
   // flexible).
   float GetFlexFactor(bool aIsUsingFlexGrow)
   {
@@ -873,16 +874,17 @@ protected:
   bool mNeedsMinSizeAutoResolution;
 
   // Does this item have an auto margin in either main or cross axis?
   bool mHasAnyAutoMargin;
 
   uint8_t mAlignSelf; // My "align-self" computed value (with "auto"
                       // swapped out for parent"s "align-items" value,
                       // in our constructor).
+  uint8_t mAlignSelfFlags; // Flags for 'align-self' (safe/unsafe/legacy)
 };
 
 /**
  * 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>
 {
@@ -1936,17 +1938,18 @@ FlexItem::FlexItem(ReflowInput& aFlexIte
     mAlignSelf = ConvertLegacyStyleToAlignItems(containerStyleXUL);
   } else {
     mAlignSelf = aFlexItemReflowInput.mStylePosition->UsedAlignSelf(
                    containerRS->mFrame->Style());
     if (MOZ_LIKELY(mAlignSelf == NS_STYLE_ALIGN_NORMAL)) {
       mAlignSelf = NS_STYLE_ALIGN_STRETCH;
     }
 
-    // XXX strip off the <overflow-position> bit until we implement that
+    // Store and strip off the <overflow-position> bits
+    mAlignSelfFlags = mAlignSelf & NS_STYLE_ALIGN_FLAG_BITS;
     mAlignSelf &= ~NS_STYLE_ALIGN_FLAG_BITS;
   }
 
   SetFlexBaseSizeAndMainSize(aFlexBaseSize);
   CheckForMinSizeAuto(aFlexItemReflowInput, aAxisTracker);
 
 
   const nsStyleSides& styleMargin =
@@ -2918,42 +2921,48 @@ MainAxisPositionTracker::
                           nscoord aContentBoxMainSize)
   : PositionTracker(aAxisTracker.GetMainAxis(),
                     aAxisTracker.IsMainAxisReversed()),
     mPackingSpaceRemaining(aContentBoxMainSize), // we chip away at this below
     mNumAutoMarginsInMainAxis(0),
     mNumPackingSpacesRemaining(0),
     mJustifyContent(aJustifyContent)
 {
+  // Extract the flag portion of mJustifyContent and strip off the flag bits
+  uint8_t justifyContentFlags = mJustifyContent & NS_STYLE_JUSTIFY_FLAG_BITS;
+  mJustifyContent &= ~NS_STYLE_JUSTIFY_FLAG_BITS;
+
   // 'normal' behaves as 'stretch', and 'stretch' behaves as 'flex-start',
   // in the main axis
   // https://drafts.csswg.org/css-align-3/#propdef-justify-content
   if (mJustifyContent == NS_STYLE_JUSTIFY_NORMAL ||
       mJustifyContent == NS_STYLE_JUSTIFY_STRETCH) {
     mJustifyContent = NS_STYLE_JUSTIFY_FLEX_START;
   }
 
-  // XXX strip off the <overflow-position> bit until we implement that
-  mJustifyContent &= ~NS_STYLE_JUSTIFY_FLAG_BITS;
-
   // mPackingSpaceRemaining is initialized to the container's main size.  Now
   // 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 and <overflow-position> is set to 'safe'
+    // all justify options fall back to 'start'
+    if (justifyContentFlags & NS_STYLE_JUSTIFY_SAFE) {
+      mJustifyContent = NS_STYLE_JUSTIFY_START;
+    }
   }
 
   // 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
   // different 'justify-content' value and share code.
   if (mPackingSpaceRemaining < 0 || aLine->NumItems() == 1) {
     if (mJustifyContent == NS_STYLE_JUSTIFY_SPACE_BETWEEN) {
@@ -3099,24 +3108,25 @@ CrossAxisPositionTracker::
                     aAxisTracker.IsCrossAxisReversed()),
     mPackingSpaceRemaining(0),
     mNumPackingSpacesRemaining(0),
     mAlignContent(aReflowInput.mStylePosition->mAlignContent),
     mCrossGapSize(aCrossGapSize)
 {
   MOZ_ASSERT(aFirstLine, "null first line pointer");
 
+  // Extract and strip the flag bits from alignContent
+  uint8_t alignContentFlags = mAlignContent & NS_STYLE_ALIGN_FLAG_BITS;
+  mAlignContent &= ~NS_STYLE_ALIGN_FLAG_BITS;
+
   // 'normal' behaves as 'stretch'
   if (mAlignContent == NS_STYLE_ALIGN_NORMAL) {
     mAlignContent = NS_STYLE_ALIGN_STRETCH;
   }
 
-  // XXX strip of the <overflow-position> bit until we implement that
-  mAlignContent &= ~NS_STYLE_ALIGN_FLAG_BITS;
-
   const bool isSingleLine =
     NS_STYLE_FLEX_WRAP_NOWRAP == aReflowInput.mStylePosition->mFlexWrap;
   if (isSingleLine) {
     MOZ_ASSERT(!aFirstLine->getNext(),
                "If we're styled as single-line, we should only have 1 line");
     // "If the flex container is single-line and has a definite cross size, the
     // cross size of the flex line is the flex container's inner cross size."
     //
@@ -3153,16 +3163,23 @@ CrossAxisPositionTracker::
     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 <overflow-position> is 'safe' and packing space is negative
+  // all align options fall back to 'start'
+  if ((alignContentFlags & NS_STYLE_ALIGN_SAFE) &&
+      mPackingSpaceRemaining < 0) {
+    mAlignContent = NS_STYLE_ALIGN_START;
+  }
+
   // 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;
@@ -3511,16 +3528,24 @@ SingleLineCrossAxisPositionTracker::
   if (aAxisTracker.AreAxesInternallyReversed()) {
     if (alignSelf == NS_STYLE_ALIGN_FLEX_START) {
       alignSelf = NS_STYLE_ALIGN_FLEX_END;
     } else if (alignSelf == NS_STYLE_ALIGN_FLEX_END) {
       alignSelf = NS_STYLE_ALIGN_FLEX_START;
     }
   }
 
+  // 'align-self' falls back to 'flex-start' if it is 'center'/'flex-end' and we
+  // have cross axis overflow
+  // XXX we should really be falling back to 'start' as of bug 1472843
+  if (aLine.GetLineCrossSize() < aItem.GetOuterCrossSize(mAxis) &&
+      (aItem.GetAlignSelfFlags() & NS_STYLE_ALIGN_SAFE)) {
+    alignSelf = NS_STYLE_ALIGN_FLEX_START;
+  }
+
   switch (alignSelf) {
     case NS_STYLE_ALIGN_SELF_START:
     case NS_STYLE_ALIGN_SELF_END:
       NS_WARNING("NYI: align-items/align-self:left/right/self-start/self-end");
       MOZ_FALLTHROUGH;
     case NS_STYLE_ALIGN_FLEX_START:
       // No space to skip over -- we're done.
       break;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-safe-overflow-position-001-ref.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<!--
+    Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0
+-->
+<html>
+<head>
+  <title>Reference: Testing safe overflow-position for align-content, justify-content, and align-items in flex containers</title>
+  <link rel="author" title="Mihir Iyer" href="mailto:miyer@mozilla.com">
+  <meta charset="utf-8">
+  <style>
+    .flex {
+      display: flex;
+      width: 85px;
+      height: 65px;
+      border: 1px solid black;
+      align-content: flex-end;
+      justify-content: center;
+      align-items: center;
+      float: left;
+      clear: both;
+      margin-top: 100px;
+    }
+    .rowNoWrap {
+      flex-flow: row nowrap;
+    }
+    .columnNoWrap {
+      flex-flow: column wrap;
+    }
+    .item {
+      border: 1px solid blue;
+      background: lightblue;
+      width: 28px;
+      height: 28px;
+      flex-shrink: 0;
+    }
+    .bigItem {
+      border: 1px solid blue;
+      background: lightblue;
+      width: 28px;
+      height: 90px;
+      flex-shrink: 0;
+    }
+    .alignContentStart {
+      align-content: start;
+    }
+    .justifyContentStart {
+      justify-content: start;
+    }
+    .alignSelfStart {
+      align-self: start;
+    }
+  </style>
+</head>
+<body>
+  <div class="flex rowNoWrap justifyContentStart">
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="bigItem alignSelfStart"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+  </div>
+  <div class="flex columnNoWrap alignContentStart">
+    <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>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-safe-overflow-position-001.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<!--
+    Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/publicdomain/zero/1.0
+-->
+<html>
+<head>
+  <title>CSS Test: Testing safe overflow-position for align-content, justify-content, and align-items in flex containers</title>
+  <link rel="author" title="Mihir Iyer" href="mailto:miyer@mozilla.com">
+  <link rel="help" href="https://drafts.csswg.org/css-align-3/#overflow-values">
+  <link rel="match" href="flexbox-safe-overflow-position-001-ref.html">
+  <meta charset="utf-8">
+  <style>
+    .flex {
+      display: flex;
+      width: 85px;
+      height: 65px;
+      border: 1px solid black;
+      align-content: safe flex-end;
+      justify-content: safe center;
+      align-items: safe center;
+      float: left;
+      clear: both;
+      margin-top: 100px;
+    }
+    .rowNoWrap {
+      flex-flow: row nowrap;
+    }
+    .columnNoWrap {
+      flex-flow: column wrap;
+    }
+    .item {
+      border: 1px solid blue;
+      background: lightblue;
+      width: 28px;
+      height: 28px;
+      flex-shrink: 0;
+    }
+    .bigItem {
+      border: 1px solid blue;
+      background: lightblue;
+      width: 28px;
+      height: 90px;
+      flex-shrink: 0;
+    }
+  </style>
+</head>
+<body>
+  <div class="flex rowNoWrap">
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="bigItem"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+    <div class="item"></div>
+  </div>
+  <div class="flex columnNoWrap">
+    <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
@@ -209,16 +209,19 @@ fails == flexbox-min-height-auto-002b.ht
 == flexbox-paint-ordering-001.xhtml flexbox-paint-ordering-001-ref.xhtml
 == flexbox-paint-ordering-002.xhtml flexbox-paint-ordering-002-ref.xhtml
 == flexbox-paint-ordering-003.html  flexbox-paint-ordering-003-ref.html
 
 # Tests for "display:flex" on root node
 == flexbox-root-node-001a.html flexbox-root-node-001-ref.html
 == flexbox-root-node-001b.html flexbox-root-node-001-ref.html
 
+# Tests for <overflow-position> "safe" keyword in CSS Alignment properties
+== flexbox-safe-overflow-position-001.html flexbox-safe-overflow-position-001-ref.html
+
 # Tests for sizing of flex containers, e.g. under min/max size constraints
 == flexbox-sizing-horiz-001.xhtml flexbox-sizing-horiz-001-ref.xhtml
 == flexbox-sizing-horiz-002.xhtml flexbox-sizing-horiz-002-ref.xhtml
 == flexbox-sizing-vert-001.xhtml  flexbox-sizing-vert-001-ref.xhtml
 == flexbox-sizing-vert-002.xhtml  flexbox-sizing-vert-002-ref.xhtml
 
 # Tests for table-fixup *not happening* on direct children of a flex container
 == flexbox-table-fixup-001.xhtml flexbox-table-fixup-001-ref.xhtml