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

style: Sync changes from mozilla-central.

See each individual commit for details.

https://bugzilla.mozilla.org/show_bug.cgi?id=1508026

<!-- 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/22213)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-11-17 07:31:17 -05:00 committed by GitHub
commit b20fab699e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 458 additions and 402 deletions

View file

@ -39,6 +39,8 @@ derive_helper_methods = true
prefix = "Style" prefix = "Style"
include = [ include = [
"Appearance", "Appearance",
"BreakBetween",
"BreakWithin",
"ComputedFontStretchRange", "ComputedFontStretchRange",
"ComputedFontStyleDescriptor", "ComputedFontStyleDescriptor",
"ComputedFontWeightRange", "ComputedFontWeightRange",
@ -53,5 +55,6 @@ include = [
"TimingFunction", "TimingFunction",
"PathCommand", "PathCommand",
"UnicodeRange", "UnicodeRange",
"UserSelect",
] ]
item_types = ["enums", "structs", "typedefs"] item_types = ["enums", "structs", "typedefs"]

View file

@ -109,7 +109,7 @@ pub struct VariableValue {
references_environment: bool, references_environment: bool,
/// Custom property names in var() functions. /// Custom property names in var() functions.
references: PrecomputedHashSet<Name>, references: Box<[Name]>,
} }
impl ToCss for SpecifiedValue { impl ToCss for SpecifiedValue {
@ -278,7 +278,7 @@ impl VariableValue {
css: String::new(), css: String::new(),
last_token_type: TokenSerializationType::nothing(), last_token_type: TokenSerializationType::nothing(),
first_token_type: TokenSerializationType::nothing(), first_token_type: TokenSerializationType::nothing(),
references: PrecomputedHashSet::default(), references: Default::default(),
references_environment: false, references_environment: false,
} }
} }
@ -335,11 +335,17 @@ impl VariableValue {
let (first_token_type, css, last_token_type) = let (first_token_type, css, last_token_type) =
parse_self_contained_declaration_value(input, Some(&mut references))?; parse_self_contained_declaration_value(input, Some(&mut references))?;
let custom_property_references = references
.custom_property_references
.into_iter()
.collect::<Vec<_>>()
.into_boxed_slice();
Ok(Arc::new(VariableValue { Ok(Arc::new(VariableValue {
css: css.into_owned(), css: css.into_owned(),
first_token_type, first_token_type,
last_token_type, last_token_type,
references: references.custom_property_references, references: custom_property_references,
references_environment: references.references_environment, references_environment: references.references_environment,
})) }))
} }

View file

@ -37,7 +37,7 @@ macro_rules! apply_non_ts_list {
("visited", Visited, visited, IN_VISITED_STATE, _), ("visited", Visited, visited, IN_VISITED_STATE, _),
("active", Active, active, IN_ACTIVE_STATE, _), ("active", Active, active, IN_ACTIVE_STATE, _),
("checked", Checked, checked, IN_CHECKED_STATE, _), ("checked", Checked, checked, IN_CHECKED_STATE, _),
("defined", Defined, defined, IN_DEFINED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), ("defined", Defined, defined, IN_DEFINED_STATE, _),
("disabled", Disabled, disabled, IN_DISABLED_STATE, _), ("disabled", Disabled, disabled, IN_DISABLED_STATE, _),
("enabled", Enabled, enabled, IN_ENABLED_STATE, _), ("enabled", Enabled, enabled, IN_ENABLED_STATE, _),
("focus", Focus, focus, IN_FOCUS_STATE, _), ("focus", Focus, focus, IN_FOCUS_STATE, _),

View file

@ -16,8 +16,8 @@ sys.path.insert(0, os.path.join(os.path.dirname(GECKO_DIR), "properties"))
import build import build
# Matches lines like `GK_ATOM(foo, "foo", 0x12345678, nsStaticAtom, PseudoElementAtom)`. # Matches lines like `GK_ATOM(foo, "foo", 0x12345678, true, nsStaticAtom, PseudoElementAtom)`.
PATTERN = re.compile('^GK_ATOM\(([^,]*),[^"]*"([^"]*)",\s*(0x[0-9a-f]+),\s*([^,]*),\s*([^)]*)\)', PATTERN = re.compile('^GK_ATOM\(([^,]*),[^"]*"([^"]*)",\s*(0x[0-9a-f]+),\s*[^,]*,\s*([^,]*),\s*([^)]*)\)',
re.MULTILINE) re.MULTILINE)
FILE = "include/nsGkAtomList.h" FILE = "include/nsGkAtomList.h"

View file

@ -5,7 +5,6 @@
//! Gecko-specific bits for selector-parsing. //! Gecko-specific bits for selector-parsing.
use crate::element_state::{DocumentState, ElementState}; use crate::element_state::{DocumentState, ElementState};
use crate::gecko_bindings::structs;
use crate::gecko_bindings::structs::RawServoSelectorList; use crate::gecko_bindings::structs::RawServoSelectorList;
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI}; use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
use crate::invalidation::element::document_state::InvalidationMatchingData; use crate::invalidation::element::document_state::InvalidationMatchingData;
@ -178,9 +177,6 @@ impl NonTSPseudoClass {
NonTSPseudoClass::Fullscreen => unsafe { NonTSPseudoClass::Fullscreen => unsafe {
mozilla::StaticPrefs_sVarCache_full_screen_api_unprefix_enabled mozilla::StaticPrefs_sVarCache_full_screen_api_unprefix_enabled
}, },
NonTSPseudoClass::Defined => unsafe {
structs::nsContentUtils_sIsCustomElementsEnabled
},
// Otherwise, a pseudo-class is enabled in content when it // Otherwise, a pseudo-class is enabled in content when it
// doesn't have any enabled flag. // doesn't have any enabled flag.
_ => !self _ => !self
@ -347,10 +343,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
#[inline] #[inline]
fn parse_slotted(&self) -> bool { fn parse_slotted(&self) -> bool {
// NOTE(emilio): Slot assignment and such works per-document, but true
// getting a document around here is not trivial, and it's not worth
// anyway to handle this in a per-doc basis.
unsafe { structs::nsContentUtils_sIsShadowDOMEnabled }
} }
#[inline] #[inline]

View file

@ -175,13 +175,19 @@ impl WeakAtom {
/// Returns whether this atom is static. /// Returns whether this atom is static.
#[inline] #[inline]
pub fn is_static(&self) -> bool { pub fn is_static(&self) -> bool {
unsafe { (*self.as_ptr()).mIsStatic() != 0 } self.0.mIsStatic() != 0
}
/// Returns whether this atom is ascii lowercase.
#[inline]
fn is_ascii_lowercase(&self) -> bool {
self.0.mIsAsciiLowercase() != 0
} }
/// Returns the length of the atom string. /// Returns the length of the atom string.
#[inline] #[inline]
pub fn len(&self) -> u32 { pub fn len(&self) -> u32 {
unsafe { (*self.as_ptr()).mLength() } self.0.mLength()
} }
/// Returns whether this atom is the empty string. /// Returns whether this atom is the empty string.
@ -199,41 +205,54 @@ impl WeakAtom {
/// Convert this atom to ASCII lower-case /// Convert this atom to ASCII lower-case
pub fn to_ascii_lowercase(&self) -> Atom { pub fn to_ascii_lowercase(&self) -> Atom {
let slice = self.as_slice(); if self.is_ascii_lowercase() {
match slice return self.clone();
.iter()
.position(|&char16| (b'A' as u16) <= char16 && char16 <= (b'Z' as u16))
{
None => self.clone(),
Some(i) => {
let mut buffer: [u16; 64] = unsafe { mem::uninitialized() };
let mut vec;
let mutable_slice = if let Some(buffer_prefix) = buffer.get_mut(..slice.len()) {
buffer_prefix.copy_from_slice(slice);
buffer_prefix
} else {
vec = slice.to_vec();
&mut vec
};
for char16 in &mut mutable_slice[i..] {
if *char16 <= 0x7F {
*char16 = (*char16 as u8).to_ascii_lowercase() as u16
}
}
Atom::from(&*mutable_slice)
},
} }
let slice = self.as_slice();
let mut buffer: [u16; 64] = unsafe { mem::uninitialized() };
let mut vec;
let mutable_slice = if let Some(buffer_prefix) = buffer.get_mut(..slice.len()) {
buffer_prefix.copy_from_slice(slice);
buffer_prefix
} else {
vec = slice.to_vec();
&mut vec
};
for char16 in &mut *mutable_slice {
if *char16 <= 0x7F {
*char16 = (*char16 as u8).to_ascii_lowercase() as u16
}
}
Atom::from(&*mutable_slice)
} }
/// Return whether two atoms are ASCII-case-insensitive matches /// Return whether two atoms are ASCII-case-insensitive matches
#[inline]
pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
if self == other { if self == other {
return true; return true;
} }
// If we know both atoms are ascii-lowercase, then we can stick with
// pointer equality.
if self.is_ascii_lowercase() && other.is_ascii_lowercase() {
debug_assert!(!self.eq_ignore_ascii_case_slow(other));
return false;
}
self.eq_ignore_ascii_case_slow(other)
}
fn eq_ignore_ascii_case_slow(&self, other: &Self) -> bool {
let a = self.as_slice(); let a = self.as_slice();
let b = other.as_slice(); let b = other.as_slice();
a.len() == b.len() && a.iter().zip(b).all(|(&a16, &b16)| {
if a.len() != b.len() {
return false;
}
a.iter().zip(b).all(|(&a16, &b16)| {
if a16 <= 0x7F && b16 <= 0x7F { if a16 <= 0x7F && b16 <= 0x7F {
(a16 as u8).eq_ignore_ascii_case(&(b16 as u8)) (a16 as u8).eq_ignore_ascii_case(&(b16 as u8))
} else { } else {
@ -241,13 +260,6 @@ impl WeakAtom {
} }
}) })
} }
/// Return whether this atom is an ASCII-case-insensitive match for the given string
pub fn eq_str_ignore_ascii_case(&self, other: &str) -> bool {
self.chars()
.map(|r| r.map(|c: char| c.to_ascii_lowercase()))
.eq(other.chars().map(|c: char| Ok(c.to_ascii_lowercase())))
}
} }
impl fmt::Debug for WeakAtom { impl fmt::Debug for WeakAtom {

View file

@ -10,8 +10,12 @@ use super::media_feature::{KeywordDiscriminant, ParsingRequirements};
use super::Device; use super::Device;
use crate::context::QuirksMode; use crate::context::QuirksMode;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use crate::gecko::media_features::MEDIA_FEATURES;
#[cfg(feature = "gecko")]
use crate::gecko_bindings::structs; use crate::gecko_bindings::structs;
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
#[cfg(feature = "servo")]
use crate::servo::media_queries::MEDIA_FEATURES;
use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase}; use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
use crate::stylesheets::Origin; use crate::stylesheets::Origin;
use crate::values::computed::{self, ToComputedValue}; use crate::values::computed::{self, ToComputedValue};
@ -150,14 +154,14 @@ impl RangeOrOperator {
/// the media query contained, and the range to evaluate. /// the media query contained, and the range to evaluate.
#[derive(Clone, Debug, MallocSizeOf)] #[derive(Clone, Debug, MallocSizeOf)]
pub struct MediaFeatureExpression { pub struct MediaFeatureExpression {
feature: &'static MediaFeatureDescription, feature_index: usize,
value: Option<MediaExpressionValue>, value: Option<MediaExpressionValue>,
range_or_operator: Option<RangeOrOperator>, range_or_operator: Option<RangeOrOperator>,
} }
impl PartialEq for MediaFeatureExpression { impl PartialEq for MediaFeatureExpression {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.feature as *const _ == other.feature as *const _ && self.feature_index == other.feature_index &&
self.value == other.value && self.value == other.value &&
self.range_or_operator == other.range_or_operator self.range_or_operator == other.range_or_operator
} }
@ -170,8 +174,9 @@ impl ToCss for MediaFeatureExpression {
{ {
dest.write_str("(")?; dest.write_str("(")?;
if self let feature = self.feature();
.feature
if feature
.requirements .requirements
.contains(ParsingRequirements::WEBKIT_PREFIX) .contains(ParsingRequirements::WEBKIT_PREFIX)
{ {
@ -186,7 +191,7 @@ impl ToCss for MediaFeatureExpression {
} }
// NB: CssStringWriter not needed, feature names are under control. // NB: CssStringWriter not needed, feature names are under control.
write!(dest, "{}", self.feature.name)?; write!(dest, "{}", feature.name)?;
if let Some(RangeOrOperator::Operator(op)) = self.range_or_operator { if let Some(RangeOrOperator::Operator(op)) = self.range_or_operator {
dest.write_char(' ')?; dest.write_char(' ')?;
@ -240,17 +245,22 @@ fn consume_operation_or_colon(input: &mut Parser) -> Result<Option<Operator>, ()
impl MediaFeatureExpression { impl MediaFeatureExpression {
fn new( fn new(
feature: &'static MediaFeatureDescription, feature_index: usize,
value: Option<MediaExpressionValue>, value: Option<MediaExpressionValue>,
range_or_operator: Option<RangeOrOperator>, range_or_operator: Option<RangeOrOperator>,
) -> Self { ) -> Self {
debug_assert!(feature_index < MEDIA_FEATURES.len());
Self { Self {
feature, feature_index,
value, value,
range_or_operator, range_or_operator,
} }
} }
fn feature(&self) -> &'static MediaFeatureDescription {
&MEDIA_FEATURES[self.feature_index]
}
/// Parse a media expression of the form: /// Parse a media expression of the form:
/// ///
/// ``` /// ```
@ -270,12 +280,8 @@ impl MediaFeatureExpression {
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
#[cfg(feature = "gecko")]
use crate::gecko::media_features::MEDIA_FEATURES;
#[cfg(feature = "servo")]
use crate::servo::media_queries::MEDIA_FEATURES;
// FIXME: remove extra indented block when lifetimes are non-lexical // FIXME: remove extra indented block when lifetimes are non-lexical
let feature_index;
let feature; let feature;
let range; let range;
{ {
@ -319,14 +325,19 @@ impl MediaFeatureExpression {
}; };
let atom = Atom::from(string_as_ascii_lowercase(feature_name)); let atom = Atom::from(string_as_ascii_lowercase(feature_name));
match MEDIA_FEATURES.iter().find(|f| f.name == atom) { match MEDIA_FEATURES
Some(f) => Ok((f, range)), .iter()
.enumerate()
.find(|(_, f)| f.name == atom)
{
Some((i, f)) => Ok((i, f, range)),
None => Err(()), None => Err(()),
} }
}; };
match result { match result {
Ok((f, r)) => { Ok((i, f, r)) => {
feature_index = i;
feature = f; feature = f;
range = r; range = r;
}, },
@ -365,7 +376,7 @@ impl MediaFeatureExpression {
); );
} }
return Ok(Self::new(feature, None, None)); return Ok(Self::new(feature_index, None, None));
}, },
Ok(operator) => operator, Ok(operator) => operator,
}; };
@ -396,7 +407,7 @@ impl MediaFeatureExpression {
.new_custom_error(StyleParseErrorKind::MediaQueryExpectedFeatureValue) .new_custom_error(StyleParseErrorKind::MediaQueryExpectedFeatureValue)
})?; })?;
Ok(Self::new(feature, Some(value), range_or_operator)) Ok(Self::new(feature_index, Some(value), range_or_operator))
} }
/// Returns whether this media query evaluates to true for the given device. /// Returns whether this media query evaluates to true for the given device.
@ -412,7 +423,7 @@ impl MediaFeatureExpression {
}; };
} }
match self.feature.evaluator { match self.feature().evaluator {
Evaluator::Length(eval) => { Evaluator::Length(eval) => {
let computed = expect!(Length).map(|specified| { let computed = expect!(Length).map(|specified| {
computed::Context::for_media_query_evaluation(device, quirks_mode, |context| { computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {
@ -492,7 +503,7 @@ impl MediaExpressionValue {
MediaExpressionValue::IntRatio(ratio) => ratio.to_css(dest), MediaExpressionValue::IntRatio(ratio) => ratio.to_css(dest),
MediaExpressionValue::Resolution(ref r) => r.to_css(dest), MediaExpressionValue::Resolution(ref r) => r.to_css(dest),
MediaExpressionValue::Ident(ref ident) => serialize_atom_identifier(ident, dest), MediaExpressionValue::Ident(ref ident) => serialize_atom_identifier(ident, dest),
MediaExpressionValue::Enumerated(value) => match for_expr.feature.evaluator { MediaExpressionValue::Enumerated(value) => match for_expr.feature().evaluator {
Evaluator::Enumerated { serializer, .. } => dest.write_str(&*serializer(value)), Evaluator::Enumerated { serializer, .. } => dest.write_str(&*serializer(value)),
_ => unreachable!(), _ => unreachable!(),
}, },

View file

@ -291,6 +291,8 @@ class Longhand(object):
"AlignItems", "AlignItems",
"AlignSelf", "AlignSelf",
"Appearance", "Appearance",
"BreakBetween",
"BreakWithin",
"BackgroundRepeat", "BackgroundRepeat",
"BorderImageRepeat", "BorderImageRepeat",
"BorderStyle", "BorderStyle",
@ -336,6 +338,7 @@ class Longhand(object):
"TextEmphasisPosition", "TextEmphasisPosition",
"TouchAction", "TouchAction",
"TransformStyle", "TransformStyle",
"UserSelect",
"XSpan", "XSpan",
"XTextZoom", "XTextZoom",
"ZIndex", "ZIndex",

View file

@ -309,7 +309,9 @@ impl PropertyDeclarationBlock {
.find(|(declaration, _)| declaration.id() == property) .find(|(declaration, _)| declaration.id() == property)
} }
fn shorthand_to_css( /// Tries to serialize a given shorthand from the declarations in this
/// block.
pub fn shorthand_to_css(
&self, &self,
shorthand: ShorthandId, shorthand: ShorthandId,
dest: &mut CssStringWriter, dest: &mut CssStringWriter,

View file

@ -1396,6 +1396,8 @@ impl Clone for ${style_struct.gecko_struct_name} {
# Types used with predefined_type()-defined properties that we can auto-generate. # Types used with predefined_type()-defined properties that we can auto-generate.
predefined_types = { predefined_types = {
"BreakBetween": impl_simple,
"BreakWithin": impl_simple,
"Color": impl_color, "Color": impl_color,
"ColorOrAuto": impl_color, "ColorOrAuto": impl_color,
"GreaterThanOrEqualToOneNumber": impl_simple, "GreaterThanOrEqualToOneNumber": impl_simple,
@ -1429,6 +1431,7 @@ impl Clone for ${style_struct.gecko_struct_name} {
"SVGWidth": impl_svg_length, "SVGWidth": impl_svg_length,
"Transform": impl_transform, "Transform": impl_transform,
"TransformOrigin": impl_transform_origin, "TransformOrigin": impl_transform_origin,
"UserSelect": impl_simple,
"url::UrlOrNone": impl_css_url, "url::UrlOrNone": impl_css_url,
} }
@ -3028,8 +3031,7 @@ fn static_assert() {
animation-iteration-count animation-timing-function animation-iteration-count animation-timing-function
transition-duration transition-delay transition-duration transition-delay
transition-timing-function transition-property transition-timing-function transition-property
page-break-before page-break-after rotate rotate scroll-snap-points-x scroll-snap-points-y
scroll-snap-points-x scroll-snap-points-y
scroll-snap-type-x scroll-snap-type-y scroll-snap-coordinate scroll-snap-type-x scroll-snap-type-y scroll-snap-coordinate
perspective-origin -moz-binding will-change perspective-origin -moz-binding will-change
offset-path overscroll-behavior-x overscroll-behavior-y offset-path overscroll-behavior-x overscroll-behavior-y
@ -3148,35 +3150,6 @@ fn static_assert() {
<%call expr="impl_coord_copy('vertical_align', 'mVerticalAlign')"></%call> <%call expr="impl_coord_copy('vertical_align', 'mVerticalAlign')"></%call>
% for kind in ["before", "after"]:
// Temp fix for Bugzilla bug 24000.
// Map 'auto' and 'avoid' to false, and 'always', 'left', and 'right' to true.
// "A conforming user agent may interpret the values 'left' and 'right'
// as 'always'." - CSS2.1, section 13.3.1
pub fn set_page_break_${kind}(&mut self, v: longhands::page_break_${kind}::computed_value::T) {
use crate::computed_values::page_break_${kind}::T;
let result = match v {
T::Auto => false,
T::Always => true,
T::Avoid => false,
T::Left => true,
T::Right => true
};
self.gecko.mBreak${kind.title()} = result;
}
${impl_simple_copy('page_break_' + kind, 'mBreak' + kind.title())}
// Temp fix for Bugzilla bug 24000.
// See set_page_break_before/after for detail.
pub fn clone_page_break_${kind}(&self) -> longhands::page_break_${kind}::computed_value::T {
use crate::computed_values::page_break_${kind}::T;
if self.gecko.mBreak${kind.title()} { T::Always } else { T::Auto }
}
% endfor
${impl_style_coord("scroll_snap_points_x", "mScrollSnapPointsX")} ${impl_style_coord("scroll_snap_points_x", "mScrollSnapPointsX")}
${impl_style_coord("scroll_snap_points_y", "mScrollSnapPointsY")} ${impl_style_coord("scroll_snap_points_y", "mScrollSnapPointsY")}

View file

@ -2108,7 +2108,7 @@ impl ComputedRotate {
// //
// If the axis is unspecified, it defaults to "0 0 1" // If the axis is unspecified, it defaults to "0 0 1"
match *self { match *self {
Rotate::None => (0., 0., 1., Angle::zero()), Rotate::None => unreachable!("None is handled by the caller"),
Rotate::Rotate3D(rx, ry, rz, angle) => (rx, ry, rz, angle), Rotate::Rotate3D(rx, ry, rz, angle) => (rx, ry, rz, angle),
Rotate::Rotate(angle) => (0., 0., 1., angle), Rotate::Rotate(angle) => (0., 0., 1., angle),
} }
@ -2122,41 +2122,58 @@ impl Animate for ComputedRotate {
other: &Self, other: &Self,
procedure: Procedure, procedure: Procedure,
) -> Result<Self, ()> { ) -> Result<Self, ()> {
let (from, to) = (self.resolve(), other.resolve()); match (self, other) {
(&Rotate::None, &Rotate::None) => Ok(Rotate::None),
(&Rotate::Rotate3D(fx, fy, fz, fa), &Rotate::None) => {
// No need to normalize `none`, so animate angle directly.
Ok(Rotate::Rotate3D(fx, fy, fz, fa.animate(&Angle::zero(), procedure)?))
},
(&Rotate::None, &Rotate::Rotate3D(tx, ty, tz, ta)) => {
// No need to normalize `none`, so animate angle directly.
Ok(Rotate::Rotate3D(tx, ty, tz, Angle::zero().animate(&ta, procedure)?))
},
(&Rotate::Rotate3D(_, ..), _) | (_, &Rotate::Rotate3D(_, ..)) => {
let (from, to) = (self.resolve(), other.resolve());
let (mut fx, mut fy, mut fz, fa) =
transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
let (mut tx, mut ty, mut tz, ta) =
transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
let (mut fx, mut fy, mut fz, fa) = if fa == Angle::from_degrees(0.) {
transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3); fx = tx;
let (mut tx, mut ty, mut tz, ta) = fy = ty;
transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3); fz = tz;
} else if ta == Angle::from_degrees(0.) {
tx = fx;
ty = fy;
tz = fz;
}
if fa == Angle::from_degrees(0.) { if (fx, fy, fz) == (tx, ty, tz) {
fx = tx; return Ok(Rotate::Rotate3D(fx, fy, fz, fa.animate(&ta, procedure)?));
fy = ty; }
fz = tz;
} else if ta == Angle::from_degrees(0.) { let fv = DirectionVector::new(fx, fy, fz);
tx = fx; let tv = DirectionVector::new(tx, ty, tz);
ty = fy; let fq = Quaternion::from_direction_and_angle(&fv, fa.radians64());
tz = fz; let tq = Quaternion::from_direction_and_angle(&tv, ta.radians64());
let rq = Quaternion::animate(&fq, &tq, procedure)?;
let (x, y, z, angle) = transform::get_normalized_vector_and_angle(
rq.0 as f32,
rq.1 as f32,
rq.2 as f32,
rq.3.acos() as f32 * 2.0,
);
Ok(Rotate::Rotate3D(x, y, z, Angle::from_radians(angle)))
},
(&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => {
// If this is a 2D rotation, we just animate the <angle>
let (from, to) = (self.resolve().3, other.resolve().3);
Ok(Rotate::Rotate(from.animate(&to, procedure)?))
},
} }
if (fx, fy, fz) == (tx, ty, tz) {
return Ok(Rotate::Rotate3D(fx, fy, fz, fa.animate(&ta, procedure)?));
}
let fv = DirectionVector::new(fx, fy, fz);
let tv = DirectionVector::new(tx, ty, tz);
let fq = Quaternion::from_direction_and_angle(&fv, fa.radians64());
let tq = Quaternion::from_direction_and_angle(&tv, ta.radians64());
let rq = Quaternion::animate(&fq, &tq, procedure)?;
let (x, y, z, angle) = transform::get_normalized_vector_and_angle(
rq.0 as f32,
rq.1 as f32,
rq.2 as f32,
rq.3.acos() as f32 * 2.0,
);
Ok(Rotate::Rotate3D(x, y, z, Angle::from_radians(angle)))
} }
} }

View file

@ -437,29 +437,33 @@ ${helpers.single_keyword(
animation_value_type="discrete", animation_value_type="discrete",
)} )}
// TODO add support for logical values recto and verso ${helpers.predefined_type(
${helpers.single_keyword(
"page-break-after", "page-break-after",
"auto always avoid left right", "BreakBetween",
"computed::BreakBetween::Auto",
needs_context=False,
products="gecko", products="gecko",
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-after", spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-after",
animation_value_type="discrete", animation_value_type="discrete",
)} )}
${helpers.single_keyword( ${helpers.predefined_type(
"page-break-before", "page-break-before",
"auto always avoid left right", "BreakBetween",
"computed::BreakBetween::Auto",
needs_context=False,
products="gecko", products="gecko",
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-before", spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-before",
animation_value_type="discrete", animation_value_type="discrete",
)} )}
${helpers.single_keyword( ${helpers.predefined_type(
"page-break-inside", "page-break-inside",
"auto avoid", "BreakWithin",
products="gecko", "computed::BreakWithin::Auto",
gecko_ffi_name="mBreakInside", gecko_ffi_name="mBreakInside",
gecko_constant_prefix="NS_STYLE_PAGE_BREAK", needs_context=False,
products="gecko",
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-inside", spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-inside",
animation_value_type="discrete", animation_value_type="discrete",
)} )}

View file

@ -31,15 +31,13 @@ ${helpers.single_keyword(
spec="https://drafts.csswg.org/css-scrollbars-1/#scrollbar-width" spec="https://drafts.csswg.org/css-scrollbars-1/#scrollbar-width"
)} )}
${helpers.single_keyword( ${helpers.predefined_type(
"-moz-user-select", "-moz-user-select",
"auto text none all element elements toggle tri-state -moz-all -moz-text", "UserSelect",
"computed::UserSelect::Auto",
products="gecko", products="gecko",
alias="-webkit-user-select",
gecko_ffi_name="mUserSelect", gecko_ffi_name="mUserSelect",
gecko_enum_prefix="StyleUserSelect", alias="-webkit-user-select",
gecko_strip_moz_prefix=False,
aliases="-moz-none=none",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select", spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select",
)} )}

View file

@ -1354,6 +1354,13 @@ impl ShorthandId {
NonCustomPropertyId::from(self).to_nscsspropertyid() NonCustomPropertyId::from(self).to_nscsspropertyid()
} }
/// Converts from a nsCSSPropertyID to a ShorthandId.
#[cfg(feature = "gecko")]
#[inline]
pub fn from_nscsspropertyid(prop: nsCSSPropertyID) -> Result<Self, ()> {
PropertyId::from_nscsspropertyid(prop)?.as_shorthand().map_err(|_| ())
}
/// Get the longhand ids that form this shorthand. /// Get the longhand ids that form this shorthand.
pub fn longhands(&self) -> NonCustomPropertyIterator<LonghandId> { pub fn longhands(&self) -> NonCustomPropertyIterator<LonghandId> {
% for property in data.shorthands: % for property in data.shorthands:

View file

@ -148,21 +148,32 @@
% endfor % endfor
image.to_css(dest)?; image.to_css(dest)?;
dest.write_str(" ")?;
mode.to_css(dest)?;
dest.write_str(" ")?;
Position { if *mode != mask_mode::single_value::get_initial_specified_value() {
horizontal: position_x.clone(), dest.write_str(" ")?;
vertical: position_y.clone() mode.to_css(dest)?;
}.to_css(dest)?; }
if *size != mask_size::single_value::get_initial_specified_value() { if *position_x != PositionComponent::zero() ||
dest.write_str(" / ")?; *position_y != PositionComponent::zero() ||
size.to_css(dest)?; *size != mask_size::single_value::get_initial_specified_value()
{
dest.write_str(" ")?;
Position {
horizontal: position_x.clone(),
vertical: position_y.clone()
}.to_css(dest)?;
if *size != mask_size::single_value::get_initial_specified_value() {
dest.write_str(" / ")?;
size.to_css(dest)?;
}
}
if *repeat != mask_repeat::single_value::get_initial_specified_value() {
dest.write_str(" ")?;
repeat.to_css(dest)?;
} }
dest.write_str(" ")?;
repeat.to_css(dest)?;
if *origin != Origin::BorderBox || *clip != Clip::BorderBox { if *origin != Origin::BorderBox || *clip != Clip::BorderBox {
dest.write_str(" ")?; dest.write_str(" ")?;
@ -173,8 +184,10 @@
} }
} }
dest.write_str(" ")?; if *composite != mask_composite::single_value::get_initial_specified_value() {
composite.to_css(dest)?; dest.write_str(" ")?;
composite.to_css(dest)?;
}
} }
Ok(()) Ok(())

View file

@ -5,12 +5,9 @@
//! Computed types for CSS values related to backgrounds. //! Computed types for CSS values related to backgrounds.
use crate::values::computed::length::NonNegativeLengthOrPercentageOrAuto; use crate::values::computed::length::NonNegativeLengthOrPercentageOrAuto;
use crate::values::computed::{Context, ToComputedValue};
use crate::values::generics::background::BackgroundSize as GenericBackgroundSize; use crate::values::generics::background::BackgroundSize as GenericBackgroundSize;
use crate::values::specified::background::BackgroundRepeat as SpecifiedBackgroundRepeat;
use crate::values::specified::background::BackgroundRepeatKeyword; pub use crate::values::specified::background::BackgroundRepeat;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
/// A computed value for the `background-size` property. /// A computed value for the `background-size` property.
pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthOrPercentageOrAuto>; pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthOrPercentageOrAuto>;
@ -24,81 +21,3 @@ impl BackgroundSize {
} }
} }
} }
/// The computed value of the `background-repeat` property:
///
/// https://drafts.csswg.org/css-backgrounds/#the-background-repeat
#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
pub struct BackgroundRepeat(pub BackgroundRepeatKeyword, pub BackgroundRepeatKeyword);
impl BackgroundRepeat {
/// Returns the `repeat repeat` value.
pub fn repeat() -> Self {
BackgroundRepeat(
BackgroundRepeatKeyword::Repeat,
BackgroundRepeatKeyword::Repeat,
)
}
}
impl ToCss for BackgroundRepeat {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
match (self.0, self.1) {
(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat) => {
dest.write_str("repeat-x")
},
(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat) => {
dest.write_str("repeat-y")
},
(horizontal, vertical) => {
horizontal.to_css(dest)?;
if horizontal != vertical {
dest.write_str(" ")?;
vertical.to_css(dest)?;
}
Ok(())
},
}
}
}
impl ToComputedValue for SpecifiedBackgroundRepeat {
type ComputedValue = BackgroundRepeat;
#[inline]
fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
match *self {
SpecifiedBackgroundRepeat::RepeatX => BackgroundRepeat(
BackgroundRepeatKeyword::Repeat,
BackgroundRepeatKeyword::NoRepeat,
),
SpecifiedBackgroundRepeat::RepeatY => BackgroundRepeat(
BackgroundRepeatKeyword::NoRepeat,
BackgroundRepeatKeyword::Repeat,
),
SpecifiedBackgroundRepeat::Keywords(horizontal, vertical) => {
BackgroundRepeat(horizontal, vertical.unwrap_or(horizontal))
},
}
}
#[inline]
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
// FIXME(emilio): Why can't this just be:
// SpecifiedBackgroundRepeat::Keywords(computed.0, computed.1)
match (computed.0, computed.1) {
(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat) => {
SpecifiedBackgroundRepeat::RepeatX
},
(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat) => {
SpecifiedBackgroundRepeat::RepeatY
},
(horizontal, vertical) => {
SpecifiedBackgroundRepeat::Keywords(horizontal, Some(vertical))
},
}
}
}

View file

@ -11,13 +11,11 @@ use crate::values::generics::box_::Perspective as GenericPerspective;
use crate::values::generics::box_::VerticalAlign as GenericVerticalAlign; use crate::values::generics::box_::VerticalAlign as GenericVerticalAlign;
use crate::values::specified::box_ as specified; use crate::values::specified::box_ as specified;
pub use crate::values::specified::box_::{ pub use crate::values::specified::box_::{AnimationName, Appearance, BreakBetween, BreakWithin};
AnimationName, Appearance, Contain, Display, OverflowClipBox,
};
pub use crate::values::specified::box_::{Clear as SpecifiedClear, Float as SpecifiedFloat}; pub use crate::values::specified::box_::{Clear as SpecifiedClear, Float as SpecifiedFloat};
pub use crate::values::specified::box_::{ pub use crate::values::specified::box_::{Contain, Display, OverflowClipBox};
OverscrollBehavior, ScrollSnapType, TouchAction, TransitionProperty, WillChange, pub use crate::values::specified::box_::{OverscrollBehavior, ScrollSnapType};
}; pub use crate::values::specified::box_::{TouchAction, TransitionProperty, WillChange};
/// A computed value for the `vertical-align` property. /// A computed value for the `vertical-align` property.
pub type VerticalAlign = GenericVerticalAlign<LengthOrPercentage>; pub type VerticalAlign = GenericVerticalAlign<LengthOrPercentage>;

View file

@ -4,10 +4,12 @@
//! Computed values. //! Computed values.
use self::transform::DirectionVector;
use super::animated::ToAnimatedValue; use super::animated::ToAnimatedValue;
use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent; use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth}; use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize}; use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
use super::generics::transform::IsParallelTo;
use super::generics::{GreaterThanOrEqualToOne, NonNegative}; use super::generics::{GreaterThanOrEqualToOne, NonNegative};
use super::specified; use super::specified;
use super::{CSSFloat, CSSInteger}; use super::{CSSFloat, CSSInteger};
@ -40,7 +42,7 @@ pub use self::border::{BorderCornerRadius, BorderRadius, BorderSpacing};
pub use self::border::{BorderImageRepeat, BorderImageSideWidth}; pub use self::border::{BorderImageRepeat, BorderImageSideWidth};
pub use self::border::{BorderImageSlice, BorderImageWidth}; pub use self::border::{BorderImageSlice, BorderImageWidth};
pub use self::box_::{AnimationIterationCount, AnimationName, Contain}; pub use self::box_::{AnimationIterationCount, AnimationName, Contain};
pub use self::box_::{Appearance, Clear, Float}; pub use self::box_::{Appearance, BreakBetween, BreakWithin, Clear, Float};
pub use self::box_::{Display, TransitionProperty}; pub use self::box_::{Display, TransitionProperty};
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize}; pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
pub use self::box_::{ScrollSnapType, TouchAction, VerticalAlign, WillChange}; pub use self::box_::{ScrollSnapType, TouchAction, VerticalAlign, WillChange};
@ -84,7 +86,7 @@ pub use self::transform::{Rotate, Scale, Transform, TransformOperation};
pub use self::transform::{TransformOrigin, TransformStyle, Translate}; pub use self::transform::{TransformOrigin, TransformStyle, Translate};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub use self::ui::CursorImage; pub use self::ui::CursorImage;
pub use self::ui::{ColorOrAuto, Cursor, MozForceBrokenImageIcon}; pub use self::ui::{ColorOrAuto, Cursor, MozForceBrokenImageIcon, UserSelect};
pub use super::specified::{BorderStyle, TextDecorationLine}; pub use super::specified::{BorderStyle, TextDecorationLine};
pub use super::{Auto, Either, None_}; pub use super::{Auto, Either, None_};
pub use app_units::Au; pub use app_units::Au;
@ -459,6 +461,19 @@ trivial_to_computed_value!(Box<str>);
/// A `<number>` value. /// A `<number>` value.
pub type Number = CSSFloat; pub type Number = CSSFloat;
impl IsParallelTo for (Number, Number, Number) {
fn is_parallel_to(&self, vector: &DirectionVector) -> bool {
use euclid::approxeq::ApproxEq;
// If a and b is parallel, the angle between them is 0deg, so
// a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0.
let self_vector = DirectionVector::new(self.0, self.1, self.2);
self_vector
.cross(*vector)
.square_length()
.approx_eq(&0.0f32)
}
}
/// A wrapper of Number, but the value >= 0. /// A wrapper of Number, but the value >= 0.
pub type NonNegativeNumber = NonNegative<CSSFloat>; pub type NonNegativeNumber = NonNegative<CSSFloat>;

View file

@ -10,7 +10,7 @@ use crate::values::computed::Number;
use crate::values::generics::ui as generics; use crate::values::generics::ui as generics;
use crate::values::{Auto, Either}; use crate::values::{Auto, Either};
pub use crate::values::specified::ui::MozForceBrokenImageIcon; pub use crate::values::specified::ui::{MozForceBrokenImageIcon, UserSelect};
/// auto | <color> /// auto | <color>
pub type ColorOrAuto = Either<Color, Auto>; pub type ColorOrAuto = Either<Color, Auto>;

View file

@ -538,7 +538,6 @@ pub fn get_normalized_vector_and_angle<T: Zero>(
SpecifiedValueInfo, SpecifiedValueInfo,
ToAnimatedZero, ToAnimatedZero,
ToComputedValue, ToComputedValue,
ToCss,
)] )]
/// A value of the `Rotate` property /// A value of the `Rotate` property
/// ///
@ -552,6 +551,53 @@ pub enum Rotate<Number, Angle> {
Rotate3D(Number, Number, Number, Angle), Rotate3D(Number, Number, Number, Angle),
} }
/// A trait to check if the current 3D vector is parallel to the DirectionVector.
/// This is especially for serialization on Rotate.
pub trait IsParallelTo {
/// Returns true if this is parallel to the vector.
fn is_parallel_to(&self, vector: &computed::transform::DirectionVector) -> bool;
}
impl<Number, Angle> ToCss for Rotate<Number, Angle>
where
Number: Copy + ToCss,
Angle: ToCss,
(Number, Number, Number): IsParallelTo,
{
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
use crate::values::computed::transform::DirectionVector;
match *self {
Rotate::None => dest.write_str("none"),
Rotate::Rotate(ref angle) => angle.to_css(dest),
Rotate::Rotate3D(x, y, z, ref angle) => {
// If a 3d rotation is specified, the property must serialize with an axis
// specified. If the axis is parallel with the x, y, or z axises, it must
// serialize as the appropriate keyword.
// https://drafts.csswg.org/css-transforms-2/#individual-transform-serialization
let v = (x, y, z);
if v.is_parallel_to(&DirectionVector::new(1., 0., 0.)) {
dest.write_char('x')?;
} else if v.is_parallel_to(&DirectionVector::new(0., 1., 0.)) {
dest.write_char('y')?;
} else if v.is_parallel_to(&DirectionVector::new(0., 0., 1.)) {
dest.write_char('z')?;
} else {
x.to_css(dest)?;
dest.write_char(' ')?;
y.to_css(dest)?;
dest.write_char(' ')?;
z.to_css(dest)?;
}
dest.write_char(' ')?;
angle.to_css(dest)
},
}
}
}
#[derive( #[derive(
Clone, Clone,
ComputeSquaredDistance, ComputeSquaredDistance,

View file

@ -9,7 +9,8 @@ use crate::values::generics::background::BackgroundSize as GenericBackgroundSize
use crate::values::specified::length::NonNegativeLengthOrPercentageOrAuto; use crate::values::specified::length::NonNegativeLengthOrPercentageOrAuto;
use cssparser::Parser; use cssparser::Parser;
use selectors::parser::SelectorParseErrorKind; use selectors::parser::SelectorParseErrorKind;
use style_traits::ParseError; use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, ToCss};
/// A specified value for the `background-size` property. /// A specified value for the `background-size` property.
pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthOrPercentageOrAuto>; pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthOrPercentageOrAuto>;
@ -56,6 +57,7 @@ impl BackgroundSize {
ToCss, ToCss,
)] )]
#[allow(missing_docs)] #[allow(missing_docs)]
#[value_info(other_values = "repeat-x,repeat-y")]
pub enum BackgroundRepeatKeyword { pub enum BackgroundRepeatKeyword {
Repeat, Repeat,
Space, Space,
@ -63,24 +65,45 @@ pub enum BackgroundRepeatKeyword {
NoRepeat, NoRepeat,
} }
/// The specified value for the `background-repeat` property. /// The value of the `background-repeat` property, with `repeat-x` / `repeat-y`
/// represented as the combination of `no-repeat` and `repeat` in the opposite
/// axes.
/// ///
/// https://drafts.csswg.org/css-backgrounds/#the-background-repeat /// https://drafts.csswg.org/css-backgrounds/#the-background-repeat
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)] #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)]
pub enum BackgroundRepeat { pub struct BackgroundRepeat(pub BackgroundRepeatKeyword, pub BackgroundRepeatKeyword);
/// `repeat-x`
RepeatX,
/// `repeat-y`
RepeatY,
/// `[repeat | space | round | no-repeat]{1,2}`
Keywords(BackgroundRepeatKeyword, Option<BackgroundRepeatKeyword>),
}
impl BackgroundRepeat { impl BackgroundRepeat {
/// Returns the `repeat` value. /// Returns the `repeat repeat` value.
#[inline]
pub fn repeat() -> Self { pub fn repeat() -> Self {
BackgroundRepeat::Keywords(BackgroundRepeatKeyword::Repeat, None) BackgroundRepeat(
BackgroundRepeatKeyword::Repeat,
BackgroundRepeatKeyword::Repeat,
)
}
}
impl ToCss for BackgroundRepeat {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
match (self.0, self.1) {
(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat) => {
dest.write_str("repeat-x")
},
(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat) => {
dest.write_str("repeat-y")
},
(horizontal, vertical) => {
horizontal.to_css(dest)?;
if horizontal != vertical {
dest.write_str(" ")?;
vertical.to_css(dest)?;
}
Ok(())
},
}
} }
} }
@ -92,8 +115,12 @@ impl Parse for BackgroundRepeat {
let ident = input.expect_ident_cloned()?; let ident = input.expect_ident_cloned()?;
match_ignore_ascii_case! { &ident, match_ignore_ascii_case! { &ident,
"repeat-x" => return Ok(BackgroundRepeat::RepeatX), "repeat-x" => {
"repeat-y" => return Ok(BackgroundRepeat::RepeatY), return Ok(BackgroundRepeat(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat));
},
"repeat-y" => {
return Ok(BackgroundRepeat(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat));
},
_ => {}, _ => {},
} }
@ -107,6 +134,6 @@ impl Parse for BackgroundRepeat {
}; };
let vertical = input.try(BackgroundRepeatKeyword::parse).ok(); let vertical = input.try(BackgroundRepeatKeyword::parse).ok();
Ok(BackgroundRepeat::Keywords(horizontal, vertical)) Ok(BackgroundRepeat(horizontal, vertical.unwrap_or(horizontal)))
} }
} }

View file

@ -8,20 +8,17 @@
//! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape //! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape
use crate::parser::{Parse, ParserContext}; use crate::parser::{Parse, ParserContext};
use crate::values::computed::Percentage;
use crate::values::generics::basic_shape as generic; use crate::values::generics::basic_shape as generic;
use crate::values::generics::basic_shape::{GeometryBox, Path, PolygonCoord}; use crate::values::generics::basic_shape::{GeometryBox, Path, PolygonCoord};
use crate::values::generics::basic_shape::{ShapeBox, ShapeSource}; use crate::values::generics::basic_shape::{ShapeBox, ShapeSource};
use crate::values::generics::rect::Rect; use crate::values::generics::rect::Rect;
use crate::values::specified::border::BorderRadius; use crate::values::specified::border::BorderRadius;
use crate::values::specified::image::Image; use crate::values::specified::image::Image;
use crate::values::specified::position::{HorizontalPosition, Position, PositionComponent}; use crate::values::specified::position::{HorizontalPosition, Position, VerticalPosition};
use crate::values::specified::position::{Side, VerticalPosition};
use crate::values::specified::url::SpecifiedUrl; use crate::values::specified::url::SpecifiedUrl;
use crate::values::specified::LengthOrPercentage; use crate::values::specified::LengthOrPercentage;
use crate::values::specified::SVGPathData; use crate::values::specified::SVGPathData;
use cssparser::Parser; use cssparser::Parser;
use std::borrow::Cow;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
@ -249,7 +246,7 @@ impl ToCss for Circle {
} }
dest.write_str("at ")?; dest.write_str("at ")?;
serialize_basicshape_position(&self.position, dest)?; self.position.to_css(dest)?;
dest.write_str(")") dest.write_str(")")
} }
} }
@ -305,7 +302,7 @@ impl ToCss for Ellipse {
} }
dest.write_str("at ")?; dest.write_str("at ")?;
serialize_basicshape_position(&self.position, dest)?; self.position.to_css(dest)?;
dest.write_str(")") dest.write_str(")")
} }
} }
@ -326,82 +323,6 @@ impl Parse for ShapeRadius {
} }
} }
/// <https://drafts.csswg.org/css-shapes/#basic-shape-serialization>
///
/// Positions get serialized differently with basic shapes. Keywords
/// are converted to percentages where possible. Only the two or four
/// value forms are used. In case of two keyword-percentage pairs,
/// the keywords are folded into the percentages
fn serialize_basicshape_position<W>(position: &Position, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
fn to_keyword_and_lop<S>(component: &PositionComponent<S>) -> (S, Cow<LengthOrPercentage>)
where
S: Copy + Side,
{
match *component {
PositionComponent::Center => (
S::start(),
Cow::Owned(LengthOrPercentage::Percentage(Percentage(0.5))),
),
PositionComponent::Side(keyword, None) => {
// left | top => 0%
// right | bottom => 100%
let p = if keyword.is_start() { 0. } else { 1. };
(
S::start(),
Cow::Owned(LengthOrPercentage::Percentage(Percentage(p))),
)
},
PositionComponent::Side(keyword, Some(ref lop)) if !keyword.is_start() => {
if let LengthOrPercentage::Percentage(p) = *to_non_zero_length(lop) {
(
S::start(),
Cow::Owned(LengthOrPercentage::Percentage(Percentage(1. - p.0))),
)
} else {
(keyword, Cow::Borrowed(lop))
}
},
PositionComponent::Length(ref lop) | PositionComponent::Side(_, Some(ref lop)) => {
(S::start(), to_non_zero_length(lop))
},
}
}
fn to_non_zero_length(lop: &LengthOrPercentage) -> Cow<LengthOrPercentage> {
match *lop {
LengthOrPercentage::Length(ref l) if l.is_zero() => {
Cow::Owned(LengthOrPercentage::Percentage(Percentage(0.)))
},
_ => Cow::Borrowed(lop),
}
}
fn write_pair<A, B, W>(a: &A, b: &B, dest: &mut CssWriter<W>) -> fmt::Result
where
A: ToCss,
B: ToCss,
W: Write,
{
a.to_css(dest)?;
dest.write_str(" ")?;
b.to_css(dest)
}
let (x_pos, x_lop) = to_keyword_and_lop(&position.horizontal);
let (y_pos, y_lop) = to_keyword_and_lop(&position.vertical);
if x_pos.is_start() && y_pos.is_start() {
return write_pair(&*x_lop, &*y_lop, dest);
}
write_pair(&x_pos, &*x_lop, dest)?;
dest.write_str(" ")?;
write_pair(&y_pos, &*y_lop, dest)
}
impl Parse for Polygon { impl Parse for Polygon {
fn parse<'i, 't>( fn parse<'i, 't>(
context: &ParserContext, context: &ParserContext,

View file

@ -52,9 +52,6 @@ fn moz_box_display_values_enabled(context: &ParserContext) -> bool {
/// If you change it, make sure to take a look at the /// If you change it, make sure to take a look at the
/// FrameConstructionDataByDisplay stuff (both the XUL and non-XUL version), and /// FrameConstructionDataByDisplay stuff (both the XUL and non-XUL version), and
/// ensure it's still correct! /// ensure it's still correct!
///
/// Also, when you change this from Gecko you may need to regenerate the
/// C++-side bindings (see components/style/cbindgen.toml).
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive( #[derive(
Clone, Clone,
@ -946,9 +943,6 @@ pub enum Resize {
/// The value for the `appearance` property. /// The value for the `appearance` property.
/// ///
/// https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance /// https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance
///
/// NOTE(emilio): When changing this you may want to regenerate the C++ bindings
/// (see components/style/cbindgen.toml)
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive( #[derive(
Clone, Clone,
@ -1295,3 +1289,52 @@ pub enum Appearance {
#[css(skip)] #[css(skip)]
Count, Count,
} }
/// A kind of break between two boxes.
///
/// https://drafts.csswg.org/css-break/#break-between
#[allow(missing_docs)]
#[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToCss,
ToComputedValue,
)]
#[repr(u8)]
pub enum BreakBetween {
Auto,
Always,
Avoid,
Left,
Right,
}
/// A kind of break within a box.
///
/// https://drafts.csswg.org/css-break/#break-within
#[allow(missing_docs)]
#[derive(
Clone,
Copy,
Debug,
Eq,
Hash,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToCss,
ToComputedValue,
)]
#[repr(u8)]
pub enum BreakWithin {
Auto,
Avoid,
}

View file

@ -6,9 +6,11 @@
//! //!
//! TODO(emilio): Enhance docs. //! TODO(emilio): Enhance docs.
use super::computed::transform::DirectionVector;
use super::computed::{Context, ToComputedValue}; use super::computed::{Context, ToComputedValue};
use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth}; use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize}; use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
use super::generics::transform::IsParallelTo;
use super::generics::{GreaterThanOrEqualToOne, NonNegative}; use super::generics::{GreaterThanOrEqualToOne, NonNegative};
use super::{Auto, CSSFloat, CSSInteger, Either}; use super::{Auto, CSSFloat, CSSInteger, Either};
use crate::context::QuirksMode; use crate::context::QuirksMode;
@ -34,7 +36,7 @@ pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth};
pub use self::border::{BorderImageRepeat, BorderImageSideWidth}; pub use self::border::{BorderImageRepeat, BorderImageSideWidth};
pub use self::border::{BorderRadius, BorderSideWidth, BorderSpacing}; pub use self::border::{BorderRadius, BorderSideWidth, BorderSpacing};
pub use self::box_::{AnimationIterationCount, AnimationName, Contain, Display}; pub use self::box_::{AnimationIterationCount, AnimationName, Contain, Display};
pub use self::box_::{Appearance, Clear, Float}; pub use self::box_::{Appearance, BreakBetween, BreakWithin, Clear, Float};
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize}; pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
pub use self::box_::{ScrollSnapType, TouchAction, TransitionProperty, VerticalAlign, WillChange}; pub use self::box_::{ScrollSnapType, TouchAction, TransitionProperty, VerticalAlign, WillChange};
pub use self::color::{Color, ColorPropertyValue, RGBAColor}; pub use self::color::{Color, ColorPropertyValue, RGBAColor};
@ -82,7 +84,7 @@ pub use self::transform::{Rotate, Scale, Transform};
pub use self::transform::{TransformOrigin, TransformStyle, Translate}; pub use self::transform::{TransformOrigin, TransformStyle, Translate};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub use self::ui::CursorImage; pub use self::ui::CursorImage;
pub use self::ui::{ColorOrAuto, Cursor, MozForceBrokenImageIcon}; pub use self::ui::{ColorOrAuto, Cursor, MozForceBrokenImageIcon, UserSelect};
pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent; pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
@ -292,6 +294,19 @@ impl ToCss for Number {
} }
} }
impl IsParallelTo for (Number, Number, Number) {
fn is_parallel_to(&self, vector: &DirectionVector) -> bool {
use euclid::approxeq::ApproxEq;
// If a and b is parallel, the angle between them is 0deg, so
// a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0.
let self_vector = DirectionVector::new(self.0.get(), self.1.get(), self.2.get());
self_vector
.cross(*vector)
.square_length()
.approx_eq(&0.0f32)
}
}
impl SpecifiedValueInfo for Number {} impl SpecifiedValueInfo for Number {}
impl From<Number> for f32 { impl From<Number> for f32 {

View file

@ -357,17 +357,38 @@ impl Parse for Rotate {
return Ok(generic::Rotate::None); return Ok(generic::Rotate::None);
} }
if let Ok(rx) = input.try(|i| Number::parse(context, i)) { // Parse <angle> or [ x | y | z | <number>{3} ] && <angle>.
// 'rotate: <number>{3} <angle>' //
let ry = Number::parse(context, input)?; // The rotate axis and angle could be in any order, so we parse angle twice to cover
let rz = Number::parse(context, input)?; // two cases. i.e. `<number>{3} <angle>` or `<angle> <number>{3}`
let angle = specified::Angle::parse(context, input)?; let angle = input.try(|i| specified::Angle::parse(context, i)).ok();
return Ok(generic::Rotate::Rotate3D(rx, ry, rz, angle)); let axis = input
} .try(|i| {
Ok(try_match_ident_ignore_ascii_case! { i,
"x" => (Number::new(1.), Number::new(0.), Number::new(0.)),
"y" => (Number::new(0.), Number::new(1.), Number::new(0.)),
"z" => (Number::new(0.), Number::new(0.), Number::new(1.)),
})
})
.or_else(|_: ParseError| -> Result<_, ParseError> {
input.try(|i| {
Ok((
Number::parse(context, i)?,
Number::parse(context, i)?,
Number::parse(context, i)?,
))
})
})
.ok();
let angle = match angle {
Some(a) => a,
None => specified::Angle::parse(context, input)?,
};
// 'rotate: <angle>' Ok(match axis {
let angle = specified::Angle::parse(context, input)?; Some((x, y, z)) => generic::Rotate::Rotate3D(x, y, z, angle),
Ok(generic::Rotate::Rotate(angle)) None => generic::Rotate::Rotate(angle),
})
} }
} }

View file

@ -140,3 +140,43 @@ impl Parse for ScrollbarColor {
}) })
} }
} }
fn in_ua_sheet(context: &ParserContext) -> bool {
use crate::stylesheets::Origin;
context.stylesheet_origin == Origin::UserAgent
}
/// The specified value for the `user-select` property.
///
/// https://drafts.csswg.org/css-ui-4/#propdef-user-select
#[allow(missing_docs)]
#[derive(
Clone,
Copy,
Debug,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
)]
#[repr(u8)]
pub enum UserSelect {
Auto,
Text,
#[parse(aliases = "-moz-none")]
None,
/// Force selection of all children, unless an ancestor has `none` set.
All,
/// Like `text`, except that it won't get overridden by ancestors having
/// `all`.
///
/// FIXME(emilio): This only has one use in contenteditable.css, can we find
/// a better way to do this?
///
/// See bug 1181130.
#[parse(condition = "in_ua_sheet")]
MozText,
}

View file

@ -1,31 +0,0 @@
[rotate-parsing-valid.html]
[e.style['rotate'\] = "400grad 100 200 300" should set the property value]
expected: FAIL
[e.style['rotate'\] = "x 400grad" should set the property value]
expected: FAIL
[e.style['rotate'\] = "400grad x" should set the property value]
expected: FAIL
[e.style['rotate'\] = "y 400grad" should set the property value]
expected: FAIL
[e.style['rotate'\] = "400grad y" should set the property value]
expected: FAIL
[e.style['rotate'\] = "z 400grad" should set the property value]
expected: FAIL
[e.style['rotate'\] = "400grad z" should set the property value]
expected: FAIL
[e.style['rotate'\] = "1 0 0 400grad" should set the property value]
expected: FAIL
[e.style['rotate'\] = "0 1 0 400grad" should set the property value]
expected: FAIL
[e.style['rotate'\] = "0 0 1 400grad" should set the property value]
expected: FAIL