Bug 1549559 - Add bindings for ArcSlice and ThinArc, and use them to reduce copies of SVG path data. r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 09 May 2019 12:43:19 +0000
changeset 473215 51a7edbe0a9e8f63e45d59d6a9a18bc0e700a199
parent 473214 dc57611c4e71768dc1f0e226e4b891634ec71b78
child 473216 c7395b64e19e763c7d1612f4d898e53a2a1f9a7c
push id113071
push usernbeleuzu@mozilla.com
push dateThu, 09 May 2019 22:20:39 +0000
treeherdermozilla-inbound@38895d59d3d0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1549559, 1549593
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 1549559 - Add bindings for ArcSlice and ThinArc, and use them to reduce copies of SVG path data. r=heycam As I said over bug 1549593, the eventual goal is to use ArcSlice in all inherited properties. But this seemed like a good first candidate that doesn't require me to move around a lot more code, since we were already using cbindgen for the path commands. Differential Revision: https://phabricator.services.mozilla.com/D30134
dom/svg/SVGPathData.cpp
dom/svg/SVGPathData.h
layout/style/GeckoBindings.cpp
layout/style/GeckoBindings.h
layout/style/ServoBindings.toml
layout/style/ServoStyleConstsForwards.h
layout/style/ServoStyleConstsInlines.h
layout/style/moz.build
layout/style/nsStyleStruct.h
servo/components/servo_arc/lib.rs
servo/components/style/gecko/conversions.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhands/svg.mako.rs
servo/components/style/values/specified/svg_path.rs
servo/ports/geckolib/cbindgen.toml
--- a/dom/svg/SVGPathData.cpp
+++ b/dom/svg/SVGPathData.cpp
@@ -525,17 +525,17 @@ already_AddRefed<Path> SVGPathData::Buil
   return BuildPath(builder, NS_STYLE_STROKE_LINECAP_BUTT, 0);
 }
 
 // We could simplify this function because this is only used by CSS motion path
 // and clip-path, which don't render the SVG Path. i.e. The returned path is
 // used as a reference.
 /* static */
 already_AddRefed<Path> SVGPathData::BuildPath(
-    const nsTArray<StylePathCommand>& aPath, PathBuilder* aBuilder,
+    Span<const StylePathCommand> aPath, PathBuilder* aBuilder,
     uint8_t aStrokeLineCap, Float aStrokeWidth, float aZoomFactor) {
   if (aPath.IsEmpty() || !aPath[0].IsMoveTo()) {
     return nullptr;  // paths without an initial moveto are invalid
   }
 
   auto toGfxPoint = [](const StyleCoordPair& aPair) {
     return Point(aPair._0, aPair._1);
   };
--- a/dom/svg/SVGPathData.h
+++ b/dom/svg/SVGPathData.h
@@ -156,22 +156,22 @@ class SVGPathData {
    * See the comment for that function for more info on that.
    */
   already_AddRefed<Path> BuildPathForMeasuring() const;
 
   already_AddRefed<Path> BuildPath(PathBuilder* aBuilder,
                                    uint8_t aStrokeLineCap,
                                    Float aStrokeWidth) const;
   /**
-   * This function tries to build the path by an array of StylePathCommand,
+   * This function tries to build the path from an array of StylePathCommand,
    * which is generated by cbindgen from Rust (see ServoStyleConsts.h).
    * Basically, this is a variant of the above BuildPath() functions.
    */
   static already_AddRefed<Path> BuildPath(
-      const nsTArray<StylePathCommand>& aPath, PathBuilder* aBuilder,
+      Span<const StylePathCommand> aPath, PathBuilder* aBuilder,
       uint8_t aStrokeLineCap, Float aStrokeWidth, float aZoomFactor = 1.0);
 
   const_iterator begin() const { return mData.Elements(); }
   const_iterator end() const { return mData.Elements() + mData.Length(); }
 
   // memory reporting methods
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -1559,19 +1559,21 @@ void Gecko_StyleShapeSource_SetURLValue(
                                         URLValue* aURL) {
   aShape->SetURL(*aURL);
 }
 
 void Gecko_NewShapeImage(StyleShapeSource* aShape) {
   aShape->SetShapeImage(MakeUnique<nsStyleImage>());
 }
 
-void Gecko_NewStyleSVGPath(StyleShapeSource* aShape) {
+void Gecko_SetToSVGPath(StyleShapeSource* aShape,
+                        StyleForgottenArcSlicePtr<StylePathCommand> aCommands,
+                        StyleFillRule aFill) {
   MOZ_ASSERT(aShape);
-  aShape->SetPath(MakeUnique<StyleSVGPath>());
+  aShape->SetPath(MakeUnique<StyleSVGPath>(aCommands, aFill));
 }
 
 void Gecko_SetStyleMotion(UniquePtr<StyleMotion>* aMotion,
                           StyleMotion* aValue) {
   MOZ_ASSERT(aMotion);
   aMotion->reset(aValue);
 }
 
--- a/layout/style/GeckoBindings.h
+++ b/layout/style/GeckoBindings.h
@@ -527,17 +527,20 @@ void Gecko_CopyShapeSourceFrom(mozilla::
 
 void Gecko_DestroyShapeSource(mozilla::StyleShapeSource* shape);
 
 void Gecko_NewShapeImage(mozilla::StyleShapeSource* shape);
 
 void Gecko_StyleShapeSource_SetURLValue(mozilla::StyleShapeSource* shape,
                                         mozilla::css::URLValue* uri);
 
-void Gecko_NewStyleSVGPath(mozilla::StyleShapeSource* shape);
+void Gecko_SetToSVGPath(
+    mozilla::StyleShapeSource* shape,
+    mozilla::StyleForgottenArcSlicePtr<mozilla::StylePathCommand>,
+    mozilla::StyleFillRule);
 
 void Gecko_SetStyleMotion(mozilla::UniquePtr<mozilla::StyleMotion>* aMotion,
                           mozilla::StyleMotion* aValue);
 
 mozilla::StyleMotion* Gecko_NewStyleMotion();
 
 void Gecko_CopyStyleMotions(mozilla::UniquePtr<mozilla::StyleMotion>* motion,
                             const mozilla::StyleMotion* other);
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -325,16 +325,17 @@ whitelist-types = [
     "mozilla::binding_danger::AssertAndSuppressCleanupPolicy",
     "mozilla::ParsingMode",
     "mozilla::InheritTarget",
     "mozilla::dom::MediaList",
     "mozilla::StyleRuleInclusion",
     "nsStyleTransformMatrix::MatrixTransformOperator",
 ]
 opaque-types = [
+    "mozilla::StyleThinArc", # https://github.com/rust-lang/rust-bindgen/issues/1557
     "std::pair__PCCP",
     "std::namespace::atomic___base", "std::atomic__My_base",
     "std::atomic",
     "std::atomic___base",
     # We want everything but FontVariation and Float to be opaque but we don't
     # have negative regexes.
     "mozilla::gfx::(.{0,4}|.{6,12}|.{14,}|([^F][^o][^n][^t][^V][^a][^r][^i][^a][^t][^i][^o][^n])|([^F][^l][^o][^a][^t]))",
     "mozilla::dom::Sequence",
@@ -469,16 +470,18 @@ cbindgen-types = [
     { gecko = "StyleGenericColorOrAuto", servo = "values::generics::color::ColorOrAuto" },
     { gecko = "StyleGenericScrollbarColor", servo = "values::generics::ui::ScrollbarColor" },
     { gecko = "StyleRGBA", servo = "cssparser::RGBA" },
     { gecko = "StyleOrigin", servo = "stylesheets::Origin" },
     { gecko = "StyleGenericGradientItem", servo = "values::generics::image::GradientItem" },
     { gecko = "StyleGenericVerticalAlign", servo = "values::generics::box_::VerticalAlign" },
     { gecko = "StyleVerticalAlignKeyword", servo = "values::generics::box_::VerticalAlignKeyword" },
     { gecko = "StyleGenericBasicShape", servo = "values::generics::basic_shape::BasicShape" },
+    { gecko = "StyleArcSlice", servo = "style_traits::arc_slice::ArcSlice" },
+    { gecko = "StyleForgottenArcSlicePtr", servo = "style_traits::arc_slice::ForgottenArcSlicePtr" },
 ]
 
 mapped-generic-types = [
     { generic = true, gecko = "mozilla::RustCell", servo = "::std::cell::Cell" },
     { generic = false, gecko = "ServoNodeData", servo = "AtomicRefCell<ElementData>" },
     { generic = false, gecko = "mozilla::ServoWritingMode", servo = "::logical_geometry::WritingMode" },
     { generic = false, gecko = "mozilla::ServoCustomPropertiesMap", servo = "Option<::servo_arc::Arc<::custom_properties::CustomPropertiesMap>>" },
     { generic = false, gecko = "mozilla::ServoRuleNode", servo = "Option<::rule_tree::StrongRuleNode>" },
--- a/layout/style/ServoStyleConstsForwards.h
+++ b/layout/style/ServoStyleConstsForwards.h
@@ -22,16 +22,17 @@
 #  include "mozilla/Span.h"
 #  include "Units.h"
 #  include "mozilla/gfx/Types.h"
 #  include "mozilla/MemoryReporting.h"
 #  include "mozilla/ServoTypes.h"
 #  include "mozilla/ServoBindingTypes.h"
 #  include "nsCSSPropertyID.h"
 #  include "nsCompatibility.h"
+#  include <atomic>
 
 struct RawServoAnimationValueTable;
 struct RawServoAnimationValueMap;
 
 class nsAtom;
 class nsIFrame;
 class nsINode;
 class nsCSSPropertyIDSet;
@@ -74,16 +75,19 @@ using ComputedKeyframeValues = nsTArray<
 class ComputedStyle;
 class SeenPtrs;
 class SharedFontList;
 class StyleSheet;
 class WritingMode;
 class ServoElementSnapshotTable;
 enum class StyleContentType : uint8_t;
 
+template<typename T>
+struct StyleForgottenArcSlicePtr;
+
 struct AnimationPropertySegment;
 struct ComputedTiming;
 struct URLExtraData;
 
 enum HalfCorner : uint8_t;
 enum LogicalSide : uint8_t;
 enum class PseudoStyleType : uint8_t;
 enum class OriginFlags : uint8_t;
@@ -130,11 +134,13 @@ using StyleMatrixTransformOperator =
 #  define SERVO_ARC_TYPE(name_, type_) using Style##type_ = type_;
 #  include "mozilla/ServoArcTypeList.h"
 #  undef SERVO_ARC_TYPE
 
 #  define SERVO_BOXED_TYPE(name_, type_) using Style##type_ = type_;
 #  include "mozilla/ServoBoxedTypeList.h"
 #  undef SERVO_BOXED_TYPE
 
+using StyleAtomicUsize = std::atomic<size_t>;
+
 }  // namespace mozilla
 
 #endif
new file mode 100644
--- /dev/null
+++ b/layout/style/ServoStyleConstsInlines.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+/* Some inline functions declared in cbindgen.toml */
+
+#ifndef mozilla_ServoStyleConstsInlines_h
+#define mozilla_ServoStyleConstsInlines_h
+
+#include "mozilla/ServoStyleConsts.h"
+#include <type_traits>
+
+// TODO(emilio): there are quite a few other implementations scattered around
+// that should move here.
+
+namespace mozilla {
+
+// This code is basically a C++ port of the Arc::clone() implementation in
+// servo/components/servo_arc/lib.rs.
+static constexpr const size_t kStaticRefcount =
+    std::numeric_limits<size_t>::max();
+static constexpr const size_t kMaxRefcount =
+    std::numeric_limits<intptr_t>::max();
+static constexpr const uint32_t kArcSliceCanary = 0xf3f3f3f3;
+
+#define ASSERT_CANARY \
+  MOZ_DIAGNOSTIC_ASSERT(_0.ptr->data.header.header == kArcSliceCanary, "Uh?");
+
+template <typename T>
+inline StyleArcSlice<T>::StyleArcSlice(const StyleArcSlice& aOther) {
+  MOZ_DIAGNOSTIC_ASSERT(aOther._0.ptr);
+  _0.ptr = aOther._0.ptr;
+  if (_0.ptr->count.load(std::memory_order_relaxed) != kStaticRefcount) {
+    auto old_size = _0.ptr->count.fetch_add(1, std::memory_order_relaxed);
+    if (MOZ_UNLIKELY(old_size > kMaxRefcount)) {
+      ::abort();
+    }
+  }
+  ASSERT_CANARY
+}
+
+template <typename T>
+inline StyleArcSlice<T>::StyleArcSlice(
+    const StyleForgottenArcSlicePtr<T>& aPtr) {
+  // See the forget() implementation to see why reinterpret_cast() is ok.
+  _0.ptr = reinterpret_cast<decltype(_0.ptr)>(aPtr._0);
+  ASSERT_CANARY
+}
+
+template <typename T>
+inline size_t StyleArcSlice<T>::Length() const {
+  ASSERT_CANARY
+  return _0.ptr->data.header.length;
+}
+
+template <typename T>
+inline Span<const T> StyleArcSlice<T>::AsSpan() const {
+  ASSERT_CANARY
+  return MakeSpan(_0.ptr->data.slice, Length());
+}
+
+template <typename T>
+inline bool StyleArcSlice<T>::operator==(const StyleArcSlice& aOther) const {
+  ASSERT_CANARY
+  return AsSpan() == aOther.AsSpan();
+}
+
+template <typename T>
+inline bool StyleArcSlice<T>::operator!=(const StyleArcSlice& aOther) const {
+  return !(*this == aOther);
+}
+
+// This is a C++ port-ish of Arc::drop().
+template <typename T>
+inline StyleArcSlice<T>::~StyleArcSlice() {
+  ASSERT_CANARY
+  if (_0.ptr->count.load(std::memory_order_relaxed) == kStaticRefcount) {
+    return;
+  }
+  if (_0.ptr->count.fetch_sub(1, std::memory_order_release) != 1) {
+    return;
+  }
+  _0.ptr->count.load(std::memory_order_acquire);
+  for (T& elem : MakeSpan(_0.ptr->data.slice, Length())) {
+    elem.~T();
+  }
+  free(_0.ptr);  // Drop the allocation now.
+}
+
+#undef ASSERT_CANARY
+
+}  // namespace mozilla
+
+#endif
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -96,16 +96,17 @@ EXPORTS.mozilla += [
     'ServoBoxedTypeList.h',
     'ServoComputedData.h',
     'ServoComputedDataInlines.h',
     'ServoCSSParser.h',
     'ServoCSSRuleList.h',
     'ServoElementSnapshot.h',
     'ServoElementSnapshotTable.h',
     'ServoStyleConstsForwards.h',
+    'ServoStyleConstsInlines.h',
     'ServoStyleSet.h',
     'ServoStyleSetInlines.h',
     'ServoTraversalStatistics.h',
     'ServoTypes.h',
     'ServoUtils.h',
     'StyleAnimationValue.h',
     'StyleColorInlines.h',
     'StyleSheet.h',
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1659,30 +1659,37 @@ struct StyleAnimation {
   RefPtr<nsAtom> mName;  // nsGkAtoms::_empty for 'none'
   dom::PlaybackDirection mDirection;
   dom::FillMode mFillMode;
   StyleAnimationPlayState mPlayState;
   float mIterationCount;  // mozilla::PositiveInfinity<float>() means infinite
 };
 
 struct StyleSVGPath final {
-  const nsTArray<StylePathCommand>& Path() const { return mPath; }
+  StyleSVGPath(StyleForgottenArcSlicePtr<StylePathCommand> aPath,
+               StyleFillRule aFill)
+    : mPath(aPath),
+      mFillRule(aFill) {}
+
+  Span<const StylePathCommand> Path() const {
+    return mPath.AsSpan();
+  }
 
   StyleFillRule FillRule() const { return mFillRule; }
 
   bool operator==(const StyleSVGPath& aOther) const {
     return mPath == aOther.mPath && mFillRule == aOther.mFillRule;
   }
 
   bool operator!=(const StyleSVGPath& aOther) const {
     return !(*this == aOther);
   }
 
  private:
-  nsTArray<StylePathCommand> mPath;
+  StyleArcSlice<StylePathCommand> mPath;
   StyleFillRule mFillRule = StyleFillRule::Nonzero;
 };
 
 struct StyleShapeSource final {
   StyleShapeSource();
 
   StyleShapeSource(const StyleShapeSource& aSource);
 
--- a/servo/components/servo_arc/lib.rs
+++ b/servo/components/servo_arc/lib.rs
@@ -303,16 +303,19 @@ impl<T: ?Sized> Arc<T> {
     fn ptr(&self) -> *mut ArcInner<T> {
         self.p.as_ptr()
     }
 }
 
 impl<T: ?Sized> Clone for Arc<T> {
     #[inline]
     fn clone(&self) -> Self {
+        // NOTE(emilio): If you change anything here, make sure that the
+        // implementation in layout/style/ServoStyleConstsInlines.h matches!
+        //
         // Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since
         // `count` never changes between STATIC_REFCOUNT and other values.
         if self.inner().count.load(Relaxed) != STATIC_REFCOUNT {
             // Using a relaxed ordering is alright here, as knowledge of the
             // original reference prevents other threads from erroneously deleting
             // the object.
             //
             // As explained in the [Boost documentation][1], Increasing the
@@ -411,16 +414,19 @@ impl<T: ?Sized> Arc<T> {
         // [1] https://github.com/servo/servo/issues/21186
         self.inner().count.load(Acquire) == 1
     }
 }
 
 impl<T: ?Sized> Drop for Arc<T> {
     #[inline]
     fn drop(&mut self) {
+        // NOTE(emilio): If you change anything here, make sure that the
+        // implementation in layout/style/ServoStyleConstsInlines.h matches!
+        //
         // Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since
         // `count` never changes between STATIC_REFCOUNT and other values.
         if self.inner().count.load(Relaxed) == STATIC_REFCOUNT {
             return;
         }
 
         // Because `fetch_sub` is already atomic, we do not need to synchronize
         // with other threads unless we are going to delete the object.
@@ -566,16 +572,17 @@ impl<T: Serialize> Serialize for Arc<T> 
     {
         (**self).serialize(serializer)
     }
 }
 
 /// Structure to allow Arc-managing some fixed-sized data and a variably-sized
 /// slice in a single allocation.
 #[derive(Debug, Eq, PartialEq, PartialOrd)]
+#[repr(C)]
 pub struct HeaderSlice<H, T: ?Sized> {
     /// The fixed-sized data.
     pub header: H,
 
     /// The dynamically-sized data.
     pub slice: T,
 }
 
@@ -738,16 +745,17 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
         vec.set_len(words_to_allocate);
         Box::into_raw(vec.into_boxed_slice()) as *mut W as *mut u8
     }
 }
 
 /// Header data with an inline length. Consumers that use HeaderWithLength as the
 /// Header type in HeaderSlice can take advantage of ThinArc.
 #[derive(Debug, Eq, PartialEq, PartialOrd)]
+#[repr(C)]
 pub struct HeaderWithLength<H> {
     /// The fixed-sized data.
     pub header: H,
 
     /// The slice length.
     length: usize,
 }
 
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -572,22 +572,20 @@ pub mod basic_shape {
                     let fill = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }.mFillRule;
                     Some(ShapeSource::Path(Path { fill, path }))
                 },
             }
         }
 
         /// Generate a SVGPathData from StyleShapeSource if possible.
         fn to_svg_path(&self) -> Option<SVGPathData> {
-            use crate::values::specified::svg_path::PathCommand;
             match self.mType {
                 StyleShapeSourceType::Path => {
                     let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr };
-                    let result: Vec<PathCommand> = gecko_path.mPath.iter().cloned().collect();
-                    Some(SVGPathData::new(result.into_boxed_slice()))
+                    Some(SVGPathData(gecko_path.mPath.clone()))
                 },
                 _ => None,
             }
         }
     }
 
     impl<'a> From<&'a StyleShapeSource> for ClippingShape {
         fn from(other: &'a StyleShapeSource) -> Self {
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -2896,17 +2896,17 @@ fn static_assert() {
         use crate::gecko_bindings::structs::StyleShapeSourceType;
         use crate::values::generics::basic_shape::FillRule;
         use crate::values::specified::OffsetPath;
 
         let motion = unsafe { Gecko_NewStyleMotion().as_mut().unwrap() };
         match v {
             OffsetPath::None => motion.mOffsetPath.mType = StyleShapeSourceType::None,
             OffsetPath::Path(p) => {
-                set_style_svg_path(&mut motion.mOffsetPath, &p, FillRule::Nonzero)
+                set_style_svg_path(&mut motion.mOffsetPath, p, FillRule::Nonzero)
             },
         }
         unsafe { Gecko_SetStyleMotion(&mut self.gecko.mMotion, motion) };
     }
 
     pub fn clone_offset_path(&self) -> longhands::offset_path::computed_value::T {
         use crate::values::specified::OffsetPath;
         match unsafe { self.gecko.mMotion.mPtr.as_ref() } {
@@ -4018,35 +4018,27 @@ fn static_assert() {
             InitialLetter::Specified(self.gecko.mInitialLetterSize, Some(self.gecko.mInitialLetterSink))
         }
     }
 </%self:impl_trait>
 
 // Set SVGPathData to StyleShapeSource.
 fn set_style_svg_path(
     shape_source: &mut structs::mozilla::StyleShapeSource,
-    servo_path: &values::specified::svg_path::SVGPathData,
+    servo_path: values::specified::svg_path::SVGPathData,
     fill: values::generics::basic_shape::FillRule,
 ) {
-    use crate::gecko_bindings::bindings::Gecko_NewStyleSVGPath;
-    use crate::gecko_bindings::structs::StyleShapeSourceType;
-
-    // Setup type.
-    shape_source.mType = StyleShapeSourceType::Path;
-
     // Setup path.
-    let gecko_path = unsafe {
-        Gecko_NewStyleSVGPath(shape_source);
-        &mut shape_source.__bindgen_anon_1.mSVGPath.as_mut().mPtr.as_mut().unwrap()
-    };
-
-    gecko_path.mPath.assign_from_iter_pod(servo_path.commands().iter().cloned());
-
-    // Setup fill-rule.
-    gecko_path.mFillRule = fill;
+    unsafe {
+        bindings::Gecko_SetToSVGPath(
+            shape_source,
+            servo_path.0.forget(),
+            fill,
+        );
+    }
 }
 
 <%def name="impl_shape_source(ident, gecko_ffi_name)">
     pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
         use crate::values::generics::basic_shape::ShapeSource;
         use crate::gecko_bindings::structs::StyleShapeSourceType;
         use crate::gecko_bindings::structs::StyleGeometryBox;
 
@@ -4076,17 +4068,17 @@ fn set_style_svg_path(
                <% raise Exception("Unknown property: %s" % ident) %>
             }
             % endif
             ShapeSource::None => {} // don't change the type
             ShapeSource::Box(reference) => {
                 ${ident}.mReferenceBox = reference.into();
                 ${ident}.mType = StyleShapeSourceType::Box;
             }
-            ShapeSource::Path(p) => set_style_svg_path(${ident}, &p.path, p.fill),
+            ShapeSource::Path(p) => set_style_svg_path(${ident}, p.path, p.fill),
             ShapeSource::Shape(servo_shape, maybe_box) => {
                 unsafe {
                     ${ident}.__bindgen_anon_1.mBasicShape.as_mut().mPtr =
                         Box::into_raw(servo_shape);
                 }
                 ${ident}.mReferenceBox =
                     maybe_box.map(Into::into).unwrap_or(StyleGeometryBox::NoBox);
                 ${ident}.mType = StyleShapeSourceType::Shape;
--- a/servo/components/style/properties/longhands/svg.mako.rs
+++ b/servo/components/style/properties/longhands/svg.mako.rs
@@ -83,17 +83,16 @@
 )}
 
 ${helpers.predefined_type(
     "clip-path",
     "basic_shape::ClippingShape",
     "generics::basic_shape::ShapeSource::None",
     products="gecko",
     animation_value_type="basic_shape::ClippingShape",
-    boxed=True,
     flags="CREATES_STACKING_CONTEXT",
     spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path",
 )}
 
 ${helpers.single_keyword(
     "mask-mode",
     "match-source alpha luminance",
     gecko_enum_prefix="StyleMaskMode",
--- a/servo/components/style/values/specified/svg_path.rs
+++ b/servo/components/style/values/specified/svg_path.rs
@@ -24,59 +24,58 @@ use style_traits::{CssWriter, ParseError
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToAnimatedZero,
     ToComputedValue,
     ToResolvedValue,
     ToShmem,
 )]
-pub struct SVGPathData(Box<[PathCommand]>);
+#[repr(C)]
+pub struct SVGPathData(
+    // TODO(emilio): Should probably measure this somehow only from the
+    // specified values.
+    #[ignore_malloc_size_of = "Arc"]
+    pub crate::ArcSlice<PathCommand>
+);
 
 impl SVGPathData {
-    /// Return SVGPathData by a slice of PathCommand.
-    #[inline]
-    pub fn new(cmd: Box<[PathCommand]>) -> Self {
-        debug_assert!(!cmd.is_empty());
-        SVGPathData(cmd)
-    }
-
     /// Get the array of PathCommand.
     #[inline]
     pub fn commands(&self) -> &[PathCommand] {
         debug_assert!(!self.0.is_empty());
         &self.0
     }
 
-    /// Create a normalized copy of this path by converting each relative command to an absolute
-    /// command.
-    fn normalize(&self) -> Self {
+    /// Create a normalized copy of this path by converting each relative
+    /// command to an absolute command.
+    fn normalize(&self) -> Box<[PathCommand]> {
         let mut state = PathTraversalState {
             subpath_start: CoordPair::new(0.0, 0.0),
             pos: CoordPair::new(0.0, 0.0),
         };
         let result = self
             .0
             .iter()
             .map(|seg| seg.normalize(&mut state))
             .collect::<Vec<_>>();
-        SVGPathData(result.into_boxed_slice())
+        result.into_boxed_slice()
     }
 }
 
 impl ToCss for SVGPathData {
     #[inline]
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: fmt::Write,
     {
         dest.write_char('"')?;
         {
             let mut writer = SequenceWriter::new(dest, " ");
-            for command in self.0.iter() {
+            for command in self.commands() {
                 writer.item(command)?;
             }
         }
         dest.write_char('"')
     }
 }
 
 impl Parse for SVGPathData {
@@ -99,46 +98,48 @@ impl Parse for SVGPathData {
         // Parse the svg path string as multiple sub-paths.
         let mut path_parser = PathParser::new(path_string);
         while skip_wsp(&mut path_parser.chars) {
             if path_parser.parse_subpath().is_err() {
                 return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
             }
         }
 
-        Ok(SVGPathData::new(path_parser.path.into_boxed_slice()))
+        Ok(SVGPathData(crate::ArcSlice::from_iter(path_parser.path.into_iter())))
     }
 }
 
 impl Animate for SVGPathData {
     fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
         if self.0.len() != other.0.len() {
             return Err(());
         }
 
+        // FIXME(emilio): This allocates three copies of the path, that's not
+        // great! Specially, once we're normalized once, we don't need to
+        // re-normalize again.
         let result = self
             .normalize()
-            .0
             .iter()
-            .zip(other.normalize().0.iter())
+            .zip(other.normalize().iter())
             .map(|(a, b)| a.animate(&b, procedure))
             .collect::<Result<Vec<_>, _>>()?;
-        Ok(SVGPathData::new(result.into_boxed_slice()))
+
+        Ok(SVGPathData(crate::ArcSlice::from_iter(result.into_iter())))
     }
 }
 
 impl ComputeSquaredDistance for SVGPathData {
     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
         if self.0.len() != other.0.len() {
             return Err(());
         }
         self.normalize()
-            .0
             .iter()
-            .zip(other.normalize().0.iter())
+            .zip(other.normalize().iter())
             .map(|(this, other)| this.compute_squared_distance(&other))
             .sum()
     }
 }
 
 /// The SVG path command.
 /// The fields of these commands are self-explanatory, so we skip the documents.
 /// Note: the index of the control points, e.g. control1, control2, are mapping to the control
--- a/servo/ports/geckolib/cbindgen.toml
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -2,17 +2,20 @@ header = """/* This Source Code Form is 
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 // See the comment in ServoBindings.h about the same.
 #pragma GCC diagnostic push
 #ifdef __clang__
 #  pragma GCC diagnostic ignored "-Wreturn-type-c-linkage"
 #endif"""
-trailer = "#pragma GCC diagnostic pop"
+trailer = """
+#pragma GCC diagnostic pop
+#include "mozilla/ServoStyleConstsInlines.h"
+"""
 autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen.
  * To generate this file:
  *   1. Get the latest cbindgen using `cargo install --force cbindgen`
  *      a. Alternatively, you can clone `https://github.com/eqrion/cbindgen` and use a tagged release
  *   2. Run `rustup run nightly cbindgen toolkit/library/rust/ --lockfile Cargo.lock --crate style -o layout/style/ServoStyleConsts.h`
  */
 """
 include_guard = "mozilla_ServoStyleConsts_h"
@@ -21,17 +24,17 @@ braces = "SameLine"
 line_length = 80
 tab_width = 2
 language = "C++"
 namespaces = ["mozilla"]
 includes = ["mozilla/ServoStyleConstsForwards.h"]
 
 [parse]
 parse_deps = true
-include = ["style", "cssparser", "style_traits"]
+include = ["style", "cssparser", "style_traits", "servo_arc"]
 
 [struct]
 derive_eq = true
 derive_neq = true
 
 [macro_expansion]
 bitflags = true
 
@@ -116,16 +119,19 @@ include = [
   "Strong",
   "ScrollbarColor",
   "Color",
   "ColorOrAuto",
   "GradientItem",
   "VerticalAlign",
   "BasicShape",
   "ShapeRadius",
+  "ArcSlice",
+  "ForgottenArcSlicePtr",
+  "HeaderWithLength",
 ]
 item_types = ["enums", "structs", "typedefs", "functions"]
 renaming_overrides_prefixing = true
 
 # Prevent some renaming for Gecko types that cbindgen doesn't otherwise understand.
 [export.rename]
 "nscolor" = "nscolor"
 "nsAtom" = "nsAtom"
@@ -366,8 +372,19 @@ renaming_overrides_prefixing = true
   bool operator==(const StyleOwnedSlice& other) const {
     return AsSpan() == other.AsSpan();
   }
 
   bool operator!=(const StyleOwnedSlice& other) const {
     return !(*this == other);
   }
 """
+
+"ArcSlice" = """
+  StyleArcSlice() = delete;
+  inline StyleArcSlice(const StyleArcSlice& aOther);
+  inline explicit StyleArcSlice(const StyleForgottenArcSlicePtr<T>& aPtr);
+  inline ~StyleArcSlice();
+  inline Span<const T> AsSpan() const;
+  inline size_t Length() const;
+  inline bool operator==(const StyleArcSlice& other) const;
+  inline bool operator!=(const StyleArcSlice& other) const;
+"""