style: Distinguish between specified and computed URLs.

This is needed to serialize computed URLs correctly from getComputedStyle.

Bug: 1461288
Reviewed-by: xidorn
MozReview-Commit-ID: 9wakhqNrszb
This commit is contained in:
Emilio Cobos Álvarez 2018-05-14 12:29:40 +02:00
parent a6328ba3ce
commit 1314f47da5
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
11 changed files with 375 additions and 295 deletions

View file

@ -145,11 +145,11 @@ impl nsStyleImage {
match image { match image {
GenericImage::Gradient(boxed_gradient) => self.set_gradient(*boxed_gradient), GenericImage::Gradient(boxed_gradient) => self.set_gradient(*boxed_gradient),
GenericImage::Url(ref url) => unsafe { 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) => { GenericImage::Rect(ref image_rect) => {
unsafe { 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); bindings::Gecko_InitializeImageCropRect(self);
// Set CropRect // Set CropRect

View file

@ -12,10 +12,13 @@ use gecko_bindings::structs::root::{RustString, nsStyleImageRequest};
use gecko_bindings::structs::root::mozilla::css::{ImageValue, URLValue}; use gecko_bindings::structs::root::mozilla::css::{ImageValue, URLValue};
use gecko_bindings::sugar::refptr::RefPtr; use gecko_bindings::sugar::refptr::RefPtr;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use nsstring::nsCString;
use parser::{Parse, ParserContext}; use parser::{Parse, ParserContext};
use servo_arc::{Arc, RawOffsetArc}; use servo_arc::{Arc, RawOffsetArc};
use std::mem; 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. /// A CSS url() value for gecko.
#[css(function = "url")] #[css(function = "url")]
@ -70,10 +73,8 @@ impl CssUrl {
self.as_str().chars().next().map_or(false, |c| c == '#') self.as_str().chars().next().map_or(false, |c| c == '#')
} }
/// Return the resolved url as string, or the empty string if it's invalid. /// Return the unresolved 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.
pub fn as_str(&self) -> &str { pub fn as_str(&self) -> &str {
&*self.serialization &*self.serialization
} }
@ -121,7 +122,7 @@ impl MallocSizeOf for CssUrl {
} }
/// A specified url() value for general usage. /// A specified url() value for general usage.
#[derive(Clone, Debug, SpecifiedValueInfo, ToComputedValue, ToCss)] #[derive(Clone, Debug, SpecifiedValueInfo, ToCss)]
pub struct SpecifiedUrl { pub struct SpecifiedUrl {
/// The specified url value. /// The specified url value.
pub url: CssUrl, pub url: CssUrl,
@ -139,15 +140,11 @@ impl SpecifiedUrl {
debug_assert!(!ptr.is_null()); debug_assert!(!ptr.is_null());
RefPtr::from_addrefed(ptr) RefPtr::from_addrefed(ptr)
}; };
SpecifiedUrl { url, url_value } Self { url, url_value }
}
/// Convert from URLValueData to SpecifiedUrl.
pub unsafe fn from_url_value_data(url: &URLValueData) -> Result<Self, ()> {
CssUrl::from_url_value_data(url).map(Self::from_css_url)
} }
} }
impl PartialEq for SpecifiedUrl { impl PartialEq for SpecifiedUrl {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.url.eq(&other.url) self.url.eq(&other.url)
@ -179,7 +176,7 @@ impl MallocSizeOf for SpecifiedUrl {
/// A specified url() value for image. /// A specified url() value for image.
/// ///
/// This exists so that we can construct `ImageValue` and reuse it. /// 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 { pub struct SpecifiedImageUrl {
/// The specified url value. /// The specified url value.
pub url: CssUrl, pub url: CssUrl,
@ -190,16 +187,6 @@ pub struct SpecifiedImageUrl {
} }
impl 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. /// Parse a URL from a string value. See SpecifiedUrl::parse_from_string.
pub fn parse_from_string<'a>( pub fn parse_from_string<'a>(
url: String, url: String,
@ -208,20 +195,14 @@ impl SpecifiedImageUrl {
CssUrl::parse_from_string(url, context).map(Self::from_css_url) CssUrl::parse_from_string(url, context).map(Self::from_css_url)
} }
/// Convert from URLValueData to SpecifiedUrl. fn from_css_url(url: CssUrl) -> Self {
pub unsafe fn from_url_value_data(url: &URLValueData) -> Result<Self, ()> { let image_value = unsafe {
CssUrl::from_url_value_data(url).map(Self::from_css_url) let ptr = bindings::Gecko_ImageValue_Create(url.for_ffi());
} // We do not expect Gecko_ImageValue_Create returns null.
debug_assert!(!ptr.is_null());
/// Convert from nsStyleImageRequest to SpecifiedUrl. RefPtr::from_addrefed(ptr)
pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Result<Self, ()> { };
if image_request.mImageValue.mRawPtr.is_null() { Self { url, image_value }
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)
} }
} }
@ -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<W>(
url_value_data: &URLValueData,
dest: &mut CssWriter<W>,
) -> 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()`. /// 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<W>(&self, dest: &mut CssWriter<W>) -> 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<Self, ()> {
Ok(ComputedUrl(
SpecifiedUrl::from_css_url(CssUrl::from_url_value_data(url)?)
))
}
}
/// The computed value of a CSS `url()` for image. /// 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<W>(&self, dest: &mut CssWriter<W>) -> 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<Self, ()> {
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<Self, ()> {
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)
}
}

View file

@ -697,7 +697,7 @@ def set_gecko_property(ffi_name, expr):
} }
SVGPaintKind::PaintServer(url) => { SVGPaintKind::PaintServer(url) => {
unsafe { unsafe {
bindings::Gecko_nsStyleSVGPaint_SetURLValue(paint, url.url_value.get()); bindings::Gecko_nsStyleSVGPaint_SetURLValue(paint, url.0.url_value.get());
} }
} }
SVGPaintKind::Color(color) => { SVGPaintKind::Color(color) => {
@ -737,8 +737,8 @@ def set_gecko_property(ffi_name, expr):
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
use values::computed::url::ComputedUrl;
use values::generics::svg::{SVGPaint, SVGPaintKind}; use values::generics::svg::{SVGPaint, SVGPaintKind};
use values::specified::url::SpecifiedUrl;
use self::structs::nsStyleSVGPaintType; use self::structs::nsStyleSVGPaintType;
use self::structs::nsStyleSVGFallbackType; use self::structs::nsStyleSVGFallbackType;
let ref paint = ${get_gecko_property(gecko_ffi_name)}; let ref paint = ${get_gecko_property(gecko_ffi_name)};
@ -760,7 +760,7 @@ def set_gecko_property(ffi_name, expr):
nsStyleSVGPaintType::eStyleSVGPaintType_Server => { nsStyleSVGPaintType::eStyleSVGPaintType_Server => {
unsafe { unsafe {
SVGPaintKind::PaintServer( SVGPaintKind::PaintServer(
SpecifiedUrl::from_url_value_data( ComputedUrl::from_url_value_data(
&(**paint.mPaint.mPaintServer.as_ref())._base &(**paint.mPaint.mPaintServer.as_ref())._base
).unwrap() ).unwrap()
) )
@ -939,7 +939,7 @@ def set_gecko_property(ffi_name, expr):
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
match v { match v {
UrlOrNone::Url(ref url) => { 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 => { UrlOrNone::None => {
unsafe { unsafe {
@ -961,16 +961,18 @@ def set_gecko_property(ffi_name, expr):
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { 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() { if self.gecko.${gecko_ffi_name}.mRawPtr.is_null() {
UrlOrNone::none() return UrlOrNone::none()
} else { }
unsafe {
let ref gecko_url_value = *self.gecko.${gecko_ffi_name}.mRawPtr; unsafe {
UrlOrNone::Url(SpecifiedUrl::from_url_value_data(&gecko_url_value._base) let gecko_url_value = &*self.gecko.${gecko_ffi_name}.mRawPtr;
.expect("${gecko_ffi_name} could not convert to SpecifiedUrl")) UrlOrNone::Url(
} ComputedUrl::from_url_value_data(&gecko_url_value._base)
.expect("${gecko_ffi_name} could not convert to ComputedUrl")
)
} }
} }
</%def> </%def>
@ -4110,7 +4112,7 @@ fn static_assert() {
} }
UrlOrNone::Url(ref url) => { UrlOrNone::Url(ref url) => {
unsafe { 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 { 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() { if self.gecko.mListStyleImage.mRawPtr.is_null() {
return UrlOrNone::None; return UrlOrNone::None;
@ -4133,8 +4135,9 @@ fn static_assert() {
unsafe { unsafe {
let ref gecko_image_request = *self.gecko.mListStyleImage.mRawPtr; let ref gecko_image_request = *self.gecko.mListStyleImage.mRawPtr;
UrlOrNone::Url(SpecifiedImageUrl::from_image_request(gecko_image_request) UrlOrNone::Url(ComputedImageUrl::from_image_request(
.expect("mListStyleImage could not convert to SpecifiedImageUrl")) gecko_image_request
).expect("mListStyleImage could not convert to ComputedImageUrl"))
} }
} }
@ -4480,7 +4483,7 @@ fn static_assert() {
}, },
Url(ref url) => { Url(ref url) => {
unsafe { 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 { pub fn clone_filter(&self) -> longhands::filter::computed_value::T {
use values::generics::effects::Filter; 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_BLUR;
use gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS; use gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS;
use gecko_bindings::structs::NS_STYLE_FILTER_CONTRAST; use gecko_bindings::structs::NS_STYLE_FILTER_CONTRAST;
@ -4541,7 +4544,7 @@ fn static_assert() {
NS_STYLE_FILTER_URL => { NS_STYLE_FILTER_URL => {
filters.push(unsafe { filters.push(unsafe {
Filter::Url( 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": % if ident == "clip_path":
ShapeSource::ImageOrUrl(ref url) => { ShapeSource::ImageOrUrl(ref url) => {
unsafe { unsafe {
bindings::Gecko_StyleShapeSource_SetURLValue(${ident}, url.url_value.get()) bindings::Gecko_StyleShapeSource_SetURLValue(${ident}, url.0.url_value.get())
} }
} }
% elif ident == "shape_outside": % elif ident == "shape_outside":
@ -5347,7 +5350,7 @@ clip-path
unsafe { unsafe {
Gecko_SetCursorImageValue( Gecko_SetCursorImageValue(
&mut self.gecko.mCursorImages[i], &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 { pub fn clone_cursor(&self) -> longhands::cursor::computed_value::T {
use values::computed::ui::CursorImage; use values::computed::ui::CursorImage;
use values::computed::url::ComputedImageUrl;
use style_traits::cursor::CursorKind; use style_traits::cursor::CursorKind;
use values::specified::url::SpecifiedImageUrl;
let keyword = match self.gecko.mCursor as u32 { let keyword = match self.gecko.mCursor as u32 {
structs::NS_STYLE_CURSOR_AUTO => CursorKind::Auto, structs::NS_STYLE_CURSOR_AUTO => CursorKind::Auto,
@ -5423,8 +5426,8 @@ clip-path
let images = self.gecko.mCursorImages.iter().map(|gecko_cursor_image| { let images = self.gecko.mCursorImages.iter().map(|gecko_cursor_image| {
let url = unsafe { let url = unsafe {
let gecko_image_request = gecko_cursor_image.mImage.mRawPtr.as_ref().unwrap(); let gecko_image_request = gecko_cursor_image.mImage.mRawPtr.as_ref().unwrap();
SpecifiedImageUrl::from_image_request(&gecko_image_request) ComputedImageUrl::from_image_request(&gecko_image_request)
.expect("mCursorImages.mImage could not convert to SpecifiedImageUrl") .expect("mCursorImages.mImage could not convert to ComputedImageUrl")
}; };
let hotspot = let hotspot =
@ -5483,7 +5486,7 @@ clip-path
pub fn set_content(&mut self, v: longhands::content::computed_value::T, device: &Device) { pub fn set_content(&mut self, v: longhands::content::computed_value::T, device: &Device) {
use values::CustomIdent; use values::CustomIdent;
use values::computed::counters::{Content, ContentItem}; use values::generics::counters::{Content, ContentItem};
use values::generics::CounterStyleOrNone; use values::generics::CounterStyleOrNone;
use gecko_bindings::structs::nsStyleContentData; use gecko_bindings::structs::nsStyleContentData;
use gecko_bindings::structs::nsStyleContentAttr; use gecko_bindings::structs::nsStyleContentAttr;
@ -5610,7 +5613,7 @@ clip-path
unsafe { unsafe {
bindings::Gecko_SetContentDataImageValue( bindings::Gecko_SetContentDataImageValue(
&mut self.gecko.mContents[i], &mut self.gecko.mContents[i],
url.image_value.get(), url.0.image_value.get(),
) )
} }
} }
@ -5635,10 +5638,10 @@ clip-path
use {Atom, Namespace}; use {Atom, Namespace};
use gecko::conversions::string_from_chars_pointer; use gecko::conversions::string_from_chars_pointer;
use gecko_bindings::structs::nsStyleContentType::*; 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::{CustomIdent, Either};
use values::generics::CounterStyleOrNone; use values::generics::CounterStyleOrNone;
use values::specified::url::SpecifiedImageUrl;
use values::specified::Attr; use values::specified::Attr;
if self.gecko.mContents.is_empty() { if self.gecko.mContents.is_empty() {
@ -5699,8 +5702,8 @@ clip-path
let gecko_image_request = let gecko_image_request =
&**gecko_content.mContent.mImage.as_ref(); &**gecko_content.mContent.mImage.as_ref();
ContentItem::Url( ContentItem::Url(
SpecifiedImageUrl::from_image_request(gecko_image_request) ComputedImageUrl::from_image_request(gecko_image_request)
.expect("mContent could not convert to SpecifiedImageUrl") .expect("mContent could not convert to ComputedImageUrl")
) )
} }
}, },

View file

@ -14,6 +14,7 @@ use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
use values::animated::color::RGBA; use values::animated::color::RGBA;
use values::computed::{Angle, Number}; use values::computed::{Angle, Number};
use values::computed::length::Length; use values::computed::length::Length;
use values::computed::url::ComputedUrl;
use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::distance::{ComputeSquaredDistance, SquaredDistance};
use values::generics::effects::BoxShadow as GenericBoxShadow; use values::generics::effects::BoxShadow as GenericBoxShadow;
use values::generics::effects::Filter as GenericFilter; use values::generics::effects::Filter as GenericFilter;
@ -42,11 +43,11 @@ pub struct FilterList(pub Vec<Filter>);
/// An animated value for a single `filter`. /// An animated value for a single `filter`.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub type Filter = GenericFilter<Angle, Number, Length, SimpleShadow>; pub type Filter = GenericFilter<Angle, Number, Length, SimpleShadow, ComputedUrl>;
/// An animated value for a single `filter`. /// An animated value for a single `filter`.
#[cfg(not(feature = "gecko"))] #[cfg(not(feature = "gecko"))]
pub type Filter = GenericFilter<Angle, Number, Length, Impossible>; pub type Filter = GenericFilter<Angle, Number, Length, Impossible, ComputedUrl>;
/// An animated value for the `drop-shadow()` filter. /// An animated value for the `drop-shadow()` filter.
pub type SimpleShadow = GenericSimpleShadow<Option<RGBA>, Length, Length>; pub type SimpleShadow = GenericSimpleShadow<Option<RGBA>, Length, Length>;

View file

@ -15,9 +15,7 @@ use values::computed::Angle as ComputedAngle;
use values::computed::BorderCornerRadius as ComputedBorderCornerRadius; use values::computed::BorderCornerRadius as ComputedBorderCornerRadius;
use values::computed::MaxLength as ComputedMaxLength; use values::computed::MaxLength as ComputedMaxLength;
use values::computed::MozLength as ComputedMozLength; use values::computed::MozLength as ComputedMozLength;
#[cfg(feature = "servo")]
use values::computed::url::ComputedUrl; use values::computed::url::ComputedUrl;
use values::specified::url::SpecifiedUrl;
pub mod color; pub mod color;
pub mod effects; pub mod effects;
@ -260,8 +258,6 @@ macro_rules! trivial_to_animated_value {
trivial_to_animated_value!(Au); trivial_to_animated_value!(Au);
trivial_to_animated_value!(ComputedAngle); trivial_to_animated_value!(ComputedAngle);
trivial_to_animated_value!(SpecifiedUrl);
#[cfg(feature = "servo")]
trivial_to_animated_value!(ComputedUrl); trivial_to_animated_value!(ComputedUrl);
trivial_to_animated_value!(bool); trivial_to_animated_value!(bool);
trivial_to_animated_value!(f32); trivial_to_animated_value!(f32);

View file

@ -4,22 +4,10 @@
//! Computed values for counter properties //! Computed values for counter properties
#[cfg(feature = "servo")] use values::computed::url::ComputedImageUrl;
use computed_values::list_style_type::T as ListStyleType; use values::generics::counters as generics;
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::CounterIncrement as GenericCounterIncrement; use values::generics::counters::CounterIncrement as GenericCounterIncrement;
use values::generics::counters::CounterReset as GenericCounterReset; 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. /// A computed value for the `counter-increment` property.
pub type CounterIncrement = GenericCounterIncrement<i32>; pub type CounterIncrement = GenericCounterIncrement<i32>;
@ -27,134 +15,9 @@ pub type CounterIncrement = GenericCounterIncrement<i32>;
/// A computed value for the `counter-increment` property. /// A computed value for the `counter-increment` property.
pub type CounterReset = GenericCounterReset<i32>; pub type CounterReset = GenericCounterReset<i32>;
impl Content { /// A computed value for the `content` property.
/// Set `content` property to `normal`. pub type Content = generics::Content<ComputedImageUrl>;
#[inline]
pub fn normal() -> Self {
Content::Normal
}
#[cfg(feature = "servo")] /// A computed content item.
fn parse_counter_style(input: &mut Parser) -> ListStyleType { pub type ContentItem = generics::ContentItem<ComputedImageUrl>;
input
.try(|input| {
input.expect_comma()?;
ListStyleType::parse(input)
})
.unwrap_or(ListStyleType::Decimal)
}
#[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 | [ <string> | <counter> | open-quote | close-quote | no-open-quote |
// no-close-quote ]+
// TODO: <uri>, attr(<identifier>)
fn parse<'i, 't>(
_context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
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()))
}
}

View file

@ -9,6 +9,7 @@ use values::Impossible;
use values::computed::{Angle, NonNegativeNumber}; use values::computed::{Angle, NonNegativeNumber};
use values::computed::color::RGBAColor; use values::computed::color::RGBAColor;
use values::computed::length::{Length, NonNegativeLength}; use values::computed::length::{Length, NonNegativeLength};
use values::computed::url::ComputedUrl;
use values::generics::effects::BoxShadow as GenericBoxShadow; use values::generics::effects::BoxShadow as GenericBoxShadow;
use values::generics::effects::Filter as GenericFilter; use values::generics::effects::Filter as GenericFilter;
use values::generics::effects::SimpleShadow as GenericSimpleShadow; use values::generics::effects::SimpleShadow as GenericSimpleShadow;
@ -18,11 +19,11 @@ pub type BoxShadow = GenericBoxShadow<Option<RGBAColor>, Length, NonNegativeLeng
/// A computed value for a single `filter`. /// A computed value for a single `filter`.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, SimpleShadow>; pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, SimpleShadow, ComputedUrl>;
/// A computed value for a single `filter`. /// A computed value for a single `filter`.
#[cfg(not(feature = "gecko"))] #[cfg(not(feature = "gecko"))]
pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible>; pub type Filter = GenericFilter<Angle, NonNegativeNumber, NonNegativeLength, Impossible, ComputedUrl>;
/// A computed value for the `drop-shadow()` filter. /// A computed value for the `drop-shadow()` filter.
pub type SimpleShadow = GenericSimpleShadow<Option<RGBAColor>, Length, NonNegativeLength>; pub type SimpleShadow = GenericSimpleShadow<Option<RGBAColor>, Length, NonNegativeLength>;

View file

@ -4,8 +4,14 @@
//! Generic types for counters-related CSS values. //! Generic types for counters-related CSS values.
#[cfg(feature = "servo")]
use computed_values::list_style_type::T as ListStyleType;
use std::ops::Deref; use std::ops::Deref;
use values::CustomIdent; use values::CustomIdent;
#[cfg(feature = "gecko")]
use values::generics::CounterStyleOrNone;
#[cfg(feature = "gecko")]
use values::specified::Attr;
/// A name / value pair for counters. /// A name / value pair for counters.
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
@ -74,3 +80,78 @@ impl<I> Default for Counters<I> {
Counters(vec![].into_boxed_slice()) 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<ImageUrl> {
/// `normal` reserved keyword.
Normal,
/// `none` reserved keyword.
None,
/// `-moz-alt-content`.
#[cfg(feature = "gecko")]
MozAltContent,
/// Content items.
Items(#[css(iterable)] Box<[ContentItem<ImageUrl>]>),
}
impl<ImageUrl> Content<ImageUrl> {
/// 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<ImageUrl> {
/// Literal string content.
String(Box<str>),
/// `counter(name, style)`.
#[css(comma, function)]
Counter(CustomIdent, #[css(skip_if = "is_decimal")] CounterStyleType),
/// `counters(name, separator, style)`.
#[css(comma, function)]
Counters(
CustomIdent,
Box<str>,
#[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),
}

View file

@ -4,9 +4,6 @@
//! Generic types for CSS values related to effects. //! Generic types for CSS values related to effects.
#[cfg(feature = "gecko")]
use values::specified::url::SpecifiedUrl;
/// A generic value for a single `box-shadow`. /// A generic value for a single `box-shadow`.
#[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, #[derive(Animate, Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo,
ToAnimatedValue, ToAnimatedZero, ToCss)] ToAnimatedValue, ToAnimatedZero, ToCss)]
@ -23,9 +20,10 @@ pub struct BoxShadow<Color, SizeLength, BlurShapeLength, ShapeLength> {
/// A generic value for a single `filter`. /// A generic value for a single `filter`.
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[animation(no_bound(Url))]
#[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq, #[derive(Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss)] SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss)]
pub enum Filter<Angle, Factor, Length, DropShadow> { pub enum Filter<Angle, Factor, Length, DropShadow, Url> {
/// `blur(<length>)` /// `blur(<length>)`
#[css(function)] #[css(function)]
Blur(Length), Blur(Length),
@ -58,8 +56,7 @@ pub enum Filter<Angle, Factor, Length, DropShadow> {
DropShadow(DropShadow), DropShadow(DropShadow),
/// `<url>` /// `<url>`
#[animation(error)] #[animation(error)]
#[cfg(feature = "gecko")] Url(Url),
Url(SpecifiedUrl),
} }
/// A generic value for the `drop-shadow()` filter and the `text-shadow` property. /// A generic value for the `drop-shadow()` filter and the `text-shadow` property.

View file

@ -8,18 +8,19 @@
use computed_values::list_style_type::T as ListStyleType; use computed_values::list_style_type::T as ListStyleType;
use cssparser::{Parser, Token}; use cssparser::{Parser, Token};
use parser::{Parse, ParserContext}; use parser::{Parse, ParserContext};
use selectors::parser::SelectorParseErrorKind;
use style_traits::{ParseError, StyleParseErrorKind}; use style_traits::{ParseError, StyleParseErrorKind};
use values::CustomIdent; use values::CustomIdent;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use values::generics::CounterStyleOrNone; use values::generics::CounterStyleOrNone;
use values::generics::counters as generics;
use values::generics::counters::CounterIncrement as GenericCounterIncrement; use values::generics::counters::CounterIncrement as GenericCounterIncrement;
use values::generics::counters::CounterPair; use values::generics::counters::CounterPair;
use values::generics::counters::CounterReset as GenericCounterReset; use values::generics::counters::CounterReset as GenericCounterReset;
use values::specified::Integer;
use values::specified::url::SpecifiedImageUrl;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use values::specified::Attr; use values::specified::Attr;
use values::specified::Integer;
#[cfg(feature = "gecko")]
use values::specified::url::SpecifiedImageUrl;
/// A specified value for the `counter-increment` property. /// A specified value for the `counter-increment` property.
pub type CounterIncrement = GenericCounterIncrement<Integer>; pub type CounterIncrement = GenericCounterIncrement<Integer>;
@ -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. /// The specified value for the `content` property.
/// pub type Content = generics::Content<SpecifiedImageUrl>;
/// https://drafts.csswg.org/css-content/#propdef-content
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, /// The specified value for a content item in the `content` property.
ToComputedValue, ToCss)] pub type ContentItem = generics::ContentItem<SpecifiedImageUrl>;
pub enum Content {
/// `normal` reserved keyword. impl Content {
Normal, #[cfg(feature = "servo")]
/// `none` reserved keyword. fn parse_counter_style(_: &ParserContext, input: &mut Parser) -> ListStyleType {
None, input
/// `-moz-alt-content`. .try(|input| {
input.expect_comma()?;
ListStyleType::parse(input)
})
.unwrap_or(ListStyleType::Decimal)
}
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
MozAltContent, fn parse_counter_style(context: &ParserContext, input: &mut Parser) -> CounterStyleOrNone {
/// Content items. input
Items(#[css(iterable)] Box<[ContentItem]>), .try(|input| {
input.expect_comma()?;
CounterStyleOrNone::parse(context, input)
})
.unwrap_or(CounterStyleOrNone::decimal())
}
} }
/// Items for the `content` property. impl Parse for Content {
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, // normal | none | [ <string> | <counter> | open-quote | close-quote | no-open-quote |
ToComputedValue, ToCss)] // no-close-quote ]+
pub enum ContentItem { // TODO: <uri>, attr(<identifier>)
/// Literal string content. fn parse<'i, 't>(
String(Box<str>), context: &ParserContext,
/// `counter(name, style)`. input: &mut Parser<'i, 't>,
#[css(comma, function)] ) -> Result<Self, ParseError<'i>> {
Counter(CustomIdent, #[css(skip_if = "is_decimal")] CounterStyleType), if input
/// `counters(name, separator, style)`. .try(|input| input.expect_ident_matching("normal"))
#[css(comma, function)] .is_ok()
Counters( {
CustomIdent, return Ok(generics::Content::Normal);
Box<str>, }
#[css(skip_if = "is_decimal")] CounterStyleType, if input
), .try(|input| input.expect_ident_matching("none"))
/// `open-quote`. .is_ok()
OpenQuote, {
/// `close-quote`. return Ok(generics::Content::None);
CloseQuote, }
/// `no-open-quote`. #[cfg(feature = "gecko")]
NoOpenQuote, {
/// `no-close-quote`. if input
NoCloseQuote, .try(|input| input.expect_ident_matching("-moz-alt-content"))
/// `attr([namespace? `|`]? ident)` .is_ok()
#[cfg(feature = "gecko")] {
Attr(Attr), return Ok(generics::Content::MozAltContent);
/// `url(url)` }
#[cfg(feature = "gecko")] }
Url(SpecifiedImageUrl),
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()))
}
} }

View file

@ -19,7 +19,6 @@ use values::generics::effects::SimpleShadow as GenericSimpleShadow;
use values::specified::{Angle, NumberOrPercentage}; use values::specified::{Angle, NumberOrPercentage};
use values::specified::color::RGBAColor; use values::specified::color::RGBAColor;
use values::specified::length::{Length, NonNegativeLength}; use values::specified::length::{Length, NonNegativeLength};
#[cfg(feature = "gecko")]
use values::specified::url::SpecifiedUrl; use values::specified::url::SpecifiedUrl;
/// A specified value for a single shadow of the `box-shadow` property. /// 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`. /// A specified value for a single `filter`.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub type Filter = GenericFilter<Angle, Factor, NonNegativeLength, SimpleShadow>; pub type Filter = GenericFilter<Angle, Factor, NonNegativeLength, SimpleShadow, SpecifiedUrl>;
/// A specified value for a single `filter`. /// A specified value for a single `filter`.
#[cfg(not(feature = "gecko"))] #[cfg(not(feature = "gecko"))]
pub type Filter = GenericFilter<Angle, Factor, NonNegativeLength, Impossible>; pub type Filter = GenericFilter<Angle, Factor, NonNegativeLength, Impossible, SpecifiedUrl>;
/// A value for the `<factor>` parts in `Filter`. /// A value for the `<factor>` parts in `Filter`.
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)] #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]