Bug 1550554 - Use OwnedSlice for will-change. r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 16 May 2019 23:23:28 +0000
changeset 533075 d0e6717d1cf00e7e33b6eea7021b60d75cc670d0
parent 533074 9efd6e065be931906e5ef21ba14c01b8ef392cf9
child 533076 29c5fc63a9bf79e53cc8991c47ed47fedafda4e1
push id11276
push userrgurzau@mozilla.com
push dateMon, 20 May 2019 13:11:24 +0000
treeherdermozilla-beta@847755a7c325 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1550554
milestone68.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 1550554 - Use OwnedSlice for will-change. r=heycam We could use ArcSlice if wanted I guess, your call. Though will change is not supposed to be used very frequently. Differential Revision: https://phabricator.services.mozilla.com/D30548
layout/base/nsLayoutDebugger.cpp
layout/generic/nsFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/painting/ActiveLayerTracker.cpp
layout/style/GeckoBindings.cpp
layout/style/GeckoBindings.h
layout/style/ServoBindings.toml
layout/style/ServoStyleConstsInlines.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/nsStyleStructInlines.h
servo/components/style/properties/gecko.mako.rs
servo/components/style/values/specified/box.rs
servo/ports/geckolib/cbindgen.toml
--- a/layout/base/nsLayoutDebugger.cpp
+++ b/layout/base/nsLayoutDebugger.cpp
@@ -164,23 +164,23 @@ static void PrintDisplayItemTo(nsDisplay
 
   for (auto iter = opaque.RectIter(); !iter.Done(); iter.Next()) {
     const nsRect& r = iter.Get();
     aStream << nsPrintfCString(" (opaque %d,%d,%d,%d)", r.x, r.y, r.width,
                                r.height);
   }
 
   const auto& willChange = aItem->Frame()->StyleDisplay()->mWillChange;
-  if (!willChange.IsEmpty()) {
+  if (!willChange.features.IsEmpty()) {
     aStream << " (will-change=";
-    for (size_t i = 0; i < willChange.Length(); i++) {
+    for (size_t i = 0; i < willChange.features.Length(); i++) {
       if (i > 0) {
         aStream << ",";
       }
-      nsDependentAtomString buffer(willChange[i]);
+      nsDependentAtomString buffer(willChange.features.AsSpan()[i].AsAtom());
       aStream << NS_LossyConvertUTF16toASCII(buffer).get();
     }
     aStream << ")";
   }
 
   if (aItem->HasHitTestInfo()) {
     auto* hitTestInfoItem = static_cast<nsDisplayHitTestInfoItem*>(aItem);
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1635,17 +1635,17 @@ bool nsIFrame::ChildrenHavePerspective(
 }
 
 bool nsIFrame::HasOpacityInternal(float aThreshold,
                                   const nsStyleDisplay* aStyleDisplay,
                                   const nsStyleEffects* aStyleEffects,
                                   EffectSet* aEffectSet) const {
   MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument");
   if (aStyleEffects->mOpacity < aThreshold ||
-      (aStyleDisplay->mWillChangeBitField & StyleWillChangeBits_OPACITY)) {
+      (aStyleDisplay->mWillChange.bits & StyleWillChangeBits_OPACITY)) {
     return true;
   }
 
   if (!mMayHaveOpacityAnimation) {
     return false;
   }
 
   return ((nsLayoutUtils::IsPrimaryStyleFrame(this) ||
@@ -2865,27 +2865,27 @@ void nsIFrame::BuildDisplayListForStacki
   // we're painting, and we're not animating opacity. Don't do this
   // if we're going to compute plugin geometry, since opacity-0 plugins
   // need to have display items built for them.
   bool needHitTestInfo = aBuilder->BuildCompositorHitTestInfo() &&
                          StyleUI()->GetEffectivePointerEvents(this) !=
                              NS_STYLE_POINTER_EVENTS_NONE;
   bool opacityItemForEventsAndPluginsOnly = false;
   if (effects->mOpacity == 0.0 && aBuilder->IsForPainting() &&
-      !(disp->mWillChangeBitField & StyleWillChangeBits_OPACITY) &&
+      !(disp->mWillChange.bits & StyleWillChangeBits_OPACITY) &&
       !nsLayoutUtils::HasAnimationOfPropertySet(
           this, nsCSSPropertyIDSet::OpacityProperties(), effectSetForOpacity)) {
     if (needHitTestInfo || aBuilder->WillComputePluginGeometry()) {
       opacityItemForEventsAndPluginsOnly = true;
     } else {
       return;
     }
   }
 
-  if (disp->mWillChangeBitField) {
+  if (disp->mWillChange.bits) {
     aBuilder->AddToWillChangeBudget(this, GetSize());
   }
 
   // For preserves3d, use the dirty rect already installed on the
   // builder, since aDirtyRect maybe distorted for transforms along
   // the chain.
   nsRect visibleRect = aBuilder->GetVisibleRect();
   nsRect dirtyRect = aBuilder->GetDirtyRect();
@@ -10599,17 +10599,17 @@ bool nsIFrame::IsStackingContext(const n
            aStyleDisplay->IsContainLayout())) ||
          // strictly speaking, 'perspective' doesn't require visual atomicity,
          // but the spec says it acts like the rest of these
          ChildrenHavePerspective(aStyleDisplay) ||
          aStyleEffects->mMixBlendMode != NS_STYLE_BLEND_NORMAL ||
          nsSVGIntegrationUtils::UsingEffectsForFrame(this) ||
          (aIsPositioned && (aStyleDisplay->IsPositionForcingStackingContext() ||
                             aStylePosition->mZIndex.IsInteger())) ||
-         (aStyleDisplay->mWillChangeBitField &
+         (aStyleDisplay->mWillChange.bits &
           StyleWillChangeBits_STACKING_CONTEXT) ||
          aStyleDisplay->mIsolation != NS_STYLE_ISOLATION_AUTO;
 }
 
 bool nsIFrame::IsStackingContext() {
   const nsStyleDisplay* disp = StyleDisplay();
   const bool isPositioned = disp->IsAbsPosContainingBlock(this);
   return IsStackingContext(disp, StylePosition(), StyleEffects(), isPositioned);
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -3450,17 +3450,17 @@ void ScrollFrameHelper::BuildDisplayList
   // borders and underneath borders and backgrounds of later elements
   // in the tree.
   // Note that this does not apply for overlay scrollbars; those are drawn
   // in the positioned-elements layer on top of everything else by the call
   // to AppendScrollPartsTo(..., true) further down.
   AppendScrollPartsTo(aBuilder, aLists, createLayersForScrollbars, false);
 
   const nsStyleDisplay* disp = mOuter->StyleDisplay();
-  if (disp->mWillChangeBitField & StyleWillChangeBits_SCROLL) {
+  if (disp->mWillChange.bits & StyleWillChangeBits_SCROLL) {
     aBuilder->AddToWillChangeBudget(mOuter, GetVisualViewportSize());
   }
 
   mScrollParentID = aBuilder->GetCurrentScrollParentId();
 
   Maybe<nsRect> contentBoxClip;
   Maybe<const DisplayItemClipChain*> extraContentBoxClipForNonCaretContent;
   if (MOZ_UNLIKELY(
@@ -5412,30 +5412,30 @@ bool ScrollFrameHelper::IsScrollbarOnRig
       return true;
     case 3:  // Always left
       return false;
   }
 }
 
 bool ScrollFrameHelper::IsMaybeScrollingActive() const {
   const nsStyleDisplay* disp = mOuter->StyleDisplay();
-  if (disp->mWillChangeBitField & StyleWillChangeBits_SCROLL) {
+  if (disp->mWillChange.bits & StyleWillChangeBits_SCROLL) {
     return true;
   }
 
   nsIContent* content = mOuter->GetContent();
   return mHasBeenScrolledRecently || IsAlwaysActive() ||
          nsLayoutUtils::HasDisplayPort(content) ||
          nsContentUtils::HasScrollgrab(content);
 }
 
 bool ScrollFrameHelper::IsScrollingActive(
     nsDisplayListBuilder* aBuilder) const {
   const nsStyleDisplay* disp = mOuter->StyleDisplay();
-  if (disp->mWillChangeBitField & StyleWillChangeBits_SCROLL &&
+  if (disp->mWillChange.bits & StyleWillChangeBits_SCROLL &&
       aBuilder->IsInWillChangeBudget(mOuter, GetVisualViewportSize())) {
     return true;
   }
 
   nsIContent* content = mOuter->GetContent();
   return mHasBeenScrolledRecently || IsAlwaysActive() ||
          nsLayoutUtils::HasDisplayPort(content) ||
          nsContentUtils::HasScrollgrab(content);
--- a/layout/painting/ActiveLayerTracker.cpp
+++ b/layout/painting/ActiveLayerTracker.cpp
@@ -478,24 +478,24 @@ bool ActiveLayerTracker::IsStyleAnimated
   // For display:table content, transforms are applied to the table wrapper
   // (primary frame) but their will-change style will be specified on the style
   // frame and, unlike other transform properties, not inherited.
   // As a result, for transform properties only we need to be careful to look up
   // the will-change style on the _style_ frame.
   const nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aFrame);
   const nsCSSPropertyIDSet transformSet =
       nsCSSPropertyIDSet::TransformLikeProperties();
-  if ((styleFrame && (styleFrame->StyleDisplay()->mWillChangeBitField &
+  if ((styleFrame && (styleFrame->StyleDisplay()->mWillChange.bits &
                       StyleWillChangeBits_TRANSFORM)) &&
       aPropertySet.Intersects(transformSet) &&
       (!aBuilder ||
        aBuilder->IsInWillChangeBudget(aFrame, aFrame->GetSize()))) {
     return true;
   }
-  if ((aFrame->StyleDisplay()->mWillChangeBitField &
+  if ((aFrame->StyleDisplay()->mWillChange.bits &
        StyleWillChangeBits_OPACITY) &&
       aPropertySet.Intersects(nsCSSPropertyIDSet::OpacityProperties()) &&
       (!aBuilder ||
        aBuilder->IsInWillChangeBudget(aFrame, aFrame->GetSize()))) {
     return true;
   }
 
   LayerActivity* layerActivity = GetLayerActivity(aFrame);
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -1422,31 +1422,16 @@ void Gecko_EnsureStyleAnimationArrayLeng
   EnsureStyleAutoArrayLength(base, aLen);
 }
 
 void Gecko_EnsureStyleTransitionArrayLength(void* aArray, size_t aLen) {
   auto base = reinterpret_cast<nsStyleAutoArray<StyleTransition>*>(aArray);
   EnsureStyleAutoArrayLength(base, aLen);
 }
 
-void Gecko_ClearWillChange(nsStyleDisplay* aDisplay, size_t aLength) {
-  aDisplay->mWillChange.Clear();
-  aDisplay->mWillChange.SetCapacity(aLength);
-}
-
-void Gecko_AppendWillChange(nsStyleDisplay* aDisplay, nsAtom* aAtom) {
-  aDisplay->mWillChange.AppendElement(aAtom);
-}
-
-void Gecko_CopyWillChangeFrom(nsStyleDisplay* aDest,
-                              const nsStyleDisplay* aSrc) {
-  aDest->mWillChange.Clear();
-  aDest->mWillChange.AppendElements(aSrc->mWillChange);
-}
-
 enum class KeyframeSearchDirection {
   Forwards,
   Backwards,
 };
 
 enum class KeyframeInsertPosition {
   Prepend,
   LastForOffset,
--- a/layout/style/GeckoBindings.h
+++ b/layout/style/GeckoBindings.h
@@ -464,19 +464,16 @@ void Gecko_CopyCounterSetsFrom(nsStyleCo
 void Gecko_CopyCounterIncrementsFrom(nsStyleContent* content,
                                      const nsStyleContent* other);
 
 void Gecko_EnsureImageLayersLength(nsStyleImageLayers* layers, size_t len,
                                    nsStyleImageLayers::LayerType layer_type);
 
 void Gecko_EnsureStyleAnimationArrayLength(void* array, size_t len);
 void Gecko_EnsureStyleTransitionArrayLength(void* array, size_t len);
-void Gecko_ClearWillChange(nsStyleDisplay* display, size_t length);
-void Gecko_AppendWillChange(nsStyleDisplay* display, nsAtom* atom);
-void Gecko_CopyWillChangeFrom(nsStyleDisplay* dest, const nsStyleDisplay* src);
 
 // Searches from the beginning of |keyframes| for a Keyframe object with the
 // specified offset and timing function. If none is found, a new Keyframe object
 // with the specified |offset| and |timingFunction| will be prepended to
 // |keyframes|.
 //
 // @param keyframes  An array of Keyframe objects, sorted by offset.
 //                   The first Keyframe in the array, if any, MUST have an
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -455,17 +455,17 @@ cbindgen-types = [
     { gecko = "StyleGenericZIndex", servo = "values::generics::position::ZIndex" },
     { gecko = "StyleTransformOrigin", servo = "values::computed::TransformOrigin" },
     { gecko = "StyleGenericBorderRadius", servo = "values::generics::border::BorderRadius" },
     { gecko = "StyleLetterSpacing", servo = "values::computed::text::LetterSpacing" },
     { gecko = "StyleGenericLineHeight", servo = "values::generics::text::LineHeight" },
     { gecko = "StyleContain", servo = "values::computed::Contain" },
     { gecko = "StyleRestyleHint", servo = "invalidation::element::restyle_hints::RestyleHint" },
     { gecko = "StyleTouchAction", servo = "values::computed::TouchAction" },
-    { gecko = "StyleWillChangeBits", servo = "values::specified::box_::WillChangeBits" },
+    { gecko = "StyleWillChange", servo = "values::specified::box_::WillChange" },
     { gecko = "StyleTextDecorationLine", servo = "values::computed::TextDecorationLine" },
     { gecko = "StyleTextTransform", servo = "values::computed::TextTransform" },
     { gecko = "StyleMozListReversed", servo = "values::computed::MozListReversed" },
     { gecko = "StyleOwned", servo = "gecko_bindings::sugar::ownership::Owned" },
     { gecko = "StyleOwnedOrNull", servo = "gecko_bindings::sugar::ownership::OwnedOrNull" },
     { gecko = "StyleStrong", servo = "gecko_bindings::sugar::ownership::Strong" },
     { gecko = "StyleGenericFontFamily", servo = "values::computed::font::GenericFontFamily" },
     { gecko = "StyleFontFamilyNameSyntax", servo = "values::computed::font::FontFamilyNameSyntax" },
--- a/layout/style/ServoStyleConstsInlines.h
+++ b/layout/style/ServoStyleConstsInlines.h
@@ -128,28 +128,37 @@ inline StyleArcSlice<T>::~StyleArcSlice(
   }
   free(_0.ptr);  // Drop the allocation now.
 }
 
 #undef ASSERT_CANARY
 
 inline bool StyleAtom::IsStatic() const { return !!(_0 & 1); }
 
+inline nsAtom* StyleAtom::AsAtom() const {
+  if (IsStatic()) {
+    return const_cast<nsStaticAtom*>(&detail::gGkAtoms.mAtoms[(_0 & ~1) >> 1]);
+  }
+  return reinterpret_cast<nsAtom*>(_0);
+}
+
 inline StyleAtom::~StyleAtom() {
   if (!IsStatic()) {
-    reinterpret_cast<nsAtom*>(_0)->Release();
+    AsAtom()->Release();
   }
 }
 
 inline StyleAtom::StyleAtom(const StyleAtom& aOther) : _0(aOther._0) {
   if (!IsStatic()) {
     reinterpret_cast<nsAtom*>(_0)->AddRef();
   }
 }
 
+inline nsAtom* StyleCustomIdent::AsAtom() const { return _0.AsAtom(); }
+
 inline nsDependentCSubstring StyleOwnedStr::AsString() const {
   Span<const uint8_t> s = _0.AsSpan();
   return nsDependentCSubstring(reinterpret_cast<const char*>(s.Elements()),
                                s.Length());
 }
 
 }  // namespace mozilla
 
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2911,17 +2911,17 @@ nsStyleDisplay::nsStyleDisplay(const Doc
       mOverflowX(StyleOverflow::Visible),
       mOverflowY(StyleOverflow::Visible),
       mOverflowClipBoxBlock(StyleOverflowClipBox::PaddingBox),
       mOverflowClipBoxInline(StyleOverflowClipBox::PaddingBox),
       mResize(StyleResize::None),
       mOrient(StyleOrient::Inline),
       mIsolation(NS_STYLE_ISOLATION_AUTO),
       mTopLayer(NS_STYLE_TOP_LAYER_NONE),
-      mWillChangeBitField({0}),
+      mWillChange{{}, {0}},
       mTouchAction(StyleTouchAction_AUTO),
       mScrollBehavior(NS_STYLE_SCROLL_BEHAVIOR_AUTO),
       mOverscrollBehaviorX(StyleOverscrollBehavior::Auto),
       mOverscrollBehaviorY(StyleOverscrollBehavior::Auto),
       mOverflowAnchor(StyleOverflowAnchor::Auto),
       mScrollSnapType(
           {StyleScrollSnapAxis::Both, StyleScrollSnapStrictness::None}),
       mScrollSnapPointsX(eStyleUnit_None),
@@ -2978,17 +2978,16 @@ nsStyleDisplay::nsStyleDisplay(const nsS
       mOverflowX(aSource.mOverflowX),
       mOverflowY(aSource.mOverflowY),
       mOverflowClipBoxBlock(aSource.mOverflowClipBoxBlock),
       mOverflowClipBoxInline(aSource.mOverflowClipBoxInline),
       mResize(aSource.mResize),
       mOrient(aSource.mOrient),
       mIsolation(aSource.mIsolation),
       mTopLayer(aSource.mTopLayer),
-      mWillChangeBitField(aSource.mWillChangeBitField),
       mWillChange(aSource.mWillChange),
       mTouchAction(aSource.mTouchAction),
       mScrollBehavior(aSource.mScrollBehavior),
       mOverscrollBehaviorX(aSource.mOverscrollBehaviorX),
       mOverscrollBehaviorY(aSource.mOverscrollBehaviorY),
       mScrollSnapType(aSource.mScrollSnapType),
       mScrollSnapPointsX(aSource.mScrollSnapPointsX),
       mScrollSnapPointsY(aSource.mScrollSnapPointsY),
@@ -3285,17 +3284,17 @@ nsChangeHint nsStyleDisplay::CalcDiffere
   // Note that the HasTransformStyle() != aNewData.HasTransformStyle()
   // test above handles relevant changes in the StyleWillChangeBit_TRANSFORM
   // bit, which in turn handles frame reconstruction for changes in the
   // containing block of fixed-positioned elements.
   //
   // TODO(emilio): Should add xor to the generated cbindgen type.
   auto willChangeBitsChanged =
       StyleWillChangeBits{static_cast<decltype(StyleWillChangeBits::bits)>(
-          mWillChangeBitField.bits ^ aNewData.mWillChangeBitField.bits)};
+          mWillChange.bits.bits ^ aNewData.mWillChange.bits.bits)};
 
   if (willChangeBitsChanged &
       (StyleWillChangeBits_STACKING_CONTEXT | StyleWillChangeBits_SCROLL |
        StyleWillChangeBits_OPACITY)) {
     hint |= nsChangeHint_RepaintFrame;
   }
 
   if (willChangeBitsChanged &
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1725,21 +1725,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   mozilla::StyleOverflow mOverflowX;
   mozilla::StyleOverflow mOverflowY;
   mozilla::StyleOverflowClipBox mOverflowClipBoxBlock;
   mozilla::StyleOverflowClipBox mOverflowClipBoxInline;
   mozilla::StyleResize mResize;
   mozilla::StyleOrient mOrient;
   uint8_t mIsolation;  // NS_STYLE_ISOLATION_*
   uint8_t mTopLayer;   // NS_STYLE_TOP_LAYER_*
-  // Stores a bitfield representation of the properties that are frequently
-  // queried. This should match mWillChange. Also tracks if any of the
-  // properties in the will-change list require a stacking context.
-  mozilla::StyleWillChangeBits mWillChangeBitField;
-  nsTArray<RefPtr<nsAtom>> mWillChange;
+  mozilla::StyleWillChange mWillChange;
 
   mozilla::StyleTouchAction mTouchAction;
   uint8_t mScrollBehavior;  // NS_STYLE_SCROLL_BEHAVIOR_*
   mozilla::StyleOverscrollBehavior mOverscrollBehaviorX;
   mozilla::StyleOverscrollBehavior mOverscrollBehaviorY;
   mozilla::StyleOverflowAnchor mOverflowAnchor;
   mozilla::StyleScrollSnapAlign mScrollSnapAlign;
   mozilla::StyleScrollSnapType mScrollSnapType;
@@ -2004,17 +2000,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   }
 
   /* Returns whether the element has the -moz-transform property
    * or a related property. */
   bool HasTransformStyle() const {
     return mSpecifiedTransform || mSpecifiedRotate || mSpecifiedTranslate ||
            mSpecifiedScale ||
            mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D ||
-           (mWillChangeBitField & mozilla::StyleWillChangeBits_TRANSFORM) ||
+           (mWillChange.bits & mozilla::StyleWillChangeBits_TRANSFORM) ||
            (mMotion && mMotion->HasPath());
   }
 
   bool HasIndividualTransform() const {
     return mSpecifiedRotate || mSpecifiedTranslate || mSpecifiedScale;
   }
 
   bool HasPerspectiveStyle() const { return !mChildPerspective.IsNone(); }
--- a/layout/style/nsStyleStructInlines.h
+++ b/layout/style/nsStyleStructInlines.h
@@ -126,17 +126,17 @@ bool nsStyleDisplay::HasPerspective(cons
 }
 
 bool nsStyleDisplay::IsFixedPosContainingBlockForNonSVGTextFrames(
     const mozilla::ComputedStyle& aStyle) const {
   // NOTE: Any CSS properties that influence the output of this function
   // should have the FIXPOS_CB flag set on them.
   NS_ASSERTION(aStyle.StyleDisplay() == this, "unexpected aStyle");
 
-  if (mWillChangeBitField & mozilla::StyleWillChangeBits_FIXPOS_CB) {
+  if (mWillChange.bits & mozilla::StyleWillChangeBits_FIXPOS_CB) {
     return true;
   }
 
   return aStyle.StyleEffects()->HasFilters();
 }
 
 bool nsStyleDisplay::
     IsFixedPosContainingBlockForContainLayoutAndPaintSupportingFrames() const {
@@ -171,17 +171,17 @@ bool nsStyleDisplay::IsFixedPosContainin
              "Any fixed-pos CB should also be an abs-pos CB");
   return true;
 }
 
 bool nsStyleDisplay::IsAbsPosContainingBlockForNonSVGTextFrames() const {
   // NOTE: Any CSS properties that influence the output of this function
   // should have the ABSPOS_CB set on them.
   return IsAbsolutelyPositionedStyle() || IsRelativelyPositionedStyle() ||
-         (mWillChangeBitField & mozilla::StyleWillChangeBits_ABSPOS_CB);
+         (mWillChange.bits & mozilla::StyleWillChangeBits_ABSPOS_CB);
 }
 
 bool nsStyleDisplay::IsAbsPosContainingBlock(
     const nsIFrame* aContextFrame) const {
   mozilla::ComputedStyle* style = aContextFrame->Style();
   NS_ASSERTION(style->StyleDisplay() == this, "unexpected aContextFrame");
   // NOTE: Any CSS properties that influence the output of this function
   // should have the ABSPOS_CB set on them.
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -2508,17 +2508,17 @@ fn static_assert() {
 <% skip_box_longhands= """display
                           animation-name animation-delay animation-duration
                           animation-direction animation-fill-mode animation-play-state
                           animation-iteration-count animation-timing-function
                           clear transition-duration transition-delay
                           transition-timing-function transition-property
                           transform-style
                           rotate scroll-snap-points-x scroll-snap-points-y
-                          scroll-snap-coordinate -moz-binding will-change
+                          scroll-snap-coordinate -moz-binding
                           offset-path shape-outside
                           translate scale -webkit-line-clamp""" %>
 <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}">
     #[inline]
     pub fn generate_combined_transform(&mut self) {
         unsafe { bindings::Gecko_StyleDisplay_GenerateCombinedTransform(&mut *self.gecko) };
     }
 
@@ -2824,76 +2824,16 @@ fn static_assert() {
     ${impl_copy_animation_value('iteration_count', 'IterationCount')}
 
     ${impl_animation_timing_function()}
 
     ${impl_individual_transform('rotate', 'Rotate', 'mSpecifiedRotate')}
     ${impl_individual_transform('translate', 'Translate', 'mSpecifiedTranslate')}
     ${impl_individual_transform('scale', 'Scale', 'mSpecifiedScale')}
 
-    pub fn set_will_change(&mut self, v: longhands::will_change::computed_value::T) {
-        use crate::gecko_bindings::bindings::{Gecko_AppendWillChange, Gecko_ClearWillChange};
-        use crate::values::specified::box_::{WillChangeBits, WillChange};
-
-        match v {
-            WillChange::AnimateableFeatures { features, bits } => {
-                unsafe {
-                    Gecko_ClearWillChange(&mut *self.gecko, features.len());
-                }
-
-                for feature in features.iter() {
-                    unsafe {
-                        Gecko_AppendWillChange(&mut *self.gecko, feature.0.as_ptr())
-                    }
-                }
-
-                self.gecko.mWillChangeBitField = bits;
-            },
-            WillChange::Auto => {
-                unsafe {
-                    Gecko_ClearWillChange(&mut *self.gecko, 0);
-                }
-                self.gecko.mWillChangeBitField = WillChangeBits::empty();
-            },
-        };
-    }
-
-    pub fn copy_will_change_from(&mut self, other: &Self) {
-        use crate::gecko_bindings::bindings::Gecko_CopyWillChangeFrom;
-
-        self.gecko.mWillChangeBitField = other.gecko.mWillChangeBitField;
-        unsafe {
-            Gecko_CopyWillChangeFrom(&mut *self.gecko, &*other.gecko);
-        }
-    }
-
-    pub fn reset_will_change(&mut self, other: &Self) {
-        self.copy_will_change_from(other)
-    }
-
-    pub fn clone_will_change(&self) -> longhands::will_change::computed_value::T {
-        use crate::values::CustomIdent;
-        use crate::values::specified::box_::WillChange;
-
-        if self.gecko.mWillChange.len() == 0 {
-            return WillChange::Auto
-        }
-
-        let custom_idents: Vec<CustomIdent> = self.gecko.mWillChange.iter().map(|gecko_atom| {
-            unsafe {
-                CustomIdent(Atom::from_raw(gecko_atom.mRawPtr))
-            }
-        }).collect();
-
-        WillChange::AnimateableFeatures {
-            features: custom_idents.into_boxed_slice(),
-            bits: self.gecko.mWillChangeBitField,
-        }
-    }
-
     <% impl_shape_source("shape_outside", "mShapeOutside") %>
 
     pub fn set_offset_path(&mut self, v: longhands::offset_path::computed_value::T) {
         use crate::gecko_bindings::bindings::{Gecko_NewStyleMotion, Gecko_SetStyleMotion};
         use crate::gecko_bindings::structs::StyleShapeSourceType;
         use crate::values::generics::basic_shape::FillRule;
         use crate::values::specified::OffsetPath;
 
--- a/servo/components/style/values/specified/box.rs
+++ b/servo/components/style/values/specified/box.rs
@@ -637,56 +637,57 @@ pub enum OverflowAnchor {
 pub enum OverflowClipBox {
     PaddingBox,
     ContentBox,
 }
 
 #[derive(
     Clone,
     Debug,
+    Default,
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToComputedValue,
     ToCss,
     ToResolvedValue,
     ToShmem,
 )]
-/// Provides a rendering hint to the user agent,
-/// stating what kinds of changes the author expects
-/// to perform on the element
+#[css(comma)]
+#[repr(C)]
+/// Provides a rendering hint to the user agent, stating what kinds of changes
+/// the author expects to perform on the element.
+///
+/// `auto` is represented by an empty `features` list.
 ///
 /// <https://drafts.csswg.org/css-will-change/#will-change>
-pub enum WillChange {
-    /// Expresses no particular intent
-    Auto,
-    /// <custom-ident>
-    #[css(comma)]
-    AnimateableFeatures {
-        /// The features that are supposed to change.
-        #[css(iterable)]
-        features: Box<[CustomIdent]>,
-        /// A bitfield with the kind of change that the value will create, based
-        /// on the above field.
-        #[css(skip)]
-        bits: WillChangeBits,
-    },
+pub struct WillChange {
+    /// The features that are supposed to change.
+    ///
+    /// TODO(emilio): Consider using ArcSlice since we just clone them from the
+    /// specified value? That'd save an allocation, which could be worth it.
+    #[css(iterable, if_empty = "auto")]
+    features: crate::OwnedSlice<CustomIdent>,
+    /// A bitfield with the kind of change that the value will create, based
+    /// on the above field.
+    #[css(skip)]
+    bits: WillChangeBits,
 }
 
 impl WillChange {
     #[inline]
     /// Get default value of `will-change` as `auto`
-    pub fn auto() -> WillChange {
-        WillChange::Auto
+    pub fn auto() -> Self {
+        Self::default()
     }
 }
 
 bitflags! {
     /// The change bits that we care about.
-    #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
+    #[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
     #[repr(C)]
     pub struct WillChangeBits: u8 {
         /// Whether the stacking context will change.
         const STACKING_CONTEXT = 1 << 0;
         /// Whether `transform` will change.
         const TRANSFORM = 1 << 1;
         /// Whether `scroll-position` will change.
         const SCROLL = 1 << 2;
@@ -741,17 +742,17 @@ impl Parse for WillChange {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Self, ParseError<'i>> {
         if input
             .try(|input| input.expect_ident_matching("auto"))
             .is_ok()
         {
-            return Ok(WillChange::Auto);
+            return Ok(Self::default());
         }
 
         let mut bits = WillChangeBits::empty();
         let custom_idents = input.parse_comma_separated(|i| {
             let location = i.current_source_location();
             let parser_ident = i.expect_ident()?;
             let ident = CustomIdent::from_ident(
                 location,
@@ -762,18 +763,18 @@ impl Parse for WillChange {
             if ident.0 == atom!("scroll-position") {
                 bits |= WillChangeBits::SCROLL;
             } else {
                 bits |= change_bits_for_maybe_property(&parser_ident, context);
             }
             Ok(ident)
         })?;
 
-        Ok(WillChange::AnimateableFeatures {
-            features: custom_idents.into_boxed_slice(),
+        Ok(Self {
+            features: custom_idents.into(),
             bits,
         })
     }
 }
 
 bitflags! {
     /// Values for the `touch-action` property.
     #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)]
--- a/servo/ports/geckolib/cbindgen.toml
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -105,17 +105,17 @@ include = [
   "Perspective",
   "ZIndex",
   "TransformOrigin",
   "WordBreak",
   "Contain",
   "Origin",
   "RestyleHint",
   "TouchAction",
-  "WillChangeBits",
+  "WillChange",
   "TextDecorationLine",
   "TextTransform",
   "MozListReversed",
   "Owned",
   "OwnedOrNull",
   "Strong",
   "ScrollbarColor",
   "Color",
@@ -380,22 +380,27 @@ renaming_overrides_prefixing = true
   inline ~StyleArcSlice();
   inline Span<const T> AsSpan() const;
   inline size_t Length() const;
   inline bool IsEmpty() const;
   inline bool operator==(const StyleArcSlice& other) const;
   inline bool operator!=(const StyleArcSlice& other) const;
 """
 
+"CustomIdent" = """
+  inline nsAtom* AsAtom() const;
+"""
+
 "Atom" = """
   StyleAtom(size_t) = delete;
   StyleAtom() = delete;
 
   // NOTE(emilio): For now we don't need to expose anything else, but it'd be trivial if we wanted to.
   inline bool IsStatic() const;
+  inline nsAtom* AsAtom() const;
 
   inline StyleAtom(const StyleAtom& aOther);
   inline ~StyleAtom();
 """
 
 "OwnedStr" = """
   inline nsDependentCSubstring AsString() const;
 """