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"
include = [
"Appearance",
"BreakBetween",
"BreakWithin",
"ComputedFontStretchRange",
"ComputedFontStyleDescriptor",
"ComputedFontWeightRange",
@ -53,5 +55,6 @@ include = [
"TimingFunction",
"PathCommand",
"UnicodeRange",
"UserSelect",
]
item_types = ["enums", "structs", "typedefs"]

View file

@ -109,7 +109,7 @@ pub struct VariableValue {
references_environment: bool,
/// Custom property names in var() functions.
references: PrecomputedHashSet<Name>,
references: Box<[Name]>,
}
impl ToCss for SpecifiedValue {
@ -278,7 +278,7 @@ impl VariableValue {
css: String::new(),
last_token_type: TokenSerializationType::nothing(),
first_token_type: TokenSerializationType::nothing(),
references: PrecomputedHashSet::default(),
references: Default::default(),
references_environment: false,
}
}
@ -335,11 +335,17 @@ impl VariableValue {
let (first_token_type, css, last_token_type) =
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 {
css: css.into_owned(),
first_token_type,
last_token_type,
references: references.custom_property_references,
references: custom_property_references,
references_environment: references.references_environment,
}))
}

View file

@ -37,7 +37,7 @@ macro_rules! apply_non_ts_list {
("visited", Visited, visited, IN_VISITED_STATE, _),
("active", Active, active, IN_ACTIVE_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, _),
("enabled", Enabled, enabled, IN_ENABLED_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
# Matches lines like `GK_ATOM(foo, "foo", 0x12345678, nsStaticAtom, PseudoElementAtom)`.
PATTERN = re.compile('^GK_ATOM\(([^,]*),[^"]*"([^"]*)",\s*(0x[0-9a-f]+),\s*([^,]*),\s*([^)]*)\)',
# Matches lines like `GK_ATOM(foo, "foo", 0x12345678, true, nsStaticAtom, PseudoElementAtom)`.
PATTERN = re.compile('^GK_ATOM\(([^,]*),[^"]*"([^"]*)",\s*(0x[0-9a-f]+),\s*[^,]*,\s*([^,]*),\s*([^)]*)\)',
re.MULTILINE)
FILE = "include/nsGkAtomList.h"

View file

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

View file

@ -175,13 +175,19 @@ impl WeakAtom {
/// Returns whether this atom is static.
#[inline]
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.
#[inline]
pub fn len(&self) -> u32 {
unsafe { (*self.as_ptr()).mLength() }
self.0.mLength()
}
/// Returns whether this atom is the empty string.
@ -199,41 +205,54 @@ impl WeakAtom {
/// Convert this atom to ASCII lower-case
pub fn to_ascii_lowercase(&self) -> Atom {
let slice = self.as_slice();
match slice
.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)
},
if self.is_ascii_lowercase() {
return self.clone();
}
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
#[inline]
pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
if self == other {
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 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 {
(a16 as u8).eq_ignore_ascii_case(&(b16 as u8))
} 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 {

View file

@ -10,8 +10,12 @@ use super::media_feature::{KeywordDiscriminant, ParsingRequirements};
use super::Device;
use crate::context::QuirksMode;
#[cfg(feature = "gecko")]
use crate::gecko::media_features::MEDIA_FEATURES;
#[cfg(feature = "gecko")]
use crate::gecko_bindings::structs;
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::stylesheets::Origin;
use crate::values::computed::{self, ToComputedValue};
@ -150,14 +154,14 @@ impl RangeOrOperator {
/// the media query contained, and the range to evaluate.
#[derive(Clone, Debug, MallocSizeOf)]
pub struct MediaFeatureExpression {
feature: &'static MediaFeatureDescription,
feature_index: usize,
value: Option<MediaExpressionValue>,
range_or_operator: Option<RangeOrOperator>,
}
impl PartialEq for MediaFeatureExpression {
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.range_or_operator == other.range_or_operator
}
@ -170,8 +174,9 @@ impl ToCss for MediaFeatureExpression {
{
dest.write_str("(")?;
if self
.feature
let feature = self.feature();
if feature
.requirements
.contains(ParsingRequirements::WEBKIT_PREFIX)
{
@ -186,7 +191,7 @@ impl ToCss for MediaFeatureExpression {
}
// 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 {
dest.write_char(' ')?;
@ -240,17 +245,22 @@ fn consume_operation_or_colon(input: &mut Parser) -> Result<Option<Operator>, ()
impl MediaFeatureExpression {
fn new(
feature: &'static MediaFeatureDescription,
feature_index: usize,
value: Option<MediaExpressionValue>,
range_or_operator: Option<RangeOrOperator>,
) -> Self {
debug_assert!(feature_index < MEDIA_FEATURES.len());
Self {
feature,
feature_index,
value,
range_or_operator,
}
}
fn feature(&self) -> &'static MediaFeatureDescription {
&MEDIA_FEATURES[self.feature_index]
}
/// Parse a media expression of the form:
///
/// ```
@ -270,12 +280,8 @@ impl MediaFeatureExpression {
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> 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
let feature_index;
let feature;
let range;
{
@ -319,14 +325,19 @@ impl MediaFeatureExpression {
};
let atom = Atom::from(string_as_ascii_lowercase(feature_name));
match MEDIA_FEATURES.iter().find(|f| f.name == atom) {
Some(f) => Ok((f, range)),
match MEDIA_FEATURES
.iter()
.enumerate()
.find(|(_, f)| f.name == atom)
{
Some((i, f)) => Ok((i, f, range)),
None => Err(()),
}
};
match result {
Ok((f, r)) => {
Ok((i, f, r)) => {
feature_index = i;
feature = f;
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,
};
@ -396,7 +407,7 @@ impl MediaFeatureExpression {
.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.
@ -412,7 +423,7 @@ impl MediaFeatureExpression {
};
}
match self.feature.evaluator {
match self.feature().evaluator {
Evaluator::Length(eval) => {
let computed = expect!(Length).map(|specified| {
computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {
@ -492,7 +503,7 @@ impl MediaExpressionValue {
MediaExpressionValue::IntRatio(ratio) => ratio.to_css(dest),
MediaExpressionValue::Resolution(ref r) => r.to_css(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)),
_ => unreachable!(),
},

View file

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

View file

@ -309,7 +309,9 @@ impl PropertyDeclarationBlock {
.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,
shorthand: ShorthandId,
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.
predefined_types = {
"BreakBetween": impl_simple,
"BreakWithin": impl_simple,
"Color": impl_color,
"ColorOrAuto": impl_color,
"GreaterThanOrEqualToOneNumber": impl_simple,
@ -1429,6 +1431,7 @@ impl Clone for ${style_struct.gecko_struct_name} {
"SVGWidth": impl_svg_length,
"Transform": impl_transform,
"TransformOrigin": impl_transform_origin,
"UserSelect": impl_simple,
"url::UrlOrNone": impl_css_url,
}
@ -3028,8 +3031,7 @@ fn static_assert() {
animation-iteration-count animation-timing-function
transition-duration transition-delay
transition-timing-function transition-property
page-break-before page-break-after rotate
scroll-snap-points-x scroll-snap-points-y
rotate scroll-snap-points-x scroll-snap-points-y
scroll-snap-type-x scroll-snap-type-y scroll-snap-coordinate
perspective-origin -moz-binding will-change
offset-path overscroll-behavior-x overscroll-behavior-y
@ -3148,35 +3150,6 @@ fn static_assert() {
<%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_y", "mScrollSnapPointsY")}

View file

@ -2108,7 +2108,7 @@ impl ComputedRotate {
//
// If the axis is unspecified, it defaults to "0 0 1"
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::Rotate(angle) => (0., 0., 1., angle),
}
@ -2122,41 +2122,58 @@ impl Animate for ComputedRotate {
other: &Self,
procedure: Procedure,
) -> 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) =
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);
if fa == Angle::from_degrees(0.) {
fx = tx;
fy = ty;
fz = tz;
} else if ta == Angle::from_degrees(0.) {
tx = fx;
ty = fy;
tz = fz;
}
if fa == Angle::from_degrees(0.) {
fx = tx;
fy = ty;
fz = tz;
} else if ta == Angle::from_degrees(0.) {
tx = fx;
ty = fy;
tz = fz;
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)))
},
(&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",
)}
// TODO add support for logical values recto and verso
${helpers.single_keyword(
${helpers.predefined_type(
"page-break-after",
"auto always avoid left right",
"BreakBetween",
"computed::BreakBetween::Auto",
needs_context=False,
products="gecko",
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-after",
animation_value_type="discrete",
)}
${helpers.single_keyword(
${helpers.predefined_type(
"page-break-before",
"auto always avoid left right",
"BreakBetween",
"computed::BreakBetween::Auto",
needs_context=False,
products="gecko",
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-before",
animation_value_type="discrete",
)}
${helpers.single_keyword(
${helpers.predefined_type(
"page-break-inside",
"auto avoid",
products="gecko",
"BreakWithin",
"computed::BreakWithin::Auto",
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",
animation_value_type="discrete",
)}

View file

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

View file

@ -1354,6 +1354,13 @@ impl ShorthandId {
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.
pub fn longhands(&self) -> NonCustomPropertyIterator<LonghandId> {
% for property in data.shorthands:

View file

@ -148,21 +148,32 @@
% endfor
image.to_css(dest)?;
dest.write_str(" ")?;
mode.to_css(dest)?;
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 *mode != mask_mode::single_value::get_initial_specified_value() {
dest.write_str(" ")?;
mode.to_css(dest)?;
}
if *position_x != PositionComponent::zero() ||
*position_y != PositionComponent::zero() ||
*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 {
dest.write_str(" ")?;
@ -173,8 +184,10 @@
}
}
dest.write_str(" ")?;
composite.to_css(dest)?;
if *composite != mask_composite::single_value::get_initial_specified_value() {
dest.write_str(" ")?;
composite.to_css(dest)?;
}
}
Ok(())

View file

@ -5,12 +5,9 @@
//! Computed types for CSS values related to backgrounds.
use crate::values::computed::length::NonNegativeLengthOrPercentageOrAuto;
use crate::values::computed::{Context, ToComputedValue};
use crate::values::generics::background::BackgroundSize as GenericBackgroundSize;
use crate::values::specified::background::BackgroundRepeat as SpecifiedBackgroundRepeat;
use crate::values::specified::background::BackgroundRepeatKeyword;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
pub use crate::values::specified::background::BackgroundRepeat;
/// A computed value for the `background-size` property.
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::specified::box_ as specified;
pub use crate::values::specified::box_::{
AnimationName, Appearance, Contain, Display, OverflowClipBox,
};
pub use crate::values::specified::box_::{AnimationName, Appearance, BreakBetween, BreakWithin};
pub use crate::values::specified::box_::{Clear as SpecifiedClear, Float as SpecifiedFloat};
pub use crate::values::specified::box_::{
OverscrollBehavior, ScrollSnapType, TouchAction, TransitionProperty, WillChange,
};
pub use crate::values::specified::box_::{Contain, Display, OverflowClipBox};
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.
pub type VerticalAlign = GenericVerticalAlign<LengthOrPercentage>;

View file

@ -4,10 +4,12 @@
//! Computed values.
use self::transform::DirectionVector;
use super::animated::ToAnimatedValue;
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::specified;
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::{BorderImageSlice, BorderImageWidth};
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_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
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};
#[cfg(feature = "gecko")]
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::{Auto, Either, None_};
pub use app_units::Au;
@ -459,6 +461,19 @@ trivial_to_computed_value!(Box<str>);
/// A `<number>` value.
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.
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::{Auto, Either};
pub use crate::values::specified::ui::MozForceBrokenImageIcon;
pub use crate::values::specified::ui::{MozForceBrokenImageIcon, UserSelect};
/// auto | <color>
pub type ColorOrAuto = Either<Color, Auto>;

View file

@ -538,7 +538,6 @@ pub fn get_normalized_vector_and_angle<T: Zero>(
SpecifiedValueInfo,
ToAnimatedZero,
ToComputedValue,
ToCss,
)]
/// A value of the `Rotate` property
///
@ -552,6 +551,53 @@ pub enum Rotate<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(
Clone,
ComputeSquaredDistance,

View file

@ -9,7 +9,8 @@ use crate::values::generics::background::BackgroundSize as GenericBackgroundSize
use crate::values::specified::length::NonNegativeLengthOrPercentageOrAuto;
use cssparser::Parser;
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.
pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthOrPercentageOrAuto>;
@ -56,6 +57,7 @@ impl BackgroundSize {
ToCss,
)]
#[allow(missing_docs)]
#[value_info(other_values = "repeat-x,repeat-y")]
pub enum BackgroundRepeatKeyword {
Repeat,
Space,
@ -63,24 +65,45 @@ pub enum BackgroundRepeatKeyword {
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
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
pub enum BackgroundRepeat {
/// `repeat-x`
RepeatX,
/// `repeat-y`
RepeatY,
/// `[repeat | space | round | no-repeat]{1,2}`
Keywords(BackgroundRepeatKeyword, Option<BackgroundRepeatKeyword>),
}
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)]
pub struct BackgroundRepeat(pub BackgroundRepeatKeyword, pub BackgroundRepeatKeyword);
impl BackgroundRepeat {
/// Returns the `repeat` value.
#[inline]
/// Returns the `repeat repeat` value.
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()?;
match_ignore_ascii_case! { &ident,
"repeat-x" => return Ok(BackgroundRepeat::RepeatX),
"repeat-y" => return Ok(BackgroundRepeat::RepeatY),
"repeat-x" => {
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();
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
use crate::parser::{Parse, ParserContext};
use crate::values::computed::Percentage;
use crate::values::generics::basic_shape as generic;
use crate::values::generics::basic_shape::{GeometryBox, Path, PolygonCoord};
use crate::values::generics::basic_shape::{ShapeBox, ShapeSource};
use crate::values::generics::rect::Rect;
use crate::values::specified::border::BorderRadius;
use crate::values::specified::image::Image;
use crate::values::specified::position::{HorizontalPosition, Position, PositionComponent};
use crate::values::specified::position::{Side, VerticalPosition};
use crate::values::specified::position::{HorizontalPosition, Position, VerticalPosition};
use crate::values::specified::url::SpecifiedUrl;
use crate::values::specified::LengthOrPercentage;
use crate::values::specified::SVGPathData;
use cssparser::Parser;
use std::borrow::Cow;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
@ -249,7 +246,7 @@ impl ToCss for Circle {
}
dest.write_str("at ")?;
serialize_basicshape_position(&self.position, dest)?;
self.position.to_css(dest)?;
dest.write_str(")")
}
}
@ -305,7 +302,7 @@ impl ToCss for Ellipse {
}
dest.write_str("at ")?;
serialize_basicshape_position(&self.position, dest)?;
self.position.to_css(dest)?;
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 {
fn parse<'i, 't>(
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
/// FrameConstructionDataByDisplay stuff (both the XUL and non-XUL version), and
/// 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)]
#[derive(
Clone,
@ -946,9 +943,6 @@ pub enum Resize {
/// The value for the `appearance` property.
///
/// 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)]
#[derive(
Clone,
@ -1295,3 +1289,52 @@ pub enum Appearance {
#[css(skip)]
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.
use super::computed::transform::DirectionVector;
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::{Auto, CSSFloat, CSSInteger, Either};
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::{BorderRadius, BorderSideWidth, BorderSpacing};
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_::{ScrollSnapType, TouchAction, TransitionProperty, VerticalAlign, WillChange};
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};
#[cfg(feature = "gecko")]
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;
#[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 From<Number> for f32 {

View file

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