Auto merge of #23017 - emilio:gecko-sync, r=emilio

style: Sync changes from mozilla-central.

See each individual commit for details.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23017)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-03-13 10:26:37 -04:00 committed by GitHub
commit d5fb0666d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
74 changed files with 1153 additions and 1534 deletions

View file

@ -58,7 +58,7 @@ use style::properties::{style_structs, ComputedValues};
use style::servo::restyle_damage::ServoRestyleDamage; use style::servo::restyle_damage::ServoRestyleDamage;
use style::values::computed::effects::SimpleShadow; use style::values::computed::effects::SimpleShadow;
use style::values::computed::image::Image as ComputedImage; use style::values::computed::image::Image as ComputedImage;
use style::values::computed::Gradient; use style::values::computed::{Gradient, LengthOrAuto};
use style::values::generics::background::BackgroundSize; use style::values::generics::background::BackgroundSize;
use style::values::generics::image::{GradientKind, Image, PaintWorklet}; use style::values::generics::image::{GradientKind, Image, PaintWorklet};
use style::values::specified::ui::CursorKind; use style::values::specified::ui::CursorKind;
@ -2627,19 +2627,22 @@ impl BlockFlow {
_ => return, _ => return,
} }
fn extract_clip_component(p: &LengthOrAuto) -> Option<Au> {
match *p {
LengthOrAuto::Auto => None,
LengthOrAuto::LengthPercentage(ref length) => Some(Au::from(*length)),
}
}
let clip_origin = Point2D::new( let clip_origin = Point2D::new(
stacking_relative_border_box.origin.x + stacking_relative_border_box.origin.x +
style_clip_rect.left.map(Au::from).unwrap_or(Au(0)), extract_clip_component(&style_clip_rect.left).unwrap_or_default(),
stacking_relative_border_box.origin.y + stacking_relative_border_box.origin.y +
style_clip_rect.top.map(Au::from).unwrap_or(Au(0)), extract_clip_component(&style_clip_rect.top).unwrap_or_default(),
); );
let right = style_clip_rect let right = extract_clip_component(&style_clip_rect.right)
.right
.map(Au::from)
.unwrap_or(stacking_relative_border_box.size.width); .unwrap_or(stacking_relative_border_box.size.width);
let bottom = style_clip_rect let bottom = extract_clip_component(&style_clip_rect.bottom)
.bottom
.map(Au::from)
.unwrap_or(stacking_relative_border_box.size.height); .unwrap_or(stacking_relative_border_box.size.height);
let clip_size = Size2D::new(right - clip_origin.x, bottom - clip_origin.y); let clip_size = Size2D::new(right - clip_origin.x, bottom - clip_origin.y);

View file

@ -64,6 +64,7 @@ use style::values::computed::counters::ContentItem;
use style::values::computed::{LengthPercentage, LengthPercentageOrAuto, Size}; use style::values::computed::{LengthPercentage, LengthPercentageOrAuto, Size};
use style::values::generics::box_::{Perspective, VerticalAlign}; use style::values::generics::box_::{Perspective, VerticalAlign};
use style::values::generics::transform; use style::values::generics::transform;
use style::Zero;
use webrender_api::{self, LayoutTransform}; use webrender_api::{self, LayoutTransform};
// From gfxFontConstants.h in Firefox. // From gfxFontConstants.h in Firefox.

View file

@ -19,7 +19,7 @@ use std::fmt;
use std::sync::Arc; use std::sync::Arc;
use style::logical_geometry::LogicalSize; use style::logical_geometry::LogicalSize;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::{MaxSize, Size}; use style::values::computed::length::{MaxSize, NonNegativeLengthOrAuto, Size};
use style::values::generics::column::ColumnCount; use style::values::generics::column::ColumnCount;
use style::values::Either; use style::values::Either;
@ -114,7 +114,9 @@ impl Flow for MulticolFlow {
let column_style = style.get_column(); let column_style = style.get_column();
let mut column_count; let mut column_count;
if let Either::First(column_width) = column_style.column_width { if let NonNegativeLengthOrAuto::LengthPercentage(column_width) =
column_style.column_width
{
let column_width = Au::from(column_width); let column_width = Au::from(column_width);
column_count = max( column_count = max(
1, 1,

View file

@ -14,7 +14,6 @@ use gfx::font::{FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions};
use gfx::text::glyph::ByteIndex; use gfx::text::glyph::ByteIndex;
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
use gfx::text::util::{self, CompressionMode}; use gfx::text::util::{self, CompressionMode};
use ordered_float::NotNan;
use range::Range; use range::Range;
use servo_atoms::Atom; use servo_atoms::Atom;
use std::borrow::ToOwned; use std::borrow::ToOwned;
@ -196,11 +195,7 @@ impl TextRunScanner {
}; };
text_transform = inherited_text_style.text_transform; text_transform = inherited_text_style.text_transform;
letter_spacing = inherited_text_style.letter_spacing; letter_spacing = inherited_text_style.letter_spacing;
word_spacing = inherited_text_style word_spacing = inherited_text_style.word_spacing.to_hash_key();
.word_spacing
.value()
.map(|lop| lop.to_hash_key())
.unwrap_or((Au(0), NotNan::new(0.0).unwrap()));
text_rendering = inherited_text_style.text_rendering; text_rendering = inherited_text_style.text_rendering;
word_break = inherited_text_style.word_break; word_break = inherited_text_style.word_break;
} }
@ -321,10 +316,8 @@ impl TextRunScanner {
// example, `finally` with a wide `letter-spacing` renders as `f i n a l l y` and not // example, `finally` with a wide `letter-spacing` renders as `f i n a l l y` and not
// `fi n a l l y`. // `fi n a l l y`.
let mut flags = ShapingFlags::empty(); let mut flags = ShapingFlags::empty();
if let Some(v) = letter_spacing.value() { if letter_spacing.0.px() != 0. {
if v.px() != 0. { flags.insert(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG);
flags.insert(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG);
}
} }
if text_rendering == TextRendering::Optimizespeed { if text_rendering == TextRendering::Optimizespeed {
flags.insert(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG); flags.insert(ShapingFlags::IGNORE_LIGATURES_SHAPING_FLAG);
@ -334,8 +327,12 @@ impl TextRunScanner {
flags.insert(ShapingFlags::KEEP_ALL_FLAG); flags.insert(ShapingFlags::KEEP_ALL_FLAG);
} }
let options = ShapingOptions { let options = ShapingOptions {
letter_spacing: letter_spacing.value().cloned().map(Au::from), letter_spacing: if letter_spacing.0.px() == 0. {
word_spacing: word_spacing, None
} else {
Some(Au::from(letter_spacing.0))
},
word_spacing,
script: Script::Common, script: Script::Common,
flags: flags, flags: flags,
}; };

View file

@ -13,6 +13,7 @@ autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated usi
class nsAtom; class nsAtom;
namespace mozilla { namespace mozilla {
class WritingMode; class WritingMode;
enum HalfCorner : uint8_t;
enum LogicalSide : uint8_t; enum LogicalSide : uint8_t;
namespace css { namespace css {
struct URLValue; struct URLValue;
@ -39,8 +40,13 @@ include = ["cssparser", "style_traits"]
derive_eq = true derive_eq = true
derive_neq = true derive_neq = true
[macro_expansion]
bitflags = true
[enum] [enum]
derive_helper_methods = true derive_helper_methods = true
derive_const_casts = true
cast_assert_name = "MOZ_ASSERT"
[export] [export]
prefix = "Style" prefix = "Style"
@ -77,8 +83,10 @@ include = [
"Resize", "Resize",
"Overflow", "Overflow",
"LengthPercentage", "LengthPercentage",
"LetterSpacing",
"NonNegativeLengthPercentage", "NonNegativeLengthPercentage",
"LengthPercentageOrAuto", "LengthPercentageOrAuto",
"LineHeight",
"NonNegativeLengthPercentageOrAuto", "NonNegativeLengthPercentageOrAuto",
"Rect", "Rect",
"IntersectionObserverRootMargin", "IntersectionObserverRootMargin",
@ -88,16 +96,20 @@ include = [
"Position", "Position",
"BackgroundSize", "BackgroundSize",
"BorderImageSlice", "BorderImageSlice",
"BorderSpacing",
"BorderRadius",
"NonNegativeLengthOrNumberRect", "NonNegativeLengthOrNumberRect",
"Perspective", "Perspective",
"ZIndex", "ZIndex",
"TransformOrigin", "TransformOrigin",
"WordBreak",
] ]
item_types = ["enums", "structs", "typedefs"] item_types = ["enums", "structs", "typedefs"]
[export.body] [export.body]
"CSSPixelLength" = """ "CSSPixelLength" = """
inline nscoord ToAppUnits() const; inline nscoord ToAppUnits() const;
inline bool IsZero() const;
""" """
"LengthPercentage" = """ "LengthPercentage" = """
@ -114,6 +126,7 @@ item_types = ["enums", "structs", "typedefs"]
inline bool ConvertsToPercentage() const; inline bool ConvertsToPercentage() const;
inline bool HasLengthAndPercentage() const; inline bool HasLengthAndPercentage() const;
inline float ToPercentage() const; inline float ToPercentage() const;
inline bool IsDefinitelyZero() const;
inline CSSCoord ResolveToCSSPixels(CSSCoord aPercentageBasisInCSSPixels) const; inline CSSCoord ResolveToCSSPixels(CSSCoord aPercentageBasisInCSSPixels) const;
template<typename T> inline CSSCoord ResolveToCSSPixelsWith(T aPercentageGetter) const; template<typename T> inline CSSCoord ResolveToCSSPixelsWith(T aPercentageGetter) const;
template<typename T, typename U> template<typename T, typename U>
@ -125,7 +138,6 @@ item_types = ["enums", "structs", "typedefs"]
""" """
"GenericLengthPercentageOrAuto" = """ "GenericLengthPercentageOrAuto" = """
inline const StyleLengthPercentage& AsLengthPercentage() const;
inline bool ConvertsToLength() const; inline bool ConvertsToLength() const;
inline nscoord ToLength() const; inline nscoord ToLength() const;
inline bool ConvertsToPercentage() const; inline bool ConvertsToPercentage() const;
@ -135,8 +147,6 @@ item_types = ["enums", "structs", "typedefs"]
""" """
"GenericSize" = """ "GenericSize" = """
inline const StyleLengthPercentage& AsLengthPercentage() const;
inline StyleExtremumLength AsExtremumLength() const;
inline bool ConvertsToLength() const; inline bool ConvertsToLength() const;
inline nscoord ToLength() const; inline nscoord ToLength() const;
inline bool ConvertsToPercentage() const; inline bool ConvertsToPercentage() const;
@ -148,12 +158,9 @@ item_types = ["enums", "structs", "typedefs"]
"GenericFlexBasis" = """ "GenericFlexBasis" = """
inline bool IsAuto() const; inline bool IsAuto() const;
inline const StyleSize& AsSize() const;
""" """
"GenericMaxSize" = """ "GenericMaxSize" = """
inline const StyleLengthPercentage& AsLengthPercentage() const;
inline StyleExtremumLength AsExtremumLength() const;
inline bool ConvertsToLength() const; inline bool ConvertsToLength() const;
inline nscoord ToLength() const; inline nscoord ToLength() const;
inline bool ConvertsToPercentage() const; inline bool ConvertsToPercentage() const;
@ -185,3 +192,7 @@ item_types = ["enums", "structs", "typedefs"]
inline const T& GetIEnd(mozilla::WritingMode) const; inline const T& GetIEnd(mozilla::WritingMode) const;
inline const T& GetBEnd(mozilla::WritingMode) const; inline const T& GetBEnd(mozilla::WritingMode) const;
""" """
"GenericBorderRadius" = """
inline const StyleLengthPercentage& Get(mozilla::HalfCorner) const;
"""

View file

@ -7,8 +7,9 @@
//! [custom]: https://drafts.csswg.org/css-variables/ //! [custom]: https://drafts.csswg.org/css-variables/
use crate::hash::map::Entry; use crate::hash::map::Entry;
use crate::properties::{CSSWideKeyword, CustomDeclarationValue}; use crate::properties::{CSSWideKeyword, CustomDeclaration, CustomDeclarationValue};
use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, PrecomputedHasher}; use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, PrecomputedHasher};
use crate::stylesheets::{Origin, PerOrigin};
use crate::Atom; use crate::Atom;
use cssparser::{Delimiter, Parser, ParserInput, SourcePosition, Token, TokenSerializationType}; use cssparser::{Delimiter, Parser, ParserInput, SourcePosition, Token, TokenSerializationType};
use indexmap::IndexMap; use indexmap::IndexMap;
@ -490,6 +491,7 @@ fn parse_env_function<'i, 't>(
/// properties. /// properties.
pub struct CustomPropertiesBuilder<'a> { pub struct CustomPropertiesBuilder<'a> {
seen: PrecomputedHashSet<&'a Name>, seen: PrecomputedHashSet<&'a Name>,
reverted: PerOrigin<PrecomputedHashSet<&'a Name>>,
may_have_cycles: bool, may_have_cycles: bool,
custom_properties: Option<CustomPropertiesMap>, custom_properties: Option<CustomPropertiesMap>,
inherited: Option<&'a Arc<CustomPropertiesMap>>, inherited: Option<&'a Arc<CustomPropertiesMap>>,
@ -504,6 +506,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
) -> Self { ) -> Self {
Self { Self {
seen: PrecomputedHashSet::default(), seen: PrecomputedHashSet::default(),
reverted: Default::default(),
may_have_cycles: false, may_have_cycles: false,
custom_properties: None, custom_properties: None,
inherited, inherited,
@ -512,13 +515,22 @@ impl<'a> CustomPropertiesBuilder<'a> {
} }
/// Cascade a given custom property declaration. /// Cascade a given custom property declaration.
pub fn cascade(&mut self, name: &'a Name, specified_value: &CustomDeclarationValue) { pub fn cascade(&mut self, declaration: &'a CustomDeclaration, origin: Origin) {
let CustomDeclaration {
ref name,
ref value,
} = *declaration;
if self.reverted.borrow_for_origin(&origin).contains(&name) {
return;
}
let was_already_present = !self.seen.insert(name); let was_already_present = !self.seen.insert(name);
if was_already_present { if was_already_present {
return; return;
} }
if !self.value_may_affect_style(name, specified_value) { if !self.value_may_affect_style(name, value) {
return; return;
} }
@ -530,7 +542,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
} }
let map = self.custom_properties.as_mut().unwrap(); let map = self.custom_properties.as_mut().unwrap();
match *specified_value { match *value {
CustomDeclarationValue::Value(ref unparsed_value) => { CustomDeclarationValue::Value(ref unparsed_value) => {
let has_references = !unparsed_value.references.is_empty(); let has_references = !unparsed_value.references.is_empty();
self.may_have_cycles |= has_references; self.may_have_cycles |= has_references;
@ -554,6 +566,12 @@ impl<'a> CustomPropertiesBuilder<'a> {
map.insert(name.clone(), value); map.insert(name.clone(), value);
}, },
CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword { CustomDeclarationValue::CSSWideKeyword(keyword) => match keyword {
CSSWideKeyword::Revert => {
self.seen.remove(name);
for origin in origin.following_including() {
self.reverted.borrow_mut_for_origin(&origin).insert(name);
}
},
CSSWideKeyword::Initial => { CSSWideKeyword::Initial => {
map.remove(name); map.remove(name);
}, },
@ -587,10 +605,10 @@ impl<'a> CustomPropertiesBuilder<'a> {
// not existing in the map. // not existing in the map.
return false; return false;
}, },
(Some(existing_value), &CustomDeclarationValue::Value(ref specified_value)) => { (Some(existing_value), &CustomDeclarationValue::Value(ref value)) => {
// Don't bother overwriting an existing inherited value with // Don't bother overwriting an existing inherited value with
// the same specified value. // the same specified value.
if existing_value == specified_value { if existing_value == value {
return false; return false;
} }
}, },

View file

@ -27,6 +27,7 @@ use crate::values::generics::box_::VerticalAlign;
use crate::values::generics::grid::{TrackListValue, TrackSize}; use crate::values::generics::grid::{TrackListValue, TrackSize};
use crate::values::generics::image::{CompatMode, GradientItem, Image as GenericImage}; use crate::values::generics::image::{CompatMode, GradientItem, Image as GenericImage};
use crate::values::generics::rect::Rect; use crate::values::generics::rect::Rect;
use crate::Zero;
use app_units::Au; use app_units::Au;
use std::f32::consts::PI; use std::f32::consts::PI;
use style_traits::values::specified::AllowedNumericType; use style_traits::values::specified::AllowedNumericType;
@ -575,17 +576,15 @@ pub mod basic_shape {
//! Conversions from and to CSS shape representations. //! Conversions from and to CSS shape representations.
use crate::gecko::values::GeckoStyleCoordConvertible; use crate::gecko::values::GeckoStyleCoordConvertible;
use crate::gecko_bindings::structs::{nsStyleCoord, nsStyleCorners}; use crate::gecko_bindings::structs::nsStyleCoord;
use crate::gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType}; use crate::gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType};
use crate::gecko_bindings::structs::{ use crate::gecko_bindings::structs::{
StyleGeometryBox, StyleShapeSource, StyleShapeSourceType, StyleGeometryBox, StyleShapeSource, StyleShapeSourceType,
}; };
use crate::gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
use crate::gecko_bindings::sugar::refptr::RefPtr; use crate::gecko_bindings::sugar::refptr::RefPtr;
use crate::values::computed::basic_shape::{ use crate::values::computed::basic_shape::{
BasicShape, ClippingShape, FloatAreaShape, ShapeRadius, BasicShape, ClippingShape, FloatAreaShape, ShapeRadius,
}; };
use crate::values::computed::border::{BorderCornerRadius, BorderRadius};
use crate::values::computed::length::LengthPercentage; use crate::values::computed::length::LengthPercentage;
use crate::values::computed::motion::OffsetPath; use crate::values::computed::motion::OffsetPath;
use crate::values::computed::url::ComputedUrl; use crate::values::computed::url::ComputedUrl;
@ -594,9 +593,7 @@ pub mod basic_shape {
}; };
use crate::values::generics::basic_shape::{Circle, Ellipse, Path, PolygonCoord}; use crate::values::generics::basic_shape::{Circle, Ellipse, Path, PolygonCoord};
use crate::values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource}; use crate::values::generics::basic_shape::{GeometryBox, ShapeBox, ShapeSource};
use crate::values::generics::border::BorderRadius as GenericBorderRadius;
use crate::values::generics::rect::Rect; use crate::values::generics::rect::Rect;
use crate::values::generics::NonNegative;
use crate::values::specified::SVGPathData; use crate::values::specified::SVGPathData;
use std::borrow::Borrow; use std::borrow::Borrow;
@ -706,8 +703,7 @@ pub mod basic_shape {
let r = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[1]); let r = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[1]);
let b = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[2]); let b = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[2]);
let l = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[3]); let l = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[3]);
let round: BorderRadius = (&other.mRadius).into(); let round = other.mRadius;
let round = if round.all_zero() { None } else { Some(round) };
let rect = Rect::new( let rect = Rect::new(
t.expect("inset() offset should be a length, percentage, or calc value"), t.expect("inset() offset should be a length, percentage, or calc value"),
r.expect("inset() offset should be a length, percentage, or calc value"), r.expect("inset() offset should be a length, percentage, or calc value"),
@ -752,65 +748,6 @@ pub mod basic_shape {
} }
} }
impl<'a> From<&'a nsStyleCorners> for BorderRadius {
fn from(other: &'a nsStyleCorners) -> Self {
let get_corner = |index| {
BorderCornerRadius::new(
NonNegative(
LengthPercentage::from_gecko_style_coord(&other.data_at(index)).expect(
"<border-radius> should be a length, percentage, or calc value",
),
),
NonNegative(
LengthPercentage::from_gecko_style_coord(&other.data_at(index + 1)).expect(
"<border-radius> should be a length, percentage, or calc value",
),
),
)
};
GenericBorderRadius {
top_left: get_corner(0),
top_right: get_corner(2),
bottom_right: get_corner(4),
bottom_left: get_corner(6),
}
}
}
// Can't be a From impl since we need to set an existing
// nsStyleCorners, not create a new one
impl BorderRadius {
/// Set this `BorderRadius` into a given `nsStyleCoord`.
pub fn set_corners(&self, other: &mut nsStyleCorners) {
let mut set_corner = |field: &BorderCornerRadius, index| {
field
.0
.width()
.to_gecko_style_coord(&mut other.data_at_mut(index));
field
.0
.height()
.to_gecko_style_coord(&mut other.data_at_mut(index + 1));
};
set_corner(&self.top_left, 0);
set_corner(&self.top_right, 2);
set_corner(&self.bottom_right, 4);
set_corner(&self.bottom_left, 6);
}
}
/// We use None for a nonexistant radius, but Gecko uses (0 0 0 0 / 0 0 0 0)
pub fn set_corners_from_radius(radius: Option<BorderRadius>, other: &mut nsStyleCorners) {
if let Some(radius) = radius {
radius.set_corners(other);
} else {
for i in 0..8 {
other.data_at_mut(i).set_value(CoordDataValue::Coord(0));
}
}
}
impl<'a> From<&'a nsStyleCoord> for ShapeRadius { impl<'a> From<&'a nsStyleCoord> for ShapeRadius {
fn from(other: &'a nsStyleCoord) -> Self { fn from(other: &'a nsStyleCoord) -> Self {
let other = other.borrow(); let other = other.borrow();

View file

@ -7,7 +7,7 @@
use crate::context::QuirksMode; use crate::context::QuirksMode;
use crate::dom::TElement; use crate::dom::TElement;
use crate::gecko_bindings::bindings::{self, RawServoStyleSet}; use crate::gecko_bindings::bindings::{self, RawServoStyleSet};
use crate::gecko_bindings::structs::{RawGeckoPresContextBorrowed, ServoStyleSetSizes}; use crate::gecko_bindings::structs::{self, ServoStyleSetSizes};
use crate::gecko_bindings::structs::{StyleSheet as DomStyleSheet, StyleSheetInfo}; use crate::gecko_bindings::structs::{StyleSheet as DomStyleSheet, StyleSheetInfo};
use crate::gecko_bindings::sugar::ownership::{HasArcFFI, HasBoxFFI, HasFFI, HasSimpleFFI}; use crate::gecko_bindings::sugar::ownership::{HasArcFFI, HasBoxFFI, HasFFI, HasSimpleFFI};
use crate::invalidation::media_queries::{MediaListKey, ToMediaListKey}; use crate::invalidation::media_queries::{MediaListKey, ToMediaListKey};
@ -142,15 +142,15 @@ pub struct PerDocumentStyleDataImpl {
pub struct PerDocumentStyleData(AtomicRefCell<PerDocumentStyleDataImpl>); pub struct PerDocumentStyleData(AtomicRefCell<PerDocumentStyleDataImpl>);
impl PerDocumentStyleData { impl PerDocumentStyleData {
/// Create a dummy `PerDocumentStyleData`. /// Create a `PerDocumentStyleData`.
pub fn new(pres_context: RawGeckoPresContextBorrowed) -> Self { pub fn new(document: *const structs::Document) -> Self {
let device = Device::new(pres_context); let device = Device::new(document);
// FIXME(emilio, tlin): How is this supposed to work with XBL? This is // FIXME(emilio, tlin): How is this supposed to work with XBL? This is
// right now not always honored, see bug 1405543... // right now not always honored, see bug 1405543...
// //
// Should we just force XBL Stylists to be NoQuirks? // Should we just force XBL Stylists to be NoQuirks?
let quirks_mode = unsafe { (*device.pres_context().mDocument.mRawPtr).mCompatMode }; let quirks_mode = device.document().mCompatMode;
PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl { PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl {
stylist: Stylist::new(device, quirks_mode.into()), stylist: Stylist::new(device, quirks_mode.into()),
@ -191,8 +191,7 @@ impl PerDocumentStyleDataImpl {
/// Returns whether visited styles are enabled. /// Returns whether visited styles are enabled.
#[inline] #[inline]
pub fn visited_styles_enabled(&self) -> bool { pub fn visited_styles_enabled(&self) -> bool {
let doc = self.stylist.device().pres_context().mDocument.mRawPtr; unsafe { bindings::Gecko_VisitedStylesEnabled(self.stylist.device().document()) }
unsafe { bindings::Gecko_VisitedStylesEnabled(doc) }
} }
/// Measure heap usage. /// Measure heap usage.

View file

@ -17,12 +17,13 @@ use app_units::Au;
use euclid::Size2D; use euclid::Size2D;
fn viewport_size(device: &Device) -> Size2D<Au> { fn viewport_size(device: &Device) -> Size2D<Au> {
let pc = device.pres_context(); if let Some(pc) = device.pres_context() {
if pc.mIsRootPaginatedDocument() != 0 { if pc.mIsRootPaginatedDocument() != 0 {
// We want the page size, including unprintable areas and margins. // We want the page size, including unprintable areas and margins.
// FIXME(emilio, bug 1414600): Not quite! // FIXME(emilio, bug 1414600): Not quite!
let area = &pc.mPageSize; let area = &pc.mPageSize;
return Size2D::new(Au(area.width), Au(area.height)); return Size2D::new(Au(area.width), Au(area.height));
}
} }
device.au_viewport_size() device.au_viewport_size()
} }

View file

@ -8,7 +8,6 @@ use crate::custom_properties::CssEnvironment;
use crate::gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor}; use crate::gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
use crate::gecko_bindings::bindings; use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs; use crate::gecko_bindings::structs;
use crate::gecko_bindings::structs::{nsPresContext, RawGeckoPresContextBorrowed};
use crate::media_queries::MediaType; use crate::media_queries::MediaType;
use crate::properties::ComputedValues; use crate::properties::ComputedValues;
use crate::string_cache::Atom; use crate::string_cache::Atom;
@ -28,10 +27,9 @@ use style_traits::{CSSPixel, DevicePixel};
/// The `Device` in Gecko wraps a pres context, has a default values computed, /// The `Device` in Gecko wraps a pres context, has a default values computed,
/// and contains all the viewport rule state. /// and contains all the viewport rule state.
pub struct Device { pub struct Device {
/// NB: The pres context lifetime is tied to the styleset, who owns the /// NB: The document owns the styleset, who owns the stylist, and thus the
/// stylist, and thus the `Device`, so having a raw pres context pointer /// `Device`, so having a raw document pointer here is fine.
/// here is fine. document: *const structs::Document,
pres_context: RawGeckoPresContextBorrowed,
default_values: Arc<ComputedValues>, default_values: Arc<ComputedValues>,
/// The font size of the root element /// The font size of the root element
/// This is set when computing the style of the root /// This is set when computing the style of the root
@ -81,16 +79,16 @@ unsafe impl Send for Device {}
impl Device { impl Device {
/// Trivially constructs a new `Device`. /// Trivially constructs a new `Device`.
pub fn new(pres_context: RawGeckoPresContextBorrowed) -> Self { pub fn new(document: *const structs::Document) -> Self {
assert!(!pres_context.is_null()); assert!(!document.is_null());
let doc = unsafe { &*document };
let prefs = unsafe { &*bindings::Gecko_GetPrefSheetPrefs(doc) };
Device { Device {
pres_context, document,
default_values: ComputedValues::default_values(unsafe { default_values: ComputedValues::default_values(doc),
&*(*pres_context).mDocument.mRawPtr
}),
// FIXME(bz): Seems dubious? // FIXME(bz): Seems dubious?
root_font_size: AtomicIsize::new(FontSize::medium().size().0 as isize), root_font_size: AtomicIsize::new(FontSize::medium().size().0 as isize),
body_text_color: AtomicUsize::new(unsafe { &*pres_context }.mDefaultColor as usize), body_text_color: AtomicUsize::new(prefs.mDefaultColor as usize),
used_root_font_size: AtomicBool::new(false), used_root_font_size: AtomicBool::new(false),
used_viewport_size: AtomicBool::new(false), used_viewport_size: AtomicBool::new(false),
environment: CssEnvironment, environment: CssEnvironment,
@ -112,11 +110,13 @@ impl Device {
/// Whether any animation name may be referenced from the style of any /// Whether any animation name may be referenced from the style of any
/// element. /// element.
pub fn animation_name_may_be_referenced(&self, name: &KeyframesName) -> bool { pub fn animation_name_may_be_referenced(&self, name: &KeyframesName) -> bool {
let pc = match self.pres_context() {
Some(pc) => pc,
None => return false,
};
unsafe { unsafe {
bindings::Gecko_AnimationNameMayBeReferencedFromStyle( bindings::Gecko_AnimationNameMayBeReferencedFromStyle(pc, name.as_atom().as_ptr())
self.pres_context(),
name.as_atom().as_ptr(),
)
} }
} }
@ -156,16 +156,29 @@ impl Device {
convert_nscolor_to_rgba(self.body_text_color.load(Ordering::Relaxed) as u32) convert_nscolor_to_rgba(self.body_text_color.load(Ordering::Relaxed) as u32)
} }
/// Gets the pres context associated with this document.
#[inline]
pub fn pres_context(&self) -> &nsPresContext {
unsafe { &*self.pres_context }
}
/// Gets the document pointer. /// Gets the document pointer.
#[inline] #[inline]
pub fn document(&self) -> &structs::Document { pub fn document(&self) -> &structs::Document {
unsafe { &*self.pres_context().mDocument.mRawPtr } unsafe { &*self.document }
}
/// Gets the pres context associated with this document.
#[inline]
pub fn pres_context(&self) -> Option<&structs::nsPresContext> {
unsafe {
self.document()
.mPresShell
.as_ref()?
.mPresContext
.mRawPtr
.as_ref()
}
}
/// Gets the preference stylesheet prefs for our document.
#[inline]
pub fn pref_sheet_prefs(&self) -> &structs::PreferenceSheet_Prefs {
unsafe { &*bindings::Gecko_GetPrefSheetPrefs(self.document()) }
} }
/// Recreates the default computed values. /// Recreates the default computed values.
@ -195,13 +208,17 @@ impl Device {
/// Returns the current media type of the device. /// Returns the current media type of the device.
pub fn media_type(&self) -> MediaType { pub fn media_type(&self) -> MediaType {
let pc = match self.pres_context() {
Some(pc) => pc,
None => return MediaType::screen(),
};
// Gecko allows emulating random media with mIsEmulatingMedia and // Gecko allows emulating random media with mIsEmulatingMedia and
// mMediaEmulated. // mMediaEmulated.
let context = self.pres_context(); let medium_to_use = if pc.mIsEmulatingMedia() != 0 {
let medium_to_use = if context.mIsEmulatingMedia() != 0 { pc.mMediaEmulated.mRawPtr
context.mMediaEmulated.mRawPtr
} else { } else {
context.mMedium pc.mMedium
}; };
MediaType(CustomIdent(unsafe { Atom::from_raw(medium_to_use) })) MediaType(CustomIdent(unsafe { Atom::from_raw(medium_to_use) }))
@ -209,7 +226,11 @@ impl Device {
/// Returns the current viewport size in app units. /// Returns the current viewport size in app units.
pub fn au_viewport_size(&self) -> Size2D<Au> { pub fn au_viewport_size(&self) -> Size2D<Au> {
let area = &self.pres_context().mVisibleArea; let pc = match self.pres_context() {
Some(pc) => pc,
None => return Size2D::new(Au(0), Au(0)),
};
let area = &pc.mVisibleArea;
Size2D::new(Au(area.width), Au(area.height)) Size2D::new(Au(area.width), Au(area.height))
} }
@ -227,34 +248,62 @@ impl Device {
/// Returns the device pixel ratio. /// Returns the device pixel ratio.
pub fn device_pixel_ratio(&self) -> TypedScale<f32, CSSPixel, DevicePixel> { pub fn device_pixel_ratio(&self) -> TypedScale<f32, CSSPixel, DevicePixel> {
let override_dppx = self.pres_context().mOverrideDPPX; let pc = match self.pres_context() {
Some(pc) => pc,
None => return TypedScale::new(1.),
};
let override_dppx = pc.mOverrideDPPX;
if override_dppx > 0.0 { if override_dppx > 0.0 {
return TypedScale::new(override_dppx); return TypedScale::new(override_dppx);
} }
let au_per_dpx = self.pres_context().mCurAppUnitsPerDevPixel as f32;
let au_per_dpx = pc.mCurAppUnitsPerDevPixel as f32;
let au_per_px = AU_PER_PX as f32; let au_per_px = AU_PER_PX as f32;
TypedScale::new(au_per_px / au_per_dpx) TypedScale::new(au_per_px / au_per_dpx)
} }
/// Returns whether document colors are enabled. /// Returns whether document colors are enabled.
#[inline]
pub fn use_document_colors(&self) -> bool { pub fn use_document_colors(&self) -> bool {
self.pres_context().mUseDocumentColors() != 0 let doc = self.document();
if doc.mIsBeingUsedAsImage() {
return true;
}
let document_color_use =
unsafe { structs::StaticPrefs_sVarCache_browser_display_document_color_use };
let prefs = self.pref_sheet_prefs();
match document_color_use {
1 => true,
2 => prefs.mIsChrome,
_ => !prefs.mUseAccessibilityTheme,
}
} }
/// Returns the default background color. /// Returns the default background color.
pub fn default_background_color(&self) -> RGBA { pub fn default_background_color(&self) -> RGBA {
convert_nscolor_to_rgba(self.pres_context().mBackgroundColor) convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultBackgroundColor)
}
/// Returns the current effective text zoom.
#[inline]
fn effective_text_zoom(&self) -> f32 {
let pc = match self.pres_context() {
Some(pc) => pc,
None => return 1.,
};
pc.mEffectiveTextZoom
} }
/// Applies text zoom to a font-size or line-height value (see nsStyleFont::ZoomText). /// Applies text zoom to a font-size or line-height value (see nsStyleFont::ZoomText).
#[inline] #[inline]
pub fn zoom_text(&self, size: Au) -> Au { pub fn zoom_text(&self, size: Au) -> Au {
size.scale_by(self.pres_context().mEffectiveTextZoom) size.scale_by(self.effective_text_zoom())
} }
/// Un-apply text zoom. /// Un-apply text zoom.
#[inline] #[inline]
pub fn unzoom_text(&self, size: Au) -> Au { pub fn unzoom_text(&self, size: Au) -> Au {
size.scale_by(1. / self.pres_context().mEffectiveTextZoom) size.scale_by(1. / self.effective_text_zoom())
} }
} }

View file

@ -19,7 +19,7 @@ use crate::values::generics::grid::{TrackBreadth, TrackKeyword};
use crate::values::generics::length::LengthPercentageOrAuto; use crate::values::generics::length::LengthPercentageOrAuto;
use crate::values::generics::{CounterStyleOrNone, NonNegative}; use crate::values::generics::{CounterStyleOrNone, NonNegative};
use crate::values::{Auto, Either, None_, Normal}; use crate::values::{Auto, Either, None_, Normal};
use crate::Atom; use crate::{Atom, Zero};
use app_units::Au; use app_units::Au;
use cssparser::RGBA; use cssparser::RGBA;
use nsstring::{nsACString, nsCStr}; use nsstring::{nsACString, nsCStr};

View file

@ -1044,9 +1044,13 @@ impl FontMetricsProvider for GeckoFontMetricsProvider {
device: &Device, device: &Device,
) -> FontMetricsQueryResult { ) -> FontMetricsQueryResult {
use crate::gecko_bindings::bindings::Gecko_GetFontMetrics; use crate::gecko_bindings::bindings::Gecko_GetFontMetrics;
let pc = match device.pres_context() {
Some(pc) => pc,
None => return FontMetricsQueryResult::NotAvailable,
};
let gecko_metrics = unsafe { let gecko_metrics = unsafe {
Gecko_GetFontMetrics( Gecko_GetFontMetrics(
device.pres_context(), pc,
wm.is_vertical() && !wm.is_sideways(), wm.is_vertical() && !wm.is_sideways(),
font.gecko(), font.gecko(),
font_size.0, font_size.0,
@ -1242,8 +1246,7 @@ impl<'le> TElement for GeckoElement<'le> {
} }
fn owner_doc_matches_for_testing(&self, device: &Device) -> bool { fn owner_doc_matches_for_testing(&self, device: &Device) -> bool {
self.as_node().owner_doc().0 as *const structs::Document == self.as_node().owner_doc().0 as *const structs::Document == device.document() as *const _
device.pres_context().mDocument.mRawPtr
} }
fn style_attribute(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>> { fn style_attribute(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>> {

View file

@ -10,6 +10,7 @@ use crate::gecko_bindings::structs::{nsCSSUnit, nsCSSValue};
use crate::gecko_bindings::structs::{nsCSSValueList, nsCSSValue_Array}; use crate::gecko_bindings::structs::{nsCSSValueList, nsCSSValue_Array};
use crate::gecko_string_cache::Atom; use crate::gecko_string_cache::Atom;
use crate::values::computed::{Angle, Length, LengthPercentage, Percentage}; use crate::values::computed::{Angle, Length, LengthPercentage, Percentage};
use crate::Zero;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem; use std::mem;
use std::ops::{Index, IndexMut}; use std::ops::{Index, IndexMut};

View file

@ -5,8 +5,8 @@
//! Rust helpers for Gecko's `nsStyleCoord`. //! Rust helpers for Gecko's `nsStyleCoord`.
use crate::gecko_bindings::bindings; use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs::nsStyleSides;
use crate::gecko_bindings::structs::{nsStyleCoord, nsStyleCoord_Calc, nsStyleCoord_CalcValue}; use crate::gecko_bindings::structs::{nsStyleCoord, nsStyleCoord_Calc, nsStyleCoord_CalcValue};
use crate::gecko_bindings::structs::{nsStyleCorners, nsStyleSides};
use crate::gecko_bindings::structs::{nsStyleUnion, nsStyleUnit, nscoord}; use crate::gecko_bindings::structs::{nsStyleUnion, nsStyleUnit, nscoord};
use std::mem; use std::mem;
@ -123,64 +123,6 @@ unsafe impl<'a> CoordDataMut for SidesDataMut<'a> {
} }
} }
impl nsStyleCorners {
/// Get a `nsStyleCoord` like object representing the given index's value
/// and unit.
#[inline]
pub fn data_at(&self, index: usize) -> CornersData {
CornersData {
corners: self,
index: index,
}
}
/// Get a `nsStyleCoord` like object representing the mutable given index's
/// value and unit.
#[inline]
pub fn data_at_mut(&mut self, index: usize) -> CornersDataMut {
CornersDataMut {
corners: self,
index: index,
}
}
}
/// A `nsStyleCoord`-like struct on top of `nsStyleCorners`.
pub struct CornersData<'a> {
corners: &'a nsStyleCorners,
index: usize,
}
/// A `nsStyleCoord`-like struct on top of a mutable `nsStyleCorners` reference.
pub struct CornersDataMut<'a> {
corners: &'a mut nsStyleCorners,
index: usize,
}
unsafe impl<'a> CoordData for CornersData<'a> {
fn unit(&self) -> nsStyleUnit {
unsafe { self.corners.get_mUnits()[self.index] }
}
fn union(&self) -> nsStyleUnion {
unsafe { self.corners.get_mValues()[self.index] }
}
}
unsafe impl<'a> CoordData for CornersDataMut<'a> {
fn unit(&self) -> nsStyleUnit {
unsafe { self.corners.get_mUnits()[self.index] }
}
fn union(&self) -> nsStyleUnion {
unsafe { self.corners.get_mValues()[self.index] }
}
}
unsafe impl<'a> CoordDataMut for CornersDataMut<'a> {
unsafe fn values_mut(&mut self) -> (&mut nsStyleUnit, &mut nsStyleUnion) {
let unit = &mut self.corners.get_mUnits_mut()[self.index] as *mut _;
let value = &mut self.corners.get_mValues_mut()[self.index] as *mut _;
(&mut *unit, &mut *value)
}
}
/// Enum representing the tagged union that is CoordData. /// Enum representing the tagged union that is CoordData.
/// ///
/// In release mode this should never actually exist in the code, and will be /// In release mode this should never actually exist in the code, and will be

View file

@ -244,3 +244,26 @@ impl CaseSensitivityExt for selectors::attr::CaseSensitivity {
} }
} }
} }
/// A trait pretty much similar to num_traits::Zero, but without the need of
/// implementing `Add`.
pub trait Zero {
/// Returns the zero value.
fn zero() -> Self;
/// Returns whether this value is zero.
fn is_zero(&self) -> bool;
}
impl<T> Zero for T
where
T: num_traits::Zero,
{
fn zero() -> Self {
<Self as num_traits::Zero>::zero()
}
fn is_zero(&self) -> bool {
<Self as num_traits::Zero>::is_zero(self)
}
}

View file

@ -21,9 +21,8 @@ use crate::stylesheets::Origin;
use crate::values::computed::{self, ToComputedValue}; use crate::values::computed::{self, ToComputedValue};
use crate::values::specified::{Integer, Length, Number, Resolution}; use crate::values::specified::{Integer, Length, Number, Resolution};
use crate::values::{serialize_atom_identifier, CSSFloat}; use crate::values::{serialize_atom_identifier, CSSFloat};
use crate::Atom; use crate::{Atom, Zero};
use cssparser::{Parser, Token}; use cssparser::{Parser, Token};
use num_traits::Zero;
use std::cmp::{Ordering, PartialOrd}; use std::cmp::{Ordering, PartialOrd};
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};

View file

@ -11,12 +11,13 @@ use crate::font_metrics::FontMetricsProvider;
use crate::logical_geometry::WritingMode; use crate::logical_geometry::WritingMode;
use crate::media_queries::Device; use crate::media_queries::Device;
use crate::properties::{ComputedValues, StyleBuilder}; use crate::properties::{ComputedValues, StyleBuilder};
use crate::properties::{LonghandId, LonghandIdSet}; use crate::properties::{LonghandId, LonghandIdSet, CSSWideKeyword};
use crate::properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator}; use crate::properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator};
use crate::properties::CASCADE_PROPERTY; use crate::properties::CASCADE_PROPERTY;
use crate::rule_cache::{RuleCache, RuleCacheConditions}; use crate::rule_cache::{RuleCache, RuleCacheConditions};
use crate::rule_tree::{CascadeLevel, StrongRuleNode}; use crate::rule_tree::{CascadeLevel, StrongRuleNode};
use crate::selector_parser::PseudoElement; use crate::selector_parser::PseudoElement;
use crate::stylesheets::{Origin, PerOrigin};
use servo_arc::Arc; use servo_arc::Arc;
use crate::shared_lock::StylesheetGuards; use crate::shared_lock::StylesheetGuards;
use smallbitvec::SmallBitVec; use smallbitvec::SmallBitVec;
@ -251,7 +252,7 @@ where
for (declaration, cascade_level) in iter_declarations() { for (declaration, cascade_level) in iter_declarations() {
declarations.push((declaration, cascade_level)); declarations.push((declaration, cascade_level));
if let PropertyDeclaration::Custom(ref declaration) = *declaration { if let PropertyDeclaration::Custom(ref declaration) = *declaration {
builder.cascade(&declaration.name, &declaration.value); builder.cascade(declaration, cascade_level.origin());
} }
} }
@ -339,14 +340,8 @@ fn should_ignore_declaration_when_ignoring_document_colors(
return false; return false;
} }
let is_ua_or_user_rule = matches!( let is_ua_or_user_rule =
cascade_level, matches!(cascade_level.origin(), Origin::User | Origin::UserAgent);
CascadeLevel::UANormal |
CascadeLevel::UserNormal |
CascadeLevel::UserImportant |
CascadeLevel::UAImportant
);
if is_ua_or_user_rule { if is_ua_or_user_rule {
return false; return false;
} }
@ -388,6 +383,7 @@ struct Cascade<'a, 'b: 'a> {
context: &'a mut computed::Context<'b>, context: &'a mut computed::Context<'b>,
cascade_mode: CascadeMode<'a>, cascade_mode: CascadeMode<'a>,
seen: LonghandIdSet, seen: LonghandIdSet,
reverted: PerOrigin<LonghandIdSet>,
saved_font_size: Option<PropertyDeclaration>, saved_font_size: Option<PropertyDeclaration>,
saved_font_family: Option<PropertyDeclaration>, saved_font_family: Option<PropertyDeclaration>,
} }
@ -398,6 +394,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
context, context,
cascade_mode, cascade_mode,
seen: LonghandIdSet::default(), seen: LonghandIdSet::default(),
reverted: Default::default(),
saved_font_size: None, saved_font_size: None,
saved_font_family: None, saved_font_family: None,
} }
@ -488,6 +485,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
for (declaration, cascade_level) in declarations { for (declaration, cascade_level) in declarations {
let declaration_id = declaration.id(); let declaration_id = declaration.id();
let origin = cascade_level.origin();
let longhand_id = match declaration_id { let longhand_id = match declaration_id {
PropertyDeclarationId::Longhand(id) => id, PropertyDeclarationId::Longhand(id) => id,
PropertyDeclarationId::Custom(..) => continue, PropertyDeclarationId::Custom(..) => continue,
@ -513,6 +511,10 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
continue; continue;
} }
if self.reverted.borrow_for_origin(&origin).contains(physical_longhand_id) {
continue;
}
// Only a few properties are allowed to depend on the visited state // Only a few properties are allowed to depend on the visited state
// of links. When cascading visited styles, we can save time by // of links. When cascading visited styles, we can save time by
// only processing these properties. // only processing these properties.
@ -540,8 +542,34 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
} }
} }
let css_wide_keyword = declaration.get_css_wide_keyword();
if let Some(CSSWideKeyword::Revert) = css_wide_keyword {
// We intentionally don't want to insert it into `self.seen`,
// `reverted` takes care of rejecting other declarations as
// needed.
for origin in origin.following_including() {
self.reverted
.borrow_mut_for_origin(&origin)
.insert(physical_longhand_id);
}
continue;
}
self.seen.insert(physical_longhand_id); self.seen.insert(physical_longhand_id);
let unset = css_wide_keyword.map_or(false, |css_wide_keyword| {
match css_wide_keyword {
CSSWideKeyword::Unset => true,
CSSWideKeyword::Inherit => inherited,
CSSWideKeyword::Initial => !inherited,
CSSWideKeyword::Revert => unreachable!(),
}
});
if unset {
continue;
}
// FIXME(emilio): We should avoid generating code for logical // FIXME(emilio): We should avoid generating code for logical
// longhands and just use the physical ones, then rename // longhands and just use the physical ones, then rename
// physical_longhand_id to just longhand_id. // physical_longhand_id to just longhand_id.
@ -800,18 +828,14 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
self.seen.contains(LonghandId::MozMinFontSizeRatio) || self.seen.contains(LonghandId::MozMinFontSizeRatio) ||
self.seen.contains(LonghandId::FontFamily) self.seen.contains(LonghandId::FontFamily)
{ {
use crate::properties::{CSSWideKeyword, WideKeywordDeclaration}; use crate::values::computed::FontSize;
// font-size must be explicitly inherited to handle lang // font-size must be explicitly inherited to handle lang
// changes and scriptlevel changes. // changes and scriptlevel changes.
// //
// FIXME(emilio): That looks a bit bogus... // FIXME(emilio): That looks a bit bogus...
let inherit = PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration { self.context.for_non_inherited_property = None;
id: LonghandId::FontSize, FontSize::cascade_inherit_font_size(&mut self.context);
keyword: CSSWideKeyword::Inherit,
});
self.apply_declaration_ignoring_phase(LonghandId::FontSize, &inherit);
} }
} }
} }

View file

@ -347,6 +347,7 @@ class Longhand(object):
"TouchAction", "TouchAction",
"TransformStyle", "TransformStyle",
"UserSelect", "UserSelect",
"WordBreak",
"XSpan", "XSpan",
"XTextZoom", "XTextZoom",
"ZIndex", "ZIndex",

View file

@ -849,7 +849,7 @@ impl PropertyDeclarationBlock {
for declaration in self.normal_declaration_iter() { for declaration in self.normal_declaration_iter() {
if let PropertyDeclaration::Custom(ref declaration) = *declaration { if let PropertyDeclaration::Custom(ref declaration) = *declaration {
builder.cascade(&declaration.name, &declaration.value); builder.cascade(declaration, Origin::Author);
} }
} }

View file

@ -52,7 +52,7 @@ use crate::rule_tree::StrongRuleNode;
use crate::selector_parser::PseudoElement; use crate::selector_parser::PseudoElement;
use servo_arc::{Arc, RawOffsetArc}; use servo_arc::{Arc, RawOffsetArc};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem::{forget, uninitialized, transmute, zeroed}; use std::mem::{forget, uninitialized, zeroed};
use std::{cmp, ops, ptr}; use std::{cmp, ops, ptr};
use crate::values::{self, CustomIdent, Either, KeyframesName, None_}; use crate::values::{self, CustomIdent, Either, KeyframesName, None_};
use crate::values::computed::{NonNegativeLength, Percentage, TransitionProperty}; use crate::values::computed::{NonNegativeLength, Percentage, TransitionProperty};
@ -463,32 +463,27 @@ def set_gecko_property(ffi_name, expr):
// set on mContextFlags, and the length field is set to the initial value. // set on mContextFlags, and the length field is set to the initial value.
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
use crate::values::generics::svg::{SVGLength, SvgLengthPercentageOrNumber}; use crate::values::generics::svg::SVGLength;
use crate::gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE; use crate::gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE;
let length = match v { let length = match v {
SVGLength::Length(length) => { SVGLength::LengthPercentage(length) => {
self.gecko.mContextFlags &= !CONTEXT_VALUE; self.gecko.mContextFlags &= !CONTEXT_VALUE;
length length
} }
SVGLength::ContextValue => { SVGLength::ContextValue => {
self.gecko.mContextFlags |= CONTEXT_VALUE; self.gecko.mContextFlags |= CONTEXT_VALUE;
match longhands::${ident}::get_initial_value() { match longhands::${ident}::get_initial_value() {
SVGLength::Length(length) => length, SVGLength::LengthPercentage(length) => length,
_ => unreachable!("Initial value should not be context-value"), _ => unreachable!("Initial value should not be context-value"),
} }
} }
}; };
match length { self.gecko.${gecko_ffi_name} = length;
SvgLengthPercentageOrNumber::LengthPercentage(lp) =>
self.gecko.${gecko_ffi_name}.set(lp),
SvgLengthPercentageOrNumber::Number(num) =>
self.gecko.${gecko_ffi_name}.set_value(CoordDataValue::Factor(num.into())),
}
} }
pub fn copy_${ident}_from(&mut self, other: &Self) { pub fn copy_${ident}_from(&mut self, other: &Self) {
use crate::gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE; use crate::gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE;
self.gecko.${gecko_ffi_name}.copy_from(&other.gecko.${gecko_ffi_name}); self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name};
self.gecko.mContextFlags = self.gecko.mContextFlags =
(self.gecko.mContextFlags & !CONTEXT_VALUE) | (self.gecko.mContextFlags & !CONTEXT_VALUE) |
(other.gecko.mContextFlags & CONTEXT_VALUE); (other.gecko.mContextFlags & CONTEXT_VALUE);
@ -499,32 +494,12 @@ def set_gecko_property(ffi_name, expr):
} }
pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
use crate::values::generics::svg::{SVGLength, SvgLengthPercentageOrNumber}; use crate::values::generics::svg::SVGLength;
use crate::values::computed::LengthPercentage;
use crate::gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE; use crate::gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE;
if (self.gecko.mContextFlags & CONTEXT_VALUE) != 0 { if (self.gecko.mContextFlags & CONTEXT_VALUE) != 0 {
return SVGLength::ContextValue; return SVGLength::ContextValue;
} }
let length = match self.gecko.${gecko_ffi_name}.as_value() { SVGLength::LengthPercentage(self.gecko.${gecko_ffi_name})
CoordDataValue::Factor(number) => {
SvgLengthPercentageOrNumber::Number(number)
},
CoordDataValue::Coord(coord) => {
SvgLengthPercentageOrNumber::LengthPercentage(
LengthPercentage::new(Au(coord).into(), None)
)
},
CoordDataValue::Percent(p) => {
SvgLengthPercentageOrNumber::LengthPercentage(
LengthPercentage::new(Au(0).into(), Some(Percentage(p)))
)
},
CoordDataValue::Calc(calc) => {
SvgLengthPercentageOrNumber::LengthPercentage(calc.into())
},
_ => unreachable!("Unexpected coordinate in ${ident}"),
};
SVGLength::Length(length.into())
} }
</%def> </%def>
@ -817,34 +792,23 @@ def set_gecko_property(ffi_name, expr):
} }
</%def> </%def>
<%def name="impl_corner_style_coord(ident, gecko_ffi_name, x_index, y_index)"> <%def name="impl_corner_style_coord(ident, gecko_ffi_name, corner)">
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
v.0.width().to_gecko_style_coord(&mut self.gecko.${gecko_ffi_name}.data_at_mut(${x_index})); self.gecko.${gecko_ffi_name}.${corner} = v;
v.0.height().to_gecko_style_coord(&mut self.gecko.${gecko_ffi_name}.data_at_mut(${y_index}));
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn copy_${ident}_from(&mut self, other: &Self) { pub fn copy_${ident}_from(&mut self, other: &Self) {
self.gecko.${gecko_ffi_name}.data_at_mut(${x_index}) self.gecko.${gecko_ffi_name}.${corner} =
.copy_from(&other.gecko.${gecko_ffi_name}.data_at(${x_index})); other.gecko.${gecko_ffi_name}.${corner};
self.gecko.${gecko_ffi_name}.data_at_mut(${y_index})
.copy_from(&other.gecko.${gecko_ffi_name}.data_at(${y_index}));
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn reset_${ident}(&mut self, other: &Self) { pub fn reset_${ident}(&mut self, other: &Self) {
self.copy_${ident}_from(other) self.copy_${ident}_from(other)
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
use crate::values::computed::border::BorderCornerRadius; self.gecko.${gecko_ffi_name}.${corner}
let width = GeckoStyleCoordConvertible::from_gecko_style_coord(
&self.gecko.${gecko_ffi_name}.data_at(${x_index}))
.expect("Failed to clone ${ident}");
let height = GeckoStyleCoordConvertible::from_gecko_style_coord(
&self.gecko.${gecko_ffi_name}.data_at(${y_index}))
.expect("Failed to clone ${ident}");
BorderCornerRadius::new(width, height)
} }
</%def> </%def>
@ -891,13 +855,13 @@ def set_gecko_property(ffi_name, expr):
transform_functions = [ transform_functions = [
("Matrix3D", "matrix3d", ["number"] * 16), ("Matrix3D", "matrix3d", ["number"] * 16),
("Matrix", "matrix", ["number"] * 6), ("Matrix", "matrix", ["number"] * 6),
("Translate", "translate", ["lp", "optional_lp"]), ("Translate", "translate", ["lp", "lp"]),
("Translate3D", "translate3d", ["lp", "lp", "length"]), ("Translate3D", "translate3d", ["lp", "lp", "length"]),
("TranslateX", "translatex", ["lp"]), ("TranslateX", "translatex", ["lp"]),
("TranslateY", "translatey", ["lp"]), ("TranslateY", "translatey", ["lp"]),
("TranslateZ", "translatez", ["length"]), ("TranslateZ", "translatez", ["length"]),
("Scale3D", "scale3d", ["number"] * 3), ("Scale3D", "scale3d", ["number"] * 3),
("Scale", "scale", ["number", "optional_number"]), ("Scale", "scale", ["number", "number"]),
("ScaleX", "scalex", ["number"]), ("ScaleX", "scalex", ["number"]),
("ScaleY", "scaley", ["number"]), ("ScaleY", "scaley", ["number"]),
("ScaleZ", "scalez", ["number"]), ("ScaleZ", "scalez", ["number"]),
@ -906,7 +870,7 @@ transform_functions = [
("RotateX", "rotatex", ["angle"]), ("RotateX", "rotatex", ["angle"]),
("RotateY", "rotatey", ["angle"]), ("RotateY", "rotatey", ["angle"]),
("RotateZ", "rotatez", ["angle"]), ("RotateZ", "rotatez", ["angle"]),
("Skew", "skew", ["angle", "optional_angle"]), ("Skew", "skew", ["angle", "angle"]),
("SkewX", "skewx", ["angle"]), ("SkewX", "skewx", ["angle"]),
("SkewY", "skewy", ["angle"]), ("SkewY", "skewy", ["angle"]),
("Perspective", "perspective", ["length"]), ("Perspective", "perspective", ["length"]),
@ -917,7 +881,6 @@ transform_functions = [
<%def name="transform_function_arm(name, keyword, items)"> <%def name="transform_function_arm(name, keyword, items)">
<% <%
has_optional = items[-1].startswith("optional_")
pattern = None pattern = None
if keyword == "matrix3d": if keyword == "matrix3d":
# m11: number1, m12: number2, .. # m11: number1, m12: number2, ..
@ -955,36 +918,20 @@ transform_functions = [
} }
%> %>
crate::values::generics::transform::TransformOperation::${name}${pattern} => { crate::values::generics::transform::TransformOperation::${name}${pattern} => {
% if has_optional: let len = ${len(items) + 1};
let optional_present = ${items[-1] + str(len(items))}.is_some();
let len = if optional_present {
${len(items) + 1}
} else {
${len(items)}
};
% else:
let len = ${len(items) + 1};
% endif
bindings::Gecko_CSSValue_SetFunction(gecko_value, len); bindings::Gecko_CSSValue_SetFunction(gecko_value, len);
bindings::Gecko_CSSValue_SetKeyword( bindings::Gecko_CSSValue_SetKeyword(
bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0), bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0),
structs::nsCSSKeyword::eCSSKeyword_${keyword} structs::nsCSSKeyword::eCSSKeyword_${keyword}
); );
% for index, item in enumerate(items): % for index, item in enumerate(items):
<% replaced_item = item.replace("optional_", "") %>
% if item.startswith("optional"):
if let Some(${replaced_item + str(index + 1)}) = ${item + str(index + 1)} {
% endif
% if item == "list": % if item == "list":
debug_assert!(!${item}${index + 1}.0.is_empty()); debug_assert!(!${item}${index + 1}.0.is_empty());
% endif % endif
${css_value_setters[replaced_item] % ( ${css_value_setters[item] % (
"bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d)" % (index + 1), "bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d)" % (index + 1),
replaced_item + str(index + 1) item + str(index + 1)
)}; )};
% if item.startswith("optional"):
}
% endif
% endfor % endfor
} }
</%def> </%def>
@ -995,8 +942,6 @@ transform_functions = [
css_value_getters = { css_value_getters = {
"length" : "Length::new(bindings::Gecko_CSSValue_GetNumber(%s))", "length" : "Length::new(bindings::Gecko_CSSValue_GetNumber(%s))",
"lp" : "%s.get_length_percentage()", "lp" : "%s.get_length_percentage()",
"lpon" : "Either::Second(%s.get_length_percentage())",
"lon" : "Either::First(%s.get_length())",
"angle" : "%s.get_angle()", "angle" : "%s.get_angle()",
"number" : "bindings::Gecko_CSSValue_GetNumber(%s)", "number" : "bindings::Gecko_CSSValue_GetNumber(%s)",
"percentage" : "Percentage(bindings::Gecko_CSSValue_GetPercentage(%s))", "percentage" : "Percentage(bindings::Gecko_CSSValue_GetPercentage(%s))",
@ -1034,20 +979,11 @@ transform_functions = [
${field_names[index]}: ${field_names[index]}:
% endif % endif
<% <%
getter = css_value_getters[item.replace("optional_", "")] % ( getter = css_value_getters[item] % (
"bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d)" % (index + 1) "bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d)" % (index + 1)
) )
%> %>
% if item.startswith("optional_"): ${getter},
if (**gecko_value.mValue.mArray.as_ref()).mCount == ${index + 1} {
None
} else {
Some(${getter})
}
% else:
${getter}
% endif
,
% endfor % endfor
${post_symbols} ${post_symbols}
}, },
@ -1291,55 +1227,23 @@ impl Clone for ${style_struct.gecko_struct_name} {
# Types used with predefined_type()-defined properties that we can auto-generate. # Types used with predefined_type()-defined properties that we can auto-generate.
predefined_types = { predefined_types = {
"Appearance": impl_simple,
"OverscrollBehavior": impl_simple,
"OverflowClipBox": impl_simple,
"ScrollSnapAlign": impl_simple,
"ScrollSnapType": impl_simple,
"Float": impl_simple,
"Overflow": impl_simple,
"BreakBetween": impl_simple,
"BreakWithin": impl_simple,
"Resize": impl_simple,
"Color": impl_color, "Color": impl_color,
"ColorOrAuto": impl_color, "ColorOrAuto": impl_color,
"GreaterThanOrEqualToOneNumber": impl_simple,
"Integer": impl_simple,
"length::LengthOrAuto": impl_style_coord, "length::LengthOrAuto": impl_style_coord,
"length::LengthOrNormal": impl_style_coord, "length::LengthOrNormal": impl_style_coord,
"length::NonNegativeLengthOrAuto": impl_style_coord, "length::NonNegativeLengthOrAuto": impl_style_coord,
"length::NonNegativeLengthPercentageOrNormal": impl_style_coord, "length::NonNegativeLengthPercentageOrNormal": impl_style_coord,
"FillRule": impl_simple,
"FlexBasis": impl_simple,
"Length": impl_absolute_length, "Length": impl_absolute_length,
"LengthOrNormal": impl_style_coord, "LengthOrNormal": impl_style_coord,
"LengthPercentage": impl_simple,
"LengthPercentageOrAuto": impl_style_coord, "LengthPercentageOrAuto": impl_style_coord,
"MaxSize": impl_simple,
"Size": impl_simple,
"MozScriptMinSize": impl_absolute_length, "MozScriptMinSize": impl_absolute_length,
"MozScriptSizeMultiplier": impl_simple,
"NonNegativeLengthPercentage": impl_simple,
"NonNegativeLengthOrNumber": impl_simple,
"NonNegativeLengthOrNumberRect": impl_simple,
"BorderImageSlice": impl_simple,
"NonNegativeNumber": impl_simple,
"Number": impl_simple,
"Opacity": impl_simple,
"OverflowWrap": impl_simple,
"OverflowAnchor": impl_simple,
"Perspective": impl_simple,
"Position": impl_simple,
"RGBAColor": impl_rgba_color, "RGBAColor": impl_rgba_color,
"SVGLength": impl_svg_length, "SVGLength": impl_svg_length,
"SVGOpacity": impl_svg_opacity, "SVGOpacity": impl_svg_opacity,
"SVGPaint": impl_svg_paint, "SVGPaint": impl_svg_paint,
"SVGWidth": impl_svg_length, "SVGWidth": impl_svg_length,
"Transform": impl_transform, "Transform": impl_transform,
"TransformOrigin": impl_simple,
"UserSelect": impl_simple,
"url::UrlOrNone": impl_css_url, "url::UrlOrNone": impl_css_url,
"ZIndex": impl_simple,
} }
def longhand_method(longhand): def longhand_method(longhand):
@ -1354,15 +1258,12 @@ impl Clone for ${style_struct.gecko_struct_name} {
args.update(keyword=longhand.keyword) args.update(keyword=longhand.keyword)
if "font" in longhand.ident: if "font" in longhand.ident:
args.update(cast_type=longhand.cast_type) args.update(cast_type=longhand.cast_type)
else: elif longhand.predefined_type in predefined_types:
method = predefined_types[longhand.predefined_type] method = predefined_types[longhand.predefined_type]
else:
method = impl_simple
method(**args) method(**args)
picked_longhands = []
for x in longhands:
if x.keyword or x.predefined_type in predefined_types or x.logical:
picked_longhands.append(x)
%> %>
impl ${style_struct.gecko_struct_name} { impl ${style_struct.gecko_struct_name} {
/* /*
@ -1374,7 +1275,7 @@ impl ${style_struct.gecko_struct_name} {
* Auto-Generated Methods. * Auto-Generated Methods.
*/ */
<% <%
for longhand in picked_longhands: for longhand in longhands:
longhand_method(longhand) longhand_method(longhand)
%> %>
} }
@ -1387,14 +1288,6 @@ class Side(object):
self.ident = name.lower() self.ident = name.lower()
self.index = index self.index = index
class Corner(object):
def __init__(self, vert, horiz, index):
self.x_name = "HalfCorner::eCorner" + vert + horiz + "X"
self.y_name = "HalfCorner::eCorner" + vert + horiz + "Y"
self.ident = (vert + "_" + horiz).lower()
self.x_index = 2 * index
self.y_index = 2 * index + 1
class GridLine(object): class GridLine(object):
def __init__(self, name): def __init__(self, name):
self.ident = "grid-" + name.lower() self.ident = "grid-" + name.lower()
@ -1402,19 +1295,12 @@ class GridLine(object):
self.gecko = "m" + to_camel_case(self.ident) self.gecko = "m" + to_camel_case(self.ident)
SIDES = [Side("Top", 0), Side("Right", 1), Side("Bottom", 2), Side("Left", 3)] SIDES = [Side("Top", 0), Side("Right", 1), Side("Bottom", 2), Side("Left", 3)]
CORNERS = [Corner("Top", "Left", 0), Corner("Top", "Right", 1), CORNERS = ["top_left", "top_right", "bottom_right", "bottom_left"]
Corner("Bottom", "Right", 2), Corner("Bottom", "Left", 3)]
GRID_LINES = map(GridLine, ["row-start", "row-end", "column-start", "column-end"]) GRID_LINES = map(GridLine, ["row-start", "row-end", "column-start", "column-end"])
%> %>
#[allow(dead_code)] #[allow(dead_code)]
fn static_assert() { fn static_assert() {
unsafe {
% for corner in CORNERS:
transmute::<_, [u32; ${corner.x_index}]>([1; structs::${corner.x_name} as usize]);
transmute::<_, [u32; ${corner.y_index}]>([1; structs::${corner.y_name} as usize]);
% endfor
}
// Note: using the above technique with an enum hits a rust bug when |structs| is in a different crate. // Note: using the above technique with an enum hits a rust bug when |structs| is in a different crate.
% for side in SIDES: % for side in SIDES:
{ const DETAIL: u32 = [0][(structs::Side::eSide${side.name} as usize != ${side.index}) as usize]; let _ = DETAIL; } { const DETAIL: u32 = [0][(structs::Side::eSide${side.name} as usize != ${side.index}) as usize]; let _ = DETAIL; }
@ -1425,7 +1311,7 @@ fn static_assert() {
<% skip_border_longhands = " ".join(["border-{0}-{1}".format(x.ident, y) <% skip_border_longhands = " ".join(["border-{0}-{1}".format(x.ident, y)
for x in SIDES for x in SIDES
for y in ["color", "style", "width"]] + for y in ["color", "style", "width"]] +
["border-{0}-radius".format(x.ident.replace("_", "-")) ["border-{0}-radius".format(x.replace("_", "-"))
for x in CORNERS]) %> for x in CORNERS]) %>
<%self:impl_trait style_struct_name="Border" <%self:impl_trait style_struct_name="Border"
@ -1494,10 +1380,9 @@ fn static_assert() {
% endfor % endfor
% for corner in CORNERS: % for corner in CORNERS:
<% impl_corner_style_coord("border_%s_radius" % corner.ident, <% impl_corner_style_coord("border_%s_radius" % corner,
"mBorderRadius", "mBorderRadius",
corner.x_index, corner) %>
corner.y_index) %>
% endfor % endfor
pub fn set_border_image_source(&mut self, image: longhands::border_image_source::computed_value::T) { pub fn set_border_image_source(&mut self, image: longhands::border_image_source::computed_value::T) {
@ -2027,7 +1912,7 @@ fn static_assert() {
</%self:impl_trait> </%self:impl_trait>
<% skip_outline_longhands = " ".join("outline-style outline-width".split() + <% skip_outline_longhands = " ".join("outline-style outline-width".split() +
["-moz-outline-radius-{0}".format(x.ident.replace("_", "")) ["-moz-outline-radius-{0}".format(x.replace("_", ""))
for x in CORNERS]) %> for x in CORNERS]) %>
<%self:impl_trait style_struct_name="Outline" <%self:impl_trait style_struct_name="Outline"
skip_longhands="${skip_outline_longhands}"> skip_longhands="${skip_outline_longhands}">
@ -2059,10 +1944,9 @@ fn static_assert() {
round_to_pixels=True) %> round_to_pixels=True) %>
% for corner in CORNERS: % for corner in CORNERS:
<% impl_corner_style_coord("_moz_outline_radius_%s" % corner.ident.replace("_", ""), <% impl_corner_style_coord("_moz_outline_radius_%s" % corner.replace("_", ""),
"mOutlineRadius", "mOutlineRadius",
corner.x_index, corner) %>
corner.y_index) %>
% endfor % endfor
pub fn outline_has_nonzero_width(&self) -> bool { pub fn outline_has_nonzero_width(&self) -> bool {
@ -2072,6 +1956,7 @@ fn static_assert() {
<% <%
skip_font_longhands = """font-family font-size font-size-adjust font-weight skip_font_longhands = """font-family font-size font-size-adjust font-weight
font-style font-stretch -moz-script-level
font-synthesis -x-lang font-variant-alternates font-synthesis -x-lang font-variant-alternates
font-variant-east-asian font-variant-ligatures font-variant-east-asian font-variant-ligatures
font-variant-numeric font-language-override font-variant-numeric font-language-override
@ -2863,6 +2748,7 @@ fn static_assert() {
animation-iteration-count animation-timing-function animation-iteration-count animation-timing-function
clear transition-duration transition-delay clear transition-duration transition-delay
transition-timing-function transition-property transition-timing-function transition-property
transform-style
rotate scroll-snap-points-x scroll-snap-points-y rotate scroll-snap-points-x scroll-snap-points-y
scroll-snap-coordinate -moz-binding will-change scroll-snap-coordinate -moz-binding will-change
offset-path shape-outside contain touch-action offset-path shape-outside contain touch-action
@ -3291,7 +3177,6 @@ fn static_assert() {
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT; use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_SIZE; use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_SIZE;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT; use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_STYLE;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT; use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS; use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS; use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS;
@ -3315,9 +3200,6 @@ fn static_assert() {
if v.contains(SpecifiedValue::LAYOUT) { if v.contains(SpecifiedValue::LAYOUT) {
bitfield |= NS_STYLE_CONTAIN_LAYOUT; bitfield |= NS_STYLE_CONTAIN_LAYOUT;
} }
if v.contains(SpecifiedValue::STYLE) {
bitfield |= NS_STYLE_CONTAIN_STYLE;
}
if v.contains(SpecifiedValue::PAINT) { if v.contains(SpecifiedValue::PAINT) {
bitfield |= NS_STYLE_CONTAIN_PAINT; bitfield |= NS_STYLE_CONTAIN_PAINT;
} }
@ -3333,7 +3215,6 @@ fn static_assert() {
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT; use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_SIZE; use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_SIZE;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT; use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_STYLE;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT; use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS; use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS; use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS;
@ -3363,9 +3244,6 @@ fn static_assert() {
if gecko_flags & (NS_STYLE_CONTAIN_LAYOUT as u8) != 0 { if gecko_flags & (NS_STYLE_CONTAIN_LAYOUT as u8) != 0 {
servo_flags.insert(SpecifiedValue::LAYOUT); servo_flags.insert(SpecifiedValue::LAYOUT);
} }
if gecko_flags & (NS_STYLE_CONTAIN_STYLE as u8) != 0 {
servo_flags.insert(SpecifiedValue::STYLE);
}
if gecko_flags & (NS_STYLE_CONTAIN_PAINT as u8) != 0 { if gecko_flags & (NS_STYLE_CONTAIN_PAINT as u8) != 0 {
servo_flags.insert(SpecifiedValue::PAINT); servo_flags.insert(SpecifiedValue::PAINT);
} }
@ -3870,6 +3748,7 @@ fn static_assert() {
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn set__moz_image_region(&mut self, v: longhands::_moz_image_region::computed_value::T) { pub fn set__moz_image_region(&mut self, v: longhands::_moz_image_region::computed_value::T) {
use crate::values::Either; use crate::values::Either;
use crate::values::generics::length::LengthPercentageOrAuto::*;
match v { match v {
Either::Second(_auto) => { Either::Second(_auto) => {
@ -3879,15 +3758,21 @@ fn static_assert() {
self.gecko.mImageRegion.height = 0; self.gecko.mImageRegion.height = 0;
} }
Either::First(rect) => { Either::First(rect) => {
self.gecko.mImageRegion.x = rect.left.map(Au::from).unwrap_or(Au(0)).0; self.gecko.mImageRegion.x = match rect.left {
self.gecko.mImageRegion.y = rect.top.map(Au::from).unwrap_or(Au(0)).0; LengthPercentage(v) => v.to_i32_au(),
Auto => 0,
};
self.gecko.mImageRegion.y = match rect.top {
LengthPercentage(v) => v.to_i32_au(),
Auto => 0,
};
self.gecko.mImageRegion.height = match rect.bottom { self.gecko.mImageRegion.height = match rect.bottom {
Some(value) => (Au::from(value) - Au(self.gecko.mImageRegion.y)).0, LengthPercentage(value) => (Au::from(value) - Au(self.gecko.mImageRegion.y)).0,
None => 0, Auto => 0,
}; };
self.gecko.mImageRegion.width = match rect.right { self.gecko.mImageRegion.width = match rect.right {
Some(value) => (Au::from(value) - Au(self.gecko.mImageRegion.x)).0, LengthPercentage(value) => (Au::from(value) - Au(self.gecko.mImageRegion.x)).0,
None => 0, Auto => 0,
}; };
} }
} }
@ -3896,6 +3781,7 @@ fn static_assert() {
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn clone__moz_image_region(&self) -> longhands::_moz_image_region::computed_value::T { pub fn clone__moz_image_region(&self) -> longhands::_moz_image_region::computed_value::T {
use crate::values::{Auto, Either}; use crate::values::{Auto, Either};
use crate::values::generics::length::LengthPercentageOrAuto::*;
use crate::values::computed::ClipRect; use crate::values::computed::ClipRect;
// There is no ideal way to detect auto type for structs::nsRect and its components, so // There is no ideal way to detect auto type for structs::nsRect and its components, so
@ -3908,10 +3794,10 @@ fn static_assert() {
} }
Either::First(ClipRect { Either::First(ClipRect {
top: Some(Au(self.gecko.mImageRegion.y).into()), top: LengthPercentage(Au(self.gecko.mImageRegion.y).into()),
right: Some(Au(self.gecko.mImageRegion.width + self.gecko.mImageRegion.x).into()), right: LengthPercentage(Au(self.gecko.mImageRegion.width + self.gecko.mImageRegion.x).into()),
bottom: Some(Au(self.gecko.mImageRegion.height + self.gecko.mImageRegion.y).into()), bottom: LengthPercentage(Au(self.gecko.mImageRegion.height + self.gecko.mImageRegion.y).into()),
left: Some(Au(self.gecko.mImageRegion.x).into()), left: LengthPercentage(Au(self.gecko.mImageRegion.x).into()),
}) })
} }
@ -3968,38 +3854,43 @@ fn static_assert() {
use crate::gecko_bindings::structs::NS_STYLE_CLIP_TOP_AUTO; use crate::gecko_bindings::structs::NS_STYLE_CLIP_TOP_AUTO;
use crate::gecko_bindings::structs::NS_STYLE_CLIP_RIGHT_AUTO; use crate::gecko_bindings::structs::NS_STYLE_CLIP_RIGHT_AUTO;
use crate::gecko_bindings::structs::NS_STYLE_CLIP_BOTTOM_AUTO; use crate::gecko_bindings::structs::NS_STYLE_CLIP_BOTTOM_AUTO;
use crate::values::generics::length::LengthPercentageOrAuto::*;
use crate::values::Either; use crate::values::Either;
match v { match v {
Either::First(rect) => { Either::First(rect) => {
self.gecko.mClipFlags = NS_STYLE_CLIP_RECT as u8; self.gecko.mClipFlags = NS_STYLE_CLIP_RECT as u8;
if let Some(left) = rect.left { self.gecko.mClip.x = match rect.left {
self.gecko.mClip.x = left.to_i32_au(); LengthPercentage(l) => l.to_i32_au(),
} else { Auto => {
self.gecko.mClip.x = 0; self.gecko.mClipFlags |= NS_STYLE_CLIP_LEFT_AUTO as u8;
self.gecko.mClipFlags |= NS_STYLE_CLIP_LEFT_AUTO as u8; 0
} }
};
if let Some(top) = rect.top { self.gecko.mClip.y = match rect.top {
self.gecko.mClip.y = top.to_i32_au(); LengthPercentage(l) => l.to_i32_au(),
} else { Auto => {
self.gecko.mClip.y = 0; self.gecko.mClipFlags |= NS_STYLE_CLIP_TOP_AUTO as u8;
self.gecko.mClipFlags |= NS_STYLE_CLIP_TOP_AUTO as u8; 0
} }
};
if let Some(bottom) = rect.bottom { self.gecko.mClip.height = match rect.bottom {
self.gecko.mClip.height = (Au::from(bottom) - Au(self.gecko.mClip.y)).0; LengthPercentage(l) => (Au::from(l) - Au(self.gecko.mClip.y)).0,
} else { Auto => {
self.gecko.mClip.height = 1 << 30; // NS_MAXSIZE self.gecko.mClipFlags |= NS_STYLE_CLIP_BOTTOM_AUTO as u8;
self.gecko.mClipFlags |= NS_STYLE_CLIP_BOTTOM_AUTO as u8; 1 << 30 // NS_MAXSIZE
} }
};
if let Some(right) = rect.right { self.gecko.mClip.width = match rect.right {
self.gecko.mClip.width = (Au::from(right) - Au(self.gecko.mClip.x)).0; LengthPercentage(l) => (Au::from(l) - Au(self.gecko.mClip.x)).0,
} else { Auto => {
self.gecko.mClip.width = 1 << 30; // NS_MAXSIZE self.gecko.mClipFlags |= NS_STYLE_CLIP_RIGHT_AUTO as u8;
self.gecko.mClipFlags |= NS_STYLE_CLIP_RIGHT_AUTO as u8; 1 << 30 // NS_MAXSIZE
} }
};
}, },
Either::Second(_auto) => { Either::Second(_auto) => {
self.gecko.mClipFlags = NS_STYLE_CLIP_AUTO as u8; self.gecko.mClipFlags = NS_STYLE_CLIP_AUTO as u8;
@ -4026,42 +3917,42 @@ fn static_assert() {
use crate::gecko_bindings::structs::NS_STYLE_CLIP_LEFT_AUTO; use crate::gecko_bindings::structs::NS_STYLE_CLIP_LEFT_AUTO;
use crate::gecko_bindings::structs::NS_STYLE_CLIP_RIGHT_AUTO; use crate::gecko_bindings::structs::NS_STYLE_CLIP_RIGHT_AUTO;
use crate::gecko_bindings::structs::NS_STYLE_CLIP_TOP_AUTO; use crate::gecko_bindings::structs::NS_STYLE_CLIP_TOP_AUTO;
use crate::values::generics::length::LengthPercentageOrAuto::*;
use crate::values::computed::{ClipRect, ClipRectOrAuto}; use crate::values::computed::{ClipRect, ClipRectOrAuto};
use crate::values::Either; use crate::values::Either;
if self.gecko.mClipFlags == NS_STYLE_CLIP_AUTO as u8 { if self.gecko.mClipFlags == NS_STYLE_CLIP_AUTO as u8 {
ClipRectOrAuto::auto() return ClipRectOrAuto::auto()
} else {
let left = if self.gecko.mClipFlags & NS_STYLE_CLIP_LEFT_AUTO as u8 != 0 {
debug_assert_eq!(self.gecko.mClip.x, 0);
None
} else {
Some(Au(self.gecko.mClip.x).into())
};
let top = if self.gecko.mClipFlags & NS_STYLE_CLIP_TOP_AUTO as u8 != 0 {
debug_assert_eq!(self.gecko.mClip.y, 0);
None
} else {
Some(Au(self.gecko.mClip.y).into())
};
let bottom = if self.gecko.mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO as u8 != 0 {
debug_assert_eq!(self.gecko.mClip.height, 1 << 30); // NS_MAXSIZE
None
} else {
Some(Au(self.gecko.mClip.y + self.gecko.mClip.height).into())
};
let right = if self.gecko.mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO as u8 != 0 {
debug_assert_eq!(self.gecko.mClip.width, 1 << 30); // NS_MAXSIZE
None
} else {
Some(Au(self.gecko.mClip.x + self.gecko.mClip.width).into())
};
Either::First(ClipRect { top, right, bottom, left })
} }
let left = if self.gecko.mClipFlags & NS_STYLE_CLIP_LEFT_AUTO as u8 != 0 {
debug_assert_eq!(self.gecko.mClip.x, 0);
Auto
} else {
LengthPercentage(Au(self.gecko.mClip.x).into())
};
let top = if self.gecko.mClipFlags & NS_STYLE_CLIP_TOP_AUTO as u8 != 0 {
debug_assert_eq!(self.gecko.mClip.y, 0);
Auto
} else {
LengthPercentage(Au(self.gecko.mClip.y).into())
};
let bottom = if self.gecko.mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO as u8 != 0 {
debug_assert_eq!(self.gecko.mClip.height, 1 << 30); // NS_MAXSIZE
Auto
} else {
LengthPercentage(Au(self.gecko.mClip.y + self.gecko.mClip.height).into())
};
let right = if self.gecko.mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO as u8 != 0 {
debug_assert_eq!(self.gecko.mClip.width, 1 << 30); // NS_MAXSIZE
Auto
} else {
LengthPercentage(Au(self.gecko.mClip.x + self.gecko.mClip.width).into())
};
Either::First(ClipRect { top, right, bottom, left })
} }
<% <%
@ -4238,7 +4129,7 @@ fn static_assert() {
<%self:impl_trait style_struct_name="InheritedText" <%self:impl_trait style_struct_name="InheritedText"
skip_longhands="text-align text-emphasis-style text-shadow line-height letter-spacing word-spacing skip_longhands="text-align text-emphasis-style text-shadow
-webkit-text-stroke-width text-emphasis-position"> -webkit-text-stroke-width text-emphasis-position">
<% text_align_keyword = Keyword("text-align", <% text_align_keyword = Keyword("text-align",
@ -4270,78 +4161,6 @@ fn static_assert() {
longhands::text_shadow::computed_value::List(buf) longhands::text_shadow::computed_value::List(buf)
} }
pub fn set_line_height(&mut self, v: longhands::line_height::computed_value::T) {
use crate::values::generics::text::LineHeight;
// FIXME: Align binary representations and ditch |match| for cast + static_asserts
let en = match v {
LineHeight::Normal => CoordDataValue::Normal,
LineHeight::Length(val) => CoordDataValue::Coord(val.0.to_i32_au()),
LineHeight::Number(val) => CoordDataValue::Factor(val.0),
LineHeight::MozBlockHeight =>
CoordDataValue::Enumerated(structs::NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT),
};
self.gecko.mLineHeight.set_value(en);
}
pub fn clone_line_height(&self) -> longhands::line_height::computed_value::T {
use crate::values::generics::text::LineHeight;
return match self.gecko.mLineHeight.as_value() {
CoordDataValue::Normal => LineHeight::Normal,
CoordDataValue::Coord(coord) => LineHeight::Length(Au(coord).into()),
CoordDataValue::Factor(n) => LineHeight::Number(n.into()),
CoordDataValue::Enumerated(val) if val == structs::NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT =>
LineHeight::MozBlockHeight,
_ => panic!("this should not happen"),
}
}
<%call expr="impl_coord_copy('line_height', 'mLineHeight')"></%call>
pub fn set_letter_spacing(&mut self, v: longhands::letter_spacing::computed_value::T) {
use crate::values::generics::text::Spacing;
match v {
Spacing::Value(value) => self.gecko.mLetterSpacing.set(value),
Spacing::Normal => self.gecko.mLetterSpacing.set_value(CoordDataValue::Normal)
}
}
pub fn clone_letter_spacing(&self) -> longhands::letter_spacing::computed_value::T {
use crate::values::computed::Length;
use crate::values::generics::text::Spacing;
debug_assert!(
matches!(self.gecko.mLetterSpacing.as_value(),
CoordDataValue::Normal |
CoordDataValue::Coord(_)),
"Unexpected computed value for letter-spacing");
Length::from_gecko_style_coord(&self.gecko.mLetterSpacing).map_or(Spacing::Normal, Spacing::Value)
}
<%call expr="impl_coord_copy('letter_spacing', 'mLetterSpacing')"></%call>
pub fn set_word_spacing(&mut self, v: longhands::word_spacing::computed_value::T) {
use crate::values::generics::text::Spacing;
match v {
Spacing::Value(lp) => self.gecko.mWordSpacing.set(lp),
// https://drafts.csswg.org/css-text-3/#valdef-word-spacing-normal
Spacing::Normal => self.gecko.mWordSpacing.set_value(CoordDataValue::Coord(0)),
}
}
pub fn clone_word_spacing(&self) -> longhands::word_spacing::computed_value::T {
use crate::values::computed::LengthPercentage;
use crate::values::generics::text::Spacing;
debug_assert!(
matches!(self.gecko.mWordSpacing.as_value(),
CoordDataValue::Normal |
CoordDataValue::Coord(_) |
CoordDataValue::Percent(_) |
CoordDataValue::Calc(_)),
"Unexpected computed value for word-spacing");
LengthPercentage::from_gecko_style_coord(&self.gecko.mWordSpacing).map_or(Spacing::Normal, Spacing::Value)
}
<%call expr="impl_coord_copy('word_spacing', 'mWordSpacing')"></%call>
fn clear_text_emphasis_style_if_string(&mut self) { fn clear_text_emphasis_style_if_string(&mut self) {
if self.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 { if self.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 {
self.gecko.mTextEmphasisStyleString.truncate(); self.gecko.mTextEmphasisStyleString.truncate();
@ -4593,7 +4412,6 @@ fn set_style_svg_path(
use crate::gecko_bindings::bindings::{Gecko_NewBasicShape, Gecko_DestroyShapeSource}; use crate::gecko_bindings::bindings::{Gecko_NewBasicShape, Gecko_DestroyShapeSource};
use crate::gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleShapeSourceType}; use crate::gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleShapeSourceType};
use crate::gecko_bindings::structs::{StyleGeometryBox, StyleShapeSource}; use crate::gecko_bindings::structs::{StyleGeometryBox, StyleShapeSource};
use crate::gecko::conversions::basic_shape::set_corners_from_radius;
use crate::gecko::values::GeckoStyleCoordConvertible; use crate::gecko::values::GeckoStyleCoordConvertible;
use crate::values::generics::basic_shape::{BasicShape, ShapeSource}; use crate::values::generics::basic_shape::{BasicShape, ShapeSource};
@ -4658,8 +4476,7 @@ fn set_style_svg_path(
inset.rect.2.to_gecko_style_coord(&mut shape.mCoordinates[2]); inset.rect.2.to_gecko_style_coord(&mut shape.mCoordinates[2]);
shape.mCoordinates[3].leaky_set_null(); shape.mCoordinates[3].leaky_set_null();
inset.rect.3.to_gecko_style_coord(&mut shape.mCoordinates[3]); inset.rect.3.to_gecko_style_coord(&mut shape.mCoordinates[3]);
shape.mRadius = inset.round;
set_corners_from_radius(inset.round, &mut shape.mRadius);
} }
BasicShape::Circle(circ) => { BasicShape::Circle(circ) => {
let shape = init_shape(${ident}, StyleBasicShapeType::Circle); let shape = init_shape(${ident}, StyleBasicShapeType::Circle);
@ -4747,7 +4564,7 @@ clip-path
pub fn set_stroke_dasharray(&mut self, v: longhands::stroke_dasharray::computed_value::T) { pub fn set_stroke_dasharray(&mut self, v: longhands::stroke_dasharray::computed_value::T) {
use crate::gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE; use crate::gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE;
use crate::values::generics::svg::{SVGStrokeDashArray, SvgLengthPercentageOrNumber}; use crate::values::generics::svg::SVGStrokeDashArray;
match v { match v {
SVGStrokeDashArray::Values(v) => { SVGStrokeDashArray::Values(v) => {
@ -4757,12 +4574,7 @@ clip-path
bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut self.gecko, v.len() as u32); bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut self.gecko, v.len() as u32);
} }
for (gecko, servo) in self.gecko.mStrokeDasharray.iter_mut().zip(v) { for (gecko, servo) in self.gecko.mStrokeDasharray.iter_mut().zip(v) {
match servo { *gecko = servo;
SvgLengthPercentageOrNumber::LengthPercentage(lp) =>
gecko.set(lp),
SvgLengthPercentageOrNumber::Number(num) =>
gecko.set_value(CoordDataValue::Factor(num.into())),
}
} }
} }
SVGStrokeDashArray::ContextValue => { SVGStrokeDashArray::ContextValue => {
@ -4790,32 +4602,13 @@ clip-path
pub fn clone_stroke_dasharray(&self) -> longhands::stroke_dasharray::computed_value::T { pub fn clone_stroke_dasharray(&self) -> longhands::stroke_dasharray::computed_value::T {
use crate::gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE; use crate::gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE;
use crate::values::computed::LengthPercentage; use crate::values::generics::svg::SVGStrokeDashArray;
use crate::values::generics::NonNegative;
use crate::values::generics::svg::{SVGStrokeDashArray, SvgLengthPercentageOrNumber};
if self.gecko.mContextFlags & CONTEXT_VALUE != 0 { if self.gecko.mContextFlags & CONTEXT_VALUE != 0 {
debug_assert_eq!(self.gecko.mStrokeDasharray.len(), 0); debug_assert_eq!(self.gecko.mStrokeDasharray.len(), 0);
return SVGStrokeDashArray::ContextValue; return SVGStrokeDashArray::ContextValue;
} }
let mut vec = vec![]; SVGStrokeDashArray::Values(self.gecko.mStrokeDasharray.iter().cloned().collect())
for gecko in self.gecko.mStrokeDasharray.iter() {
match gecko.as_value() {
CoordDataValue::Factor(number) =>
vec.push(SvgLengthPercentageOrNumber::Number(number.into())),
CoordDataValue::Coord(coord) =>
vec.push(SvgLengthPercentageOrNumber::LengthPercentage(
NonNegative(LengthPercentage::new(Au(coord).into(), None).into()))),
CoordDataValue::Percent(p) =>
vec.push(SvgLengthPercentageOrNumber::LengthPercentage(
NonNegative(LengthPercentage::new_percent(Percentage(p)).into()))),
CoordDataValue::Calc(calc) =>
vec.push(SvgLengthPercentageOrNumber::LengthPercentage(
NonNegative(LengthPercentage::from(calc).clamp_to_non_negative()))),
_ => unreachable!(),
}
}
SVGStrokeDashArray::Values(vec)
} }
#[allow(non_snake_case)] #[allow(non_snake_case)]

View file

@ -19,6 +19,8 @@
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::values::specified::AllowQuirks; use crate::values::specified::AllowQuirks;
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::Zero;
#[allow(unused_imports)]
use smallvec::SmallVec; use smallvec::SmallVec;
pub use crate::values::specified::${type} as SpecifiedValue; pub use crate::values::specified::${type} as SpecifiedValue;
pub mod computed_value { pub mod computed_value {
@ -324,29 +326,32 @@
PropertyDeclaration::CSSWideKeyword(ref declaration) => { PropertyDeclaration::CSSWideKeyword(ref declaration) => {
debug_assert_eq!(declaration.id, LonghandId::${property.camel_case}); debug_assert_eq!(declaration.id, LonghandId::${property.camel_case});
match declaration.keyword { match declaration.keyword {
% if not data.current_style_struct.inherited: % if not property.style_struct.inherited:
CSSWideKeyword::Unset | CSSWideKeyword::Unset |
% endif % endif
CSSWideKeyword::Initial => { CSSWideKeyword::Initial => {
% if property.ident == "font_size": % if not property.style_struct.inherited:
computed::FontSize::cascade_initial_font_size(context); debug_assert!(false, "Should be handled in apply_properties");
% else: % else:
% if property.name == "font-size":
computed::FontSize::cascade_initial_font_size(context);
% else:
context.builder.reset_${property.ident}(); context.builder.reset_${property.ident}();
% endif
% endif % endif
}, },
% if data.current_style_struct.inherited: % if property.style_struct.inherited:
CSSWideKeyword::Unset | CSSWideKeyword::Unset |
% endif % endif
CSSWideKeyword::Inherit => { CSSWideKeyword::Inherit => {
% if not property.style_struct.inherited: % if property.style_struct.inherited:
context.rule_cache_conditions.borrow_mut().set_uncacheable(); debug_assert!(false, "Should be handled in apply_properties");
% endif
% if property.ident == "font_size":
computed::FontSize::cascade_inherit_font_size(context);
% else: % else:
context.rule_cache_conditions.borrow_mut().set_uncacheable();
context.builder.inherit_${property.ident}(); context.builder.inherit_${property.ident}();
% endif % endif
} }
CSSWideKeyword::Revert => unreachable!("Should never get here"),
} }
return; return;
} }

View file

@ -452,14 +452,23 @@ impl AnimationValue {
% for prop in data.longhands: % for prop in data.longhands:
% if prop.animatable: % if prop.animatable:
LonghandId::${prop.camel_case} => { LonghandId::${prop.camel_case} => {
// FIXME(emilio, bug 1533327): I think
// CSSWideKeyword::Revert handling is not fine here, but
// what to do instead?
//
// Seems we'd need the computed value as if it was
// revert, somehow. Treating it as `unset` seems fine
// for now...
let style_struct = match declaration.keyword { let style_struct = match declaration.keyword {
% if not prop.style_struct.inherited: % if not prop.style_struct.inherited:
CSSWideKeyword::Revert |
CSSWideKeyword::Unset | CSSWideKeyword::Unset |
% endif % endif
CSSWideKeyword::Initial => { CSSWideKeyword::Initial => {
initial.get_${prop.style_struct.name_lower}() initial.get_${prop.style_struct.name_lower}()
}, },
% if prop.style_struct.inherited: % if prop.style_struct.inherited:
CSSWideKeyword::Revert |
CSSWideKeyword::Unset | CSSWideKeyword::Unset |
% endif % endif
CSSWideKeyword::Inherit => { CSSWideKeyword::Inherit => {
@ -802,29 +811,29 @@ impl ToAnimatedZero for Visibility {
impl Animate for ClipRect { impl Animate for ClipRect {
#[inline] #[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
use crate::values::computed::Length; use crate::values::computed::LengthOrAuto;
let animate_component = |this: &Option<Length>, other: &Option<Length>| { let animate_component = |this: &LengthOrAuto, other: &LengthOrAuto| {
match (this.animate(other, procedure)?, procedure) { let result = this.animate(other, procedure)?;
(None, Procedure::Interpolate { .. }) => Ok(None), if let Procedure::Interpolate { .. } = procedure {
(None, _) => Err(()), return Ok(result);
(result, _) => Ok(result),
} }
if result.is_auto() {
// FIXME(emilio): Why? A couple SMIL tests fail without this,
// but it seems extremely fishy.
return Err(());
}
Ok(result)
}; };
Ok(ClipRect { Ok(ClipRect {
top: animate_component(&self.top, &other.top)?, top: animate_component(&self.top, &other.top)?,
right: animate_component(&self.right, &other.right)?, right: animate_component(&self.right, &other.right)?,
bottom: animate_component(&self.bottom, &other.bottom)?, bottom: animate_component(&self.bottom, &other.bottom)?,
left: animate_component(&self.left, &other.left)?, left: animate_component(&self.left, &other.left)?,
}) })
} }
} }
impl ToAnimatedZero for ClipRect {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> { Err(()) }
}
<% <%
FILTER_FUNCTIONS = [ 'Blur', 'Brightness', 'Contrast', 'Grayscale', FILTER_FUNCTIONS = [ 'Blur', 'Brightness', 'Contrast', 'Grayscale',
'HueRotate', 'Invert', 'Opacity', 'Saturate', 'HueRotate', 'Invert', 'Opacity', 'Saturate',

View file

@ -9,8 +9,8 @@
${helpers.predefined_type( ${helpers.predefined_type(
"column-width", "column-width",
"length::NonNegativeLengthOrAuto", "length::NonNegativeLengthOrAuto",
"Either::Second(Auto)", "computed::length::NonNegativeLengthOrAuto::auto()",
initial_specified_value="Either::Second(Auto)", initial_specified_value="specified::length::NonNegativeLengthOrAuto::auto()",
extra_prefixes="moz", extra_prefixes="moz",
animation_value_type="NonNegativeLengthOrAuto", animation_value_type="NonNegativeLengthOrAuto",
servo_pref="layout.columns.enabled", servo_pref="layout.columns.enabled",

View file

@ -84,7 +84,8 @@ ${helpers.predefined_type(
)} )}
${helpers.predefined_type( ${helpers.predefined_type(
"stroke-width", "SVGWidth", "stroke-width",
"SVGWidth",
"computed::SVGWidth::one()", "computed::SVGWidth::one()",
products="gecko", products="gecko",
animation_value_type="crate::values::computed::SVGWidth", animation_value_type="crate::values::computed::SVGWidth",
@ -109,11 +110,11 @@ ${helpers.single_keyword(
${helpers.predefined_type( ${helpers.predefined_type(
"stroke-miterlimit", "stroke-miterlimit",
"GreaterThanOrEqualToOneNumber", "NonNegativeNumber",
"From::from(4.0)", "From::from(4.0)",
products="gecko", products="gecko",
animation_value_type="crate::values::computed::GreaterThanOrEqualToOneNumber", animation_value_type="crate::values::computed::NonNegativeNumber",
spec="https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty", spec="https://www.w3.org/TR/SVG2/painting.html#StrokeMiterlimitProperty",
)} )}
${helpers.predefined_type( ${helpers.predefined_type(

View file

@ -74,13 +74,13 @@ ${helpers.predefined_type(
servo_restyle_damage="rebuild_and_reflow", servo_restyle_damage="rebuild_and_reflow",
)} )}
// TODO(pcwalton): Support `word-break: keep-all` once we have better CJK support. ${helpers.predefined_type(
${helpers.single_keyword(
"word-break", "word-break",
"normal break-all keep-all", "WordBreak",
gecko_constant_prefix="NS_STYLE_WORDBREAK", "computed::WordBreak::Normal",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-word-break", spec="https://drafts.csswg.org/css-text/#propdef-word-break",
needs_context=False,
servo_restyle_damage="rebuild_and_reflow", servo_restyle_damage="rebuild_and_reflow",
)} )}
@ -157,7 +157,7 @@ ${helpers.predefined_type(
${helpers.predefined_type( ${helpers.predefined_type(
"word-spacing", "word-spacing",
"WordSpacing", "WordSpacing",
"computed::WordSpacing::normal()", "computed::WordSpacing::zero()",
animation_value_type="ComputedValue", animation_value_type="ComputedValue",
flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
spec="https://drafts.csswg.org/css-text/#propdef-word-spacing", spec="https://drafts.csswg.org/css-text/#propdef-word-spacing",

View file

@ -39,6 +39,6 @@
logical=side[1], logical=side[1],
logical_group="scroll-padding", logical_group="scroll-padding",
spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-%s" % side[0], spec="https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding-%s" % side[0],
animation_value_type="ComputedValue", animation_value_type="NonNegativeLengthPercentageOrAuto",
)} )}
% endfor % endfor

View file

@ -42,6 +42,7 @@ use crate::values::computed;
use crate::values::computed::NonNegativeLength; use crate::values::computed::NonNegativeLength;
use crate::values::serialize_atom_name; use crate::values::serialize_atom_name;
use crate::rule_tree::StrongRuleNode; use crate::rule_tree::StrongRuleNode;
use crate::Zero;
use self::computed_value_flags::*; use self::computed_value_flags::*;
use crate::str::{CssString, CssStringBorrow, CssStringWriter}; use crate::str::{CssString, CssStringBorrow, CssStringWriter};
@ -899,6 +900,8 @@ pub enum CSSWideKeyword {
Inherit, Inherit,
/// The `unset` keyword. /// The `unset` keyword.
Unset, Unset,
/// The `revert` keyword.
Revert,
} }
impl CSSWideKeyword { impl CSSWideKeyword {
@ -907,6 +910,7 @@ impl CSSWideKeyword {
CSSWideKeyword::Initial => "initial", CSSWideKeyword::Initial => "initial",
CSSWideKeyword::Inherit => "inherit", CSSWideKeyword::Inherit => "inherit",
CSSWideKeyword::Unset => "unset", CSSWideKeyword::Unset => "unset",
CSSWideKeyword::Revert => "revert",
} }
} }
} }
@ -920,6 +924,7 @@ impl CSSWideKeyword {
"initial" => CSSWideKeyword::Initial, "initial" => CSSWideKeyword::Initial,
"inherit" => CSSWideKeyword::Inherit, "inherit" => CSSWideKeyword::Inherit,
"unset" => CSSWideKeyword::Unset, "unset" => CSSWideKeyword::Unset,
"revert" => CSSWideKeyword::Revert,
_ => return Err(()), _ => return Err(()),
} }
}; };
@ -2102,6 +2107,7 @@ impl PropertyDeclaration {
} }
/// Returns a CSS-wide keyword if the declaration's value is one. /// Returns a CSS-wide keyword if the declaration's value is one.
#[inline]
pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> { pub fn get_css_wide_keyword(&self) -> Option<CSSWideKeyword> {
match *self { match *self {
PropertyDeclaration::CSSWideKeyword(ref declaration) => { PropertyDeclaration::CSSWideKeyword(ref declaration) => {
@ -2585,7 +2591,8 @@ pub mod style_structs {
/// Whether the border-${side} property has nonzero width. /// Whether the border-${side} property has nonzero width.
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn border_${side}_has_nonzero_width(&self) -> bool { pub fn border_${side}_has_nonzero_width(&self) -> bool {
self.border_${side}_width != NonNegativeLength::zero() use crate::Zero;
!self.border_${side}_width.is_zero()
} }
% endfor % endfor
% elif style_struct.name == "Font": % elif style_struct.name == "Font":
@ -2624,7 +2631,8 @@ pub mod style_structs {
/// Whether the outline-width property is non-zero. /// Whether the outline-width property is non-zero.
#[inline] #[inline]
pub fn outline_has_nonzero_width(&self) -> bool { pub fn outline_has_nonzero_width(&self) -> bool {
self.outline_width != NonNegativeLength::zero() use crate::Zero;
!self.outline_width.is_zero()
} }
% elif style_struct.name == "Text": % elif style_struct.name == "Text":
/// Whether the text decoration has an underline. /// Whether the text decoration has an underline.
@ -2718,11 +2726,7 @@ pub mod style_structs {
/// Whether this is a multicol style. /// Whether this is a multicol style.
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
pub fn is_multicol(&self) -> bool { pub fn is_multicol(&self) -> bool {
use crate::values::Either; !self.column_width.is_auto() || !self.column_count.is_auto()
match self.column_width {
Either::First(_width) => true,
Either::Second(_auto) => !self.column_count.is_auto(),
}
} }
% endif % endif
} }
@ -3435,22 +3439,16 @@ impl<'a> StyleBuilder<'a> {
} }
% for property in data.longhands: % for property in data.longhands:
% if property.ident != "font_size": % if not property.style_struct.inherited:
/// Inherit `${property.ident}` from our parent style. /// Inherit `${property.ident}` from our parent style.
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn inherit_${property.ident}(&mut self) { pub fn inherit_${property.ident}(&mut self) {
let inherited_struct = let inherited_struct =
% if property.style_struct.inherited:
self.inherited_style.get_${property.style_struct.name_lower}();
% else:
self.inherited_style_ignoring_first_line self.inherited_style_ignoring_first_line
.get_${property.style_struct.name_lower}(); .get_${property.style_struct.name_lower}();
% endif
% if not property.style_struct.inherited:
self.flags.insert(ComputedValueFlags::INHERITS_RESET_STYLE);
self.modified_reset = true; self.modified_reset = true;
% endif self.flags.insert(ComputedValueFlags::INHERITS_RESET_STYLE);
% if property.ident == "content": % if property.ident == "content":
self.flags.insert(ComputedValueFlags::INHERITS_CONTENT); self.flags.insert(ComputedValueFlags::INHERITS_CONTENT);
@ -3472,17 +3470,13 @@ impl<'a> StyleBuilder<'a> {
% endif % endif
); );
} }
% elif property.name != "font-size":
/// Reset `${property.ident}` to the initial value. /// Reset `${property.ident}` to the initial value.
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn reset_${property.ident}(&mut self) { pub fn reset_${property.ident}(&mut self) {
let reset_struct = let reset_struct =
self.reset_style.get_${property.style_struct.name_lower}(); self.reset_style.get_${property.style_struct.name_lower}();
% if not property.style_struct.inherited:
self.modified_reset = true;
% endif
if self.${property.style_struct.ident}.ptr_eq(reset_struct) { if self.${property.style_struct.ident}.ptr_eq(reset_struct) {
return; return;
} }
@ -3495,6 +3489,7 @@ impl<'a> StyleBuilder<'a> {
% endif % endif
); );
} }
% endif
% if not property.is_vector: % if not property.is_vector:
/// Set the `${property.ident}` to the computed value `value`. /// Set the `${property.ident}` to the computed value `value`.
@ -3516,7 +3511,6 @@ impl<'a> StyleBuilder<'a> {
); );
} }
% endif % endif
% endif
% endfor % endfor
<% del property %> <% del property %>

View file

@ -11,7 +11,7 @@ use crate::applicable_declarations::ApplicableDeclarationList;
use crate::gecko::selector_parser::PseudoElement; use crate::gecko::selector_parser::PseudoElement;
use crate::properties::{Importance, LonghandIdSet, PropertyDeclarationBlock}; use crate::properties::{Importance, LonghandIdSet, PropertyDeclarationBlock};
use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
use crate::stylesheets::StyleRule; use crate::stylesheets::{Origin, StyleRule};
use crate::thread_state; use crate::thread_state;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
@ -659,6 +659,25 @@ impl CascadeLevel {
} }
} }
/// Returns the cascade origin of the rule.
#[inline]
pub fn origin(&self) -> Origin {
match *self {
CascadeLevel::UAImportant | CascadeLevel::UANormal => Origin::UserAgent,
CascadeLevel::UserImportant | CascadeLevel::UserNormal => Origin::User,
CascadeLevel::PresHints |
CascadeLevel::InnerShadowNormal |
CascadeLevel::SameTreeAuthorNormal |
CascadeLevel::StyleAttributeNormal |
CascadeLevel::SMILOverride |
CascadeLevel::Animations |
CascadeLevel::SameTreeAuthorImportant |
CascadeLevel::StyleAttributeImportant |
CascadeLevel::InnerShadowImportant |
CascadeLevel::Transitions => Origin::Author,
}
}
/// Returns whether this cascade level represents an animation rules. /// Returns whether this cascade level represents an animation rules.
#[inline] #[inline]
pub fn is_animation(&self) -> bool { pub fn is_animation(&self) -> bool {

View file

@ -36,6 +36,25 @@ impl Origin {
_ => return None, _ => return None,
}) })
} }
fn to_index(self) -> i8 {
match self {
Origin::Author => 0,
Origin::User => 1,
Origin::UserAgent => 2,
}
}
/// Returns an iterator from this origin, towards all the less specific
/// origins. So for `UserAgent`, it'd iterate through all origins.
#[inline]
pub fn following_including(self) -> OriginSetIterator {
OriginSetIterator {
set: OriginSet::ORIGIN_USER | OriginSet::ORIGIN_AUTHOR | OriginSet::ORIGIN_USER_AGENT,
cur: self.to_index(),
rev: true,
}
}
} }
bitflags! { bitflags! {
@ -57,7 +76,11 @@ impl OriginSet {
/// See the `OriginSet` documentation for information about the order /// See the `OriginSet` documentation for information about the order
/// origins are iterated. /// origins are iterated.
pub fn iter(&self) -> OriginSetIterator { pub fn iter(&self) -> OriginSetIterator {
OriginSetIterator { set: *self, cur: 0 } OriginSetIterator {
set: *self,
cur: 0,
rev: false,
}
} }
} }
@ -79,6 +102,7 @@ impl BitOrAssign<Origin> for OriginSet {
pub struct OriginSetIterator { pub struct OriginSetIterator {
set: OriginSet, set: OriginSet,
cur: i8, cur: i8,
rev: bool,
} }
impl Iterator for OriginSetIterator { impl Iterator for OriginSetIterator {
@ -88,7 +112,11 @@ impl Iterator for OriginSetIterator {
loop { loop {
let origin = Origin::from_index(self.cur)?; let origin = Origin::from_index(self.cur)?;
self.cur += 1; if self.rev {
self.cur -= 1;
} else {
self.cur += 1;
}
if self.set.contains(origin.into()) { if self.set.contains(origin.into()) {
return Some(origin); return Some(origin);

View file

@ -686,7 +686,7 @@ where
// sibling or cousin. Otherwise, recascading a bunch of identical // sibling or cousin. Otherwise, recascading a bunch of identical
// elements would unnecessarily flood the cache with identical entries. // elements would unnecessarily flood the cache with identical entries.
// //
// This is analagous to the obvious "don't insert an element that just // This is analogous to the obvious "don't insert an element that just
// got a hit in the style sharing cache" behavior in the MatchAndCascade // got a hit in the style sharing cache" behavior in the MatchAndCascade
// handling above. // handling above.
// //

View file

@ -16,7 +16,7 @@ use crate::values::computed::Image;
use crate::values::specified::SVGPathData; use crate::values::specified::SVGPathData;
use crate::values::CSSFloat; use crate::values::CSSFloat;
use app_units::Au; use app_units::Au;
use euclid::{Point2D, Size2D}; use euclid::Point2D;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::cmp; use std::cmp;
@ -241,19 +241,6 @@ impl Animate for Au {
} }
} }
impl<T> Animate for Size2D<T>
where
T: Animate,
{
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
Ok(Size2D::new(
self.width.animate(&other.width, procedure)?,
self.height.animate(&other.height, procedure)?,
))
}
}
impl<T> Animate for Point2D<T> impl<T> Animate for Point2D<T>
where where
T: Animate, T: Animate,
@ -397,16 +384,13 @@ where
} }
} }
impl<T> ToAnimatedZero for Size2D<T> impl<T> ToAnimatedZero for Vec<T>
where where
T: ToAnimatedZero, T: ToAnimatedZero,
{ {
#[inline] #[inline]
fn to_animated_zero(&self) -> Result<Self, ()> { fn to_animated_zero(&self) -> Result<Self, ()> {
Ok(Size2D::new( self.iter().map(|v| v.to_animated_zero()).collect()
self.width.to_animated_zero()?,
self.height.to_animated_zero()?,
))
} }
} }

View file

@ -8,10 +8,8 @@ use super::{Animate, Procedure, ToAnimatedZero};
use crate::properties::animated_properties::ListAnimation; use crate::properties::animated_properties::ListAnimation;
use crate::values::animated::color::Color as AnimatedColor; use crate::values::animated::color::Color as AnimatedColor;
use crate::values::computed::url::ComputedUrl; use crate::values::computed::url::ComputedUrl;
use crate::values::computed::{LengthPercentage, Number, NumberOrPercentage};
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::svg::{SVGLength, SVGPaint, SvgLengthPercentageOrNumber}; use crate::values::generics::svg::{SVGPaint, SVGStrokeDashArray};
use crate::values::generics::svg::{SVGOpacity, SVGStrokeDashArray};
/// Animated SVGPaint. /// Animated SVGPaint.
pub type IntermediateSVGPaint = SVGPaint<AnimatedColor, ComputedUrl>; pub type IntermediateSVGPaint = SVGPaint<AnimatedColor, ComputedUrl>;
@ -26,67 +24,6 @@ impl ToAnimatedZero for IntermediateSVGPaint {
} }
} }
// FIXME: We need to handle calc here properly, see
// https://bugzilla.mozilla.org/show_bug.cgi?id=1386967
fn to_number_or_percentage(
value: &SvgLengthPercentageOrNumber<LengthPercentage, Number>,
) -> Result<NumberOrPercentage, ()> {
Ok(match *value {
SvgLengthPercentageOrNumber::LengthPercentage(ref l) => match l.specified_percentage() {
Some(p) => {
if l.unclamped_length().px() != 0. {
return Err(());
}
NumberOrPercentage::Percentage(p)
},
None => NumberOrPercentage::Number(l.length().px()),
},
SvgLengthPercentageOrNumber::Number(ref n) => NumberOrPercentage::Number(*n),
})
}
impl Animate for SvgLengthPercentageOrNumber<LengthPercentage, Number> {
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
let this = to_number_or_percentage(self)?;
let other = to_number_or_percentage(other)?;
match (this, other) {
(NumberOrPercentage::Number(ref this), NumberOrPercentage::Number(ref other)) => Ok(
SvgLengthPercentageOrNumber::Number(this.animate(other, procedure)?),
),
(
NumberOrPercentage::Percentage(ref this),
NumberOrPercentage::Percentage(ref other),
) => Ok(SvgLengthPercentageOrNumber::LengthPercentage(
LengthPercentage::new_percent(this.animate(other, procedure)?),
)),
_ => Err(()),
}
}
}
impl ComputeSquaredDistance for SvgLengthPercentageOrNumber<LengthPercentage, Number> {
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
to_number_or_percentage(self)?.compute_squared_distance(&to_number_or_percentage(other)?)
}
}
impl<L> Animate for SVGLength<L>
where
L: Animate + Clone,
{
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
match (self, other) {
(&SVGLength::Length(ref this), &SVGLength::Length(ref other)) => {
Ok(SVGLength::Length(this.animate(other, procedure)?))
},
_ => Err(()),
}
}
}
/// <https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty> /// <https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty>
impl<L> Animate for SVGStrokeDashArray<L> impl<L> Animate for SVGStrokeDashArray<L>
where where
@ -121,36 +58,3 @@ where
} }
} }
} }
impl<L> ToAnimatedZero for SVGStrokeDashArray<L>
where
L: ToAnimatedZero,
{
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
match *self {
SVGStrokeDashArray::Values(ref values) => Ok(SVGStrokeDashArray::Values(
values
.iter()
.map(ToAnimatedZero::to_animated_zero)
.collect::<Result<Vec<_>, _>>()?,
)),
SVGStrokeDashArray::ContextValue => Ok(SVGStrokeDashArray::ContextValue),
}
}
}
impl<O> Animate for SVGOpacity<O>
where
O: Animate + Clone,
{
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
match (self, other) {
(&SVGOpacity::Opacity(ref this), &SVGOpacity::Opacity(ref other)) => {
Ok(SVGOpacity::Opacity(this.animate(other, procedure)?))
},
_ => Err(()),
}
}
}

View file

@ -22,7 +22,7 @@ use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::transform::{self, Transform, TransformOperation}; use crate::values::generics::transform::{self, Transform, TransformOperation};
use crate::values::generics::transform::{Rotate, Scale, Translate}; use crate::values::generics::transform::{Rotate, Scale, Translate};
use crate::values::CSSFloat; use crate::values::CSSFloat;
use num_traits::Zero; use crate::Zero;
use std::cmp; use std::cmp;
// ------------------------------------ // ------------------------------------
@ -1003,18 +1003,12 @@ impl Animate for ComputedTransformOperation {
(&TransformOperation::Matrix(ref this), &TransformOperation::Matrix(ref other)) => { (&TransformOperation::Matrix(ref this), &TransformOperation::Matrix(ref other)) => {
Ok(TransformOperation::Matrix(this.animate(other, procedure)?)) Ok(TransformOperation::Matrix(this.animate(other, procedure)?))
}, },
(&TransformOperation::Skew(ref fx, None), &TransformOperation::Skew(ref tx, None)) => {
Ok(TransformOperation::Skew(fx.animate(tx, procedure)?, None))
},
( (
&TransformOperation::Skew(ref fx, ref fy), &TransformOperation::Skew(ref fx, ref fy),
&TransformOperation::Skew(ref tx, ref ty), &TransformOperation::Skew(ref tx, ref ty),
) => Ok(TransformOperation::Skew( ) => Ok(TransformOperation::Skew(
fx.animate(tx, procedure)?, fx.animate(tx, procedure)?,
Some( fy.animate(ty, procedure)?,
fy.unwrap_or(Angle::zero())
.animate(&ty.unwrap_or(Angle::zero()), procedure)?,
),
)), )),
(&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t)) => { (&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t)) => {
Ok(TransformOperation::SkewX(f.animate(t, procedure)?)) Ok(TransformOperation::SkewX(f.animate(t, procedure)?))
@ -1030,22 +1024,12 @@ impl Animate for ComputedTransformOperation {
fy.animate(ty, procedure)?, fy.animate(ty, procedure)?,
fz.animate(tz, procedure)?, fz.animate(tz, procedure)?,
)), )),
(
&TransformOperation::Translate(ref fx, None),
&TransformOperation::Translate(ref tx, None),
) => Ok(TransformOperation::Translate(
fx.animate(tx, procedure)?,
None,
)),
( (
&TransformOperation::Translate(ref fx, ref fy), &TransformOperation::Translate(ref fx, ref fy),
&TransformOperation::Translate(ref tx, ref ty), &TransformOperation::Translate(ref tx, ref ty),
) => Ok(TransformOperation::Translate( ) => Ok(TransformOperation::Translate(
fx.animate(tx, procedure)?, fx.animate(tx, procedure)?,
Some( fy.animate(ty, procedure)?,
fy.unwrap_or(LengthPercentage::zero())
.animate(&ty.unwrap_or(LengthPercentage::zero()), procedure)?,
),
)), )),
(&TransformOperation::TranslateX(ref f), &TransformOperation::TranslateX(ref t)) => { (&TransformOperation::TranslateX(ref f), &TransformOperation::TranslateX(ref t)) => {
Ok(TransformOperation::TranslateX(f.animate(t, procedure)?)) Ok(TransformOperation::TranslateX(f.animate(t, procedure)?))
@ -1073,22 +1057,12 @@ impl Animate for ComputedTransformOperation {
(&TransformOperation::ScaleZ(ref f), &TransformOperation::ScaleZ(ref t)) => Ok( (&TransformOperation::ScaleZ(ref f), &TransformOperation::ScaleZ(ref t)) => Ok(
TransformOperation::ScaleZ(animate_multiplicative_factor(*f, *t, procedure)?), TransformOperation::ScaleZ(animate_multiplicative_factor(*f, *t, procedure)?),
), ),
(&TransformOperation::Scale(ref f, None), &TransformOperation::Scale(ref t, None)) => {
Ok(TransformOperation::Scale(
animate_multiplicative_factor(*f, *t, procedure)?,
None,
))
},
( (
&TransformOperation::Scale(ref fx, ref fy), &TransformOperation::Scale(ref fx, ref fy),
&TransformOperation::Scale(ref tx, ref ty), &TransformOperation::Scale(ref tx, ref ty),
) => Ok(TransformOperation::Scale( ) => Ok(TransformOperation::Scale(
animate_multiplicative_factor(*fx, *tx, procedure)?, animate_multiplicative_factor(*fx, *tx, procedure)?,
Some(animate_multiplicative_factor( animate_multiplicative_factor(*fy, *ty, procedure)?,
fy.unwrap_or(*fx),
ty.unwrap_or(*tx),
procedure,
)?),
)), )),
( (
&TransformOperation::Rotate3D(fx, fy, fz, fa), &TransformOperation::Rotate3D(fx, fy, fz, fa),

View file

@ -6,7 +6,7 @@
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::CSSFloat; use crate::values::CSSFloat;
use num_traits::Zero; use crate::Zero;
use std::f64::consts::PI; use std::f64::consts::PI;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use std::{f32, f64}; use std::{f32, f64};

View file

@ -12,8 +12,9 @@ use crate::values::generics::border::BorderImageSlice as GenericBorderImageSlice
use crate::values::generics::border::BorderRadius as GenericBorderRadius; use crate::values::generics::border::BorderRadius as GenericBorderRadius;
use crate::values::generics::border::BorderSpacing as GenericBorderSpacing; use crate::values::generics::border::BorderSpacing as GenericBorderSpacing;
use crate::values::generics::rect::Rect; use crate::values::generics::rect::Rect;
use crate::values::generics::size::Size; use crate::values::generics::size::Size2D;
use crate::values::generics::NonNegative; use crate::values::generics::NonNegative;
use crate::Zero;
use app_units::Au; use app_units::Au;
pub use crate::values::specified::border::BorderImageRepeat; pub use crate::values::specified::border::BorderImageRepeat;
@ -59,7 +60,7 @@ impl BorderImageSlice {
impl BorderSpacing { impl BorderSpacing {
/// Returns `0 0`. /// Returns `0 0`.
pub fn zero() -> Self { pub fn zero() -> Self {
GenericBorderSpacing(Size::new( GenericBorderSpacing(Size2D::new(
NonNegativeLength::zero(), NonNegativeLength::zero(),
NonNegativeLength::zero(), NonNegativeLength::zero(),
)) ))
@ -75,29 +76,3 @@ impl BorderSpacing {
Au::from(*self.0.height()) Au::from(*self.0.height())
} }
} }
impl BorderCornerRadius {
/// Returns `0 0`.
pub fn zero() -> Self {
GenericBorderCornerRadius(Size::new(
NonNegativeLengthPercentage::zero(),
NonNegativeLengthPercentage::zero(),
))
}
}
impl BorderRadius {
/// Returns whether all the values are `0px`.
pub fn all_zero(&self) -> bool {
fn all(corner: &BorderCornerRadius) -> bool {
fn is_zero(l: &NonNegativeLengthPercentage) -> bool {
*l == NonNegativeLengthPercentage::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)
}
}

View file

@ -12,11 +12,11 @@ use crate::values::generics::length as generics;
use crate::values::generics::length::{ use crate::values::generics::length::{
GenericLengthOrNumber, MaxSize as GenericMaxSize, Size as GenericSize, GenericLengthOrNumber, MaxSize as GenericMaxSize, Size as GenericSize,
}; };
use crate::values::generics::transform::IsZeroLength;
use crate::values::generics::NonNegative; use crate::values::generics::NonNegative;
use crate::values::specified::length::ViewportPercentageLength; use crate::values::specified::length::ViewportPercentageLength;
use crate::values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength}; use crate::values::specified::length::{AbsoluteLength, FontBaseSize, FontRelativeLength};
use crate::values::{specified, Auto, CSSFloat, Either, Normal}; use crate::values::{specified, CSSFloat, Either, Normal};
use crate::Zero;
use app_units::Au; use app_units::Au;
use ordered_float::NotNan; use ordered_float::NotNan;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
@ -203,12 +203,7 @@ impl LengthPercentage {
return None; return None;
} }
if self.clamping_mode.clamp(self.percentage.0) != self.percentage.0 { Some(Percentage(self.clamping_mode.clamp(self.percentage.0)))
debug_assert!(self.was_calc);
return None;
}
Some(self.percentage)
} }
/// Convert the computed value into used value. /// Convert the computed value into used value.
@ -347,12 +342,6 @@ impl ToComputedValue for specified::CalcLengthPercentage {
} }
impl LengthPercentage { impl LengthPercentage {
#[inline]
#[allow(missing_docs)]
pub fn zero() -> LengthPercentage {
LengthPercentage::new(Length::new(0.), None)
}
/// 1px length value for SVG defaults /// 1px length value for SVG defaults
#[inline] #[inline]
pub fn one() -> LengthPercentage { pub fn one() -> LengthPercentage {
@ -433,14 +422,13 @@ impl ToComputedValue for specified::LengthPercentage {
} }
fn from_computed_value(computed: &LengthPercentage) -> Self { fn from_computed_value(computed: &LengthPercentage) -> Self {
let length = computed.unclamped_length();
if let Some(p) = computed.as_percentage() { if let Some(p) = computed.as_percentage() {
return specified::LengthPercentage::Percentage(p); return specified::LengthPercentage::Percentage(p);
} }
if !computed.has_percentage && computed.clamping_mode.clamp(length.px()) == length.px() { if !computed.has_percentage {
return specified::LengthPercentage::Length(ToComputedValue::from_computed_value( return specified::LengthPercentage::Length(ToComputedValue::from_computed_value(
&length, &computed.length(),
)); ));
} }
@ -448,9 +436,13 @@ impl ToComputedValue for specified::LengthPercentage {
} }
} }
impl IsZeroLength for LengthPercentage { impl Zero for LengthPercentage {
fn zero() -> Self {
LengthPercentage::new(Length::zero(), None)
}
#[inline] #[inline]
fn is_zero_length(&self) -> bool { fn is_zero(&self) -> bool {
self.is_definitely_zero() self.is_definitely_zero()
} }
} }
@ -459,12 +451,6 @@ impl IsZeroLength for LengthPercentage {
/// length-percentage or auto. /// length-percentage or auto.
macro_rules! computed_length_percentage_or_auto { macro_rules! computed_length_percentage_or_auto {
($inner:ty) => { ($inner:ty) => {
/// Returns the `0` value.
#[inline]
pub fn zero() -> Self {
generics::LengthPercentageOrAuto::LengthPercentage(<$inner>::zero())
}
/// Returns the used value. /// Returns the used value.
#[inline] #[inline]
pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> { pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
@ -553,12 +539,6 @@ impl From<Au> for LengthPercentage {
} }
impl NonNegativeLengthPercentage { impl NonNegativeLengthPercentage {
/// Get zero value.
#[inline]
pub fn zero() -> Self {
NonNegative(LengthPercentage::zero())
}
/// Returns true if the computed value is absolute 0 or 0%. /// Returns true if the computed value is absolute 0 or 0%.
#[inline] #[inline]
pub fn is_definitely_zero(&self) -> bool { pub fn is_definitely_zero(&self) -> bool {
@ -662,12 +642,16 @@ impl CSSPixelLength {
pub fn clamp_to_non_negative(self) -> Self { pub fn clamp_to_non_negative(self) -> Self {
CSSPixelLength::new(self.0.max(0.)) CSSPixelLength::new(self.0.max(0.))
} }
}
/// Zero value impl Zero for CSSPixelLength {
#[inline] fn zero() -> Self {
pub fn zero() -> Self {
CSSPixelLength::new(0.) CSSPixelLength::new(0.)
} }
fn is_zero(&self) -> bool {
self.px() == 0.
}
} }
impl ToCss for CSSPixelLength { impl ToCss for CSSPixelLength {
@ -717,7 +701,10 @@ impl From<Au> for CSSPixelLength {
pub type Length = CSSPixelLength; pub type Length = CSSPixelLength;
/// Either a computed `<length>` or the `auto` keyword. /// Either a computed `<length>` or the `auto` keyword.
pub type LengthOrAuto = Either<Length, Auto>; pub type LengthOrAuto = generics::GenericLengthPercentageOrAuto<Length>;
/// Either a non-negative `<length>` or the `auto` keyword.
pub type NonNegativeLengthOrAuto = generics::GenericLengthPercentageOrAuto<NonNegativeLength>;
/// Either a computed `<length>` or a `<number>` value. /// Either a computed `<length>` or a `<number>` value.
pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>; pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
@ -749,12 +736,6 @@ impl NonNegativeLength {
NonNegative(Length::new(px.max(0.))) NonNegative(Length::new(px.max(0.)))
} }
/// Return a zero value.
#[inline]
pub fn zero() -> Self {
Self::new(0.)
}
/// Return the pixel value of |NonNegativeLength|. /// Return the pixel value of |NonNegativeLength|.
#[inline] #[inline]
pub fn px(&self) -> CSSFloat { pub fn px(&self) -> CSSFloat {
@ -801,9 +782,6 @@ impl From<NonNegativeLength> for Au {
} }
} }
/// Either a computed NonNegativeLength or the `auto` keyword.
pub type NonNegativeLengthOrAuto = Either<NonNegativeLength, Auto>;
/// Either a computed NonNegativeLength or the `normal` keyword. /// Either a computed NonNegativeLength or the `normal` keyword.
pub type NonNegativeLengthOrNormal = Either<NonNegativeLength, Normal>; pub type NonNegativeLengthOrNormal = Either<NonNegativeLength, Normal>;

View file

@ -10,7 +10,7 @@ use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent
use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth}; use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize}; use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
use super::generics::transform::IsParallelTo; use super::generics::transform::IsParallelTo;
use super::generics::{GreaterThanOrEqualToOne, NonNegative}; use super::generics::{self, GreaterThanOrEqualToOne, NonNegative};
use super::specified; use super::specified;
use super::{CSSFloat, CSSInteger}; use super::{CSSFloat, CSSInteger};
use crate::context::QuirksMode; use crate::context::QuirksMode;
@ -27,8 +27,6 @@ use euclid::Size2D;
use std::cell::RefCell; use std::cell::RefCell;
use std::cmp; use std::cmp;
use std::f32; use std::f32;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub use self::align::{AlignContent, AlignItems, JustifyContent, JustifyItems, SelfAlignment}; pub use self::align::{AlignContent, AlignItems, JustifyContent, JustifyItems, SelfAlignment};
@ -62,7 +60,7 @@ pub use self::gecko::ScrollSnapPoint;
pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect}; pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect};
pub use self::length::{CSSPixelLength, ExtremumLength, NonNegativeLength}; pub use self::length::{CSSPixelLength, ExtremumLength, NonNegativeLength};
pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber}; pub use self::length::{Length, LengthOrNumber, LengthPercentage, NonNegativeLengthOrNumber};
pub use self::length::{LengthPercentageOrAuto, MaxSize, Size}; pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size};
pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto}; pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub use self::list::ListStyleType; pub use self::list::ListStyleType;
@ -78,7 +76,7 @@ pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
pub use self::table::XSpan; pub use self::table::XSpan;
pub use self::text::{InitialLetter, LetterSpacing, LineHeight}; pub use self::text::{InitialLetter, LetterSpacing, LineHeight};
pub use self::text::{OverflowWrap, TextOverflow, WordSpacing}; pub use self::text::{OverflowWrap, TextOverflow, WordBreak, WordSpacing};
pub use self::text::{TextAlign, TextEmphasisPosition, TextEmphasisStyle}; pub use self::text::{TextAlign, TextEmphasisPosition, TextEmphasisStyle};
pub use self::time::Time; pub use self::time::Time;
pub use self::transform::{Rotate, Scale, Transform, TransformOperation}; pub use self::transform::{Rotate, Scale, Transform, TransformOperation};
@ -637,52 +635,8 @@ impl From<CSSInteger> for PositiveInteger {
} }
} }
#[allow(missing_docs)] /// rect(...)
#[cfg_attr(feature = "servo", derive(MallocSizeOf))] pub type ClipRect = generics::ClipRect<LengthOrAuto>;
#[derive(Clone, ComputeSquaredDistance, Copy, Debug, PartialEq)]
/// A computed cliprect for clip and image-region
pub struct ClipRect {
pub top: Option<Length>,
pub right: Option<Length>,
pub bottom: Option<Length>,
pub left: Option<Length>,
}
impl ToCss for ClipRect {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
dest.write_str("rect(")?;
if let Some(top) = self.top {
top.to_css(dest)?;
dest.write_str(", ")?;
} else {
dest.write_str("auto, ")?;
}
if let Some(right) = self.right {
right.to_css(dest)?;
dest.write_str(", ")?;
} else {
dest.write_str("auto, ")?;
}
if let Some(bottom) = self.bottom {
bottom.to_css(dest)?;
dest.write_str(", ")?;
} else {
dest.write_str("auto, ")?;
}
if let Some(left) = self.left {
left.to_css(dest)?;
} else {
dest.write_str("auto")?;
}
dest.write_str(")")
}
}
/// rect(...) | auto /// rect(...) | auto
pub type ClipRectOrAuto = Either<ClipRect, Auto>; pub type ClipRectOrAuto = Either<ClipRect, Auto>;

View file

@ -7,6 +7,7 @@
use crate::values::animated::ToAnimatedValue; use crate::values::animated::ToAnimatedValue;
use crate::values::generics::NonNegative; use crate::values::generics::NonNegative;
use crate::values::{serialize_percentage, CSSFloat}; use crate::values::{serialize_percentage, CSSFloat};
use crate::Zero;
use std::fmt; use std::fmt;
use style_traits::{CssWriter, ToCss}; use style_traits::{CssWriter, ToCss};
@ -31,12 +32,6 @@ use style_traits::{CssWriter, ToCss};
pub struct Percentage(pub CSSFloat); pub struct Percentage(pub CSSFloat);
impl Percentage { impl Percentage {
/// 0%
#[inline]
pub fn zero() -> Self {
Percentage(0.)
}
/// 100% /// 100%
#[inline] #[inline]
pub fn hundred() -> Self { pub fn hundred() -> Self {
@ -56,6 +51,16 @@ impl Percentage {
} }
} }
impl Zero for Percentage {
fn zero() -> Self {
Percentage(0.)
}
fn is_zero(&self) -> bool {
self.0 == 0.
}
}
impl ToCss for Percentage { impl ToCss for Percentage {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where where
@ -69,12 +74,6 @@ impl ToCss for Percentage {
pub type NonNegativePercentage = NonNegative<Percentage>; pub type NonNegativePercentage = NonNegative<Percentage>;
impl NonNegativePercentage { impl NonNegativePercentage {
/// 0%
#[inline]
pub fn zero() -> Self {
NonNegative(Percentage::zero())
}
/// 100% /// 100%
#[inline] #[inline]
pub fn hundred() -> Self { pub fn hundred() -> Self {

View file

@ -11,6 +11,7 @@ use crate::values::computed::{Integer, LengthPercentage, Percentage};
use crate::values::generics::position::Position as GenericPosition; use crate::values::generics::position::Position as GenericPosition;
use crate::values::generics::position::ZIndex as GenericZIndex; use crate::values::generics::position::ZIndex as GenericZIndex;
pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas}; pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas};
use crate::Zero;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss}; use style_traits::{CssWriter, ToCss};

View file

@ -6,10 +6,10 @@
use crate::values::computed::color::Color; use crate::values::computed::color::Color;
use crate::values::computed::url::ComputedUrl; use crate::values::computed::url::ComputedUrl;
use crate::values::computed::{LengthPercentage, NonNegativeLengthPercentage}; use crate::values::computed::{LengthPercentage, NonNegativeLengthPercentage, Opacity};
use crate::values::computed::{NonNegativeNumber, Number, Opacity};
use crate::values::generics::svg as generic; use crate::values::generics::svg as generic;
use crate::values::RGBA; use crate::values::RGBA;
use crate::Zero;
pub use crate::values::specified::SVGPaintOrder; pub use crate::values::specified::SVGPaintOrder;
@ -40,58 +40,29 @@ impl SVGPaint {
} }
} }
/// A value of <length> | <percentage> | <number> for stroke-dashoffset.
/// <https://www.w3.org/TR/SVG11/painting.html#StrokeProperties>
pub type SvgLengthPercentageOrNumber =
generic::SvgLengthPercentageOrNumber<LengthPercentage, Number>;
/// <length> | <percentage> | <number> | context-value /// <length> | <percentage> | <number> | context-value
pub type SVGLength = generic::SVGLength<SvgLengthPercentageOrNumber>; pub type SVGLength = generic::SVGLength<LengthPercentage>;
impl SVGLength { impl SVGLength {
/// `0px` /// `0px`
pub fn zero() -> Self { pub fn zero() -> Self {
generic::SVGLength::Length(generic::SvgLengthPercentageOrNumber::LengthPercentage( generic::SVGLength::LengthPercentage(LengthPercentage::zero())
LengthPercentage::zero(),
))
}
}
/// A value of <length> | <percentage> | <number> for stroke-width/stroke-dasharray.
/// <https://www.w3.org/TR/SVG11/painting.html#StrokeProperties>
pub type NonNegativeSvgLengthPercentageOrNumber =
generic::SvgLengthPercentageOrNumber<NonNegativeLengthPercentage, NonNegativeNumber>;
// FIXME(emilio): This is really hacky, and can go away with a bit of work on
// the clone_stroke_width code in gecko.mako.rs.
impl Into<NonNegativeSvgLengthPercentageOrNumber> for SvgLengthPercentageOrNumber {
fn into(self) -> NonNegativeSvgLengthPercentageOrNumber {
match self {
generic::SvgLengthPercentageOrNumber::LengthPercentage(lop) => {
generic::SvgLengthPercentageOrNumber::LengthPercentage(lop.into())
},
generic::SvgLengthPercentageOrNumber::Number(num) => {
generic::SvgLengthPercentageOrNumber::Number(num.into())
},
}
} }
} }
/// An non-negative wrapper of SVGLength. /// An non-negative wrapper of SVGLength.
pub type SVGWidth = generic::SVGLength<NonNegativeSvgLengthPercentageOrNumber>; pub type SVGWidth = generic::SVGLength<NonNegativeLengthPercentage>;
impl SVGWidth { impl SVGWidth {
/// `1px`. /// `1px`.
pub fn one() -> Self { pub fn one() -> Self {
use crate::values::generics::NonNegative; use crate::values::generics::NonNegative;
generic::SVGLength::Length(generic::SvgLengthPercentageOrNumber::LengthPercentage( generic::SVGLength::LengthPercentage(NonNegative(LengthPercentage::one()))
NonNegative(LengthPercentage::one()),
))
} }
} }
/// [ <length> | <percentage> | <number> ]# | context-value /// [ <length> | <percentage> | <number> ]# | context-value
pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<NonNegativeSvgLengthPercentageOrNumber>; pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<NonNegativeLengthPercentage>;
impl Default for SVGStrokeDashArray { impl Default for SVGStrokeDashArray {
fn default() -> Self { fn default() -> Self {

View file

@ -7,28 +7,97 @@
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
use crate::properties::StyleBuilder; use crate::properties::StyleBuilder;
use crate::values::computed::length::{Length, LengthPercentage}; use crate::values::computed::length::{Length, LengthPercentage};
use crate::values::computed::{NonNegativeLength, NonNegativeNumber}; use crate::values::computed::{Context, NonNegativeLength, NonNegativeNumber, ToComputedValue};
use crate::values::generics::text::InitialLetter as GenericInitialLetter; use crate::values::generics::text::InitialLetter as GenericInitialLetter;
use crate::values::generics::text::LineHeight as GenericLineHeight; use crate::values::generics::text::LineHeight as GenericLineHeight;
use crate::values::generics::text::Spacing; use crate::values::generics::text::Spacing;
use crate::values::specified::text::TextOverflowSide; use crate::values::specified::text::{self as specified, TextOverflowSide};
use crate::values::specified::text::{TextEmphasisFillMode, TextEmphasisShapeKeyword}; use crate::values::specified::text::{TextEmphasisFillMode, TextEmphasisShapeKeyword};
use crate::values::{CSSFloat, CSSInteger}; use crate::values::{CSSFloat, CSSInteger};
use crate::Zero;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss}; use style_traits::{CssWriter, ToCss};
pub use crate::values::specified::OverflowWrap;
pub use crate::values::specified::TextAlignKeyword as TextAlign; pub use crate::values::specified::TextAlignKeyword as TextAlign;
pub use crate::values::specified::TextEmphasisPosition; pub use crate::values::specified::TextEmphasisPosition;
pub use crate::values::specified::{OverflowWrap, WordBreak};
/// A computed value for the `initial-letter` property. /// A computed value for the `initial-letter` property.
pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>; pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;
/// A computed value for the `letter-spacing` property. /// A computed value for the `letter-spacing` property.
pub type LetterSpacing = Spacing<Length>; #[repr(transparent)]
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
MallocSizeOf,
PartialEq,
ToAnimatedValue,
ToAnimatedZero,
)]
pub struct LetterSpacing(pub Length);
impl LetterSpacing {
/// Return the `normal` computed value, which is just zero.
#[inline]
pub fn normal() -> Self {
LetterSpacing(Length::zero())
}
}
impl ToCss for LetterSpacing {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
// https://drafts.csswg.org/css-text/#propdef-letter-spacing
//
// For legacy reasons, a computed letter-spacing of zero yields a
// resolved value (getComputedStyle() return value) of normal.
if self.0.is_zero() {
return dest.write_str("normal");
}
self.0.to_css(dest)
}
}
impl ToComputedValue for specified::LetterSpacing {
type ComputedValue = LetterSpacing;
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match *self {
Spacing::Normal => LetterSpacing(Length::zero()),
Spacing::Value(ref v) => LetterSpacing(v.to_computed_value(context)),
}
}
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
if computed.0.is_zero() {
return Spacing::Normal;
}
Spacing::Value(ToComputedValue::from_computed_value(&computed.0))
}
}
/// A computed value for the `word-spacing` property. /// A computed value for the `word-spacing` property.
pub type WordSpacing = Spacing<LengthPercentage>; pub type WordSpacing = LengthPercentage;
impl ToComputedValue for specified::WordSpacing {
type ComputedValue = WordSpacing;
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
match *self {
Spacing::Normal => LengthPercentage::zero(),
Spacing::Value(ref v) => v.to_computed_value(context),
}
}
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
Spacing::Value(ToComputedValue::from_computed_value(computed))
}
}
/// A computed value for the `line-height` property. /// A computed value for the `line-height` property.
pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLength>; pub type LineHeight = GenericLineHeight<NonNegativeNumber, NonNegativeLength>;

View file

@ -9,8 +9,8 @@ use crate::values::animated::transform::{Perspective, Scale3D, Translate3D};
use crate::values::animated::ToAnimatedZero; use crate::values::animated::ToAnimatedZero;
use crate::values::computed::{Angle, Integer, Length, LengthPercentage, Number, Percentage}; use crate::values::computed::{Angle, Integer, Length, LengthPercentage, Number, Percentage};
use crate::values::generics::transform as generic; use crate::values::generics::transform as generic;
use crate::Zero;
use euclid::{Transform3D, Vector3D}; use euclid::{Transform3D, Vector3D};
use num_traits::Zero;
pub use crate::values::generics::transform::TransformStyle; pub use crate::values::generics::transform::TransformStyle;
@ -371,15 +371,14 @@ impl TransformOperation {
pub fn to_translate_3d(&self) -> Self { pub fn to_translate_3d(&self) -> Self {
match *self { match *self {
generic::TransformOperation::Translate3D(..) => self.clone(), generic::TransformOperation::Translate3D(..) => self.clone(),
generic::TransformOperation::TranslateX(ref x) | generic::TransformOperation::TranslateX(ref x) => {
generic::TransformOperation::Translate(ref x, None) => {
generic::TransformOperation::Translate3D( generic::TransformOperation::Translate3D(
x.clone(), x.clone(),
LengthPercentage::zero(), LengthPercentage::zero(),
Length::zero(), Length::zero(),
) )
}, },
generic::TransformOperation::Translate(ref x, Some(ref y)) => { generic::TransformOperation::Translate(ref x, ref y) => {
generic::TransformOperation::Translate3D(x.clone(), y.clone(), Length::zero()) generic::TransformOperation::Translate3D(x.clone(), y.clone(), Length::zero())
}, },
generic::TransformOperation::TranslateY(ref y) => { generic::TransformOperation::TranslateY(ref y) => {
@ -426,10 +425,7 @@ impl TransformOperation {
pub fn to_scale_3d(&self) -> Self { pub fn to_scale_3d(&self) -> Self {
match *self { match *self {
generic::TransformOperation::Scale3D(..) => self.clone(), generic::TransformOperation::Scale3D(..) => self.clone(),
generic::TransformOperation::Scale(s, None) => { generic::TransformOperation::Scale(x, y) => {
generic::TransformOperation::Scale3D(s, s, 1.)
},
generic::TransformOperation::Scale(x, Some(y)) => {
generic::TransformOperation::Scale3D(x, y, 1.) generic::TransformOperation::Scale3D(x, y, 1.)
}, },
generic::TransformOperation::ScaleX(x) => { generic::TransformOperation::ScaleX(x) => {
@ -494,7 +490,7 @@ impl ToAnimatedZero for TransformOperation {
Ok(generic::TransformOperation::Scale3D(1.0, 1.0, 1.0)) Ok(generic::TransformOperation::Scale3D(1.0, 1.0, 1.0))
}, },
generic::TransformOperation::Scale(_, _) => { generic::TransformOperation::Scale(_, _) => {
Ok(generic::TransformOperation::Scale(1.0, Some(1.0))) Ok(generic::TransformOperation::Scale(1.0, 1.0))
}, },
generic::TransformOperation::ScaleX(..) => Ok(generic::TransformOperation::ScaleX(1.0)), generic::TransformOperation::ScaleX(..) => Ok(generic::TransformOperation::ScaleX(1.0)),
generic::TransformOperation::ScaleY(..) => Ok(generic::TransformOperation::ScaleY(1.0)), generic::TransformOperation::ScaleY(..) => Ok(generic::TransformOperation::ScaleY(1.0)),
@ -585,7 +581,7 @@ impl Translate {
match *self { match *self {
generic::Translate::None => None, generic::Translate::None => None,
generic::Translate::Translate(tx, ty) => { generic::Translate::Translate(tx, ty) => {
Some(generic::TransformOperation::Translate(tx, Some(ty))) Some(generic::TransformOperation::Translate(tx, ty))
}, },
generic::Translate::Translate3D(tx, ty, tz) => { generic::Translate::Translate3D(tx, ty, tz) => {
Some(generic::TransformOperation::Translate3D(tx, ty, tz)) Some(generic::TransformOperation::Translate3D(tx, ty, tz))
@ -596,9 +592,7 @@ impl Translate {
/// Convert Translate to TransformOperation. /// Convert Translate to TransformOperation.
pub fn from_transform_operation(operation: &TransformOperation) -> Translate { pub fn from_transform_operation(operation: &TransformOperation) -> Translate {
match *operation { match *operation {
generic::TransformOperation::Translate(tx, Some(ty)) => { generic::TransformOperation::Translate(tx, ty) => generic::Translate::Translate(tx, ty),
generic::Translate::Translate(tx, ty)
},
generic::TransformOperation::Translate3D(tx, ty, tz) => { generic::TransformOperation::Translate3D(tx, ty, tz) => {
generic::Translate::Translate3D(tx, ty, tz) generic::Translate::Translate3D(tx, ty, tz)
}, },
@ -615,7 +609,7 @@ impl Scale {
pub fn to_transform_operation(&self) -> Option<TransformOperation> { pub fn to_transform_operation(&self) -> Option<TransformOperation> {
match *self { match *self {
generic::Scale::None => None, generic::Scale::None => None,
generic::Scale::Scale(sx, sy) => Some(generic::TransformOperation::Scale(sx, Some(sy))), generic::Scale::Scale(sx, sy) => Some(generic::TransformOperation::Scale(sx, sy)),
generic::Scale::Scale3D(sx, sy, sz) => { generic::Scale::Scale3D(sx, sy, sz) => {
Some(generic::TransformOperation::Scale3D(sx, sy, sz)) Some(generic::TransformOperation::Scale3D(sx, sy, sz))
}, },
@ -625,8 +619,7 @@ impl Scale {
/// Convert Scale to TransformOperation. /// Convert Scale to TransformOperation.
pub fn from_transform_operation(operation: &TransformOperation) -> Scale { pub fn from_transform_operation(operation: &TransformOperation) -> Scale {
match *operation { match *operation {
generic::TransformOperation::Scale(sx, Some(sy)) => generic::Scale::Scale(sx, sy), generic::TransformOperation::Scale(sx, sy) => generic::Scale::Scale(sx, sy),
generic::TransformOperation::Scale(sx, None) => generic::Scale::Scale(sx, sx),
generic::TransformOperation::Scale3D(sx, sy, sz) => generic::Scale::Scale3D(sx, sy, sz), generic::TransformOperation::Scale3D(sx, sy, sz) => generic::Scale::Scale3D(sx, sy, sz),
_ => unreachable!("Found unexpected value for scale"), _ => unreachable!("Found unexpected value for scale"),
} }

View file

@ -5,8 +5,13 @@
//! Generic types for CSS values related to backgrounds. //! Generic types for CSS values related to backgrounds.
use crate::values::generics::length::{GenericLengthPercentageOrAuto, LengthPercentageOrAuto}; use crate::values::generics::length::{GenericLengthPercentageOrAuto, LengthPercentageOrAuto};
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss}; fn width_and_height_are_auto<L>(
width: &LengthPercentageOrAuto<L>,
height: &LengthPercentageOrAuto<L>,
) -> bool {
width.is_auto() && height.is_auto()
}
/// A generic value for the `background-size` property. /// A generic value for the `background-size` property.
#[derive( #[derive(
@ -21,6 +26,7 @@ use style_traits::{CssWriter, ToCss};
ToAnimatedValue, ToAnimatedValue,
ToAnimatedZero, ToAnimatedZero,
ToComputedValue, ToComputedValue,
ToCss,
)] )]
#[repr(C, u8)] #[repr(C, u8)]
pub enum GenericBackgroundSize<LengthPercent> { pub enum GenericBackgroundSize<LengthPercent> {
@ -29,6 +35,10 @@ pub enum GenericBackgroundSize<LengthPercent> {
/// Explicit width. /// Explicit width.
width: GenericLengthPercentageOrAuto<LengthPercent>, width: GenericLengthPercentageOrAuto<LengthPercent>,
/// Explicit height. /// Explicit height.
/// NOTE(emilio): We should probably simplify all these in case `width`
/// and `height` are the same, but all other browsers agree on only
/// special-casing `auto`.
#[css(contextual_skip_if = "width_and_height_are_auto")]
height: GenericLengthPercentageOrAuto<LengthPercent>, height: GenericLengthPercentageOrAuto<LengthPercent>,
}, },
/// `cover` /// `cover`
@ -41,32 +51,6 @@ pub enum GenericBackgroundSize<LengthPercent> {
pub use self::GenericBackgroundSize as BackgroundSize; pub use self::GenericBackgroundSize as BackgroundSize;
impl<LengthPercentage> ToCss for BackgroundSize<LengthPercentage>
where
LengthPercentage: ToCss,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
match self {
BackgroundSize::ExplicitSize { width, height } => {
width.to_css(dest)?;
// NOTE(emilio): We should probably simplify all these in case
// `width == `height`, but all other browsers agree on only
// special-casing `auto`.
if !width.is_auto() || !height.is_auto() {
dest.write_str(" ")?;
height.to_css(dest)?;
}
Ok(())
},
BackgroundSize::Cover => dest.write_str("cover"),
BackgroundSize::Contain => dest.write_str("contain"),
}
}
}
impl<LengthPercentage> BackgroundSize<LengthPercentage> { impl<LengthPercentage> BackgroundSize<LengthPercentage> {
/// Returns `auto auto`. /// Returns `auto auto`.
pub fn auto() -> Self { pub fn auto() -> Self {

View file

@ -11,6 +11,7 @@ use crate::values::generics::border::BorderRadius;
use crate::values::generics::position::Position; use crate::values::generics::position::Position;
use crate::values::generics::rect::Rect; use crate::values::generics::rect::Rect;
use crate::values::specified::SVGPathData; use crate::values::specified::SVGPathData;
use crate::Zero;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss}; use style_traits::{CssWriter, ToCss};
@ -127,7 +128,7 @@ pub enum BasicShape<H, V, LengthPercentage, NonNegativeLengthPercentage> {
)] )]
pub struct InsetRect<LengthPercentage, NonNegativeLengthPercentage> { pub struct InsetRect<LengthPercentage, NonNegativeLengthPercentage> {
pub rect: Rect<LengthPercentage>, pub rect: Rect<LengthPercentage>,
pub round: Option<BorderRadius<NonNegativeLengthPercentage>>, pub round: BorderRadius<NonNegativeLengthPercentage>,
} }
/// <https://drafts.csswg.org/css-shapes/#funcdef-circle> /// <https://drafts.csswg.org/css-shapes/#funcdef-circle>
@ -311,7 +312,7 @@ impl<B, T, U> ToAnimatedZero for ShapeSource<B, T, U> {
impl<Length, NonNegativeLength> ToCss for InsetRect<Length, NonNegativeLength> impl<Length, NonNegativeLength> ToCss for InsetRect<Length, NonNegativeLength>
where where
Length: ToCss + PartialEq, Length: ToCss + PartialEq,
NonNegativeLength: ToCss + PartialEq, NonNegativeLength: ToCss + PartialEq + Zero,
{ {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where where
@ -319,9 +320,9 @@ where
{ {
dest.write_str("inset(")?; dest.write_str("inset(")?;
self.rect.to_css(dest)?; self.rect.to_css(dest)?;
if let Some(ref radius) = self.round { if !self.round.is_zero() {
dest.write_str(" round ")?; dest.write_str(" round ")?;
radius.to_css(dest)?; self.round.to_css(dest)?;
} }
dest.write_str(")") dest.write_str(")")
} }

View file

@ -5,7 +5,8 @@
//! Generic types for CSS values related to borders. //! Generic types for CSS values related to borders.
use crate::values::generics::rect::Rect; use crate::values::generics::rect::Rect;
use crate::values::generics::size::Size; use crate::values::generics::size::Size2D;
use crate::Zero;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss}; use style_traits::{CssWriter, ToCss};
@ -53,12 +54,25 @@ pub use self::GenericBorderImageSlice as BorderImageSlice;
ToComputedValue, ToComputedValue,
ToCss, ToCss,
)] )]
pub struct BorderCornerRadius<L>(#[css(field_bound)] pub Size<L>); #[repr(C)]
pub struct GenericBorderCornerRadius<L>(#[css(field_bound)] pub Size2D<L>);
pub use self::GenericBorderCornerRadius as BorderCornerRadius;
impl<L> BorderCornerRadius<L> { impl<L> BorderCornerRadius<L> {
/// Trivially create a `BorderCornerRadius`. /// Trivially create a `BorderCornerRadius`.
pub fn new(w: L, h: L) -> Self { pub fn new(w: L, h: L) -> Self {
BorderCornerRadius(Size::new(w, h)) BorderCornerRadius(Size2D::new(w, h))
}
}
impl<L: Zero> Zero for BorderCornerRadius<L> {
fn zero() -> Self {
BorderCornerRadius(Size2D::zero())
}
fn is_zero(&self) -> bool {
self.0.is_zero()
} }
} }
@ -77,12 +91,13 @@ impl<L> BorderCornerRadius<L> {
ToComputedValue, ToComputedValue,
ToCss, ToCss,
)] )]
pub struct BorderSpacing<L>(#[css(field_bound)] pub Size<L>); #[repr(transparent)]
pub struct BorderSpacing<L>(#[css(field_bound)] pub Size2D<L>);
impl<L> BorderSpacing<L> { impl<L> BorderSpacing<L> {
/// Trivially create a `BorderCornerRadius`. /// Trivially create a `BorderCornerRadius`.
pub fn new(w: L, h: L) -> Self { pub fn new(w: L, h: L) -> Self {
BorderSpacing(Size::new(w, h)) BorderSpacing(Size2D::new(w, h))
} }
} }
@ -101,17 +116,20 @@ impl<L> BorderSpacing<L> {
ToAnimatedValue, ToAnimatedValue,
ToComputedValue, ToComputedValue,
)] )]
pub struct BorderRadius<LengthPercentage> { #[repr(C)]
pub struct GenericBorderRadius<LengthPercentage> {
/// The top left radius. /// The top left radius.
pub top_left: BorderCornerRadius<LengthPercentage>, pub top_left: GenericBorderCornerRadius<LengthPercentage>,
/// The top right radius. /// The top right radius.
pub top_right: BorderCornerRadius<LengthPercentage>, pub top_right: GenericBorderCornerRadius<LengthPercentage>,
/// The bottom right radius. /// The bottom right radius.
pub bottom_right: BorderCornerRadius<LengthPercentage>, pub bottom_right: GenericBorderCornerRadius<LengthPercentage>,
/// The bottom left radius. /// The bottom left radius.
pub bottom_left: BorderCornerRadius<LengthPercentage>, pub bottom_left: GenericBorderCornerRadius<LengthPercentage>,
} }
pub use self::GenericBorderRadius as BorderRadius;
impl<L> BorderRadius<L> { impl<L> BorderRadius<L> {
/// Returns a new `BorderRadius<L>`. /// Returns a new `BorderRadius<L>`.
#[inline] #[inline]
@ -128,12 +146,7 @@ impl<L> BorderRadius<L> {
bottom_left: bl, bottom_left: bl,
} }
} }
}
impl<L> BorderRadius<L>
where
L: PartialEq + ToCss,
{
/// Serialises two given rects following the syntax of the `border-radius`` /// Serialises two given rects following the syntax of the `border-radius``
/// property. /// property.
pub fn serialize_rects<W>( pub fn serialize_rects<W>(
@ -142,14 +155,11 @@ where
dest: &mut CssWriter<W>, dest: &mut CssWriter<W>,
) -> fmt::Result ) -> fmt::Result
where where
L: PartialEq + ToCss,
W: Write, W: Write,
{ {
widths.to_css(dest)?; widths.to_css(dest)?;
if widths.0 != heights.0 || if widths != heights {
widths.1 != heights.1 ||
widths.2 != heights.2 ||
widths.3 != heights.3
{
dest.write_str(" / ")?; dest.write_str(" / ")?;
heights.to_css(dest)?; heights.to_css(dest)?;
} }
@ -157,6 +167,24 @@ where
} }
} }
impl<L: Zero> Zero for BorderRadius<L> {
fn zero() -> Self {
Self::new(
BorderCornerRadius::<L>::zero(),
BorderCornerRadius::<L>::zero(),
BorderCornerRadius::<L>::zero(),
BorderCornerRadius::<L>::zero(),
)
}
fn is_zero(&self) -> bool {
self.top_left.is_zero() &&
self.top_right.is_zero() &&
self.bottom_right.is_zero() &&
self.bottom_left.is_zero()
}
}
impl<L> ToCss for BorderRadius<L> impl<L> ToCss for BorderRadius<L>
where where
L: PartialEq + ToCss, L: PartialEq + ToCss,
@ -172,8 +200,8 @@ where
bottom_left: BorderCornerRadius(ref bl), bottom_left: BorderCornerRadius(ref bl),
} = *self; } = *self;
let widths = Rect::new(&tl.0.width, &tr.0.width, &br.0.width, &bl.0.width); let widths = Rect::new(&tl.width, &tr.width, &br.width, &bl.width);
let heights = Rect::new(&tl.0.height, &tr.0.height, &br.0.height, &bl.0.height); let heights = Rect::new(&tl.height, &tr.height, &br.height, &bl.height);
Self::serialize_rects(widths, heights, dest) Self::serialize_rects(widths, heights, dest)
} }

View file

@ -7,8 +7,8 @@
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use crate::values::computed::ExtremumLength; use crate::values::computed::ExtremumLength;
use crate::Zero;
use cssparser::Parser; use cssparser::Parser;
use num_traits::Zero;
use style_traits::ParseError; use style_traits::ParseError;
/// A `<length-percentage> | auto` value. /// A `<length-percentage> | auto` value.
@ -67,6 +67,19 @@ impl<LengthPercentage> LengthPercentageOrAuto<LengthPercentage> {
} }
} }
impl<LengthPercentage: Zero> Zero for LengthPercentageOrAuto<LengthPercentage> {
fn zero() -> Self {
LengthPercentageOrAuto::LengthPercentage(Zero::zero())
}
fn is_zero(&self) -> bool {
match *self {
LengthPercentageOrAuto::LengthPercentage(ref l) => l.is_zero(),
LengthPercentageOrAuto::Auto => false,
}
}
}
impl<LengthPercentage: Parse> Parse for LengthPercentageOrAuto<LengthPercentage> { impl<LengthPercentage: Parse> Parse for LengthPercentageOrAuto<LengthPercentage> {
fn parse<'i, 't>( fn parse<'i, 't>(
context: &ParserContext, context: &ParserContext,
@ -186,12 +199,15 @@ pub enum GenericLengthOrNumber<L, N> {
pub use self::GenericLengthOrNumber as LengthOrNumber; pub use self::GenericLengthOrNumber as LengthOrNumber;
impl<L, N> LengthOrNumber<L, N> { impl<L, N: Zero> Zero for LengthOrNumber<L, N> {
/// Returns `0`. fn zero() -> Self {
pub fn zero() -> Self LengthOrNumber::Number(Zero::zero())
where }
N: Zero,
{ fn is_zero(&self) -> bool {
LengthOrNumber::Number(num_traits::Zero::zero()) match *self {
LengthOrNumber::Number(ref n) => n.is_zero(),
LengthOrNumber::Length(..) => false,
}
} }
} }

View file

@ -8,8 +8,8 @@
use super::CustomIdent; use super::CustomIdent;
use crate::counter_style::{parse_counter_style_name, Symbols}; use crate::counter_style::{parse_counter_style_name, Symbols};
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
use crate::Zero;
use cssparser::Parser; use cssparser::Parser;
use num_traits::Zero;
use std::ops::Add; use std::ops::Add;
use style_traits::{KeywordsCollectFn, ParseError}; use style_traits::{KeywordsCollectFn, ParseError};
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind};
@ -212,3 +212,26 @@ impl<T: Zero> Zero for NonNegative<T> {
ToCss, ToCss,
)] )]
pub struct GreaterThanOrEqualToOne<T>(pub T); pub struct GreaterThanOrEqualToOne<T>(pub T);
/// A clip rect for clip and image-region
#[allow(missing_docs)]
#[derive(
Clone,
ComputeSquaredDistance,
Copy,
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
)]
#[css(function = "rect", comma)]
pub struct ClipRect<LengthOrAuto> {
pub top: LengthOrAuto,
pub right: LengthOrAuto,
pub bottom: LengthOrAuto,
pub left: LengthOrAuto,
}

View file

@ -5,11 +5,10 @@
//! Generic type for CSS properties that are composed by two dimensions. //! Generic type for CSS properties that are composed by two dimensions.
use crate::parser::ParserContext; use crate::parser::ParserContext;
use crate::values::animated::ToAnimatedValue; use crate::Zero;
use cssparser::Parser; use cssparser::Parser;
use euclid::Size2D;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, ToCss}; use style_traits::{CssWriter, ParseError, ToCss};
/// A generic size, for `border-*-radius` longhand properties, or /// A generic size, for `border-*-radius` longhand properties, or
/// `border-spacing`. /// `border-spacing`.
@ -21,29 +20,36 @@ use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, ToCss};
Debug, Debug,
MallocSizeOf, MallocSizeOf,
PartialEq, PartialEq,
SpecifiedValueInfo,
ToAnimatedZero, ToAnimatedZero,
ToAnimatedValue,
ToComputedValue, ToComputedValue,
)] )]
pub struct Size<L>(pub Size2D<L>); #[allow(missing_docs)]
#[repr(C)]
pub struct Size2D<L> {
pub width: L,
pub height: L,
}
impl<L> Size<L> { impl<L> Size2D<L> {
#[inline] #[inline]
/// Create a new `Size` for an area of given width and height. /// Create a new `Size2D` for an area of given width and height.
pub fn new(width: L, height: L) -> Size<L> { pub fn new(width: L, height: L) -> Self {
Size(Size2D::new(width, height)) Self { width, height }
} }
/// Returns the width component. /// Returns the width component.
pub fn width(&self) -> &L { pub fn width(&self) -> &L {
&self.0.width &self.width
} }
/// Returns the height component. /// Returns the height component.
pub fn height(&self) -> &L { pub fn height(&self) -> &L {
&self.0.height &self.height
} }
/// Parse a `Size` with a given parsing function. /// Parse a `Size2D` with a given parsing function.
pub fn parse_with<'i, 't, F>( pub fn parse_with<'i, 't, F>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
@ -61,7 +67,7 @@ impl<L> Size<L> {
} }
} }
impl<L> ToCss for Size<L> impl<L> ToCss for Size2D<L>
where where
L: ToCss + PartialEq, L: ToCss + PartialEq,
{ {
@ -69,40 +75,23 @@ where
where where
W: Write, W: Write,
{ {
self.0.width.to_css(dest)?; self.width.to_css(dest)?;
if self.0.height != self.0.width { if self.height != self.width {
dest.write_str(" ")?; dest.write_str(" ")?;
self.0.height.to_css(dest)?; self.height.to_css(dest)?;
} }
Ok(()) Ok(())
} }
} }
impl<L> ToAnimatedValue for Size<L> impl<L: Zero> Zero for Size2D<L> {
where fn zero() -> Self {
L: ToAnimatedValue, Self::new(L::zero(), L::zero())
{
type AnimatedValue = Size<L::AnimatedValue>;
#[inline]
fn to_animated_value(self) -> Self::AnimatedValue {
Size(Size2D::new(
self.0.width.to_animated_value(),
self.0.height.to_animated_value(),
))
} }
#[inline] fn is_zero(&self) -> bool {
fn from_animated_value(animated: Self::AnimatedValue) -> Self { self.width.is_zero() && self.height.is_zero()
Size(Size2D::new(
L::from_animated_value(animated.0.width),
L::from_animated_value(animated.0.height),
))
} }
} }
impl<L: SpecifiedValueInfo> SpecifiedValueInfo for Size<L> {
const SUPPORTED_TYPES: u8 = L::SUPPORTED_TYPES;
}

View file

@ -128,64 +128,26 @@ impl<ColorType: Parse, UrlPaintServer: Parse> Parse for SVGPaint<ColorType, UrlP
} }
} }
/// A value of <length> | <percentage> | <number> for svg which allow unitless length.
/// <https://www.w3.org/TR/SVG11/painting.html#StrokeProperties>
#[derive(
Clone,
Copy,
Debug,
MallocSizeOf,
PartialEq,
Parse,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
)]
pub enum SvgLengthPercentageOrNumber<LengthPercentage, Number> {
/// <number>
///
/// Note that this needs to be before, so it gets parsed before the length,
/// to handle `0` correctly as a number instead of a `<length>`.
Number(Number),
/// <length> | <percentage>
LengthPercentage(LengthPercentage),
}
/// Whether the `context-value` value is enabled.
#[cfg(feature = "gecko")]
pub fn is_context_value_enabled(_: &ParserContext) -> bool {
use crate::gecko_bindings::structs::mozilla;
unsafe { mozilla::StaticPrefs_sVarCache_gfx_font_rendering_opentype_svg_enabled }
}
/// Whether the `context-value` value is enabled.
#[cfg(not(feature = "gecko"))]
pub fn is_context_value_enabled(_: &ParserContext) -> bool {
false
}
/// An SVG length value supports `context-value` in addition to length. /// An SVG length value supports `context-value` in addition to length.
#[derive( #[derive(
Animate,
Clone, Clone,
ComputeSquaredDistance, ComputeSquaredDistance,
Copy, Copy,
Debug, Debug,
MallocSizeOf, MallocSizeOf,
PartialEq, PartialEq,
Parse,
SpecifiedValueInfo, SpecifiedValueInfo,
ToAnimatedValue, ToAnimatedValue,
ToAnimatedZero, ToAnimatedZero,
ToComputedValue, ToComputedValue,
ToCss, ToCss,
)] )]
pub enum SVGLength<LengthType> { pub enum SVGLength<L> {
/// `<length> | <percentage> | <number>` /// `<length> | <percentage> | <number>`
Length(LengthType), LengthPercentage(L),
/// `context-value` /// `context-value`
#[parse(condition = "is_context_value_enabled")] #[animation(error)]
ContextValue, ContextValue,
} }
@ -197,13 +159,14 @@ pub enum SVGLength<LengthType> {
PartialEq, PartialEq,
SpecifiedValueInfo, SpecifiedValueInfo,
ToAnimatedValue, ToAnimatedValue,
ToAnimatedZero,
ToComputedValue, ToComputedValue,
ToCss, ToCss,
)] )]
pub enum SVGStrokeDashArray<LengthType> { pub enum SVGStrokeDashArray<L> {
/// `[ <length> | <percentage> | <number> ]#` /// `[ <length> | <percentage> | <number> ]#`
#[css(comma)] #[css(comma)]
Values(#[css(if_empty = "none", iterable)] Vec<LengthType>), Values(#[css(if_empty = "none", iterable)] Vec<L>),
/// `context-value` /// `context-value`
ContextValue, ContextValue,
} }
@ -211,6 +174,7 @@ pub enum SVGStrokeDashArray<LengthType> {
/// An SVG opacity value accepts `context-{fill,stroke}-opacity` in /// An SVG opacity value accepts `context-{fill,stroke}-opacity` in
/// addition to opacity value. /// addition to opacity value.
#[derive( #[derive(
Animate,
Clone, Clone,
ComputeSquaredDistance, ComputeSquaredDistance,
Copy, Copy,
@ -227,7 +191,9 @@ pub enum SVGOpacity<OpacityType> {
/// `<opacity-value>` /// `<opacity-value>`
Opacity(OpacityType), Opacity(OpacityType),
/// `context-fill-opacity` /// `context-fill-opacity`
#[animation(error)]
ContextFillOpacity, ContextFillOpacity,
/// `context-stroke-opacity` /// `context-stroke-opacity`
#[animation(error)]
ContextStrokeOpacity, ContextStrokeOpacity,
} }

View file

@ -5,9 +5,7 @@
//! Generic types for text properties. //! Generic types for text properties.
use crate::parser::ParserContext; use crate::parser::ParserContext;
use crate::values::animated::{Animate, Procedure, ToAnimatedZero}; use crate::values::animated::ToAnimatedZero;
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use app_units::Au;
use cssparser::Parser; use cssparser::Parser;
use style_traits::ParseError; use style_traits::ParseError;
@ -31,9 +29,7 @@ impl<N, I> InitialLetter<N, I> {
} }
/// A generic spacing value for the `letter-spacing` and `word-spacing` properties. /// A generic spacing value for the `letter-spacing` and `word-spacing` properties.
#[derive( #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss,
)]
pub enum Spacing<Value> { pub enum Spacing<Value> {
/// `normal` /// `normal`
Normal, Normal,
@ -63,51 +59,6 @@ impl<Value> Spacing<Value> {
} }
parse(context, input).map(Spacing::Value) parse(context, input).map(Spacing::Value)
} }
/// Returns the spacing value, if not `normal`.
#[inline]
pub fn value(&self) -> Option<&Value> {
match *self {
Spacing::Normal => None,
Spacing::Value(ref value) => Some(value),
}
}
}
impl<Value> Animate for Spacing<Value>
where
Value: Animate + From<Au>,
{
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
if let (&Spacing::Normal, &Spacing::Normal) = (self, other) {
return Ok(Spacing::Normal);
}
let zero = Value::from(Au(0));
let this = self.value().unwrap_or(&zero);
let other = other.value().unwrap_or(&zero);
Ok(Spacing::Value(this.animate(other, procedure)?))
}
}
impl<V> ComputeSquaredDistance for Spacing<V>
where
V: ComputeSquaredDistance + From<Au>,
{
#[inline]
fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
let zero = V::from(Au(0));
let this = self.value().unwrap_or(&zero);
let other = other.value().unwrap_or(&zero);
this.compute_squared_distance(other)
}
}
impl<V> ToAnimatedZero for Spacing<V> {
#[inline]
fn to_animated_zero(&self) -> Result<Self, ()> {
Err(())
}
} }
/// A generic value for the `line-height` property. /// A generic value for the `line-height` property.
@ -123,18 +74,21 @@ impl<V> ToAnimatedZero for Spacing<V> {
ToAnimatedValue, ToAnimatedValue,
ToCss, ToCss,
)] )]
pub enum LineHeight<Number, LengthPercentage> { #[repr(C, u8)]
pub enum GenericLineHeight<N, L> {
/// `normal` /// `normal`
Normal, Normal,
/// `-moz-block-height` /// `-moz-block-height`
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
MozBlockHeight, MozBlockHeight,
/// `<number>` /// `<number>`
Number(Number), Number(N),
/// `<length-or-percentage>` /// `<length-percentage>`
Length(LengthPercentage), Length(L),
} }
pub use self::GenericLineHeight as LineHeight;
impl<N, L> ToAnimatedZero for LineHeight<N, L> { impl<N, L> ToAnimatedZero for LineHeight<N, L> {
#[inline] #[inline]
fn to_animated_zero(&self) -> Result<Self, ()> { fn to_animated_zero(&self) -> Result<Self, ()> {

View file

@ -10,9 +10,9 @@ use crate::values::specified::angle::Angle as SpecifiedAngle;
use crate::values::specified::length::Length as SpecifiedLength; use crate::values::specified::length::Length as SpecifiedLength;
use crate::values::specified::length::LengthPercentage as SpecifiedLengthPercentage; use crate::values::specified::length::LengthPercentage as SpecifiedLengthPercentage;
use crate::values::{computed, CSSFloat}; use crate::values::{computed, CSSFloat};
use crate::Zero;
use app_units::Au; use app_units::Au;
use euclid::{self, Rect, Transform3D}; use euclid::{self, Rect, Transform3D};
use num_traits::Zero;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss}; use style_traits::{CssWriter, ToCss};
@ -106,9 +106,18 @@ impl<H, V, D> TransformOrigin<H, V, D> {
} }
} }
fn is_same<N: PartialEq>(x: &N, y: &N) -> bool {
x == y
}
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)] #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)]
/// A single operation in the list of a `transform` value /// A single operation in the list of a `transform` value
pub enum TransformOperation<Angle, Number, Length, Integer, LengthPercentage> { pub enum TransformOperation<Angle, Number, Length, Integer, LengthPercentage>
where
Angle: Zero,
LengthPercentage: Zero,
Number: PartialEq,
{
/// Represents a 2D 2x3 matrix. /// Represents a 2D 2x3 matrix.
Matrix(Matrix<Number>), Matrix(Matrix<Number>),
/// Represents a 3D 4x4 matrix. /// Represents a 3D 4x4 matrix.
@ -119,7 +128,7 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthPercentage> {
/// ///
/// Syntax can be skew(angle) or skew(angle, angle) /// Syntax can be skew(angle) or skew(angle, angle)
#[css(comma, function)] #[css(comma, function)]
Skew(Angle, Option<Angle>), Skew(Angle, #[css(skip_if = "Zero::is_zero")] Angle),
/// skewX(angle) /// skewX(angle)
#[css(function = "skewX")] #[css(function = "skewX")]
SkewX(Angle), SkewX(Angle),
@ -128,7 +137,10 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthPercentage> {
SkewY(Angle), SkewY(Angle),
/// translate(x, y) or translate(x) /// translate(x, y) or translate(x)
#[css(comma, function)] #[css(comma, function)]
Translate(LengthPercentage, Option<LengthPercentage>), Translate(
LengthPercentage,
#[css(skip_if = "Zero::is_zero")] LengthPercentage,
),
/// translateX(x) /// translateX(x)
#[css(function = "translateX")] #[css(function = "translateX")]
TranslateX(LengthPercentage), TranslateX(LengthPercentage),
@ -143,14 +155,9 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthPercentage> {
Translate3D(LengthPercentage, LengthPercentage, Length), Translate3D(LengthPercentage, LengthPercentage, Length),
/// A 2D scaling factor. /// A 2D scaling factor.
/// ///
/// `scale(2)` is parsed as `Scale(Number::new(2.0), None)` and is equivalent to
/// writing `scale(2, 2)` (`Scale(Number::new(2.0), Some(Number::new(2.0)))`).
///
/// Negative values are allowed and flip the element.
///
/// Syntax can be scale(factor) or scale(factor, factor) /// Syntax can be scale(factor) or scale(factor, factor)
#[css(comma, function)] #[css(comma, function)]
Scale(Number, Option<Number>), Scale(Number, #[css(contextual_skip_if = "is_same")] Number),
/// scaleX(factor) /// scaleX(factor)
#[css(function = "scaleX")] #[css(function = "scaleX")]
ScaleX(Number), ScaleX(Number),
@ -214,6 +221,10 @@ pub struct Transform<T>(#[css(if_empty = "none", iterable)] pub Vec<T>);
impl<Angle, Number, Length, Integer, LengthPercentage> impl<Angle, Number, Length, Integer, LengthPercentage>
TransformOperation<Angle, Number, Length, Integer, LengthPercentage> TransformOperation<Angle, Number, Length, Integer, LengthPercentage>
where
Angle: Zero,
LengthPercentage: Zero,
Number: PartialEq,
{ {
/// Check if it is any rotate function. /// Check if it is any rotate function.
pub fn is_rotate(&self) -> bool { pub fn is_rotate(&self) -> bool {
@ -333,10 +344,10 @@ impl ToRadians for SpecifiedAngle {
impl<Angle, Number, Length, Integer, LoP> ToMatrix impl<Angle, Number, Length, Integer, LoP> ToMatrix
for TransformOperation<Angle, Number, Length, Integer, LoP> for TransformOperation<Angle, Number, Length, Integer, LoP>
where where
Angle: ToRadians + Copy, Angle: Zero + ToRadians + Copy,
Number: Copy + Into<f32> + Into<f64>, Number: PartialEq + Copy + Into<f32> + Into<f64>,
Length: ToAbsoluteLength, Length: ToAbsoluteLength,
LoP: ToAbsoluteLength, LoP: Zero + ToAbsoluteLength,
{ {
#[inline] #[inline]
fn is_3d(&self) -> bool { fn is_3d(&self) -> bool {
@ -389,7 +400,7 @@ where
m.cast() m.cast()
}, },
Scale3D(sx, sy, sz) => Transform3D::create_scale(sx.into(), sy.into(), sz.into()), Scale3D(sx, sy, sz) => Transform3D::create_scale(sx.into(), sy.into(), sz.into()),
Scale(sx, sy) => Transform3D::create_scale(sx.into(), sy.unwrap_or(sx).into(), 1.), Scale(sx, sy) => Transform3D::create_scale(sx.into(), sy.into(), 1.),
ScaleX(s) => Transform3D::create_scale(s.into(), 1., 1.), ScaleX(s) => Transform3D::create_scale(s.into(), 1., 1.),
ScaleY(s) => Transform3D::create_scale(1., s.into(), 1.), ScaleY(s) => Transform3D::create_scale(1., s.into(), 1.),
ScaleZ(s) => Transform3D::create_scale(1., 1., s.into()), ScaleZ(s) => Transform3D::create_scale(1., 1., s.into()),
@ -398,12 +409,12 @@ where
let ty = ty.to_pixel_length(reference_height)? as f64; let ty = ty.to_pixel_length(reference_height)? as f64;
Transform3D::create_translation(tx, ty, tz.to_pixel_length(None)? as f64) Transform3D::create_translation(tx, ty, tz.to_pixel_length(None)? as f64)
}, },
Translate(ref tx, Some(ref ty)) => { Translate(ref tx, ref ty) => {
let tx = tx.to_pixel_length(reference_width)? as f64; let tx = tx.to_pixel_length(reference_width)? as f64;
let ty = ty.to_pixel_length(reference_height)? as f64; let ty = ty.to_pixel_length(reference_height)? as f64;
Transform3D::create_translation(tx, ty, 0.) Transform3D::create_translation(tx, ty, 0.)
}, },
TranslateX(ref t) | Translate(ref t, None) => { TranslateX(ref t) => {
let t = t.to_pixel_length(reference_width)? as f64; let t = t.to_pixel_length(reference_width)? as f64;
Transform3D::create_translation(t, 0., 0.) Transform3D::create_translation(t, 0., 0.)
}, },
@ -416,7 +427,7 @@ where
}, },
Skew(theta_x, theta_y) => Transform3D::create_skew( Skew(theta_x, theta_y) => Transform3D::create_skew(
euclid::Angle::radians(theta_x.radians64()), euclid::Angle::radians(theta_x.radians64()),
euclid::Angle::radians(theta_y.map_or(0., |a| a.radians64())), euclid::Angle::radians(theta_y.radians64()),
), ),
SkewX(theta) => Transform3D::create_skew( SkewX(theta) => Transform3D::create_skew(
euclid::Angle::radians(theta.radians64()), euclid::Angle::radians(theta.radians64()),
@ -629,64 +640,44 @@ impl<Number: ToCss + PartialEq> ToCss for Scale<Number> {
} }
#[derive( #[derive(
Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedZero, ToComputedValue, Clone,
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToAnimatedZero,
ToComputedValue,
ToCss,
)] )]
/// A value of the `Translate` property /// A value of the `translate` property
///
/// https://drafts.csswg.org/css-transforms-2/#individual-transform-serialization:
///
/// If a 2d translation is specified, the property must serialize with only one
/// or two values (per usual, if the second value is 0px, the default, it must
/// be omitted when serializing).
///
/// If a 3d translation is specified, all three values must be serialized.
///
/// We don't omit the 3rd component even if it is 0px for now, and the
/// related spec issue is https://github.com/w3c/csswg-drafts/issues/3305
/// ///
/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms> /// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
pub enum Translate<LengthPercentage, Length> { pub enum Translate<LengthPercentage, Length>
where
LengthPercentage: Zero,
{
/// 'none' /// 'none'
None, None,
/// '<length-percentage>' or '<length-percentage> <length-percentage>' /// '<length-percentage>' or '<length-percentage> <length-percentage>'
Translate(LengthPercentage, LengthPercentage), Translate(
LengthPercentage,
#[css(skip_if = "Zero::is_zero")] LengthPercentage,
),
/// '<length-percentage> <length-percentage> <length>' /// '<length-percentage> <length-percentage> <length>'
Translate3D(LengthPercentage, LengthPercentage, Length), Translate3D(LengthPercentage, LengthPercentage, Length),
} }
/// A trait to check if this is a zero length.
/// An alternative way is use num_traits::Zero. However, in order to implement num_traits::Zero,
/// we also have to implement Add, which may be complicated for LengthPercentage::Calc.
/// We could do this if other types also need it. If so, we could drop this trait.
pub trait IsZeroLength {
/// Returns true if this is a zero length.
fn is_zero_length(&self) -> bool;
}
impl<LoP: ToCss + IsZeroLength, L: ToCss> ToCss for Translate<LoP, L> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
// The spec says:
// 1. If a 2d translation is specified, the property must serialize with only one or two
// values (per usual, if the second value is 0px, the default, it must be omitted when
// serializing).
// 2. If a 3d translation is specified, all three values must be serialized.
// https://drafts.csswg.org/css-transforms-2/#individual-transform-serialization
//
// We don't omit the 3rd component even if it is 0px for now, and the related
// spec issue is https://github.com/w3c/csswg-drafts/issues/3305
match *self {
Translate::None => dest.write_str("none"),
Translate::Translate(ref x, ref y) => {
x.to_css(dest)?;
if !y.is_zero_length() {
dest.write_char(' ')?;
y.to_css(dest)?;
}
Ok(())
},
Translate::Translate3D(ref x, ref y, ref z) => {
x.to_css(dest)?;
dest.write_char(' ')?;
y.to_css(dest)?;
dest.write_char(' ')?;
z.to_css(dest)
},
}
}
}
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive( #[derive(
Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss,

View file

@ -160,7 +160,7 @@ impl CustomIdent {
excluding: &[&str], excluding: &[&str],
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
let valid = match_ignore_ascii_case! { ident, let valid = match_ignore_ascii_case! { ident,
"initial" | "inherit" | "unset" | "default" => false, "initial" | "inherit" | "unset" | "default" | "revert" => false,
_ => true _ => true
}; };
if !valid { if !valid {

View file

@ -9,6 +9,7 @@ use crate::values::computed::angle::Angle as ComputedAngle;
use crate::values::computed::{Context, ToComputedValue}; use crate::values::computed::{Context, ToComputedValue};
use crate::values::specified::calc::CalcNode; use crate::values::specified::calc::CalcNode;
use crate::values::CSSFloat; use crate::values::CSSFloat;
use crate::Zero;
use cssparser::{Parser, Token}; use cssparser::{Parser, Token};
use std::f32::consts::PI; use std::f32::consts::PI;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
@ -32,6 +33,21 @@ pub enum AngleDimension {
Turn(CSSFloat), Turn(CSSFloat),
} }
impl Zero for AngleDimension {
fn zero() -> Self {
AngleDimension::Deg(0.)
}
fn is_zero(&self) -> bool {
match *self {
AngleDimension::Deg(ref f) |
AngleDimension::Grad(ref f) |
AngleDimension::Rad(ref f) |
AngleDimension::Turn(ref f) => *f == 0.,
}
}
}
impl AngleDimension { impl AngleDimension {
/// Returns the amount of degrees this angle represents. /// Returns the amount of degrees this angle represents.
#[inline] #[inline]
@ -58,6 +74,19 @@ pub struct Angle {
was_calc: bool, was_calc: bool,
} }
impl Zero for Angle {
fn zero() -> Self {
Self {
value: Zero::zero(),
was_calc: false,
}
}
fn is_zero(&self) -> bool {
self.value.is_zero()
}
}
impl ToCss for Angle { impl ToCss for Angle {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where where
@ -101,6 +130,11 @@ impl Angle {
} }
} }
/// Return `0deg`.
pub fn zero() -> Self {
Self::from_degrees(0.0, false)
}
/// Returns the value of the angle in degrees, mostly for `calc()`. /// Returns the value of the angle in degrees, mostly for `calc()`.
#[inline] #[inline]
pub fn degrees(&self) -> CSSFloat { pub fn degrees(&self) -> CSSFloat {
@ -113,12 +147,6 @@ impl Angle {
self.was_calc self.was_calc
} }
/// Returns `0deg`.
#[inline]
pub fn zero() -> Self {
Self::from_degrees(0.0, false)
}
/// Returns an `Angle` parsed from a `calc()` expression. /// Returns an `Angle` parsed from a `calc()` expression.
pub fn from_calc(degrees: CSSFloat) -> Self { pub fn from_calc(degrees: CSSFloat) -> Self {
Angle { Angle {

View file

@ -18,6 +18,7 @@ use crate::values::specified::position::{HorizontalPosition, Position, VerticalP
use crate::values::specified::url::SpecifiedUrl; use crate::values::specified::url::SpecifiedUrl;
use crate::values::specified::SVGPathData; use crate::values::specified::SVGPathData;
use crate::values::specified::{LengthPercentage, NonNegativeLengthPercentage}; use crate::values::specified::{LengthPercentage, NonNegativeLengthPercentage};
use crate::Zero;
use cssparser::Parser; use cssparser::Parser;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
@ -202,9 +203,9 @@ impl InsetRect {
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
let rect = Rect::parse_with(context, input, LengthPercentage::parse)?; let rect = Rect::parse_with(context, input, LengthPercentage::parse)?;
let round = if input.try(|i| i.expect_ident_matching("round")).is_ok() { let round = if input.try(|i| i.expect_ident_matching("round")).is_ok() {
Some(BorderRadius::parse(context, input)?) BorderRadius::parse(context, input)?
} else { } else {
None BorderRadius::zero()
}; };
Ok(generic::InsetRect { rect, round }) Ok(generic::InsetRect { rect, round })
} }

View file

@ -12,9 +12,10 @@ use crate::values::generics::border::BorderImageSlice as GenericBorderImageSlice
use crate::values::generics::border::BorderRadius as GenericBorderRadius; use crate::values::generics::border::BorderRadius as GenericBorderRadius;
use crate::values::generics::border::BorderSpacing as GenericBorderSpacing; use crate::values::generics::border::BorderSpacing as GenericBorderSpacing;
use crate::values::generics::rect::Rect; use crate::values::generics::rect::Rect;
use crate::values::generics::size::Size; use crate::values::generics::size::Size2D;
use crate::values::specified::length::{NonNegativeLength, NonNegativeLengthPercentage}; use crate::values::specified::length::{NonNegativeLength, NonNegativeLengthPercentage};
use crate::values::specified::{AllowQuirks, NonNegativeNumber, NonNegativeNumberOrPercentage}; use crate::values::specified::{AllowQuirks, NonNegativeNumber, NonNegativeNumberOrPercentage};
use crate::Zero;
use cssparser::Parser; use cssparser::Parser;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, ToCss}; use style_traits::{CssWriter, ParseError, ToCss};
@ -228,7 +229,7 @@ impl Parse for BorderCornerRadius {
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
Size::parse_with(context, input, NonNegativeLengthPercentage::parse) Size2D::parse_with(context, input, NonNegativeLengthPercentage::parse)
.map(GenericBorderCornerRadius) .map(GenericBorderCornerRadius)
} }
} }
@ -238,7 +239,7 @@ impl Parse for BorderSpacing {
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
Size::parse_with(context, input, |context, input| { Size2D::parse_with(context, input, |context, input| {
NonNegativeLength::parse_quirky(context, input, AllowQuirks::Yes).map(From::from) NonNegativeLength::parse_quirky(context, input, AllowQuirks::Yes).map(From::from)
}) })
.map(GenericBorderSpacing) .map(GenericBorderSpacing)

View file

@ -752,25 +752,23 @@ pub fn assert_touch_action_matches() {
bitflags! { bitflags! {
#[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)] #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)]
#[value_info(other_values = "none,strict,content,size,layout,style,paint")] #[value_info(other_values = "none,strict,content,size,layout,paint")]
/// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property /// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property
pub struct Contain: u8 { pub struct Contain: u8 {
/// 'size' variant, turns on size containment /// 'size' variant, turns on size containment
const SIZE = 0x01; const SIZE = 0x01;
/// `layout` variant, turns on layout containment /// `layout` variant, turns on layout containment
const LAYOUT = 0x02; const LAYOUT = 0x02;
/// `style` variant, turns on style containment
const STYLE = 0x04;
/// `paint` variant, turns on paint containment /// `paint` variant, turns on paint containment
const PAINT = 0x08; const PAINT = 0x04;
/// `strict` variant, turns on all types of containment /// `strict` variant, turns on all types of containment
const STRICT = 0x10; const STRICT = 0x08;
/// 'content' variant, turns on style, layout, and paint containment /// 'content' variant, turns on layout and paint containment
const CONTENT = 0x20; const CONTENT = 0x10;
/// variant with all the bits that contain: strict turns on /// variant with all the bits that contain: strict turns on
const STRICT_BITS = Contain::LAYOUT.bits | Contain::STYLE.bits | Contain::PAINT.bits | Contain::SIZE.bits; const STRICT_BITS = Contain::LAYOUT.bits | Contain::PAINT.bits | Contain::SIZE.bits;
/// variant with all the bits that contain: content turns on /// variant with all the bits that contain: content turns on
const CONTENT_BITS = Contain::STYLE.bits | Contain::LAYOUT.bits | Contain::PAINT.bits; const CONTENT_BITS = Contain::LAYOUT.bits | Contain::PAINT.bits;
} }
} }
@ -803,7 +801,6 @@ impl ToCss for Contain {
} }
maybe_write_value!(Contain::SIZE => "size"); maybe_write_value!(Contain::SIZE => "size");
maybe_write_value!(Contain::LAYOUT => "layout"); maybe_write_value!(Contain::LAYOUT => "layout");
maybe_write_value!(Contain::STYLE => "style");
maybe_write_value!(Contain::PAINT => "paint"); maybe_write_value!(Contain::PAINT => "paint");
debug_assert!(has_any); debug_assert!(has_any);
@ -812,7 +809,7 @@ impl ToCss for Contain {
} }
impl Parse for Contain { impl Parse for Contain {
/// none | strict | content | [ size || layout || style || paint ] /// none | strict | content | [ size || layout || paint ]
fn parse<'i, 't>( fn parse<'i, 't>(
_context: &ParserContext, _context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
@ -822,7 +819,6 @@ impl Parse for Contain {
let flag = match_ignore_ascii_case! { &name, let flag = match_ignore_ascii_case! { &name,
"size" => Some(Contain::SIZE), "size" => Some(Contain::SIZE),
"layout" => Some(Contain::LAYOUT), "layout" => Some(Contain::LAYOUT),
"style" => Some(Contain::STYLE),
"paint" => Some(Contain::PAINT), "paint" => Some(Contain::PAINT),
"strict" if result.is_empty() => return Ok(Contain::STRICT | Contain::STRICT_BITS), "strict" if result.is_empty() => return Ok(Contain::STRICT | Contain::STRICT_BITS),
"content" if result.is_empty() => return Ok(Contain::CONTENT | Contain::CONTENT_BITS), "content" if result.is_empty() => return Ok(Contain::CONTENT | Contain::CONTENT_BITS),

View file

@ -351,13 +351,13 @@ impl Color {
Color::Special(special) => { Color::Special(special) => {
use self::gecko::SpecialColorKeyword as Keyword; use self::gecko::SpecialColorKeyword as Keyword;
_context.map(|context| { _context.map(|context| {
let pres_context = context.device().pres_context(); let prefs = context.device().pref_sheet_prefs();
convert_nscolor_to_computedcolor(match special { convert_nscolor_to_computedcolor(match special {
Keyword::MozDefaultColor => pres_context.mDefaultColor, Keyword::MozDefaultColor => prefs.mDefaultColor,
Keyword::MozDefaultBackgroundColor => pres_context.mBackgroundColor, Keyword::MozDefaultBackgroundColor => prefs.mDefaultBackgroundColor,
Keyword::MozHyperlinktext => pres_context.mLinkColor, Keyword::MozHyperlinktext => prefs.mLinkColor,
Keyword::MozActivehyperlinktext => pres_context.mActiveLinkColor, Keyword::MozActivehyperlinktext => prefs.mActiveLinkColor,
Keyword::MozVisitedhyperlinktext => pres_context.mVisitedLinkColor, Keyword::MozVisitedhyperlinktext => prefs.mVisitedLinkColor,
}) })
}) })
}, },

View file

@ -20,6 +20,7 @@ use crate::values::specified::url::SpecifiedUrl;
use crate::values::specified::{Angle, NumberOrPercentage}; use crate::values::specified::{Angle, NumberOrPercentage};
#[cfg(not(feature = "gecko"))] #[cfg(not(feature = "gecko"))]
use crate::values::Impossible; use crate::values::Impossible;
use crate::Zero;
use cssparser::{self, BasicParseErrorKind, Parser, Token}; use cssparser::{self, BasicParseErrorKind, Parser, Token};
use style_traits::{ParseError, StyleParseErrorKind, ValueParseErrorKind}; use style_traits::{ParseError, StyleParseErrorKind, ValueParseErrorKind};

View file

@ -14,11 +14,11 @@ use crate::values::generics::length as generics;
use crate::values::generics::length::{ use crate::values::generics::length::{
GenericLengthOrNumber, MaxSize as GenericMaxSize, Size as GenericSize, GenericLengthOrNumber, MaxSize as GenericMaxSize, Size as GenericSize,
}; };
use crate::values::generics::transform::IsZeroLength;
use crate::values::generics::NonNegative; use crate::values::generics::NonNegative;
use crate::values::specified::calc::CalcNode; use crate::values::specified::calc::CalcNode;
use crate::values::specified::NonNegativeNumber; use crate::values::specified::NonNegativeNumber;
use crate::values::{Auto, CSSFloat, Either, Normal}; use crate::values::{CSSFloat, Either, Normal};
use crate::Zero;
use app_units::Au; use app_units::Au;
use cssparser::{Parser, Token}; use cssparser::{Parser, Token};
use euclid::Size2D; use euclid::Size2D;
@ -477,21 +477,6 @@ impl NoCalcLength {
}) })
} }
#[inline]
/// Returns a `zero` length.
pub fn zero() -> NoCalcLength {
NoCalcLength::Absolute(AbsoluteLength::Px(0.))
}
#[inline]
/// Checks whether the length value is zero.
pub fn is_zero(&self) -> bool {
match *self {
NoCalcLength::Absolute(length) => length.is_zero(),
_ => false,
}
}
/// Get a px value without context. /// Get a px value without context.
#[inline] #[inline]
pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> { pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
@ -510,9 +495,12 @@ impl NoCalcLength {
impl SpecifiedValueInfo for NoCalcLength {} impl SpecifiedValueInfo for NoCalcLength {}
impl IsZeroLength for NoCalcLength { impl Zero for NoCalcLength {
#[inline] fn zero() -> Self {
fn is_zero_length(&self) -> bool { NoCalcLength::Absolute(AbsoluteLength::Px(0.))
}
fn is_zero(&self) -> bool {
match *self { match *self {
NoCalcLength::Absolute(v) => v.is_zero(), NoCalcLength::Absolute(v) => v.is_zero(),
NoCalcLength::FontRelative(v) => v.is_zero(), NoCalcLength::FontRelative(v) => v.is_zero(),
@ -584,12 +572,6 @@ impl Mul<CSSFloat> for ViewportPercentageLength {
} }
impl Length { impl Length {
#[inline]
/// Returns a `zero` length.
pub fn zero() -> Length {
Length::NoCalc(NoCalcLength::zero())
}
#[inline] #[inline]
fn parse_internal<'i, 't>( fn parse_internal<'i, 't>(
context: &ParserContext, context: &ParserContext,
@ -672,6 +654,21 @@ impl Parse for Length {
} }
} }
impl Zero for Length {
fn zero() -> Self {
Length::NoCalc(NoCalcLength::zero())
}
fn is_zero(&self) -> bool {
// FIXME(emilio): Seems a bit weird to treat calc() unconditionally as
// non-zero here?
match *self {
Length::NoCalc(ref l) => l.is_zero(),
Length::Calc(..) => false,
}
}
}
impl Length { impl Length {
/// Parses a length, with quirks. /// Parses a length, with quirks.
pub fn parse_quirky<'i, 't>( pub fn parse_quirky<'i, 't>(
@ -711,12 +708,6 @@ impl From<Length> for NonNegativeLength {
} }
impl NonNegativeLength { impl NonNegativeLength {
/// Returns a `zero` length.
#[inline]
pub fn zero() -> Self {
Length::zero().into()
}
/// Get an absolute length from a px value. /// Get an absolute length from a px value.
#[inline] #[inline]
pub fn from_px(px_value: CSSFloat) -> Self { pub fn from_px(px_value: CSSFloat) -> Self {
@ -738,9 +729,6 @@ impl NonNegativeLength {
} }
} }
/// Either a NonNegativeLength or the `auto` keyword.
pub type NonNegativeLengthOrAuto = Either<NonNegativeLength, Auto>;
/// A `<length-percentage>` value. This can be either a `<length>`, a /// A `<length-percentage>` value. This can be either a `<length>`, a
/// `<percentage>`, or a combination of both via `calc()`. /// `<percentage>`, or a combination of both via `calc()`.
/// ///
@ -801,12 +789,6 @@ impl Parse for LengthPercentage {
} }
impl LengthPercentage { impl LengthPercentage {
#[inline]
/// Returns a `zero` length.
pub fn zero() -> LengthPercentage {
LengthPercentage::Length(NoCalcLength::zero())
}
#[inline] #[inline]
/// Returns a `0%` value. /// Returns a `0%` value.
pub fn zero_percent() -> LengthPercentage { pub fn zero_percent() -> LengthPercentage {
@ -898,11 +880,14 @@ impl LengthPercentage {
} }
} }
impl IsZeroLength for LengthPercentage { impl Zero for LengthPercentage {
#[inline] fn zero() -> Self {
fn is_zero_length(&self) -> bool { LengthPercentage::Length(NoCalcLength::zero())
}
fn is_zero(&self) -> bool {
match *self { match *self {
LengthPercentage::Length(l) => l.is_zero_length(), LengthPercentage::Length(l) => l.is_zero(),
LengthPercentage::Percentage(p) => p.0 == 0.0, LengthPercentage::Percentage(p) => p.0 == 0.0,
LengthPercentage::Calc(_) => false, LengthPercentage::Calc(_) => false,
} }
@ -913,11 +898,6 @@ impl IsZeroLength for LengthPercentage {
pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>; pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>;
impl LengthPercentageOrAuto { impl LengthPercentageOrAuto {
/// Returns a value representing a `0` length.
pub fn zero() -> Self {
generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero())
}
/// Returns a value representing `0%`. /// Returns a value representing `0%`.
#[inline] #[inline]
pub fn zero_percent() -> Self { pub fn zero_percent() -> Self {
@ -943,11 +923,6 @@ pub type NonNegativeLengthPercentageOrAuto =
generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>; generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>;
impl NonNegativeLengthPercentageOrAuto { impl NonNegativeLengthPercentageOrAuto {
/// Returns a value representing a `0` length.
pub fn zero() -> Self {
generics::LengthPercentageOrAuto::LengthPercentage(NonNegativeLengthPercentage::zero())
}
/// Returns a value representing `0%`. /// Returns a value representing `0%`.
#[inline] #[inline]
pub fn zero_percent() -> Self { pub fn zero_percent() -> Self {
@ -997,12 +972,6 @@ impl Parse for NonNegativeLengthPercentage {
} }
impl NonNegativeLengthPercentage { impl NonNegativeLengthPercentage {
#[inline]
/// Returns a `zero` length.
pub fn zero() -> Self {
NonNegative(LengthPercentage::zero())
}
#[inline] #[inline]
/// Returns a `0%` value. /// Returns a `0%` value.
pub fn zero_percent() -> Self { pub fn zero_percent() -> Self {
@ -1025,7 +994,29 @@ impl NonNegativeLengthPercentage {
pub type LengthOrNormal = Either<Length, Normal>; pub type LengthOrNormal = Either<Length, Normal>;
/// Either a `<length>` or the `auto` keyword. /// Either a `<length>` or the `auto` keyword.
pub type LengthOrAuto = Either<Length, Auto>; ///
/// Note that we use LengthPercentage just for convenience, since it pretty much
/// is everything we care about, but we could just add a similar LengthOrAuto
/// instead if we think getting rid of this weirdness is worth it.
pub type LengthOrAuto = generics::LengthPercentageOrAuto<Length>;
impl LengthOrAuto {
/// Parses a length, allowing the unitless length quirk.
/// <https://quirks.spec.whatwg.org/#the-unitless-length-quirk>
#[inline]
pub fn parse_quirky<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
allow_quirks: AllowQuirks,
) -> Result<Self, ParseError<'i>> {
Self::parse_with(context, input, |context, input| {
Length::parse_quirky(context, input, allow_quirks)
})
}
}
/// Either a non-negative `<length>` or the `auto` keyword.
pub type NonNegativeLengthOrAuto = generics::LengthPercentageOrAuto<NonNegativeLength>;
/// Either a `<length>` or a `<number>`. /// Either a `<length>` or a `<number>`.
pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>; pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;

View file

@ -11,7 +11,7 @@ use super::computed::{Context, ToComputedValue};
use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth}; use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize}; use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
use super::generics::transform::IsParallelTo; use super::generics::transform::IsParallelTo;
use super::generics::{GreaterThanOrEqualToOne, NonNegative}; use super::generics::{self, GreaterThanOrEqualToOne, NonNegative};
use super::{Auto, CSSFloat, CSSInteger, Either}; use super::{Auto, CSSFloat, CSSInteger, Either};
use crate::context::QuirksMode; use crate::context::QuirksMode;
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
@ -60,7 +60,7 @@ pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
pub use self::image::{GradientItem, GradientKind, Image, ImageLayer, MozImageRect}; pub use self::image::{GradientItem, GradientKind, Image, ImageLayer, MozImageRect};
pub use self::length::{AbsoluteLength, CalcLengthPercentage, CharacterWidth}; pub use self::length::{AbsoluteLength, CalcLengthPercentage, CharacterWidth};
pub use self::length::{FontRelativeLength, Length, LengthOrNumber, NonNegativeLengthOrNumber}; pub use self::length::{FontRelativeLength, Length, LengthOrNumber, NonNegativeLengthOrNumber};
pub use self::length::{LengthPercentage, LengthPercentageOrAuto}; pub use self::length::{LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
pub use self::length::{MaxSize, Size}; pub use self::length::{MaxSize, Size};
pub use self::length::{NoCalcLength, ViewportPercentageLength}; pub use self::length::{NoCalcLength, ViewportPercentageLength};
pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto}; pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto};
@ -80,7 +80,7 @@ pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
pub use self::svg_path::SVGPathData; pub use self::svg_path::SVGPathData;
pub use self::table::XSpan; pub use self::table::XSpan;
pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextAlign}; pub use self::text::{InitialLetter, LetterSpacing, LineHeight, TextAlign};
pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle}; pub use self::text::{OverflowWrap, TextEmphasisPosition, TextEmphasisStyle, WordBreak};
pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing}; pub use self::text::{TextAlignKeyword, TextDecorationLine, TextOverflow, WordSpacing};
pub use self::time::Time; pub use self::time::Time;
pub use self::transform::{Rotate, Scale, Transform}; pub use self::transform::{Rotate, Scale, Transform};
@ -605,99 +605,8 @@ pub type GridLine = GenericGridLine<Integer>;
/// `<grid-template-rows> | <grid-template-columns>` /// `<grid-template-rows> | <grid-template-columns>`
pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>; pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)] /// rect(...)
/// rect(<top>, <left>, <bottom>, <right>) used by clip and image-region pub type ClipRect = generics::ClipRect<LengthOrAuto>;
#[css(function = "rect")]
pub struct ClipRect {
/// <top> (<length> | <auto>)
pub top: Option<Length>,
/// <right> (<length> | <auto>)
pub right: Option<Length>,
/// <bottom> (<length> | <auto>)
pub bottom: Option<Length>,
/// <left> (<length> | <auto>)
pub left: Option<Length>,
}
impl ToCss for ClipRect {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
dest.write_str("rect(")?;
if let Some(ref top) = self.top {
top.to_css(dest)?;
dest.write_str(", ")?;
} else {
dest.write_str("auto, ")?;
}
if let Some(ref right) = self.right {
right.to_css(dest)?;
dest.write_str(", ")?;
} else {
dest.write_str("auto, ")?;
}
if let Some(ref bottom) = self.bottom {
bottom.to_css(dest)?;
dest.write_str(", ")?;
} else {
dest.write_str("auto, ")?;
}
if let Some(ref left) = self.left {
left.to_css(dest)?;
} else {
dest.write_str("auto")?;
}
dest.write_str(")")?;
Ok(())
}
}
impl ToComputedValue for ClipRect {
type ComputedValue = super::computed::ClipRect;
#[inline]
fn to_computed_value(&self, context: &Context) -> super::computed::ClipRect {
super::computed::ClipRect {
top: self.top.as_ref().map(|top| top.to_computed_value(context)),
right: self
.right
.as_ref()
.map(|right| right.to_computed_value(context)),
bottom: self
.bottom
.as_ref()
.map(|bottom| bottom.to_computed_value(context)),
left: self
.left
.as_ref()
.map(|left| left.to_computed_value(context)),
}
}
#[inline]
fn from_computed_value(computed: &super::computed::ClipRect) -> Self {
ClipRect {
top: computed
.top
.map(|top| ToComputedValue::from_computed_value(&top)),
right: computed
.right
.map(|right| ToComputedValue::from_computed_value(&right)),
bottom: computed
.bottom
.map(|bottom| ToComputedValue::from_computed_value(&bottom)),
left: computed
.left
.map(|left| ToComputedValue::from_computed_value(&left)),
}
}
}
impl Parse for ClipRect { impl Parse for ClipRect {
fn parse<'i, 't>( fn parse<'i, 't>(
@ -715,25 +624,16 @@ impl ClipRect {
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
allow_quirks: AllowQuirks, allow_quirks: AllowQuirks,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
use crate::values::specified::Length; input.expect_function_matching("rect")?;
fn parse_argument<'i, 't>( fn parse_argument<'i, 't>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
allow_quirks: AllowQuirks, allow_quirks: AllowQuirks,
) -> Result<Option<Length>, ParseError<'i>> { ) -> Result<LengthOrAuto, ParseError<'i>> {
if input LengthOrAuto::parse_quirky(context, input, allow_quirks)
.try(|input| input.expect_ident_matching("auto"))
.is_ok()
{
Ok(None)
} else {
Length::parse_quirky(context, input, allow_quirks).map(Some)
}
} }
input.expect_function_matching("rect")?;
input.parse_nested_block(|input| { input.parse_nested_block(|input| {
let top = parse_argument(context, input, allow_quirks)?; let top = parse_argument(context, input, allow_quirks)?;
let right; let right;
@ -751,11 +651,12 @@ impl ClipRect {
bottom = parse_argument(context, input, allow_quirks)?; bottom = parse_argument(context, input, allow_quirks)?;
left = parse_argument(context, input, allow_quirks)?; left = parse_argument(context, input, allow_quirks)?;
} }
Ok(ClipRect { Ok(ClipRect {
top: top, top,
right: right, right,
bottom: bottom, bottom,
left: left, left,
}) })
}) })
} }
@ -782,16 +683,22 @@ impl ClipRectOrAuto {
/// Whether quirks are allowed in this context. /// Whether quirks are allowed in this context.
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq)]
pub enum AllowQuirks { pub enum AllowQuirks {
/// Quirks are allowed.
Yes,
/// Quirks are not allowed. /// Quirks are not allowed.
No, No,
/// Quirks are allowed, in quirks mode.
Yes,
/// Quirks are always allowed, used for SVG lengths.
Always,
} }
impl AllowQuirks { impl AllowQuirks {
/// Returns `true` if quirks are allowed in this context. /// Returns `true` if quirks are allowed in this context.
pub fn allowed(self, quirks_mode: QuirksMode) -> bool { pub fn allowed(self, quirks_mode: QuirksMode) -> bool {
self == AllowQuirks::Yes && quirks_mode == QuirksMode::Quirks match self {
AllowQuirks::Always => true,
AllowQuirks::No => false,
AllowQuirks::Yes => quirks_mode == QuirksMode::Quirks,
}
} }
} }

View file

@ -17,6 +17,7 @@ use crate::values::generics::position::ZIndex as GenericZIndex;
use crate::values::specified::transform::OriginComponent; use crate::values::specified::transform::OriginComponent;
use crate::values::specified::{AllowQuirks, Integer, LengthPercentage}; use crate::values::specified::{AllowQuirks, Integer, LengthPercentage};
use crate::values::{Either, None_}; use crate::values::{Either, None_};
use crate::Zero;
use cssparser::Parser; use cssparser::Parser;
use selectors::parser::SelectorParseErrorKind; use selectors::parser::SelectorParseErrorKind;
use servo_arc::Arc; use servo_arc::Arc;

View file

@ -8,9 +8,9 @@ use crate::parser::{Parse, ParserContext};
use crate::values::generics::svg as generic; use crate::values::generics::svg as generic;
use crate::values::specified::color::Color; use crate::values::specified::color::Color;
use crate::values::specified::url::SpecifiedUrl; use crate::values::specified::url::SpecifiedUrl;
use crate::values::specified::AllowQuirks;
use crate::values::specified::LengthPercentage; use crate::values::specified::LengthPercentage;
use crate::values::specified::{NonNegativeLengthPercentage, NonNegativeNumber}; use crate::values::specified::{NonNegativeLengthPercentage, Opacity};
use crate::values::specified::{Number, Opacity};
use crate::values::CustomIdent; use crate::values::CustomIdent;
use cssparser::Parser; use cssparser::Parser;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
@ -23,36 +23,52 @@ pub type SVGPaint = generic::SVGPaint<Color, SpecifiedUrl>;
/// Specified SVG Paint Kind value /// Specified SVG Paint Kind value
pub type SVGPaintKind = generic::SVGPaintKind<Color, SpecifiedUrl>; pub type SVGPaintKind = generic::SVGPaintKind<Color, SpecifiedUrl>;
/// A value of <length> | <percentage> | <number> for stroke-dashoffset.
/// <https://www.w3.org/TR/SVG11/painting.html#StrokeProperties>
pub type SvgLengthPercentageOrNumber =
generic::SvgLengthPercentageOrNumber<LengthPercentage, Number>;
/// <length> | <percentage> | <number> | context-value /// <length> | <percentage> | <number> | context-value
pub type SVGLength = generic::SVGLength<SvgLengthPercentageOrNumber>; pub type SVGLength = generic::SVGLength<LengthPercentage>;
impl From<SvgLengthPercentageOrNumber> for SVGLength {
fn from(length: SvgLengthPercentageOrNumber) -> Self {
generic::SVGLength::Length(length)
}
}
/// A value of <length> | <percentage> | <number> for stroke-width/stroke-dasharray.
/// <https://www.w3.org/TR/SVG11/painting.html#StrokeProperties>
pub type NonNegativeSvgLengthPercentageOrNumber =
generic::SvgLengthPercentageOrNumber<NonNegativeLengthPercentage, NonNegativeNumber>;
/// A non-negative version of SVGLength. /// A non-negative version of SVGLength.
pub type SVGWidth = generic::SVGLength<NonNegativeSvgLengthPercentageOrNumber>; pub type SVGWidth = generic::SVGLength<NonNegativeLengthPercentage>;
impl From<NonNegativeSvgLengthPercentageOrNumber> for SVGWidth {
fn from(length: NonNegativeSvgLengthPercentageOrNumber) -> Self {
generic::SVGLength::Length(length)
}
}
/// [ <length> | <percentage> | <number> ]# | context-value /// [ <length> | <percentage> | <number> ]# | context-value
pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<NonNegativeSvgLengthPercentageOrNumber>; pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<NonNegativeLengthPercentage>;
/// Whether the `context-value` value is enabled.
#[cfg(feature = "gecko")]
pub fn is_context_value_enabled() -> bool {
use crate::gecko_bindings::structs::mozilla;
unsafe { mozilla::StaticPrefs_sVarCache_gfx_font_rendering_opentype_svg_enabled }
}
/// Whether the `context-value` value is enabled.
#[cfg(not(feature = "gecko"))]
pub fn is_context_value_enabled() -> bool {
false
}
macro_rules! parse_svg_length {
($ty:ty, $lp:ty) => {
impl Parse for $ty {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if let Ok(lp) = input.try(|i| <$lp>::parse_quirky(context, i, AllowQuirks::Always))
{
return Ok(generic::SVGLength::LengthPercentage(lp));
}
try_match_ident_ignore_ascii_case! { input,
"context-value" if is_context_value_enabled() => {
Ok(generic::SVGLength::ContextValue)
},
}
}
}
};
}
parse_svg_length!(SVGLength, LengthPercentage);
parse_svg_length!(SVGWidth, NonNegativeLengthPercentage);
impl Parse for SVGStrokeDashArray { impl Parse for SVGStrokeDashArray {
fn parse<'i, 't>( fn parse<'i, 't>(
@ -61,14 +77,14 @@ impl Parse for SVGStrokeDashArray {
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
if let Ok(values) = input.try(|i| { if let Ok(values) = input.try(|i| {
CommaWithSpace::parse(i, |i| { CommaWithSpace::parse(i, |i| {
NonNegativeSvgLengthPercentageOrNumber::parse(context, i) NonNegativeLengthPercentage::parse_quirky(context, i, AllowQuirks::Always)
}) })
}) { }) {
return Ok(generic::SVGStrokeDashArray::Values(values)); return Ok(generic::SVGStrokeDashArray::Values(values));
} }
try_match_ident_ignore_ascii_case! { input, try_match_ident_ignore_ascii_case! { input,
"context-value" if generic::is_context_value_enabled(context) => { "context-value" if is_context_value_enabled() => {
Ok(generic::SVGStrokeDashArray::ContextValue) Ok(generic::SVGStrokeDashArray::ContextValue)
}, },
"none" => Ok(generic::SVGStrokeDashArray::Values(vec![])), "none" => Ok(generic::SVGStrokeDashArray::Values(vec![])),

View file

@ -813,6 +813,33 @@ impl From<TextEmphasisPosition> for u8 {
} }
} }
/// Values for the `word-break` property.
#[repr(u8)]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
)]
#[allow(missing_docs)]
pub enum WordBreak {
Normal,
BreakAll,
KeepAll,
/// The break-word value, needed for compat.
///
/// Specifying `word-break: break-word` makes `overflow-wrap` behave as
/// `anywhere`, and `word-break` behave like `normal`.
#[cfg(feature = "gecko")]
BreakWord,
}
/// Values for the `overflow-wrap` property. /// Values for the `overflow-wrap` property.
#[repr(u8)] #[repr(u8)]
#[derive( #[derive(

View file

@ -11,6 +11,7 @@ use crate::values::generics::transform as generic;
use crate::values::generics::transform::{Matrix, Matrix3D}; use crate::values::generics::transform::{Matrix, Matrix3D};
use crate::values::specified::position::{Side, X, Y}; use crate::values::specified::position::{Side, X, Y};
use crate::values::specified::{self, Angle, Integer, Length, LengthPercentage, Number}; use crate::values::specified::{self, Angle, Integer, Length, LengthPercentage, Number};
use crate::Zero;
use cssparser::Parser; use cssparser::Parser;
use style_traits::{ParseError, StyleParseErrorKind}; use style_traits::{ParseError, StyleParseErrorKind};
@ -108,9 +109,9 @@ impl Transform {
let sx = specified::LengthPercentage::parse(context, input)?; let sx = specified::LengthPercentage::parse(context, input)?;
if input.try(|input| input.expect_comma()).is_ok() { if input.try(|input| input.expect_comma()).is_ok() {
let sy = specified::LengthPercentage::parse(context, input)?; let sy = specified::LengthPercentage::parse(context, input)?;
Ok(generic::TransformOperation::Translate(sx, Some(sy))) Ok(generic::TransformOperation::Translate(sx, sy))
} else { } else {
Ok(generic::TransformOperation::Translate(sx, None)) Ok(generic::TransformOperation::Translate(sx, Zero::zero()))
} }
}, },
"translatex" => { "translatex" => {
@ -137,9 +138,9 @@ impl Transform {
let sx = Number::parse(context, input)?; let sx = Number::parse(context, input)?;
if input.try(|input| input.expect_comma()).is_ok() { if input.try(|input| input.expect_comma()).is_ok() {
let sy = Number::parse(context, input)?; let sy = Number::parse(context, input)?;
Ok(generic::TransformOperation::Scale(sx, Some(sy))) Ok(generic::TransformOperation::Scale(sx, sy))
} else { } else {
Ok(generic::TransformOperation::Scale(sx, None)) Ok(generic::TransformOperation::Scale(sx, sx))
} }
}, },
"scalex" => { "scalex" => {
@ -193,9 +194,9 @@ impl Transform {
let ax = specified::Angle::parse_with_unitless(context, input)?; let ax = specified::Angle::parse_with_unitless(context, input)?;
if input.try(|input| input.expect_comma()).is_ok() { if input.try(|input| input.expect_comma()).is_ok() {
let ay = specified::Angle::parse_with_unitless(context, input)?; let ay = specified::Angle::parse_with_unitless(context, input)?;
Ok(generic::TransformOperation::Skew(ax, Some(ay))) Ok(generic::TransformOperation::Skew(ax, ay))
} else { } else {
Ok(generic::TransformOperation::Skew(ax, None)) Ok(generic::TransformOperation::Skew(ax, Zero::zero()))
} }
}, },
"skewx" => { "skewx" => {

View file

@ -12,6 +12,70 @@ use syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple};
use syn::{Variant, WherePredicate}; use syn::{Variant, WherePredicate};
use synstructure::{self, BindStyle, BindingInfo, VariantAst, VariantInfo}; use synstructure::{self, BindStyle, BindingInfo, VariantAst, VariantInfo};
/// Given an input type which has some where clauses already, like:
///
/// struct InputType<T>
/// where
/// T: Zero,
/// {
/// ...
/// }
///
/// Add the necessary `where` clauses so that the output type of a trait
/// fulfils them.
///
/// For example:
///
/// <T as ToComputedValue>::ComputedValue: Zero,
///
/// This needs to run before adding other bounds to the type parameters.
pub fn propagate_clauses_to_output_type(
where_clause: &mut Option<syn::WhereClause>,
generics: &syn::Generics,
trait_path: Path,
trait_output: Ident,
) {
let where_clause = match *where_clause {
Some(ref mut clause) => clause,
None => return,
};
let mut extra_bounds = vec![];
for pred in &where_clause.predicates {
let ty = match *pred {
syn::WherePredicate::Type(ref ty) => ty,
ref predicate => panic!("Unhanded complex where predicate: {:?}", predicate),
};
let path = match ty.bounded_ty {
syn::Type::Path(ref p) => &p.path,
ref ty => panic!("Unhanded complex where type: {:?}", ty),
};
assert!(
ty.lifetimes.is_none(),
"Unhanded complex lifetime bound: {:?}",
ty,
);
let ident = match path_to_ident(path) {
Some(i) => i,
None => panic!("Unhanded complex where type path: {:?}", path),
};
if generics.type_params().any(|param| param.ident == *ident) {
extra_bounds.push(ty.clone());
}
}
for bound in extra_bounds {
let ty = bound.bounded_ty;
let bounds = bound.bounds;
where_clause
.predicates
.push(parse_quote!(<#ty as #trait_path>::#trait_output: #bounds))
}
}
pub fn add_predicate(where_clause: &mut Option<syn::WhereClause>, pred: WherePredicate) { pub fn add_predicate(where_clause: &mut Option<syn::WhereClause>, pred: WherePredicate) {
where_clause where_clause
.get_or_insert(parse_quote!(where)) .get_or_insert(parse_quote!(where))

View file

@ -9,6 +9,12 @@ use synstructure::BindStyle;
pub fn derive(mut input: DeriveInput) -> TokenStream { pub fn derive(mut input: DeriveInput) -> TokenStream {
let mut where_clause = input.generics.where_clause.take(); let mut where_clause = input.generics.where_clause.take();
cg::propagate_clauses_to_output_type(
&mut where_clause,
&input.generics,
parse_quote!(crate::values::animated::ToAnimatedValue),
parse_quote!(AnimatedValue),
);
for param in input.generics.type_params() { for param in input.generics.type_params() {
cg::add_predicate( cg::add_predicate(
&mut where_clause, &mut where_clause,

View file

@ -9,6 +9,12 @@ use synstructure::BindStyle;
pub fn derive(mut input: DeriveInput) -> TokenStream { pub fn derive(mut input: DeriveInput) -> TokenStream {
let mut where_clause = input.generics.where_clause.take(); let mut where_clause = input.generics.where_clause.take();
cg::propagate_clauses_to_output_type(
&mut where_clause,
&input.generics,
parse_quote!(crate::values::computed::ToComputedValue),
parse_quote!(ComputedValue),
);
let (to_body, from_body) = { let (to_body, from_body) = {
let params = input.generics.type_params().collect::<Vec<_>>(); let params = input.generics.type_params().collect::<Vec<_>>();
for param in &params { for param in &params {

View file

@ -148,12 +148,20 @@ fn derive_variant_fields_expr(
} }
} }
} }
if let Some(condition) = attrs.contextual_skip_if {
expr = quote! {
if !#condition(#(#bindings), *) {
#expr
}
}
}
return expr; return expr;
} }
let mut expr = derive_single_field_expr(first, attrs, where_clause); let mut expr = derive_single_field_expr(first, attrs, where_clause, bindings);
for (binding, attrs) in iter { for (binding, attrs) in iter {
derive_single_field_expr(binding, attrs, where_clause).to_tokens(&mut expr) derive_single_field_expr(binding, attrs, where_clause, bindings).to_tokens(&mut expr)
} }
quote! {{ quote! {{
@ -167,6 +175,7 @@ fn derive_single_field_expr(
field: &BindingInfo, field: &BindingInfo,
attrs: CssFieldAttrs, attrs: CssFieldAttrs,
where_clause: &mut Option<WhereClause>, where_clause: &mut Option<WhereClause>,
bindings: &[BindingInfo],
) -> TokenStream { ) -> TokenStream {
let mut expr = if attrs.iterable { let mut expr = if attrs.iterable {
if let Some(if_empty) = attrs.if_empty { if let Some(if_empty) = attrs.if_empty {
@ -216,6 +225,14 @@ fn derive_single_field_expr(
} }
} }
if let Some(condition) = attrs.contextual_skip_if {
expr = quote! {
if !#condition(#(#bindings), *) {
#expr
}
}
}
expr expr
} }
@ -249,5 +266,6 @@ pub struct CssFieldAttrs {
pub iterable: bool, pub iterable: bool,
pub skip: bool, pub skip: bool,
pub represents_keyword: bool, pub represents_keyword: bool,
pub contextual_skip_if: Option<Path>,
pub skip_if: Option<Path>, pub skip_if: Option<Path>,
} }

View file

@ -37,6 +37,10 @@ use std::fmt::{self, Write};
/// * if `#[css(skip_if = "function")]` is found on a field, the `ToCss` call /// * if `#[css(skip_if = "function")]` is found on a field, the `ToCss` call
/// for that field is skipped if `function` returns true. This function is /// for that field is skipped if `function` returns true. This function is
/// provided the field as an argument; /// provided the field as an argument;
/// * if `#[css(contextual_skip_if = "function")]` is found on a field, the
/// `ToCss` call for that field is skipped if `function` returns true. This
/// function is given all the fields in the current struct or variant as an
/// argument;
/// * `#[css(represents_keyword)]` can be used on bool fields in order to /// * `#[css(represents_keyword)]` can be used on bool fields in order to
/// serialize the field name if the field is true, or nothing otherwise. It /// serialize the field name if the field is true, or nothing otherwise. It
/// also collects those keywords for `SpecifiedValueInfo`. /// also collects those keywords for `SpecifiedValueInfo`.

View file

@ -92,53 +92,3 @@ fn test_transform_interpolation_on_scale() {
Transform(vec![TransformOperation::Scale3D(1.5, 3.0, 1.5)]) Transform(vec![TransformOperation::Scale3D(1.5, 3.0, 1.5)])
); );
} }
#[test]
fn test_transform_interpolation_on_rotate() {
use style::values::computed::Angle;
let from = Transform(vec![TransformOperation::Rotate3D(
0.0,
0.0,
1.0,
Angle::from_radians(0.0),
)]);
let to = Transform(vec![TransformOperation::Rotate3D(
0.0,
0.0,
1.0,
Angle::from_radians(100.0),
)]);
assert_eq!(
from.animate(&to, Procedure::Interpolate { progress: 0.5 })
.unwrap(),
Transform(vec![TransformOperation::Rotate3D(
0.0,
0.0,
1.0,
Angle::from_radians(50.0)
)])
);
}
#[test]
fn test_transform_interpolation_on_skew() {
use style::values::computed::Angle;
let from = Transform(vec![TransformOperation::Skew(
Angle::from_radians(0.0),
Some(Angle::from_radians(100.0)),
)]);
let to = Transform(vec![TransformOperation::Skew(
Angle::from_radians(100.0),
Some(Angle::from_radians(0.0)),
)]);
assert_eq!(
from.animate(&to, Procedure::Interpolate { progress: 0.5 })
.unwrap(),
Transform(vec![TransformOperation::Skew(
Angle::from_radians(50.0),
Some(Angle::from_radians(50.0)),
)])
);
}

View file

@ -7,30 +7,30 @@ use servo_arc::Arc;
use style::custom_properties::{ use style::custom_properties::{
CssEnvironment, CustomPropertiesBuilder, CustomPropertiesMap, Name, SpecifiedValue, CssEnvironment, CustomPropertiesBuilder, CustomPropertiesMap, Name, SpecifiedValue,
}; };
use style::properties::CustomDeclarationValue; use style::properties::{CustomDeclaration, CustomDeclarationValue};
use style::stylesheets::Origin;
use test::{self, Bencher}; use test::{self, Bencher};
fn cascade( fn cascade(
name_and_value: &[(&str, &str)], name_and_value: &[(&str, &str)],
inherited: Option<&Arc<CustomPropertiesMap>>, inherited: Option<&Arc<CustomPropertiesMap>>,
) -> Option<Arc<CustomPropertiesMap>> { ) -> Option<Arc<CustomPropertiesMap>> {
let values = name_and_value let declarations = name_and_value
.iter() .iter()
.map(|&(name, value)| { .map(|&(name, value)| {
let mut input = ParserInput::new(value); let mut input = ParserInput::new(value);
let mut parser = Parser::new(&mut input); let mut parser = Parser::new(&mut input);
( let name = Name::from(name);
Name::from(name), let value = CustomDeclarationValue::Value(SpecifiedValue::parse(&mut parser).unwrap());
SpecifiedValue::parse(&mut parser).unwrap(), CustomDeclaration { name, value }
)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let env = CssEnvironment; let env = CssEnvironment;
let mut builder = CustomPropertiesBuilder::new(inherited, &env); let mut builder = CustomPropertiesBuilder::new(inherited, &env);
for &(ref name, ref val) in &values { for declaration in &declarations {
builder.cascade(name, &CustomDeclarationValue::Value(val.clone())); builder.cascade(declaration, Origin::Author);
} }
builder.build() builder.build()

View file

@ -2,6 +2,3 @@
[Property text-justify inherits] [Property text-justify inherits]
expected: FAIL expected: FAIL
[Property word-spacing has initial value 0px]
expected: FAIL

View file

@ -1,7 +0,0 @@
[variable-definition-keywords.html]
[specified style revert]
expected: FAIL
[computed style revert]
expected: FAIL

View file

@ -110,9 +110,6 @@
[Testing 'visibility'.] [Testing 'visibility'.]
expected: FAIL expected: FAIL
[Testing 'word-spacing'.]
expected: FAIL
[Testing 'writing-mode'.] [Testing 'writing-mode'.]
expected: FAIL expected: FAIL