mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Auto merge of #24978 - emilio:gecko-sync, r=emilio
style: Sync changes from mozilla-central. See individual commits for details.
This commit is contained in:
commit
1dd6ad7063
76 changed files with 1328 additions and 1043 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -5333,7 +5333,7 @@ name = "style"
|
|||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"app_units",
|
||||
"arrayvec 0.4.6",
|
||||
"arrayvec 0.5.1",
|
||||
"atomic_refcell",
|
||||
"bindgen",
|
||||
"bitflags",
|
||||
|
@ -5941,11 +5941,11 @@ checksum = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
|
|||
|
||||
[[package]]
|
||||
name = "uluru"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2606e9192f308ddc4f0b3c5d1bf3400e28a70fff956e9d9f46d23b094746d9f"
|
||||
checksum = "6d7b39d0c32eba57d52d334e4bdd150df6e755264eefaa1ae2e7cd125f35e1ca"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.6",
|
||||
"arrayvec 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -22,7 +22,7 @@ bitflags = "1.0"
|
|||
byteorder = "1"
|
||||
canvas_traits = {path = "../canvas_traits"}
|
||||
crossbeam-channel = "0.3"
|
||||
cssparser = "0.27.1"
|
||||
cssparser = "0.27"
|
||||
embedder_traits = {path = "../embedder_traits"}
|
||||
euclid = "0.20"
|
||||
fnv = "1.0"
|
||||
|
|
|
@ -14,7 +14,7 @@ path = "lib.rs"
|
|||
webgl_backtrace = []
|
||||
|
||||
[dependencies]
|
||||
cssparser = "0.27.1"
|
||||
cssparser = "0.27"
|
||||
euclid = "0.20"
|
||||
ipc-channel = "0.12"
|
||||
lazy_static = "1"
|
||||
|
|
|
@ -514,6 +514,10 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
|||
false
|
||||
}
|
||||
|
||||
fn exports_any_part(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn style_attribute(&self) -> Option<ArcBorrow<StyleLocked<PropertyDeclarationBlock>>> {
|
||||
unsafe {
|
||||
(*self.element.style_attribute())
|
||||
|
@ -999,6 +1003,14 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
|
|||
false
|
||||
}
|
||||
|
||||
fn exported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
||||
fn imported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||
unsafe { self.element.has_class_for_layout(name, case_sensitivity) }
|
||||
|
@ -1533,6 +1545,16 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
|
|||
false
|
||||
}
|
||||
|
||||
fn exported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
debug!("ServoThreadSafeLayoutElement::exported_part called");
|
||||
None
|
||||
}
|
||||
|
||||
fn imported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
debug!("ServoThreadSafeLayoutElement::imported_part called");
|
||||
None
|
||||
}
|
||||
|
||||
fn has_class(&self, _name: &Atom, _case_sensitivity: CaseSensitivity) -> bool {
|
||||
debug!("ServoThreadSafeLayoutElement::has_class called");
|
||||
false
|
||||
|
|
|
@ -521,6 +521,10 @@ impl<'le> TElement for ServoLayoutElement<'le> {
|
|||
false
|
||||
}
|
||||
|
||||
fn exports_any_part(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn style_attribute(&self) -> Option<ArcBorrow<StyleLocked<PropertyDeclarationBlock>>> {
|
||||
unsafe {
|
||||
(*self.element.style_attribute())
|
||||
|
@ -1006,6 +1010,14 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
|
|||
false
|
||||
}
|
||||
|
||||
fn exported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
||||
fn imported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||
unsafe { self.element.has_class_for_layout(name, case_sensitivity) }
|
||||
|
@ -1540,6 +1552,16 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
|
|||
false
|
||||
}
|
||||
|
||||
fn exported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
debug!("ServoThreadSafeLayoutElement::exported_part called");
|
||||
None
|
||||
}
|
||||
|
||||
fn imported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
debug!("ServoThreadSafeLayoutElement::imported_part called");
|
||||
None
|
||||
}
|
||||
|
||||
fn has_class(&self, _name: &Atom, _case_sensitivity: CaseSensitivity) -> bool {
|
||||
debug!("ServoThreadSafeLayoutElement::has_class called");
|
||||
false
|
||||
|
|
|
@ -28,7 +28,7 @@ servo = [
|
|||
app_units = "0.7"
|
||||
content-security-policy = {version = "0.3.0", features = ["serde"], optional = true}
|
||||
crossbeam-channel = { version = "0.3", optional = true }
|
||||
cssparser = "0.27.1"
|
||||
cssparser = "0.27"
|
||||
euclid = "0.20"
|
||||
hashglobe = { path = "../hashglobe" }
|
||||
hyper = { version = "0.12", optional = true }
|
||||
|
|
|
@ -43,7 +43,7 @@ content-security-policy = {version = "0.3.0", features = ["serde"]}
|
|||
cookie = "0.11"
|
||||
chrono = "0.4"
|
||||
crossbeam-channel = "0.3"
|
||||
cssparser = "0.27.1"
|
||||
cssparser = "0.27"
|
||||
deny_public_fields = {path = "../deny_public_fields"}
|
||||
devtools_traits = {path = "../devtools_traits"}
|
||||
dom_struct = {path = "../dom_struct"}
|
||||
|
|
|
@ -3073,6 +3073,14 @@ impl<'a> SelectorsElement for DomRoot<Element> {
|
|||
false
|
||||
}
|
||||
|
||||
fn exported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
||||
fn imported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
||||
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||
Element::has_class(&**self, name, case_sensitivity)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ bench = []
|
|||
[dependencies]
|
||||
bitflags = "1.0"
|
||||
matches = "0.1"
|
||||
cssparser = "0.27.1"
|
||||
cssparser = "0.27"
|
||||
derive_more = "0.13"
|
||||
log = "0.4"
|
||||
fxhash = "0.2"
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::nth_index_cache::NthIndexCacheInner;
|
|||
use crate::parser::{AncestorHashes, Combinator, Component, LocalName};
|
||||
use crate::parser::{NonTSPseudoClass, Selector, SelectorImpl, SelectorIter, SelectorList};
|
||||
use crate::tree::Element;
|
||||
use smallvec::SmallVec;
|
||||
use std::borrow::Borrow;
|
||||
use std::iter;
|
||||
|
||||
|
@ -667,7 +668,41 @@ where
|
|||
|
||||
match *selector {
|
||||
Component::Combinator(_) => unreachable!(),
|
||||
Component::Part(ref parts) => parts.iter().all(|part| element.is_part(part)),
|
||||
Component::Part(ref parts) => {
|
||||
let mut hosts = SmallVec::<[E; 4]>::new();
|
||||
|
||||
let mut host = match element.containing_shadow_host() {
|
||||
Some(h) => h,
|
||||
None => return false,
|
||||
};
|
||||
|
||||
loop {
|
||||
let outer_host = host.containing_shadow_host();
|
||||
if outer_host.as_ref().map(|h| h.opaque()) == context.shared.current_host {
|
||||
break;
|
||||
}
|
||||
let outer_host = match outer_host {
|
||||
Some(h) => h,
|
||||
None => return false,
|
||||
};
|
||||
// TODO(emilio): if worth it, we could early return if
|
||||
// host doesn't have the exportparts attribute.
|
||||
hosts.push(host);
|
||||
host = outer_host;
|
||||
}
|
||||
|
||||
// Translate the part into the right scope.
|
||||
parts.iter().all(|part| {
|
||||
let mut part = part.clone();
|
||||
for host in hosts.iter().rev() {
|
||||
part = match host.imported_part(&part) {
|
||||
Some(p) => p,
|
||||
None => return false,
|
||||
};
|
||||
}
|
||||
element.is_part(&part)
|
||||
})
|
||||
},
|
||||
Component::Slotted(ref selector) => {
|
||||
// <slots> are never flattened tree slottables.
|
||||
!element.is_html_slot_element() &&
|
||||
|
|
|
@ -117,6 +117,20 @@ pub trait Element: Sized + Clone + Debug {
|
|||
case_sensitivity: CaseSensitivity,
|
||||
) -> bool;
|
||||
|
||||
/// Returns the mapping from the `exportparts` attribute in the regular
|
||||
/// direction, that is, inner-tree -> outer-tree.
|
||||
fn exported_part(
|
||||
&self,
|
||||
name: &<Self::Impl as SelectorImpl>::PartName,
|
||||
) -> Option<<Self::Impl as SelectorImpl>::PartName>;
|
||||
|
||||
/// Returns the mapping from the `exportparts` attribute in the reverse
|
||||
/// direction, that is, in an outer-tree -> inner-tree direction.
|
||||
fn imported_part(
|
||||
&self,
|
||||
name: &<Self::Impl as SelectorImpl>::PartName,
|
||||
) -> Option<<Self::Impl as SelectorImpl>::PartName>;
|
||||
|
||||
fn is_part(&self, name: &<Self::Impl as SelectorImpl>::PartName) -> bool;
|
||||
|
||||
/// Returns whether this element matches `:empty`.
|
||||
|
|
|
@ -485,6 +485,14 @@ impl<T: ?Sized> Arc<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Whether or not the `Arc` is a static reference.
|
||||
#[inline]
|
||||
pub fn is_static(&self) -> bool {
|
||||
// Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since
|
||||
// `count` never changes between STATIC_REFCOUNT and other values.
|
||||
self.inner().count.load(Relaxed) == STATIC_REFCOUNT
|
||||
}
|
||||
|
||||
/// Whether or not the `Arc` is uniquely owned (is the refcount 1?) and not
|
||||
/// a static reference.
|
||||
#[inline]
|
||||
|
@ -501,10 +509,7 @@ impl<T: ?Sized> Drop for Arc<T> {
|
|||
fn drop(&mut self) {
|
||||
// NOTE(emilio): If you change anything here, make sure that the
|
||||
// implementation in layout/style/ServoStyleConstsInlines.h matches!
|
||||
//
|
||||
// Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since
|
||||
// `count` never changes between STATIC_REFCOUNT and other values.
|
||||
if self.inner().count.load(Relaxed) == STATIC_REFCOUNT {
|
||||
if self.is_static() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,9 @@ doctest = false
|
|||
[features]
|
||||
gecko = ["style_traits/gecko", "fallible/known_system_malloc", "bindgen", "regex", "toml"]
|
||||
servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5ever",
|
||||
"cssparser/serde", "encoding_rs", "malloc_size_of/servo", "arrayvec/use_union",
|
||||
"servo_url", "string_cache", "crossbeam-channel", "to_shmem/servo", "servo_arc/servo"]
|
||||
"cssparser/serde", "encoding_rs", "malloc_size_of/servo", "servo_url",
|
||||
"string_cache", "crossbeam-channel", "to_shmem/servo",
|
||||
"servo_arc/servo"]
|
||||
servo-layout-2013 = []
|
||||
servo-layout-2020 = []
|
||||
gecko_debug = []
|
||||
|
@ -28,11 +29,11 @@ gecko_profiler = []
|
|||
|
||||
[dependencies]
|
||||
app_units = "0.7"
|
||||
arrayvec = "0.4.6"
|
||||
arrayvec = "0.5"
|
||||
atomic_refcell = "0.1"
|
||||
bitflags = "1.0"
|
||||
byteorder = "1.0"
|
||||
cssparser = "0.27.1"
|
||||
cssparser = "0.27"
|
||||
crossbeam-channel = { version = "0.3", optional = true }
|
||||
derive_more = "0.13"
|
||||
new_debug_unreachable = "1.0"
|
||||
|
@ -74,7 +75,7 @@ thin-slice = "0.1.0"
|
|||
to_shmem = {path = "../to_shmem"}
|
||||
to_shmem_derive = {path = "../to_shmem_derive"}
|
||||
time = "0.1"
|
||||
uluru = "0.3"
|
||||
uluru = "0.4"
|
||||
unicode-bidi = "0.3"
|
||||
unicode-segmentation = "1.0"
|
||||
void = "1.0.2"
|
||||
|
|
|
@ -16,8 +16,8 @@ use crate::properties::animated_properties::AnimatedProperty;
|
|||
use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
|
||||
use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
|
||||
use crate::properties::{self, CascadeMode, ComputedValues, LonghandId};
|
||||
use crate::rule_tree::CascadeLevel;
|
||||
use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
|
||||
use crate::stylesheets::Origin;
|
||||
use crate::timer::Timer;
|
||||
use crate::values::computed::box_::TransitionProperty;
|
||||
use crate::values::computed::Time;
|
||||
|
@ -491,7 +491,7 @@ where
|
|||
guard
|
||||
.normal_declaration_iter()
|
||||
.filter(|declaration| declaration.is_animatable())
|
||||
.map(|decl| (decl, CascadeLevel::Animations))
|
||||
.map(|decl| (decl, Origin::Author))
|
||||
};
|
||||
|
||||
// This currently ignores visited styles, which seems acceptable,
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
//! Applicable declarations management.
|
||||
|
||||
use crate::properties::PropertyDeclarationBlock;
|
||||
use crate::rule_tree::{CascadeLevel, ShadowCascadeOrder, StyleSource};
|
||||
use crate::rule_tree::{CascadeLevel, StyleSource};
|
||||
use crate::shared_lock::Locked;
|
||||
use servo_arc::Arc;
|
||||
use smallvec::SmallVec;
|
||||
use std::fmt::{self, Debug};
|
||||
|
||||
/// List of applicable declarations. This is a transient structure that shuttles
|
||||
/// declarations between selector matching and inserting into the rule tree, and
|
||||
|
@ -31,64 +30,29 @@ const SOURCE_ORDER_BITS: usize = 24;
|
|||
const SOURCE_ORDER_MAX: u32 = (1 << SOURCE_ORDER_BITS) - 1;
|
||||
const SOURCE_ORDER_MASK: u32 = SOURCE_ORDER_MAX << SOURCE_ORDER_SHIFT;
|
||||
|
||||
/// We store up-to-15 shadow order levels.
|
||||
///
|
||||
/// You'd need an element slotted across 16 components with ::slotted rules to
|
||||
/// trigger this as of this writing, which looks... Unlikely.
|
||||
const SHADOW_CASCADE_ORDER_SHIFT: usize = SOURCE_ORDER_BITS;
|
||||
const SHADOW_CASCADE_ORDER_BITS: usize = 4;
|
||||
const SHADOW_CASCADE_ORDER_MAX: u8 = (1 << SHADOW_CASCADE_ORDER_BITS) - 1;
|
||||
const SHADOW_CASCADE_ORDER_MASK: u32 =
|
||||
(SHADOW_CASCADE_ORDER_MAX as u32) << SHADOW_CASCADE_ORDER_SHIFT;
|
||||
|
||||
const CASCADE_LEVEL_SHIFT: usize = SOURCE_ORDER_BITS + SHADOW_CASCADE_ORDER_BITS;
|
||||
const CASCADE_LEVEL_BITS: usize = 4;
|
||||
const CASCADE_LEVEL_MAX: u8 = (1 << CASCADE_LEVEL_BITS) - 1;
|
||||
const CASCADE_LEVEL_MASK: u32 = (CASCADE_LEVEL_MAX as u32) << CASCADE_LEVEL_SHIFT;
|
||||
/// We pack the cascade level in a single byte, see CascadeLevel::to_byte_lossy
|
||||
/// for the different trade-offs there.
|
||||
const CASCADE_LEVEL_SHIFT: usize = SOURCE_ORDER_BITS;
|
||||
|
||||
/// Stores the source order of a block, the cascade level it belongs to, and the
|
||||
/// counter needed to handle Shadow DOM cascade order properly.
|
||||
#[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq)]
|
||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
|
||||
struct ApplicableDeclarationBits(u32);
|
||||
|
||||
impl ApplicableDeclarationBits {
|
||||
fn new(
|
||||
source_order: u32,
|
||||
cascade_level: CascadeLevel,
|
||||
shadow_cascade_order: ShadowCascadeOrder,
|
||||
) -> Self {
|
||||
debug_assert!(
|
||||
cascade_level as u8 <= CASCADE_LEVEL_MAX,
|
||||
"Gotta find more bits!"
|
||||
);
|
||||
let mut bits = ::std::cmp::min(source_order, SOURCE_ORDER_MAX);
|
||||
bits |= ((shadow_cascade_order & SHADOW_CASCADE_ORDER_MAX) as u32) <<
|
||||
SHADOW_CASCADE_ORDER_SHIFT;
|
||||
bits |= (cascade_level as u8 as u32) << CASCADE_LEVEL_SHIFT;
|
||||
ApplicableDeclarationBits(bits)
|
||||
fn new(source_order: u32, cascade_level: CascadeLevel) -> Self {
|
||||
Self(
|
||||
(source_order & SOURCE_ORDER_MASK) |
|
||||
((cascade_level.to_byte_lossy() as u32) << CASCADE_LEVEL_SHIFT),
|
||||
)
|
||||
}
|
||||
|
||||
fn source_order(&self) -> u32 {
|
||||
(self.0 & SOURCE_ORDER_MASK) >> SOURCE_ORDER_SHIFT
|
||||
}
|
||||
|
||||
fn shadow_cascade_order(&self) -> ShadowCascadeOrder {
|
||||
((self.0 & SHADOW_CASCADE_ORDER_MASK) >> SHADOW_CASCADE_ORDER_SHIFT) as ShadowCascadeOrder
|
||||
self.0 & SOURCE_ORDER_MASK
|
||||
}
|
||||
|
||||
fn level(&self) -> CascadeLevel {
|
||||
let byte = ((self.0 & CASCADE_LEVEL_MASK) >> CASCADE_LEVEL_SHIFT) as u8;
|
||||
unsafe { CascadeLevel::from_byte(byte) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ApplicableDeclarationBits {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("ApplicableDeclarationBits")
|
||||
.field("source_order", &self.source_order())
|
||||
.field("shadow_cascade_order", &self.shadow_cascade_order())
|
||||
.field("level", &self.level())
|
||||
.finish()
|
||||
CascadeLevel::from_byte((self.0 >> CASCADE_LEVEL_SHIFT) as u8)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,23 +83,17 @@ impl ApplicableDeclarationBlock {
|
|||
) -> Self {
|
||||
ApplicableDeclarationBlock {
|
||||
source: StyleSource::from_declarations(declarations),
|
||||
bits: ApplicableDeclarationBits::new(0, level, 0),
|
||||
bits: ApplicableDeclarationBits::new(0, level),
|
||||
specificity: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs an applicable declaration block from the given components
|
||||
#[inline]
|
||||
pub fn new(
|
||||
source: StyleSource,
|
||||
order: u32,
|
||||
level: CascadeLevel,
|
||||
specificity: u32,
|
||||
shadow_cascade_order: ShadowCascadeOrder,
|
||||
) -> Self {
|
||||
pub fn new(source: StyleSource, order: u32, level: CascadeLevel, specificity: u32) -> Self {
|
||||
ApplicableDeclarationBlock {
|
||||
source,
|
||||
bits: ApplicableDeclarationBits::new(order, level, shadow_cascade_order),
|
||||
bits: ApplicableDeclarationBits::new(order, level),
|
||||
specificity,
|
||||
}
|
||||
}
|
||||
|
@ -155,9 +113,8 @@ impl ApplicableDeclarationBlock {
|
|||
/// Convenience method to consume self and return the right thing for the
|
||||
/// rule tree to iterate over.
|
||||
#[inline]
|
||||
pub fn for_rule_tree(self) -> (StyleSource, CascadeLevel, ShadowCascadeOrder) {
|
||||
pub fn for_rule_tree(self) -> (StyleSource, CascadeLevel) {
|
||||
let level = self.level();
|
||||
let cascade_order = self.bits.shadow_cascade_order();
|
||||
(self.source, level, cascade_order)
|
||||
(self.source, level)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -306,8 +306,7 @@ fn parse_declaration_value_block<'i, 't>(
|
|||
) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> {
|
||||
let mut token_start = input.position();
|
||||
let mut token = match input.next_including_whitespace_and_comments() {
|
||||
// FIXME: remove clone() when borrows are non-lexical
|
||||
Ok(token) => token.clone(),
|
||||
Ok(token) => token,
|
||||
Err(_) => {
|
||||
return Ok((
|
||||
TokenSerializationType::nothing(),
|
||||
|
@ -335,8 +334,9 @@ fn parse_declaration_value_block<'i, 't>(
|
|||
}
|
||||
};
|
||||
}
|
||||
let last_token_type = match token {
|
||||
let last_token_type = match *token {
|
||||
Token::Comment(_) => {
|
||||
let serialization_type = token.serialization_type();
|
||||
let token_slice = input.slice_from(token_start);
|
||||
if !token_slice.ends_with("*/") {
|
||||
missing_closing_characters.push_str(if token_slice.ends_with('*') {
|
||||
|
@ -345,14 +345,14 @@ fn parse_declaration_value_block<'i, 't>(
|
|||
"*/"
|
||||
})
|
||||
}
|
||||
token.serialization_type()
|
||||
serialization_type
|
||||
},
|
||||
Token::BadUrl(u) => {
|
||||
let e = StyleParseErrorKind::BadUrlInDeclarationValueBlock(u);
|
||||
Token::BadUrl(ref u) => {
|
||||
let e = StyleParseErrorKind::BadUrlInDeclarationValueBlock(u.clone());
|
||||
return Err(input.new_custom_error(e));
|
||||
},
|
||||
Token::BadString(s) => {
|
||||
let e = StyleParseErrorKind::BadStringInDeclarationValueBlock(s);
|
||||
Token::BadString(ref s) => {
|
||||
let e = StyleParseErrorKind::BadStringInDeclarationValueBlock(s.clone());
|
||||
return Err(input.new_custom_error(e));
|
||||
},
|
||||
Token::CloseParenthesis => {
|
||||
|
@ -401,13 +401,14 @@ fn parse_declaration_value_block<'i, 't>(
|
|||
Token::CloseSquareBracket.serialization_type()
|
||||
},
|
||||
Token::QuotedString(_) => {
|
||||
let serialization_type = token.serialization_type();
|
||||
let token_slice = input.slice_from(token_start);
|
||||
let quote = &token_slice[..1];
|
||||
debug_assert!(matches!(quote, "\"" | "'"));
|
||||
if !(token_slice.ends_with(quote) && token_slice.len() > 1) {
|
||||
missing_closing_characters.push_str(quote)
|
||||
}
|
||||
token.serialization_type()
|
||||
serialization_type
|
||||
},
|
||||
Token::Ident(ref value) |
|
||||
Token::AtKeyword(ref value) |
|
||||
|
@ -417,6 +418,8 @@ fn parse_declaration_value_block<'i, 't>(
|
|||
Token::Dimension {
|
||||
unit: ref value, ..
|
||||
} => {
|
||||
let serialization_type = token.serialization_type();
|
||||
let is_unquoted_url = matches!(token, Token::UnquotedUrl(_));
|
||||
if value.ends_with("<EFBFBD>") && input.slice_from(token_start).ends_with("\\") {
|
||||
// Unescaped backslash at EOF in these contexts is interpreted as U+FFFD
|
||||
// Check the value in case the final backslash was itself escaped.
|
||||
|
@ -424,18 +427,17 @@ fn parse_declaration_value_block<'i, 't>(
|
|||
// (Unescaped U+FFFD would also work, but removing the backslash is annoying.)
|
||||
missing_closing_characters.push_str("<EFBFBD>")
|
||||
}
|
||||
if matches!(token, Token::UnquotedUrl(_)) {
|
||||
if is_unquoted_url {
|
||||
check_closed!(")");
|
||||
}
|
||||
token.serialization_type()
|
||||
serialization_type
|
||||
},
|
||||
_ => token.serialization_type(),
|
||||
};
|
||||
|
||||
token_start = input.position();
|
||||
token = match input.next_including_whitespace_and_comments() {
|
||||
// FIXME: remove clone() when borrows are non-lexical
|
||||
Ok(token) => token.clone(),
|
||||
Ok(token) => token,
|
||||
Err(..) => return Ok((first_token_type, last_token_type)),
|
||||
};
|
||||
}
|
||||
|
@ -888,15 +890,12 @@ fn substitute_block<'i>(
|
|||
let mut set_position_at_next_iteration = false;
|
||||
loop {
|
||||
let before_this_token = input.position();
|
||||
// FIXME: remove clone() when borrows are non-lexical
|
||||
let next = input
|
||||
.next_including_whitespace_and_comments()
|
||||
.map(|t| t.clone());
|
||||
let next = input.next_including_whitespace_and_comments();
|
||||
if set_position_at_next_iteration {
|
||||
*position = (
|
||||
before_this_token,
|
||||
match next {
|
||||
Ok(ref token) => token.serialization_type(),
|
||||
Ok(token) => token.serialization_type(),
|
||||
Err(_) => TokenSerializationType::nothing(),
|
||||
},
|
||||
);
|
||||
|
|
|
@ -331,7 +331,7 @@ where
|
|||
}
|
||||
|
||||
/// The ShadowRoot trait.
|
||||
pub trait TShadowRoot: Sized + Copy + Clone + PartialEq {
|
||||
pub trait TShadowRoot: Sized + Copy + Clone + Debug + PartialEq {
|
||||
/// The concrete node type.
|
||||
type ConcreteNode: TNode<ConcreteShadowRoot = Self>;
|
||||
|
||||
|
@ -523,6 +523,9 @@ pub trait TElement:
|
|||
/// Returns whether this element has a `part` attribute.
|
||||
fn has_part_attr(&self) -> bool;
|
||||
|
||||
/// Returns whether this element exports any part from its shadow tree.
|
||||
fn exports_any_part(&self) -> bool;
|
||||
|
||||
/// The ID for this element.
|
||||
fn id(&self) -> Option<&WeakAtom>;
|
||||
|
||||
|
|
|
@ -172,7 +172,11 @@ where
|
|||
};
|
||||
|
||||
for selector in self.selector_list.0.iter() {
|
||||
target_vector.push(Invalidation::new(selector, 0))
|
||||
target_vector.push(Invalidation::new(
|
||||
selector,
|
||||
self.matching_context.current_host.clone(),
|
||||
0,
|
||||
))
|
||||
}
|
||||
|
||||
false
|
||||
|
|
|
@ -237,7 +237,13 @@ impl Device {
|
|||
/// used for viewport unit resolution.
|
||||
pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> Size2D<Au> {
|
||||
self.used_viewport_size.store(true, Ordering::Relaxed);
|
||||
self.au_viewport_size()
|
||||
|
||||
let pc = match self.pres_context() {
|
||||
Some(pc) => pc,
|
||||
None => return Size2D::new(Au(0), Au(0)),
|
||||
};
|
||||
let size = &pc.mSizeForViewportUnits;
|
||||
Size2D::new(Au(size.width), Au(size.height))
|
||||
}
|
||||
|
||||
/// Returns whether we ever looked up the viewport size of the Device.
|
||||
|
@ -268,13 +274,7 @@ impl Device {
|
|||
if doc.mIsBeingUsedAsImage() {
|
||||
return true;
|
||||
}
|
||||
let document_color_use = static_prefs::pref!("browser.display.document_color_use");
|
||||
let prefs = self.pref_sheet_prefs();
|
||||
match document_color_use {
|
||||
1 => true,
|
||||
2 => prefs.mIsChrome,
|
||||
_ => !prefs.mUseAccessibilityTheme,
|
||||
}
|
||||
self.pref_sheet_prefs().mUseDocumentColors
|
||||
}
|
||||
|
||||
/// Returns the default background color.
|
||||
|
|
|
@ -140,6 +140,12 @@ impl PseudoElement {
|
|||
*self == PseudoElement::FieldsetContent
|
||||
}
|
||||
|
||||
/// Whether this pseudo-element is the ::-moz-color-swatch pseudo.
|
||||
#[inline]
|
||||
pub fn is_color_swatch(&self) -> bool {
|
||||
*self == PseudoElement::MozColorSwatch
|
||||
}
|
||||
|
||||
/// Whether this pseudo-element is lazily-cascaded.
|
||||
#[inline]
|
||||
pub fn is_lazy(&self) -> bool {
|
||||
|
|
|
@ -132,7 +132,7 @@ PRELUDE = '''
|
|||
RULE_TEMPLATE = '''
|
||||
("{atom}") => {{{{
|
||||
#[allow(unsafe_code)] #[allow(unused_unsafe)]
|
||||
unsafe {{ $crate::string_cache::Atom::from_index({index}) }}
|
||||
unsafe {{ $crate::string_cache::Atom::from_index_unchecked({index}) }}
|
||||
}}}};
|
||||
'''[1:]
|
||||
|
||||
|
|
|
@ -193,6 +193,16 @@ impl ElementSnapshot for GeckoElementSnapshot {
|
|||
snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn exported_part(&self, name: &Atom) -> Option<Atom> {
|
||||
snapshot_helpers::exported_part(&*self.mAttrs, name)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn imported_part(&self, name: &Atom) -> Option<Atom> {
|
||||
snapshot_helpers::imported_part(&*self.mAttrs, name)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||
if !self.has_any(Flags::MaybeClass) {
|
||||
|
|
|
@ -82,6 +82,32 @@ pub fn get_id(attrs: &[structs::AttrArray_InternalAttr]) -> Option<&WeakAtom> {
|
|||
Some(unsafe { get_id_from_attr(find_attr(attrs, &atom!("id"))?) })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn exported_part(
|
||||
attrs: &[structs::AttrArray_InternalAttr],
|
||||
name: &Atom,
|
||||
) -> Option<Atom> {
|
||||
let attr = find_attr(attrs, &atom!("exportparts"))?;
|
||||
let atom = unsafe { bindings::Gecko_Element_ExportedPart(attr, name.as_ptr()) };
|
||||
if atom.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(unsafe { Atom::from_raw(atom) })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn imported_part(
|
||||
attrs: &[structs::AttrArray_InternalAttr],
|
||||
name: &Atom,
|
||||
) -> Option<Atom> {
|
||||
let attr = find_attr(attrs, &atom!("exportparts"))?;
|
||||
let atom = unsafe { bindings::Gecko_Element_ImportedPart(attr, name.as_ptr()) };
|
||||
if atom.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(unsafe { Atom::from_raw(atom) })
|
||||
}
|
||||
|
||||
/// Given a class or part name, a case sensitivity, and an array of attributes,
|
||||
/// returns whether the attribute has that name.
|
||||
#[inline(always)]
|
||||
|
|
|
@ -144,6 +144,13 @@ impl<'ld> TDocument for GeckoDocument<'ld> {
|
|||
#[derive(Clone, Copy)]
|
||||
pub struct GeckoShadowRoot<'lr>(pub &'lr structs::ShadowRoot);
|
||||
|
||||
impl<'ln> fmt::Debug for GeckoShadowRoot<'ln> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// TODO(emilio): Maybe print the host or something?
|
||||
write!(f, "<shadow-root> ({:#x})", self.as_node().opaque().0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lr> PartialEq for GeckoShadowRoot<'lr> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
|
@ -232,8 +239,8 @@ impl<'ln> fmt::Debug for GeckoNode<'ln> {
|
|||
return write!(f, "<document> ({:#x})", self.opaque().0);
|
||||
}
|
||||
|
||||
if self.is_shadow_root() {
|
||||
return write!(f, "<shadow-root> ({:#x})", self.opaque().0);
|
||||
if let Some(sr) = self.as_shadow_root() {
|
||||
return sr.fmt(f);
|
||||
}
|
||||
|
||||
write!(f, "<non-text node> ({:#x})", self.opaque().0)
|
||||
|
@ -300,7 +307,10 @@ impl<'ln> GeckoNode<'ln> {
|
|||
fn flattened_tree_parent_is_parent(&self) -> bool {
|
||||
use crate::gecko_bindings::structs::*;
|
||||
let flags = self.flags();
|
||||
if flags & (NODE_MAY_BE_IN_BINDING_MNGR as u32 | NODE_IS_IN_SHADOW_TREE as u32) != 0 {
|
||||
|
||||
// FIXME(emilio): The shadow tree condition seems it shouldn't be needed
|
||||
// anymore, if we check for slots.
|
||||
if self.is_in_shadow_tree() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -758,13 +768,6 @@ impl<'le> GeckoElement<'le> {
|
|||
data.damage |= damage;
|
||||
}
|
||||
|
||||
/// This logic is duplicated in Gecko's nsIContent::IsRootOfAnonymousSubtree.
|
||||
#[inline]
|
||||
fn is_root_of_anonymous_subtree(&self) -> bool {
|
||||
use crate::gecko_bindings::structs::NODE_IS_ANONYMOUS_ROOT;
|
||||
self.flags() & (NODE_IS_ANONYMOUS_ROOT as u32) != 0
|
||||
}
|
||||
|
||||
/// This logic is duplicated in Gecko's nsIContent::IsRootOfNativeAnonymousSubtree.
|
||||
#[inline]
|
||||
fn is_root_of_native_anonymous_subtree(&self) -> bool {
|
||||
|
@ -772,11 +775,6 @@ impl<'le> GeckoElement<'le> {
|
|||
return self.flags() & (NODE_IS_NATIVE_ANONYMOUS_ROOT as u32) != 0;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_in_anonymous_subtree(&self) -> bool {
|
||||
unsafe { bindings::Gecko_IsInAnonymousSubtree(self.0) }
|
||||
}
|
||||
|
||||
/// Returns true if this node is the shadow root of an use-element shadow tree.
|
||||
#[inline]
|
||||
fn is_root_of_use_element_shadow_tree(&self) -> bool {
|
||||
|
@ -1025,7 +1023,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
// StyleChildrenIterator::IsNeeded does, except that it might return
|
||||
// true if we used to (but no longer) have anonymous content from
|
||||
// ::before/::after, or nsIAnonymousContentCreators.
|
||||
if self.is_in_anonymous_subtree() ||
|
||||
if self.is_in_native_anonymous_subtree() ||
|
||||
self.is_html_slot_element() ||
|
||||
self.shadow_root().is_some() ||
|
||||
self.may_have_anonymous_children()
|
||||
|
@ -1250,6 +1248,11 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
.get_bool_flag(nsINode_BooleanFlag::ElementHasPart)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn exports_any_part(&self) -> bool {
|
||||
snapshot_helpers::find_attr(self.attrs(), &atom!("exportparts")).is_some()
|
||||
}
|
||||
|
||||
// FIXME(emilio): we should probably just return a reference to the Atom.
|
||||
#[inline]
|
||||
fn id(&self) -> Option<&WeakAtom> {
|
||||
|
@ -2192,7 +2195,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
#[inline]
|
||||
fn is_link(&self) -> bool {
|
||||
self.state()
|
||||
.intersects(NonTSPseudoClass::AnyLink.state_flag())
|
||||
.intersects(ElementState::IN_VISITED_OR_UNVISITED_STATE)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -2219,6 +2222,16 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn exported_part(&self, name: &Atom) -> Option<Atom> {
|
||||
snapshot_helpers::exported_part(self.attrs(), name)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn imported_part(&self, name: &Atom) -> Option<Atom> {
|
||||
snapshot_helpers::imported_part(self.attrs(), name)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||
let attr = match self.get_class_attr() {
|
||||
|
@ -2241,7 +2254,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
|
||||
#[inline]
|
||||
fn ignores_nth_child_selectors(&self) -> bool {
|
||||
self.is_root_of_anonymous_subtree()
|
||||
self.is_root_of_native_anonymous_subtree()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ use std::fmt::{self, Write};
|
|||
use std::hash::{Hash, Hasher};
|
||||
use std::iter::Cloned;
|
||||
use std::mem::{self, ManuallyDrop};
|
||||
use std::num::NonZeroUsize;
|
||||
use std::ops::Deref;
|
||||
use std::{slice, str};
|
||||
use style_traits::SpecifiedValueInfo;
|
||||
|
@ -48,13 +49,18 @@ macro_rules! local_name {
|
|||
};
|
||||
}
|
||||
|
||||
/// A handle to a Gecko atom.
|
||||
/// A handle to a Gecko atom. This is a type that can represent either:
|
||||
///
|
||||
/// * A strong reference to a dynamic atom (an `nsAtom` pointer), in which case
|
||||
/// the `usize` just holds the pointer value.
|
||||
///
|
||||
/// * A byte offset from `gGkAtoms` to the `nsStaticAtom` object (shifted to
|
||||
/// the left one bit, and with the lower bit set to `1` to differentiate it
|
||||
/// from the above), so `(offset << 1 | 1)`.
|
||||
///
|
||||
/// This is either a strong reference to a dynamic atom (an nsAtom pointer),
|
||||
/// or an offset from gGkAtoms to the nsStaticAtom object.
|
||||
#[derive(Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct Atom(usize);
|
||||
pub struct Atom(NonZeroUsize);
|
||||
|
||||
/// An atom *without* a strong reference.
|
||||
///
|
||||
|
@ -86,7 +92,7 @@ fn static_atoms() -> &'static [nsStaticAtom; STATIC_ATOM_COUNT] {
|
|||
fn valid_static_atom_addr(addr: usize) -> bool {
|
||||
unsafe {
|
||||
let atoms = static_atoms();
|
||||
let start = atoms.get_unchecked(0) as *const _;
|
||||
let start = atoms.as_ptr();
|
||||
let end = atoms.get_unchecked(STATIC_ATOM_COUNT) as *const _;
|
||||
let in_range = addr >= start as usize && addr < end as usize;
|
||||
let aligned = addr % mem::align_of::<nsStaticAtom>() == 0;
|
||||
|
@ -101,9 +107,9 @@ impl Deref for Atom {
|
|||
fn deref(&self) -> &WeakAtom {
|
||||
unsafe {
|
||||
let addr = if self.is_static() {
|
||||
(&gGkAtoms as *const _ as usize) + (self.0 >> 1)
|
||||
(&gGkAtoms as *const _ as usize) + (self.0.get() >> 1)
|
||||
} else {
|
||||
self.0
|
||||
self.0.get()
|
||||
};
|
||||
debug_assert!(!self.is_static() || valid_static_atom_addr(addr));
|
||||
WeakAtom::new(addr as *const nsAtom)
|
||||
|
@ -341,29 +347,29 @@ impl fmt::Display for WeakAtom {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn make_handle(ptr: *const nsAtom) -> usize {
|
||||
unsafe fn make_handle(ptr: *const nsAtom) -> NonZeroUsize {
|
||||
debug_assert!(!ptr.is_null());
|
||||
if !WeakAtom::new(ptr).is_static() {
|
||||
ptr as usize
|
||||
NonZeroUsize::new_unchecked(ptr as usize)
|
||||
} else {
|
||||
make_static_handle(ptr as *mut nsStaticAtom)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn make_static_handle(ptr: *const nsStaticAtom) -> usize {
|
||||
unsafe fn make_static_handle(ptr: *const nsStaticAtom) -> NonZeroUsize {
|
||||
// FIXME(heycam): Use offset_from once it's stabilized.
|
||||
// https://github.com/rust-lang/rust/issues/41079
|
||||
debug_assert!(valid_static_atom_addr(ptr as usize));
|
||||
let base = &gGkAtoms as *const _;
|
||||
let offset = ptr as usize - base as usize;
|
||||
(offset << 1) | 1
|
||||
NonZeroUsize::new_unchecked((offset << 1) | 1)
|
||||
}
|
||||
|
||||
impl Atom {
|
||||
#[inline]
|
||||
fn is_static(&self) -> bool {
|
||||
self.0 & 1 == 1
|
||||
self.0.get() & 1 == 1
|
||||
}
|
||||
|
||||
/// Execute a callback with the atom represented by `ptr`.
|
||||
|
@ -378,17 +384,17 @@ impl Atom {
|
|||
}
|
||||
|
||||
/// Creates a static atom from its index in the static atom table, without
|
||||
/// checking in release builds.
|
||||
/// checking.
|
||||
#[inline]
|
||||
pub unsafe fn from_index(index: u16) -> Self {
|
||||
let ptr = static_atoms().get_unchecked(index as usize) as *const _;
|
||||
let handle = make_static_handle(ptr);
|
||||
let atom = Atom(handle);
|
||||
debug_assert!(valid_static_atom_addr(ptr as usize));
|
||||
debug_assert!(atom.is_static());
|
||||
debug_assert!((*atom).is_static());
|
||||
debug_assert!(handle == make_handle(atom.as_ptr()));
|
||||
atom
|
||||
pub const unsafe fn from_index_unchecked(index: u16) -> Self {
|
||||
// FIXME(emilio): No support for debug_assert! in const fn for now. Note
|
||||
// that violating this invariant will debug-assert in the `Deref` impl
|
||||
// though.
|
||||
//
|
||||
// debug_assert!((index as usize) < STATIC_ATOM_COUNT);
|
||||
let offset =
|
||||
(index as usize) * std::mem::size_of::<nsStaticAtom>() + kGkAtomsArrayOffset as usize;
|
||||
Atom(NonZeroUsize::new_unchecked((offset << 1) | 1))
|
||||
}
|
||||
|
||||
/// Creates an atom from an atom pointer.
|
||||
|
|
|
@ -79,7 +79,13 @@ where
|
|||
continue;
|
||||
}
|
||||
|
||||
self_invalidations.push(Invalidation::new(&dependency.selector, 0));
|
||||
// We pass `None` as a scope, as document state selectors aren't
|
||||
// affected by the current scope.
|
||||
self_invalidations.push(Invalidation::new(
|
||||
&dependency.selector,
|
||||
/* scope = */ None,
|
||||
0,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,12 @@ pub trait ElementSnapshot: Sized {
|
|||
/// called if `has_attrs()` returns true.
|
||||
fn is_part(&self, name: &Atom) -> bool;
|
||||
|
||||
/// See Element::exported_part.
|
||||
fn exported_part(&self, name: &Atom) -> Option<Atom>;
|
||||
|
||||
/// See Element::imported_part.
|
||||
fn imported_part(&self, name: &Atom) -> Option<Atom>;
|
||||
|
||||
/// A callback that should be called for each class of the snapshot. Should
|
||||
/// only be called if `has_attrs()` returns true.
|
||||
fn each_class<F>(&self, _: F)
|
||||
|
@ -270,7 +276,10 @@ where
|
|||
}
|
||||
|
||||
fn is_link(&self) -> bool {
|
||||
self.element.is_link()
|
||||
match self.snapshot().and_then(|s| s.state()) {
|
||||
Some(state) => state.intersects(ElementState::IN_VISITED_OR_UNVISITED_STATE),
|
||||
None => self.element.is_link(),
|
||||
}
|
||||
}
|
||||
|
||||
fn opaque(&self) -> OpaqueElement {
|
||||
|
@ -362,6 +371,20 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn exported_part(&self, name: &Atom) -> Option<Atom> {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs() => snapshot.exported_part(name),
|
||||
_ => self.element.exported_part(name),
|
||||
}
|
||||
}
|
||||
|
||||
fn imported_part(&self, name: &Atom) -> Option<Atom> {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs() => snapshot.imported_part(name),
|
||||
_ => self.element.imported_part(name),
|
||||
}
|
||||
}
|
||||
|
||||
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||
match self.snapshot() {
|
||||
Some(snapshot) if snapshot.has_attrs() => snapshot.has_class(name, case_sensitivity),
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::selector_parser::SelectorImpl;
|
|||
use selectors::matching::matches_compound_selector_from;
|
||||
use selectors::matching::{CompoundSelectorMatchingResult, MatchingContext};
|
||||
use selectors::parser::{Combinator, Component, Selector};
|
||||
use selectors::OpaqueElement;
|
||||
use smallvec::SmallVec;
|
||||
use std::fmt;
|
||||
|
||||
|
@ -127,6 +128,12 @@ enum InvalidationKind {
|
|||
#[derive(Clone)]
|
||||
pub struct Invalidation<'a> {
|
||||
selector: &'a Selector<SelectorImpl>,
|
||||
/// The right shadow host from where the rule came from, if any.
|
||||
///
|
||||
/// This is needed to ensure that we match the selector with the right
|
||||
/// state, as whether some selectors like :host and ::part() match depends
|
||||
/// on it.
|
||||
scope: Option<OpaqueElement>,
|
||||
/// The offset of the selector pointing to a compound selector.
|
||||
///
|
||||
/// This order is a "parse order" offset, that is, zero is the leftmost part
|
||||
|
@ -143,9 +150,14 @@ pub struct Invalidation<'a> {
|
|||
|
||||
impl<'a> Invalidation<'a> {
|
||||
/// Create a new invalidation for a given selector and offset.
|
||||
pub fn new(selector: &'a Selector<SelectorImpl>, offset: usize) -> Self {
|
||||
pub fn new(
|
||||
selector: &'a Selector<SelectorImpl>,
|
||||
scope: Option<OpaqueElement>,
|
||||
offset: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
selector,
|
||||
scope,
|
||||
offset,
|
||||
matched_by_any_previous: false,
|
||||
}
|
||||
|
@ -471,6 +483,47 @@ where
|
|||
any_descendant
|
||||
}
|
||||
|
||||
fn invalidate_parts_in_shadow_tree(
|
||||
&mut self,
|
||||
shadow: <E::ConcreteNode as TNode>::ConcreteShadowRoot,
|
||||
invalidations: &[Invalidation<'b>],
|
||||
) -> bool {
|
||||
debug_assert!(!invalidations.is_empty());
|
||||
|
||||
let mut any = false;
|
||||
let mut sibling_invalidations = InvalidationVector::new();
|
||||
|
||||
for node in shadow.as_node().dom_descendants() {
|
||||
let element = match node.as_element() {
|
||||
Some(e) => e,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
if element.has_part_attr() {
|
||||
any |= self.invalidate_child(
|
||||
element,
|
||||
invalidations,
|
||||
&mut sibling_invalidations,
|
||||
DescendantInvalidationKind::Part,
|
||||
);
|
||||
debug_assert!(
|
||||
sibling_invalidations.is_empty(),
|
||||
"::part() shouldn't have sibling combinators to the right, \
|
||||
this makes no sense! {:?}",
|
||||
sibling_invalidations
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(shadow) = element.shadow_root() {
|
||||
if element.exports_any_part() {
|
||||
any |= self.invalidate_parts_in_shadow_tree(shadow, invalidations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
any
|
||||
}
|
||||
|
||||
fn invalidate_parts(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
|
||||
if invalidations.is_empty() {
|
||||
return false;
|
||||
|
@ -481,23 +534,7 @@ where
|
|||
None => return false,
|
||||
};
|
||||
|
||||
let mut any = false;
|
||||
let mut sibling_invalidations = InvalidationVector::new();
|
||||
for element in shadow.parts() {
|
||||
any |= self.invalidate_child(
|
||||
*element,
|
||||
invalidations,
|
||||
&mut sibling_invalidations,
|
||||
DescendantInvalidationKind::Part,
|
||||
);
|
||||
debug_assert!(
|
||||
sibling_invalidations.is_empty(),
|
||||
"::part() shouldn't have sibling combinators to the right, \
|
||||
this makes no sense! {:?}",
|
||||
sibling_invalidations
|
||||
);
|
||||
}
|
||||
any
|
||||
self.invalidate_parts_in_shadow_tree(shadow, invalidations)
|
||||
}
|
||||
|
||||
fn invalidate_slotted_elements(&mut self, invalidations: &[Invalidation<'b>]) -> bool {
|
||||
|
@ -721,12 +758,17 @@ where
|
|||
self.element, invalidation, invalidation_kind
|
||||
);
|
||||
|
||||
let matching_result = matches_compound_selector_from(
|
||||
&invalidation.selector,
|
||||
invalidation.offset,
|
||||
self.processor.matching_context(),
|
||||
&self.element,
|
||||
);
|
||||
let matching_result = {
|
||||
let context = self.processor.matching_context();
|
||||
context.current_host = invalidation.scope;
|
||||
|
||||
matches_compound_selector_from(
|
||||
&invalidation.selector,
|
||||
invalidation.offset,
|
||||
context,
|
||||
&self.element,
|
||||
)
|
||||
};
|
||||
|
||||
let mut invalidated_self = false;
|
||||
let mut matched = false;
|
||||
|
@ -809,6 +851,7 @@ where
|
|||
|
||||
let next_invalidation = Invalidation {
|
||||
selector: invalidation.selector,
|
||||
scope: invalidation.scope,
|
||||
offset: next_combinator_offset + 1,
|
||||
matched_by_any_previous: false,
|
||||
};
|
||||
|
|
|
@ -158,7 +158,11 @@ where
|
|||
// If we the visited state changed, we force a restyle here. Matching
|
||||
// doesn't depend on the actual visited state at all, so we can't look
|
||||
// at matching results to decide what to do for this case.
|
||||
if state_changes.intersects(ElementState::IN_VISITED_OR_UNVISITED_STATE) {
|
||||
//
|
||||
// TODO(emilio): This piece of code should be removed when
|
||||
// layout.css.always-repaint-on-unvisited is true, since we cannot get
|
||||
// into this situation in that case.
|
||||
if state_changes.contains(ElementState::IN_VISITED_OR_UNVISITED_STATE) {
|
||||
trace!(" > visitedness change, force subtree restyle");
|
||||
// We can't just return here because there may also be attribute
|
||||
// changes as well that imply additional hints for siblings.
|
||||
|
@ -453,6 +457,7 @@ where
|
|||
|
||||
let invalidation = Invalidation::new(
|
||||
&dependency.selector,
|
||||
self.matching_context.current_host.clone(),
|
||||
dependency.selector.len() - dependency.selector_offset + 1,
|
||||
);
|
||||
|
||||
|
|
|
@ -137,12 +137,12 @@ trait PrivateMatchMethods: TElement {
|
|||
if replacements.contains(RestyleHint::RESTYLE_STYLE_ATTRIBUTE) {
|
||||
let style_attribute = self.style_attribute();
|
||||
result |= replace_rule_node(
|
||||
CascadeLevel::StyleAttributeNormal,
|
||||
CascadeLevel::same_tree_author_normal(),
|
||||
style_attribute,
|
||||
primary_rules,
|
||||
);
|
||||
result |= replace_rule_node(
|
||||
CascadeLevel::StyleAttributeImportant,
|
||||
CascadeLevel::same_tree_author_important(),
|
||||
style_attribute,
|
||||
primary_rules,
|
||||
);
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::properties::{LonghandId, LonghandIdSet, CSSWideKeyword};
|
|||
use crate::properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator};
|
||||
use crate::properties::CASCADE_PROPERTY;
|
||||
use crate::rule_cache::{RuleCache, RuleCacheConditions};
|
||||
use crate::rule_tree::{CascadeLevel, StrongRuleNode};
|
||||
use crate::rule_tree::StrongRuleNode;
|
||||
use crate::selector_parser::PseudoElement;
|
||||
use crate::stylesheets::{Origin, PerOrigin};
|
||||
use servo_arc::Arc;
|
||||
|
@ -134,11 +134,15 @@ where
|
|||
let restriction = pseudo.and_then(|p| p.property_restriction());
|
||||
let iter_declarations = || {
|
||||
rule_node.self_and_ancestors().flat_map(|node| {
|
||||
let cascade_level = node.cascade_level();
|
||||
let origin = node.cascade_level().origin();
|
||||
let node_importance = node.importance();
|
||||
let guard = match origin {
|
||||
Origin::Author => guards.author,
|
||||
Origin::User | Origin::UserAgent => guards.ua_or_user,
|
||||
};
|
||||
let declarations = match node.style_source() {
|
||||
Some(source) => source
|
||||
.read(cascade_level.guard(guards))
|
||||
.read(guard)
|
||||
.declaration_importance_iter(),
|
||||
None => DeclarationImportanceIterator::new(&[], &empty),
|
||||
};
|
||||
|
@ -153,14 +157,14 @@ where
|
|||
// longhands are only allowed if they have our
|
||||
// restriction flag set.
|
||||
if let PropertyDeclarationId::Longhand(id) = declaration.id() {
|
||||
if !id.flags().contains(restriction) && cascade_level.origin() != Origin::UserAgent {
|
||||
if !id.flags().contains(restriction) && origin != Origin::UserAgent {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if declaration_importance == node_importance {
|
||||
Some((declaration, cascade_level))
|
||||
Some((declaration, origin))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -223,7 +227,7 @@ pub fn apply_declarations<'a, E, F, I>(
|
|||
where
|
||||
E: TElement,
|
||||
F: Fn() -> I,
|
||||
I: Iterator<Item = (&'a PropertyDeclaration, CascadeLevel)>,
|
||||
I: Iterator<Item = (&'a PropertyDeclaration, Origin)>,
|
||||
{
|
||||
debug_assert!(layout_parent_style.is_none() || parent_style.is_some());
|
||||
debug_assert_eq!(
|
||||
|
@ -242,17 +246,17 @@ where
|
|||
|
||||
let inherited_style = parent_style.unwrap_or(device.default_computed_values());
|
||||
|
||||
let mut declarations = SmallVec::<[(&_, CascadeLevel); 32]>::new();
|
||||
let mut declarations = SmallVec::<[(&_, Origin); 32]>::new();
|
||||
let custom_properties = {
|
||||
let mut builder = CustomPropertiesBuilder::new(
|
||||
inherited_style.custom_properties(),
|
||||
device.environment(),
|
||||
);
|
||||
|
||||
for (declaration, cascade_level) in iter_declarations() {
|
||||
declarations.push((declaration, cascade_level));
|
||||
for (declaration, origin) in iter_declarations() {
|
||||
declarations.push((declaration, origin));
|
||||
if let PropertyDeclaration::Custom(ref declaration) = *declaration {
|
||||
builder.cascade(declaration, cascade_level.origin());
|
||||
builder.cascade(declaration, origin);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,7 +336,7 @@ where
|
|||
fn should_ignore_declaration_when_ignoring_document_colors(
|
||||
device: &Device,
|
||||
longhand_id: LonghandId,
|
||||
cascade_level: CascadeLevel,
|
||||
origin: Origin,
|
||||
pseudo: Option<&PseudoElement>,
|
||||
declaration: &mut Cow<PropertyDeclaration>,
|
||||
) -> bool {
|
||||
|
@ -340,22 +344,15 @@ fn should_ignore_declaration_when_ignoring_document_colors(
|
|||
return false;
|
||||
}
|
||||
|
||||
let is_ua_or_user_rule =
|
||||
matches!(cascade_level.origin(), Origin::User | Origin::UserAgent);
|
||||
let is_ua_or_user_rule = matches!(origin, Origin::User | Origin::UserAgent);
|
||||
if is_ua_or_user_rule {
|
||||
return false;
|
||||
}
|
||||
|
||||
let is_style_attribute = matches!(
|
||||
cascade_level,
|
||||
CascadeLevel::StyleAttributeNormal | CascadeLevel::StyleAttributeImportant
|
||||
);
|
||||
|
||||
// Don't override colors on pseudo-element's style attributes. The
|
||||
// background-color on ::-moz-color-swatch is an example. Those are set
|
||||
// as an author style (via the style attribute), but it's pretty
|
||||
// important for it to show up for obvious reasons :)
|
||||
if pseudo.is_some() && is_style_attribute {
|
||||
// Don't override background-color on ::-moz-color-swatch. It is set as an
|
||||
// author style (via the style attribute), but it's pretty important for it
|
||||
// to show up for obvious reasons :)
|
||||
if pseudo.map_or(false, |p| p.is_color_swatch()) && longhand_id == LonghandId::BackgroundColor {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -452,7 +449,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
declarations: I,
|
||||
) where
|
||||
Phase: CascadePhase,
|
||||
I: Iterator<Item = (&'decls PropertyDeclaration, CascadeLevel)>,
|
||||
I: Iterator<Item = (&'decls PropertyDeclaration, Origin)>,
|
||||
{
|
||||
let apply_reset = apply_reset == ApplyResetProperties::Yes;
|
||||
|
||||
|
@ -465,9 +462,8 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
|
||||
let ignore_colors = !self.context.builder.device.use_document_colors();
|
||||
|
||||
for (declaration, cascade_level) in declarations {
|
||||
for (declaration, origin) in declarations {
|
||||
let declaration_id = declaration.id();
|
||||
let origin = cascade_level.origin();
|
||||
let longhand_id = match declaration_id {
|
||||
PropertyDeclarationId::Longhand(id) => id,
|
||||
PropertyDeclarationId::Custom(..) => continue,
|
||||
|
@ -515,7 +511,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
|
|||
let should_ignore = should_ignore_declaration_when_ignoring_document_colors(
|
||||
self.context.builder.device,
|
||||
longhand_id,
|
||||
cascade_level,
|
||||
origin,
|
||||
self.context.builder.pseudo,
|
||||
&mut declaration,
|
||||
);
|
||||
|
|
|
@ -257,10 +257,12 @@ class Longhand(object):
|
|||
def type():
|
||||
return "longhand"
|
||||
|
||||
# For a given logical property return all the physical
|
||||
# property names corresponding to it.
|
||||
def all_physical_mapped_properties(self):
|
||||
assert self.logical
|
||||
# For a given logical property return all the physical property names
|
||||
# corresponding to it.
|
||||
def all_physical_mapped_properties(self, data):
|
||||
if not self.logical:
|
||||
return []
|
||||
|
||||
candidates = [s for s in LOGICAL_SIDES + LOGICAL_SIZES + LOGICAL_CORNERS
|
||||
if s in self.name] + [s for s in LOGICAL_AXES if self.name.endswith(s)]
|
||||
assert(len(candidates) == 1)
|
||||
|
@ -270,7 +272,7 @@ class Longhand(object):
|
|||
else PHYSICAL_SIZES if logical_side in LOGICAL_SIZES \
|
||||
else PHYSICAL_AXES if logical_side in LOGICAL_AXES \
|
||||
else LOGICAL_CORNERS
|
||||
return [to_phys(self.name, logical_side, physical_side)
|
||||
return [data.longhands_by_name[to_phys(self.name, logical_side, physical_side)]
|
||||
for physical_side in physical]
|
||||
|
||||
def experimental(self, engine):
|
||||
|
|
|
@ -46,7 +46,7 @@ use crate::rule_tree::StrongRuleNode;
|
|||
use crate::selector_parser::PseudoElement;
|
||||
use servo_arc::{Arc, RawOffsetArc, UniqueArc};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::{forget, zeroed, ManuallyDrop};
|
||||
use std::mem::{forget, MaybeUninit};
|
||||
use std::{cmp, ops, ptr};
|
||||
use crate::values::{self, CustomIdent, Either, KeyframesName, None_};
|
||||
use crate::values::computed::{Percentage, TransitionProperty};
|
||||
|
@ -218,7 +218,7 @@ impl ComputedValuesInner {
|
|||
unsafe {
|
||||
let mut arc = UniqueArc::<ComputedValues>::new_uninit();
|
||||
bindings::Gecko_ComputedStyle_Init(
|
||||
&mut (*arc.as_mut_ptr()).0,
|
||||
arc.as_mut_ptr() as *mut _,
|
||||
&self,
|
||||
pseudo_ty,
|
||||
);
|
||||
|
@ -621,14 +621,17 @@ def set_gecko_property(ffi_name, expr):
|
|||
impl ${style_struct.gecko_struct_name} {
|
||||
#[allow(dead_code, unused_variables)]
|
||||
pub fn default(document: &structs::Document) -> Arc<Self> {
|
||||
let mut result = Arc::new(${style_struct.gecko_struct_name} { gecko: ManuallyDrop::new(unsafe { zeroed() }) });
|
||||
unsafe {
|
||||
let mut result = UniqueArc::<Self>::new_uninit();
|
||||
// FIXME(bug 1595895): Zero the memory to keep valgrind happy, but
|
||||
// these looks like Valgrind false-positives at a quick glance.
|
||||
ptr::write_bytes::<Self>(result.as_mut_ptr(), 0, 1);
|
||||
Gecko_Construct_Default_${style_struct.gecko_ffi_name}(
|
||||
&mut *Arc::get_mut(&mut result).unwrap().gecko,
|
||||
result.as_mut_ptr() as *mut _,
|
||||
document,
|
||||
);
|
||||
UniqueArc::assume_init(result).shareable()
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
impl Drop for ${style_struct.gecko_struct_name} {
|
||||
|
@ -641,9 +644,12 @@ impl Drop for ${style_struct.gecko_struct_name} {
|
|||
impl Clone for ${style_struct.gecko_struct_name} {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let mut result = ${style_struct.gecko_struct_name} { gecko: ManuallyDrop::new(zeroed()) };
|
||||
Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(&mut *result.gecko, &*self.gecko);
|
||||
result
|
||||
let mut result = MaybeUninit::<Self>::uninit();
|
||||
// FIXME(bug 1595895): Zero the memory to keep valgrind happy, but
|
||||
// these looks like Valgrind false-positives at a quick glance.
|
||||
ptr::write_bytes::<Self>(result.as_mut_ptr(), 0, 1);
|
||||
Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(result.as_mut_ptr() as *mut _, &*self.gecko);
|
||||
result.assume_init()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ ${helpers.single_keyword(
|
|||
"mixed upright sideways",
|
||||
engines="gecko",
|
||||
gecko_aliases="sideways-right=sideways",
|
||||
gecko_enum_prefix="StyleTextOrientation",
|
||||
animation_value_type="none",
|
||||
spec="https://drafts.csswg.org/css-writing-modes/#propdef-text-orientation",
|
||||
)}
|
||||
|
|
|
@ -25,6 +25,7 @@ ${helpers.single_keyword(
|
|||
engines="gecko",
|
||||
animation_value_type="discrete",
|
||||
spec="https://www.w3.org/TR/SVG/text.html#TextAnchorProperty",
|
||||
gecko_enum_prefix="StyleTextAnchor",
|
||||
)}
|
||||
|
||||
// Section 11 - Painting: Filling, Stroking and Marker Symbols
|
||||
|
@ -80,6 +81,7 @@ ${helpers.single_keyword(
|
|||
engines="gecko",
|
||||
animation_value_type="discrete",
|
||||
spec="https://www.w3.org/TR/SVG11/painting.html#ShapeRenderingProperty",
|
||||
gecko_enum_prefix = "StyleShapeRendering",
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
|
@ -107,6 +109,7 @@ ${helpers.single_keyword(
|
|||
engines="gecko",
|
||||
animation_value_type="discrete",
|
||||
spec="https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty",
|
||||
gecko_enum_prefix = "StyleStrokeLinecap",
|
||||
)}
|
||||
|
||||
${helpers.single_keyword(
|
||||
|
|
|
@ -331,6 +331,7 @@ ${helpers.single_keyword(
|
|||
engines="gecko",
|
||||
animation_value_type="discrete",
|
||||
spec="https://drafts.csswg.org/css-images/#propdef-object-fit",
|
||||
gecko_enum_prefix = "StyleObjectFit",
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
|
|
|
@ -15,6 +15,7 @@ ${helpers.single_keyword(
|
|||
"ime-mode",
|
||||
"auto normal active disabled inactive",
|
||||
engines="gecko",
|
||||
gecko_enum_prefix="StyleImeMode",
|
||||
gecko_ffi_name="mIMEMode",
|
||||
animation_value_type="discrete",
|
||||
spec="https://drafts.csswg.org/css-ui/#input-method-editor",
|
||||
|
@ -58,7 +59,7 @@ ${helpers.single_keyword(
|
|||
"none default menu tooltip sheet",
|
||||
engines="gecko",
|
||||
gecko_ffi_name="mWindowShadow",
|
||||
gecko_constant_prefix="NS_STYLE_WINDOW_SHADOW",
|
||||
gecko_enum_prefix="StyleWindowShadow",
|
||||
animation_value_type="discrete",
|
||||
enabled_in="chrome",
|
||||
spec="None (Nonstandard internal property)",
|
||||
|
|
|
@ -64,16 +64,6 @@ ${helpers.single_keyword(
|
|||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)",
|
||||
)}
|
||||
|
||||
${helpers.single_keyword(
|
||||
"-moz-stack-sizing",
|
||||
"stretch-to-fit ignore ignore-horizontal ignore-vertical",
|
||||
engines="gecko",
|
||||
gecko_ffi_name="mStackSizing",
|
||||
gecko_enum_prefix="StyleStackSizing",
|
||||
animation_value_type="discrete",
|
||||
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-stack-sizing)",
|
||||
)}
|
||||
|
||||
${helpers.predefined_type(
|
||||
"-moz-box-ordinal-group",
|
||||
"Integer",
|
||||
|
|
|
@ -1651,13 +1651,25 @@ impl UnparsedValue {
|
|||
shorthands::${shorthand.ident}::parse_value(&context, input)
|
||||
.map(|longhands| {
|
||||
match longhand_id {
|
||||
<% seen = set() %>
|
||||
% for property in shorthand.sub_properties:
|
||||
LonghandId::${property.camel_case} => {
|
||||
PropertyDeclaration::${property.camel_case}(
|
||||
longhands.${property.ident}
|
||||
)
|
||||
}
|
||||
// When animating logical properties, we end up
|
||||
// physicalizing the value during the animation, but
|
||||
// the value still comes from the logical shorthand.
|
||||
//
|
||||
// So we need to handle the physical properties too.
|
||||
% for prop in [property] + property.all_physical_mapped_properties(data):
|
||||
% if prop.camel_case not in seen:
|
||||
LonghandId::${prop.camel_case} => {
|
||||
PropertyDeclaration::${prop.camel_case}(
|
||||
longhands.${property.ident}
|
||||
)
|
||||
}
|
||||
<% seen.add(prop.camel_case) %>
|
||||
% endif
|
||||
% endfor
|
||||
% endfor
|
||||
<% del seen %>
|
||||
_ => unreachable!()
|
||||
}
|
||||
})
|
||||
|
@ -1801,8 +1813,8 @@ pub enum CountedUnknownProperty {
|
|||
}
|
||||
|
||||
impl CountedUnknownProperty {
|
||||
/// Parse the counted unknown property.
|
||||
pub fn parse_for_test(property_name: &str) -> Option<Self> {
|
||||
/// Parse the counted unknown property, for testing purposes only.
|
||||
pub fn parse_for_testing(property_name: &str) -> Option<Self> {
|
||||
ascii_case_insensitive_phf_map! {
|
||||
unknown_id -> CountedUnknownProperty = {
|
||||
% for property in data.counted_unknown_properties:
|
||||
|
@ -1814,6 +1826,7 @@ impl CountedUnknownProperty {
|
|||
}
|
||||
|
||||
/// Returns the underlying index, used for use counter.
|
||||
#[inline]
|
||||
pub fn bit(self) -> usize {
|
||||
self as usize
|
||||
}
|
||||
|
@ -1830,9 +1843,16 @@ impl PropertyId {
|
|||
})
|
||||
}
|
||||
|
||||
/// Returns a given property from the string `s`.
|
||||
/// Returns a given property from the given name, _regardless of whether it
|
||||
/// is enabled or not_, or Err(()) for unknown properties.
|
||||
///
|
||||
/// Returns Err(()) for unknown properties.
|
||||
/// Do not use for non-testing purposes.
|
||||
pub fn parse_unchecked_for_testing(name: &str) -> Result<Self, ()> {
|
||||
Self::parse_unchecked(name, None)
|
||||
}
|
||||
|
||||
/// Returns a given property from the given name, _regardless of whether it
|
||||
/// is enabled or not_, or Err(()) for unknown properties.
|
||||
fn parse_unchecked(
|
||||
property_name: &str,
|
||||
use_counters: Option< &UseCounters>,
|
||||
|
@ -2176,14 +2196,12 @@ impl PropertyDeclaration {
|
|||
let mut ret = self.clone();
|
||||
|
||||
% for prop in data.longhands:
|
||||
% if prop.logical:
|
||||
% for physical_property in prop.all_physical_mapped_properties():
|
||||
% if data.longhands_by_name[physical_property].specified_type() != prop.specified_type():
|
||||
% for physical_property in prop.all_physical_mapped_properties(data):
|
||||
% if physical_property.specified_type() != prop.specified_type():
|
||||
<% raise "Logical property %s should share specified value with physical property %s" % \
|
||||
(prop.name, physical_property) %>
|
||||
(prop.name, physical_property.name) %>
|
||||
% endif
|
||||
% endfor
|
||||
% endif
|
||||
% endfor
|
||||
|
||||
unsafe {
|
||||
|
|
|
@ -464,11 +464,13 @@ ${helpers.two_properties_shorthand(
|
|||
};
|
||||
|
||||
// Make sure that the initial value matches the values for the
|
||||
// longhands, just for general sanity.
|
||||
// longhands, just for general sanity. `zoom: 1` and `zoom: 0` are
|
||||
// ignored, see [1][2]. They are just hack for the "has layout" mode on
|
||||
// IE.
|
||||
//
|
||||
// FIXME: Should we just do this for the "normal" case? Seems weird
|
||||
// either way, so maybe not?
|
||||
Ok(if zoom.get() == 1.0 {
|
||||
// [1]: https://bugs.webkit.org/show_bug.cgi?id=18467
|
||||
// [2]: https://bugzilla.mozilla.org/show_bug.cgi?id=1593009
|
||||
Ok(if zoom.get() == 1.0 || zoom.get() == 0.0 {
|
||||
expanded! {
|
||||
transform: Transform::none(),
|
||||
transform_origin: TransformOrigin::initial_value(),
|
||||
|
|
|
@ -181,8 +181,18 @@
|
|||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
// Return the shortest possible serialization of the `grid-${kind}-[start/end]` values.
|
||||
// This function exploits the opportunities to omit the end value per this spec text:
|
||||
//
|
||||
// https://drafts.csswg.org/css-grid/#propdef-grid-column
|
||||
// "When the second value is omitted, if the first value is a <custom-ident>,
|
||||
// the grid-row-end/grid-column-end longhand is also set to that <custom-ident>;
|
||||
// otherwise, it is set to auto."
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
self.grid_${kind}_start.to_css(dest)?;
|
||||
if self.grid_${kind}_start.can_omit(self.grid_${kind}_end) {
|
||||
return Ok(()); // the end value is redundant
|
||||
}
|
||||
dest.write_str(" / ")?;
|
||||
self.grid_${kind}_end.to_css(dest)
|
||||
}
|
||||
|
@ -247,14 +257,39 @@
|
|||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
// Return the shortest possible serialization of the `grid-[column/row]-[start/end]` values.
|
||||
// This function exploits the opportunities to omit trailing values per this spec text:
|
||||
//
|
||||
// https://drafts.csswg.org/css-grid/#propdef-grid-area
|
||||
// "If four <grid-line> values are specified, grid-row-start is set to the first value,
|
||||
// grid-column-start is set to the second value, grid-row-end is set to the third value,
|
||||
// and grid-column-end is set to the fourth value.
|
||||
//
|
||||
// When grid-column-end is omitted, if grid-column-start is a <custom-ident>,
|
||||
// grid-column-end is set to that <custom-ident>; otherwise, it is set to auto.
|
||||
//
|
||||
// When grid-row-end is omitted, if grid-row-start is a <custom-ident>, grid-row-end is
|
||||
// set to that <custom-ident>; otherwise, it is set to auto.
|
||||
//
|
||||
// When grid-column-start is omitted, if grid-row-start is a <custom-ident>, all four
|
||||
// longhands are set to that value. Otherwise, it is set to auto."
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
self.grid_row_start.to_css(dest)?;
|
||||
let mut trailing_values = 3;
|
||||
if self.grid_column_start.can_omit(self.grid_column_end) {
|
||||
trailing_values -= 1;
|
||||
if self.grid_row_start.can_omit(self.grid_row_end) {
|
||||
trailing_values -= 1;
|
||||
if self.grid_row_start.can_omit(self.grid_column_start) {
|
||||
trailing_values -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
let values = [&self.grid_column_start, &self.grid_row_end, &self.grid_column_end];
|
||||
for value in &values {
|
||||
for value in values.iter().take(trailing_values) {
|
||||
dest.write_str(" / ")?;
|
||||
value.to_css(dest)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -301,27 +336,37 @@
|
|||
% endfor
|
||||
|
||||
let first_line_names = input.try(parse_line_names).unwrap_or_default();
|
||||
if let Ok(mut string) = input.try(|i| i.expect_string().map(|s| s.as_ref().to_owned().into())) {
|
||||
if let Ok(string) = input.try(|i| i.expect_string().map(|s| s.as_ref().to_owned().into())) {
|
||||
let mut strings = vec![];
|
||||
let mut values = vec![];
|
||||
let mut line_names = vec![];
|
||||
let mut names = first_line_names;
|
||||
line_names.push(first_line_names);
|
||||
strings.push(string);
|
||||
loop {
|
||||
line_names.push(names);
|
||||
strings.push(string);
|
||||
let size = input.try(|i| TrackSize::parse(context, i)).unwrap_or_default();
|
||||
values.push(TrackListValue::TrackSize(size));
|
||||
names = input.try(parse_line_names).unwrap_or_default();
|
||||
if let Ok(v) = input.try(parse_line_names) {
|
||||
let mut names_vec = names.into_vec();
|
||||
names_vec.extend(v.into_iter());
|
||||
names = names_vec.into();
|
||||
}
|
||||
let mut names = input.try(parse_line_names).unwrap_or_default();
|
||||
let more_names = input.try(parse_line_names);
|
||||
|
||||
string = match input.try(|i| i.expect_string().map(|s| s.as_ref().to_owned().into())) {
|
||||
Ok(s) => s,
|
||||
_ => { // only the named area determines whether we should bail out
|
||||
line_names.push(names.into());
|
||||
match input.try(|i| i.expect_string().map(|s| s.as_ref().to_owned().into())) {
|
||||
Ok(string) => {
|
||||
strings.push(string);
|
||||
if let Ok(v) = more_names {
|
||||
// We got `[names] [more_names] "string"` - merge the two name lists.
|
||||
let mut names_vec = names.into_vec();
|
||||
names_vec.extend(v.into_iter());
|
||||
names = names_vec.into();
|
||||
}
|
||||
line_names.push(names);
|
||||
},
|
||||
Err(e) => {
|
||||
if more_names.is_ok() {
|
||||
// We've parsed `"string" [names] [more_names]` but then failed to parse another `"string"`.
|
||||
// The grammar doesn't allow two trailing `<line-names>` so this is an invalid value.
|
||||
return Err(e.into());
|
||||
}
|
||||
// only the named area determines whether we should bail out
|
||||
line_names.push(names);
|
||||
break
|
||||
},
|
||||
};
|
||||
|
@ -350,7 +395,7 @@
|
|||
|
||||
value
|
||||
} else {
|
||||
GenericGridTemplateComponent::None
|
||||
GridTemplateComponent::default()
|
||||
};
|
||||
|
||||
Ok((
|
||||
|
@ -399,6 +444,9 @@
|
|||
W: Write {
|
||||
match *template_areas {
|
||||
GridTemplateAreas::None => {
|
||||
if template_rows.is_initial() && template_columns.is_initial() {
|
||||
return GridTemplateComponent::default().to_css(dest);
|
||||
}
|
||||
template_rows.to_css(dest)?;
|
||||
dest.write_str(" / ")?;
|
||||
template_columns.to_css(dest)
|
||||
|
@ -455,8 +503,12 @@
|
|||
}
|
||||
|
||||
string.to_css(dest)?;
|
||||
dest.write_str(" ")?;
|
||||
value.to_css(dest)?;
|
||||
|
||||
// If the track size is the initial value then it's redundant here.
|
||||
if !value.is_initial() {
|
||||
dest.write_str(" ")?;
|
||||
value.to_css(dest)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(names) = names_iter.next() {
|
||||
|
@ -503,8 +555,8 @@
|
|||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Longhands, ParseError<'i>> {
|
||||
let mut temp_rows = GridTemplateComponent::None;
|
||||
let mut temp_cols = GridTemplateComponent::None;
|
||||
let mut temp_rows = GridTemplateComponent::default();
|
||||
let mut temp_cols = GridTemplateComponent::default();
|
||||
let mut temp_areas = GridTemplateAreas::None;
|
||||
let mut auto_rows = ImplicitGridTracks::default();
|
||||
let mut auto_cols = ImplicitGridTracks::default();
|
||||
|
@ -577,8 +629,8 @@
|
|||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
if *self.grid_template_areas != GridTemplateAreas::None ||
|
||||
(*self.grid_template_rows != GridTemplateComponent::None &&
|
||||
*self.grid_template_columns != GridTemplateComponent::None) ||
|
||||
(!self.grid_template_rows.is_initial() &&
|
||||
!self.grid_template_columns.is_initial()) ||
|
||||
self.is_grid_template() {
|
||||
return super::grid_template::serialize_grid_template(self.grid_template_rows,
|
||||
self.grid_template_columns,
|
||||
|
@ -588,7 +640,7 @@
|
|||
if self.grid_auto_flow.autoflow == AutoFlow::Column {
|
||||
// It should fail to serialize if other branch of the if condition's values are set.
|
||||
if !self.grid_auto_rows.is_initial() ||
|
||||
*self.grid_template_columns != GridTemplateComponent::None {
|
||||
!self.grid_template_columns.is_initial() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -612,7 +664,7 @@
|
|||
} else {
|
||||
// It should fail to serialize if other branch of the if condition's values are set.
|
||||
if !self.grid_auto_columns.is_initial() ||
|
||||
*self.grid_template_rows != GridTemplateComponent::None {
|
||||
!self.grid_template_rows.is_initial() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
|
|
@ -26,9 +26,7 @@ pub struct RuleCacheConditions {
|
|||
impl RuleCacheConditions {
|
||||
/// Sets the style as depending in the font-size value.
|
||||
pub fn set_font_size_dependency(&mut self, font_size: NonNegativeLength) {
|
||||
if let Some(f) = &self.font_size {
|
||||
debug_assert_eq!(*f, font_size);
|
||||
}
|
||||
debug_assert!(self.font_size.map_or(true, |f| f == font_size));
|
||||
self.font_size = Some(font_size);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ use crate::selector_parser::PseudoElement;
|
|||
use crate::shared_lock::Locked;
|
||||
use crate::stylesheets::Origin;
|
||||
use crate::stylist::{AuthorStylesEnabled, Rule, RuleInclusion, Stylist};
|
||||
use crate::Atom;
|
||||
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
|
||||
use servo_arc::ArcBorrow;
|
||||
use smallvec::SmallVec;
|
||||
|
@ -79,7 +80,6 @@ where
|
|||
rules: &'a mut ApplicableDeclarationList,
|
||||
context: &'a mut MatchingContext<'b, E::Impl>,
|
||||
flags_setter: &'a mut F,
|
||||
shadow_cascade_order: ShadowCascadeOrder,
|
||||
matches_user_and_author_rules: bool,
|
||||
matches_document_author_rules: bool,
|
||||
}
|
||||
|
@ -132,7 +132,6 @@ where
|
|||
context,
|
||||
flags_setter,
|
||||
rules,
|
||||
shadow_cascade_order: 0,
|
||||
matches_user_and_author_rules,
|
||||
matches_document_author_rules: matches_user_and_author_rules,
|
||||
}
|
||||
|
@ -142,7 +141,7 @@ where
|
|||
let cascade_level = match origin {
|
||||
Origin::UserAgent => CascadeLevel::UANormal,
|
||||
Origin::User => CascadeLevel::UserNormal,
|
||||
Origin::Author => CascadeLevel::SameTreeAuthorNormal,
|
||||
Origin::Author => CascadeLevel::same_tree_author_normal(),
|
||||
};
|
||||
|
||||
let cascade_data = self.stylist.cascade_data().borrow_for_origin(origin);
|
||||
|
@ -198,7 +197,6 @@ where
|
|||
) {
|
||||
debug_assert!(shadow_host.shadow_root().is_some());
|
||||
self.collect_rules_internal(Some(shadow_host), map, cascade_level);
|
||||
self.shadow_cascade_order += 1;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -212,7 +210,6 @@ where
|
|||
let rule_hash_target = self.rule_hash_target;
|
||||
let rules = &mut self.rules;
|
||||
let flags_setter = &mut self.flags_setter;
|
||||
let shadow_cascade_order = self.shadow_cascade_order;
|
||||
let start = rules.len();
|
||||
self.context.with_shadow_host(shadow_host, |context| {
|
||||
map.get_all_matching_rules(
|
||||
|
@ -222,16 +219,18 @@ where
|
|||
context,
|
||||
flags_setter,
|
||||
cascade_level,
|
||||
shadow_cascade_order,
|
||||
);
|
||||
});
|
||||
sort_rules_from(rules, start);
|
||||
}
|
||||
|
||||
/// Collects the rules for the ::slotted pseudo-element.
|
||||
fn collect_slotted_rules(&mut self) {
|
||||
/// Collects the rules for the ::slotted pseudo-element and the :host
|
||||
/// pseudo-class.
|
||||
fn collect_host_and_slotted_rules(&mut self) {
|
||||
let mut slots = SmallVec::<[_; 3]>::new();
|
||||
let mut current = self.rule_hash_target.assigned_slot();
|
||||
let mut shadow_cascade_order = ShadowCascadeOrder::for_outermost_shadow_tree();
|
||||
|
||||
while let Some(slot) = current {
|
||||
debug_assert!(
|
||||
self.matches_user_and_author_rules,
|
||||
|
@ -239,11 +238,16 @@ where
|
|||
);
|
||||
slots.push(slot);
|
||||
current = slot.assigned_slot();
|
||||
shadow_cascade_order.dec();
|
||||
}
|
||||
|
||||
self.collect_host_rules(shadow_cascade_order);
|
||||
|
||||
// Match slotted rules in reverse order, so that the outer slotted rules
|
||||
// come before the inner rules (and thus have less priority).
|
||||
for slot in slots.iter().rev() {
|
||||
shadow_cascade_order.inc();
|
||||
|
||||
let shadow = slot.containing_shadow().unwrap();
|
||||
let data = match shadow.style_data() {
|
||||
Some(d) => d,
|
||||
|
@ -253,10 +257,13 @@ where
|
|||
Some(r) => r,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
self.collect_rules_in_shadow_tree(
|
||||
shadow.host(),
|
||||
slotted_rules,
|
||||
CascadeLevel::InnerShadowNormal,
|
||||
CascadeLevel::AuthorNormal {
|
||||
shadow_cascade_order,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -277,12 +284,12 @@ where
|
|||
let cascade_data = containing_shadow.style_data();
|
||||
let host = containing_shadow.host();
|
||||
if let Some(map) = cascade_data.and_then(|data| data.normal_rules(self.pseudo_element)) {
|
||||
self.collect_rules_in_shadow_tree(host, map, CascadeLevel::SameTreeAuthorNormal);
|
||||
self.collect_rules_in_shadow_tree(host, map, CascadeLevel::same_tree_author_normal());
|
||||
}
|
||||
}
|
||||
|
||||
/// Collects the rules for the :host pseudo-class.
|
||||
fn collect_host_rules(&mut self) {
|
||||
fn collect_host_rules(&mut self, shadow_cascade_order: ShadowCascadeOrder) {
|
||||
let shadow = match self.rule_hash_target.shadow_root() {
|
||||
Some(s) => s,
|
||||
None => return,
|
||||
|
@ -307,7 +314,9 @@ where
|
|||
self.collect_rules_in_shadow_tree(
|
||||
rule_hash_target,
|
||||
host_rules,
|
||||
CascadeLevel::InnerShadowNormal,
|
||||
CascadeLevel::AuthorNormal {
|
||||
shadow_cascade_order,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -324,68 +333,89 @@ where
|
|||
return;
|
||||
}
|
||||
|
||||
let shadow = match self.rule_hash_target.containing_shadow() {
|
||||
let mut inner_shadow = match self.rule_hash_target.containing_shadow() {
|
||||
Some(s) => s,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let host = shadow.host();
|
||||
let containing_shadow = host.containing_shadow();
|
||||
let part_rules = match containing_shadow {
|
||||
Some(shadow) => shadow
|
||||
.style_data()
|
||||
.and_then(|data| data.part_rules(self.pseudo_element)),
|
||||
None => self
|
||||
.stylist
|
||||
.cascade_data()
|
||||
.borrow_for_origin(Origin::Author)
|
||||
.part_rules(self.pseudo_element),
|
||||
};
|
||||
let mut shadow_cascade_order = ShadowCascadeOrder::for_innermost_containing_tree();
|
||||
|
||||
// TODO(emilio): SameTreeAuthorNormal is a bit of a lie here, we may
|
||||
// need an OuterTreeAuthorNormal cascade level or such, and change the
|
||||
// cascade order, if we allow to forward parts to even outer trees.
|
||||
//
|
||||
// Though the current thing kinda works because we apply them after
|
||||
// the outer tree, so as long as we don't allow forwarding we're
|
||||
// good.
|
||||
if let Some(part_rules) = part_rules {
|
||||
let containing_host = containing_shadow.map(|s| s.host());
|
||||
let element = self.element;
|
||||
let rule_hash_target = self.rule_hash_target;
|
||||
let rules = &mut self.rules;
|
||||
let flags_setter = &mut self.flags_setter;
|
||||
let shadow_cascade_order = self.shadow_cascade_order;
|
||||
let cascade_level = CascadeLevel::SameTreeAuthorNormal;
|
||||
let start = rules.len();
|
||||
self.context.with_shadow_host(containing_host, |context| {
|
||||
rule_hash_target.each_part(|p| {
|
||||
if let Some(part_rules) = part_rules.get(p) {
|
||||
SelectorMap::get_matching_rules(
|
||||
element,
|
||||
&part_rules,
|
||||
rules,
|
||||
context,
|
||||
flags_setter,
|
||||
cascade_level,
|
||||
shadow_cascade_order,
|
||||
);
|
||||
let mut parts = SmallVec::<[Atom; 3]>::new();
|
||||
self.rule_hash_target.each_part(|p| parts.push(p.clone()));
|
||||
|
||||
loop {
|
||||
if parts.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let outer_shadow = inner_shadow.host().containing_shadow();
|
||||
let part_rules = match outer_shadow {
|
||||
Some(shadow) => shadow
|
||||
.style_data()
|
||||
.and_then(|data| data.part_rules(self.pseudo_element)),
|
||||
None => self
|
||||
.stylist
|
||||
.cascade_data()
|
||||
.borrow_for_origin(Origin::Author)
|
||||
.part_rules(self.pseudo_element),
|
||||
};
|
||||
|
||||
if let Some(part_rules) = part_rules {
|
||||
let containing_host = outer_shadow.map(|s| s.host());
|
||||
let element = self.element;
|
||||
let rules = &mut self.rules;
|
||||
let flags_setter = &mut self.flags_setter;
|
||||
let cascade_level = CascadeLevel::AuthorNormal {
|
||||
shadow_cascade_order,
|
||||
};
|
||||
let start = rules.len();
|
||||
self.context.with_shadow_host(containing_host, |context| {
|
||||
for p in &parts {
|
||||
if let Some(part_rules) = part_rules.get(p) {
|
||||
SelectorMap::get_matching_rules(
|
||||
element,
|
||||
&part_rules,
|
||||
rules,
|
||||
context,
|
||||
flags_setter,
|
||||
cascade_level,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
sort_rules_from(rules, start);
|
||||
shadow_cascade_order.inc();
|
||||
}
|
||||
|
||||
let inner_shadow_host = inner_shadow.host();
|
||||
|
||||
inner_shadow = match outer_shadow {
|
||||
Some(s) => s,
|
||||
None => break, // Nowhere to export to.
|
||||
};
|
||||
|
||||
parts.retain(|part| {
|
||||
let exported_part = match inner_shadow_host.exported_part(part) {
|
||||
Some(part) => part,
|
||||
None => return false,
|
||||
};
|
||||
std::mem::replace(part, exported_part);
|
||||
true
|
||||
});
|
||||
sort_rules_from(rules, start);
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_style_attribute_and_animation_rules(&mut self) {
|
||||
fn collect_style_attribute(&mut self) {
|
||||
if let Some(sa) = self.style_attribute {
|
||||
self.rules
|
||||
.push(ApplicableDeclarationBlock::from_declarations(
|
||||
sa.clone_arc(),
|
||||
CascadeLevel::StyleAttributeNormal,
|
||||
CascadeLevel::same_tree_author_normal(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_animation_rules(&mut self) {
|
||||
if let Some(so) = self.smil_override {
|
||||
self.rules
|
||||
.push(ApplicableDeclarationBlock::from_declarations(
|
||||
|
@ -431,11 +461,11 @@ where
|
|||
if self.stylist.author_styles_enabled() == AuthorStylesEnabled::No {
|
||||
return;
|
||||
}
|
||||
self.collect_host_rules();
|
||||
self.collect_slotted_rules();
|
||||
self.collect_host_and_slotted_rules();
|
||||
self.collect_normal_rules_from_containing_shadow_tree();
|
||||
self.collect_document_author_rules();
|
||||
self.collect_style_attribute();
|
||||
self.collect_part_rules();
|
||||
self.collect_style_attribute_and_animation_rules();
|
||||
self.collect_animation_rules();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,15 +169,60 @@ const FREE_LIST_SENTINEL: *mut RuleNode = 0x01 as *mut RuleNode;
|
|||
/// another thread is currently adding an entry). We spin if we find this value.
|
||||
const FREE_LIST_LOCKED: *mut RuleNode = 0x02 as *mut RuleNode;
|
||||
|
||||
/// A counter to track how many inner shadow roots rules deep we are.
|
||||
///
|
||||
/// This is used to handle:
|
||||
/// A counter to track how many shadow root rules deep we are. This is used to
|
||||
/// handle:
|
||||
///
|
||||
/// https://drafts.csswg.org/css-scoping/#shadow-cascading
|
||||
///
|
||||
/// In particular, it'd be `0` for the innermost shadow host, `1` for the next,
|
||||
/// and so on.
|
||||
pub type ShadowCascadeOrder = u8;
|
||||
/// See the static functions for the meaning of different values.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)]
|
||||
pub struct ShadowCascadeOrder(i8);
|
||||
|
||||
impl ShadowCascadeOrder {
|
||||
/// A level for the outermost shadow tree (the shadow tree we own, and the
|
||||
/// ones from the slots we're slotted in).
|
||||
#[inline]
|
||||
pub fn for_outermost_shadow_tree() -> Self {
|
||||
Self(-1)
|
||||
}
|
||||
|
||||
/// A level for the element's tree.
|
||||
#[inline]
|
||||
fn for_same_tree() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
|
||||
/// A level for the innermost containing tree (the one closest to the
|
||||
/// element).
|
||||
#[inline]
|
||||
pub fn for_innermost_containing_tree() -> Self {
|
||||
Self(1)
|
||||
}
|
||||
|
||||
/// Decrement the level, moving inwards. We should only move inwards if
|
||||
/// we're traversing slots.
|
||||
#[inline]
|
||||
pub fn dec(&mut self) {
|
||||
debug_assert!(self.0 < 0);
|
||||
self.0 = self.0.saturating_sub(1);
|
||||
}
|
||||
|
||||
/// The level, moving inwards. We should only move inwards if we're
|
||||
/// traversing slots.
|
||||
#[inline]
|
||||
pub fn inc(&mut self) {
|
||||
debug_assert_ne!(self.0, -1);
|
||||
self.0 = self.0.saturating_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Neg for ShadowCascadeOrder {
|
||||
type Output = Self;
|
||||
#[inline]
|
||||
fn neg(self) -> Self {
|
||||
Self(self.0.neg())
|
||||
}
|
||||
}
|
||||
|
||||
impl RuleTree {
|
||||
/// Construct a new rule tree.
|
||||
|
@ -215,26 +260,20 @@ impl RuleTree {
|
|||
guards: &StylesheetGuards,
|
||||
) -> StrongRuleNode
|
||||
where
|
||||
I: Iterator<Item = (StyleSource, CascadeLevel, ShadowCascadeOrder)>,
|
||||
I: Iterator<Item = (StyleSource, CascadeLevel)>,
|
||||
{
|
||||
use self::CascadeLevel::*;
|
||||
let mut current = self.root.clone();
|
||||
let mut last_level = current.get().level;
|
||||
|
||||
let mut found_important = false;
|
||||
let mut important_style_attr = None;
|
||||
|
||||
let mut important_same_tree = SmallVec::<[StyleSource; 4]>::new();
|
||||
let mut important_inner_shadow = SmallVec::<[SmallVec<[StyleSource; 4]>; 4]>::new();
|
||||
important_inner_shadow.push(SmallVec::new());
|
||||
let mut important_author = SmallVec::<[(StyleSource, ShadowCascadeOrder); 4]>::new();
|
||||
|
||||
let mut important_user = SmallVec::<[StyleSource; 4]>::new();
|
||||
let mut important_ua = SmallVec::<[StyleSource; 4]>::new();
|
||||
let mut transition = None;
|
||||
|
||||
let mut last_cascade_order = 0;
|
||||
for (source, level, shadow_cascade_order) in iter {
|
||||
debug_assert!(level >= last_level, "Not really ordered");
|
||||
for (source, level) in iter {
|
||||
debug_assert!(!level.is_important(), "Important levels handled internally");
|
||||
let any_important = {
|
||||
let pdb = source.read(level.guard(guards));
|
||||
|
@ -244,29 +283,13 @@ impl RuleTree {
|
|||
if any_important {
|
||||
found_important = true;
|
||||
match level {
|
||||
InnerShadowNormal => {
|
||||
debug_assert!(
|
||||
shadow_cascade_order >= last_cascade_order,
|
||||
"Not really ordered"
|
||||
);
|
||||
if shadow_cascade_order > last_cascade_order &&
|
||||
!important_inner_shadow.last().unwrap().is_empty()
|
||||
{
|
||||
last_cascade_order = shadow_cascade_order;
|
||||
important_inner_shadow.push(SmallVec::new());
|
||||
}
|
||||
important_inner_shadow
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.push(source.clone())
|
||||
AuthorNormal {
|
||||
shadow_cascade_order,
|
||||
} => {
|
||||
important_author.push((source.clone(), shadow_cascade_order));
|
||||
},
|
||||
SameTreeAuthorNormal => important_same_tree.push(source.clone()),
|
||||
UANormal => important_ua.push(source.clone()),
|
||||
UserNormal => important_user.push(source.clone()),
|
||||
StyleAttributeNormal => {
|
||||
debug_assert!(important_style_attr.is_none());
|
||||
important_style_attr = Some(source.clone());
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
|
@ -290,7 +313,6 @@ impl RuleTree {
|
|||
} else {
|
||||
current = current.ensure_child(self.root.downgrade(), source, level);
|
||||
}
|
||||
last_level = level;
|
||||
}
|
||||
|
||||
// Early-return in the common case of no !important declarations.
|
||||
|
@ -298,23 +320,39 @@ impl RuleTree {
|
|||
return current;
|
||||
}
|
||||
|
||||
//
|
||||
// Insert important declarations, in order of increasing importance,
|
||||
// followed by any transition rule.
|
||||
//
|
||||
|
||||
for source in important_same_tree.drain() {
|
||||
current = current.ensure_child(self.root.downgrade(), source, SameTreeAuthorImportant);
|
||||
// Inner shadow wins over same-tree, which wins over outer-shadow.
|
||||
//
|
||||
// We negate the shadow cascade order to preserve the right PartialOrd
|
||||
// behavior.
|
||||
if !important_author.is_empty() &&
|
||||
important_author.first().unwrap().1 != important_author.last().unwrap().1
|
||||
{
|
||||
// We only need to sort if the important rules come from
|
||||
// different trees, but we need this sort to be stable.
|
||||
//
|
||||
// FIXME(emilio): This could maybe be smarter, probably by chunking
|
||||
// the important rules while inserting, and iterating the outer
|
||||
// chunks in reverse order.
|
||||
//
|
||||
// That is, if we have rules with levels like: -1 -1 -1 0 0 0 1 1 1,
|
||||
// we're really only sorting the chunks, while keeping elements
|
||||
// inside the same chunk already sorted. Seems like we could try to
|
||||
// keep a SmallVec-of-SmallVecs with the chunks and just iterate the
|
||||
// outer in reverse.
|
||||
important_author.sort_by_key(|&(_, order)| -order);
|
||||
}
|
||||
|
||||
if let Some(source) = important_style_attr {
|
||||
current = current.ensure_child(self.root.downgrade(), source, StyleAttributeImportant);
|
||||
}
|
||||
|
||||
for mut list in important_inner_shadow.drain().rev() {
|
||||
for source in list.drain() {
|
||||
current = current.ensure_child(self.root.downgrade(), source, InnerShadowImportant);
|
||||
}
|
||||
for (source, shadow_cascade_order) in important_author.drain() {
|
||||
current = current.ensure_child(
|
||||
self.root.downgrade(),
|
||||
source,
|
||||
AuthorImportant {
|
||||
shadow_cascade_order: -shadow_cascade_order,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
for source in important_user.drain() {
|
||||
|
@ -359,11 +397,8 @@ impl RuleTree {
|
|||
I: Iterator<Item = (StyleSource, CascadeLevel)>,
|
||||
{
|
||||
let mut current = from;
|
||||
let mut last_level = current.get().level;
|
||||
for (source, level) in iter {
|
||||
debug_assert!(last_level <= level, "Not really ordered");
|
||||
current = current.ensure_child(self.root.downgrade(), source, level);
|
||||
last_level = level;
|
||||
}
|
||||
current
|
||||
}
|
||||
|
@ -439,7 +474,6 @@ impl RuleTree {
|
|||
guards: &StylesheetGuards,
|
||||
important_rules_changed: &mut bool,
|
||||
) -> Option<StrongRuleNode> {
|
||||
debug_assert!(level.is_unique_per_element());
|
||||
// TODO(emilio): Being smarter with lifetimes we could avoid a bit of
|
||||
// the refcount churn.
|
||||
let mut current = path.clone();
|
||||
|
@ -468,7 +502,11 @@ impl RuleTree {
|
|||
if current.get().level == level {
|
||||
*important_rules_changed |= level.is_important();
|
||||
|
||||
if let Some(pdb) = pdb {
|
||||
let current_decls = current.get().source.as_ref().unwrap().as_declarations();
|
||||
|
||||
// If the only rule at the level we're replacing is exactly the
|
||||
// same as `pdb`, we're done, and `path` is still valid.
|
||||
if let (Some(ref pdb), Some(ref current_decls)) = (pdb, current_decls) {
|
||||
// If the only rule at the level we're replacing is exactly the
|
||||
// same as `pdb`, we're done, and `path` is still valid.
|
||||
//
|
||||
|
@ -478,25 +516,17 @@ impl RuleTree {
|
|||
// also equally valid. This is less likely, and would require an
|
||||
// in-place mutation of the source, which is, at best, fiddly,
|
||||
// so let's skip it for now.
|
||||
let current_decls = current
|
||||
.get()
|
||||
.source
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.as_declarations()
|
||||
.expect("Replacing non-declarations style?");
|
||||
let is_here_already = ArcBorrow::ptr_eq(&pdb, ¤t_decls);
|
||||
let is_here_already = ArcBorrow::ptr_eq(pdb, current_decls);
|
||||
if is_here_already {
|
||||
debug!("Picking the fast path in rule replacement");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
current = current.parent().unwrap().clone();
|
||||
|
||||
if current_decls.is_some() {
|
||||
current = current.parent().unwrap().clone();
|
||||
}
|
||||
}
|
||||
debug_assert!(
|
||||
current.get().level != level,
|
||||
"Multiple rules should've been replaced?"
|
||||
);
|
||||
|
||||
// Insert the rule if it's relevant at this level in the cascade.
|
||||
//
|
||||
|
@ -611,61 +641,127 @@ const RULE_TREE_GC_INTERVAL: usize = 300;
|
|||
/// tree may affect an element connected to the document or an "outer" shadow
|
||||
/// tree.
|
||||
///
|
||||
/// We need to differentiate between rules from the same tree and "inner" shadow
|
||||
/// trees in order to be able to find the right position for the style attribute
|
||||
/// easily. Otherwise we wouldn't be able to avoid selector-matching when a
|
||||
/// style attribute is added or removed.
|
||||
///
|
||||
/// [1]: https://drafts.csswg.org/css-cascade/#cascade-origin
|
||||
/// [2]: https://drafts.csswg.org/css-cascade/#preshint
|
||||
/// [3]: https://html.spec.whatwg.org/multipage/#presentational-hints
|
||||
/// [4]: https://drafts.csswg.org/css-scoping/#shadow-cascading
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)]
|
||||
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd)]
|
||||
pub enum CascadeLevel {
|
||||
/// Normal User-Agent rules.
|
||||
UANormal = 0,
|
||||
UANormal,
|
||||
/// User normal rules.
|
||||
UserNormal,
|
||||
/// Presentational hints.
|
||||
PresHints,
|
||||
/// Shadow DOM styles from "inner" shadow trees.
|
||||
///
|
||||
/// See above for why this is needed instead of merging InnerShadowNormal,
|
||||
/// SameTreeAuthorNormal and StyleAttributeNormal inside something like
|
||||
/// AuthorNormal.
|
||||
InnerShadowNormal,
|
||||
/// Author normal rules from the same tree the element is in.
|
||||
SameTreeAuthorNormal,
|
||||
/// Style attribute normal rules.
|
||||
StyleAttributeNormal,
|
||||
/// Shadow DOM styles from author styles.
|
||||
AuthorNormal {
|
||||
/// The order in the shadow tree hierarchy. This number is relative to
|
||||
/// the tree of the element, and thus the only invariants that need to
|
||||
/// be preserved is:
|
||||
///
|
||||
/// * Zero is the same tree as the element that matched the rule. This
|
||||
/// is important so that we can optimize style attribute insertions.
|
||||
///
|
||||
/// * The levels are ordered in accordance with
|
||||
/// https://drafts.csswg.org/css-scoping/#shadow-cascading
|
||||
shadow_cascade_order: ShadowCascadeOrder,
|
||||
},
|
||||
/// SVG SMIL animations.
|
||||
SMILOverride,
|
||||
/// CSS animations and script-generated animations.
|
||||
Animations,
|
||||
/// Author-supplied important rules from the same tree the element came
|
||||
/// from.
|
||||
SameTreeAuthorImportant,
|
||||
/// Style attribute important rules.
|
||||
StyleAttributeImportant,
|
||||
/// Shadow DOM important rules.
|
||||
InnerShadowImportant,
|
||||
/// Author-supplied important rules.
|
||||
AuthorImportant {
|
||||
/// The order in the shadow tree hierarchy, inverted, so that PartialOrd
|
||||
/// does the right thing.
|
||||
shadow_cascade_order: ShadowCascadeOrder,
|
||||
},
|
||||
/// User important rules.
|
||||
UserImportant,
|
||||
/// User-agent important rules.
|
||||
UAImportant,
|
||||
/// Transitions
|
||||
///
|
||||
/// NB: If this changes from being last, change from_byte below.
|
||||
Transitions,
|
||||
}
|
||||
|
||||
impl CascadeLevel {
|
||||
/// Converts a raw byte to a CascadeLevel.
|
||||
pub unsafe fn from_byte(byte: u8) -> Self {
|
||||
debug_assert!(byte <= CascadeLevel::Transitions as u8);
|
||||
mem::transmute(byte)
|
||||
/// Pack this cascade level in a single byte.
|
||||
///
|
||||
/// We have 10 levels, which we can represent with 4 bits, and then a
|
||||
/// cascade order optionally, which we can clamp to three bits max, and
|
||||
/// represent with a fourth bit for the sign.
|
||||
///
|
||||
/// So this creates: SOOODDDD
|
||||
///
|
||||
/// Where `S` is the sign of the order (one if negative, 0 otherwise), `O`
|
||||
/// is the absolute value of the order, and `D`s are the discriminant.
|
||||
#[inline]
|
||||
pub fn to_byte_lossy(&self) -> u8 {
|
||||
let (discriminant, order) = match *self {
|
||||
Self::UANormal => (0, 0),
|
||||
Self::UserNormal => (1, 0),
|
||||
Self::PresHints => (2, 0),
|
||||
Self::AuthorNormal {
|
||||
shadow_cascade_order,
|
||||
} => (3, shadow_cascade_order.0),
|
||||
Self::SMILOverride => (4, 0),
|
||||
Self::Animations => (5, 0),
|
||||
Self::AuthorImportant {
|
||||
shadow_cascade_order,
|
||||
} => (6, shadow_cascade_order.0),
|
||||
Self::UserImportant => (7, 0),
|
||||
Self::UAImportant => (8, 0),
|
||||
Self::Transitions => (9, 0),
|
||||
};
|
||||
|
||||
debug_assert_eq!(discriminant & 0xf, discriminant);
|
||||
if order == 0 {
|
||||
return discriminant;
|
||||
}
|
||||
|
||||
let negative = order < 0;
|
||||
let value = std::cmp::min(order.abs() as u8, 0b111);
|
||||
(negative as u8) << 7 | value << 4 | discriminant
|
||||
}
|
||||
|
||||
/// Convert back from the single-byte representation of the cascade level
|
||||
/// explained above.
|
||||
#[inline]
|
||||
pub fn from_byte(b: u8) -> Self {
|
||||
let order = {
|
||||
let abs = ((b & 0b01110000) >> 4) as i8;
|
||||
let negative = b & 0b10000000 != 0;
|
||||
if negative {
|
||||
-abs
|
||||
} else {
|
||||
abs
|
||||
}
|
||||
};
|
||||
let discriminant = b & 0xf;
|
||||
let level = match discriminant {
|
||||
0 => Self::UANormal,
|
||||
1 => Self::UserNormal,
|
||||
2 => Self::PresHints,
|
||||
3 => {
|
||||
return Self::AuthorNormal {
|
||||
shadow_cascade_order: ShadowCascadeOrder(order),
|
||||
}
|
||||
},
|
||||
4 => Self::SMILOverride,
|
||||
5 => Self::Animations,
|
||||
6 => {
|
||||
return Self::AuthorImportant {
|
||||
shadow_cascade_order: ShadowCascadeOrder(order),
|
||||
}
|
||||
},
|
||||
7 => Self::UserImportant,
|
||||
8 => Self::UAImportant,
|
||||
9 => Self::Transitions,
|
||||
_ => unreachable!("Didn't expect {} as a discriminant", discriminant),
|
||||
};
|
||||
debug_assert_eq!(order, 0, "Didn't expect an order value for {:?}", level);
|
||||
level
|
||||
}
|
||||
|
||||
/// Select a lock guard for this level
|
||||
|
@ -679,16 +775,21 @@ impl CascadeLevel {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns whether this cascade level is unique per element, in which case
|
||||
/// we can replace the path in the cascade without fear.
|
||||
pub fn is_unique_per_element(&self) -> bool {
|
||||
match *self {
|
||||
CascadeLevel::Transitions |
|
||||
CascadeLevel::Animations |
|
||||
CascadeLevel::SMILOverride |
|
||||
CascadeLevel::StyleAttributeNormal |
|
||||
CascadeLevel::StyleAttributeImportant => true,
|
||||
_ => false,
|
||||
/// Returns the cascade level for author important declarations from the
|
||||
/// same tree as the element.
|
||||
#[inline]
|
||||
pub fn same_tree_author_important() -> Self {
|
||||
CascadeLevel::AuthorImportant {
|
||||
shadow_cascade_order: ShadowCascadeOrder::for_same_tree(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the cascade level for author normal declarations from the same
|
||||
/// tree as the element.
|
||||
#[inline]
|
||||
pub fn same_tree_author_normal() -> Self {
|
||||
CascadeLevel::AuthorNormal {
|
||||
shadow_cascade_order: ShadowCascadeOrder::for_same_tree(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -697,9 +798,7 @@ impl CascadeLevel {
|
|||
#[inline]
|
||||
pub fn is_important(&self) -> bool {
|
||||
match *self {
|
||||
CascadeLevel::SameTreeAuthorImportant |
|
||||
CascadeLevel::InnerShadowImportant |
|
||||
CascadeLevel::StyleAttributeImportant |
|
||||
CascadeLevel::AuthorImportant { .. } |
|
||||
CascadeLevel::UserImportant |
|
||||
CascadeLevel::UAImportant => true,
|
||||
_ => false,
|
||||
|
@ -724,14 +823,10 @@ impl CascadeLevel {
|
|||
CascadeLevel::UAImportant | CascadeLevel::UANormal => Origin::UserAgent,
|
||||
CascadeLevel::UserImportant | CascadeLevel::UserNormal => Origin::User,
|
||||
CascadeLevel::PresHints |
|
||||
CascadeLevel::InnerShadowNormal |
|
||||
CascadeLevel::SameTreeAuthorNormal |
|
||||
CascadeLevel::StyleAttributeNormal |
|
||||
CascadeLevel::AuthorNormal { .. } |
|
||||
CascadeLevel::AuthorImportant { .. } |
|
||||
CascadeLevel::SMILOverride |
|
||||
CascadeLevel::Animations |
|
||||
CascadeLevel::SameTreeAuthorImportant |
|
||||
CascadeLevel::StyleAttributeImportant |
|
||||
CascadeLevel::InnerShadowImportant |
|
||||
CascadeLevel::Transitions => Origin::Author,
|
||||
}
|
||||
}
|
||||
|
@ -1146,6 +1241,15 @@ impl StrongRuleNode {
|
|||
) -> StrongRuleNode {
|
||||
use parking_lot::RwLockUpgradableReadGuard;
|
||||
|
||||
debug_assert!(
|
||||
self.get().level <= level,
|
||||
"Should be ordered (instead {:?} > {:?}), from {:?} and {:?}",
|
||||
self.get().level,
|
||||
level,
|
||||
self.get().source,
|
||||
source,
|
||||
);
|
||||
|
||||
let key = ChildKey(level, source.key());
|
||||
|
||||
let read_guard = self.get().children.upgradable_read();
|
||||
|
@ -1448,55 +1552,36 @@ impl StrongRuleNode {
|
|||
}
|
||||
});
|
||||
|
||||
match node.cascade_level() {
|
||||
// Non-author rules:
|
||||
CascadeLevel::UANormal |
|
||||
CascadeLevel::UAImportant |
|
||||
CascadeLevel::UserNormal |
|
||||
CascadeLevel::UserImportant => {
|
||||
for (id, declaration) in longhands {
|
||||
if properties.contains(id) {
|
||||
// This property was set by a non-author rule.
|
||||
// Stop looking for it in this element's rule
|
||||
// nodes.
|
||||
properties.remove(id);
|
||||
let is_author = node.cascade_level().origin() == Origin::Author;
|
||||
for (id, declaration) in longhands {
|
||||
if !properties.contains(id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// However, if it is inherited, then it might be
|
||||
// inherited from an author rule from an
|
||||
// ancestor element's rule nodes.
|
||||
if declaration.get_css_wide_keyword() ==
|
||||
Some(CSSWideKeyword::Inherit)
|
||||
{
|
||||
have_explicit_ua_inherit = true;
|
||||
inherited_properties.insert(id);
|
||||
}
|
||||
if is_author {
|
||||
if !author_colors_allowed {
|
||||
// FIXME(emilio): this looks wrong, this should
|
||||
// do: if color is not transparent, then return
|
||||
// true, or something.
|
||||
if let PropertyDeclaration::BackgroundColor(ref color) = *declaration {
|
||||
return *color == Color::transparent();
|
||||
}
|
||||
}
|
||||
},
|
||||
// Author rules:
|
||||
CascadeLevel::PresHints |
|
||||
CascadeLevel::SameTreeAuthorNormal |
|
||||
CascadeLevel::InnerShadowNormal |
|
||||
CascadeLevel::StyleAttributeNormal |
|
||||
CascadeLevel::SMILOverride |
|
||||
CascadeLevel::Animations |
|
||||
CascadeLevel::SameTreeAuthorImportant |
|
||||
CascadeLevel::InnerShadowImportant |
|
||||
CascadeLevel::StyleAttributeImportant |
|
||||
CascadeLevel::Transitions => {
|
||||
for (id, declaration) in longhands {
|
||||
if properties.contains(id) {
|
||||
if !author_colors_allowed {
|
||||
if let PropertyDeclaration::BackgroundColor(ref color) =
|
||||
*declaration
|
||||
{
|
||||
return *color == Color::transparent();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
return true;
|
||||
}
|
||||
|
||||
// This property was set by a non-author rule.
|
||||
// Stop looking for it in this element's rule
|
||||
// nodes.
|
||||
properties.remove(id);
|
||||
|
||||
// However, if it is inherited, then it might be
|
||||
// inherited from an author rule from an
|
||||
// ancestor element's rule nodes.
|
||||
if declaration.get_css_wide_keyword() == Some(CSSWideKeyword::Inherit) {
|
||||
have_explicit_ua_inherit = true;
|
||||
inherited_properties.insert(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::context::QuirksMode;
|
|||
use crate::dom::TElement;
|
||||
use crate::hash::map as hash_map;
|
||||
use crate::hash::{HashMap, HashSet};
|
||||
use crate::rule_tree::{CascadeLevel, ShadowCascadeOrder};
|
||||
use crate::rule_tree::CascadeLevel;
|
||||
use crate::selector_parser::SelectorImpl;
|
||||
use crate::stylist::Rule;
|
||||
use crate::{Atom, LocalName, Namespace, WeakAtom};
|
||||
|
@ -171,7 +171,6 @@ impl SelectorMap<Rule> {
|
|||
context: &mut MatchingContext<E::Impl>,
|
||||
flags_setter: &mut F,
|
||||
cascade_level: CascadeLevel,
|
||||
shadow_cascade_order: ShadowCascadeOrder,
|
||||
) where
|
||||
E: TElement,
|
||||
F: FnMut(&E, ElementSelectorFlags),
|
||||
|
@ -190,7 +189,6 @@ impl SelectorMap<Rule> {
|
|||
context,
|
||||
flags_setter,
|
||||
cascade_level,
|
||||
shadow_cascade_order,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -203,7 +201,6 @@ impl SelectorMap<Rule> {
|
|||
context,
|
||||
flags_setter,
|
||||
cascade_level,
|
||||
shadow_cascade_order,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -217,7 +214,6 @@ impl SelectorMap<Rule> {
|
|||
context,
|
||||
flags_setter,
|
||||
cascade_level,
|
||||
shadow_cascade_order,
|
||||
)
|
||||
}
|
||||
});
|
||||
|
@ -230,7 +226,6 @@ impl SelectorMap<Rule> {
|
|||
context,
|
||||
flags_setter,
|
||||
cascade_level,
|
||||
shadow_cascade_order,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -242,7 +237,6 @@ impl SelectorMap<Rule> {
|
|||
context,
|
||||
flags_setter,
|
||||
cascade_level,
|
||||
shadow_cascade_order,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -253,7 +247,6 @@ impl SelectorMap<Rule> {
|
|||
context,
|
||||
flags_setter,
|
||||
cascade_level,
|
||||
shadow_cascade_order,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -265,7 +258,6 @@ impl SelectorMap<Rule> {
|
|||
context: &mut MatchingContext<E::Impl>,
|
||||
flags_setter: &mut F,
|
||||
cascade_level: CascadeLevel,
|
||||
shadow_cascade_order: ShadowCascadeOrder,
|
||||
) where
|
||||
E: TElement,
|
||||
F: FnMut(&E, ElementSelectorFlags),
|
||||
|
@ -279,9 +271,7 @@ impl SelectorMap<Rule> {
|
|||
context,
|
||||
flags_setter,
|
||||
) {
|
||||
matching_rules.push(
|
||||
rule.to_applicable_declaration_block(cascade_level, shadow_cascade_order),
|
||||
);
|
||||
matching_rules.push(rule.to_applicable_declaration_block(cascade_level));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -173,6 +173,12 @@ impl PseudoElement {
|
|||
false
|
||||
}
|
||||
|
||||
/// Whether this pseudo-element is the ::-moz-color-swatch pseudo.
|
||||
#[inline]
|
||||
pub fn is_color_swatch(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Whether this pseudo-element is eagerly-cascaded.
|
||||
#[inline]
|
||||
pub fn is_eager(&self) -> bool {
|
||||
|
@ -694,6 +700,14 @@ impl ElementSnapshot for ServoElementSnapshot {
|
|||
false
|
||||
}
|
||||
|
||||
fn exported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
||||
fn imported_part(&self, _: &Atom) -> Option<Atom> {
|
||||
None
|
||||
}
|
||||
|
||||
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
|
||||
self.get_attr(&ns!(), &local_name!("class"))
|
||||
.map_or(false, |v| {
|
||||
|
|
|
@ -63,43 +63,38 @@ where
|
|||
{
|
||||
use crate::Atom;
|
||||
|
||||
// FIXME(emilio): This should be an actual static.
|
||||
lazy_static! {
|
||||
static ref SPECIAL_HTML_ELEMENTS: [Atom; 16] = [
|
||||
atom!("br"),
|
||||
atom!("wbr"),
|
||||
atom!("meter"),
|
||||
atom!("progress"),
|
||||
atom!("canvas"),
|
||||
atom!("embed"),
|
||||
atom!("object"),
|
||||
atom!("audio"),
|
||||
atom!("iframe"),
|
||||
atom!("img"),
|
||||
atom!("video"),
|
||||
atom!("frame"),
|
||||
atom!("frameset"),
|
||||
atom!("input"),
|
||||
atom!("textarea"),
|
||||
atom!("select"),
|
||||
];
|
||||
}
|
||||
const SPECIAL_HTML_ELEMENTS: [Atom; 16] = [
|
||||
atom!("br"),
|
||||
atom!("wbr"),
|
||||
atom!("meter"),
|
||||
atom!("progress"),
|
||||
atom!("canvas"),
|
||||
atom!("embed"),
|
||||
atom!("object"),
|
||||
atom!("audio"),
|
||||
atom!("iframe"),
|
||||
atom!("img"),
|
||||
atom!("video"),
|
||||
atom!("frame"),
|
||||
atom!("frameset"),
|
||||
atom!("input"),
|
||||
atom!("textarea"),
|
||||
atom!("select"),
|
||||
];
|
||||
|
||||
// https://drafts.csswg.org/css-display/#unbox-svg
|
||||
//
|
||||
// There's a note about "Unknown elements", but there's not a good way to
|
||||
// know what that means, or to get that information from here, and no other
|
||||
// UA implements this either.
|
||||
lazy_static! {
|
||||
static ref SPECIAL_SVG_ELEMENTS: [Atom; 6] = [
|
||||
atom!("svg"),
|
||||
atom!("a"),
|
||||
atom!("g"),
|
||||
atom!("use"),
|
||||
atom!("tspan"),
|
||||
atom!("textPath"),
|
||||
];
|
||||
}
|
||||
const SPECIAL_SVG_ELEMENTS: [Atom; 6] = [
|
||||
atom!("svg"),
|
||||
atom!("a"),
|
||||
atom!("g"),
|
||||
atom!("use"),
|
||||
atom!("tspan"),
|
||||
atom!("textPath"),
|
||||
];
|
||||
|
||||
// https://drafts.csswg.org/css-display/#unbox-html
|
||||
if element.is_html_element() {
|
||||
|
|
|
@ -124,6 +124,7 @@ impl StylesheetContents {
|
|||
url_data: UrlExtraData,
|
||||
quirks_mode: QuirksMode,
|
||||
) -> Self {
|
||||
debug_assert!(rules.is_static());
|
||||
Self {
|
||||
rules,
|
||||
origin,
|
||||
|
@ -144,6 +145,9 @@ impl StylesheetContents {
|
|||
/// Measure heap usage.
|
||||
#[cfg(feature = "gecko")]
|
||||
pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
|
||||
if self.rules.is_static() {
|
||||
return 0;
|
||||
}
|
||||
// Measurement of other fields may be added later.
|
||||
self.rules.unconditional_shallow_size_of(ops) +
|
||||
self.rules.read_with(guard).size_of(guard, ops)
|
||||
|
|
|
@ -178,8 +178,7 @@ impl SupportsCondition {
|
|||
while input.try(Parser::expect_whitespace).is_ok() {}
|
||||
let pos = input.position();
|
||||
let location = input.current_source_location();
|
||||
// FIXME: remove clone() when lifetimes are non-lexical
|
||||
match input.next()?.clone() {
|
||||
match *input.next()? {
|
||||
Token::ParenthesisBlock => {
|
||||
let nested =
|
||||
input.try(|input| input.parse_nested_block(parse_condition_or_declaration));
|
||||
|
@ -187,7 +186,8 @@ impl SupportsCondition {
|
|||
return nested;
|
||||
}
|
||||
},
|
||||
Token::Function(ident) => {
|
||||
Token::Function(ref ident) => {
|
||||
let ident = ident.clone();
|
||||
let nested = input.try(|input| {
|
||||
input.parse_nested_block(|input| {
|
||||
SupportsCondition::parse_functional(&ident, input)
|
||||
|
@ -197,7 +197,7 @@ impl SupportsCondition {
|
|||
return nested;
|
||||
}
|
||||
},
|
||||
t => return Err(location.new_unexpected_token_error(t)),
|
||||
ref t => return Err(location.new_unexpected_token_error(t.clone())),
|
||||
}
|
||||
input.parse_nested_block(consume_any_value)?;
|
||||
Ok(SupportsCondition::FutureSyntax(
|
||||
|
|
|
@ -18,7 +18,7 @@ use crate::properties::{self, CascadeMode, ComputedValues};
|
|||
use crate::properties::{AnimationRules, PropertyDeclarationBlock};
|
||||
use crate::rule_cache::{RuleCache, RuleCacheConditions};
|
||||
use crate::rule_collector::{containing_shadow_ignoring_svg_use, RuleCollector};
|
||||
use crate::rule_tree::{CascadeLevel, RuleTree, ShadowCascadeOrder, StrongRuleNode, StyleSource};
|
||||
use crate::rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
|
||||
use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, SelectorMap, SelectorMapEntry};
|
||||
use crate::selector_parser::{PerPseudoElementMap, PseudoElement, SelectorImpl, SnapshotMap};
|
||||
use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
|
||||
|
@ -1322,16 +1322,17 @@ impl Stylist {
|
|||
let iter_declarations = || {
|
||||
block
|
||||
.declaration_importance_iter()
|
||||
.map(|(declaration, importance)| {
|
||||
debug_assert!(!importance.important());
|
||||
(declaration, CascadeLevel::StyleAttributeNormal)
|
||||
})
|
||||
.map(|(declaration, _)| (declaration, Origin::Author))
|
||||
};
|
||||
|
||||
let metrics = get_metrics_provider_for_product();
|
||||
|
||||
// We don't bother inserting these declarations in the rule tree, since
|
||||
// it'd be quite useless and slow.
|
||||
//
|
||||
// TODO(emilio): Now that we fixed bug 1493420, we should consider
|
||||
// reversing this as it shouldn't be slow anymore, and should avoid
|
||||
// generating two instantiations of apply_declarations.
|
||||
properties::apply_declarations::<E, _, _>(
|
||||
&self.device,
|
||||
/* pseudo = */ None,
|
||||
|
@ -1987,7 +1988,6 @@ impl CascadeData {
|
|||
self.rules_source_order,
|
||||
CascadeLevel::UANormal,
|
||||
selector.specificity(),
|
||||
0,
|
||||
));
|
||||
continue;
|
||||
}
|
||||
|
@ -2323,16 +2323,9 @@ impl Rule {
|
|||
pub fn to_applicable_declaration_block(
|
||||
&self,
|
||||
level: CascadeLevel,
|
||||
shadow_cascade_order: ShadowCascadeOrder,
|
||||
) -> ApplicableDeclarationBlock {
|
||||
let source = StyleSource::from_rule(self.style_rule.clone());
|
||||
ApplicableDeclarationBlock::new(
|
||||
source,
|
||||
self.source_order,
|
||||
level,
|
||||
self.specificity(),
|
||||
shadow_cascade_order,
|
||||
)
|
||||
ApplicableDeclarationBlock::new(source, self.source_order, level, self.specificity())
|
||||
}
|
||||
|
||||
/// Creates a new Rule.
|
||||
|
|
|
@ -1249,7 +1249,11 @@ impl Animate for ComputedRotate {
|
|||
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.
|
||||
// We always normalize direction vector for rotate3d() first, so we should also
|
||||
// apply the same rule for rotate property. In other words, we promote none into
|
||||
// a 3d rotate, and normalize both direction vector first, and then do
|
||||
// interpolation.
|
||||
let (fx, fy, fz, fa) = transform::get_normalized_vector_and_angle(fx, fy, fz, fa);
|
||||
Ok(Rotate::Rotate3D(
|
||||
fx,
|
||||
fy,
|
||||
|
@ -1258,7 +1262,8 @@ impl Animate for ComputedRotate {
|
|||
))
|
||||
},
|
||||
(&Rotate::None, &Rotate::Rotate3D(tx, ty, tz, ta)) => {
|
||||
// No need to normalize `none`, so animate angle directly.
|
||||
// Normalize direction vector first.
|
||||
let (tx, ty, tz, ta) = transform::get_normalized_vector_and_angle(tx, ty, tz, ta);
|
||||
Ok(Rotate::Rotate3D(
|
||||
tx,
|
||||
ty,
|
||||
|
@ -1368,8 +1373,7 @@ impl ComputedTranslate {
|
|||
LengthPercentage::zero(),
|
||||
Length::zero(),
|
||||
),
|
||||
Translate::Translate3D(tx, ty, tz) => (tx, ty, tz),
|
||||
Translate::Translate(tx, ty) => (tx, ty, Length::zero()),
|
||||
Translate::Translate(tx, ty, tz) => (tx, ty, tz),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1379,19 +1383,12 @@ impl Animate for ComputedTranslate {
|
|||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
match (self, other) {
|
||||
(&Translate::None, &Translate::None) => Ok(Translate::None),
|
||||
(&Translate::Translate3D(_, ..), _) | (_, &Translate::Translate3D(_, ..)) => {
|
||||
let (from, to) = (self.resolve(), other.resolve());
|
||||
Ok(Translate::Translate3D(
|
||||
from.0.animate(&to.0, procedure)?,
|
||||
from.1.animate(&to.1, procedure)?,
|
||||
from.2.animate(&to.2, procedure)?,
|
||||
))
|
||||
},
|
||||
(&Translate::Translate(_, ..), _) | (_, &Translate::Translate(_, ..)) => {
|
||||
let (from, to) = (self.resolve(), other.resolve());
|
||||
Ok(Translate::Translate(
|
||||
from.0.animate(&to.0, procedure)?,
|
||||
from.1.animate(&to.1, procedure)?,
|
||||
from.2.animate(&to.2, procedure)?,
|
||||
))
|
||||
},
|
||||
}
|
||||
|
@ -1417,8 +1414,7 @@ impl ComputedScale {
|
|||
// Unspecified scales default to 1
|
||||
match *self {
|
||||
Scale::None => (1.0, 1.0, 1.0),
|
||||
Scale::Scale3D(sx, sy, sz) => (sx, sy, sz),
|
||||
Scale::Scale(sx, sy) => (sx, sy, 1.),
|
||||
Scale::Scale(sx, sy, sz) => (sx, sy, sz),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1428,7 +1424,7 @@ impl Animate for ComputedScale {
|
|||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
match (self, other) {
|
||||
(&Scale::None, &Scale::None) => Ok(Scale::None),
|
||||
(&Scale::Scale3D(_, ..), _) | (_, &Scale::Scale3D(_, ..)) => {
|
||||
(&Scale::Scale(_, ..), _) | (_, &Scale::Scale(_, ..)) => {
|
||||
let (from, to) = (self.resolve(), other.resolve());
|
||||
// For transform lists, we add by appending to the list of
|
||||
// transform functions. However, ComputedScale cannot be
|
||||
|
@ -1436,24 +1432,12 @@ impl Animate for ComputedScale {
|
|||
// result here.
|
||||
if procedure == Procedure::Add {
|
||||
// scale(x1,y1,z1)*scale(x2,y2,z2) = scale(x1*x2, y1*y2, z1*z2)
|
||||
return Ok(Scale::Scale3D(from.0 * to.0, from.1 * to.1, from.2 * to.2));
|
||||
}
|
||||
Ok(Scale::Scale3D(
|
||||
animate_multiplicative_factor(from.0, to.0, procedure)?,
|
||||
animate_multiplicative_factor(from.1, to.1, procedure)?,
|
||||
animate_multiplicative_factor(from.2, to.2, procedure)?,
|
||||
))
|
||||
},
|
||||
(&Scale::Scale(_, ..), _) | (_, &Scale::Scale(_, ..)) => {
|
||||
let (from, to) = (self.resolve(), other.resolve());
|
||||
// As with Scale3D, addition needs special handling.
|
||||
if procedure == Procedure::Add {
|
||||
// scale(x1,y1)*scale(x2,y2) = scale(x1*x2, y1*y2)
|
||||
return Ok(Scale::Scale(from.0 * to.0, from.1 * to.1));
|
||||
return Ok(Scale::Scale(from.0 * to.0, from.1 * to.1, from.2 * to.2));
|
||||
}
|
||||
Ok(Scale::Scale(
|
||||
animate_multiplicative_factor(from.0, to.0, procedure)?,
|
||||
animate_multiplicative_factor(from.1, to.1, procedure)?,
|
||||
animate_multiplicative_factor(from.2, to.2, procedure)?,
|
||||
))
|
||||
},
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ pub use self::GenericGridLine as GridLine;
|
|||
|
||||
impl<Integer> GridLine<Integer>
|
||||
where
|
||||
Integer: Zero,
|
||||
Integer: PartialEq + Zero,
|
||||
{
|
||||
/// The `auto` value.
|
||||
pub fn auto() -> Self {
|
||||
|
@ -73,11 +73,27 @@ where
|
|||
pub fn is_auto(&self) -> bool {
|
||||
self.ident == atom!("") && self.line_num.is_zero() && !self.is_span
|
||||
}
|
||||
|
||||
/// Check whether this `<grid-line>` represents a `<custom-ident>` value.
|
||||
pub fn is_ident_only(&self) -> bool {
|
||||
self.ident != atom!("") && self.line_num.is_zero() && !self.is_span
|
||||
}
|
||||
|
||||
/// Check if `self` makes `other` omittable according to the rules at:
|
||||
/// https://drafts.csswg.org/css-grid/#propdef-grid-column
|
||||
/// https://drafts.csswg.org/css-grid/#propdef-grid-area
|
||||
pub fn can_omit(&self, other: &Self) -> bool {
|
||||
if self.is_ident_only() {
|
||||
self == other
|
||||
} else {
|
||||
other.is_auto()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Integer> ToCss for GridLine<Integer>
|
||||
where
|
||||
Integer: ToCss + Zero,
|
||||
Integer: ToCss + PartialEq + Zero,
|
||||
{
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
|
@ -261,6 +277,19 @@ pub enum GenericTrackSize<L> {
|
|||
pub use self::GenericTrackSize as TrackSize;
|
||||
|
||||
impl<L> TrackSize<L> {
|
||||
/// The initial value.
|
||||
const INITIAL_VALUE: Self = TrackSize::Breadth(TrackBreadth::Auto);
|
||||
|
||||
/// Returns the initial value.
|
||||
pub const fn initial_value() -> Self {
|
||||
Self::INITIAL_VALUE
|
||||
}
|
||||
|
||||
/// Returns true if `self` is the initial value.
|
||||
pub fn is_initial(&self) -> bool {
|
||||
matches!(*self, TrackSize::Breadth(TrackBreadth::Auto)) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585
|
||||
}
|
||||
|
||||
/// Check whether this is a `<fixed-size>`
|
||||
///
|
||||
/// <https://drafts.csswg.org/css-grid/#typedef-fixed-size>
|
||||
|
@ -286,17 +315,9 @@ impl<L> TrackSize<L> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<L: PartialEq> TrackSize<L> {
|
||||
/// Return true if it is `auto`.
|
||||
#[inline]
|
||||
pub fn is_auto(&self) -> bool {
|
||||
*self == TrackSize::Breadth(TrackBreadth::Auto)
|
||||
}
|
||||
}
|
||||
|
||||
impl<L> Default for TrackSize<L> {
|
||||
fn default() -> Self {
|
||||
TrackSize::Breadth(TrackBreadth::Auto)
|
||||
Self::initial_value()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -513,9 +534,27 @@ pub enum GenericTrackListValue<LengthPercentage, Integer> {
|
|||
pub use self::GenericTrackListValue as TrackListValue;
|
||||
|
||||
impl<L, I> TrackListValue<L, I> {
|
||||
// FIXME: can't use TrackSize::initial_value() here b/c rustc error "is not yet stable as a const fn"
|
||||
const INITIAL_VALUE: Self = TrackListValue::TrackSize(TrackSize::Breadth(TrackBreadth::Auto));
|
||||
|
||||
fn is_repeat(&self) -> bool {
|
||||
matches!(*self, TrackListValue::TrackRepeat(..))
|
||||
}
|
||||
|
||||
/// Returns true if `self` is the initial value.
|
||||
pub fn is_initial(&self) -> bool {
|
||||
matches!(
|
||||
*self,
|
||||
TrackListValue::TrackSize(TrackSize::Breadth(TrackBreadth::Auto))
|
||||
) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, I> Default for TrackListValue<L, I> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::INITIAL_VALUE
|
||||
}
|
||||
}
|
||||
|
||||
/// A grid `<track-list>` type.
|
||||
|
@ -755,6 +794,9 @@ pub enum GenericGridTemplateComponent<L, I> {
|
|||
pub use self::GenericGridTemplateComponent as GridTemplateComponent;
|
||||
|
||||
impl<L, I> GridTemplateComponent<L, I> {
|
||||
/// The initial value.
|
||||
const INITIAL_VALUE: Self = Self::None;
|
||||
|
||||
/// Returns length of the <track-list>s <track-size>
|
||||
pub fn track_list_len(&self) -> usize {
|
||||
match *self {
|
||||
|
@ -762,4 +804,16 @@ impl<L, I> GridTemplateComponent<L, I> {
|
|||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `self` is the initial value.
|
||||
pub fn is_initial(&self) -> bool {
|
||||
matches!(*self, Self::None) // FIXME: can't use Self::INITIAL_VALUE here yet: https://github.com/rust-lang/rust/issues/66585
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, I> Default for GridTemplateComponent<L, I> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::INITIAL_VALUE
|
||||
}
|
||||
}
|
||||
|
|
|
@ -700,40 +700,52 @@ where
|
|||
pub enum GenericScale<Number> {
|
||||
/// 'none'
|
||||
None,
|
||||
/// '<number>{1,2}'
|
||||
Scale(Number, Number),
|
||||
/// '<number>{3}'
|
||||
Scale3D(Number, Number, Number),
|
||||
/// '<number>{1,3}'
|
||||
Scale(Number, Number, Number),
|
||||
}
|
||||
|
||||
pub use self::GenericScale as Scale;
|
||||
|
||||
impl<Number: ToCss + PartialEq> ToCss for Scale<Number> {
|
||||
impl<Number> ToCss for Scale<Number>
|
||||
where
|
||||
Number: ToCss + PartialEq + Copy,
|
||||
f32: From<Number>,
|
||||
{
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
W: fmt::Write,
|
||||
f32: From<Number>,
|
||||
{
|
||||
match *self {
|
||||
Scale::None => dest.write_str("none"),
|
||||
Scale::Scale(ref x, ref y) => {
|
||||
Scale::Scale(ref x, ref y, ref z) => {
|
||||
x.to_css(dest)?;
|
||||
if x != y {
|
||||
|
||||
let is_3d = f32::from(*z) != 1.0;
|
||||
if is_3d || x != y {
|
||||
dest.write_char(' ')?;
|
||||
y.to_css(dest)?;
|
||||
}
|
||||
|
||||
if is_3d {
|
||||
dest.write_char(' ')?;
|
||||
z.to_css(dest)?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
Scale::Scale3D(ref x, ref y, ref z) => {
|
||||
x.to_css(dest)?;
|
||||
dest.write_char(' ')?;
|
||||
y.to_css(dest)?;
|
||||
dest.write_char(' ')?;
|
||||
z.to_css(dest)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn y_axis_and_z_axis_are_zero<LengthPercentage: Zero, Length: Zero>(
|
||||
_: &LengthPercentage,
|
||||
y: &LengthPercentage,
|
||||
z: &Length,
|
||||
) -> bool {
|
||||
y.is_zero() && z.is_zero()
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
|
@ -755,25 +767,24 @@ impl<Number: ToCss + PartialEq> ToCss for Scale<Number> {
|
|||
/// or two values (per usual, if the second value is 0px, the default, it must
|
||||
/// be omitted when serializing).
|
||||
///
|
||||
/// If a 3d translation is specified, all three values must be serialized.
|
||||
///
|
||||
/// We don't omit the 3rd component even if it is 0px for now, and the
|
||||
/// related spec issue is https://github.com/w3c/csswg-drafts/issues/3305
|
||||
/// If a 3d translation is specified and the value can be expressed as 2d, we treat as 2d and
|
||||
/// serialize accoringly. Otherwise, we serialize all three values.
|
||||
/// https://github.com/w3c/csswg-drafts/issues/3305
|
||||
///
|
||||
/// <https://drafts.csswg.org/css-transforms-2/#individual-transforms>
|
||||
pub enum GenericTranslate<LengthPercentage, Length>
|
||||
where
|
||||
LengthPercentage: Zero,
|
||||
Length: Zero,
|
||||
{
|
||||
/// 'none'
|
||||
None,
|
||||
/// '<length-percentage>' or '<length-percentage> <length-percentage>'
|
||||
/// <length-percentage> [ <length-percentage> <length>? ]?
|
||||
Translate(
|
||||
LengthPercentage,
|
||||
#[css(skip_if = "Zero::is_zero")] LengthPercentage,
|
||||
#[css(contextual_skip_if = "y_axis_and_z_axis_are_zero")] LengthPercentage,
|
||||
#[css(skip_if = "Zero::is_zero")] Length,
|
||||
),
|
||||
/// '<length-percentage> <length-percentage> <length>'
|
||||
Translate3D(LengthPercentage, LengthPercentage, Length),
|
||||
}
|
||||
|
||||
pub use self::GenericTranslate as Translate;
|
||||
|
|
|
@ -208,24 +208,34 @@ impl Angle {
|
|||
input: &mut Parser<'i, 't>,
|
||||
allow_unitless_zero: AllowUnitlessZeroAngle,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
// FIXME: remove clone() when lifetimes are non-lexical
|
||||
let token = input.next()?.clone();
|
||||
match token {
|
||||
let t = input.next()?;
|
||||
match *t {
|
||||
Token::Dimension {
|
||||
value, ref unit, ..
|
||||
} => {
|
||||
Angle::parse_dimension(value, unit, /* from_calc = */ false)
|
||||
match Angle::parse_dimension(value, unit, /* from_calc = */ false) {
|
||||
Ok(angle) => Ok(angle),
|
||||
Err(()) => {
|
||||
let t = t.clone();
|
||||
Err(input.new_unexpected_token_error(t))
|
||||
},
|
||||
}
|
||||
},
|
||||
Token::Number { value, .. } if value == 0. => match allow_unitless_zero {
|
||||
AllowUnitlessZeroAngle::Yes => Ok(Angle::zero()),
|
||||
AllowUnitlessZeroAngle::No => Err(()),
|
||||
AllowUnitlessZeroAngle::No => {
|
||||
let t = t.clone();
|
||||
Err(input.new_unexpected_token_error(t))
|
||||
},
|
||||
},
|
||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
||||
return input.parse_nested_block(|i| CalcNode::parse_angle(context, i));
|
||||
},
|
||||
_ => Err(()),
|
||||
ref t => {
|
||||
let t = t.clone();
|
||||
Err(input.new_unexpected_token_error(t))
|
||||
},
|
||||
}
|
||||
.map_err(|()| input.new_unexpected_token_error(token.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -109,8 +109,6 @@ pub enum DisplayInside {
|
|||
#[cfg(feature = "gecko")]
|
||||
MozGridLine,
|
||||
#[cfg(feature = "gecko")]
|
||||
MozStack,
|
||||
#[cfg(feature = "gecko")]
|
||||
MozDeck,
|
||||
#[cfg(feature = "gecko")]
|
||||
MozGroupbox,
|
||||
|
@ -243,8 +241,6 @@ impl Display {
|
|||
#[cfg(feature = "gecko")]
|
||||
pub const MozGridLine: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGridLine);
|
||||
#[cfg(feature = "gecko")]
|
||||
pub const MozStack: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozStack);
|
||||
#[cfg(feature = "gecko")]
|
||||
pub const MozDeck: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozDeck);
|
||||
#[cfg(feature = "gecko")]
|
||||
pub const MozGroupbox: Self = Self::new(DisplayOutside::XUL, DisplayInside::MozGroupbox);
|
||||
|
@ -669,8 +665,6 @@ impl Parse for Display {
|
|||
#[cfg(feature = "gecko")]
|
||||
"-moz-grid-line" if moz_display_values_enabled(context) => Display::MozGridLine,
|
||||
#[cfg(feature = "gecko")]
|
||||
"-moz-stack" if moz_display_values_enabled(context) => Display::MozStack,
|
||||
#[cfg(feature = "gecko")]
|
||||
"-moz-deck" if moz_display_values_enabled(context) => Display::MozDeck,
|
||||
#[cfg(feature = "gecko")]
|
||||
"-moz-groupbox" if moz_display_values_enabled(context) => Display::MozGroupbox,
|
||||
|
@ -1157,7 +1151,9 @@ fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits {
|
|||
LonghandId::Opacity => WillChangeBits::OPACITY,
|
||||
LonghandId::Transform => WillChangeBits::TRANSFORM,
|
||||
#[cfg(feature = "gecko")]
|
||||
LonghandId::Translate | LonghandId::Rotate | LonghandId::Scale => WillChangeBits::TRANSFORM,
|
||||
LonghandId::Translate | LonghandId::Rotate | LonghandId::Scale | LonghandId::OffsetPath => {
|
||||
WillChangeBits::TRANSFORM
|
||||
},
|
||||
_ => WillChangeBits::empty(),
|
||||
};
|
||||
|
||||
|
|
|
@ -163,9 +163,8 @@ impl CalcNode {
|
|||
expected_unit: CalcUnit,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
let location = input.current_source_location();
|
||||
// FIXME: remove early returns when lifetimes are non-lexical
|
||||
match (input.next()?, expected_unit) {
|
||||
(&Token::Number { value, .. }, _) => return Ok(CalcNode::Number(value)),
|
||||
(&Token::Number { value, .. }, _) => Ok(CalcNode::Number(value)),
|
||||
(
|
||||
&Token::Dimension {
|
||||
value, ref unit, ..
|
||||
|
@ -177,24 +176,18 @@ impl CalcNode {
|
|||
value, ref unit, ..
|
||||
},
|
||||
CalcUnit::LengthPercentage,
|
||||
) => {
|
||||
return NoCalcLength::parse_dimension(context, value, unit)
|
||||
.map(CalcNode::Length)
|
||||
.map_err(|()| {
|
||||
location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
|
||||
});
|
||||
},
|
||||
) => NoCalcLength::parse_dimension(context, value, unit)
|
||||
.map(CalcNode::Length)
|
||||
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
||||
(
|
||||
&Token::Dimension {
|
||||
value, ref unit, ..
|
||||
},
|
||||
CalcUnit::Angle,
|
||||
) => {
|
||||
return Angle::parse_dimension(value, unit, /* from_calc = */ true)
|
||||
Angle::parse_dimension(value, unit, /* from_calc = */ true)
|
||||
.map(CalcNode::Angle)
|
||||
.map_err(|()| {
|
||||
location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
|
||||
});
|
||||
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
},
|
||||
(
|
||||
&Token::Dimension {
|
||||
|
@ -202,21 +195,22 @@ impl CalcNode {
|
|||
},
|
||||
CalcUnit::Time,
|
||||
) => {
|
||||
return Time::parse_dimension(value, unit, /* from_calc = */ true)
|
||||
Time::parse_dimension(value, unit, /* from_calc = */ true)
|
||||
.map(CalcNode::Time)
|
||||
.map_err(|()| {
|
||||
location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
|
||||
});
|
||||
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
},
|
||||
(&Token::Percentage { unit_value, .. }, CalcUnit::LengthPercentage) |
|
||||
(&Token::Percentage { unit_value, .. }, CalcUnit::Percentage) => {
|
||||
return Ok(CalcNode::Percentage(unit_value));
|
||||
Ok(CalcNode::Percentage(unit_value))
|
||||
},
|
||||
(&Token::ParenthesisBlock, _) => {},
|
||||
(&Token::Function(ref name), _) if name.eq_ignore_ascii_case("calc") => {},
|
||||
(t, _) => return Err(location.new_unexpected_token_error(t.clone())),
|
||||
(&Token::ParenthesisBlock, _) => {
|
||||
input.parse_nested_block(|i| CalcNode::parse(context, i, expected_unit))
|
||||
},
|
||||
(&Token::Function(ref name), _) if name.eq_ignore_ascii_case("calc") => {
|
||||
input.parse_nested_block(|i| CalcNode::parse(context, i, expected_unit))
|
||||
},
|
||||
(t, _) => Err(location.new_unexpected_token_error(t.clone())),
|
||||
}
|
||||
input.parse_nested_block(|i| CalcNode::parse(context, i, expected_unit))
|
||||
}
|
||||
|
||||
/// Parse a top-level `calc` expression, with all nested sub-expressions.
|
||||
|
@ -236,8 +230,7 @@ impl CalcNode {
|
|||
if input.is_exhausted() {
|
||||
break; // allow trailing whitespace
|
||||
}
|
||||
// FIXME: remove clone() when lifetimes are non-lexical
|
||||
match input.next()?.clone() {
|
||||
match *input.next()? {
|
||||
Token::Delim('+') => {
|
||||
let rhs = Self::parse_product(context, input, expected_unit)?;
|
||||
let new_root = CalcNode::Sum(Box::new(root), Box::new(rhs));
|
||||
|
@ -248,7 +241,10 @@ impl CalcNode {
|
|||
let new_root = CalcNode::Sub(Box::new(root), Box::new(rhs));
|
||||
root = new_root;
|
||||
},
|
||||
t => return Err(input.new_unexpected_token_error(t)),
|
||||
ref t => {
|
||||
let t = t.clone();
|
||||
return Err(input.new_unexpected_token_error(t));
|
||||
},
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
|
|
|
@ -36,9 +36,6 @@ pub enum Color {
|
|||
/// A system color
|
||||
#[cfg(feature = "gecko")]
|
||||
System(SystemColor),
|
||||
/// A special color keyword value used in Gecko
|
||||
#[cfg(feature = "gecko")]
|
||||
Special(gecko::SpecialColorKeyword),
|
||||
/// Quirksmode-only rule for inheriting color from the body
|
||||
#[cfg(feature = "gecko")]
|
||||
InheritFromBodyQuirk,
|
||||
|
@ -143,6 +140,8 @@ pub enum SystemColor {
|
|||
Windowframe,
|
||||
Windowtext,
|
||||
MozButtondefault,
|
||||
MozDefaultColor,
|
||||
MozDefaultBackgroundColor,
|
||||
MozDialog,
|
||||
MozDialogtext,
|
||||
/// Used to highlight valid regions to drop something onto.
|
||||
|
@ -231,6 +230,10 @@ pub enum SystemColor {
|
|||
/// colors.
|
||||
MozNativehyperlinktext,
|
||||
|
||||
MozHyperlinktext,
|
||||
MozActivehyperlinktext,
|
||||
MozVisitedhyperlinktext,
|
||||
|
||||
/// Combobox widgets
|
||||
MozComboboxtext,
|
||||
MozCombobox,
|
||||
|
@ -246,24 +249,20 @@ impl SystemColor {
|
|||
#[inline]
|
||||
fn compute(&self, cx: &Context) -> ComputedColor {
|
||||
use crate::gecko_bindings::bindings;
|
||||
unsafe {
|
||||
convert_nscolor_to_computedcolor(bindings::Gecko_GetLookAndFeelSystemColor(
|
||||
*self as i32,
|
||||
cx.device().document(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
mod gecko {
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
|
||||
pub enum SpecialColorKeyword {
|
||||
MozDefaultColor,
|
||||
MozDefaultBackgroundColor,
|
||||
MozHyperlinktext,
|
||||
MozActivehyperlinktext,
|
||||
MozVisitedhyperlinktext,
|
||||
let prefs = cx.device().pref_sheet_prefs();
|
||||
|
||||
convert_nscolor_to_computedcolor(match *self {
|
||||
SystemColor::MozDefaultColor => prefs.mDefaultColor,
|
||||
SystemColor::MozDefaultBackgroundColor => prefs.mDefaultBackgroundColor,
|
||||
SystemColor::MozHyperlinktext => prefs.mLinkColor,
|
||||
SystemColor::MozActivehyperlinktext => prefs.mActiveLinkColor,
|
||||
SystemColor::MozVisitedhyperlinktext => prefs.mVisitedLinkColor,
|
||||
|
||||
_ => unsafe {
|
||||
bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document())
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,10 +363,6 @@ impl Parse for Color {
|
|||
if let Ok(system) = input.try(|i| SystemColor::parse(context, i)) {
|
||||
return Ok(Color::System(system));
|
||||
}
|
||||
|
||||
if let Ok(c) = input.try(gecko::SpecialColorKeyword::parse) {
|
||||
return Ok(Color::Special(c));
|
||||
}
|
||||
}
|
||||
|
||||
match e.kind {
|
||||
|
@ -401,8 +396,6 @@ impl ToCss for Color {
|
|||
#[cfg(feature = "gecko")]
|
||||
Color::System(system) => system.to_css(dest),
|
||||
#[cfg(feature = "gecko")]
|
||||
Color::Special(special) => special.to_css(dest),
|
||||
#[cfg(feature = "gecko")]
|
||||
Color::InheritFromBodyQuirk => Ok(()),
|
||||
}
|
||||
}
|
||||
|
@ -553,18 +546,6 @@ impl Color {
|
|||
#[cfg(feature = "gecko")]
|
||||
Color::System(system) => system.compute(_context?),
|
||||
#[cfg(feature = "gecko")]
|
||||
Color::Special(special) => {
|
||||
use self::gecko::SpecialColorKeyword as Keyword;
|
||||
let prefs = _context?.device().pref_sheet_prefs();
|
||||
convert_nscolor_to_computedcolor(match special {
|
||||
Keyword::MozDefaultColor => prefs.mDefaultColor,
|
||||
Keyword::MozDefaultBackgroundColor => prefs.mDefaultBackgroundColor,
|
||||
Keyword::MozHyperlinktext => prefs.mLinkColor,
|
||||
Keyword::MozActivehyperlinktext => prefs.mActiveLinkColor,
|
||||
Keyword::MozVisitedhyperlinktext => prefs.mVisitedLinkColor,
|
||||
})
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
Color::InheritFromBodyQuirk => {
|
||||
ComputedColor::rgba(_context?.device().body_text_color())
|
||||
},
|
||||
|
|
|
@ -63,7 +63,10 @@ fn parse_counters<'i, 't>(
|
|||
let location = input.current_source_location();
|
||||
let name = match input.next() {
|
||||
Ok(&Token::Ident(ref ident)) => CustomIdent::from_ident(location, ident, &["none"])?,
|
||||
Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
|
||||
Ok(t) => {
|
||||
let t = t.clone();
|
||||
return Err(location.new_unexpected_token_error(t));
|
||||
},
|
||||
Err(_) => break,
|
||||
};
|
||||
|
||||
|
@ -147,57 +150,60 @@ impl Parse for Content {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
// FIXME: remove clone() when lifetimes are non-lexical
|
||||
match input.next().map(|t| t.clone()) {
|
||||
Ok(Token::QuotedString(ref value)) => {
|
||||
match input.next() {
|
||||
Ok(&Token::QuotedString(ref value)) => {
|
||||
content.push(generics::ContentItem::String(
|
||||
value.as_ref().to_owned().into_boxed_str(),
|
||||
));
|
||||
},
|
||||
Ok(Token::Function(ref name)) => {
|
||||
Ok(&Token::Function(ref name)) => {
|
||||
let result = match_ignore_ascii_case! { &name,
|
||||
"counter" => Some(input.parse_nested_block(|input| {
|
||||
"counter" => input.parse_nested_block(|input| {
|
||||
let location = input.current_source_location();
|
||||
let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?;
|
||||
let style = Content::parse_counter_style(context, input);
|
||||
Ok(generics::ContentItem::Counter(name, style))
|
||||
})),
|
||||
"counters" => Some(input.parse_nested_block(|input| {
|
||||
}),
|
||||
"counters" => input.parse_nested_block(|input| {
|
||||
let location = input.current_source_location();
|
||||
let name = CustomIdent::from_ident(location, input.expect_ident()?, &[])?;
|
||||
input.expect_comma()?;
|
||||
let separator = input.expect_string()?.as_ref().to_owned().into_boxed_str();
|
||||
let style = Content::parse_counter_style(context, input);
|
||||
Ok(generics::ContentItem::Counters(name, separator, style))
|
||||
})),
|
||||
}),
|
||||
#[cfg(feature = "gecko")]
|
||||
"attr" => Some(input.parse_nested_block(|input| {
|
||||
"attr" => input.parse_nested_block(|input| {
|
||||
Ok(generics::ContentItem::Attr(Attr::parse_function(context, input)?))
|
||||
})),
|
||||
_ => None
|
||||
};
|
||||
match result {
|
||||
Some(result) => content.push(result?),
|
||||
None => {
|
||||
}),
|
||||
_ => {
|
||||
let name = name.clone();
|
||||
return Err(input.new_custom_error(
|
||||
StyleParseErrorKind::UnexpectedFunction(name.clone()),
|
||||
));
|
||||
},
|
||||
}
|
||||
StyleParseErrorKind::UnexpectedFunction(name),
|
||||
))
|
||||
}
|
||||
}?;
|
||||
content.push(result);
|
||||
},
|
||||
Ok(Token::Ident(ref ident)) => {
|
||||
Ok(&Token::Ident(ref ident)) => {
|
||||
content.push(match_ignore_ascii_case! { &ident,
|
||||
"open-quote" => generics::ContentItem::OpenQuote,
|
||||
"close-quote" => generics::ContentItem::CloseQuote,
|
||||
"no-open-quote" => generics::ContentItem::NoOpenQuote,
|
||||
"no-close-quote" => generics::ContentItem::NoCloseQuote,
|
||||
_ => return Err(input.new_custom_error(
|
||||
SelectorParseErrorKind::UnexpectedIdent(ident.clone())
|
||||
))
|
||||
_ =>{
|
||||
let ident = ident.clone();
|
||||
return Err(input.new_custom_error(
|
||||
SelectorParseErrorKind::UnexpectedIdent(ident)
|
||||
));
|
||||
}
|
||||
});
|
||||
},
|
||||
Err(_) => break,
|
||||
Ok(t) => return Err(input.new_unexpected_token_error(t)),
|
||||
Ok(t) => {
|
||||
let t = t.clone();
|
||||
return Err(input.new_unexpected_token_error(t));
|
||||
},
|
||||
}
|
||||
}
|
||||
if content.is_empty() {
|
||||
|
|
|
@ -1212,15 +1212,15 @@ impl Parse for FontVariantAlternates {
|
|||
parsed_alternates |= $flag;
|
||||
)
|
||||
);
|
||||
while let Ok(_) = input.try(|input| {
|
||||
// FIXME: remove clone() when lifetimes are non-lexical
|
||||
match input.next()?.clone() {
|
||||
Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
|
||||
check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
|
||||
alternates.push(VariantAlternates::HistoricalForms);
|
||||
Ok(())
|
||||
},
|
||||
Token::Function(ref name) => input.parse_nested_block(|i| {
|
||||
while let Ok(_) = input.try(|input| match *input.next()? {
|
||||
Token::Ident(ref value) if value.eq_ignore_ascii_case("historical-forms") => {
|
||||
check_if_parsed!(input, VariantAlternatesParsingFlags::HISTORICAL_FORMS);
|
||||
alternates.push(VariantAlternates::HistoricalForms);
|
||||
Ok(())
|
||||
},
|
||||
Token::Function(ref name) => {
|
||||
let name = name.clone();
|
||||
input.parse_nested_block(|i| {
|
||||
match_ignore_ascii_case! { &name,
|
||||
"swash" => {
|
||||
check_if_parsed!(i, VariantAlternatesParsingFlags::SWASH);
|
||||
|
@ -1270,9 +1270,9 @@ impl Parse for FontVariantAlternates {
|
|||
},
|
||||
_ => return Err(i.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
||||
}
|
||||
}),
|
||||
_ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
||||
}
|
||||
})
|
||||
},
|
||||
_ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
||||
}) {}
|
||||
|
||||
if parsed_alternates.is_empty() {
|
||||
|
|
|
@ -102,8 +102,8 @@ impl Parse for ImplicitGridTracks<TrackSize<LengthPercentage>> {
|
|||
) -> Result<Self, ParseError<'i>> {
|
||||
use style_traits::{Separator, Space};
|
||||
let track_sizes = Space::parse(input, |i| TrackSize::parse(context, i))?;
|
||||
if track_sizes.len() == 1 && track_sizes[0].is_auto() {
|
||||
//`auto`, which is the initial value, is always represented by an empty slice.
|
||||
if track_sizes.len() == 1 && track_sizes[0].is_initial() {
|
||||
// A single track with the initial value is always represented by an empty slice.
|
||||
return Ok(Default::default());
|
||||
}
|
||||
return Ok(ImplicitGridTracks(track_sizes.into()));
|
||||
|
|
|
@ -192,62 +192,57 @@ impl Parse for Gradient {
|
|||
Radial,
|
||||
}
|
||||
|
||||
// FIXME: remove clone() when lifetimes are non-lexical
|
||||
let func = input.expect_function()?.clone();
|
||||
let result = match_ignore_ascii_case! { &func,
|
||||
let func = input.expect_function()?;
|
||||
let (shape, repeating, mut compat_mode) = match_ignore_ascii_case! { &func,
|
||||
"linear-gradient" => {
|
||||
Some((Shape::Linear, false, GradientCompatMode::Modern))
|
||||
(Shape::Linear, false, GradientCompatMode::Modern)
|
||||
},
|
||||
"-webkit-linear-gradient" => {
|
||||
Some((Shape::Linear, false, GradientCompatMode::WebKit))
|
||||
(Shape::Linear, false, GradientCompatMode::WebKit)
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
"-moz-linear-gradient" => {
|
||||
Some((Shape::Linear, false, GradientCompatMode::Moz))
|
||||
(Shape::Linear, false, GradientCompatMode::Moz)
|
||||
},
|
||||
"repeating-linear-gradient" => {
|
||||
Some((Shape::Linear, true, GradientCompatMode::Modern))
|
||||
(Shape::Linear, true, GradientCompatMode::Modern)
|
||||
},
|
||||
"-webkit-repeating-linear-gradient" => {
|
||||
Some((Shape::Linear, true, GradientCompatMode::WebKit))
|
||||
(Shape::Linear, true, GradientCompatMode::WebKit)
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
"-moz-repeating-linear-gradient" => {
|
||||
Some((Shape::Linear, true, GradientCompatMode::Moz))
|
||||
(Shape::Linear, true, GradientCompatMode::Moz)
|
||||
},
|
||||
"radial-gradient" => {
|
||||
Some((Shape::Radial, false, GradientCompatMode::Modern))
|
||||
(Shape::Radial, false, GradientCompatMode::Modern)
|
||||
},
|
||||
"-webkit-radial-gradient" => {
|
||||
Some((Shape::Radial, false, GradientCompatMode::WebKit))
|
||||
(Shape::Radial, false, GradientCompatMode::WebKit)
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
"-moz-radial-gradient" => {
|
||||
Some((Shape::Radial, false, GradientCompatMode::Moz))
|
||||
(Shape::Radial, false, GradientCompatMode::Moz)
|
||||
},
|
||||
"repeating-radial-gradient" => {
|
||||
Some((Shape::Radial, true, GradientCompatMode::Modern))
|
||||
(Shape::Radial, true, GradientCompatMode::Modern)
|
||||
},
|
||||
"-webkit-repeating-radial-gradient" => {
|
||||
Some((Shape::Radial, true, GradientCompatMode::WebKit))
|
||||
(Shape::Radial, true, GradientCompatMode::WebKit)
|
||||
},
|
||||
#[cfg(feature = "gecko")]
|
||||
"-moz-repeating-radial-gradient" => {
|
||||
Some((Shape::Radial, true, GradientCompatMode::Moz))
|
||||
(Shape::Radial, true, GradientCompatMode::Moz)
|
||||
},
|
||||
"-webkit-gradient" => {
|
||||
return input.parse_nested_block(|i| {
|
||||
Self::parse_webkit_gradient_argument(context, i)
|
||||
});
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let (shape, repeating, mut compat_mode) = match result {
|
||||
Some(result) => result,
|
||||
None => {
|
||||
_ => {
|
||||
let func = func.clone();
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(func)));
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
let (kind, items) = input.parse_nested_block(|i| {
|
||||
|
|
|
@ -587,39 +587,34 @@ impl Length {
|
|||
num_context: AllowedNumericType,
|
||||
allow_quirks: AllowQuirks,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
// FIXME: remove early returns when lifetimes are non-lexical
|
||||
{
|
||||
let location = input.current_source_location();
|
||||
let token = input.next()?;
|
||||
match *token {
|
||||
Token::Dimension {
|
||||
value, ref unit, ..
|
||||
} if num_context.is_ok(context.parsing_mode, value) => {
|
||||
return NoCalcLength::parse_dimension(context, value, unit)
|
||||
.map(Length::NoCalc)
|
||||
.map_err(|()| location.new_unexpected_token_error(token.clone()));
|
||||
},
|
||||
Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
|
||||
if value != 0. &&
|
||||
!context.parsing_mode.allows_unitless_lengths() &&
|
||||
!allow_quirks.allowed(context.quirks_mode)
|
||||
{
|
||||
return Err(
|
||||
location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
|
||||
);
|
||||
}
|
||||
return Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(
|
||||
value,
|
||||
))));
|
||||
},
|
||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {},
|
||||
ref token => return Err(location.new_unexpected_token_error(token.clone())),
|
||||
}
|
||||
let location = input.current_source_location();
|
||||
let token = input.next()?;
|
||||
match *token {
|
||||
Token::Dimension {
|
||||
value, ref unit, ..
|
||||
} if num_context.is_ok(context.parsing_mode, value) => {
|
||||
NoCalcLength::parse_dimension(context, value, unit)
|
||||
.map(Length::NoCalc)
|
||||
.map_err(|()| location.new_unexpected_token_error(token.clone()))
|
||||
},
|
||||
Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
|
||||
if value != 0. &&
|
||||
!context.parsing_mode.allows_unitless_lengths() &&
|
||||
!allow_quirks.allowed(context.quirks_mode)
|
||||
{
|
||||
return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
Ok(Length::NoCalc(NoCalcLength::Absolute(AbsoluteLength::Px(
|
||||
value,
|
||||
))))
|
||||
},
|
||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => input
|
||||
.parse_nested_block(|input| {
|
||||
CalcNode::parse_length(context, input, num_context)
|
||||
.map(|calc| Length::Calc(Box::new(calc)))
|
||||
}),
|
||||
ref token => return Err(location.new_unexpected_token_error(token.clone())),
|
||||
}
|
||||
input.parse_nested_block(|input| {
|
||||
CalcNode::parse_length(context, input, num_context)
|
||||
.map(|calc| Length::Calc(Box::new(calc)))
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse a non-negative length
|
||||
|
@ -809,44 +804,41 @@ impl LengthPercentage {
|
|||
num_context: AllowedNumericType,
|
||||
allow_quirks: AllowQuirks,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
// FIXME: remove early returns when lifetimes are non-lexical
|
||||
{
|
||||
let location = input.current_source_location();
|
||||
let token = input.next()?;
|
||||
match *token {
|
||||
Token::Dimension {
|
||||
value, ref unit, ..
|
||||
} if num_context.is_ok(context.parsing_mode, value) => {
|
||||
return NoCalcLength::parse_dimension(context, value, unit)
|
||||
.map(LengthPercentage::Length)
|
||||
.map_err(|()| location.new_unexpected_token_error(token.clone()));
|
||||
},
|
||||
Token::Percentage { unit_value, .. }
|
||||
if num_context.is_ok(context.parsing_mode, unit_value) =>
|
||||
{
|
||||
return Ok(LengthPercentage::Percentage(computed::Percentage(
|
||||
unit_value,
|
||||
)));
|
||||
}
|
||||
Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
|
||||
if value != 0. &&
|
||||
!context.parsing_mode.allows_unitless_lengths() &&
|
||||
!allow_quirks.allowed(context.quirks_mode)
|
||||
{
|
||||
return Err(location.new_unexpected_token_error(token.clone()));
|
||||
} else {
|
||||
return Ok(LengthPercentage::Length(NoCalcLength::from_px(value)));
|
||||
}
|
||||
},
|
||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {},
|
||||
_ => return Err(location.new_unexpected_token_error(token.clone())),
|
||||
let location = input.current_source_location();
|
||||
let token = input.next()?;
|
||||
match *token {
|
||||
Token::Dimension {
|
||||
value, ref unit, ..
|
||||
} if num_context.is_ok(context.parsing_mode, value) => {
|
||||
return NoCalcLength::parse_dimension(context, value, unit)
|
||||
.map(LengthPercentage::Length)
|
||||
.map_err(|()| location.new_unexpected_token_error(token.clone()));
|
||||
},
|
||||
Token::Percentage { unit_value, .. }
|
||||
if num_context.is_ok(context.parsing_mode, unit_value) =>
|
||||
{
|
||||
return Ok(LengthPercentage::Percentage(computed::Percentage(
|
||||
unit_value,
|
||||
)));
|
||||
}
|
||||
Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
|
||||
if value != 0. &&
|
||||
!context.parsing_mode.allows_unitless_lengths() &&
|
||||
!allow_quirks.allowed(context.quirks_mode)
|
||||
{
|
||||
return Err(location.new_unexpected_token_error(token.clone()));
|
||||
} else {
|
||||
return Ok(LengthPercentage::Length(NoCalcLength::from_px(value)));
|
||||
}
|
||||
},
|
||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
||||
let calc = input.parse_nested_block(|i| {
|
||||
CalcNode::parse_length_or_percentage(context, i, num_context)
|
||||
})?;
|
||||
Ok(LengthPercentage::Calc(Box::new(calc)))
|
||||
},
|
||||
_ => return Err(location.new_unexpected_token_error(token.clone())),
|
||||
}
|
||||
|
||||
let calc = input.parse_nested_block(|i| {
|
||||
CalcNode::parse_length_or_percentage(context, i, num_context)
|
||||
})?;
|
||||
Ok(LengthPercentage::Calc(Box::new(calc)))
|
||||
}
|
||||
|
||||
/// Parses allowing the unitless length quirk.
|
||||
|
|
|
@ -136,24 +136,22 @@ fn parse_number_with_clamping_mode<'i, 't>(
|
|||
clamping_mode: AllowedNumericType,
|
||||
) -> Result<Number, ParseError<'i>> {
|
||||
let location = input.current_source_location();
|
||||
// FIXME: remove early returns when lifetimes are non-lexical
|
||||
match *input.next()? {
|
||||
Token::Number { value, .. } if clamping_mode.is_ok(context.parsing_mode, value) => {
|
||||
return Ok(Number {
|
||||
Ok(Number {
|
||||
value: value.min(f32::MAX).max(f32::MIN),
|
||||
calc_clamping_mode: None,
|
||||
});
|
||||
})
|
||||
},
|
||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {},
|
||||
ref t => return Err(location.new_unexpected_token_error(t.clone())),
|
||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
||||
let result = input.parse_nested_block(|i| CalcNode::parse_number(context, i))?;
|
||||
Ok(Number {
|
||||
value: result.min(f32::MAX).max(f32::MIN),
|
||||
calc_clamping_mode: Some(clamping_mode),
|
||||
})
|
||||
},
|
||||
ref t => Err(location.new_unexpected_token_error(t.clone())),
|
||||
}
|
||||
|
||||
let result = input.parse_nested_block(|i| CalcNode::parse_number(context, i))?;
|
||||
|
||||
Ok(Number {
|
||||
value: result.min(f32::MAX).max(f32::MIN),
|
||||
calc_clamping_mode: Some(clamping_mode),
|
||||
})
|
||||
}
|
||||
|
||||
/// A CSS `<number>` specified value.
|
||||
|
@ -540,19 +538,16 @@ impl Parse for Integer {
|
|||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
let location = input.current_source_location();
|
||||
|
||||
// FIXME: remove early returns when lifetimes are non-lexical
|
||||
match *input.next()? {
|
||||
Token::Number {
|
||||
int_value: Some(v), ..
|
||||
} => return Ok(Integer::new(v)),
|
||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {},
|
||||
ref t => return Err(location.new_unexpected_token_error(t.clone())),
|
||||
} => Ok(Integer::new(v)),
|
||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
||||
let result = input.parse_nested_block(|i| CalcNode::parse_integer(context, i))?;
|
||||
Ok(Integer::from_calc(result))
|
||||
},
|
||||
ref t => Err(location.new_unexpected_token_error(t.clone())),
|
||||
}
|
||||
|
||||
let result = input.parse_nested_block(|i| CalcNode::parse_integer(context, i))?;
|
||||
|
||||
Ok(Integer::from_calc(result))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,11 +16,24 @@ use style_traits::{ParseError, StyleParseErrorKind};
|
|||
/// The specified value of `offset-path`.
|
||||
pub type OffsetPath = GenericOffsetPath<Angle>;
|
||||
|
||||
#[cfg(feature = "gecko")]
|
||||
fn is_ray_enabled() -> bool {
|
||||
static_prefs::pref!("layout.css.motion-path-ray.enabled")
|
||||
}
|
||||
#[cfg(feature = "servo")]
|
||||
fn is_ray_enabled() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
impl Parse for RayFunction<Angle> {
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
if !is_ray_enabled() {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||
}
|
||||
|
||||
let mut angle = None;
|
||||
let mut size = None;
|
||||
let mut contain = false;
|
||||
|
|
|
@ -111,25 +111,25 @@ impl Percentage {
|
|||
num_context: AllowedNumericType,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
let location = input.current_source_location();
|
||||
// FIXME: remove early returns when lifetimes are non-lexical
|
||||
match *input.next()? {
|
||||
Token::Percentage { unit_value, .. }
|
||||
if num_context.is_ok(context.parsing_mode, unit_value) =>
|
||||
{
|
||||
return Ok(Percentage::new(unit_value));
|
||||
}
|
||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {},
|
||||
ref t => return Err(location.new_unexpected_token_error(t.clone())),
|
||||
Ok(Percentage::new(unit_value))
|
||||
},
|
||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
||||
let result =
|
||||
input.parse_nested_block(|i| CalcNode::parse_percentage(context, i))?;
|
||||
|
||||
// TODO(emilio): -moz-image-rect is the only thing that uses
|
||||
// the clamping mode... I guess we could disallow it...
|
||||
Ok(Percentage {
|
||||
value: result,
|
||||
calc_clamping_mode: Some(num_context),
|
||||
})
|
||||
},
|
||||
ref t => Err(location.new_unexpected_token_error(t.clone())),
|
||||
}
|
||||
|
||||
let result = input.parse_nested_block(|i| CalcNode::parse_percentage(context, i))?;
|
||||
|
||||
// TODO(emilio): -moz-image-rect is the only thing that uses
|
||||
// the clamping mode... I guess we could disallow it...
|
||||
Ok(Percentage {
|
||||
value: result,
|
||||
calc_clamping_mode: Some(num_context),
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses a percentage token, but rejects it if it's negative.
|
||||
|
|
|
@ -83,27 +83,25 @@ impl Time {
|
|||
use style_traits::ParsingMode;
|
||||
|
||||
let location = input.current_source_location();
|
||||
// FIXME: remove early returns when lifetimes are non-lexical
|
||||
match input.next() {
|
||||
match *input.next()? {
|
||||
// Note that we generally pass ParserContext to is_ok() to check
|
||||
// that the ParserMode of the ParserContext allows all numeric
|
||||
// values for SMIL regardless of clamping_mode, but in this Time
|
||||
// value case, the value does not animate for SMIL at all, so we use
|
||||
// ParsingMode::DEFAULT directly.
|
||||
Ok(&Token::Dimension {
|
||||
Token::Dimension {
|
||||
value, ref unit, ..
|
||||
}) if clamping_mode.is_ok(ParsingMode::DEFAULT, value) => {
|
||||
return Time::parse_dimension(value, unit, /* from_calc = */ false).map_err(|()| {
|
||||
location.new_custom_error(StyleParseErrorKind::UnspecifiedError)
|
||||
});
|
||||
} if clamping_mode.is_ok(ParsingMode::DEFAULT, value) => {
|
||||
Time::parse_dimension(value, unit, /* from_calc = */ false)
|
||||
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
},
|
||||
Ok(&Token::Function(ref name)) if name.eq_ignore_ascii_case("calc") => {},
|
||||
Ok(t) => return Err(location.new_unexpected_token_error(t.clone())),
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
match input.parse_nested_block(|i| CalcNode::parse_time(context, i)) {
|
||||
Ok(time) if clamping_mode.is_ok(ParsingMode::DEFAULT, time.seconds) => Ok(time),
|
||||
_ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
||||
match input.parse_nested_block(|i| CalcNode::parse_time(context, i)) {
|
||||
Ok(time) if clamping_mode.is_ok(ParsingMode::DEFAULT, time.seconds) => Ok(time),
|
||||
_ => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
|
||||
}
|
||||
},
|
||||
ref t => return Err(location.new_unexpected_token_error(t.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,9 @@ use crate::values::generics::transform::{Matrix, Matrix3D};
|
|||
use crate::values::specified::position::{
|
||||
HorizontalPositionKeyword, Side, VerticalPositionKeyword,
|
||||
};
|
||||
use crate::values::specified::{self, Angle, Integer, Length, LengthPercentage, Number};
|
||||
use crate::values::specified::{
|
||||
self, Angle, Integer, Length, LengthPercentage, Number, NumberOrPercentage,
|
||||
};
|
||||
use crate::Zero;
|
||||
use cssparser::Parser;
|
||||
use style_traits::{ParseError, StyleParseErrorKind};
|
||||
|
@ -163,32 +165,32 @@ impl Transform {
|
|||
Ok(generic::TransformOperation::Translate3D(tx, ty, tz))
|
||||
},
|
||||
"scale" => {
|
||||
let sx = Number::parse(context, input)?;
|
||||
let sx = NumberOrPercentage::parse(context, input)?.to_number();
|
||||
if input.try(|input| input.expect_comma()).is_ok() {
|
||||
let sy = Number::parse(context, input)?;
|
||||
let sy = NumberOrPercentage::parse(context, input)?.to_number();
|
||||
Ok(generic::TransformOperation::Scale(sx, sy))
|
||||
} else {
|
||||
Ok(generic::TransformOperation::Scale(sx, sx))
|
||||
}
|
||||
},
|
||||
"scalex" => {
|
||||
let sx = Number::parse(context, input)?;
|
||||
let sx = NumberOrPercentage::parse(context, input)?.to_number();
|
||||
Ok(generic::TransformOperation::ScaleX(sx))
|
||||
},
|
||||
"scaley" => {
|
||||
let sy = Number::parse(context, input)?;
|
||||
let sy = NumberOrPercentage::parse(context, input)?.to_number();
|
||||
Ok(generic::TransformOperation::ScaleY(sy))
|
||||
},
|
||||
"scalez" => {
|
||||
let sz = Number::parse(context, input)?;
|
||||
let sz = NumberOrPercentage::parse(context, input)?.to_number();
|
||||
Ok(generic::TransformOperation::ScaleZ(sz))
|
||||
},
|
||||
"scale3d" => {
|
||||
let sx = Number::parse(context, input)?;
|
||||
let sx = NumberOrPercentage::parse(context, input)?.to_number();
|
||||
input.expect_comma()?;
|
||||
let sy = Number::parse(context, input)?;
|
||||
let sy = NumberOrPercentage::parse(context, input)?.to_number();
|
||||
input.expect_comma()?;
|
||||
let sz = Number::parse(context, input)?;
|
||||
let sz = NumberOrPercentage::parse(context, input)?.to_number();
|
||||
Ok(generic::TransformOperation::Scale3D(sx, sy, sz))
|
||||
},
|
||||
"rotate" => {
|
||||
|
@ -421,17 +423,22 @@ impl Parse for Translate {
|
|||
if let Ok(ty) = input.try(|i| specified::LengthPercentage::parse(context, i)) {
|
||||
if let Ok(tz) = input.try(|i| specified::Length::parse(context, i)) {
|
||||
// 'translate: <length-percentage> <length-percentage> <length>'
|
||||
return Ok(generic::Translate::Translate3D(tx, ty, tz));
|
||||
return Ok(generic::Translate::Translate(tx, ty, tz));
|
||||
}
|
||||
|
||||
// translate: <length-percentage> <length-percentage>'
|
||||
return Ok(generic::Translate::Translate(tx, ty));
|
||||
return Ok(generic::Translate::Translate(
|
||||
tx,
|
||||
ty,
|
||||
specified::Length::zero(),
|
||||
));
|
||||
}
|
||||
|
||||
// 'translate: <length-percentage> '
|
||||
Ok(generic::Translate::Translate(
|
||||
tx,
|
||||
specified::LengthPercentage::zero(),
|
||||
specified::Length::zero(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -440,6 +447,9 @@ impl Parse for Translate {
|
|||
pub type Scale = generic::Scale<Number>;
|
||||
|
||||
impl Parse for Scale {
|
||||
/// Scale accepts <number> | <percentage>, so we parse it as NumberOrPercentage,
|
||||
/// and then convert into an Number if it's a Percentage.
|
||||
/// https://github.com/w3c/csswg-drafts/pull/4396
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
|
@ -448,18 +458,19 @@ impl Parse for Scale {
|
|||
return Ok(generic::Scale::None);
|
||||
}
|
||||
|
||||
let sx = Number::parse(context, input)?;
|
||||
if let Ok(sy) = input.try(|i| Number::parse(context, i)) {
|
||||
if let Ok(sz) = input.try(|i| Number::parse(context, i)) {
|
||||
let sx = NumberOrPercentage::parse(context, input)?.to_number();
|
||||
if let Ok(sy) = input.try(|i| NumberOrPercentage::parse(context, i)) {
|
||||
let sy = sy.to_number();
|
||||
if let Ok(sz) = input.try(|i| NumberOrPercentage::parse(context, i)) {
|
||||
// 'scale: <number> <number> <number>'
|
||||
return Ok(generic::Scale::Scale3D(sx, sy, sz));
|
||||
return Ok(generic::Scale::Scale(sx, sy, sz.to_number()));
|
||||
}
|
||||
|
||||
// 'scale: <number> <number>'
|
||||
return Ok(generic::Scale::Scale(sx, sy));
|
||||
return Ok(generic::Scale::Scale(sx, sy, Number::new(1.0)));
|
||||
}
|
||||
|
||||
// 'scale: <number>'
|
||||
Ok(generic::Scale::Scale(sx, sx))
|
||||
Ok(generic::Scale::Scale(sx, sx, Number::new(1.0)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ gecko = []
|
|||
|
||||
[dependencies]
|
||||
app_units = "0.7"
|
||||
cssparser = "0.27.1"
|
||||
cssparser = "0.27"
|
||||
bitflags = "1.0"
|
||||
euclid = "0.20"
|
||||
lazy_static = "1"
|
||||
|
|
|
@ -14,7 +14,7 @@ servo = ["cssparser/serde", "string_cache"]
|
|||
gecko = []
|
||||
|
||||
[dependencies]
|
||||
cssparser = "0.27.1"
|
||||
cssparser = "0.27"
|
||||
servo_arc = { path = "../servo_arc" }
|
||||
smallbitvec = "2.1.1"
|
||||
smallvec = "0.6.6"
|
||||
|
|
|
@ -11,7 +11,7 @@ doctest = false
|
|||
|
||||
[dependencies]
|
||||
app_units = "0.7"
|
||||
cssparser = "0.27.1"
|
||||
cssparser = "0.27"
|
||||
euclid = "0.20"
|
||||
html5ever = "0.25"
|
||||
rayon = "1"
|
||||
|
|
|
@ -95,9 +95,6 @@
|
|||
[CSS Transitions: property <rotate> from [1 0 0 0deg\] to [0 1 0 10deg\] at (-1) should be [0 1 0 -10deg\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <rotate> from [none\] to [7 -8 9 400grad\] at (1) should be [0.5 -0.57 0.65 400grad\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <rotate> from [0 1 0 100deg\] to [0 1 0 -100deg\] at (0.25) should be [0 1 0 50deg\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -821,9 +818,6 @@
|
|||
[Web Animations: property <rotate> from [100deg\] to [-100deg\] at (0) should be [100deg\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <rotate> from [none\] to [7 -8 9 400grad\] at (1) should be [0.5 -0.57 0.65 400grad\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <rotate> from [1 0 0 0deg\] to [0 1 0 10deg\] at (1) should be [0 1 0 10deg\]]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -98,9 +98,6 @@
|
|||
[CSS Transitions: property <scale> from [2 30 400\] to [10 110 1200\] at (2) should be [18 190 2000\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [2 0.5 1\] to [inherit\] at (0) should be [2 0.5\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [none\] to [4 3 2\] at (2) should be [7 5 3\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -251,9 +248,6 @@
|
|||
[Web Animations: property <scale> from [-10 5\] to [10 -5\] at (1) should be [10 -5\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [inherit\] to [2 0.5 1\] at (1) should be [2 0.5\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [unset\] to [1.5 1\] at (2) should be [2 1\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -272,9 +266,6 @@
|
|||
[CSS Transitions with transition: all: property <scale> from [initial\] to [2 0.5 1\] at (2) should be [3 0\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [-10 5 1\] to [1\] at (1) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [-10 5\] to [10 -5\] at (0.75) should be [5 -2.5\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -311,9 +302,6 @@
|
|||
[Web Animations: property <scale> from [2 0.5 1\] to [inherit\] at (2) should be [-1 1.5 3\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [initial\] to [inherit\] at (0) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <scale> from [none\] to [none\] at (0.125) should be [none\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -347,9 +335,6 @@
|
|||
[CSS Transitions with transition: all: property <scale> from neutral to [1.5 1\] at (2) should be [1.9 1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [none\] to [4 3 2\] at (0) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <scale> from [initial\] to [inherit\] at (1) should be [0.5 1 2\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -359,27 +344,15 @@
|
|||
[Web Animations: property <scale> from [2 30 400\] to [10 110 1200\] at (-1) should be [-6 -50 -400\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [26 17 9\] to [2 1\] at (1) should be [2 1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [inherit\] to [2 0.5 1\] at (0.25) should be [0.875 0.875 1.75\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [-10 5 1\] to [1\] at (0) should be [-10 5\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <scale> from neutral to [1.5 1\] at (0.25) should be [1.2 1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [-10 5 1\] to [1\] at (1) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [1\] to [10 -5 0\] at (0.25) should be [3.25 -0.5 0.75\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [2 0.5 1\] to [initial\] at (0) should be [2 0.5\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [none\] to [4 3 2\] at (0.875) should be [3.625 2.75 1.875\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -479,9 +452,6 @@
|
|||
[Web Animations: property <scale> from [2 0.5 1\] to [inherit\] at (-1) should be [3.5 0 0\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [initial\] to [2 0.5 1\] at (1) should be [2 0.5\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <scale> from [-10 5 1\] to [1\] at (0.75) should be [-1.75 2\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -515,9 +485,6 @@
|
|||
[CSS Transitions with transition: all: property <scale> from [none\] to [4 3 2\] at (0.125) should be [1.375 1.25 1.125\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [2 0.5 1\] to [initial\] at (1) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [initial\] to [inherit\] at (0.75) should be [0.625 1 1.75\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -533,9 +500,6 @@
|
|||
[CSS Transitions with transition: all: property <scale> from [26 17 9\] to [2 1\] at (-1) should be [50 33 17\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [1\] to [10 -5 0\] at (0) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <scale> from [unset\] to [1.5 1\] at (0.75) should be [1.375 1\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -581,15 +545,6 @@
|
|||
[Web Animations: property <scale> from [1\] to [10 -5 0\] at (2) should be [19 -11 -1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [inherit\] to [initial\] at (1) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [2 0.5 1\] to [initial\] at (1) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [initial\] to [2 0.5 1\] at (0) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <scale> from [inherit\] to [initial\] at (1) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -605,9 +560,6 @@
|
|||
[CSS Animations: property <scale> from [2 0.5 1\] to [inherit\] at (-1) should be [3.5 0 0\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [initial\] to [2 0.5 1\] at (1) should be [2 0.5\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <scale> from neutral to [1.5 1\] at (0.75) should be [1.4 1\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -629,9 +581,6 @@
|
|||
[CSS Transitions: property <scale> from [2 0.5 1\] to [initial\] at (-1) should be [3 0\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [initial\] to [inherit\] at (0) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <scale> from [inherit\] to [initial\] at (0) should be [0.5 1 2\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -698,9 +647,6 @@
|
|||
[Web Animations: property <scale> from [none\] to [4 3 2\] at (0) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [-10 5 1\] to [1\] at (0) should be [-10 5\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <scale> from [2 0.5 1\] to [inherit\] at (0) should be [2 0.5\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -752,9 +698,6 @@
|
|||
[Web Animations: property <scale> from [unset\] to [1.5 1\] at (1) should be [1.5 1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [inherit\] to [initial\] at (1) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <scale> from [2 0.5 1\] to [inherit\] at (0.25) should be [1.625 0.625 1.25\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -791,9 +734,6 @@
|
|||
[CSS Animations: property <scale> from [initial\] to [inherit\] at (0.75) should be [0.625 1 1.75\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [1\] to [10 -5 0\] at (0) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [26 17 9\] to [2 1\] at (2) should be [-22 -15 -7\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -809,9 +749,6 @@
|
|||
[CSS Transitions with transition: all: property <scale> from [initial\] to [inherit\] at (0.25) should be [0.875 1 1.25\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [none\] to [4 3 2\] at (0) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <scale> from [unset\] to [1.5 1\] at (2) should be [2 1\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -827,9 +764,6 @@
|
|||
[CSS Transitions with transition: all: property <scale> from [2 0.5 1\] to [inherit\] at (-1) should be [3.5 0 0\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [26 17 9\] to [2 1\] at (1) should be [2 1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <scale> from [initial\] to [inherit\] at (0.25) should be [0.875 1 1.25\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -908,9 +842,6 @@
|
|||
[CSS Animations: property <scale> from neutral to [1.5 1\] at (1) should be [1.5 1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [2 0.5 1\] to [inherit\] at (0) should be [2 0.5\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [2 0.5 1\] to [initial\] at (0.25) should be [1.75 0.6251\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -944,9 +875,6 @@
|
|||
[CSS Transitions with transition: all: property <scale> from neutral to [1.5 1\] at (0.25) should be [1.2 1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [inherit\] to [2 0.5 1\] at (1) should be [2 0.5\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [inherit\] to [2 0.5 1\] at (0.75) should be [1.625 0.625 1.25\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -977,9 +905,6 @@
|
|||
[CSS Transitions with transition: all: property <scale> from [initial\] to [inherit\] at (-1) should be [1.5 1 0\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <scale> from [2 0.5 1\] to [initial\] at (0) should be [2 0.5\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <scale> from neutral to [1.5 1\] at (-1) should be [0.7 1\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1001,9 +926,6 @@
|
|||
[CSS Transitions: property <scale> from [1\] to [10 -5 0\] at (0.75) should be [7.75 -3.5 0.25\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <scale> from [initial\] to [2 0.5 1\] at (0) should be [1\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <scale> from [2 30 400\] to [10 110 1200\] at (2) should be [18 190 2000\]]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -74,15 +74,6 @@
|
|||
[translate interpolation]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <translate> from [480px 400px 320px\] to [240% 160%\] at (1) should be [240% 160%\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <translate> from [200px 100px 400px\] to [initial\] at (1) should be [0px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <translate> from [none\] to [8px 80% 800px\] at (0) should be [0px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <translate> from [-100px -50px\] to [100px 50px\] at (2) should be [300px 150px\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -125,9 +116,6 @@
|
|||
[CSS Transitions: property <translate> from [200px 100px 400px\] to [initial\] at (-1) should be [400px 200px 800px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <translate> from [-100px -50px 100px\] to [0px\] at (1) should be [0px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <translate> from [200px 100px 200px\] to [inherit\] at (2) should be [0px 300px 400px\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -161,9 +149,6 @@
|
|||
[CSS Transitions with transition: all: property <translate> from [unset\] to [20px\] at (0.75) should be [15px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <translate> from [480px 400px 320px\] to [240% 160%\] at (1) should be [240% 160%\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <translate> from [200px 100px 400px\] to [initial\] at (0) should be [200px 100px 400px\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -173,15 +158,9 @@
|
|||
[Web Animations: property <translate> from [inherit\] to [initial\] at (0) should be [100px 200px 300px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <translate> from [0px\] to [-100px -50px 100px\] at (0) should be [0px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <translate> from [-100%\] to [100%\] at (0) should be [-100%\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <translate> from [inherit\] to [initial\] at (1) should be [0px\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <translate> from [-100px -50px\] to [100px 50px\] at (0.25) should be [-50px -25px\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -245,9 +224,6 @@
|
|||
[CSS Animations: property <translate> from [220px 240px 260px\] to [300px 400px 500px\] at (0.125) should be [230px 260px 290px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <translate> from [initial\] to [200px 100px 200px\] at (0) should be [0px\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <translate> from [-100px\] to [100px\] at (-1) should be [-300px\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -341,9 +317,6 @@
|
|||
[CSS Transitions: property <translate> from [200px 100px 200px\] to [inherit\] at (2) should be [0px 300px 400px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <translate> from [initial\] to [inherit\] at (0) should be [0px\]]
|
||||
expected: FAIL
|
||||
|
||||
[Web Animations: property <translate> from [none\] to [8px 80% 800px\] at (1) should be [8px 80% 800px\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -437,9 +410,6 @@
|
|||
[Web Animations: property <translate> from [-100px -50px 100px\] to [0px\] at (-1) should be [-200px -100px 200px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <translate> from [0px\] to [-100px -50px 100px\] at (0) should be [0px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <translate> from [-100px -50px\] to [100px 50px\] at (0.75) should be [50px 25px\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -527,9 +497,6 @@
|
|||
[CSS Animations: property <translate> from neutral to [20px\] at (2) should be [30px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <translate> from [-100px -50px 100px\] to [0px\] at (1) should be [0px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <translate> from [-100px -50px\] to [100px 50px\] at (2) should be [300px 150px\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -635,9 +602,6 @@
|
|||
[Web Animations: property <translate> from [inherit\] to [initial\] at (-1) should be [200px 400px 600px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <translate> from [none\] to [8px 80% 800px\] at (0) should be [0px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <translate> from [0px\] to [-100px -50px 100px\] at (0.75) should be [-75px -37.5px 75px\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -764,9 +728,6 @@
|
|||
[Web Animations: property <translate> from [480px 400px 320px\] to [240% 160%\] at (0.875) should be [calc(210% + 60px) calc(140% + 50px) 40px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions with transition: all: property <translate> from [inherit\] to [initial\] at (1) should be [0px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <translate> from [480px 400px 320px\] to [240% 160%\] at (0.125) should be [calc(420px + 30%) calc(350px + 20%) 280px\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -788,9 +749,6 @@
|
|||
[CSS Animations: property <translate> from [200px 100px 200px\] to [inherit\] at (0.75) should be [125px 175px 275px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <translate> from [initial\] to [200px 100px 200px\] at (0) should be [0px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <translate> from [none\] to [8px 80% 800px\] at (0.125) should be [1px 10% 100px\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1037,9 +995,6 @@
|
|||
[CSS Transitions with transition: all: property <translate> from [none\] to [8px 80% 800px\] at (2) should be [16px 160% 1600px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <translate> from [initial\] to [inherit\] at (0) should be [0px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <translate> from neutral to [20px\] at (0.75) should be [17.5px\]]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1064,9 +1019,6 @@
|
|||
[Web Animations: property <translate> from [220px 240px 260px\] to [300px 400px 500px\] at (1) should be [300px 400px 500px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Transitions: property <translate> from [200px 100px 400px\] to [initial\] at (1) should be [0px\]]
|
||||
expected: FAIL
|
||||
|
||||
[CSS Animations: property <translate> from [200px 100px 400px\] to [initial\] at (0.25) should be [150px 75px 300px\]]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
[scale-parsing-valid.html]
|
||||
[e.style['scale'\] = "100 200 1" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['scale'\] = "100 100 1" should set the property value]
|
||||
expected: FAIL
|
||||
|
|
@ -8,12 +8,3 @@
|
|||
[e.style['translate'\] = "100px calc(10px - 10%)" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['translate'\] = "100px 200px 0px" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['translate'\] = "100px 0px 0px" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['translate'\] = "1px 2px 0" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
[transform-scale-percent-001.html]
|
||||
expected: FAIL
|
Loading…
Add table
Add a link
Reference in a new issue