Bug 1550554 - Use ArcSlice for quotes. r=heycam
☠☠ backed out by 283b94c196a1 ☠ ☠
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 09 May 2019 05:35:04 +0200
changeset 532916 b28a48e2ed212dc5dea20ddab26e37f66b00a2c0
parent 532915 2c31fe18eefd0a53bac3dec0c752fcc156d001e9
child 532917 882ab9868c957394c82bffbf3f3fa7d61db2b0a2
push id11276
push userrgurzau@mozilla.com
push dateMon, 20 May 2019 13:11:24 +0000
treeherdermozilla-beta@847755a7c325 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1550554
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1550554 - Use ArcSlice for quotes. r=heycam This saves the intermediate allocation. Differential Revision: https://phabricator.services.mozilla.com/D30546
layout/base/nsQuoteList.cpp
layout/style/ServoBindings.toml
layout/style/ServoStyleConstsInlines.h
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
servo/components/style/gecko/arc_types.rs
servo/components/style/lib.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/values/computed/list.rs
servo/components/style/values/specified/list.rs
servo/components/style_traits/lib.rs
servo/components/style_traits/owned_str.rs
servo/components/style_traits/specified_value_info.rs
servo/components/style_traits/values.rs
servo/ports/geckolib/cbindgen.toml
servo/ports/geckolib/glue.rs
--- a/layout/base/nsQuoteList.cpp
+++ b/layout/base/nsQuoteList.cpp
@@ -32,20 +32,39 @@ bool nsQuoteNode::InitTextFrame(nsGenCon
   }
   return dirty;
 }
 
 nsString nsQuoteNode::Text() {
   NS_ASSERTION(mType == StyleContentType::OpenQuote ||
                    mType == StyleContentType::CloseQuote,
                "should only be called when mText should be non-null");
+  nsString result;
+  int32_t depth = Depth();
+  MOZ_ASSERT(depth >= -1);
 
-  nsString result;
-  Servo_Quotes_GetQuote(mPseudoFrame->StyleList()->mQuotes.get(), Depth(),
-                        mType, &result);
+  Span<const StyleQuotePair> quotes =
+    mPseudoFrame->StyleList()->mQuotes._0.AsSpan();
+
+  // Reuse the last pair when the depth is greater than the number of
+  // pairs of quotes.  (Also make 'quotes: none' and close-quote from
+  // a depth of 0 equivalent for the next test.)
+  if (depth >= static_cast<int32_t>(quotes.Length())) {
+      depth = static_cast<int32_t>(quotes.Length()) - 1;
+  }
+
+  if (depth == -1) {
+    // close-quote from a depth of 0 or 'quotes: none'
+    return result;
+  }
+
+  const StyleQuotePair& pair = quotes[depth];
+  const StyleOwnedStr& quote =
+    mType == StyleContentType::OpenQuote ? pair.opening : pair.closing;
+  result.Assign(NS_ConvertUTF8toUTF16(quote.AsString()));
   return result;
 }
 
 void nsQuoteList::Calc(nsQuoteNode* aNode) {
   if (aNode == FirstNode()) {
     aNode->mDepthBefore = 0;
   } else {
     aNode->mDepthBefore = Prev(aNode)->DepthAfter();
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -476,16 +476,18 @@ cbindgen-types = [
     { 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" },
     { gecko = "StyleMozContextProperties", servo = "values::specified::svg::MozContextProperties" },
+    { gecko = "StyleQuotes", servo = "values::specified::list::Quotes" },
+    { gecko = "StyleOwnedStr", servo = "style_traits::owned_str::OwnedStr" },
 ]
 
 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/ServoStyleConstsInlines.h
+++ b/layout/style/ServoStyleConstsInlines.h
@@ -106,11 +106,17 @@ inline StyleAtom::~StyleAtom() {
 }
 
 inline StyleAtom::StyleAtom(const StyleAtom& aOther) : _0(aOther._0) {
   if (!IsStatic()) {
     reinterpret_cast<nsAtom*>(_0)->AddRef();
   }
 }
 
+inline nsDependentCSubstring StyleOwnedStr::AsString() const {
+  Span<const uint8_t> s = _0.AsSpan();
+  return nsDependentCSubstring(reinterpret_cast<const char*>(s.Elements()),
+                               s.Length());
+}
+
 }  // namespace mozilla
 
 #endif
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -475,22 +475,22 @@ nsChangeHint nsStyleOutline::CalcDiffere
   return nsChangeHint(0);
 }
 
 // --------------------
 // nsStyleList
 //
 nsStyleList::nsStyleList(const Document& aDocument)
     : mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE),
+      mQuotes{StyleArcSlice<StyleQuotePair>(Servo_Quotes_GetInitialValue())},
       mMozListReversed(StyleMozListReversed::False) {
   MOZ_COUNT_CTOR(nsStyleList);
   MOZ_ASSERT(NS_IsMainThread());
 
   mCounterStyle = nsGkAtoms::disc;
-  mQuotes = Servo_Quotes_GetInitialValue().Consume();
 }
 
 nsStyleList::~nsStyleList() { MOZ_COUNT_DTOR(nsStyleList); }
 
 nsStyleList::nsStyleList(const nsStyleList& aSource)
     : mListStylePosition(aSource.mListStylePosition),
       mListStyleImage(aSource.mListStyleImage),
       mCounterStyle(aSource.mCounterStyle),
@@ -509,18 +509,17 @@ void nsStyleList::TriggerImageLoads(Docu
         aDocument, aOldStyle ? aOldStyle->mListStyleImage.get() : nullptr);
   }
 }
 
 nsChangeHint nsStyleList::CalcDifference(
     const nsStyleList& aNewData, const nsStyleDisplay& aOldDisplay) const {
   // If the quotes implementation is ever going to change we might not need
   // a framechange here and a reflow should be sufficient.  See bug 35768.
-  if (mQuotes != aNewData.mQuotes &&
-      !Servo_Quotes_Equal(mQuotes.get(), aNewData.mQuotes.get())) {
+  if (mQuotes != aNewData.mQuotes) {
     return nsChangeHint_ReconstructFrame;
   }
   nsChangeHint hint = nsChangeHint(0);
   // Only elements whose display value is list-item can be affected by
   // list-style-position and list-style-type. If the old display struct
   // doesn't exist, assume it isn't affected by display value at all,
   // and thus these properties should not affect it either. This also
   // relies on that when the display value changes from something else
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1098,17 +1098,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   RefPtr<nsStyleImageRequest> mListStyleImage;
 
   mozilla::CounterStylePtr mCounterStyle;
 
  private:
   nsStyleList& operator=(const nsStyleList& aOther) = delete;
 
  public:
-  RefPtr<RawServoQuotes> mQuotes;
+  mozilla::StyleQuotes mQuotes;
   nsRect mImageRegion;  // the rect to use within an image
   mozilla::StyleMozListReversed
       mMozListReversed;  // true in an <ol reversed> scope
 };
 
 struct nsStyleGridLine {
   // http://dev.w3.org/csswg/css-grid/#typedef-grid-line
   // XXXmats we could optimize memory size here
--- a/servo/components/style/gecko/arc_types.rs
+++ b/servo/components/style/gecko/arc_types.rs
@@ -18,34 +18,32 @@ use crate::gecko_bindings::structs::RawS
 use crate::gecko_bindings::structs::RawServoImportRule;
 use crate::gecko_bindings::structs::RawServoKeyframe;
 use crate::gecko_bindings::structs::RawServoKeyframesRule;
 use crate::gecko_bindings::structs::RawServoMediaList;
 use crate::gecko_bindings::structs::RawServoMediaRule;
 use crate::gecko_bindings::structs::RawServoMozDocumentRule;
 use crate::gecko_bindings::structs::RawServoNamespaceRule;
 use crate::gecko_bindings::structs::RawServoPageRule;
-use crate::gecko_bindings::structs::RawServoQuotes;
 use crate::gecko_bindings::structs::RawServoStyleRule;
 use crate::gecko_bindings::structs::RawServoStyleSheetContents;
 use crate::gecko_bindings::structs::RawServoSupportsRule;
 use crate::gecko_bindings::structs::ServoCssRules;
 use crate::gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI, Strong};
 use crate::media_queries::MediaList;
 use crate::properties::animated_properties::AnimationValue;
 use crate::properties::{ComputedValues, PropertyDeclarationBlock};
 use crate::shared_lock::Locked;
 use crate::stylesheets::keyframes_rule::Keyframe;
 use crate::stylesheets::{CounterStyleRule, CssRules, FontFaceRule, FontFeatureValuesRule};
 use crate::stylesheets::{DocumentRule, ImportRule, KeyframesRule, MediaRule};
 use crate::stylesheets::{NamespaceRule, PageRule};
 use crate::stylesheets::{StyleRule, StylesheetContents, SupportsRule};
 use servo_arc::{Arc, ArcBorrow};
 use std::{mem, ptr};
-use values::computed::QuotePair;
 
 macro_rules! impl_arc_ffi {
     ($servo_type:ty => $gecko_type:ty[$addref:ident, $release:ident]) => {
         unsafe impl HasFFI for $servo_type {
             type FFIType = $gecko_type;
         }
         unsafe impl HasArcFFI for $servo_type {}
 
@@ -110,19 +108,16 @@ impl_arc_ffi!(Locked<FontFaceRule> => Ra
               [Servo_FontFaceRule_AddRef, Servo_FontFaceRule_Release]);
 
 impl_arc_ffi!(Locked<CounterStyleRule> => RawServoCounterStyleRule
               [Servo_CounterStyleRule_AddRef, Servo_CounterStyleRule_Release]);
 
 impl_arc_ffi!(CssUrlData => RawServoCssUrlData
               [Servo_CssUrlData_AddRef, Servo_CssUrlData_Release]);
 
-impl_arc_ffi!(Box<[QuotePair]> => RawServoQuotes
-              [Servo_Quotes_AddRef, Servo_Quotes_Release]);
-
 // ComputedStyle is not an opaque type on any side of FFI.
 // This means that doing the HasArcFFI type trick is actually unsound,
 // since it gives us a way to construct an Arc<ComputedStyle> from
 // an &ComputedStyle, which in general is not allowed. So we
 // implement the restricted set of arc type functionality we need.
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_ComputedStyle_AddRef(obj: &ComputedValues) {
--- a/servo/components/style/lib.rs
+++ b/servo/components/style/lib.rs
@@ -185,16 +185,17 @@ pub use html5ever::LocalName;
 pub use html5ever::Namespace;
 #[cfg(feature = "servo")]
 pub use html5ever::Prefix;
 #[cfg(feature = "servo")]
 pub use servo_atoms::Atom;
 
 pub use style_traits::arc_slice::ArcSlice;
 pub use style_traits::owned_slice::OwnedSlice;
+pub use style_traits::owned_str::OwnedStr;
 
 /// The CSS properties supported by the style system.
 /// Generated from the properties.mako.rs template by build.rs
 #[macro_use]
 #[allow(unsafe_code)]
 #[deny(missing_docs)]
 pub mod properties {
     include!(concat!(env!("OUT_DIR"), "/properties.rs"));
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -3297,17 +3297,17 @@ fn static_assert() {
                   skip_longhands="${skip_background_longhands}">
 
     <% impl_common_image_layer_properties("background") %>
     <% impl_simple_image_array_property("attachment", "background", "mImage", "mAttachment", "Background") %>
     <% impl_simple_image_array_property("blend_mode", "background", "mImage", "mBlendMode", "Background") %>
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="List"
-                  skip_longhands="list-style-image list-style-type quotes -moz-image-region">
+                  skip_longhands="list-style-image list-style-type -moz-image-region">
 
     pub fn set_list_style_image(&mut self, image: longhands::list_style_image::computed_value::T) {
         match image {
             UrlOrNone::None => {
                 unsafe {
                     Gecko_SetListStyleImageNone(&mut *self.gecko);
                 }
             }
@@ -3373,38 +3373,16 @@ fn static_assert() {
 
         let result = CounterStyleOrNone::from_gecko_value(&self.gecko.mCounterStyle);
         match result {
             Either::First(counter_style) => T::CounterStyle(counter_style),
             Either::Second(string) => T::String(string),
         }
     }
 
-    pub fn set_quotes(&mut self, other: longhands::quotes::computed_value::T) {
-        self.gecko.mQuotes.set_arc(other.0.clone());
-    }
-
-    pub fn copy_quotes_from(&mut self, other: &Self) {
-        self.set_quotes(other.clone_quotes());
-    }
-
-    pub fn reset_quotes(&mut self, other: &Self) {
-        self.copy_quotes_from(other)
-    }
-
-    pub fn clone_quotes(&self) -> longhands::quotes::computed_value::T {
-        use gecko_bindings::sugar::ownership::HasArcFFI;
-        use values::computed::QuotePair;
-
-        let quote_pairs = unsafe { &*self.gecko.mQuotes.mRawPtr };
-        longhands::quotes::computed_value::T(
-            Box::<[QuotePair]>::as_arc(&quote_pairs).clone_arc()
-        )
-    }
-
     #[allow(non_snake_case)]
     pub fn set__moz_image_region(&mut self, v: longhands::_moz_image_region::computed_value::T) {
         use crate::values::Either;
         use crate::values::generics::length::LengthPercentageOrAuto::*;
 
         match v {
             Either::Second(_auto) => {
                 self.gecko.mImageRegion.x = 0;
--- a/servo/components/style/values/computed/list.rs
+++ b/servo/components/style/values/computed/list.rs
@@ -4,31 +4,29 @@
 
 //! `list` computed values.
 
 #[cfg(feature = "gecko")]
 pub use crate::values::specified::list::ListStyleType;
 pub use crate::values::specified::list::MozListReversed;
 pub use crate::values::specified::list::{QuotePair, Quotes};
 
-use servo_arc::Arc;
-
 lazy_static! {
-    static ref INITIAL_QUOTES: Arc<Box<[QuotePair]>> = Arc::new(
+    static ref INITIAL_QUOTES: crate::ArcSlice<QuotePair> = crate::ArcSlice::from_iter(
         vec![
             QuotePair {
-                opening: "\u{201c}".to_owned().into_boxed_str(),
-                closing: "\u{201d}".to_owned().into_boxed_str(),
+                opening: "\u{201c}".to_owned().into(),
+                closing: "\u{201d}".to_owned().into(),
             },
             QuotePair {
-                opening: "\u{2018}".to_owned().into_boxed_str(),
-                closing: "\u{2019}".to_owned().into_boxed_str(),
+                opening: "\u{2018}".to_owned().into(),
+                closing: "\u{2019}".to_owned().into(),
             },
         ]
-        .into_boxed_slice()
+        .into_iter()
     );
 }
 
 impl Quotes {
     /// Initial value for `quotes`.
     #[inline]
     pub fn get_initial_value() -> Quotes {
         Quotes(INITIAL_QUOTES.clone())
--- a/servo/components/style/values/specified/list.rs
+++ b/servo/components/style/values/specified/list.rs
@@ -5,17 +5,16 @@
 //! `list` specified values.
 
 use crate::parser::{Parse, ParserContext};
 #[cfg(feature = "gecko")]
 use crate::values::generics::CounterStyleOrNone;
 #[cfg(feature = "gecko")]
 use crate::values::CustomIdent;
 use cssparser::{Parser, Token};
-use servo_arc::Arc;
 use style_traits::{ParseError, StyleParseErrorKind};
 
 /// Specified and computed `list-style-type` property.
 #[cfg(feature = "gecko")]
 #[derive(
     Clone,
     Debug,
     Eq,
@@ -91,69 +90,74 @@ impl Parse for ListStyleType {
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToComputedValue,
     ToCss,
     ToResolvedValue,
     ToShmem,
 )]
+#[repr(C)]
 pub struct QuotePair {
     /// The opening quote.
-    pub opening: Box<str>,
+    pub opening: crate::OwnedStr,
 
     /// The closing quote.
-    pub closing: Box<str>,
+    pub closing: crate::OwnedStr,
 }
 
 /// Specified and computed `quotes` property.
 #[derive(
     Clone,
     Debug,
+    Default,
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
     ToComputedValue,
     ToCss,
     ToResolvedValue,
     ToShmem,
 )]
+#[repr(C)]
 pub struct Quotes(
     #[css(iterable, if_empty = "none")]
     #[ignore_malloc_size_of = "Arc"]
-    pub Arc<Box<[QuotePair]>>,
+    pub crate::ArcSlice<QuotePair>,
 );
 
 impl Parse for Quotes {
     fn parse<'i, 't>(
         _: &ParserContext,
         input: &mut Parser<'i, 't>,
     ) -> Result<Quotes, ParseError<'i>> {
         if input
             .try(|input| input.expect_ident_matching("none"))
             .is_ok()
         {
-            return Ok(Quotes(Arc::new(Box::new([]))));
+            return Ok(Self::default());
         }
 
         let mut quotes = Vec::new();
         loop {
             let location = input.current_source_location();
             let opening = match input.next() {
-                Ok(&Token::QuotedString(ref value)) => value.as_ref().to_owned().into_boxed_str(),
+                Ok(&Token::QuotedString(ref value)) => {
+                    value.as_ref().to_owned().into()
+                },
                 Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
                 Err(_) => break,
             };
 
-            let closing = input.expect_string()?.as_ref().to_owned().into_boxed_str();
+            let closing = input.expect_string()?.as_ref().to_owned().into();
             quotes.push(QuotePair { opening, closing });
         }
 
         if !quotes.is_empty() {
-            Ok(Quotes(Arc::new(quotes.into_boxed_slice())))
+            Ok(Quotes(crate::ArcSlice::from_iter(quotes.into_iter())))
         } else {
             Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
         }
     }
 }
 
 /// Specified and computed `-moz-list-reversed` property (for UA sheets only).
 #[derive(
--- a/servo/components/style_traits/lib.rs
+++ b/servo/components/style_traits/lib.rs
@@ -88,16 +88,17 @@ pub enum CSSPixel {}
 
 pub mod arc_slice;
 pub mod specified_value_info;
 #[macro_use]
 pub mod values;
 #[macro_use]
 pub mod viewport;
 pub mod owned_slice;
+pub mod owned_str;
 
 pub use crate::specified_value_info::{CssType, KeywordsCollectFn, SpecifiedValueInfo};
 pub use crate::values::{
     Comma, CommaWithSpace, CssWriter, OneOrMoreSeparated, Separator, Space, ToCss,
 };
 
 /// The error type for all CSS parsing routines.
 pub type ParseError<'i> = cssparser::ParseError<'i, StyleParseErrorKind<'i>>;
new file mode 100644
--- /dev/null
+++ b/servo/components/style_traits/owned_str.rs
@@ -0,0 +1,53 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#![allow(unsafe_code)]
+
+//! A replacement for `Box<str>` that has a defined layout for FFI.
+
+use crate::owned_slice::OwnedSlice;
+use std::fmt;
+use std::ops::{Deref, DerefMut};
+
+/// A struct that basically replaces a Box<str>, but with a defined layout,
+/// suitable for FFI.
+#[repr(C)]
+#[derive(Default, Clone, PartialEq, Eq, MallocSizeOf, ToShmem)]
+pub struct OwnedStr(OwnedSlice<u8>);
+
+impl fmt::Debug for OwnedStr {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        self.deref().fmt(formatter)
+    }
+}
+
+impl Deref for OwnedStr {
+    type Target = str;
+
+    #[inline(always)]
+    fn deref(&self) -> &Self::Target {
+        unsafe { std::str::from_utf8_unchecked(&*self.0) }
+    }
+}
+
+impl DerefMut for OwnedStr {
+    #[inline(always)]
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        unsafe { std::str::from_utf8_unchecked_mut(&mut *self.0) }
+    }
+}
+
+impl From<Box<str>> for OwnedStr {
+    #[inline]
+    fn from(b: Box<str>) -> Self {
+        Self::from(b.into_string())
+    }
+}
+
+impl From<String> for OwnedStr {
+    #[inline]
+    fn from(s: String) -> Self {
+        OwnedStr(s.into_bytes().into())
+    }
+}
--- a/servo/components/style_traits/specified_value_info.rs
+++ b/servo/components/style_traits/specified_value_info.rs
@@ -79,16 +79,17 @@ impl SpecifiedValueInfo for bool {}
 impl SpecifiedValueInfo for f32 {}
 impl SpecifiedValueInfo for i8 {}
 impl SpecifiedValueInfo for i32 {}
 impl SpecifiedValueInfo for u8 {}
 impl SpecifiedValueInfo for u16 {}
 impl SpecifiedValueInfo for u32 {}
 impl SpecifiedValueInfo for str {}
 impl SpecifiedValueInfo for String {}
+impl SpecifiedValueInfo for crate::owned_str::OwnedStr {}
 
 #[cfg(feature = "servo")]
 impl SpecifiedValueInfo for ::servo_atoms::Atom {}
 #[cfg(feature = "servo")]
 impl SpecifiedValueInfo for ::servo_url::ServoUrl {}
 
 impl<T: SpecifiedValueInfo + ?Sized> SpecifiedValueInfo for Box<T> {
     const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES;
--- a/servo/components/style_traits/values.rs
+++ b/servo/components/style_traits/values.rs
@@ -70,16 +70,26 @@ where
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
         (*self).to_css(dest)
     }
 }
 
+impl ToCss for crate::owned_str::OwnedStr {
+    #[inline]
+    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+    where
+        W: Write,
+    {
+        serialize_string(self, dest)
+    }
+}
+
 impl ToCss for str {
     #[inline]
     fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
     where
         W: Write,
     {
         serialize_string(self, dest)
     }
--- a/servo/ports/geckolib/cbindgen.toml
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -123,16 +123,17 @@ include = [
   "GradientItem",
   "VerticalAlign",
   "BasicShape",
   "ShapeRadius",
   "ArcSlice",
   "ForgottenArcSlicePtr",
   "HeaderWithLength",
   "MozContextProperties",
+  "Quotes",
 ]
 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"
@@ -395,8 +396,12 @@ renaming_overrides_prefixing = true
   StyleAtom() = delete;
 
   // NOTE(emilio): For now we don't need to expose anything else, but it'd be trivial if we wanted to.
   inline bool IsStatic() const;
 
   inline StyleAtom(const StyleAtom& aOther);
   inline ~StyleAtom();
 """
+
+"OwnedStr" = """
+  inline nsDependentCSubstring AsString() const;
+"""
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -46,17 +46,17 @@ use style::gecko_bindings::bindings::Gec
 use style::gecko_bindings::bindings::Gecko_GetOrCreateFinalKeyframe;
 use style::gecko_bindings::bindings::Gecko_GetOrCreateInitialKeyframe;
 use style::gecko_bindings::bindings::Gecko_GetOrCreateKeyframeAtStart;
 use style::gecko_bindings::bindings::Gecko_HaveSeenPtr;
 use style::gecko_bindings::bindings::Gecko_NewNoneTransform;
 use style::gecko_bindings::structs;
 use style::gecko_bindings::structs::{Element as RawGeckoElement, nsINode as RawGeckoNode};
 use style::gecko_bindings::structs::{
-    RawServoQuotes, RawServoStyleSet, RawServoAuthorStyles,
+    RawServoStyleSet, RawServoAuthorStyles,
     RawServoCssUrlData, RawServoDeclarationBlock, RawServoMediaList,
     RawServoCounterStyleRule, RawServoAnimationValue, RawServoSupportsRule,
     RawServoKeyframesRule, ServoCssRules, RawServoStyleSheetContents,
     RawServoPageRule, RawServoNamespaceRule, RawServoMozDocumentRule,
     RawServoKeyframe, RawServoMediaRule, RawServoImportRule,
     RawServoFontFaceRule, RawServoFontFeatureValuesRule,
     RawServoSharedMemoryBuilder
 };
@@ -88,17 +88,16 @@ use style::gecko_bindings::structs::RawS
 use style::gecko_bindings::structs::RawServoStyleRule;
 use style::gecko_bindings::structs::SeenPtrs;
 use style::gecko_bindings::structs::ServoElementSnapshotTable;
 use style::gecko_bindings::structs::ServoStyleSetSizes;
 use style::gecko_bindings::structs::ServoTraversalFlags;
 use style::gecko_bindings::structs::SheetLoadData;
 use style::gecko_bindings::structs::SheetLoadDataHolder;
 use style::gecko_bindings::structs::SheetParsingMode;
-use style::gecko_bindings::structs::StyleContentType as ContentType;
 use style::gecko_bindings::structs::StyleRuleInclusion;
 use style::gecko_bindings::structs::StyleSheet as DomStyleSheet;
 use style::gecko_bindings::structs::URLExtraData;
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasArcFFI, HasFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, HasBoxFFI, Strong, Owned, OwnedOrNull};
 use style::gecko_bindings::sugar::refptr::RefPtr;
 use style::gecko_properties;
 use style::global_style_data::{GlobalStyleData, GLOBAL_STYLE_DATA, STYLE_THREAD_POOL};
@@ -128,17 +127,17 @@ use style::stylesheets::{StyleRule, Styl
 use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist};
 use style::thread_state;
 use style::timer::Timer;
 use style::traversal::resolve_style;
 use style::traversal::DomTraversal;
 use style::traversal_flags::{self, TraversalFlags};
 use style::use_counters::UseCounters;
 use style::values::animated::{Animate, Procedure, ToAnimatedZero};
-use style::values::computed::{self, Context, QuotePair, ToComputedValue};
+use style::values::computed::{self, Context, ToComputedValue};
 use style::values::distance::ComputeSquaredDistance;
 use style::values::specified;
 use style::values::specified::gecko::IntersectionObserverRootMargin;
 use style::values::specified::source_size_list::SourceSizeList;
 use style::values::{CustomIdent, KeyframesName};
 use style_traits::{CssWriter, ParsingMode, StyleParseErrorKind, ToCss};
 use to_shmem::SharedMemoryBuilder;
 
@@ -6298,62 +6297,18 @@ pub unsafe extern "C" fn Servo_IsCssProp
         Some(id) => id,
         None => return false,
     };
 
     use_counters.non_custom_properties.recorded(non_custom_id)
 }
 
 #[no_mangle]
-pub extern "C" fn Servo_Quotes_GetInitialValue() -> Strong<RawServoQuotes> {
-    computed::Quotes::get_initial_value()
-        .0
-        .clone()
-        .into_strong()
-}
-
-#[no_mangle]
-pub extern "C" fn Servo_Quotes_Equal(a: &RawServoQuotes, b: &RawServoQuotes) -> bool {
-    let a = Box::<[QuotePair]>::as_arc(&a);
-    let b = Box::<[QuotePair]>::as_arc(&b);
-
-    a == b
-}
-
-#[no_mangle]
-pub unsafe extern "C" fn Servo_Quotes_GetQuote(
-    quotes: &RawServoQuotes,
-    mut depth: i32,
-    quote_type: ContentType,
-    result: *mut nsAString,
-) {
-    debug_assert!(depth >= -1);
-
-    let quotes = Box::<[QuotePair]>::as_arc(&quotes);
-
-    // Reuse the last pair when the depth is greater than the number of
-    // pairs of quotes.  (Also make 'quotes: none' and close-quote from
-    // a depth of 0 equivalent for the next test.)
-    if depth >= quotes.len() as i32 {
-        depth = quotes.len() as i32 - 1;
-    }
-
-    if depth == -1 {
-        // close-quote from a depth of 0 or 'quotes: none'
-        return;
-    }
-
-    let quote_pair = &quotes[depth as usize];
-    let quote = if quote_type == ContentType::OpenQuote {
-        &quote_pair.opening
-    } else {
-        debug_assert!(quote_type == ContentType::CloseQuote);
-        &quote_pair.closing
-    };
-    (*result).write_str(quote).unwrap();
+pub extern "C" fn Servo_Quotes_GetInitialValue() -> style_traits::arc_slice::ForgottenArcSlicePtr<specified::list::QuotePair> {
+    computed::Quotes::get_initial_value().0.forget()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_SharedMemoryBuilder_Create(
     buffer: *mut u8,
     len: usize,
 ) -> *mut RawServoSharedMemoryBuilder {
     let mut builder = Box::new(SharedMemoryBuilder::new(buffer, len));