mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
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:
commit
b20fab699e
27 changed files with 458 additions and 402 deletions
|
@ -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"]
|
||||
|
|
|
@ -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,
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -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, _),
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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!(),
|
||||
},
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")}
|
||||
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
)}
|
||||
|
|
|
@ -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",
|
||||
)}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue