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:
bors-servo 2019-11-30 16:11:15 -05:00 committed by GitHub
commit 1dd6ad7063
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
76 changed files with 1328 additions and 1043 deletions

8
Cargo.lock generated
View file

@ -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]]

View file

@ -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"

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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 }

View file

@ -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"}

View file

@ -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)
}

View file

@ -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"

View file

@ -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() &&

View file

@ -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`.

View file

@ -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;
}

View file

@ -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"

View file

@ -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,

View file

@ -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)
}
}

View file

@ -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(),
},
);

View file

@ -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>;

View file

@ -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

View file

@ -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.

View file

@ -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 {

View file

@ -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:]

View file

@ -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) {

View file

@ -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)]

View file

@ -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()
}
}

View file

@ -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.

View file

@ -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,
));
}
}

View file

@ -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),

View file

@ -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,
};

View file

@ -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,
);

View file

@ -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,
);

View file

@ -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,
);

View file

@ -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):

View file

@ -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()
}
}
}

View file

@ -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",
)}

View file

@ -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(

View file

@ -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(

View file

@ -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)",

View file

@ -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",

View file

@ -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 {

View file

@ -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(),

View file

@ -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(());
}

View file

@ -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);
}

View file

@ -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();
}
}

View file

@ -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, &current_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);
}
}
}

View file

@ -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));
}
}
}

View file

@ -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| {

View file

@ -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() {

View file

@ -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)

View file

@ -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(

View file

@ -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.

View file

@ -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)?,
))
},
}

View file

@ -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
}
}

View file

@ -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;

View file

@ -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()))
}
}

View file

@ -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(),
};

View file

@ -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));
},
}
},
_ => {

View file

@ -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())
},

View file

@ -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() {

View file

@ -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() {

View file

@ -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()));

View file

@ -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| {

View file

@ -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.

View file

@ -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))
}
}

View file

@ -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;

View file

@ -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.

View file

@ -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())),
}
}

View file

@ -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)))
}
}

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,2 @@
[transform-scale-percent-001.html]
expected: FAIL