Bug 1367904 - Part 13: stylo: Flatten ServoComputedValues into ServoStyleContext; r?bholley draft
authorManish Goregaokar <manishearth@gmail.com>
Thu, 15 Jun 2017 22:49:50 -0700
changeset 608737 cce0abb8faac2ef21617f70623bad45c69beef18
parent 608103 1f903f07e41a45451963f1bac7715c591319a589
child 608836 572ac57dcd1e5b0ac785ad72c2e725b9562ac8c5
push id68400
push userbmo:manishearth@gmail.com
push dateFri, 14 Jul 2017 05:28:32 +0000
reviewersbholley
bugs1367904
milestone56.0a1
Bug 1367904 - Part 13: stylo: Flatten ServoComputedValues into ServoStyleContext; r?bholley This patch also removes the duplication of style contexts during the restyle, because otherwise pointer equality of ServoComputedValues stops holding (and we assert on that in a few places) MozReview-Commit-ID: 7Evc1p8ZfM2
layout/base/ServoRestyleManager.cpp
layout/style/ServoArcTypeList.h
layout/style/ServoBindingList.h
layout/style/ServoBindingTypes.h
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
layout/style/ServoBindings.toml
layout/style/ServoStyleContext.cpp
layout/style/ServoStyleContext.h
layout/style/ServoStyleSet.cpp
layout/style/ServoTypes.h
layout/style/nsStyleContext.h
layout/style/nsStyleContextInlines.h
servo/components/style/gecko/arc_types.rs
servo/components/style/properties/gecko.mako.rs
servo/ports/geckolib/glue.rs
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -507,17 +507,17 @@ ServoRestyleManager::ProcessPostTraversa
   // changes too).
   //
   // Otherwise we should probably encode that information somehow to avoid
   // expensive checks in the common case.
   //
   // Also, we're going to need to check for pseudos of display: contents
   // elements, though that is buggy right now even in non-stylo mode, see
   // bug 1251799.
-  const bool recreateContext = oldStyleContext &&
+  const bool updateContext = oldStyleContext &&
     oldStyleContext->ComputedValues() != currentContext->ComputedValues();
 
   Maybe<ServoRestyleState> thisFrameRestyleState;
   if (styleFrame) {
     auto type = isOutOfFlow
       ? ServoRestyleState::Type::OutOfFlow
       : ServoRestyleState::Type::InFlow;
 
@@ -525,29 +525,21 @@ ServoRestyleManager::ProcessPostTraversa
   }
 
   // We can't really assume as used changes from display: contents elements (or
   // other elements without frames).
   ServoRestyleState& childrenRestyleState =
     thisFrameRestyleState ? *thisFrameRestyleState : aRestyleState;
 
   RefPtr<ServoStyleContext> newContext = nullptr;
-  if (recreateContext) {
+  if (updateContext) {
     MOZ_ASSERT(styleFrame || displayContentsNode);
 
-    auto pseudo = aElement->GetPseudoElementType();
-    nsIAtom* pseudoTag = pseudo == CSSPseudoElementType::NotPseudo
-      ? nullptr : nsCSSPseudoElements::GetPseudoAtom(pseudo);
 
-    // XXXManishearth we should just reuse the old one here
-    RefPtr<ServoStyleContext> tempContext =
-      Servo_StyleContext_NewContext(currentContext->ComputedValues(), aParentContext,
-                                    PresContext(), pseudo, pseudoTag).Consume();
-    newContext = aRestyleState.StyleSet().GetContext(tempContext.forget(), aParentContext,
-                                                     pseudoTag, pseudo, aElement);
+    newContext = currentContext;
 
     newContext->ResolveSameStructsAs(PresContext(), oldStyleContext);
 
     // We want to walk all the continuations here, even the ones with different
     // styles.  In practice, the only reason we get continuations with different
     // styles here is ::first-line (::first-letter never affects element
     // styles).  But in that case, newContext is the right context for the
     // _later_ continuations anyway (the ones not affected by ::first-line), not
@@ -593,41 +585,41 @@ ServoRestyleManager::ProcessPostTraversa
   }
 
   const bool descendantsNeedFrames =
     aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
   const bool traverseElementChildren =
     aElement->HasDirtyDescendantsForServo() ||
     aElement->HasAnimationOnlyDirtyDescendantsForServo() ||
     descendantsNeedFrames;
-  const bool traverseTextChildren = recreateContext || descendantsNeedFrames;
-  bool recreatedAnyContext = recreateContext;
+  const bool traverseTextChildren = updateContext || descendantsNeedFrames;
+  bool recreatedAnyContext = updateContext;
   if (traverseElementChildren || traverseTextChildren) {
     ServoStyleContext* upToDateContext =
-      recreateContext ? newContext : oldStyleContext;
+      updateContext ? newContext : oldStyleContext;
 
     StyleChildrenIterator it(aElement);
     TextPostTraversalState textState(*upToDateContext,
-                                     displayContentsNode && recreateContext,
+                                     displayContentsNode && updateContext,
                                      childrenRestyleState);
     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
       if (traverseElementChildren && n->IsElement()) {
         recreatedAnyContext |= ProcessPostTraversal(
           n->AsElement(), upToDateContext, childrenRestyleState);
       } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
         recreatedAnyContext |= ProcessPostTraversalForText(n, textState);
       }
     }
   }
 
   // We want to update frame pseudo-element styles after we've traversed our
   // kids, because some of those updates (::first-line/::first-letter) need to
   // modify the styles of the kids, and the child traversal above would just
   // clobber those modifications.
-  if (recreateContext && styleFrame) {
+  if (updateContext && styleFrame) {
     UpdateFramePseudoElementStyles(styleFrame, childrenRestyleState);
   }
 
   aElement->UnsetHasDirtyDescendantsForServo();
   aElement->UnsetHasAnimationOnlyDirtyDescendantsForServo();
   aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
   return recreatedAnyContext;
 }
--- a/layout/style/ServoArcTypeList.h
+++ b/layout/style/ServoArcTypeList.h
@@ -3,17 +3,16 @@
 /* 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/. */
 
 /* a list of all Servo Arc types used in stylo bindings for preprocessing */
 
 SERVO_ARC_TYPE(CssRules, ServoCssRules)
 SERVO_ARC_TYPE(StyleSheetContents, RawServoStyleSheetContents)
-SERVO_ARC_TYPE(ComputedValues, ServoComputedValues)
 SERVO_ARC_TYPE(DeclarationBlock, RawServoDeclarationBlock)
 SERVO_ARC_TYPE(StyleRule, RawServoStyleRule)
 SERVO_ARC_TYPE(ImportRule, RawServoImportRule)
 SERVO_ARC_TYPE(AnimationValue, RawServoAnimationValue)
 SERVO_ARC_TYPE(Keyframe, RawServoKeyframe)
 SERVO_ARC_TYPE(KeyframesRule, RawServoKeyframesRule)
 SERVO_ARC_TYPE(MediaList, RawServoMediaList)
 SERVO_ARC_TYPE(MediaRule, RawServoMediaRule)
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -468,16 +468,17 @@ SERVO_BINDING_FUNC(Servo_ComputedValues_
                    RawServoStyleSetBorrowed set,
                    mozilla::CSSPseudoElementType pseudo_type,
                    nsIAtom* pseudo_tag,
                    ServoStyleContextBorrowedOrNull parent_style,
                    mozilla::InheritTarget target)
 SERVO_BINDING_FUNC(Servo_ComputedValues_GetVisitedStyle,
                    ServoStyleContextStrong,
                    ServoComputedValuesBorrowed values)
+
 // Gets the source style rules for the computed values. This returns
 // the result via rules, which would include a list of unowned pointers
 // to RawServoStyleRule.
 SERVO_BINDING_FUNC(Servo_ComputedValues_GetStyleRuleList, void,
                    ServoComputedValuesBorrowed values,
                    RawGeckoServoStyleRuleListBorrowedMut rules)
 
 SERVO_BINDING_FUNC(Servo_StyleContext_NewContext,
--- a/layout/style/ServoBindingTypes.h
+++ b/layout/style/ServoBindingTypes.h
@@ -100,16 +100,19 @@ typedef mozilla::dom::StyleChildrenItera
     type_* mPtr;                             \
     already_AddRefed<type_> Consume();       \
   };
 #include "mozilla/ServoArcTypeList.h"
 #undef SERVO_ARC_TYPE
 
 typedef mozilla::ServoStyleContext const* ServoStyleContextBorrowed;
 typedef mozilla::ServoStyleContext const* ServoStyleContextBorrowedOrNull;
+typedef ServoComputedValues const* ServoComputedValuesBorrowed;
+typedef ServoComputedValues const* ServoComputedValuesBorrowedOrNull;
+
 struct MOZ_MUST_USE_TYPE ServoStyleContextStrong
 {
   mozilla::ServoStyleContext* mPtr;
   already_AddRefed<mozilla::ServoStyleContext> Consume();
 };
 
 #define DECL_OWNED_REF_TYPE_FOR(type_)    \
   typedef type_* type_##Owned;            \
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -204,25 +204,29 @@ Gecko_DestroyAnonymousContentList(nsTArr
 {
   MOZ_ASSERT(aAnonContent);
   delete aAnonContent;
 }
 
 void
 Gecko_ServoStyleContext_Init(ServoStyleContext* aContext,
                              const ServoStyleContext* aParentContext,
-                             RawGeckoPresContextBorrowed aPresContext, ServoComputedValuesStrong aValues,
+                             RawGeckoPresContextBorrowed aPresContext, const ServoComputedValues* aValues,
                              mozilla::CSSPseudoElementType aPseudoType, nsIAtom* aPseudoTag)
 {
   // because it is within an Arc it is unsafe for the Rust side to ever
   // carry around a mutable non opaque reference to the context, so we
   // cast it here.
   ServoStyleContext* parent = const_cast<ServoStyleContext*>(aParentContext);
   nsPresContext* pres = const_cast<nsPresContext*>(aPresContext);
-  new (KnownNotNull, aContext) ServoStyleContext(parent, pres, aPseudoTag, aPseudoType, aValues.Consume());
+  new (KnownNotNull, aContext) ServoStyleContext(parent, pres, aPseudoTag, aPseudoType, aValues);
+}
+
+ServoComputedValues::ServoComputedValues(const ServoComputedValuesForgotten aValue) {
+  PodAssign(this, aValue.mPtr);
 }
 
 void
 Gecko_ServoStyleContext_Destroy(ServoStyleContext* aContext)
 {
   aContext->~ServoStyleContext();
 }
 
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -138,17 +138,17 @@ bool Gecko_IsSignificantChild(RawGeckoNo
 RawGeckoNodeBorrowedOrNull Gecko_GetLastChild(RawGeckoNodeBorrowed node);
 RawGeckoNodeBorrowedOrNull Gecko_GetFlattenedTreeParentNode(RawGeckoNodeBorrowed node);
 RawGeckoElementBorrowedOrNull Gecko_GetBeforeOrAfterPseudo(RawGeckoElementBorrowed element, bool is_before);
 nsTArray<nsIContent*>* Gecko_GetAnonymousContentForElement(RawGeckoElementBorrowed element);
 void Gecko_DestroyAnonymousContentList(nsTArray<nsIContent*>* anon_content);
 
 void Gecko_ServoStyleContext_Init(mozilla::ServoStyleContext* context,
                                   ServoStyleContextBorrowedOrNull parent_context,
-                                  RawGeckoPresContextBorrowed pres_context, ServoComputedValuesStrong values,
+                                  RawGeckoPresContextBorrowed pres_context, ServoComputedValuesBorrowed values,
                                   mozilla::CSSPseudoElementType pseudo_type, nsIAtom* pseudo_tag);
 void Gecko_ServoStyleContext_Destroy(mozilla::ServoStyleContext* context);
 
 // By default, Servo walks the DOM by traversing the siblings of the DOM-view
 // first child. This generally works, but misses anonymous children, which we
 // want to traverse during styling. To support these cases, we create an
 // optional stack-allocated iterator in aIterator for nodes that need it.
 void Gecko_ConstructStyleChildrenIterator(RawGeckoElementBorrowed aElement,
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -243,16 +243,18 @@ whitelist-types = [
     "nsTArray",
     "nsTArrayHeader",
     "Position",
     "PropertyValuePair",
     "Runnable",
     "ServoAttrSnapshot",
     "ServoBundledURI",
     "ServoComputedValues",
+    "ServoComputedValuesBorrowed",
+    "ServoComputedValuesBorrowedOrNull",
     "ServoElementSnapshot",
     "ServoStyleContextStrong",
     "ServoStyleContextBorrowed",
     "ServoStyleContextBorrowedOrNull",
     "SheetParsingMode",
     "StaticRefPtr",
     "StyleAnimation",
     "StyleBasicShape",
@@ -340,16 +342,18 @@ hide-types = [
 raw-lines = [
     "pub use nsstring::{nsACString, nsAString, nsString, nsStringRepr};",
     "use gecko_bindings::structs::nsStyleTransformMatrix;",
     "use gecko_bindings::structs::nsTArray;",
     "type nsACString_internal = nsACString;",
     "type nsAString_internal = nsAString;",
     "pub type ServoStyleContextBorrowed<'a> = &'a ServoStyleContext;",
     "pub type ServoStyleContextBorrowedOrNull<'a> = Option<&'a ::properties::ComputedValues>;",
+    "pub type ServoComputedValuesBorrowed<'a> = &'a ServoComputedValues;",
+    "pub type ServoComputedValuesBorrowedOrNull<'a> = Option<&'a ServoComputedValues>;",
 ]
 whitelist-functions = ["Servo_.*", "Gecko_.*"]
 structs-types = [
     "mozilla::css::GridTemplateAreasValue",
     "mozilla::css::ErrorReporter",
     "mozilla::css::ImageValue",
     "mozilla::css::URLValue",
     "mozilla::css::URLValueData",
--- a/layout/style/ServoStyleContext.cpp
+++ b/layout/style/ServoStyleContext.cpp
@@ -12,19 +12,19 @@
 #include "mozilla/ServoBindings.h"
 
 using namespace mozilla;
 
 ServoStyleContext::ServoStyleContext(nsStyleContext* aParent,
                                nsPresContext* aPresContext,
                                nsIAtom* aPseudoTag,
                                CSSPseudoElementType aPseudoType,
-                               already_AddRefed<ServoComputedValues> aComputedValues)
+                               const ServoComputedValues* aComputedValues)
   : nsStyleContext(aParent, aPseudoTag, aPseudoType),
-  mSource(Move(aComputedValues))
+    mSource(aComputedValues->Forget())
 {
   mPresContext = aPresContext;
 
   FinishConstruction();
 
   // No need to call ApplyStyleFixups here, since fixups are handled by Servo when
   // producing the ServoComputedValues.
 }
--- a/layout/style/ServoStyleContext.h
+++ b/layout/style/ServoStyleContext.h
@@ -12,24 +12,24 @@
 namespace mozilla {
 
 class ServoStyleContext final : public nsStyleContext {
 public:
   ServoStyleContext(nsStyleContext* aParent,
                     nsPresContext* aPresContext,
                     nsIAtom* aPseudoTag,
                     CSSPseudoElementType aPseudoType,
-                    already_AddRefed<ServoComputedValues> aComputedValues);
+                    const ServoComputedValues* aComputedValues);
 
   nsPresContext* PresContext() const {
     return mPresContext;
   }
 
-  ServoComputedValues* ComputedValues() const {
-    return mSource;
+  const ServoComputedValues* ComputedValues() const {
+    return &mSource;
   }
 
   void AddRef() {
     Servo_StyleContext_AddRef(this);
   }
 
   void Release() {
     Servo_StyleContext_Release(this);
@@ -53,14 +53,14 @@ public:
   #include "nsStyleStructList.h"
   #undef STYLE_STRUCT
 
     mBits |= newBits;
   }
 
 private:
   nsPresContext* mPresContext;
-  RefPtr<ServoComputedValues> mSource;
+  ServoComputedValues mSource;
 };
 
 }
 
 #endif // mozilla_ServoStyleContext_h
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -215,23 +215,22 @@ ServoStyleSet::GetContext(already_AddRef
   // we can precompute these and pass them as flags, similar to nsStyleSet.cpp.
   if (aElementForAnimation) {
     isLink = nsCSSRuleProcessor::IsLink(aElementForAnimation);
     isVisitedLink = nsCSSRuleProcessor::GetContentState(aElementForAnimation)
                                        .HasState(NS_EVENT_STATE_VISITED);
   }
 
   RefPtr<ServoStyleContext> result = Move(aComputedValues);
-  RefPtr<ServoComputedValues> computedValues = result->ComputedValues();
 
   MOZ_ASSERT(result->GetPseudoType() == aPseudoType);
   MOZ_ASSERT(result->GetPseudo() == aPseudoTag);
 
   RefPtr<ServoStyleContext> resultIfVisited =
-    Servo_ComputedValues_GetVisitedStyle(computedValues).Consume();
+    Servo_ComputedValues_GetVisitedStyle(result->ComputedValues()).Consume();
 
   // If `resultIfVisited` is non-null, then there was a relevant link and
   // visited styles were computed.  This corresponds to the cases where Gecko's
   // style system produces `aVisitedRuleNode`.
   // Set up `parentIfVisited` depending on whether our parent context has a
   // a visited style.  If it doesn't but we do have visited styles, use the
   // regular parent context for visited.
   nsStyleContext *parentIfVisited =
--- a/layout/style/ServoTypes.h
+++ b/layout/style/ServoTypes.h
@@ -163,25 +163,27 @@ struct ServoComputedValueFlags {
 #define STYLE_STRUCT_LIST_IGNORE_VARIABLES
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 #undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
 
 } // namespace mozilla
 
 
+struct ServoComputedValues;
+struct ServoComputedValuesForgotten {
+  ServoComputedValuesForgotten(const ServoComputedValues* aValue) : mPtr(aValue) {}
+  const ServoComputedValues* mPtr;
+};
+
 /**
  * We want C++ to be abe to read the style struct fields of ComputedValues
  * so we define this type on the C++ side and use the bindgenned version
  * on the Rust side.
  *
- * C++ just sees pointers and opaque types here, so bindgen will attempt to generate a Copy
- * impl. This will fail because the bindgenned version contains owned types. Opt out.
- *
- * <div rustbindgen nocopy></div>
  */
 struct ServoComputedValues {
 #define STYLE_STRUCT(name_, checkdata_cb_) mozilla::ServoRawOffsetArc<mozilla::Gecko##name_> name_;
   #define STYLE_STRUCT_LIST_IGNORE_VARIABLES
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
   #undef STYLE_STRUCT_LIST_IGNORE_VARIABLES
   mozilla::ServoCustomPropertiesMap custom_properties;
@@ -191,12 +193,34 @@ struct ServoComputedValues {
   /// node.  Can be None for default values and text nodes.  This is
   /// essentially an optimization to avoid referencing the root rule node.
   mozilla::ServoRuleNode rules;
   /// The element's computed values if visited, only computed if there's a
   /// relevant link for this element. A element's "relevant link" is the
   /// element being matched if it is a link or the nearest ancestor link.
   mozilla::ServoVisitedStyle visited_style;
   mozilla::ServoComputedValueFlags flags;
-  ~ServoComputedValues() {} // do nothing, but prevent Copy from being impl'd by bindgen
+
+  // C++ just sees this struct as a bucket of bits, and will
+  // do the wrong thing if we let it use the default copy ctor/assignment
+  // operator. Remove them so that there is no footgun.
+  //
+  // We remove the move ctor/assignment operator as well, because
+  // moves in C++ don't prevent destructors from being called,
+  // which will lead to double frees.
+  ServoComputedValues& operator=(const ServoComputedValues&) = delete;
+  ServoComputedValues(const ServoComputedValues&) = delete;
+  ServoComputedValues&& operator=(const ServoComputedValues&&) = delete;
+  ServoComputedValues(const ServoComputedValues&&) = delete;
+
+  // Constructs via memcpy. Will not invalidate old struct
+  explicit ServoComputedValues(const ServoComputedValuesForgotten aValue);
+
+  // Make sure you manually mem::forget the backing ServoComputedValues
+  // after calling this
+  ServoComputedValuesForgotten Forget() const {
+    return ServoComputedValuesForgotten(this);
+  }
 };
 
+
+
 #endif // mozilla_ServoTypes_h
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -217,17 +217,17 @@ public:
    * happen because it was inherited from the parent style context, or
    * because it was stored conditionally on the rule node.
    */
   bool HasCachedDependentStyleData(nsStyleStructID aSID) {
     return mBits & nsCachedStyleData::GetBitForSID(aSID);
   }
 
   inline nsRuleNode* RuleNode();
-  inline ServoComputedValues* ComputedValues();
+  inline const ServoComputedValues* ComputedValues();
 
   void AddStyleBit(const uint64_t& aBit) { mBits |= aBit; }
 
   /**
    * Define typesafe getter functions for each style struct by
    * preprocessing the list of style structs.  These functions are the
    * preferred way to get style data.  The macro creates functions like:
    *   const nsStyleBorder* StyleBorder();
--- a/layout/style/nsStyleContextInlines.h
+++ b/layout/style/nsStyleContextInlines.h
@@ -25,17 +25,17 @@ MOZ_DEFINE_STYLO_METHODS(nsStyleContext,
 
 nsRuleNode*
 nsStyleContext::RuleNode()
 {
     MOZ_RELEASE_ASSERT(IsGecko());
     return AsGecko()->RuleNode();
 }
 
-ServoComputedValues*
+const ServoComputedValues*
 nsStyleContext::ComputedValues()
 {
     MOZ_RELEASE_ASSERT(IsServo());
     return AsServo()->ComputedValues();
 }
 
 void
 nsStyleContext::AddRef()
--- a/servo/components/style/gecko/arc_types.rs
+++ b/servo/components/style/gecko/arc_types.rs
@@ -10,20 +10,20 @@
 
 use gecko_bindings::bindings::{RawServoImportRule, RawServoSupportsRule};
 use gecko_bindings::bindings::{RawServoKeyframe, RawServoKeyframesRule};
 use gecko_bindings::bindings::RawServoMediaRule;
 use gecko_bindings::bindings::{RawServoNamespaceRule, RawServoPageRule};
 use gecko_bindings::bindings::{RawServoRuleNode, RawServoRuleNodeStrong, RawServoDocumentRule};
 use gecko_bindings::bindings::ServoCssRules;
 use gecko_bindings::structs::{RawServoAnimationValue, RawServoDeclarationBlock, RawServoStyleRule, RawServoMediaList};
-use gecko_bindings::structs::{RawServoStyleSheetContents, ServoComputedValues, ServoStyleContext};
+use gecko_bindings::structs::{RawServoStyleSheetContents, ServoStyleContext};
 use gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI};
 use media_queries::MediaList;
-use properties::{ComputedValues, ComputedValuesInner, PropertyDeclarationBlock};
+use properties::{ComputedValues, PropertyDeclarationBlock};
 use properties::animated_properties::AnimationValue;
 use rule_tree::StrongRuleNode;
 use shared_lock::Locked;
 use std::{mem, ptr};
 use stylesheets::{CssRules, StylesheetContents, StyleRule, ImportRule, KeyframesRule, MediaRule};
 use stylesheets::{NamespaceRule, PageRule, SupportsRule, DocumentRule};
 use stylesheets::keyframes_rule::Keyframe;
 
@@ -47,19 +47,16 @@ macro_rules! impl_arc_ffi {
 }
 
 impl_arc_ffi!(Locked<CssRules> => ServoCssRules
               [Servo_CssRules_AddRef, Servo_CssRules_Release]);
 
 impl_arc_ffi!(StylesheetContents => RawServoStyleSheetContents
               [Servo_StyleSheetContents_AddRef, Servo_StyleSheetContents_Release]);
 
-impl_arc_ffi!(ComputedValuesInner => ServoComputedValues
-              [Servo_ComputedValues_AddRef, Servo_ComputedValues_Release]);
-
 impl_arc_ffi!(ComputedValues => ServoStyleContext
               [Servo_StyleContext_AddRef, Servo_StyleContext_Release]);
 
 impl_arc_ffi!(Locked<PropertyDeclarationBlock> => RawServoDeclarationBlock
               [Servo_DeclarationBlock_AddRef, Servo_DeclarationBlock_Release]);
 
 impl_arc_ffi!(Locked<StyleRule> => RawServoStyleRule
               [Servo_StyleRule_AddRef, Servo_StyleRule_Release]);
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -155,44 +155,46 @@ impl ComputedValuesInner {
     pub fn to_outer(self, device: &Device, parent: ParentStyleContextInfo,
                     info: Option<PseudoInfo>) -> Arc<ComputedValues> {
         let (tag, ty) = if let Some(info) = info {
             info
         } else {
             (ptr::null_mut(), structs::CSSPseudoElementType::NotPseudo)
         };
 
-        unsafe { Self::to_outer_from_arc(Arc::new(self), device.pres_context(), parent, ty, tag) }
-    }
-
-    pub unsafe fn to_outer_from_arc(s: Arc<Self>, pres_context: bindings::RawGeckoPresContextBorrowed,
+        unsafe { self.to_outer_helper(device.pres_context(), parent, ty, tag) }
+    }
+
+    pub unsafe fn to_outer_helper(self, pres_context: bindings::RawGeckoPresContextBorrowed,
                                     parent: ParentStyleContextInfo,
                                     pseudo_ty: structs::CSSPseudoElementType,
                                     pseudo_tag: *mut structs::nsIAtom) -> Arc<ComputedValues> {
-        use gecko_bindings::sugar::ownership::FFIArcHelpers;
         let arc = unsafe {
             let arc: Arc<ComputedValues> = Arc::new(uninitialized());
             bindings::Gecko_ServoStyleContext_Init(&arc.0 as *const _ as *mut _, parent, pres_context,
-                                                   s.into_strong(), pseudo_ty, pseudo_tag);
+                                                   &self, pseudo_ty, pseudo_tag);
+            // We're simulating a move by having C++ do a memcpy and then forgetting
+            // it on this end.
+            forget(self);
             arc
         };
         arc
     }
 }
 
 impl ops::Deref for ComputedValues {
     type Target = ComputedValuesInner;
     fn deref(&self) -> &ComputedValuesInner {
-        unsafe { &*self.0.mSource.mRawPtr }
+        &self.0.mSource
     }
 }
 
 impl ops::DerefMut for ComputedValues {
     fn deref_mut(&mut self) -> &mut ComputedValuesInner {
-        unsafe { &mut *self.0.mSource.mRawPtr }
+        &mut self.0.mSource
     }
 }
 
 impl ComputedValuesInner {
     #[inline]
     pub fn is_display_contents(&self) -> bool {
         self.get_box().clone_display() == longhands::display::computed_value::T::contents
     }
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1719,20 +1719,19 @@ pub extern "C" fn Servo_ComputedValues_G
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleContext_NewContext(values: ServoComputedValuesBorrowed,
                                                 parent: ServoStyleContextBorrowedOrNull,
                                                 pres_context: bindings::RawGeckoPresContextBorrowed,
                                                 pseudo_type: CSSPseudoElementType,
                                                 pseudo_tag: *mut nsIAtom)
                                                        -> ServoStyleContextStrong {
-    let arc = ComputedValuesInner::as_arc(&values);
     unsafe {
-        ComputedValuesInner::to_outer_from_arc(arc.clone_arc(), pres_context, parent,
-                                               pseudo_type, pseudo_tag).into_strong()
+        (*values).clone().to_outer_helper(pres_context, parent,
+                                        pseudo_type, pseudo_tag).into_strong()
     }
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_ComputedValues_SpecifiesAnimationsOrTransitions(values: ServoComputedValuesBorrowed)
                                                                         -> bool {
     let b = values.get_box();
     b.specifies_animations() || b.specifies_transitions()
@@ -3337,27 +3336,27 @@ pub extern "C" fn Servo_GetCustomPropert
     };
 
     computed_value.to_css(unsafe { value.as_mut().unwrap() }).unwrap();
     true
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_GetCustomPropertiesCount(computed_values: ServoComputedValuesBorrowed) -> u32 {
-    match ComputedValuesInner::as_arc(&computed_values).custom_properties() {
+    match computed_values.custom_properties() {
         Some(p) => p.len() as u32,
         None => 0,
     }
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_GetCustomPropertyNameAt(computed_values: ServoComputedValuesBorrowed,
                                                 index: u32,
                                                 name: *mut nsAString) -> bool {
-    let custom_properties = match ComputedValuesInner::as_arc(&computed_values).custom_properties() {
+    let custom_properties = match computed_values.custom_properties() {
         Some(p) => p,
         None => return false,
     };
 
     let property_name = match custom_properties.get_name_at(index) {
         Some(n) => n,
         None => return false,
     };