Bug 1367904 - Part 9: stylo: Make Servo Arc types use ptr to T instead of ptr to ArcInner<T>; r?bholley draft
authorManish Goregaokar <manishearth@gmail.com>
Mon, 17 Jul 2017 11:41:56 -0700
changeset 610217 3497d70b4389cb2ead05ace82510ab8b0ae3c35d
parent 610216 2bbfc7ed05df7d38d9f6e1a1f1f30e79e27095b4
child 610218 4dd4496419e56c14ec0e2ad0b34ffc225287ef88
push id68805
push userbmo:manishearth@gmail.com
push dateTue, 18 Jul 2017 00:30:54 +0000
reviewersbholley
bugs1367904
milestone56.0a1
Bug 1367904 - Part 9: stylo: Make Servo Arc types use ptr to T instead of ptr to ArcInner<T>; r?bholley MozReview-Commit-ID: CQs2Sxd59am
servo/components/servo_arc/lib.rs
servo/components/style/dom.rs
servo/components/style/gecko/restyle_damage.rs
servo/components/style/gecko/wrapper.rs
servo/components/style/gecko_bindings/sugar/ownership.rs
servo/components/style/gecko_bindings/sugar/refptr.rs
servo/components/style/matching.rs
servo/components/style/rule_tree/mod.rs
servo/components/style/sharing/checks.rs
servo/components/style/stylesheets/rule_list.rs
servo/components/style/stylist.rs
servo/ports/geckolib/glue.rs
--- a/servo/components/servo_arc/lib.rs
+++ b/servo/components/servo_arc/lib.rs
@@ -189,16 +189,40 @@ impl<T> Arc<T> {
     pub unsafe fn from_raw(ptr: *const T) -> Self {
         // To find the corresponding pointer to the `ArcInner` we need
         // to subtract the offset of the `data` field from the pointer.
         let ptr = (ptr as *const u8).offset(-offset_of!(ArcInner<T>, data));
         Arc {
             p: NonZeroPtrMut::new(ptr as *mut ArcInner<T>),
         }
     }
+
+    /// Produce a pointer to the data that can be converted back
+    /// to an arc
+    pub fn borrow_arc<'a>(&'a self) -> ArcBorrow<'a, T> {
+        ArcBorrow(&**self)
+    }
+    /// Temporarily converts |self| into a bonafide RawOffsetArc and exposes it to the
+    /// provided callback. The refcount is not modified.
+    #[inline(always)]
+    pub fn with_raw_offset_arc<F, U>(&self, f: F) -> U
+        where F: FnOnce(&RawOffsetArc<T>) -> U
+    {
+        // Synthesize transient Arc, which never touches the refcount of the ArcInner.
+        let transient = unsafe { NoDrop::new(Arc::into_raw_offset(ptr::read(self))) };
+
+        // Expose the transient Arc to the callback, which may clone it if it wants.
+        let result = f(&transient);
+
+        // Forget the transient Arc to leave the refcount untouched.
+        mem::forget(transient);
+
+        // Forward the result.
+        result
+    }
 }
 
 impl<T: ?Sized> Arc<T> {
     #[inline]
     fn inner(&self) -> &ArcInner<T> {
         // This unsafety is ok because while this arc is alive we're guaranteed
         // that the inner pointer is valid. Furthermore, we know that the
         // `ArcInner` structure itself is `Sync` because the inner data is
@@ -712,49 +736,60 @@ impl<H: Eq + 'static, T: Eq + 'static> E
 ///  ---------------------
 /// | RefCount | T (data) | [ArcInner<T>]
 ///  ---------------------
 /// ```
 ///
 /// This means that this is a direct pointer to
 /// its contained data (and can be read from by both C++ and Rust),
 /// but we can also convert it to a "regular" Arc<T> by removing the offset
+#[derive(Eq)]
 pub struct RawOffsetArc<T: 'static> {
     ptr: NonZeroPtrMut<T>,
 }
 
 unsafe impl<T: 'static + Sync + Send> Send for RawOffsetArc<T> {}
 unsafe impl<T: 'static + Sync + Send> Sync for RawOffsetArc<T> {}
 
 impl<T: 'static> Deref for RawOffsetArc<T> {
     type Target = T;
     fn deref(&self) -> &Self::Target {
         unsafe { &*self.ptr.ptr() }
     }
 }
 
 impl<T: 'static> Clone for RawOffsetArc<T> {
     fn clone(&self) -> Self {
-        RawOffsetArc::with_arc(self, |a| Arc::into_raw_offset(a.clone()))
+        Arc::into_raw_offset(self.clone_arc())
     }
 }
 
 impl<T: 'static> Drop for RawOffsetArc<T> {
     fn drop(&mut self) {
         let _ = Arc::from_raw_offset(RawOffsetArc { ptr: self.ptr.clone() });
     }
 }
 
 
 impl<T: fmt::Debug + 'static> fmt::Debug for RawOffsetArc<T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Debug::fmt(&**self, f)
     }
 }
 
+impl<T: PartialEq> PartialEq for RawOffsetArc<T> {
+    fn eq(&self, other: &RawOffsetArc<T>) -> bool {
+        *(*self) == *(*other)
+    }
+
+    fn ne(&self, other: &RawOffsetArc<T>) -> bool {
+        *(*self) != *(*other)
+    }
+}
+
 impl<T: 'static> RawOffsetArc<T> {
     /// Temporarily converts |self| into a bonafide Arc and exposes it to the
     /// provided callback. The refcount is not modified.
     #[inline(always)]
     pub fn with_arc<F, U>(&self, f: F) -> U
         where F: FnOnce(&Arc<T>) -> U
     {
         // Synthesize transient Arc, which never touches the refcount of the ArcInner.
@@ -784,16 +819,27 @@ impl<T: 'static> RawOffsetArc<T> {
             // This may mutate `arc`
             let ret = Arc::make_mut(&mut arc) as *mut _;
             // Store the possibly-mutated arc back inside, after converting
             // it to a RawOffsetArc again
             ptr::write(self, Arc::into_raw_offset(arc));
             &mut *ret
         }
     }
+
+    /// Clone it as an Arc
+    pub fn clone_arc(&self) -> Arc<T> {
+        RawOffsetArc::with_arc(self, |a| a.clone())
+    }
+
+    /// Produce a pointer to the data that can be converted back
+    /// to an arc
+    pub fn borrow_arc<'a>(&'a self) -> ArcBorrow<'a, T> {
+        ArcBorrow(&**self)
+    }
 }
 
 impl<T: 'static> Arc<T> {
     /// Converts an Arc into a RawOffsetArc. This consumes the Arc, so the refcount
     /// is not modified.
     #[inline]
     pub fn into_raw_offset(a: Self) -> RawOffsetArc<T> {
         RawOffsetArc {
@@ -806,16 +852,72 @@ impl<T: 'static> Arc<T> {
     #[inline]
     pub fn from_raw_offset(a: RawOffsetArc<T>) -> Self {
         let ptr = a.ptr.ptr();
         mem::forget(a);
         unsafe { Arc::from_raw(ptr) }
     }
 }
 
+/// A "borrowed Arc". This is a pointer to
+/// a T that is known to have been allocated within an
+/// Arc.
+///
+/// This is equivalent in guarantees to `&Arc<T>`, however it is
+/// a bit more flexible. To obtain an `&Arc<T>` you must have
+/// an Arc<T> instance somewhere pinned down until we're done with it.
+///
+/// However, Gecko hands us refcounted things as pointers to T directly,
+/// so we have to conjure up a temporary Arc on the stack each time. The
+/// same happens for when the object is managed by a RawOffsetArc.
+///
+/// ArcBorrow lets us deal with borrows of known-refcounted objects
+/// without needing to worry about how they're actually stored.
+#[derive(PartialEq, Eq)]
+pub struct ArcBorrow<'a, T: 'a>(&'a T);
+
+impl<'a, T> Copy for ArcBorrow<'a, T> {}
+impl<'a, T> Clone for ArcBorrow<'a, T> {
+    fn clone(&self) -> Self {
+        *self
+    }
+}
+
+impl<'a, T> ArcBorrow<'a, T> {
+    pub fn clone_arc(&self) -> Arc<T> {
+        let arc = unsafe { Arc::from_raw(self.0) };
+        // addref it!
+        mem::forget(arc.clone());
+        arc
+    }
+
+    pub fn with_arc<F, U>(&self, f: F) -> U where F: FnOnce(&Arc<T>) -> U, T: 'static {
+        // Synthesize transient Arc, which never touches the refcount.
+        let transient = unsafe { NoDrop::new(Arc::from_raw(self.0)) };
+
+        // Expose the transient Arc to the callback, which may clone it if it wants.
+        let result = f(&transient);
+
+        // Forget the transient Arc to leave the refcount untouched.
+        // XXXManishearth this can be removed when unions stabilize,
+        // since then NoDrop becomes zero overhead
+        mem::forget(transient);
+
+        // Forward the result.
+        result
+    }
+}
+
+impl<'a, T> Deref for ArcBorrow<'a, T> {
+    type Target = T;
+    fn deref(&self) -> &T {
+        &*self.0
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use std::clone::Clone;
     use std::ops::Drop;
     use std::sync::atomic;
     use std::sync::atomic::Ordering::{Acquire, SeqCst};
     use super::{Arc, HeaderWithLength, ThinArc};
 
--- a/servo/components/style/dom.rs
+++ b/servo/components/style/dom.rs
@@ -25,17 +25,17 @@ use selectors::matching::{ElementSelecto
 use selectors::sink::Push;
 use shared_lock::Locked;
 use smallvec::VecLike;
 use std::fmt;
 #[cfg(feature = "gecko")] use std::collections::HashMap;
 use std::fmt::Debug;
 use std::hash::Hash;
 use std::ops::Deref;
-use stylearc::Arc;
+use stylearc::{Arc, ArcBorrow};
 use stylist::Stylist;
 use thread_state;
 
 pub use style_traits::UnsafeNode;
 
 /// An opaque handle to a node, which, unlike UnsafeNode, cannot be transformed
 /// back into a non-opaque representation. The only safe operation that can be
 /// performed on this node is to compare it to another opaque handle or to another
@@ -355,25 +355,25 @@ pub trait TElement : Eq + PartialEq + De
 
     /// For a given NAC element, return the closest non-NAC ancestor, which is
     /// guaranteed to exist.
     fn closest_non_native_anonymous_ancestor(&self) -> Option<Self> {
         unreachable!("Servo doesn't know about NAC");
     }
 
     /// Get this element's style attribute.
-    fn style_attribute(&self) -> Option<&Arc<Locked<PropertyDeclarationBlock>>>;
+    fn style_attribute(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>;
 
     /// Unset the style attribute's dirty bit.
     /// Servo doesn't need to manage ditry bit for style attribute.
     fn unset_dirty_style_attribute(&self) {
     }
 
     /// Get this element's SMIL override declarations.
-    fn get_smil_override(&self) -> Option<&Arc<Locked<PropertyDeclarationBlock>>> {
+    fn get_smil_override(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>> {
         None
     }
 
     /// Get this element's animation rule by the cascade level.
     fn get_animation_rule_by_cascade(&self,
                                      _cascade_level: CascadeLevel)
                                      -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
         None
--- a/servo/components/style/gecko/restyle_damage.rs
+++ b/servo/components/style/gecko/restyle_damage.rs
@@ -51,17 +51,17 @@ impl GeckoRestyleDamage {
         source: &nsStyleContext,
         new_style: &Arc<ComputedValues>
     ) -> StyleDifference {
         // TODO(emilio): Const-ify this?
         let context = source as *const nsStyleContext as *mut nsStyleContext;
         let mut any_style_changed: bool = false;
         let hint = unsafe {
             bindings::Gecko_CalcStyleDifference(context,
-                                                new_style.as_borrowed_opt().unwrap(),
+                                                new_style.as_borrowed(),
                                                 &mut any_style_changed)
         };
         let change = if any_style_changed { StyleChange::Changed } else { StyleChange::Unchanged };
         StyleDifference::new(GeckoRestyleDamage(hint), change)
     }
 
     /// Returns true if this restyle damage contains all the damage of |other|.
     pub fn contains(self, other: Self) -> bool {
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -59,17 +59,17 @@ use gecko_bindings::structs::{nsIAtom, n
 use gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT;
 use gecko_bindings::structs::ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO;
 use gecko_bindings::structs::ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO;
 use gecko_bindings::structs::ELEMENT_HAS_SNAPSHOT;
 use gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel;
 use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
 use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS;
 use gecko_bindings::structs::nsIDocument_DocumentTheme as DocumentTheme;
-use gecko_bindings::sugar::ownership::{HasArcFFI, HasSimpleFFI};
+use gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI, HasSimpleFFI};
 use logical_geometry::WritingMode;
 use media_queries::Device;
 use properties::{ComputedValues, parse_style_attribute};
 use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
 use properties::animated_properties::{AnimatableLonghand, AnimationValue, AnimationValueMap};
 use properties::animated_properties::TransitionProperty;
 use properties::style_structs::Font;
 use rule_tree::CascadeLevel as ServoCascadeLevel;
@@ -83,17 +83,17 @@ use shared_lock::Locked;
 use std::cell::RefCell;
 use std::collections::HashMap;
 use std::fmt;
 use std::hash::{Hash, Hasher};
 use std::mem;
 use std::ops::DerefMut;
 use std::ptr;
 use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
-use stylearc::Arc;
+use stylearc::{Arc, ArcBorrow, RawOffsetArc};
 use stylesheets::UrlExtraData;
 use stylist::Stylist;
 
 /// A simple wrapper over a non-null Gecko node (`nsINode`) pointer.
 ///
 /// Important: We don't currently refcount the DOM, because the wrapper lifetime
 /// magic guarantees that our LayoutFoo references won't outlive the root, and
 /// we don't mutate any of the references on the Gecko side during restyle.
@@ -849,36 +849,40 @@ impl<'le> TElement for GeckoElement<'le>
         unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) }
     }
 
     fn owner_doc_matches_for_testing(&self, device: &Device) -> bool {
         self.as_node().owner_doc() as *const structs::nsIDocument ==
             device.pres_context().mDocument.raw::<structs::nsIDocument>()
     }
 
-    fn style_attribute(&self) -> Option<&Arc<Locked<PropertyDeclarationBlock>>> {
+    fn style_attribute(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>> {
         if !self.may_have_style_attribute() {
             return None;
         }
 
         let declarations = unsafe { Gecko_GetStyleAttrDeclarationBlock(self.0) };
-        declarations.map_or(None, |s| s.as_arc_opt())
+        let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>>
+            = declarations.and_then(|s| s.as_arc_opt());
+        declarations.map(|s| s.borrow_arc())
     }
 
     fn unset_dirty_style_attribute(&self) {
         if !self.may_have_style_attribute() {
             return;
         }
 
         unsafe { Gecko_UnsetDirtyStyleAttr(self.0) };
     }
 
-    fn get_smil_override(&self) -> Option<&Arc<Locked<PropertyDeclarationBlock>>> {
+    fn get_smil_override(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>> {
         let declarations = unsafe { Gecko_GetSMILOverrideDeclarationBlock(self.0) };
-        declarations.map(|s| s.as_arc_opt()).unwrap_or(None)
+        let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>>
+            = declarations.and_then(|s| s.as_arc_opt());
+        declarations.map(|s| s.borrow_arc())
     }
 
     fn get_animation_rule_by_cascade(&self, cascade_level: ServoCascadeLevel)
                                      -> Option<Arc<Locked<PropertyDeclarationBlock>>> {
         match cascade_level {
             ServoCascadeLevel::Animations => self.get_animation_rule(),
             ServoCascadeLevel::Transitions => self.get_transition_rule(),
             _ => panic!("Unsupported cascade level for getting the animation rule")
@@ -1087,19 +1091,19 @@ impl<'le> TElement for GeckoElement<'le>
                          tasks: UpdateAnimationsTasks) {
         // We have to update animations even if the element has no computed
         // style since it means the element is in a display:none subtree, we
         // should destroy all CSS animations in display:none subtree.
         let computed_data = self.borrow_data();
         let computed_values =
             computed_data.as_ref().map(|d| d.styles.primary());
         let computed_values_opt =
-            computed_values.map(|v| *HasArcFFI::arc_as_borrowed(v));
+            computed_values.map(|v| v.as_borrowed());
         let before_change_values =
-            before_change_style.as_ref().map(|v| *HasArcFFI::arc_as_borrowed(v));
+            before_change_style.as_ref().map(|v| v.as_borrowed());
         unsafe {
             Gecko_UpdateAnimations(self.0,
                                    before_change_values,
                                    computed_values_opt,
                                    tasks.bits());
         }
     }
 
@@ -1171,17 +1175,17 @@ impl<'le> TElement for GeckoElement<'le>
         let mut map = HashMap::with_capacity(collection_length);
         for i in 0..collection_length {
             let (property, raw_end_value) = unsafe {
                 (Gecko_ElementTransitions_PropertyAt(self.0, i as usize).into(),
                  Gecko_ElementTransitions_EndValueAt(self.0, i as usize))
             };
             let end_value = AnimationValue::arc_from_borrowed(&raw_end_value);
             debug_assert!(end_value.is_some());
-            map.insert(property, end_value.unwrap().clone());
+            map.insert(property, end_value.unwrap().clone_arc());
         }
         map
     }
 
     fn might_need_transitions_update(&self,
                                      old_values: Option<&ComputedValues>,
                                      new_values: &ComputedValues) -> bool {
         use properties::longhands::display::computed_value as display;
@@ -1417,27 +1421,29 @@ impl<'le> PresentationalHintsSynthesizer
             if self.get_local_name().as_ptr() == atom!("th").as_ptr() {
                 hints.push(TH_RULE.clone());
             } else if self.get_local_name().as_ptr() == atom!("table").as_ptr() &&
                       self.as_node().owner_doc().mCompatMode == structs::nsCompatibility::eCompatibility_NavQuirks {
                 hints.push(TABLE_COLOR_RULE.clone());
             }
         }
         let declarations = unsafe { Gecko_GetHTMLPresentationAttrDeclarationBlock(self.0) };
-        let declarations = declarations.and_then(|s| s.as_arc_opt());
+        let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>>
+            = declarations.and_then(|s| s.as_arc_opt());
         if let Some(decl) = declarations {
             hints.push(
-                ApplicableDeclarationBlock::from_declarations(Clone::clone(decl), ServoCascadeLevel::PresHints)
+                ApplicableDeclarationBlock::from_declarations(decl.clone_arc(), ServoCascadeLevel::PresHints)
             );
         }
         let declarations = unsafe { Gecko_GetExtraContentStyleDeclarations(self.0) };
-        let declarations = declarations.and_then(|s| s.as_arc_opt());
+        let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>>
+            = declarations.and_then(|s| s.as_arc_opt());
         if let Some(decl) = declarations {
             hints.push(
-                ApplicableDeclarationBlock::from_declarations(Clone::clone(decl), ServoCascadeLevel::PresHints)
+                ApplicableDeclarationBlock::from_declarations(decl.clone_arc(), ServoCascadeLevel::PresHints)
             );
         }
 
         // Support for link, vlink, and alink presentation hints on <body>
         if self.is_link() {
             // Unvisited vs. visited styles are computed up-front based on the
             // visited mode (not the element's actual state).
             let declarations = match visited_handling {
@@ -1447,30 +1453,32 @@ impl<'le> PresentationalHintsSynthesizer
                 },
                 VisitedHandlingMode::AllLinksUnvisited => unsafe {
                     Gecko_GetUnvisitedLinkAttrDeclarationBlock(self.0)
                 },
                 VisitedHandlingMode::RelevantLinkVisited => unsafe {
                     Gecko_GetVisitedLinkAttrDeclarationBlock(self.0)
                 },
             };
-            let declarations = declarations.and_then(|s| s.as_arc_opt());
+            let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> =
+                declarations.and_then(|s| s.as_arc_opt());
             if let Some(decl) = declarations {
                 hints.push(
-                    ApplicableDeclarationBlock::from_declarations(Clone::clone(decl), ServoCascadeLevel::PresHints)
+                    ApplicableDeclarationBlock::from_declarations(decl.clone_arc(), ServoCascadeLevel::PresHints)
                 );
             }
 
             let active = self.get_state().intersects(NonTSPseudoClass::Active.state_flag());
             if active {
                 let declarations = unsafe { Gecko_GetActiveLinkAttrDeclarationBlock(self.0) };
-                let declarations = declarations.and_then(|s| s.as_arc_opt());
+                let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>>
+                    = declarations.and_then(|s| s.as_arc_opt());
                 if let Some(decl) = declarations {
                     hints.push(
-                        ApplicableDeclarationBlock::from_declarations(Clone::clone(decl), ServoCascadeLevel::PresHints)
+                        ApplicableDeclarationBlock::from_declarations(decl.clone_arc(), ServoCascadeLevel::PresHints)
                     );
                 }
             }
         }
 
         // xml:lang has precedence over lang, which can be
         // set by Gecko_GetHTMLPresentationAttrDeclarationBlock
         //
--- a/servo/components/style/gecko_bindings/sugar/ownership.rs
+++ b/servo/components/style/gecko_bindings/sugar/ownership.rs
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Helpers for different FFI pointer kinds that Gecko's FFI layer uses.
 
 use std::marker::PhantomData;
 use std::mem::{forget, transmute};
 use std::ops::{Deref, DerefMut};
 use std::ptr;
-use stylearc::Arc;
+use stylearc::{Arc, RawOffsetArc};
 
 /// Indicates that a given Servo type has a corresponding Gecko FFI type.
 pub unsafe trait HasFFI : Sized + 'static {
     /// The corresponding Gecko type that this rust type represents.
     ///
     /// See the examples in `components/style/gecko/conversions.rs`.
     type FFIType: Sized;
 }
@@ -83,61 +83,61 @@ pub unsafe trait HasArcFFI : HasFFI {
     }
 
     /// Given a (possibly null) borrowed FFI reference, decrements the refcount.
     /// Unsafe since it doesn't consume the backing Arc. Run it only when you
     /// know that a strong reference to the backing Arc is disappearing
     /// (usually on the C++ side) without running the Arc destructor.
     unsafe fn release_opt(ptr: Option<&Self::FFIType>) {
         if let Some(arc) = Self::arc_from_borrowed(&ptr) {
-            let _: Arc<_> = ptr::read(arc as *const Arc<_>);
+            let _: RawOffsetArc<_> = ptr::read(arc as *const RawOffsetArc<_>);
         }
     }
 
     /// Artificially increments the refcount of a borrowed Arc over FFI.
     unsafe fn addref(ptr: &Self::FFIType) {
         forget(Self::as_arc(&ptr).clone())
     }
 
     /// Given a non-null borrowed FFI reference, decrements the refcount.
     /// Unsafe since it doesn't consume the backing Arc. Run it only when you
     /// know that a strong reference to the backing Arc is disappearing
     /// (usually on the C++ side) without running the Arc destructor.
     unsafe fn release(ptr: &Self::FFIType) {
-        let _: Arc<_> = ptr::read(Self::as_arc(&ptr) as *const Arc<_>);
+        let _: RawOffsetArc<_> = ptr::read(Self::as_arc(&ptr) as *const RawOffsetArc<_>);
     }
     #[inline]
     /// Converts a borrowed FFI reference to a borrowed Arc.
     ///
     /// &GeckoType -> &Arc<ServoType>
-    fn as_arc<'a>(ptr: &'a &Self::FFIType) -> &'a Arc<Self> {
+    fn as_arc<'a>(ptr: &'a &Self::FFIType) -> &'a RawOffsetArc<Self> {
         debug_assert!(!(ptr as *const _).is_null());
         unsafe {
-            transmute::<&&Self::FFIType, &Arc<Self>>(ptr)
+            transmute::<&&Self::FFIType, &RawOffsetArc<Self>>(ptr)
         }
     }
 
     #[inline]
     /// Converts a borrowed Arc to a borrowed FFI reference.
     ///
     /// &Arc<ServoType> -> &GeckoType
-    fn arc_as_borrowed<'a>(arc: &'a Arc<Self>) -> &'a &Self::FFIType {
+    fn arc_as_borrowed<'a>(arc: &'a RawOffsetArc<Self>) -> &'a &Self::FFIType {
         unsafe {
-            transmute::<&Arc<Self>, &&Self::FFIType>(arc)
+            transmute::<&RawOffsetArc<Self>, &&Self::FFIType>(arc)
         }
     }
 
     #[inline]
     /// Converts a borrowed nullable FFI reference to a borrowed Arc.
     ///
     /// &GeckoType -> &Arc<ServoType>
-    fn arc_from_borrowed<'a>(ptr: &'a Option<&Self::FFIType>) -> Option<&'a Arc<Self>> {
+    fn arc_from_borrowed<'a>(ptr: &'a Option<&Self::FFIType>) -> Option<&'a RawOffsetArc<Self>> {
         unsafe {
             if let Some(ref reference) = *ptr {
-                Some(transmute::<&&Self::FFIType, &Arc<_>>(reference))
+                Some(transmute::<&&Self::FFIType, &RawOffsetArc<_>>(reference))
             } else {
                 None
             }
         }
     }
 }
 
 #[repr(C)]
@@ -160,46 +160,46 @@ impl<GeckoType> Strong<GeckoType> {
 
     #[inline]
     /// Given a non-null strong FFI reference, converts it into a servo-side
     /// Arc.
     ///
     /// Panics on null.
     ///
     /// Strong<GeckoType> -> Arc<ServoType>
-    pub fn into_arc<ServoType>(self) -> Arc<ServoType>
+    pub fn into_arc<ServoType>(self) -> RawOffsetArc<ServoType>
         where ServoType: HasArcFFI<FFIType = GeckoType>,
     {
         self.into_arc_opt().unwrap()
     }
 
     #[inline]
     /// Given a strong FFI reference,
     /// converts it into a servo-side Arc
     /// Returns None on null.
     ///
     /// Strong<GeckoType> -> Arc<ServoType>
-    pub fn into_arc_opt<ServoType>(self) -> Option<Arc<ServoType>>
+    pub fn into_arc_opt<ServoType>(self) -> Option<RawOffsetArc<ServoType>>
         where ServoType: HasArcFFI<FFIType = GeckoType>,
     {
         if self.is_null() {
             None
         } else {
             unsafe { Some(transmute(self)) }
         }
     }
 
     #[inline]
     /// Given a reference to a strong FFI reference, converts it to a reference
     /// to a servo-side Arc.
     ///
     /// Returns None on null.
     ///
     /// Strong<GeckoType> -> Arc<ServoType>
-    pub fn as_arc_opt<ServoType>(&self) -> Option<&Arc<ServoType>>
+    pub fn as_arc_opt<ServoType>(&self) -> Option<&RawOffsetArc<ServoType>>
         where ServoType: HasArcFFI<FFIType = GeckoType>,
     {
         if self.is_null() {
             None
         } else {
             unsafe { Some(transmute(self)) }
         }
     }
@@ -217,39 +217,49 @@ pub unsafe trait FFIArcHelpers {
     /// The Rust FFI type that we're implementing methods for.
     type Inner: HasArcFFI;
 
     /// Converts an Arc into a strong FFI reference.
     ///
     /// Arc<ServoType> -> Strong<GeckoType>
     fn into_strong(self) -> Strong<<Self::Inner as HasFFI>::FFIType>;
 
-    /// Produces a (nullable) borrowed FFI reference by borrowing an Arc.
+    /// Produces a borrowed FFI reference by borrowing an Arc.
     ///
-    /// &Arc<ServoType> -> Option<&GeckoType>
-    ///
-    /// FIXME(emilio): What's the point of the nullability? Arc should be
-    /// non-null, right?
+    /// &Arc<ServoType> -> &GeckoType
     ///
     /// Then the `arc_as_borrowed` method can go away.
-    fn as_borrowed_opt(&self) -> Option<&<Self::Inner as HasFFI>::FFIType>;
+    fn as_borrowed(&self) -> &<Self::Inner as HasFFI>::FFIType;
 }
 
-unsafe impl<T: HasArcFFI> FFIArcHelpers for Arc<T> {
+unsafe impl<T: HasArcFFI> FFIArcHelpers for RawOffsetArc<T> {
     type Inner = T;
 
     #[inline]
     fn into_strong(self) -> Strong<T::FFIType> {
         unsafe { transmute(self) }
     }
 
     #[inline]
-    fn as_borrowed_opt(&self) -> Option<&T::FFIType> {
-        let borrowedptr = self as *const Arc<T> as *const Option<&T::FFIType>;
-        unsafe { ptr::read(borrowedptr) }
+    fn as_borrowed(&self) -> &T::FFIType {
+        unsafe { &*(&**self as *const T as *const T::FFIType) }
+    }
+}
+
+unsafe impl<T: HasArcFFI> FFIArcHelpers for Arc<T> {
+    type Inner = T;
+
+    #[inline]
+    fn into_strong(self) -> Strong<T::FFIType> {
+        Arc::into_raw_offset(self).into_strong()
+    }
+
+    #[inline]
+    fn as_borrowed(&self) -> &T::FFIType {
+        unsafe { &*(&**self as *const T as *const T::FFIType) }
     }
 }
 
 #[repr(C)]
 #[derive(Debug)]
 /// Gecko-FFI-safe owned pointer.
 ///
 /// Cannot be null, and leaks on drop, so needs to be converted into a rust-side
--- a/servo/components/style/gecko_bindings/sugar/refptr.rs
+++ b/servo/components/style/gecko_bindings/sugar/refptr.rs
@@ -206,17 +206,17 @@ impl<T: RefCounted> structs::RefPtr<T> {
         *self = other.forget();
     }
 }
 
 impl<T> structs::RefPtr<T> {
     /// Sets the contents to an Arc<T>
     /// will leak existing contents
     pub fn set_arc_leaky<U>(&mut self, other: Arc<U>) where U: HasArcFFI<FFIType = T> {
-        *self = unsafe { mem::transmute(other) }; // Arc::into_raw is unstable :(
+        *self = unsafe { mem::transmute(Arc::into_raw_offset(other)) };
     }
 }
 
 impl<T: RefCounted> Drop for RefPtr<T> {
     fn drop(&mut self) {
         unsafe { self.release() }
     }
 }
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -13,17 +13,17 @@ use dom::TElement;
 use invalidation::element::restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS};
 use invalidation::element::restyle_hints::{RESTYLE_SMIL, RESTYLE_STYLE_ATTRIBUTE};
 use invalidation::element::restyle_hints::RestyleHint;
 use properties::ComputedValues;
 use properties::longhands::display::computed_value as display;
 use rule_tree::{CascadeLevel, StrongRuleNode};
 use selector_parser::{PseudoElement, RestyleDamage};
 use selectors::matching::ElementSelectorFlags;
-use stylearc::Arc;
+use stylearc::{Arc, ArcBorrow};
 
 /// Represents the result of comparing an element's old and new style.
 pub struct StyleDifference {
     /// The resulting damage.
     pub damage: RestyleDamage,
 
     /// Whether any styles changed.
     pub change: StyleChange,
@@ -685,17 +685,17 @@ pub trait MatchMethods : TElement {
             };
 
         let primary_rules = match primary_rules {
             Some(r) => r,
             None => return false,
         };
 
         let replace_rule_node = |level: CascadeLevel,
-                                 pdb: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
+                                 pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
                                  path: &mut StrongRuleNode| -> bool {
             let new_node = stylist.rule_tree()
                                   .update_rule_at_level(level, pdb, path, guards);
             match new_node {
                 Some(n) => {
                     *path = n;
                     level.is_important()
                 },
@@ -732,17 +732,17 @@ pub trait MatchMethods : TElement {
                                   self.get_smil_override(),
                                   primary_rules);
             }
 
             let replace_rule_node_for_animation = |level: CascadeLevel,
                                                    primary_rules: &mut StrongRuleNode| {
                 let animation_rule = self.get_animation_rule_by_cascade(level);
                 replace_rule_node(level,
-                                  animation_rule.as_ref(),
+                                  animation_rule.as_ref().map(|a| a.borrow_arc()),
                                   primary_rules);
             };
 
             // Apply Transition rules and Animation rules if the corresponding restyle hint
             // is contained.
             if replacements.contains(RESTYLE_CSS_TRANSITIONS) {
                 replace_rule_node_for_animation(CascadeLevel::Transitions,
                                                 primary_rules);
--- a/servo/components/style/rule_tree/mod.rs
+++ b/servo/components/style/rule_tree/mod.rs
@@ -11,17 +11,17 @@ use applicable_declarations::ApplicableD
 use heapsize::HeapSizeOf;
 use properties::{Importance, LonghandIdSet, PropertyDeclarationBlock};
 use shared_lock::{Locked, StylesheetGuards, SharedRwLockReadGuard};
 use smallvec::SmallVec;
 use std::io::{self, Write};
 use std::mem;
 use std::ptr;
 use std::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
-use stylearc::{Arc, NonZeroPtrMut};
+use stylearc::{Arc, ArcBorrow, NonZeroPtrMut};
 use stylesheets::StyleRule;
 use thread_state;
 
 /// The rule tree, the structure servo uses to preserve the results of selector
 /// matching.
 ///
 /// This is organized as a tree of rules. When a node matches a set of rules,
 /// they're inserted in order in the tree, starting with the less specific one.
@@ -303,17 +303,17 @@ impl RuleTree {
     }
 
     /// Replaces a rule in a given level (if present) for another rule.
     ///
     /// Returns the resulting node that represents the new path, or None if
     /// the old path is still valid.
     pub fn update_rule_at_level(&self,
                                 level: CascadeLevel,
-                                pdb: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
+                                pdb: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
                                 path: &StrongRuleNode,
                                 guards: &StylesheetGuards)
                                 -> Option<StrongRuleNode> {
         debug_assert!(level.is_unique_per_element());
         // TODO(emilio): Being smarter with lifetimes we could avoid a bit of
         // the refcount churn.
         let mut current = path.clone();
 
@@ -342,17 +342,17 @@ impl RuleTree {
                 // TODO(emilio): Another potential optimization is the one where
                 // we can just replace the rule at that level for `pdb`, and
                 // then we don't need to re-create the children, and `path` is
                 // also equally valid. This is less likely, and would require an
                 // in-place mutation of the source, which is, at best, fiddly,
                 // so let's skip it for now.
                 let is_here_already = match &current.get().source {
                     &StyleSource::Declarations(ref already_here) => {
-                        Arc::ptr_eq(pdb, already_here)
+                        pdb.with_arc(|arc| Arc::ptr_eq(arc, already_here))
                     },
                     _ => unreachable!("Replacing non-declarations style?"),
                 };
 
                 if is_here_already {
                     debug!("Picking the fast path in rule replacement");
                     return None;
                 }
@@ -366,23 +366,23 @@ impl RuleTree {
         //
         // These optimizations are likely to be important, because the levels
         // where replacements apply (style and animations) tend to trigger
         // pretty bad styling cases already.
         if let Some(pdb) = pdb {
             if level.is_important() {
                 if pdb.read_with(level.guard(guards)).any_important() {
                     current = current.ensure_child(self.root.downgrade(),
-                                                   StyleSource::Declarations(pdb.clone()),
+                                                   StyleSource::Declarations(pdb.clone_arc()),
                                                    level);
                 }
             } else {
                 if pdb.read_with(level.guard(guards)).any_normal() {
                     current = current.ensure_child(self.root.downgrade(),
-                                                   StyleSource::Declarations(pdb.clone()),
+                                                   StyleSource::Declarations(pdb.clone_arc()),
                                                    level);
                 }
             }
         }
 
         // Now the rule is in the relevant place, push the children as
         // necessary.
         let rule =
--- a/servo/components/style/sharing/checks.rs
+++ b/servo/components/style/sharing/checks.rs
@@ -38,17 +38,17 @@ pub fn have_same_style_attribute<E>(
     target: &mut StyleSharingTarget<E>,
     candidate: &mut StyleSharingCandidate<E>
 ) -> bool
     where E: TElement,
 {
     match (target.style_attribute(), candidate.style_attribute()) {
         (None, None) => true,
         (Some(_), None) | (None, Some(_)) => false,
-        (Some(a), Some(b)) => Arc::ptr_eq(a, b)
+        (Some(a), Some(b)) => &*a as *const _ == &*b as *const _
     }
 }
 
 /// Whether two elements have the same same presentational attributes.
 pub fn have_same_presentational_hints<E>(
     target: &mut StyleSharingTarget<E>,
     candidate: &mut StyleSharingCandidate<E>
 ) -> bool
--- a/servo/components/style/stylesheets/rule_list.rs
+++ b/servo/components/style/stylesheets/rule_list.rs
@@ -1,16 +1,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 CSS rules.
 
 use shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard};
-use stylearc::Arc;
+use stylearc::{Arc, RawOffsetArc};
 use stylesheets::{CssRule, RulesMutateError};
 use stylesheets::loader::StylesheetLoader;
 use stylesheets::memory::{MallocSizeOfFn, MallocSizeOfWithGuard};
 use stylesheets::rule_parser::State;
 use stylesheets::stylesheet::StylesheetContents;
 
 /// A list of CSS rules.
 #[derive(Debug)]
@@ -104,17 +104,17 @@ pub trait CssRulesHelpers {
                    rule: &str,
                    parent_stylesheet_contents: &StylesheetContents,
                    index: usize,
                    nested: bool,
                    loader: Option<&StylesheetLoader>)
                    -> Result<CssRule, RulesMutateError>;
 }
 
-impl CssRulesHelpers for Arc<Locked<CssRules>> {
+impl CssRulesHelpers for RawOffsetArc<Locked<CssRules>> {
     fn insert_rule(&self,
                    lock: &SharedRwLock,
                    rule: &str,
                    parent_stylesheet_contents: &StylesheetContents,
                    index: usize,
                    nested: bool,
                    loader: Option<&StylesheetLoader>)
                    -> Result<CssRule, RulesMutateError> {
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -32,17 +32,17 @@ use selectors::parser::{SelectorIter, Se
 use selectors::sink::Push;
 use selectors::visitor::SelectorVisitor;
 use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
 use smallvec::VecLike;
 use std::fmt::Debug;
 #[cfg(feature = "servo")]
 use std::marker::PhantomData;
 use style_traits::viewport::ViewportConstraints;
-use stylearc::Arc;
+use stylearc::{Arc, ArcBorrow};
 #[cfg(feature = "gecko")]
 use stylesheets::{CounterStyleRule, FontFaceRule};
 use stylesheets::{CssRule, StyleRule};
 use stylesheets::{StylesheetInDocument, Origin, UserAgentStylesheets};
 use stylesheets::keyframes_rule::KeyframesAnimation;
 use stylesheets::viewport_rule::{self, MaybeNew, ViewportRule};
 use thread_state;
 
@@ -1098,18 +1098,18 @@ impl Stylist {
     /// This corresponds to `ElementRuleCollector` in WebKit.
     ///
     /// The `StyleRelations` recorded in `MatchingContext` indicate hints about
     /// which kind of rules have matched.
     pub fn push_applicable_declarations<E, V, F>(
                                         &self,
                                         element: &E,
                                         pseudo_element: Option<&PseudoElement>,
-                                        style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
-                                        smil_override: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
+                                        style_attribute: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
+                                        smil_override: Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>,
                                         animation_rules: AnimationRules,
                                         rule_inclusion: RuleInclusion,
                                         applicable_declarations: &mut V,
                                         context: &mut MatchingContext,
                                         flags_setter: &mut F)
         where E: TElement,
               V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> + Debug,
               F: FnMut(&E, ElementSelectorFlags),
@@ -1203,26 +1203,26 @@ impl Stylist {
             debug!("skipping author normal rules");
         }
 
         if !only_default_rules {
             // Step 4: Normal style attributes.
             if let Some(sa) = style_attribute {
                 Push::push(
                     applicable_declarations,
-                    ApplicableDeclarationBlock::from_declarations(sa.clone(),
+                    ApplicableDeclarationBlock::from_declarations(sa.clone_arc(),
                                                                   CascadeLevel::StyleAttributeNormal));
             }
 
             // Step 5: SMIL override.
             // Declarations from SVG SMIL animation elements.
             if let Some(so) = smil_override {
                 Push::push(
                     applicable_declarations,
-                    ApplicableDeclarationBlock::from_declarations(so.clone(),
+                    ApplicableDeclarationBlock::from_declarations(so.clone_arc(),
                                                                   CascadeLevel::SMILOverride));
             }
 
             // Step 6: Animations.
             // The animations sheet (CSS animations, script-generated animations,
             // and CSS transitions that are no longer tied to CSS markup)
             if let Some(anim) = animation_rules.0 {
                 Push::push(
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -85,28 +85,28 @@ use style::gecko_bindings::structs::nsre
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI, HasBoxFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
 use style::gecko_bindings::sugar::refptr::RefPtr;
 use style::gecko_properties::{self, style_structs};
 use style::invalidation::element::restyle_hints::{self, RestyleHint};
 use style::media_queries::{MediaList, parse_media_query_list};
 use style::parallel;
 use style::parser::ParserContext;
-use style::properties::{ComputedValues, Importance, SourcePropertyDeclaration};
+use style::properties::{ComputedValues, ComputedValuesInner, Importance, SourcePropertyDeclaration};
 use style::properties::{LonghandIdSet, PropertyDeclaration, PropertyDeclarationBlock, PropertyId, StyleBuilder};
 use style::properties::SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP;
 use style::properties::animated_properties::{Animatable, AnimatableLonghand, AnimationValue};
 use style::properties::parse_one_declaration_into;
 use style::rule_tree::StyleSource;
 use style::selector_parser::PseudoElementCascadeType;
 use style::sequential;
 use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
 use style::string_cache::Atom;
 use style::style_adjuster::StyleAdjuster;
-use style::stylearc::Arc;
+use style::stylearc::{Arc, RawOffsetArc};
 use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule};
 use style::stylesheets::{ImportRule, KeyframesRule, MallocSizeOfWithGuard, MediaRule};
 use style::stylesheets::{NamespaceRule, Origin, PageRule, StyleRule, SupportsRule};
 use style::stylesheets::StylesheetContents;
 use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
 use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
 use style::stylesheets::supports_rule::parse_condition_or_declaration;
 use style::stylist::RuleInclusion;
@@ -424,17 +424,17 @@ pub extern "C" fn Servo_AnimationCompose
     // If either of the segment endpoints are null, get the underlying value to
     // use from the current value in the values map (set by a lower-priority
     // effect), or, if there is no current value, look up the cached base value
     // for this property.
     let underlying_value = if need_underlying_value {
         let previous_composed_value = value_map.get(&property).cloned();
         previous_composed_value.or_else(|| {
             let raw_base_style = unsafe { Gecko_AnimationGetBaseStyle(base_values, css_property) };
-            AnimationValue::arc_from_borrowed(&raw_base_style).map(|v| v.as_ref()).cloned()
+            AnimationValue::arc_from_borrowed(&raw_base_style).map(|v| &**v).cloned()
         })
     } else {
         None
     };
 
     if need_underlying_value && underlying_value.is_none() {
         warn!("Underlying value should be valid when we expect to use it");
         return;
@@ -454,17 +454,17 @@ pub extern "C" fn Servo_AnimationCompose
         raw_to_value = unsafe { &*segment.mToValue.mServo.mRawPtr };
         Some(AnimationValue::as_arc(&raw_to_value))
     } else {
         None
     };
 
     // Composite with underlying value.
     // A return value of None means, "Just use keyframe_value as-is."
-    let composite_endpoint = |keyframe_value: Option<&Arc<AnimationValue>>,
+    let composite_endpoint = |keyframe_value: Option<&RawOffsetArc<AnimationValue>>,
                               composite_op: CompositeOperation| -> Option<AnimationValue> {
         match keyframe_value {
             Some(keyframe_value) => {
                 match composite_op {
                     CompositeOperation::Add => {
                         debug_assert!(need_underlying_value,
                                       "Should have detected we need an underlying value");
                         underlying_value.as_ref().unwrap().add(keyframe_value).ok()
@@ -493,25 +493,25 @@ pub extern "C" fn Servo_AnimationCompose
                   "Should have a suitable to value to use");
 
     // Apply iteration composite behavior.
     if iteration_composite == IterationCompositeOperation::Accumulate &&
        computed_timing.mCurrentIteration > 0 {
         let raw_last_value;
         let last_value = if !last_segment.mToValue.mServo.mRawPtr.is_null() {
             raw_last_value = unsafe { &*last_segment.mToValue.mServo.mRawPtr };
-            AnimationValue::as_arc(&raw_last_value).as_ref()
+            &*AnimationValue::as_arc(&raw_last_value)
         } else {
             debug_assert!(need_underlying_value,
                           "Should have detected we need an underlying value");
             underlying_value.as_ref().unwrap()
         };
 
         // As with composite_endpoint, a return value of None means, "Use keyframe_value as-is."
-        let apply_iteration_composite = |keyframe_value: Option<&Arc<AnimationValue>>,
+        let apply_iteration_composite = |keyframe_value: Option<&RawOffsetArc<AnimationValue>>,
                                          composited_value: Option<AnimationValue>|
                                         -> Option<AnimationValue> {
             let count = computed_timing.mCurrentIteration;
             match composited_value {
                 Some(endpoint) => last_value.accumulate(&endpoint, count)
                                             .ok()
                                             .or(Some(endpoint)),
                 None => last_value.accumulate(keyframe_value.unwrap(), count)
@@ -1239,17 +1239,17 @@ pub extern "C" fn Servo_StyleRule_GetSty
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleRule_SetStyle(rule: RawServoStyleRuleBorrowed,
                                            declarations: RawServoDeclarationBlockBorrowed) {
     let declarations = Locked::<PropertyDeclarationBlock>::as_arc(&declarations);
     write_locked_arc(rule, |rule: &mut StyleRule| {
-        rule.block = declarations.clone();
+        rule.block = declarations.clone_arc();
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleRule_GetSelectorText(rule: RawServoStyleRuleBorrowed, result: *mut nsAString) {
     read_locked_arc(rule, |rule: &StyleRule| {
         rule.selectors.to_css(unsafe { result.as_mut().unwrap() }).unwrap();
     })
@@ -1377,17 +1377,17 @@ pub extern "C" fn Servo_Keyframe_GetStyl
     read_locked_arc(keyframe, |keyframe: &Keyframe| keyframe.block.clone().into_strong())
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_Keyframe_SetStyle(keyframe: RawServoKeyframeBorrowed,
                                           declarations: RawServoDeclarationBlockBorrowed) {
     let declarations = Locked::<PropertyDeclarationBlock>::as_arc(&declarations);
     write_locked_arc(keyframe, |keyframe: &mut Keyframe| {
-        keyframe.block = declarations.clone();
+        keyframe.block = declarations.clone_arc();
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_KeyframesRule_GetName(rule: RawServoKeyframesRuleBorrowed) -> *mut nsIAtom {
     read_locked_arc(rule, |rule: &KeyframesRule| rule.name.as_atom().as_ptr())
 }
 
@@ -1475,17 +1475,17 @@ pub extern "C" fn Servo_PageRule_GetStyl
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_PageRule_SetStyle(rule: RawServoPageRuleBorrowed,
                                            declarations: RawServoDeclarationBlockBorrowed) {
     let declarations = Locked::<PropertyDeclarationBlock>::as_arc(&declarations);
     write_locked_arc(rule, |rule: &mut PageRule| {
-        rule.block = declarations.clone();
+        rule.block = declarations.clone_arc();
     })
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_SupportsRule_GetConditionText(rule: RawServoSupportsRuleBorrowed,
                                                       result: *mut nsAString) {
     read_locked_arc(rule, |rule: &SupportsRule| {
         rule.condition.to_css(unsafe { result.as_mut().unwrap() }).unwrap();
@@ -1554,17 +1554,17 @@ pub extern "C" fn Servo_ResolvePseudoSty
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let guard = global_style_data.shared_lock.read();
     let style = get_pseudo_style(
         &guard,
         element,
         &pseudo,
         RuleInclusion::All,
         &data.styles,
-        ComputedValues::arc_from_borrowed(&inherited_style),
+        ComputedValues::arc_from_borrowed(&inherited_style).map(|x| &***x),
         &*doc_data,
         is_probe
     );
 
     match style {
         Some(s) => s.into_strong(),
         None => {
             debug_assert!(is_probe);
@@ -1579,17 +1579,17 @@ pub extern "C" fn Servo_SetExplicitStyle
 {
     let element = GeckoElement(element);
     let style = ComputedValues::as_arc(&style);
     debug!("Servo_SetExplicitStyle: {:?}", element);
     // We only support this API for initial styling. There's no reason it couldn't
     // work for other things, we just haven't had a reason to do so.
     debug_assert!(element.get_data().is_none());
     let mut data = unsafe { element.ensure_data() };
-    data.styles.primary = Some(style.clone());
+    data.styles.primary = Some(style.clone_arc());
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_HasAuthorSpecifiedRules(element: RawGeckoElementBorrowed,
                                                 rule_type_mask: u32,
                                                 author_colors_allowed: bool)
     -> bool
 {
@@ -1608,17 +1608,17 @@ pub extern "C" fn Servo_HasAuthorSpecifi
 }
 
 fn get_pseudo_style(
     guard: &SharedRwLockReadGuard,
     element: GeckoElement,
     pseudo: &PseudoElement,
     rule_inclusion: RuleInclusion,
     styles: &ElementStyles,
-    inherited_styles: Option<&Arc<ComputedValues>>,
+    inherited_styles: Option<&ComputedValuesInner>,
     doc_data: &PerDocumentStyleDataImpl,
     is_probe: bool,
 ) -> Option<Arc<ComputedValues>> {
     let style = match pseudo.cascade_type() {
         PseudoElementCascadeType::Eager => {
             match *pseudo {
                 PseudoElement::FirstLetter => {
                     styles.pseudos.get(&pseudo).and_then(|pseudo_styles| {
@@ -1637,27 +1637,27 @@ fn get_pseudo_style(
                                 &inputs,
                                 &guards,
                                 inherited_styles,
                                 &metrics)
                     })
                 },
                 _ => {
                     debug_assert!(inherited_styles.is_none() ||
-                                  ptr::eq(&**inherited_styles.unwrap(),
-                                          &**styles.primary()));
+                                  ptr::eq(&*inherited_styles.unwrap(),
+                                          &***styles.primary()));
                     styles.pseudos.get(&pseudo).cloned()
                 },
             }
         }
         PseudoElementCascadeType::Precomputed => unreachable!("No anonymous boxes"),
         PseudoElementCascadeType::Lazy => {
             debug_assert!(inherited_styles.is_none() ||
-                          ptr::eq(&**inherited_styles.unwrap(),
-                                  &**styles.primary()));
+                          ptr::eq(&*inherited_styles.unwrap(),
+                                  &***styles.primary()));
             let base = if pseudo.inherits_from_default_values() {
                 doc_data.default_computed_values()
             } else {
                 styles.primary()
             };
             let guards = StylesheetGuards::same(guard);
             let metrics = get_metrics_provider_for_product();
             doc_data.stylist
@@ -1756,22 +1756,24 @@ pub extern "C" fn Servo_ComputedValues_E
 #[no_mangle]
 pub extern "C" fn Servo_ComputedValues_GetStyleRuleList(values: ServoComputedValuesBorrowed,
                                                         rules: RawGeckoServoStyleRuleListBorrowedMut) {
     let values = ComputedValues::as_arc(&values);
     if let Some(ref rule_node) = values.rules {
         let mut result = vec![];
         for node in rule_node.self_and_ancestors() {
             if let &StyleSource::Style(ref rule) = node.style_source() {
-                result.push(Locked::<StyleRule>::arc_as_borrowed(&rule));
+                result.push(rule);
             }
         }
         unsafe { rules.set_len(result.len() as u32) };
-        for (&src, dest) in result.into_iter().zip(rules.iter_mut()) {
-            *dest = src;
+        for (ref src, ref mut dest) in result.into_iter().zip(rules.iter_mut()) {
+            src.with_raw_offset_arc(|arc| {
+                **dest = *Locked::<StyleRule>::arc_as_borrowed(arc);
+            })
         }
     }
 }
 
 /// See the comment in `Device` to see why it's ok to pass an owned reference to
 /// the pres context (hint: the context outlives the StyleSet, that holds the
 /// device alive).
 #[no_mangle]
@@ -3268,17 +3270,17 @@ pub extern "C" fn Servo_StyleSet_Resolve
         Some(parent) => &parent.inner,
         None => doc_data.default_computed_values(),
     };
 
     let declarations = Locked::<PropertyDeclarationBlock>::as_arc(&declarations);
 
     doc_data.stylist.compute_for_declarations(&guards,
                                               parent_style,
-                                              declarations.clone()).into_strong()
+                                              declarations.clone_arc()).into_strong()
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_StyleSet_MightHaveAttributeDependency(
     raw_data: RawServoStyleSetBorrowed,
     element: RawGeckoElementBorrowed,
     local_name: *mut nsIAtom,
 ) -> bool {