servo: Merge #14055 - support cursor property url() values in stylo (from heycam:cursor); r=Manishearth
authorCameron McCormack <cam@mcc.id.au>
Mon, 21 Nov 2016 05:49:05 -0600
changeset 340188 880a28f39fcd54bb5b2b2ad2e5fcaac78b925e4e
parent 340187 430216a218cda077d17da6e1e53b49909adb7581
child 340189 fd5fb0702412443062e41ad79176b596ae81c400
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersManishearth
bugs1310560
servo: Merge #14055 - support cursor property url() values in stylo (from heycam:cursor); r=Manishearth <!-- Please describe your changes on the following line: --> This is the Servo-side change for [bug 1310560](https://bugzilla.mozilla.org/show_bug.cgi?id=1310560), which @Manishearth has already r+ed. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [ ] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: eb531c15d990391f73f3a1030b103dfc3e023427
servo/components/layout/display_list_builder.rs
servo/components/style/binding_tools/regen.py
servo/components/style/gecko_bindings/bindings.rs
servo/components/style/gecko_bindings/structs_debug.rs
servo/components/style/gecko_bindings/structs_release.rs
servo/components/style/properties/gecko.mako.rs
servo/components/style/properties/longhand/pointing.mako.rs
--- a/servo/components/layout/display_list_builder.rs
+++ b/servo/components/layout/display_list_builder.rs
@@ -2241,18 +2241,18 @@ trait ServoComputedValuesCursorUtility {
 impl ServoComputedValuesCursorUtility for ServoComputedValues {
     /// Gets the cursor to use given the specific ServoComputedValues.  `default_cursor` specifies
     /// the cursor to use if `cursor` is `auto`. Typically, this will be `PointerCursor`, but for
     /// text display items it may be `TextCursor` or `VerticalTextCursor`.
     #[inline]
     fn get_cursor(&self, default_cursor: Cursor) -> Option<Cursor> {
         match (self.get_pointing().pointer_events, self.get_pointing().cursor) {
             (pointer_events::T::none, _) => None,
-            (pointer_events::T::auto, cursor::T::AutoCursor) => Some(default_cursor),
-            (pointer_events::T::auto, cursor::T::SpecifiedCursor(cursor)) => Some(cursor),
+            (pointer_events::T::auto, cursor::Keyword::AutoCursor) => Some(default_cursor),
+            (pointer_events::T::auto, cursor::Keyword::SpecifiedCursor(cursor)) => Some(cursor),
         }
     }
 }
 
 // A helper data structure for gradients.
 #[derive(Copy, Clone)]
 struct StopRun {
     start_offset: f32,
--- a/servo/components/style/binding_tools/regen.py
+++ b/servo/components/style/binding_tools/regen.py
@@ -273,16 +273,17 @@ COMPILATION_TARGETS = {
             "StyleBasicShapeType",
             "StyleClipPath",
             "nscoord",
             "nsCSSKeyword",
             "nsCSSShadowArray",
             "nsCSSValue",
             "nsCSSValueSharedList",
             "nsChangeHint",
+            "nsCursorImage",
             "nsFont",
             "nsIAtom",
             "nsIDocument",
             "nsINode",
             "nsIPrincipal",
             "nsIURI",
             "nsMainThreadPtrHolder",
             "nsRestyleHint",
--- a/servo/components/style/gecko_bindings/bindings.rs
+++ b/servo/components/style/gecko_bindings/bindings.rs
@@ -55,16 +55,17 @@ use gecko_bindings::structs::StyleBasicS
 use gecko_bindings::structs::StyleBasicShapeType;
 use gecko_bindings::structs::StyleClipPath;
 use gecko_bindings::structs::nscoord;
 use gecko_bindings::structs::nsCSSKeyword;
 use gecko_bindings::structs::nsCSSShadowArray;
 use gecko_bindings::structs::nsCSSValue;
 use gecko_bindings::structs::nsCSSValueSharedList;
 use gecko_bindings::structs::nsChangeHint;
+use gecko_bindings::structs::nsCursorImage;
 use gecko_bindings::structs::nsFont;
 use gecko_bindings::structs::nsIAtom;
 use gecko_bindings::structs::nsIDocument;
 use gecko_bindings::structs::nsINode;
 use gecko_bindings::structs::nsIPrincipal;
 use gecko_bindings::structs::nsIURI;
 use gecko_bindings::structs::nsMainThreadPtrHolder;
 use gecko_bindings::structs::nsRestyleHint;
@@ -506,16 +507,31 @@ extern "C" {
                                    referrer: *mut ThreadSafeURIHolder,
                                    principal: *mut ThreadSafePrincipalHolder);
 }
 extern "C" {
     pub fn Gecko_CopyListStyleImageFrom(dest: *mut nsStyleList,
                                         src: *const nsStyleList);
 }
 extern "C" {
+    pub fn Gecko_SetCursorArrayLength(ui: *mut nsStyleUserInterface,
+                                      len: usize);
+}
+extern "C" {
+    pub fn Gecko_SetCursorImage(cursor: *mut nsCursorImage,
+                                string_bytes: *const u8, string_length: u32,
+                                base_uri: *mut ThreadSafeURIHolder,
+                                referrer: *mut ThreadSafeURIHolder,
+                                principal: *mut ThreadSafePrincipalHolder);
+}
+extern "C" {
+    pub fn Gecko_CopyCursorArrayFrom(dest: *mut nsStyleUserInterface,
+                                     src: *const nsStyleUserInterface);
+}
+extern "C" {
     pub fn Gecko_SetMozBinding(style_struct: *mut nsStyleDisplay,
                                string_bytes: *const u8, string_length: u32,
                                base_uri: *mut ThreadSafeURIHolder,
                                referrer: *mut ThreadSafeURIHolder,
                                principal: *mut ThreadSafePrincipalHolder);
 }
 extern "C" {
     pub fn Gecko_CopyMozBindingFrom(des: *mut nsStyleDisplay,
--- a/servo/components/style/gecko_bindings/structs_debug.rs
+++ b/servo/components/style/gecko_bindings/structs_debug.rs
@@ -11861,17 +11861,17 @@ fn bindgen_test_layout_nsStyleUIReset() 
     assert_eq!(::std::mem::align_of::<nsStyleUIReset>() , 1usize);
 }
 #[repr(C)]
 #[derive(Debug)]
 pub struct nsCursorImage {
     pub mHaveHotspot: bool,
     pub mHotspotX: f32,
     pub mHotspotY: f32,
-    pub mImage: nsCOMPtr<imgIRequest>,
+    pub mImage: RefPtr<nsStyleImageRequest>,
 }
 #[test]
 fn bindgen_test_layout_nsCursorImage() {
     assert_eq!(::std::mem::size_of::<nsCursorImage>() , 24usize);
     assert_eq!(::std::mem::align_of::<nsCursorImage>() , 8usize);
 }
 #[repr(C)]
 #[derive(Debug)]
--- a/servo/components/style/gecko_bindings/structs_release.rs
+++ b/servo/components/style/gecko_bindings/structs_release.rs
@@ -11817,17 +11817,17 @@ fn bindgen_test_layout_nsStyleUIReset() 
     assert_eq!(::std::mem::align_of::<nsStyleUIReset>() , 1usize);
 }
 #[repr(C)]
 #[derive(Debug)]
 pub struct nsCursorImage {
     pub mHaveHotspot: bool,
     pub mHotspotX: f32,
     pub mHotspotY: f32,
-    pub mImage: nsCOMPtr<imgIRequest>,
+    pub mImage: RefPtr<nsStyleImageRequest>,
 }
 #[test]
 fn bindgen_test_layout_nsCursorImage() {
     assert_eq!(::std::mem::size_of::<nsCursorImage>() , 24usize);
     assert_eq!(::std::mem::align_of::<nsCursorImage>() , 8usize);
 }
 #[repr(C)]
 #[derive(Debug)]
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -14,25 +14,28 @@ use app_units::Au;
 use custom_properties::ComputedValuesMap;
 use gecko_bindings::bindings;
 % for style_struct in data.style_structs:
 use gecko_bindings::structs::${style_struct.gecko_ffi_name};
 use gecko_bindings::bindings::Gecko_Construct_${style_struct.gecko_ffi_name};
 use gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ffi_name};
 use gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name};
 % endfor
+use gecko_bindings::bindings::Gecko_CopyCursorArrayFrom;
 use gecko_bindings::bindings::Gecko_CopyFontFamilyFrom;
 use gecko_bindings::bindings::Gecko_CopyImageValueFrom;
 use gecko_bindings::bindings::Gecko_CopyListStyleImageFrom;
 use gecko_bindings::bindings::Gecko_CopyListStyleTypeFrom;
 use gecko_bindings::bindings::Gecko_CopyMozBindingFrom;
 use gecko_bindings::bindings::Gecko_EnsureImageLayersLength;
 use gecko_bindings::bindings::Gecko_FontFamilyList_AppendGeneric;
 use gecko_bindings::bindings::Gecko_FontFamilyList_AppendNamed;
 use gecko_bindings::bindings::Gecko_FontFamilyList_Clear;
+use gecko_bindings::bindings::Gecko_SetCursorArrayLength;
+use gecko_bindings::bindings::Gecko_SetCursorImage;
 use gecko_bindings::bindings::Gecko_SetListStyleImage;
 use gecko_bindings::bindings::Gecko_SetListStyleImageNone;
 use gecko_bindings::bindings::Gecko_SetListStyleType;
 use gecko_bindings::bindings::Gecko_SetMozBinding;
 use gecko_bindings::bindings::Gecko_SetNullImageValue;
 use gecko_bindings::bindings::ServoComputedValuesBorrowedOrNull;
 use gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom};
 use gecko_bindings::structs;
@@ -2190,22 +2193,22 @@ clip-path
         let color = ${get_gecko_property("mColor")} as u32;
         convert_nscolor_to_rgba(color)
     }
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Pointing"
                   skip_longhands="cursor">
     pub fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) {
-        use properties::longhands::cursor::computed_value::T;
+        use properties::longhands::cursor::computed_value::Keyword;
         use style_traits::cursor::Cursor;
 
-        self.gecko.mCursor = match v {
-            T::AutoCursor => structs::NS_STYLE_CURSOR_AUTO,
-            T::SpecifiedCursor(cursor) => match cursor {
+        self.gecko.mCursor = match v.keyword {
+            Keyword::AutoCursor => structs::NS_STYLE_CURSOR_AUTO,
+            Keyword::SpecifiedCursor(cursor) => match cursor {
                 Cursor::None => structs::NS_STYLE_CURSOR_NONE,
                 Cursor::Default => structs::NS_STYLE_CURSOR_DEFAULT,
                 Cursor::Pointer => structs::NS_STYLE_CURSOR_POINTER,
                 Cursor::ContextMenu => structs::NS_STYLE_CURSOR_CONTEXT_MENU,
                 Cursor::Help => structs::NS_STYLE_CURSOR_HELP,
                 Cursor::Progress => structs::NS_STYLE_CURSOR_DEFAULT, // Gecko doesn't support "progress" yet
                 Cursor::Wait => structs::NS_STYLE_CURSOR_WAIT,
                 Cursor::Cell => structs::NS_STYLE_CURSOR_CELL,
@@ -2233,19 +2236,44 @@ clip-path
                 Cursor::NwseResize => structs::NS_STYLE_CURSOR_NWSE_RESIZE,
                 Cursor::ColResize => structs::NS_STYLE_CURSOR_COL_RESIZE,
                 Cursor::RowResize => structs::NS_STYLE_CURSOR_ROW_RESIZE,
                 Cursor::AllScroll => structs::NS_STYLE_CURSOR_ALL_SCROLL,
                 Cursor::ZoomIn => structs::NS_STYLE_CURSOR_ZOOM_IN,
                 Cursor::ZoomOut => structs::NS_STYLE_CURSOR_ZOOM_OUT,
             }
         } as u8;
+
+        unsafe {
+            Gecko_SetCursorArrayLength(&mut self.gecko, v.images.len());
+        }
+        for i in 0..v.images.len() {
+            let image = &v.images[i];
+            let extra_data = image.url.extra_data();
+            let (ptr, len) = image.url.as_slice_components();
+            unsafe {
+                Gecko_SetCursorImage(&mut self.gecko.mCursorImages[i],
+                                     ptr, len as u32,
+                                     extra_data.base.get(),
+                                     extra_data.referrer.get(),
+                                     extra_data.principal.get());
+            }
+            // We don't need to record this struct as uncacheable, like when setting
+            // background-image to a url() value, since only properties in reset structs
+            // are re-used from the applicable declaration cache, and the Pointing struct
+            // is an inherited struct.
+        }
     }
 
-    ${impl_simple_copy('cursor', 'mCursor')}
+    pub fn copy_cursor_from(&mut self, other: &Self) {
+        self.gecko.mCursor = other.gecko.mCursor;
+        unsafe {
+            Gecko_CopyCursorArrayFrom(&mut self.gecko, &other.gecko);
+        }
+    }
 </%self:impl_trait>
 
 <%self:impl_trait style_struct_name="Column"
                   skip_longhands="column-width column-count">
 
     pub fn set_column_width(&mut self, v: longhands::column_width::computed_value::T) {
         match v.0 {
             Some(au) => self.gecko.mColumnWidth.set(au),
--- a/servo/components/style/properties/longhand/pointing.mako.rs
+++ b/servo/components/style/properties/longhand/pointing.mako.rs
@@ -5,56 +5,146 @@
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% data.new_style_struct("Pointing", inherited=True, gecko_name="UserInterface") %>
 
 <%helpers:longhand name="cursor" animatable="False">
     pub use self::computed_value::T as SpecifiedValue;
     use values::NoViewportPercentage;
     use values::computed::ComputedValueAsSpecified;
+    use values::specified::url::SpecifiedUrl;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
     impl NoViewportPercentage for SpecifiedValue {}
 
     pub mod computed_value {
         use std::fmt;
         use style_traits::cursor::Cursor;
         use style_traits::ToCss;
+        use values::specified::url::SpecifiedUrl;
 
-        #[derive(Clone, PartialEq, Eq, Copy, Debug)]
+        #[derive(Clone, PartialEq, Copy, Debug)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        pub enum T {
+        pub enum Keyword {
             AutoCursor,
             SpecifiedCursor(Cursor),
         }
 
-        impl ToCss for T {
+        #[cfg(not(feature = "gecko"))]
+        pub type T = Keyword;
+
+        #[cfg(feature = "gecko")]
+        #[derive(Clone, PartialEq, Debug)]
+        pub struct Image {
+            pub url: SpecifiedUrl,
+            pub hotspot: Option<(f32, f32)>,
+        }
+
+        #[cfg(feature = "gecko")]
+        #[derive(Clone, PartialEq, Debug)]
+        pub struct T {
+            pub images: Vec<Image>,
+            pub keyword: Keyword,
+        }
+
+        impl ToCss for Keyword {
             fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                 match *self {
-                    T::AutoCursor => dest.write_str("auto"),
-                    T::SpecifiedCursor(c) => c.to_css(dest),
+                    Keyword::AutoCursor => dest.write_str("auto"),
+                    Keyword::SpecifiedCursor(c) => c.to_css(dest),
                 }
             }
         }
+
+        #[cfg(feature = "gecko")]
+        impl ToCss for Image {
+            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+                try!(self.url.to_css(dest));
+                if let Some((x, y)) = self.hotspot {
+                    try!(dest.write_str(" "));
+                    try!(x.to_css(dest));
+                    try!(dest.write_str(" "));
+                    try!(y.to_css(dest));
+                }
+                Ok(())
+            }
+        }
+
+        #[cfg(feature = "gecko")]
+        impl ToCss for T {
+            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
+                for url in &self.images {
+                    try!(url.to_css(dest));
+                    try!(dest.write_str(", "));
+                }
+                self.keyword.to_css(dest)
+            }
+        }
     }
 
+    #[cfg(not(feature = "gecko"))]
+    #[inline]
+    pub fn get_initial_value() -> computed_value::T {
+        computed_value::Keyword::AutoCursor
+    }
+
+    #[cfg(feature = "gecko")]
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
-        computed_value::T::AutoCursor
+        computed_value::T {
+            images: vec![],
+            keyword: computed_value::Keyword::AutoCursor
+        }
     }
+
+    impl Parse for computed_value::Keyword {
+        fn parse(input: &mut Parser) -> Result<computed_value::Keyword, ()> {
+            use std::ascii::AsciiExt;
+            use style_traits::cursor::Cursor;
+            let ident = try!(input.expect_ident());
+            if ident.eq_ignore_ascii_case("auto") {
+                Ok(computed_value::Keyword::AutoCursor)
+            } else {
+                Cursor::from_css_keyword(&ident).map(computed_value::Keyword::SpecifiedCursor)
+            }
+        }
+    }
+
+    #[cfg(feature = "gecko")]
+    fn parse_image(context: &ParserContext, input: &mut Parser) -> Result<computed_value::Image, ()> {
+        Ok(computed_value::Image {
+            url: try!(SpecifiedUrl::parse(context, input)),
+            hotspot: match input.try(|input| input.expect_number()) {
+                Ok(number) => Some((number, try!(input.expect_number()))),
+                Err(()) => None,
+            },
+        })
+    }
+
+    #[cfg(not(feature = "gecko"))]
     pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
-        use std::ascii::AsciiExt;
-        use style_traits::cursor::Cursor;
-        let ident = try!(input.expect_ident());
-        if ident.eq_ignore_ascii_case("auto") {
-            Ok(SpecifiedValue::AutoCursor)
-        } else {
-            Cursor::from_css_keyword(&ident)
-            .map(SpecifiedValue::SpecifiedCursor)
+        computed_value::Keyword::parse(input)
+    }
+
+    /// cursor: [<url> [<number> <number>]?]# [auto | default | ...]
+    #[cfg(feature = "gecko")]
+    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
+        let mut images = vec![];
+        loop {
+            match input.try(|input| parse_image(context, input)) {
+                Ok(image) => images.push(image),
+                Err(()) => break,
+            }
+            try!(input.expect_comma());
         }
+
+        Ok(computed_value::T {
+            images: images,
+            keyword: try!(computed_value::Keyword::parse(input)),
+        })
     }
 </%helpers:longhand>
 
 // NB: `pointer-events: auto` (and use of `pointer-events` in anything that isn't SVG, in fact)
 // is nonstandard, slated for CSS4-UI.
 // TODO(pcwalton): SVG-only values.
 ${helpers.single_keyword("pointer-events", "auto none", animatable=False)}