style: Use cbindgen for content property.

This cleans up and also allows us to keep the distinction between content: none
and content: normal, which allows us to fix the computed style we return from
getComputedStyle().

Do this last bit from the resolved value instead of StyleAdjuster, because
otherwise we need to tweak every initial struct for ::before / ::after.

Differential Revision: https://phabricator.services.mozilla.com/D58276
This commit is contained in:
Emilio Cobos Álvarez 2020-01-05 13:10:39 +00:00
parent 07d0eea5fb
commit 219c0f6328
No known key found for this signature in database
GPG key ID: E1152D0994E4BF8A
13 changed files with 122 additions and 343 deletions

View file

@ -409,6 +409,7 @@ impl ToCss for System {
/// <https://drafts.csswg.org/css-counter-styles/#typedef-symbol>
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToShmem)]
#[repr(u8)]
pub enum Symbol {
/// <string>
String(crate::OwnedStr),
@ -554,6 +555,7 @@ impl Parse for Fallback {
/// <https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols>
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss, ToShmem)]
#[repr(C)]
pub struct Symbols(#[css(iterable)] pub crate::OwnedSlice<Symbol>);
impl Parse for Symbols {

View file

@ -7,13 +7,13 @@
//! Different kind of helpers to interact with Gecko values.
use crate::counter_style::{Symbol, Symbols};
use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs::CounterStylePtr;
use crate::values::generics::CounterStyle;
use crate::values::Either;
use crate::Atom;
use app_units::Au;
use cssparser::RGBA;
use nsstring::{nsACString, nsCStr};
use std::cmp::max;
/// Convert a given RGBA value to `nscolor`.
@ -51,43 +51,13 @@ pub fn round_border_to_device_pixels(width: Au, au_per_device_px: Au) -> Au {
impl CounterStyle {
/// Convert this counter style to a Gecko CounterStylePtr.
pub fn to_gecko_value(self, gecko_value: &mut CounterStylePtr) {
use crate::gecko_bindings::bindings::Gecko_SetCounterStyleToName as set_name;
use crate::gecko_bindings::bindings::Gecko_SetCounterStyleToSymbols as set_symbols;
match self {
CounterStyle::Name(name) => unsafe {
debug_assert_ne!(name.0, atom!("none"));
set_name(gecko_value, name.0.into_addrefed());
},
CounterStyle::Symbols(symbols_type, symbols) => {
let symbols: Vec<_> = symbols
.0
.iter()
.map(|symbol| match *symbol {
Symbol::String(ref s) => nsCStr::from(&**s),
Symbol::Ident(_) => unreachable!("Should not have identifier in symbols()"),
})
.collect();
let symbols: Vec<_> = symbols
.iter()
.map(|symbol| symbol as &nsACString as *const _)
.collect();
unsafe {
set_symbols(
gecko_value,
symbols_type.to_gecko_keyword(),
symbols.as_ptr(),
symbols.len() as u32,
)
};
},
}
#[inline]
pub fn to_gecko_value(&self, gecko_value: &mut CounterStylePtr) {
unsafe { bindings::Gecko_CounterStyle_ToPtr(self, gecko_value) }
}
/// Convert Gecko CounterStylePtr to CounterStyle or String.
pub fn from_gecko_value(gecko_value: &CounterStylePtr) -> Either<Self, String> {
use crate::gecko_bindings::bindings;
use crate::values::generics::SymbolsType;
use crate::values::CustomIdent;
let name = unsafe { bindings::Gecko_CounterStyle_GetName(gecko_value) };
@ -103,7 +73,7 @@ impl CounterStyle {
debug_assert_eq!(symbols.len(), 1);
Either::Second(symbols[0].to_string())
} else {
let symbol_type = SymbolsType::from_gecko_keyword(anonymous.mSystem as u32);
let symbol_type = anonymous.mSymbolsType;
let symbols = symbols
.iter()
.map(|gecko_symbol| Symbol::String(gecko_symbol.to_string().into()))

View file

@ -25,6 +25,7 @@ macro_rules! ns {
/// A Gecko namespace is just a wrapped atom.
#[derive(Clone, Debug, Default, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
#[repr(transparent)]
pub struct Namespace(pub Atom);
impl PrecomputedHash for Namespace {

View file

@ -173,10 +173,12 @@ pub mod values;
pub use crate::gecko_string_cache as string_cache;
#[cfg(feature = "gecko")]
pub use crate::gecko_string_cache::Atom;
/// The namespace prefix type for Gecko, which is just an atom.
#[cfg(feature = "gecko")]
pub use crate::gecko_string_cache::Atom as Prefix;
pub type Prefix = crate::gecko_string_cache::Atom;
/// The local name of an element for Gecko, which is just an atom.
#[cfg(feature = "gecko")]
pub use crate::gecko_string_cache::Atom as LocalName;
pub type LocalName = crate::gecko_string_cache::Atom;
#[cfg(feature = "gecko")]
pub use crate::gecko_string_cache::Namespace;

View file

@ -36,7 +36,6 @@ use crate::gecko_bindings::bindings::Gecko_SetNullImageValue;
use crate::gecko_bindings::structs;
use crate::gecko_bindings::structs::nsCSSPropertyID;
use crate::gecko_bindings::structs::mozilla::PseudoStyleType;
use crate::gecko_bindings::sugar::refptr::RefPtr;
use crate::gecko::values::round_border_to_device_pixels;
use crate::logical_geometry::WritingMode;
use crate::media_queries::Device;
@ -45,7 +44,6 @@ use crate::properties::longhands;
use crate::rule_tree::StrongRuleNode;
use crate::selector_parser::PseudoElement;
use servo_arc::{Arc, RawOffsetArc, UniqueArc};
use std::marker::PhantomData;
use std::mem::{forget, MaybeUninit};
use std::{cmp, ops, ptr};
use crate::values::{self, CustomIdent, Either, KeyframesName, None_};
@ -2200,19 +2198,18 @@ fn static_assert() {
}
pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T) {
use crate::gecko_bindings::bindings::Gecko_SetCounterStyleToName;
use crate::gecko_bindings::bindings::Gecko_SetCounterStyleToString;
use nsstring::{nsACString, nsCStr};
use self::longhands::list_style_type::computed_value::T;
match v {
T::None => unsafe {
Gecko_SetCounterStyleToName(&mut self.gecko.mCounterStyle,
atom!("none").into_addrefed());
bindings::Gecko_SetCounterStyleToNone(&mut self.gecko.mCounterStyle)
}
T::CounterStyle(s) => s.to_gecko_value(&mut self.gecko.mCounterStyle),
T::String(s) => unsafe {
Gecko_SetCounterStyleToString(&mut self.gecko.mCounterStyle,
&nsCStr::from(&s) as &nsACString)
bindings::Gecko_SetCounterStyleToString(
&mut self.gecko.mCounterStyle,
&nsCStr::from(&s) as &nsACString,
)
}
}
}
@ -2580,236 +2577,9 @@ clip-path
${impl_simple('column_rule_style', 'mColumnRuleStyle')}
</%self:impl_trait>
<%self:impl_trait style_struct_name="Counters" skip_longhands="content">
<%self:impl_trait style_struct_name="Counters">
pub fn ineffective_content_property(&self) -> bool {
self.gecko.mContents.is_empty()
}
pub fn set_content(&mut self, v: longhands::content::computed_value::T) {
use crate::values::CustomIdent;
use crate::values::generics::counters::{Content, ContentItem};
use crate::values::generics::CounterStyle;
use crate::gecko_bindings::structs::nsStyleContentData;
use crate::gecko_bindings::structs::nsStyleContentAttr;
use crate::gecko_bindings::structs::StyleContentType;
use crate::gecko_bindings::bindings::Gecko_ClearAndResizeStyleContents;
// Converts a string as utf16, and returns an owned, zero-terminated raw buffer.
fn as_utf16_and_forget(s: &str) -> *mut u16 {
use std::mem;
let mut vec = s.encode_utf16().collect::<Vec<_>>();
vec.push(0u16);
let ptr = vec.as_mut_ptr();
mem::forget(vec);
ptr
}
fn set_counter_function(
data: &mut nsStyleContentData,
content_type: StyleContentType,
name: CustomIdent,
sep: &str,
style: CounterStyle,
) {
debug_assert!(content_type == StyleContentType::Counter ||
content_type == StyleContentType::Counters);
let counter_func = unsafe {
bindings::Gecko_SetCounterFunction(data, content_type).as_mut().unwrap()
};
counter_func.mIdent.set_move(unsafe {
RefPtr::from_addrefed(name.0.into_addrefed())
});
if content_type == StyleContentType::Counters {
counter_func.mSeparator.assign_str(sep);
}
style.to_gecko_value(&mut counter_func.mCounterStyle);
}
match v {
Content::None |
Content::Normal => {
// Ensure destructors run, otherwise we could leak.
if !self.gecko.mContents.is_empty() {
unsafe {
Gecko_ClearAndResizeStyleContents(&mut *self.gecko, 0);
}
}
},
Content::MozAltContent => {
unsafe {
Gecko_ClearAndResizeStyleContents(&mut *self.gecko, 1);
*self.gecko.mContents[0].mContent.mString.as_mut() = ptr::null_mut();
}
self.gecko.mContents[0].mType = StyleContentType::AltContent;
},
Content::Items(items) => {
unsafe {
Gecko_ClearAndResizeStyleContents(&mut *self.gecko,
items.len() as u32);
}
for (i, item) in items.into_vec().into_iter().enumerate() {
// NB: Gecko compares the mString value if type is not image
// or URI independently of whatever gets there. In the quote
// cases, they set it to null, so do the same here.
unsafe {
*self.gecko.mContents[i].mContent.mString.as_mut() = ptr::null_mut();
}
match item {
ContentItem::String(ref value) => {
self.gecko.mContents[i].mType = StyleContentType::String;
unsafe {
// NB: we share allocators, so doing this is fine.
*self.gecko.mContents[i].mContent.mString.as_mut() =
as_utf16_and_forget(&value);
}
}
ContentItem::Attr(ref attr) => {
self.gecko.mContents[i].mType = StyleContentType::Attr;
unsafe {
// NB: we share allocators, so doing this is fine.
let maybe_ns = attr.namespace.clone();
let attr_struct = Box::new(nsStyleContentAttr {
mName: structs::RefPtr {
mRawPtr: attr.attribute.clone().into_addrefed(),
_phantom_0: PhantomData,
},
mNamespaceURL: structs::RefPtr {
mRawPtr: maybe_ns.map_or(ptr::null_mut(), |x| (x.1).0.into_addrefed()),
_phantom_0: PhantomData,
},
});
*self.gecko.mContents[i].mContent.mAttr.as_mut() =
Box::into_raw(attr_struct);
}
}
ContentItem::OpenQuote
=> self.gecko.mContents[i].mType = StyleContentType::OpenQuote,
ContentItem::CloseQuote
=> self.gecko.mContents[i].mType = StyleContentType::CloseQuote,
ContentItem::NoOpenQuote
=> self.gecko.mContents[i].mType = StyleContentType::NoOpenQuote,
ContentItem::NoCloseQuote
=> self.gecko.mContents[i].mType = StyleContentType::NoCloseQuote,
ContentItem::Counter(name, style) => {
set_counter_function(
&mut self.gecko.mContents[i],
StyleContentType::Counter,
name,
"",
style,
);
}
ContentItem::Counters(name, sep, style) => {
set_counter_function(
&mut self.gecko.mContents[i],
StyleContentType::Counters,
name,
&sep,
style,
);
}
ContentItem::Url(ref url) => {
unsafe {
bindings::Gecko_SetContentDataImageValue(
&mut self.gecko.mContents[i],
url,
)
}
}
}
}
}
}
}
pub fn copy_content_from(&mut self, other: &Self) {
use crate::gecko_bindings::bindings::Gecko_CopyStyleContentsFrom;
unsafe {
Gecko_CopyStyleContentsFrom(&mut *self.gecko, &*other.gecko)
}
}
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 crate::gecko::conversions::string_from_chars_pointer;
use crate::gecko_bindings::structs::StyleContentType;
use crate::values::generics::counters::{Content, ContentItem};
use crate::values::{CustomIdent, Either};
use crate::values::generics::CounterStyle;
use crate::values::specified::Attr;
if self.gecko.mContents.is_empty() {
return Content::None;
}
if self.gecko.mContents.len() == 1 &&
self.gecko.mContents[0].mType == StyleContentType::AltContent {
return Content::MozAltContent;
}
Content::Items(
self.gecko.mContents.iter().map(|gecko_content| {
match gecko_content.mType {
StyleContentType::OpenQuote => ContentItem::OpenQuote,
StyleContentType::CloseQuote => ContentItem::CloseQuote,
StyleContentType::NoOpenQuote => ContentItem::NoOpenQuote,
StyleContentType::NoCloseQuote => ContentItem::NoCloseQuote,
StyleContentType::String => {
let gecko_chars = unsafe { gecko_content.mContent.mString.as_ref() };
let string = unsafe { string_from_chars_pointer(*gecko_chars) };
ContentItem::String(string.into_boxed_str())
},
StyleContentType::Attr => {
let (namespace, attribute) = unsafe {
let s = &**gecko_content.mContent.mAttr.as_ref();
let ns = if s.mNamespaceURL.mRawPtr.is_null() {
None
} else {
// FIXME(bholley): We don't have any way to get the prefix here. :-(
let prefix = atom!("");
Some((prefix, Namespace(Atom::from_raw(s.mNamespaceURL.mRawPtr))))
};
(ns, Atom::from_raw(s.mName.mRawPtr))
};
ContentItem::Attr(Attr { namespace, attribute })
},
StyleContentType::Counter | StyleContentType::Counters => {
let gecko_function =
unsafe { &**gecko_content.mContent.mCounters.as_ref() };
let ident = CustomIdent(unsafe {
Atom::from_raw(gecko_function.mIdent.mRawPtr)
});
let style =
CounterStyle::from_gecko_value(&gecko_function.mCounterStyle);
let style = match style {
Either::First(counter_style) => counter_style,
Either::Second(_) =>
unreachable!("counter function shouldn't have single string type"),
};
if gecko_content.mType == StyleContentType::Counter {
ContentItem::Counter(ident, style)
} else {
let separator = gecko_function.mSeparator.to_string();
ContentItem::Counters(ident, separator.into_boxed_str(), style)
}
},
StyleContentType::Image => {
unsafe {
let gecko_image_request =
&**gecko_content.mContent.mImage.as_ref();
ContentItem::Url(
ComputedImageUrl::from_image_request(gecko_image_request)
)
}
},
_ => panic!("Found unexpected value in style struct for content property"),
}
}).collect::<Vec<_>>().into_boxed_slice()
)
!self.gecko.mContent.is_items()
}
</%self:impl_trait>

View file

@ -16,7 +16,7 @@ pub type CounterIncrement = GenericCounterIncrement<i32>;
pub type CounterSetOrReset = GenericCounterSetOrReset<i32>;
/// A computed value for the `content` property.
pub type Content = generics::Content<ComputedImageUrl>;
pub type Content = generics::GenericContent<ComputedImageUrl>;
/// A computed content item.
pub type ContentItem = generics::ContentItem<ComputedImageUrl>;
pub type ContentItem = generics::GenericContentItem<ComputedImageUrl>;

View file

@ -471,6 +471,7 @@ trivial_to_computed_value!(Atom);
trivial_to_computed_value!(Prefix);
trivial_to_computed_value!(String);
trivial_to_computed_value!(Box<str>);
trivial_to_computed_value!(crate::OwnedStr);
/// A `<number>` value.
pub type Number = CSSFloat;

View file

@ -153,22 +153,26 @@ fn is_decimal(counter_type: &CounterStyleType) -> bool {
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
pub enum Content<ImageUrl> {
#[repr(u8)]
pub enum GenericContent<ImageUrl> {
/// `normal` reserved keyword.
Normal,
/// `none` reserved keyword.
None,
/// `-moz-alt-content`.
#[cfg(feature = "gecko")]
MozAltContent,
/// Content items.
Items(#[css(iterable)] Box<[ContentItem<ImageUrl>]>),
Items(#[css(iterable)] crate::OwnedSlice<GenericContentItem<ImageUrl>>),
}
pub use self::GenericContent as Content;
impl<ImageUrl> Content<ImageUrl> {
#[inline]
pub(crate) fn is_items(&self) -> bool {
matches!(*self, Self::Items(..))
}
/// Set `content` property to `normal`.
#[inline]
pub fn normal() -> Self {
@ -189,9 +193,10 @@ impl<ImageUrl> Content<ImageUrl> {
ToResolvedValue,
ToShmem,
)]
pub enum ContentItem<ImageUrl> {
#[repr(u8)]
pub enum GenericContentItem<ImageUrl> {
/// Literal string content.
String(Box<str>),
String(crate::OwnedStr),
/// `counter(name, style)`.
#[css(comma, function)]
Counter(CustomIdent, #[css(skip_if = "is_decimal")] CounterStyleType),
@ -199,7 +204,7 @@ pub enum ContentItem<ImageUrl> {
#[css(comma, function)]
Counters(
CustomIdent,
Box<str>,
crate::OwnedStr,
#[css(skip_if = "is_decimal")] CounterStyleType,
),
/// `open-quote`.
@ -210,9 +215,14 @@ pub enum ContentItem<ImageUrl> {
NoOpenQuote,
/// `no-close-quote`.
NoCloseQuote,
/// `-moz-alt-content`.
#[cfg(feature = "gecko")]
MozAltContent,
/// `attr([namespace? `|`]? ident)`
#[cfg(feature = "gecko")]
Attr(Attr),
/// `url(url)`
Url(ImageUrl),
}
pub use self::GenericContentItem as ContentItem;

View file

@ -39,7 +39,7 @@ pub mod transform;
pub mod ui;
pub mod url;
// https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type
/// https://drafts.csswg.org/css-counter-styles/#typedef-symbols-type
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
#[derive(
@ -55,6 +55,7 @@ pub mod url;
ToResolvedValue,
ToShmem,
)]
#[repr(u8)]
pub enum SymbolsType {
Cyclic,
Numeric,
@ -63,39 +64,12 @@ pub enum SymbolsType {
Fixed,
}
#[cfg(feature = "gecko")]
impl SymbolsType {
/// Convert symbols type to their corresponding Gecko values.
pub fn to_gecko_keyword(self) -> u8 {
use crate::gecko_bindings::structs;
match self {
SymbolsType::Cyclic => structs::NS_STYLE_COUNTER_SYSTEM_CYCLIC as u8,
SymbolsType::Numeric => structs::NS_STYLE_COUNTER_SYSTEM_NUMERIC as u8,
SymbolsType::Alphabetic => structs::NS_STYLE_COUNTER_SYSTEM_ALPHABETIC as u8,
SymbolsType::Symbolic => structs::NS_STYLE_COUNTER_SYSTEM_SYMBOLIC as u8,
SymbolsType::Fixed => structs::NS_STYLE_COUNTER_SYSTEM_FIXED as u8,
}
}
/// Convert Gecko value to symbol type.
pub fn from_gecko_keyword(gecko_value: u32) -> SymbolsType {
use crate::gecko_bindings::structs;
match gecko_value {
structs::NS_STYLE_COUNTER_SYSTEM_CYCLIC => SymbolsType::Cyclic,
structs::NS_STYLE_COUNTER_SYSTEM_NUMERIC => SymbolsType::Numeric,
structs::NS_STYLE_COUNTER_SYSTEM_ALPHABETIC => SymbolsType::Alphabetic,
structs::NS_STYLE_COUNTER_SYSTEM_SYMBOLIC => SymbolsType::Symbolic,
structs::NS_STYLE_COUNTER_SYSTEM_FIXED => SymbolsType::Fixed,
x => panic!("Unexpected value for symbol type {}", x),
}
}
}
/// <https://drafts.csswg.org/css-counter-styles/#typedef-counter-style>
///
/// Note that 'none' is not a valid name.
#[cfg_attr(feature = "gecko", derive(MallocSizeOf))]
#[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToCss, ToResolvedValue, ToShmem)]
#[repr(u8)]
pub enum CounterStyle {
/// `<counter-style-name>`
Name(CustomIdent),

View file

@ -0,0 +1,46 @@
/* 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 https://mozilla.org/MPL/2.0/. */
//! Resolved values for counter properties
use super::{Context, ToResolvedValue};
use crate::values::computed;
/// https://drafts.csswg.org/css-content/#content-property
///
/// We implement this at resolved value time because otherwise it causes us to
/// allocate a bunch of useless initial structs for ::before / ::after, which is
/// a bit unfortunate.
///
/// Though these should be temporary, mostly, so if this causes complexity in
/// other places, it should be fine to move to `StyleAdjuster`.
///
/// See https://github.com/w3c/csswg-drafts/issues/4632 for where some related
/// issues are being discussed.
impl ToResolvedValue for computed::Content {
type ResolvedValue = Self;
#[inline]
fn to_resolved_value(self, context: &Context) -> Self {
let is_before_or_after =
context.style.pseudo().map_or(false, |p| p.is_before_or_after());
match self {
Self::Normal if is_before_or_after => Self::None,
// For now, make `content: none` compute to `normal` on other
// elements, as we don't respect it.
//
// FIXME(emilio, bug 1605473): for marker this should be preserved
// and respected, probably.
Self::None if !is_before_or_after => Self::Normal,
other => other,
}
}
#[inline]
fn from_resolved_value(resolved: Self) -> Self {
resolved
}
}

View file

@ -10,6 +10,7 @@ use cssparser;
use smallvec::SmallVec;
mod color;
mod counters;
use crate::values::computed;
@ -68,6 +69,7 @@ trivial_to_resolved_value!(u32);
trivial_to_resolved_value!(usize);
trivial_to_resolved_value!(String);
trivial_to_resolved_value!(Box<str>);
trivial_to_resolved_value!(crate::OwnedStr);
trivial_to_resolved_value!(cssparser::RGBA);
trivial_to_resolved_value!(crate::Atom);
trivial_to_resolved_value!(app_units::Au);

View file

@ -9,8 +9,6 @@ use crate::computed_values::list_style_type::T as ListStyleType;
use crate::parser::{Parse, ParserContext};
use crate::values::generics::counters as generics;
use crate::values::generics::counters::CounterPair;
use crate::values::generics::counters::GenericCounterIncrement;
use crate::values::generics::counters::GenericCounterSetOrReset;
#[cfg(feature = "gecko")]
use crate::values::generics::CounterStyle;
use crate::values::specified::url::SpecifiedImageUrl;
@ -23,7 +21,7 @@ use selectors::parser::SelectorParseErrorKind;
use style_traits::{ParseError, StyleParseErrorKind};
/// A specified value for the `counter-increment` property.
pub type CounterIncrement = GenericCounterIncrement<Integer>;
pub type CounterIncrement = generics::GenericCounterIncrement<Integer>;
impl Parse for CounterIncrement {
fn parse<'i, 't>(
@ -35,7 +33,7 @@ impl Parse for CounterIncrement {
}
/// A specified value for the `counter-set` and `counter-reset` properties.
pub type CounterSetOrReset = GenericCounterSetOrReset<Integer>;
pub type CounterSetOrReset = generics::GenericCounterSetOrReset<Integer>;
impl Parse for CounterSetOrReset {
fn parse<'i, 't>(
@ -84,10 +82,10 @@ fn parse_counters<'i, 't>(
}
/// The specified value for the `content` property.
pub type Content = generics::Content<SpecifiedImageUrl>;
pub type Content = generics::GenericContent<SpecifiedImageUrl>;
/// The specified value for a content item in the `content` property.
pub type ContentItem = generics::ContentItem<SpecifiedImageUrl>;
pub type ContentItem = generics::GenericContentItem<SpecifiedImageUrl>;
impl Content {
#[cfg(feature = "servo")]
@ -131,17 +129,9 @@ impl Parse for Content {
{
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![];
let mut has_alt_content = false;
loop {
#[cfg(feature = "gecko")]
{
@ -153,7 +143,7 @@ impl Parse for Content {
match input.next() {
Ok(&Token::QuotedString(ref value)) => {
content.push(generics::ContentItem::String(
value.as_ref().to_owned().into_boxed_str(),
value.as_ref().to_owned().into(),
));
},
Ok(&Token::Function(ref name)) => {
@ -168,7 +158,7 @@ impl Parse for Content {
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 separator = input.expect_string()?.as_ref().to_owned().into();
let style = Content::parse_counter_style(context, input);
Ok(generics::ContentItem::Counters(name, separator, style))
}),
@ -191,6 +181,11 @@ impl Parse for Content {
"close-quote" => generics::ContentItem::CloseQuote,
"no-open-quote" => generics::ContentItem::NoOpenQuote,
"no-close-quote" => generics::ContentItem::NoCloseQuote,
#[cfg(feature = "gecko")]
"-moz-alt-content" => {
has_alt_content = true;
generics::ContentItem::MozAltContent
},
_ =>{
let ident = ident.clone();
return Err(input.new_custom_error(
@ -206,9 +201,10 @@ impl Parse for Content {
},
}
}
if content.is_empty() {
// We don't allow to parse `-moz-alt-content in multiple positions.
if content.is_empty() || (has_alt_content && content.len() != 1) {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
Ok(generics::Content::Items(content.into_boxed_slice()))
Ok(generics::Content::Items(content.into()))
}
}

View file

@ -767,9 +767,12 @@ impl AllowQuirks {
ToShmem,
)]
#[css(function)]
#[repr(C)]
pub struct Attr {
/// Optional namespace prefix and URL.
pub namespace: Option<(Prefix, Namespace)>,
/// Optional namespace prefix.
pub namespace_prefix: Prefix,
/// Optional namespace URL.
pub namespace_url: Namespace,
/// Attribute name
pub attribute: Atom,
}
@ -814,7 +817,7 @@ impl Attr {
ref t => return Err(location.new_unexpected_token_error(t.clone())),
};
let prefix_and_ns = if let Some(ns) = first {
let (namespace_prefix, namespace_url) = if let Some(ns) = first {
let prefix = Prefix::from(ns.as_ref());
let ns = match get_namespace_for_prefix(&prefix, context) {
Some(ns) => ns,
@ -823,17 +826,18 @@ impl Attr {
.new_custom_error(StyleParseErrorKind::UnspecifiedError));
},
};
Some((prefix, ns))
(prefix, ns)
} else {
None
(Prefix::default(), Namespace::default())
};
return Ok(Attr {
namespace: prefix_and_ns,
namespace_prefix,
namespace_url,
attribute: Atom::from(second_token.as_ref()),
});
},
// In the case of attr(foobar ) we don't want to error out
// because of the trailing whitespace
// because of the trailing whitespace.
Token::WhiteSpace(..) => {},
ref t => return Err(input.new_unexpected_token_error(t.clone())),
}
@ -841,7 +845,8 @@ impl Attr {
if let Some(first) = first {
Ok(Attr {
namespace: None,
namespace_prefix: Prefix::default(),
namespace_url: Namespace::default(),
attribute: Atom::from(first.as_ref()),
})
} else {
@ -856,8 +861,8 @@ impl ToCss for Attr {
W: Write,
{
dest.write_str("attr(")?;
if let Some((ref prefix, ref _url)) = self.namespace {
serialize_atom_identifier(prefix, dest)?;
if !self.namespace_prefix.is_empty() {
serialize_atom_identifier(&self.namespace_prefix, dest)?;
dest.write_str("|")?;
}
serialize_atom_identifier(&self.attribute, dest)?;