Bug 1384542: Get rid of GetParentAllowServo in implementation of CSS 'justify-items' property. r?dholbert,heycam draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Wed, 26 Jul 2017 15:26:19 +0200
changeset 617517 1e11e0a69d49c50ef62c638478a2e9b3d2fa4740
parent 617516 f36aadde3231eb44ca19f82176a50c2d081987f4
child 617518 ba000fc4c55c38bb1860e924b6f396c4626047d2
push id71069
push userbmo:emilio+bugs@crisal.io
push dateFri, 28 Jul 2017 15:20:24 +0000
reviewersdholbert, heycam
bugs1384542
milestone56.0a1
Bug 1384542: Get rid of GetParentAllowServo in implementation of CSS 'justify-items' property. r?dholbert,heycam MozReview-Commit-ID: 4qydjqSoVXs
layout/generic/nsBlockFrame.cpp
layout/style/GeckoStyleContext.cpp
layout/style/nsComputedDOMStyle.cpp
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhand/position.mako.rs
servo/components/style/properties/properties.mako.rs
servo/components/style/style_adjuster.rs
servo/components/style/values/computed/align.rs
servo/components/style/values/computed/mod.rs
servo/components/style/values/specified/align.rs
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -1526,23 +1526,21 @@ nsBlockFrame::Reflow(nsPresContext*     
               (value == NS_STYLE_ALIGN_AUTO));
     };
 
     // First check this frame for non-default values of the css-align properties
     // that apply to block containers.
     // Note: we check here for non-default "justify-items", though technically
     // that'd only affect rendering if some child has "justify-self:auto".
     // (It's safe to assume that's likely, since it's the default value that
-    // a child would have.) We also pass in nullptr for the parent style context
-    // because an accurate parameter is slower and only necessary to detect a
-    // narrow edge case with the "legacy" keyword.
+    // a child would have.)
     const nsStylePosition* stylePosition = reflowInput->mStylePosition;
     if (!IsStyleNormalOrAuto(stylePosition->mJustifyContent) ||
         !IsStyleNormalOrAuto(stylePosition->mAlignContent) ||
-        !IsStyleNormalOrAuto(stylePosition->ComputedJustifyItems(nullptr))) {
+        !IsStyleNormalOrAuto(stylePosition->mJustifyItems)) {
       Telemetry::Accumulate(Telemetry::BOX_ALIGN_PROPS_IN_BLOCKS_FLAG, true);
     } else {
       // If not already flagged by the parent, now check justify-self of the
       // block-level child frames.
       for (nsBlockFrame::LineIterator line = LinesBegin();
            line != LinesEnd(); ++line) {
         if (line->IsBlock() &&
             !IsStyleNormalOrAuto(line->mFirstChild->StylePosition()->mJustifySelf)) {
--- a/layout/style/GeckoStyleContext.cpp
+++ b/layout/style/GeckoStyleContext.cpp
@@ -392,16 +392,17 @@ GeckoStyleContext::GetUniqueStyleData(co
 #define UNIQUE_CASE(c_)                                                       \
   case eStyleStruct_##c_:                                                     \
     result = new (presContext) nsStyle##c_(                                   \
       * static_cast<const nsStyle##c_ *>(current));                           \
     break;
 
   UNIQUE_CASE(Font)
   UNIQUE_CASE(Display)
+  UNIQUE_CASE(Position)
   UNIQUE_CASE(Text)
   UNIQUE_CASE(TextReset)
   UNIQUE_CASE(Visibility)
 
 #undef UNIQUE_CASE
 
   default:
     NS_ERROR("Struct type not supported.  Please find another way to do this if you can!");
@@ -860,16 +861,33 @@ GeckoStyleContext::ApplyStyleFixups(bool
         text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
         text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)
     {
       nsStyleText* uniqueText = GET_UNIQUE_STYLE_DATA(Text);
       uniqueText->mTextAlign = NS_STYLE_TEXT_ALIGN_START;
     }
   }
 
+  // Fixup the "justify-items: auto" value based on our parent style here if
+  // needed.
+  //
+  // Note that this only pulls a unique struct in the case the parent has the
+  // "legacy" modifier (which is not the default), and the computed value would
+  // change as a result.
+  //
+  // We check the parent first just to avoid unconditionally pulling the
+  // nsStylePosition struct on every style context.
+  if (mParent &&
+      mParent->StylePosition()->mJustifyItems & NS_STYLE_JUSTIFY_LEGACY &&
+      StylePosition()->mSpecifiedJustifyItems == NS_STYLE_JUSTIFY_AUTO &&
+      StylePosition()->mJustifyItems != mParent->StylePosition()->mJustifyItems) {
+    nsStylePosition* uniquePosition = GET_UNIQUE_STYLE_DATA(Position);
+    uniquePosition->mJustifyItems = mParent->StylePosition()->mJustifyItems;
+  }
+
   // CSS2.1 section 9.2.4 specifies fixups for the 'display' property of
   // the root element.  We can't implement them in nsRuleNode because we
   // don't want to store all display structs that aren't 'block',
   // 'inline', or 'table' in the style context tree on the off chance
   // that the root element has its style reresolved later.  So do them
   // here if needed, by changing the style data, so that other code
   // doesn't get confused by looking at the style data.
   if (!mParent &&
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -4689,18 +4689,17 @@ nsComputedDOMStyle::DoGetJustifyContent(
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetJustifyItems()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   nsAutoString str;
-  auto justify =
-    StylePosition()->ComputedJustifyItems(mStyleContext->GetParentAllowServo());
+  auto justify = StylePosition()->mJustifyItems;
   nsCSSValue::AppendAlignJustifyValueToString(justify, str);
   val->SetString(str);
   return val.forget();
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetJustifySelf()
 {
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -8654,31 +8654,38 @@ nsRuleNode::ComputePositionData(void* aS
            pos->mJustifyContent, conditions,
            SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL,
            parentPos->mJustifyContent,
            NS_STYLE_JUSTIFY_NORMAL);
 
   // justify-items: enum, inherit, initial
   const auto& justifyItemsValue = *aRuleData->ValueForJustifyItems();
   if (MOZ_UNLIKELY(justifyItemsValue.GetUnit() == eCSSUnit_Inherit)) {
-    if (MOZ_LIKELY(parentContext)) {
-      pos->mJustifyItems =
-        parentPos->ComputedJustifyItems(parentContext->GetParent());
-    } else {
-      pos->mJustifyItems = NS_STYLE_JUSTIFY_NORMAL;
-    }
+    pos->mSpecifiedJustifyItems =
+      MOZ_LIKELY(parentContext)
+        ? parentPos->mJustifyItems
+        : NS_STYLE_JUSTIFY_NORMAL;
     conditions.SetUncacheable();
   } else {
     SetValue(justifyItemsValue,
-             pos->mJustifyItems, conditions,
+             pos->mSpecifiedJustifyItems, conditions,
              SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL,
-             parentPos->mJustifyItems, // unused, we handle 'inherit' above
+             parentPos->mSpecifiedJustifyItems, // unused, we handle 'inherit' above
              NS_STYLE_JUSTIFY_AUTO);
   }
 
+  // NOTE(emilio): Even though "auto" technically depends on the parent style
+  // context, most of the time it'll resolve to "normal".  So, we
+  // optimistically assume here that it does resolve to "normal", and we
+  // handle the other cases in ApplyStyleFixups. This way, position structs
+  // can be cached in the default/common case.
+  pos->mJustifyItems =
+    pos->mSpecifiedJustifyItems == NS_STYLE_JUSTIFY_AUTO
+      ? NS_STYLE_JUSTIFY_NORMAL : pos->mSpecifiedJustifyItems;
+
   // justify-self: enum, inherit, initial
   SetValue(*aRuleData->ValueForJustifySelf(),
            pos->mJustifySelf, conditions,
            SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL,
            parentPos->mJustifySelf,
            NS_STYLE_JUSTIFY_AUTO);
 
   // flex-basis: auto, length, percent, enum, calc, inherit, initial
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1439,17 +1439,18 @@ nsStylePosition::nsStylePosition(const n
   , mGridAutoRowsMin(eStyleUnit_Auto)
   , mGridAutoRowsMax(eStyleUnit_Auto)
   , mGridAutoFlow(NS_STYLE_GRID_AUTO_FLOW_ROW)
   , mBoxSizing(StyleBoxSizing::Content)
   , mAlignContent(NS_STYLE_ALIGN_NORMAL)
   , mAlignItems(NS_STYLE_ALIGN_NORMAL)
   , mAlignSelf(NS_STYLE_ALIGN_AUTO)
   , mJustifyContent(NS_STYLE_JUSTIFY_NORMAL)
-  , mJustifyItems(NS_STYLE_JUSTIFY_AUTO)
+  , mSpecifiedJustifyItems(NS_STYLE_JUSTIFY_AUTO)
+  , mJustifyItems(NS_STYLE_JUSTIFY_NORMAL)
   , mJustifySelf(NS_STYLE_JUSTIFY_AUTO)
   , mFlexDirection(NS_STYLE_FLEX_DIRECTION_ROW)
   , mFlexWrap(NS_STYLE_FLEX_WRAP_NOWRAP)
   , mObjectFit(NS_STYLE_OBJECT_FIT_FILL)
   , mOrder(NS_STYLE_ORDER_INITIAL)
   , mFlexGrow(0.0f)
   , mFlexShrink(1.0f)
   , mZIndex(eStyleUnit_Auto)
@@ -1497,16 +1498,17 @@ nsStylePosition::nsStylePosition(const n
   , mGridAutoRowsMin(aSource.mGridAutoRowsMin)
   , mGridAutoRowsMax(aSource.mGridAutoRowsMax)
   , mGridAutoFlow(aSource.mGridAutoFlow)
   , mBoxSizing(aSource.mBoxSizing)
   , mAlignContent(aSource.mAlignContent)
   , mAlignItems(aSource.mAlignItems)
   , mAlignSelf(aSource.mAlignSelf)
   , mJustifyContent(aSource.mJustifyContent)
+  , mSpecifiedJustifyItems(aSource.mSpecifiedJustifyItems)
   , mJustifyItems(aSource.mJustifyItems)
   , mJustifySelf(aSource.mJustifySelf)
   , mFlexDirection(aSource.mFlexDirection)
   , mFlexWrap(aSource.mFlexWrap)
   , mObjectFit(aSource.mObjectFit)
   , mOrder(aSource.mOrder)
   , mFlexGrow(aSource.mFlexGrow)
   , mFlexShrink(aSource.mFlexShrink)
@@ -1628,16 +1630,22 @@ nsStylePosition::CalcDifference(const ns
   // Changing 'justify-content/items/self' might affect the positioning,
   // but it won't affect any sizing.
   if (mJustifyContent != aNewData.mJustifyContent ||
       mJustifyItems != aNewData.mJustifyItems ||
       mJustifySelf != aNewData.mJustifySelf) {
     hint |= nsChangeHint_NeedReflow;
   }
 
+  // No need to do anything if mSpecifiedJustifyItems changes, as long as
+  // mJustifyItems (tested above) is unchanged.
+  if (mSpecifiedJustifyItems != aNewData.mSpecifiedJustifyItems) {
+    hint |= nsChangeHint_NeutralChange;
+  }
+
   // 'align-content' doesn't apply to a single-line flexbox but we don't know
   // if we're a flex container at this point so we can't optimize for that.
   if (mAlignContent != aNewData.mAlignContent) {
     hint |= nsChangeHint_NeedReflow;
   }
 
   bool widthChanged = mWidth != aNewData.mWidth ||
                       mMinWidth != aNewData.mMinWidth ||
@@ -1710,42 +1718,23 @@ nsStylePosition::UsedAlignSelf(nsStyleCo
     MOZ_ASSERT(!(parentAlignItems & NS_STYLE_ALIGN_LEGACY),
                "align-items can't have 'legacy'");
     return parentAlignItems;
   }
   return NS_STYLE_ALIGN_NORMAL;
 }
 
 uint8_t
-nsStylePosition::ComputedJustifyItems(nsStyleContext* aParent) const
-{
-  if (mJustifyItems != NS_STYLE_JUSTIFY_AUTO) {
-    return mJustifyItems;
-  }
-  if (MOZ_LIKELY(aParent)) {
-    auto inheritedJustifyItems = aParent->StylePosition()->ComputedJustifyItems(
-      aParent->GetParentAllowServo());
-    // "If the inherited value of justify-items includes the 'legacy' keyword,
-    // 'auto' computes to the inherited value."  Otherwise, 'normal'.
-    if (inheritedJustifyItems & NS_STYLE_JUSTIFY_LEGACY) {
-      return inheritedJustifyItems;
-    }
-  }
-  return NS_STYLE_JUSTIFY_NORMAL;
-}
-
-uint8_t
 nsStylePosition::UsedJustifySelf(nsStyleContext* aParent) const
 {
   if (mJustifySelf != NS_STYLE_JUSTIFY_AUTO) {
     return mJustifySelf;
   }
   if (MOZ_LIKELY(aParent)) {
-    auto inheritedJustifyItems = aParent->StylePosition()->ComputedJustifyItems(
-      aParent->GetParentAllowServo());
+    auto inheritedJustifyItems = aParent->StylePosition()->mJustifyItems;
     return inheritedJustifyItems & ~NS_STYLE_JUSTIFY_LEGACY;
   }
   return NS_STYLE_JUSTIFY_NORMAL;
 }
 
 // --------------------
 // nsStyleTable
 //
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1674,22 +1674,16 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
 
   /**
    * Return the used value for 'align-self' given our parent StyleContext
    * aParent (or null for the root).
    */
   uint8_t UsedAlignSelf(nsStyleContext* aParent) const;
 
   /**
-   * Return the computed value for 'justify-items' given our parent StyleContext
-   * aParent (or null for the root).
-   */
-  uint8_t ComputedJustifyItems(nsStyleContext* aParent) const;
-
-  /**
    * Return the used value for 'justify-self' given our parent StyleContext
    * aParent (or null for the root).
    */
   uint8_t UsedJustifySelf(nsStyleContext* aParent) const;
 
   mozilla::Position mObjectPosition;    // [reset]
   nsStyleSides  mOffset;                // [reset] coord, percent, calc, auto
   nsStyleCoord  mWidth;                 // [reset] coord, percent, enum, calc, auto
@@ -1705,24 +1699,27 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   nsStyleCoord  mGridAutoRowsMax;       // [reset] coord, percent, enum, calc, flex
   uint8_t       mGridAutoFlow;          // [reset] enumerated. See nsStyleConsts.h
   mozilla::StyleBoxSizing mBoxSizing;   // [reset] see nsStyleConsts.h
 
   uint16_t      mAlignContent;          // [reset] fallback value in the high byte
   uint8_t       mAlignItems;            // [reset] see nsStyleConsts.h
   uint8_t       mAlignSelf;             // [reset] see nsStyleConsts.h
   uint16_t      mJustifyContent;        // [reset] fallback value in the high byte
-private:
-  friend class nsRuleNode;
-
-  // mJustifyItems should only be read via ComputedJustifyItems(), which
-  // lazily resolves its "auto" value. nsRuleNode needs direct access so
-  // it can set mJustifyItems' value when populating this struct.
+  // We cascade mSpecifiedJustifyItems, to handle the auto value, but store the
+  // computed value in mJustifyItems.
+  //
+  // They're effectively only different in this regard: mJustifyItems is set to
+  // mSpecifiedJustifyItems, except when the latter is AUTO -- in that case,
+  // mJustifyItems is set to NORMAL, or to the parent style context's
+  // mJustifyItems if it has the legacy flag.
+  //
+  // This last part happens in nsStyleContext::ApplyStyleFixups.
+  uint8_t       mSpecifiedJustifyItems; // [reset] see nsStyleConsts.h
   uint8_t       mJustifyItems;          // [reset] see nsStyleConsts.h
-public:
   uint8_t       mJustifySelf;           // [reset] see nsStyleConsts.h
   uint8_t       mFlexDirection;         // [reset] see nsStyleConsts.h
   uint8_t       mFlexWrap;              // [reset] see nsStyleConsts.h
   uint8_t       mObjectFit;             // [reset] see nsStyleConsts.h
   int32_t       mOrder;                 // [reset] integer
   float         mFlexGrow;              // [reset] float
   float         mFlexShrink;            // [reset] float
   nsStyleCoord  mZIndex;                // [reset] integer, auto
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -55,17 +55,17 @@ use properties::animated_properties::Tra
 use properties::computed_value_flags::ComputedValueFlags;
 use properties::{longhands, FontComputationData, Importance, LonghandId};
 use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId};
 use rule_tree::StrongRuleNode;
 use selector_parser::PseudoElement;
 use servo_arc::{Arc, RawOffsetArc};
 use std::mem::{forget, uninitialized, transmute, zeroed};
 use std::{cmp, ops, ptr};
-use values::{Auto, CustomIdent, Either, KeyframesName};
+use values::{self, Auto, CustomIdent, Either, KeyframesName};
 use values::computed::ToComputedValue;
 use values::computed::effects::{BoxShadow, Filter, SimpleShadow};
 use values::computed::length::Percentage;
 use computed_values::border_style;
 
 pub mod style_structs {
     % for style_struct in data.style_structs:
     pub use super::${style_struct.gecko_struct_name} as ${style_struct.name};
@@ -1349,18 +1349,45 @@ fn static_assert() {
                 Either::First(0)
             }
         }
     }
 
     % for kind in ["align", "justify"]:
     ${impl_simple_type_with_conversion(kind + "_content")}
     ${impl_simple_type_with_conversion(kind + "_self")}
-    ${impl_simple_type_with_conversion(kind + "_items")}
     % endfor
+    ${impl_simple_type_with_conversion("align_items")}
+
+    pub fn set_justify_items(&mut self, v: longhands::justify_items::computed_value::T) {
+        self.gecko.mSpecifiedJustifyItems = v.specified.into();
+        self.set_computed_justify_items(v.computed);
+    }
+
+    pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) {
+        debug_assert!(v.0 != ::values::specified::align::ALIGN_AUTO);
+        self.gecko.mJustifyItems = v.into();
+    }
+
+    pub fn reset_justify_items(&mut self, reset_style: &Self) {
+        self.gecko.mJustifyItems = reset_style.gecko.mJustifyItems;
+        self.gecko.mSpecifiedJustifyItems = reset_style.gecko.mSpecifiedJustifyItems;
+    }
+
+    pub fn copy_justify_items_from(&mut self, other: &Self) {
+        self.gecko.mJustifyItems = other.gecko.mJustifyItems;
+        self.gecko.mSpecifiedJustifyItems = other.gecko.mJustifyItems;
+    }
+
+    pub fn clone_justify_items(&self) -> longhands::justify_items::computed_value::T {
+        longhands::justify_items::computed_value::T {
+            computed: self.gecko.mJustifyItems.into(),
+            specified: self.gecko.mSpecifiedJustifyItems.into(),
+        }
+    }
 
     pub fn set_order(&mut self, v: longhands::order::computed_value::T) {
         self.gecko.mOrder = v;
     }
 
     pub fn clone_order(&self) -> longhands::order::computed_value::T {
         self.gecko.mOrder
     }
--- a/servo/components/style/properties/longhand/position.mako.rs
+++ b/servo/components/style/properties/longhand/position.mako.rs
@@ -103,17 +103,17 @@ macro_rules! impl_align_conversions {
                               extra_prefixes="webkit",
                               animation_value_type="discrete")}
 
     #[cfg(feature = "gecko")]
     impl_align_conversions!(::values::specified::align::AlignItems);
 
     ${helpers.predefined_type(name="justify-items",
                               type="JustifyItems",
-                              initial_value="specified::JustifyItems::auto()",
+                              initial_value="computed::JustifyItems::auto()",
                               spec="https://drafts.csswg.org/css-align/#propdef-justify-items",
                               animation_value_type="discrete")}
 
     #[cfg(feature = "gecko")]
     impl_align_conversions!(::values::specified::align::JustifyItems);
 % endif
 
 // Flex item properties
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -2600,43 +2600,50 @@ impl<'a> StyleBuilder<'a> {
         }
     }
 
     % for property in data.longhands:
     % if property.ident != "font_size":
     /// Inherit `${property.ident}` from our parent style.
     #[allow(non_snake_case)]
     pub fn inherit_${property.ident}(&mut self) {
+        let inherited_struct =
         % if property.style_struct.inherited:
-        let inherited_struct =
             self.inherited_style.get_${property.style_struct.name_lower}();
         % else:
-        let inherited_struct =
             self.inherited_style_ignoring_first_line.get_${property.style_struct.name_lower}();
         % endif
+
         self.${property.style_struct.ident}.mutate()
             .copy_${property.ident}_from(
                 inherited_struct,
                 % if property.logical:
                 self.writing_mode,
                 % endif
             );
     }
 
     /// Reset `${property.ident}` to the initial value.
     #[allow(non_snake_case)]
     pub fn reset_${property.ident}(&mut self) {
-        let reset_struct = self.reset_style.get_${property.style_struct.name_lower}();
-        self.${property.style_struct.ident}.mutate()
-            .copy_${property.ident}_from(
-                reset_struct,
-                % if property.logical:
-                self.writing_mode,
-                % endif
-            );
+        let reset_struct =
+            self.reset_style.get_${property.style_struct.name_lower}();
+        % if property.ident == "justify_items":
+            // TODO(emilio): Generalise this!
+            self.${property.style_struct.ident}.mutate()
+                .reset_${property.ident}(reset_struct)
+        % else:
+            self.${property.style_struct.ident}.mutate()
+                .copy_${property.ident}_from(
+                    reset_struct,
+                    % if property.logical:
+                    self.writing_mode,
+                    % endif
+                );
+        % endif
     }
 
     % if not property.is_vector:
     /// Set the `${property.ident}` to the computed value `value`.
     #[allow(non_snake_case)]
     pub fn set_${property.ident}(
         &mut self,
         value: longhands::${property.ident}::computed_value::T
--- a/servo/components/style/style_adjuster.rs
+++ b/servo/components/style/style_adjuster.rs
@@ -449,16 +449,46 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
             self.style.inherited_flags().contains(IS_RELEVANT_LINK_VISITED)
         };
 
         if relevant_link_visited {
             self.style.flags.insert(IS_RELEVANT_LINK_VISITED);
         }
     }
 
+    /// Resolves "justify-items: auto" based on the inherited style if needed to
+    /// comply with:
+    ///
+    /// https://drafts.csswg.org/css-align/#valdef-justify-items-legacy
+    ///
+    /// (Note that "auto" is being renamed to "legacy")
+    #[cfg(feature = "gecko")]
+    fn adjust_for_justify_items(&mut self) {
+        use values::specified::align;
+        let justify_items = self.style.get_position().clone_justify_items();
+        if justify_items.specified.0 != align::ALIGN_AUTO {
+            return;
+        }
+
+        let parent_justify_items =
+            self.style.get_parent_position().clone_justify_items();
+
+        if !parent_justify_items.computed.0.contains(align::ALIGN_LEGACY) {
+            return;
+        }
+
+        if parent_justify_items.computed == justify_items.computed {
+            return;
+        }
+
+        self.style
+            .mutate_position()
+            .set_computed_justify_items(parent_justify_items.computed);
+    }
+
     /// Adjusts the style to account for various fixups that don't fit naturally
     /// into the cascade.
     ///
     /// When comparing to Gecko, this is similar to the work done by
     /// `nsStyleContext::ApplyStyleFixups`, plus some parts of
     /// `nsStyleSet::GetContext`.
     pub fn adjust(&mut self,
                   layout_parent_style: &ComputedValues,
@@ -478,16 +508,17 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
             self.adjust_for_table_text_align();
             self.adjust_for_contain();
             self.adjust_for_mathvariant();
         }
         #[cfg(feature = "servo")]
         {
             self.adjust_for_alignment(layout_parent_style);
         }
+        self.adjust_for_justify_items();
         self.adjust_for_border_width();
         self.adjust_for_outline();
         self.adjust_for_writing_mode(layout_parent_style);
         self.adjust_for_text_decoration_lines(layout_parent_style);
         #[cfg(feature = "gecko")]
         {
             self.adjust_for_ruby(layout_parent_style, flags);
         }
new file mode 100644
--- /dev/null
+++ b/servo/components/style/values/computed/align.rs
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Values for CSS Box Alignment properties
+//!
+//! https://drafts.csswg.org/css-align/
+
+
+use values::computed::{ComputedValueAsSpecified, Context, ToComputedValue};
+use values::specified;
+use std::fmt;
+use style_traits::ToCss;
+
+pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf};
+
+/// The computed value for the `justify-items` property.
+///
+/// Need to carry around both the specified and computed value to handle the
+/// special legacy keyword. Sigh.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct JustifyItems {
+    /// The specified value for the property. Can contain `auto`.
+    pub specified: specified::JustifyItems,
+    /// The computed value for the property. Cannot contain `auto`.
+    pub computed: specified::JustifyItems,
+}
+
+impl ToCss for JustifyItems {
+    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+        where W: fmt::Write,
+    {
+        self.computed.to_css(dest)
+    }
+}
+
+impl JustifyItems {
+    /// Returns the `auto` value.
+    pub fn auto() -> Self {
+        Self {
+            specified: specified::JustifyItems::auto(),
+            computed: specified::JustifyItems::normal(),
+        }
+    }
+}
+
+impl ToComputedValue for specified::JustifyItems {
+    type ComputedValue = JustifyItems;
+
+    /// https://drafts.csswg.org/css-align/#valdef-justify-items-legacy
+    fn to_computed_value(&self, _context: &Context) -> JustifyItems {
+        use values::specified::align;
+        let specified = *self;
+        let computed =
+            if self.0 != align::ALIGN_AUTO {
+                *self
+            } else {
+                // If the inherited value of `justify-items` includes the
+                // `legacy` keyword, `auto` computes to the inherited value,
+                // but we assume it computes to `normal`, and handle that
+                // special-case in StyleAdjuster.
+                Self::normal()
+            };
+
+        JustifyItems { specified, computed }
+    }
+
+    #[inline]
+    fn from_computed_value(computed: &JustifyItems) -> Self {
+        computed.specified
+    }
+}
+
+impl ComputedValueAsSpecified for AlignItems {}
+impl ComputedValueAsSpecified for AlignJustifyContent {}
+impl ComputedValueAsSpecified for AlignJustifySelf {}
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -20,38 +20,40 @@ use style_traits::ToCss;
 use super::{CSSFloat, CSSInteger, RGBA};
 use super::generics::grid::{TrackBreadth as GenericTrackBreadth, TrackSize as GenericTrackSize};
 use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
 use super::generics::grid::TrackList as GenericTrackList;
 use super::specified;
 
 pub use app_units::Au;
 pub use properties::animated_properties::TransitionProperty;
+#[cfg(feature = "gecko")]
+pub use self::align::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use self::background::BackgroundSize;
 pub use self::border::{BorderImageSlice, BorderImageWidth, BorderImageSideWidth};
 pub use self::border::{BorderRadius, BorderCornerRadius};
 pub use self::color::{Color, RGBAColor};
 pub use self::effects::{BoxShadow, Filter, SimpleShadow};
 pub use self::flex::FlexBasis;
 pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect};
 #[cfg(feature = "gecko")]
 pub use self::gecko::ScrollSnapPoint;
 pub use self::rect::LengthOrNumberRect;
 pub use super::{Auto, Either, None_};
-#[cfg(feature = "gecko")]
-pub use super::specified::{AlignItems, AlignJustifyContent, AlignJustifySelf, JustifyItems};
 pub use super::specified::{BorderStyle, UrlOrNone};
 pub use super::generics::grid::GridLine;
 pub use super::specified::url::SpecifiedUrl;
 pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNone, LengthOrNumber, LengthOrPercentage};
 pub use self::length::{LengthOrPercentageOrAuto, LengthOrPercentageOrNone, MaxLength, MozLength, Percentage};
 pub use self::position::Position;
 pub use self::text::{InitialLetter, LetterSpacing, LineHeight, WordSpacing};
 pub use self::transform::{TimingFunction, TransformOrigin};
 
+#[cfg(feature = "gecko")]
+pub mod align;
 pub mod background;
 pub mod basic_shape;
 pub mod border;
 pub mod color;
 pub mod effects;
 pub mod flex;
 pub mod image;
 #[cfg(feature = "gecko")]
@@ -391,46 +393,16 @@ impl Time {
 impl ToCss for Time {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result
         where W: fmt::Write,
     {
         write!(dest, "{}s", self.seconds())
     }
 }
 
-#[cfg(feature = "gecko")]
-impl ToComputedValue for specified::JustifyItems {
-    type ComputedValue = JustifyItems;
-
-    // https://drafts.csswg.org/css-align/#valdef-justify-items-auto
-    fn to_computed_value(&self, context: &Context) -> JustifyItems {
-        use values::specified::align;
-        // If the inherited value of `justify-items` includes the `legacy` keyword, `auto` computes
-        // to the inherited value.
-        if self.0 == align::ALIGN_AUTO {
-            let inherited = context.builder.get_parent_position().clone_justify_items();
-            if inherited.0.contains(align::ALIGN_LEGACY) {
-                return inherited
-            }
-        }
-        return *self
-    }
-
-    #[inline]
-    fn from_computed_value(computed: &JustifyItems) -> Self {
-        *computed
-    }
-}
-
-#[cfg(feature = "gecko")]
-impl ComputedValueAsSpecified for specified::AlignItems {}
-#[cfg(feature = "gecko")]
-impl ComputedValueAsSpecified for specified::AlignJustifyContent {}
-#[cfg(feature = "gecko")]
-impl ComputedValueAsSpecified for specified::AlignJustifySelf {}
 impl ComputedValueAsSpecified for specified::BorderStyle {}
 
 /// A `<number>` value.
 pub type Number = CSSFloat;
 
 #[allow(missing_docs)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 #[derive(Clone, Copy, Debug, PartialEq, ToCss)]
--- a/servo/components/style/values/specified/align.rs
+++ b/servo/components/style/values/specified/align.rs
@@ -287,16 +287,22 @@ pub struct JustifyItems(pub AlignFlags);
 
 impl JustifyItems {
     /// The initial value 'auto'
     #[inline]
     pub fn auto() -> Self {
         JustifyItems(ALIGN_AUTO)
     }
 
+    /// The value 'normal'
+    #[inline]
+    pub fn normal() -> Self {
+        JustifyItems(ALIGN_NORMAL)
+    }
+
     /// Whether this value has extra flags.
     #[inline]
     pub fn has_extra_flags(self) -> bool {
         self.0.intersects(ALIGN_FLAG_BITS)
     }
 }
 
 no_viewport_percentage!(JustifyItems);