From b20bbea033596a1f20a32cb8ecff7c8c6c354294 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Mon, 20 Aug 2018 05:34:16 +0000 Subject: [PATCH 01/34] style: Replace NormalDeclarationIterator return type with `impl Trait`. Differential Revision: https://phabricator.services.mozilla.com/D3747 --- .../style/properties/declaration_block.rs | 65 ++++--------------- 1 file changed, 13 insertions(+), 52 deletions(-) diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs index ad5aa75e8f2..7d1e2f3aa32 100644 --- a/components/style/properties/declaration_block.rs +++ b/components/style/properties/declaration_block.rs @@ -138,56 +138,9 @@ impl<'a> DoubleEndedIterator for DeclarationImportanceIterator<'a> { } } -/// Iterator over `PropertyDeclaration` for Importance::Normal. -/// -/// TODO(emilio): This should be replaced by `impl Trait`, returning a -/// filter()ed iterator when available instead, and all the boilerplate below -/// should go. -pub struct NormalDeclarationIterator<'a>(DeclarationImportanceIterator<'a>); - -impl<'a> NormalDeclarationIterator<'a> { - #[inline] - fn new(declarations: &'a [PropertyDeclaration], important: &'a SmallBitVec) -> Self { - NormalDeclarationIterator( - DeclarationImportanceIterator::new(declarations, important) - ) - } -} - -impl<'a> Iterator for NormalDeclarationIterator<'a> { - type Item = &'a PropertyDeclaration; - - #[inline] - fn next(&mut self) -> Option { - loop { - let (decl, importance) = self.0.iter.next()?; - if !importance { - return Some(decl); - } - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.0.iter.size_hint() - } -} - -impl<'a> DoubleEndedIterator for NormalDeclarationIterator<'a> { - #[inline] - fn next_back(&mut self) -> Option { - loop { - let (decl, importance) = self.0.iter.next_back()?; - if !importance { - return Some(decl); - } - } - } -} - /// Iterator for AnimationValue to be generated from PropertyDeclarationBlock. pub struct AnimationValueIterator<'a, 'cx, 'cx_a:'cx> { - iter: NormalDeclarationIterator<'a>, + iter: DeclarationImportanceIterator<'a>, context: &'cx mut Context<'cx_a>, default_values: &'a ComputedValues, /// Custom properties in a keyframe if exists. @@ -202,7 +155,7 @@ impl<'a, 'cx, 'cx_a:'cx> AnimationValueIterator<'a, 'cx, 'cx_a> { extra_custom_properties: Option<&'a Arc<::custom_properties::CustomPropertiesMap>>, ) -> AnimationValueIterator<'a, 'cx, 'cx_a> { AnimationValueIterator { - iter: declarations.normal_declaration_iter(), + iter: declarations.declaration_importance_iter(), context, default_values, extra_custom_properties, @@ -215,7 +168,11 @@ impl<'a, 'cx, 'cx_a:'cx> Iterator for AnimationValueIterator<'a, 'cx, 'cx_a> { #[inline] fn next(&mut self) -> Option { loop { - let decl = self.iter.next()?; + let (decl, importance) = self.iter.next()?; + + if importance.important() { + continue; + } let animation = AnimationValue::from_declaration( decl, @@ -287,8 +244,12 @@ impl PropertyDeclarationBlock { /// Iterate over `PropertyDeclaration` for Importance::Normal #[inline] - pub fn normal_declaration_iter(&self) -> NormalDeclarationIterator { - NormalDeclarationIterator::new(&self.declarations, &self.declarations_importance) + pub fn normal_declaration_iter<'a>( + &'a self, + ) -> impl DoubleEndedIterator { + self.declaration_importance_iter() + .filter(|(_, importance)| !importance.important()) + .map(|(declaration, _)| declaration) } /// Return an iterator of (AnimatableLonghand, AnimationValue). From 249b865eb8e19157cbf49aa7d81abb54ba98f6a6 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Mon, 20 Aug 2018 04:11:37 +0000 Subject: [PATCH 02/34] style: Simplify PropertyDeclarationBlock::get a little. Depends On D3747 Differential Revision: https://phabricator.services.mozilla.com/D3748 --- components/style/properties/declaration_block.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs index 7d1e2f3aa32..38af8f2fa43 100644 --- a/components/style/properties/declaration_block.rs +++ b/components/style/properties/declaration_block.rs @@ -305,14 +305,8 @@ impl PropertyDeclarationBlock { } } - self.declarations.iter().enumerate().find(|&(_, decl)| decl.id() == property).map(|(i, decl)| { - let importance = if self.declarations_importance[i] { - Importance::Important - } else { - Importance::Normal - }; - (decl, importance) - }) + self.declaration_importance_iter() + .find(|(declaration, _)| declaration.id() == property) } fn shorthand_to_css( From dce2e2927f79374a7515763eb090efd9dee77580 Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Wed, 22 Aug 2018 01:24:13 +0000 Subject: [PATCH 03/34] style: Define offset-path and implement it in style system. Define OffsetPath & SVGPathData on the servo-side, and StyleMotion & StyleSVGPath on the gecko-side. We parse the SVG Path string into a vector of PathCommand. To build the gfx::Path, we will convert it into gfx::Path later in a different patch. The basic flow is: - Parse SVG Path String into SVGPathData (in Rust). - Use cbindgen to make sure the layout of PathCommand and StylePathCommand, and then set the Box[PathCommand] into nsTArray. - Try to convert nsTArray into gfx::Path. (This part will be implemented in a different patch.) Finally, we use the gfx::Path to create a motion path transform. The layout implementation is in the later patch. Depends on D2962 Differential Revision: https://phabricator.services.mozilla.com/D2963 --- components/style/cbindgen.toml | 5 +- components/style/gecko/conversions.rs | 25 + components/style/properties/gecko.mako.rs | 47 +- .../style/properties/longhands/box.mako.rs | 11 + components/style/values/computed/mod.rs | 2 + components/style/values/computed/motion.rs | 10 + components/style/values/specified/mod.rs | 2 + components/style/values/specified/motion.rs | 643 ++++++++++++++++++ 8 files changed, 742 insertions(+), 3 deletions(-) create mode 100644 components/style/values/computed/motion.rs create mode 100644 components/style/values/specified/motion.rs diff --git a/components/style/cbindgen.toml b/components/style/cbindgen.toml index 5a0e5e19ea7..c0eeee9b968 100644 --- a/components/style/cbindgen.toml +++ b/components/style/cbindgen.toml @@ -7,6 +7,7 @@ autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated usi * a. Alternatively, you can clone `https://github.com/eqrion/cbindgen` and use a tagged release * 2. Run `rustup run nightly cbindgen toolkit/library/rust/ --lockfile Cargo.lock --crate style -o layout/style/ServoStyleConsts.h` */""" +include_guard = "mozilla_ServoStyleConsts_h" include_version = true braces = "SameLine" line_length = 80 @@ -22,5 +23,5 @@ derive_helper_methods = true [export] prefix = "Style" -include = ["StyleDisplay", "StyleAppearance", "StyleDisplayMode"] -item_types = ["enums"] +include = ["StyleDisplay", "StyleAppearance", "StyleDisplayMode", "StylePathCommand"] +item_types = ["enums", "structs", "typedefs"] diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 3b1f31a1b08..154ad95bc43 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -638,6 +638,7 @@ pub mod basic_shape { use values::computed::basic_shape::{BasicShape, ClippingShape, FloatAreaShape, ShapeRadius}; use values::computed::border::{BorderCornerRadius, BorderRadius}; use values::computed::length::LengthOrPercentage; + use values::computed::motion::OffsetPath; use values::computed::position; use values::computed::url::ComputedUrl; use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon}; @@ -669,6 +670,7 @@ pub mod basic_shape { Some(ShapeSource::Shape(shape, reference_box)) }, StyleShapeSourceType::URL | StyleShapeSourceType::Image => None, + StyleShapeSourceType::Path => None, } } } @@ -710,6 +712,29 @@ pub mod basic_shape { } } + impl<'a> From<&'a StyleShapeSource> for OffsetPath { + fn from(other: &'a StyleShapeSource) -> Self { + use gecko_bindings::structs::StylePathCommand; + use values::specified::motion::{SVGPathData, PathCommand}; + match other.mType { + StyleShapeSourceType::Path => { + let gecko_path = unsafe { &*other.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; + let result: Vec = + gecko_path.mPath.iter().map(|gecko: &StylePathCommand| { + // unsafe: cbindgen ensures the representation is the same. + unsafe{ ::std::mem::transmute(*gecko) } + }).collect(); + OffsetPath::Path(SVGPathData::new(result.into_boxed_slice())) + }, + StyleShapeSourceType::None => OffsetPath::none(), + StyleShapeSourceType::Shape | + StyleShapeSourceType::Box | + StyleShapeSourceType::URL | + StyleShapeSourceType::Image => unreachable!("Unsupported offset-path type"), + } + } + } + impl<'a> From<&'a StyleBasicShape> for BasicShape { fn from(other: &'a StyleBasicShape) -> Self { match other.mType { diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 33a66498b2d..596dc29fb19 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -3053,7 +3053,7 @@ fn static_assert() { scroll-snap-points-x scroll-snap-points-y scroll-snap-type-x scroll-snap-type-y scroll-snap-coordinate perspective-origin -moz-binding will-change - overscroll-behavior-x overscroll-behavior-y + offset-path overscroll-behavior-x overscroll-behavior-y overflow-clip-box-inline overflow-clip-box-block perspective-origin -moz-binding will-change shape-outside contain touch-action translate @@ -3681,6 +3681,51 @@ fn static_assert() { ${impl_simple_copy("contain", "mContain")} ${impl_simple_type_with_conversion("touch_action")} + + pub fn set_offset_path(&mut self, v: longhands::offset_path::computed_value::T) { + use gecko_bindings::bindings::{Gecko_NewStyleMotion, Gecko_NewStyleSVGPath}; + use gecko_bindings::bindings::Gecko_SetStyleMotion; + use gecko_bindings::structs::StyleShapeSourceType; + use values::specified::OffsetPath; + + let motion = unsafe { Gecko_NewStyleMotion().as_mut().unwrap() }; + match v { + OffsetPath::None => motion.mOffsetPath.mType = StyleShapeSourceType::None, + OffsetPath::Path(servo_path) => { + motion.mOffsetPath.mType = StyleShapeSourceType::Path; + let gecko_path = unsafe { + let ref mut source = motion.mOffsetPath; + Gecko_NewStyleSVGPath(source); + &mut source.__bindgen_anon_1.mSVGPath.as_mut().mPtr.as_mut().unwrap().mPath + }; + unsafe { gecko_path.set_len(servo_path.commands().len() as u32) }; + debug_assert_eq!(gecko_path.len(), servo_path.commands().len()); + for (servo, gecko) in servo_path.commands().iter().zip(gecko_path.iter_mut()) { + // unsafe: cbindgen ensures the representation is the same. + *gecko = unsafe { transmute(*servo) }; + } + }, + } + unsafe { Gecko_SetStyleMotion(&mut self.gecko.mMotion, motion) }; + } + + pub fn clone_offset_path(&self) -> longhands::offset_path::computed_value::T { + use values::specified::OffsetPath; + match unsafe { self.gecko.mMotion.mPtr.as_ref() } { + None => OffsetPath::none(), + Some(v) => (&v.mOffsetPath).into() + } + } + + pub fn copy_offset_path_from(&mut self, other: &Self) { + use gecko_bindings::bindings::Gecko_CopyStyleMotions; + unsafe { Gecko_CopyStyleMotions(&mut self.gecko.mMotion, other.gecko.mMotion.mPtr) }; + } + + pub fn reset_offset_path(&mut self, other: &Self) { + self.copy_offset_path_from(other); + } + <%def name="simple_image_array_property(name, shorthand, field_name)"> diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index 6b6bcf9cbe3..4ef45a502bc 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -356,6 +356,17 @@ ${helpers.predefined_type( servo_restyle_damage="reflow_out_of_flow" )} +// Motion Path Module Level 1 +${helpers.predefined_type( + "offset-path", + "OffsetPath", + "computed::OffsetPath::none()", + animation_value_type="none", + gecko_pref="layout.css.motion-path.enabled", + flags="CREATES_STACKING_CONTEXT FIXPOS_CB", + spec="https://drafts.fxtf.org/motion-1/#offset-path-property" +)} + // CSSOM View Module // https://www.w3.org/TR/cssom-view-1/ ${helpers.single_keyword("scroll-behavior", diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 9a6b5fd76b5..d8cc938c1f3 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -65,6 +65,7 @@ pub use self::length::{NonNegativeLengthOrPercentage, NonNegativeLengthOrPercent pub use self::list::Quotes; #[cfg(feature = "gecko")] pub use self::list::ListStyleType; +pub use self::motion::OffsetPath; pub use self::outline::OutlineStyle; pub use self::percentage::{Percentage, NonNegativePercentage}; pub use self::position::{GridAutoFlow, GridTemplateAreas, Position, ZIndex}; @@ -100,6 +101,7 @@ pub mod gecko; pub mod image; pub mod length; pub mod list; +pub mod motion; pub mod outline; pub mod percentage; pub mod position; diff --git a/components/style/values/computed/motion.rs b/components/style/values/computed/motion.rs new file mode 100644 index 00000000000..935ba57f845 --- /dev/null +++ b/components/style/values/computed/motion.rs @@ -0,0 +1,10 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Computed types for CSS values that are related to motion path. + +/// A computed offset-path. The computed value is as specified value. +/// +/// https://drafts.fxtf.org/motion-1/#offset-path-property +pub use values::specified::motion::OffsetPath as OffsetPath; diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 2da4c7e93d3..1bd52e916d0 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -58,6 +58,7 @@ pub use self::length::{NonNegativeLengthOrPercentage, NonNegativeLengthOrPercent pub use self::list::Quotes; #[cfg(feature = "gecko")] pub use self::list::ListStyleType; +pub use self::motion::OffsetPath; pub use self::outline::OutlineStyle; pub use self::rect::LengthOrNumberRect; pub use self::resolution::Resolution; @@ -101,6 +102,7 @@ pub mod image; pub mod length; pub mod list; pub mod outline; +pub mod motion; pub mod percentage; pub mod position; pub mod rect; diff --git a/components/style/values/specified/motion.rs b/components/style/values/specified/motion.rs new file mode 100644 index 00000000000..8b370369b85 --- /dev/null +++ b/components/style/values/specified/motion.rs @@ -0,0 +1,643 @@ +/* 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/. */ + +//! Specified types for CSS values that are related to motion path. + +use cssparser::Parser; +use parser::{Parse, ParserContext}; +use std::fmt::{self, Write}; +use std::iter::Peekable; +use std::str::Chars; +use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; +use style_traits::values::SequenceWriter; +use values::CSSFloat; + +/// The offset-path value. +/// +/// https://drafts.fxtf.org/motion-1/#offset-path-property +#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)] +pub enum OffsetPath { + // We could merge SVGPathData into ShapeSource, so we could reuse them. However, + // we don't want to support other value for offset-path, so use SVGPathData only for now. + /// Path value for path(). + #[css(function)] + Path(SVGPathData), + /// None value. + None, + // Bug 1186329: Implement ray(), , , and . +} + +impl OffsetPath { + /// Return None. + #[inline] + pub fn none() -> Self { + OffsetPath::None + } +} + +impl Parse for OffsetPath { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't> + ) -> Result> { + // Parse none. + if input.try(|i| i.expect_ident_matching("none")).is_ok() { + return Ok(OffsetPath::none()); + } + + // Parse possible functions. + let location = input.current_source_location(); + let function = input.expect_function()?.clone(); + input.parse_nested_block(move |i| { + match_ignore_ascii_case! { &function, + // Bug 1186329: Implement the parser for ray(), , , + // and . + "path" => SVGPathData::parse(context, i).map(OffsetPath::Path), + _ => { + Err(location.new_custom_error( + StyleParseErrorKind::UnexpectedFunction(function.clone()) + )) + }, + } + }) + } +} + +/// SVG Path parser. +struct PathParser<'a> { + chars: Peekable>, + path: Vec, +} + +impl<'a> PathParser<'a> { + /// Parse a sub-path. + fn parse_subpath(&mut self) -> Result<(), ()> { + // Handle "moveto" Command first. If there is no "moveto", this is not a valid sub-path + // (i.e. not a valid moveto-drawto-command-group). + self.parse_moveto()?; + + // Handle other commands. + loop { + skip_wsp(&mut self.chars); + if self.chars.peek().map_or(true, |m| *m == 'M' || *m == 'm') { + break; + } + + match self.chars.next() { + Some(command) => { + let abs = command.is_uppercase(); + match command { + 'Z' | 'z' => { + // Note: A "closepath" coulbe be followed immediately by "moveto" or + // any other command, so we don't break this loop. + self.path.push(PathCommand::ClosePath); + }, + 'L' | 'l' => { + skip_wsp(&mut self.chars); + self.parse_lineto(abs)?; + }, + 'H' | 'h' => { + skip_wsp(&mut self.chars); + self.parse_h_lineto(abs)?; + }, + 'V' | 'v' => { + skip_wsp(&mut self.chars); + self.parse_v_lineto(abs)?; + }, + 'C' | 'c' => { + skip_wsp(&mut self.chars); + self.parse_curveto(abs)?; + }, + 'S' | 's' => { + skip_wsp(&mut self.chars); + self.parse_smooth_curveto(abs)?; + }, + 'Q' | 'q' => { + skip_wsp(&mut self.chars); + self.parse_quadratic_bezier_curveto(abs)?; + }, + 'T' | 't' => { + skip_wsp(&mut self.chars); + self.parse_smooth_quadratic_bezier_curveto(abs)?; + }, + 'A' | 'a' => { + skip_wsp(&mut self.chars); + self.parse_elliprical_arc(abs)?; + }, + _ => return Err(()), + } + }, + _ => break, // no more commands. + } + } + Ok(()) + } + + /// Parse "moveto" command. + fn parse_moveto(&mut self) -> Result<(), ()> { + let command = match self.chars.next() { + Some(c) if c == 'M' || c == 'm' => c, + _ => return Err(()), + }; + + skip_wsp(&mut self.chars); + let point = parse_coord(&mut self.chars)?; + let absolute = command == 'M'; + self.path.push(PathCommand::MoveTo { point, absolute } ); + + // End of string or the next character is a possible new command. + if !skip_wsp(&mut self.chars) || + self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { + return Ok(()); + } + skip_comma_wsp(&mut self.chars); + + // If a moveto is followed by multiple pairs of coordinates, the subsequent + // pairs are treated as implicit lineto commands. + self.parse_lineto(absolute) + } + + /// Parse "lineto" command. + fn parse_lineto(&mut self, absolute: bool) -> Result<(), ()> { + loop { + let point = parse_coord(&mut self.chars)?; + self.path.push(PathCommand::LineTo { point, absolute }); + + // End of string or the next character is a possible new command. + if !skip_wsp(&mut self.chars) || + self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { + break; + } + skip_comma_wsp(&mut self.chars); + } + Ok(()) + } + + /// Parse horizontal "lineto" command. + fn parse_h_lineto(&mut self, absolute: bool) -> Result<(), ()> { + loop { + let x = parse_number(&mut self.chars)?; + self.path.push(PathCommand::HorizontalLineTo { x, absolute }); + + // End of string or the next character is a possible new command. + if !skip_wsp(&mut self.chars) || + self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { + break; + } + skip_comma_wsp(&mut self.chars); + } + Ok(()) + } + + /// Parse vertical "lineto" command. + fn parse_v_lineto(&mut self, absolute: bool) -> Result<(), ()> { + loop { + let y = parse_number(&mut self.chars)?; + self.path.push(PathCommand::VerticalLineTo { y, absolute }); + + // End of string or the next character is a possible new command. + if !skip_wsp(&mut self.chars) || + self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { + break; + } + skip_comma_wsp(&mut self.chars); + } + Ok(()) + } + + /// Parse cubic Bézier curve command. + fn parse_curveto(&mut self, absolute: bool) -> Result<(), ()> { + loop { + let control1 = parse_coord(&mut self.chars)?; + skip_comma_wsp(&mut self.chars); + let control2 = parse_coord(&mut self.chars)?; + skip_comma_wsp(&mut self.chars); + let point = parse_coord(&mut self.chars)?; + + self.path.push(PathCommand::CurveTo { control1, control2, point, absolute }); + + // End of string or the next character is a possible new command. + if !skip_wsp(&mut self.chars) || + self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { + break; + } + skip_comma_wsp(&mut self.chars); + } + Ok(()) + } + + /// Parse smooth "curveto" command. + fn parse_smooth_curveto(&mut self, absolute: bool) -> Result<(), ()> { + loop { + let control2 = parse_coord(&mut self.chars)?; + skip_comma_wsp(&mut self.chars); + let point = parse_coord(&mut self.chars)?; + + self.path.push(PathCommand::SmoothCurveTo { control2, point, absolute }); + + // End of string or the next character is a possible new command. + if !skip_wsp(&mut self.chars) || + self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { + break; + } + skip_comma_wsp(&mut self.chars); + } + Ok(()) + } + + /// Parse quadratic Bézier curve command. + fn parse_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> { + loop { + let control1 = parse_coord(&mut self.chars)?; + skip_comma_wsp(&mut self.chars); + let point = parse_coord(&mut self.chars)?; + + self.path.push(PathCommand::QuadBezierCurveTo { control1, point, absolute }); + + // End of string or the next character is a possible new command. + if !skip_wsp(&mut self.chars) || + self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { + break; + } + skip_comma_wsp(&mut self.chars); + } + Ok(()) + } + + /// Parse smooth quadratic Bézier curveto command. + fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> { + loop { + let point = parse_coord(&mut self.chars)?; + + self.path.push(PathCommand::SmoothQuadBezierCurveTo { point, absolute }); + + // End of string or the next character is a possible new command. + if !skip_wsp(&mut self.chars) || + self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { + break; + } + skip_comma_wsp(&mut self.chars); + } + Ok(()) + } + + /// Parse elliptical arc curve command. + fn parse_elliprical_arc(&mut self, absolute: bool) -> Result<(), ()> { + // Parse a flag whose value is '0' or '1'; otherwise, return Err(()). + let parse_flag = |iter: &mut Peekable| -> Result { + let value = match iter.peek() { + Some(c) if *c == '0' || *c == '1' => *c == '1', + _ => return Err(()), + }; + iter.next(); + Ok(value) + }; + + loop { + let rx = parse_number(&mut self.chars)?; + skip_comma_wsp(&mut self.chars); + let ry = parse_number(&mut self.chars)?; + skip_comma_wsp(&mut self.chars); + let angle = parse_number(&mut self.chars)?; + skip_comma_wsp(&mut self.chars); + let large_arc_flag = parse_flag(&mut self.chars)?; + skip_comma_wsp(&mut self.chars); + let sweep_flag = parse_flag(&mut self.chars)?; + skip_comma_wsp(&mut self.chars); + let point = parse_coord(&mut self.chars)?; + + self.path.push( + PathCommand::EllipticalArc { + rx, ry, angle, large_arc_flag, sweep_flag, point, absolute + } + ); + + // End of string or the next character is a possible new command. + if !skip_wsp(&mut self.chars) || + self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { + break; + } + skip_comma_wsp(&mut self.chars); + } + Ok(()) + } +} + +/// The SVG path data. +/// +/// https://www.w3.org/TR/SVG11/paths.html#PathData +#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)] +pub struct SVGPathData(Box<[PathCommand]>); + +impl SVGPathData { + /// Return SVGPathData by a slice of PathCommand. + #[inline] + pub fn new(cmd: Box<[PathCommand]>) -> Self { + debug_assert!(!cmd.is_empty()); + SVGPathData(cmd) + } + + /// Get the array of PathCommand. + #[inline] + pub fn commands(&self) -> &[PathCommand] { + debug_assert!(!self.0.is_empty()); + &self.0 + } +} + +impl ToCss for SVGPathData { + #[inline] + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: fmt::Write + { + dest.write_char('"')?; + { + let mut writer = SequenceWriter::new(dest, " "); + for command in self.0.iter() { + writer.item(command)?; + } + } + dest.write_char('"') + } +} + +impl Parse for SVGPathData { + // We cannot use cssparser::Parser to parse a SVG path string because the spec wants to make + // the SVG path string as compact as possible. (i.e. The whitespaces may be dropped.) + // e.g. "M100 200L100 200" is a valid SVG path string. If we use tokenizer, the first ident + // is "M100", instead of "M", and this is not correct. Therefore, we use a Peekable + // str::Char iterator to check each character. + fn parse<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't> + ) -> Result> { + let location = input.current_source_location(); + let path_string = input.expect_string()?.as_ref(); + if path_string.is_empty() { + // Treat an empty string as invalid, so we will not set it. + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + // Parse the svg path string as multiple sub-paths. + let mut path_parser = PathParser { + chars: path_string.chars().peekable(), + path: Vec::new(), + }; + while skip_wsp(&mut path_parser.chars) { + if path_parser.parse_subpath().is_err() { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + } + + Ok(SVGPathData::new(path_parser.path.into_boxed_slice())) + } +} + + +/// The SVG path command. +/// The fields of these commands are self-explanatory, so we skip the documents. +/// Note: the index of the control points, e.g. control1, control2, are mapping to the control +/// points of the Bézier curve in the spec. +/// +/// https://www.w3.org/TR/SVG11/paths.html#PathData +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)] +#[allow(missing_docs)] +#[repr(C, u8)] +pub enum PathCommand { + /// The unknown type. + /// https://www.w3.org/TR/SVG/paths.html#__svg__SVGPathSeg__PATHSEG_UNKNOWN + Unknown, + /// The "moveto" command. + MoveTo { point: CoordPair, absolute: bool }, + /// The "lineto" command. + LineTo { point: CoordPair, absolute: bool }, + /// The horizontal "lineto" command. + HorizontalLineTo { x: CSSFloat, absolute: bool }, + /// The vertical "lineto" command. + VerticalLineTo { y: CSSFloat, absolute: bool }, + /// The cubic Bézier curve command. + CurveTo { control1: CoordPair, control2: CoordPair, point: CoordPair, absolute: bool }, + /// The smooth curve command. + SmoothCurveTo { control2: CoordPair, point: CoordPair, absolute: bool }, + /// The quadratic Bézier curve command. + QuadBezierCurveTo { control1: CoordPair, point: CoordPair, absolute: bool }, + /// The smooth quadratic Bézier curve command. + SmoothQuadBezierCurveTo { point: CoordPair, absolute: bool }, + /// The elliptical arc curve command. + EllipticalArc { + rx: CSSFloat, + ry: CSSFloat, + angle: CSSFloat, + large_arc_flag: bool, + sweep_flag: bool, + point: CoordPair, + absolute: bool + }, + /// The "closepath" command. + ClosePath, +} + +impl ToCss for PathCommand { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: fmt::Write + { + use self::PathCommand::*; + match *self { + Unknown => dest.write_str("X"), + ClosePath => dest.write_str("Z"), + MoveTo { point, absolute } => { + dest.write_char(if absolute { 'M' } else { 'm' })?; + dest.write_char(' ')?; + point.to_css(dest) + } + LineTo { point, absolute } => { + dest.write_char(if absolute { 'L' } else { 'l' })?; + dest.write_char(' ')?; + point.to_css(dest) + } + CurveTo { control1, control2, point, absolute } => { + dest.write_char(if absolute { 'C' } else { 'c' })?; + dest.write_char(' ')?; + control1.to_css(dest)?; + dest.write_char(' ')?; + control2.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + QuadBezierCurveTo { control1, point, absolute } => { + dest.write_char(if absolute { 'Q' } else { 'q' })?; + dest.write_char(' ')?; + control1.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + EllipticalArc { rx, ry, angle, large_arc_flag, sweep_flag, point, absolute } => { + dest.write_char(if absolute { 'A' } else { 'a' })?; + dest.write_char(' ')?; + rx.to_css(dest)?; + dest.write_char(' ')?; + ry.to_css(dest)?; + dest.write_char(' ')?; + angle.to_css(dest)?; + dest.write_char(' ')?; + (large_arc_flag as i32).to_css(dest)?; + dest.write_char(' ')?; + (sweep_flag as i32).to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + HorizontalLineTo { x, absolute } => { + dest.write_char(if absolute { 'H' } else { 'h' })?; + dest.write_char(' ')?; + x.to_css(dest) + }, + VerticalLineTo { y, absolute } => { + dest.write_char(if absolute { 'V' } else { 'v' })?; + dest.write_char(' ')?; + y.to_css(dest) + }, + SmoothCurveTo { control2, point, absolute } => { + dest.write_char(if absolute { 'S' } else { 's' })?; + dest.write_char(' ')?; + control2.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + SmoothQuadBezierCurveTo { point, absolute } => { + dest.write_char(if absolute { 'T' } else { 't' })?; + dest.write_char(' ')?; + point.to_css(dest) + }, + } + } +} + +/// The path coord type. +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)] +#[repr(C)] +pub struct CoordPair(CSSFloat, CSSFloat); + +impl CoordPair { + /// Create a CoordPair. + #[inline] + pub fn new(x: CSSFloat, y: CSSFloat) -> Self { + CoordPair(x, y) + } +} + +/// Parse a pair of numbers into CoordPair. +fn parse_coord(iter: &mut Peekable) -> Result { + let x = parse_number(iter)?; + skip_comma_wsp(iter); + let y = parse_number(iter)?; + Ok(CoordPair::new(x, y)) +} + +/// This is a special version which parses the number for SVG Path. e.g. "M 0.6.5" should be parsed +/// as MoveTo with a coordinate of ("0.6", ".5"), instead of treating 0.6.5 as a non-valid floating +/// point number. In other words, the logic here is similar with that of +/// tokenizer::consume_numeric, which also consumes the number as many as possible, but here the +/// input is a Peekable and we only accept an integer of a floating point number. +/// +/// The "number" syntax in https://www.w3.org/TR/SVG/paths.html#PathDataBNF +fn parse_number(iter: &mut Peekable) -> Result { + // 1. Check optional sign. + let sign = if iter.peek().map_or(false, |&sign: &char| sign == '+' || sign == '-') { + if iter.next().unwrap() == '-' { -1. } else { 1. } + } else { + 1. + }; + + // 2. Check integer part. + let mut integral_part: f64 = 0.; + let got_dot = if !iter.peek().map_or(false, |&n: &char| n == '.') { + // If the first digit in integer part is neither a dot nor a digit, this is not a number. + if iter.peek().map_or(true, |n: &char| !n.is_ascii_digit()) { + return Err(()); + } + + while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { + integral_part = + integral_part * 10. + iter.next().unwrap().to_digit(10).unwrap() as f64; + } + + iter.peek().map_or(false, |&n: &char| n == '.') + } else { + true + }; + + // 3. Check fractional part. + let mut fractional_part: f64 = 0.; + if got_dot { + // Consume '.'. + iter.next(); + // If the first digit in fractional part is not a digit, this is not a number. + if iter.peek().map_or(true, |n: &char| !n.is_ascii_digit()) { + return Err(()); + } + + let mut factor = 0.1; + while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { + fractional_part += iter.next().unwrap().to_digit(10).unwrap() as f64 * factor; + factor *= 0.1; + } + } + + let mut value = sign * (integral_part + fractional_part); + + // 4. Check exp part. The segment name of SVG Path doesn't include 'E' or 'e', so it's ok to + // treat the numbers after 'E' or 'e' are in the exponential part. + if iter.peek().map_or(false, |&exp: &char| exp == 'E' || exp == 'e') { + // Consume 'E' or 'e'. + iter.next(); + let exp_sign = if iter.peek().map_or(false, |&sign: &char| sign == '+' || sign == '-') { + if iter.next().unwrap() == '-' { -1. } else { 1. } + } else { + 1. + }; + + let mut exp: f64 = 0.; + while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { + exp = exp * 10. + iter.next().unwrap().to_digit(10).unwrap() as f64; + } + + value *= f64::powf(10., exp * exp_sign); + } + + if value.is_finite() { + Ok(value.min(::std::f32::MAX as f64).max(::std::f32::MIN as f64) as CSSFloat) + } else { + Err(()) + } +} + +/// Skip all svg whitespaces, and return true if |iter| hasn't finished. +#[inline] +fn skip_wsp(iter: &mut Peekable) -> bool { + // Note: SVG 1.1 defines the whitespaces as \u{9}, \u{20}, \u{A}, \u{D}. + // However, SVG 2 has one extra whitespace: \u{C}. + // Therefore, we follow the newest spec for the definition of whitespace, + // i.e. \u{9}, \u{20}, \u{A}, \u{C}, \u{D}, by is_ascii_whitespace(). + while iter.peek().map_or(false, |c: &char| c.is_ascii_whitespace()) { + iter.next(); + } + iter.peek().is_some() +} + +/// Skip all svg whitespaces and one comma, and return true if |iter| hasn't finished. +#[inline] +fn skip_comma_wsp(iter: &mut Peekable) -> bool { + if !skip_wsp(iter) { + return false; + } + + if *iter.peek().unwrap() != ',' { + return true; + } + iter.next(); + + skip_wsp(iter) +} From b85c734c417dee4cd0e87ab3236aed0b1590d68e Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Wed, 22 Aug 2018 01:20:21 +0000 Subject: [PATCH 04/34] style: Use macro for path parser. There are a lot of duplicates, so we use macro to refine them. Depends on D2963 Differential Revision: https://phabricator.services.mozilla.com/D2966 --- components/style/values/specified/motion.rs | 241 +++++++------------- 1 file changed, 78 insertions(+), 163 deletions(-) diff --git a/components/style/values/specified/motion.rs b/components/style/values/specified/motion.rs index 8b370369b85..ffaaf7cff17 100644 --- a/components/style/values/specified/motion.rs +++ b/components/style/values/specified/motion.rs @@ -70,6 +70,34 @@ struct PathParser<'a> { path: Vec, } +macro_rules! parse_arguments { + ( + $parser:ident, + $abs:ident, + $enum:ident, + [ $para:ident => $func:ident $(, $other_para:ident => $other_func:ident)* ] + ) => { + { + loop { + let $para = $func(&mut $parser.chars)?; + $( + skip_comma_wsp(&mut $parser.chars); + let $other_para = $other_func(&mut $parser.chars)?; + )* + $parser.path.push(PathCommand::$enum { $para $(, $other_para)*, $abs }); + + // End of string or the next character is a possible new command. + if !skip_wsp(&mut $parser.chars) || + $parser.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { + break; + } + skip_comma_wsp(&mut $parser.chars); + } + Ok(()) + } + } +} + impl<'a> PathParser<'a> { /// Parse a sub-path. fn parse_subpath(&mut self) -> Result<(), ()> { @@ -87,46 +115,30 @@ impl<'a> PathParser<'a> { match self.chars.next() { Some(command) => { let abs = command.is_uppercase(); - match command { - 'Z' | 'z' => { - // Note: A "closepath" coulbe be followed immediately by "moveto" or - // any other command, so we don't break this loop. - self.path.push(PathCommand::ClosePath); - }, - 'L' | 'l' => { - skip_wsp(&mut self.chars); - self.parse_lineto(abs)?; - }, - 'H' | 'h' => { - skip_wsp(&mut self.chars); - self.parse_h_lineto(abs)?; - }, - 'V' | 'v' => { - skip_wsp(&mut self.chars); - self.parse_v_lineto(abs)?; - }, - 'C' | 'c' => { - skip_wsp(&mut self.chars); - self.parse_curveto(abs)?; - }, - 'S' | 's' => { - skip_wsp(&mut self.chars); - self.parse_smooth_curveto(abs)?; - }, - 'Q' | 'q' => { - skip_wsp(&mut self.chars); - self.parse_quadratic_bezier_curveto(abs)?; - }, - 'T' | 't' => { - skip_wsp(&mut self.chars); - self.parse_smooth_quadratic_bezier_curveto(abs)?; - }, - 'A' | 'a' => { - skip_wsp(&mut self.chars); - self.parse_elliprical_arc(abs)?; - }, - _ => return Err(()), + macro_rules! parse_command { + ( $($($p:pat)|+ => $parse_func:ident,)* ) => { + match command { + $( + $($p)|+ => { + skip_wsp(&mut self.chars); + self.$parse_func(abs)?; + }, + )* + _ => return Err(()), + } + } } + parse_command!( + 'Z' | 'z' => parse_closepath, + 'L' | 'l' => parse_lineto, + 'H' | 'h' => parse_h_lineto, + 'V' | 'v' => parse_v_lineto, + 'C' | 'c' => parse_curveto, + 'S' | 's' => parse_smooth_curveto, + 'Q' | 'q' => parse_quadratic_bezier_curveto, + 'T' | 't' => parse_smooth_quadratic_bezier_curveto, + 'A' | 'a' => parse_elliprical_arc, + ); }, _ => break, // no more commands. } @@ -158,128 +170,51 @@ impl<'a> PathParser<'a> { self.parse_lineto(absolute) } + /// Parse "closepath" command. + fn parse_closepath(&mut self, _absolute: bool) -> Result<(), ()> { + self.path.push(PathCommand::ClosePath); + Ok(()) + } + /// Parse "lineto" command. fn parse_lineto(&mut self, absolute: bool) -> Result<(), ()> { - loop { - let point = parse_coord(&mut self.chars)?; - self.path.push(PathCommand::LineTo { point, absolute }); - - // End of string or the next character is a possible new command. - if !skip_wsp(&mut self.chars) || - self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { - break; - } - skip_comma_wsp(&mut self.chars); - } - Ok(()) + parse_arguments!(self, absolute, LineTo, [ point => parse_coord ]) } /// Parse horizontal "lineto" command. fn parse_h_lineto(&mut self, absolute: bool) -> Result<(), ()> { - loop { - let x = parse_number(&mut self.chars)?; - self.path.push(PathCommand::HorizontalLineTo { x, absolute }); - - // End of string or the next character is a possible new command. - if !skip_wsp(&mut self.chars) || - self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { - break; - } - skip_comma_wsp(&mut self.chars); - } - Ok(()) + parse_arguments!(self, absolute, HorizontalLineTo, [ x => parse_number ]) } /// Parse vertical "lineto" command. fn parse_v_lineto(&mut self, absolute: bool) -> Result<(), ()> { - loop { - let y = parse_number(&mut self.chars)?; - self.path.push(PathCommand::VerticalLineTo { y, absolute }); - - // End of string or the next character is a possible new command. - if !skip_wsp(&mut self.chars) || - self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { - break; - } - skip_comma_wsp(&mut self.chars); - } - Ok(()) + parse_arguments!(self, absolute, VerticalLineTo, [ y => parse_number ]) } /// Parse cubic Bézier curve command. fn parse_curveto(&mut self, absolute: bool) -> Result<(), ()> { - loop { - let control1 = parse_coord(&mut self.chars)?; - skip_comma_wsp(&mut self.chars); - let control2 = parse_coord(&mut self.chars)?; - skip_comma_wsp(&mut self.chars); - let point = parse_coord(&mut self.chars)?; - - self.path.push(PathCommand::CurveTo { control1, control2, point, absolute }); - - // End of string or the next character is a possible new command. - if !skip_wsp(&mut self.chars) || - self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { - break; - } - skip_comma_wsp(&mut self.chars); - } - Ok(()) + parse_arguments!(self, absolute, CurveTo, [ + control1 => parse_coord, control2 => parse_coord, point => parse_coord + ]) } /// Parse smooth "curveto" command. fn parse_smooth_curveto(&mut self, absolute: bool) -> Result<(), ()> { - loop { - let control2 = parse_coord(&mut self.chars)?; - skip_comma_wsp(&mut self.chars); - let point = parse_coord(&mut self.chars)?; - - self.path.push(PathCommand::SmoothCurveTo { control2, point, absolute }); - - // End of string or the next character is a possible new command. - if !skip_wsp(&mut self.chars) || - self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { - break; - } - skip_comma_wsp(&mut self.chars); - } - Ok(()) + parse_arguments!(self, absolute, SmoothCurveTo, [ + control2 => parse_coord, point => parse_coord + ]) } /// Parse quadratic Bézier curve command. fn parse_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> { - loop { - let control1 = parse_coord(&mut self.chars)?; - skip_comma_wsp(&mut self.chars); - let point = parse_coord(&mut self.chars)?; - - self.path.push(PathCommand::QuadBezierCurveTo { control1, point, absolute }); - - // End of string or the next character is a possible new command. - if !skip_wsp(&mut self.chars) || - self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { - break; - } - skip_comma_wsp(&mut self.chars); - } - Ok(()) + parse_arguments!(self, absolute, QuadBezierCurveTo, [ + control1 => parse_coord, point => parse_coord + ]) } /// Parse smooth quadratic Bézier curveto command. fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> { - loop { - let point = parse_coord(&mut self.chars)?; - - self.path.push(PathCommand::SmoothQuadBezierCurveTo { point, absolute }); - - // End of string or the next character is a possible new command. - if !skip_wsp(&mut self.chars) || - self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { - break; - } - skip_comma_wsp(&mut self.chars); - } - Ok(()) + parse_arguments!(self, absolute, SmoothQuadBezierCurveTo, [ point => parse_coord ]) } /// Parse elliptical arc curve command. @@ -293,34 +228,14 @@ impl<'a> PathParser<'a> { iter.next(); Ok(value) }; - - loop { - let rx = parse_number(&mut self.chars)?; - skip_comma_wsp(&mut self.chars); - let ry = parse_number(&mut self.chars)?; - skip_comma_wsp(&mut self.chars); - let angle = parse_number(&mut self.chars)?; - skip_comma_wsp(&mut self.chars); - let large_arc_flag = parse_flag(&mut self.chars)?; - skip_comma_wsp(&mut self.chars); - let sweep_flag = parse_flag(&mut self.chars)?; - skip_comma_wsp(&mut self.chars); - let point = parse_coord(&mut self.chars)?; - - self.path.push( - PathCommand::EllipticalArc { - rx, ry, angle, large_arc_flag, sweep_flag, point, absolute - } - ); - - // End of string or the next character is a possible new command. - if !skip_wsp(&mut self.chars) || - self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { - break; - } - skip_comma_wsp(&mut self.chars); - } - Ok(()) + parse_arguments!(self, absolute, EllipticalArc, [ + rx => parse_number, + ry => parse_number, + angle => parse_number, + large_arc_flag => parse_flag, + sweep_flag => parse_flag, + point => parse_coord + ]) } } From e96b025f10e5e8f435edc3fb3b7b36cb4683f596 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Wed, 22 Aug 2018 05:37:39 +0000 Subject: [PATCH 05/34] style: Add MallocSizeOf impls for 128-bit integers. Differential Revision: https://phabricator.services.mozilla.com/D3947 --- components/malloc_size_of/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/malloc_size_of/lib.rs b/components/malloc_size_of/lib.rs index f4774d66a3e..11e96254172 100644 --- a/components/malloc_size_of/lib.rs +++ b/components/malloc_size_of/lib.rs @@ -845,8 +845,8 @@ macro_rules! malloc_size_of_is_0( ); malloc_size_of_is_0!(bool, char, str); -malloc_size_of_is_0!(u8, u16, u32, u64, usize); -malloc_size_of_is_0!(i8, i16, i32, i64, isize); +malloc_size_of_is_0!(u8, u16, u32, u64, u128, usize); +malloc_size_of_is_0!(i8, i16, i32, i64, i128, isize); malloc_size_of_is_0!(f32, f64); malloc_size_of_is_0!(std::sync::atomic::AtomicBool); From e338bd3addaa996fcaa179bd9a0c8f708fd01955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 17 Aug 2018 20:26:29 +0200 Subject: [PATCH 06/34] style: Serialize clip-path and shape-outside using Servo. Differential Revision: https://phabricator.services.mozilla.com/D3653 --- components/style/gecko/conversions.rs | 112 +++++++++++++-------- components/style/values/computed/border.rs | 16 +++ 2 files changed, 85 insertions(+), 43 deletions(-) diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 154ad95bc43..3c1fcde28cd 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -19,6 +19,7 @@ use stylesheets::{Origin, RulesMutateError}; use values::computed::{Angle, CalcLengthOrPercentage, Gradient, Image}; use values::computed::{Integer, LengthOrPercentage, LengthOrPercentageOrAuto}; use values::computed::{Percentage, TextAlign}; +use values::computed::image::LineDirection; use values::computed::url::ComputedImageUrl; use values::generics::box_::VerticalAlign; use values::generics::grid::{TrackListValue, TrackSize}; @@ -139,6 +140,68 @@ impl Angle { } } +fn line_direction( + horizontal: LengthOrPercentage, + vertical: LengthOrPercentage, +) -> LineDirection { + use values::computed::position::Position; + use values::specified::position::{X, Y}; + + let horizontal_percentage = match horizontal { + LengthOrPercentage::Percentage(percentage) => Some(percentage.0), + _ => None, + }; + + let vertical_percentage = match vertical { + LengthOrPercentage::Percentage(percentage) => Some(percentage.0), + _ => None, + }; + + let horizontal_as_corner = horizontal_percentage.and_then(|percentage| { + if percentage == 0.0 { + Some(X::Left) + } else if percentage == 1.0 { + Some(X::Right) + } else { + None + } + }); + + let vertical_as_corner = vertical_percentage.and_then(|percentage| { + if percentage == 0.0 { + Some(Y::Top) + } else if percentage == 1.0 { + Some(Y::Bottom) + } else { + None + } + }); + + if let (Some(hc), Some(vc)) = (horizontal_as_corner, vertical_as_corner) { + return LineDirection::Corner(hc, vc) + } + + if let Some(hc) = horizontal_as_corner { + if vertical_percentage == Some(0.5) { + return LineDirection::Horizontal(hc) + } + } + + if let Some(vc) = vertical_as_corner { + if horizontal_percentage == Some(0.5) { + return LineDirection::Vertical(vc) + } + } + + LineDirection::MozPosition( + Some(Position { + horizontal, + vertical, + }), + None, + ) +} + impl nsStyleImage { /// Set a given Servo `Image` value into this `nsStyleImage`. pub fn set(&mut self, image: Image) { @@ -174,13 +237,13 @@ impl nsStyleImage { } } + // FIXME(emilio): This is really complex, we should use cbindgen for this. fn set_gradient(&mut self, gradient: Gradient) { use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_CORNER as CLOSEST_CORNER; use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE as CLOSEST_SIDE; use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as FARTHEST_CORNER; use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE as FARTHEST_SIDE; use self::structs::nsStyleCoord; - use values::computed::image::LineDirection; use values::generics::image::{Circle, Ellipse, EndingShape, GradientKind, ShapeExtent}; use values::specified::position::{X, Y}; @@ -437,12 +500,11 @@ impl nsStyleImage { use self::structs::NS_STYLE_GRADIENT_SIZE_CLOSEST_SIDE as CLOSEST_SIDE; use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER as FARTHEST_CORNER; use self::structs::NS_STYLE_GRADIENT_SIZE_FARTHEST_SIDE as FARTHEST_SIDE; - use values::computed::{Length, LengthOrPercentage}; + use values::computed::Length; use values::computed::image::LineDirection; use values::computed::position::Position; use values::generics::image::{Circle, ColorStop, CompatMode, Ellipse}; use values::generics::image::{EndingShape, GradientKind, ShapeExtent}; - use values::specified::position::{X, Y}; let gecko_gradient = bindings::Gecko_GetGradientImageValue(self) .as_ref() @@ -456,41 +518,7 @@ impl nsStyleImage { let line_direction = match (angle, horizontal_style, vertical_style) { (Some(a), None, None) => LineDirection::Angle(a), (None, Some(horizontal), Some(vertical)) => { - let horizontal_as_corner = match horizontal { - LengthOrPercentage::Percentage(percentage) => { - if percentage.0 == 0.0 { - Some(X::Left) - } else if percentage.0 == 1.0 { - Some(X::Right) - } else { - None - } - }, - _ => None, - }; - let vertical_as_corner = match vertical { - LengthOrPercentage::Percentage(percentage) => { - if percentage.0 == 0.0 { - Some(Y::Top) - } else if percentage.0 == 1.0 { - Some(Y::Bottom) - } else { - None - } - }, - _ => None, - }; - - match (horizontal_as_corner, vertical_as_corner) { - (Some(hc), Some(vc)) => LineDirection::Corner(hc, vc), - _ => LineDirection::MozPosition( - Some(Position { - horizontal, - vertical, - }), - None, - ), - } + line_direction(horizontal, vertical) }, (Some(_), Some(horizontal), Some(vertical)) => LineDirection::MozPosition( Some(Position { @@ -743,17 +771,15 @@ pub mod basic_shape { let r = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[1]); let b = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[2]); let l = LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[3]); - let round = (&other.mRadius).into(); + let round: BorderRadius = (&other.mRadius).into(); + let round = if round.all_zero() { None } else { Some(round) }; let rect = Rect::new( t.expect("inset() offset should be a length, percentage, or calc value"), r.expect("inset() offset should be a length, percentage, or calc value"), b.expect("inset() offset should be a length, percentage, or calc value"), l.expect("inset() offset should be a length, percentage, or calc value"), ); - GenericBasicShape::Inset(InsetRect { - rect: rect, - round: Some(round), - }) + GenericBasicShape::Inset(InsetRect { rect, round }) }, StyleBasicShapeType::Circle => GenericBasicShape::Circle(Circle { radius: (&other.mCoordinates[0]).into(), diff --git a/components/style/values/computed/border.rs b/components/style/values/computed/border.rs index cf97a96ad73..8c54aeba43b 100644 --- a/components/style/values/computed/border.rs +++ b/components/style/values/computed/border.rs @@ -81,3 +81,19 @@ impl ToAnimatedZero for BorderCornerRadius { Err(()) } } + +impl BorderRadius { + /// Returns whether all the values are `0px`. + pub fn all_zero(&self) -> bool { + fn all(corner: &BorderCornerRadius) -> bool { + fn is_zero(l: &LengthOrPercentage) -> bool { + *l == LengthOrPercentage::zero() + } + is_zero(corner.0.width()) && is_zero(corner.0.height()) + } + all(&self.top_left) && + all(&self.top_right) && + all(&self.bottom_left) && + all(&self.bottom_right) + } +} From a2d6566d0b2d541e51c3338a95a871ce8f480b57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 21 Aug 2018 16:42:27 +0200 Subject: [PATCH 07/34] style: Make text-orientation, unicode-bidi, contain and will-change non-animatable. Per recent CSSWG resolutions: https://github.com/w3c/csswg-drafts/issues/2737 https://github.com/w3c/csswg-drafts/issues/2751 Differential Revision: https://phabricator.services.mozilla.com/D3888 --- .../style/properties/longhands/box.mako.rs | 20 ++++++++++--------- .../longhands/inherited_box.mako.rs | 5 +---- .../style/properties/longhands/text.mako.rs | 12 ++++++----- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index 4ef45a502bc..c408bfe11e9 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -513,14 +513,16 @@ ${helpers.predefined_type( servo_restyle_damage="reflow_out_of_flow" )} -${helpers.predefined_type("contain", - "Contain", - "specified::Contain::empty()", - animation_value_type="discrete", - products="gecko", - flags="CREATES_STACKING_CONTEXT FIXPOS_CB", - gecko_pref="layout.css.contain.enabled", - spec="https://drafts.csswg.org/css-contain/#contain-property")} +${helpers.predefined_type( + "contain", + "Contain", + "specified::Contain::empty()", + animation_value_type="none", + products="gecko", + flags="CREATES_STACKING_CONTEXT FIXPOS_CB", + gecko_pref="layout.css.contain.enabled", + spec="https://drafts.csswg.org/css-contain/#contain-property", +)} // Non-standard ${helpers.predefined_type( @@ -552,7 +554,7 @@ ${helpers.predefined_type( "WillChange", "computed::WillChange::auto()", products="gecko", - animation_value_type="discrete", + animation_value_type="none", spec="https://drafts.csswg.org/css-will-change/#will-change" )} diff --git a/components/style/properties/longhands/inherited_box.mako.rs b/components/style/properties/longhands/inherited_box.mako.rs index 4482931fd7a..d810ec3d29a 100644 --- a/components/style/properties/longhands/inherited_box.mako.rs +++ b/components/style/properties/longhands/inherited_box.mako.rs @@ -40,15 +40,12 @@ ${helpers.single_keyword( servo_restyle_damage="rebuild_and_reflow", )} -// TODO(emilio): Should text-orientation be non-animatable? It affects the -// WritingMode value, but not the logical -> physical mapping of properties, -// which is the reason direction / writing-mode are non-animatable. ${helpers.single_keyword( "text-orientation", "mixed upright sideways", extra_gecko_aliases="sideways-right=sideways", products="gecko", - animation_value_type="discrete", + animation_value_type="none", spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation", )} diff --git a/components/style/properties/longhands/text.mako.rs b/components/style/properties/longhands/text.mako.rs index 613ed6c5f67..f87fe4915a3 100644 --- a/components/style/properties/longhands/text.mako.rs +++ b/components/style/properties/longhands/text.mako.rs @@ -21,11 +21,13 @@ ${helpers.predefined_type("text-overflow", spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow", servo_restyle_damage="rebuild_and_reflow")} -${helpers.single_keyword("unicode-bidi", - "normal embed isolate bidi-override isolate-override plaintext", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-writing-modes/#propdef-unicode-bidi", - servo_restyle_damage="rebuild_and_reflow")} +${helpers.single_keyword( + "unicode-bidi", + "normal embed isolate bidi-override isolate-override plaintext", + animation_value_type="none", + spec="https://drafts.csswg.org/css-writing-modes/#propdef-unicode-bidi", + servo_restyle_damage="rebuild_and_reflow", +)} ${helpers.predefined_type("text-decoration-line", "TextDecorationLine", From ae671a7d2669a3fcb897ceb7d530dcfbce8931eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 21 Aug 2018 17:23:30 +0200 Subject: [PATCH 08/34] style: Use a consistent style for longhands. It's a bit of a mess. Differential Revision: https://phabricator.services.mozilla.com/D3892 --- .../properties/longhands/background.mako.rs | 43 +- .../style/properties/longhands/border.mako.rs | 88 ++-- .../style/properties/longhands/box.mako.rs | 445 ++++++++++-------- .../style/properties/longhands/color.mako.rs | 8 +- .../style/properties/longhands/column.mako.rs | 87 ++-- .../properties/longhands/counters.mako.rs | 20 +- .../properties/longhands/effects.mako.rs | 37 +- .../longhands/inherited_svg.mako.rs | 219 +++++---- .../longhands/inherited_table.mako.rs | 59 ++- .../longhands/inherited_text.mako.rs | 233 +++++---- .../properties/longhands/inherited_ui.mako.rs | 70 +-- .../style/properties/longhands/list.mako.rs | 46 +- .../properties/longhands/outline.mako.rs | 34 +- .../properties/longhands/position.mako.rs | 361 ++++++++------ .../style/properties/longhands/svg.mako.rs | 98 ++-- .../style/properties/longhands/table.mako.rs | 28 +- .../style/properties/longhands/text.mako.rs | 69 +-- .../style/properties/longhands/ui.mako.rs | 78 +-- .../style/properties/longhands/xul.mako.rs | 112 +++-- 19 files changed, 1267 insertions(+), 868 deletions(-) diff --git a/components/style/properties/longhands/background.mako.rs b/components/style/properties/longhands/background.mako.rs index 9bb4e03e523..65df61a2e71 100644 --- a/components/style/properties/longhands/background.mako.rs +++ b/components/style/properties/longhands/background.mako.rs @@ -18,14 +18,17 @@ ${helpers.predefined_type( flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", )} -${helpers.predefined_type("background-image", "ImageLayer", +${helpers.predefined_type( + "background-image", + "ImageLayer", initial_value="Either::First(None_)", initial_specified_value="Either::First(None_)", spec="https://drafts.csswg.org/css-backgrounds/#the-background-image", vector="True", animation_value_type="discrete", ignored_when_colors_disabled="True", - flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")} + flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", +)} % for (axis, direction, initial) in [("x", "Horizontal", "left"), ("y", "Vertical", "top")]: ${helpers.predefined_type( @@ -52,13 +55,15 @@ ${helpers.predefined_type( flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", )} -${helpers.single_keyword("background-attachment", - "scroll fixed" + (" local" if product == "gecko" else ""), - vector=True, - gecko_enum_prefix="StyleImageLayerAttachment", - spec="https://drafts.csswg.org/css-backgrounds/#the-background-attachment", - animation_value_type="discrete", - flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")} +${helpers.single_keyword( + "background-attachment", + "scroll fixed" + (" local" if product == "gecko" else ""), + vector=True, + gecko_enum_prefix="StyleImageLayerAttachment", + spec="https://drafts.csswg.org/css-backgrounds/#the-background-attachment", + animation_value_type="discrete", + flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", +)} ${helpers.single_keyword( "background-clip", @@ -96,12 +101,14 @@ ${helpers.predefined_type( extra_prefixes="webkit")} // https://drafts.fxtf.org/compositing/#background-blend-mode -${helpers.single_keyword("background-blend-mode", - """normal multiply screen overlay darken lighten color-dodge - color-burn hard-light soft-light difference exclusion hue - saturation color luminosity""", - gecko_constant_prefix="NS_STYLE_BLEND", - gecko_pref="layout.css.background-blend-mode.enabled", - vector=True, products="gecko", animation_value_type="discrete", - spec="https://drafts.fxtf.org/compositing/#background-blend-mode", - flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER")} +${helpers.single_keyword( + "background-blend-mode", + """normal multiply screen overlay darken lighten color-dodge + color-burn hard-light soft-light difference exclusion hue + saturation color luminosity""", + gecko_constant_prefix="NS_STYLE_BLEND", + gecko_pref="layout.css.background-blend-mode.enabled", + vector=True, products="gecko", animation_value_type="discrete", + spec="https://drafts.fxtf.org/compositing/#background-blend-mode", + flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", +)} diff --git a/components/style/properties/longhands/border.mako.rs b/components/style/properties/longhands/border.mako.rs index 901d29728b1..1066dbbc619 100644 --- a/components/style/properties/longhands/border.mako.rs +++ b/components/style/properties/longhands/border.mako.rs @@ -61,51 +61,70 @@ )} % endfor -${helpers.gecko_keyword_conversion(Keyword('border-style', - "none solid double dotted dashed hidden groove ridge inset outset"), - type="::values::specified::BorderStyle")} +${helpers.gecko_keyword_conversion( + Keyword('border-style', + "none solid double dotted dashed hidden groove ridge inset outset"), + type="::values::specified::BorderStyle", +)} // FIXME(#4126): when gfx supports painting it, make this Size2D % for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]: - ${helpers.predefined_type("border-" + corner + "-radius", "BorderCornerRadius", - "computed::BorderCornerRadius::zero()", - "parse", extra_prefixes="webkit", - spec="https://drafts.csswg.org/css-backgrounds/#border-%s-radius" % corner, - boxed=True, - flags="APPLIES_TO_FIRST_LETTER", - animation_value_type="BorderCornerRadius")} + ${helpers.predefined_type( + "border-" + corner + "-radius", + "BorderCornerRadius", + "computed::BorderCornerRadius::zero()", + "parse", + extra_prefixes="webkit", + spec="https://drafts.csswg.org/css-backgrounds/#border-%s-radius" % corner, + boxed=True, + flags="APPLIES_TO_FIRST_LETTER", + animation_value_type="BorderCornerRadius", + )} % endfor -${helpers.single_keyword("box-decoration-break", "slice clone", - gecko_enum_prefix="StyleBoxDecorationBreak", - gecko_pref="layout.css.box-decoration-break.enabled", - spec="https://drafts.csswg.org/css-break/#propdef-box-decoration-break", - products="gecko", animation_value_type="discrete")} +${helpers.single_keyword( + "box-decoration-break", + "slice clone", + gecko_enum_prefix="StyleBoxDecorationBreak", + gecko_pref="layout.css.box-decoration-break.enabled", + spec="https://drafts.csswg.org/css-break/#propdef-box-decoration-break", + products="gecko", + animation_value_type="discrete", +)} -${helpers.single_keyword("-moz-float-edge", "content-box margin-box", - gecko_ffi_name="mFloatEdge", - gecko_enum_prefix="StyleFloatEdge", - products="gecko", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-float-edge)", - animation_value_type="discrete")} +${helpers.single_keyword( + "-moz-float-edge", + "content-box margin-box", + gecko_ffi_name="mFloatEdge", + gecko_enum_prefix="StyleFloatEdge", + products="gecko", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-float-edge)", + animation_value_type="discrete", +)} -${helpers.predefined_type("border-image-source", "ImageLayer", +${helpers.predefined_type( + "border-image-source", + "ImageLayer", initial_value="Either::First(None_)", initial_specified_value="Either::First(None_)", spec="https://drafts.csswg.org/css-backgrounds/#the-background-image", vector=False, animation_value_type="discrete", flags="APPLIES_TO_FIRST_LETTER", - boxed=True)} + boxed=True, +)} -${helpers.predefined_type("border-image-outset", "LengthOrNumberRect", +${helpers.predefined_type( + "border-image-outset", + "LengthOrNumberRect", parse_method="parse_non_negative", initial_value="computed::LengthOrNumberRect::all(computed::LengthOrNumber::zero())", initial_specified_value="specified::LengthOrNumberRect::all(specified::LengthOrNumber::zero())", spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset", animation_value_type="discrete", flags="APPLIES_TO_FIRST_LETTER", - boxed=True)} + boxed=True, +)} ${helpers.predefined_type( "border-image-repeat", @@ -117,21 +136,27 @@ ${helpers.predefined_type( flags="APPLIES_TO_FIRST_LETTER", )} -${helpers.predefined_type("border-image-width", "BorderImageWidth", +${helpers.predefined_type( + "border-image-width", + "BorderImageWidth", initial_value="computed::BorderImageWidth::all(computed::BorderImageSideWidth::one())", initial_specified_value="specified::BorderImageWidth::all(specified::BorderImageSideWidth::one())", spec="https://drafts.csswg.org/css-backgrounds/#border-image-width", animation_value_type="discrete", flags="APPLIES_TO_FIRST_LETTER", - boxed=True)} + boxed=True, +)} -${helpers.predefined_type("border-image-slice", "BorderImageSlice", +${helpers.predefined_type( + "border-image-slice", + "BorderImageSlice", initial_value="computed::NumberOrPercentage::Percentage(computed::Percentage(1.)).into()", initial_specified_value="specified::NumberOrPercentage::Percentage(specified::Percentage::new(1.)).into()", spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice", animation_value_type="discrete", flags="APPLIES_TO_FIRST_LETTER", - boxed=True)} + boxed=True, +)} #[cfg(feature = "gecko")] impl ::values::computed::BorderImageWidth { @@ -155,8 +180,9 @@ impl ::values::computed::BorderImageWidth { % endfor } - pub fn from_gecko_rect(sides: &::gecko_bindings::structs::nsStyleSides) - -> Option<::values::computed::BorderImageWidth> { + pub fn from_gecko_rect( + sides: &::gecko_bindings::structs::nsStyleSides, + ) -> Option<::values::computed::BorderImageWidth> { use gecko_bindings::structs::nsStyleUnit::{eStyleUnit_Factor, eStyleUnit_Auto}; use gecko_bindings::sugar::ns_style_coord::CoordData; use gecko::values::GeckoStyleCoordConvertible; diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index c408bfe11e9..06742d45e68 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -41,18 +41,25 @@ ${helpers.gecko_keyword_conversion( type="::values::specified::Display" )} -${helpers.single_keyword("-moz-top-layer", "none top", - gecko_constant_prefix="NS_STYLE_TOP_LAYER", - gecko_ffi_name="mTopLayer", - products="gecko", animation_value_type="none", - enabled_in="ua", - spec="Internal (not web-exposed)")} +${helpers.single_keyword( + "-moz-top-layer", + "none top", + gecko_constant_prefix="NS_STYLE_TOP_LAYER", + gecko_ffi_name="mTopLayer", + products="gecko", + animation_value_type="none", + enabled_in="ua", + spec="Internal (not web-exposed)", +)} -${helpers.single_keyword("position", "static absolute relative fixed sticky", - animation_value_type="discrete", - flags="CREATES_STACKING_CONTEXT ABSPOS_CB", - spec="https://drafts.csswg.org/css-position/#position-property", - servo_restyle_damage="rebuild_and_reflow")} +${helpers.single_keyword( + "position", + "static absolute relative fixed sticky", + animation_value_type="discrete", + flags="CREATES_STACKING_CONTEXT ABSPOS_CB", + spec="https://drafts.csswg.org/css-position/#position-property", + servo_restyle_damage="rebuild_and_reflow", +)} ${helpers.predefined_type( "float", @@ -64,7 +71,7 @@ ${helpers.predefined_type( needs_context=False, flags="APPLIES_TO_FIRST_LETTER", servo_restyle_damage="rebuild_and_reflow", - gecko_ffi_name="mFloat" + gecko_ffi_name="mFloat", )} ${helpers.predefined_type( @@ -75,7 +82,7 @@ ${helpers.predefined_type( needs_context=False, gecko_ffi_name="mBreakType", spec="https://drafts.csswg.org/css-box/#propdef-clear", - servo_restyle_damage="rebuild_and_reflow" + servo_restyle_damage="rebuild_and_reflow", )} ${helpers.predefined_type( @@ -85,7 +92,7 @@ ${helpers.predefined_type( animation_value_type="ComputedValue", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", spec="https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align", - servo_restyle_damage = "reflow" + servo_restyle_damage = "reflow", )} // CSS 2.1, Section 11 - Visual effects @@ -118,14 +125,17 @@ ${helpers.single_keyword("-servo-overflow-clip-box", "padding-box content-box", // FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`. // // We allow it to apply to placeholders for UA sheets, which set it !important. -${helpers.single_keyword("overflow-x", "visible hidden scroll auto", - animation_value_type="discrete", - extra_gecko_values="-moz-hidden-unscrollable", - custom_consts=overflow_custom_consts, - gecko_constant_prefix="NS_STYLE_OVERFLOW", - flags="APPLIES_TO_PLACEHOLDER", - spec="https://drafts.csswg.org/css-overflow/#propdef-overflow-x", - servo_restyle_damage = "reflow")} +${helpers.single_keyword( + "overflow-x", + "visible hidden scroll auto", + animation_value_type="discrete", + extra_gecko_values="-moz-hidden-unscrollable", + custom_consts=overflow_custom_consts, + gecko_constant_prefix="NS_STYLE_OVERFLOW", + flags="APPLIES_TO_PLACEHOLDER", + spec="https://drafts.csswg.org/css-overflow/#propdef-overflow-x", + servo_restyle_damage = "reflow", +)} // FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`. // @@ -139,26 +149,30 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto", <% transition_extra_prefixes = "moz:layout.css.prefixes.transitions webkit" %> -${helpers.predefined_type("transition-duration", - "Time", - "computed::Time::zero()", - initial_specified_value="specified::Time::zero()", - parse_method="parse_non_negative", - vector=True, - need_index=True, - animation_value_type="none", - extra_prefixes=transition_extra_prefixes, - spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration")} +${helpers.predefined_type( + "transition-duration", + "Time", + "computed::Time::zero()", + initial_specified_value="specified::Time::zero()", + parse_method="parse_non_negative", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes=transition_extra_prefixes, + spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration", +)} -${helpers.predefined_type("transition-timing-function", - "TimingFunction", - "computed::TimingFunction::ease()", - initial_specified_value="specified::TimingFunction::ease()", - vector=True, - need_index=True, - animation_value_type="none", - extra_prefixes=transition_extra_prefixes, - spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function")} +${helpers.predefined_type( + "transition-timing-function", + "TimingFunction", + "computed::TimingFunction::ease()", + initial_specified_value="specified::TimingFunction::ease()", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes=transition_extra_prefixes, + spec="https://drafts.csswg.org/css-transitions/#propdef-transition-timing-function", +)} ${helpers.predefined_type( "transition-property", @@ -173,16 +187,17 @@ ${helpers.predefined_type( spec="https://drafts.csswg.org/css-transitions/#propdef-transition-property", )} -${helpers.predefined_type("transition-delay", - "Time", - "computed::Time::zero()", - initial_specified_value="specified::Time::zero()", - vector=True, - need_index=True, - animation_value_type="none", - extra_prefixes=transition_extra_prefixes, - spec="https://drafts.csswg.org/css-transitions/#propdef-transition-delay")} - +${helpers.predefined_type( + "transition-delay", + "Time", + "computed::Time::zero()", + initial_specified_value="specified::Time::zero()", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes=transition_extra_prefixes, + spec="https://drafts.csswg.org/css-transitions/#propdef-transition-delay", +)} <% animation_extra_prefixes = "moz:layout.css.prefixes.animations webkit" %> @@ -199,29 +214,33 @@ ${helpers.predefined_type( spec="https://drafts.csswg.org/css-animations/#propdef-animation-name", )} -${helpers.predefined_type("animation-duration", - "Time", - "computed::Time::zero()", - initial_specified_value="specified::Time::zero()", - parse_method="parse_non_negative", - vector=True, - need_index=True, - animation_value_type="none", - extra_prefixes=animation_extra_prefixes, - spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration")} +${helpers.predefined_type( + "animation-duration", + "Time", + "computed::Time::zero()", + initial_specified_value="specified::Time::zero()", + parse_method="parse_non_negative", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes=animation_extra_prefixes, + spec="https://drafts.csswg.org/css-transitions/#propdef-transition-duration", +)} // animation-timing-function is the exception to the rule for allowed_in_keyframe_block: // https://drafts.csswg.org/css-animations/#keyframes -${helpers.predefined_type("animation-timing-function", - "TimingFunction", - "computed::TimingFunction::ease()", - initial_specified_value="specified::TimingFunction::ease()", - vector=True, - need_index=True, - animation_value_type="none", - extra_prefixes=animation_extra_prefixes, - allowed_in_keyframe_block=True, - spec="https://drafts.csswg.org/css-transitions/#propdef-animation-timing-function")} +${helpers.predefined_type( + "animation-timing-function", + "TimingFunction", + "computed::TimingFunction::ease()", + initial_specified_value="specified::TimingFunction::ease()", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes=animation_extra_prefixes, + allowed_in_keyframe_block=True, + spec="https://drafts.csswg.org/css-transitions/#propdef-animation-timing-function", +)} ${helpers.predefined_type( "animation-iteration-count", @@ -237,46 +256,54 @@ ${helpers.predefined_type( )} <% animation_direction_custom_consts = { "alternate-reverse": "Alternate_reverse" } %> -${helpers.single_keyword("animation-direction", - "normal reverse alternate alternate-reverse", - need_index=True, - animation_value_type="none", - vector=True, - gecko_enum_prefix="PlaybackDirection", - custom_consts=animation_direction_custom_consts, - extra_prefixes=animation_extra_prefixes, - spec="https://drafts.csswg.org/css-animations/#propdef-animation-direction", - allowed_in_keyframe_block=False)} +${helpers.single_keyword( + "animation-direction", + "normal reverse alternate alternate-reverse", + need_index=True, + animation_value_type="none", + vector=True, + gecko_enum_prefix="PlaybackDirection", + custom_consts=animation_direction_custom_consts, + extra_prefixes=animation_extra_prefixes, + spec="https://drafts.csswg.org/css-animations/#propdef-animation-direction", + allowed_in_keyframe_block=False, +)} -${helpers.single_keyword("animation-play-state", - "running paused", - need_index=True, - animation_value_type="none", - vector=True, - extra_prefixes=animation_extra_prefixes, - spec="https://drafts.csswg.org/css-animations/#propdef-animation-play-state", - allowed_in_keyframe_block=False)} +${helpers.single_keyword( + "animation-play-state", + "running paused", + need_index=True, + animation_value_type="none", + vector=True, + extra_prefixes=animation_extra_prefixes, + spec="https://drafts.csswg.org/css-animations/#propdef-animation-play-state", + allowed_in_keyframe_block=False, +)} -${helpers.single_keyword("animation-fill-mode", - "none forwards backwards both", - need_index=True, - animation_value_type="none", - vector=True, - gecko_enum_prefix="FillMode", - extra_prefixes=animation_extra_prefixes, - spec="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode", - allowed_in_keyframe_block=False)} +${helpers.single_keyword( + "animation-fill-mode", + "none forwards backwards both", + need_index=True, + animation_value_type="none", + vector=True, + gecko_enum_prefix="FillMode", + extra_prefixes=animation_extra_prefixes, + spec="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode", + allowed_in_keyframe_block=False, +)} -${helpers.predefined_type("animation-delay", - "Time", - "computed::Time::zero()", - initial_specified_value="specified::Time::zero()", - vector=True, - need_index=True, - animation_value_type="none", - extra_prefixes=animation_extra_prefixes, - spec="https://drafts.csswg.org/css-animations/#propdef-animation-delay", - allowed_in_keyframe_block=False)} +${helpers.predefined_type( + "animation-delay", + "Time", + "computed::Time::zero()", + initial_specified_value="specified::Time::zero()", + vector=True, + need_index=True, + animation_value_type="none", + extra_prefixes=animation_extra_prefixes, + spec="https://drafts.csswg.org/css-animations/#propdef-animation-delay", + allowed_in_keyframe_block=False, +)} % for axis in ["x", "y"]: ${helpers.predefined_type( @@ -290,14 +317,16 @@ ${helpers.predefined_type("animation-delay", )} % endfor -${helpers.predefined_type("scroll-snap-destination", - "Position", - "computed::Position::zero()", - products="gecko", - gecko_pref="layout.css.scroll-snap.enabled", - boxed=True, - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-destination)", - animation_value_type="discrete")} +${helpers.predefined_type( + "scroll-snap-destination", + "Position", + "computed::Position::zero()", + products="gecko", + gecko_pref="layout.css.scroll-snap.enabled", + boxed=True, + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-destination)", + animation_value_type="discrete", +)} ${helpers.predefined_type( "scroll-snap-coordinate", @@ -308,7 +337,7 @@ ${helpers.predefined_type( gecko_pref="layout.css.scroll-snap.enabled", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-destination)", animation_value_type="discrete", - allow_empty="NotInitial" + allow_empty="NotInitial", )} <% transform_extra_prefixes = "moz:layout.css.prefixes.transforms webkit" %> @@ -323,26 +352,32 @@ ${helpers.predefined_type( flags="CREATES_STACKING_CONTEXT FIXPOS_CB \ GETCS_NEEDS_LAYOUT_FLUSH CAN_ANIMATE_ON_COMPOSITOR", spec="https://drafts.csswg.org/css-transforms/#propdef-transform", - servo_restyle_damage="reflow_out_of_flow" + servo_restyle_damage="reflow_out_of_flow", )} -${helpers.predefined_type("rotate", "Rotate", - "generics::transform::Rotate::None", - animation_value_type="ComputedValue", - boxed=True, - flags="CREATES_STACKING_CONTEXT FIXPOS_CB", - gecko_pref="layout.css.individual-transform.enabled", - spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms", - servo_restyle_damage = "reflow_out_of_flow")} +${helpers.predefined_type( + "rotate", + "Rotate", + "generics::transform::Rotate::None", + animation_value_type="ComputedValue", + boxed=True, + flags="CREATES_STACKING_CONTEXT FIXPOS_CB", + gecko_pref="layout.css.individual-transform.enabled", + spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms", + servo_restyle_damage = "reflow_out_of_flow", +)} -${helpers.predefined_type("scale", "Scale", - "generics::transform::Scale::None", - animation_value_type="ComputedValue", - boxed=True, - flags="CREATES_STACKING_CONTEXT FIXPOS_CB", - gecko_pref="layout.css.individual-transform.enabled", - spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms", - servo_restyle_damage = "reflow_out_of_flow")} +${helpers.predefined_type( + "scale", + "Scale", + "generics::transform::Scale::None", + animation_value_type="ComputedValue", + boxed=True, + flags="CREATES_STACKING_CONTEXT FIXPOS_CB", + gecko_pref="layout.css.individual-transform.enabled", + spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms", + servo_restyle_damage = "reflow_out_of_flow", +)} ${helpers.predefined_type( "translate", @@ -353,7 +388,7 @@ ${helpers.predefined_type( flags="CREATES_STACKING_CONTEXT FIXPOS_CB GETCS_NEEDS_LAYOUT_FLUSH", gecko_pref="layout.css.individual-transform.enabled", spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms", - servo_restyle_damage="reflow_out_of_flow" + servo_restyle_damage="reflow_out_of_flow", )} // Motion Path Module Level 1 @@ -369,12 +404,14 @@ ${helpers.predefined_type( // CSSOM View Module // https://www.w3.org/TR/cssom-view-1/ -${helpers.single_keyword("scroll-behavior", - "auto smooth", - gecko_pref="layout.css.scroll-behavior.property-enabled", - products="gecko", - spec="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior", - animation_value_type="discrete")} +${helpers.single_keyword( + "scroll-behavior", + "auto smooth", + gecko_pref="layout.css.scroll-behavior.property-enabled", + products="gecko", + spec="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior", + animation_value_type="discrete", +)} % for axis in ["x", "y"]: ${helpers.predefined_type( @@ -385,7 +422,7 @@ ${helpers.single_keyword("scroll-behavior", needs_context=False, gecko_pref="layout.css.scroll-snap.enabled", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type-x)", - animation_value_type="discrete" + animation_value_type="discrete", )} % endfor @@ -398,38 +435,48 @@ ${helpers.single_keyword("scroll-behavior", needs_context=False, gecko_pref="layout.css.overscroll-behavior.enabled", spec="https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties", - animation_value_type="discrete" + animation_value_type="discrete", )} % endfor // Compositing and Blending Level 1 // http://www.w3.org/TR/compositing-1/ -${helpers.single_keyword("isolation", - "auto isolate", - products="gecko", - gecko_pref="layout.css.isolation.enabled", - spec="https://drafts.fxtf.org/compositing/#isolation", - flags="CREATES_STACKING_CONTEXT", - animation_value_type="discrete")} +${helpers.single_keyword( + "isolation", + "auto isolate", + products="gecko", + gecko_pref="layout.css.isolation.enabled", + spec="https://drafts.fxtf.org/compositing/#isolation", + flags="CREATES_STACKING_CONTEXT", + animation_value_type="discrete", +)} // TODO add support for logical values recto and verso -${helpers.single_keyword("page-break-after", - "auto always avoid left right", - products="gecko", - spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-after", - animation_value_type="discrete")} -${helpers.single_keyword("page-break-before", - "auto always avoid left right", - products="gecko", - spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-before", - animation_value_type="discrete")} -${helpers.single_keyword("page-break-inside", - "auto avoid", - products="gecko", - gecko_ffi_name="mBreakInside", - gecko_constant_prefix="NS_STYLE_PAGE_BREAK", - spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-inside", - animation_value_type="discrete")} +${helpers.single_keyword( + "page-break-after", + "auto always avoid left right", + products="gecko", + spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-after", + animation_value_type="discrete", +)} + +${helpers.single_keyword( + "page-break-before", + "auto always avoid left right", + products="gecko", + spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-before", + animation_value_type="discrete", +)} + +${helpers.single_keyword( + "page-break-inside", + "auto avoid", + products="gecko", + gecko_ffi_name="mBreakInside", + gecko_constant_prefix="NS_STYLE_PAGE_BREAK", + spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-inside", + animation_value_type="discrete", +)} // CSS Basic User Interface Module Level 3 // http://dev.w3.org/csswg/css-ui @@ -473,20 +520,24 @@ ${helpers.predefined_type( servo_restyle_damage="reflow_out_of_flow" )} -${helpers.single_keyword("backface-visibility", - "visible hidden", - spec="https://drafts.csswg.org/css-transforms/#backface-visibility-property", - extra_prefixes=transform_extra_prefixes, - animation_value_type="discrete")} +${helpers.single_keyword( + "backface-visibility", + "visible hidden", + spec="https://drafts.csswg.org/css-transforms/#backface-visibility-property", + extra_prefixes=transform_extra_prefixes, + animation_value_type="discrete", +)} -${helpers.single_keyword("transform-box", - "border-box fill-box view-box", - gecko_enum_prefix="StyleGeometryBox", - products="gecko", - gecko_pref="svg.transform-box.enabled", - spec="https://drafts.csswg.org/css-transforms/#transform-box", - gecko_inexhaustive="True", - animation_value_type="discrete")} +${helpers.single_keyword( + "transform-box", + "border-box fill-box view-box", + gecko_enum_prefix="StyleGeometryBox", + products="gecko", + gecko_pref="svg.transform-box.enabled", + spec="https://drafts.csswg.org/css-transforms/#transform-box", + gecko_inexhaustive="True", + animation_value_type="discrete", +)} ${helpers.predefined_type( "transform-style", @@ -510,7 +561,7 @@ ${helpers.predefined_type( boxed=True, flags="GETCS_NEEDS_LAYOUT_FLUSH", spec="https://drafts.csswg.org/css-transforms/#transform-origin-property", - servo_restyle_damage="reflow_out_of_flow" + servo_restyle_damage="reflow_out_of_flow", )} ${helpers.predefined_type( @@ -535,19 +586,25 @@ ${helpers.predefined_type( animation_value_type="discrete", )} -${helpers.predefined_type("-moz-binding", "url::UrlOrNone", "computed::url::UrlOrNone::none()", - products="gecko", - animation_value_type="none", - gecko_ffi_name="mBinding", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-binding)")} +${helpers.predefined_type( + "-moz-binding", + "url::UrlOrNone", + "computed::url::UrlOrNone::none()", + products="gecko", + animation_value_type="none", + gecko_ffi_name="mBinding", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-binding)", +)} -${helpers.single_keyword("-moz-orient", - "inline block horizontal vertical", - products="gecko", - gecko_ffi_name="mOrient", - gecko_enum_prefix="StyleOrient", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-orient)", - animation_value_type="discrete")} +${helpers.single_keyword( + "-moz-orient", + "inline block horizontal vertical", + products="gecko", + gecko_ffi_name="mOrient", + gecko_enum_prefix="StyleOrient", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-orient)", + animation_value_type="discrete", +)} ${helpers.predefined_type( "will-change", @@ -555,7 +612,7 @@ ${helpers.predefined_type( "computed::WillChange::auto()", products="gecko", animation_value_type="none", - spec="https://drafts.csswg.org/css-will-change/#will-change" + spec="https://drafts.csswg.org/css-will-change/#will-change", )} ${helpers.predefined_type( diff --git a/components/style/properties/longhands/color.mako.rs b/components/style/properties/longhands/color.mako.rs index a4d1830190a..d53632835b6 100644 --- a/components/style/properties/longhands/color.mako.rs +++ b/components/style/properties/longhands/color.mako.rs @@ -15,7 +15,7 @@ ${helpers.predefined_type( animation_value_type="AnimatedRGBA", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", ignored_when_colors_disabled="True", - spec="https://drafts.csswg.org/css-color/#color" + spec="https://drafts.csswg.org/css-color/#color", )} // FIXME(#15973): Add servo support for system colors @@ -96,8 +96,10 @@ pub mod system_colors { #[inline] fn to_computed_value(&self, cx: &Context) -> Self::ComputedValue { unsafe { - Gecko_GetLookAndFeelSystemColor(*self as i32, - cx.device().pres_context()) + Gecko_GetLookAndFeelSystemColor( + *self as i32, + cx.device().pres_context(), + ) } } diff --git a/components/style/properties/longhands/column.mako.rs b/components/style/properties/longhands/column.mako.rs index e7305e5b4f3..adc9371e99f 100644 --- a/components/style/properties/longhands/column.mako.rs +++ b/components/style/properties/longhands/column.mako.rs @@ -6,16 +6,17 @@ <% data.new_style_struct("Column", inherited=False) %> -${helpers.predefined_type("column-width", - "length::NonNegativeLengthOrAuto", - "Either::Second(Auto)", - initial_specified_value="Either::Second(Auto)", - extra_prefixes="moz", - animation_value_type="NonNegativeLengthOrAuto", - servo_pref="layout.columns.enabled", - spec="https://drafts.csswg.org/css-multicol/#propdef-column-width", - servo_restyle_damage="rebuild_and_reflow")} - +${helpers.predefined_type( + "column-width", + "length::NonNegativeLengthOrAuto", + "Either::Second(Auto)", + initial_specified_value="Either::Second(Auto)", + extra_prefixes="moz", + animation_value_type="NonNegativeLengthOrAuto", + servo_pref="layout.columns.enabled", + spec="https://drafts.csswg.org/css-multicol/#propdef-column-width", + servo_restyle_damage="rebuild_and_reflow", +)} ${helpers.predefined_type( "column-count", @@ -29,22 +30,27 @@ ${helpers.predefined_type( servo_restyle_damage="rebuild_and_reflow", )} +${helpers.single_keyword( + "column-fill", + "balance auto", + extra_prefixes="moz", + products="gecko", + animation_value_type="discrete", + gecko_enum_prefix="StyleColumnFill", + spec="https://drafts.csswg.org/css-multicol/#propdef-column-fill", +)} - -${helpers.single_keyword("column-fill", "balance auto", extra_prefixes="moz", - products="gecko", animation_value_type="discrete", - gecko_enum_prefix="StyleColumnFill", - spec="https://drafts.csswg.org/css-multicol/#propdef-column-fill")} - -${helpers.predefined_type("column-rule-width", - "BorderSideWidth", - "::values::computed::NonNegativeLength::new(3.)", - initial_specified_value="specified::BorderSideWidth::Medium", - computed_type="::values::computed::NonNegativeLength", - products="gecko", - spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width", - animation_value_type="NonNegativeLength", - extra_prefixes="moz")} +${helpers.predefined_type( + "column-rule-width", + "BorderSideWidth", + "::values::computed::NonNegativeLength::new(3.)", + initial_specified_value="specified::BorderSideWidth::Medium", + computed_type="::values::computed::NonNegativeLength", + products="gecko", + spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width", + animation_value_type="NonNegativeLength", + extra_prefixes="moz", +)} // https://drafts.csswg.org/css-multicol-1/#crc ${helpers.predefined_type( @@ -59,16 +65,23 @@ ${helpers.predefined_type( spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color", )} -${helpers.single_keyword("column-span", "none all", - products="gecko", animation_value_type="discrete", - gecko_enum_prefix="StyleColumnSpan", - gecko_pref="layout.css.column-span.enabled", - spec="https://drafts.csswg.org/css-multicol/#propdef-column-span", - extra_prefixes="moz:layout.css.column-span.enabled")} +${helpers.single_keyword( + "column-span", + "none all", + products="gecko", + animation_value_type="discrete", + gecko_enum_prefix="StyleColumnSpan", + gecko_pref="layout.css.column-span.enabled", + spec="https://drafts.csswg.org/css-multicol/#propdef-column-span", + extra_prefixes="moz:layout.css.column-span.enabled", +)} -${helpers.single_keyword("column-rule-style", - "none hidden dotted dashed solid double groove ridge inset outset", - products="gecko", extra_prefixes="moz", - gecko_constant_prefix="NS_STYLE_BORDER_STYLE", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-style")} +${helpers.single_keyword( + "column-rule-style", + "none hidden dotted dashed solid double groove ridge inset outset", + products="gecko", + extra_prefixes="moz", + gecko_constant_prefix="NS_STYLE_BORDER_STYLE", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-style", +)} diff --git a/components/style/properties/longhands/counters.mako.rs b/components/style/properties/longhands/counters.mako.rs index ececf58310f..4a8fd0bb6d1 100644 --- a/components/style/properties/longhands/counters.mako.rs +++ b/components/style/properties/longhands/counters.mako.rs @@ -6,13 +6,15 @@ <% data.new_style_struct("Counters", inherited=False, gecko_name="Content") %> -${helpers.predefined_type("content", - "Content", - "computed::Content::normal()", - initial_specified_value="specified::Content::normal()", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-content/#propdef-content", - servo_restyle_damage="rebuild_and_reflow")} +${helpers.predefined_type( + "content", + "Content", + "computed::Content::normal()", + initial_specified_value="specified::Content::normal()", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-content/#propdef-content", + servo_restyle_damage="rebuild_and_reflow", +)} ${helpers.predefined_type( "counter-increment", @@ -20,7 +22,7 @@ ${helpers.predefined_type( initial_value="Default::default()", animation_value_type="discrete", spec="https://drafts.csswg.org/css-lists/#propdef-counter-increment", - servo_restyle_damage="rebuild_and_reflow" + servo_restyle_damage="rebuild_and_reflow", )} ${helpers.predefined_type( @@ -29,5 +31,5 @@ ${helpers.predefined_type( initial_value="Default::default()", animation_value_type="discrete", spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset", - servo_restyle_damage="rebuild_and_reflow" + servo_restyle_damage="rebuild_and_reflow", )} diff --git a/components/style/properties/longhands/effects.mako.rs b/components/style/properties/longhands/effects.mako.rs index 674340c391a..6649dc4d55b 100644 --- a/components/style/properties/longhands/effects.mako.rs +++ b/components/style/properties/longhands/effects.mako.rs @@ -15,7 +15,7 @@ ${helpers.predefined_type( flags="CREATES_STACKING_CONTEXT APPLIES_TO_PLACEHOLDER \ CAN_ANIMATE_ON_COMPOSITOR", spec="https://drafts.csswg.org/css-color/#opacity", - servo_restyle_damage = "reflow_out_of_flow" + servo_restyle_damage = "reflow_out_of_flow", )} ${helpers.predefined_type( @@ -31,13 +31,15 @@ ${helpers.predefined_type( spec="https://drafts.csswg.org/css-backgrounds/#box-shadow", )} -${helpers.predefined_type("clip", - "ClipRectOrAuto", - "computed::ClipRectOrAuto::auto()", - animation_value_type="ComputedValue", - boxed=True, - allow_quirks=True, - spec="https://drafts.fxtf.org/css-masking/#clip-property")} +${helpers.predefined_type( + "clip", + "ClipRectOrAuto", + "computed::ClipRectOrAuto::auto()", + animation_value_type="ComputedValue", + boxed=True, + allow_quirks=True, + spec="https://drafts.fxtf.org/css-masking/#clip-property", +)} ${helpers.predefined_type( "filter", @@ -52,11 +54,14 @@ ${helpers.predefined_type( spec="https://drafts.fxtf.org/filters/#propdef-filter", )} -${helpers.single_keyword("mix-blend-mode", - """normal multiply screen overlay darken lighten color-dodge - color-burn hard-light soft-light difference exclusion hue - saturation color luminosity""", gecko_constant_prefix="NS_STYLE_BLEND", - animation_value_type="discrete", - flags="CREATES_STACKING_CONTEXT", - gecko_pref="layout.css.mix-blend-mode.enabled", - spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode")} +${helpers.single_keyword( + "mix-blend-mode", + """normal multiply screen overlay darken lighten color-dodge + color-burn hard-light soft-light difference exclusion hue + saturation color luminosity""", + gecko_constant_prefix="NS_STYLE_BLEND", + animation_value_type="discrete", + flags="CREATES_STACKING_CONTEXT", + gecko_pref="layout.css.mix-blend-mode.enabled", + spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode", +)} diff --git a/components/style/properties/longhands/inherited_svg.mako.rs b/components/style/properties/longhands/inherited_svg.mako.rs index b3bc12b1367..40c61b10129 100644 --- a/components/style/properties/longhands/inherited_svg.mako.rs +++ b/components/style/properties/longhands/inherited_svg.mako.rs @@ -6,61 +6,81 @@ // SVG 1.1 (Second Edition) // https://www.w3.org/TR/SVG/ -<% data.new_style_struct("InheritedSVG", - inherited=True, - gecko_name="SVG") %> +<% data.new_style_struct("InheritedSVG", inherited=True, gecko_name="SVG") %> // Section 10 - Text -${helpers.single_keyword("text-anchor", - "start middle end", - products="gecko", - animation_value_type="discrete", - spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty")} +${helpers.single_keyword( + "text-anchor", + "start middle end", + products="gecko", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty", +)} // Section 11 - Painting: Filling, Stroking and Marker Symbols -${helpers.single_keyword("color-interpolation", - "srgb auto linearrgb", - products="gecko", - animation_value_type="discrete", - spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty")} +${helpers.single_keyword( + "color-interpolation", + "srgb auto linearrgb", + products="gecko", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationProperty", +)} -${helpers.single_keyword("color-interpolation-filters", "linearrgb auto srgb", - products="gecko", - gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION", - animation_value_type="discrete", - spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty")} +${helpers.single_keyword( + "color-interpolation-filters", + "linearrgb auto srgb", + products="gecko", + gecko_constant_prefix="NS_STYLE_COLOR_INTERPOLATION", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVG11/painting.html#ColorInterpolationFiltersProperty", +)} ${helpers.predefined_type( - "fill", "SVGPaint", + "fill", + "SVGPaint", "::values::computed::SVGPaint::black()", products="gecko", animation_value_type="IntermediateSVGPaint", boxed=True, - spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint")} - -${helpers.predefined_type("fill-opacity", "SVGOpacity", "Default::default()", - products="gecko", animation_value_type="ComputedValue", - spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty")} - -${helpers.single_keyword("fill-rule", "nonzero evenodd", - gecko_enum_prefix="StyleFillRule", - products="gecko", animation_value_type="discrete", - spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty")} - -${helpers.single_keyword("shape-rendering", - "auto optimizespeed crispedges geometricprecision", - products="gecko", - animation_value_type="discrete", - spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty")} + spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingFillPaint", +)} ${helpers.predefined_type( - "stroke", "SVGPaint", + "fill-opacity", + "SVGOpacity", + "Default::default()", + products="gecko", + animation_value_type="ComputedValue", + spec="https://www.w3.org/TR/SVG11/painting.html#FillOpacityProperty", +)} + +${helpers.single_keyword( + "fill-rule", + "nonzero evenodd", + gecko_enum_prefix="StyleFillRule", + products="gecko", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty", +)} + +${helpers.single_keyword( + "shape-rendering", + "auto optimizespeed crispedges geometricprecision", + products="gecko", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty", +)} + +${helpers.predefined_type( + "stroke", + "SVGPaint", "Default::default()", products="gecko", animation_value_type="IntermediateSVGPaint", boxed=True, - spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint")} + spec="https://www.w3.org/TR/SVG2/painting.html#SpecifyingStrokePaint", +)} ${helpers.predefined_type( "stroke-width", "SVGWidth", @@ -70,23 +90,39 @@ ${helpers.predefined_type( spec="https://www.w3.org/TR/SVG2/painting.html#StrokeWidth", )} -${helpers.single_keyword("stroke-linecap", "butt round square", - products="gecko", animation_value_type="discrete", - spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty")} +${helpers.single_keyword( + "stroke-linecap", + "butt round square", + products="gecko", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty", +)} -${helpers.single_keyword("stroke-linejoin", "miter round bevel", - products="gecko", animation_value_type="discrete", - spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty")} +${helpers.single_keyword( + "stroke-linejoin", + "miter round bevel", + products="gecko", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty", +)} -${helpers.predefined_type("stroke-miterlimit", "GreaterThanOrEqualToOneNumber", - "From::from(4.0)", - products="gecko", - animation_value_type="::values::computed::GreaterThanOrEqualToOneNumber", - spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty")} +${helpers.predefined_type( + "stroke-miterlimit", + "GreaterThanOrEqualToOneNumber", + "From::from(4.0)", + products="gecko", + animation_value_type="::values::computed::GreaterThanOrEqualToOneNumber", + spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty", +)} -${helpers.predefined_type("stroke-opacity", "SVGOpacity", "Default::default()", - products="gecko", animation_value_type="ComputedValue", - spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty")} +${helpers.predefined_type( + "stroke-opacity", + "SVGOpacity", + "Default::default()", + products="gecko", + animation_value_type="ComputedValue", + spec="https://www.w3.org/TR/SVG11/painting.html#StrokeOpacityProperty", +)} ${helpers.predefined_type( "stroke-dasharray", @@ -106,38 +142,59 @@ ${helpers.predefined_type( )} // Section 14 - Clipping, Masking and Compositing -${helpers.single_keyword("clip-rule", "nonzero evenodd", - products="gecko", - gecko_enum_prefix="StyleFillRule", - animation_value_type="discrete", - spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty")} +${helpers.single_keyword( + "clip-rule", + "nonzero evenodd", + products="gecko", + gecko_enum_prefix="StyleFillRule", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty", +)} -${helpers.predefined_type("marker-start", "url::UrlOrNone", "computed::url::UrlOrNone::none()", - products="gecko", - animation_value_type="discrete", - spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")} +${helpers.predefined_type( + "marker-start", + "url::UrlOrNone", + "computed::url::UrlOrNone::none()", + products="gecko", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties", +)} -${helpers.predefined_type("marker-mid", "url::UrlOrNone", "computed::url::UrlOrNone::none()", - products="gecko", - animation_value_type="discrete", - spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")} +${helpers.predefined_type( + "marker-mid", + "url::UrlOrNone", + "computed::url::UrlOrNone::none()", + products="gecko", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties", +)} -${helpers.predefined_type("marker-end", "url::UrlOrNone", "computed::url::UrlOrNone::none()", - products="gecko", - animation_value_type="discrete", - spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties")} +${helpers.predefined_type( + "marker-end", + "url::UrlOrNone", + "computed::url::UrlOrNone::none()", + products="gecko", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVG2/painting.html#VertexMarkerProperties", +)} -${helpers.predefined_type("paint-order", "SVGPaintOrder", "computed::SVGPaintOrder::normal()", - products="gecko", - animation_value_type="discrete", - spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder")} +${helpers.predefined_type( + "paint-order", + "SVGPaintOrder", + "computed::SVGPaintOrder::normal()", + products="gecko", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVG2/painting.html#PaintOrder", +)} -${helpers.predefined_type("-moz-context-properties", - "MozContextProperties", - initial_value=None, - vector=True, - need_index=True, - animation_value_type="none", - products="gecko", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)", - allow_empty=True)} +${helpers.predefined_type( + "-moz-context-properties", + "MozContextProperties", + initial_value=None, + vector=True, + need_index=True, + animation_value_type="none", + products="gecko", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)", + allow_empty=True, +)} diff --git a/components/style/properties/longhands/inherited_table.mako.rs b/components/style/properties/longhands/inherited_table.mako.rs index 4f55d9b1fa5..177907dc8f7 100644 --- a/components/style/properties/longhands/inherited_table.mako.rs +++ b/components/style/properties/longhands/inherited_table.mako.rs @@ -6,27 +6,40 @@ <% data.new_style_struct("InheritedTable", inherited=True, gecko_name="TableBorder") %> -${helpers.single_keyword("border-collapse", "separate collapse", - gecko_constant_prefix="NS_STYLE_BORDER", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-tables/#propdef-border-collapse", - servo_restyle_damage = "reflow")} -${helpers.single_keyword("empty-cells", "show hide", - gecko_constant_prefix="NS_STYLE_TABLE_EMPTY_CELLS", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-tables/#propdef-empty-cells", - servo_restyle_damage="rebuild_and_reflow")} -${helpers.single_keyword("caption-side", "top bottom", - extra_gecko_values="right left top-outside bottom-outside", - needs_conversion="True", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-tables/#propdef-caption-side", - servo_restyle_damage="rebuild_and_reflow")} +${helpers.single_keyword( + "border-collapse", + "separate collapse", + gecko_constant_prefix="NS_STYLE_BORDER", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-tables/#propdef-border-collapse", + servo_restyle_damage = "reflow", +)} -${helpers.predefined_type("border-spacing", - "BorderSpacing", - "computed::BorderSpacing::zero()", - animation_value_type="BorderSpacing", - boxed=True, - spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing", - servo_restyle_damage = "reflow")} +${helpers.single_keyword( + "empty-cells", + "show hide", + gecko_constant_prefix="NS_STYLE_TABLE_EMPTY_CELLS", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-tables/#propdef-empty-cells", + servo_restyle_damage="rebuild_and_reflow", +)} + +${helpers.single_keyword( + "caption-side", + "top bottom", + extra_gecko_values="right left top-outside bottom-outside", + needs_conversion="True", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-tables/#propdef-caption-side", + servo_restyle_damage="rebuild_and_reflow", +)} + +${helpers.predefined_type( + "border-spacing", + "BorderSpacing", + "computed::BorderSpacing::zero()", + animation_value_type="BorderSpacing", + boxed=True, + spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing", + servo_restyle_damage = "reflow", +)} diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs index 62819a78cbc..de7b613a183 100644 --- a/components/style/properties/longhands/inherited_text.mako.rs +++ b/components/style/properties/longhands/inherited_text.mako.rs @@ -20,51 +20,68 @@ ${helpers.predefined_type( // CSS Text Module Level 3 // TODO(pcwalton): `full-width` -${helpers.single_keyword("text-transform", - "none capitalize uppercase lowercase", - extra_gecko_values="full-width", - animation_value_type="discrete", - flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", - spec="https://drafts.csswg.org/css-text/#propdef-text-transform", - servo_restyle_damage="rebuild_and_reflow")} +${helpers.single_keyword( + "text-transform", + "none capitalize uppercase lowercase", + extra_gecko_values="full-width", + animation_value_type="discrete", + flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", + spec="https://drafts.csswg.org/css-text/#propdef-text-transform", + servo_restyle_damage="rebuild_and_reflow", +)} -${helpers.single_keyword("hyphens", "manual none auto", - gecko_enum_prefix="StyleHyphens", - products="gecko", animation_value_type="discrete", extra_prefixes="moz", - spec="https://drafts.csswg.org/css-text/#propdef-hyphens")} +${helpers.single_keyword( + "hyphens", + "manual none auto", + gecko_enum_prefix="StyleHyphens", + products="gecko", + animation_value_type="discrete", + extra_prefixes="moz", + spec="https://drafts.csswg.org/css-text/#propdef-hyphens", +)} // TODO: Support -${helpers.single_keyword("-moz-text-size-adjust", "auto none", - gecko_constant_prefix="NS_STYLE_TEXT_SIZE_ADJUST", - gecko_ffi_name="mTextSizeAdjust", - products="gecko", animation_value_type="discrete", - spec="https://drafts.csswg.org/css-size-adjust/#adjustment-control", - alias="-webkit-text-size-adjust")} +${helpers.single_keyword( + "-moz-text-size-adjust", + "auto none", + gecko_constant_prefix="NS_STYLE_TEXT_SIZE_ADJUST", + gecko_ffi_name="mTextSizeAdjust", + products="gecko", animation_value_type="discrete", + spec="https://drafts.csswg.org/css-size-adjust/#adjustment-control", + alias="-webkit-text-size-adjust", +)} -${helpers.predefined_type("text-indent", - "LengthOrPercentage", - "computed::LengthOrPercentage::Length(computed::Length::new(0.))", - animation_value_type="ComputedValue", - spec="https://drafts.csswg.org/css-text/#propdef-text-indent", - allow_quirks=True, servo_restyle_damage = "reflow")} +${helpers.predefined_type( + "text-indent", + "LengthOrPercentage", + "computed::LengthOrPercentage::Length(computed::Length::new(0.))", + animation_value_type="ComputedValue", + spec="https://drafts.csswg.org/css-text/#propdef-text-indent", + allow_quirks=True, + servo_restyle_damage = "reflow", +)} // Also known as "word-wrap" (which is more popular because of IE), but this is the preferred // name per CSS-TEXT 6.2. -${helpers.single_keyword("overflow-wrap", - "normal break-word", - gecko_constant_prefix="NS_STYLE_OVERFLOWWRAP", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-text/#propdef-overflow-wrap", - alias="word-wrap", - servo_restyle_damage="rebuild_and_reflow")} +${helpers.single_keyword( + "overflow-wrap", + "normal break-word", + gecko_constant_prefix="NS_STYLE_OVERFLOWWRAP", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-text/#propdef-overflow-wrap", + alias="word-wrap", + servo_restyle_damage="rebuild_and_reflow", +)} // TODO(pcwalton): Support `word-break: keep-all` once we have better CJK support. -${helpers.single_keyword("word-break", - "normal break-all keep-all", - gecko_constant_prefix="NS_STYLE_WORDBREAK", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-text/#propdef-word-break", - servo_restyle_damage="rebuild_and_reflow")} +${helpers.single_keyword( + "word-break", + "normal break-all keep-all", + gecko_constant_prefix="NS_STYLE_WORDBREAK", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-text/#propdef-word-break", + servo_restyle_damage="rebuild_and_reflow", +)} // TODO(pcwalton): Support `text-justify: distribute`. <%helpers:single_keyword @@ -106,39 +123,45 @@ ${helpers.single_keyword("word-break", % endif -${helpers.single_keyword("text-align-last", - "auto start end left right center justify", - products="gecko", - gecko_constant_prefix="NS_STYLE_TEXT_ALIGN", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-text/#propdef-text-align-last")} +${helpers.single_keyword( + "text-align-last", + "auto start end left right center justify", + products="gecko", + gecko_constant_prefix="NS_STYLE_TEXT_ALIGN", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-text/#propdef-text-align-last", +)} // TODO make this a shorthand and implement text-align-last/text-align-all -// -// FIXME(emilio): This can't really be that complicated. -${helpers.predefined_type("text-align", - "TextAlign", - "computed::TextAlign::start()", - animation_value_type="discrete", - flags="APPLIES_TO_PLACEHOLDER", - spec="https://drafts.csswg.org/css-text/#propdef-text-align", - servo_restyle_damage = "reflow")} +${helpers.predefined_type( + "text-align", + "TextAlign", + "computed::TextAlign::start()", + animation_value_type="discrete", + flags="APPLIES_TO_PLACEHOLDER", + spec="https://drafts.csswg.org/css-text/#propdef-text-align", + servo_restyle_damage = "reflow", +)} -${helpers.predefined_type("letter-spacing", - "LetterSpacing", - "computed::LetterSpacing::normal()", - animation_value_type="ComputedValue", - flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", - spec="https://drafts.csswg.org/css-text/#propdef-letter-spacing", - servo_restyle_damage="rebuild_and_reflow")} +${helpers.predefined_type( + "letter-spacing", + "LetterSpacing", + "computed::LetterSpacing::normal()", + animation_value_type="ComputedValue", + flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", + spec="https://drafts.csswg.org/css-text/#propdef-letter-spacing", + servo_restyle_damage="rebuild_and_reflow", +)} -${helpers.predefined_type("word-spacing", - "WordSpacing", - "computed::WordSpacing::normal()", - animation_value_type="ComputedValue", - flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", - spec="https://drafts.csswg.org/css-text/#propdef-word-spacing", - servo_restyle_damage="rebuild_and_reflow")} +${helpers.predefined_type( + "word-spacing", + "WordSpacing", + "computed::WordSpacing::normal()", + animation_value_type="ComputedValue", + flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", + spec="https://drafts.csswg.org/css-text/#propdef-word-spacing", + servo_restyle_damage="rebuild_and_reflow", +)} <%helpers:single_keyword name="white-space" @@ -267,47 +290,65 @@ ${helpers.predefined_type( spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-color", )} -${helpers.predefined_type("-webkit-text-stroke-width", - "BorderSideWidth", - "::values::computed::NonNegativeLength::new(0.)", - initial_specified_value="specified::BorderSideWidth::Length(specified::Length::zero())", - computed_type="::values::computed::NonNegativeLength", - products="gecko", - gecko_pref="layout.css.prefixes.webkit", - flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", - spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-width", - animation_value_type="discrete")} +${helpers.predefined_type( + "-webkit-text-stroke-width", + "BorderSideWidth", + "::values::computed::NonNegativeLength::new(0.)", + initial_specified_value="specified::BorderSideWidth::Length(specified::Length::zero())", + computed_type="::values::computed::NonNegativeLength", + products="gecko", + gecko_pref="layout.css.prefixes.webkit", + flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", + spec="https://compat.spec.whatwg.org/#the-webkit-text-stroke-width", + animation_value_type="discrete", +)} // CSS Ruby Layout Module Level 1 // https://drafts.csswg.org/css-ruby/ -${helpers.single_keyword("ruby-align", "space-around start center space-between", - products="gecko", animation_value_type="discrete", - spec="https://drafts.csswg.org/css-ruby/#ruby-align-property")} +${helpers.single_keyword( + "ruby-align", + "space-around start center space-between", + products="gecko", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-ruby/#ruby-align-property", +)} -${helpers.single_keyword("ruby-position", "over under", - products="gecko", animation_value_type="discrete", - spec="https://drafts.csswg.org/css-ruby/#ruby-position-property")} +${helpers.single_keyword( + "ruby-position", + "over under", + products="gecko", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-ruby/#ruby-position-property", +)} // CSS Writing Modes Module Level 3 // https://drafts.csswg.org/css-writing-modes-3/ -${helpers.single_keyword("text-combine-upright", "none all", - products="gecko", animation_value_type="discrete", - spec="https://drafts.csswg.org/css-writing-modes-3/#text-combine-upright")} +${helpers.single_keyword( + "text-combine-upright", + "none all", + products="gecko", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-writing-modes-3/#text-combine-upright", +)} // SVG 1.1: Section 11 - Painting: Filling, Stroking and Marker Symbols -${helpers.single_keyword("text-rendering", - "auto optimizespeed optimizelegibility geometricprecision", - animation_value_type="discrete", - spec="https://www.w3.org/TR/SVG11/painting.html#TextRenderingProperty", - servo_restyle_damage="rebuild_and_reflow")} +${helpers.single_keyword( + "text-rendering", + "auto optimizespeed optimizelegibility geometricprecision", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVG11/painting.html#TextRenderingProperty", + servo_restyle_damage="rebuild_and_reflow", +)} // FIXME Firefox expects the initial value of this property to change depending // on the value of the layout.css.control-characters.visible pref. -${helpers.single_keyword("-moz-control-character-visibility", - "hidden visible", - gecko_constant_prefix="NS_STYLE_CONTROL_CHARACTER_VISIBILITY", - gecko_ffi_name="mControlCharacterVisibility", - animation_value_type="none", - products="gecko", - spec="Nonstandard")} +${helpers.single_keyword( + "-moz-control-character-visibility", + "hidden visible", + gecko_constant_prefix="NS_STYLE_CONTROL_CHARACTER_VISIBILITY", + gecko_ffi_name="mControlCharacterVisibility", + animation_value_type="none", + products="gecko", + spec="Nonstandard", +)} diff --git a/components/style/properties/longhands/inherited_ui.mako.rs b/components/style/properties/longhands/inherited_ui.mako.rs index e597f97ce8c..0b2b590cf7f 100644 --- a/components/style/properties/longhands/inherited_ui.mako.rs +++ b/components/style/properties/longhands/inherited_ui.mako.rs @@ -6,40 +6,56 @@ <% data.new_style_struct("InheritedUI", inherited=True, gecko_name="UI") %> -${helpers.predefined_type("cursor", - "Cursor", - "computed::Cursor::auto()", - initial_specified_value="specified::Cursor::auto()", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-ui/#cursor")} +${helpers.predefined_type( + "cursor", + "Cursor", + "computed::Cursor::auto()", + initial_specified_value="specified::Cursor::auto()", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-ui/#cursor", +)} // NB: `pointer-events: auto` (and use of `pointer-events` in anything that isn't SVG, in fact) // is nonstandard, slated for CSS4-UI. // TODO(pcwalton): SVG-only values. -${helpers.single_keyword("pointer-events", "auto none", animation_value_type="discrete", - extra_gecko_values="visiblepainted visiblefill visiblestroke visible painted fill stroke all", - flags="APPLIES_TO_PLACEHOLDER", - spec="https://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty")} +${helpers.single_keyword( + "pointer-events", + "auto none", + animation_value_type="discrete", + extra_gecko_values="visiblepainted visiblefill visiblestroke visible painted fill stroke all", + flags="APPLIES_TO_PLACEHOLDER", + spec="https://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty", +)} -${helpers.single_keyword("-moz-user-input", "auto none", - products="gecko", gecko_ffi_name="mUserInput", - gecko_enum_prefix="StyleUserInput", - animation_value_type="discrete", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-input)")} +${helpers.single_keyword( + "-moz-user-input", + "auto none", + products="gecko", + gecko_ffi_name="mUserInput", + gecko_enum_prefix="StyleUserInput", + animation_value_type="discrete", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-input)", +)} -${helpers.single_keyword("-moz-user-modify", "read-only read-write write-only", - products="gecko", gecko_ffi_name="mUserModify", - gecko_enum_prefix="StyleUserModify", - needs_conversion=True, - animation_value_type="discrete", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-modify)")} +${helpers.single_keyword( + "-moz-user-modify", + "read-only read-write write-only", + products="gecko", + gecko_ffi_name="mUserModify", + gecko_enum_prefix="StyleUserModify", + needs_conversion=True, + animation_value_type="discrete", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-modify)", +)} -${helpers.single_keyword("-moz-user-focus", - "none ignore normal select-after select-before select-menu select-same select-all", - products="gecko", gecko_ffi_name="mUserFocus", - gecko_enum_prefix="StyleUserFocus", - animation_value_type="discrete", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-focus)")} +${helpers.single_keyword( + "-moz-user-focus", + "none ignore normal select-after select-before select-menu select-same select-all", + products="gecko", gecko_ffi_name="mUserFocus", + gecko_enum_prefix="StyleUserFocus", + animation_value_type="discrete", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-user-focus)", +)} ${helpers.predefined_type( "caret-color", diff --git a/components/style/properties/longhands/list.mako.rs b/components/style/properties/longhands/list.mako.rs index 5ed1ea44831..732275fb03e 100644 --- a/components/style/properties/longhands/list.mako.rs +++ b/components/style/properties/longhands/list.mako.rs @@ -40,25 +40,31 @@ ${helpers.single_keyword("list-style-position", "outside inside", animation_valu )} % endif -${helpers.predefined_type("list-style-image", - "url::ImageUrlOrNone", - initial_value="computed::url::ImageUrlOrNone::none()", - initial_specified_value="specified::url::ImageUrlOrNone::none()", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image", - servo_restyle_damage="rebuild_and_reflow")} +${helpers.predefined_type( + "list-style-image", + "url::ImageUrlOrNone", + initial_value="computed::url::ImageUrlOrNone::none()", + initial_specified_value="specified::url::ImageUrlOrNone::none()", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image", + servo_restyle_damage="rebuild_and_reflow", +)} -${helpers.predefined_type("quotes", - "Quotes", - "computed::Quotes::get_initial_value()", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-content/#propdef-quotes", - servo_restyle_damage="rebuild_and_reflow")} +${helpers.predefined_type( + "quotes", + "Quotes", + "computed::Quotes::get_initial_value()", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-content/#propdef-quotes", + servo_restyle_damage="rebuild_and_reflow", +)} -${helpers.predefined_type("-moz-image-region", - "ClipRectOrAuto", - "computed::ClipRectOrAuto::auto()", - animation_value_type="ComputedValue", - products="gecko", - boxed=True, - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-image-region)")} +${helpers.predefined_type( + "-moz-image-region", + "ClipRectOrAuto", + "computed::ClipRectOrAuto::auto()", + animation_value_type="ComputedValue", + products="gecko", + boxed=True, + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-image-region)", +)} diff --git a/components/style/properties/longhands/outline.mako.rs b/components/style/properties/longhands/outline.mako.rs index 9446745ec3e..a61aae06d7c 100644 --- a/components/style/properties/longhands/outline.mako.rs +++ b/components/style/properties/longhands/outline.mako.rs @@ -29,24 +29,34 @@ ${helpers.predefined_type( spec="https://drafts.csswg.org/css-ui/#propdef-outline-style", )} -${helpers.predefined_type("outline-width", - "BorderSideWidth", - "::values::computed::NonNegativeLength::new(3.)", - initial_specified_value="specified::BorderSideWidth::Medium", - computed_type="::values::computed::NonNegativeLength", - animation_value_type="NonNegativeLength", - spec="https://drafts.csswg.org/css-ui/#propdef-outline-width")} +${helpers.predefined_type( + "outline-width", + "BorderSideWidth", + "::values::computed::NonNegativeLength::new(3.)", + initial_specified_value="specified::BorderSideWidth::Medium", + computed_type="::values::computed::NonNegativeLength", + animation_value_type="NonNegativeLength", + spec="https://drafts.csswg.org/css-ui/#propdef-outline-width", +)} // The -moz-outline-radius-* properties are non-standard and not on a standards track. % for corner in ["topleft", "topright", "bottomright", "bottomleft"]: - ${helpers.predefined_type("-moz-outline-radius-" + corner, "BorderCornerRadius", + ${helpers.predefined_type( + "-moz-outline-radius-" + corner, + "BorderCornerRadius", "computed::BorderCornerRadius::zero()", products="gecko", boxed=True, animation_value_type="BorderCornerRadius", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)")} + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)", + )} % endfor -${helpers.predefined_type("outline-offset", "Length", "::values::computed::Length::new(0.)", - products="servo gecko", animation_value_type="ComputedValue", - spec="https://drafts.csswg.org/css-ui/#propdef-outline-offset")} +${helpers.predefined_type( + "outline-offset", + "Length", + "::values::computed::Length::new(0.)", + products="servo gecko", + animation_value_type="ComputedValue", + spec="https://drafts.csswg.org/css-ui/#propdef-outline-offset", +)} diff --git a/components/style/properties/longhands/position.mako.rs b/components/style/properties/longhands/position.mako.rs index 957335d32b2..5ec34961d22 100644 --- a/components/style/properties/longhands/position.mako.rs +++ b/components/style/properties/longhands/position.mako.rs @@ -68,73 +68,93 @@ ${helpers.predefined_type( // http://www.w3.org/TR/css3-flexbox/ // Flex container properties -${helpers.single_keyword("flex-direction", "row row-reverse column column-reverse", - spec="https://drafts.csswg.org/css-flexbox/#flex-direction-property", - extra_prefixes="webkit", - animation_value_type="discrete", - servo_restyle_damage = "reflow")} +${helpers.single_keyword( + "flex-direction", + "row row-reverse column column-reverse", + spec="https://drafts.csswg.org/css-flexbox/#flex-direction-property", + extra_prefixes="webkit", + animation_value_type="discrete", + servo_restyle_damage = "reflow", +)} -${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse", - spec="https://drafts.csswg.org/css-flexbox/#flex-wrap-property", - extra_prefixes="webkit", - animation_value_type="discrete", - servo_restyle_damage = "reflow")} +${helpers.single_keyword( + "flex-wrap", + "nowrap wrap wrap-reverse", + spec="https://drafts.csswg.org/css-flexbox/#flex-wrap-property", + extra_prefixes="webkit", + animation_value_type="discrete", + servo_restyle_damage = "reflow", +)} % if product == "servo": // FIXME: Update Servo to support the same Syntax as Gecko. - ${helpers.single_keyword("justify-content", "flex-start stretch flex-end center space-between space-around", - extra_prefixes="webkit", - spec="https://drafts.csswg.org/css-align/#propdef-justify-content", - animation_value_type="discrete", - servo_restyle_damage = "reflow")} + ${helpers.single_keyword( + "justify-content", + "flex-start stretch flex-end center space-between space-around", + extra_prefixes="webkit", + spec="https://drafts.csswg.org/css-align/#propdef-justify-content", + animation_value_type="discrete", + servo_restyle_damage = "reflow", + )} % else: - ${helpers.predefined_type(name="justify-content", - type="JustifyContent", - initial_value="specified::JustifyContent(specified::ContentDistribution::normal())", - spec="https://drafts.csswg.org/css-align/#propdef-justify-content", - extra_prefixes="webkit", - animation_value_type="discrete", - servo_restyle_damage = "reflow")} + ${helpers.predefined_type( + "justify-content", + "JustifyContent", + "specified::JustifyContent(specified::ContentDistribution::normal())", + spec="https://drafts.csswg.org/css-align/#propdef-justify-content", + extra_prefixes="webkit", + animation_value_type="discrete", + servo_restyle_damage="reflow", + )} % endif % if product == "servo": // FIXME: Update Servo to support the same Syntax as Gecko. - ${helpers.single_keyword("align-content", "stretch flex-start flex-end center space-between space-around", - extra_prefixes="webkit", - spec="https://drafts.csswg.org/css-align/#propdef-align-content", - animation_value_type="discrete", - servo_restyle_damage = "reflow")} + ${helpers.single_keyword( + "align-content", + "stretch flex-start flex-end center space-between space-around", + extra_prefixes="webkit", + spec="https://drafts.csswg.org/css-align/#propdef-align-content", + animation_value_type="discrete", + servo_restyle_damage="reflow", + )} - ${helpers.single_keyword("align-items", - "stretch flex-start flex-end center baseline", - extra_prefixes="webkit", - spec="https://drafts.csswg.org/css-flexbox/#align-items-property", - animation_value_type="discrete", - servo_restyle_damage = "reflow")} + ${helpers.single_keyword( + "align-items", + "stretch flex-start flex-end center baseline", + extra_prefixes="webkit", + spec="https://drafts.csswg.org/css-flexbox/#align-items-property", + animation_value_type="discrete", + servo_restyle_damage="reflow", + )} % else: - ${helpers.predefined_type(name="align-content", - type="AlignContent", - initial_value="specified::AlignContent(specified::ContentDistribution::normal())", - spec="https://drafts.csswg.org/css-align/#propdef-align-content", - extra_prefixes="webkit", - animation_value_type="discrete", - servo_restyle_damage = "reflow")} + ${helpers.predefined_type( + "align-content", + "AlignContent", + "specified::AlignContent(specified::ContentDistribution::normal())", + spec="https://drafts.csswg.org/css-align/#propdef-align-content", + extra_prefixes="webkit", + animation_value_type="discrete", + servo_restyle_damage="reflow", + )} - ${helpers.predefined_type(name="align-items", - type="AlignItems", - initial_value="specified::AlignItems::normal()", - spec="https://drafts.csswg.org/css-align/#propdef-align-items", - extra_prefixes="webkit", - animation_value_type="discrete", - servo_restyle_damage = "reflow")} + ${helpers.predefined_type( + "align-items", + "AlignItems", + "specified::AlignItems::normal()", + spec="https://drafts.csswg.org/css-align/#propdef-align-items", + extra_prefixes="webkit", + animation_value_type="discrete", + servo_restyle_damage="reflow", + )} #[cfg(feature = "gecko")] impl_align_conversions!(::values::specified::align::AlignItems); ${helpers.predefined_type( - name="justify-items", - type="JustifyItems", - initial_value="computed::JustifyItems::legacy()", + "justify-items", + "JustifyItems", + "computed::JustifyItems::legacy()", spec="https://drafts.csswg.org/css-align/#propdef-justify-items", animation_value_type="discrete", )} @@ -144,52 +164,69 @@ ${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse", % endif // Flex item properties -${helpers.predefined_type("flex-grow", "NonNegativeNumber", - "From::from(0.0)", - spec="https://drafts.csswg.org/css-flexbox/#flex-grow-property", - extra_prefixes="webkit", - animation_value_type="NonNegativeNumber", - servo_restyle_damage = "reflow")} +${helpers.predefined_type( + "flex-grow", + "NonNegativeNumber", + "From::from(0.0)", + spec="https://drafts.csswg.org/css-flexbox/#flex-grow-property", + extra_prefixes="webkit", + animation_value_type="NonNegativeNumber", + servo_restyle_damage="reflow", +)} -${helpers.predefined_type("flex-shrink", "NonNegativeNumber", - "From::from(1.0)", - spec="https://drafts.csswg.org/css-flexbox/#flex-shrink-property", - extra_prefixes="webkit", - animation_value_type="NonNegativeNumber", - servo_restyle_damage = "reflow")} +${helpers.predefined_type( + "flex-shrink", + "NonNegativeNumber", + "From::from(1.0)", + spec="https://drafts.csswg.org/css-flexbox/#flex-shrink-property", + extra_prefixes="webkit", + animation_value_type="NonNegativeNumber", + servo_restyle_damage = "reflow", +)} // https://drafts.csswg.org/css-align/#align-self-property % if product == "servo": // FIXME: Update Servo to support the same syntax as Gecko. - ${helpers.single_keyword("align-self", "auto stretch flex-start flex-end center baseline", - extra_prefixes="webkit", - spec="https://drafts.csswg.org/css-flexbox/#propdef-align-self", - animation_value_type="discrete", - servo_restyle_damage = "reflow")} + ${helpers.single_keyword( + "align-self", + "auto stretch flex-start flex-end center baseline", + extra_prefixes="webkit", + spec="https://drafts.csswg.org/css-flexbox/#propdef-align-self", + animation_value_type="discrete", + servo_restyle_damage = "reflow", + )} % else: - ${helpers.predefined_type(name="align-self", - type="AlignSelf", - initial_value="specified::AlignSelf(specified::SelfAlignment::auto())", - spec="https://drafts.csswg.org/css-align/#align-self-property", - extra_prefixes="webkit", - animation_value_type="discrete")} + ${helpers.predefined_type( + "align-self", + "AlignSelf", + "specified::AlignSelf(specified::SelfAlignment::auto())", + spec="https://drafts.csswg.org/css-align/#align-self-property", + extra_prefixes="webkit", + animation_value_type="discrete", + )} - ${helpers.predefined_type(name="justify-self", - type="JustifySelf", - initial_value="specified::JustifySelf(specified::SelfAlignment::auto())", - spec="https://drafts.csswg.org/css-align/#justify-self-property", - animation_value_type="discrete")} + ${helpers.predefined_type( + "justify-self", + "JustifySelf", + "specified::JustifySelf(specified::SelfAlignment::auto())", + spec="https://drafts.csswg.org/css-align/#justify-self-property", + animation_value_type="discrete", + )} #[cfg(feature = "gecko")] impl_align_conversions!(::values::specified::align::SelfAlignment); % endif // https://drafts.csswg.org/css-flexbox/#propdef-order -${helpers.predefined_type("order", "Integer", "0", - extra_prefixes="webkit", - animation_value_type="ComputedValue", - spec="https://drafts.csswg.org/css-flexbox/#order-property", - servo_restyle_damage = "reflow")} +${helpers.predefined_type( + "order", + "Integer", + "0", + extra_prefixes="webkit", + animation_value_type="ComputedValue", + spec="https://drafts.csswg.org/css-flexbox/#order-property", + servo_restyle_damage = "reflow", +)} ${helpers.predefined_type( "flex-basis", @@ -198,7 +235,7 @@ ${helpers.predefined_type( spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property", extra_prefixes="webkit", animation_value_type="FlexBasis", - servo_restyle_damage = "reflow" + servo_restyle_damage = "reflow", )} % for (size, logical) in ALL_SIZES: @@ -228,7 +265,7 @@ ${helpers.predefined_type( spec=spec % size, animation_value_type="MozLength", flags="GETCS_NEEDS_LAYOUT_FLUSH", - servo_restyle_damage="reflow" + servo_restyle_damage="reflow", )} // min-width, min-height, min-block-size, min-inline-size, ${helpers.predefined_type( @@ -241,7 +278,7 @@ ${helpers.predefined_type( allow_quirks=not logical, spec=spec % size, animation_value_type="MozLength", - servo_restyle_damage = "reflow" + servo_restyle_damage="reflow", )} ${helpers.predefined_type( "max-%s" % size, @@ -253,7 +290,7 @@ ${helpers.predefined_type( allow_quirks=not logical, spec=spec % size, animation_value_type="MaxLength", - servo_restyle_damage = "reflow" + servo_restyle_damage="reflow", )} % else: // servo versions (no keyword support) @@ -266,7 +303,7 @@ ${helpers.predefined_type( logical_group="size", allow_quirks=not logical, animation_value_type="ComputedValue", logical = logical, - servo_restyle_damage = "reflow", + servo_restyle_damage="reflow", )} ${helpers.predefined_type( "min-%s" % size, @@ -278,7 +315,7 @@ ${helpers.predefined_type( animation_value_type="ComputedValue", logical=logical, allow_quirks=not logical, - servo_restyle_damage = "reflow", + servo_restyle_damage="reflow", )} ${helpers.predefined_type( "max-%s" % size, @@ -290,52 +327,64 @@ ${helpers.predefined_type( animation_value_type="ComputedValue", logical=logical, allow_quirks=not logical, - servo_restyle_damage = "reflow", + servo_restyle_damage="reflow", )} % endif % endfor -${helpers.single_keyword("box-sizing", - "content-box border-box", - extra_prefixes="moz:layout.css.prefixes.box-sizing webkit", - spec="https://drafts.csswg.org/css-ui/#propdef-box-sizing", - gecko_enum_prefix="StyleBoxSizing", - custom_consts={ "content-box": "Content", "border-box": "Border" }, - animation_value_type="discrete", - servo_restyle_damage = "reflow")} +${helpers.single_keyword( + "box-sizing", + "content-box border-box", + extra_prefixes="moz:layout.css.prefixes.box-sizing webkit", + spec="https://drafts.csswg.org/css-ui/#propdef-box-sizing", + gecko_enum_prefix="StyleBoxSizing", + custom_consts={ "content-box": "Content", "border-box": "Border" }, + animation_value_type="discrete", + servo_restyle_damage = "reflow", +)} -${helpers.single_keyword("object-fit", "fill contain cover none scale-down", - products="gecko", animation_value_type="discrete", - spec="https://drafts.csswg.org/css-images/#propdef-object-fit")} +${helpers.single_keyword( + "object-fit", + "fill contain cover none scale-down", + products="gecko", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-images/#propdef-object-fit", +)} -${helpers.predefined_type("object-position", - "Position", - "computed::Position::zero()", - products="gecko", - boxed=True, - spec="https://drafts.csswg.org/css-images-3/#the-object-position", - animation_value_type="ComputedValue")} +${helpers.predefined_type( + "object-position", + "Position", + "computed::Position::zero()", + products="gecko", + boxed=True, + spec="https://drafts.csswg.org/css-images-3/#the-object-position", + animation_value_type="ComputedValue", +)} % for kind in ["row", "column"]: % for range in ["start", "end"]: - ${helpers.predefined_type("grid-%s-%s" % (kind, range), - "GridLine", - "Default::default()", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-%s" % (kind, range), - products="gecko", - boxed=True)} + ${helpers.predefined_type( + "grid-%s-%s" % (kind, range), + "GridLine", + "Default::default()", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-%s" % (kind, range), + products="gecko", + boxed=True, + )} % endfor // NOTE: According to the spec, this should handle multiple values of ``, // but gecko supports only a single value - ${helpers.predefined_type("grid-auto-%ss" % kind, - "TrackSize", - "Default::default()", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind, - products="gecko", - boxed=True)} + ${helpers.predefined_type( + "grid-auto-%ss" % kind, + "TrackSize", + "Default::default()", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind, + products="gecko", + boxed=True, + )} ${helpers.predefined_type( "grid-template-%ss" % kind, @@ -345,41 +394,49 @@ ${helpers.predefined_type("object-position", spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-%ss" % kind, boxed=True, flags="GETCS_NEEDS_LAYOUT_FLUSH", - animation_value_type="discrete" + animation_value_type="discrete", )} % endfor -${helpers.predefined_type("grid-auto-flow", - "GridAutoFlow", - initial_value="computed::GridAutoFlow::row()", - products="gecko", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow")} +${helpers.predefined_type( + "grid-auto-flow", + "GridAutoFlow", + "computed::GridAutoFlow::row()", + products="gecko", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow", +)} -${helpers.predefined_type("grid-template-areas", - "GridTemplateAreas", - initial_value="computed::GridTemplateAreas::none()", - products="gecko", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-areas")} +${helpers.predefined_type( + "grid-template-areas", + "GridTemplateAreas", + "computed::GridTemplateAreas::none()", + products="gecko", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-areas", +)} -${helpers.predefined_type("column-gap", - "length::NonNegativeLengthOrPercentageOrNormal", - "Either::Second(Normal)", - alias="grid-column-gap" if product == "gecko" else "", - extra_prefixes="moz", - servo_pref="layout.columns.enabled", - spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap", - animation_value_type="NonNegativeLengthOrPercentageOrNormal", - servo_restyle_damage = "reflow")} +${helpers.predefined_type( + "column-gap", + "length::NonNegativeLengthOrPercentageOrNormal", + "Either::Second(Normal)", + alias="grid-column-gap" if product == "gecko" else "", + extra_prefixes="moz", + servo_pref="layout.columns.enabled", + spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap", + animation_value_type="NonNegativeLengthOrPercentageOrNormal", + servo_restyle_damage="reflow", +)} // no need for -moz- prefixed alias for this property -${helpers.predefined_type("row-gap", - "length::NonNegativeLengthOrPercentageOrNormal", - "Either::Second(Normal)", - alias="grid-row-gap", - products="gecko", - spec="https://drafts.csswg.org/css-align-3/#propdef-row-gap", - animation_value_type="NonNegativeLengthOrPercentageOrNormal", - servo_restyle_damage = "reflow")} +${helpers.predefined_type( + "row-gap", + "length::NonNegativeLengthOrPercentageOrNormal", + "Either::Second(Normal)", + alias="grid-row-gap", + products="gecko", + spec="https://drafts.csswg.org/css-align-3/#propdef-row-gap", + animation_value_type="NonNegativeLengthOrPercentageOrNormal", + servo_restyle_damage="reflow", +)} diff --git a/components/style/properties/longhands/svg.mako.rs b/components/style/properties/longhands/svg.mako.rs index 6d7bf803f1e..36105247676 100644 --- a/components/style/properties/longhands/svg.mako.rs +++ b/components/style/properties/longhands/svg.mako.rs @@ -6,16 +6,22 @@ <% data.new_style_struct("SVG", inherited=False, gecko_name="SVGReset") %> -${helpers.single_keyword("dominant-baseline", - """auto use-script no-change reset-size ideographic alphabetic hanging - mathematical central middle text-after-edge text-before-edge""", - products="gecko", - animation_value_type="discrete", - spec="https://www.w3.org/TR/SVG11/text.html#DominantBaselineProperty")} +${helpers.single_keyword( + "dominant-baseline", + """auto use-script no-change reset-size ideographic alphabetic hanging + mathematical central middle text-after-edge text-before-edge""", + products="gecko", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVG11/text.html#DominantBaselineProperty", +)} -${helpers.single_keyword("vector-effect", "none non-scaling-stroke", - products="gecko", animation_value_type="discrete", - spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty")} +${helpers.single_keyword( + "vector-effect", + "none non-scaling-stroke", + products="gecko", + animation_value_type="discrete", + spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty", +)} // Section 13 - Gradients and Patterns @@ -28,10 +34,14 @@ ${helpers.predefined_type( spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty", )} -${helpers.predefined_type("stop-opacity", "Opacity", "1.0", - products="gecko", - animation_value_type="ComputedValue", - spec="https://www.w3.org/TR/SVGTiny12/painting.html#propdef-stop-opacity")} +${helpers.predefined_type( + "stop-opacity", + "Opacity", + "1.0", + products="gecko", + animation_value_type="ComputedValue", + spec="https://www.w3.org/TR/SVGTiny12/painting.html#propdef-stop-opacity", +)} // Section 15 - Filter Effects @@ -44,9 +54,14 @@ ${helpers.predefined_type( spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty", )} -${helpers.predefined_type("flood-opacity", "Opacity", - "1.0", products="gecko", animation_value_type="ComputedValue", - spec="https://www.w3.org/TR/SVG/filters.html#FloodOpacityProperty")} +${helpers.predefined_type( + "flood-opacity", + "Opacity", + "1.0", + products="gecko", + animation_value_type="ComputedValue", + spec="https://www.w3.org/TR/SVG/filters.html#FloodOpacityProperty", +)} ${helpers.predefined_type( "lighting-color", @@ -59,9 +74,13 @@ ${helpers.predefined_type( // CSS Masking Module Level 1 // https://drafts.fxtf.org/css-masking -${helpers.single_keyword("mask-type", "luminance alpha", - products="gecko", animation_value_type="discrete", - spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type")} +${helpers.single_keyword( + "mask-type", + "luminance alpha", + products="gecko", + animation_value_type="discrete", + spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type", +)} ${helpers.predefined_type( "clip-path", @@ -74,12 +93,14 @@ ${helpers.predefined_type( spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path", )} -${helpers.single_keyword("mask-mode", - "match-source alpha luminance", - vector=True, - products="gecko", - animation_value_type="discrete", - spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode")} +${helpers.single_keyword( + "mask-mode", + "match-source alpha luminance", + vector=True, + products="gecko", + animation_value_type="discrete", + spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode", +)} ${helpers.predefined_type( "mask-repeat", @@ -97,9 +118,9 @@ ${helpers.predefined_type( ${helpers.predefined_type( "mask-position-" + axis, "position::" + direction + "Position", + "computed::LengthOrPercentage::zero()", products="gecko", extra_prefixes="webkit", - initial_value="computed::LengthOrPercentage::zero()", initial_specified_value="specified::PositionComponent::Center", spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position", animation_value_type="ComputedValue", @@ -147,20 +168,25 @@ ${helpers.predefined_type( vector_animation_type="repeatable_list", )} -${helpers.single_keyword("mask-composite", - "add subtract intersect exclude", - vector=True, - products="gecko", - extra_prefixes="webkit", - animation_value_type="discrete", - spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite")} +${helpers.single_keyword( + "mask-composite", + "add subtract intersect exclude", + vector=True, + products="gecko", + extra_prefixes="webkit", + animation_value_type="discrete", + spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite", +)} -${helpers.predefined_type("mask-image", "ImageLayer", - initial_value="Either::First(None_)", +${helpers.predefined_type( + "mask-image", + "ImageLayer", + "Either::First(None_)", initial_specified_value="Either::First(None_)", spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image", vector=True, products="gecko", extra_prefixes="webkit", animation_value_type="discrete", - flags="CREATES_STACKING_CONTEXT")} + flags="CREATES_STACKING_CONTEXT", +)} diff --git a/components/style/properties/longhands/table.mako.rs b/components/style/properties/longhands/table.mako.rs index 2af39e1d6e3..8a371f269a6 100644 --- a/components/style/properties/longhands/table.mako.rs +++ b/components/style/properties/longhands/table.mako.rs @@ -6,15 +6,21 @@ <% data.new_style_struct("Table", inherited=False) %> -${helpers.single_keyword("table-layout", "auto fixed", - gecko_ffi_name="mLayoutStrategy", animation_value_type="discrete", - spec="https://drafts.csswg.org/css-tables/#propdef-table-layout", - servo_restyle_damage = "reflow")} +${helpers.single_keyword( + "table-layout", + "auto fixed", + gecko_ffi_name="mLayoutStrategy", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-tables/#propdef-table-layout", + servo_restyle_damage="reflow", +)} -${helpers.predefined_type("-x-span", - "XSpan", - "computed::XSpan(1)", - products="gecko", - spec="Internal-only (for `` pres attr)", - animation_value_type="none", - enabled_in="")} +${helpers.predefined_type( + "-x-span", + "XSpan", + "computed::XSpan(1)", + products="gecko", + spec="Internal-only (for `` pres attr)", + animation_value_type="none", + enabled_in="", +)} diff --git a/components/style/properties/longhands/text.mako.rs b/components/style/properties/longhands/text.mako.rs index f87fe4915a3..715019f9c08 100644 --- a/components/style/properties/longhands/text.mako.rs +++ b/components/style/properties/longhands/text.mako.rs @@ -5,21 +5,27 @@ <%namespace name="helpers" file="/helpers.mako.rs" /> <% from data import Method %> -<% data.new_style_struct("Text", - inherited=False, - gecko_name="TextReset", - additional_methods=[Method("has_underline", "bool"), - Method("has_overline", "bool"), - Method("has_line_through", "bool")]) %> +<% data.new_style_struct( + "Text", + inherited=False, + gecko_name="TextReset", + additional_methods=[ + Method("has_underline", "bool"), + Method("has_overline", "bool"), + Method("has_line_through", "bool"), + ] +) %> -${helpers.predefined_type("text-overflow", - "TextOverflow", - "computed::TextOverflow::get_initial_value()", - animation_value_type="discrete", - boxed=True, - flags="APPLIES_TO_PLACEHOLDER", - spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow", - servo_restyle_damage="rebuild_and_reflow")} +${helpers.predefined_type( + "text-overflow", + "TextOverflow", + "computed::TextOverflow::get_initial_value()", + animation_value_type="discrete", + boxed=True, + flags="APPLIES_TO_PLACEHOLDER", + spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow", + servo_restyle_damage="rebuild_and_reflow", +)} ${helpers.single_keyword( "unicode-bidi", @@ -29,21 +35,25 @@ ${helpers.single_keyword( servo_restyle_damage="rebuild_and_reflow", )} -${helpers.predefined_type("text-decoration-line", - "TextDecorationLine", - "specified::TextDecorationLine::none()", - initial_specified_value="specified::TextDecorationLine::none()", - animation_value_type="discrete", - flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", - spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line", - servo_restyle_damage="rebuild_and_reflow")} +${helpers.predefined_type( + "text-decoration-line", + "TextDecorationLine", + "specified::TextDecorationLine::none()", + initial_specified_value="specified::TextDecorationLine::none()", + animation_value_type="discrete", + flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", + spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line", + servo_restyle_damage="rebuild_and_reflow", +)} -${helpers.single_keyword("text-decoration-style", - "solid double dotted dashed wavy -moz-none", - products="gecko", - animation_value_type="discrete", - flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", - spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style")} +${helpers.single_keyword( + "text-decoration-style", + "solid double dotted dashed wavy -moz-none", + products="gecko", + animation_value_type="discrete", + flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", + spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style", +)} ${helpers.predefined_type( "text-decoration-color", @@ -66,4 +76,5 @@ ${helpers.predefined_type( products="gecko", flags="APPLIES_TO_FIRST_LETTER", gecko_pref="layout.css.initial-letter.enabled", - spec="https://drafts.csswg.org/css-inline/#sizing-drop-initials")} + spec="https://drafts.csswg.org/css-inline/#sizing-drop-initials", +)} diff --git a/components/style/properties/longhands/ui.mako.rs b/components/style/properties/longhands/ui.mako.rs index 419024862d4..b8a188d896b 100644 --- a/components/style/properties/longhands/ui.mako.rs +++ b/components/style/properties/longhands/ui.mako.rs @@ -11,10 +11,14 @@ // TODO spec says that UAs should not support this // we should probably remove from gecko (https://bugzilla.mozilla.org/show_bug.cgi?id=1328331) -${helpers.single_keyword("ime-mode", "auto normal active disabled inactive", - products="gecko", gecko_ffi_name="mIMEMode", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-ui/#input-method-editor")} +${helpers.single_keyword( + "ime-mode", + "auto normal active disabled inactive", + products="gecko", + gecko_ffi_name="mIMEMode", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-ui/#input-method-editor", +)} ${helpers.single_keyword( "scrollbar-width", @@ -27,30 +31,40 @@ ${helpers.single_keyword( spec="https://drafts.csswg.org/css-scrollbars-1/#scrollbar-width" )} -${helpers.single_keyword("-moz-user-select", "auto text none all element elements" + - " toggle tri-state -moz-all -moz-text", - products="gecko", - alias="-webkit-user-select", - gecko_ffi_name="mUserSelect", - gecko_enum_prefix="StyleUserSelect", - gecko_strip_moz_prefix=False, - aliases="-moz-none=none", - animation_value_type="discrete", - spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select")} +${helpers.single_keyword( + "-moz-user-select", + "auto text none all element elements toggle tri-state -moz-all -moz-text", + products="gecko", + alias="-webkit-user-select", + gecko_ffi_name="mUserSelect", + gecko_enum_prefix="StyleUserSelect", + gecko_strip_moz_prefix=False, + aliases="-moz-none=none", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select", +)} // TODO(emilio): This probably should be hidden from content. -${helpers.single_keyword("-moz-window-dragging", "default drag no-drag", products="gecko", - gecko_ffi_name="mWindowDragging", - gecko_enum_prefix="StyleWindowDragging", - animation_value_type="discrete", - spec="None (Nonstandard Firefox-only property)")} +${helpers.single_keyword( + "-moz-window-dragging", + "default drag no-drag", + products="gecko", + gecko_ffi_name="mWindowDragging", + gecko_enum_prefix="StyleWindowDragging", + animation_value_type="discrete", + spec="None (Nonstandard Firefox-only property)", +)} -${helpers.single_keyword("-moz-window-shadow", "none default menu tooltip sheet", products="gecko", - gecko_ffi_name="mWindowShadow", - gecko_constant_prefix="NS_STYLE_WINDOW_SHADOW", - animation_value_type="discrete", - enabled_in="chrome", - spec="None (Nonstandard internal property)")} +${helpers.single_keyword( + "-moz-window-shadow", + "none default menu tooltip sheet", + products="gecko", + gecko_ffi_name="mWindowShadow", + gecko_constant_prefix="NS_STYLE_WINDOW_SHADOW", + animation_value_type="discrete", + enabled_in="chrome", + spec="None (Nonstandard internal property)", +)} ${helpers.predefined_type( "-moz-window-opacity", @@ -89,9 +103,11 @@ ${helpers.predefined_type( )} // TODO(emilio): Probably also should be hidden from content. -${helpers.predefined_type("-moz-force-broken-image-icon", - "MozForceBrokenImageIcon", - "computed::MozForceBrokenImageIcon::false_value()", - animation_value_type="discrete", - products="gecko", - spec="None (Nonstandard Firefox-only property)")} +${helpers.predefined_type( + "-moz-force-broken-image-icon", + "MozForceBrokenImageIcon", + "computed::MozForceBrokenImageIcon::false_value()", + animation_value_type="discrete", + products="gecko", + spec="None (Nonstandard Firefox-only property)", +)} diff --git a/components/style/properties/longhands/xul.mako.rs b/components/style/properties/longhands/xul.mako.rs index 15b8a695411..5089bbf985b 100644 --- a/components/style/properties/longhands/xul.mako.rs +++ b/components/style/properties/longhands/xul.mako.rs @@ -8,51 +8,79 @@ // Non-standard properties that Gecko uses for XUL elements. <% data.new_style_struct("XUL", inherited=False) %> -${helpers.single_keyword("-moz-box-align", "stretch start center baseline end", - products="gecko", gecko_ffi_name="mBoxAlign", - gecko_enum_prefix="StyleBoxAlign", - animation_value_type="discrete", - alias="-webkit-box-align", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-align)")} +${helpers.single_keyword( + "-moz-box-align", + "stretch start center baseline end", + products="gecko", + gecko_ffi_name="mBoxAlign", + gecko_enum_prefix="StyleBoxAlign", + animation_value_type="discrete", + alias="-webkit-box-align", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-align)", +)} -${helpers.single_keyword("-moz-box-direction", "normal reverse", - products="gecko", gecko_ffi_name="mBoxDirection", - gecko_enum_prefix="StyleBoxDirection", - animation_value_type="discrete", - alias="-webkit-box-direction", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-direction)")} +${helpers.single_keyword( + "-moz-box-direction", + "normal reverse", + products="gecko", + gecko_ffi_name="mBoxDirection", + gecko_enum_prefix="StyleBoxDirection", + animation_value_type="discrete", + alias="-webkit-box-direction", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-direction)", +)} -${helpers.predefined_type("-moz-box-flex", "NonNegativeNumber", "From::from(0.)", - products="gecko", gecko_ffi_name="mBoxFlex", - animation_value_type="NonNegativeNumber", - alias="-webkit-box-flex", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-flex)")} +${helpers.predefined_type( + "-moz-box-flex", + "NonNegativeNumber", + "From::from(0.)", + products="gecko", + gecko_ffi_name="mBoxFlex", + animation_value_type="NonNegativeNumber", + alias="-webkit-box-flex", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-flex)", +)} -${helpers.single_keyword("-moz-box-orient", "horizontal vertical", - products="gecko", gecko_ffi_name="mBoxOrient", - extra_gecko_aliases="inline-axis=horizontal block-axis=vertical", - gecko_enum_prefix="StyleBoxOrient", - animation_value_type="discrete", - alias="-webkit-box-orient", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-orient)")} +${helpers.single_keyword( + "-moz-box-orient", + "horizontal vertical", + products="gecko", + gecko_ffi_name="mBoxOrient", + extra_gecko_aliases="inline-axis=horizontal block-axis=vertical", + gecko_enum_prefix="StyleBoxOrient", + animation_value_type="discrete", + alias="-webkit-box-orient", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-orient)", +)} -${helpers.single_keyword("-moz-box-pack", "start center end justify", - products="gecko", gecko_ffi_name="mBoxPack", - gecko_enum_prefix="StyleBoxPack", - animation_value_type="discrete", - alias="-webkit-box-pack", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)")} +${helpers.single_keyword( + "-moz-box-pack", + "start center end justify", + products="gecko", gecko_ffi_name="mBoxPack", + gecko_enum_prefix="StyleBoxPack", + animation_value_type="discrete", + alias="-webkit-box-pack", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)", +)} -${helpers.single_keyword("-moz-stack-sizing", "stretch-to-fit ignore ignore-horizontal ignore-vertical", - products="gecko", gecko_ffi_name="mStackSizing", - gecko_enum_prefix="StyleStackSizing", - animation_value_type="discrete", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-stack-sizing)")} +${helpers.single_keyword( + "-moz-stack-sizing", + "stretch-to-fit ignore ignore-horizontal ignore-vertical", + products="gecko", + gecko_ffi_name="mStackSizing", + gecko_enum_prefix="StyleStackSizing", + animation_value_type="discrete", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-stack-sizing)", +)} -${helpers.predefined_type("-moz-box-ordinal-group", "Integer", "0", - parse_method="parse_non_negative", - products="gecko", - alias="-webkit-box-ordinal-group", - gecko_ffi_name="mBoxOrdinal", - animation_value_type="discrete", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-box-ordinal-group)")} +${helpers.predefined_type( + "-moz-box-ordinal-group", + "Integer", + "0", + parse_method="parse_non_negative", + products="gecko", + alias="-webkit-box-ordinal-group", + gecko_ffi_name="mBoxOrdinal", + animation_value_type="discrete", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-box-ordinal-group)", +)} From c3a4b27441ff1efb7e3fb16ef3c2267c038edffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 21 Aug 2018 17:34:21 +0200 Subject: [PATCH 09/34] style: Remove useless StyleDisplay conversion. Differential Revision: https://phabricator.services.mozilla.com/D3896 --- .../style/properties/longhands/box.mako.rs | 18 ------------------ components/style/values/specified/box.rs | 4 ++-- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index 06742d45e68..dcc29227301 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -23,24 +23,6 @@ ${helpers.predefined_type( needs_context=product == "gecko" )} -// FIXME(emilio): Listing all the display values here is very unfortunate, we should teach C++ to use the -// Rust enum directly, or generate the conversions to `StyleDisplay`. -${helpers.gecko_keyword_conversion( - Keyword('display', """ - inline block inline-block - table inline-table table-row-group table-header-group table-footer-group - table-row table-column-group table-column table-cell table-caption - list-item none flex inline-flex grid inline-grid ruby ruby-base ruby-base-container - ruby-text ruby-text-container contents flow-root -webkit-box - -webkit-inline-box -moz-box -moz-inline-box -moz-grid -moz-inline-grid - -moz-grid-group -moz-grid-line -moz-stack -moz-inline-stack -moz-deck - -moz-popup -moz-groupbox - """, - gecko_enum_prefix='StyleDisplay', - gecko_strip_moz_prefix=False), - type="::values::specified::Display" -)} - ${helpers.single_keyword( "-moz-top-layer", "none top", diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index 6b474689df3..9554d76890c 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -57,8 +57,8 @@ fn moz_box_display_values_enabled(context: &ParserContext) -> bool { /// Also, when you change this from Gecko you may need to regenerate the /// C++-side bindings (see components/style/cbindgen.toml). #[allow(missing_docs)] -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, - SpecifiedValueInfo, ToComputedValue, ToCss)] +#[derive(Clone, Copy, Debug, Eq, FromPrimitive, Hash, MallocSizeOf, Parse, + PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[repr(u8)] pub enum Display { From c8e5b7f1b070374999ba05b8365b105251348dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 16 Aug 2018 16:38:56 +0200 Subject: [PATCH 10/34] style: Add a very simple use counter implementation. As simple as I could make it, for now. We can improve on this. Differential Revision: https://phabricator.services.mozilla.com/D3827 --- components/style/lib.rs | 1 + components/style/parser.rs | 12 ++- .../style/properties/properties.mako.rs | 97 +++++++++++-------- components/style/use_counters/mod.rs | 40 ++++++++ 4 files changed, 105 insertions(+), 45 deletions(-) create mode 100644 components/style/use_counters/mod.rs diff --git a/components/style/lib.rs b/components/style/lib.rs index 1c3630b0986..03caab33e19 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -157,6 +157,7 @@ pub mod thread_state; pub mod timer; pub mod traversal; pub mod traversal_flags; +pub mod use_counters; #[macro_use] #[allow(non_camel_case_types)] pub mod values; diff --git a/components/style/parser.rs b/components/style/parser.rs index a4b7d816203..006d2ce6b88 100644 --- a/components/style/parser.rs +++ b/components/style/parser.rs @@ -9,6 +9,7 @@ use cssparser::{Parser, SourceLocation, UnicodeRange}; use error_reporting::{ContextualParseError, ParseErrorReporter}; use style_traits::{OneOrMoreSeparated, ParseError, ParsingMode, Separator}; use stylesheets::{CssRuleType, Namespaces, Origin, UrlExtraData}; +use use_counters::UseCounters; /// Asserts that all ParsingMode flags have a matching ParsingMode value in gecko. #[cfg(feature = "gecko")] @@ -53,6 +54,8 @@ pub struct ParserContext<'a> { error_reporter: Option<&'a ParseErrorReporter>, /// The currently active namespaces. pub namespaces: Option<&'a Namespaces>, + /// The use counters we want to record while parsing style rules, if any. + pub use_counters: Option<&'a UseCounters>, } impl<'a> ParserContext<'a> { @@ -66,7 +69,7 @@ impl<'a> ParserContext<'a> { quirks_mode: QuirksMode, error_reporter: Option<&'a ParseErrorReporter>, ) -> Self { - ParserContext { + Self { stylesheet_origin, url_data, rule_type, @@ -74,6 +77,7 @@ impl<'a> ParserContext<'a> { quirks_mode, error_reporter, namespaces: None, + use_counters: None, } } @@ -96,14 +100,15 @@ impl<'a> ParserContext<'a> { ) } - /// Create a parser context based on a previous context, but with a modified rule type. + /// Create a parser context based on a previous context, but with a modified + /// rule type. #[inline] pub fn new_with_rule_type( context: &'a ParserContext, rule_type: CssRuleType, namespaces: &'a Namespaces, ) -> ParserContext<'a> { - ParserContext { + Self { stylesheet_origin: context.stylesheet_origin, url_data: context.url_data, rule_type: Some(rule_type), @@ -111,6 +116,7 @@ impl<'a> ParserContext<'a> { quirks_mode: context.quirks_mode, namespaces: Some(namespaces), error_reporter: context.error_reporter, + use_counters: context.use_counters, } } diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 64e99ba205d..eba290fc14d 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -425,6 +425,10 @@ pub mod animated_properties { #[derive(Clone, Copy, Debug)] pub struct NonCustomPropertyId(usize); +/// The length of all the non-custom properties. +pub const NON_CUSTOM_PROPERTY_ID_COUNT: usize = + ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}; + % if product == "gecko": #[allow(dead_code)] unsafe fn static_assert_nscsspropertyid() { @@ -435,6 +439,11 @@ unsafe fn static_assert_nscsspropertyid() { % endif impl NonCustomPropertyId { + /// Returns the underlying index, used for use counter. + pub fn bit(self) -> usize { + self.0 + } + #[cfg(feature = "gecko")] #[inline] fn to_nscsspropertyid(self) -> nsCSSPropertyID { @@ -450,7 +459,7 @@ impl NonCustomPropertyId { if prop < 0 { return Err(()); } - if prop >= ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())} { + if prop >= NON_CUSTOM_PROPERTY_ID_COUNT as i32 { return Err(()); } // unsafe: guaranteed by static_assert_nscsspropertyid above. @@ -460,7 +469,7 @@ impl NonCustomPropertyId { /// Get the property name. #[inline] pub fn name(self) -> &'static str { - static MAP: [&'static str; ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}] = [ + static MAP: [&'static str; NON_CUSTOM_PROPERTY_ID_COUNT] = [ % for property in data.longhands + data.shorthands + data.all_aliases(): "${property.name}", % endfor @@ -635,7 +644,7 @@ impl NonCustomPropertyId { PropertyId::Shorthand(transmute((self.0 - ${len(data.longhands)}) as u16)) } } - assert!(self.0 < ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}); + assert!(self.0 < NON_CUSTOM_PROPERTY_ID_COUNT); let alias_id: AliasId = unsafe { transmute((self.0 - ${len(data.longhands) + len(data.shorthands)}) as u16) }; @@ -671,7 +680,7 @@ impl From for NonCustomPropertyId { /// A set of all properties #[derive(Clone, PartialEq)] pub struct NonCustomPropertyIdSet { - storage: [u32; (${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())} - 1 + 32) / 32] + storage: [u32; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + 32) / 32] } impl NonCustomPropertyIdSet { @@ -2198,7 +2207,7 @@ impl PropertyDeclaration { // FIXME: fully implement https://github.com/w3c/csswg-drafts/issues/774 // before adding skip_whitespace here. // This probably affects some test results. - let value = match input.try(|i| CSSWideKeyword::parse(i)) { + let value = match input.try(CSSWideKeyword::parse) { Ok(keyword) => DeclaredValueOwned::CSSWideKeyword(keyword), Err(()) => match ::custom_properties::SpecifiedValue::parse(input) { Ok(value) => DeclaredValueOwned::Value(value), @@ -2212,12 +2221,12 @@ impl PropertyDeclaration { name: property_name, value, })); - Ok(()) + return Ok(()); } PropertyId::LonghandAlias(id, _) | PropertyId::Longhand(id) => { input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less. - input.try(|i| CSSWideKeyword::parse(i)).map(|keyword| { + input.try(CSSWideKeyword::parse).map(|keyword| { PropertyDeclaration::CSSWideKeyword( WideKeywordDeclaration { id, keyword }, ) @@ -2253,12 +2262,12 @@ impl PropertyDeclaration { }) }).map(|declaration| { declarations.push(declaration) - }) + })?; } PropertyId::ShorthandAlias(id, _) | PropertyId::Shorthand(id) => { input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less. - if let Ok(keyword) = input.try(|i| CSSWideKeyword::parse(i)) { + if let Ok(keyword) = input.try(CSSWideKeyword::parse) { if id == ShorthandId::All { declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword) } else { @@ -2271,51 +2280,55 @@ impl PropertyDeclaration { )) } } - Ok(()) } else { input.look_for_var_functions(); // Not using parse_entirely here: each ${shorthand.ident}::parse_into function // needs to do so *before* pushing to `declarations`. id.parse_into(declarations, context, input).or_else(|err| { while let Ok(_) = input.next() {} // Look for var() after the error. - if input.seen_var_functions() { - input.reset(&start); - let (first_token_type, css) = - ::custom_properties::parse_non_custom_with_var(input).map_err(|e| { - StyleParseErrorKind::new_invalid( - non_custom_id.unwrap().name(), - e, - ) - })?; - let unparsed = Arc::new(UnparsedValue { - css: css.into_owned(), - first_token_type: first_token_type, - url_data: context.url_data.clone(), - from_shorthand: Some(id), - }); - if id == ShorthandId::All { - declarations.all_shorthand = AllShorthand::WithVariables(unparsed) - } else { - for id in id.longhands() { - declarations.push( - PropertyDeclaration::WithVariables(VariableDeclaration { - id, - value: unparsed.clone(), - }) - ) - } - } - Ok(()) - } else { - Err(StyleParseErrorKind::new_invalid( + if !input.seen_var_functions() { + return Err(StyleParseErrorKind::new_invalid( non_custom_id.unwrap().name(), err, - )) + )); } - }) + + input.reset(&start); + let (first_token_type, css) = + ::custom_properties::parse_non_custom_with_var(input).map_err(|e| { + StyleParseErrorKind::new_invalid( + non_custom_id.unwrap().name(), + e, + ) + })?; + let unparsed = Arc::new(UnparsedValue { + css: css.into_owned(), + first_token_type: first_token_type, + url_data: context.url_data.clone(), + from_shorthand: Some(id), + }); + if id == ShorthandId::All { + declarations.all_shorthand = AllShorthand::WithVariables(unparsed) + } else { + for id in id.longhands() { + declarations.push( + PropertyDeclaration::WithVariables(VariableDeclaration { + id, + value: unparsed.clone(), + }) + ) + } + } + Ok(()) + })?; } } } + debug_assert!(non_custom_id.is_some(), "Custom properties should've returned earlier"); + if let Some(use_counters) = context.use_counters { + use_counters.non_custom_properties.record(non_custom_id.unwrap()); + } + Ok(()) } } diff --git a/components/style/use_counters/mod.rs b/components/style/use_counters/mod.rs new file mode 100644 index 00000000000..02ae4ea1ab2 --- /dev/null +++ b/components/style/use_counters/mod.rs @@ -0,0 +1,40 @@ +/* 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/. */ + +//! Various stuff for CSS property use counters. + +use properties::{NonCustomPropertyId, NON_CUSTOM_PROPERTY_ID_COUNT}; +// FIXME(emilio): We need AtomicU32 on stable ASAP... +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; + +#[cfg(target_pointer_width = "64")] +const BITS_PER_ENTRY: usize = 64; + +#[cfg(target_pointer_width = "32")] +const BITS_PER_ENTRY: usize = 32; + +/// One bit per each non-custom CSS property. +#[derive(Default)] +pub struct NonCustomPropertyUseCounters { + storage: [AtomicUsize; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + BITS_PER_ENTRY) / BITS_PER_ENTRY], +} + +impl NonCustomPropertyUseCounters { + /// Record that a given non-custom property ID has been parsed. + #[inline] + pub fn record(&self, id: NonCustomPropertyId) { + let bit = id.bit(); + let bucket = bit / BITS_PER_ENTRY; + let bit_in_bucket = bit % BITS_PER_ENTRY; + self.storage[bucket].fetch_or(1 << bit_in_bucket, Ordering::Relaxed); + } +} + +/// The use-counter data related to a given document we want to store. +pub struct UseCounters { + /// The counters for non-custom properties that have been parsed in the + /// document's stylesheets. + pub non_custom_properties: NonCustomPropertyUseCounters, +} From 89b8f307376b4177962591edffb8881ab37af882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Mon, 20 Aug 2018 19:03:11 +0200 Subject: [PATCH 11/34] style: Hook the use counters into StyleSheet parsing. Still not hooked into telemetry, I talked with :janerik and :gfritzsche about that, but test incoming! This intentionally doesn't handle CSSOM and such for now, will file followups for those, though should be trivial. I want to unify / clean up how we do the use counters and the error reporting stuff for CSSOM, since the current function call still shows up in profiles, but that should be a follow-up. Differential Revision: https://phabricator.services.mozilla.com/D3828 --- components/style/gecko/arc_types.rs | 5 +++++ components/style/parser.rs | 5 ++++- components/style/properties/declaration_block.rs | 2 ++ components/style/properties/properties.mako.rs | 1 + components/style/stylesheets/keyframes_rule.rs | 1 + components/style/stylesheets/mod.rs | 1 + components/style/stylesheets/stylesheet.rs | 10 ++++++++++ components/style/use_counters/mod.rs | 1 + 8 files changed, 25 insertions(+), 1 deletion(-) diff --git a/components/style/gecko/arc_types.rs b/components/style/gecko/arc_types.rs index 7351cbfeac4..271915f4fa8 100644 --- a/components/style/gecko/arc_types.rs +++ b/components/style/gecko/arc_types.rs @@ -21,6 +21,7 @@ use gecko_bindings::bindings::RawServoRuleNode; use gecko_bindings::bindings::RawServoRuleNodeStrong; use gecko_bindings::bindings::RawServoSupportsRule; use gecko_bindings::bindings::ServoCssRules; +use gecko_bindings::bindings::StyleUseCounters; use gecko_bindings::structs::RawServoAnimationValue; use gecko_bindings::structs::RawServoDeclarationBlock; use gecko_bindings::structs::RawServoFontFaceRule; @@ -39,6 +40,7 @@ use stylesheets::{CounterStyleRule, CssRules, FontFaceRule, FontFeatureValuesRul use stylesheets::{DocumentRule, ImportRule, KeyframesRule, MediaRule, NamespaceRule, PageRule}; use stylesheets::{StyleRule, StylesheetContents, SupportsRule}; use stylesheets::keyframes_rule::Keyframe; +use use_counters::UseCounters; macro_rules! impl_arc_ffi { ($servo_type:ty => $gecko_type:ty[$addref:ident, $release:ident]) => { @@ -59,6 +61,9 @@ macro_rules! impl_arc_ffi { }; } +impl_arc_ffi!(UseCounters => StyleUseCounters + [Servo_UseCounters_AddRef, Servo_UseCounters_Release]); + impl_arc_ffi!(Locked => ServoCssRules [Servo_CssRules_AddRef, Servo_CssRules_Release]); diff --git a/components/style/parser.rs b/components/style/parser.rs index 006d2ce6b88..db5b8f1e7f9 100644 --- a/components/style/parser.rs +++ b/components/style/parser.rs @@ -68,6 +68,7 @@ impl<'a> ParserContext<'a> { parsing_mode: ParsingMode, quirks_mode: QuirksMode, error_reporter: Option<&'a ParseErrorReporter>, + use_counters: Option<&'a UseCounters>, ) -> Self { Self { stylesheet_origin, @@ -77,7 +78,7 @@ impl<'a> ParserContext<'a> { quirks_mode, error_reporter, namespaces: None, - use_counters: None, + use_counters, } } @@ -89,6 +90,7 @@ impl<'a> ParserContext<'a> { parsing_mode: ParsingMode, quirks_mode: QuirksMode, error_reporter: Option<&'a ParseErrorReporter>, + use_counters: Option<&'a UseCounters>, ) -> Self { Self::new( Origin::Author, @@ -97,6 +99,7 @@ impl<'a> ParserContext<'a> { parsing_mode, quirks_mode, error_reporter, + use_counters, ) } diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs index 38af8f2fa43..56aa2fde4f4 100644 --- a/components/style/properties/declaration_block.rs +++ b/components/style/properties/declaration_block.rs @@ -1203,6 +1203,7 @@ pub fn parse_style_attribute( ParsingMode::DEFAULT, quirks_mode, error_reporter, + None, ); let mut input = ParserInput::new(input); @@ -1230,6 +1231,7 @@ pub fn parse_one_declaration_into( parsing_mode, quirks_mode, error_reporter, + None, ); let mut input = ParserInput::new(input); diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index eba290fc14d..95f2504fe66 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -1563,6 +1563,7 @@ impl UnparsedValue { ParsingMode::DEFAULT, quirks_mode, None, + None, ); let mut input = ParserInput::new(&css); diff --git a/components/style/stylesheets/keyframes_rule.rs b/components/style/stylesheets/keyframes_rule.rs index 91bc14be5b5..0246efa6780 100644 --- a/components/style/stylesheets/keyframes_rule.rs +++ b/components/style/stylesheets/keyframes_rule.rs @@ -219,6 +219,7 @@ impl Keyframe { ParsingMode::DEFAULT, parent_stylesheet_contents.quirks_mode, None, + None, ); context.namespaces = Some(&*namespaces); let mut input = ParserInput::new(css); diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs index e3641f3e515..80766c289f8 100644 --- a/components/style/stylesheets/mod.rs +++ b/components/style/stylesheets/mod.rs @@ -264,6 +264,7 @@ impl CssRule { ParsingMode::DEFAULT, parent_stylesheet_contents.quirks_mode, None, + None, ); let mut input = ParserInput::new(css); diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs index 02730f7090d..1403a1d2547 100644 --- a/components/style/stylesheets/stylesheet.rs +++ b/components/style/stylesheets/stylesheet.rs @@ -24,6 +24,7 @@ use stylesheets::loader::StylesheetLoader; use stylesheets::rule_parser::{State, TopLevelRuleParser}; use stylesheets::rules_iterator::{EffectiveRules, EffectiveRulesIterator}; use stylesheets::rules_iterator::{NestedRuleIterationCondition, RulesIterator}; +use use_counters::UseCounters; /// This structure holds the user-agent and user stylesheets. pub struct UserAgentStylesheets { @@ -78,6 +79,7 @@ impl StylesheetContents { error_reporter: Option<&ParseErrorReporter>, quirks_mode: QuirksMode, line_number_offset: u32, + use_counters: Option<&UseCounters>, ) -> Self { let namespaces = RwLock::new(Namespaces::default()); let (rules, source_map_url, source_url) = Stylesheet::parse_rules( @@ -90,6 +92,7 @@ impl StylesheetContents { error_reporter, quirks_mode, line_number_offset, + use_counters, ); Self { @@ -315,6 +318,8 @@ impl Stylesheet { line_number_offset: u32, ) { let namespaces = RwLock::new(Namespaces::default()); + + // FIXME: Consider adding use counters to Servo? let (rules, source_map_url, source_url) = Self::parse_rules( css, &url_data, @@ -325,6 +330,7 @@ impl Stylesheet { error_reporter, existing.contents.quirks_mode, line_number_offset, + /* use_counters = */ None, ); *existing.contents.url_data.write() = url_data; @@ -350,6 +356,7 @@ impl Stylesheet { error_reporter: Option<&ParseErrorReporter>, quirks_mode: QuirksMode, line_number_offset: u32, + use_counters: Option<&UseCounters>, ) -> (Vec, Option, Option) { let mut rules = Vec::new(); let mut input = ParserInput::new_with_line_number_offset(css, line_number_offset); @@ -362,6 +369,7 @@ impl Stylesheet { ParsingMode::DEFAULT, quirks_mode, error_reporter, + use_counters, ); let rule_parser = TopLevelRuleParser { @@ -421,6 +429,7 @@ impl Stylesheet { quirks_mode: QuirksMode, line_number_offset: u32, ) -> Self { + // FIXME: Consider adding use counters to Servo? let contents = StylesheetContents::from_str( css, url_data, @@ -430,6 +439,7 @@ impl Stylesheet { error_reporter, quirks_mode, line_number_offset, + /* use_counters = */ None, ); Stylesheet { diff --git a/components/style/use_counters/mod.rs b/components/style/use_counters/mod.rs index 02ae4ea1ab2..68f04da64a0 100644 --- a/components/style/use_counters/mod.rs +++ b/components/style/use_counters/mod.rs @@ -33,6 +33,7 @@ impl NonCustomPropertyUseCounters { } /// The use-counter data related to a given document we want to store. +#[derive(Default)] pub struct UseCounters { /// The counters for non-custom properties that have been parsed in the /// document's stylesheets. From 4f04988c1373b9e14e407d8682407165aad03b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Mon, 20 Aug 2018 22:07:19 +0200 Subject: [PATCH 12/34] style: Add a test for the use counters. Mostly testing that they work, and that they record what we expect them to record, that is, the actual property that was parsed, and not the properties that it'd resolve or expand to. That may be another tricky part for CSSOM, I think style setters would fail an alias test if implemented with the current setup (we do the property lookup in C++). Differential Revision: https://phabricator.services.mozilla.com/D3829 --- .../style/properties/properties.mako.rs | 3 ++- components/style/use_counters/mod.rs | 24 +++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 95f2504fe66..681e9556653 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -1863,7 +1863,8 @@ impl PropertyId { } } - fn non_custom_id(&self) -> Option { + /// Returns the `NonCustomPropertyId` corresponding to this property id. + pub fn non_custom_id(&self) -> Option { Some(match *self { PropertyId::Custom(_) => return None, PropertyId::Shorthand(shorthand_id) => shorthand_id.into(), diff --git a/components/style/use_counters/mod.rs b/components/style/use_counters/mod.rs index 68f04da64a0..03fdc045365 100644 --- a/components/style/use_counters/mod.rs +++ b/components/style/use_counters/mod.rs @@ -22,13 +22,29 @@ pub struct NonCustomPropertyUseCounters { } impl NonCustomPropertyUseCounters { - /// Record that a given non-custom property ID has been parsed. - #[inline] - pub fn record(&self, id: NonCustomPropertyId) { + /// Returns the bucket a given property belongs in, and the bitmask for that + /// property. + #[inline(always)] + fn bucket_and_pattern(id: NonCustomPropertyId) -> (usize, usize) { let bit = id.bit(); let bucket = bit / BITS_PER_ENTRY; let bit_in_bucket = bit % BITS_PER_ENTRY; - self.storage[bucket].fetch_or(1 << bit_in_bucket, Ordering::Relaxed); + (bucket, 1 << bit_in_bucket) + } + + /// Record that a given non-custom property ID has been parsed. + #[inline] + pub fn record(&self, id: NonCustomPropertyId) { + let (bucket, pattern) = Self::bucket_and_pattern(id); + self.storage[bucket].fetch_or(pattern, Ordering::Relaxed); + } + + /// Returns whether a given non-custom property ID has been recorded + /// earlier. + #[inline] + pub fn recorded(&self, id: NonCustomPropertyId) -> bool { + let (bucket, pattern) = Self::bucket_and_pattern(id); + self.storage[bucket].load(Ordering::Relaxed) & pattern != 0 } } From 86b4b7036962247e4c6e270390e9765062e5ef99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Tue, 21 Aug 2018 12:31:11 +0200 Subject: [PATCH 13/34] style: Make the counters non-atomic counters and merge afterwards. This was consistently faster in the benchmark (even when counters were disabled, which was slightly suspicious, but...). Anyway, it's not really much code, most of it is FFI copy-pasta. Differential Revision: https://phabricator.services.mozilla.com/D3874 --- components/style/gecko/arc_types.rs | 5 --- .../style/gecko_bindings/sugar/ownership.rs | 9 ++++ components/style/use_counters/mod.rs | 42 ++++++++++++++++--- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/components/style/gecko/arc_types.rs b/components/style/gecko/arc_types.rs index 271915f4fa8..7351cbfeac4 100644 --- a/components/style/gecko/arc_types.rs +++ b/components/style/gecko/arc_types.rs @@ -21,7 +21,6 @@ use gecko_bindings::bindings::RawServoRuleNode; use gecko_bindings::bindings::RawServoRuleNodeStrong; use gecko_bindings::bindings::RawServoSupportsRule; use gecko_bindings::bindings::ServoCssRules; -use gecko_bindings::bindings::StyleUseCounters; use gecko_bindings::structs::RawServoAnimationValue; use gecko_bindings::structs::RawServoDeclarationBlock; use gecko_bindings::structs::RawServoFontFaceRule; @@ -40,7 +39,6 @@ use stylesheets::{CounterStyleRule, CssRules, FontFaceRule, FontFeatureValuesRul use stylesheets::{DocumentRule, ImportRule, KeyframesRule, MediaRule, NamespaceRule, PageRule}; use stylesheets::{StyleRule, StylesheetContents, SupportsRule}; use stylesheets::keyframes_rule::Keyframe; -use use_counters::UseCounters; macro_rules! impl_arc_ffi { ($servo_type:ty => $gecko_type:ty[$addref:ident, $release:ident]) => { @@ -61,9 +59,6 @@ macro_rules! impl_arc_ffi { }; } -impl_arc_ffi!(UseCounters => StyleUseCounters - [Servo_UseCounters_AddRef, Servo_UseCounters_Release]); - impl_arc_ffi!(Locked => ServoCssRules [Servo_CssRules_AddRef, Servo_CssRules_Release]); diff --git a/components/style/gecko_bindings/sugar/ownership.rs b/components/style/gecko_bindings/sugar/ownership.rs index 2bc85f53ff7..91c7a504eb1 100644 --- a/components/style/gecko_bindings/sugar/ownership.rs +++ b/components/style/gecko_bindings/sugar/ownership.rs @@ -305,6 +305,15 @@ pub struct OwnedOrNull { } impl OwnedOrNull { + /// Returns a null pointer. + #[inline] + pub fn null() -> Self { + Self { + ptr: ptr::null_mut(), + _marker: PhantomData, + } + } + /// Returns whether this pointer is null. #[inline] pub fn is_null(&self) -> bool { diff --git a/components/style/use_counters/mod.rs b/components/style/use_counters/mod.rs index 03fdc045365..92bc6adb01a 100644 --- a/components/style/use_counters/mod.rs +++ b/components/style/use_counters/mod.rs @@ -4,10 +4,10 @@ //! Various stuff for CSS property use counters. +#[cfg(feature = "gecko")] +use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI}; use properties::{NonCustomPropertyId, NON_CUSTOM_PROPERTY_ID_COUNT}; -// FIXME(emilio): We need AtomicU32 on stable ASAP... -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; +use std::cell::Cell; #[cfg(target_pointer_width = "64")] const BITS_PER_ENTRY: usize = 64; @@ -18,7 +18,7 @@ const BITS_PER_ENTRY: usize = 32; /// One bit per each non-custom CSS property. #[derive(Default)] pub struct NonCustomPropertyUseCounters { - storage: [AtomicUsize; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + BITS_PER_ENTRY) / BITS_PER_ENTRY], + storage: [Cell; (NON_CUSTOM_PROPERTY_ID_COUNT - 1 + BITS_PER_ENTRY) / BITS_PER_ENTRY], } impl NonCustomPropertyUseCounters { @@ -36,7 +36,8 @@ impl NonCustomPropertyUseCounters { #[inline] pub fn record(&self, id: NonCustomPropertyId) { let (bucket, pattern) = Self::bucket_and_pattern(id); - self.storage[bucket].fetch_or(pattern, Ordering::Relaxed); + let bucket = &self.storage[bucket]; + bucket.set(bucket.get() | pattern) } /// Returns whether a given non-custom property ID has been recorded @@ -44,7 +45,15 @@ impl NonCustomPropertyUseCounters { #[inline] pub fn recorded(&self, id: NonCustomPropertyId) -> bool { let (bucket, pattern) = Self::bucket_and_pattern(id); - self.storage[bucket].load(Ordering::Relaxed) & pattern != 0 + self.storage[bucket].get() & pattern != 0 + } + + /// Merge `other` into `self`. + #[inline] + fn merge(&self, other: &Self) { + for (bucket, other_bucket) in self.storage.iter().zip(other.storage.iter()) { + bucket.set(bucket.get() | other_bucket.get()) + } } } @@ -55,3 +64,24 @@ pub struct UseCounters { /// document's stylesheets. pub non_custom_properties: NonCustomPropertyUseCounters, } + +impl UseCounters { + /// Merge the use counters. + /// + /// Used for parallel parsing, where we parse off-main-thread. + #[inline] + pub fn merge(&self, other: &Self) { + self.non_custom_properties.merge(&other.non_custom_properties) + } +} + +#[cfg(feature = "gecko")] +unsafe impl HasFFI for UseCounters { + type FFIType = ::gecko_bindings::structs::StyleUseCounters; +} + +#[cfg(feature = "gecko")] +unsafe impl HasSimpleFFI for UseCounters {} + +#[cfg(feature = "gecko")] +unsafe impl HasBoxFFI for UseCounters {} From dceb58664e5bb1f74109a7c83c34703b7292bbab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 23 Aug 2018 16:56:27 +0200 Subject: [PATCH 14/34] style: Remove an assertion that doesn't hold in some cases. --- .../invalidation/element/state_and_attributes.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/components/style/invalidation/element/state_and_attributes.rs b/components/style/invalidation/element/state_and_attributes.rs index 1eb022b298a..fa0ccdd8be9 100644 --- a/components/style/invalidation/element/state_and_attributes.rs +++ b/components/style/invalidation/element/state_and_attributes.rs @@ -159,13 +159,18 @@ where // force a restyle here. Matching doesn't depend on the actual visited // state at all, so we can't look at matching results to decide what to // do for this case. - if state_changes.intersects(ElementState::IN_VISITED_OR_UNVISITED_STATE) { + if state_changes.intersects(ElementState::IN_VISITED_OR_UNVISITED_STATE) && + self.shared_context.visited_styles_enabled + { trace!(" > visitedness change, force subtree restyle"); - // If we get here with visited links disabled, we should probably - // just avoid the restyle and remove the state change here, not only - // as an optimization, but also because it kind of would kill the + // We shouldn't get here with visited links disabled, but it's hard + // to assert in cases where you record a visitedness change and + // afterwards you change some of the stuff (like the pref) that + // changes whether visited styles are enabled. + // + // So just avoid the restyle here, because it kind of would kill the // point of disabling visited links. - debug_assert!(self.shared_context.visited_styles_enabled); + // // We can't just return here because there may also be attribute // changes as well that imply additional hints for siblings. self.data.hint.insert(RestyleHint::restyle_subtree()); From c587fa35867a0206a77ea67b7b8738045c7d42a9 Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Fri, 24 Aug 2018 09:23:05 +0000 Subject: [PATCH 15/34] style: Make tree pseudo-element prefix not case-sensitive. Differential Revision: https://phabricator.services.mozilla.com/D4185 --- components/style/gecko/pseudo_element.rs | 1 + components/style/gecko/pseudo_element_definition.mako.rs | 4 +--- components/style/gecko/selector_parser.rs | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/components/style/gecko/pseudo_element.rs b/components/style/gecko/pseudo_element.rs index a08da8d40f1..73fe905ebd8 100644 --- a/components/style/gecko/pseudo_element.rs +++ b/components/style/gecko/pseudo_element.rs @@ -14,6 +14,7 @@ use properties::{ComputedValues, PropertyFlags}; use properties::longhands::display::computed_value::T as Display; use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl}; use std::fmt; +use str::starts_with_ignore_ascii_case; use string_cache::Atom; use thin_slice::ThinBoxedSlice; use values::serialize_atom_identifier; diff --git a/components/style/gecko/pseudo_element_definition.mako.rs b/components/style/gecko/pseudo_element_definition.mako.rs index be0318eadea..dd27dfd5676 100644 --- a/components/style/gecko/pseudo_element_definition.mako.rs +++ b/components/style/gecko/pseudo_element_definition.mako.rs @@ -242,9 +242,7 @@ impl PseudoElement { return Some(PseudoElement::Placeholder); } _ => { - // FIXME: -moz-tree check should probably be - // ascii-case-insensitive. - if name.starts_with("-moz-tree-") { + if starts_with_ignore_ascii_case(name, "-moz-tree-") { return PseudoElement::tree_pseudo_element(name, Box::new([])) } } diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index fa514835e8c..cc84f58aa13 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -17,6 +17,7 @@ use selectors::parser::{SelectorParseErrorKind, Visit}; use selectors::parser::{self as selector_parser, Selector}; use selectors::visitor::SelectorVisitor; use std::fmt; +use str::starts_with_ignore_ascii_case; use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_}; use thin_slice::ThinBoxedSlice; @@ -468,8 +469,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { name: CowRcStr<'i>, parser: &mut Parser<'i, 't>, ) -> Result> { - // FIXME: -moz-tree check should probably be ascii-case-insensitive. - if name.starts_with("-moz-tree-") { + if starts_with_ignore_ascii_case(&name, "-moz-tree-") { // Tree pseudo-elements can have zero or more arguments, separated // by either comma or space. let mut args = Vec::new(); From e46daa09ea8a8fdfd9a39d51f748392321c962e2 Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Thu, 23 Aug 2018 20:32:53 +0000 Subject: [PATCH 16/34] style: Drop the manually implementation of ToCSS for BasicShape::Polygon. The implementation of ToCSS for Polygon has some rule, and we could use skip_if to handle and serialization of fill-rule. However, we should derive ToCSS for the pair of LengthOrPercentages, so define a new type for it. Differential Revision: https://phabricator.services.mozilla.com/D4153 --- components/style/gecko/conversions.rs | 15 ++++--- .../style/values/generics/basic_shape.rs | 45 +++++++------------ .../style/values/specified/basic_shape.rs | 4 +- 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 3c1fcde28cd..4dafe457371 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -670,7 +670,7 @@ pub mod basic_shape { use values::computed::position; use values::computed::url::ComputedUrl; use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon}; - use values::generics::basic_shape::{Circle, Ellipse, FillRule}; + use values::generics::basic_shape::{Circle, Ellipse, FillRule, PolygonCoord}; use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource}; use values::generics::border::BorderRadius as GenericBorderRadius; use values::generics::rect::Rect; @@ -800,11 +800,14 @@ pub mod basic_shape { for i in 0..(other.mCoordinates.len() / 2) { let x = 2 * i; let y = x + 1; - coords.push((LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[x]) - .expect("polygon() coordinate should be a length, percentage, or calc value"), - LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[y]) - .expect("polygon() coordinate should be a length, percentage, or calc value") - )) + coords.push(PolygonCoord( + LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[x]) + .expect("polygon() coordinate should be a length, percentage, \ + or calc value"), + LengthOrPercentage::from_gecko_style_coord(&other.mCoordinates[y]) + .expect("polygon() coordinate should be a length, percentage, \ + or calc value") + )) } GenericBasicShape::Polygon(Polygon { fill: fill_rule, diff --git a/components/style/values/generics/basic_shape.rs b/components/style/values/generics/basic_shape.rs index 3bad738c1e6..274673b9c61 100644 --- a/components/style/values/generics/basic_shape.rs +++ b/components/style/values/generics/basic_shape.rs @@ -113,16 +113,23 @@ pub enum ShapeRadius { /// A generic type for representing the `polygon()` function /// /// -#[css(function)] +#[css(comma, function)] #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, - ToComputedValue)] + ToComputedValue, ToCss)] pub struct Polygon { /// The filling rule for a polygon. + #[css(skip_if = "fill_is_default")] pub fill: FillRule, /// A collection of (x, y) coordinates to draw the polygon. - pub coordinates: Vec<(LengthOrPercentage, LengthOrPercentage)>, + #[css(iterable)] + pub coordinates: Vec>, } +/// Coordinates for Polygon. +#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, + ToComputedValue, ToCss)] +pub struct PolygonCoord(pub LengthOrPercentage, pub LengthOrPercentage); + // https://drafts.csswg.org/css-shapes/#typedef-fill-rule // NOTE: Basic shapes spec says that these are the only two values, however // https://www.w3.org/TR/SVG/painting.html#FillRuleProperty @@ -203,7 +210,7 @@ where .iter() .zip(other.coordinates.iter()) .map(|(this, other)| { - Ok(( + Ok(PolygonCoord( this.0.animate(&other.0, procedure)?, this.1.animate(&other.1, procedure)?, )) @@ -239,34 +246,14 @@ where } } -impl ToCss for Polygon { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: Write, - { - dest.write_str("polygon(")?; - if self.fill != FillRule::default() { - self.fill.to_css(dest)?; - dest.write_str(", ")?; - } - - for (i, coord) in self.coordinates.iter().enumerate() { - if i > 0 { - dest.write_str(", ")?; - } - - coord.0.to_css(dest)?; - dest.write_str(" ")?; - coord.1.to_css(dest)?; - } - - dest.write_str(")") - } -} - impl Default for FillRule { #[inline] fn default() -> Self { FillRule::Nonzero } } + +#[inline] +fn fill_is_default(fill: &FillRule) -> bool { + *fill == FillRule::default() +} diff --git a/components/style/values/specified/basic_shape.rs b/components/style/values/specified/basic_shape.rs index 23ae91d564e..2fa693a1978 100644 --- a/components/style/values/specified/basic_shape.rs +++ b/components/style/values/specified/basic_shape.rs @@ -14,7 +14,7 @@ use std::fmt::{self, Write}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use values::computed::Percentage; use values::generics::basic_shape as generic; -use values::generics::basic_shape::{FillRule, GeometryBox, ShapeBox, ShapeSource}; +use values::generics::basic_shape::{FillRule, GeometryBox, PolygonCoord, ShapeBox, ShapeSource}; use values::generics::rect::Rect; use values::specified::LengthOrPercentage; use values::specified::border::BorderRadius; @@ -381,7 +381,7 @@ impl Polygon { .unwrap_or_default(); let buf = input.parse_comma_separated(|i| { - Ok(( + Ok(PolygonCoord( LengthOrPercentage::parse(context, i)?, LengthOrPercentage::parse(context, i)?, )) From 68ab6217bdcfae5355c321957aef6ecb766d0472 Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Thu, 23 Aug 2018 23:00:16 +0000 Subject: [PATCH 17/34] style: Use cbindgen for basic_shape::FillRule. Just a minor fix to use cbindgen to avoid the conversion between basic_shape::FillRule and mozilla::StyleFillRule. Differential Revision: https://phabricator.services.mozilla.com/D4171 --- components/style/cbindgen.toml | 8 +++++++- components/style/properties/gecko.mako.rs | 11 ++++------- components/style/values/generics/basic_shape.rs | 1 + 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/components/style/cbindgen.toml b/components/style/cbindgen.toml index c0eeee9b968..46162e43db5 100644 --- a/components/style/cbindgen.toml +++ b/components/style/cbindgen.toml @@ -23,5 +23,11 @@ derive_helper_methods = true [export] prefix = "Style" -include = ["StyleDisplay", "StyleAppearance", "StyleDisplayMode", "StylePathCommand"] +include = [ + "StyleAppearance", + "StyleDisplay", + "StyleDisplayMode", + "StyleFillRule", + "StylePathCommand" +] item_types = ["enums", "structs", "typedefs"] diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 596dc29fb19..76d61838708 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -4986,10 +4986,10 @@ fn static_assert() { pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { use gecko_bindings::bindings::{Gecko_NewBasicShape, Gecko_DestroyShapeSource}; use gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleShapeSourceType}; - use gecko_bindings::structs::{StyleFillRule, StyleGeometryBox, StyleShapeSource}; + use gecko_bindings::structs::{StyleGeometryBox, StyleShapeSource}; use gecko::conversions::basic_shape::set_corners_from_radius; use gecko::values::GeckoStyleCoordConvertible; - use values::generics::basic_shape::{BasicShape, FillRule, ShapeSource}; + use values::generics::basic_shape::{BasicShape, ShapeSource}; let ref mut ${ident} = self.gecko.${gecko_ffi_name}; @@ -5083,11 +5083,8 @@ fn static_assert() { coord.0.to_gecko_style_coord(&mut shape.mCoordinates[2 * i]); coord.1.to_gecko_style_coord(&mut shape.mCoordinates[2 * i + 1]); } - shape.mFillRule = if poly.fill == FillRule::Evenodd { - StyleFillRule::Evenodd - } else { - StyleFillRule::Nonzero - }; + // unsafe: cbindgen ensures the representation is the same. + shape.mFillRule = unsafe { transmute(poly.fill) }; } } diff --git a/components/style/values/generics/basic_shape.rs b/components/style/values/generics/basic_shape.rs index 274673b9c61..ee7455e777d 100644 --- a/components/style/values/generics/basic_shape.rs +++ b/components/style/values/generics/basic_shape.rs @@ -138,6 +138,7 @@ pub struct PolygonCoord(pub LengthOrPercentage, pub LengthOr #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)] +#[repr(u8)] pub enum FillRule { Nonzero, Evenodd, From d12d4209743f644ba7aa940e6c70e63fdfeedeff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Fri, 24 Aug 2018 13:44:21 +0200 Subject: [PATCH 18/34] style: Adjust an assertion to account for the changes from bug 1485930. --- components/style/gecko/pseudo_element_definition.mako.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/style/gecko/pseudo_element_definition.mako.rs b/components/style/gecko/pseudo_element_definition.mako.rs index dd27dfd5676..2395ff32f78 100644 --- a/components/style/gecko/pseudo_element_definition.mako.rs +++ b/components/style/gecko/pseudo_element_definition.mako.rs @@ -257,7 +257,7 @@ impl PseudoElement { /// Returns `None` if the pseudo-element is not recognized. #[inline] pub fn tree_pseudo_element(name: &str, args: Box<[Atom]>) -> Option { - debug_assert!(name.starts_with("-moz-tree-")); + debug_assert!(starts_with_ignore_ascii_case(name, "-moz-tree-")); let tree_part = &name[10..]; % for pseudo in TREE_PSEUDOS: if tree_part.eq_ignore_ascii_case("${pseudo.value[11:]}") { From d4163eae965e1a78a35cc46857e38c1412e11b9c Mon Sep 17 00:00:00 2001 From: Xidorn Quan Date: Fri, 24 Aug 2018 22:17:37 +1000 Subject: [PATCH 19/34] style: Accept unknown webkit pseudo-element. Differential Revision: https://phabricator.services.mozilla.com/D4186 --- components/style/gecko/pseudo_element.rs | 2 +- .../gecko/pseudo_element_definition.mako.rs | 30 +++++++++++++++++-- components/style/stylist.rs | 7 ++++- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/components/style/gecko/pseudo_element.rs b/components/style/gecko/pseudo_element.rs index 73fe905ebd8..b30d7764d17 100644 --- a/components/style/gecko/pseudo_element.rs +++ b/components/style/gecko/pseudo_element.rs @@ -14,7 +14,7 @@ use properties::{ComputedValues, PropertyFlags}; use properties::longhands::display::computed_value::T as Display; use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl}; use std::fmt; -use str::starts_with_ignore_ascii_case; +use str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase}; use string_cache::Atom; use thin_slice::ThinBoxedSlice; use values::serialize_atom_identifier; diff --git a/components/style/gecko/pseudo_element_definition.mako.rs b/components/style/gecko/pseudo_element_definition.mako.rs index 2395ff32f78..7c45ee8bfb7 100644 --- a/components/style/gecko/pseudo_element_definition.mako.rs +++ b/components/style/gecko/pseudo_element_definition.mako.rs @@ -13,6 +13,9 @@ pub enum PseudoElement { ${pseudo.capitalized_pseudo()}, % endif % endfor + /// ::-webkit-* that we don't recognize + /// https://github.com/whatwg/compat/issues/103 + UnknownWebkit(Atom), } /// Important: If you change this, you should also update Gecko's @@ -47,11 +50,12 @@ PseudoElement::${pseudo.capitalized_pseudo()}${"({})".format(tree_arg) if pseudo impl PseudoElement { /// Get the pseudo-element as an atom. #[inline] - pub fn atom(&self) -> Atom { + fn atom(&self) -> Atom { match *self { % for pseudo in PSEUDOS: ${pseudo_element_variant(pseudo)} => atom!("${pseudo.value}"), % endfor + PseudoElement::UnknownWebkit(..) => unreachable!(), } } @@ -62,6 +66,7 @@ impl PseudoElement { % for i, pseudo in enumerate(PSEUDOS): ${pseudo_element_variant(pseudo)} => ${i}, % endfor + PseudoElement::UnknownWebkit(..) => unreachable!(), } } @@ -105,6 +110,12 @@ impl PseudoElement { } } + /// Whether this pseudo-element is an unknown Webkit-prefixed pseudo-element. + #[inline] + pub fn is_unknown_webkit_pseudo_element(&self) -> bool { + matches!(*self, PseudoElement::UnknownWebkit(..)) + } + /// Gets the flags associated to this pseudo-element, or 0 if it's an /// anonymous box. pub fn flags(&self) -> u32 { @@ -123,6 +134,7 @@ impl PseudoElement { structs::SERVO_CSS_PSEUDO_ELEMENT_FLAGS_${pseudo.pseudo_ident}, % endif % endfor + PseudoElement::UnknownWebkit(..) => 0, } } @@ -143,7 +155,7 @@ impl PseudoElement { /// Construct a `CSSPseudoElementType` from a pseudo-element #[inline] - pub fn pseudo_type(&self) -> CSSPseudoElementType { + fn pseudo_type(&self) -> CSSPseudoElementType { use gecko_bindings::structs::CSSPseudoElementType_InheritingAnonBox; match *self { @@ -158,6 +170,7 @@ impl PseudoElement { PseudoElement::${pseudo.capitalized_pseudo()} => CSSPseudoElementType::NonInheritingAnonBox, % endif % endfor + PseudoElement::UnknownWebkit(..) => unreachable!(), } } @@ -245,6 +258,15 @@ impl PseudoElement { if starts_with_ignore_ascii_case(name, "-moz-tree-") { return PseudoElement::tree_pseudo_element(name, Box::new([])) } + if unsafe { + structs::StaticPrefs_sVarCache_layout_css_unknown_webkit_pseudo_element + } { + const WEBKIT_PREFIX: &str = "-webkit-"; + if starts_with_ignore_ascii_case(name, WEBKIT_PREFIX) { + let part = string_as_ascii_lowercase(&name[WEBKIT_PREFIX.len()..]); + return Some(PseudoElement::UnknownWebkit(part.into())); + } + } } } @@ -275,6 +297,10 @@ impl ToCss for PseudoElement { % for pseudo in PSEUDOS: ${pseudo_element_variant(pseudo)} => dest.write_str("${pseudo.value}")?, % endfor + PseudoElement::UnknownWebkit(ref atom) => { + dest.write_str(":-webkit-")?; + serialize_atom_identifier(atom, dest)?; + } } if let Some(args) = self.tree_pseudo_args() { if !args.is_empty() { diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 1fbf493667f..5dbb4dd5dc8 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -1878,7 +1878,9 @@ impl ElementAndPseudoRules { pseudo_element: Option<&PseudoElement>, quirks_mode: QuirksMode, ) -> Result<(), FailedAllocationError> { - debug_assert!(pseudo_element.map_or(true, |pseudo| !pseudo.is_precomputed())); + debug_assert!(pseudo_element.map_or(true, |pseudo| { + !pseudo.is_precomputed() && !pseudo.is_unknown_webkit_pseudo_element() + })); let map = match pseudo_element { None => &mut self.element_map, @@ -2191,6 +2193,9 @@ impl CascadeData { )); continue; } + if pseudo.is_unknown_webkit_pseudo_element() { + continue; + } } let hashes = AncestorHashes::new(&selector, quirks_mode); From a92f9c105a1191b889fc2e6e9db039039e92af68 Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Thu, 16 Aug 2018 11:19:59 -0700 Subject: [PATCH 20/34] style: Move SVGPathData and its parser into svg_path.rs. SVGPathData will be used by clip-path and offset-path (and/or more on the properties which support ). Therefore, let's move SVGPathData out of motion.rs. Differential Revision: https://phabricator.services.mozilla.com/D3631 --- components/style/gecko/conversions.rs | 2 +- components/style/values/specified/mod.rs | 2 + components/style/values/specified/motion.rs | 501 +---------------- components/style/values/specified/svg_path.rs | 517 ++++++++++++++++++ 4 files changed, 522 insertions(+), 500 deletions(-) create mode 100644 components/style/values/specified/svg_path.rs diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 4dafe457371..a3109a1f226 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -743,7 +743,7 @@ pub mod basic_shape { impl<'a> From<&'a StyleShapeSource> for OffsetPath { fn from(other: &'a StyleShapeSource) -> Self { use gecko_bindings::structs::StylePathCommand; - use values::specified::motion::{SVGPathData, PathCommand}; + use values::specified::svg_path::{SVGPathData, PathCommand}; match other.mType { StyleShapeSourceType::Path => { let gecko_path = unsafe { &*other.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 1bd52e916d0..31e37b3041d 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -68,6 +68,7 @@ pub use self::position::{PositionComponent, ZIndex}; pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind}; pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; pub use self::svg::MozContextProperties; +pub use self::svg_path::SVGPathData; pub use self::table::XSpan; pub use self::text::{InitialLetter, LetterSpacing, LineHeight, MozTabSize, TextAlign}; pub use self::text::{TextEmphasisPosition, TextEmphasisStyle}; @@ -109,6 +110,7 @@ pub mod rect; pub mod resolution; pub mod source_size_list; pub mod svg; +pub mod svg_path; pub mod table; pub mod text; pub mod time; diff --git a/components/style/values/specified/motion.rs b/components/style/values/specified/motion.rs index ffaaf7cff17..87ff39d9a3a 100644 --- a/components/style/values/specified/motion.rs +++ b/components/style/values/specified/motion.rs @@ -6,12 +6,8 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; -use std::fmt::{self, Write}; -use std::iter::Peekable; -use std::str::Chars; -use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; -use style_traits::values::SequenceWriter; -use values::CSSFloat; +use style_traits::{ParseError, StyleParseErrorKind}; +use values::specified::SVGPathData; /// The offset-path value. /// @@ -63,496 +59,3 @@ impl Parse for OffsetPath { }) } } - -/// SVG Path parser. -struct PathParser<'a> { - chars: Peekable>, - path: Vec, -} - -macro_rules! parse_arguments { - ( - $parser:ident, - $abs:ident, - $enum:ident, - [ $para:ident => $func:ident $(, $other_para:ident => $other_func:ident)* ] - ) => { - { - loop { - let $para = $func(&mut $parser.chars)?; - $( - skip_comma_wsp(&mut $parser.chars); - let $other_para = $other_func(&mut $parser.chars)?; - )* - $parser.path.push(PathCommand::$enum { $para $(, $other_para)*, $abs }); - - // End of string or the next character is a possible new command. - if !skip_wsp(&mut $parser.chars) || - $parser.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { - break; - } - skip_comma_wsp(&mut $parser.chars); - } - Ok(()) - } - } -} - -impl<'a> PathParser<'a> { - /// Parse a sub-path. - fn parse_subpath(&mut self) -> Result<(), ()> { - // Handle "moveto" Command first. If there is no "moveto", this is not a valid sub-path - // (i.e. not a valid moveto-drawto-command-group). - self.parse_moveto()?; - - // Handle other commands. - loop { - skip_wsp(&mut self.chars); - if self.chars.peek().map_or(true, |m| *m == 'M' || *m == 'm') { - break; - } - - match self.chars.next() { - Some(command) => { - let abs = command.is_uppercase(); - macro_rules! parse_command { - ( $($($p:pat)|+ => $parse_func:ident,)* ) => { - match command { - $( - $($p)|+ => { - skip_wsp(&mut self.chars); - self.$parse_func(abs)?; - }, - )* - _ => return Err(()), - } - } - } - parse_command!( - 'Z' | 'z' => parse_closepath, - 'L' | 'l' => parse_lineto, - 'H' | 'h' => parse_h_lineto, - 'V' | 'v' => parse_v_lineto, - 'C' | 'c' => parse_curveto, - 'S' | 's' => parse_smooth_curveto, - 'Q' | 'q' => parse_quadratic_bezier_curveto, - 'T' | 't' => parse_smooth_quadratic_bezier_curveto, - 'A' | 'a' => parse_elliprical_arc, - ); - }, - _ => break, // no more commands. - } - } - Ok(()) - } - - /// Parse "moveto" command. - fn parse_moveto(&mut self) -> Result<(), ()> { - let command = match self.chars.next() { - Some(c) if c == 'M' || c == 'm' => c, - _ => return Err(()), - }; - - skip_wsp(&mut self.chars); - let point = parse_coord(&mut self.chars)?; - let absolute = command == 'M'; - self.path.push(PathCommand::MoveTo { point, absolute } ); - - // End of string or the next character is a possible new command. - if !skip_wsp(&mut self.chars) || - self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { - return Ok(()); - } - skip_comma_wsp(&mut self.chars); - - // If a moveto is followed by multiple pairs of coordinates, the subsequent - // pairs are treated as implicit lineto commands. - self.parse_lineto(absolute) - } - - /// Parse "closepath" command. - fn parse_closepath(&mut self, _absolute: bool) -> Result<(), ()> { - self.path.push(PathCommand::ClosePath); - Ok(()) - } - - /// Parse "lineto" command. - fn parse_lineto(&mut self, absolute: bool) -> Result<(), ()> { - parse_arguments!(self, absolute, LineTo, [ point => parse_coord ]) - } - - /// Parse horizontal "lineto" command. - fn parse_h_lineto(&mut self, absolute: bool) -> Result<(), ()> { - parse_arguments!(self, absolute, HorizontalLineTo, [ x => parse_number ]) - } - - /// Parse vertical "lineto" command. - fn parse_v_lineto(&mut self, absolute: bool) -> Result<(), ()> { - parse_arguments!(self, absolute, VerticalLineTo, [ y => parse_number ]) - } - - /// Parse cubic Bézier curve command. - fn parse_curveto(&mut self, absolute: bool) -> Result<(), ()> { - parse_arguments!(self, absolute, CurveTo, [ - control1 => parse_coord, control2 => parse_coord, point => parse_coord - ]) - } - - /// Parse smooth "curveto" command. - fn parse_smooth_curveto(&mut self, absolute: bool) -> Result<(), ()> { - parse_arguments!(self, absolute, SmoothCurveTo, [ - control2 => parse_coord, point => parse_coord - ]) - } - - /// Parse quadratic Bézier curve command. - fn parse_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> { - parse_arguments!(self, absolute, QuadBezierCurveTo, [ - control1 => parse_coord, point => parse_coord - ]) - } - - /// Parse smooth quadratic Bézier curveto command. - fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> { - parse_arguments!(self, absolute, SmoothQuadBezierCurveTo, [ point => parse_coord ]) - } - - /// Parse elliptical arc curve command. - fn parse_elliprical_arc(&mut self, absolute: bool) -> Result<(), ()> { - // Parse a flag whose value is '0' or '1'; otherwise, return Err(()). - let parse_flag = |iter: &mut Peekable| -> Result { - let value = match iter.peek() { - Some(c) if *c == '0' || *c == '1' => *c == '1', - _ => return Err(()), - }; - iter.next(); - Ok(value) - }; - parse_arguments!(self, absolute, EllipticalArc, [ - rx => parse_number, - ry => parse_number, - angle => parse_number, - large_arc_flag => parse_flag, - sweep_flag => parse_flag, - point => parse_coord - ]) - } -} - -/// The SVG path data. -/// -/// https://www.w3.org/TR/SVG11/paths.html#PathData -#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)] -pub struct SVGPathData(Box<[PathCommand]>); - -impl SVGPathData { - /// Return SVGPathData by a slice of PathCommand. - #[inline] - pub fn new(cmd: Box<[PathCommand]>) -> Self { - debug_assert!(!cmd.is_empty()); - SVGPathData(cmd) - } - - /// Get the array of PathCommand. - #[inline] - pub fn commands(&self) -> &[PathCommand] { - debug_assert!(!self.0.is_empty()); - &self.0 - } -} - -impl ToCss for SVGPathData { - #[inline] - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: fmt::Write - { - dest.write_char('"')?; - { - let mut writer = SequenceWriter::new(dest, " "); - for command in self.0.iter() { - writer.item(command)?; - } - } - dest.write_char('"') - } -} - -impl Parse for SVGPathData { - // We cannot use cssparser::Parser to parse a SVG path string because the spec wants to make - // the SVG path string as compact as possible. (i.e. The whitespaces may be dropped.) - // e.g. "M100 200L100 200" is a valid SVG path string. If we use tokenizer, the first ident - // is "M100", instead of "M", and this is not correct. Therefore, we use a Peekable - // str::Char iterator to check each character. - fn parse<'i, 't>( - _context: &ParserContext, - input: &mut Parser<'i, 't> - ) -> Result> { - let location = input.current_source_location(); - let path_string = input.expect_string()?.as_ref(); - if path_string.is_empty() { - // Treat an empty string as invalid, so we will not set it. - return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - - // Parse the svg path string as multiple sub-paths. - let mut path_parser = PathParser { - chars: path_string.chars().peekable(), - path: Vec::new(), - }; - while skip_wsp(&mut path_parser.chars) { - if path_parser.parse_subpath().is_err() { - return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - } - - Ok(SVGPathData::new(path_parser.path.into_boxed_slice())) - } -} - - -/// The SVG path command. -/// The fields of these commands are self-explanatory, so we skip the documents. -/// Note: the index of the control points, e.g. control1, control2, are mapping to the control -/// points of the Bézier curve in the spec. -/// -/// https://www.w3.org/TR/SVG11/paths.html#PathData -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)] -#[allow(missing_docs)] -#[repr(C, u8)] -pub enum PathCommand { - /// The unknown type. - /// https://www.w3.org/TR/SVG/paths.html#__svg__SVGPathSeg__PATHSEG_UNKNOWN - Unknown, - /// The "moveto" command. - MoveTo { point: CoordPair, absolute: bool }, - /// The "lineto" command. - LineTo { point: CoordPair, absolute: bool }, - /// The horizontal "lineto" command. - HorizontalLineTo { x: CSSFloat, absolute: bool }, - /// The vertical "lineto" command. - VerticalLineTo { y: CSSFloat, absolute: bool }, - /// The cubic Bézier curve command. - CurveTo { control1: CoordPair, control2: CoordPair, point: CoordPair, absolute: bool }, - /// The smooth curve command. - SmoothCurveTo { control2: CoordPair, point: CoordPair, absolute: bool }, - /// The quadratic Bézier curve command. - QuadBezierCurveTo { control1: CoordPair, point: CoordPair, absolute: bool }, - /// The smooth quadratic Bézier curve command. - SmoothQuadBezierCurveTo { point: CoordPair, absolute: bool }, - /// The elliptical arc curve command. - EllipticalArc { - rx: CSSFloat, - ry: CSSFloat, - angle: CSSFloat, - large_arc_flag: bool, - sweep_flag: bool, - point: CoordPair, - absolute: bool - }, - /// The "closepath" command. - ClosePath, -} - -impl ToCss for PathCommand { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result - where - W: fmt::Write - { - use self::PathCommand::*; - match *self { - Unknown => dest.write_str("X"), - ClosePath => dest.write_str("Z"), - MoveTo { point, absolute } => { - dest.write_char(if absolute { 'M' } else { 'm' })?; - dest.write_char(' ')?; - point.to_css(dest) - } - LineTo { point, absolute } => { - dest.write_char(if absolute { 'L' } else { 'l' })?; - dest.write_char(' ')?; - point.to_css(dest) - } - CurveTo { control1, control2, point, absolute } => { - dest.write_char(if absolute { 'C' } else { 'c' })?; - dest.write_char(' ')?; - control1.to_css(dest)?; - dest.write_char(' ')?; - control2.to_css(dest)?; - dest.write_char(' ')?; - point.to_css(dest) - }, - QuadBezierCurveTo { control1, point, absolute } => { - dest.write_char(if absolute { 'Q' } else { 'q' })?; - dest.write_char(' ')?; - control1.to_css(dest)?; - dest.write_char(' ')?; - point.to_css(dest) - }, - EllipticalArc { rx, ry, angle, large_arc_flag, sweep_flag, point, absolute } => { - dest.write_char(if absolute { 'A' } else { 'a' })?; - dest.write_char(' ')?; - rx.to_css(dest)?; - dest.write_char(' ')?; - ry.to_css(dest)?; - dest.write_char(' ')?; - angle.to_css(dest)?; - dest.write_char(' ')?; - (large_arc_flag as i32).to_css(dest)?; - dest.write_char(' ')?; - (sweep_flag as i32).to_css(dest)?; - dest.write_char(' ')?; - point.to_css(dest) - }, - HorizontalLineTo { x, absolute } => { - dest.write_char(if absolute { 'H' } else { 'h' })?; - dest.write_char(' ')?; - x.to_css(dest) - }, - VerticalLineTo { y, absolute } => { - dest.write_char(if absolute { 'V' } else { 'v' })?; - dest.write_char(' ')?; - y.to_css(dest) - }, - SmoothCurveTo { control2, point, absolute } => { - dest.write_char(if absolute { 'S' } else { 's' })?; - dest.write_char(' ')?; - control2.to_css(dest)?; - dest.write_char(' ')?; - point.to_css(dest) - }, - SmoothQuadBezierCurveTo { point, absolute } => { - dest.write_char(if absolute { 'T' } else { 't' })?; - dest.write_char(' ')?; - point.to_css(dest) - }, - } - } -} - -/// The path coord type. -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)] -#[repr(C)] -pub struct CoordPair(CSSFloat, CSSFloat); - -impl CoordPair { - /// Create a CoordPair. - #[inline] - pub fn new(x: CSSFloat, y: CSSFloat) -> Self { - CoordPair(x, y) - } -} - -/// Parse a pair of numbers into CoordPair. -fn parse_coord(iter: &mut Peekable) -> Result { - let x = parse_number(iter)?; - skip_comma_wsp(iter); - let y = parse_number(iter)?; - Ok(CoordPair::new(x, y)) -} - -/// This is a special version which parses the number for SVG Path. e.g. "M 0.6.5" should be parsed -/// as MoveTo with a coordinate of ("0.6", ".5"), instead of treating 0.6.5 as a non-valid floating -/// point number. In other words, the logic here is similar with that of -/// tokenizer::consume_numeric, which also consumes the number as many as possible, but here the -/// input is a Peekable and we only accept an integer of a floating point number. -/// -/// The "number" syntax in https://www.w3.org/TR/SVG/paths.html#PathDataBNF -fn parse_number(iter: &mut Peekable) -> Result { - // 1. Check optional sign. - let sign = if iter.peek().map_or(false, |&sign: &char| sign == '+' || sign == '-') { - if iter.next().unwrap() == '-' { -1. } else { 1. } - } else { - 1. - }; - - // 2. Check integer part. - let mut integral_part: f64 = 0.; - let got_dot = if !iter.peek().map_or(false, |&n: &char| n == '.') { - // If the first digit in integer part is neither a dot nor a digit, this is not a number. - if iter.peek().map_or(true, |n: &char| !n.is_ascii_digit()) { - return Err(()); - } - - while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { - integral_part = - integral_part * 10. + iter.next().unwrap().to_digit(10).unwrap() as f64; - } - - iter.peek().map_or(false, |&n: &char| n == '.') - } else { - true - }; - - // 3. Check fractional part. - let mut fractional_part: f64 = 0.; - if got_dot { - // Consume '.'. - iter.next(); - // If the first digit in fractional part is not a digit, this is not a number. - if iter.peek().map_or(true, |n: &char| !n.is_ascii_digit()) { - return Err(()); - } - - let mut factor = 0.1; - while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { - fractional_part += iter.next().unwrap().to_digit(10).unwrap() as f64 * factor; - factor *= 0.1; - } - } - - let mut value = sign * (integral_part + fractional_part); - - // 4. Check exp part. The segment name of SVG Path doesn't include 'E' or 'e', so it's ok to - // treat the numbers after 'E' or 'e' are in the exponential part. - if iter.peek().map_or(false, |&exp: &char| exp == 'E' || exp == 'e') { - // Consume 'E' or 'e'. - iter.next(); - let exp_sign = if iter.peek().map_or(false, |&sign: &char| sign == '+' || sign == '-') { - if iter.next().unwrap() == '-' { -1. } else { 1. } - } else { - 1. - }; - - let mut exp: f64 = 0.; - while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { - exp = exp * 10. + iter.next().unwrap().to_digit(10).unwrap() as f64; - } - - value *= f64::powf(10., exp * exp_sign); - } - - if value.is_finite() { - Ok(value.min(::std::f32::MAX as f64).max(::std::f32::MIN as f64) as CSSFloat) - } else { - Err(()) - } -} - -/// Skip all svg whitespaces, and return true if |iter| hasn't finished. -#[inline] -fn skip_wsp(iter: &mut Peekable) -> bool { - // Note: SVG 1.1 defines the whitespaces as \u{9}, \u{20}, \u{A}, \u{D}. - // However, SVG 2 has one extra whitespace: \u{C}. - // Therefore, we follow the newest spec for the definition of whitespace, - // i.e. \u{9}, \u{20}, \u{A}, \u{C}, \u{D}, by is_ascii_whitespace(). - while iter.peek().map_or(false, |c: &char| c.is_ascii_whitespace()) { - iter.next(); - } - iter.peek().is_some() -} - -/// Skip all svg whitespaces and one comma, and return true if |iter| hasn't finished. -#[inline] -fn skip_comma_wsp(iter: &mut Peekable) -> bool { - if !skip_wsp(iter) { - return false; - } - - if *iter.peek().unwrap() != ',' { - return true; - } - iter.next(); - - skip_wsp(iter) -} diff --git a/components/style/values/specified/svg_path.rs b/components/style/values/specified/svg_path.rs new file mode 100644 index 00000000000..8d92e94aee0 --- /dev/null +++ b/components/style/values/specified/svg_path.rs @@ -0,0 +1,517 @@ +/* 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/. */ + +//! Specified types for SVG Path. + +use cssparser::Parser; +use parser::{Parse, ParserContext}; +use std::fmt::{self, Write}; +use std::iter::Peekable; +use std::str::Chars; +use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; +use style_traits::values::SequenceWriter; +use values::CSSFloat; + + +/// The SVG path data. +/// +/// https://www.w3.org/TR/SVG11/paths.html#PathData +#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)] +pub struct SVGPathData(Box<[PathCommand]>); + +impl SVGPathData { + /// Return SVGPathData by a slice of PathCommand. + #[inline] + pub fn new(cmd: Box<[PathCommand]>) -> Self { + debug_assert!(!cmd.is_empty()); + SVGPathData(cmd) + } + + /// Get the array of PathCommand. + #[inline] + pub fn commands(&self) -> &[PathCommand] { + debug_assert!(!self.0.is_empty()); + &self.0 + } +} + +impl ToCss for SVGPathData { + #[inline] + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: fmt::Write + { + dest.write_char('"')?; + { + let mut writer = SequenceWriter::new(dest, " "); + for command in self.0.iter() { + writer.item(command)?; + } + } + dest.write_char('"') + } +} + +impl Parse for SVGPathData { + // We cannot use cssparser::Parser to parse a SVG path string because the spec wants to make + // the SVG path string as compact as possible. (i.e. The whitespaces may be dropped.) + // e.g. "M100 200L100 200" is a valid SVG path string. If we use tokenizer, the first ident + // is "M100", instead of "M", and this is not correct. Therefore, we use a Peekable + // str::Char iterator to check each character. + fn parse<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't> + ) -> Result> { + let location = input.current_source_location(); + let path_string = input.expect_string()?.as_ref(); + if path_string.is_empty() { + // Treat an empty string as invalid, so we will not set it. + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + // Parse the svg path string as multiple sub-paths. + let mut path_parser = PathParser::new(path_string); + while skip_wsp(&mut path_parser.chars) { + if path_parser.parse_subpath().is_err() { + return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + } + + Ok(SVGPathData::new(path_parser.path.into_boxed_slice())) + } +} + + +/// The SVG path command. +/// The fields of these commands are self-explanatory, so we skip the documents. +/// Note: the index of the control points, e.g. control1, control2, are mapping to the control +/// points of the Bézier curve in the spec. +/// +/// https://www.w3.org/TR/SVG11/paths.html#PathData +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)] +#[allow(missing_docs)] +#[repr(C, u8)] +pub enum PathCommand { + /// The unknown type. + /// https://www.w3.org/TR/SVG/paths.html#__svg__SVGPathSeg__PATHSEG_UNKNOWN + Unknown, + /// The "moveto" command. + MoveTo { point: CoordPair, absolute: bool }, + /// The "lineto" command. + LineTo { point: CoordPair, absolute: bool }, + /// The horizontal "lineto" command. + HorizontalLineTo { x: CSSFloat, absolute: bool }, + /// The vertical "lineto" command. + VerticalLineTo { y: CSSFloat, absolute: bool }, + /// The cubic Bézier curve command. + CurveTo { control1: CoordPair, control2: CoordPair, point: CoordPair, absolute: bool }, + /// The smooth curve command. + SmoothCurveTo { control2: CoordPair, point: CoordPair, absolute: bool }, + /// The quadratic Bézier curve command. + QuadBezierCurveTo { control1: CoordPair, point: CoordPair, absolute: bool }, + /// The smooth quadratic Bézier curve command. + SmoothQuadBezierCurveTo { point: CoordPair, absolute: bool }, + /// The elliptical arc curve command. + EllipticalArc { + rx: CSSFloat, + ry: CSSFloat, + angle: CSSFloat, + large_arc_flag: bool, + sweep_flag: bool, + point: CoordPair, + absolute: bool + }, + /// The "closepath" command. + ClosePath, +} + +impl ToCss for PathCommand { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: fmt::Write + { + use self::PathCommand::*; + match *self { + Unknown => dest.write_str("X"), + ClosePath => dest.write_str("Z"), + MoveTo { point, absolute } => { + dest.write_char(if absolute { 'M' } else { 'm' })?; + dest.write_char(' ')?; + point.to_css(dest) + } + LineTo { point, absolute } => { + dest.write_char(if absolute { 'L' } else { 'l' })?; + dest.write_char(' ')?; + point.to_css(dest) + } + CurveTo { control1, control2, point, absolute } => { + dest.write_char(if absolute { 'C' } else { 'c' })?; + dest.write_char(' ')?; + control1.to_css(dest)?; + dest.write_char(' ')?; + control2.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + QuadBezierCurveTo { control1, point, absolute } => { + dest.write_char(if absolute { 'Q' } else { 'q' })?; + dest.write_char(' ')?; + control1.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + EllipticalArc { rx, ry, angle, large_arc_flag, sweep_flag, point, absolute } => { + dest.write_char(if absolute { 'A' } else { 'a' })?; + dest.write_char(' ')?; + rx.to_css(dest)?; + dest.write_char(' ')?; + ry.to_css(dest)?; + dest.write_char(' ')?; + angle.to_css(dest)?; + dest.write_char(' ')?; + (large_arc_flag as i32).to_css(dest)?; + dest.write_char(' ')?; + (sweep_flag as i32).to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + HorizontalLineTo { x, absolute } => { + dest.write_char(if absolute { 'H' } else { 'h' })?; + dest.write_char(' ')?; + x.to_css(dest) + }, + VerticalLineTo { y, absolute } => { + dest.write_char(if absolute { 'V' } else { 'v' })?; + dest.write_char(' ')?; + y.to_css(dest) + }, + SmoothCurveTo { control2, point, absolute } => { + dest.write_char(if absolute { 'S' } else { 's' })?; + dest.write_char(' ')?; + control2.to_css(dest)?; + dest.write_char(' ')?; + point.to_css(dest) + }, + SmoothQuadBezierCurveTo { point, absolute } => { + dest.write_char(if absolute { 'T' } else { 't' })?; + dest.write_char(' ')?; + point.to_css(dest) + }, + } + } +} + + +/// The path coord type. +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)] +#[repr(C)] +pub struct CoordPair(CSSFloat, CSSFloat); + +impl CoordPair { + /// Create a CoordPair. + #[inline] + pub fn new(x: CSSFloat, y: CSSFloat) -> Self { + CoordPair(x, y) + } +} + + +/// SVG Path parser. +struct PathParser<'a> { + chars: Peekable>, + path: Vec, +} + +macro_rules! parse_arguments { + ( + $parser:ident, + $abs:ident, + $enum:ident, + [ $para:ident => $func:ident $(, $other_para:ident => $other_func:ident)* ] + ) => { + { + loop { + let $para = $func(&mut $parser.chars)?; + $( + skip_comma_wsp(&mut $parser.chars); + let $other_para = $other_func(&mut $parser.chars)?; + )* + $parser.path.push(PathCommand::$enum { $para $(, $other_para)*, $abs }); + + // End of string or the next character is a possible new command. + if !skip_wsp(&mut $parser.chars) || + $parser.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { + break; + } + skip_comma_wsp(&mut $parser.chars); + } + Ok(()) + } + } +} + +impl<'a> PathParser<'a> { + /// Return a PathParser. + #[inline] + fn new(string: &'a str) -> Self { + PathParser { + chars: string.chars().peekable(), + path: Vec::new(), + } + } + + /// Parse a sub-path. + fn parse_subpath(&mut self) -> Result<(), ()> { + // Handle "moveto" Command first. If there is no "moveto", this is not a valid sub-path + // (i.e. not a valid moveto-drawto-command-group). + self.parse_moveto()?; + + // Handle other commands. + loop { + skip_wsp(&mut self.chars); + if self.chars.peek().map_or(true, |m| *m == 'M' || *m == 'm') { + break; + } + + match self.chars.next() { + Some(command) => { + let abs = command.is_uppercase(); + macro_rules! parse_command { + ( $($($p:pat)|+ => $parse_func:ident,)* ) => { + match command { + $( + $($p)|+ => { + skip_wsp(&mut self.chars); + self.$parse_func(abs)?; + }, + )* + _ => return Err(()), + } + } + } + parse_command!( + 'Z' | 'z' => parse_closepath, + 'L' | 'l' => parse_lineto, + 'H' | 'h' => parse_h_lineto, + 'V' | 'v' => parse_v_lineto, + 'C' | 'c' => parse_curveto, + 'S' | 's' => parse_smooth_curveto, + 'Q' | 'q' => parse_quadratic_bezier_curveto, + 'T' | 't' => parse_smooth_quadratic_bezier_curveto, + 'A' | 'a' => parse_elliprical_arc, + ); + }, + _ => break, // no more commands. + } + } + Ok(()) + } + + /// Parse "moveto" command. + fn parse_moveto(&mut self) -> Result<(), ()> { + let command = match self.chars.next() { + Some(c) if c == 'M' || c == 'm' => c, + _ => return Err(()), + }; + + skip_wsp(&mut self.chars); + let point = parse_coord(&mut self.chars)?; + let absolute = command == 'M'; + self.path.push(PathCommand::MoveTo { point, absolute } ); + + // End of string or the next character is a possible new command. + if !skip_wsp(&mut self.chars) || + self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic()) { + return Ok(()); + } + skip_comma_wsp(&mut self.chars); + + // If a moveto is followed by multiple pairs of coordinates, the subsequent + // pairs are treated as implicit lineto commands. + self.parse_lineto(absolute) + } + + /// Parse "closepath" command. + fn parse_closepath(&mut self, _absolute: bool) -> Result<(), ()> { + self.path.push(PathCommand::ClosePath); + Ok(()) + } + + /// Parse "lineto" command. + fn parse_lineto(&mut self, absolute: bool) -> Result<(), ()> { + parse_arguments!(self, absolute, LineTo, [ point => parse_coord ]) + } + + /// Parse horizontal "lineto" command. + fn parse_h_lineto(&mut self, absolute: bool) -> Result<(), ()> { + parse_arguments!(self, absolute, HorizontalLineTo, [ x => parse_number ]) + } + + /// Parse vertical "lineto" command. + fn parse_v_lineto(&mut self, absolute: bool) -> Result<(), ()> { + parse_arguments!(self, absolute, VerticalLineTo, [ y => parse_number ]) + } + + /// Parse cubic Bézier curve command. + fn parse_curveto(&mut self, absolute: bool) -> Result<(), ()> { + parse_arguments!(self, absolute, CurveTo, [ + control1 => parse_coord, control2 => parse_coord, point => parse_coord + ]) + } + + /// Parse smooth "curveto" command. + fn parse_smooth_curveto(&mut self, absolute: bool) -> Result<(), ()> { + parse_arguments!(self, absolute, SmoothCurveTo, [ + control2 => parse_coord, point => parse_coord + ]) + } + + /// Parse quadratic Bézier curve command. + fn parse_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> { + parse_arguments!(self, absolute, QuadBezierCurveTo, [ + control1 => parse_coord, point => parse_coord + ]) + } + + /// Parse smooth quadratic Bézier curveto command. + fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: bool) -> Result<(), ()> { + parse_arguments!(self, absolute, SmoothQuadBezierCurveTo, [ point => parse_coord ]) + } + + /// Parse elliptical arc curve command. + fn parse_elliprical_arc(&mut self, absolute: bool) -> Result<(), ()> { + // Parse a flag whose value is '0' or '1'; otherwise, return Err(()). + let parse_flag = |iter: &mut Peekable| -> Result { + let value = match iter.peek() { + Some(c) if *c == '0' || *c == '1' => *c == '1', + _ => return Err(()), + }; + iter.next(); + Ok(value) + }; + parse_arguments!(self, absolute, EllipticalArc, [ + rx => parse_number, + ry => parse_number, + angle => parse_number, + large_arc_flag => parse_flag, + sweep_flag => parse_flag, + point => parse_coord + ]) + } +} + + +/// Parse a pair of numbers into CoordPair. +fn parse_coord(iter: &mut Peekable) -> Result { + let x = parse_number(iter)?; + skip_comma_wsp(iter); + let y = parse_number(iter)?; + Ok(CoordPair::new(x, y)) +} + +/// This is a special version which parses the number for SVG Path. e.g. "M 0.6.5" should be parsed +/// as MoveTo with a coordinate of ("0.6", ".5"), instead of treating 0.6.5 as a non-valid floating +/// point number. In other words, the logic here is similar with that of +/// tokenizer::consume_numeric, which also consumes the number as many as possible, but here the +/// input is a Peekable and we only accept an integer of a floating point number. +/// +/// The "number" syntax in https://www.w3.org/TR/SVG/paths.html#PathDataBNF +fn parse_number(iter: &mut Peekable) -> Result { + // 1. Check optional sign. + let sign = if iter.peek().map_or(false, |&sign: &char| sign == '+' || sign == '-') { + if iter.next().unwrap() == '-' { -1. } else { 1. } + } else { + 1. + }; + + // 2. Check integer part. + let mut integral_part: f64 = 0.; + let got_dot = if !iter.peek().map_or(false, |&n: &char| n == '.') { + // If the first digit in integer part is neither a dot nor a digit, this is not a number. + if iter.peek().map_or(true, |n: &char| !n.is_ascii_digit()) { + return Err(()); + } + + while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { + integral_part = + integral_part * 10. + iter.next().unwrap().to_digit(10).unwrap() as f64; + } + + iter.peek().map_or(false, |&n: &char| n == '.') + } else { + true + }; + + // 3. Check fractional part. + let mut fractional_part: f64 = 0.; + if got_dot { + // Consume '.'. + iter.next(); + // If the first digit in fractional part is not a digit, this is not a number. + if iter.peek().map_or(true, |n: &char| !n.is_ascii_digit()) { + return Err(()); + } + + let mut factor = 0.1; + while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { + fractional_part += iter.next().unwrap().to_digit(10).unwrap() as f64 * factor; + factor *= 0.1; + } + } + + let mut value = sign * (integral_part + fractional_part); + + // 4. Check exp part. The segment name of SVG Path doesn't include 'E' or 'e', so it's ok to + // treat the numbers after 'E' or 'e' are in the exponential part. + if iter.peek().map_or(false, |&exp: &char| exp == 'E' || exp == 'e') { + // Consume 'E' or 'e'. + iter.next(); + let exp_sign = if iter.peek().map_or(false, |&sign: &char| sign == '+' || sign == '-') { + if iter.next().unwrap() == '-' { -1. } else { 1. } + } else { + 1. + }; + + let mut exp: f64 = 0.; + while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { + exp = exp * 10. + iter.next().unwrap().to_digit(10).unwrap() as f64; + } + + value *= f64::powf(10., exp * exp_sign); + } + + if value.is_finite() { + Ok(value.min(::std::f32::MAX as f64).max(::std::f32::MIN as f64) as CSSFloat) + } else { + Err(()) + } +} + +/// Skip all svg whitespaces, and return true if |iter| hasn't finished. +#[inline] +fn skip_wsp(iter: &mut Peekable) -> bool { + // Note: SVG 1.1 defines the whitespaces as \u{9}, \u{20}, \u{A}, \u{D}. + // However, SVG 2 has one extra whitespace: \u{C}. + // Therefore, we follow the newest spec for the definition of whitespace, + // i.e. \u{9}, \u{20}, \u{A}, \u{C}, \u{D}, by is_ascii_whitespace(). + while iter.peek().map_or(false, |c: &char| c.is_ascii_whitespace()) { + iter.next(); + } + iter.peek().is_some() +} + +/// Skip all svg whitespaces and one comma, and return true if |iter| hasn't finished. +#[inline] +fn skip_comma_wsp(iter: &mut Peekable) -> bool { + if !skip_wsp(iter) { + return false; + } + + if *iter.peek().unwrap() != ',' { + return true; + } + iter.next(); + + skip_wsp(iter) +} From bb65d1fb6d306fe83f784487ea46011d11827545 Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Tue, 14 Aug 2018 18:58:18 -0700 Subject: [PATCH 21/34] style: Define path() for clip-path. For now, |clip-path: path()| is chrome-only, and not for shape-outside, so we only implement the parser for clip-path. Besides, I didn't put path() in BasicShape because path() doesn't use the reference box to resolve the percentage or keywords (i.e. SVG path only accept floating point or integer number as the css pixel value). Therefore, I add it into ShapeSource, instead of BasicShape. Differential Revision: https://phabricator.services.mozilla.com/D3633 --- components/style/gecko/conversions.rs | 42 ++++++++---- components/style/properties/gecko.mako.rs | 49 +++++++++----- .../style/values/generics/basic_shape.rs | 17 +++++ .../style/values/specified/basic_shape.rs | 64 ++++++++++++++++++- 4 files changed, 143 insertions(+), 29 deletions(-) diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index a3109a1f226..e85bc8ec310 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -670,10 +670,11 @@ pub mod basic_shape { use values::computed::position; use values::computed::url::ComputedUrl; use values::generics::basic_shape::{BasicShape as GenericBasicShape, InsetRect, Polygon}; - use values::generics::basic_shape::{Circle, Ellipse, FillRule, PolygonCoord}; + use values::generics::basic_shape::{Circle, Ellipse, FillRule, Path, PolygonCoord}; use values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource}; use values::generics::border::BorderRadius as GenericBorderRadius; use values::generics::rect::Rect; + use values::specified::SVGPathData; impl StyleShapeSource { /// Convert StyleShapeSource to ShapeSource except URL and Image @@ -698,7 +699,34 @@ pub mod basic_shape { Some(ShapeSource::Shape(shape, reference_box)) }, StyleShapeSourceType::URL | StyleShapeSourceType::Image => None, - StyleShapeSourceType::Path => None, + StyleShapeSourceType::Path => { + let path = self.to_svg_path().expect("expect an SVGPathData"); + let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; + let fill = if gecko_path.mFillRule == StyleFillRule::Evenodd { + FillRule::Evenodd + } else { + FillRule::Nonzero + }; + Some(ShapeSource::Path(Path { fill, path })) + }, + } + } + + /// Generate a SVGPathData from StyleShapeSource if possible. + fn to_svg_path(&self) -> Option { + use gecko_bindings::structs::StylePathCommand; + use values::specified::svg_path::PathCommand; + match self.mType { + StyleShapeSourceType::Path => { + let gecko_path = unsafe { &*self.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; + let result: Vec = + gecko_path.mPath.iter().map(|gecko: &StylePathCommand| { + // unsafe: cbindgen ensures the representation is the same. + unsafe{ ::std::mem::transmute(*gecko) } + }).collect(); + Some(SVGPathData::new(result.into_boxed_slice())) + }, + _ => None, } } } @@ -742,17 +770,9 @@ pub mod basic_shape { impl<'a> From<&'a StyleShapeSource> for OffsetPath { fn from(other: &'a StyleShapeSource) -> Self { - use gecko_bindings::structs::StylePathCommand; - use values::specified::svg_path::{SVGPathData, PathCommand}; match other.mType { StyleShapeSourceType::Path => { - let gecko_path = unsafe { &*other.__bindgen_anon_1.mSVGPath.as_ref().mPtr }; - let result: Vec = - gecko_path.mPath.iter().map(|gecko: &StylePathCommand| { - // unsafe: cbindgen ensures the representation is the same. - unsafe{ ::std::mem::transmute(*gecko) } - }).collect(); - OffsetPath::Path(SVGPathData::new(result.into_boxed_slice())) + OffsetPath::Path(other.to_svg_path().expect("Cannot convert to SVGPathData")) }, StyleShapeSourceType::None => OffsetPath::none(), StyleShapeSourceType::Shape | diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 76d61838708..9e9a6835a42 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -3683,27 +3683,16 @@ fn static_assert() { ${impl_simple_type_with_conversion("touch_action")} pub fn set_offset_path(&mut self, v: longhands::offset_path::computed_value::T) { - use gecko_bindings::bindings::{Gecko_NewStyleMotion, Gecko_NewStyleSVGPath}; - use gecko_bindings::bindings::Gecko_SetStyleMotion; + use gecko_bindings::bindings::{Gecko_NewStyleMotion, Gecko_SetStyleMotion}; use gecko_bindings::structs::StyleShapeSourceType; + use values::generics::basic_shape::FillRule; use values::specified::OffsetPath; let motion = unsafe { Gecko_NewStyleMotion().as_mut().unwrap() }; match v { OffsetPath::None => motion.mOffsetPath.mType = StyleShapeSourceType::None, - OffsetPath::Path(servo_path) => { - motion.mOffsetPath.mType = StyleShapeSourceType::Path; - let gecko_path = unsafe { - let ref mut source = motion.mOffsetPath; - Gecko_NewStyleSVGPath(source); - &mut source.__bindgen_anon_1.mSVGPath.as_mut().mPtr.as_mut().unwrap().mPath - }; - unsafe { gecko_path.set_len(servo_path.commands().len() as u32) }; - debug_assert_eq!(gecko_path.len(), servo_path.commands().len()); - for (servo, gecko) in servo_path.commands().iter().zip(gecko_path.iter_mut()) { - // unsafe: cbindgen ensures the representation is the same. - *gecko = unsafe { transmute(*servo) }; - } + OffsetPath::Path(p) => { + set_style_svg_path(&mut motion.mOffsetPath, &p, FillRule::Nonzero) }, } unsafe { Gecko_SetStyleMotion(&mut self.gecko.mMotion, motion) }; @@ -4982,6 +4971,35 @@ fn static_assert() { } +// Set SVGPathData to StyleShapeSource. +fn set_style_svg_path( + shape_source: &mut structs::mozilla::StyleShapeSource, + servo_path: &values::specified::svg_path::SVGPathData, + fill: values::generics::basic_shape::FillRule, +) { + use gecko_bindings::bindings::Gecko_NewStyleSVGPath; + use gecko_bindings::structs::StyleShapeSourceType; + + // Setup type. + shape_source.mType = StyleShapeSourceType::Path; + + // Setup path. + let gecko_path = unsafe { + Gecko_NewStyleSVGPath(shape_source); + &mut shape_source.__bindgen_anon_1.mSVGPath.as_mut().mPtr.as_mut().unwrap() + }; + unsafe { gecko_path.mPath.set_len(servo_path.commands().len() as u32) }; + debug_assert_eq!(gecko_path.mPath.len(), servo_path.commands().len()); + for (servo, gecko) in servo_path.commands().iter().zip(gecko_path.mPath.iter_mut()) { + // unsafe: cbindgen ensures the representation is the same. + *gecko = unsafe { transmute(*servo) }; + } + + // Setup fill-rule. + // unsafe: cbindgen ensures the representation is the same. + gecko_path.mFillRule = unsafe { transmute(fill) }; +} + <%def name="impl_shape_source(ident, gecko_ffi_name)"> pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { use gecko_bindings::bindings::{Gecko_NewBasicShape, Gecko_DestroyShapeSource}; @@ -5021,6 +5039,7 @@ fn static_assert() { ${ident}.mReferenceBox = reference.into(); ${ident}.mType = StyleShapeSourceType::Box; } + ShapeSource::Path(p) => set_style_svg_path(${ident}, &p.path, p.fill), ShapeSource::Shape(servo_shape, maybe_box) => { fn init_shape(${ident}: &mut StyleShapeSource, basic_shape_type: StyleBasicShapeType) -> &mut StyleBasicShape { diff --git a/components/style/values/generics/basic_shape.rs b/components/style/values/generics/basic_shape.rs index ee7455e777d..0eccf011c71 100644 --- a/components/style/values/generics/basic_shape.rs +++ b/components/style/values/generics/basic_shape.rs @@ -12,6 +12,7 @@ use values::distance::{ComputeSquaredDistance, SquaredDistance}; use values::generics::border::BorderRadius; use values::generics::position::Position; use values::generics::rect::Rect; +use values::specified::SVGPathData; /// A clipping shape, for `clip-path`. pub type ClippingShape = ShapeSource; @@ -54,6 +55,9 @@ pub enum ShapeSource { #[animation(error)] Box(ReferenceBox), #[animation(error)] + #[css(function)] + Path(Path), + #[animation(error)] None, } @@ -144,6 +148,19 @@ pub enum FillRule { Evenodd, } +/// The path function defined in css-shape-2. +/// +/// https://drafts.csswg.org/css-shapes-2/#funcdef-path +#[css(comma)] +#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)] +pub struct Path { + /// The filling rule for the svg path. + #[css(skip_if = "fill_is_default")] + pub fill: FillRule, + /// The svg path data. + pub path: SVGPathData, +} + // FIXME(nox): Implement ComputeSquaredDistance for T types and stop // using PartialEq here, this will let us derive this impl. impl ComputeSquaredDistance for ShapeSource diff --git a/components/style/values/specified/basic_shape.rs b/components/style/values/specified/basic_shape.rs index 2fa693a1978..824c13f1f01 100644 --- a/components/style/values/specified/basic_shape.rs +++ b/components/style/values/specified/basic_shape.rs @@ -14,9 +14,11 @@ use std::fmt::{self, Write}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use values::computed::Percentage; use values::generics::basic_shape as generic; -use values::generics::basic_shape::{FillRule, GeometryBox, PolygonCoord, ShapeBox, ShapeSource}; +use values::generics::basic_shape::{FillRule, GeometryBox, Path, PolygonCoord}; +use values::generics::basic_shape::{ShapeBox, ShapeSource}; use values::generics::rect::Rect; use values::specified::LengthOrPercentage; +use values::specified::SVGPathData; use values::specified::border::BorderRadius; use values::specified::image::Image; use values::specified::position::{HorizontalPosition, Position, PositionComponent}; @@ -47,12 +49,42 @@ pub type ShapeRadius = generic::ShapeRadius; /// The specified value of `Polygon` pub type Polygon = generic::Polygon; -impl Parse for ShapeSource +impl Parse for ClippingShape { + #[inline] + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + // |clip-path:path()| is a chrome-only property value support for now. `path()` is + // defined in css-shape-2, but the spec is not stable enough, and we haven't decided + // to make it public yet. However, it has some benefits for the front-end, so we + // implement it. + if context.chrome_rules_enabled() { + if let Ok(p) = input.try(|i| Path::parse(context, i)) { + return Ok(ShapeSource::Path(p)); + } + } + Self::parse_internal(context, input) + } +} + +impl Parse for FloatAreaShape { + #[inline] + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + Self::parse_internal(context, input) + } +} + +impl ShapeSource where ReferenceBox: Parse, ImageOrUrl: Parse, { - fn parse<'i, 't>( + /// The internal parser for ShapeSource. + fn parse_internal<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { @@ -393,3 +425,29 @@ impl Polygon { }) } } + +impl Parse for Path { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + input.expect_function_matching("path")?; + input.parse_nested_block(|i| Self::parse_function_arguments(context, i)) + } +} + +impl Path { + /// Parse the inner arguments of a `path` function. + fn parse_function_arguments<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let fill = input.try(|i| -> Result<_, ParseError> { + let fill = FillRule::parse(i)?; + i.expect_comma()?; + Ok(fill) + }).unwrap_or_default(); + let path = SVGPathData::parse(context, input)?; + Ok(Path { fill, path }) + } +} From 1bc452703b7f5e372c302249949d3f2a5328cc76 Mon Sep 17 00:00:00 2001 From: Boris Chiou Date: Mon, 27 Aug 2018 18:14:49 +0000 Subject: [PATCH 22/34] style: Parse byte slice in PathParser. We only care about ascii char for svg path, so we could parse the string as byte slice. Differential Revision: https://phabricator.services.mozilla.com/D4168 --- components/style/values/specified/svg_path.rs | 94 +++++++++---------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/components/style/values/specified/svg_path.rs b/components/style/values/specified/svg_path.rs index 8d92e94aee0..48ae185477d 100644 --- a/components/style/values/specified/svg_path.rs +++ b/components/style/values/specified/svg_path.rs @@ -7,8 +7,8 @@ use cssparser::Parser; use parser::{Parse, ParserContext}; use std::fmt::{self, Write}; -use std::iter::Peekable; -use std::str::Chars; +use std::iter::{Cloned, Peekable}; +use std::slice; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use style_traits::values::SequenceWriter; use values::CSSFloat; @@ -133,8 +133,8 @@ impl ToCss for PathCommand { { use self::PathCommand::*; match *self { - Unknown => dest.write_str("X"), - ClosePath => dest.write_str("Z"), + Unknown => dest.write_char('X'), + ClosePath => dest.write_char('Z'), MoveTo { point, absolute } => { dest.write_char(if absolute { 'M' } else { 'm' })?; dest.write_char(' ')?; @@ -219,7 +219,7 @@ impl CoordPair { /// SVG Path parser. struct PathParser<'a> { - chars: Peekable>, + chars: Peekable>>, path: Vec, } @@ -256,7 +256,7 @@ impl<'a> PathParser<'a> { #[inline] fn new(string: &'a str) -> Self { PathParser { - chars: string.chars().peekable(), + chars: string.as_bytes().iter().cloned().peekable(), path: Vec::new(), } } @@ -270,13 +270,13 @@ impl<'a> PathParser<'a> { // Handle other commands. loop { skip_wsp(&mut self.chars); - if self.chars.peek().map_or(true, |m| *m == 'M' || *m == 'm') { + if self.chars.peek().map_or(true, |&m| m == b'M' || m == b'm') { break; } match self.chars.next() { Some(command) => { - let abs = command.is_uppercase(); + let abs = command.is_ascii_uppercase(); macro_rules! parse_command { ( $($($p:pat)|+ => $parse_func:ident,)* ) => { match command { @@ -291,15 +291,15 @@ impl<'a> PathParser<'a> { } } parse_command!( - 'Z' | 'z' => parse_closepath, - 'L' | 'l' => parse_lineto, - 'H' | 'h' => parse_h_lineto, - 'V' | 'v' => parse_v_lineto, - 'C' | 'c' => parse_curveto, - 'S' | 's' => parse_smooth_curveto, - 'Q' | 'q' => parse_quadratic_bezier_curveto, - 'T' | 't' => parse_smooth_quadratic_bezier_curveto, - 'A' | 'a' => parse_elliprical_arc, + b'Z' | b'z' => parse_closepath, + b'L' | b'l' => parse_lineto, + b'H' | b'h' => parse_h_lineto, + b'V' | b'v' => parse_v_lineto, + b'C' | b'c' => parse_curveto, + b'S' | b's' => parse_smooth_curveto, + b'Q' | b'q' => parse_quadratic_bezier_curveto, + b'T' | b't' => parse_smooth_quadratic_bezier_curveto, + b'A' | b'a' => parse_elliprical_arc, ); }, _ => break, // no more commands. @@ -311,13 +311,13 @@ impl<'a> PathParser<'a> { /// Parse "moveto" command. fn parse_moveto(&mut self) -> Result<(), ()> { let command = match self.chars.next() { - Some(c) if c == 'M' || c == 'm' => c, + Some(c) if c == b'M' || c == b'm' => c, _ => return Err(()), }; skip_wsp(&mut self.chars); let point = parse_coord(&mut self.chars)?; - let absolute = command == 'M'; + let absolute = command == b'M'; self.path.push(PathCommand::MoveTo { point, absolute } ); // End of string or the next character is a possible new command. @@ -382,13 +382,11 @@ impl<'a> PathParser<'a> { /// Parse elliptical arc curve command. fn parse_elliprical_arc(&mut self, absolute: bool) -> Result<(), ()> { // Parse a flag whose value is '0' or '1'; otherwise, return Err(()). - let parse_flag = |iter: &mut Peekable| -> Result { - let value = match iter.peek() { - Some(c) if *c == '0' || *c == '1' => *c == '1', - _ => return Err(()), - }; - iter.next(); - Ok(value) + let parse_flag = |iter: &mut Peekable>>| -> Result { + match iter.next() { + Some(c) if c == b'0' || c == b'1' => Ok(c == b'1'), + _ => Err(()), + } }; parse_arguments!(self, absolute, EllipticalArc, [ rx => parse_number, @@ -403,7 +401,7 @@ impl<'a> PathParser<'a> { /// Parse a pair of numbers into CoordPair. -fn parse_coord(iter: &mut Peekable) -> Result { +fn parse_coord(iter: &mut Peekable>>) -> Result { let x = parse_number(iter)?; skip_comma_wsp(iter); let y = parse_number(iter)?; @@ -417,28 +415,28 @@ fn parse_coord(iter: &mut Peekable) -> Result { /// input is a Peekable and we only accept an integer of a floating point number. /// /// The "number" syntax in https://www.w3.org/TR/SVG/paths.html#PathDataBNF -fn parse_number(iter: &mut Peekable) -> Result { +fn parse_number(iter: &mut Peekable>>) -> Result { // 1. Check optional sign. - let sign = if iter.peek().map_or(false, |&sign: &char| sign == '+' || sign == '-') { - if iter.next().unwrap() == '-' { -1. } else { 1. } + let sign = if iter.peek().map_or(false, |&sign| sign == b'+' || sign == b'-') { + if iter.next().unwrap() == b'-' { -1. } else { 1. } } else { 1. }; // 2. Check integer part. let mut integral_part: f64 = 0.; - let got_dot = if !iter.peek().map_or(false, |&n: &char| n == '.') { + let got_dot = if !iter.peek().map_or(false, |&n| n == b'.') { // If the first digit in integer part is neither a dot nor a digit, this is not a number. - if iter.peek().map_or(true, |n: &char| !n.is_ascii_digit()) { + if iter.peek().map_or(true, |n| !n.is_ascii_digit()) { return Err(()); } - while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { + while iter.peek().map_or(false, |n| n.is_ascii_digit()) { integral_part = - integral_part * 10. + iter.next().unwrap().to_digit(10).unwrap() as f64; + integral_part * 10. + (iter.next().unwrap() - b'0') as f64; } - iter.peek().map_or(false, |&n: &char| n == '.') + iter.peek().map_or(false, |&n| n == b'.') } else { true }; @@ -449,13 +447,13 @@ fn parse_number(iter: &mut Peekable) -> Result { // Consume '.'. iter.next(); // If the first digit in fractional part is not a digit, this is not a number. - if iter.peek().map_or(true, |n: &char| !n.is_ascii_digit()) { + if iter.peek().map_or(true, |n| !n.is_ascii_digit()) { return Err(()); } let mut factor = 0.1; - while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { - fractional_part += iter.next().unwrap().to_digit(10).unwrap() as f64 * factor; + while iter.peek().map_or(false, |n| n.is_ascii_digit()) { + fractional_part += (iter.next().unwrap() - b'0') as f64 * factor; factor *= 0.1; } } @@ -464,18 +462,18 @@ fn parse_number(iter: &mut Peekable) -> Result { // 4. Check exp part. The segment name of SVG Path doesn't include 'E' or 'e', so it's ok to // treat the numbers after 'E' or 'e' are in the exponential part. - if iter.peek().map_or(false, |&exp: &char| exp == 'E' || exp == 'e') { + if iter.peek().map_or(false, |&exp| exp == b'E' || exp == b'e') { // Consume 'E' or 'e'. iter.next(); - let exp_sign = if iter.peek().map_or(false, |&sign: &char| sign == '+' || sign == '-') { - if iter.next().unwrap() == '-' { -1. } else { 1. } + let exp_sign = if iter.peek().map_or(false, |&sign| sign == b'+' || sign == b'-') { + if iter.next().unwrap() == b'-' { -1. } else { 1. } } else { 1. }; let mut exp: f64 = 0.; - while iter.peek().map_or(false, |n: &char| n.is_ascii_digit()) { - exp = exp * 10. + iter.next().unwrap().to_digit(10).unwrap() as f64; + while iter.peek().map_or(false, |n| n.is_ascii_digit()) { + exp = exp * 10. + (iter.next().unwrap() - b'0') as f64; } value *= f64::powf(10., exp * exp_sign); @@ -490,12 +488,12 @@ fn parse_number(iter: &mut Peekable) -> Result { /// Skip all svg whitespaces, and return true if |iter| hasn't finished. #[inline] -fn skip_wsp(iter: &mut Peekable) -> bool { +fn skip_wsp(iter: &mut Peekable>>) -> bool { // Note: SVG 1.1 defines the whitespaces as \u{9}, \u{20}, \u{A}, \u{D}. // However, SVG 2 has one extra whitespace: \u{C}. // Therefore, we follow the newest spec for the definition of whitespace, - // i.e. \u{9}, \u{20}, \u{A}, \u{C}, \u{D}, by is_ascii_whitespace(). - while iter.peek().map_or(false, |c: &char| c.is_ascii_whitespace()) { + // i.e. \u{9}, \u{20}, \u{A}, \u{C}, \u{D}. + while iter.peek().map_or(false, |c| c.is_ascii_whitespace()) { iter.next(); } iter.peek().is_some() @@ -503,12 +501,12 @@ fn skip_wsp(iter: &mut Peekable) -> bool { /// Skip all svg whitespaces and one comma, and return true if |iter| hasn't finished. #[inline] -fn skip_comma_wsp(iter: &mut Peekable) -> bool { +fn skip_comma_wsp(iter: &mut Peekable>>) -> bool { if !skip_wsp(iter) { return false; } - if *iter.peek().unwrap() != ',' { + if *iter.peek().unwrap() != b',' { return true; } iter.next(); From 1e6aa62c6f272cbce49c6016a5f4396980128097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 30 Aug 2018 12:00:00 +0000 Subject: [PATCH 23/34] style: Make the allocation of AuthorStyles for ShadowRoot lazy. So that we don't waste a bunch of memory with stuff like . I plan to shrink AuthorStyles further, but this should help regardless, and isn't very complex. Differential Revision: https://phabricator.services.mozilla.com/D4618 --- components/style/dom.rs | 38 ++++++++++++++++++------------- components/style/gecko/wrapper.rs | 11 ++++----- components/style/stylist.rs | 20 +++++++++++----- 3 files changed, 41 insertions(+), 28 deletions(-) diff --git a/components/style/dom.rs b/components/style/dom.rs index 42f6e9ba0c0..7552505a752 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -342,7 +342,7 @@ pub trait TShadowRoot: Sized + Copy + Clone + PartialEq { fn host(&self) -> ::ConcreteElement; /// Get the style data for this ShadowRoot. - fn style_data<'a>(&self) -> &'a CascadeData + fn style_data<'a>(&self) -> Option<&'a CascadeData> where Self: 'a; @@ -824,30 +824,36 @@ pub trait TElement: if let Some(shadow) = self.containing_shadow() { doc_rules_apply = false; - f( - shadow.style_data(), - self.as_node().owner_doc().quirks_mode(), - Some(shadow.host()), - ); + if let Some(data) = shadow.style_data() { + f( + data, + self.as_node().owner_doc().quirks_mode(), + Some(shadow.host()), + ); + } } if let Some(shadow) = self.shadow_root() { - f( - shadow.style_data(), - self.as_node().owner_doc().quirks_mode(), - Some(shadow.host()), - ); + if let Some(data) = shadow.style_data() { + f( + data, + self.as_node().owner_doc().quirks_mode(), + Some(shadow.host()), + ); + } } let mut current = self.assigned_slot(); while let Some(slot) = current { // Slots can only have assigned nodes when in a shadow tree. let shadow = slot.containing_shadow().unwrap(); - f( - shadow.style_data(), - self.as_node().owner_doc().quirks_mode(), - Some(shadow.host()), - ); + if let Some(data) = shadow.style_data() { + f( + data, + self.as_node().owner_doc().quirks_mode(), + Some(shadow.host()), + ); + } current = slot.assigned_slot(); } diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 1ba850696d1..00b75b83512 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -167,15 +167,14 @@ impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> { } #[inline] - fn style_data<'a>(&self) -> &'a CascadeData + fn style_data<'a>(&self) -> Option<&'a CascadeData> where Self: 'a, { - debug_assert!(!self.0.mServoStyles.mPtr.is_null()); - let author_styles = unsafe { - &*(self.0.mServoStyles.mPtr as *const structs::RawServoAuthorStyles - as *const bindings::RawServoAuthorStyles) + (self.0.mServoStyles.mPtr + as *const structs::RawServoAuthorStyles + as *const bindings::RawServoAuthorStyles).as_ref()? }; @@ -187,7 +186,7 @@ impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> { author_styles.stylesheets.dirty() ); - &author_styles.data + Some(&author_styles.data) } #[inline] diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 5dbb4dd5dc8..53e2a43b1d5 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -1206,7 +1206,7 @@ impl Stylist { // to add some sort of AuthorScoped cascade level or something. if matches_author_rules { if let Some(shadow) = rule_hash_target.shadow_root() { - if let Some(map) = shadow.style_data().host_rules(pseudo_element) { + if let Some(map) = shadow.style_data().and_then(|data| data.host_rules(pseudo_element)) { context.with_shadow_host(Some(rule_hash_target), |context| { map.get_all_matching_rules( element, @@ -1233,8 +1233,7 @@ impl Stylist { for slot in slots.iter().rev() { let shadow = slot.containing_shadow().unwrap(); - let styles = shadow.style_data(); - if let Some(map) = styles.slotted_rules(pseudo_element) { + if let Some(map) = shadow.style_data().and_then(|data| data.slotted_rules(pseudo_element)) { context.with_shadow_host(Some(shadow.host()), |context| { map.get_all_matching_rules( element, @@ -1253,7 +1252,7 @@ impl Stylist { if let Some(containing_shadow) = rule_hash_target.containing_shadow() { let cascade_data = containing_shadow.style_data(); let host = containing_shadow.host(); - if let Some(map) = cascade_data.normal_rules(pseudo_element) { + if let Some(map) = cascade_data.and_then(|data| data.normal_rules(pseudo_element)) { context.with_shadow_host(Some(host), |context| { map.get_all_matching_rules( element, @@ -1283,6 +1282,11 @@ impl Stylist { // // See: https://github.com/w3c/svgwg/issues/505 // + // FIXME(emilio, bug 1487259): We now do after bug 1483882, we + // should jump out of the shadow tree chain now. + // + // Unless the used node is cross-doc, I guess, in which case doc + // rules are probably ok... let host_is_svg_use = host.is_svg_element() && host.local_name() == &*local_name!("use"); @@ -1431,11 +1435,15 @@ impl Stylist { // [2]: https://github.com/w3c/csswg-drafts/issues/1995 // [3]: https://bugzil.la/1458189 if let Some(shadow) = element.shadow_root() { - try_find_in!(shadow.style_data()); + if let Some(data) = shadow.style_data() { + try_find_in!(data); + } } if let Some(shadow) = element.containing_shadow() { - try_find_in!(shadow.style_data()); + if let Some(data) = shadow.style_data() { + try_find_in!(data); + } } else { try_find_in!(self.cascade_data.author); } From 4ee3b56d549eec83a312dc048476b1bebabb9a39 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Fri, 31 Aug 2018 15:18:59 +1000 Subject: [PATCH 24/34] style: Use an Atom to represent Direction values in pseudo-classes. Differential Revision: https://phabricator.services.mozilla.com/D4730 --- components/style/gecko/selector_parser.rs | 12 ++-- components/style/gecko/wrapper.rs | 22 ++++--- .../invalidation/element/element_wrapper.rs | 3 +- .../invalidation/element/invalidation_map.rs | 18 +----- components/style/selector_parser.rs | 57 ++++++++++++------- 5 files changed, 54 insertions(+), 58 deletions(-) diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index cc84f58aa13..e9652b8386f 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -54,14 +54,14 @@ macro_rules! pseudo_class_name { $s_name(PseudoClassStringArg), )* /// The `:dir` pseudo-class. - Dir(Box), + Dir(Direction), /// The non-standard `:-moz-any` pseudo-class. /// /// TODO(emilio): We disallow combinators and pseudos here, so we /// should use SimpleSelector instead MozAny(ThinBoxedSlice>), /// The non-standard `:-moz-locale-dir` pseudo-class. - MozLocaleDir(Box), + MozLocaleDir(Direction), } } } @@ -411,14 +411,10 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { NonTSPseudoClass::$s_name(utf16.into_boxed_slice().into()) }, )* "-moz-locale-dir" => { - NonTSPseudoClass::MozLocaleDir( - Box::new(Direction::parse(parser)?) - ) + NonTSPseudoClass::MozLocaleDir(Direction::parse(parser)?) }, "dir" => { - NonTSPseudoClass::Dir( - Box::new(Direction::parse(parser)?) - ) + NonTSPseudoClass::Dir(Direction::parse(parser)?) }, "-moz-any" => { NonTSPseudoClass::MozAny( diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 00b75b83512..914bdad852b 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -69,7 +69,7 @@ use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock}; use properties::animated_properties::{AnimationValue, AnimationValueMap}; use properties::style_structs::Font; use rule_tree::CascadeLevel as ServoCascadeLevel; -use selector_parser::{AttrValue, Direction, PseudoClassStringArg}; +use selector_parser::{AttrValue, HorizontalDirection, PseudoClassStringArg}; use selectors::{Element, OpaqueElement}; use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator}; use selectors::attr::{CaseSensitivity, NamespaceConstraint}; @@ -2237,24 +2237,22 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { NonTSPseudoClass::MozLocaleDir(ref dir) => { let state_bit = DocumentState::NS_DOCUMENT_STATE_RTL_LOCALE; if context.extra_data.document_state.intersects(state_bit) { - // NOTE(emilio): We could still return false for - // Direction::Other(..), but we don't bother. + // NOTE(emilio): We could still return false for values + // other than "ltr" and "rtl", but we don't bother. return !context.in_negation(); } let doc_is_rtl = self.document_state().contains(state_bit); - match **dir { - Direction::Ltr => !doc_is_rtl, - Direction::Rtl => doc_is_rtl, - Direction::Other(..) => false, + match dir.as_horizontal_direction() { + Some(HorizontalDirection::Ltr) => !doc_is_rtl, + Some(HorizontalDirection::Rtl) => doc_is_rtl, + None => false, } }, - NonTSPseudoClass::Dir(ref dir) => match **dir { - Direction::Ltr => self.state().intersects(ElementState::IN_LTR_STATE), - Direction::Rtl => self.state().intersects(ElementState::IN_RTL_STATE), - Direction::Other(..) => false, - }, + NonTSPseudoClass::Dir(ref dir) => { + self.state().intersects(dir.element_state()) + } } } diff --git a/components/style/invalidation/element/element_wrapper.rs b/components/style/invalidation/element/element_wrapper.rs index cb4f79e92e1..e788d2dd876 100644 --- a/components/style/invalidation/element/element_wrapper.rs +++ b/components/style/invalidation/element/element_wrapper.rs @@ -188,8 +188,7 @@ where // support we don't forget to update this code? #[cfg(feature = "gecko")] NonTSPseudoClass::Dir(ref dir) => { - use invalidation::element::invalidation_map::dir_selector_to_state; - let selector_flag = dir_selector_to_state(dir); + let selector_flag = dir.element_state(); if selector_flag.is_empty() { // :dir() with some random argument; does not match. return false; diff --git a/components/style/invalidation/element/invalidation_map.rs b/components/style/invalidation/element/invalidation_map.rs index 67849d89627..26a4609760f 100644 --- a/components/style/invalidation/element/invalidation_map.rs +++ b/components/style/invalidation/element/invalidation_map.rs @@ -10,8 +10,6 @@ use element_state::{DocumentState, ElementState}; use fallible::FallibleVec; use hashglobe::FailedAllocationError; use selector_map::{MaybeCaseInsensitiveHashMap, SelectorMap, SelectorMapEntry}; -#[cfg(feature = "gecko")] -use selector_parser::Direction; use selector_parser::SelectorImpl; use selectors::attr::NamespaceConstraint; use selectors::parser::{Combinator, Component}; @@ -19,20 +17,6 @@ use selectors::parser::{Selector, SelectorIter, Visit}; use selectors::visitor::SelectorVisitor; use smallvec::SmallVec; -#[cfg(feature = "gecko")] -/// Gets the element state relevant to the given `:dir` pseudo-class selector. -pub fn dir_selector_to_state(dir: &Direction) -> ElementState { - match *dir { - Direction::Ltr => ElementState::IN_LTR_STATE, - Direction::Rtl => ElementState::IN_RTL_STATE, - Direction::Other(_) => { - // :dir(something-random) is a valid selector, but shouldn't - // match anything. - ElementState::empty() - }, - } -} - /// Mapping between (partial) CompoundSelectors (and the combinator to their /// right) and the states and attributes they depend on. /// @@ -382,7 +366,7 @@ impl<'a> SelectorVisitor for CompoundSelectorDependencyCollector<'a> { self.other_attributes |= pc.is_attr_based(); self.state |= match *pc { #[cfg(feature = "gecko")] - NonTSPseudoClass::Dir(ref dir) => dir_selector_to_state(dir), + NonTSPseudoClass::Dir(ref dir) => dir.element_state(), _ => pc.state_flag(), }; *self.document_state |= pc.document_state_flag(); diff --git a/components/style/selector_parser.rs b/components/style/selector_parser.rs index 7250502b1ad..14f09f50d34 100644 --- a/components/style/selector_parser.rs +++ b/components/style/selector_parser.rs @@ -7,10 +7,13 @@ #![deny(missing_docs)] use cssparser::{Parser as CssParser, ParserInput}; +use element_state::ElementState; use selectors::parser::SelectorList; use std::fmt::{self, Debug, Write}; +use string_cache::Atom; use style_traits::{CssWriter, ParseError, ToCss}; use stylesheets::{Namespaces, Origin, UrlExtraData}; +use values::serialize_atom_identifier; /// A convenient alias for the type that represents an attribute value used for /// selector parser implementation. @@ -172,27 +175,49 @@ impl PerPseudoElementMap { } /// Values for the :dir() pseudo class +/// +/// "ltr" and "rtl" values are normalized to lowercase. #[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)] -pub enum Direction { - /// left-to-right semantic directionality +pub struct Direction(pub Atom); + +/// Horizontal values for the :dir() pseudo class +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum HorizontalDirection { + /// :dir(ltr) Ltr, - /// right-to-left semantic directionality + /// :dir(rtl) Rtl, - /// Some other provided directionality value - /// - /// TODO(emilio): If we atomize we can then unbox in NonTSPseudoClass. - Other(Box), } impl Direction { /// Parse a direction value. pub fn parse<'i, 't>(parser: &mut CssParser<'i, 't>) -> Result> { let ident = parser.expect_ident()?; - Ok(match_ignore_ascii_case! { &ident, - "rtl" => Direction::Rtl, - "ltr" => Direction::Ltr, - _ => Direction::Other(Box::from(ident.as_ref())), - }) + Ok(Direction(match_ignore_ascii_case! { &ident, + "rtl" => atom!("rtl"), + "ltr" => atom!("ltr"), + _ => Atom::from(ident.as_ref()), + })) + } + + /// Convert this Direction into a HorizontalDirection, if applicable + pub fn as_horizontal_direction(&self) -> Option { + if self.0 == atom!("ltr") { + Some(HorizontalDirection::Ltr) + } else if self.0 == atom!("rtl") { + Some(HorizontalDirection::Rtl) + } else { + None + } + } + + /// Gets the element state relevant to this :dir() selector. + pub fn element_state(&self) -> ElementState { + match self.as_horizontal_direction() { + Some(HorizontalDirection::Ltr) => ElementState::IN_LTR_STATE, + Some(HorizontalDirection::Rtl) => ElementState::IN_RTL_STATE, + None => ElementState::empty(), + } } } @@ -201,12 +226,6 @@ impl ToCss for Direction { where W: Write, { - let dir_str = match *self { - Direction::Rtl => "rtl", - Direction::Ltr => "ltr", - // FIXME: This should be escaped as an identifier; see #19231 - Direction::Other(ref other) => other, - }; - dest.write_str(dir_str) + serialize_atom_identifier(&self.0, dest) } } From 2af9264cfe00701d7672e441ef63363a35da85b7 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Fri, 31 Aug 2018 17:05:56 +1000 Subject: [PATCH 25/34] style: Use an Atom to store pseudo-class string arguments. Differential Revision: https://phabricator.services.mozilla.com/D4740 --- components/style/gecko/selector_parser.rs | 20 +++----------------- components/style/gecko/wrapper.rs | 6 +----- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index e9652b8386f..dcdb06e466a 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -37,7 +37,7 @@ bitflags! { } /// The type used for storing pseudo-class string arguments. -pub type PseudoClassStringArg = ThinBoxedSlice; +pub type PseudoClassStringArg = Atom; macro_rules! pseudo_class_name { (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*], @@ -72,24 +72,13 @@ impl ToCss for NonTSPseudoClass { where W: fmt::Write, { - use cssparser::CssStringWriter; - use std::fmt::Write; macro_rules! pseudo_class_serialize { (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*], string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => { match *self { $(NonTSPseudoClass::$name => concat!(":", $css),)* $(NonTSPseudoClass::$s_name(ref s) => { - dest.write_str(concat!(":", $s_css, "("))?; - { - // FIXME(emilio): Avoid the extra allocation! - let mut css = CssStringWriter::new(dest); - - // Discount the null char in the end from the - // string. - css.write_str(&String::from_utf16(&s[..s.len() - 1]).unwrap())?; - } - return dest.write_str(")") + return write!(dest, concat!(":", $s_css, "({})"), s) }, )* NonTSPseudoClass::MozLocaleDir(ref dir) => { dest.write_str(":-moz-locale-dir(")?; @@ -405,10 +394,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { match_ignore_ascii_case! { &name, $($s_css => { let name = parser.expect_ident_or_string()?; - // convert to null terminated utf16 string - // since that's what Gecko deals with - let utf16: Vec = name.encode_utf16().chain(Some(0u16)).collect(); - NonTSPseudoClass::$s_name(utf16.into_boxed_slice().into()) + NonTSPseudoClass::$s_name(Atom::from(name.as_ref())) }, )* "-moz-locale-dir" => { NonTSPseudoClass::MozLocaleDir(Direction::parse(parser)?) diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 914bdad852b..031a9f40fdd 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -1721,10 +1721,6 @@ impl<'le> TElement for GeckoElement<'le> { // Gecko supports :lang() from CSS Selectors 3, which only accepts a // single language tag, and which performs simple dash-prefix matching // on it. - debug_assert!( - value.len() > 0 && value[value.len() - 1] == 0, - "expected value to be null terminated" - ); let override_lang_ptr = match &override_lang { &Some(Some(ref atom)) => atom.as_ptr(), _ => ptr::null_mut(), @@ -1734,7 +1730,7 @@ impl<'le> TElement for GeckoElement<'le> { self.0, override_lang_ptr, override_lang.is_some(), - value.as_ptr(), + value.as_slice().as_ptr(), ) } } From 40d2cbe8edb4894c29efb83d864cedc4ca401450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sun, 2 Sep 2018 23:03:47 +0000 Subject: [PATCH 26/34] style: Shrink CascadeData by turning the bloom filters into hash sets. We're wasting 1kb there, which is kind of stupid. The only advantage of using a bloom filter is that memory usage doesn't increase even if there's a gazillion attribute selectors and such. But: * For IDs we already have a bunch of data structures for invalidation and such which key on the id, so the bloom filter would be a very minor thing. * For attribute selectors we don't have such a data structure, but if people used a gazillion attribute selectors we should! Differential Revision: https://phabricator.services.mozilla.com/D4668 --- components/style/stylist.rs | 63 +++++++++++-------------------------- 1 file changed, 18 insertions(+), 45 deletions(-) diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 53e2a43b1d5..fbf2b18036e 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -24,11 +24,11 @@ use properties::{self, CascadeMode, ComputedValues}; use properties::{AnimationRules, PropertyDeclarationBlock}; use rule_cache::{RuleCache, RuleCacheConditions}; use rule_tree::{CascadeLevel, RuleTree, ShadowCascadeOrder, StrongRuleNode, StyleSource}; -use selector_map::{PrecomputedHashMap, SelectorMap, SelectorMapEntry}; +use selector_map::{PrecomputedHashMap, PrecomputedHashSet, SelectorMap, SelectorMapEntry}; use selector_parser::{PerPseudoElementMap, PseudoElement, SelectorImpl, SnapshotMap}; use selectors::NthIndexCache; use selectors::attr::{CaseSensitivity, NamespaceConstraint}; -use selectors::bloom::{BloomFilter, NonCountingBloomFilter}; +use selectors::bloom::BloomFilter; use selectors::matching::{matches_selector, ElementSelectorFlags, MatchingContext, MatchingMode}; use selectors::matching::VisitedHandlingMode; use selectors::parser::{AncestorHashes, Combinator, Component, Selector}; @@ -1396,8 +1396,7 @@ impl Stylist { CaseSensitivity::CaseSensitive => {}, } - let hash = id.get_hash(); - self.any_applicable_rule_data(element, |data| data.mapped_ids.might_contain_hash(hash)) + self.any_applicable_rule_data(element, |data| data.mapped_ids.contains(id)) } /// Returns the registered `@keyframes` animation for the specified name. @@ -1753,11 +1752,9 @@ struct StylistSelectorVisitor<'a> { passed_rightmost_selector: bool, /// The filter with all the id's getting referenced from rightmost /// selectors. - mapped_ids: &'a mut NonCountingBloomFilter, + mapped_ids: &'a mut PrecomputedHashSet, /// The filter with the local names of attributes there are selectors for. - attribute_dependencies: &'a mut NonCountingBloomFilter, - /// Whether there's any attribute selector for the [style] attribute. - style_attribute_dependency: &'a mut bool, + attribute_dependencies: &'a mut PrecomputedHashSet, /// All the states selectors in the page reference. state_dependencies: &'a mut ElementState, /// All the document states selectors in the page reference. @@ -1822,13 +1819,8 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> { name: &LocalName, lower_name: &LocalName, ) -> bool { - if *lower_name == local_name!("style") { - *self.style_attribute_dependency = true; - } else { - self.attribute_dependencies.insert_hash(name.get_hash()); - self.attribute_dependencies - .insert_hash(lower_name.get_hash()); - } + self.attribute_dependencies.insert(name.clone()); + self.attribute_dependencies.insert(lower_name.clone()); true } @@ -1854,7 +1846,7 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> { // // NOTE(emilio): See the comment regarding on when this may // break in visit_complex_selector. - self.mapped_ids.insert_hash(id.get_hash()); + self.mapped_ids.insert(id.clone()); }, _ => {}, } @@ -1954,18 +1946,9 @@ pub struct CascadeData { /// The attribute local names that appear in attribute selectors. Used /// to avoid taking element snapshots when an irrelevant attribute changes. - /// (We don't bother storing the namespace, since namespaced attributes - /// are rare.) - #[ignore_malloc_size_of = "just an array"] - attribute_dependencies: NonCountingBloomFilter, - - /// Whether `"style"` appears in an attribute selector. This is not common, - /// and by tracking this explicitly, we can avoid taking an element snapshot - /// in the common case of style=""` changing due to modifying - /// `element.style`. (We could track this in `attribute_dependencies`, like - /// all other attributes, but we should probably not risk incorrectly - /// returning `true` for `"style"` just due to a hash collision.) - style_attribute_dependency: bool, + /// (We don't bother storing the namespace, since namespaced attributes are + /// rare.) + attribute_dependencies: PrecomputedHashSet, /// The element state bits that are relied on by selectors. Like /// `attribute_dependencies`, this is used to avoid taking element snapshots @@ -1981,8 +1964,7 @@ pub struct CascadeData { /// hence in our selector maps). Used to determine when sharing styles is /// safe: we disallow style sharing for elements whose id matches this /// filter, and hence might be in one of our selector maps. - #[ignore_malloc_size_of = "just an array"] - mapped_ids: NonCountingBloomFilter, + mapped_ids: PrecomputedHashSet, /// Selectors that require explicit cache revalidation (i.e. which depend /// on state that is not otherwise visible to the cache, like attributes or @@ -2019,11 +2001,10 @@ impl CascadeData { host_rules: None, slotted_rules: None, invalidation_map: InvalidationMap::new(), - attribute_dependencies: NonCountingBloomFilter::new(), - style_attribute_dependency: false, + attribute_dependencies: PrecomputedHashSet::default(), state_dependencies: ElementState::empty(), document_state_dependencies: DocumentState::empty(), - mapped_ids: NonCountingBloomFilter::new(), + mapped_ids: PrecomputedHashSet::default(), selectors_for_cache_revalidation: SelectorMap::new(), animations: Default::default(), extra_data: ExtraStyleData::default(), @@ -2088,13 +2069,9 @@ impl CascadeData { /// selector of some rule. #[inline] pub fn might_have_attribute_dependency(&self, local_name: &LocalName) -> bool { - if *local_name == local_name!("style") { - return self.style_attribute_dependency; - } - - self.attribute_dependencies - .might_contain_hash(local_name.get_hash()) + self.attribute_dependencies.contains(local_name) } + #[inline] fn normal_rules(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap> { self.normal_rules.rules(pseudo) @@ -2221,7 +2198,6 @@ impl CascadeData { needs_revalidation: false, passed_rightmost_selector: false, attribute_dependencies: &mut self.attribute_dependencies, - style_attribute_dependency: &mut self.style_attribute_dependency, state_dependencies: &mut self.state_dependencies, document_state_dependencies: &mut self.document_state_dependencies, mapped_ids: &mut self.mapped_ids, @@ -2431,7 +2407,6 @@ impl CascadeData { self.clear_cascade_data(); self.invalidation_map.clear(); self.attribute_dependencies.clear(); - self.style_attribute_dependency = false; self.state_dependencies = ElementState::empty(); self.document_state_dependencies = DocumentState::empty(); self.mapped_ids.clear(); @@ -2527,16 +2502,14 @@ impl Rule { /// A function to be able to test the revalidation stuff. pub fn needs_revalidation_for_testing(s: &Selector) -> bool { - let mut attribute_dependencies = NonCountingBloomFilter::new(); - let mut mapped_ids = NonCountingBloomFilter::new(); - let mut style_attribute_dependency = false; + let mut attribute_dependencies = Default::default(); + let mut mapped_ids = Default::default(); let mut state_dependencies = ElementState::empty(); let mut document_state_dependencies = DocumentState::empty(); let mut visitor = StylistSelectorVisitor { needs_revalidation: false, passed_rightmost_selector: false, attribute_dependencies: &mut attribute_dependencies, - style_attribute_dependency: &mut style_attribute_dependency, state_dependencies: &mut state_dependencies, document_state_dependencies: &mut document_state_dependencies, mapped_ids: &mut mapped_ids, From d56e53788283738c6d40cd9ba68c26621b983bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sun, 2 Sep 2018 22:54:57 +0000 Subject: [PATCH 27/34] style: Use proper escaping for pseudo-class strings. We always serialize as an atom, which is the previous behavior (though previous code was using string escaping which I think was not totally sound either...). Differential Revision: https://phabricator.services.mozilla.com/D4753 --- components/style/gecko/selector_parser.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index dcdb06e466a..32a7b1f6ad0 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -21,6 +21,7 @@ use str::starts_with_ignore_ascii_case; use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_}; use thin_slice::ThinBoxedSlice; +use values::serialize_atom_identifier; pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT}; pub use gecko::snapshot::SnapshotMap; @@ -78,7 +79,9 @@ impl ToCss for NonTSPseudoClass { match *self { $(NonTSPseudoClass::$name => concat!(":", $css),)* $(NonTSPseudoClass::$s_name(ref s) => { - return write!(dest, concat!(":", $s_css, "({})"), s) + write!(dest, concat!(":", $s_css, "("))?; + serialize_atom_identifier(s, dest)?; + return dest.write_char(')'); }, )* NonTSPseudoClass::MozLocaleDir(ref dir) => { dest.write_str(":-moz-locale-dir(")?; @@ -99,7 +102,7 @@ impl ToCss for NonTSPseudoClass { dest.write_str(", ")?; selector.to_css(dest)?; } - return dest.write_str(")") + return dest.write_char(')') } } } From 72b29d3202f1808ec96291403fd28f2b72497d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sun, 2 Sep 2018 22:54:12 +0000 Subject: [PATCH 28/34] style: Simplify the Lang pseudo-class stuff a bit. Differential Revision: https://phabricator.services.mozilla.com/D4754 --- components/style/dom.rs | 4 +- .../style/gecko/non_ts_pseudo_class_list.rs | 16 +--- components/style/gecko/selector_parser.rs | 85 ++++++++----------- components/style/gecko/wrapper.rs | 4 +- components/style/servo/selector_parser.rs | 6 +- 5 files changed, 45 insertions(+), 70 deletions(-) diff --git a/components/style/dom.rs b/components/style/dom.rs index 7552505a752..2f65a266f01 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -19,7 +19,7 @@ use element_state::ElementState; use font_metrics::FontMetricsProvider; use media_queries::Device; use properties::{AnimationRules, ComputedValues, PropertyDeclarationBlock}; -use selector_parser::{AttrValue, PseudoClassStringArg, PseudoElement, SelectorImpl}; +use selector_parser::{AttrValue, Lang, PseudoElement, SelectorImpl}; use selectors::Element as SelectorsElement; use selectors::matching::{ElementSelectorFlags, QuirksMode, VisitedHandlingMode}; use selectors::sink::Push; @@ -895,7 +895,7 @@ pub trait TElement: fn match_element_lang( &self, override_lang: Option>, - value: &PseudoClassStringArg, + value: &Lang, ) -> bool; /// Returns whether this element is the main body element of the HTML diff --git a/components/style/gecko/non_ts_pseudo_class_list.rs b/components/style/gecko/non_ts_pseudo_class_list.rs index 04f57b73885..813250e4383 100644 --- a/components/style/gecko/non_ts_pseudo_class_list.rs +++ b/components/style/gecko/non_ts_pseudo_class_list.rs @@ -12,22 +12,13 @@ * Expected usage is as follows: * ``` * macro_rules! pseudo_class_macro{ - * (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*], - * string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => { - * keyword: [$(($k_css:expr, $k_name:ident, $k_gecko_type:tt, $k_state:tt, $k_flags:tt),)*]) => { + * ([$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*]) => { * // do stuff * } * } * apply_non_ts_list!(pseudo_class_macro) * ``` * - * The `string` and `keyword` variables will be applied to pseudoclasses that are of the form of - * functions with string or keyword arguments. - * - * Pending pseudo-classes: - * - * :scope ->