--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -2129,16 +2129,35 @@ MOZ_DEFINE_MALLOC_SIZE_OF(GeckoURLValueM
size_t
Gecko_URLValue_SizeOfIncludingThis(URLValue* aURL)
{
MOZ_ASSERT(NS_IsMainThread());
return aURL->SizeOfIncludingThis(GeckoURLValueMallocSizeOf);
}
+void
+Gecko_GetComputedURLSpec(const URLValueData* aURL, nsCString* aOut)
+{
+ MOZ_ASSERT(aURL);
+ MOZ_ASSERT(aOut);
+ if (aURL->IsLocalRef()) {
+ aOut->Assign(aURL->GetString());
+ return;
+ }
+ if (nsIURI* uri = aURL->GetURI()) {
+ nsresult rv = uri->GetSpec(*aOut);
+ if (NS_SUCCEEDED(rv)) {
+ return;
+ }
+ }
+
+ aOut->AssignLiteral("about:invalid");
+}
+
NS_IMPL_THREADSAFE_FFI_REFCOUNTING(css::URLValue, CSSURLValue);
NS_IMPL_THREADSAFE_FFI_REFCOUNTING(URLExtraData, URLExtraData);
NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
nsCSSShadowArray*
Gecko_NewCSSShadowArray(uint32_t aLen)
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -540,16 +540,17 @@ void Gecko_nsStyleSVGPaint_Reset(nsStyle
void Gecko_nsStyleSVG_SetDashArrayLength(nsStyleSVG* svg, uint32_t len);
void Gecko_nsStyleSVG_CopyDashArray(nsStyleSVG* dst, const nsStyleSVG* src);
void Gecko_nsStyleSVG_SetContextPropertiesLength(nsStyleSVG* svg, uint32_t len);
void Gecko_nsStyleSVG_CopyContextProperties(nsStyleSVG* dst, const nsStyleSVG* src);
mozilla::css::URLValue* Gecko_NewURLValue(ServoBundledURI uri);
size_t Gecko_URLValue_SizeOfIncludingThis(mozilla::css::URLValue* url);
+void Gecko_GetComputedURLSpec(const mozilla::css::URLValueData* url, nsCString* spec);
NS_DECL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::URLValue, CSSURLValue);
NS_DECL_THREADSAFE_FFI_REFCOUNTING(RawGeckoURLExtraData, URLExtraData);
void Gecko_FillAllImageLayers(nsStyleImageLayers* layers, uint32_t max_len);
NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
nsCSSShadowArray* Gecko_NewCSSShadowArray(uint32_t len);
NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsCSSShadowArray, CSSShadowArray);
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -435,17 +435,17 @@ fixups = [
headers = ["mozilla/ServoBindings.h"]
hide-types = [
"nsACString_internal",
"nsAString_internal",
"ComputedStyleBorrowed",
"ComputedStyleBorrowedOrNull",
]
raw-lines = [
- "pub use nsstring::{nsACString, nsAString, nsString, nsStringRepr};",
+ "pub use nsstring::{nsACString, nsAString, nsCString, nsString, nsStringRepr};",
"use gecko_bindings::structs::nsStyleTransformMatrix;",
"use gecko_bindings::structs::nsTArray;",
"type nsACString_internal = nsACString;",
"type nsAString_internal = nsAString;",
"pub type ComputedStyleBorrowed<'a> = &'a ::properties::ComputedValues;",
"pub type ComputedStyleBorrowedOrNull<'a> = Option<&'a ::properties::ComputedValues>;",
"pub type ServoComputedDataBorrowed<'a> = &'a ServoComputedData;",
"pub type RawServoAnimationValueTableBorrowed<'a> = &'a ();"
--- a/servo/components/style/gecko/conversions.rs
+++ b/servo/components/style/gecko/conversions.rs
@@ -140,21 +140,21 @@ impl Angle {
}
impl nsStyleImage {
/// Set a given Servo `Image` value into this `nsStyleImage`.
pub fn set(&mut self, image: Image) {
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
let ref mut rect = *self.mCropRect.mPtr;
image_rect
.top
.to_gecko_style_coord(&mut rect.data_at_mut(0));
image_rect
--- a/servo/components/style/gecko/url.rs
+++ b/servo/components/style/gecko/url.rs
@@ -7,20 +7,23 @@
use cssparser::Parser;
use gecko_bindings::bindings;
use gecko_bindings::structs::{ServoBundledURI, URLExtraData};
use gecko_bindings::structs::mozilla::css::URLValueData;
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")]
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct CssUrl {
/// The URL in unresolved string form.
///
/// Refcounted since cloning this should be cheap and data: uris can be
@@ -65,20 +68,18 @@ impl CssUrl {
}
/// Returns true if this URL looks like a fragment.
/// See https://drafts.csswg.org/css-values/#local-urls
pub fn is_fragment(&self) -> bool {
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
}
/// Little helper for Gecko's ffi.
pub fn as_slice_components(&self) -> (*const u8, usize) {
(
self.serialization.as_str().as_ptr(),
@@ -116,17 +117,17 @@ impl MallocSizeOf for CssUrl {
// We ignore `extra_data`, because RefPtr is tricky, and there aren't
// many of them in practise (sharing is common).
0
}
}
/// 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,
/// Gecko's URLValue so that we can reuse it while rematching a
/// property with this specified value.
#[css(skip)]
pub url_value: RefPtr<URLValue>,
}
@@ -134,25 +135,21 @@ pub struct SpecifiedUrl {
impl SpecifiedUrl {
fn from_css_url(url: CssUrl) -> Self {
let url_value = unsafe {
let ptr = bindings::Gecko_NewURLValue(url.for_ffi());
// We do not expect Gecko_NewURLValue returns null.
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<Self, ()> {
- 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)
}
}
impl Eq for SpecifiedUrl {}
@@ -174,59 +171,43 @@ impl MallocSizeOf for SpecifiedUrl {
n += unsafe { bindings::Gecko_URLValue_SizeOfIncludingThis(self.url_value.get()) };
n
}
}
/// 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,
/// Gecko's ImageValue so that we can reuse it while rematching a
/// property with this specified value.
#[css(skip)]
pub image_value: RefPtr<ImageValue>,
}
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,
context: &ParserContext,
) -> Result<Self, ParseError<'a>> {
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<Self, ()> {
- 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<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)
+ 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 }
}
}
impl Parse for SpecifiedImageUrl {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
@@ -248,12 +229,109 @@ impl MallocSizeOf for SpecifiedImageUrl
// Although this is a RefPtr, this is the primary reference because
// SpecifiedUrl is responsible for creating the image_value. So we
// measure unconditionally here.
n += unsafe { bindings::Gecko_ImageValue_SizeOfIncludingThis(self.image_value.get()) };
n
}
}
+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()`.
-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.
-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)
+ }
+}
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -692,17 +692,17 @@ def set_gecko_property(ffi_name, expr):
SVGPaintKind::ContextFill => {
paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_ContextFill;
}
SVGPaintKind::ContextStroke => {
paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_ContextStroke;
}
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) => {
paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_Color;
unsafe {
*paint.mPaint.mColor.as_mut() = convert_rgba_to_nscolor(&color);
}
}
@@ -732,18 +732,18 @@ def set_gecko_property(ffi_name, expr):
#[allow(non_snake_case)]
pub fn reset_${ident}(&mut self, other: &Self) {
self.copy_${ident}_from(other)
}
#[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)};
let fallback = match paint.mFallbackType {
nsStyleSVGFallbackType::eStyleSVGFallbackType_Color => {
Some(Either::First(convert_nscolor_to_rgba(paint.mFallbackColor)))
},
@@ -755,17 +755,17 @@ def set_gecko_property(ffi_name, expr):
let kind = match paint.mType {
nsStyleSVGPaintType::eStyleSVGPaintType_None => SVGPaintKind::None,
nsStyleSVGPaintType::eStyleSVGPaintType_ContextFill => SVGPaintKind::ContextFill,
nsStyleSVGPaintType::eStyleSVGPaintType_ContextStroke => SVGPaintKind::ContextStroke,
nsStyleSVGPaintType::eStyleSVGPaintType_Server => {
unsafe {
SVGPaintKind::PaintServer(
- SpecifiedUrl::from_url_value_data(
+ ComputedUrl::from_url_value_data(
&(**paint.mPaint.mPaintServer.as_ref())._base
).unwrap()
)
}
}
nsStyleSVGPaintType::eStyleSVGPaintType_Color => {
unsafe { SVGPaintKind::Color(convert_nscolor_to_rgba(*paint.mPaint.mColor.as_ref())) }
}
@@ -934,17 +934,17 @@ def set_gecko_property(ffi_name, expr):
}
</%def>
<%def name="impl_css_url(ident, gecko_ffi_name)">
#[allow(non_snake_case)]
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 {
self.gecko.${gecko_ffi_name}.clear();
}
}
}
}
@@ -956,26 +956,28 @@ def set_gecko_property(ffi_name, expr):
}
#[allow(non_snake_case)]
pub fn reset_${ident}(&mut self, other: &Self) {
self.copy_${ident}_from(other)
}
#[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")
+ )
}
}
</%def>
<%
transform_functions = [
("Matrix3D", "matrix3d", ["number"] * 16),
("Matrix", "matrix", ["number"] * 6),
@@ -4105,41 +4107,42 @@ fn static_assert() {
match image {
UrlOrNone::None => {
unsafe {
Gecko_SetListStyleImageNone(&mut self.gecko);
}
}
UrlOrNone::Url(ref url) => {
unsafe {
- Gecko_SetListStyleImageImageValue(&mut self.gecko, url.image_value.get());
+ Gecko_SetListStyleImageImageValue(&mut self.gecko, url.0.image_value.get());
}
}
}
}
pub fn copy_list_style_image_from(&mut self, other: &Self) {
unsafe { Gecko_CopyListStyleImageFrom(&mut self.gecko, &other.gecko); }
}
pub fn reset_list_style_image(&mut self, other: &Self) {
self.copy_list_style_image_from(other)
}
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;
}
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"))
}
}
pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T, device: &Device) {
use gecko_bindings::bindings::Gecko_SetCounterStyleToString;
use nsstring::{nsACString, nsCStr};
use self::longhands::list_style_type::computed_value::T;
match v {
@@ -4475,17 +4478,17 @@ fn static_assert() {
}
}
let gecko_shadow = init_shadow(gecko_filter);
gecko_shadow.mArray[0].set_from_simple_shadow(shadow);
},
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());
}
},
}
}
}
pub fn copy_filter_from(&mut self, other: &Self) {
unsafe {
@@ -4494,17 +4497,17 @@ fn static_assert() {
}
pub fn reset_filter(&mut self, other: &Self) {
self.copy_filter_from(other)
}
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;
use gecko_bindings::structs::NS_STYLE_FILTER_GRAYSCALE;
use gecko_bindings::structs::NS_STYLE_FILTER_INVERT;
use gecko_bindings::structs::NS_STYLE_FILTER_OPACITY;
use gecko_bindings::structs::NS_STYLE_FILTER_SATURATE;
use gecko_bindings::structs::NS_STYLE_FILTER_SEPIA;
@@ -4536,17 +4539,17 @@ fn static_assert() {
Filter::DropShadow(
(**filter.__bindgen_anon_1.mDropShadow.as_ref()).mArray[0].to_simple_shadow(),
)
});
},
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()
)
});
}
_ => {},
}
}
longhands::filter::computed_value::T(filters)
}
@@ -5002,17 +5005,17 @@ fn static_assert() {
// clean up existing struct
unsafe { Gecko_DestroyShapeSource(${ident}) };
${ident}.mType = StyleShapeSourceType::None;
match v {
% 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":
ShapeSource::ImageOrUrl(image) => {
unsafe {
bindings::Gecko_NewShapeImage(${ident});
let style_image = &mut *${ident}.mShapeImage.mPtr;
style_image.set(image);
@@ -5342,17 +5345,17 @@ clip-path
unsafe {
Gecko_SetCursorArrayLength(&mut self.gecko, v.images.len());
}
for i in 0..v.images.len() {
unsafe {
Gecko_SetCursorImageValue(
&mut self.gecko.mCursorImages[i],
- v.images[i].url.image_value.get(),
+ v.images[i].url.0.image_value.get(),
);
}
match v.images[i].hotspot {
Some((x, y)) => {
self.gecko.mCursorImages[i].mHaveHotspot = true;
self.gecko.mCursorImages[i].mHotspotX = x;
self.gecko.mCursorImages[i].mHotspotY = y;
@@ -5372,18 +5375,18 @@ clip-path
}
pub fn reset_cursor(&mut self, other: &Self) {
self.copy_cursor_from(other)
}
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,
structs::NS_STYLE_CURSOR_NONE => CursorKind::None,
structs::NS_STYLE_CURSOR_DEFAULT => CursorKind::Default,
structs::NS_STYLE_CURSOR_POINTER => CursorKind::Pointer,
structs::NS_STYLE_CURSOR_CONTEXT_MENU => CursorKind::ContextMenu,
structs::NS_STYLE_CURSOR_HELP => CursorKind::Help,
@@ -5418,18 +5421,18 @@ clip-path
structs::NS_STYLE_CURSOR_ZOOM_IN => CursorKind::ZoomIn,
structs::NS_STYLE_CURSOR_ZOOM_OUT => CursorKind::ZoomOut,
_ => panic!("Found unexpected value in style struct for cursor property"),
};
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 =
if gecko_cursor_image.mHaveHotspot {
Some((gecko_cursor_image.mHotspotX, gecko_cursor_image.mHotspotY))
} else {
None
};
@@ -5478,17 +5481,17 @@ clip-path
<%self:impl_trait style_struct_name="Counters"
skip_longhands="content counter-increment counter-reset">
pub fn ineffective_content_property(&self) -> bool {
self.gecko.mContents.is_empty()
}
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;
use gecko_bindings::structs::nsStyleContentType;
use gecko_bindings::structs::nsStyleContentType::*;
use gecko_bindings::bindings::Gecko_ClearAndResizeStyleContents;
// Converts a string as utf16, and returns an owned, zero-terminated raw buffer.
@@ -5605,17 +5608,17 @@ clip-path
style.clone(),
device,
);
}
ContentItem::Url(ref url) => {
unsafe {
bindings::Gecko_SetContentDataImageValue(
&mut self.gecko.mContents[i],
- url.image_value.get(),
+ url.0.image_value.get(),
)
}
}
}
}
}
}
}
@@ -5630,20 +5633,20 @@ clip-path
pub fn reset_content(&mut self, other: &Self) {
self.copy_content_from(other)
}
pub fn clone_content(&self) -> longhands::content::computed_value::T {
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() {
return Content::Normal;
}
if self.gecko.mContents.len() == 1 &&
self.gecko.mContents[0].mType == eStyleContentType_AltContent {
@@ -5694,18 +5697,18 @@ clip-path
ContentItem::Counters(ident, separator.into_boxed_str(), style)
}
},
eStyleContentType_Image => {
unsafe {
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")
)
}
},
_ => panic!("Found unexpected value in style struct for content property"),
}
}).collect::<Vec<_>>().into_boxed_slice()
)
}
--- a/servo/components/style/values/animated/effects.rs
+++ b/servo/components/style/values/animated/effects.rs
@@ -9,16 +9,17 @@ use properties::longhands::filter::compu
use properties::longhands::text_shadow::computed_value::T as ComputedTextShadowList;
use std::cmp;
#[cfg(not(feature = "gecko"))]
use values::Impossible;
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;
use values::generics::effects::SimpleShadow as GenericSimpleShadow;
/// An animated value for the `box-shadow` property.
pub type BoxShadowList = ShadowList<BoxShadow>;
@@ -37,21 +38,21 @@ pub type BoxShadow = GenericBoxShadow<Op
/// An animated value for the `filter` property.
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
#[derive(Clone, Debug, PartialEq)]
pub struct FilterList(pub Vec<Filter>);
/// An animated value for a single `filter`.
#[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`.
#[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.
pub type SimpleShadow = GenericSimpleShadow<Option<RGBA>, Length, Length>;
impl ToAnimatedValue for ComputedBoxShadowList {
type AnimatedValue = BoxShadowList;
#[inline]
--- a/servo/components/style/values/animated/mod.rs
+++ b/servo/components/style/values/animated/mod.rs
@@ -10,19 +10,17 @@
use app_units::Au;
use euclid::{Point2D, Size2D};
use smallvec::SmallVec;
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;
/// Animate from one value to another.
///
/// This trait is derivable with `#[derive(Animate)]`. The derived
/// implementation uses a `match` expression with identical patterns for both
@@ -255,18 +253,16 @@ macro_rules! trivial_to_animated_value {
animated
}
}
};
}
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);
impl ToAnimatedValue for ComputedBorderCornerRadius {
type AnimatedValue = Self;
#[inline]
--- a/servo/components/style/values/computed/counters.rs
+++ b/servo/components/style/values/computed/counters.rs
@@ -1,160 +1,23 @@
/* 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/. */
//! 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<i32>;
/// A computed value for the `counter-increment` property.
pub type CounterReset = GenericCounterReset<i32>;
-impl Content {
- /// Set `content` property to `normal`.
- #[inline]
- pub fn normal() -> Self {
- Content::Normal
- }
-
- #[cfg(feature = "servo")]
- fn parse_counter_style(input: &mut Parser) -> ListStyleType {
- 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);
- }
- }
+/// A computed value for the `content` property.
+pub type Content = generics::Content<ComputedImageUrl>;
- 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()))
- }
-}
+/// A computed content item.
+pub type ContentItem = generics::ContentItem<ComputedImageUrl>;
+
--- a/servo/components/style/values/computed/effects.rs
+++ b/servo/components/style/values/computed/effects.rs
@@ -4,25 +4,26 @@
//! Computed types for CSS values related to effects.
#[cfg(not(feature = "gecko"))]
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;
/// A computed value for a single shadow of the `box-shadow` property.
pub type BoxShadow = GenericBoxShadow<Option<RGBAColor>, Length, NonNegativeLength, Length>;
/// A computed value for a single `filter`.
#[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`.
#[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.
pub type SimpleShadow = GenericSimpleShadow<Option<RGBAColor>, Length, NonNegativeLength>;
--- a/servo/components/style/values/generics/counters.rs
+++ b/servo/components/style/values/generics/counters.rs
@@ -1,16 +1,22 @@
/* 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/. */
//! 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,
ToComputedValue, ToCss)]
pub struct CounterPair<Integer> {
/// The name of the counter.
pub name: CustomIdent,
/// The value of the counter / increment / etc.
@@ -69,8 +75,83 @@ impl<I> Deref for CounterReset<I> {
pub struct Counters<I>(#[css(iterable, if_empty = "none")] Box<[CounterPair<I>]>);
impl<I> Default for Counters<I> {
#[inline]
fn default() -> Self {
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),
+}
--- a/servo/components/style/values/generics/effects.rs
+++ b/servo/components/style/values/generics/effects.rs
@@ -1,36 +1,34 @@
/* 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/. */
//! 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)]
pub struct BoxShadow<Color, SizeLength, BlurShapeLength, ShapeLength> {
/// The base shadow.
pub base: SimpleShadow<Color, SizeLength, BlurShapeLength>,
/// The spread radius.
pub spread: ShapeLength,
/// Whether this is an inset box shadow.
#[animation(constant)]
#[css(represents_keyword)]
pub inset: bool,
}
/// 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<Angle, Factor, Length, DropShadow> {
+pub enum Filter<Angle, Factor, Length, DropShadow, Url> {
/// `blur(<length>)`
#[css(function)]
Blur(Length),
/// `brightness(<factor>)`
#[css(function)]
Brightness(Factor),
/// `contrast(<factor>)`
#[css(function)]
@@ -53,18 +51,17 @@ pub enum Filter<Angle, Factor, Length, D
/// `sepia(<factor>)`
#[css(function)]
Sepia(Factor),
/// `drop-shadow(...)`
#[css(function)]
DropShadow(DropShadow),
/// `<url>`
#[animation(error)]
- #[cfg(feature = "gecko")]
- Url(SpecifiedUrl),
+ Url(Url),
}
/// A generic value for the `drop-shadow()` filter and the `text-shadow` property.
///
/// Contrary to the canonical order from the spec, the color is serialised
/// first, like in Gecko and Webkit.
#[derive(Animate, Clone, ComputeSquaredDistance, Debug, MallocSizeOf, PartialEq,
SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero, ToCss)]
--- a/servo/components/style/values/specified/counters.rs
+++ b/servo/components/style/values/specified/counters.rs
@@ -3,28 +3,29 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Specified types 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::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<Integer>;
impl Parse for CounterIncrement {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
@@ -74,74 +75,134 @@ fn parse_counters<'i, 't>(
if !counters.is_empty() {
Ok(counters)
} else {
Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
}
}
-#[cfg(feature = "servo")]
-type CounterStyleType = ListStyleType;
+/// The specified value for the `content` property.
+pub type Content = generics::Content<SpecifiedImageUrl>;
-#[cfg(feature = "gecko")]
-type CounterStyleType = CounterStyleOrNone;
+/// The specified value for a content item in the `content` property.
+pub type ContentItem = generics::ContentItem<SpecifiedImageUrl>;
-#[cfg(feature = "servo")]
-#[inline]
-fn is_decimal(counter_type: &CounterStyleType) -> bool {
- *counter_type == ListStyleType::Decimal
-}
+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")]
-#[inline]
-fn is_decimal(counter_type: &CounterStyleType) -> bool {
- *counter_type == CounterStyleOrNone::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())
+ }
}
-/// 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 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(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);
+ }
+ }
-/// Items for the `content` property.
-#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo,
- ToComputedValue, ToCss)]
-pub enum ContentItem {
- /// 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)`
- #[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()))
+ }
}
--- a/servo/components/style/values/specified/effects.rs
+++ b/servo/components/style/values/specified/effects.rs
@@ -14,30 +14,29 @@ use values::computed::effects::BoxShadow
use values::computed::effects::SimpleShadow as ComputedSimpleShadow;
use values::generics::NonNegative;
use values::generics::effects::BoxShadow as GenericBoxShadow;
use values::generics::effects::Filter as GenericFilter;
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.
pub type BoxShadow =
GenericBoxShadow<Option<RGBAColor>, Length, Option<NonNegativeLength>, Option<Length>>;
/// A specified value for a single `filter`.
#[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`.
#[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`.
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct Factor(NumberOrPercentage);
impl Factor {
/// Parse this factor but clamp to one if the value is over 100%.
#[inline]