mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +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"
|
prefix = "Style"
|
||||||
include = [
|
include = [
|
||||||
"Appearance",
|
"Appearance",
|
||||||
|
"BreakBetween",
|
||||||
|
"BreakWithin",
|
||||||
"ComputedFontStretchRange",
|
"ComputedFontStretchRange",
|
||||||
"ComputedFontStyleDescriptor",
|
"ComputedFontStyleDescriptor",
|
||||||
"ComputedFontWeightRange",
|
"ComputedFontWeightRange",
|
||||||
|
@ -53,5 +55,6 @@ include = [
|
||||||
"TimingFunction",
|
"TimingFunction",
|
||||||
"PathCommand",
|
"PathCommand",
|
||||||
"UnicodeRange",
|
"UnicodeRange",
|
||||||
|
"UserSelect",
|
||||||
]
|
]
|
||||||
item_types = ["enums", "structs", "typedefs"]
|
item_types = ["enums", "structs", "typedefs"]
|
||||||
|
|
|
@ -109,7 +109,7 @@ pub struct VariableValue {
|
||||||
references_environment: bool,
|
references_environment: bool,
|
||||||
|
|
||||||
/// Custom property names in var() functions.
|
/// Custom property names in var() functions.
|
||||||
references: PrecomputedHashSet<Name>,
|
references: Box<[Name]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCss for SpecifiedValue {
|
impl ToCss for SpecifiedValue {
|
||||||
|
@ -278,7 +278,7 @@ impl VariableValue {
|
||||||
css: String::new(),
|
css: String::new(),
|
||||||
last_token_type: TokenSerializationType::nothing(),
|
last_token_type: TokenSerializationType::nothing(),
|
||||||
first_token_type: TokenSerializationType::nothing(),
|
first_token_type: TokenSerializationType::nothing(),
|
||||||
references: PrecomputedHashSet::default(),
|
references: Default::default(),
|
||||||
references_environment: false,
|
references_environment: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,11 +335,17 @@ impl VariableValue {
|
||||||
let (first_token_type, css, last_token_type) =
|
let (first_token_type, css, last_token_type) =
|
||||||
parse_self_contained_declaration_value(input, Some(&mut references))?;
|
parse_self_contained_declaration_value(input, Some(&mut references))?;
|
||||||
|
|
||||||
|
let custom_property_references = references
|
||||||
|
.custom_property_references
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_boxed_slice();
|
||||||
|
|
||||||
Ok(Arc::new(VariableValue {
|
Ok(Arc::new(VariableValue {
|
||||||
css: css.into_owned(),
|
css: css.into_owned(),
|
||||||
first_token_type,
|
first_token_type,
|
||||||
last_token_type,
|
last_token_type,
|
||||||
references: references.custom_property_references,
|
references: custom_property_references,
|
||||||
references_environment: references.references_environment,
|
references_environment: references.references_environment,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ macro_rules! apply_non_ts_list {
|
||||||
("visited", Visited, visited, IN_VISITED_STATE, _),
|
("visited", Visited, visited, IN_VISITED_STATE, _),
|
||||||
("active", Active, active, IN_ACTIVE_STATE, _),
|
("active", Active, active, IN_ACTIVE_STATE, _),
|
||||||
("checked", Checked, checked, IN_CHECKED_STATE, _),
|
("checked", Checked, checked, IN_CHECKED_STATE, _),
|
||||||
("defined", Defined, defined, IN_DEFINED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
|
("defined", Defined, defined, IN_DEFINED_STATE, _),
|
||||||
("disabled", Disabled, disabled, IN_DISABLED_STATE, _),
|
("disabled", Disabled, disabled, IN_DISABLED_STATE, _),
|
||||||
("enabled", Enabled, enabled, IN_ENABLED_STATE, _),
|
("enabled", Enabled, enabled, IN_ENABLED_STATE, _),
|
||||||
("focus", Focus, focus, IN_FOCUS_STATE, _),
|
("focus", Focus, focus, IN_FOCUS_STATE, _),
|
||||||
|
|
|
@ -16,8 +16,8 @@ sys.path.insert(0, os.path.join(os.path.dirname(GECKO_DIR), "properties"))
|
||||||
import build
|
import build
|
||||||
|
|
||||||
|
|
||||||
# Matches lines like `GK_ATOM(foo, "foo", 0x12345678, nsStaticAtom, PseudoElementAtom)`.
|
# Matches lines like `GK_ATOM(foo, "foo", 0x12345678, true, nsStaticAtom, PseudoElementAtom)`.
|
||||||
PATTERN = re.compile('^GK_ATOM\(([^,]*),[^"]*"([^"]*)",\s*(0x[0-9a-f]+),\s*([^,]*),\s*([^)]*)\)',
|
PATTERN = re.compile('^GK_ATOM\(([^,]*),[^"]*"([^"]*)",\s*(0x[0-9a-f]+),\s*[^,]*,\s*([^,]*),\s*([^)]*)\)',
|
||||||
re.MULTILINE)
|
re.MULTILINE)
|
||||||
FILE = "include/nsGkAtomList.h"
|
FILE = "include/nsGkAtomList.h"
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
//! Gecko-specific bits for selector-parsing.
|
//! Gecko-specific bits for selector-parsing.
|
||||||
|
|
||||||
use crate::element_state::{DocumentState, ElementState};
|
use crate::element_state::{DocumentState, ElementState};
|
||||||
use crate::gecko_bindings::structs;
|
|
||||||
use crate::gecko_bindings::structs::RawServoSelectorList;
|
use crate::gecko_bindings::structs::RawServoSelectorList;
|
||||||
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
|
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
|
||||||
use crate::invalidation::element::document_state::InvalidationMatchingData;
|
use crate::invalidation::element::document_state::InvalidationMatchingData;
|
||||||
|
@ -178,9 +177,6 @@ impl NonTSPseudoClass {
|
||||||
NonTSPseudoClass::Fullscreen => unsafe {
|
NonTSPseudoClass::Fullscreen => unsafe {
|
||||||
mozilla::StaticPrefs_sVarCache_full_screen_api_unprefix_enabled
|
mozilla::StaticPrefs_sVarCache_full_screen_api_unprefix_enabled
|
||||||
},
|
},
|
||||||
NonTSPseudoClass::Defined => unsafe {
|
|
||||||
structs::nsContentUtils_sIsCustomElementsEnabled
|
|
||||||
},
|
|
||||||
// Otherwise, a pseudo-class is enabled in content when it
|
// Otherwise, a pseudo-class is enabled in content when it
|
||||||
// doesn't have any enabled flag.
|
// doesn't have any enabled flag.
|
||||||
_ => !self
|
_ => !self
|
||||||
|
@ -347,10 +343,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn parse_slotted(&self) -> bool {
|
fn parse_slotted(&self) -> bool {
|
||||||
// NOTE(emilio): Slot assignment and such works per-document, but
|
true
|
||||||
// getting a document around here is not trivial, and it's not worth
|
|
||||||
// anyway to handle this in a per-doc basis.
|
|
||||||
unsafe { structs::nsContentUtils_sIsShadowDOMEnabled }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -175,13 +175,19 @@ impl WeakAtom {
|
||||||
/// Returns whether this atom is static.
|
/// Returns whether this atom is static.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_static(&self) -> bool {
|
pub fn is_static(&self) -> bool {
|
||||||
unsafe { (*self.as_ptr()).mIsStatic() != 0 }
|
self.0.mIsStatic() != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether this atom is ascii lowercase.
|
||||||
|
#[inline]
|
||||||
|
fn is_ascii_lowercase(&self) -> bool {
|
||||||
|
self.0.mIsAsciiLowercase() != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the length of the atom string.
|
/// Returns the length of the atom string.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn len(&self) -> u32 {
|
pub fn len(&self) -> u32 {
|
||||||
unsafe { (*self.as_ptr()).mLength() }
|
self.0.mLength()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether this atom is the empty string.
|
/// Returns whether this atom is the empty string.
|
||||||
|
@ -199,41 +205,54 @@ impl WeakAtom {
|
||||||
|
|
||||||
/// Convert this atom to ASCII lower-case
|
/// Convert this atom to ASCII lower-case
|
||||||
pub fn to_ascii_lowercase(&self) -> Atom {
|
pub fn to_ascii_lowercase(&self) -> Atom {
|
||||||
let slice = self.as_slice();
|
if self.is_ascii_lowercase() {
|
||||||
match slice
|
return self.clone();
|
||||||
.iter()
|
|
||||||
.position(|&char16| (b'A' as u16) <= char16 && char16 <= (b'Z' as u16))
|
|
||||||
{
|
|
||||||
None => self.clone(),
|
|
||||||
Some(i) => {
|
|
||||||
let mut buffer: [u16; 64] = unsafe { mem::uninitialized() };
|
|
||||||
let mut vec;
|
|
||||||
let mutable_slice = if let Some(buffer_prefix) = buffer.get_mut(..slice.len()) {
|
|
||||||
buffer_prefix.copy_from_slice(slice);
|
|
||||||
buffer_prefix
|
|
||||||
} else {
|
|
||||||
vec = slice.to_vec();
|
|
||||||
&mut vec
|
|
||||||
};
|
|
||||||
for char16 in &mut mutable_slice[i..] {
|
|
||||||
if *char16 <= 0x7F {
|
|
||||||
*char16 = (*char16 as u8).to_ascii_lowercase() as u16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Atom::from(&*mutable_slice)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let slice = self.as_slice();
|
||||||
|
let mut buffer: [u16; 64] = unsafe { mem::uninitialized() };
|
||||||
|
let mut vec;
|
||||||
|
let mutable_slice = if let Some(buffer_prefix) = buffer.get_mut(..slice.len()) {
|
||||||
|
buffer_prefix.copy_from_slice(slice);
|
||||||
|
buffer_prefix
|
||||||
|
} else {
|
||||||
|
vec = slice.to_vec();
|
||||||
|
&mut vec
|
||||||
|
};
|
||||||
|
for char16 in &mut *mutable_slice {
|
||||||
|
if *char16 <= 0x7F {
|
||||||
|
*char16 = (*char16 as u8).to_ascii_lowercase() as u16
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Atom::from(&*mutable_slice)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return whether two atoms are ASCII-case-insensitive matches
|
/// Return whether two atoms are ASCII-case-insensitive matches
|
||||||
|
#[inline]
|
||||||
pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
|
pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool {
|
||||||
if self == other {
|
if self == other {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we know both atoms are ascii-lowercase, then we can stick with
|
||||||
|
// pointer equality.
|
||||||
|
if self.is_ascii_lowercase() && other.is_ascii_lowercase() {
|
||||||
|
debug_assert!(!self.eq_ignore_ascii_case_slow(other));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.eq_ignore_ascii_case_slow(other)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eq_ignore_ascii_case_slow(&self, other: &Self) -> bool {
|
||||||
let a = self.as_slice();
|
let a = self.as_slice();
|
||||||
let b = other.as_slice();
|
let b = other.as_slice();
|
||||||
a.len() == b.len() && a.iter().zip(b).all(|(&a16, &b16)| {
|
|
||||||
|
if a.len() != b.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.iter().zip(b).all(|(&a16, &b16)| {
|
||||||
if a16 <= 0x7F && b16 <= 0x7F {
|
if a16 <= 0x7F && b16 <= 0x7F {
|
||||||
(a16 as u8).eq_ignore_ascii_case(&(b16 as u8))
|
(a16 as u8).eq_ignore_ascii_case(&(b16 as u8))
|
||||||
} else {
|
} else {
|
||||||
|
@ -241,13 +260,6 @@ impl WeakAtom {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return whether this atom is an ASCII-case-insensitive match for the given string
|
|
||||||
pub fn eq_str_ignore_ascii_case(&self, other: &str) -> bool {
|
|
||||||
self.chars()
|
|
||||||
.map(|r| r.map(|c: char| c.to_ascii_lowercase()))
|
|
||||||
.eq(other.chars().map(|c: char| Ok(c.to_ascii_lowercase())))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for WeakAtom {
|
impl fmt::Debug for WeakAtom {
|
||||||
|
|
|
@ -10,8 +10,12 @@ use super::media_feature::{KeywordDiscriminant, ParsingRequirements};
|
||||||
use super::Device;
|
use super::Device;
|
||||||
use crate::context::QuirksMode;
|
use crate::context::QuirksMode;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
use crate::gecko::media_features::MEDIA_FEATURES;
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
use crate::gecko_bindings::structs;
|
use crate::gecko_bindings::structs;
|
||||||
use crate::parser::{Parse, ParserContext};
|
use crate::parser::{Parse, ParserContext};
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
use crate::servo::media_queries::MEDIA_FEATURES;
|
||||||
use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
|
use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
|
||||||
use crate::stylesheets::Origin;
|
use crate::stylesheets::Origin;
|
||||||
use crate::values::computed::{self, ToComputedValue};
|
use crate::values::computed::{self, ToComputedValue};
|
||||||
|
@ -150,14 +154,14 @@ impl RangeOrOperator {
|
||||||
/// the media query contained, and the range to evaluate.
|
/// the media query contained, and the range to evaluate.
|
||||||
#[derive(Clone, Debug, MallocSizeOf)]
|
#[derive(Clone, Debug, MallocSizeOf)]
|
||||||
pub struct MediaFeatureExpression {
|
pub struct MediaFeatureExpression {
|
||||||
feature: &'static MediaFeatureDescription,
|
feature_index: usize,
|
||||||
value: Option<MediaExpressionValue>,
|
value: Option<MediaExpressionValue>,
|
||||||
range_or_operator: Option<RangeOrOperator>,
|
range_or_operator: Option<RangeOrOperator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for MediaFeatureExpression {
|
impl PartialEq for MediaFeatureExpression {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.feature as *const _ == other.feature as *const _ &&
|
self.feature_index == other.feature_index &&
|
||||||
self.value == other.value &&
|
self.value == other.value &&
|
||||||
self.range_or_operator == other.range_or_operator
|
self.range_or_operator == other.range_or_operator
|
||||||
}
|
}
|
||||||
|
@ -170,8 +174,9 @@ impl ToCss for MediaFeatureExpression {
|
||||||
{
|
{
|
||||||
dest.write_str("(")?;
|
dest.write_str("(")?;
|
||||||
|
|
||||||
if self
|
let feature = self.feature();
|
||||||
.feature
|
|
||||||
|
if feature
|
||||||
.requirements
|
.requirements
|
||||||
.contains(ParsingRequirements::WEBKIT_PREFIX)
|
.contains(ParsingRequirements::WEBKIT_PREFIX)
|
||||||
{
|
{
|
||||||
|
@ -186,7 +191,7 @@ impl ToCss for MediaFeatureExpression {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: CssStringWriter not needed, feature names are under control.
|
// NB: CssStringWriter not needed, feature names are under control.
|
||||||
write!(dest, "{}", self.feature.name)?;
|
write!(dest, "{}", feature.name)?;
|
||||||
|
|
||||||
if let Some(RangeOrOperator::Operator(op)) = self.range_or_operator {
|
if let Some(RangeOrOperator::Operator(op)) = self.range_or_operator {
|
||||||
dest.write_char(' ')?;
|
dest.write_char(' ')?;
|
||||||
|
@ -240,17 +245,22 @@ fn consume_operation_or_colon(input: &mut Parser) -> Result<Option<Operator>, ()
|
||||||
|
|
||||||
impl MediaFeatureExpression {
|
impl MediaFeatureExpression {
|
||||||
fn new(
|
fn new(
|
||||||
feature: &'static MediaFeatureDescription,
|
feature_index: usize,
|
||||||
value: Option<MediaExpressionValue>,
|
value: Option<MediaExpressionValue>,
|
||||||
range_or_operator: Option<RangeOrOperator>,
|
range_or_operator: Option<RangeOrOperator>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
debug_assert!(feature_index < MEDIA_FEATURES.len());
|
||||||
Self {
|
Self {
|
||||||
feature,
|
feature_index,
|
||||||
value,
|
value,
|
||||||
range_or_operator,
|
range_or_operator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn feature(&self) -> &'static MediaFeatureDescription {
|
||||||
|
&MEDIA_FEATURES[self.feature_index]
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a media expression of the form:
|
/// Parse a media expression of the form:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -270,12 +280,8 @@ impl MediaFeatureExpression {
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
use crate::gecko::media_features::MEDIA_FEATURES;
|
|
||||||
#[cfg(feature = "servo")]
|
|
||||||
use crate::servo::media_queries::MEDIA_FEATURES;
|
|
||||||
|
|
||||||
// FIXME: remove extra indented block when lifetimes are non-lexical
|
// FIXME: remove extra indented block when lifetimes are non-lexical
|
||||||
|
let feature_index;
|
||||||
let feature;
|
let feature;
|
||||||
let range;
|
let range;
|
||||||
{
|
{
|
||||||
|
@ -319,14 +325,19 @@ impl MediaFeatureExpression {
|
||||||
};
|
};
|
||||||
|
|
||||||
let atom = Atom::from(string_as_ascii_lowercase(feature_name));
|
let atom = Atom::from(string_as_ascii_lowercase(feature_name));
|
||||||
match MEDIA_FEATURES.iter().find(|f| f.name == atom) {
|
match MEDIA_FEATURES
|
||||||
Some(f) => Ok((f, range)),
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, f)| f.name == atom)
|
||||||
|
{
|
||||||
|
Some((i, f)) => Ok((i, f, range)),
|
||||||
None => Err(()),
|
None => Err(()),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok((f, r)) => {
|
Ok((i, f, r)) => {
|
||||||
|
feature_index = i;
|
||||||
feature = f;
|
feature = f;
|
||||||
range = r;
|
range = r;
|
||||||
},
|
},
|
||||||
|
@ -365,7 +376,7 @@ impl MediaFeatureExpression {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Self::new(feature, None, None));
|
return Ok(Self::new(feature_index, None, None));
|
||||||
},
|
},
|
||||||
Ok(operator) => operator,
|
Ok(operator) => operator,
|
||||||
};
|
};
|
||||||
|
@ -396,7 +407,7 @@ impl MediaFeatureExpression {
|
||||||
.new_custom_error(StyleParseErrorKind::MediaQueryExpectedFeatureValue)
|
.new_custom_error(StyleParseErrorKind::MediaQueryExpectedFeatureValue)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Self::new(feature, Some(value), range_or_operator))
|
Ok(Self::new(feature_index, Some(value), range_or_operator))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether this media query evaluates to true for the given device.
|
/// Returns whether this media query evaluates to true for the given device.
|
||||||
|
@ -412,7 +423,7 @@ impl MediaFeatureExpression {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.feature.evaluator {
|
match self.feature().evaluator {
|
||||||
Evaluator::Length(eval) => {
|
Evaluator::Length(eval) => {
|
||||||
let computed = expect!(Length).map(|specified| {
|
let computed = expect!(Length).map(|specified| {
|
||||||
computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {
|
computed::Context::for_media_query_evaluation(device, quirks_mode, |context| {
|
||||||
|
@ -492,7 +503,7 @@ impl MediaExpressionValue {
|
||||||
MediaExpressionValue::IntRatio(ratio) => ratio.to_css(dest),
|
MediaExpressionValue::IntRatio(ratio) => ratio.to_css(dest),
|
||||||
MediaExpressionValue::Resolution(ref r) => r.to_css(dest),
|
MediaExpressionValue::Resolution(ref r) => r.to_css(dest),
|
||||||
MediaExpressionValue::Ident(ref ident) => serialize_atom_identifier(ident, dest),
|
MediaExpressionValue::Ident(ref ident) => serialize_atom_identifier(ident, dest),
|
||||||
MediaExpressionValue::Enumerated(value) => match for_expr.feature.evaluator {
|
MediaExpressionValue::Enumerated(value) => match for_expr.feature().evaluator {
|
||||||
Evaluator::Enumerated { serializer, .. } => dest.write_str(&*serializer(value)),
|
Evaluator::Enumerated { serializer, .. } => dest.write_str(&*serializer(value)),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
|
|
|
@ -291,6 +291,8 @@ class Longhand(object):
|
||||||
"AlignItems",
|
"AlignItems",
|
||||||
"AlignSelf",
|
"AlignSelf",
|
||||||
"Appearance",
|
"Appearance",
|
||||||
|
"BreakBetween",
|
||||||
|
"BreakWithin",
|
||||||
"BackgroundRepeat",
|
"BackgroundRepeat",
|
||||||
"BorderImageRepeat",
|
"BorderImageRepeat",
|
||||||
"BorderStyle",
|
"BorderStyle",
|
||||||
|
@ -336,6 +338,7 @@ class Longhand(object):
|
||||||
"TextEmphasisPosition",
|
"TextEmphasisPosition",
|
||||||
"TouchAction",
|
"TouchAction",
|
||||||
"TransformStyle",
|
"TransformStyle",
|
||||||
|
"UserSelect",
|
||||||
"XSpan",
|
"XSpan",
|
||||||
"XTextZoom",
|
"XTextZoom",
|
||||||
"ZIndex",
|
"ZIndex",
|
||||||
|
|
|
@ -309,7 +309,9 @@ impl PropertyDeclarationBlock {
|
||||||
.find(|(declaration, _)| declaration.id() == property)
|
.find(|(declaration, _)| declaration.id() == property)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shorthand_to_css(
|
/// Tries to serialize a given shorthand from the declarations in this
|
||||||
|
/// block.
|
||||||
|
pub fn shorthand_to_css(
|
||||||
&self,
|
&self,
|
||||||
shorthand: ShorthandId,
|
shorthand: ShorthandId,
|
||||||
dest: &mut CssStringWriter,
|
dest: &mut CssStringWriter,
|
||||||
|
|
|
@ -1396,6 +1396,8 @@ impl Clone for ${style_struct.gecko_struct_name} {
|
||||||
|
|
||||||
# Types used with predefined_type()-defined properties that we can auto-generate.
|
# Types used with predefined_type()-defined properties that we can auto-generate.
|
||||||
predefined_types = {
|
predefined_types = {
|
||||||
|
"BreakBetween": impl_simple,
|
||||||
|
"BreakWithin": impl_simple,
|
||||||
"Color": impl_color,
|
"Color": impl_color,
|
||||||
"ColorOrAuto": impl_color,
|
"ColorOrAuto": impl_color,
|
||||||
"GreaterThanOrEqualToOneNumber": impl_simple,
|
"GreaterThanOrEqualToOneNumber": impl_simple,
|
||||||
|
@ -1429,6 +1431,7 @@ impl Clone for ${style_struct.gecko_struct_name} {
|
||||||
"SVGWidth": impl_svg_length,
|
"SVGWidth": impl_svg_length,
|
||||||
"Transform": impl_transform,
|
"Transform": impl_transform,
|
||||||
"TransformOrigin": impl_transform_origin,
|
"TransformOrigin": impl_transform_origin,
|
||||||
|
"UserSelect": impl_simple,
|
||||||
"url::UrlOrNone": impl_css_url,
|
"url::UrlOrNone": impl_css_url,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3028,8 +3031,7 @@ fn static_assert() {
|
||||||
animation-iteration-count animation-timing-function
|
animation-iteration-count animation-timing-function
|
||||||
transition-duration transition-delay
|
transition-duration transition-delay
|
||||||
transition-timing-function transition-property
|
transition-timing-function transition-property
|
||||||
page-break-before page-break-after rotate
|
rotate scroll-snap-points-x scroll-snap-points-y
|
||||||
scroll-snap-points-x scroll-snap-points-y
|
|
||||||
scroll-snap-type-x scroll-snap-type-y scroll-snap-coordinate
|
scroll-snap-type-x scroll-snap-type-y scroll-snap-coordinate
|
||||||
perspective-origin -moz-binding will-change
|
perspective-origin -moz-binding will-change
|
||||||
offset-path overscroll-behavior-x overscroll-behavior-y
|
offset-path overscroll-behavior-x overscroll-behavior-y
|
||||||
|
@ -3148,35 +3150,6 @@ fn static_assert() {
|
||||||
|
|
||||||
<%call expr="impl_coord_copy('vertical_align', 'mVerticalAlign')"></%call>
|
<%call expr="impl_coord_copy('vertical_align', 'mVerticalAlign')"></%call>
|
||||||
|
|
||||||
% for kind in ["before", "after"]:
|
|
||||||
// Temp fix for Bugzilla bug 24000.
|
|
||||||
// Map 'auto' and 'avoid' to false, and 'always', 'left', and 'right' to true.
|
|
||||||
// "A conforming user agent may interpret the values 'left' and 'right'
|
|
||||||
// as 'always'." - CSS2.1, section 13.3.1
|
|
||||||
pub fn set_page_break_${kind}(&mut self, v: longhands::page_break_${kind}::computed_value::T) {
|
|
||||||
use crate::computed_values::page_break_${kind}::T;
|
|
||||||
|
|
||||||
let result = match v {
|
|
||||||
T::Auto => false,
|
|
||||||
T::Always => true,
|
|
||||||
T::Avoid => false,
|
|
||||||
T::Left => true,
|
|
||||||
T::Right => true
|
|
||||||
};
|
|
||||||
self.gecko.mBreak${kind.title()} = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
${impl_simple_copy('page_break_' + kind, 'mBreak' + kind.title())}
|
|
||||||
|
|
||||||
// Temp fix for Bugzilla bug 24000.
|
|
||||||
// See set_page_break_before/after for detail.
|
|
||||||
pub fn clone_page_break_${kind}(&self) -> longhands::page_break_${kind}::computed_value::T {
|
|
||||||
use crate::computed_values::page_break_${kind}::T;
|
|
||||||
|
|
||||||
if self.gecko.mBreak${kind.title()} { T::Always } else { T::Auto }
|
|
||||||
}
|
|
||||||
% endfor
|
|
||||||
|
|
||||||
${impl_style_coord("scroll_snap_points_x", "mScrollSnapPointsX")}
|
${impl_style_coord("scroll_snap_points_x", "mScrollSnapPointsX")}
|
||||||
${impl_style_coord("scroll_snap_points_y", "mScrollSnapPointsY")}
|
${impl_style_coord("scroll_snap_points_y", "mScrollSnapPointsY")}
|
||||||
|
|
||||||
|
|
|
@ -2108,7 +2108,7 @@ impl ComputedRotate {
|
||||||
//
|
//
|
||||||
// If the axis is unspecified, it defaults to "0 0 1"
|
// If the axis is unspecified, it defaults to "0 0 1"
|
||||||
match *self {
|
match *self {
|
||||||
Rotate::None => (0., 0., 1., Angle::zero()),
|
Rotate::None => unreachable!("None is handled by the caller"),
|
||||||
Rotate::Rotate3D(rx, ry, rz, angle) => (rx, ry, rz, angle),
|
Rotate::Rotate3D(rx, ry, rz, angle) => (rx, ry, rz, angle),
|
||||||
Rotate::Rotate(angle) => (0., 0., 1., angle),
|
Rotate::Rotate(angle) => (0., 0., 1., angle),
|
||||||
}
|
}
|
||||||
|
@ -2122,41 +2122,58 @@ impl Animate for ComputedRotate {
|
||||||
other: &Self,
|
other: &Self,
|
||||||
procedure: Procedure,
|
procedure: Procedure,
|
||||||
) -> Result<Self, ()> {
|
) -> Result<Self, ()> {
|
||||||
let (from, to) = (self.resolve(), other.resolve());
|
match (self, other) {
|
||||||
|
(&Rotate::None, &Rotate::None) => Ok(Rotate::None),
|
||||||
|
(&Rotate::Rotate3D(fx, fy, fz, fa), &Rotate::None) => {
|
||||||
|
// No need to normalize `none`, so animate angle directly.
|
||||||
|
Ok(Rotate::Rotate3D(fx, fy, fz, fa.animate(&Angle::zero(), procedure)?))
|
||||||
|
},
|
||||||
|
(&Rotate::None, &Rotate::Rotate3D(tx, ty, tz, ta)) => {
|
||||||
|
// No need to normalize `none`, so animate angle directly.
|
||||||
|
Ok(Rotate::Rotate3D(tx, ty, tz, Angle::zero().animate(&ta, procedure)?))
|
||||||
|
},
|
||||||
|
(&Rotate::Rotate3D(_, ..), _) | (_, &Rotate::Rotate3D(_, ..)) => {
|
||||||
|
let (from, to) = (self.resolve(), other.resolve());
|
||||||
|
let (mut fx, mut fy, mut fz, fa) =
|
||||||
|
transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
|
||||||
|
let (mut tx, mut ty, mut tz, ta) =
|
||||||
|
transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
|
||||||
|
|
||||||
let (mut fx, mut fy, mut fz, fa) =
|
if fa == Angle::from_degrees(0.) {
|
||||||
transform::get_normalized_vector_and_angle(from.0, from.1, from.2, from.3);
|
fx = tx;
|
||||||
let (mut tx, mut ty, mut tz, ta) =
|
fy = ty;
|
||||||
transform::get_normalized_vector_and_angle(to.0, to.1, to.2, to.3);
|
fz = tz;
|
||||||
|
} else if ta == Angle::from_degrees(0.) {
|
||||||
|
tx = fx;
|
||||||
|
ty = fy;
|
||||||
|
tz = fz;
|
||||||
|
}
|
||||||
|
|
||||||
if fa == Angle::from_degrees(0.) {
|
if (fx, fy, fz) == (tx, ty, tz) {
|
||||||
fx = tx;
|
return Ok(Rotate::Rotate3D(fx, fy, fz, fa.animate(&ta, procedure)?));
|
||||||
fy = ty;
|
}
|
||||||
fz = tz;
|
|
||||||
} else if ta == Angle::from_degrees(0.) {
|
let fv = DirectionVector::new(fx, fy, fz);
|
||||||
tx = fx;
|
let tv = DirectionVector::new(tx, ty, tz);
|
||||||
ty = fy;
|
let fq = Quaternion::from_direction_and_angle(&fv, fa.radians64());
|
||||||
tz = fz;
|
let tq = Quaternion::from_direction_and_angle(&tv, ta.radians64());
|
||||||
|
|
||||||
|
let rq = Quaternion::animate(&fq, &tq, procedure)?;
|
||||||
|
let (x, y, z, angle) = transform::get_normalized_vector_and_angle(
|
||||||
|
rq.0 as f32,
|
||||||
|
rq.1 as f32,
|
||||||
|
rq.2 as f32,
|
||||||
|
rq.3.acos() as f32 * 2.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Rotate::Rotate3D(x, y, z, Angle::from_radians(angle)))
|
||||||
|
},
|
||||||
|
(&Rotate::Rotate(_), _) | (_, &Rotate::Rotate(_)) => {
|
||||||
|
// If this is a 2D rotation, we just animate the <angle>
|
||||||
|
let (from, to) = (self.resolve().3, other.resolve().3);
|
||||||
|
Ok(Rotate::Rotate(from.animate(&to, procedure)?))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fx, fy, fz) == (tx, ty, tz) {
|
|
||||||
return Ok(Rotate::Rotate3D(fx, fy, fz, fa.animate(&ta, procedure)?));
|
|
||||||
}
|
|
||||||
|
|
||||||
let fv = DirectionVector::new(fx, fy, fz);
|
|
||||||
let tv = DirectionVector::new(tx, ty, tz);
|
|
||||||
let fq = Quaternion::from_direction_and_angle(&fv, fa.radians64());
|
|
||||||
let tq = Quaternion::from_direction_and_angle(&tv, ta.radians64());
|
|
||||||
|
|
||||||
let rq = Quaternion::animate(&fq, &tq, procedure)?;
|
|
||||||
let (x, y, z, angle) = transform::get_normalized_vector_and_angle(
|
|
||||||
rq.0 as f32,
|
|
||||||
rq.1 as f32,
|
|
||||||
rq.2 as f32,
|
|
||||||
rq.3.acos() as f32 * 2.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(Rotate::Rotate3D(x, y, z, Angle::from_radians(angle)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -437,29 +437,33 @@ ${helpers.single_keyword(
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
// TODO add support for logical values recto and verso
|
${helpers.predefined_type(
|
||||||
${helpers.single_keyword(
|
|
||||||
"page-break-after",
|
"page-break-after",
|
||||||
"auto always avoid left right",
|
"BreakBetween",
|
||||||
|
"computed::BreakBetween::Auto",
|
||||||
|
needs_context=False,
|
||||||
products="gecko",
|
products="gecko",
|
||||||
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-after",
|
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-after",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${helpers.single_keyword(
|
${helpers.predefined_type(
|
||||||
"page-break-before",
|
"page-break-before",
|
||||||
"auto always avoid left right",
|
"BreakBetween",
|
||||||
|
"computed::BreakBetween::Auto",
|
||||||
|
needs_context=False,
|
||||||
products="gecko",
|
products="gecko",
|
||||||
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-before",
|
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-before",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${helpers.single_keyword(
|
${helpers.predefined_type(
|
||||||
"page-break-inside",
|
"page-break-inside",
|
||||||
"auto avoid",
|
"BreakWithin",
|
||||||
products="gecko",
|
"computed::BreakWithin::Auto",
|
||||||
gecko_ffi_name="mBreakInside",
|
gecko_ffi_name="mBreakInside",
|
||||||
gecko_constant_prefix="NS_STYLE_PAGE_BREAK",
|
needs_context=False,
|
||||||
|
products="gecko",
|
||||||
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-inside",
|
spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-inside",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -31,15 +31,13 @@ ${helpers.single_keyword(
|
||||||
spec="https://drafts.csswg.org/css-scrollbars-1/#scrollbar-width"
|
spec="https://drafts.csswg.org/css-scrollbars-1/#scrollbar-width"
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${helpers.single_keyword(
|
${helpers.predefined_type(
|
||||||
"-moz-user-select",
|
"-moz-user-select",
|
||||||
"auto text none all element elements toggle tri-state -moz-all -moz-text",
|
"UserSelect",
|
||||||
|
"computed::UserSelect::Auto",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
alias="-webkit-user-select",
|
|
||||||
gecko_ffi_name="mUserSelect",
|
gecko_ffi_name="mUserSelect",
|
||||||
gecko_enum_prefix="StyleUserSelect",
|
alias="-webkit-user-select",
|
||||||
gecko_strip_moz_prefix=False,
|
|
||||||
aliases="-moz-none=none",
|
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select",
|
spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select",
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1354,6 +1354,13 @@ impl ShorthandId {
|
||||||
NonCustomPropertyId::from(self).to_nscsspropertyid()
|
NonCustomPropertyId::from(self).to_nscsspropertyid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts from a nsCSSPropertyID to a ShorthandId.
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
#[inline]
|
||||||
|
pub fn from_nscsspropertyid(prop: nsCSSPropertyID) -> Result<Self, ()> {
|
||||||
|
PropertyId::from_nscsspropertyid(prop)?.as_shorthand().map_err(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the longhand ids that form this shorthand.
|
/// Get the longhand ids that form this shorthand.
|
||||||
pub fn longhands(&self) -> NonCustomPropertyIterator<LonghandId> {
|
pub fn longhands(&self) -> NonCustomPropertyIterator<LonghandId> {
|
||||||
% for property in data.shorthands:
|
% for property in data.shorthands:
|
||||||
|
|
|
@ -148,21 +148,32 @@
|
||||||
% endfor
|
% endfor
|
||||||
|
|
||||||
image.to_css(dest)?;
|
image.to_css(dest)?;
|
||||||
dest.write_str(" ")?;
|
|
||||||
mode.to_css(dest)?;
|
|
||||||
dest.write_str(" ")?;
|
|
||||||
|
|
||||||
Position {
|
if *mode != mask_mode::single_value::get_initial_specified_value() {
|
||||||
horizontal: position_x.clone(),
|
dest.write_str(" ")?;
|
||||||
vertical: position_y.clone()
|
mode.to_css(dest)?;
|
||||||
}.to_css(dest)?;
|
}
|
||||||
|
|
||||||
if *size != mask_size::single_value::get_initial_specified_value() {
|
if *position_x != PositionComponent::zero() ||
|
||||||
dest.write_str(" / ")?;
|
*position_y != PositionComponent::zero() ||
|
||||||
size.to_css(dest)?;
|
*size != mask_size::single_value::get_initial_specified_value()
|
||||||
|
{
|
||||||
|
dest.write_str(" ")?;
|
||||||
|
Position {
|
||||||
|
horizontal: position_x.clone(),
|
||||||
|
vertical: position_y.clone()
|
||||||
|
}.to_css(dest)?;
|
||||||
|
|
||||||
|
if *size != mask_size::single_value::get_initial_specified_value() {
|
||||||
|
dest.write_str(" / ")?;
|
||||||
|
size.to_css(dest)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *repeat != mask_repeat::single_value::get_initial_specified_value() {
|
||||||
|
dest.write_str(" ")?;
|
||||||
|
repeat.to_css(dest)?;
|
||||||
}
|
}
|
||||||
dest.write_str(" ")?;
|
|
||||||
repeat.to_css(dest)?;
|
|
||||||
|
|
||||||
if *origin != Origin::BorderBox || *clip != Clip::BorderBox {
|
if *origin != Origin::BorderBox || *clip != Clip::BorderBox {
|
||||||
dest.write_str(" ")?;
|
dest.write_str(" ")?;
|
||||||
|
@ -173,8 +184,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dest.write_str(" ")?;
|
if *composite != mask_composite::single_value::get_initial_specified_value() {
|
||||||
composite.to_css(dest)?;
|
dest.write_str(" ")?;
|
||||||
|
composite.to_css(dest)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -5,12 +5,9 @@
|
||||||
//! Computed types for CSS values related to backgrounds.
|
//! Computed types for CSS values related to backgrounds.
|
||||||
|
|
||||||
use crate::values::computed::length::NonNegativeLengthOrPercentageOrAuto;
|
use crate::values::computed::length::NonNegativeLengthOrPercentageOrAuto;
|
||||||
use crate::values::computed::{Context, ToComputedValue};
|
|
||||||
use crate::values::generics::background::BackgroundSize as GenericBackgroundSize;
|
use crate::values::generics::background::BackgroundSize as GenericBackgroundSize;
|
||||||
use crate::values::specified::background::BackgroundRepeat as SpecifiedBackgroundRepeat;
|
|
||||||
use crate::values::specified::background::BackgroundRepeatKeyword;
|
pub use crate::values::specified::background::BackgroundRepeat;
|
||||||
use std::fmt::{self, Write};
|
|
||||||
use style_traits::{CssWriter, ToCss};
|
|
||||||
|
|
||||||
/// A computed value for the `background-size` property.
|
/// A computed value for the `background-size` property.
|
||||||
pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthOrPercentageOrAuto>;
|
pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthOrPercentageOrAuto>;
|
||||||
|
@ -24,81 +21,3 @@ impl BackgroundSize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The computed value of the `background-repeat` property:
|
|
||||||
///
|
|
||||||
/// https://drafts.csswg.org/css-backgrounds/#the-background-repeat
|
|
||||||
#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
|
|
||||||
pub struct BackgroundRepeat(pub BackgroundRepeatKeyword, pub BackgroundRepeatKeyword);
|
|
||||||
|
|
||||||
impl BackgroundRepeat {
|
|
||||||
/// Returns the `repeat repeat` value.
|
|
||||||
pub fn repeat() -> Self {
|
|
||||||
BackgroundRepeat(
|
|
||||||
BackgroundRepeatKeyword::Repeat,
|
|
||||||
BackgroundRepeatKeyword::Repeat,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for BackgroundRepeat {
|
|
||||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
|
||||||
where
|
|
||||||
W: Write,
|
|
||||||
{
|
|
||||||
match (self.0, self.1) {
|
|
||||||
(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat) => {
|
|
||||||
dest.write_str("repeat-x")
|
|
||||||
},
|
|
||||||
(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat) => {
|
|
||||||
dest.write_str("repeat-y")
|
|
||||||
},
|
|
||||||
(horizontal, vertical) => {
|
|
||||||
horizontal.to_css(dest)?;
|
|
||||||
if horizontal != vertical {
|
|
||||||
dest.write_str(" ")?;
|
|
||||||
vertical.to_css(dest)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToComputedValue for SpecifiedBackgroundRepeat {
|
|
||||||
type ComputedValue = BackgroundRepeat;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
|
|
||||||
match *self {
|
|
||||||
SpecifiedBackgroundRepeat::RepeatX => BackgroundRepeat(
|
|
||||||
BackgroundRepeatKeyword::Repeat,
|
|
||||||
BackgroundRepeatKeyword::NoRepeat,
|
|
||||||
),
|
|
||||||
SpecifiedBackgroundRepeat::RepeatY => BackgroundRepeat(
|
|
||||||
BackgroundRepeatKeyword::NoRepeat,
|
|
||||||
BackgroundRepeatKeyword::Repeat,
|
|
||||||
),
|
|
||||||
SpecifiedBackgroundRepeat::Keywords(horizontal, vertical) => {
|
|
||||||
BackgroundRepeat(horizontal, vertical.unwrap_or(horizontal))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
|
|
||||||
// FIXME(emilio): Why can't this just be:
|
|
||||||
// SpecifiedBackgroundRepeat::Keywords(computed.0, computed.1)
|
|
||||||
match (computed.0, computed.1) {
|
|
||||||
(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat) => {
|
|
||||||
SpecifiedBackgroundRepeat::RepeatX
|
|
||||||
},
|
|
||||||
(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat) => {
|
|
||||||
SpecifiedBackgroundRepeat::RepeatY
|
|
||||||
},
|
|
||||||
(horizontal, vertical) => {
|
|
||||||
SpecifiedBackgroundRepeat::Keywords(horizontal, Some(vertical))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,13 +11,11 @@ use crate::values::generics::box_::Perspective as GenericPerspective;
|
||||||
use crate::values::generics::box_::VerticalAlign as GenericVerticalAlign;
|
use crate::values::generics::box_::VerticalAlign as GenericVerticalAlign;
|
||||||
use crate::values::specified::box_ as specified;
|
use crate::values::specified::box_ as specified;
|
||||||
|
|
||||||
pub use crate::values::specified::box_::{
|
pub use crate::values::specified::box_::{AnimationName, Appearance, BreakBetween, BreakWithin};
|
||||||
AnimationName, Appearance, Contain, Display, OverflowClipBox,
|
|
||||||
};
|
|
||||||
pub use crate::values::specified::box_::{Clear as SpecifiedClear, Float as SpecifiedFloat};
|
pub use crate::values::specified::box_::{Clear as SpecifiedClear, Float as SpecifiedFloat};
|
||||||
pub use crate::values::specified::box_::{
|
pub use crate::values::specified::box_::{Contain, Display, OverflowClipBox};
|
||||||
OverscrollBehavior, ScrollSnapType, TouchAction, TransitionProperty, WillChange,
|
pub use crate::values::specified::box_::{OverscrollBehavior, ScrollSnapType};
|
||||||
};
|
pub use crate::values::specified::box_::{TouchAction, TransitionProperty, WillChange};
|
||||||
|
|
||||||
/// A computed value for the `vertical-align` property.
|
/// A computed value for the `vertical-align` property.
|
||||||
pub type VerticalAlign = GenericVerticalAlign<LengthOrPercentage>;
|
pub type VerticalAlign = GenericVerticalAlign<LengthOrPercentage>;
|
||||||
|
|
|
@ -4,10 +4,12 @@
|
||||||
|
|
||||||
//! Computed values.
|
//! Computed values.
|
||||||
|
|
||||||
|
use self::transform::DirectionVector;
|
||||||
use super::animated::ToAnimatedValue;
|
use super::animated::ToAnimatedValue;
|
||||||
use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
|
use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
|
||||||
use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
|
use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
|
||||||
use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
|
use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
|
||||||
|
use super::generics::transform::IsParallelTo;
|
||||||
use super::generics::{GreaterThanOrEqualToOne, NonNegative};
|
use super::generics::{GreaterThanOrEqualToOne, NonNegative};
|
||||||
use super::specified;
|
use super::specified;
|
||||||
use super::{CSSFloat, CSSInteger};
|
use super::{CSSFloat, CSSInteger};
|
||||||
|
@ -40,7 +42,7 @@ pub use self::border::{BorderCornerRadius, BorderRadius, BorderSpacing};
|
||||||
pub use self::border::{BorderImageRepeat, BorderImageSideWidth};
|
pub use self::border::{BorderImageRepeat, BorderImageSideWidth};
|
||||||
pub use self::border::{BorderImageSlice, BorderImageWidth};
|
pub use self::border::{BorderImageSlice, BorderImageWidth};
|
||||||
pub use self::box_::{AnimationIterationCount, AnimationName, Contain};
|
pub use self::box_::{AnimationIterationCount, AnimationName, Contain};
|
||||||
pub use self::box_::{Appearance, Clear, Float};
|
pub use self::box_::{Appearance, BreakBetween, BreakWithin, Clear, Float};
|
||||||
pub use self::box_::{Display, TransitionProperty};
|
pub use self::box_::{Display, TransitionProperty};
|
||||||
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
|
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
|
||||||
pub use self::box_::{ScrollSnapType, TouchAction, VerticalAlign, WillChange};
|
pub use self::box_::{ScrollSnapType, TouchAction, VerticalAlign, WillChange};
|
||||||
|
@ -84,7 +86,7 @@ pub use self::transform::{Rotate, Scale, Transform, TransformOperation};
|
||||||
pub use self::transform::{TransformOrigin, TransformStyle, Translate};
|
pub use self::transform::{TransformOrigin, TransformStyle, Translate};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub use self::ui::CursorImage;
|
pub use self::ui::CursorImage;
|
||||||
pub use self::ui::{ColorOrAuto, Cursor, MozForceBrokenImageIcon};
|
pub use self::ui::{ColorOrAuto, Cursor, MozForceBrokenImageIcon, UserSelect};
|
||||||
pub use super::specified::{BorderStyle, TextDecorationLine};
|
pub use super::specified::{BorderStyle, TextDecorationLine};
|
||||||
pub use super::{Auto, Either, None_};
|
pub use super::{Auto, Either, None_};
|
||||||
pub use app_units::Au;
|
pub use app_units::Au;
|
||||||
|
@ -459,6 +461,19 @@ trivial_to_computed_value!(Box<str>);
|
||||||
/// A `<number>` value.
|
/// A `<number>` value.
|
||||||
pub type Number = CSSFloat;
|
pub type Number = CSSFloat;
|
||||||
|
|
||||||
|
impl IsParallelTo for (Number, Number, Number) {
|
||||||
|
fn is_parallel_to(&self, vector: &DirectionVector) -> bool {
|
||||||
|
use euclid::approxeq::ApproxEq;
|
||||||
|
// If a and b is parallel, the angle between them is 0deg, so
|
||||||
|
// a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0.
|
||||||
|
let self_vector = DirectionVector::new(self.0, self.1, self.2);
|
||||||
|
self_vector
|
||||||
|
.cross(*vector)
|
||||||
|
.square_length()
|
||||||
|
.approx_eq(&0.0f32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A wrapper of Number, but the value >= 0.
|
/// A wrapper of Number, but the value >= 0.
|
||||||
pub type NonNegativeNumber = NonNegative<CSSFloat>;
|
pub type NonNegativeNumber = NonNegative<CSSFloat>;
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::values::computed::Number;
|
||||||
use crate::values::generics::ui as generics;
|
use crate::values::generics::ui as generics;
|
||||||
use crate::values::{Auto, Either};
|
use crate::values::{Auto, Either};
|
||||||
|
|
||||||
pub use crate::values::specified::ui::MozForceBrokenImageIcon;
|
pub use crate::values::specified::ui::{MozForceBrokenImageIcon, UserSelect};
|
||||||
|
|
||||||
/// auto | <color>
|
/// auto | <color>
|
||||||
pub type ColorOrAuto = Either<Color, Auto>;
|
pub type ColorOrAuto = Either<Color, Auto>;
|
||||||
|
|
|
@ -538,7 +538,6 @@ pub fn get_normalized_vector_and_angle<T: Zero>(
|
||||||
SpecifiedValueInfo,
|
SpecifiedValueInfo,
|
||||||
ToAnimatedZero,
|
ToAnimatedZero,
|
||||||
ToComputedValue,
|
ToComputedValue,
|
||||||
ToCss,
|
|
||||||
)]
|
)]
|
||||||
/// A value of the `Rotate` property
|
/// A value of the `Rotate` property
|
||||||
///
|
///
|
||||||
|
@ -552,6 +551,53 @@ pub enum Rotate<Number, Angle> {
|
||||||
Rotate3D(Number, Number, Number, Angle),
|
Rotate3D(Number, Number, Number, Angle),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A trait to check if the current 3D vector is parallel to the DirectionVector.
|
||||||
|
/// This is especially for serialization on Rotate.
|
||||||
|
pub trait IsParallelTo {
|
||||||
|
/// Returns true if this is parallel to the vector.
|
||||||
|
fn is_parallel_to(&self, vector: &computed::transform::DirectionVector) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Number, Angle> ToCss for Rotate<Number, Angle>
|
||||||
|
where
|
||||||
|
Number: Copy + ToCss,
|
||||||
|
Angle: ToCss,
|
||||||
|
(Number, Number, Number): IsParallelTo,
|
||||||
|
{
|
||||||
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: fmt::Write,
|
||||||
|
{
|
||||||
|
use crate::values::computed::transform::DirectionVector;
|
||||||
|
match *self {
|
||||||
|
Rotate::None => dest.write_str("none"),
|
||||||
|
Rotate::Rotate(ref angle) => angle.to_css(dest),
|
||||||
|
Rotate::Rotate3D(x, y, z, ref angle) => {
|
||||||
|
// If a 3d rotation is specified, the property must serialize with an axis
|
||||||
|
// specified. If the axis is parallel with the x, y, or z axises, it must
|
||||||
|
// serialize as the appropriate keyword.
|
||||||
|
// https://drafts.csswg.org/css-transforms-2/#individual-transform-serialization
|
||||||
|
let v = (x, y, z);
|
||||||
|
if v.is_parallel_to(&DirectionVector::new(1., 0., 0.)) {
|
||||||
|
dest.write_char('x')?;
|
||||||
|
} else if v.is_parallel_to(&DirectionVector::new(0., 1., 0.)) {
|
||||||
|
dest.write_char('y')?;
|
||||||
|
} else if v.is_parallel_to(&DirectionVector::new(0., 0., 1.)) {
|
||||||
|
dest.write_char('z')?;
|
||||||
|
} else {
|
||||||
|
x.to_css(dest)?;
|
||||||
|
dest.write_char(' ')?;
|
||||||
|
y.to_css(dest)?;
|
||||||
|
dest.write_char(' ')?;
|
||||||
|
z.to_css(dest)?;
|
||||||
|
}
|
||||||
|
dest.write_char(' ')?;
|
||||||
|
angle.to_css(dest)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
ComputeSquaredDistance,
|
ComputeSquaredDistance,
|
||||||
|
|
|
@ -9,7 +9,8 @@ use crate::values::generics::background::BackgroundSize as GenericBackgroundSize
|
||||||
use crate::values::specified::length::NonNegativeLengthOrPercentageOrAuto;
|
use crate::values::specified::length::NonNegativeLengthOrPercentageOrAuto;
|
||||||
use cssparser::Parser;
|
use cssparser::Parser;
|
||||||
use selectors::parser::SelectorParseErrorKind;
|
use selectors::parser::SelectorParseErrorKind;
|
||||||
use style_traits::ParseError;
|
use std::fmt::{self, Write};
|
||||||
|
use style_traits::{CssWriter, ParseError, ToCss};
|
||||||
|
|
||||||
/// A specified value for the `background-size` property.
|
/// A specified value for the `background-size` property.
|
||||||
pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthOrPercentageOrAuto>;
|
pub type BackgroundSize = GenericBackgroundSize<NonNegativeLengthOrPercentageOrAuto>;
|
||||||
|
@ -56,6 +57,7 @@ impl BackgroundSize {
|
||||||
ToCss,
|
ToCss,
|
||||||
)]
|
)]
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
#[value_info(other_values = "repeat-x,repeat-y")]
|
||||||
pub enum BackgroundRepeatKeyword {
|
pub enum BackgroundRepeatKeyword {
|
||||||
Repeat,
|
Repeat,
|
||||||
Space,
|
Space,
|
||||||
|
@ -63,24 +65,45 @@ pub enum BackgroundRepeatKeyword {
|
||||||
NoRepeat,
|
NoRepeat,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The specified value for the `background-repeat` property.
|
/// The value of the `background-repeat` property, with `repeat-x` / `repeat-y`
|
||||||
|
/// represented as the combination of `no-repeat` and `repeat` in the opposite
|
||||||
|
/// axes.
|
||||||
///
|
///
|
||||||
/// https://drafts.csswg.org/css-backgrounds/#the-background-repeat
|
/// https://drafts.csswg.org/css-backgrounds/#the-background-repeat
|
||||||
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
|
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue)]
|
||||||
pub enum BackgroundRepeat {
|
pub struct BackgroundRepeat(pub BackgroundRepeatKeyword, pub BackgroundRepeatKeyword);
|
||||||
/// `repeat-x`
|
|
||||||
RepeatX,
|
|
||||||
/// `repeat-y`
|
|
||||||
RepeatY,
|
|
||||||
/// `[repeat | space | round | no-repeat]{1,2}`
|
|
||||||
Keywords(BackgroundRepeatKeyword, Option<BackgroundRepeatKeyword>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BackgroundRepeat {
|
impl BackgroundRepeat {
|
||||||
/// Returns the `repeat` value.
|
/// Returns the `repeat repeat` value.
|
||||||
#[inline]
|
|
||||||
pub fn repeat() -> Self {
|
pub fn repeat() -> Self {
|
||||||
BackgroundRepeat::Keywords(BackgroundRepeatKeyword::Repeat, None)
|
BackgroundRepeat(
|
||||||
|
BackgroundRepeatKeyword::Repeat,
|
||||||
|
BackgroundRepeatKeyword::Repeat,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for BackgroundRepeat {
|
||||||
|
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||||
|
where
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
match (self.0, self.1) {
|
||||||
|
(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat) => {
|
||||||
|
dest.write_str("repeat-x")
|
||||||
|
},
|
||||||
|
(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat) => {
|
||||||
|
dest.write_str("repeat-y")
|
||||||
|
},
|
||||||
|
(horizontal, vertical) => {
|
||||||
|
horizontal.to_css(dest)?;
|
||||||
|
if horizontal != vertical {
|
||||||
|
dest.write_str(" ")?;
|
||||||
|
vertical.to_css(dest)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,8 +115,12 @@ impl Parse for BackgroundRepeat {
|
||||||
let ident = input.expect_ident_cloned()?;
|
let ident = input.expect_ident_cloned()?;
|
||||||
|
|
||||||
match_ignore_ascii_case! { &ident,
|
match_ignore_ascii_case! { &ident,
|
||||||
"repeat-x" => return Ok(BackgroundRepeat::RepeatX),
|
"repeat-x" => {
|
||||||
"repeat-y" => return Ok(BackgroundRepeat::RepeatY),
|
return Ok(BackgroundRepeat(BackgroundRepeatKeyword::Repeat, BackgroundRepeatKeyword::NoRepeat));
|
||||||
|
},
|
||||||
|
"repeat-y" => {
|
||||||
|
return Ok(BackgroundRepeat(BackgroundRepeatKeyword::NoRepeat, BackgroundRepeatKeyword::Repeat));
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +134,6 @@ impl Parse for BackgroundRepeat {
|
||||||
};
|
};
|
||||||
|
|
||||||
let vertical = input.try(BackgroundRepeatKeyword::parse).ok();
|
let vertical = input.try(BackgroundRepeatKeyword::parse).ok();
|
||||||
Ok(BackgroundRepeat::Keywords(horizontal, vertical))
|
Ok(BackgroundRepeat(horizontal, vertical.unwrap_or(horizontal)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,20 +8,17 @@
|
||||||
//! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape
|
//! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape
|
||||||
|
|
||||||
use crate::parser::{Parse, ParserContext};
|
use crate::parser::{Parse, ParserContext};
|
||||||
use crate::values::computed::Percentage;
|
|
||||||
use crate::values::generics::basic_shape as generic;
|
use crate::values::generics::basic_shape as generic;
|
||||||
use crate::values::generics::basic_shape::{GeometryBox, Path, PolygonCoord};
|
use crate::values::generics::basic_shape::{GeometryBox, Path, PolygonCoord};
|
||||||
use crate::values::generics::basic_shape::{ShapeBox, ShapeSource};
|
use crate::values::generics::basic_shape::{ShapeBox, ShapeSource};
|
||||||
use crate::values::generics::rect::Rect;
|
use crate::values::generics::rect::Rect;
|
||||||
use crate::values::specified::border::BorderRadius;
|
use crate::values::specified::border::BorderRadius;
|
||||||
use crate::values::specified::image::Image;
|
use crate::values::specified::image::Image;
|
||||||
use crate::values::specified::position::{HorizontalPosition, Position, PositionComponent};
|
use crate::values::specified::position::{HorizontalPosition, Position, VerticalPosition};
|
||||||
use crate::values::specified::position::{Side, VerticalPosition};
|
|
||||||
use crate::values::specified::url::SpecifiedUrl;
|
use crate::values::specified::url::SpecifiedUrl;
|
||||||
use crate::values::specified::LengthOrPercentage;
|
use crate::values::specified::LengthOrPercentage;
|
||||||
use crate::values::specified::SVGPathData;
|
use crate::values::specified::SVGPathData;
|
||||||
use cssparser::Parser;
|
use cssparser::Parser;
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||||
|
|
||||||
|
@ -249,7 +246,7 @@ impl ToCss for Circle {
|
||||||
}
|
}
|
||||||
|
|
||||||
dest.write_str("at ")?;
|
dest.write_str("at ")?;
|
||||||
serialize_basicshape_position(&self.position, dest)?;
|
self.position.to_css(dest)?;
|
||||||
dest.write_str(")")
|
dest.write_str(")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -305,7 +302,7 @@ impl ToCss for Ellipse {
|
||||||
}
|
}
|
||||||
|
|
||||||
dest.write_str("at ")?;
|
dest.write_str("at ")?;
|
||||||
serialize_basicshape_position(&self.position, dest)?;
|
self.position.to_css(dest)?;
|
||||||
dest.write_str(")")
|
dest.write_str(")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,82 +323,6 @@ impl Parse for ShapeRadius {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <https://drafts.csswg.org/css-shapes/#basic-shape-serialization>
|
|
||||||
///
|
|
||||||
/// Positions get serialized differently with basic shapes. Keywords
|
|
||||||
/// are converted to percentages where possible. Only the two or four
|
|
||||||
/// value forms are used. In case of two keyword-percentage pairs,
|
|
||||||
/// the keywords are folded into the percentages
|
|
||||||
fn serialize_basicshape_position<W>(position: &Position, dest: &mut CssWriter<W>) -> fmt::Result
|
|
||||||
where
|
|
||||||
W: Write,
|
|
||||||
{
|
|
||||||
fn to_keyword_and_lop<S>(component: &PositionComponent<S>) -> (S, Cow<LengthOrPercentage>)
|
|
||||||
where
|
|
||||||
S: Copy + Side,
|
|
||||||
{
|
|
||||||
match *component {
|
|
||||||
PositionComponent::Center => (
|
|
||||||
S::start(),
|
|
||||||
Cow::Owned(LengthOrPercentage::Percentage(Percentage(0.5))),
|
|
||||||
),
|
|
||||||
PositionComponent::Side(keyword, None) => {
|
|
||||||
// left | top => 0%
|
|
||||||
// right | bottom => 100%
|
|
||||||
let p = if keyword.is_start() { 0. } else { 1. };
|
|
||||||
(
|
|
||||||
S::start(),
|
|
||||||
Cow::Owned(LengthOrPercentage::Percentage(Percentage(p))),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
PositionComponent::Side(keyword, Some(ref lop)) if !keyword.is_start() => {
|
|
||||||
if let LengthOrPercentage::Percentage(p) = *to_non_zero_length(lop) {
|
|
||||||
(
|
|
||||||
S::start(),
|
|
||||||
Cow::Owned(LengthOrPercentage::Percentage(Percentage(1. - p.0))),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(keyword, Cow::Borrowed(lop))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
PositionComponent::Length(ref lop) | PositionComponent::Side(_, Some(ref lop)) => {
|
|
||||||
(S::start(), to_non_zero_length(lop))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_non_zero_length(lop: &LengthOrPercentage) -> Cow<LengthOrPercentage> {
|
|
||||||
match *lop {
|
|
||||||
LengthOrPercentage::Length(ref l) if l.is_zero() => {
|
|
||||||
Cow::Owned(LengthOrPercentage::Percentage(Percentage(0.)))
|
|
||||||
},
|
|
||||||
_ => Cow::Borrowed(lop),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_pair<A, B, W>(a: &A, b: &B, dest: &mut CssWriter<W>) -> fmt::Result
|
|
||||||
where
|
|
||||||
A: ToCss,
|
|
||||||
B: ToCss,
|
|
||||||
W: Write,
|
|
||||||
{
|
|
||||||
a.to_css(dest)?;
|
|
||||||
dest.write_str(" ")?;
|
|
||||||
b.to_css(dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
let (x_pos, x_lop) = to_keyword_and_lop(&position.horizontal);
|
|
||||||
let (y_pos, y_lop) = to_keyword_and_lop(&position.vertical);
|
|
||||||
|
|
||||||
if x_pos.is_start() && y_pos.is_start() {
|
|
||||||
return write_pair(&*x_lop, &*y_lop, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
write_pair(&x_pos, &*x_lop, dest)?;
|
|
||||||
dest.write_str(" ")?;
|
|
||||||
write_pair(&y_pos, &*y_lop, dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for Polygon {
|
impl Parse for Polygon {
|
||||||
fn parse<'i, 't>(
|
fn parse<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
|
|
|
@ -52,9 +52,6 @@ fn moz_box_display_values_enabled(context: &ParserContext) -> bool {
|
||||||
/// If you change it, make sure to take a look at the
|
/// If you change it, make sure to take a look at the
|
||||||
/// FrameConstructionDataByDisplay stuff (both the XUL and non-XUL version), and
|
/// FrameConstructionDataByDisplay stuff (both the XUL and non-XUL version), and
|
||||||
/// ensure it's still correct!
|
/// ensure it's still correct!
|
||||||
///
|
|
||||||
/// Also, when you change this from Gecko you may need to regenerate the
|
|
||||||
/// C++-side bindings (see components/style/cbindgen.toml).
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
|
@ -946,9 +943,6 @@ pub enum Resize {
|
||||||
/// The value for the `appearance` property.
|
/// The value for the `appearance` property.
|
||||||
///
|
///
|
||||||
/// https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance
|
/// https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance
|
||||||
///
|
|
||||||
/// NOTE(emilio): When changing this you may want to regenerate the C++ bindings
|
|
||||||
/// (see components/style/cbindgen.toml)
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone,
|
Clone,
|
||||||
|
@ -1295,3 +1289,52 @@ pub enum Appearance {
|
||||||
#[css(skip)]
|
#[css(skip)]
|
||||||
Count,
|
Count,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A kind of break between two boxes.
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/css-break/#break-between
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Debug,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
MallocSizeOf,
|
||||||
|
Parse,
|
||||||
|
PartialEq,
|
||||||
|
SpecifiedValueInfo,
|
||||||
|
ToCss,
|
||||||
|
ToComputedValue,
|
||||||
|
)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum BreakBetween {
|
||||||
|
Auto,
|
||||||
|
Always,
|
||||||
|
Avoid,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A kind of break within a box.
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/css-break/#break-within
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Debug,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
MallocSizeOf,
|
||||||
|
Parse,
|
||||||
|
PartialEq,
|
||||||
|
SpecifiedValueInfo,
|
||||||
|
ToCss,
|
||||||
|
ToComputedValue,
|
||||||
|
)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum BreakWithin {
|
||||||
|
Auto,
|
||||||
|
Avoid,
|
||||||
|
}
|
||||||
|
|
|
@ -6,9 +6,11 @@
|
||||||
//!
|
//!
|
||||||
//! TODO(emilio): Enhance docs.
|
//! TODO(emilio): Enhance docs.
|
||||||
|
|
||||||
|
use super::computed::transform::DirectionVector;
|
||||||
use super::computed::{Context, ToComputedValue};
|
use super::computed::{Context, ToComputedValue};
|
||||||
use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
|
use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as GenericTrackBreadth};
|
||||||
use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
|
use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize};
|
||||||
|
use super::generics::transform::IsParallelTo;
|
||||||
use super::generics::{GreaterThanOrEqualToOne, NonNegative};
|
use super::generics::{GreaterThanOrEqualToOne, NonNegative};
|
||||||
use super::{Auto, CSSFloat, CSSInteger, Either};
|
use super::{Auto, CSSFloat, CSSInteger, Either};
|
||||||
use crate::context::QuirksMode;
|
use crate::context::QuirksMode;
|
||||||
|
@ -34,7 +36,7 @@ pub use self::border::{BorderCornerRadius, BorderImageSlice, BorderImageWidth};
|
||||||
pub use self::border::{BorderImageRepeat, BorderImageSideWidth};
|
pub use self::border::{BorderImageRepeat, BorderImageSideWidth};
|
||||||
pub use self::border::{BorderRadius, BorderSideWidth, BorderSpacing};
|
pub use self::border::{BorderRadius, BorderSideWidth, BorderSpacing};
|
||||||
pub use self::box_::{AnimationIterationCount, AnimationName, Contain, Display};
|
pub use self::box_::{AnimationIterationCount, AnimationName, Contain, Display};
|
||||||
pub use self::box_::{Appearance, Clear, Float};
|
pub use self::box_::{Appearance, BreakBetween, BreakWithin, Clear, Float};
|
||||||
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
|
pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize};
|
||||||
pub use self::box_::{ScrollSnapType, TouchAction, TransitionProperty, VerticalAlign, WillChange};
|
pub use self::box_::{ScrollSnapType, TouchAction, TransitionProperty, VerticalAlign, WillChange};
|
||||||
pub use self::color::{Color, ColorPropertyValue, RGBAColor};
|
pub use self::color::{Color, ColorPropertyValue, RGBAColor};
|
||||||
|
@ -82,7 +84,7 @@ pub use self::transform::{Rotate, Scale, Transform};
|
||||||
pub use self::transform::{TransformOrigin, TransformStyle, Translate};
|
pub use self::transform::{TransformOrigin, TransformStyle, Translate};
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub use self::ui::CursorImage;
|
pub use self::ui::CursorImage;
|
||||||
pub use self::ui::{ColorOrAuto, Cursor, MozForceBrokenImageIcon};
|
pub use self::ui::{ColorOrAuto, Cursor, MozForceBrokenImageIcon, UserSelect};
|
||||||
pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
|
pub use super::generics::grid::GridTemplateComponent as GenericGridTemplateComponent;
|
||||||
|
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
|
@ -292,6 +294,19 @@ impl ToCss for Number {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IsParallelTo for (Number, Number, Number) {
|
||||||
|
fn is_parallel_to(&self, vector: &DirectionVector) -> bool {
|
||||||
|
use euclid::approxeq::ApproxEq;
|
||||||
|
// If a and b is parallel, the angle between them is 0deg, so
|
||||||
|
// a x b = |a|*|b|*sin(0)*n = 0 * n, |a x b| == 0.
|
||||||
|
let self_vector = DirectionVector::new(self.0.get(), self.1.get(), self.2.get());
|
||||||
|
self_vector
|
||||||
|
.cross(*vector)
|
||||||
|
.square_length()
|
||||||
|
.approx_eq(&0.0f32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl SpecifiedValueInfo for Number {}
|
impl SpecifiedValueInfo for Number {}
|
||||||
|
|
||||||
impl From<Number> for f32 {
|
impl From<Number> for f32 {
|
||||||
|
|
|
@ -357,17 +357,38 @@ impl Parse for Rotate {
|
||||||
return Ok(generic::Rotate::None);
|
return Ok(generic::Rotate::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(rx) = input.try(|i| Number::parse(context, i)) {
|
// Parse <angle> or [ x | y | z | <number>{3} ] && <angle>.
|
||||||
// 'rotate: <number>{3} <angle>'
|
//
|
||||||
let ry = Number::parse(context, input)?;
|
// The rotate axis and angle could be in any order, so we parse angle twice to cover
|
||||||
let rz = Number::parse(context, input)?;
|
// two cases. i.e. `<number>{3} <angle>` or `<angle> <number>{3}`
|
||||||
let angle = specified::Angle::parse(context, input)?;
|
let angle = input.try(|i| specified::Angle::parse(context, i)).ok();
|
||||||
return Ok(generic::Rotate::Rotate3D(rx, ry, rz, angle));
|
let axis = input
|
||||||
}
|
.try(|i| {
|
||||||
|
Ok(try_match_ident_ignore_ascii_case! { i,
|
||||||
|
"x" => (Number::new(1.), Number::new(0.), Number::new(0.)),
|
||||||
|
"y" => (Number::new(0.), Number::new(1.), Number::new(0.)),
|
||||||
|
"z" => (Number::new(0.), Number::new(0.), Number::new(1.)),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.or_else(|_: ParseError| -> Result<_, ParseError> {
|
||||||
|
input.try(|i| {
|
||||||
|
Ok((
|
||||||
|
Number::parse(context, i)?,
|
||||||
|
Number::parse(context, i)?,
|
||||||
|
Number::parse(context, i)?,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
let angle = match angle {
|
||||||
|
Some(a) => a,
|
||||||
|
None => specified::Angle::parse(context, input)?,
|
||||||
|
};
|
||||||
|
|
||||||
// 'rotate: <angle>'
|
Ok(match axis {
|
||||||
let angle = specified::Angle::parse(context, input)?;
|
Some((x, y, z)) => generic::Rotate::Rotate3D(x, y, z, angle),
|
||||||
Ok(generic::Rotate::Rotate(angle))
|
None => generic::Rotate::Rotate(angle),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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