mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Auto merge of #16455 - servo:counter-style, r=upsuper
Bug 1354970 - Add @counter-style rules <!-- Reviewable:start --> This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16455) <!-- Reviewable:end -->
This commit is contained in:
commit
2eff661ebb
27 changed files with 1140 additions and 134 deletions
|
@ -12,7 +12,20 @@ use std::path::Path;
|
||||||
fn main() {
|
fn main() {
|
||||||
let static_atoms = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join("static_atoms.txt");
|
let static_atoms = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join("static_atoms.txt");
|
||||||
let static_atoms = BufReader::new(File::open(&static_atoms).unwrap());
|
let static_atoms = BufReader::new(File::open(&static_atoms).unwrap());
|
||||||
string_cache_codegen::AtomType::new("Atom", "atom!")
|
let mut atom_type = string_cache_codegen::AtomType::new("Atom", "atom!");
|
||||||
|
|
||||||
|
macro_rules! predefined {
|
||||||
|
($($name: expr,)+) => {
|
||||||
|
{
|
||||||
|
$(
|
||||||
|
atom_type.atom($name);
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
include!("../style/counter_style/predefined.rs");
|
||||||
|
|
||||||
|
atom_type
|
||||||
.atoms(static_atoms.lines().map(Result::unwrap))
|
.atoms(static_atoms.lines().map(Result::unwrap))
|
||||||
.write_to_file(&Path::new(&env::var("OUT_DIR").unwrap()).join("atom.rs"))
|
.write_to_file(&Path::new(&env::var("OUT_DIR").unwrap()).join("atom.rs"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -8,6 +8,8 @@ left
|
||||||
center
|
center
|
||||||
right
|
right
|
||||||
|
|
||||||
|
none
|
||||||
|
|
||||||
hidden
|
hidden
|
||||||
submit
|
submit
|
||||||
button
|
button
|
||||||
|
|
|
@ -273,6 +273,7 @@ impl<'a,'b> ResolveGeneratedContentFragmentMutator<'a,'b> {
|
||||||
self.traversal.list_item.truncate_to_level(self.level);
|
self.traversal.list_item.truncate_to_level(self.level);
|
||||||
|
|
||||||
for &(ref counter_name, value) in &fragment.style().get_counters().counter_reset.0 {
|
for &(ref counter_name, value) in &fragment.style().get_counters().counter_reset.0 {
|
||||||
|
let counter_name = &*counter_name.0;
|
||||||
if let Some(ref mut counter) = self.traversal.counters.get_mut(counter_name) {
|
if let Some(ref mut counter) = self.traversal.counters.get_mut(counter_name) {
|
||||||
counter.reset(self.level, value);
|
counter.reset(self.level, value);
|
||||||
continue
|
continue
|
||||||
|
@ -280,10 +281,11 @@ impl<'a,'b> ResolveGeneratedContentFragmentMutator<'a,'b> {
|
||||||
|
|
||||||
let mut counter = Counter::new();
|
let mut counter = Counter::new();
|
||||||
counter.reset(self.level, value);
|
counter.reset(self.level, value);
|
||||||
self.traversal.counters.insert((*counter_name).clone(), counter);
|
self.traversal.counters.insert(counter_name.to_owned(), counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
for &(ref counter_name, value) in &fragment.style().get_counters().counter_increment.0 {
|
for &(ref counter_name, value) in &fragment.style().get_counters().counter_increment.0 {
|
||||||
|
let counter_name = &*counter_name.0;
|
||||||
if let Some(ref mut counter) = self.traversal.counters.get_mut(counter_name) {
|
if let Some(ref mut counter) = self.traversal.counters.get_mut(counter_name) {
|
||||||
counter.increment(self.level, value);
|
counter.increment(self.level, value);
|
||||||
continue
|
continue
|
||||||
|
@ -291,7 +293,7 @@ impl<'a,'b> ResolveGeneratedContentFragmentMutator<'a,'b> {
|
||||||
|
|
||||||
let mut counter = Counter::new();
|
let mut counter = Counter::new();
|
||||||
counter.increment(self.level, value);
|
counter.increment(self.level, value);
|
||||||
self.traversal.counters.insert((*counter_name).clone(), counter);
|
self.traversal.counters.insert(counter_name.to_owned(), counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.incremented = true
|
self.incremented = true
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
use cssparser::Parser;
|
use cssparser::Parser;
|
||||||
use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding;
|
use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding;
|
||||||
use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding::CSSKeyframesRuleMethods;
|
use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding::CSSKeyframesRuleMethods;
|
||||||
use dom::bindings::error::{Error, ErrorResult};
|
use dom::bindings::error::ErrorResult;
|
||||||
use dom::bindings::inheritance::Castable;
|
use dom::bindings::inheritance::Castable;
|
||||||
use dom::bindings::js::{MutNullableJS, Root};
|
use dom::bindings::js::{MutNullableJS, Root};
|
||||||
use dom::bindings::reflector::{DomObject, reflect_dom_object};
|
use dom::bindings::reflector::{DomObject, reflect_dom_object};
|
||||||
|
@ -16,11 +16,11 @@ use dom::cssrulelist::{CSSRuleList, RulesSource};
|
||||||
use dom::cssstylesheet::CSSStyleSheet;
|
use dom::cssstylesheet::CSSStyleSheet;
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use servo_atoms::Atom;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use style::keyframes::{Keyframe, KeyframeSelector};
|
use style::keyframes::{Keyframe, KeyframeSelector};
|
||||||
use style::shared_lock::{Locked, ToCssWithGuard};
|
use style::shared_lock::{Locked, ToCssWithGuard};
|
||||||
use style::stylesheets::KeyframesRule;
|
use style::stylesheets::KeyframesRule;
|
||||||
|
use style::values::KeyframesName;
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct CSSKeyframesRule {
|
pub struct CSSKeyframesRule {
|
||||||
|
@ -107,23 +107,17 @@ impl CSSKeyframesRuleMethods for CSSKeyframesRule {
|
||||||
// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name
|
// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name
|
||||||
fn Name(&self) -> DOMString {
|
fn Name(&self) -> DOMString {
|
||||||
let guard = self.cssrule.shared_lock().read();
|
let guard = self.cssrule.shared_lock().read();
|
||||||
DOMString::from(&*self.keyframesrule.read_with(&guard).name)
|
DOMString::from(&**self.keyframesrule.read_with(&guard).name.as_atom())
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name
|
// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name
|
||||||
fn SetName(&self, value: DOMString) -> ErrorResult {
|
fn SetName(&self, value: DOMString) -> ErrorResult {
|
||||||
// https://github.com/w3c/csswg-drafts/issues/801
|
// Spec deviation: https://github.com/w3c/csswg-drafts/issues/801
|
||||||
// Setting this property to a CSS-wide keyword or `none` will
|
// Setting this property to a CSS-wide keyword or `none` does not throw,
|
||||||
// throw a Syntax Error.
|
// it stores a value that serializes as a quoted string.
|
||||||
match_ignore_ascii_case! { &value,
|
let name = KeyframesName::from_ident(value.into());
|
||||||
"initial" => return Err(Error::Syntax),
|
|
||||||
"inherit" => return Err(Error::Syntax),
|
|
||||||
"unset" => return Err(Error::Syntax),
|
|
||||||
"none" => return Err(Error::Syntax),
|
|
||||||
_ => ()
|
|
||||||
}
|
|
||||||
let mut guard = self.cssrule.shared_lock().write();
|
let mut guard = self.cssrule.shared_lock().write();
|
||||||
self.keyframesrule.write_with(&mut guard).name = Atom::from(value);
|
self.keyframesrule.write_with(&mut guard).name = name;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,7 @@ impl CSSRule {
|
||||||
StyleCssRule::Import(s) => Root::upcast(CSSImportRule::new(window, parent_stylesheet, s)),
|
StyleCssRule::Import(s) => Root::upcast(CSSImportRule::new(window, parent_stylesheet, s)),
|
||||||
StyleCssRule::Style(s) => Root::upcast(CSSStyleRule::new(window, parent_stylesheet, s)),
|
StyleCssRule::Style(s) => Root::upcast(CSSStyleRule::new(window, parent_stylesheet, s)),
|
||||||
StyleCssRule::FontFace(s) => Root::upcast(CSSFontFaceRule::new(window, parent_stylesheet, s)),
|
StyleCssRule::FontFace(s) => Root::upcast(CSSFontFaceRule::new(window, parent_stylesheet, s)),
|
||||||
|
StyleCssRule::CounterStyle(_) => unimplemented!(),
|
||||||
StyleCssRule::Keyframes(s) => Root::upcast(CSSKeyframesRule::new(window, parent_stylesheet, s)),
|
StyleCssRule::Keyframes(s) => Root::upcast(CSSKeyframesRule::new(window, parent_stylesheet, s)),
|
||||||
StyleCssRule::Media(s) => Root::upcast(CSSMediaRule::new(window, parent_stylesheet, s)),
|
StyleCssRule::Media(s) => Root::upcast(CSSMediaRule::new(window, parent_stylesheet, s)),
|
||||||
StyleCssRule::Namespace(s) => Root::upcast(CSSNamespaceRule::new(window, parent_stylesheet, s)),
|
StyleCssRule::Namespace(s) => Root::upcast(CSSNamespaceRule::new(window, parent_stylesheet, s)),
|
||||||
|
|
|
@ -464,13 +464,19 @@ pub fn maybe_start_animations(context: &SharedStyleContext,
|
||||||
|
|
||||||
let box_style = new_style.get_box();
|
let box_style = new_style.get_box();
|
||||||
for (i, name) in box_style.animation_name_iter().enumerate() {
|
for (i, name) in box_style.animation_name_iter().enumerate() {
|
||||||
|
let name = if let Some(atom) = name.as_atom() {
|
||||||
|
atom
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
};
|
||||||
|
|
||||||
debug!("maybe_start_animations: name={}", name);
|
debug!("maybe_start_animations: name={}", name);
|
||||||
let total_duration = box_style.animation_duration_mod(i).seconds();
|
let total_duration = box_style.animation_duration_mod(i).seconds();
|
||||||
if total_duration == 0. {
|
if total_duration == 0. {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref anim) = context.stylist.animations().get(&name.0) {
|
if let Some(ref anim) = context.stylist.animations().get(name) {
|
||||||
debug!("maybe_start_animations: animation {} found", name);
|
debug!("maybe_start_animations: animation {} found", name);
|
||||||
|
|
||||||
// If this animation doesn't have any keyframe, we can just continue
|
// If this animation doesn't have any keyframe, we can just continue
|
||||||
|
@ -506,7 +512,7 @@ pub fn maybe_start_animations(context: &SharedStyleContext,
|
||||||
|
|
||||||
|
|
||||||
new_animations_sender
|
new_animations_sender
|
||||||
.send(Animation::Keyframes(node, name.0.clone(), KeyframesAnimationState {
|
.send(Animation::Keyframes(node, name.clone(), KeyframesAnimationState {
|
||||||
started_at: animation_start,
|
started_at: animation_start,
|
||||||
duration: duration as f64,
|
duration: duration as f64,
|
||||||
delay: delay as f64,
|
delay: delay as f64,
|
||||||
|
@ -584,9 +590,10 @@ pub fn update_style_for_animation(context: &SharedStyleContext,
|
||||||
|
|
||||||
debug_assert!(!animation.steps.is_empty());
|
debug_assert!(!animation.steps.is_empty());
|
||||||
|
|
||||||
let maybe_index = style.get_box()
|
let maybe_index = style
|
||||||
.animation_name_iter()
|
.get_box()
|
||||||
.position(|animation_name| *name == animation_name.0);
|
.animation_name_iter()
|
||||||
|
.position(|animation_name| Some(name) == animation_name.as_atom());
|
||||||
|
|
||||||
let index = match maybe_index {
|
let index = match maybe_index {
|
||||||
Some(index) => index,
|
Some(index) => index,
|
||||||
|
|
632
components/style/counter_style/mod.rs
Normal file
632
components/style/counter_style/mod.rs
Normal file
|
@ -0,0 +1,632 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
//! The [`@counter-style`][counter-style] at-rule.
|
||||||
|
//!
|
||||||
|
//! [counter-style]: https://drafts.csswg.org/css-counter-styles/
|
||||||
|
|
||||||
|
use Atom;
|
||||||
|
use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser, Token};
|
||||||
|
use cssparser::{serialize_string, serialize_identifier};
|
||||||
|
#[cfg(feature = "gecko")] use gecko::rules::CounterStyleDescriptors;
|
||||||
|
#[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSCounterDesc;
|
||||||
|
use parser::{ParserContext, log_css_error, Parse};
|
||||||
|
use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
|
||||||
|
use std::ascii::AsciiExt;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::fmt;
|
||||||
|
use std::ops::Range;
|
||||||
|
use style_traits::{ToCss, OneOrMoreCommaSeparated};
|
||||||
|
use values::CustomIdent;
|
||||||
|
|
||||||
|
/// Parse the prelude of an @counter-style rule
|
||||||
|
pub fn parse_counter_style_name(input: &mut Parser) -> Result<CustomIdent, ()> {
|
||||||
|
macro_rules! predefined {
|
||||||
|
($($name: expr,)+) => {
|
||||||
|
{
|
||||||
|
ascii_case_insensitive_phf_map! {
|
||||||
|
// FIXME: use static atoms https://github.com/rust-lang/rust/issues/33156
|
||||||
|
predefined -> &'static str = {
|
||||||
|
$(
|
||||||
|
$name => $name,
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ident = input.expect_ident()?;
|
||||||
|
if let Some(&lower_cased) = predefined(&ident) {
|
||||||
|
Ok(CustomIdent(Atom::from(lower_cased)))
|
||||||
|
} else {
|
||||||
|
// https://github.com/w3c/csswg-drafts/issues/1295 excludes "none"
|
||||||
|
CustomIdent::from_ident(ident, &["none"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
include!("predefined.rs")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse the body (inside `{}`) of an @counter-style rule
|
||||||
|
pub fn parse_counter_style_body(name: CustomIdent, context: &ParserContext, input: &mut Parser)
|
||||||
|
-> Result<CounterStyleRule, ()> {
|
||||||
|
let start = input.position();
|
||||||
|
let mut rule = CounterStyleRule::empty(name);
|
||||||
|
{
|
||||||
|
let parser = CounterStyleRuleParser {
|
||||||
|
context: context,
|
||||||
|
rule: &mut rule,
|
||||||
|
};
|
||||||
|
let mut iter = DeclarationListParser::new(input, parser);
|
||||||
|
while let Some(declaration) = iter.next() {
|
||||||
|
if let Err(range) = declaration {
|
||||||
|
let pos = range.start;
|
||||||
|
let message = format!("Unsupported @counter-style descriptor declaration: '{}'",
|
||||||
|
iter.input.slice(range));
|
||||||
|
log_css_error(iter.input, pos, &*message, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let error = match *rule.system() {
|
||||||
|
ref system @ System::Cyclic |
|
||||||
|
ref system @ System::Fixed { .. } |
|
||||||
|
ref system @ System::Symbolic |
|
||||||
|
ref system @ System::Alphabetic |
|
||||||
|
ref system @ System::Numeric
|
||||||
|
if rule.symbols.is_none() => {
|
||||||
|
let system = system.to_css_string();
|
||||||
|
Some(format!("Invalid @counter-style rule: 'system: {}' without 'symbols'", system))
|
||||||
|
}
|
||||||
|
ref system @ System::Alphabetic |
|
||||||
|
ref system @ System::Numeric
|
||||||
|
if rule.symbols().unwrap().0.len() < 2 => {
|
||||||
|
let system = system.to_css_string();
|
||||||
|
Some(format!("Invalid @counter-style rule: 'system: {}' less than two 'symbols'",
|
||||||
|
system))
|
||||||
|
}
|
||||||
|
System::Additive if rule.additive_symbols.is_none() => {
|
||||||
|
let s = "Invalid @counter-style rule: 'system: additive' without 'additive-symbols'";
|
||||||
|
Some(s.to_owned())
|
||||||
|
}
|
||||||
|
System::Extends(_) if rule.symbols.is_some() => {
|
||||||
|
let s = "Invalid @counter-style rule: 'system: extends …' with 'symbols'";
|
||||||
|
Some(s.to_owned())
|
||||||
|
}
|
||||||
|
System::Extends(_) if rule.additive_symbols.is_some() => {
|
||||||
|
let s = "Invalid @counter-style rule: 'system: extends …' with 'additive-symbols'";
|
||||||
|
Some(s.to_owned())
|
||||||
|
}
|
||||||
|
_ => None
|
||||||
|
};
|
||||||
|
if let Some(message) = error {
|
||||||
|
log_css_error(input, start, &message, context);
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
Ok(rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CounterStyleRuleParser<'a, 'b: 'a> {
|
||||||
|
context: &'a ParserContext<'b>,
|
||||||
|
rule: &'a mut CounterStyleRule,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default methods reject all at rules.
|
||||||
|
impl<'a, 'b> AtRuleParser for CounterStyleRuleParser<'a, 'b> {
|
||||||
|
type Prelude = ();
|
||||||
|
type AtRule = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! accessor {
|
||||||
|
(#[$doc: meta] $name: tt $ident: ident: $ty: ty = !) => {
|
||||||
|
#[$doc]
|
||||||
|
pub fn $ident(&self) -> Option<&$ty> {
|
||||||
|
self.$ident.as_ref()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(#[$doc: meta] $name: tt $ident: ident: $ty: ty = $initial: expr) => {
|
||||||
|
#[$doc]
|
||||||
|
pub fn $ident(&self) -> Cow<$ty> {
|
||||||
|
if let Some(ref value) = self.$ident {
|
||||||
|
Cow::Borrowed(value)
|
||||||
|
} else {
|
||||||
|
Cow::Owned($initial)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! counter_style_descriptors {
|
||||||
|
(
|
||||||
|
$( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty = $initial: tt )+
|
||||||
|
) => {
|
||||||
|
/// An @counter-style rule
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CounterStyleRule {
|
||||||
|
name: CustomIdent,
|
||||||
|
$(
|
||||||
|
#[$doc]
|
||||||
|
$ident: Option<$ty>,
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CounterStyleRule {
|
||||||
|
fn empty(name: CustomIdent) -> Self {
|
||||||
|
CounterStyleRule {
|
||||||
|
name: name,
|
||||||
|
$(
|
||||||
|
$ident: None,
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
accessor!(#[$doc] $name $ident: $ty = $initial);
|
||||||
|
)+
|
||||||
|
|
||||||
|
/// Convert to Gecko types
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
pub fn set_descriptors(&self, descriptors: &mut CounterStyleDescriptors) {
|
||||||
|
$(
|
||||||
|
if let Some(ref value) = self.$ident {
|
||||||
|
descriptors[nsCSSCounterDesc::$gecko_ident as usize].set_from(value)
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> DeclarationParser for CounterStyleRuleParser<'a, 'b> {
|
||||||
|
type Declaration = ();
|
||||||
|
|
||||||
|
fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(), ()> {
|
||||||
|
match_ignore_ascii_case! { name,
|
||||||
|
$(
|
||||||
|
$name => {
|
||||||
|
// DeclarationParser also calls parse_entirely
|
||||||
|
// so we’d normally not need to,
|
||||||
|
// but in this case we do because we set the value as a side effect
|
||||||
|
// rather than returning it.
|
||||||
|
let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
|
||||||
|
self.rule.$ident = Some(value)
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
_ => return Err(())
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCssWithGuard for CounterStyleRule {
|
||||||
|
fn to_css<W>(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
|
||||||
|
where W: fmt::Write {
|
||||||
|
dest.write_str("@counter-style ")?;
|
||||||
|
self.name.to_css(dest)?;
|
||||||
|
dest.write_str(" {\n")?;
|
||||||
|
$(
|
||||||
|
if let Some(ref value) = self.$ident {
|
||||||
|
dest.write_str(concat!(" ", $name, ": "))?;
|
||||||
|
ToCss::to_css(value, dest)?;
|
||||||
|
dest.write_str(";\n")?;
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
dest.write_str("}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
counter_style_descriptors! {
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#counter-style-system
|
||||||
|
"system" system / eCSSCounterDesc_System: System = {
|
||||||
|
System::Symbolic
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#counter-style-negative
|
||||||
|
"negative" negative / eCSSCounterDesc_Negative: Negative = {
|
||||||
|
Negative(Symbol::String("-".to_owned()), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#counter-style-prefix
|
||||||
|
"prefix" prefix / eCSSCounterDesc_Prefix: Symbol = {
|
||||||
|
Symbol::String("".to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#counter-style-suffix
|
||||||
|
"suffix" suffix / eCSSCounterDesc_Suffix: Symbol = {
|
||||||
|
Symbol::String(". ".to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#counter-style-range
|
||||||
|
"range" range / eCSSCounterDesc_Range: Ranges = {
|
||||||
|
Ranges(Vec::new()) // Empty Vec represents 'auto'
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#counter-style-pad
|
||||||
|
"pad" pad / eCSSCounterDesc_Pad: Pad = {
|
||||||
|
Pad(0, Symbol::String("".to_owned()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#counter-style-fallback
|
||||||
|
"fallback" fallback / eCSSCounterDesc_Fallback: Fallback = {
|
||||||
|
// FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1359323 use atom!()
|
||||||
|
Fallback(CustomIdent(Atom::from("decimal")))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols
|
||||||
|
"symbols" symbols / eCSSCounterDesc_Symbols: Symbols = !
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-additive-symbols
|
||||||
|
"additive-symbols" additive_symbols / eCSSCounterDesc_AdditiveSymbols: AdditiveSymbols = !
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#counter-style-speak-as
|
||||||
|
"speak-as" speak_as / eCSSCounterDesc_SpeakAs: SpeakAs = {
|
||||||
|
SpeakAs::Auto
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#counter-style-system
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum System {
|
||||||
|
/// 'cyclic'
|
||||||
|
Cyclic,
|
||||||
|
/// 'numeric'
|
||||||
|
Numeric,
|
||||||
|
/// 'alphabetic'
|
||||||
|
Alphabetic,
|
||||||
|
/// 'symbolic'
|
||||||
|
Symbolic,
|
||||||
|
/// 'additive'
|
||||||
|
Additive,
|
||||||
|
/// 'fixed <integer>?'
|
||||||
|
Fixed {
|
||||||
|
/// '<integer>?'
|
||||||
|
first_symbol_value: Option<i32>
|
||||||
|
},
|
||||||
|
/// 'extends <counter-style-name>'
|
||||||
|
Extends(CustomIdent),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for System {
|
||||||
|
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
match_ignore_ascii_case! { &input.expect_ident()?,
|
||||||
|
"cyclic" => Ok(System::Cyclic),
|
||||||
|
"numeric" => Ok(System::Numeric),
|
||||||
|
"alphabetic" => Ok(System::Alphabetic),
|
||||||
|
"symbolic" => Ok(System::Symbolic),
|
||||||
|
"additive" => Ok(System::Additive),
|
||||||
|
"fixed" => {
|
||||||
|
let first_symbol_value = input.try(|i| i.expect_integer()).ok();
|
||||||
|
Ok(System::Fixed { first_symbol_value: first_symbol_value })
|
||||||
|
}
|
||||||
|
"extends" => {
|
||||||
|
let other = parse_counter_style_name(input)?;
|
||||||
|
Ok(System::Extends(other))
|
||||||
|
}
|
||||||
|
_ => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for System {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
match *self {
|
||||||
|
System::Cyclic => dest.write_str("cyclic"),
|
||||||
|
System::Numeric => dest.write_str("numeric"),
|
||||||
|
System::Alphabetic => dest.write_str("alphabetic"),
|
||||||
|
System::Symbolic => dest.write_str("symbolic"),
|
||||||
|
System::Additive => dest.write_str("additive"),
|
||||||
|
System::Fixed { first_symbol_value } => {
|
||||||
|
if let Some(value) = first_symbol_value {
|
||||||
|
write!(dest, "fixed {}", value)
|
||||||
|
} else {
|
||||||
|
dest.write_str("fixed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System::Extends(ref other) => {
|
||||||
|
dest.write_str("extends ")?;
|
||||||
|
other.to_css(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#typedef-symbol
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Symbol {
|
||||||
|
/// <string>
|
||||||
|
String(String),
|
||||||
|
/// <ident>
|
||||||
|
Ident(String),
|
||||||
|
// Not implemented:
|
||||||
|
// /// <image>
|
||||||
|
// Image(Image),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Symbol {
|
||||||
|
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
match input.next() {
|
||||||
|
Ok(Token::QuotedString(s)) => Ok(Symbol::String(s.into_owned())),
|
||||||
|
Ok(Token::Ident(s)) => Ok(Symbol::Ident(s.into_owned())),
|
||||||
|
_ => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for Symbol {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
match *self {
|
||||||
|
Symbol::String(ref s) => serialize_string(s, dest),
|
||||||
|
Symbol::Ident(ref s) => serialize_identifier(s, dest),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#counter-style-negative
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Negative(pub Symbol, pub Option<Symbol>);
|
||||||
|
|
||||||
|
impl Parse for Negative {
|
||||||
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
Ok(Negative(
|
||||||
|
Symbol::parse(context, input)?,
|
||||||
|
input.try(|input| Symbol::parse(context, input)).ok(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for Negative {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
self.0.to_css(dest)?;
|
||||||
|
if let Some(ref symbol) = self.1 {
|
||||||
|
dest.write_char(' ')?;
|
||||||
|
symbol.to_css(dest)?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#counter-style-range
|
||||||
|
///
|
||||||
|
/// Empty Vec represents 'auto'
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Ranges(pub Vec<Range<Option<i32>>>);
|
||||||
|
|
||||||
|
impl Parse for Ranges {
|
||||||
|
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
|
||||||
|
Ok(Ranges(Vec::new()))
|
||||||
|
} else {
|
||||||
|
input.parse_comma_separated(|input| {
|
||||||
|
let opt_start = parse_bound(input)?;
|
||||||
|
let opt_end = parse_bound(input)?;
|
||||||
|
if let (Some(start), Some(end)) = (opt_start, opt_end) {
|
||||||
|
if start > end {
|
||||||
|
return Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(opt_start..opt_end)
|
||||||
|
}).map(Ranges)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_bound(input: &mut Parser) -> Result<Option<i32>, ()> {
|
||||||
|
match input.next() {
|
||||||
|
Ok(Token::Number(ref v)) if v.int_value.is_some() => Ok(Some(v.int_value.unwrap())),
|
||||||
|
Ok(Token::Ident(ref ident)) if ident.eq_ignore_ascii_case("infinite") => Ok(None),
|
||||||
|
_ => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for Ranges {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
let mut iter = self.0.iter();
|
||||||
|
if let Some(first) = iter.next() {
|
||||||
|
range_to_css(first, dest)?;
|
||||||
|
for item in iter {
|
||||||
|
dest.write_str(", ")?;
|
||||||
|
range_to_css(item, dest)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
dest.write_str("auto")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn range_to_css<W>(range: &Range<Option<i32>>, dest: &mut W) -> fmt::Result
|
||||||
|
where W: fmt::Write {
|
||||||
|
bound_to_css(range.start, dest)?;
|
||||||
|
dest.write_char(' ')?;
|
||||||
|
bound_to_css(range.end, dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bound_to_css<W>(range: Option<i32>, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
if let Some(finite) = range {
|
||||||
|
write!(dest, "{}", finite)
|
||||||
|
} else {
|
||||||
|
dest.write_str("infinite")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#counter-style-pad
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Pad(pub u32, pub Symbol);
|
||||||
|
|
||||||
|
impl Parse for Pad {
|
||||||
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
let pad_with = input.try(|input| Symbol::parse(context, input));
|
||||||
|
let min_length = input.expect_integer()?;
|
||||||
|
if min_length < 0 {
|
||||||
|
return Err(())
|
||||||
|
}
|
||||||
|
let pad_with = pad_with.or_else(|()| Symbol::parse(context, input))?;
|
||||||
|
Ok(Pad(min_length as u32, pad_with))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for Pad {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
write!(dest, "{} ", self.0)?;
|
||||||
|
self.1.to_css(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#counter-style-fallback
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Fallback(pub CustomIdent);
|
||||||
|
|
||||||
|
impl Parse for Fallback {
|
||||||
|
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
parse_counter_style_name(input).map(Fallback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for Fallback {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
self.0.to_css(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-symbols
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Symbols(pub Vec<Symbol>);
|
||||||
|
|
||||||
|
impl Parse for Symbols {
|
||||||
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
let mut symbols = Vec::new();
|
||||||
|
loop {
|
||||||
|
if let Ok(s) = input.try(|input| Symbol::parse(context, input)) {
|
||||||
|
symbols.push(s)
|
||||||
|
} else {
|
||||||
|
if symbols.is_empty() {
|
||||||
|
return Err(())
|
||||||
|
} else {
|
||||||
|
return Ok(Symbols(symbols))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for Symbols {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
let mut iter = self.0.iter();
|
||||||
|
let first = iter.next().expect("expected at least one symbol");
|
||||||
|
first.to_css(dest)?;
|
||||||
|
for item in iter {
|
||||||
|
dest.write_char(' ')?;
|
||||||
|
item.to_css(dest)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#descdef-counter-style-additive-symbols
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AdditiveSymbols(pub Vec<AdditiveTuple>);
|
||||||
|
|
||||||
|
impl Parse for AdditiveSymbols {
|
||||||
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
let tuples = Vec::<AdditiveTuple>::parse(context, input)?;
|
||||||
|
// FIXME maybe? https://github.com/w3c/csswg-drafts/issues/1220
|
||||||
|
if tuples.windows(2).any(|window| window[0].value <= window[1].value) {
|
||||||
|
return Err(())
|
||||||
|
}
|
||||||
|
Ok(AdditiveSymbols(tuples))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for AdditiveSymbols {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
self.0.to_css(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <integer> && <symbol>
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AdditiveTuple {
|
||||||
|
value: u32,
|
||||||
|
symbol: Symbol,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OneOrMoreCommaSeparated for AdditiveTuple {}
|
||||||
|
|
||||||
|
impl Parse for AdditiveTuple {
|
||||||
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
let symbol = input.try(|input| Symbol::parse(context, input));
|
||||||
|
let value = input.expect_integer()?;
|
||||||
|
if value < 0 {
|
||||||
|
return Err(())
|
||||||
|
}
|
||||||
|
let symbol = symbol.or_else(|()| Symbol::parse(context, input))?;
|
||||||
|
Ok(AdditiveTuple {
|
||||||
|
value: value as u32,
|
||||||
|
symbol: symbol,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for AdditiveTuple {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
write!(dest, "{} ", self.value)?;
|
||||||
|
self.symbol.to_css(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-counter-styles/#counter-style-speak-as
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum SpeakAs {
|
||||||
|
/// auto
|
||||||
|
Auto,
|
||||||
|
/// bullets
|
||||||
|
Bullets,
|
||||||
|
/// numbers
|
||||||
|
Numbers,
|
||||||
|
/// words
|
||||||
|
Words,
|
||||||
|
// /// spell-out, not supported, see bug 1024178
|
||||||
|
// SpellOut,
|
||||||
|
/// <counter-style-name>
|
||||||
|
Other(CustomIdent),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for SpeakAs {
|
||||||
|
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
let mut is_spell_out = false;
|
||||||
|
let result = input.try(|input| {
|
||||||
|
match_ignore_ascii_case! { &input.expect_ident()?,
|
||||||
|
"auto" => Ok(SpeakAs::Auto),
|
||||||
|
"bullets" => Ok(SpeakAs::Bullets),
|
||||||
|
"numbers" => Ok(SpeakAs::Numbers),
|
||||||
|
"words" => Ok(SpeakAs::Words),
|
||||||
|
"spell-out" => {
|
||||||
|
is_spell_out = true;
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
_ => Err(())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if is_spell_out {
|
||||||
|
// spell-out is not supported, but don’t parse it as a <counter-style-name>.
|
||||||
|
// See bug 1024178.
|
||||||
|
return Err(())
|
||||||
|
}
|
||||||
|
result.or_else(|()| {
|
||||||
|
Ok(SpeakAs::Other(parse_counter_style_name(input)?))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for SpeakAs {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
match *self {
|
||||||
|
SpeakAs::Auto => dest.write_str("auto"),
|
||||||
|
SpeakAs::Bullets => dest.write_str("bullets"),
|
||||||
|
SpeakAs::Numbers => dest.write_str("numbers"),
|
||||||
|
SpeakAs::Words => dest.write_str("words"),
|
||||||
|
SpeakAs::Other(ref other) => other.to_css(dest),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
components/style/counter_style/predefined.rs
Normal file
61
components/style/counter_style/predefined.rs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
predefined! {
|
||||||
|
"decimal",
|
||||||
|
"decimal-leading-zero",
|
||||||
|
"arabic-indic",
|
||||||
|
"armenian",
|
||||||
|
"upper-armenian",
|
||||||
|
"lower-armenian",
|
||||||
|
"bengali",
|
||||||
|
"cambodian",
|
||||||
|
"khmer",
|
||||||
|
"cjk-decimal",
|
||||||
|
"devanagari",
|
||||||
|
"georgian",
|
||||||
|
"gujarati",
|
||||||
|
"gurmukhi",
|
||||||
|
"hebrew",
|
||||||
|
"kannada",
|
||||||
|
"lao",
|
||||||
|
"malayalam",
|
||||||
|
"mongolian",
|
||||||
|
"myanmar",
|
||||||
|
"oriya",
|
||||||
|
"persian",
|
||||||
|
"lower-roman",
|
||||||
|
"upper-roman",
|
||||||
|
"tamil",
|
||||||
|
"telugu",
|
||||||
|
"thai",
|
||||||
|
"tibetan",
|
||||||
|
"lower-alpha",
|
||||||
|
"lower-latin",
|
||||||
|
"upper-alpha",
|
||||||
|
"upper-latin",
|
||||||
|
"cjk-earthly-branch",
|
||||||
|
"cjk-heavenly-stem",
|
||||||
|
"lower-greek",
|
||||||
|
"hiragana",
|
||||||
|
"hiragana-iroha",
|
||||||
|
"katakana",
|
||||||
|
"katakana-iroha",
|
||||||
|
"disc",
|
||||||
|
"circle",
|
||||||
|
"square",
|
||||||
|
"disclosure-open",
|
||||||
|
"disclosure-closed",
|
||||||
|
"japanese-informal",
|
||||||
|
"japanese-formal",
|
||||||
|
"korean-hangul-formal",
|
||||||
|
"korean-hanja-informal",
|
||||||
|
"korean-hanja-formal",
|
||||||
|
"simp-chinese-informal",
|
||||||
|
"simp-chinese-formal",
|
||||||
|
"trad-chinese-informal",
|
||||||
|
"trad-chinese-formal",
|
||||||
|
"cjk-ideographic",
|
||||||
|
"ethiopic-numeric",
|
||||||
|
}
|
32
components/style/counter_style/update_predefined.py
Executable file
32
components/style/counter_style/update_predefined.py
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# 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/. */
|
||||||
|
|
||||||
|
import os.path
|
||||||
|
import re
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
|
||||||
|
def main(filename):
|
||||||
|
names = [
|
||||||
|
re.search('>([^>]+)(</dfn>|<a class="self-link")', line).group(1)
|
||||||
|
for line in urllib.urlopen("https://drafts.csswg.org/css-counter-styles/")
|
||||||
|
if 'data-dfn-for="<counter-style-name>"' in line
|
||||||
|
or 'data-dfn-for="<counter-style>"' in line
|
||||||
|
]
|
||||||
|
with open(filename, "wb") as f:
|
||||||
|
f.write("""\
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
predefined! {
|
||||||
|
""")
|
||||||
|
for name in names:
|
||||||
|
f.write(' "%s",\n' % name)
|
||||||
|
f.write('}\n')
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(os.path.join(os.path.dirname(__file__), "predefined.rs"))
|
|
@ -6,10 +6,11 @@
|
||||||
|
|
||||||
use computed_values::{font_style, font_weight, font_stretch};
|
use computed_values::{font_style, font_weight, font_stretch};
|
||||||
use computed_values::font_family::FamilyName;
|
use computed_values::font_family::FamilyName;
|
||||||
|
use counter_style;
|
||||||
use cssparser::UnicodeRange;
|
use cssparser::UnicodeRange;
|
||||||
use font_face::{FontFaceRuleData, Source};
|
use font_face::{FontFaceRuleData, Source};
|
||||||
use gecko_bindings::bindings;
|
use gecko_bindings::bindings;
|
||||||
use gecko_bindings::structs::{self, nsCSSFontFaceRule, nsCSSValue};
|
use gecko_bindings::structs::{self, nsCSSFontFaceRule, nsCSSValue, nsCSSCounterDesc};
|
||||||
use gecko_bindings::sugar::ns_css_value::ToNsCssValue;
|
use gecko_bindings::sugar::ns_css_value::ToNsCssValue;
|
||||||
use gecko_bindings::sugar::refptr::{RefPtr, UniqueRefPtr};
|
use gecko_bindings::sugar::refptr::{RefPtr, UniqueRefPtr};
|
||||||
use shared_lock::{ToCssWithGuard, SharedRwLockReadGuard};
|
use shared_lock::{ToCssWithGuard, SharedRwLockReadGuard};
|
||||||
|
@ -133,3 +134,123 @@ impl ToCssWithGuard for FontFaceRule {
|
||||||
write!(dest, "{}", css_text)
|
write!(dest, "{}", css_text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The type of nsCSSCounterStyleRule::mValues
|
||||||
|
pub type CounterStyleDescriptors = [nsCSSValue; nsCSSCounterDesc::eCSSCounterDesc_COUNT as usize];
|
||||||
|
|
||||||
|
impl ToNsCssValue for counter_style::System {
|
||||||
|
fn convert(&self, nscssvalue: &mut nsCSSValue) {
|
||||||
|
use counter_style::System::*;
|
||||||
|
match *self {
|
||||||
|
Cyclic => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_CYCLIC as i32),
|
||||||
|
Numeric => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_NUMERIC as i32),
|
||||||
|
Alphabetic => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_ALPHABETIC as i32),
|
||||||
|
Symbolic => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_SYMBOLIC as i32),
|
||||||
|
Additive => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_ADDITIVE as i32),
|
||||||
|
Fixed { first_symbol_value } => {
|
||||||
|
let mut a = nsCSSValue::null();
|
||||||
|
let mut b = nsCSSValue::null();
|
||||||
|
a.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_FIXED as i32);
|
||||||
|
b.set_integer(first_symbol_value.unwrap_or(1));
|
||||||
|
//nscssvalue.set_pair(a, b); // FIXME: add bindings for nsCSSValue::SetPairValue
|
||||||
|
}
|
||||||
|
Extends(ref other) => {
|
||||||
|
let mut a = nsCSSValue::null();
|
||||||
|
let mut b = nsCSSValue::null();
|
||||||
|
a.set_enum(structs::NS_STYLE_COUNTER_SYSTEM_EXTENDS as i32);
|
||||||
|
b.set_string_from_atom(&other.0);
|
||||||
|
//nscssvalue.set_pair(a, b); // FIXME: add bindings for nsCSSValue::SetPairValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToNsCssValue for counter_style::Negative {
|
||||||
|
fn convert(&self, nscssvalue: &mut nsCSSValue) {
|
||||||
|
if let Some(ref second) = self.1 {
|
||||||
|
let mut a = nsCSSValue::null();
|
||||||
|
let mut b = nsCSSValue::null();
|
||||||
|
a.set_from(&self.0);
|
||||||
|
b.set_from(second);
|
||||||
|
//nscssvalue.set_pair(a, b); // FIXME: add bindings for nsCSSValue::SetPairValue
|
||||||
|
} else {
|
||||||
|
nscssvalue.set_from(&self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToNsCssValue for counter_style::Symbol {
|
||||||
|
fn convert(&self, nscssvalue: &mut nsCSSValue) {
|
||||||
|
match *self {
|
||||||
|
counter_style::Symbol::String(ref s) => nscssvalue.set_string(s),
|
||||||
|
counter_style::Symbol::Ident(ref s) => nscssvalue.set_ident(s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToNsCssValue for counter_style::Ranges {
|
||||||
|
fn convert(&self, _nscssvalue: &mut nsCSSValue) {
|
||||||
|
if self.0.is_empty() {
|
||||||
|
//nscssvalue.set_auto(); // FIXME: add bindings for nsCSSValue::SetAutoValue
|
||||||
|
} else {
|
||||||
|
for range in &self.0 {
|
||||||
|
fn set_bound(bound: Option<i32>, nscssvalue: &mut nsCSSValue) {
|
||||||
|
if let Some(finite) = bound {
|
||||||
|
nscssvalue.set_integer(finite)
|
||||||
|
} else {
|
||||||
|
nscssvalue.set_enum(structs::NS_STYLE_COUNTER_RANGE_INFINITE as i32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut start = nsCSSValue::null();
|
||||||
|
let mut end = nsCSSValue::null();
|
||||||
|
set_bound(range.start, &mut start);
|
||||||
|
set_bound(range.end, &mut end);
|
||||||
|
// FIXME: add bindings for nsCSSValuePairList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToNsCssValue for counter_style::Pad {
|
||||||
|
fn convert(&self, _nscssvalue: &mut nsCSSValue) {
|
||||||
|
let mut min_length = nsCSSValue::null();
|
||||||
|
let mut pad_with = nsCSSValue::null();
|
||||||
|
min_length.set_integer(self.0 as i32);
|
||||||
|
pad_with.set_from(&self.1);
|
||||||
|
// FIXME: add bindings for nsCSSValue::SetPairValue
|
||||||
|
//nscssvalue.set_pair(min_length, pad_with);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToNsCssValue for counter_style::Fallback {
|
||||||
|
fn convert(&self, nscssvalue: &mut nsCSSValue) {
|
||||||
|
nscssvalue.set_ident_from_atom(&self.0 .0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToNsCssValue for counter_style::Symbols {
|
||||||
|
fn convert(&self, _nscssvalue: &mut nsCSSValue) {
|
||||||
|
debug_assert!(!self.0.is_empty());
|
||||||
|
// FIXME: add bindings for nsCSSValueList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToNsCssValue for counter_style::AdditiveSymbols {
|
||||||
|
fn convert(&self, _nscssvalue: &mut nsCSSValue) {
|
||||||
|
debug_assert!(!self.0.is_empty());
|
||||||
|
// FIXME: add bindings for nsCSSValuePairList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToNsCssValue for counter_style::SpeakAs {
|
||||||
|
fn convert(&self, nscssvalue: &mut nsCSSValue) {
|
||||||
|
use counter_style::SpeakAs::*;
|
||||||
|
match *self {
|
||||||
|
Auto => {} //nscssvalue.set_auto(), // FIXME: add bindings for nsCSSValue::SetAutoValue
|
||||||
|
Bullets => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SPEAKAS_BULLETS as i32),
|
||||||
|
Numbers => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SPEAKAS_NUMBERS as i32),
|
||||||
|
Words => nscssvalue.set_enum(structs::NS_STYLE_COUNTER_SPEAKAS_WORDS as i32),
|
||||||
|
Other(ref other) => nscssvalue.set_ident_from_atom(&other.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -124,6 +124,11 @@ impl nsCSSValue {
|
||||||
self.set_string_from_atom_internal(s, nsCSSUnit::eCSSUnit_String)
|
self.set_string_from_atom_internal(s, nsCSSUnit::eCSSUnit_String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set to a ident value from the given atom
|
||||||
|
pub fn set_ident_from_atom(&mut self, s: &Atom) {
|
||||||
|
self.set_string_from_atom_internal(s, nsCSSUnit::eCSSUnit_Ident)
|
||||||
|
}
|
||||||
|
|
||||||
/// Set to an identifier value
|
/// Set to an identifier value
|
||||||
pub fn set_ident(&mut self, s: &str) {
|
pub fn set_ident(&mut self, s: &str) {
|
||||||
self.set_string_internal(s, nsCSSUnit::eCSSUnit_Ident)
|
self.set_string_internal(s, nsCSSUnit::eCSSUnit_Ident)
|
||||||
|
|
|
@ -13,6 +13,7 @@ use gecko_bindings::bindings::Gecko_ReleaseAtom;
|
||||||
use gecko_bindings::structs::nsIAtom;
|
use gecko_bindings::structs::nsIAtom;
|
||||||
use nsstring::nsAString;
|
use nsstring::nsAString;
|
||||||
use precomputed_hash::PrecomputedHash;
|
use precomputed_hash::PrecomputedHash;
|
||||||
|
use std::ascii::AsciiExt;
|
||||||
use std::borrow::{Cow, Borrow};
|
use std::borrow::{Cow, Borrow};
|
||||||
use std::char::{self, DecodeUtf16};
|
use std::char::{self, DecodeUtf16};
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
|
@ -163,6 +164,12 @@ impl WeakAtom {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether this atom is the empty string.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the atom as a mutable pointer.
|
/// Returns the atom as a mutable pointer.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_ptr(&self) -> *mut nsIAtom {
|
pub fn as_ptr(&self) -> *mut nsIAtom {
|
||||||
|
@ -218,6 +225,25 @@ impl Atom {
|
||||||
Atom(WeakAtom::new(ptr))
|
Atom(WeakAtom::new(ptr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return whether two atoms are ASCII-case-insensitive matches
|
||||||
|
pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
|
||||||
|
let a = self.as_slice();
|
||||||
|
let b = other.as_slice();
|
||||||
|
a.len() == b.len() && a.iter().zip(b).all(|(&a16, &b16)| {
|
||||||
|
if a16 <= 0x7F && b16 <= 0x7F {
|
||||||
|
(a16 as u8).eq_ignore_ascii_case(&(b16 as u8))
|
||||||
|
} else {
|
||||||
|
a16 == b16
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return whether this atom is an ASCII-case-insensitive match for the given string
|
||||||
|
pub fn eq_str_ignore_ascii_case(&self, other: &str) -> bool {
|
||||||
|
self.chars().map(|r| r.map(|c: char| c.to_ascii_lowercase()))
|
||||||
|
.eq(other.chars().map(|c: char| Ok(c.to_ascii_lowercase())))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Atom {
|
impl Hash for Atom {
|
||||||
|
|
|
@ -89,6 +89,7 @@ pub mod bloom;
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
pub mod cascade_info;
|
pub mod cascade_info;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
|
pub mod counter_style;
|
||||||
pub mod custom_properties;
|
pub mod custom_properties;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod dom;
|
pub mod dom;
|
||||||
|
|
|
@ -560,7 +560,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
pseudo: Option<&PseudoElement>) -> bool {
|
pseudo: Option<&PseudoElement>) -> bool {
|
||||||
let ref new_box_style = new_values.get_box();
|
let ref new_box_style = new_values.get_box();
|
||||||
let has_new_animation_style = new_box_style.animation_name_count() >= 1 &&
|
let has_new_animation_style = new_box_style.animation_name_count() >= 1 &&
|
||||||
new_box_style.animation_name_at(0).0.len() != 0;
|
new_box_style.animation_name_at(0).0.is_some();
|
||||||
let has_animations = self.has_css_animations(pseudo);
|
let has_animations = self.has_css_animations(pseudo);
|
||||||
|
|
||||||
old_values.as_ref().map_or(has_new_animation_style, |ref old| {
|
old_values.as_ref().map_or(has_new_animation_style, |ref old| {
|
||||||
|
|
|
@ -62,7 +62,7 @@ use std::ptr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use values::computed::ToComputedValue;
|
use values::computed::ToComputedValue;
|
||||||
use values::{Either, Auto};
|
use values::{Either, Auto, KeyframesName};
|
||||||
use computed_values::border_style;
|
use computed_values::border_style;
|
||||||
|
|
||||||
pub mod style_structs {
|
pub mod style_structs {
|
||||||
|
@ -1175,11 +1175,10 @@ fn static_assert() {
|
||||||
|
|
||||||
% for value in GRID_LINES:
|
% for value in GRID_LINES:
|
||||||
pub fn set_${value.name}(&mut self, v: longhands::${value.name}::computed_value::T) {
|
pub fn set_${value.name}(&mut self, v: longhands::${value.name}::computed_value::T) {
|
||||||
use nsstring::nsCString;
|
|
||||||
use gecko_bindings::structs::{nsStyleGridLine_kMinLine, nsStyleGridLine_kMaxLine};
|
use gecko_bindings::structs::{nsStyleGridLine_kMinLine, nsStyleGridLine_kMaxLine};
|
||||||
|
|
||||||
let ident = v.ident.unwrap_or(String::new());
|
let ident = v.ident.unwrap_or(String::new());
|
||||||
self.gecko.${value.gecko}.mLineName.assign_utf8(&nsCString::from(&*ident));
|
self.gecko.${value.gecko}.mLineName.assign_utf8(&ident);
|
||||||
self.gecko.${value.gecko}.mHasSpan = v.is_span;
|
self.gecko.${value.gecko}.mHasSpan = v.is_span;
|
||||||
self.gecko.${value.gecko}.mInteger = v.integer.map(|i| {
|
self.gecko.${value.gecko}.mInteger = v.integer.map(|i| {
|
||||||
// clamping the integer between a range
|
// clamping the integer between a range
|
||||||
|
@ -2197,23 +2196,28 @@ fn static_assert() {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_animation_name(&mut self, v: longhands::animation_name::computed_value::T) {
|
pub fn set_animation_name(&mut self, v: longhands::animation_name::computed_value::T) {
|
||||||
use nsstring::nsCString;
|
|
||||||
|
|
||||||
debug_assert!(!v.0.is_empty());
|
debug_assert!(!v.0.is_empty());
|
||||||
unsafe { self.gecko.mAnimations.ensure_len(v.0.len()) };
|
unsafe { self.gecko.mAnimations.ensure_len(v.0.len()) };
|
||||||
|
|
||||||
self.gecko.mAnimationNameCount = v.0.len() as u32;
|
self.gecko.mAnimationNameCount = v.0.len() as u32;
|
||||||
for (servo, gecko) in v.0.into_iter().zip(self.gecko.mAnimations.iter_mut()) {
|
for (servo, gecko) in v.0.into_iter().zip(self.gecko.mAnimations.iter_mut()) {
|
||||||
// TODO This is inefficient. We should fix this in bug 1329169.
|
// TODO This is inefficient. We should fix this in bug 1329169.
|
||||||
gecko.mName.assign_utf8(&nsCString::from(servo.0.to_string()));
|
gecko.mName.assign(match servo.0 {
|
||||||
|
Some(ref name) => name.as_atom().as_slice(),
|
||||||
|
None => &[], // Empty string for 'none'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn animation_name_at(&self, index: usize)
|
pub fn animation_name_at(&self, index: usize)
|
||||||
-> longhands::animation_name::computed_value::SingleComputedValue {
|
-> longhands::animation_name::computed_value::SingleComputedValue {
|
||||||
use Atom;
|
|
||||||
use properties::longhands::animation_name::single_value::SpecifiedValue as AnimationName;
|
use properties::longhands::animation_name::single_value::SpecifiedValue as AnimationName;
|
||||||
// XXX: Is there any effective ways?
|
// XXX: Is there any effective ways?
|
||||||
AnimationName(Atom::from(String::from_utf16_lossy(&self.gecko.mAnimations[index].mName[..])))
|
let atom = &self.gecko.mAnimations[index].mName;
|
||||||
|
if atom.is_empty() {
|
||||||
|
AnimationName(None)
|
||||||
|
} else {
|
||||||
|
AnimationName(Some(KeyframesName::from_ident(atom.to_string())))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn copy_animation_name_from(&mut self, other: &Self) {
|
pub fn copy_animation_name_from(&mut self, other: &Self) {
|
||||||
unsafe { self.gecko.mAnimations.ensure_len(other.gecko.mAnimations.len()) };
|
unsafe { self.gecko.mAnimations.ensure_len(other.gecko.mAnimations.len()) };
|
||||||
|
@ -2886,15 +2890,14 @@ fn static_assert() {
|
||||||
pub fn set_quotes(&mut self, other: longhands::quotes::computed_value::T) {
|
pub fn set_quotes(&mut self, other: longhands::quotes::computed_value::T) {
|
||||||
use gecko_bindings::bindings::Gecko_NewStyleQuoteValues;
|
use gecko_bindings::bindings::Gecko_NewStyleQuoteValues;
|
||||||
use gecko_bindings::sugar::refptr::UniqueRefPtr;
|
use gecko_bindings::sugar::refptr::UniqueRefPtr;
|
||||||
use nsstring::nsCString;
|
|
||||||
|
|
||||||
let mut refptr = unsafe {
|
let mut refptr = unsafe {
|
||||||
UniqueRefPtr::from_addrefed(Gecko_NewStyleQuoteValues(other.0.len() as u32))
|
UniqueRefPtr::from_addrefed(Gecko_NewStyleQuoteValues(other.0.len() as u32))
|
||||||
};
|
};
|
||||||
|
|
||||||
for (servo, gecko) in other.0.into_iter().zip(refptr.mQuotePairs.iter_mut()) {
|
for (servo, gecko) in other.0.into_iter().zip(refptr.mQuotePairs.iter_mut()) {
|
||||||
gecko.first.assign_utf8(&nsCString::from(&*servo.0));
|
gecko.first.assign_utf8(&servo.0);
|
||||||
gecko.second.assign_utf8(&nsCString::from(&*servo.1));
|
gecko.second.assign_utf8(&servo.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe { self.gecko.mQuotes.set_move(refptr.get()) }
|
unsafe { self.gecko.mQuotes.set_move(refptr.get()) }
|
||||||
|
@ -3381,7 +3384,6 @@ fn static_assert() {
|
||||||
<%call expr="impl_simple_copy('text_emphasis_position', 'mTextEmphasisPosition')"></%call>
|
<%call expr="impl_simple_copy('text_emphasis_position', 'mTextEmphasisPosition')"></%call>
|
||||||
|
|
||||||
pub fn set_text_emphasis_style(&mut self, v: longhands::text_emphasis_style::computed_value::T) {
|
pub fn set_text_emphasis_style(&mut self, v: longhands::text_emphasis_style::computed_value::T) {
|
||||||
use nsstring::nsCString;
|
|
||||||
use properties::longhands::text_emphasis_style::computed_value::T;
|
use properties::longhands::text_emphasis_style::computed_value::T;
|
||||||
use properties::longhands::text_emphasis_style::ShapeKeyword;
|
use properties::longhands::text_emphasis_style::ShapeKeyword;
|
||||||
|
|
||||||
|
@ -3408,7 +3410,7 @@ fn static_assert() {
|
||||||
(structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING, &**s)
|
(structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING, &**s)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
self.gecko.mTextEmphasisStyleString.assign_utf8(&nsCString::from(s));
|
self.gecko.mTextEmphasisStyleString.assign_utf8(s);
|
||||||
self.gecko.mTextEmphasisStyle = te as u8;
|
self.gecko.mTextEmphasisStyle = te as u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3489,12 +3491,11 @@ fn static_assert() {
|
||||||
use properties::longhands::text_overflow::{SpecifiedValue, Side};
|
use properties::longhands::text_overflow::{SpecifiedValue, Side};
|
||||||
|
|
||||||
fn set(side: &mut nsStyleTextOverflowSide, value: &Side) {
|
fn set(side: &mut nsStyleTextOverflowSide, value: &Side) {
|
||||||
use nsstring::nsCString;
|
|
||||||
let ty = match *value {
|
let ty = match *value {
|
||||||
Side::Clip => structs::NS_STYLE_TEXT_OVERFLOW_CLIP,
|
Side::Clip => structs::NS_STYLE_TEXT_OVERFLOW_CLIP,
|
||||||
Side::Ellipsis => structs::NS_STYLE_TEXT_OVERFLOW_ELLIPSIS,
|
Side::Ellipsis => structs::NS_STYLE_TEXT_OVERFLOW_ELLIPSIS,
|
||||||
Side::String(ref s) => {
|
Side::String(ref s) => {
|
||||||
side.mString.assign_utf8(&nsCString::from(&**s));
|
side.mString.assign_utf8(s);
|
||||||
structs::NS_STYLE_TEXT_OVERFLOW_STRING
|
structs::NS_STYLE_TEXT_OVERFLOW_STRING
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -4011,9 +4012,9 @@ clip-path
|
||||||
unsafe {
|
unsafe {
|
||||||
bindings::Gecko_ClearAndResizeCounter${counter_property}s(&mut self.gecko,
|
bindings::Gecko_ClearAndResizeCounter${counter_property}s(&mut self.gecko,
|
||||||
v.0.len() as u32);
|
v.0.len() as u32);
|
||||||
for (i, item) in v.0.into_iter().enumerate() {
|
for (i, (name, value)) in v.0.into_iter().enumerate() {
|
||||||
self.gecko.m${counter_property}s[i].mCounter.assign_utf8(&item.0);
|
self.gecko.m${counter_property}s[i].mCounter.assign(name.0.as_slice());
|
||||||
self.gecko.m${counter_property}s[i].mValue = item.1;
|
self.gecko.m${counter_property}s[i].mValue = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -796,7 +796,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
use values::computed::ComputedValueAsSpecified;
|
use values::computed::ComputedValueAsSpecified;
|
||||||
use values::HasViewportPercentage;
|
use values::{HasViewportPercentage, KeyframesName};
|
||||||
|
|
||||||
pub mod computed_value {
|
pub mod computed_value {
|
||||||
pub use super::SpecifiedValue as T;
|
pub use super::SpecifiedValue as T;
|
||||||
|
@ -804,7 +804,14 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
|
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct SpecifiedValue(pub Atom);
|
pub struct SpecifiedValue(pub Option<KeyframesName>);
|
||||||
|
|
||||||
|
impl SpecifiedValue {
|
||||||
|
/// As an Atom
|
||||||
|
pub fn as_atom(&self) -> Option< &Atom> {
|
||||||
|
self.0.as_ref().map(|n| n.as_atom())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_initial_value() -> computed_value::T {
|
pub fn get_initial_value() -> computed_value::T {
|
||||||
|
@ -813,48 +820,32 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_initial_specified_value() -> SpecifiedValue {
|
pub fn get_initial_specified_value() -> SpecifiedValue {
|
||||||
SpecifiedValue(atom!(""))
|
SpecifiedValue(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SpecifiedValue {
|
impl fmt::Display for SpecifiedValue {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
self.0.fmt(f)
|
self.to_css(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCss for SpecifiedValue {
|
impl ToCss for SpecifiedValue {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
if self.0 == atom!("") {
|
if let Some(ref name) = self.0 {
|
||||||
dest.write_str("none")
|
name.to_css(dest)
|
||||||
} else {
|
} else {
|
||||||
dest.write_str(&*self.0.to_string())
|
dest.write_str("none")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for SpecifiedValue {
|
impl Parse for SpecifiedValue {
|
||||||
fn parse(_context: &ParserContext, input: &mut ::cssparser::Parser) -> Result<Self, ()> {
|
fn parse(context: &ParserContext, input: &mut ::cssparser::Parser) -> Result<Self, ()> {
|
||||||
use cssparser::Token;
|
if let Ok(name) = input.try(|input| KeyframesName::parse(context, input)) {
|
||||||
use properties::CSSWideKeyword;
|
Ok(SpecifiedValue(Some(name)))
|
||||||
use std::ascii::AsciiExt;
|
} else {
|
||||||
|
input.expect_ident_matching("none").map(|()| SpecifiedValue(None))
|
||||||
let atom = match input.next() {
|
}
|
||||||
Ok(Token::Ident(ref value)) => {
|
|
||||||
if CSSWideKeyword::from_ident(value).is_some() {
|
|
||||||
// We allow any ident for the animation-name except one
|
|
||||||
// of the CSS-wide keywords.
|
|
||||||
return Err(());
|
|
||||||
} else if value.eq_ignore_ascii_case("none") {
|
|
||||||
// FIXME We may want to support `@keyframes ""` at some point.
|
|
||||||
atom!("")
|
|
||||||
} else {
|
|
||||||
Atom::from(&**value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Token::QuotedString(value)) => Atom::from(&*value),
|
|
||||||
_ => return Err(()),
|
|
||||||
};
|
|
||||||
Ok(SpecifiedValue(atom))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
no_viewport_percentage!(SpecifiedValue);
|
no_viewport_percentage!(SpecifiedValue);
|
||||||
|
|
|
@ -240,21 +240,22 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
use super::content;
|
use super::content;
|
||||||
use values::HasViewportPercentage;
|
use values::{HasViewportPercentage, CustomIdent};
|
||||||
|
|
||||||
use cssparser::{Token, serialize_identifier};
|
use cssparser::{Token, serialize_identifier};
|
||||||
use std::borrow::{Cow, ToOwned};
|
use std::borrow::{Cow, ToOwned};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct SpecifiedValue(pub Vec<(String, specified::Integer)>);
|
pub struct SpecifiedValue(pub Vec<(CustomIdent, specified::Integer)>);
|
||||||
|
|
||||||
pub mod computed_value {
|
pub mod computed_value {
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
|
use values::CustomIdent;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
pub struct T(pub Vec<(String, i32)>);
|
pub struct T(pub Vec<(CustomIdent, i32)>);
|
||||||
|
|
||||||
impl ToCss for T {
|
impl ToCss for T {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
|
@ -266,14 +267,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for pair in &self.0 {
|
for &(ref name, value) in &self.0 {
|
||||||
if !first {
|
if !first {
|
||||||
try!(dest.write_str(" "));
|
dest.write_str(" ")?;
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
try!(serialize_identifier(&pair.0, dest));
|
name.to_css(dest)?;
|
||||||
try!(dest.write_str(" "));
|
dest.write_str(" ")?;
|
||||||
try!(pair.1.to_css(dest));
|
value.to_css(dest)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -284,14 +285,14 @@
|
||||||
type ComputedValue = computed_value::T;
|
type ComputedValue = computed_value::T;
|
||||||
|
|
||||||
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
|
||||||
computed_value::T(self.0.iter().map(|entry| {
|
computed_value::T(self.0.iter().map(|&(ref name, ref value)| {
|
||||||
(entry.0.clone(), entry.1.to_computed_value(context))
|
(name.clone(), value.to_computed_value(context))
|
||||||
}).collect::<Vec<_>>())
|
}).collect::<Vec<_>>())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
||||||
SpecifiedValue(computed.0.iter().map(|entry| {
|
SpecifiedValue(computed.0.iter().map(|&(ref name, ref value)| {
|
||||||
(entry.0.clone(), specified::Integer::from_computed_value(&entry.1))
|
(name.clone(), specified::Integer::from_computed_value(&value))
|
||||||
}).collect::<Vec<_>>())
|
}).collect::<Vec<_>>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,14 +312,14 @@
|
||||||
return dest.write_str("none");
|
return dest.write_str("none");
|
||||||
}
|
}
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for pair in &self.0 {
|
for &(ref name, ref value) in &self.0 {
|
||||||
if !first {
|
if !first {
|
||||||
try!(dest.write_str(" "));
|
dest.write_str(" ")?;
|
||||||
}
|
}
|
||||||
first = false;
|
first = false;
|
||||||
try!(serialize_identifier(&pair.0, dest));
|
name.to_css(dest)?;
|
||||||
try!(dest.write_str(" "));
|
dest.write_str(" ")?;
|
||||||
try!(pair.1.to_css(dest));
|
value.to_css(dest)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -339,13 +340,7 @@
|
||||||
let mut counters = Vec::new();
|
let mut counters = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
let counter_name = match input.next() {
|
let counter_name = match input.next() {
|
||||||
Ok(Token::Ident(ident)) => {
|
Ok(Token::Ident(ident)) => CustomIdent::from_ident(ident, &["none"])?,
|
||||||
if CSSWideKeyword::from_ident(&ident).is_some() || ident.eq_ignore_ascii_case("none") {
|
|
||||||
// Don't accept CSS-wide keywords or none as the counter name.
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
(*ident).to_owned()
|
|
||||||
}
|
|
||||||
Ok(_) => return Err(()),
|
Ok(_) => return Err(()),
|
||||||
Err(_) => break,
|
Err(_) => break,
|
||||||
};
|
};
|
||||||
|
|
|
@ -444,6 +444,7 @@ impl CSSWideKeyword {
|
||||||
/// to a CSSWideKeyword.
|
/// to a CSSWideKeyword.
|
||||||
pub fn from_ident<'i>(ident: &Cow<'i, str>) -> Option<Self> {
|
pub fn from_ident<'i>(ident: &Cow<'i, str>) -> Option<Self> {
|
||||||
match_ignore_ascii_case! { ident,
|
match_ignore_ascii_case! { ident,
|
||||||
|
// If modifying this set of keyword, also update values::CustomIdent::from_ident
|
||||||
"initial" => Some(CSSWideKeyword::Initial),
|
"initial" => Some(CSSWideKeyword::Initial),
|
||||||
"inherit" => Some(CSSWideKeyword::Inherit),
|
"inherit" => Some(CSSWideKeyword::Inherit),
|
||||||
"unset" => Some(CSSWideKeyword::Unset),
|
"unset" => Some(CSSWideKeyword::Unset),
|
||||||
|
@ -1616,7 +1617,7 @@ pub mod style_structs {
|
||||||
/// Returns whether there is any animation specified with
|
/// Returns whether there is any animation specified with
|
||||||
/// animation-name other than `none`.
|
/// animation-name other than `none`.
|
||||||
pub fn specifies_animations(&self) -> bool {
|
pub fn specifies_animations(&self) -> bool {
|
||||||
self.animation_name_iter().any(|name| name.0 != atom!(""))
|
self.animation_name_iter().any(|name| name.0.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether there are any transitions specified.
|
/// Returns whether there are any transitions specified.
|
||||||
|
|
|
@ -7,8 +7,9 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use {Atom, Prefix, Namespace};
|
use {Atom, Prefix, Namespace};
|
||||||
|
use counter_style::{CounterStyleRule, parse_counter_style_name, parse_counter_style_body};
|
||||||
use cssparser::{AtRuleParser, Parser, QualifiedRuleParser};
|
use cssparser::{AtRuleParser, Parser, QualifiedRuleParser};
|
||||||
use cssparser::{AtRuleType, RuleListParser, Token, parse_one_rule};
|
use cssparser::{AtRuleType, RuleListParser, parse_one_rule};
|
||||||
use cssparser::ToCss as ParserToCss;
|
use cssparser::ToCss as ParserToCss;
|
||||||
use error_reporting::{ParseErrorReporter, NullReporter};
|
use error_reporting::{ParseErrorReporter, NullReporter};
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
|
@ -40,6 +41,7 @@ use str::starts_with_ignore_ascii_case;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
use stylist::FnvHashMap;
|
use stylist::FnvHashMap;
|
||||||
use supports::SupportsCondition;
|
use supports::SupportsCondition;
|
||||||
|
use values::{CustomIdent, KeyframesName};
|
||||||
use values::specified::url::SpecifiedUrl;
|
use values::specified::url::SpecifiedUrl;
|
||||||
use viewport::ViewportRule;
|
use viewport::ViewportRule;
|
||||||
|
|
||||||
|
@ -289,6 +291,7 @@ pub enum CssRule {
|
||||||
Style(Arc<Locked<StyleRule>>),
|
Style(Arc<Locked<StyleRule>>),
|
||||||
Media(Arc<Locked<MediaRule>>),
|
Media(Arc<Locked<MediaRule>>),
|
||||||
FontFace(Arc<Locked<FontFaceRule>>),
|
FontFace(Arc<Locked<FontFaceRule>>),
|
||||||
|
CounterStyle(Arc<Locked<CounterStyleRule>>),
|
||||||
Viewport(Arc<Locked<ViewportRule>>),
|
Viewport(Arc<Locked<ViewportRule>>),
|
||||||
Keyframes(Arc<Locked<KeyframesRule>>),
|
Keyframes(Arc<Locked<KeyframesRule>>),
|
||||||
Supports(Arc<Locked<SupportsRule>>),
|
Supports(Arc<Locked<SupportsRule>>),
|
||||||
|
@ -331,15 +334,16 @@ impl CssRule {
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub fn rule_type(&self) -> CssRuleType {
|
pub fn rule_type(&self) -> CssRuleType {
|
||||||
match *self {
|
match *self {
|
||||||
CssRule::Style(_) => CssRuleType::Style,
|
CssRule::Style(_) => CssRuleType::Style,
|
||||||
CssRule::Import(_) => CssRuleType::Import,
|
CssRule::Import(_) => CssRuleType::Import,
|
||||||
CssRule::Media(_) => CssRuleType::Media,
|
CssRule::Media(_) => CssRuleType::Media,
|
||||||
CssRule::FontFace(_) => CssRuleType::FontFace,
|
CssRule::FontFace(_) => CssRuleType::FontFace,
|
||||||
|
CssRule::CounterStyle(_) => CssRuleType::CounterStyle,
|
||||||
CssRule::Keyframes(_) => CssRuleType::Keyframes,
|
CssRule::Keyframes(_) => CssRuleType::Keyframes,
|
||||||
CssRule::Namespace(_) => CssRuleType::Namespace,
|
CssRule::Namespace(_) => CssRuleType::Namespace,
|
||||||
CssRule::Viewport(_) => CssRuleType::Viewport,
|
CssRule::Viewport(_) => CssRuleType::Viewport,
|
||||||
CssRule::Supports(_) => CssRuleType::Supports,
|
CssRule::Supports(_) => CssRuleType::Supports,
|
||||||
CssRule::Page(_) => CssRuleType::Page,
|
CssRule::Page(_) => CssRuleType::Page,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,6 +376,7 @@ impl CssRule {
|
||||||
CssRule::Namespace(_) |
|
CssRule::Namespace(_) |
|
||||||
CssRule::Style(_) |
|
CssRule::Style(_) |
|
||||||
CssRule::FontFace(_) |
|
CssRule::FontFace(_) |
|
||||||
|
CssRule::CounterStyle(_) |
|
||||||
CssRule::Viewport(_) |
|
CssRule::Viewport(_) |
|
||||||
CssRule::Keyframes(_) |
|
CssRule::Keyframes(_) |
|
||||||
CssRule::Page(_) => {
|
CssRule::Page(_) => {
|
||||||
|
@ -445,6 +450,7 @@ impl ToCssWithGuard for CssRule {
|
||||||
CssRule::Import(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
CssRule::Import(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
||||||
CssRule::Style(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
CssRule::Style(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
||||||
CssRule::FontFace(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
CssRule::FontFace(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
||||||
|
CssRule::CounterStyle(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
||||||
CssRule::Viewport(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
CssRule::Viewport(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
||||||
CssRule::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
CssRule::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
||||||
CssRule::Media(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
CssRule::Media(ref lock) => lock.read_with(guard).to_css(guard, dest),
|
||||||
|
@ -513,7 +519,7 @@ impl ToCssWithGuard for ImportRule {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct KeyframesRule {
|
pub struct KeyframesRule {
|
||||||
/// The name of the current animation.
|
/// The name of the current animation.
|
||||||
pub name: Atom,
|
pub name: KeyframesName,
|
||||||
/// The keyframes specified for this CSS rule.
|
/// The keyframes specified for this CSS rule.
|
||||||
pub keyframes: Vec<Arc<Locked<Keyframe>>>,
|
pub keyframes: Vec<Arc<Locked<Keyframe>>>,
|
||||||
/// Vendor prefix type the @keyframes has.
|
/// Vendor prefix type the @keyframes has.
|
||||||
|
@ -525,7 +531,7 @@ impl ToCssWithGuard for KeyframesRule {
|
||||||
fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
|
fn to_css<W>(&self, guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
|
||||||
where W: fmt::Write {
|
where W: fmt::Write {
|
||||||
try!(dest.write_str("@keyframes "));
|
try!(dest.write_str("@keyframes "));
|
||||||
try!(dest.write_str(&*self.name.to_string()));
|
try!(self.name.to_css(dest));
|
||||||
try!(dest.write_str(" { "));
|
try!(dest.write_str(" { "));
|
||||||
let iter = self.keyframes.iter();
|
let iter = self.keyframes.iter();
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
|
@ -834,6 +840,7 @@ rule_filter! {
|
||||||
effective_style_rules(Style => StyleRule),
|
effective_style_rules(Style => StyleRule),
|
||||||
effective_media_rules(Media => MediaRule),
|
effective_media_rules(Media => MediaRule),
|
||||||
effective_font_face_rules(FontFace => FontFaceRule),
|
effective_font_face_rules(FontFace => FontFaceRule),
|
||||||
|
effective_counter_style_rules(CounterStyle => CounterStyleRule),
|
||||||
effective_viewport_rules(Viewport => ViewportRule),
|
effective_viewport_rules(Viewport => ViewportRule),
|
||||||
effective_keyframes_rules(Keyframes => KeyframesRule),
|
effective_keyframes_rules(Keyframes => KeyframesRule),
|
||||||
effective_supports_rules(Supports => SupportsRule),
|
effective_supports_rules(Supports => SupportsRule),
|
||||||
|
@ -915,6 +922,8 @@ pub enum VendorPrefix {
|
||||||
enum AtRulePrelude {
|
enum AtRulePrelude {
|
||||||
/// A @font-face rule prelude.
|
/// A @font-face rule prelude.
|
||||||
FontFace,
|
FontFace,
|
||||||
|
/// A @counter-style rule prelude, with its counter style name.
|
||||||
|
CounterStyle(CustomIdent),
|
||||||
/// A @media rule prelude, with its media queries.
|
/// A @media rule prelude, with its media queries.
|
||||||
Media(Arc<Locked<MediaList>>),
|
Media(Arc<Locked<MediaList>>),
|
||||||
/// An @supports rule, with its conditional
|
/// An @supports rule, with its conditional
|
||||||
|
@ -922,7 +931,7 @@ enum AtRulePrelude {
|
||||||
/// A @viewport rule prelude.
|
/// A @viewport rule prelude.
|
||||||
Viewport,
|
Viewport,
|
||||||
/// A @keyframes rule, with its animation name and vendor prefix if exists.
|
/// A @keyframes rule, with its animation name and vendor prefix if exists.
|
||||||
Keyframes(Atom, Option<VendorPrefix>),
|
Keyframes(KeyframesName, Option<VendorPrefix>),
|
||||||
/// A @page rule prelude.
|
/// A @page rule prelude.
|
||||||
Page,
|
Page,
|
||||||
}
|
}
|
||||||
|
@ -1102,6 +1111,20 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
|
||||||
"font-face" => {
|
"font-face" => {
|
||||||
Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace))
|
Ok(AtRuleType::WithBlock(AtRulePrelude::FontFace))
|
||||||
},
|
},
|
||||||
|
"counter-style" => {
|
||||||
|
if !cfg!(feature = "gecko") {
|
||||||
|
// Support for this rule is not fully implemented in Servo yet.
|
||||||
|
return Err(())
|
||||||
|
}
|
||||||
|
let name = parse_counter_style_name(input)?;
|
||||||
|
// ASCII-case-insensitive matches for "decimal" are already lower-cased
|
||||||
|
// by `parse_counter_style_name`, so we can use == here.
|
||||||
|
// FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1359323 use atom!("decimal")
|
||||||
|
if name.0 == Atom::from("decimal") {
|
||||||
|
return Err(())
|
||||||
|
}
|
||||||
|
Ok(AtRuleType::WithBlock(AtRulePrelude::CounterStyle(name)))
|
||||||
|
},
|
||||||
"viewport" => {
|
"viewport" => {
|
||||||
if is_viewport_enabled() {
|
if is_viewport_enabled() {
|
||||||
Ok(AtRuleType::WithBlock(AtRulePrelude::Viewport))
|
Ok(AtRuleType::WithBlock(AtRulePrelude::Viewport))
|
||||||
|
@ -1122,13 +1145,9 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
|
||||||
// Servo should not support @-moz-keyframes.
|
// Servo should not support @-moz-keyframes.
|
||||||
return Err(())
|
return Err(())
|
||||||
}
|
}
|
||||||
let name = match input.next() {
|
let name = KeyframesName::parse(self.context, input)?;
|
||||||
Ok(Token::Ident(ref value)) if value != "none" => Atom::from(&**value),
|
|
||||||
Ok(Token::QuotedString(value)) => Atom::from(&*value),
|
|
||||||
_ => return Err(())
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(Atom::from(name), prefix)))
|
Ok(AtRuleType::WithBlock(AtRulePrelude::Keyframes(name, prefix)))
|
||||||
},
|
},
|
||||||
"page" => {
|
"page" => {
|
||||||
if cfg!(feature = "gecko") {
|
if cfg!(feature = "gecko") {
|
||||||
|
@ -1148,6 +1167,11 @@ impl<'a, 'b> AtRuleParser for NestedRuleParser<'a, 'b> {
|
||||||
Ok(CssRule::FontFace(Arc::new(self.shared_lock.wrap(
|
Ok(CssRule::FontFace(Arc::new(self.shared_lock.wrap(
|
||||||
parse_font_face_block(&context, input).into()))))
|
parse_font_face_block(&context, input).into()))))
|
||||||
}
|
}
|
||||||
|
AtRulePrelude::CounterStyle(name) => {
|
||||||
|
let context = ParserContext::new_with_rule_type(self.context, Some(CssRuleType::CounterStyle));
|
||||||
|
Ok(CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(
|
||||||
|
parse_counter_style_body(name, &context, input)?))))
|
||||||
|
}
|
||||||
AtRulePrelude::Media(media_queries) => {
|
AtRulePrelude::Media(media_queries) => {
|
||||||
Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
|
Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
|
||||||
media_queries: media_queries,
|
media_queries: media_queries,
|
||||||
|
|
|
@ -349,13 +349,13 @@ impl Stylist {
|
||||||
|
|
||||||
// Don't let a prefixed keyframes animation override a non-prefixed one.
|
// Don't let a prefixed keyframes animation override a non-prefixed one.
|
||||||
let needs_insertion = keyframes_rule.vendor_prefix.is_none() ||
|
let needs_insertion = keyframes_rule.vendor_prefix.is_none() ||
|
||||||
self.animations.get(&keyframes_rule.name).map_or(true, |rule|
|
self.animations.get(keyframes_rule.name.as_atom()).map_or(true, |rule|
|
||||||
rule.vendor_prefix.is_some());
|
rule.vendor_prefix.is_some());
|
||||||
if needs_insertion {
|
if needs_insertion {
|
||||||
let animation = KeyframesAnimation::from_keyframes(
|
let animation = KeyframesAnimation::from_keyframes(
|
||||||
&keyframes_rule.keyframes, keyframes_rule.vendor_prefix.clone(), guard);
|
&keyframes_rule.keyframes, keyframes_rule.vendor_prefix.clone(), guard);
|
||||||
debug!("Found valid keyframe animation: {:?}", animation);
|
debug!("Found valid keyframe animation: {:?}", animation);
|
||||||
self.animations.insert(keyframes_rule.name.clone(), animation);
|
self.animations.insert(keyframes_rule.name.as_atom().clone(), animation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CssRule::FontFace(ref rule) => {
|
CssRule::FontFace(ref rule) => {
|
||||||
|
|
|
@ -8,9 +8,13 @@
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
pub use cssparser::{RGBA, Parser};
|
use Atom;
|
||||||
|
pub use cssparser::{RGBA, Token, Parser, serialize_identifier, serialize_string};
|
||||||
use parser::{Parse, ParserContext};
|
use parser::{Parse, ParserContext};
|
||||||
|
use std::ascii::AsciiExt;
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
|
use std::hash;
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
|
|
||||||
macro_rules! define_numbered_css_keyword_enum {
|
macro_rules! define_numbered_css_keyword_enum {
|
||||||
|
@ -211,6 +215,93 @@ impl<A: ToComputedValue, B: ToComputedValue> ToComputedValue for Either<A, B> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-values-4/#custom-idents
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub struct CustomIdent(pub Atom);
|
||||||
|
|
||||||
|
impl CustomIdent {
|
||||||
|
/// Parse an already-tokenizer identifier
|
||||||
|
pub fn from_ident(ident: Cow<str>, excluding: &[&str]) -> Result<Self, ()> {
|
||||||
|
match_ignore_ascii_case! { &ident,
|
||||||
|
"initial" | "inherit" | "unset" | "default" => return Err(()),
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
if excluding.iter().any(|s| ident.eq_ignore_ascii_case(s)) {
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
Ok(CustomIdent(ident.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for CustomIdent {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
serialize_identifier(&self.0.to_string(), dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-animations/#typedef-keyframes-name
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub enum KeyframesName {
|
||||||
|
/// <custom-ident>
|
||||||
|
Ident(CustomIdent),
|
||||||
|
/// <string>
|
||||||
|
QuotedString(Atom),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyframesName {
|
||||||
|
/// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name
|
||||||
|
pub fn from_ident(value: String) -> Self {
|
||||||
|
match CustomIdent::from_ident((&*value).into(), &["none"]) {
|
||||||
|
Ok(ident) => KeyframesName::Ident(ident),
|
||||||
|
Err(()) => KeyframesName::QuotedString(value.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The name as an Atom
|
||||||
|
pub fn as_atom(&self) -> &Atom {
|
||||||
|
match *self {
|
||||||
|
KeyframesName::Ident(ref ident) => &ident.0,
|
||||||
|
KeyframesName::QuotedString(ref atom) => atom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for KeyframesName {}
|
||||||
|
|
||||||
|
impl PartialEq for KeyframesName {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.as_atom() == other.as_atom()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl hash::Hash for KeyframesName {
|
||||||
|
fn hash<H>(&self, state: &mut H) where H: hash::Hasher {
|
||||||
|
self.as_atom().hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for KeyframesName {
|
||||||
|
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
match input.next() {
|
||||||
|
Ok(Token::Ident(s)) => Ok(KeyframesName::Ident(CustomIdent::from_ident(s, &["none"])?)),
|
||||||
|
Ok(Token::QuotedString(s)) => Ok(KeyframesName::QuotedString(s.into())),
|
||||||
|
_ => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for KeyframesName {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
match *self {
|
||||||
|
KeyframesName::Ident(ref ident) => ident.to_css(dest),
|
||||||
|
KeyframesName::QuotedString(ref atom) => serialize_string(&atom.to_string(), dest),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A type for possible values for min- and max- flavors of width,
|
// A type for possible values for min- and max- flavors of width,
|
||||||
// height, block-size, and inline-size.
|
// height, block-size, and inline-size.
|
||||||
define_css_keyword_enum!(ExtremumLength:
|
define_css_keyword_enum!(ExtremumLength:
|
||||||
|
|
|
@ -13,7 +13,7 @@ extern crate parking_lot;
|
||||||
extern crate rayon;
|
extern crate rayon;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
extern crate selectors;
|
extern crate selectors;
|
||||||
#[macro_use] extern crate servo_atoms;
|
extern crate servo_atoms;
|
||||||
extern crate servo_config;
|
extern crate servo_config;
|
||||||
extern crate servo_url;
|
extern crate servo_url;
|
||||||
extern crate style;
|
extern crate style;
|
||||||
|
|
|
@ -7,6 +7,7 @@ use servo_atoms::Atom;
|
||||||
use style::parser::Parse;
|
use style::parser::Parse;
|
||||||
use style::properties::longhands::animation_iteration_count::single_value::computed_value::T as AnimationIterationCount;
|
use style::properties::longhands::animation_iteration_count::single_value::computed_value::T as AnimationIterationCount;
|
||||||
use style::properties::longhands::animation_name;
|
use style::properties::longhands::animation_name;
|
||||||
|
use style::values::{KeyframesName, CustomIdent};
|
||||||
use style_traits::ToCss;
|
use style_traits::ToCss;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -14,13 +15,13 @@ fn test_animation_name() {
|
||||||
use self::animation_name::single_value::SpecifiedValue as SingleValue;
|
use self::animation_name::single_value::SpecifiedValue as SingleValue;
|
||||||
let other_name = Atom::from("other-name");
|
let other_name = Atom::from("other-name");
|
||||||
assert_eq!(parse_longhand!(animation_name, "none"),
|
assert_eq!(parse_longhand!(animation_name, "none"),
|
||||||
animation_name::SpecifiedValue(vec![SingleValue(atom!(""))]));
|
animation_name::SpecifiedValue(vec![SingleValue(None)]));
|
||||||
assert_eq!(parse_longhand!(animation_name, "other-name, none, 'other-name', \"other-name\""),
|
assert_eq!(parse_longhand!(animation_name, "other-name, none, 'other-name', \"other-name\""),
|
||||||
animation_name::SpecifiedValue(
|
animation_name::SpecifiedValue(
|
||||||
vec![SingleValue(other_name.clone()),
|
vec![SingleValue(Some(KeyframesName::Ident(CustomIdent(other_name.clone())))),
|
||||||
SingleValue(atom!("")),
|
SingleValue(None),
|
||||||
SingleValue(other_name.clone()),
|
SingleValue(Some(KeyframesName::QuotedString(other_name.clone()))),
|
||||||
SingleValue(other_name.clone())]));
|
SingleValue(Some(KeyframesName::QuotedString(other_name.clone())))]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -8,6 +8,7 @@ use style::properties::{PropertyDeclaration, Importance, PropertyId};
|
||||||
use style::properties::longhands::outline_color::computed_value::T as ComputedColor;
|
use style::properties::longhands::outline_color::computed_value::T as ComputedColor;
|
||||||
use style::properties::parse_property_declaration_list;
|
use style::properties::parse_property_declaration_list;
|
||||||
use style::values::{RGBA, Auto};
|
use style::values::{RGBA, Auto};
|
||||||
|
use style::values::CustomIdent;
|
||||||
use style::values::specified::{BorderStyle, BorderWidth, CSSColor, Length, NoCalcLength};
|
use style::values::specified::{BorderStyle, BorderWidth, CSSColor, Length, NoCalcLength};
|
||||||
use style::values::specified::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrAutoOrContent};
|
use style::values::specified::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrAutoOrContent};
|
||||||
use style::values::specified::url::SpecifiedUrl;
|
use style::values::specified::url::SpecifiedUrl;
|
||||||
|
@ -1259,8 +1260,8 @@ mod shorthand_serialization {
|
||||||
fn counter_increment_with_properties_should_serialize_correctly() {
|
fn counter_increment_with_properties_should_serialize_correctly() {
|
||||||
let mut properties = Vec::new();
|
let mut properties = Vec::new();
|
||||||
|
|
||||||
properties.push(("counter1".to_owned(), Integer::new(1)));
|
properties.push((CustomIdent("counter1".into()), Integer::new(1)));
|
||||||
properties.push(("counter2".to_owned(), Integer::new(-4)));
|
properties.push((CustomIdent("counter2".into()), Integer::new(-4)));
|
||||||
|
|
||||||
let counter_increment = CounterIncrement(properties);
|
let counter_increment = CounterIncrement(properties);
|
||||||
let counter_increment_css = "counter1 1 counter2 -4";
|
let counter_increment_css = "counter1 1 counter2 -4";
|
||||||
|
|
|
@ -23,6 +23,7 @@ use style::properties::longhands::animation_play_state;
|
||||||
use style::shared_lock::SharedRwLock;
|
use style::shared_lock::SharedRwLock;
|
||||||
use style::stylesheets::{Origin, Namespaces};
|
use style::stylesheets::{Origin, Namespaces};
|
||||||
use style::stylesheets::{Stylesheet, NamespaceRule, CssRule, CssRules, StyleRule, KeyframesRule};
|
use style::stylesheets::{Stylesheet, NamespaceRule, CssRule, CssRules, StyleRule, KeyframesRule};
|
||||||
|
use style::values::{KeyframesName, CustomIdent};
|
||||||
use style::values::specified::{LengthOrPercentageOrAuto, Percentage};
|
use style::values::specified::{LengthOrPercentageOrAuto, Percentage};
|
||||||
|
|
||||||
pub fn block_from<I>(iterable: I) -> PropertyDeclarationBlock
|
pub fn block_from<I>(iterable: I) -> PropertyDeclarationBlock
|
||||||
|
@ -221,7 +222,7 @@ fn test_parse_stylesheet() {
|
||||||
]))),
|
]))),
|
||||||
}))),
|
}))),
|
||||||
CssRule::Keyframes(Arc::new(stylesheet.shared_lock.wrap(KeyframesRule {
|
CssRule::Keyframes(Arc::new(stylesheet.shared_lock.wrap(KeyframesRule {
|
||||||
name: "foo".into(),
|
name: KeyframesName::Ident(CustomIdent("foo".into())),
|
||||||
keyframes: vec![
|
keyframes: vec![
|
||||||
Arc::new(stylesheet.shared_lock.wrap(Keyframe {
|
Arc::new(stylesheet.shared_lock.wrap(Keyframe {
|
||||||
selector: KeyframeSelector::new_for_unit_testing(
|
selector: KeyframeSelector::new_for_unit_testing(
|
||||||
|
|
|
@ -553680,7 +553680,7 @@
|
||||||
"testharness"
|
"testharness"
|
||||||
],
|
],
|
||||||
"cssom/CSSKeyframesRule.html": [
|
"cssom/CSSKeyframesRule.html": [
|
||||||
"3efb8e5cef257a0b433192742d526709357b24c7",
|
"bca997a63c1389ef6d14aac2f32ab770fbd15ec4",
|
||||||
"testharness"
|
"testharness"
|
||||||
],
|
],
|
||||||
"cssom/CSSNamespaceRule.html": [
|
"cssom/CSSNamespaceRule.html": [
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
0% { top: 0px; }
|
0% { top: 0px; }
|
||||||
100% { top: 200px; }
|
100% { top: 200px; }
|
||||||
}
|
}
|
||||||
|
@keyframes empty {}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -54,16 +55,18 @@
|
||||||
assert_equals(keyframe.cssRules[2].cssText, "0% { top: 50px; }", "CSSKeyframesRule cssRule cssText attribute after deleteRule function");
|
assert_equals(keyframe.cssRules[2].cssText, "0% { top: 50px; }", "CSSKeyframesRule cssRule cssText attribute after deleteRule function");
|
||||||
assert_equals(keyframe.cssRules[3], undefined, "CSSKeyframesRule cssRule cssText attribute after deleteRule function");
|
assert_equals(keyframe.cssRules[3], undefined, "CSSKeyframesRule cssRule cssText attribute after deleteRule function");
|
||||||
|
|
||||||
keyframe.name = "bar";
|
var empty = document.styleSheets[0].cssRules[1];
|
||||||
assert_equals(keyframe.name, "bar", "CSSKeyframesRule name setter");
|
empty.name = "bar";
|
||||||
|
assert_equals(empty.name, "bar", "CSSKeyframesRule name setter");
|
||||||
|
assert_equals(empty.cssText.replace(/\s/g, ""), "@keyframesbar{}", "CSSKeyframesRule cssText attribute");
|
||||||
|
|
||||||
assert_throws("SyntaxError",
|
empty.name = "initial";
|
||||||
function () { keyframe.name = "initial"; },
|
assert_equals(empty.name, "initial", "CSSKeyframesRule name setter, CSS-wide keyword");
|
||||||
"CSSKeyframesRule name setter on invalid keyword.");
|
assert_equals(empty.cssText.replace(/\s/g, ""), "@keyframes\"initial\"{}", "CSSKeyframesRule cssText attribute with CSS-wide keyword name");
|
||||||
|
|
||||||
assert_throws("SyntaxError",
|
empty.name = "none";
|
||||||
function () { keyframe.name = "none"; },
|
assert_equals(empty.name, "none", "CSSKeyframesRule name setter, 'none'");
|
||||||
"CSSKeyframesRule name setter on invalid keyword.");
|
assert_equals(empty.cssText.replace(/\s/g, ""), "@keyframes\"none\"{}", "CSSKeyframesRule cssText attribute with 'none' name");
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue