diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 78d09bac3ea..951360b8f5e 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -145,11 +145,11 @@ impl nsStyleImage { match image { GenericImage::Gradient(boxed_gradient) => self.set_gradient(*boxed_gradient), GenericImage::Url(ref url) => unsafe { - bindings::Gecko_SetLayerImageImageValue(self, url.image_value.get()); + bindings::Gecko_SetLayerImageImageValue(self, url.0.image_value.get()); }, GenericImage::Rect(ref image_rect) => { unsafe { - bindings::Gecko_SetLayerImageImageValue(self, image_rect.url.image_value.get()); + bindings::Gecko_SetLayerImageImageValue(self, image_rect.url.0.image_value.get()); bindings::Gecko_InitializeImageCropRect(self); // Set CropRect diff --git a/components/style/gecko/url.rs b/components/style/gecko/url.rs index bca7e10883a..4961a6e21c8 100644 --- a/components/style/gecko/url.rs +++ b/components/style/gecko/url.rs @@ -12,10 +12,13 @@ use gecko_bindings::structs::root::{RustString, nsStyleImageRequest}; use gecko_bindings::structs::root::mozilla::css::{ImageValue, URLValue}; use gecko_bindings::sugar::refptr::RefPtr; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; +use nsstring::nsCString; use parser::{Parse, ParserContext}; use servo_arc::{Arc, RawOffsetArc}; use std::mem; -use style_traits::ParseError; +use std::fmt::{self, Write}; +use style_traits::{CssWriter, ParseError, ToCss}; +use values::computed::{Context, ToComputedValue}; /// A CSS url() value for gecko. #[css(function = "url")] @@ -70,10 +73,8 @@ impl CssUrl { self.as_str().chars().next().map_or(false, |c| c == '#') } - /// Return the resolved url as string, or the empty string if it's invalid. - /// - /// FIXME(bholley): This returns the unresolved URL while the servo version - /// returns the resolved URL. + /// Return the unresolved url as string, or the empty string if it's + /// invalid. pub fn as_str(&self) -> &str { &*self.serialization } @@ -121,7 +122,7 @@ impl MallocSizeOf for CssUrl { } /// A specified url() value for general usage. -#[derive(Clone, Debug, SpecifiedValueInfo, ToComputedValue, ToCss)] +#[derive(Clone, Debug, SpecifiedValueInfo, ToCss)] pub struct SpecifiedUrl { /// The specified url value. pub url: CssUrl, @@ -139,15 +140,11 @@ impl SpecifiedUrl { debug_assert!(!ptr.is_null()); RefPtr::from_addrefed(ptr) }; - SpecifiedUrl { url, url_value } - } - - /// Convert from URLValueData to SpecifiedUrl. - pub unsafe fn from_url_value_data(url: &URLValueData) -> Result { - CssUrl::from_url_value_data(url).map(Self::from_css_url) + Self { url, url_value } } } + impl PartialEq for SpecifiedUrl { fn eq(&self, other: &Self) -> bool { self.url.eq(&other.url) @@ -179,7 +176,7 @@ impl MallocSizeOf for SpecifiedUrl { /// A specified url() value for image. /// /// This exists so that we can construct `ImageValue` and reuse it. -#[derive(Clone, Debug, SpecifiedValueInfo, ToComputedValue, ToCss)] +#[derive(Clone, Debug, SpecifiedValueInfo, ToCss)] pub struct SpecifiedImageUrl { /// The specified url value. pub url: CssUrl, @@ -190,16 +187,6 @@ pub struct SpecifiedImageUrl { } impl SpecifiedImageUrl { - fn from_css_url(url: CssUrl) -> Self { - let image_value = unsafe { - let ptr = bindings::Gecko_ImageValue_Create(url.for_ffi()); - // We do not expect Gecko_ImageValue_Create returns null. - debug_assert!(!ptr.is_null()); - RefPtr::from_addrefed(ptr) - }; - SpecifiedImageUrl { url, image_value } - } - /// Parse a URL from a string value. See SpecifiedUrl::parse_from_string. pub fn parse_from_string<'a>( url: String, @@ -208,20 +195,14 @@ impl SpecifiedImageUrl { CssUrl::parse_from_string(url, context).map(Self::from_css_url) } - /// Convert from URLValueData to SpecifiedUrl. - pub unsafe fn from_url_value_data(url: &URLValueData) -> Result { - CssUrl::from_url_value_data(url).map(Self::from_css_url) - } - - /// Convert from nsStyleImageRequest to SpecifiedUrl. - pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Result { - if image_request.mImageValue.mRawPtr.is_null() { - return Err(()); - } - - let image_value = image_request.mImageValue.mRawPtr.as_ref().unwrap(); - let url_value_data = &image_value._base; - Self::from_url_value_data(url_value_data) + fn from_css_url(url: CssUrl) -> Self { + let image_value = unsafe { + let ptr = bindings::Gecko_ImageValue_Create(url.for_ffi()); + // We do not expect Gecko_ImageValue_Create returns null. + debug_assert!(!ptr.is_null()); + RefPtr::from_addrefed(ptr) + }; + Self { url, image_value } } } @@ -253,7 +234,104 @@ impl MallocSizeOf for SpecifiedImageUrl { } } +impl ToComputedValue for SpecifiedUrl { + type ComputedValue = ComputedUrl; + + #[inline] + fn to_computed_value(&self, _: &Context) -> Self::ComputedValue { + ComputedUrl(self.clone()) + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + computed.0.clone() + } +} + +impl ToComputedValue for SpecifiedImageUrl { + type ComputedValue = ComputedImageUrl; + + #[inline] + fn to_computed_value(&self, _: &Context) -> Self::ComputedValue { + ComputedImageUrl(self.clone()) + } + + #[inline] + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + computed.0.clone() + } +} + +fn serialize_computed_url( + url_value_data: &URLValueData, + dest: &mut CssWriter, +) -> fmt::Result +where + W: Write, +{ + dest.write_str("url(")?; + unsafe { + let mut string = nsCString::new(); + bindings::Gecko_GetComputedURLSpec(url_value_data, &mut string); + string.as_str_unchecked().to_css(dest)?; + } + dest.write_char(')') +} + /// The computed value of a CSS `url()`. -pub type ComputedUrl = SpecifiedUrl; +/// +/// The only difference between specified and computed URLs is the +/// serialization. +#[derive(Clone, Debug, Eq, PartialEq, MallocSizeOf)] +pub struct ComputedUrl(pub SpecifiedUrl); + +impl ToCss for ComputedUrl { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write + { + serialize_computed_url(&self.0.url_value._base, dest) + } +} + +impl ComputedUrl { + /// Convert from URLValueData to ComputedUrl. + pub unsafe fn from_url_value_data(url: &URLValueData) -> Result { + Ok(ComputedUrl( + SpecifiedUrl::from_css_url(CssUrl::from_url_value_data(url)?) + )) + } +} + /// The computed value of a CSS `url()` for image. -pub type ComputedImageUrl = SpecifiedImageUrl; +#[derive(Clone, Debug, Eq, PartialEq, MallocSizeOf)] +pub struct ComputedImageUrl(pub SpecifiedImageUrl); + +impl ToCss for ComputedImageUrl { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write + { + serialize_computed_url(&self.0.image_value._base, dest) + } +} + +impl ComputedImageUrl { + /// Convert from URLValueData to SpecifiedUrl. + pub unsafe fn from_url_value_data(url: &URLValueData) -> Result { + Ok(ComputedImageUrl( + SpecifiedImageUrl::from_css_url(CssUrl::from_url_value_data(url)?) + )) + } + + /// Convert from nsStyleImageReques to ComputedImageUrl. + pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Result { + if image_request.mImageValue.mRawPtr.is_null() { + return Err(()); + } + + let image_value = image_request.mImageValue.mRawPtr.as_ref().unwrap(); + let url_value_data = &image_value._base; + Self::from_url_value_data(url_value_data) + } +} diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 05c26d1f949..db8e3e502d8 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -697,7 +697,7 @@ def set_gecko_property(ffi_name, expr): } SVGPaintKind::PaintServer(url) => { unsafe { - bindings::Gecko_nsStyleSVGPaint_SetURLValue(paint, url.url_value.get()); + bindings::Gecko_nsStyleSVGPaint_SetURLValue(paint, url.0.url_value.get()); } } SVGPaintKind::Color(color) => { @@ -737,8 +737,8 @@ def set_gecko_property(ffi_name, expr): #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { + use values::computed::url::ComputedUrl; use values::generics::svg::{SVGPaint, SVGPaintKind}; - use values::specified::url::SpecifiedUrl; use self::structs::nsStyleSVGPaintType; use self::structs::nsStyleSVGFallbackType; let ref paint = ${get_gecko_property(gecko_ffi_name)}; @@ -760,7 +760,7 @@ def set_gecko_property(ffi_name, expr): nsStyleSVGPaintType::eStyleSVGPaintType_Server => { unsafe { SVGPaintKind::PaintServer( - SpecifiedUrl::from_url_value_data( + ComputedUrl::from_url_value_data( &(**paint.mPaint.mPaintServer.as_ref())._base ).unwrap() ) @@ -939,7 +939,7 @@ def set_gecko_property(ffi_name, expr): pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { match v { UrlOrNone::Url(ref url) => { - self.gecko.${gecko_ffi_name}.set_move(url.url_value.clone()) + self.gecko.${gecko_ffi_name}.set_move(url.0.url_value.clone()) } UrlOrNone::None => { unsafe { @@ -961,16 +961,18 @@ def set_gecko_property(ffi_name, expr): #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { - use values::specified::url::SpecifiedUrl; + use values::computed::url::ComputedUrl; if self.gecko.${gecko_ffi_name}.mRawPtr.is_null() { - UrlOrNone::none() - } else { - unsafe { - let ref gecko_url_value = *self.gecko.${gecko_ffi_name}.mRawPtr; - UrlOrNone::Url(SpecifiedUrl::from_url_value_data(&gecko_url_value._base) - .expect("${gecko_ffi_name} could not convert to SpecifiedUrl")) - } + return UrlOrNone::none() + } + + unsafe { + let gecko_url_value = &*self.gecko.${gecko_ffi_name}.mRawPtr; + UrlOrNone::Url( + ComputedUrl::from_url_value_data(&gecko_url_value._base) + .expect("${gecko_ffi_name} could not convert to ComputedUrl") + ) } } @@ -4110,7 +4112,7 @@ fn static_assert() { } UrlOrNone::Url(ref url) => { unsafe { - Gecko_SetListStyleImageImageValue(&mut self.gecko, url.image_value.get()); + Gecko_SetListStyleImageImageValue(&mut self.gecko, url.0.image_value.get()); } } } @@ -4125,7 +4127,7 @@ fn static_assert() { } pub fn clone_list_style_image(&self) -> longhands::list_style_image::computed_value::T { - use values::specified::url::SpecifiedImageUrl; + use values::computed::url::ComputedImageUrl; if self.gecko.mListStyleImage.mRawPtr.is_null() { return UrlOrNone::None; @@ -4133,8 +4135,9 @@ fn static_assert() { unsafe { let ref gecko_image_request = *self.gecko.mListStyleImage.mRawPtr; - UrlOrNone::Url(SpecifiedImageUrl::from_image_request(gecko_image_request) - .expect("mListStyleImage could not convert to SpecifiedImageUrl")) + UrlOrNone::Url(ComputedImageUrl::from_image_request( + gecko_image_request + ).expect("mListStyleImage could not convert to ComputedImageUrl")) } } @@ -4480,7 +4483,7 @@ fn static_assert() { }, Url(ref url) => { unsafe { - bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, url.url_value.get()); + bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, url.0.url_value.get()); } }, } @@ -4499,7 +4502,7 @@ fn static_assert() { pub fn clone_filter(&self) -> longhands::filter::computed_value::T { use values::generics::effects::Filter; - use values::specified::url::SpecifiedUrl; + use values::computed::url::ComputedUrl; use gecko_bindings::structs::NS_STYLE_FILTER_BLUR; use gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS; use gecko_bindings::structs::NS_STYLE_FILTER_CONTRAST; @@ -4541,7 +4544,7 @@ fn static_assert() { NS_STYLE_FILTER_URL => { filters.push(unsafe { Filter::Url( - SpecifiedUrl::from_url_value_data(&(**filter.__bindgen_anon_1.mURL.as_ref())._base).unwrap() + ComputedUrl::from_url_value_data(&(**filter.__bindgen_anon_1.mURL.as_ref())._base).unwrap() ) }); } @@ -5007,7 +5010,7 @@ fn static_assert() { % if ident == "clip_path": ShapeSource::ImageOrUrl(ref url) => { unsafe { - bindings::Gecko_StyleShapeSource_SetURLValue(${ident}, url.url_value.get()) + bindings::Gecko_StyleShapeSource_SetURLValue(${ident}, url.0.url_value.get()) } } % elif ident == "shape_outside": @@ -5347,7 +5350,7 @@ clip-path unsafe { Gecko_SetCursorImageValue( &mut self.gecko.mCursorImages[i], - v.images[i].url.image_value.get(), + v.images[i].url.0.image_value.get(), ); } @@ -5377,8 +5380,8 @@ clip-path pub fn clone_cursor(&self) -> longhands::cursor::computed_value::T { use values::computed::ui::CursorImage; + use values::computed::url::ComputedImageUrl; use style_traits::cursor::CursorKind; - use values::specified::url::SpecifiedImageUrl; let keyword = match self.gecko.mCursor as u32 { structs::NS_STYLE_CURSOR_AUTO => CursorKind::Auto, @@ -5423,8 +5426,8 @@ clip-path let images = self.gecko.mCursorImages.iter().map(|gecko_cursor_image| { let url = unsafe { let gecko_image_request = gecko_cursor_image.mImage.mRawPtr.as_ref().unwrap(); - SpecifiedImageUrl::from_image_request(&gecko_image_request) - .expect("mCursorImages.mImage could not convert to SpecifiedImageUrl") + ComputedImageUrl::from_image_request(&gecko_image_request) + .expect("mCursorImages.mImage could not convert to ComputedImageUrl") }; let hotspot = @@ -5483,7 +5486,7 @@ clip-path pub fn set_content(&mut self, v: longhands::content::computed_value::T, device: &Device) { use values::CustomIdent; - use values::computed::counters::{Content, ContentItem}; + use values::generics::counters::{Content, ContentItem}; use values::generics::CounterStyleOrNone; use gecko_bindings::structs::nsStyleContentData; use gecko_bindings::structs::nsStyleContentAttr; @@ -5610,7 +5613,7 @@ clip-path unsafe { bindings::Gecko_SetContentDataImageValue( &mut self.gecko.mContents[i], - url.image_value.get(), + url.0.image_value.get(), ) } } @@ -5635,10 +5638,10 @@ clip-path use {Atom, Namespace}; use gecko::conversions::string_from_chars_pointer; use gecko_bindings::structs::nsStyleContentType::*; - use values::computed::counters::{Content, ContentItem}; + use values::generics::counters::{Content, ContentItem}; + use values::computed::url::ComputedImageUrl; use values::{CustomIdent, Either}; use values::generics::CounterStyleOrNone; - use values::specified::url::SpecifiedImageUrl; use values::specified::Attr; if self.gecko.mContents.is_empty() { @@ -5699,8 +5702,8 @@ clip-path let gecko_image_request = &**gecko_content.mContent.mImage.as_ref(); ContentItem::Url( - SpecifiedImageUrl::from_image_request(gecko_image_request) - .expect("mContent could not convert to SpecifiedImageUrl") + ComputedImageUrl::from_image_request(gecko_image_request) + .expect("mContent could not convert to ComputedImageUrl") ) } }, diff --git a/components/style/values/animated/effects.rs b/components/style/values/animated/effects.rs index 9ce9c3be9a7..1bc04c6167a 100644 --- a/components/style/values/animated/effects.rs +++ b/components/style/values/animated/effects.rs @@ -14,6 +14,7 @@ use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; use values::animated::color::RGBA; use values::computed::{Angle, Number}; use values::computed::length::Length; +use values::computed::url::ComputedUrl; use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::effects::BoxShadow as GenericBoxShadow; use values::generics::effects::Filter as GenericFilter; @@ -42,11 +43,11 @@ pub struct FilterList(pub Vec); /// An animated value for a single `filter`. #[cfg(feature = "gecko")] -pub type Filter = GenericFilter; +pub type Filter = GenericFilter; /// An animated value for a single `filter`. #[cfg(not(feature = "gecko"))] -pub type Filter = GenericFilter; +pub type Filter = GenericFilter; /// An animated value for the `drop-shadow()` filter. pub type SimpleShadow = GenericSimpleShadow, Length, Length>; diff --git a/components/style/values/animated/mod.rs b/components/style/values/animated/mod.rs index ec31f7ab18c..533d917be6f 100644 --- a/components/style/values/animated/mod.rs +++ b/components/style/values/animated/mod.rs @@ -15,9 +15,7 @@ use values::computed::Angle as ComputedAngle; use values::computed::BorderCornerRadius as ComputedBorderCornerRadius; use values::computed::MaxLength as ComputedMaxLength; use values::computed::MozLength as ComputedMozLength; -#[cfg(feature = "servo")] use values::computed::url::ComputedUrl; -use values::specified::url::SpecifiedUrl; pub mod color; pub mod effects; @@ -260,8 +258,6 @@ macro_rules! trivial_to_animated_value { trivial_to_animated_value!(Au); trivial_to_animated_value!(ComputedAngle); -trivial_to_animated_value!(SpecifiedUrl); -#[cfg(feature = "servo")] trivial_to_animated_value!(ComputedUrl); trivial_to_animated_value!(bool); trivial_to_animated_value!(f32); diff --git a/components/style/values/computed/counters.rs b/components/style/values/computed/counters.rs index 3cae67f22f7..fd8d7763f1c 100644 --- a/components/style/values/computed/counters.rs +++ b/components/style/values/computed/counters.rs @@ -4,22 +4,10 @@ //! Computed values for counter properties -#[cfg(feature = "servo")] -use computed_values::list_style_type::T as ListStyleType; -use cssparser::{Parser, Token}; -use parser::{Parse, ParserContext}; -use selectors::parser::SelectorParseErrorKind; -use style_traits::{ParseError, StyleParseErrorKind}; -use values::CustomIdent; -#[cfg(feature = "gecko")] -use values::generics::CounterStyleOrNone; +use values::computed::url::ComputedImageUrl; +use values::generics::counters as generics; use values::generics::counters::CounterIncrement as GenericCounterIncrement; use values::generics::counters::CounterReset as GenericCounterReset; -#[cfg(feature = "gecko")] -use values::specified::Attr; -#[cfg(feature = "gecko")] -use values::specified::url::SpecifiedImageUrl; -pub use values::specified::{Content, ContentItem}; /// A computed value for the `counter-increment` property. pub type CounterIncrement = GenericCounterIncrement; @@ -27,134 +15,9 @@ pub type CounterIncrement = GenericCounterIncrement; /// A computed value for the `counter-increment` property. pub type CounterReset = GenericCounterReset; -impl Content { - /// Set `content` property to `normal`. - #[inline] - pub fn normal() -> Self { - Content::Normal - } +/// A computed value for the `content` property. +pub type Content = generics::Content; - #[cfg(feature = "servo")] - fn parse_counter_style(input: &mut Parser) -> ListStyleType { - input - .try(|input| { - input.expect_comma()?; - ListStyleType::parse(input) - }) - .unwrap_or(ListStyleType::Decimal) - } +/// A computed content item. +pub type ContentItem = generics::ContentItem; - #[cfg(feature = "gecko")] - fn parse_counter_style(context: &ParserContext, input: &mut Parser) -> CounterStyleOrNone { - input - .try(|input| { - input.expect_comma()?; - CounterStyleOrNone::parse(context, input) - }) - .unwrap_or(CounterStyleOrNone::decimal()) - } -} - -impl Parse for Content { - // normal | none | [ | | open-quote | close-quote | no-open-quote | - // no-close-quote ]+ - // TODO: , attr() - fn parse<'i, 't>( - _context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - if input - .try(|input| input.expect_ident_matching("normal")) - .is_ok() - { - return Ok(Content::Normal); - } - if input - .try(|input| input.expect_ident_matching("none")) - .is_ok() - { - return Ok(Content::None); - } - #[cfg(feature = "gecko")] - { - if input - .try(|input| input.expect_ident_matching("-moz-alt-content")) - .is_ok() - { - return Ok(Content::MozAltContent); - } - } - - let mut content = vec![]; - loop { - #[cfg(feature = "gecko")] - { - if let Ok(url) = input.try(|i| SpecifiedImageUrl::parse(_context, i)) { - content.push(ContentItem::Url(url)); - continue; - } - } - // FIXME: remove clone() when lifetimes are non-lexical - match input.next().map(|t| t.clone()) { - Ok(Token::QuotedString(ref value)) => { - content.push(ContentItem::String( - value.as_ref().to_owned().into_boxed_str(), - )); - }, - Ok(Token::Function(ref name)) => { - let result = match_ignore_ascii_case! { &name, - "counter" => Some(input.parse_nested_block(|input| { - let location = input.current_source_location(); - let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?; - #[cfg(feature = "servo")] - let style = Content::parse_counter_style(input); - #[cfg(feature = "gecko")] - let style = Content::parse_counter_style(_context, input); - Ok(ContentItem::Counter(name, style)) - })), - "counters" => Some(input.parse_nested_block(|input| { - let location = input.current_source_location(); - let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?; - input.expect_comma()?; - let separator = input.expect_string()?.as_ref().to_owned().into_boxed_str(); - #[cfg(feature = "servo")] - let style = Content::parse_counter_style(input); - #[cfg(feature = "gecko")] - let style = Content::parse_counter_style(_context, input); - Ok(ContentItem::Counters(name, separator, style)) - })), - #[cfg(feature = "gecko")] - "attr" => Some(input.parse_nested_block(|input| { - Ok(ContentItem::Attr(Attr::parse_function(_context, input)?)) - })), - _ => None - }; - match result { - Some(result) => content.push(result?), - None => { - return Err(input.new_custom_error( - StyleParseErrorKind::UnexpectedFunction(name.clone()), - )) - }, - } - }, - Ok(Token::Ident(ref ident)) => { - content.push(match_ignore_ascii_case! { &ident, - "open-quote" => ContentItem::OpenQuote, - "close-quote" => ContentItem::CloseQuote, - "no-open-quote" => ContentItem::NoOpenQuote, - "no-close-quote" => ContentItem::NoCloseQuote, - _ => return Err(input.new_custom_error( - SelectorParseErrorKind::UnexpectedIdent(ident.clone()))) - }); - }, - Err(_) => break, - Ok(t) => return Err(input.new_unexpected_token_error(t)), - } - } - if content.is_empty() { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - Ok(Content::Items(content.into_boxed_slice())) - } -} diff --git a/components/style/values/computed/effects.rs b/components/style/values/computed/effects.rs index 92e91f163f0..3db1caf4f90 100644 --- a/components/style/values/computed/effects.rs +++ b/components/style/values/computed/effects.rs @@ -9,6 +9,7 @@ use values::Impossible; use values::computed::{Angle, NonNegativeNumber}; use values::computed::color::RGBAColor; use values::computed::length::{Length, NonNegativeLength}; +use values::computed::url::ComputedUrl; use values::generics::effects::BoxShadow as GenericBoxShadow; use values::generics::effects::Filter as GenericFilter; use values::generics::effects::SimpleShadow as GenericSimpleShadow; @@ -18,11 +19,11 @@ pub type BoxShadow = GenericBoxShadow, Length, NonNegativeLeng /// A computed value for a single `filter`. #[cfg(feature = "gecko")] -pub type Filter = GenericFilter; +pub type Filter = GenericFilter; /// A computed value for a single `filter`. #[cfg(not(feature = "gecko"))] -pub type Filter = GenericFilter; +pub type Filter = GenericFilter; /// A computed value for the `drop-shadow()` filter. pub type SimpleShadow = GenericSimpleShadow, Length, NonNegativeLength>; diff --git a/components/style/values/generics/counters.rs b/components/style/values/generics/counters.rs index 9f50ab1e2b0..66da2625ffd 100644 --- a/components/style/values/generics/counters.rs +++ b/components/style/values/generics/counters.rs @@ -4,8 +4,14 @@ //! Generic types for counters-related CSS values. +#[cfg(feature = "servo")] +use computed_values::list_style_type::T as ListStyleType; use std::ops::Deref; use values::CustomIdent; +#[cfg(feature = "gecko")] +use values::generics::CounterStyleOrNone; +#[cfg(feature = "gecko")] +use values::specified::Attr; /// A name / value pair for counters. #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, @@ -74,3 +80,78 @@ impl Default for Counters { Counters(vec![].into_boxed_slice()) } } + +#[cfg(feature = "servo")] +type CounterStyleType = ListStyleType; + +#[cfg(feature = "gecko")] +type CounterStyleType = CounterStyleOrNone; + +#[cfg(feature = "servo")] +#[inline] +fn is_decimal(counter_type: &CounterStyleType) -> bool { + *counter_type == ListStyleType::Decimal +} + +#[cfg(feature = "gecko")] +#[inline] +fn is_decimal(counter_type: &CounterStyleType) -> bool { + *counter_type == CounterStyleOrNone::decimal() +} + +/// The specified value for the `content` property. +/// +/// https://drafts.csswg.org/css-content/#propdef-content +#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, + ToComputedValue, ToCss)] +pub enum Content { + /// `normal` reserved keyword. + Normal, + /// `none` reserved keyword. + None, + /// `-moz-alt-content`. + #[cfg(feature = "gecko")] + MozAltContent, + /// Content items. + Items(#[css(iterable)] Box<[ContentItem]>), +} + +impl Content { + /// Set `content` property to `normal`. + #[inline] + pub fn normal() -> Self { + Content::Normal + } + +} + +/// Items for the `content` property. +#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, + ToComputedValue, ToCss)] +pub enum ContentItem { + /// Literal string content. + String(Box), + /// `counter(name, style)`. + #[css(comma, function)] + Counter(CustomIdent, #[css(skip_if = "is_decimal")] CounterStyleType), + /// `counters(name, separator, style)`. + #[css(comma, function)] + Counters( + CustomIdent, + Box, + #[css(skip_if = "is_decimal")] CounterStyleType, + ), + /// `open-quote`. + OpenQuote, + /// `close-quote`. + CloseQuote, + /// `no-open-quote`. + NoOpenQuote, + /// `no-close-quote`. + NoCloseQuote, + /// `attr([namespace? `|`]? ident)` + #[cfg(feature = "gecko")] + Attr(Attr), + /// `url(url)` + Url(ImageUrl), +} diff --git a/components/style/values/generics/effects.rs b/components/style/values/generics/effects.rs index e5ee10fcada..f05dfe82d66 100644 --- a/components/style/values/generics/effects.rs +++ b/components/style/values/generics/effects.rs @@ -4,9 +4,6 @@ //! Generic types for CSS values related to effects. -#[cfg(feature = "gecko")] -use values::specified::url::SpecifiedUrl; - /// A generic value for a single `box-shadow`. #[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero, ToCss)] @@ -23,9 +20,10 @@ pub struct BoxShadow { /// A generic value for a single `filter`. #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] +#[animation(no_bound(Url))] #[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss)] -pub enum Filter { +pub enum Filter { /// `blur()` #[css(function)] Blur(Length), @@ -58,8 +56,7 @@ pub enum Filter { DropShadow(DropShadow), /// `` #[animation(error)] - #[cfg(feature = "gecko")] - Url(SpecifiedUrl), + Url(Url), } /// A generic value for the `drop-shadow()` filter and the `text-shadow` property. diff --git a/components/style/values/specified/counters.rs b/components/style/values/specified/counters.rs index 3daf8ee56a0..e67a2c35238 100644 --- a/components/style/values/specified/counters.rs +++ b/components/style/values/specified/counters.rs @@ -8,18 +8,19 @@ use computed_values::list_style_type::T as ListStyleType; use cssparser::{Parser, Token}; use parser::{Parse, ParserContext}; +use selectors::parser::SelectorParseErrorKind; use style_traits::{ParseError, StyleParseErrorKind}; use values::CustomIdent; #[cfg(feature = "gecko")] use values::generics::CounterStyleOrNone; +use values::generics::counters as generics; use values::generics::counters::CounterIncrement as GenericCounterIncrement; use values::generics::counters::CounterPair; use values::generics::counters::CounterReset as GenericCounterReset; +use values::specified::Integer; +use values::specified::url::SpecifiedImageUrl; #[cfg(feature = "gecko")] use values::specified::Attr; -use values::specified::Integer; -#[cfg(feature = "gecko")] -use values::specified::url::SpecifiedImageUrl; /// A specified value for the `counter-increment` property. pub type CounterIncrement = GenericCounterIncrement; @@ -79,69 +80,129 @@ fn parse_counters<'i, 't>( } } -#[cfg(feature = "servo")] -type CounterStyleType = ListStyleType; - -#[cfg(feature = "gecko")] -type CounterStyleType = CounterStyleOrNone; - -#[cfg(feature = "servo")] -#[inline] -fn is_decimal(counter_type: &CounterStyleType) -> bool { - *counter_type == ListStyleType::Decimal -} - -#[cfg(feature = "gecko")] -#[inline] -fn is_decimal(counter_type: &CounterStyleType) -> bool { - *counter_type == CounterStyleOrNone::decimal() -} - /// The specified value for the `content` property. -/// -/// https://drafts.csswg.org/css-content/#propdef-content -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, - ToComputedValue, ToCss)] -pub enum Content { - /// `normal` reserved keyword. - Normal, - /// `none` reserved keyword. - None, - /// `-moz-alt-content`. +pub type Content = generics::Content; + +/// The specified value for a content item in the `content` property. +pub type ContentItem = generics::ContentItem; + +impl Content { + #[cfg(feature = "servo")] + fn parse_counter_style(_: &ParserContext, input: &mut Parser) -> ListStyleType { + input + .try(|input| { + input.expect_comma()?; + ListStyleType::parse(input) + }) + .unwrap_or(ListStyleType::Decimal) + } + #[cfg(feature = "gecko")] - MozAltContent, - /// Content items. - Items(#[css(iterable)] Box<[ContentItem]>), + fn parse_counter_style(context: &ParserContext, input: &mut Parser) -> CounterStyleOrNone { + input + .try(|input| { + input.expect_comma()?; + CounterStyleOrNone::parse(context, input) + }) + .unwrap_or(CounterStyleOrNone::decimal()) + } } -/// Items for the `content` property. -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, - ToComputedValue, ToCss)] -pub enum ContentItem { - /// Literal string content. - String(Box), - /// `counter(name, style)`. - #[css(comma, function)] - Counter(CustomIdent, #[css(skip_if = "is_decimal")] CounterStyleType), - /// `counters(name, separator, style)`. - #[css(comma, function)] - Counters( - CustomIdent, - Box, - #[css(skip_if = "is_decimal")] CounterStyleType, - ), - /// `open-quote`. - OpenQuote, - /// `close-quote`. - CloseQuote, - /// `no-open-quote`. - NoOpenQuote, - /// `no-close-quote`. - NoCloseQuote, - /// `attr([namespace? `|`]? ident)` - #[cfg(feature = "gecko")] - Attr(Attr), - /// `url(url)` - #[cfg(feature = "gecko")] - Url(SpecifiedImageUrl), +impl Parse for Content { + // normal | none | [ | | open-quote | close-quote | no-open-quote | + // no-close-quote ]+ + // TODO: , attr() + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + if input + .try(|input| input.expect_ident_matching("normal")) + .is_ok() + { + return Ok(generics::Content::Normal); + } + if input + .try(|input| input.expect_ident_matching("none")) + .is_ok() + { + return Ok(generics::Content::None); + } + #[cfg(feature = "gecko")] + { + if input + .try(|input| input.expect_ident_matching("-moz-alt-content")) + .is_ok() + { + return Ok(generics::Content::MozAltContent); + } + } + + let mut content = vec![]; + loop { + #[cfg(feature = "gecko")] + { + if let Ok(url) = input.try(|i| SpecifiedImageUrl::parse(context, i)) { + content.push(generics::ContentItem::Url(url)); + continue; + } + } + // FIXME: remove clone() when lifetimes are non-lexical + match input.next().map(|t| t.clone()) { + Ok(Token::QuotedString(ref value)) => { + content.push(generics::ContentItem::String( + value.as_ref().to_owned().into_boxed_str(), + )); + }, + Ok(Token::Function(ref name)) => { + let result = match_ignore_ascii_case! { &name, + "counter" => Some(input.parse_nested_block(|input| { + let location = input.current_source_location(); + let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?; + let style = Content::parse_counter_style(context, input); + Ok(generics::ContentItem::Counter(name, style)) + })), + "counters" => Some(input.parse_nested_block(|input| { + let location = input.current_source_location(); + let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?; + input.expect_comma()?; + let separator = input.expect_string()?.as_ref().to_owned().into_boxed_str(); + let style = Content::parse_counter_style(context, input); + Ok(generics::ContentItem::Counters(name, separator, style)) + })), + #[cfg(feature = "gecko")] + "attr" => Some(input.parse_nested_block(|input| { + Ok(generics::ContentItem::Attr(Attr::parse_function(context, input)?)) + })), + _ => None + }; + match result { + Some(result) => content.push(result?), + None => { + return Err(input.new_custom_error( + StyleParseErrorKind::UnexpectedFunction(name.clone()), + )) + }, + } + }, + Ok(Token::Ident(ref ident)) => { + content.push(match_ignore_ascii_case! { &ident, + "open-quote" => generics::ContentItem::OpenQuote, + "close-quote" => generics::ContentItem::CloseQuote, + "no-open-quote" => generics::ContentItem::NoOpenQuote, + "no-close-quote" => generics::ContentItem::NoCloseQuote, + _ => return Err(input.new_custom_error( + SelectorParseErrorKind::UnexpectedIdent(ident.clone()) + )) + }); + }, + Err(_) => break, + Ok(t) => return Err(input.new_unexpected_token_error(t)), + } + } + if content.is_empty() { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + Ok(generics::Content::Items(content.into_boxed_slice())) + } } diff --git a/components/style/values/specified/effects.rs b/components/style/values/specified/effects.rs index 86464f1ba0c..5d76d4ecf88 100644 --- a/components/style/values/specified/effects.rs +++ b/components/style/values/specified/effects.rs @@ -19,7 +19,6 @@ use values::generics::effects::SimpleShadow as GenericSimpleShadow; use values::specified::{Angle, NumberOrPercentage}; use values::specified::color::RGBAColor; use values::specified::length::{Length, NonNegativeLength}; -#[cfg(feature = "gecko")] use values::specified::url::SpecifiedUrl; /// A specified value for a single shadow of the `box-shadow` property. @@ -28,11 +27,11 @@ pub type BoxShadow = /// A specified value for a single `filter`. #[cfg(feature = "gecko")] -pub type Filter = GenericFilter; +pub type Filter = GenericFilter; /// A specified value for a single `filter`. #[cfg(not(feature = "gecko"))] -pub type Filter = GenericFilter; +pub type Filter = GenericFilter; /// A value for the `` parts in `Filter`. #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]