diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2017-02-18 04:13:59 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-18 04:13:59 -0800 |
commit | d3ba09e5884b8fb2513dd3fb7d9842de53fc3309 (patch) | |
tree | 7e2e0267608627b40554ed728671e0d233defeef | |
parent | eb6082fd7877fd3a14d14369f107a8b840f128d5 (diff) | |
parent | 66a28a4f5aec9375aa8c8e92533b14ada1a69b38 (diff) | |
download | servo-d3ba09e5884b8fb2513dd3fb7d9842de53fc3309.tar.gz servo-d3ba09e5884b8fb2513dd3fb7d9842de53fc3309.zip |
Auto merge of #15627 - Manishearth:stylo-svg, r=heycam
stylo: Finish all SVG properties
reviewed in bug https://bugzilla.mozilla.org/show_bug.cgi?id=1338388
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/15627)
<!-- Reviewable:end -->
-rw-r--r-- | components/style/build_gecko.rs | 6 | ||||
-rw-r--r-- | components/style/gecko_bindings/bindings.rs | 61 | ||||
-rw-r--r-- | components/style/gecko_bindings/structs_debug.rs | 17 | ||||
-rw-r--r-- | components/style/gecko_bindings/structs_release.rs | 17 | ||||
-rw-r--r-- | components/style/gecko_bindings/sugar/refptr.rs | 3 | ||||
-rw-r--r-- | components/style/parser.rs | 16 | ||||
-rw-r--r-- | components/style/properties/gecko.mako.rs | 242 | ||||
-rw-r--r-- | components/style/properties/helpers.mako.rs | 15 | ||||
-rw-r--r-- | components/style/properties/longhand/box.mako.rs | 1 | ||||
-rw-r--r-- | components/style/properties/longhand/effects.mako.rs | 33 | ||||
-rw-r--r-- | components/style/properties/longhand/inherited_svg.mako.rs | 192 | ||||
-rw-r--r-- | components/style/properties/shorthand/inherited_svg.mako.rs | 36 | ||||
-rw-r--r-- | components/style/values/computed/length.rs | 8 | ||||
-rw-r--r-- | components/style/values/computed/mod.rs | 81 | ||||
-rw-r--r-- | components/style/values/specified/length.rs | 28 | ||||
-rw-r--r-- | components/style/values/specified/mod.rs | 169 | ||||
-rw-r--r-- | components/style/values/specified/url.rs | 20 |
17 files changed, 844 insertions, 101 deletions
diff --git a/components/style/build_gecko.rs b/components/style/build_gecko.rs index 15ca0d55c3e..58064f4f897 100644 --- a/components/style/build_gecko.rs +++ b/components/style/build_gecko.rs @@ -365,6 +365,7 @@ mod bindings { "nsStylePadding", "nsStylePosition", "nsStyleSVG", + "nsStyleSVGPaint", "nsStyleSVGReset", "nsStyleTable", "nsStyleTableBorder", @@ -383,6 +384,7 @@ mod bindings { "PropertyValuePair", "Runnable", "ServoAttrSnapshot", + "ServoBundledURI", "ServoElementSnapshot", "SheetParsingMode", "StaticRefPtr", @@ -506,6 +508,7 @@ mod bindings { .whitelisted_function("Servo_.*") .whitelisted_function("Gecko_.*"); let structs_types = [ + "mozilla::css::URLValue", "RawGeckoDocument", "RawGeckoElement", "RawGeckoKeyframeList", @@ -523,6 +526,7 @@ mod bindings { "FontFamilyList", "FontFamilyType", "Keyframe", + "ServoBundledURI", "ServoElementSnapshot", "SheetParsingMode", "StyleBasicShape", @@ -550,6 +554,7 @@ mod bindings { "nsStyleCoord_CalcValue", "nsStyleDisplay", "nsStyleEffects", + "nsStyleFilter", "nsStyleFont", "nsStyleGradient", "nsStyleGradientStop", @@ -565,6 +570,7 @@ mod bindings { "nsStylePosition", "nsStyleQuoteValues", "nsStyleSVG", + "nsStyleSVGPaint", "nsStyleSVGReset", "nsStyleTable", "nsStyleTableBorder", diff --git a/components/style/gecko_bindings/bindings.rs b/components/style/gecko_bindings/bindings.rs index 46b7fe63bab..415bf710781 100644 --- a/components/style/gecko_bindings/bindings.rs +++ b/components/style/gecko_bindings/bindings.rs @@ -3,6 +3,7 @@ pub use nsstring::{nsACString, nsAString}; type nsACString_internal = nsACString; type nsAString_internal = nsAString; +use gecko_bindings::structs::mozilla::css::URLValue; use gecko_bindings::structs::RawGeckoDocument; use gecko_bindings::structs::RawGeckoElement; use gecko_bindings::structs::RawGeckoKeyframeList; @@ -20,6 +21,7 @@ use gecko_bindings::structs::TraversalRootBehavior; use gecko_bindings::structs::FontFamilyList; use gecko_bindings::structs::FontFamilyType; use gecko_bindings::structs::Keyframe; +use gecko_bindings::structs::ServoBundledURI; use gecko_bindings::structs::ServoElementSnapshot; use gecko_bindings::structs::SheetParsingMode; use gecko_bindings::structs::StyleBasicShape; @@ -69,6 +71,9 @@ unsafe impl Sync for nsStyleDisplay {} use gecko_bindings::structs::nsStyleEffects; unsafe impl Send for nsStyleEffects {} unsafe impl Sync for nsStyleEffects {} +use gecko_bindings::structs::nsStyleFilter; +unsafe impl Send for nsStyleFilter {} +unsafe impl Sync for nsStyleFilter {} use gecko_bindings::structs::nsStyleFont; unsafe impl Send for nsStyleFont {} unsafe impl Sync for nsStyleFont {} @@ -114,6 +119,9 @@ unsafe impl Sync for nsStyleQuoteValues {} use gecko_bindings::structs::nsStyleSVG; unsafe impl Send for nsStyleSVG {} unsafe impl Sync for nsStyleSVG {} +use gecko_bindings::structs::nsStyleSVGPaint; +unsafe impl Send for nsStyleSVGPaint {} +unsafe impl Sync for nsStyleSVGPaint {} use gecko_bindings::structs::nsStyleSVGReset; unsafe impl Send for nsStyleSVGReset {} unsafe impl Sync for nsStyleSVGReset {} @@ -590,11 +598,7 @@ extern "C" { } extern "C" { pub fn Gecko_SetListStyleImage(style_struct: *mut nsStyleList, - string_bytes: *const u8, - string_length: u32, - base_uri: *mut ThreadSafeURIHolder, - referrer: *mut ThreadSafeURIHolder, - principal: *mut ThreadSafePrincipalHolder); + uri: ServoBundledURI); } extern "C" { pub fn Gecko_CopyListStyleImageFrom(dest: *mut nsStyleList, @@ -616,17 +620,6 @@ extern "C" { 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, - src: *const nsStyleDisplay); -} -extern "C" { pub fn Gecko_GetNodeFlags(node: RawGeckoNodeBorrowed) -> u32; } extern "C" { @@ -708,6 +701,10 @@ extern "C" { -> *mut StyleBasicShape; } extern "C" { + pub fn Gecko_StyleClipPath_SetURLValue(clip: *mut StyleClipPath, + uri: ServoBundledURI); +} +extern "C" { pub fn Gecko_ResetFilters(effects: *mut nsStyleEffects, new_len: usize); } extern "C" { @@ -715,6 +712,38 @@ extern "C" { aDest: *mut nsStyleEffects); } extern "C" { + pub fn Gecko_nsStyleFilter_SetURLValue(effects: *mut nsStyleFilter, + uri: ServoBundledURI); +} +extern "C" { + pub fn Gecko_nsStyleSVGPaint_CopyFrom(dest: *mut nsStyleSVGPaint, + src: *const nsStyleSVGPaint); +} +extern "C" { + pub fn Gecko_nsStyleSVGPaint_SetURLValue(paint: *mut nsStyleSVGPaint, + uri: ServoBundledURI); +} +extern "C" { + pub fn Gecko_nsStyleSVGPaint_Reset(paint: *mut nsStyleSVGPaint); +} +extern "C" { + pub fn Gecko_nsStyleSVG_SetDashArrayLength(svg: *mut nsStyleSVG, + len: u32); +} +extern "C" { + pub fn Gecko_nsStyleSVG_CopyDashArray(dst: *mut nsStyleSVG, + src: *const nsStyleSVG); +} +extern "C" { + pub fn Gecko_NewURLValue(uri: ServoBundledURI) -> *mut URLValue; +} +extern "C" { + pub fn Gecko_AddRefCSSURLValueArbitraryThread(aPtr: *mut URLValue); +} +extern "C" { + pub fn Gecko_ReleaseCSSURLValueArbitraryThread(aPtr: *mut URLValue); +} +extern "C" { pub fn Gecko_FillAllBackgroundLists(layers: *mut nsStyleImageLayers, max_len: u32); } diff --git a/components/style/gecko_bindings/structs_debug.rs b/components/style/gecko_bindings/structs_debug.rs index cf1c9ce517b..302aa8bb0a1 100644 --- a/components/style/gecko_bindings/structs_debug.rs +++ b/components/style/gecko_bindings/structs_debug.rs @@ -25363,6 +25363,23 @@ pub mod root { pub type ThreadSafePrincipalHolder = root::nsMainThreadPtrHolder<root::nsIPrincipal>; pub type ThreadSafeURIHolder = root::nsMainThreadPtrHolder<root::nsIURI>; + #[repr(C)] + #[derive(Debug, Copy)] + pub struct ServoBundledURI { + pub mURLString: *const u8, + pub mURLStringLength: u32, + pub mBaseURI: *mut root::ThreadSafeURIHolder, + pub mReferrer: *mut root::ThreadSafeURIHolder, + pub mPrincipal: *mut root::ThreadSafePrincipalHolder, + } + #[test] + fn bindgen_test_layout_ServoBundledURI() { + assert_eq!(::std::mem::size_of::<ServoBundledURI>() , 40usize); + assert_eq!(::std::mem::align_of::<ServoBundledURI>() , 8usize); + } + impl Clone for ServoBundledURI { + fn clone(&self) -> Self { *self } + } pub type nsMediaFeatureValueGetter = ::std::option::Option<unsafe extern "C" fn(aPresContext: *mut root::nsPresContext, diff --git a/components/style/gecko_bindings/structs_release.rs b/components/style/gecko_bindings/structs_release.rs index d50cc1ee1a0..b7f7be53fb8 100644 --- a/components/style/gecko_bindings/structs_release.rs +++ b/components/style/gecko_bindings/structs_release.rs @@ -24719,6 +24719,23 @@ pub mod root { pub type ThreadSafePrincipalHolder = root::nsMainThreadPtrHolder<root::nsIPrincipal>; pub type ThreadSafeURIHolder = root::nsMainThreadPtrHolder<root::nsIURI>; + #[repr(C)] + #[derive(Debug, Copy)] + pub struct ServoBundledURI { + pub mURLString: *const u8, + pub mURLStringLength: u32, + pub mBaseURI: *mut root::ThreadSafeURIHolder, + pub mReferrer: *mut root::ThreadSafeURIHolder, + pub mPrincipal: *mut root::ThreadSafePrincipalHolder, + } + #[test] + fn bindgen_test_layout_ServoBundledURI() { + assert_eq!(::std::mem::size_of::<ServoBundledURI>() , 40usize); + assert_eq!(::std::mem::align_of::<ServoBundledURI>() , 8usize); + } + impl Clone for ServoBundledURI { + fn clone(&self) -> Self { *self } + } pub type nsMediaFeatureValueGetter = ::std::option::Option<unsafe extern "C" fn(aPresContext: *mut root::nsPresContext, diff --git a/components/style/gecko_bindings/sugar/refptr.rs b/components/style/gecko_bindings/sugar/refptr.rs index 1689522e892..0202afb3292 100644 --- a/components/style/gecko_bindings/sugar/refptr.rs +++ b/components/style/gecko_bindings/sugar/refptr.rs @@ -264,6 +264,9 @@ impl_threadsafe_refcount!(::gecko_bindings::structs::nsStyleQuoteValues, impl_threadsafe_refcount!(::gecko_bindings::structs::nsCSSValueSharedList, Gecko_AddRefCSSValueSharedListArbitraryThread, Gecko_ReleaseCSSValueSharedListArbitraryThread); +impl_threadsafe_refcount!(::gecko_bindings::structs::mozilla::css::URLValue, + Gecko_AddRefCSSURLValueArbitraryThread, + Gecko_ReleaseCSSURLValueArbitraryThread); /// A Gecko `ThreadSafePrincipalHolder` wrapped in a safe refcounted pointer, to /// use during stylesheet parsing and style computation. pub type GeckoArcPrincipal = RefPtr<::gecko_bindings::structs::ThreadSafePrincipalHolder>; diff --git a/components/style/parser.rs b/components/style/parser.rs index 12e6290304f..4145650b2c8 100644 --- a/components/style/parser.rs +++ b/components/style/parser.rs @@ -110,6 +110,22 @@ impl<T> Parse for Vec<T> where T: Parse + OneOrMoreCommaSeparated { } } +/// Parse a non-empty space-separated or comma-separated list of objects parsed by parse_one +pub fn parse_space_or_comma_separated<F, T>(input: &mut Parser, mut parse_one: F) + -> Result<Vec<T>, ()> + where F: FnMut(&mut Parser) -> Result<T, ()> { + let first = parse_one(input)?; + let mut vec = vec![first]; + loop { + let _ = input.try(|i| i.expect_comma()); + if let Ok(val) = input.try(|i| parse_one(i)) { + vec.push(val) + } else { + break + } + } + Ok(vec) +} impl Parse for UnicodeRange { fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { UnicodeRange::parse(input) diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 722faf11326..2e60378dd1c 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -11,6 +11,7 @@ <%namespace name="helpers" file="/helpers.mako.rs" /> use app_units::Au; +use cssparser::Color; use custom_properties::ComputedValuesMap; use gecko_bindings::bindings; % for style_struct in data.style_structs: @@ -25,7 +26,6 @@ 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; @@ -36,7 +36,6 @@ use gecko_bindings::bindings::Gecko_NewCSSShadowArray; 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}; @@ -274,6 +273,19 @@ def set_gecko_property(ffi_name, expr): } </%def> + +/// Convert a Servo color into an nscolor; with currentColor as 0 +/// +/// Call sites will need to be updated after https://bugzilla.mozilla.org/show_bug.cgi?id=760345 +fn color_to_nscolor_zero_currentcolor(color: Color) -> structs::nscolor { + match color { + Color::RGBA(rgba) => { + convert_rgba_to_nscolor(&rgba) + }, + Color::CurrentColor => 0, + } +} + <%def name="impl_color_setter(ident, gecko_ffi_name, complex_color=True)"> #[allow(unreachable_code)] #[allow(non_snake_case)] @@ -281,12 +293,7 @@ def set_gecko_property(ffi_name, expr): % if complex_color: let result = v.into(); % else: - use cssparser::Color; - let result = match v { - Color::RGBA(rgba) => convert_rgba_to_nscolor(&rgba), - // FIXME #13547 - Color::CurrentColor => 0, - }; + let result = color_to_nscolor_zero_currentcolor(v); % endif ${set_gecko_property(gecko_ffi_name, "result")} } @@ -306,7 +313,6 @@ def set_gecko_property(ffi_name, expr): % if complex_color: ${get_gecko_property(gecko_ffi_name)}.into() % else: - use cssparser::Color; Color::RGBA(convert_nscolor_to_rgba(${get_gecko_property(gecko_ffi_name)})) % endif } @@ -369,6 +375,58 @@ def set_gecko_property(ffi_name, expr): % endif </%def> +<%def name="impl_svg_paint(ident, gecko_ffi_name, need_clone=False, complex_color=True)"> + #[allow(non_snake_case)] + pub fn set_${ident}(&mut self, mut v: longhands::${ident}::computed_value::T) { + use values::computed::SVGPaintKind; + use self::structs::nsStyleSVGPaintType; + + let ref mut paint = ${get_gecko_property(gecko_ffi_name)}; + unsafe { + bindings::Gecko_nsStyleSVGPaint_Reset(paint); + } + let fallback = v.fallback.take(); + match v.kind { + SVGPaintKind::None => return, + SVGPaintKind::ContextFill => { + paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_ContextFill; + } + SVGPaintKind::ContextStroke => { + paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_ContextStroke; + } + SVGPaintKind::PaintServer(url) => { + unsafe { + if let Some(ffi) = url.for_ffi() { + bindings::Gecko_nsStyleSVGPaint_SetURLValue(paint, ffi); + } else { + return; + } + } + } + SVGPaintKind::Color(color) => { + paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_Color; + unsafe { + *paint.mPaint.mColor.as_mut() = color_to_nscolor_zero_currentcolor(color); + } + } + } + + if let Some(fallback) = fallback { + paint.mFallbackColor = color_to_nscolor_zero_currentcolor(fallback); + } + } + + #[allow(non_snake_case)] + pub fn copy_${ident}_from(&mut self, other: &Self) { + unsafe { + bindings::Gecko_nsStyleSVGPaint_CopyFrom( + &mut ${get_gecko_property(gecko_ffi_name)}, + & ${get_gecko_property(gecko_ffi_name, "other")} + ); + } + } +</%def> + <%def name="impl_app_units(ident, gecko_ffi_name, need_clone, round_to_pixels=False)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { @@ -455,6 +513,41 @@ def set_gecko_property(ffi_name, expr): % endif </%def> +<%def name="impl_css_url(ident, gecko_ffi_name, need_clone=False)"> + #[allow(non_snake_case)] + pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { + use gecko_bindings::sugar::refptr::RefPtr; + match v { + Either::First(url) => { + let refptr = unsafe { + if let Some(ffi) = url.for_ffi() { + let ptr = bindings::Gecko_NewURLValue(ffi); + RefPtr::from_addrefed(ptr) + } else { + self.gecko.${gecko_ffi_name}.clear(); + return; + } + }; + self.gecko.${gecko_ffi_name}.set_move(refptr) + } + Either::Second(_none) => { + unsafe { + self.gecko.${gecko_ffi_name}.clear(); + } + } + } + } + #[allow(non_snake_case)] + pub fn copy_${ident}_from(&mut self, other: &Self) { + unsafe { + self.gecko.${gecko_ffi_name}.set(&other.gecko.${gecko_ffi_name}); + } + } + % if need_clone: + <% raise Exception("Do not know how to handle clone ") %> + % endif +</%def> + <%def name="impl_logical(name, need_clone=False, **kwargs)"> ${helpers.logical_setter(name, need_clone)} </%def> @@ -543,6 +636,8 @@ impl Debug for ${style_struct.gecko_struct_name} { "Number": impl_simple, "Opacity": impl_simple, "CSSColor": impl_color, + "SVGPaint": impl_svg_paint, + "UrlOrNone": impl_css_url, } def longhand_method(longhand): @@ -1221,7 +1316,7 @@ fn static_assert() { animation-name animation-delay animation-duration animation-direction animation-fill-mode animation-play-state animation-iteration-count animation-timing-function - -moz-binding page-break-before page-break-after + page-break-before page-break-after scroll-snap-points-x scroll-snap-points-y transform scroll-snap-type-y scroll-snap-coordinate perspective-origin transform-origin""" %> @@ -1316,33 +1411,6 @@ fn static_assert() { <%call expr="impl_coord_copy('vertical_align', 'mVerticalAlign')"></%call> - #[allow(non_snake_case)] - pub fn set__moz_binding(&mut self, v: longhands::_moz_binding::computed_value::T) { - use values::Either; - match v { - Either::Second(_none) => debug_assert!(self.gecko.mBinding.mRawPtr.is_null()), - Either::First(ref url) => { - let extra_data = url.extra_data(); - let (ptr, len) = match url.as_slice_components() { - Ok(value) => value, - Err(_) => (ptr::null(), 0), - }; - unsafe { - Gecko_SetMozBinding(&mut self.gecko, - ptr, - len as u32, - extra_data.base.get(), - extra_data.referrer.get(), - extra_data.principal.get()); - } - } - } - } - #[allow(non_snake_case)] - pub fn copy__moz_binding_from(&mut self, other: &Self) { - unsafe { Gecko_CopyMozBindingFrom(&mut self.gecko, &other.gecko); } - } - // Temp fix for Bugzilla bug 24000. // Map 'auto' and 'avoid' to false, and 'always', 'left', and 'right' to true. // "A conforming user agent may interpret the values 'left' and 'right' @@ -2066,17 +2134,13 @@ fn static_assert() { } } Either::First(ref url) => { - let (ptr, len) = match url.as_slice_components() { - Ok(value) | Err(value) => value - }; - let extra_data = url.extra_data(); unsafe { - Gecko_SetListStyleImage(&mut self.gecko, - ptr, - len as u32, - extra_data.base.get(), - extra_data.referrer.get(), - extra_data.principal.get()); + if let Some(ffi) = url.for_ffi() { + Gecko_SetListStyleImage(&mut self.gecko, + ffi); + } else { + Gecko_SetListStyleImageNone(&mut self.gecko); + } } // 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 @@ -2147,7 +2211,6 @@ fn static_assert() { <%self:impl_trait style_struct_name="Effects" skip_longhands="box-shadow filter"> pub fn set_box_shadow(&mut self, v: longhands::box_shadow::computed_value::T) { - use cssparser::Color; self.gecko.mBoxShadow.replace_with_new(v.0.len() as u32); @@ -2178,8 +2241,6 @@ fn static_assert() { } pub fn clone_box_shadow(&self) -> longhands::box_shadow::computed_value::T { - use cssparser::Color; - let buf = self.gecko.mBoxShadow.iter().map(|shadow| { longhands::box_shadow::single_value::computed_value::T { offset_x: Au(shadow.mXOffset), @@ -2194,7 +2255,6 @@ fn static_assert() { } pub fn set_filter(&mut self, v: longhands::filter::computed_value::T) { - use cssparser::Color; use properties::longhands::filter::computed_value::Filter::*; use gecko_bindings::structs::nsCSSShadowArray; use gecko_bindings::structs::nsStyleFilter; @@ -2279,6 +2339,13 @@ fn static_assert() { Color::CurrentColor => 0, }; } + Url(ref url) => { + unsafe { + if let Some(ffi) = url.for_ffi() { + bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, ffi); + } + } + } } } } @@ -2316,7 +2383,6 @@ fn static_assert() { ${impl_keyword('text_align', 'mTextAlign', text_align_keyword, need_clone=False)} pub fn set_text_shadow(&mut self, v: longhands::text_shadow::computed_value::T) { - use cssparser::Color; self.gecko.mTextShadow.replace_with_new(v.0.len() as u32); for (servo, gecko_shadow) in v.0.into_iter() @@ -2344,7 +2410,6 @@ fn static_assert() { } pub fn clone_text_shadow(&self) -> longhands::text_shadow::computed_value::T { - use cssparser::Color; let buf = self.gecko.mTextShadow.iter().map(|shadow| { longhands::text_shadow::computed_value::TextShadow { @@ -2636,7 +2701,13 @@ clip-path clip_path.mType = StyleShapeSourceType::None; match v { - ShapeSource::Url(..) => warn!("stylo: clip-path: url() not yet implemented"), + ShapeSource::Url(ref url) => { + unsafe { + if let Some(ffi) = url.for_ffi() { + bindings::Gecko_StyleClipPath_SetURLValue(clip_path, ffi); + } + } + } ShapeSource::None => {} // don't change the type ShapeSource::Box(reference) => { clip_path.mReferenceBox = reference.into(); @@ -2726,32 +2797,51 @@ clip-path Gecko_CopyClipPathValueFrom(&mut self.gecko.mClipPath, &other.gecko.mClipPath); } } +</%self:impl_trait> - pub fn clone_clip_path(&self) -> longhands::clip_path::computed_value::T { - use gecko_bindings::structs::StyleShapeSourceType; - use gecko_bindings::structs::StyleGeometryBox; - use values::computed::basic_shape::*; - let ref clip_path = self.gecko.mClipPath; +<%self:impl_trait style_struct_name="InheritedSVG" + skip_longhands="paint-order stroke-dasharray" + skip_additionals="*"> + pub fn set_paint_order(&mut self, v: longhands::paint_order::computed_value::T) { + use self::longhands::paint_order; - match clip_path.mType { - StyleShapeSourceType::None => ShapeSource::None, - StyleShapeSourceType::Box => { - ShapeSource::Box(clip_path.mReferenceBox.into()) - } - StyleShapeSourceType::URL => { - warn!("stylo: clip-path: url() not implemented yet"); - Default::default() - } - StyleShapeSourceType::Shape => { - let reference = if let StyleGeometryBox::NoBox = clip_path.mReferenceBox { - None - } else { - Some(clip_path.mReferenceBox.into()) + if v.0 == 0 { + self.gecko.mPaintOrder = structs::NS_STYLE_PAINT_ORDER_NORMAL as u8; + } else { + let mut order = 0; + + for pos in 0..3 { + let geckoval = match v.bits_at(pos) { + paint_order::FILL => structs::NS_STYLE_PAINT_ORDER_FILL as u8, + paint_order::STROKE => structs::NS_STYLE_PAINT_ORDER_STROKE as u8, + paint_order::MARKERS => structs::NS_STYLE_PAINT_ORDER_MARKERS as u8, + _ => unreachable!(), }; - let union = clip_path.__bindgen_anon_1; - let shape = unsafe { &**union.mBasicShape.as_ref() }; - ShapeSource::Shape(shape.into(), reference) + order |= geckoval << (pos * structs::NS_STYLE_PAINT_ORDER_BITWIDTH as u8); } + + self.gecko.mPaintOrder = order; + } + } + + ${impl_simple_copy('paint_order', 'mPaintOrder')} + + pub fn set_stroke_dasharray(&mut self, v: longhands::stroke_dasharray::computed_value::T) { + unsafe { + bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut self.gecko, v.0.len() as u32); + } + + for (mut gecko, servo) in self.gecko.mStrokeDasharray.iter_mut().zip(v.0.into_iter()) { + match servo { + Either::First(lop) => gecko.set(lop), + Either::Second(number) => gecko.set_value(CoordDataValue::Factor(number)), + } + } + } + + pub fn copy_stroke_dasharray_from(&mut self, other: &Self) { + unsafe { + bindings::Gecko_nsStyleSVG_CopyDashArray(&mut self.gecko, &other.gecko); } } </%self:impl_trait> diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index e89ebaeb21a..c9b26aa6489 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -66,7 +66,8 @@ We assume that the default/initial value is an empty vector for these. `initial_value` need not be defined for these. </%doc> -<%def name="vector_longhand(name, gecko_only=False, allow_empty=False, delegate_animate=False, **kwargs)"> +<%def name="vector_longhand(name, gecko_only=False, allow_empty=False, + delegate_animate=False, space_separated_allowed=False, **kwargs)"> <%call expr="longhand(name, **kwargs)"> % if not gecko_only: use std::fmt; @@ -86,6 +87,7 @@ use properties::{CSSWideKeyword, DeclaredValue, ShorthandId}; use values::computed::{Context, ToComputedValue}; use values::{computed, specified}; + use values::{Auto, Either, None_, Normal}; ${caller.body()} } @@ -166,16 +168,23 @@ } pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> { + use parser::parse_space_or_comma_separated; + + <% + parse_func = "Parser::parse_comma_separated" + if space_separated_allowed: + parse_func = "parse_space_or_comma_separated" + %> % if allow_empty: if input.try(|input| input.expect_ident_matching("none")).is_ok() { Ok(SpecifiedValue(Vec::new())) } else { - input.parse_comma_separated(|parser| { + ${parse_func}(input, |parser| { single_value::parse(context, parser) }).map(SpecifiedValue) } % else: - input.parse_comma_separated(|parser| { + ${parse_func}(input, |parser| { single_value::parse(context, parser) }).map(SpecifiedValue) % endif diff --git a/components/style/properties/longhand/box.mako.rs b/components/style/properties/longhand/box.mako.rs index a0284a23bec..6cb2026b1d6 100644 --- a/components/style/properties/longhand/box.mako.rs +++ b/components/style/properties/longhand/box.mako.rs @@ -1922,6 +1922,7 @@ ${helpers.single_keyword("-moz-appearance", ${helpers.predefined_type("-moz-binding", "UrlOrNone", "Either::Second(None_)", products="gecko", animatable="False", + gecko_ffi_name="mBinding", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-binding)", disable_when_testing="True", boxed=True)} diff --git a/components/style/properties/longhand/effects.mako.rs b/components/style/properties/longhand/effects.mako.rs index eb4879f7afb..b5dbdf60dc8 100644 --- a/components/style/properties/longhand/effects.mako.rs +++ b/components/style/properties/longhand/effects.mako.rs @@ -94,6 +94,7 @@ ${helpers.predefined_type("clip", use style_traits::{self, ToCss}; use values::{CSSFloat, HasViewportPercentage}; use values::specified::{Angle, CSSColor, Length, Shadow}; + use values::specified::url::SpecifiedUrl; impl HasViewportPercentage for SpecifiedValue { fn has_viewport_percentage(&self) -> bool { @@ -129,6 +130,7 @@ ${helpers.predefined_type("clip", Sepia(CSSFloat), % if product == "gecko": DropShadow(Shadow), + Url(SpecifiedUrl), % endif } @@ -136,7 +138,8 @@ ${helpers.predefined_type("clip", use app_units::Au; use values::CSSFloat; use values::computed::{CSSColor, Shadow}; - use values::specified::{Angle}; + use values::specified::Angle; + use values::specified::url::SpecifiedUrl; #[derive(Clone, PartialEq, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))] @@ -152,6 +155,7 @@ ${helpers.predefined_type("clip", Sepia(CSSFloat), % if product == "gecko": DropShadow(Shadow), + Url(SpecifiedUrl), % endif } @@ -262,6 +266,11 @@ ${helpers.predefined_type("clip", try!(shadow.color.to_css(dest)); try!(dest.write_str(")")); } + computed_value::Filter::Url(ref url) => { + dest.write_str("url(")?; + url.to_css(dest)?; + dest.write_str(")")?; + } % endif } Ok(()) @@ -302,6 +311,11 @@ ${helpers.predefined_type("clip", } try!(dest.write_str(")")); } + SpecifiedFilter::Url(ref url) => { + dest.write_str("url(")?; + url.to_css(dest)?; + dest.write_str(")")?; + } % endif } Ok(()) @@ -319,6 +333,11 @@ ${helpers.predefined_type("clip", return Ok(SpecifiedValue(filters)) } loop { + % if product == "gecko": + if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) { + filters.push(SpecifiedFilter::Url(url)); + } else + % endif if let Ok(function_name) = input.try(|input| input.expect_function()) { filters.push(try!(input.parse_nested_block(|input| { match_ignore_ascii_case! { function_name, @@ -375,6 +394,9 @@ ${helpers.predefined_type("clip", SpecifiedFilter::DropShadow(ref shadow) => { computed_value::Filter::DropShadow(shadow.to_computed_value(context)) }, + SpecifiedFilter::Url(ref url) => { + computed_value::Filter::Url(url.to_computed_value(context)) + } % endif } }).collect() } @@ -394,9 +416,14 @@ ${helpers.predefined_type("clip", computed_value::Filter::Saturate(factor) => SpecifiedFilter::Saturate(factor), computed_value::Filter::Sepia(factor) => SpecifiedFilter::Sepia(factor), % if product == "gecko": - computed_value::Filter::DropShadow(shadow) => { + computed_value::Filter::DropShadow(ref shadow) => { SpecifiedFilter::DropShadow( - ToComputedValue::from_computed_value(&shadow), + ToComputedValue::from_computed_value(shadow), + ) + } + computed_value::Filter::Url(ref url) => { + SpecifiedFilter::Url( + ToComputedValue::from_computed_value(url), ) } % endif diff --git a/components/style/properties/longhand/inherited_svg.mako.rs b/components/style/properties/longhand/inherited_svg.mako.rs index 56987f0ba75..ffba35e83f3 100644 --- a/components/style/properties/longhand/inherited_svg.mako.rs +++ b/components/style/properties/longhand/inherited_svg.mako.rs @@ -33,6 +33,14 @@ ${helpers.single_keyword("color-interpolation-filters", "auto sRGB linearRGB", animatable=False, spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty")} +${helpers.predefined_type( + "fill", "SVGPaint", + "::values::computed::SVGPaint::black()", + products="gecko", + animatable=False, + boxed=True, + spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint")} + ${helpers.predefined_type("fill-opacity", "Opacity", "1.0", products="gecko", animatable=False, spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty")} @@ -49,6 +57,23 @@ ${helpers.single_keyword("shape-rendering", animatable=False, spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty")} +${helpers.predefined_type( + "stroke", "SVGPaint", + "Default::default()", + products="gecko", + animatable=False, + boxed=True, + spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint")} + +${helpers.predefined_type( + "stroke-width", "LengthOrPercentage", + "computed::LengthOrPercentage::one()", + "parse_numbers_are_pixels_non_negative", + products="gecko", + animatable=True, + needs_context=False, + spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth")} + ${helpers.single_keyword("stroke-linecap", "butt round square", products="gecko", animatable=False, spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty")} @@ -67,6 +92,23 @@ ${helpers.predefined_type("stroke-opacity", "Opacity", "1.0", products="gecko", animatable=False, spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty")} +${helpers.predefined_type("stroke-dasharray", "LoPOrNumber", "Either::Second(0.0)", + "parse_non_negative", + vector="True", + products="gecko", + animatable="False", + space_separated_allowed="True", + spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")} + +${helpers.predefined_type( + "stroke-dashoffset", "LengthOrPercentage", + "computed::LengthOrPercentage::zero()", + "parse_numbers_are_pixels", + products="gecko", + animatable=True, + needs_context=False, + spec="https://www.w3.org/TR/SVG2/painting.html#StrokeDashing")} + // Section 14 - Clipping, Masking and Compositing ${helpers.single_keyword("clip-rule", "nonzero evenodd", products="gecko", @@ -74,3 +116,153 @@ ${helpers.single_keyword("clip-rule", "nonzero evenodd", gecko_inexhaustive=True, animatable=False, spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")} + +${helpers.predefined_type("marker-start", "UrlOrNone", "Either::Second(None_)", + products="gecko", + animatable="False", + spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")} + +${helpers.predefined_type("marker-mid", "UrlOrNone", "Either::Second(None_)", + products="gecko", + animatable="False", + spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")} + +${helpers.predefined_type("marker-end", "UrlOrNone", "Either::Second(None_)", + products="gecko", + animatable="False", + spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")} + +<%helpers:longhand name="paint-order" + animatable="False" + products="gecko" + spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder"> + + use values::computed::ComputedValueAsSpecified; + use std::fmt; + use style_traits::ToCss; + use values::HasViewportPercentage; + + pub const NORMAL: u8 = 0; + pub const FILL: u8 = 1; + pub const STROKE: u8 = 2; + pub const MARKERS: u8 = 3; + + // number of bits for each component + pub const SHIFT: u8 = 2; + // mask with above bits set + pub const MASK: u8 = 0b11; + // number of non-normal keyword values + pub const COUNT: u8 = 3; + // all keywords + pub const ALL: [u8; 3] = [FILL, STROKE, MARKERS]; + + /// Represented as a six-bit field, of 3 two-bit pairs + /// + /// Each pair can be set to FILL, STROKE, or MARKERS + /// Lowest significant bit pairs are highest priority. + /// `normal` is the empty bitfield. The three pairs are + /// never zero in any case other than `normal`. + /// + /// Higher priority values, i.e. the values specified first, + /// will be painted first (and may be covered by paintings of lower priority) + #[derive(PartialEq, Clone, Copy, Debug)] + #[cfg_attr(feature = "servo", derive(HeapSizeOf))] + pub struct SpecifiedValue(pub u8); + + pub mod computed_value { + pub use super::SpecifiedValue as T; + } + + pub fn get_initial_value() -> SpecifiedValue { + SpecifiedValue(NORMAL) + } + + impl SpecifiedValue { + pub fn bits_at(&self, pos: u8) -> u8 { + (self.0 >> pos * SHIFT) & MASK + } + } + + pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> { + if let Ok(()) = input.try(|i| i.expect_ident_matching("normal")) { + Ok(SpecifiedValue(0)) + } else { + let mut value = 0; + // bitfield representing what we've seen so far + // bit 1 is fill, bit 2 is stroke, bit 3 is markers + let mut seen = 0; + let mut pos = 0; + + loop { + + let result = input.try(|i| { + match_ignore_ascii_case! { i.expect_ident()?, + "fill" => Ok(FILL), + "stroke" => Ok(STROKE), + "markers" => Ok(MARKERS), + _ => Err(()) + } + }); + + match result { + Ok(val) => { + if (seen & (1 << val)) != 0 { + // don't parse the same ident twice + return Err(()) + } else { + value |= val << (pos * SHIFT); + seen |= 1 << val; + pos += 1; + } + } + Err(()) => break, + } + } + + if value == 0 { + // couldn't find any keyword + Err(()) + } else { + // fill in rest + for i in pos..COUNT { + for paint in &ALL { + // if not seen, set bit at position, mark as seen + if (seen & (1 << paint)) == 0 { + seen |= 1 << paint; + value |= paint << (i * SHIFT); + break; + } + } + } + + Ok(SpecifiedValue(value)) + } + } + } + + impl ToCss for SpecifiedValue { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + if self.0 == 0 { + return dest.write_str("normal") + } + + for pos in 0..COUNT { + if pos != 0 { + dest.write_str(" ")? + } + match self.bits_at(pos) { + FILL => dest.write_str("fill")?, + STROKE => dest.write_str("stroke")?, + MARKERS => dest.write_str("markers")?, + _ => unreachable!(), + } + } + Ok(()) + } + } + + no_viewport_percentage!(SpecifiedValue); + + impl ComputedValueAsSpecified for SpecifiedValue { } +</%helpers:longhand> + diff --git a/components/style/properties/shorthand/inherited_svg.mako.rs b/components/style/properties/shorthand/inherited_svg.mako.rs new file mode 100644 index 00000000000..24d6e37890c --- /dev/null +++ b/components/style/properties/shorthand/inherited_svg.mako.rs @@ -0,0 +1,36 @@ +/* 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/. */ + +<%namespace name="helpers" file="/helpers.mako.rs" /> + +<%helpers:shorthand name="marker" products="gecko" + sub_properties="marker-start marker-end marker-mid" + spec="https://www.w3.org/TR/SVG2/painting.html#MarkerShorthand"> + use values::specified::UrlOrNone; + + pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> { + let url = UrlOrNone::parse(context, input)?; + + Ok(Longhands { + marker_start: url.clone(), + marker_mid: url.clone(), + marker_end: url, + }) + } + + impl<'a> LonghandsToSerialize<'a> { + fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + if let DeclaredValue::Value(ref start) = *self.marker_start { + if let DeclaredValue::Value(ref mid) = *self.marker_mid { + if let DeclaredValue::Value(ref end) = *self.marker_end { + if start == mid && mid == end { + start.to_css(dest)?; + } + } + } + } + Ok(()) + } + } +</%helpers:shorthand> diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index 74c9fd6287f..492bbfb4bc0 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -4,7 +4,7 @@ //! `<length>` computed values, and related ones. -use app_units::Au; +use app_units::{Au, AU_PER_PX}; use ordered_float::NotNaN; use std::fmt; use style_traits::ToCss; @@ -195,6 +195,12 @@ impl LengthOrPercentage { LengthOrPercentage::Length(Au(0)) } + #[inline] + /// 1px length value for SVG defaults + pub fn one() -> LengthOrPercentage { + LengthOrPercentage::Length(Au(AU_PER_PX)) + } + /// Returns true if the computed value is absolute 0 or 0%. /// /// (Returns false for calc() values, even if ones that may resolve to zero.) diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 3c22ab1a8bb..b505d2364c8 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -10,7 +10,7 @@ use font_metrics::FontMetricsProvider; use properties::ComputedValues; use std::fmt; use style_traits::ToCss; -use super::{CSSFloat, specified}; +use super::{CSSFloat, RGBA, specified}; pub use cssparser::Color as CSSColor; pub use self::image::{AngleOrCorner, EndingShape as GradientShape, Gradient, GradientKind, Image}; @@ -19,7 +19,7 @@ pub use super::{Auto, Either, None_}; #[cfg(feature = "gecko")] pub use super::specified::{AlignJustifyContent, AlignJustifySelf}; pub use super::specified::{Angle, BorderStyle, GridLine, Time, UrlOrNone}; -pub use super::specified::url::UrlExtraData; +pub use super::specified::url::{SpecifiedUrl, UrlExtraData}; pub use self::length::{CalcLengthOrPercentage, Length, LengthOrNumber, LengthOrPercentage, LengthOrPercentageOrAuto}; pub use self::length::{LengthOrPercentageOrAutoOrContent, LengthOrPercentageOrNone, LengthOrNone}; pub use self::position::Position; @@ -185,6 +185,83 @@ pub type Number = CSSFloat; pub type Opacity = CSSFloat; +/// An SVG paint value +/// +/// https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct SVGPaint { + /// The paint source + pub kind: SVGPaintKind, + /// The fallback color + pub fallback: Option<CSSColor>, +} + +impl Default for SVGPaint { + fn default() -> Self { + SVGPaint { + kind: SVGPaintKind::None, + fallback: None, + } + } +} + +impl SVGPaint { + /// Opaque black color + pub fn black() -> Self { + let rgba = RGBA::from_floats(0., 0., 0., 1.); + SVGPaint { + kind: SVGPaintKind::Color(CSSColor::RGBA(rgba)), + fallback: None, + } + } +} + +/// An SVG paint value without the fallback +/// +/// Whereas the spec only allows PaintServer +/// to have a fallback, Gecko lets the context +/// properties have a fallback as well. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum SVGPaintKind { + /// `none` + None, + /// `<color>` + Color(CSSColor), + /// `url(...)` + PaintServer(SpecifiedUrl), + /// `context-fill` + ContextFill, + /// `context-stroke` + ContextStroke, +} + +impl ToCss for SVGPaintKind { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + SVGPaintKind::None => dest.write_str("none"), + SVGPaintKind::ContextStroke => dest.write_str("context-stroke"), + SVGPaintKind::ContextFill => dest.write_str("context-fill"), + SVGPaintKind::Color(ref color) => color.to_css(dest), + SVGPaintKind::PaintServer(ref server) => server.to_css(dest), + } + } +} + +impl ToCss for SVGPaint { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + self.kind.to_css(dest)?; + if let Some(ref fallback) = self.fallback { + fallback.to_css(dest)?; + } + Ok(()) + } +} + +/// <length> | <percentage> | <number> +pub type LoPOrNumber = Either<LengthOrPercentage, Number>; + #[derive(Clone, PartialEq, Eq, Copy, Debug)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[allow(missing_docs)] diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs index fb4ac9c0b60..d2e64481a39 100644 --- a/components/style/values/specified/length.rs +++ b/components/style/values/specified/length.rs @@ -987,6 +987,34 @@ impl LengthOrPercentage { LengthOrPercentage::parse_internal(input, AllowedNumericType::NonNegative) } + /// Parse a length, treating dimensionless numbers as pixels + /// + /// https://www.w3.org/TR/SVG2/types.html#presentation-attribute-css-value + pub fn parse_numbers_are_pixels(input: &mut Parser) -> Result<LengthOrPercentage, ()> { + if let Ok(lop) = input.try(|i| Self::parse_internal(i, AllowedNumericType::All)) { + Ok(lop) + } else { + let num = input.expect_number()?; + Ok(LengthOrPercentage::Length(NoCalcLength::Absolute(Au((AU_PER_PX * num) as i32)))) + } + } + + /// Parse a non-negative length, treating dimensionless numbers as pixels + /// + /// This is nonstandard behavior used by Firefox for SVG + pub fn parse_numbers_are_pixels_non_negative(input: &mut Parser) -> Result<LengthOrPercentage, ()> { + if let Ok(lop) = input.try(|i| Self::parse_internal(i, AllowedNumericType::NonNegative)) { + Ok(lop) + } else { + let num = input.expect_number()?; + if num >= 0. { + Ok(LengthOrPercentage::Length(NoCalcLength::Absolute(Au((AU_PER_PX * num) as i32)))) + } else { + Err(()) + } + } + } + /// Extract value from ref without a clone, replacing it with a 0 Au /// /// Use when you need to move out of a length array without cloning diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index e79d68b7777..f959a63902a 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -670,6 +670,175 @@ impl Shadow { } } +no_viewport_percentage!(SVGPaint); + +/// An SVG paint value +/// +/// https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub struct SVGPaint { + /// The paint source + pub kind: SVGPaintKind, + /// The fallback color + pub fallback: Option<CSSColor>, +} + +/// An SVG paint value without the fallback +/// +/// Whereas the spec only allows PaintServer +/// to have a fallback, Gecko lets the context +/// properties have a fallback as well. +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "servo", derive(HeapSizeOf))] +pub enum SVGPaintKind { + /// `none` + None, + /// `<color>` + Color(CSSColor), + /// `url(...)` + PaintServer(SpecifiedUrl), + /// `context-fill` + ContextFill, + /// `context-stroke` + ContextStroke, +} + +impl SVGPaintKind { + fn parse_ident(input: &mut Parser) -> Result<Self, ()> { + Ok(match_ignore_ascii_case! { input.expect_ident()?, + "none" => SVGPaintKind::None, + "context-fill" => SVGPaintKind::ContextFill, + "context-stroke" => SVGPaintKind::ContextStroke, + _ => return Err(()) + }) + } +} + +impl Parse for SVGPaint { + fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> { + if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) { + let fallback = input.try(|i| CSSColor::parse(context, i)); + Ok(SVGPaint { + kind: SVGPaintKind::PaintServer(url), + fallback: fallback.ok(), + }) + } else if let Ok(kind) = input.try(SVGPaintKind::parse_ident) { + if kind == SVGPaintKind::None { + Ok(SVGPaint { + kind: kind, + fallback: None, + }) + } else { + let fallback = input.try(|i| CSSColor::parse(context, i)); + Ok(SVGPaint { + kind: kind, + fallback: fallback.ok(), + }) + } + } else if let Ok(color) = input.try(|i| CSSColor::parse(context, i)) { + Ok(SVGPaint { + kind: SVGPaintKind::Color(color), + fallback: None, + }) + } else { + Err(()) + } + } +} + +impl ToCss for SVGPaintKind { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + match *self { + SVGPaintKind::None => dest.write_str("none"), + SVGPaintKind::ContextStroke => dest.write_str("context-stroke"), + SVGPaintKind::ContextFill => dest.write_str("context-fill"), + SVGPaintKind::Color(ref color) => color.to_css(dest), + SVGPaintKind::PaintServer(ref server) => server.to_css(dest), + } + } +} + +impl ToCss for SVGPaint { + fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { + self.kind.to_css(dest)?; + if let Some(ref fallback) = self.fallback { + fallback.to_css(dest)?; + } + Ok(()) + } +} + + +impl ToComputedValue for SVGPaint { + type ComputedValue = super::computed::SVGPaint; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + super::computed::SVGPaint { + kind: self.kind.to_computed_value(context), + fallback: self.fallback.as_ref().map(|f| f.to_computed_value(context)) + } + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + SVGPaint { + kind: ToComputedValue::from_computed_value(&computed.kind), + fallback: computed.fallback.as_ref().map(ToComputedValue::from_computed_value) + } + } +} + +impl ToComputedValue for SVGPaintKind { + type ComputedValue = super::computed::SVGPaintKind; + + #[inline] + fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { + match *self { + SVGPaintKind::None => super::computed::SVGPaintKind::None, + SVGPaintKind::ContextStroke => super::computed::SVGPaintKind::ContextStroke, + SVGPaintKind::ContextFill => super::computed::SVGPaintKind::ContextFill, + SVGPaintKind::Color(ref color) => { + super::computed::SVGPaintKind::Color(color.to_computed_value(context)) + } + SVGPaintKind::PaintServer(ref server) => { + super::computed::SVGPaintKind::PaintServer(server.to_computed_value(context)) + } + } + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + super::computed::SVGPaintKind::None => SVGPaintKind::None, + super::computed::SVGPaintKind::ContextStroke => SVGPaintKind::ContextStroke, + super::computed::SVGPaintKind::ContextFill => SVGPaintKind::ContextFill, + super::computed::SVGPaintKind::Color(ref color) => { + SVGPaintKind::Color(ToComputedValue::from_computed_value(color)) + } + super::computed::SVGPaintKind::PaintServer(ref server) => { + SVGPaintKind::PaintServer(ToComputedValue::from_computed_value(server)) + } + } + } +} + +/// <length> | <percentage> | <number> +pub type LoPOrNumber = Either<LengthOrPercentage, Number>; + +impl LoPOrNumber { + /// parse a <length-percentage> | <number> enforcing that the contents aren't negative + pub fn parse_non_negative(_: &ParserContext, input: &mut Parser) -> Result<Self, ()> { + if let Ok(lop) = input.try(LengthOrPercentage::parse_non_negative) { + Ok(Either::First(lop)) + } else if let Ok(num) = input.try(Number::parse_non_negative) { + Ok(Either::Second(num)) + } else { + Err(()) + } + } +} impl HasViewportPercentage for ClipRect { fn has_viewport_percentage(&self) -> bool { diff --git a/components/style/values/specified/url.rs b/components/style/values/specified/url.rs index 41af5a16dea..f6b8dd2de29 100644 --- a/components/style/values/specified/url.rs +++ b/components/style/values/specified/url.rs @@ -6,6 +6,8 @@ use cssparser::{CssStringWriter, Parser}; #[cfg(feature = "gecko")] +use gecko_bindings::structs::ServoBundledURI; +#[cfg(feature = "gecko")] use gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI}; use parser::{Parse, ParserContext}; #[cfg(feature = "gecko")] @@ -167,6 +169,24 @@ impl SpecifiedUrl { extra_data: UrlExtraData {} } } + + /// Create a bundled URI suitable for sending to Gecko + /// to be constructed into a css::URLValue + #[cfg(feature = "gecko")] + pub fn for_ffi(&self) -> Option<ServoBundledURI> { + let extra_data = self.extra_data(); + let (ptr, len) = match self.as_slice_components() { + Ok(value) => value, + Err(_) => return None, + }; + Some(ServoBundledURI { + mURLString: ptr, + mURLStringLength: len as u32, + mBaseURI: extra_data.base.get(), + mReferrer: extra_data.referrer.get(), + mPrincipal: extra_data.principal.get(), + }) + } } impl PartialEq for SpecifiedUrl { |