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::values::computed::effects::SimpleShadow;
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::image::{GradientKind, Image, PaintWorklet};
use style::values::specified::ui::CursorKind;
@ -2627,19 +2627,22 @@ impl BlockFlow {
_ => 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(
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 +
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
.right
.map(Au::from)
let right = extract_clip_component(&style_clip_rect.right)
.unwrap_or(stacking_relative_border_box.size.width);
let bottom = style_clip_rect
.bottom
.map(Au::from)
let bottom = extract_clip_component(&style_clip_rect.bottom)
.unwrap_or(stacking_relative_border_box.size.height);
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::generics::box_::{Perspective, VerticalAlign};
use style::values::generics::transform;
use style::Zero;
use webrender_api::{self, LayoutTransform};
// From gfxFontConstants.h in Firefox.

View file

@ -19,7 +19,7 @@ use std::fmt;
use std::sync::Arc;
use style::logical_geometry::LogicalSize;
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::Either;
@ -114,7 +114,9 @@ impl Flow for MulticolFlow {
let column_style = style.get_column();
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);
column_count = max(
1,

View file

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

View file

@ -13,6 +13,7 @@ autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated usi
class nsAtom;
namespace mozilla {
class WritingMode;
enum HalfCorner : uint8_t;
enum LogicalSide : uint8_t;
namespace css {
struct URLValue;
@ -39,8 +40,13 @@ include = ["cssparser", "style_traits"]
derive_eq = true
derive_neq = true
[macro_expansion]
bitflags = true
[enum]
derive_helper_methods = true
derive_const_casts = true
cast_assert_name = "MOZ_ASSERT"
[export]
prefix = "Style"
@ -77,8 +83,10 @@ include = [
"Resize",
"Overflow",
"LengthPercentage",
"LetterSpacing",
"NonNegativeLengthPercentage",
"LengthPercentageOrAuto",
"LineHeight",
"NonNegativeLengthPercentageOrAuto",
"Rect",
"IntersectionObserverRootMargin",
@ -88,16 +96,20 @@ include = [
"Position",
"BackgroundSize",
"BorderImageSlice",
"BorderSpacing",
"BorderRadius",
"NonNegativeLengthOrNumberRect",
"Perspective",
"ZIndex",
"TransformOrigin",
"WordBreak",
]
item_types = ["enums", "structs", "typedefs"]
[export.body]
"CSSPixelLength" = """
inline nscoord ToAppUnits() const;
inline bool IsZero() const;
"""
"LengthPercentage" = """
@ -114,6 +126,7 @@ item_types = ["enums", "structs", "typedefs"]
inline bool ConvertsToPercentage() const;
inline bool HasLengthAndPercentage() const;
inline float ToPercentage() const;
inline bool IsDefinitelyZero() const;
inline CSSCoord ResolveToCSSPixels(CSSCoord aPercentageBasisInCSSPixels) const;
template<typename T> inline CSSCoord ResolveToCSSPixelsWith(T aPercentageGetter) const;
template<typename T, typename U>
@ -125,7 +138,6 @@ item_types = ["enums", "structs", "typedefs"]
"""
"GenericLengthPercentageOrAuto" = """
inline const StyleLengthPercentage& AsLengthPercentage() const;
inline bool ConvertsToLength() const;
inline nscoord ToLength() const;
inline bool ConvertsToPercentage() const;
@ -135,8 +147,6 @@ item_types = ["enums", "structs", "typedefs"]
"""
"GenericSize" = """
inline const StyleLengthPercentage& AsLengthPercentage() const;
inline StyleExtremumLength AsExtremumLength() const;
inline bool ConvertsToLength() const;
inline nscoord ToLength() const;
inline bool ConvertsToPercentage() const;
@ -148,12 +158,9 @@ item_types = ["enums", "structs", "typedefs"]
"GenericFlexBasis" = """
inline bool IsAuto() const;
inline const StyleSize& AsSize() const;
"""
"GenericMaxSize" = """
inline const StyleLengthPercentage& AsLengthPercentage() const;
inline StyleExtremumLength AsExtremumLength() const;
inline bool ConvertsToLength() const;
inline nscoord ToLength() const;
inline bool ConvertsToPercentage() const;
@ -185,3 +192,7 @@ item_types = ["enums", "structs", "typedefs"]
inline const T& GetIEnd(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/
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::stylesheets::{Origin, PerOrigin};
use crate::Atom;
use cssparser::{Delimiter, Parser, ParserInput, SourcePosition, Token, TokenSerializationType};
use indexmap::IndexMap;
@ -490,6 +491,7 @@ fn parse_env_function<'i, 't>(
/// properties.
pub struct CustomPropertiesBuilder<'a> {
seen: PrecomputedHashSet<&'a Name>,
reverted: PerOrigin<PrecomputedHashSet<&'a Name>>,
may_have_cycles: bool,
custom_properties: Option<CustomPropertiesMap>,
inherited: Option<&'a Arc<CustomPropertiesMap>>,
@ -504,6 +506,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
) -> Self {
Self {
seen: PrecomputedHashSet::default(),
reverted: Default::default(),
may_have_cycles: false,
custom_properties: None,
inherited,
@ -512,13 +515,22 @@ impl<'a> CustomPropertiesBuilder<'a> {
}
/// 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);
if was_already_present {
return;
}
if !self.value_may_affect_style(name, specified_value) {
if !self.value_may_affect_style(name, value) {
return;
}
@ -530,7 +542,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
}
let map = self.custom_properties.as_mut().unwrap();
match *specified_value {
match *value {
CustomDeclarationValue::Value(ref unparsed_value) => {
let has_references = !unparsed_value.references.is_empty();
self.may_have_cycles |= has_references;
@ -554,6 +566,12 @@ impl<'a> CustomPropertiesBuilder<'a> {
map.insert(name.clone(), value);
},
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 => {
map.remove(name);
},
@ -587,10 +605,10 @@ impl<'a> CustomPropertiesBuilder<'a> {
// not existing in the map.
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
// the same specified value.
if existing_value == specified_value {
if existing_value == value {
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::image::{CompatMode, GradientItem, Image as GenericImage};
use crate::values::generics::rect::Rect;
use crate::Zero;
use app_units::Au;
use std::f32::consts::PI;
use style_traits::values::specified::AllowedNumericType;
@ -575,17 +576,15 @@ pub mod basic_shape {
//! Conversions from and to CSS shape representations.
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::{
StyleGeometryBox, StyleShapeSource, StyleShapeSourceType,
};
use crate::gecko_bindings::sugar::ns_style_coord::{CoordDataMut, CoordDataValue};
use crate::gecko_bindings::sugar::refptr::RefPtr;
use crate::values::computed::basic_shape::{
BasicShape, ClippingShape, FloatAreaShape, ShapeRadius,
};
use crate::values::computed::border::{BorderCornerRadius, BorderRadius};
use crate::values::computed::length::LengthPercentage;
use crate::values::computed::motion::OffsetPath;
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::{GeometryBox, ShapeBox, ShapeSource};
use crate::values::generics::border::BorderRadius as GenericBorderRadius;
use crate::values::generics::rect::Rect;
use crate::values::generics::NonNegative;
use crate::values::specified::SVGPathData;
use std::borrow::Borrow;
@ -706,8 +703,7 @@ pub mod basic_shape {
let r = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[1]);
let b = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[2]);
let l = LengthPercentage::from_gecko_style_coord(&other.mCoordinates[3]);
let round: BorderRadius = (&other.mRadius).into();
let round = if round.all_zero() { None } else { Some(round) };
let round = other.mRadius;
let rect = Rect::new(
t.expect("inset() offset should be a length, percentage, or calc value"),
r.expect("inset() offset should be a length, percentage, or calc value"),
@ -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 {
fn from(other: &'a nsStyleCoord) -> Self {
let other = other.borrow();

View file

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

View file

@ -17,12 +17,13 @@ use app_units::Au;
use euclid::Size2D;
fn viewport_size(device: &Device) -> Size2D<Au> {
let pc = device.pres_context();
if pc.mIsRootPaginatedDocument() != 0 {
// We want the page size, including unprintable areas and margins.
// FIXME(emilio, bug 1414600): Not quite!
let area = &pc.mPageSize;
return Size2D::new(Au(area.width), Au(area.height));
if let Some(pc) = device.pres_context() {
if pc.mIsRootPaginatedDocument() != 0 {
// We want the page size, including unprintable areas and margins.
// FIXME(emilio, bug 1414600): Not quite!
let area = &pc.mPageSize;
return Size2D::new(Au(area.width), Au(area.height));
}
}
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_bindings::bindings;
use crate::gecko_bindings::structs;
use crate::gecko_bindings::structs::{nsPresContext, RawGeckoPresContextBorrowed};
use crate::media_queries::MediaType;
use crate::properties::ComputedValues;
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,
/// and contains all the viewport rule state.
pub struct Device {
/// NB: The pres context lifetime is tied to the styleset, who owns the
/// stylist, and thus the `Device`, so having a raw pres context pointer
/// here is fine.
pres_context: RawGeckoPresContextBorrowed,
/// NB: The document owns the styleset, who owns the stylist, and thus the
/// `Device`, so having a raw document pointer here is fine.
document: *const structs::Document,
default_values: Arc<ComputedValues>,
/// The font size of the root element
/// This is set when computing the style of the root
@ -81,16 +79,16 @@ unsafe impl Send for Device {}
impl Device {
/// Trivially constructs a new `Device`.
pub fn new(pres_context: RawGeckoPresContextBorrowed) -> Self {
assert!(!pres_context.is_null());
pub fn new(document: *const structs::Document) -> Self {
assert!(!document.is_null());
let doc = unsafe { &*document };
let prefs = unsafe { &*bindings::Gecko_GetPrefSheetPrefs(doc) };
Device {
pres_context,
default_values: ComputedValues::default_values(unsafe {
&*(*pres_context).mDocument.mRawPtr
}),
document,
default_values: ComputedValues::default_values(doc),
// FIXME(bz): Seems dubious?
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_viewport_size: AtomicBool::new(false),
environment: CssEnvironment,
@ -112,11 +110,13 @@ impl Device {
/// Whether any animation name may be referenced from the style of any
/// element.
pub fn animation_name_may_be_referenced(&self, name: &KeyframesName) -> bool {
let pc = match self.pres_context() {
Some(pc) => pc,
None => return false,
};
unsafe {
bindings::Gecko_AnimationNameMayBeReferencedFromStyle(
self.pres_context(),
name.as_atom().as_ptr(),
)
bindings::Gecko_AnimationNameMayBeReferencedFromStyle(pc, name.as_atom().as_ptr())
}
}
@ -156,16 +156,29 @@ impl Device {
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.
#[inline]
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.
@ -195,13 +208,17 @@ impl Device {
/// Returns the current media type of the device.
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
// mMediaEmulated.
let context = self.pres_context();
let medium_to_use = if context.mIsEmulatingMedia() != 0 {
context.mMediaEmulated.mRawPtr
let medium_to_use = if pc.mIsEmulatingMedia() != 0 {
pc.mMediaEmulated.mRawPtr
} else {
context.mMedium
pc.mMedium
};
MediaType(CustomIdent(unsafe { Atom::from_raw(medium_to_use) }))
@ -209,7 +226,11 @@ impl Device {
/// Returns the current viewport size in app units.
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))
}
@ -227,34 +248,62 @@ impl Device {
/// Returns the device pixel ratio.
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 {
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;
TypedScale::new(au_per_px / au_per_dpx)
}
/// Returns whether document colors are enabled.
#[inline]
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.
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).
#[inline]
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.
#[inline]
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::{CounterStyleOrNone, NonNegative};
use crate::values::{Auto, Either, None_, Normal};
use crate::Atom;
use crate::{Atom, Zero};
use app_units::Au;
use cssparser::RGBA;
use nsstring::{nsACString, nsCStr};

View file

@ -1044,9 +1044,13 @@ impl FontMetricsProvider for GeckoFontMetricsProvider {
device: &Device,
) -> FontMetricsQueryResult {
use crate::gecko_bindings::bindings::Gecko_GetFontMetrics;
let pc = match device.pres_context() {
Some(pc) => pc,
None => return FontMetricsQueryResult::NotAvailable,
};
let gecko_metrics = unsafe {
Gecko_GetFontMetrics(
device.pres_context(),
pc,
wm.is_vertical() && !wm.is_sideways(),
font.gecko(),
font_size.0,
@ -1242,8 +1246,7 @@ impl<'le> TElement for GeckoElement<'le> {
}
fn owner_doc_matches_for_testing(&self, device: &Device) -> bool {
self.as_node().owner_doc().0 as *const structs::Document ==
device.pres_context().mDocument.mRawPtr
self.as_node().owner_doc().0 as *const structs::Document == device.document() as *const _
}
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_string_cache::Atom;
use crate::values::computed::{Angle, Length, LengthPercentage, Percentage};
use crate::Zero;
use std::marker::PhantomData;
use std::mem;
use std::ops::{Index, IndexMut};

View file

@ -5,8 +5,8 @@
//! Rust helpers for Gecko's `nsStyleCoord`.
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::{nsStyleCorners, nsStyleSides};
use crate::gecko_bindings::structs::{nsStyleUnion, nsStyleUnit, nscoord};
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.
///
/// 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::specified::{Integer, Length, Number, Resolution};
use crate::values::{serialize_atom_identifier, CSSFloat};
use crate::Atom;
use crate::{Atom, Zero};
use cssparser::{Parser, Token};
use num_traits::Zero;
use std::cmp::{Ordering, PartialOrd};
use std::fmt::{self, Write};
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::media_queries::Device;
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::CASCADE_PROPERTY;
use crate::rule_cache::{RuleCache, RuleCacheConditions};
use crate::rule_tree::{CascadeLevel, StrongRuleNode};
use crate::selector_parser::PseudoElement;
use crate::stylesheets::{Origin, PerOrigin};
use servo_arc::Arc;
use crate::shared_lock::StylesheetGuards;
use smallbitvec::SmallBitVec;
@ -251,7 +252,7 @@ where
for (declaration, cascade_level) in iter_declarations() {
declarations.push((declaration, cascade_level));
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;
}
let is_ua_or_user_rule = matches!(
cascade_level,
CascadeLevel::UANormal |
CascadeLevel::UserNormal |
CascadeLevel::UserImportant |
CascadeLevel::UAImportant
);
let is_ua_or_user_rule =
matches!(cascade_level.origin(), Origin::User | Origin::UserAgent);
if is_ua_or_user_rule {
return false;
}
@ -388,6 +383,7 @@ struct Cascade<'a, 'b: 'a> {
context: &'a mut computed::Context<'b>,
cascade_mode: CascadeMode<'a>,
seen: LonghandIdSet,
reverted: PerOrigin<LonghandIdSet>,
saved_font_size: Option<PropertyDeclaration>,
saved_font_family: Option<PropertyDeclaration>,
}
@ -398,6 +394,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
context,
cascade_mode,
seen: LonghandIdSet::default(),
reverted: Default::default(),
saved_font_size: None,
saved_font_family: None,
}
@ -488,6 +485,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
for (declaration, cascade_level) in declarations {
let declaration_id = declaration.id();
let origin = cascade_level.origin();
let longhand_id = match declaration_id {
PropertyDeclarationId::Longhand(id) => id,
PropertyDeclarationId::Custom(..) => continue,
@ -513,6 +511,10 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
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
// of links. When cascading visited styles, we can save time by
// 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);
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
// longhands and just use the physical ones, then rename
// 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::FontFamily)
{
use crate::properties::{CSSWideKeyword, WideKeywordDeclaration};
use crate::values::computed::FontSize;
// font-size must be explicitly inherited to handle lang
// changes and scriptlevel changes.
//
// FIXME(emilio): That looks a bit bogus...
let inherit = PropertyDeclaration::CSSWideKeyword(WideKeywordDeclaration {
id: LonghandId::FontSize,
keyword: CSSWideKeyword::Inherit,
});
self.apply_declaration_ignoring_phase(LonghandId::FontSize, &inherit);
self.context.for_non_inherited_property = None;
FontSize::cascade_inherit_font_size(&mut self.context);
}
}
}

View file

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

View file

@ -849,7 +849,7 @@ impl PropertyDeclarationBlock {
for declaration in self.normal_declaration_iter() {
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 servo_arc::{Arc, RawOffsetArc};
use std::marker::PhantomData;
use std::mem::{forget, uninitialized, transmute, zeroed};
use std::mem::{forget, uninitialized, zeroed};
use std::{cmp, ops, ptr};
use crate::values::{self, CustomIdent, Either, KeyframesName, None_};
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.
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;
let length = match v {
SVGLength::Length(length) => {
SVGLength::LengthPercentage(length) => {
self.gecko.mContextFlags &= !CONTEXT_VALUE;
length
}
SVGLength::ContextValue => {
self.gecko.mContextFlags |= CONTEXT_VALUE;
match longhands::${ident}::get_initial_value() {
SVGLength::Length(length) => length,
SVGLength::LengthPercentage(length) => length,
_ => unreachable!("Initial value should not be context-value"),
}
}
};
match 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())),
}
self.gecko.${gecko_ffi_name} = length;
}
pub fn copy_${ident}_from(&mut self, other: &Self) {
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 & !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 {
use crate::values::generics::svg::{SVGLength, SvgLengthPercentageOrNumber};
use crate::values::computed::LengthPercentage;
use crate::values::generics::svg::SVGLength;
use crate::gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE;
if (self.gecko.mContextFlags & CONTEXT_VALUE) != 0 {
return SVGLength::ContextValue;
}
let length = match self.gecko.${gecko_ffi_name}.as_value() {
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())
SVGLength::LengthPercentage(self.gecko.${gecko_ffi_name})
}
</%def>
@ -817,34 +792,23 @@ def set_gecko_property(ffi_name, expr):
}
</%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)]
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}));
v.0.height().to_gecko_style_coord(&mut self.gecko.${gecko_ffi_name}.data_at_mut(${y_index}));
self.gecko.${gecko_ffi_name}.${corner} = v;
}
#[allow(non_snake_case)]
pub fn copy_${ident}_from(&mut self, other: &Self) {
self.gecko.${gecko_ffi_name}.data_at_mut(${x_index})
.copy_from(&other.gecko.${gecko_ffi_name}.data_at(${x_index}));
self.gecko.${gecko_ffi_name}.data_at_mut(${y_index})
.copy_from(&other.gecko.${gecko_ffi_name}.data_at(${y_index}));
self.gecko.${gecko_ffi_name}.${corner} =
other.gecko.${gecko_ffi_name}.${corner};
}
#[allow(non_snake_case)]
pub fn reset_${ident}(&mut self, other: &Self) {
self.copy_${ident}_from(other)
}
#[allow(non_snake_case)]
pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
use crate::values::computed::border::BorderCornerRadius;
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)
self.gecko.${gecko_ffi_name}.${corner}
}
</%def>
@ -891,13 +855,13 @@ def set_gecko_property(ffi_name, expr):
transform_functions = [
("Matrix3D", "matrix3d", ["number"] * 16),
("Matrix", "matrix", ["number"] * 6),
("Translate", "translate", ["lp", "optional_lp"]),
("Translate", "translate", ["lp", "lp"]),
("Translate3D", "translate3d", ["lp", "lp", "length"]),
("TranslateX", "translatex", ["lp"]),
("TranslateY", "translatey", ["lp"]),
("TranslateZ", "translatez", ["length"]),
("Scale3D", "scale3d", ["number"] * 3),
("Scale", "scale", ["number", "optional_number"]),
("Scale", "scale", ["number", "number"]),
("ScaleX", "scalex", ["number"]),
("ScaleY", "scaley", ["number"]),
("ScaleZ", "scalez", ["number"]),
@ -906,7 +870,7 @@ transform_functions = [
("RotateX", "rotatex", ["angle"]),
("RotateY", "rotatey", ["angle"]),
("RotateZ", "rotatez", ["angle"]),
("Skew", "skew", ["angle", "optional_angle"]),
("Skew", "skew", ["angle", "angle"]),
("SkewX", "skewx", ["angle"]),
("SkewY", "skewy", ["angle"]),
("Perspective", "perspective", ["length"]),
@ -917,7 +881,6 @@ transform_functions = [
<%def name="transform_function_arm(name, keyword, items)">
<%
has_optional = items[-1].startswith("optional_")
pattern = None
if keyword == "matrix3d":
# m11: number1, m12: number2, ..
@ -955,36 +918,20 @@ transform_functions = [
}
%>
crate::values::generics::transform::TransformOperation::${name}${pattern} => {
% if has_optional:
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
let len = ${len(items) + 1};
bindings::Gecko_CSSValue_SetFunction(gecko_value, len);
bindings::Gecko_CSSValue_SetKeyword(
bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0),
structs::nsCSSKeyword::eCSSKeyword_${keyword}
);
% 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":
debug_assert!(!${item}${index + 1}.0.is_empty());
% endif
${css_value_setters[replaced_item] % (
${css_value_setters[item] % (
"bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d)" % (index + 1),
replaced_item + str(index + 1)
item + str(index + 1)
)};
% if item.startswith("optional"):
}
% endif
% endfor
}
</%def>
@ -995,8 +942,6 @@ transform_functions = [
css_value_getters = {
"length" : "Length::new(bindings::Gecko_CSSValue_GetNumber(%s))",
"lp" : "%s.get_length_percentage()",
"lpon" : "Either::Second(%s.get_length_percentage())",
"lon" : "Either::First(%s.get_length())",
"angle" : "%s.get_angle()",
"number" : "bindings::Gecko_CSSValue_GetNumber(%s)",
"percentage" : "Percentage(bindings::Gecko_CSSValue_GetPercentage(%s))",
@ -1034,20 +979,11 @@ transform_functions = [
${field_names[index]}:
% endif
<%
getter = css_value_getters[item.replace("optional_", "")] % (
getter = css_value_getters[item] % (
"bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d)" % (index + 1)
)
%>
% if item.startswith("optional_"):
if (**gecko_value.mValue.mArray.as_ref()).mCount == ${index + 1} {
None
} else {
Some(${getter})
}
% else:
${getter}
% endif
,
${getter},
% endfor
${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.
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,
"ColorOrAuto": impl_color,
"GreaterThanOrEqualToOneNumber": impl_simple,
"Integer": impl_simple,
"length::LengthOrAuto": impl_style_coord,
"length::LengthOrNormal": impl_style_coord,
"length::NonNegativeLengthOrAuto": impl_style_coord,
"length::NonNegativeLengthPercentageOrNormal": impl_style_coord,
"FillRule": impl_simple,
"FlexBasis": impl_simple,
"Length": impl_absolute_length,
"LengthOrNormal": impl_style_coord,
"LengthPercentage": impl_simple,
"LengthPercentageOrAuto": impl_style_coord,
"MaxSize": impl_simple,
"Size": impl_simple,
"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,
"SVGLength": impl_svg_length,
"SVGOpacity": impl_svg_opacity,
"SVGPaint": impl_svg_paint,
"SVGWidth": impl_svg_length,
"Transform": impl_transform,
"TransformOrigin": impl_simple,
"UserSelect": impl_simple,
"url::UrlOrNone": impl_css_url,
"ZIndex": impl_simple,
}
def longhand_method(longhand):
@ -1354,15 +1258,12 @@ impl Clone for ${style_struct.gecko_struct_name} {
args.update(keyword=longhand.keyword)
if "font" in longhand.ident:
args.update(cast_type=longhand.cast_type)
else:
elif longhand.predefined_type in predefined_types:
method = predefined_types[longhand.predefined_type]
else:
method = impl_simple
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} {
/*
@ -1374,7 +1275,7 @@ impl ${style_struct.gecko_struct_name} {
* Auto-Generated Methods.
*/
<%
for longhand in picked_longhands:
for longhand in longhands:
longhand_method(longhand)
%>
}
@ -1387,14 +1288,6 @@ class Side(object):
self.ident = name.lower()
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):
def __init__(self, name):
self.ident = "grid-" + name.lower()
@ -1402,19 +1295,12 @@ class GridLine(object):
self.gecko = "m" + to_camel_case(self.ident)
SIDES = [Side("Top", 0), Side("Right", 1), Side("Bottom", 2), Side("Left", 3)]
CORNERS = [Corner("Top", "Left", 0), Corner("Top", "Right", 1),
Corner("Bottom", "Right", 2), Corner("Bottom", "Left", 3)]
CORNERS = ["top_left", "top_right", "bottom_right", "bottom_left"]
GRID_LINES = map(GridLine, ["row-start", "row-end", "column-start", "column-end"])
%>
#[allow(dead_code)]
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.
% for side in SIDES:
{ 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)
for x in SIDES
for y in ["color", "style", "width"]] +
["border-{0}-radius".format(x.ident.replace("_", "-"))
["border-{0}-radius".format(x.replace("_", "-"))
for x in CORNERS]) %>
<%self:impl_trait style_struct_name="Border"
@ -1494,10 +1380,9 @@ fn static_assert() {
% endfor
% for corner in CORNERS:
<% impl_corner_style_coord("border_%s_radius" % corner.ident,
<% impl_corner_style_coord("border_%s_radius" % corner,
"mBorderRadius",
corner.x_index,
corner.y_index) %>
corner) %>
% endfor
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>
<% 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]) %>
<%self:impl_trait style_struct_name="Outline"
skip_longhands="${skip_outline_longhands}">
@ -2059,10 +1944,9 @@ fn static_assert() {
round_to_pixels=True) %>
% 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",
corner.x_index,
corner.y_index) %>
corner) %>
% endfor
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
font-style font-stretch -moz-script-level
font-synthesis -x-lang font-variant-alternates
font-variant-east-asian font-variant-ligatures
font-variant-numeric font-language-override
@ -2863,6 +2748,7 @@ fn static_assert() {
animation-iteration-count animation-timing-function
clear transition-duration transition-delay
transition-timing-function transition-property
transform-style
rotate scroll-snap-points-x scroll-snap-points-y
scroll-snap-coordinate -moz-binding will-change
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_SIZE;
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_ALL_BITS;
use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS;
@ -3315,9 +3200,6 @@ fn static_assert() {
if v.contains(SpecifiedValue::LAYOUT) {
bitfield |= NS_STYLE_CONTAIN_LAYOUT;
}
if v.contains(SpecifiedValue::STYLE) {
bitfield |= NS_STYLE_CONTAIN_STYLE;
}
if v.contains(SpecifiedValue::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_SIZE;
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_ALL_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 {
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 {
servo_flags.insert(SpecifiedValue::PAINT);
}
@ -3870,6 +3748,7 @@ fn static_assert() {
#[allow(non_snake_case)]
pub fn set__moz_image_region(&mut self, v: longhands::_moz_image_region::computed_value::T) {
use crate::values::Either;
use crate::values::generics::length::LengthPercentageOrAuto::*;
match v {
Either::Second(_auto) => {
@ -3879,15 +3758,21 @@ fn static_assert() {
self.gecko.mImageRegion.height = 0;
}
Either::First(rect) => {
self.gecko.mImageRegion.x = rect.left.map(Au::from).unwrap_or(Au(0)).0;
self.gecko.mImageRegion.y = rect.top.map(Au::from).unwrap_or(Au(0)).0;
self.gecko.mImageRegion.x = match rect.left {
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 {
Some(value) => (Au::from(value) - Au(self.gecko.mImageRegion.y)).0,
None => 0,
LengthPercentage(value) => (Au::from(value) - Au(self.gecko.mImageRegion.y)).0,
Auto => 0,
};
self.gecko.mImageRegion.width = match rect.right {
Some(value) => (Au::from(value) - Au(self.gecko.mImageRegion.x)).0,
None => 0,
LengthPercentage(value) => (Au::from(value) - Au(self.gecko.mImageRegion.x)).0,
Auto => 0,
};
}
}
@ -3896,6 +3781,7 @@ fn static_assert() {
#[allow(non_snake_case)]
pub fn clone__moz_image_region(&self) -> longhands::_moz_image_region::computed_value::T {
use crate::values::{Auto, Either};
use crate::values::generics::length::LengthPercentageOrAuto::*;
use crate::values::computed::ClipRect;
// 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 {
top: Some(Au(self.gecko.mImageRegion.y).into()),
right: Some(Au(self.gecko.mImageRegion.width + self.gecko.mImageRegion.x).into()),
bottom: Some(Au(self.gecko.mImageRegion.height + self.gecko.mImageRegion.y).into()),
left: Some(Au(self.gecko.mImageRegion.x).into()),
top: LengthPercentage(Au(self.gecko.mImageRegion.y).into()),
right: LengthPercentage(Au(self.gecko.mImageRegion.width + self.gecko.mImageRegion.x).into()),
bottom: LengthPercentage(Au(self.gecko.mImageRegion.height + self.gecko.mImageRegion.y).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_RIGHT_AUTO;
use crate::gecko_bindings::structs::NS_STYLE_CLIP_BOTTOM_AUTO;
use crate::values::generics::length::LengthPercentageOrAuto::*;
use crate::values::Either;
match v {
Either::First(rect) => {
self.gecko.mClipFlags = NS_STYLE_CLIP_RECT as u8;
if let Some(left) = rect.left {
self.gecko.mClip.x = left.to_i32_au();
} else {
self.gecko.mClip.x = 0;
self.gecko.mClipFlags |= NS_STYLE_CLIP_LEFT_AUTO as u8;
}
self.gecko.mClip.x = match rect.left {
LengthPercentage(l) => l.to_i32_au(),
Auto => {
self.gecko.mClipFlags |= NS_STYLE_CLIP_LEFT_AUTO as u8;
0
}
};
if let Some(top) = rect.top {
self.gecko.mClip.y = top.to_i32_au();
} else {
self.gecko.mClip.y = 0;
self.gecko.mClipFlags |= NS_STYLE_CLIP_TOP_AUTO as u8;
}
self.gecko.mClip.y = match rect.top {
LengthPercentage(l) => l.to_i32_au(),
Auto => {
self.gecko.mClipFlags |= NS_STYLE_CLIP_TOP_AUTO as u8;
0
}
};
if let Some(bottom) = rect.bottom {
self.gecko.mClip.height = (Au::from(bottom) - Au(self.gecko.mClip.y)).0;
} else {
self.gecko.mClip.height = 1 << 30; // NS_MAXSIZE
self.gecko.mClipFlags |= NS_STYLE_CLIP_BOTTOM_AUTO as u8;
}
self.gecko.mClip.height = match rect.bottom {
LengthPercentage(l) => (Au::from(l) - Au(self.gecko.mClip.y)).0,
Auto => {
self.gecko.mClipFlags |= NS_STYLE_CLIP_BOTTOM_AUTO as u8;
1 << 30 // NS_MAXSIZE
}
};
if let Some(right) = rect.right {
self.gecko.mClip.width = (Au::from(right) - Au(self.gecko.mClip.x)).0;
} else {
self.gecko.mClip.width = 1 << 30; // NS_MAXSIZE
self.gecko.mClipFlags |= NS_STYLE_CLIP_RIGHT_AUTO as u8;
}
self.gecko.mClip.width = match rect.right {
LengthPercentage(l) => (Au::from(l) - Au(self.gecko.mClip.x)).0,
Auto => {
self.gecko.mClipFlags |= NS_STYLE_CLIP_RIGHT_AUTO as u8;
1 << 30 // NS_MAXSIZE
}
};
},
Either::Second(_auto) => {
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_RIGHT_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::Either;
if self.gecko.mClipFlags == NS_STYLE_CLIP_AUTO as u8 {
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 })
return ClipRectOrAuto::auto()
}
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"
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">
<% text_align_keyword = Keyword("text-align",
@ -4270,78 +4161,6 @@ fn static_assert() {
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) {
if self.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 {
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::structs::{StyleBasicShape, StyleBasicShapeType, StyleShapeSourceType};
use crate::gecko_bindings::structs::{StyleGeometryBox, StyleShapeSource};
use crate::gecko::conversions::basic_shape::set_corners_from_radius;
use crate::gecko::values::GeckoStyleCoordConvertible;
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]);
shape.mCoordinates[3].leaky_set_null();
inset.rect.3.to_gecko_style_coord(&mut shape.mCoordinates[3]);
set_corners_from_radius(inset.round, &mut shape.mRadius);
shape.mRadius = inset.round;
}
BasicShape::Circle(circ) => {
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) {
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 {
SVGStrokeDashArray::Values(v) => {
@ -4757,12 +4574,7 @@ clip-path
bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut self.gecko, v.len() as u32);
}
for (gecko, servo) in self.gecko.mStrokeDasharray.iter_mut().zip(v) {
match servo {
SvgLengthPercentageOrNumber::LengthPercentage(lp) =>
gecko.set(lp),
SvgLengthPercentageOrNumber::Number(num) =>
gecko.set_value(CoordDataValue::Factor(num.into())),
}
*gecko = servo;
}
}
SVGStrokeDashArray::ContextValue => {
@ -4790,32 +4602,13 @@ clip-path
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::values::computed::LengthPercentage;
use crate::values::generics::NonNegative;
use crate::values::generics::svg::{SVGStrokeDashArray, SvgLengthPercentageOrNumber};
use crate::values::generics::svg::SVGStrokeDashArray;
if self.gecko.mContextFlags & CONTEXT_VALUE != 0 {
debug_assert_eq!(self.gecko.mStrokeDasharray.len(), 0);
return SVGStrokeDashArray::ContextValue;
}
let mut vec = vec![];
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)
SVGStrokeDashArray::Values(self.gecko.mStrokeDasharray.iter().cloned().collect())
}
#[allow(non_snake_case)]

View file

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

View file

@ -452,14 +452,23 @@ impl AnimationValue {
% for prop in data.longhands:
% if prop.animatable:
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 {
% if not prop.style_struct.inherited:
CSSWideKeyword::Revert |
CSSWideKeyword::Unset |
% endif
CSSWideKeyword::Initial => {
initial.get_${prop.style_struct.name_lower}()
},
% if prop.style_struct.inherited:
CSSWideKeyword::Revert |
CSSWideKeyword::Unset |
% endif
CSSWideKeyword::Inherit => {
@ -802,29 +811,29 @@ impl ToAnimatedZero for Visibility {
impl Animate for ClipRect {
#[inline]
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
use crate::values::computed::Length;
let animate_component = |this: &Option<Length>, other: &Option<Length>| {
match (this.animate(other, procedure)?, procedure) {
(None, Procedure::Interpolate { .. }) => Ok(None),
(None, _) => Err(()),
(result, _) => Ok(result),
use crate::values::computed::LengthOrAuto;
let animate_component = |this: &LengthOrAuto, other: &LengthOrAuto| {
let result = this.animate(other, procedure)?;
if let Procedure::Interpolate { .. } = procedure {
return 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 {
top: animate_component(&self.top, &other.top)?,
right: animate_component(&self.right, &other.right)?,
top: animate_component(&self.top, &other.top)?,
right: animate_component(&self.right, &other.right)?,
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',
'HueRotate', 'Invert', 'Opacity', 'Saturate',

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -11,7 +11,7 @@ use crate::applicable_declarations::ApplicableDeclarationList;
use crate::gecko::selector_parser::PseudoElement;
use crate::properties::{Importance, LonghandIdSet, PropertyDeclarationBlock};
use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
use crate::stylesheets::StyleRule;
use crate::stylesheets::{Origin, StyleRule};
use crate::thread_state;
#[cfg(feature = "gecko")]
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.
#[inline]
pub fn is_animation(&self) -> bool {

View file

@ -36,6 +36,25 @@ impl Origin {
_ => 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! {
@ -57,7 +76,11 @@ impl OriginSet {
/// See the `OriginSet` documentation for information about the order
/// origins are iterated.
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 {
set: OriginSet,
cur: i8,
rev: bool,
}
impl Iterator for OriginSetIterator {
@ -88,7 +112,11 @@ impl Iterator for OriginSetIterator {
loop {
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()) {
return Some(origin);

View file

@ -686,7 +686,7 @@ where
// sibling or cousin. Otherwise, recascading a bunch of identical
// 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
// handling above.
//

View file

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

View file

@ -8,10 +8,8 @@ use super::{Animate, Procedure, ToAnimatedZero};
use crate::properties::animated_properties::ListAnimation;
use crate::values::animated::color::Color as AnimatedColor;
use crate::values::computed::url::ComputedUrl;
use crate::values::computed::{LengthPercentage, Number, NumberOrPercentage};
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::svg::{SVGLength, SVGPaint, SvgLengthPercentageOrNumber};
use crate::values::generics::svg::{SVGOpacity, SVGStrokeDashArray};
use crate::values::generics::svg::{SVGPaint, SVGStrokeDashArray};
/// Animated SVGPaint.
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>
impl<L> Animate for SVGStrokeDashArray<L>
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::{Rotate, Scale, Translate};
use crate::values::CSSFloat;
use num_traits::Zero;
use crate::Zero;
use std::cmp;
// ------------------------------------
@ -1003,18 +1003,12 @@ impl Animate for ComputedTransformOperation {
(&TransformOperation::Matrix(ref this), &TransformOperation::Matrix(ref other)) => {
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 tx, ref ty),
) => Ok(TransformOperation::Skew(
fx.animate(tx, procedure)?,
Some(
fy.unwrap_or(Angle::zero())
.animate(&ty.unwrap_or(Angle::zero()), procedure)?,
),
fy.animate(ty, procedure)?,
)),
(&TransformOperation::SkewX(ref f), &TransformOperation::SkewX(ref t)) => {
Ok(TransformOperation::SkewX(f.animate(t, procedure)?))
@ -1030,22 +1024,12 @@ impl Animate for ComputedTransformOperation {
fy.animate(ty, 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 tx, ref ty),
) => Ok(TransformOperation::Translate(
fx.animate(tx, procedure)?,
Some(
fy.unwrap_or(LengthPercentage::zero())
.animate(&ty.unwrap_or(LengthPercentage::zero()), procedure)?,
),
fy.animate(ty, procedure)?,
)),
(&TransformOperation::TranslateX(ref f), &TransformOperation::TranslateX(ref t)) => {
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(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 tx, ref ty),
) => Ok(TransformOperation::Scale(
animate_multiplicative_factor(*fx, *tx, procedure)?,
Some(animate_multiplicative_factor(
fy.unwrap_or(*fx),
ty.unwrap_or(*tx),
procedure,
)?),
animate_multiplicative_factor(*fy, *ty, procedure)?,
)),
(
&TransformOperation::Rotate3D(fx, fy, fz, fa),

View file

@ -6,7 +6,7 @@
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::CSSFloat;
use num_traits::Zero;
use crate::Zero;
use std::f64::consts::PI;
use std::fmt::{self, Write};
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::BorderSpacing as GenericBorderSpacing;
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::Zero;
use app_units::Au;
pub use crate::values::specified::border::BorderImageRepeat;
@ -59,7 +60,7 @@ impl BorderImageSlice {
impl BorderSpacing {
/// Returns `0 0`.
pub fn zero() -> Self {
GenericBorderSpacing(Size::new(
GenericBorderSpacing(Size2D::new(
NonNegativeLength::zero(),
NonNegativeLength::zero(),
))
@ -75,29 +76,3 @@ impl BorderSpacing {
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::{
GenericLengthOrNumber, MaxSize as GenericMaxSize, Size as GenericSize,
};
use crate::values::generics::transform::IsZeroLength;
use crate::values::generics::NonNegative;
use crate::values::specified::length::ViewportPercentageLength;
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 ordered_float::NotNan;
use std::fmt::{self, Write};
@ -203,12 +203,7 @@ impl LengthPercentage {
return None;
}
if self.clamping_mode.clamp(self.percentage.0) != self.percentage.0 {
debug_assert!(self.was_calc);
return None;
}
Some(self.percentage)
Some(Percentage(self.clamping_mode.clamp(self.percentage.0)))
}
/// Convert the computed value into used value.
@ -347,12 +342,6 @@ impl ToComputedValue for specified::CalcLengthPercentage {
}
impl LengthPercentage {
#[inline]
#[allow(missing_docs)]
pub fn zero() -> LengthPercentage {
LengthPercentage::new(Length::new(0.), None)
}
/// 1px length value for SVG defaults
#[inline]
pub fn one() -> LengthPercentage {
@ -433,14 +422,13 @@ impl ToComputedValue for specified::LengthPercentage {
}
fn from_computed_value(computed: &LengthPercentage) -> Self {
let length = computed.unclamped_length();
if let Some(p) = computed.as_percentage() {
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(
&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]
fn is_zero_length(&self) -> bool {
fn is_zero(&self) -> bool {
self.is_definitely_zero()
}
}
@ -459,12 +451,6 @@ impl IsZeroLength for LengthPercentage {
/// length-percentage or auto.
macro_rules! computed_length_percentage_or_auto {
($inner:ty) => {
/// Returns the `0` value.
#[inline]
pub fn zero() -> Self {
generics::LengthPercentageOrAuto::LengthPercentage(<$inner>::zero())
}
/// Returns the used value.
#[inline]
pub fn to_used_value(&self, percentage_basis: Au) -> Option<Au> {
@ -553,12 +539,6 @@ impl From<Au> for LengthPercentage {
}
impl NonNegativeLengthPercentage {
/// Get zero value.
#[inline]
pub fn zero() -> Self {
NonNegative(LengthPercentage::zero())
}
/// Returns true if the computed value is absolute 0 or 0%.
#[inline]
pub fn is_definitely_zero(&self) -> bool {
@ -662,12 +642,16 @@ impl CSSPixelLength {
pub fn clamp_to_non_negative(self) -> Self {
CSSPixelLength::new(self.0.max(0.))
}
}
/// Zero value
#[inline]
pub fn zero() -> Self {
impl Zero for CSSPixelLength {
fn zero() -> Self {
CSSPixelLength::new(0.)
}
fn is_zero(&self) -> bool {
self.px() == 0.
}
}
impl ToCss for CSSPixelLength {
@ -717,7 +701,10 @@ impl From<Au> for CSSPixelLength {
pub type Length = CSSPixelLength;
/// 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.
pub type LengthOrNumber = GenericLengthOrNumber<Length, Number>;
@ -749,12 +736,6 @@ impl NonNegativeLength {
NonNegative(Length::new(px.max(0.)))
}
/// Return a zero value.
#[inline]
pub fn zero() -> Self {
Self::new(0.)
}
/// Return the pixel value of |NonNegativeLength|.
#[inline]
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.
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::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
use super::generics::transform::IsParallelTo;
use super::generics::{GreaterThanOrEqualToOne, NonNegative};
use super::generics::{self, GreaterThanOrEqualToOne, NonNegative};
use super::specified;
use super::{CSSFloat, CSSInteger};
use crate::context::QuirksMode;
@ -27,8 +27,6 @@ use euclid::Size2D;
use std::cell::RefCell;
use std::cmp;
use std::f32;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
#[cfg(feature = "gecko")]
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::length::{CSSPixelLength, ExtremumLength, NonNegativeLength};
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};
#[cfg(feature = "gecko")]
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::table::XSpan;
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::time::Time;
pub use self::transform::{Rotate, Scale, Transform, TransformOperation};
@ -637,52 +635,8 @@ impl From<CSSInteger> for PositiveInteger {
}
}
#[allow(missing_docs)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
#[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(...)
pub type ClipRect = generics::ClipRect<LengthOrAuto>;
/// rect(...) | auto
pub type ClipRectOrAuto = Either<ClipRect, Auto>;

View file

@ -7,6 +7,7 @@
use crate::values::animated::ToAnimatedValue;
use crate::values::generics::NonNegative;
use crate::values::{serialize_percentage, CSSFloat};
use crate::Zero;
use std::fmt;
use style_traits::{CssWriter, ToCss};
@ -31,12 +32,6 @@ use style_traits::{CssWriter, ToCss};
pub struct Percentage(pub CSSFloat);
impl Percentage {
/// 0%
#[inline]
pub fn zero() -> Self {
Percentage(0.)
}
/// 100%
#[inline]
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 {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
@ -69,12 +74,6 @@ impl ToCss for Percentage {
pub type NonNegativePercentage = NonNegative<Percentage>;
impl NonNegativePercentage {
/// 0%
#[inline]
pub fn zero() -> Self {
NonNegative(Percentage::zero())
}
/// 100%
#[inline]
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::ZIndex as GenericZIndex;
pub use crate::values::specified::position::{GridAutoFlow, GridTemplateAreas};
use crate::Zero;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};

View file

@ -6,10 +6,10 @@
use crate::values::computed::color::Color;
use crate::values::computed::url::ComputedUrl;
use crate::values::computed::{LengthPercentage, NonNegativeLengthPercentage};
use crate::values::computed::{NonNegativeNumber, Number, Opacity};
use crate::values::computed::{LengthPercentage, NonNegativeLengthPercentage, Opacity};
use crate::values::generics::svg as generic;
use crate::values::RGBA;
use crate::Zero;
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
pub type SVGLength = generic::SVGLength<SvgLengthPercentageOrNumber>;
pub type SVGLength = generic::SVGLength<LengthPercentage>;
impl SVGLength {
/// `0px`
pub fn zero() -> Self {
generic::SVGLength::Length(generic::SvgLengthPercentageOrNumber::LengthPercentage(
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())
},
}
generic::SVGLength::LengthPercentage(LengthPercentage::zero())
}
}
/// An non-negative wrapper of SVGLength.
pub type SVGWidth = generic::SVGLength<NonNegativeSvgLengthPercentageOrNumber>;
pub type SVGWidth = generic::SVGLength<NonNegativeLengthPercentage>;
impl SVGWidth {
/// `1px`.
pub fn one() -> Self {
use crate::values::generics::NonNegative;
generic::SVGLength::Length(generic::SvgLengthPercentageOrNumber::LengthPercentage(
NonNegative(LengthPercentage::one()),
))
generic::SVGLength::LengthPercentage(NonNegative(LengthPercentage::one()))
}
}
/// [ <length> | <percentage> | <number> ]# | context-value
pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<NonNegativeSvgLengthPercentageOrNumber>;
pub type SVGStrokeDashArray = generic::SVGStrokeDashArray<NonNegativeLengthPercentage>;
impl Default for SVGStrokeDashArray {
fn default() -> Self {

View file

@ -7,28 +7,97 @@
#[cfg(feature = "servo")]
use crate::properties::StyleBuilder;
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::LineHeight as GenericLineHeight;
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::{CSSFloat, CSSInteger};
use crate::Zero;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
pub use crate::values::specified::OverflowWrap;
pub use crate::values::specified::TextAlignKeyword as TextAlign;
pub use crate::values::specified::TextEmphasisPosition;
pub use crate::values::specified::{OverflowWrap, WordBreak};
/// A computed value for the `initial-letter` property.
pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>;
/// 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.
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.
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::computed::{Angle, Integer, Length, LengthPercentage, Number, Percentage};
use crate::values::generics::transform as generic;
use crate::Zero;
use euclid::{Transform3D, Vector3D};
use num_traits::Zero;
pub use crate::values::generics::transform::TransformStyle;
@ -371,15 +371,14 @@ impl TransformOperation {
pub fn to_translate_3d(&self) -> Self {
match *self {
generic::TransformOperation::Translate3D(..) => self.clone(),
generic::TransformOperation::TranslateX(ref x) |
generic::TransformOperation::Translate(ref x, None) => {
generic::TransformOperation::TranslateX(ref x) => {
generic::TransformOperation::Translate3D(
x.clone(),
LengthPercentage::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::TranslateY(ref y) => {
@ -426,10 +425,7 @@ impl TransformOperation {
pub fn to_scale_3d(&self) -> Self {
match *self {
generic::TransformOperation::Scale3D(..) => self.clone(),
generic::TransformOperation::Scale(s, None) => {
generic::TransformOperation::Scale3D(s, s, 1.)
},
generic::TransformOperation::Scale(x, Some(y)) => {
generic::TransformOperation::Scale(x, y) => {
generic::TransformOperation::Scale3D(x, y, 1.)
},
generic::TransformOperation::ScaleX(x) => {
@ -494,7 +490,7 @@ impl ToAnimatedZero for TransformOperation {
Ok(generic::TransformOperation::Scale3D(1.0, 1.0, 1.0))
},
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::ScaleY(..) => Ok(generic::TransformOperation::ScaleY(1.0)),
@ -585,7 +581,7 @@ impl Translate {
match *self {
generic::Translate::None => None,
generic::Translate::Translate(tx, ty) => {
Some(generic::TransformOperation::Translate(tx, Some(ty)))
Some(generic::TransformOperation::Translate(tx, ty))
},
generic::Translate::Translate3D(tx, ty, tz) => {
Some(generic::TransformOperation::Translate3D(tx, ty, tz))
@ -596,9 +592,7 @@ impl Translate {
/// Convert Translate to TransformOperation.
pub fn from_transform_operation(operation: &TransformOperation) -> Translate {
match *operation {
generic::TransformOperation::Translate(tx, Some(ty)) => {
generic::Translate::Translate(tx, ty)
},
generic::TransformOperation::Translate(tx, ty) => generic::Translate::Translate(tx, ty),
generic::TransformOperation::Translate3D(tx, ty, tz) => {
generic::Translate::Translate3D(tx, ty, tz)
},
@ -615,7 +609,7 @@ impl Scale {
pub fn to_transform_operation(&self) -> Option<TransformOperation> {
match *self {
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) => {
Some(generic::TransformOperation::Scale3D(sx, sy, sz))
},
@ -625,8 +619,7 @@ impl Scale {
/// Convert Scale to TransformOperation.
pub fn from_transform_operation(operation: &TransformOperation) -> Scale {
match *operation {
generic::TransformOperation::Scale(sx, Some(sy)) => generic::Scale::Scale(sx, sy),
generic::TransformOperation::Scale(sx, None) => generic::Scale::Scale(sx, sx),
generic::TransformOperation::Scale(sx, sy) => generic::Scale::Scale(sx, sy),
generic::TransformOperation::Scale3D(sx, sy, sz) => generic::Scale::Scale3D(sx, sy, sz),
_ => unreachable!("Found unexpected value for scale"),
}

View file

@ -5,8 +5,13 @@
//! Generic types for CSS values related to backgrounds.
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.
#[derive(
@ -21,6 +26,7 @@ use style_traits::{CssWriter, ToCss};
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
)]
#[repr(C, u8)]
pub enum GenericBackgroundSize<LengthPercent> {
@ -29,6 +35,10 @@ pub enum GenericBackgroundSize<LengthPercent> {
/// Explicit width.
width: GenericLengthPercentageOrAuto<LengthPercent>,
/// 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>,
},
/// `cover`
@ -41,32 +51,6 @@ pub enum GenericBackgroundSize<LengthPercent> {
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> {
/// Returns `auto auto`.
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::rect::Rect;
use crate::values::specified::SVGPathData;
use crate::Zero;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
@ -127,7 +128,7 @@ pub enum BasicShape<H, V, LengthPercentage, NonNegativeLengthPercentage> {
)]
pub struct InsetRect<LengthPercentage, NonNegativeLengthPercentage> {
pub rect: Rect<LengthPercentage>,
pub round: Option<BorderRadius<NonNegativeLengthPercentage>>,
pub round: BorderRadius<NonNegativeLengthPercentage>,
}
/// <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>
where
Length: ToCss + PartialEq,
NonNegativeLength: ToCss + PartialEq,
NonNegativeLength: ToCss + PartialEq + Zero,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
@ -319,9 +320,9 @@ where
{
dest.write_str("inset(")?;
self.rect.to_css(dest)?;
if let Some(ref radius) = self.round {
if !self.round.is_zero() {
dest.write_str(" round ")?;
radius.to_css(dest)?;
self.round.to_css(dest)?;
}
dest.write_str(")")
}

View file

@ -5,7 +5,8 @@
//! Generic types for CSS values related to borders.
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 style_traits::{CssWriter, ToCss};
@ -53,12 +54,25 @@ pub use self::GenericBorderImageSlice as BorderImageSlice;
ToComputedValue,
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> {
/// Trivially create a `BorderCornerRadius`.
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,
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> {
/// Trivially create a `BorderCornerRadius`.
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,
ToComputedValue,
)]
pub struct BorderRadius<LengthPercentage> {
#[repr(C)]
pub struct GenericBorderRadius<LengthPercentage> {
/// The top left radius.
pub top_left: BorderCornerRadius<LengthPercentage>,
pub top_left: GenericBorderCornerRadius<LengthPercentage>,
/// The top right radius.
pub top_right: BorderCornerRadius<LengthPercentage>,
pub top_right: GenericBorderCornerRadius<LengthPercentage>,
/// The bottom right radius.
pub bottom_right: BorderCornerRadius<LengthPercentage>,
pub bottom_right: GenericBorderCornerRadius<LengthPercentage>,
/// The bottom left radius.
pub bottom_left: BorderCornerRadius<LengthPercentage>,
pub bottom_left: GenericBorderCornerRadius<LengthPercentage>,
}
pub use self::GenericBorderRadius as BorderRadius;
impl<L> BorderRadius<L> {
/// Returns a new `BorderRadius<L>`.
#[inline]
@ -128,12 +146,7 @@ impl<L> BorderRadius<L> {
bottom_left: bl,
}
}
}
impl<L> BorderRadius<L>
where
L: PartialEq + ToCss,
{
/// Serialises two given rects following the syntax of the `border-radius``
/// property.
pub fn serialize_rects<W>(
@ -142,14 +155,11 @@ where
dest: &mut CssWriter<W>,
) -> fmt::Result
where
L: PartialEq + ToCss,
W: Write,
{
widths.to_css(dest)?;
if widths.0 != heights.0 ||
widths.1 != heights.1 ||
widths.2 != heights.2 ||
widths.3 != heights.3
{
if widths != heights {
dest.write_str(" / ")?;
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>
where
L: PartialEq + ToCss,
@ -172,8 +200,8 @@ where
bottom_left: BorderCornerRadius(ref bl),
} = *self;
let widths = Rect::new(&tl.0.width, &tr.0.width, &br.0.width, &bl.0.width);
let heights = Rect::new(&tl.0.height, &tr.0.height, &br.0.height, &bl.0.height);
let widths = Rect::new(&tl.width, &tr.width, &br.width, &bl.width);
let heights = Rect::new(&tl.height, &tr.height, &br.height, &bl.height);
Self::serialize_rects(widths, heights, dest)
}

View file

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

View file

@ -8,8 +8,8 @@
use super::CustomIdent;
use crate::counter_style::{parse_counter_style_name, Symbols};
use crate::parser::{Parse, ParserContext};
use crate::Zero;
use cssparser::Parser;
use num_traits::Zero;
use std::ops::Add;
use style_traits::{KeywordsCollectFn, ParseError};
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind};
@ -212,3 +212,26 @@ impl<T: Zero> Zero for NonNegative<T> {
ToCss,
)]
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.
use crate::parser::ParserContext;
use crate::values::animated::ToAnimatedValue;
use crate::Zero;
use cssparser::Parser;
use euclid::Size2D;
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
/// `border-spacing`.
@ -21,29 +20,36 @@ use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, ToCss};
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToAnimatedZero,
ToAnimatedValue,
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]
/// Create a new `Size` for an area of given width and height.
pub fn new(width: L, height: L) -> Size<L> {
Size(Size2D::new(width, height))
/// Create a new `Size2D` for an area of given width and height.
pub fn new(width: L, height: L) -> Self {
Self { width, height }
}
/// Returns the width component.
pub fn width(&self) -> &L {
&self.0.width
&self.width
}
/// Returns the height component.
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>(
context: &ParserContext,
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
L: ToCss + PartialEq,
{
@ -69,40 +75,23 @@ where
where
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(" ")?;
self.0.height.to_css(dest)?;
self.height.to_css(dest)?;
}
Ok(())
}
}
impl<L> ToAnimatedValue for Size<L>
where
L: ToAnimatedValue,
{
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(),
))
impl<L: Zero> Zero for Size2D<L> {
fn zero() -> Self {
Self::new(L::zero(), L::zero())
}
#[inline]
fn from_animated_value(animated: Self::AnimatedValue) -> Self {
Size(Size2D::new(
L::from_animated_value(animated.0.width),
L::from_animated_value(animated.0.height),
))
fn is_zero(&self) -> bool {
self.width.is_zero() && self.height.is_zero()
}
}
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.
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
Debug,
MallocSizeOf,
PartialEq,
Parse,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
)]
pub enum SVGLength<LengthType> {
pub enum SVGLength<L> {
/// `<length> | <percentage> | <number>`
Length(LengthType),
LengthPercentage(L),
/// `context-value`
#[parse(condition = "is_context_value_enabled")]
#[animation(error)]
ContextValue,
}
@ -197,13 +159,14 @@ pub enum SVGLength<LengthType> {
PartialEq,
SpecifiedValueInfo,
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
ToCss,
)]
pub enum SVGStrokeDashArray<LengthType> {
pub enum SVGStrokeDashArray<L> {
/// `[ <length> | <percentage> | <number> ]#`
#[css(comma)]
Values(#[css(if_empty = "none", iterable)] Vec<LengthType>),
Values(#[css(if_empty = "none", iterable)] Vec<L>),
/// `context-value`
ContextValue,
}
@ -211,6 +174,7 @@ pub enum SVGStrokeDashArray<LengthType> {
/// An SVG opacity value accepts `context-{fill,stroke}-opacity` in
/// addition to opacity value.
#[derive(
Animate,
Clone,
ComputeSquaredDistance,
Copy,
@ -227,7 +191,9 @@ pub enum SVGOpacity<OpacityType> {
/// `<opacity-value>`
Opacity(OpacityType),
/// `context-fill-opacity`
#[animation(error)]
ContextFillOpacity,
/// `context-stroke-opacity`
#[animation(error)]
ContextStrokeOpacity,
}

View file

@ -5,9 +5,7 @@
//! Generic types for text properties.
use crate::parser::ParserContext;
use crate::values::animated::{Animate, Procedure, ToAnimatedZero};
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use app_units::Au;
use crate::values::animated::ToAnimatedZero;
use cssparser::Parser;
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.
#[derive(
Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss,
)]
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
pub enum Spacing<Value> {
/// `normal`
Normal,
@ -63,51 +59,6 @@ impl<Value> 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.
@ -123,18 +74,21 @@ impl<V> ToAnimatedZero for Spacing<V> {
ToAnimatedValue,
ToCss,
)]
pub enum LineHeight<Number, LengthPercentage> {
#[repr(C, u8)]
pub enum GenericLineHeight<N, L> {
/// `normal`
Normal,
/// `-moz-block-height`
#[cfg(feature = "gecko")]
MozBlockHeight,
/// `<number>`
Number(Number),
/// `<length-or-percentage>`
Length(LengthPercentage),
Number(N),
/// `<length-percentage>`
Length(L),
}
pub use self::GenericLineHeight as LineHeight;
impl<N, L> ToAnimatedZero for LineHeight<N, L> {
#[inline]
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::LengthPercentage as SpecifiedLengthPercentage;
use crate::values::{computed, CSSFloat};
use crate::Zero;
use app_units::Au;
use euclid::{self, Rect, Transform3D};
use num_traits::Zero;
use std::fmt::{self, Write};
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)]
/// 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.
Matrix(Matrix<Number>),
/// 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)
#[css(comma, function)]
Skew(Angle, Option<Angle>),
Skew(Angle, #[css(skip_if = "Zero::is_zero")] Angle),
/// skewX(angle)
#[css(function = "skewX")]
SkewX(Angle),
@ -128,7 +137,10 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthPercentage> {
SkewY(Angle),
/// translate(x, y) or translate(x)
#[css(comma, function)]
Translate(LengthPercentage, Option<LengthPercentage>),
Translate(
LengthPercentage,
#[css(skip_if = "Zero::is_zero")] LengthPercentage,
),
/// translateX(x)
#[css(function = "translateX")]
TranslateX(LengthPercentage),
@ -143,14 +155,9 @@ pub enum TransformOperation<Angle, Number, Length, Integer, LengthPercentage> {
Translate3D(LengthPercentage, LengthPercentage, Length),
/// 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)
#[css(comma, function)]
Scale(Number, Option<Number>),
Scale(Number, #[css(contextual_skip_if = "is_same")] Number),
/// scaleX(factor)
#[css(function = "scaleX")]
ScaleX(Number),
@ -214,6 +221,10 @@ pub struct Transform<T>(#[css(if_empty = "none", iterable)] pub Vec<T>);
impl<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.
pub fn is_rotate(&self) -> bool {
@ -333,10 +344,10 @@ impl ToRadians for SpecifiedAngle {
impl<Angle, Number, Length, Integer, LoP> ToMatrix
for TransformOperation<Angle, Number, Length, Integer, LoP>
where
Angle: ToRadians + Copy,
Number: Copy + Into<f32> + Into<f64>,
Angle: Zero + ToRadians + Copy,
Number: PartialEq + Copy + Into<f32> + Into<f64>,
Length: ToAbsoluteLength,
LoP: ToAbsoluteLength,
LoP: Zero + ToAbsoluteLength,
{
#[inline]
fn is_3d(&self) -> bool {
@ -389,7 +400,7 @@ where
m.cast()
},
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.),
ScaleY(s) => Transform3D::create_scale(1., s.into(), 1.),
ScaleZ(s) => Transform3D::create_scale(1., 1., s.into()),
@ -398,12 +409,12 @@ where
let ty = ty.to_pixel_length(reference_height)? 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 ty = ty.to_pixel_length(reference_height)? as f64;
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;
Transform3D::create_translation(t, 0., 0.)
},
@ -416,7 +427,7 @@ where
},
Skew(theta_x, theta_y) => Transform3D::create_skew(
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(
euclid::Angle::radians(theta.radians64()),
@ -629,64 +640,44 @@ impl<Number: ToCss + PartialEq> ToCss for Scale<Number> {
}
#[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>
pub enum Translate<LengthPercentage, Length> {
pub enum Translate<LengthPercentage, Length>
where
LengthPercentage: Zero,
{
/// 'none'
None,
/// '<length-percentage>' or '<length-percentage> <length-percentage>'
Translate(LengthPercentage, LengthPercentage),
Translate(
LengthPercentage,
#[css(skip_if = "Zero::is_zero")] LengthPercentage,
),
/// '<length-percentage> <length-percentage> <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)]
#[derive(
Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss,

View file

@ -160,7 +160,7 @@ impl CustomIdent {
excluding: &[&str],
) -> Result<Self, ParseError<'i>> {
let valid = match_ignore_ascii_case! { ident,
"initial" | "inherit" | "unset" | "default" => false,
"initial" | "inherit" | "unset" | "default" | "revert" => false,
_ => true
};
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::specified::calc::CalcNode;
use crate::values::CSSFloat;
use crate::Zero;
use cssparser::{Parser, Token};
use std::f32::consts::PI;
use std::fmt::{self, Write};
@ -32,6 +33,21 @@ pub enum AngleDimension {
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 {
/// Returns the amount of degrees this angle represents.
#[inline]
@ -58,6 +74,19 @@ pub struct Angle {
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 {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
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()`.
#[inline]
pub fn degrees(&self) -> CSSFloat {
@ -113,12 +147,6 @@ impl Angle {
self.was_calc
}
/// Returns `0deg`.
#[inline]
pub fn zero() -> Self {
Self::from_degrees(0.0, false)
}
/// Returns an `Angle` parsed from a `calc()` expression.
pub fn from_calc(degrees: CSSFloat) -> Self {
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::SVGPathData;
use crate::values::specified::{LengthPercentage, NonNegativeLengthPercentage};
use crate::Zero;
use cssparser::Parser;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
@ -202,9 +203,9 @@ impl InsetRect {
) -> Result<Self, ParseError<'i>> {
let rect = Rect::parse_with(context, input, LengthPercentage::parse)?;
let round = if input.try(|i| i.expect_ident_matching("round")).is_ok() {
Some(BorderRadius::parse(context, input)?)
BorderRadius::parse(context, input)?
} else {
None
BorderRadius::zero()
};
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::BorderSpacing as GenericBorderSpacing;
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::{AllowQuirks, NonNegativeNumber, NonNegativeNumberOrPercentage};
use crate::Zero;
use cssparser::Parser;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, ToCss};
@ -228,7 +229,7 @@ impl Parse for BorderCornerRadius {
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
Size::parse_with(context, input, NonNegativeLengthPercentage::parse)
Size2D::parse_with(context, input, NonNegativeLengthPercentage::parse)
.map(GenericBorderCornerRadius)
}
}
@ -238,7 +239,7 @@ impl Parse for BorderSpacing {
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> 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)
})
.map(GenericBorderSpacing)

View file

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

View file

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

View file

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

View file

@ -14,11 +14,11 @@ use crate::values::generics::length as generics;
use crate::values::generics::length::{
GenericLengthOrNumber, MaxSize as GenericMaxSize, Size as GenericSize,
};
use crate::values::generics::transform::IsZeroLength;
use crate::values::generics::NonNegative;
use crate::values::specified::calc::CalcNode;
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 cssparser::{Parser, Token};
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.
#[inline]
pub fn to_computed_pixel_length_without_context(&self) -> Result<CSSFloat, ()> {
@ -510,9 +495,12 @@ impl NoCalcLength {
impl SpecifiedValueInfo for NoCalcLength {}
impl IsZeroLength for NoCalcLength {
#[inline]
fn is_zero_length(&self) -> bool {
impl Zero for NoCalcLength {
fn zero() -> Self {
NoCalcLength::Absolute(AbsoluteLength::Px(0.))
}
fn is_zero(&self) -> bool {
match *self {
NoCalcLength::Absolute(v) => v.is_zero(),
NoCalcLength::FontRelative(v) => v.is_zero(),
@ -584,12 +572,6 @@ impl Mul<CSSFloat> for ViewportPercentageLength {
}
impl Length {
#[inline]
/// Returns a `zero` length.
pub fn zero() -> Length {
Length::NoCalc(NoCalcLength::zero())
}
#[inline]
fn parse_internal<'i, 't>(
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 {
/// Parses a length, with quirks.
pub fn parse_quirky<'i, 't>(
@ -711,12 +708,6 @@ impl From<Length> for NonNegativeLength {
}
impl NonNegativeLength {
/// Returns a `zero` length.
#[inline]
pub fn zero() -> Self {
Length::zero().into()
}
/// Get an absolute length from a px value.
#[inline]
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
/// `<percentage>`, or a combination of both via `calc()`.
///
@ -801,12 +789,6 @@ impl Parse for LengthPercentage {
}
impl LengthPercentage {
#[inline]
/// Returns a `zero` length.
pub fn zero() -> LengthPercentage {
LengthPercentage::Length(NoCalcLength::zero())
}
#[inline]
/// Returns a `0%` value.
pub fn zero_percent() -> LengthPercentage {
@ -898,11 +880,14 @@ impl LengthPercentage {
}
}
impl IsZeroLength for LengthPercentage {
#[inline]
fn is_zero_length(&self) -> bool {
impl Zero for LengthPercentage {
fn zero() -> Self {
LengthPercentage::Length(NoCalcLength::zero())
}
fn is_zero(&self) -> bool {
match *self {
LengthPercentage::Length(l) => l.is_zero_length(),
LengthPercentage::Length(l) => l.is_zero(),
LengthPercentage::Percentage(p) => p.0 == 0.0,
LengthPercentage::Calc(_) => false,
}
@ -913,11 +898,6 @@ impl IsZeroLength for LengthPercentage {
pub type LengthPercentageOrAuto = generics::LengthPercentageOrAuto<LengthPercentage>;
impl LengthPercentageOrAuto {
/// Returns a value representing a `0` length.
pub fn zero() -> Self {
generics::LengthPercentageOrAuto::LengthPercentage(LengthPercentage::zero())
}
/// Returns a value representing `0%`.
#[inline]
pub fn zero_percent() -> Self {
@ -943,11 +923,6 @@ pub type NonNegativeLengthPercentageOrAuto =
generics::LengthPercentageOrAuto<NonNegativeLengthPercentage>;
impl NonNegativeLengthPercentageOrAuto {
/// Returns a value representing a `0` length.
pub fn zero() -> Self {
generics::LengthPercentageOrAuto::LengthPercentage(NonNegativeLengthPercentage::zero())
}
/// Returns a value representing `0%`.
#[inline]
pub fn zero_percent() -> Self {
@ -997,12 +972,6 @@ impl Parse for NonNegativeLengthPercentage {
}
impl NonNegativeLengthPercentage {
#[inline]
/// Returns a `zero` length.
pub fn zero() -> Self {
NonNegative(LengthPercentage::zero())
}
#[inline]
/// Returns a `0%` value.
pub fn zero_percent() -> Self {
@ -1025,7 +994,29 @@ impl NonNegativeLengthPercentage {
pub type LengthOrNormal = Either<Length, Normal>;
/// 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>`.
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::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
use super::generics::transform::IsParallelTo;
use super::generics::{GreaterThanOrEqualToOne, NonNegative};
use super::generics::{self, GreaterThanOrEqualToOne, NonNegative};
use super::{Auto, CSSFloat, CSSInteger, Either};
use crate::context::QuirksMode;
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::length::{AbsoluteLength, CalcLengthPercentage, CharacterWidth};
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::{NoCalcLength, ViewportPercentageLength};
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::table::XSpan;
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::time::Time;
pub use self::transform::{Rotate, Scale, Transform};
@ -605,99 +605,8 @@ pub type GridLine = GenericGridLine<Integer>;
/// `<grid-template-rows> | <grid-template-columns>`
pub type GridTemplateComponent = GenericGridTemplateComponent<LengthPercentage, Integer>;
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)]
/// rect(<top>, <left>, <bottom>, <right>) used by clip and image-region
#[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)),
}
}
}
/// rect(...)
pub type ClipRect = generics::ClipRect<LengthOrAuto>;
impl Parse for ClipRect {
fn parse<'i, 't>(
@ -715,25 +624,16 @@ impl ClipRect {
input: &mut Parser<'i, 't>,
allow_quirks: AllowQuirks,
) -> Result<Self, ParseError<'i>> {
use crate::values::specified::Length;
input.expect_function_matching("rect")?;
fn parse_argument<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
allow_quirks: AllowQuirks,
) -> Result<Option<Length>, ParseError<'i>> {
if input
.try(|input| input.expect_ident_matching("auto"))
.is_ok()
{
Ok(None)
} else {
Length::parse_quirky(context, input, allow_quirks).map(Some)
}
) -> Result<LengthOrAuto, ParseError<'i>> {
LengthOrAuto::parse_quirky(context, input, allow_quirks)
}
input.expect_function_matching("rect")?;
input.parse_nested_block(|input| {
let top = parse_argument(context, input, allow_quirks)?;
let right;
@ -751,11 +651,12 @@ impl ClipRect {
bottom = parse_argument(context, input, allow_quirks)?;
left = parse_argument(context, input, allow_quirks)?;
}
Ok(ClipRect {
top: top,
right: right,
bottom: bottom,
left: left,
top,
right,
bottom,
left,
})
})
}
@ -782,16 +683,22 @@ impl ClipRectOrAuto {
/// Whether quirks are allowed in this context.
#[derive(Clone, Copy, PartialEq)]
pub enum AllowQuirks {
/// Quirks are allowed.
Yes,
/// Quirks are not allowed.
No,
/// Quirks are allowed, in quirks mode.
Yes,
/// Quirks are always allowed, used for SVG lengths.
Always,
}
impl AllowQuirks {
/// Returns `true` if quirks are allowed in this context.
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::{AllowQuirks, Integer, LengthPercentage};
use crate::values::{Either, None_};
use crate::Zero;
use cssparser::Parser;
use selectors::parser::SelectorParseErrorKind;
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::specified::color::Color;
use crate::values::specified::url::SpecifiedUrl;
use crate::values::specified::AllowQuirks;
use crate::values::specified::LengthPercentage;
use crate::values::specified::{NonNegativeLengthPercentage, NonNegativeNumber};
use crate::values::specified::{Number, Opacity};
use crate::values::specified::{NonNegativeLengthPercentage, Opacity};
use crate::values::CustomIdent;
use cssparser::Parser;
use std::fmt::{self, Write};
@ -23,36 +23,52 @@ pub type SVGPaint = generic::SVGPaint<Color, SpecifiedUrl>;
/// Specified SVG Paint Kind value
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
pub type SVGLength = generic::SVGLength<SvgLengthPercentageOrNumber>;
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>;
pub type SVGLength = generic::SVGLength<LengthPercentage>;
/// A non-negative version of SVGLength.
pub type SVGWidth = generic::SVGLength<NonNegativeSvgLengthPercentageOrNumber>;
impl From<NonNegativeSvgLengthPercentageOrNumber> for SVGWidth {
fn from(length: NonNegativeSvgLengthPercentageOrNumber) -> Self {
generic::SVGLength::Length(length)
}
}
pub type SVGWidth = generic::SVGLength<NonNegativeLengthPercentage>;
/// [ <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 {
fn parse<'i, 't>(
@ -61,14 +77,14 @@ impl Parse for SVGStrokeDashArray {
) -> Result<Self, ParseError<'i>> {
if let Ok(values) = input.try(|i| {
CommaWithSpace::parse(i, |i| {
NonNegativeSvgLengthPercentageOrNumber::parse(context, i)
NonNegativeLengthPercentage::parse_quirky(context, i, AllowQuirks::Always)
})
}) {
return Ok(generic::SVGStrokeDashArray::Values(values));
}
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)
},
"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.
#[repr(u8)]
#[derive(

View file

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

View file

@ -12,6 +12,70 @@ use syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple};
use syn::{Variant, WherePredicate};
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) {
where_clause
.get_or_insert(parse_quote!(where))

View file

@ -9,6 +9,12 @@ use synstructure::BindStyle;
pub fn derive(mut input: DeriveInput) -> TokenStream {
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() {
cg::add_predicate(
&mut where_clause,

View file

@ -9,6 +9,12 @@ use synstructure::BindStyle;
pub fn derive(mut input: DeriveInput) -> TokenStream {
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 params = input.generics.type_params().collect::<Vec<_>>();
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;
}
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 {
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! {{
@ -167,6 +175,7 @@ fn derive_single_field_expr(
field: &BindingInfo,
attrs: CssFieldAttrs,
where_clause: &mut Option<WhereClause>,
bindings: &[BindingInfo],
) -> TokenStream {
let mut expr = if attrs.iterable {
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
}
@ -249,5 +266,6 @@ pub struct CssFieldAttrs {
pub iterable: bool,
pub skip: bool,
pub represents_keyword: bool,
pub contextual_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
/// for that field is skipped if `function` returns true. This function is
/// 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
/// serialize the field name if the field is true, or nothing otherwise. It
/// 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)])
);
}
#[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::{
CssEnvironment, CustomPropertiesBuilder, CustomPropertiesMap, Name, SpecifiedValue,
};
use style::properties::CustomDeclarationValue;
use style::properties::{CustomDeclaration, CustomDeclarationValue};
use style::stylesheets::Origin;
use test::{self, Bencher};
fn cascade(
name_and_value: &[(&str, &str)],
inherited: Option<&Arc<CustomPropertiesMap>>,
) -> Option<Arc<CustomPropertiesMap>> {
let values = name_and_value
let declarations = name_and_value
.iter()
.map(|&(name, value)| {
let mut input = ParserInput::new(value);
let mut parser = Parser::new(&mut input);
(
Name::from(name),
SpecifiedValue::parse(&mut parser).unwrap(),
)
let name = Name::from(name);
let value = CustomDeclarationValue::Value(SpecifiedValue::parse(&mut parser).unwrap());
CustomDeclaration { name, value }
})
.collect::<Vec<_>>();
let env = CssEnvironment;
let mut builder = CustomPropertiesBuilder::new(inherited, &env);
for &(ref name, ref val) in &values {
builder.cascade(name, &CustomDeclarationValue::Value(val.clone()));
for declaration in &declarations {
builder.cascade(declaration, Origin::Author);
}
builder.build()

View file

@ -2,6 +2,3 @@
[Property text-justify inherits]
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'.]
expected: FAIL
[Testing 'word-spacing'.]
expected: FAIL
[Testing 'writing-mode'.]
expected: FAIL