summaryrefslogtreecommitdiffstats
path: root/servo/components
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components')
-rw-r--r--servo/components/selectors/attr.rs17
-rw-r--r--servo/components/selectors/builder.rs198
-rw-r--r--servo/components/selectors/context.rs36
-rw-r--r--servo/components/selectors/kleene_value.rs131
-rw-r--r--servo/components/selectors/lib.rs1
-rw-r--r--servo/components/selectors/matching.rs350
-rw-r--r--servo/components/selectors/parser.rs257
-rw-r--r--servo/components/selectors/tree.rs5
-rw-r--r--servo/components/servo_arc/Cargo.toml3
-rw-r--r--servo/components/servo_arc/lib.rs142
-rw-r--r--servo/components/style/Cargo.toml2
-rw-r--r--servo/components/style/animation.rs7
-rw-r--r--servo/components/style/applicable_declarations.rs5
-rw-r--r--servo/components/style/bloom.rs5
-rw-r--r--servo/components/style/build.rs12
-rw-r--r--servo/components/style/color/color_function.rs198
-rw-r--r--servo/components/style/color/component.rs58
-rw-r--r--servo/components/style/color/mix.rs3
-rw-r--r--servo/components/style/color/mod.rs6
-rw-r--r--servo/components/style/color/parsing.rs416
-rw-r--r--servo/components/style/context.rs1
-rw-r--r--servo/components/style/custom_properties.rs7
-rw-r--r--servo/components/style/dom.rs18
-rw-r--r--servo/components/style/dom_apis.rs6
-rw-r--r--servo/components/style/driver.rs21
-rw-r--r--servo/components/style/gecko/arc_types.rs13
-rw-r--r--servo/components/style/gecko/media_features.rs19
-rw-r--r--servo/components/style/gecko/media_queries.rs41
-rw-r--r--servo/components/style/gecko/non_ts_pseudo_class_list.rs1
-rw-r--r--servo/components/style/gecko/pseudo_element.rs6
-rw-r--r--servo/components/style/gecko/selector_parser.rs4
-rw-r--r--servo/components/style/gecko/snapshot.rs29
-rw-r--r--servo/components/style/gecko/url.rs2
-rw-r--r--servo/components/style/gecko/wrapper.rs79
-rw-r--r--servo/components/style/gecko_bindings/sugar/ns_style_auto_array.rs2
-rw-r--r--servo/components/style/gecko_string_cache/mod.rs2
-rw-r--r--servo/components/style/global_style_data.rs2
-rw-r--r--servo/components/style/invalidation/element/element_wrapper.rs24
-rw-r--r--servo/components/style/invalidation/element/invalidation_map.rs37
-rw-r--r--servo/components/style/invalidation/element/relative_selector.rs25
-rw-r--r--servo/components/style/invalidation/element/state_and_attributes.rs66
-rw-r--r--servo/components/style/invalidation/stylesheets.rs7
-rw-r--r--servo/components/style/matching.rs14
-rw-r--r--servo/components/style/media_queries/media_list.rs2
-rw-r--r--servo/components/style/parallel.rs1
-rw-r--r--servo/components/style/parser.rs70
-rw-r--r--servo/components/style/properties/build.py72
-rw-r--r--servo/components/style/properties/cascade.rs22
-rw-r--r--servo/components/style/properties/data.py108
-rw-r--r--servo/components/style/properties/declaration_block.rs4
-rw-r--r--servo/components/style/properties/helpers.mako.rs6
-rw-r--r--servo/components/style/properties/helpers/animated_properties.mako.rs14
-rw-r--r--servo/components/style/properties/longhands/background.mako.rs20
-rw-r--r--servo/components/style/properties/longhands/border.mako.rs25
-rw-r--r--servo/components/style/properties/longhands/box.mako.rs53
-rw-r--r--servo/components/style/properties/longhands/column.mako.rs15
-rw-r--r--servo/components/style/properties/longhands/counters.mako.rs10
-rw-r--r--servo/components/style/properties/longhands/effects.mako.rs15
-rw-r--r--servo/components/style/properties/longhands/font.mako.rs18
-rw-r--r--servo/components/style/properties/longhands/inherited_box.mako.rs15
-rw-r--r--servo/components/style/properties/longhands/inherited_svg.mako.rs4
-rw-r--r--servo/components/style/properties/longhands/inherited_table.mako.rs14
-rw-r--r--servo/components/style/properties/longhands/inherited_text.mako.rs34
-rw-r--r--servo/components/style/properties/longhands/inherited_ui.mako.rs6
-rw-r--r--servo/components/style/properties/longhands/list.mako.rs19
-rw-r--r--servo/components/style/properties/longhands/margin.mako.rs3
-rw-r--r--servo/components/style/properties/longhands/outline.mako.rs16
-rw-r--r--servo/components/style/properties/longhands/padding.mako.rs3
-rw-r--r--servo/components/style/properties/longhands/page.mako.rs2
-rw-r--r--servo/components/style/properties/longhands/position.mako.rs68
-rw-r--r--servo/components/style/properties/longhands/svg.mako.rs2
-rw-r--r--servo/components/style/properties/longhands/table.mako.rs5
-rw-r--r--servo/components/style/properties/longhands/text.mako.rs15
-rw-r--r--servo/components/style/properties/longhands/ui.mako.rs36
-rw-r--r--servo/components/style/properties/longhands/xul.mako.rs4
-rw-r--r--servo/components/style/properties/mod.rs8
-rw-r--r--servo/components/style/properties/properties.mako.rs30
-rw-r--r--servo/components/style/properties/shorthands/background.mako.rs4
-rw-r--r--servo/components/style/properties/shorthands/border.mako.rs19
-rw-r--r--servo/components/style/properties/shorthands/box.mako.rs37
-rw-r--r--servo/components/style/properties/shorthands/column.mako.rs4
-rw-r--r--servo/components/style/properties/shorthands/counters.mako.rs3
-rw-r--r--servo/components/style/properties/shorthands/effects.mako.rs3
-rw-r--r--servo/components/style/properties/shorthands/font.mako.rs5
-rw-r--r--servo/components/style/properties/shorthands/inherited_box.mako.rs3
-rw-r--r--servo/components/style/properties/shorthands/inherited_table.mako.rs3
-rw-r--r--servo/components/style/properties/shorthands/inherited_ui.mako.rs3
-rw-r--r--servo/components/style/properties/shorthands/list.mako.rs21
-rw-r--r--servo/components/style/properties/shorthands/margin.mako.rs6
-rw-r--r--servo/components/style/properties/shorthands/outline.mako.rs2
-rw-r--r--servo/components/style/properties/shorthands/padding.mako.rs6
-rw-r--r--servo/components/style/properties/shorthands/page.mako.rs3
-rw-r--r--servo/components/style/properties/shorthands/position.mako.rs14
-rw-r--r--servo/components/style/properties/shorthands/table.mako.rs3
-rw-r--r--servo/components/style/properties/shorthands/text.mako.rs2
-rw-r--r--servo/components/style/properties/shorthands/ui.mako.rs104
-rw-r--r--servo/components/style/properties/shorthands/xul.mako.rs3
-rw-r--r--servo/components/style/queries/condition.rs88
-rw-r--r--servo/components/style/queries/feature.rs6
-rw-r--r--servo/components/style/queries/feature_expression.rs4
-rw-r--r--servo/components/style/scoped_tls.rs1
-rw-r--r--servo/components/style/str.rs1
-rw-r--r--servo/components/style/style_adjuster.rs8
-rw-r--r--servo/components/style/stylesheets/container_rule.rs2
-rw-r--r--servo/components/style/stylesheets/import_rule.rs2
-rw-r--r--servo/components/style/stylesheets/mod.rs41
-rw-r--r--servo/components/style/stylesheets/page_rule.rs2
-rw-r--r--servo/components/style/stylesheets/rule_list.rs5
-rw-r--r--servo/components/style/stylesheets/rule_parser.rs71
-rw-r--r--servo/components/style/stylesheets/rules_iterator.rs2
-rw-r--r--servo/components/style/stylesheets/scope_rule.rs161
-rw-r--r--servo/components/style/stylesheets/starting_style_rule.rs57
-rw-r--r--servo/components/style/stylesheets/stylesheet.rs6
-rw-r--r--servo/components/style/stylist.rs52
-rw-r--r--servo/components/style/traversal.rs4
-rw-r--r--servo/components/style/values/computed/basic_shape.rs188
-rw-r--r--servo/components/style/values/computed/box.rs11
-rw-r--r--servo/components/style/values/computed/font.rs14
-rw-r--r--servo/components/style/values/computed/length.rs23
-rw-r--r--servo/components/style/values/computed/length_percentage.rs20
-rw-r--r--servo/components/style/values/computed/ratio.rs2
-rw-r--r--servo/components/style/values/generics/basic_shape.rs532
-rw-r--r--servo/components/style/values/generics/counters.rs14
-rw-r--r--servo/components/style/values/generics/image.rs4
-rw-r--r--servo/components/style/values/generics/transform.rs1
-rw-r--r--servo/components/style/values/resolved/mod.rs1
-rw-r--r--servo/components/style/values/specified/animation.rs40
-rw-r--r--servo/components/style/values/specified/basic_shape.rs206
-rw-r--r--servo/components/style/values/specified/box.rs50
-rw-r--r--servo/components/style/values/specified/color.rs159
-rw-r--r--servo/components/style/values/specified/counters.rs11
-rw-r--r--servo/components/style/values/specified/easing.rs11
-rw-r--r--servo/components/style/values/specified/effects.rs2
-rw-r--r--servo/components/style/values/specified/image.rs2
-rw-r--r--servo/components/style/values/specified/length.rs57
-rw-r--r--servo/components/style/values/specified/motion.rs23
-rw-r--r--servo/components/style/values/specified/svg_path.rs562
-rw-r--r--servo/components/style/values/specified/text.rs7
-rw-r--r--servo/components/style_traits/owned_slice.rs2
-rw-r--r--servo/components/style_traits/values.rs3
140 files changed, 3734 insertions, 1989 deletions
diff --git a/servo/components/selectors/attr.rs b/servo/components/selectors/attr.rs
index fee2962237..70a0c5ea95 100644
--- a/servo/components/selectors/attr.rs
+++ b/servo/components/selectors/attr.rs
@@ -111,16 +111,21 @@ impl AttrSelectorOperator {
let case = case_sensitivity;
match self {
AttrSelectorOperator::Equal => case.eq(e, s),
- AttrSelectorOperator::Prefix => e.len() >= s.len() && case.eq(&e[..s.len()], s),
+ AttrSelectorOperator::Prefix => {
+ !s.is_empty() && e.len() >= s.len() && case.eq(&e[..s.len()], s)
+ },
AttrSelectorOperator::Suffix => {
- e.len() >= s.len() && case.eq(&e[(e.len() - s.len())..], s)
+ !s.is_empty() && e.len() >= s.len() && case.eq(&e[(e.len() - s.len())..], s)
},
AttrSelectorOperator::Substring => {
- case.contains(element_attr_value, attr_selector_value)
+ !s.is_empty() && case.contains(element_attr_value, attr_selector_value)
+ },
+ AttrSelectorOperator::Includes => {
+ !s.is_empty() &&
+ element_attr_value
+ .split(SELECTOR_WHITESPACE)
+ .any(|part| case.eq(part.as_bytes(), s))
},
- AttrSelectorOperator::Includes => element_attr_value
- .split(SELECTOR_WHITESPACE)
- .any(|part| case.eq(part.as_bytes(), s)),
AttrSelectorOperator::DashMatch => {
case.eq(e, s) || (e.get(s.len()) == Some(&b'-') && case.eq(&e[..s.len()], s))
},
diff --git a/servo/components/selectors/builder.rs b/servo/components/selectors/builder.rs
index 2406cd84f6..c706554185 100644
--- a/servo/components/selectors/builder.rs
+++ b/servo/components/selectors/builder.rs
@@ -6,59 +6,45 @@
//!
//! Our selector representation is designed to optimize matching, and has
//! several requirements:
-//! * All simple selectors and combinators are stored inline in the same buffer
-//! as Component instances.
-//! * We store the top-level compound selectors from right to left, i.e. in
-//! matching order.
-//! * We store the simple selectors for each combinator from left to right, so
-//! that we match the cheaper simple selectors first.
+//! * All simple selectors and combinators are stored inline in the same buffer as Component
+//! instances.
+//! * We store the top-level compound selectors from right to left, i.e. in matching order.
+//! * We store the simple selectors for each combinator from left to right, so that we match the
+//! cheaper simple selectors first.
//!
-//! Meeting all these constraints without extra memmove traffic during parsing
-//! is non-trivial. This module encapsulates those details and presents an
-//! easy-to-use API for the parser.
+//! For example, the selector:
+//!
+//! .bar:hover > .baz:nth-child(2) + .qux
+//!
+//! Gets stored as:
+//!
+//! [.qux, + , .baz, :nth-child(2), > , .bar, :hover]
+//!
+//! Meeting all these constraints without extra memmove traffic during parsing is non-trivial. This
+//! module encapsulates those details and presents an easy-to-use API for the parser.
-use crate::parser::{Combinator, Component, RelativeSelector, Selector, SelectorImpl};
+use crate::parser::{Combinator, Component, RelativeSelector, Selector, SelectorImpl, ParseRelative};
use crate::sink::Push;
use servo_arc::{Arc, ThinArc};
-use smallvec::{self, SmallVec};
+use smallvec::SmallVec;
use std::cmp;
-use std::iter;
-use std::ptr;
use std::slice;
-/// Top-level SelectorBuilder struct. This should be stack-allocated by the
-/// consumer and never moved (because it contains a lot of inline data that
-/// would be slow to memmov).
+/// Top-level SelectorBuilder struct. This should be stack-allocated by the consumer and never
+/// moved (because it contains a lot of inline data that would be slow to memmove).
///
-/// After instantation, callers may call the push_simple_selector() and
-/// push_combinator() methods to append selector data as it is encountered
-/// (from left to right). Once the process is complete, callers should invoke
-/// build(), which transforms the contents of the SelectorBuilder into a heap-
-/// allocated Selector and leaves the builder in a drained state.
+/// After instantiation, callers may call the push_simple_selector() and push_combinator() methods
+/// to append selector data as it is encountered (from left to right). Once the process is
+/// complete, callers should invoke build(), which transforms the contents of the SelectorBuilder
+/// into a heap- allocated Selector and leaves the builder in a drained state.
#[derive(Debug)]
pub struct SelectorBuilder<Impl: SelectorImpl> {
- /// The entire sequence of simple selectors, from left to right, without combinators.
- ///
- /// We make this large because the result of parsing a selector is fed into a new
- /// Arc-ed allocation, so any spilled vec would be a wasted allocation. Also,
- /// Components are large enough that we don't have much cache locality benefit
- /// from reserving stack space for fewer of them.
- simple_selectors: SmallVec<[Component<Impl>; 32]>,
- /// The combinators, and the length of the compound selector to their left.
- combinators: SmallVec<[(Combinator, usize); 16]>,
- /// The length of the current compount selector.
- current_len: usize,
-}
-
-impl<Impl: SelectorImpl> Default for SelectorBuilder<Impl> {
- #[inline(always)]
- fn default() -> Self {
- SelectorBuilder {
- simple_selectors: SmallVec::new(),
- combinators: SmallVec::new(),
- current_len: 0,
- }
- }
+ /// The entire sequence of components. We make this large because the result of parsing a
+ /// selector is fed into a new Arc-ed allocation, so any spilled vec would be a wasted
+ /// allocation. Also, Components are large enough that we don't have much cache locality
+ /// benefit from reserving stack space for fewer of them.
+ components: SmallVec<[Component<Impl>; 32]>,
+ last_compound_start: Option<usize>,
}
impl<Impl: SelectorImpl> Push<Component<Impl>> for SelectorBuilder<Impl> {
@@ -71,101 +57,115 @@ impl<Impl: SelectorImpl> SelectorBuilder<Impl> {
/// Pushes a simple selector onto the current compound selector.
#[inline(always)]
pub fn push_simple_selector(&mut self, ss: Component<Impl>) {
- assert!(!ss.is_combinator());
- self.simple_selectors.push(ss);
- self.current_len += 1;
+ debug_assert!(!ss.is_combinator());
+ self.components.push(ss);
}
- /// Completes the current compound selector and starts a new one, delimited
- /// by the given combinator.
+ /// Completes the current compound selector and starts a new one, delimited by the given
+ /// combinator.
#[inline(always)]
pub fn push_combinator(&mut self, c: Combinator) {
- self.combinators.push((c, self.current_len));
- self.current_len = 0;
+ self.reverse_last_compound();
+ self.components.push(Component::Combinator(c));
+ self.last_compound_start = Some(self.components.len());
+ }
+
+ fn reverse_last_compound(&mut self) {
+ let start = self.last_compound_start.unwrap_or(0);
+ self.components[start..].reverse();
}
/// Returns true if combinators have ever been pushed to this builder.
#[inline(always)]
pub fn has_combinators(&self) -> bool {
- !self.combinators.is_empty()
+ self.last_compound_start.is_some()
}
/// Consumes the builder, producing a Selector.
#[inline(always)]
- pub fn build(&mut self) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
+ pub fn build(&mut self, parse_relative: ParseRelative) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
// Compute the specificity and flags.
- let sf = specificity_and_flags(self.simple_selectors.iter());
- self.build_with_specificity_and_flags(sf)
+ let sf = specificity_and_flags(self.components.iter());
+ self.build_with_specificity_and_flags(sf, parse_relative)
}
- /// Builds with an explicit SpecificityAndFlags. This is separated from build() so
- /// that unit tests can pass an explicit specificity.
+ /// Builds with an explicit SpecificityAndFlags. This is separated from build() so that unit
+ /// tests can pass an explicit specificity.
#[inline(always)]
pub(crate) fn build_with_specificity_and_flags(
&mut self,
- spec: SpecificityAndFlags,
+ mut spec: SpecificityAndFlags,
+ parse_relative: ParseRelative,
) -> ThinArc<SpecificityAndFlags, Component<Impl>> {
- // Create the Arc using an iterator that drains our buffers.
- // Panic-safety: if SelectorBuilderIter is not iterated to the end, some simple selectors
- // will safely leak.
- let raw_simple_selectors = unsafe {
- let simple_selectors_len = self.simple_selectors.len();
- self.simple_selectors.set_len(0);
- std::slice::from_raw_parts(self.simple_selectors.as_ptr(), simple_selectors_len)
- };
- let (rest, current) = split_from_end(raw_simple_selectors, self.current_len);
- let iter = SelectorBuilderIter {
- current_simple_selectors: current.iter(),
- rest_of_simple_selectors: rest,
- combinators: self.combinators.drain(..).rev(),
+ let implicit_parent = parse_relative.needs_implicit_parent_selector() &&
+ !spec.flags.contains(SelectorFlags::HAS_PARENT);
+
+ let parent_selector_and_combinator;
+ let implicit_parent = if implicit_parent {
+ spec.flags.insert(SelectorFlags::HAS_PARENT);
+ parent_selector_and_combinator = [
+ Component::Combinator(Combinator::Descendant),
+ Component::ParentSelector,
+ ];
+ &parent_selector_and_combinator[..]
+ } else {
+ &[]
};
- Arc::from_header_and_iter(spec, iter)
+ // As an optimization, for a selector without combinators, we can just keep the order
+ // as-is.
+ if self.last_compound_start.is_none() {
+ return Arc::from_header_and_iter(spec, ExactChain(self.components.drain(..), implicit_parent.iter().cloned()));
+ }
+
+ self.reverse_last_compound();
+ Arc::from_header_and_iter(spec, ExactChain(self.components.drain(..).rev(), implicit_parent.iter().cloned()))
}
}
-struct SelectorBuilderIter<'a, Impl: SelectorImpl> {
- current_simple_selectors: slice::Iter<'a, Component<Impl>>,
- rest_of_simple_selectors: &'a [Component<Impl>],
- combinators: iter::Rev<smallvec::Drain<'a, [(Combinator, usize); 16]>>,
+
+impl<Impl: SelectorImpl> Default for SelectorBuilder<Impl> {
+ #[inline(always)]
+ fn default() -> Self {
+ SelectorBuilder {
+ components: SmallVec::new(),
+ last_compound_start: None,
+ }
+ }
}
-impl<'a, Impl: SelectorImpl> ExactSizeIterator for SelectorBuilderIter<'a, Impl> {
+// This is effectively a Chain<>, but Chain isn't an ExactSizeIterator, see
+// https://github.com/rust-lang/rust/issues/34433
+struct ExactChain<A, B>(A, B);
+
+impl<A, B, Item> ExactSizeIterator for ExactChain<A, B>
+where
+ A: ExactSizeIterator<Item = Item>,
+ B: ExactSizeIterator<Item = Item>,
+{
fn len(&self) -> usize {
- self.current_simple_selectors.len() +
- self.rest_of_simple_selectors.len() +
- self.combinators.len()
+ self.0.len() + self.1.len()
}
}
-impl<'a, Impl: SelectorImpl> Iterator for SelectorBuilderIter<'a, Impl> {
- type Item = Component<Impl>;
+impl<A, B, Item> Iterator for ExactChain<A, B>
+where
+ A: ExactSizeIterator<Item = Item>,
+ B: ExactSizeIterator<Item = Item>,
+{
+ type Item = Item;
+
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
- if let Some(simple_selector_ref) = self.current_simple_selectors.next() {
- // Move a simple selector out of this slice iterator.
- // This is safe because we’ve called SmallVec::set_len(0) above,
- // so SmallVec::drop won’t drop this simple selector.
- unsafe { Some(ptr::read(simple_selector_ref)) }
- } else {
- self.combinators.next().map(|(combinator, len)| {
- let (rest, current) = split_from_end(self.rest_of_simple_selectors, len);
- self.rest_of_simple_selectors = rest;
- self.current_simple_selectors = current.iter();
- Component::Combinator(combinator)
- })
- }
+ self.0.next().or_else(|| self.1.next())
}
fn size_hint(&self) -> (usize, Option<usize>) {
- (self.len(), Some(self.len()))
+ let len = self.len();
+ (len, Some(len))
}
}
-fn split_from_end<T>(s: &[T], at: usize) -> (&[T], &[T]) {
- s.split_at(s.len() - at)
-}
-
/// Flags that indicate at which point of parsing a selector are we.
#[derive(Clone, Copy, Default, Eq, PartialEq, ToShmem)]
pub(crate) struct SelectorFlags(u8);
diff --git a/servo/components/selectors/context.rs b/servo/components/selectors/context.rs
index 84ee262dfe..289b081b64 100644
--- a/servo/components/selectors/context.rs
+++ b/servo/components/selectors/context.rs
@@ -79,10 +79,18 @@ pub enum NeedsSelectorFlags {
}
/// Whether we're matching in the contect of invalidation.
-#[derive(PartialEq)]
+#[derive(Clone, Copy, PartialEq)]
pub enum MatchingForInvalidation {
No,
Yes,
+ YesForComparison,
+}
+
+impl MatchingForInvalidation {
+ /// Are we matching for invalidation?
+ pub fn is_for_invalidation(&self) -> bool {
+ matches!(*self, Self::Yes | Self::YesForComparison)
+ }
}
/// Which quirks mode is this document in.
@@ -314,7 +322,31 @@ where
/// Whether or not we're matching to invalidate.
#[inline]
pub fn matching_for_invalidation(&self) -> bool {
- self.matching_for_invalidation == MatchingForInvalidation::Yes
+ self.matching_for_invalidation.is_for_invalidation()
+ }
+
+ /// Whether or not we're comparing for invalidation, if we are matching for invalidation.
+ #[inline]
+ pub fn matching_for_invalidation_comparison(&self) -> Option<bool> {
+ match self.matching_for_invalidation {
+ MatchingForInvalidation::No => None,
+ MatchingForInvalidation::Yes => Some(false),
+ MatchingForInvalidation::YesForComparison => Some(true),
+ }
+ }
+
+ /// Run the given matching function for before/after invalidation comparison.
+ #[inline]
+ pub fn for_invalidation_comparison<F, R>(&mut self, f: F) -> R
+ where
+ F: FnOnce(&mut Self) -> R,
+ {
+ debug_assert!(self.matching_for_invalidation(), "Not matching for invalidation?");
+ let prev = self.matching_for_invalidation;
+ self.matching_for_invalidation = MatchingForInvalidation::YesForComparison;
+ let result = f(self);
+ self.matching_for_invalidation = prev;
+ result
}
/// The case-sensitivity for class and ID selectors
diff --git a/servo/components/selectors/kleene_value.rs b/servo/components/selectors/kleene_value.rs
new file mode 100644
index 0000000000..58141c1156
--- /dev/null
+++ b/servo/components/selectors/kleene_value.rs
@@ -0,0 +1,131 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Kleen logic: https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics
+
+/// A "trilean" value based on Kleen logic.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum KleeneValue {
+ /// False
+ False = 0,
+ /// True
+ True = 1,
+ /// Either true or false, but we’re not sure which yet.
+ Unknown,
+}
+
+impl From<bool> for KleeneValue {
+ fn from(b: bool) -> Self {
+ if b {
+ Self::True
+ } else {
+ Self::False
+ }
+ }
+}
+
+impl KleeneValue {
+ /// Turns this Kleene value to a bool, taking the unknown value as an
+ /// argument.
+ pub fn to_bool(self, unknown: bool) -> bool {
+ match self {
+ Self::True => true,
+ Self::False => false,
+ Self::Unknown => unknown,
+ }
+ }
+
+ /// Return true if any result of f() is true. Otherwise, return the strongest value seen.
+ /// Returns false if empty, like that of `Iterator`.
+ pub fn any<T>(
+ iter: impl Iterator<Item = T>,
+ f: impl FnMut(T) -> Self,
+ ) -> Self {
+ Self::any_value(iter, Self::True, Self::False, f)
+ }
+
+ /// Return false if any results of f() is false. Otherwise, return the strongest value seen.
+ /// Returns true if empty, opposite of `Iterator`.
+ pub fn any_false<T>(
+ iter: impl Iterator<Item = T>,
+ f: impl FnMut(T) -> Self,
+ ) -> Self {
+ Self::any_value(iter, Self::False, Self::True, f)
+ }
+
+ fn any_value<T>(
+ iter: impl Iterator<Item = T>,
+ value: Self,
+ on_empty: Self,
+ mut f: impl FnMut(T) -> Self,
+ ) -> Self {
+ let mut result = None;
+ for item in iter {
+ let r = f(item);
+ if r == value {
+ return r;
+ }
+ if let Some(v) = result.as_mut() {
+ *v = *v & r;
+ } else {
+ result = Some(r);
+ }
+ }
+ result.unwrap_or(on_empty)
+ }
+}
+
+impl std::ops::Not for KleeneValue {
+ type Output = Self;
+
+ fn not(self) -> Self {
+ match self {
+ Self::True => Self::False,
+ Self::False => Self::True,
+ Self::Unknown => Self::Unknown,
+ }
+ }
+}
+
+// Implements the logical and operation.
+impl std::ops::BitAnd for KleeneValue {
+ type Output = Self;
+
+ fn bitand(self, other: Self) -> Self {
+ if self == Self::False || other == Self::False {
+ return Self::False;
+ }
+ if self == Self::Unknown || other == Self::Unknown {
+ return Self::Unknown;
+ }
+ Self::True
+ }
+}
+
+// Implements the logical or operation.
+impl std::ops::BitOr for KleeneValue {
+ type Output = Self;
+
+ fn bitor(self, other: Self) -> Self {
+ if self == Self::True || other == Self::True {
+ return Self::True;
+ }
+ if self == Self::Unknown || other == Self::Unknown {
+ return Self::Unknown;
+ }
+ Self::False
+ }
+}
+
+impl std::ops::BitOrAssign for KleeneValue {
+ fn bitor_assign(&mut self, other: Self) {
+ *self = *self | other;
+ }
+}
+
+impl std::ops::BitAndAssign for KleeneValue {
+ fn bitand_assign(&mut self, other: Self) {
+ *self = *self & other;
+ }
+}
diff --git a/servo/components/selectors/lib.rs b/servo/components/selectors/lib.rs
index d909059ccf..6e300893d2 100644
--- a/servo/components/selectors/lib.rs
+++ b/servo/components/selectors/lib.rs
@@ -35,6 +35,7 @@ pub mod relative_selector;
pub mod sink;
mod tree;
pub mod visitor;
+pub mod kleene_value;
pub use crate::nth_index_cache::NthIndexCache;
pub use crate::parser::{Parser, SelectorImpl, SelectorList};
diff --git a/servo/components/selectors/matching.rs b/servo/components/selectors/matching.rs
index 763f65d547..282d04064b 100644
--- a/servo/components/selectors/matching.rs
+++ b/servo/components/selectors/matching.rs
@@ -7,6 +7,7 @@ use crate::attr::{
ParsedAttrSelectorOperation, ParsedCaseSensitivity,
};
use crate::bloom::{BloomFilter, BLOOM_HASH_MASK};
+use crate::kleene_value::KleeneValue;
use crate::parser::{
AncestorHashes, Combinator, Component, LocalName, NthSelectorData, RelativeSelectorMatchHint,
};
@@ -200,12 +201,39 @@ fn may_match(hashes: &AncestorHashes, bf: &BloomFilter) -> bool {
/// However since the selector "c1" raises
/// NotMatchedAndRestartFromClosestDescendant. So the selector
/// "b1 + c1 > b2 ~ " doesn't match and restart matching from "d1".
+///
+/// There is also the unknown result, which is used during invalidation when
+/// specific selector is being tested for before/after comparison. More specifically,
+/// selectors that are too expensive to correctly compute during invalidation may
+/// return unknown, as the computation will be thrown away and only to be recomputed
+/// during styling. For most cases, the unknown result can be treated as matching.
+/// This is because a compound of selectors acts like &&, and unknown && matched
+/// == matched and unknown && not-matched == not-matched. However, some selectors,
+/// like `:is()`, behave like || i.e. `:is(.a, .b)` == a || b. Treating unknown
+/// == matching then causes these selectors to always return matching, which undesired
+/// for before/after comparison. Coercing to not-matched doesn't work since each
+/// inner selector may have compounds: e.g. Toggling `.foo` in `:is(.foo:has(..))`
+/// with coersion to not-matched would result in an invalid before/after comparison
+/// of not-matched/not-matched.
#[derive(Clone, Copy, Eq, PartialEq)]
enum SelectorMatchingResult {
Matched,
NotMatchedAndRestartFromClosestLaterSibling,
NotMatchedAndRestartFromClosestDescendant,
NotMatchedGlobally,
+ Unknown,
+}
+
+impl From<SelectorMatchingResult> for KleeneValue {
+ fn from(value: SelectorMatchingResult) -> Self {
+ match value {
+ SelectorMatchingResult::Matched => KleeneValue::True,
+ SelectorMatchingResult::Unknown => KleeneValue::Unknown,
+ SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling |
+ SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant |
+ SelectorMatchingResult::NotMatchedGlobally => KleeneValue::False,
+ }
+ }
}
/// Matches a selector, fast-rejecting against a bloom filter.
@@ -227,11 +255,33 @@ pub fn matches_selector<E>(
where
E: Element,
{
+ let result = matches_selector_kleene(selector, offset, hashes, element, context);
+ if cfg!(debug_assertions) && result == KleeneValue::Unknown {
+ debug_assert!(
+ context.matching_for_invalidation_comparison().unwrap_or(false),
+ "How did we return unknown?"
+ );
+ }
+ result.to_bool(true)
+}
+
+/// Same as matches_selector, but returns the Kleene value as-is.
+#[inline(always)]
+pub fn matches_selector_kleene<E>(
+ selector: &Selector<E::Impl>,
+ offset: usize,
+ hashes: Option<&AncestorHashes>,
+ element: &E,
+ context: &mut MatchingContext<E::Impl>,
+) -> KleeneValue
+where
+ E: Element,
+{
// Use the bloom filter to fast-reject.
if let Some(hashes) = hashes {
if let Some(filter) = context.bloom_filter {
if !may_match(hashes, filter) {
- return false;
+ return KleeneValue::False;
}
}
}
@@ -275,6 +325,12 @@ pub fn matches_compound_selector_from<E>(
where
E: Element,
{
+ debug_assert!(
+ !context
+ .matching_for_invalidation_comparison()
+ .unwrap_or(false),
+ "CompoundSelectorMatchingResult doesn't support unknown"
+ );
if cfg!(debug_assertions) && from_offset != 0 {
selector.combinator_at_parse_order(from_offset - 1); // This asserts.
}
@@ -320,7 +376,9 @@ where
);
for component in iter {
- if !matches_simple_selector(component, element, &mut local_context) {
+ let result = matches_simple_selector(component, element, &mut local_context);
+ debug_assert!(result != KleeneValue::Unknown, "Returned unknown in non invalidation context?");
+ if !result.to_bool(true) {
return CompoundSelectorMatchingResult::NotMatched;
}
}
@@ -341,7 +399,7 @@ fn matches_complex_selector<E>(
element: &E,
context: &mut MatchingContext<E::Impl>,
rightmost: SubjectOrPseudoElement,
-) -> bool
+) -> KleeneValue
where
E: Element,
{
@@ -353,7 +411,7 @@ where
Component::PseudoElement(ref pseudo) => {
if let Some(ref f) = context.pseudo_element_matching_fn {
if !f(pseudo) {
- return false;
+ return KleeneValue::False;
}
}
},
@@ -364,12 +422,12 @@ where
in a non-pseudo selector {:?}",
other
);
- return false;
+ return KleeneValue::False;
},
}
if !iter.matches_for_stateless_pseudo_element() {
- return false;
+ return KleeneValue::False;
}
// Advance to the non-pseudo-element part of the selector.
@@ -377,9 +435,14 @@ where
debug_assert_eq!(next_sequence, Combinator::PseudoElement);
}
- let result = matches_complex_selector_internal(iter, element, context, rightmost);
-
- matches!(result, SelectorMatchingResult::Matched)
+ matches_complex_selector_internal(
+ iter,
+ element,
+ context,
+ rightmost,
+ SubjectOrPseudoElement::Yes,
+ )
+ .into()
}
/// Matches each selector of a list as a complex selector
@@ -388,13 +451,16 @@ fn matches_complex_selector_list<E: Element>(
element: &E,
context: &mut MatchingContext<E::Impl>,
rightmost: SubjectOrPseudoElement,
-) -> bool {
- for selector in list {
- if matches_complex_selector(selector.iter(), element, context, rightmost) {
- return true;
- }
- }
- false
+) -> KleeneValue {
+ KleeneValue::any(
+ list.iter(),
+ |selector| matches_complex_selector(
+ selector.iter(),
+ element,
+ context,
+ rightmost
+ )
+ )
}
fn matches_relative_selector<E: Element>(
@@ -423,7 +489,8 @@ fn matches_relative_selector<E: Element>(
&el,
context,
rightmost,
- );
+ )
+ .to_bool(true);
if !matched && relative_selector.match_hint.is_subtree() {
matched = matches_relative_selector_subtree(
&relative_selector.selector,
@@ -472,6 +539,7 @@ fn matches_relative_selector<E: Element>(
)
} else {
matches_complex_selector(relative_selector.selector.iter(), &el, context, rightmost)
+ .to_bool(true)
};
if matched {
return true;
@@ -490,12 +558,6 @@ fn relative_selector_match_early<E: Element>(
element: &E,
context: &mut MatchingContext<E::Impl>,
) -> Option<bool> {
- if context.matching_for_invalidation() {
- // In the context of invalidation, we can't use caching/filtering due to
- // now/then matches. DOM structure also may have changed, so just pretend
- // that we always match.
- return Some(!context.in_negation());
- }
// See if we can return a cached result.
if let Some(cached) = context
.selector_caches
@@ -526,18 +588,28 @@ fn match_relative_selectors<E: Element>(
element: &E,
context: &mut MatchingContext<E::Impl>,
rightmost: SubjectOrPseudoElement,
-) -> bool {
+) -> KleeneValue {
if context.relative_selector_anchor().is_some() {
// FIXME(emilio): This currently can happen with nesting, and it's not fully
// correct, arguably. But the ideal solution isn't super-clear either. For now,
// cope with it and explicitly reject it at match time. See [1] for discussion.
//
// [1]: https://github.com/w3c/csswg-drafts/issues/9600
- return false;
+ return KleeneValue::False;
+ }
+ if let Some(may_return_unknown) = context.matching_for_invalidation_comparison() {
+ // In the context of invalidation, :has is expensive, especially because we
+ // can't use caching/filtering due to now/then matches. DOM structure also
+ // may have changed.
+ return if may_return_unknown {
+ KleeneValue::Unknown
+ } else {
+ KleeneValue::from(!context.in_negation())
+ };
}
context.nest_for_relative_selector(element.opaque(), |context| {
do_match_relative_selectors(selectors, element, context, rightmost)
- })
+ }).into()
}
/// Matches a relative selector in a list of relative selectors.
@@ -604,7 +676,7 @@ fn matches_relative_selector_subtree<E: Element>(
ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR,
);
}
- if matches_complex_selector(selector.iter(), &el, context, rightmost) {
+ if matches_complex_selector(selector.iter(), &el, context, rightmost).to_bool(true) {
return true;
}
@@ -643,19 +715,8 @@ fn hover_and_active_quirk_applies<Impl: SelectorImpl>(
}
selector_iter.clone().all(|simple| match *simple {
- Component::LocalName(_) |
- Component::AttributeInNoNamespaceExists { .. } |
- Component::AttributeInNoNamespace { .. } |
- Component::AttributeOther(_) |
- Component::ID(_) |
- Component::Class(_) |
- Component::PseudoElement(_) |
- Component::Negation(_) |
- Component::Empty |
- Component::Nth(_) |
- Component::NthOf(_) => false,
Component::NonTSPseudoClass(ref pseudo_class) => pseudo_class.is_active_or_hover(),
- _ => true,
+ _ => false,
})
}
@@ -753,6 +814,7 @@ fn matches_complex_selector_internal<E>(
element: &E,
context: &mut MatchingContext<E::Impl>,
rightmost: SubjectOrPseudoElement,
+ first_subject_compound: SubjectOrPseudoElement,
) -> SelectorMatchingResult
where
E: Element,
@@ -762,8 +824,24 @@ where
selector_iter, element
);
- let matches_compound_selector =
- matches_compound_selector(&mut selector_iter, element, context, rightmost);
+ let matches_compound_selector = {
+ let result = matches_compound_selector(&mut selector_iter, element, context, rightmost);
+ // We only care for unknown match in the first subject in compound - in the context of comparison
+ // invalidation, ancestors/previous sibling being an unknown match doesn't matter - we must
+ // invalidate to guarantee correctness.
+ if result == KleeneValue::Unknown && first_subject_compound == SubjectOrPseudoElement::No {
+ debug_assert!(
+ context
+ .matching_for_invalidation_comparison()
+ .unwrap_or(false),
+ "How did we return unknown?"
+ );
+ // Coerce the result to matched.
+ KleeneValue::True
+ } else {
+ result
+ }
+ };
let combinator = selector_iter.next_sequence();
if combinator.map_or(false, |c| c.is_sibling()) {
@@ -772,24 +850,42 @@ where
}
}
- if !matches_compound_selector {
+ // We don't short circuit unknown here, since the rest of the selector
+ // to the left of this compound may return false.
+ if matches_compound_selector == KleeneValue::False {
return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling;
}
let combinator = match combinator {
- None => return SelectorMatchingResult::Matched,
+ None => {
+ return match matches_compound_selector {
+ KleeneValue::True => SelectorMatchingResult::Matched,
+ KleeneValue::Unknown => SelectorMatchingResult::Unknown,
+ KleeneValue::False => unreachable!(),
+ }
+ },
Some(c) => c,
};
- let (candidate_not_found, mut rightmost) = match combinator {
- Combinator::NextSibling | Combinator::LaterSibling => {
- (SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant, SubjectOrPseudoElement::No)
- },
+ let (candidate_not_found, rightmost, first_subject_compound) = match combinator {
+ Combinator::NextSibling | Combinator::LaterSibling => (
+ SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant,
+ SubjectOrPseudoElement::No,
+ SubjectOrPseudoElement::No,
+ ),
Combinator::Child |
Combinator::Descendant |
Combinator::SlotAssignment |
- Combinator::Part => (SelectorMatchingResult::NotMatchedGlobally, SubjectOrPseudoElement::No),
- Combinator::PseudoElement => (SelectorMatchingResult::NotMatchedGlobally, rightmost),
+ Combinator::Part => (
+ SelectorMatchingResult::NotMatchedGlobally,
+ SubjectOrPseudoElement::No,
+ SubjectOrPseudoElement::No,
+ ),
+ Combinator::PseudoElement => (
+ SelectorMatchingResult::NotMatchedGlobally,
+ rightmost,
+ first_subject_compound,
+ ),
};
// Stop matching :visited as soon as we find a link, or a combinator for
@@ -818,18 +914,27 @@ where
&element,
context,
rightmost,
+ first_subject_compound,
)
});
- if !matches!(combinator, Combinator::PseudoElement) {
- rightmost = SubjectOrPseudoElement::No;
- }
-
match (result, combinator) {
// Return the status immediately.
- (SelectorMatchingResult::Matched, _) |
- (SelectorMatchingResult::NotMatchedGlobally, _) |
- (_, Combinator::NextSibling) => {
+ (SelectorMatchingResult::Matched | SelectorMatchingResult::Unknown, _) => {
+ debug_assert!(
+ matches_compound_selector.to_bool(true),
+ "Compound didn't match?"
+ );
+ if result == SelectorMatchingResult::Matched &&
+ matches_compound_selector.to_bool(false)
+ {
+ // Matches without question
+ return result;
+ }
+ // Something returned unknown, so return unknown.
+ return SelectorMatchingResult::Unknown;
+ },
+ (SelectorMatchingResult::NotMatchedGlobally, _) | (_, Combinator::NextSibling) => {
return result;
},
@@ -921,18 +1026,18 @@ fn matches_host<E>(
selector: Option<&Selector<E::Impl>>,
context: &mut MatchingContext<E::Impl>,
rightmost: SubjectOrPseudoElement,
-) -> bool
+) -> KleeneValue
where
E: Element,
{
let host = match context.shadow_host() {
Some(h) => h,
- None => return false,
+ None => return KleeneValue::False,
};
if host != element.opaque() {
- return false;
+ return KleeneValue::False;
}
- selector.map_or(true, |selector| {
+ selector.map_or(KleeneValue::True, |selector| {
context
.nest(|context| matches_complex_selector(selector.iter(), element, context, rightmost))
})
@@ -943,13 +1048,13 @@ fn matches_slotted<E>(
selector: &Selector<E::Impl>,
context: &mut MatchingContext<E::Impl>,
rightmost: SubjectOrPseudoElement,
-) -> bool
+) -> KleeneValue
where
E: Element,
{
// <slots> are never flattened tree slottables.
if element.is_html_slot_element() {
- return false;
+ return KleeneValue::False;
}
context.nest(|context| matches_complex_selector(selector.iter(), element, context, rightmost))
}
@@ -994,7 +1099,7 @@ fn matches_compound_selector<E>(
element: &E,
context: &mut MatchingContext<E::Impl>,
rightmost: SubjectOrPseudoElement,
-) -> bool
+) -> KleeneValue
where
E: Element,
{
@@ -1008,7 +1113,14 @@ where
rightmost,
quirks_data,
};
- selector_iter.all(|simple| matches_simple_selector(simple, element, &mut local_context))
+ KleeneValue::any_false(
+ selector_iter,
+ |simple| matches_simple_selector(
+ simple,
+ element,
+ &mut local_context
+ )
+ )
}
/// Determines whether the given element matches the given single selector.
@@ -1016,13 +1128,13 @@ fn matches_simple_selector<E>(
selector: &Component<E::Impl>,
element: &E,
context: &mut LocalMatchingContext<E::Impl>,
-) -> bool
+) -> KleeneValue
where
E: Element,
{
debug_assert!(context.shared.is_nested() || !context.shared.in_negation());
let rightmost = context.rightmost;
- match *selector {
+ KleeneValue::from(match *selector {
Component::ID(ref id) => {
element.has_id(id, context.shared.classes_and_ids_case_sensitivity())
},
@@ -1053,7 +1165,7 @@ where
},
Component::Part(ref parts) => matches_part(element, parts, &mut context.shared),
Component::Slotted(ref selector) => {
- matches_slotted(element, selector, &mut context.shared, rightmost)
+ return matches_slotted(element, selector, &mut context.shared, rightmost);
},
Component::PseudoElement(ref pseudo) => {
element.match_pseudo_element(pseudo, context.shared)
@@ -1072,7 +1184,7 @@ where
!element.is_link() &&
hover_and_active_quirk_applies(iter, context.shared, context.rightmost)
{
- return false;
+ return KleeneValue::False;
}
}
element.match_non_ts_pseudo_class(pc, &mut context.shared)
@@ -1085,32 +1197,43 @@ where
element.is_empty()
},
Component::Host(ref selector) => {
- matches_host(element, selector.as_ref(), &mut context.shared, rightmost)
+ return matches_host(element, selector.as_ref(), &mut context.shared, rightmost);
},
Component::ParentSelector | Component::Scope => match context.shared.scope_element {
Some(ref scope_element) => element.opaque() == *scope_element,
None => element.is_root(),
},
Component::Nth(ref nth_data) => {
- matches_generic_nth_child(element, context.shared, nth_data, &[], rightmost)
+ return matches_generic_nth_child(element, context.shared, nth_data, &[], rightmost);
},
- Component::NthOf(ref nth_of_data) => context.shared.nest(|context| {
- matches_generic_nth_child(
+ Component::NthOf(ref nth_of_data) => {
+ return context.shared.nest(|context| {
+ matches_generic_nth_child(
+ element,
+ context,
+ nth_of_data.nth_data(),
+ nth_of_data.selectors(),
+ rightmost,
+ )
+ })
+ },
+ Component::Is(ref list) | Component::Where(ref list) => {
+ return context.shared.nest(|context| {
+ matches_complex_selector_list(list.slice(), element, context, rightmost)
+ })
+ },
+ Component::Negation(ref list) => {
+ return context.shared.nest_for_negation(|context| {
+ !matches_complex_selector_list(list.slice(), element, context, rightmost)
+ })
+ },
+ Component::Has(ref relative_selectors) => {
+ return match_relative_selectors(
+ relative_selectors,
element,
- context,
- nth_of_data.nth_data(),
- nth_of_data.selectors(),
+ context.shared,
rightmost,
- )
- }),
- Component::Is(ref list) | Component::Where(ref list) => context.shared.nest(|context| {
- matches_complex_selector_list(list.slice(), element, context, rightmost)
- }),
- Component::Negation(ref list) => context.shared.nest_for_negation(|context| {
- !matches_complex_selector_list(list.slice(), element, context, rightmost)
- }),
- Component::Has(ref relative_selectors) => {
- match_relative_selectors(relative_selectors, element, context.shared, rightmost)
+ );
},
Component::Combinator(_) => unsafe {
debug_unreachable!("Shouldn't try to selector-match combinators")
@@ -1121,7 +1244,7 @@ where
anchor.map_or(true, |a| a == element.opaque())
},
Component::Invalid(..) => false,
- }
+ })
}
#[inline(always)]
@@ -1163,19 +1286,23 @@ fn matches_generic_nth_child<E>(
nth_data: &NthSelectorData,
selectors: &[Selector<E::Impl>],
rightmost: SubjectOrPseudoElement,
-) -> bool
+) -> KleeneValue
where
E: Element,
{
if element.ignores_nth_child_selectors() {
- return false;
+ return KleeneValue::False;
}
let has_selectors = !selectors.is_empty();
- let selectors_match =
- !has_selectors || matches_complex_selector_list(selectors, element, context, rightmost);
- if context.matching_for_invalidation() {
+ let selectors_match = !has_selectors ||
+ matches_complex_selector_list(selectors, element, context, rightmost).to_bool(true);
+ if let Some(may_return_unknown) = context.matching_for_invalidation_comparison() {
// Skip expensive indexing math in invalidation.
- return selectors_match && !context.in_negation();
+ return if selectors_match && may_return_unknown {
+ KleeneValue::Unknown
+ } else {
+ KleeneValue::from(selectors_match && !context.in_negation())
+ };
}
let NthSelectorData { ty, a, b, .. } = *nth_data;
@@ -1185,18 +1312,23 @@ where
!has_selectors,
":only-child and :only-of-type cannot have a selector list!"
);
- return matches_generic_nth_child(
- element,
- context,
- &NthSelectorData::first(is_of_type),
- selectors,
- rightmost,
- ) && matches_generic_nth_child(
- element,
- context,
- &NthSelectorData::last(is_of_type),
- selectors,
- rightmost,
+ return KleeneValue::from(
+ matches_generic_nth_child(
+ element,
+ context,
+ &NthSelectorData::first(is_of_type),
+ selectors,
+ rightmost,
+ )
+ .to_bool(true) &&
+ matches_generic_nth_child(
+ element,
+ context,
+ &NthSelectorData::last(is_of_type),
+ selectors,
+ rightmost,
+ )
+ .to_bool(true),
);
}
@@ -1223,7 +1355,7 @@ where
}
if !selectors_match {
- return false;
+ return KleeneValue::False;
}
// :first/last-child are rather trivial to match, don't bother with the
@@ -1234,7 +1366,8 @@ where
} else {
element.prev_sibling_element()
}
- .is_none();
+ .is_none()
+ .into();
}
// Lookup or compute the index.
@@ -1280,6 +1413,7 @@ where
None /* a == 0 */ => an == 0,
},
}
+ .into()
}
#[inline]
@@ -1314,7 +1448,7 @@ where
let matches = if is_of_type {
element.is_same_type(&curr)
} else if !selectors.is_empty() {
- matches_complex_selector_list(selectors, &curr, context, rightmost)
+ matches_complex_selector_list(selectors, &curr, context, rightmost).to_bool(true)
} else {
true
};
@@ -1345,7 +1479,7 @@ where
let matches = if is_of_type {
element.is_same_type(&curr)
} else if !selectors.is_empty() {
- matches_complex_selector_list(selectors, &curr, context, rightmost)
+ matches_complex_selector_list(selectors, &curr, context, rightmost).to_bool(true)
} else {
true
};
diff --git a/servo/components/selectors/parser.rs b/servo/components/selectors/parser.rs
index 9b0acb0671..37b2b00ca5 100644
--- a/servo/components/selectors/parser.rs
+++ b/servo/components/selectors/parser.rs
@@ -438,10 +438,20 @@ pub enum ParseRelative {
/// Allow selectors to start with a combinator, prepending a parent selector if so. Do nothing
/// otherwise
ForNesting,
+ /// Allow selectors to start with a combinator, prepending a scope selector if so. Do nothing
+ /// otherwise
+ ForScope,
/// Treat as parse error if any selector begins with a combinator.
No,
}
+impl ParseRelative {
+ #[inline]
+ pub(crate) fn needs_implicit_parent_selector(self) -> bool {
+ matches!(self, Self::ForNesting)
+ }
+}
+
impl<Impl: SelectorImpl> SelectorList<Impl> {
/// Returns a selector list with a single `&`
pub fn ampersand() -> Self {
@@ -469,6 +479,23 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
)
}
+ pub fn parse_forgiving<'i, 't, P>(
+ parser: &P,
+ input: &mut CssParser<'i, 't>,
+ parse_relative: ParseRelative,
+ ) -> Result<Self, ParseError<'i, P::Error>>
+ where
+ P: Parser<'i, Impl = Impl>,
+ {
+ Self::parse_with_state(
+ parser,
+ input,
+ SelectorParsingState::empty(),
+ ForgivingParsing::Yes,
+ parse_relative,
+ )
+ }
+
#[inline]
fn parse_with_state<'i, 't, P>(
parser: &P,
@@ -932,7 +959,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
}
}
let spec = SpecificityAndFlags { specificity, flags };
- Selector(builder.build_with_specificity_and_flags(spec))
+ Selector(builder.build_with_specificity_and_flags(spec, ParseRelative::No))
}
#[inline]
@@ -960,9 +987,6 @@ impl<Impl: SelectorImpl> Selector<Impl> {
}
let result = SelectorList::from_iter(orig.iter().map(|s| {
- if !s.has_parent_selector() {
- return s.clone();
- }
s.replace_parent_selector(parent)
}));
@@ -1030,82 +1054,53 @@ impl<Impl: SelectorImpl> Selector<Impl> {
flags: &mut SelectorFlags,
flags_to_propagate: SelectorFlags,
) -> Selector<Impl> {
- if !orig.has_parent_selector() {
- return orig.clone();
- }
let new_selector = orig.replace_parent_selector(parent);
*specificity += Specificity::from(new_selector.specificity() - orig.specificity());
flags.insert(new_selector.flags().intersection(flags_to_propagate));
new_selector
}
- let mut items = if !self.has_parent_selector() {
- // Implicit `&` plus descendant combinator.
- let iter = self.iter_raw_match_order();
- let len = iter.len() + 2;
- specificity += Specificity::from(parent_specificity_and_flags.specificity);
- flags.insert(
- parent_specificity_and_flags
- .flags
- .intersection(SelectorFlags::for_nesting()),
- );
- let iter = iter
- .cloned()
- .chain(std::iter::once(Component::Combinator(
- Combinator::Descendant,
- )))
- .chain(std::iter::once(Component::Is(parent.clone())));
- UniqueArc::from_header_and_iter_with_size(Default::default(), iter, len)
- } else {
- let iter = self.iter_raw_match_order().map(|component| {
- use self::Component::*;
- match *component {
- LocalName(..) |
- ID(..) |
- Class(..) |
- AttributeInNoNamespaceExists { .. } |
- AttributeInNoNamespace { .. } |
- AttributeOther(..) |
- ExplicitUniversalType |
- ExplicitAnyNamespace |
- ExplicitNoNamespace |
- DefaultNamespace(..) |
- Namespace(..) |
- Root |
- Empty |
- Scope |
- Nth(..) |
- NonTSPseudoClass(..) |
- PseudoElement(..) |
- Combinator(..) |
- Host(None) |
- Part(..) |
- Invalid(..) |
- RelativeSelectorAnchor => component.clone(),
- ParentSelector => {
- specificity += Specificity::from(parent_specificity_and_flags.specificity);
- flags.insert(
- parent_specificity_and_flags
- .flags
- .intersection(SelectorFlags::for_nesting()),
- );
- Is(parent.clone())
- },
- Negation(ref selectors) => {
- Negation(
- replace_parent_on_selector_list(
- selectors.slice(),
- parent,
- &mut specificity,
- &mut flags,
- /* propagate_specificity = */ true,
- SelectorFlags::for_nesting(),
- )
- .unwrap_or_else(|| selectors.clone()),
- )
- },
- Is(ref selectors) => {
- Is(replace_parent_on_selector_list(
+ if !self.has_parent_selector() {
+ return self.clone();
+ }
+
+ let iter = self.iter_raw_match_order().map(|component| {
+ use self::Component::*;
+ match *component {
+ LocalName(..) |
+ ID(..) |
+ Class(..) |
+ AttributeInNoNamespaceExists { .. } |
+ AttributeInNoNamespace { .. } |
+ AttributeOther(..) |
+ ExplicitUniversalType |
+ ExplicitAnyNamespace |
+ ExplicitNoNamespace |
+ DefaultNamespace(..) |
+ Namespace(..) |
+ Root |
+ Empty |
+ Scope |
+ Nth(..) |
+ NonTSPseudoClass(..) |
+ PseudoElement(..) |
+ Combinator(..) |
+ Host(None) |
+ Part(..) |
+ Invalid(..) |
+ RelativeSelectorAnchor => component.clone(),
+ ParentSelector => {
+ specificity += Specificity::from(parent_specificity_and_flags.specificity);
+ flags.insert(
+ parent_specificity_and_flags
+ .flags
+ .intersection(SelectorFlags::for_nesting()),
+ );
+ Is(parent.clone())
+ },
+ Negation(ref selectors) => {
+ Negation(
+ replace_parent_on_selector_list(
selectors.slice(),
parent,
&mut specificity,
@@ -1113,64 +1108,75 @@ impl<Impl: SelectorImpl> Selector<Impl> {
/* propagate_specificity = */ true,
SelectorFlags::for_nesting(),
)
- .unwrap_or_else(|| selectors.clone()))
- },
- Where(ref selectors) => {
- Where(
- replace_parent_on_selector_list(
- selectors.slice(),
- parent,
- &mut specificity,
- &mut flags,
- /* propagate_specificity = */ false,
- SelectorFlags::for_nesting(),
- )
- .unwrap_or_else(|| selectors.clone()),
- )
- },
- Has(ref selectors) => Has(replace_parent_on_relative_selector_list(
- selectors,
+ .unwrap_or_else(|| selectors.clone()),
+ )
+ },
+ Is(ref selectors) => {
+ Is(replace_parent_on_selector_list(
+ selectors.slice(),
parent,
&mut specificity,
&mut flags,
+ /* propagate_specificity = */ true,
SelectorFlags::for_nesting(),
)
- .into_boxed_slice()),
-
- Host(Some(ref selector)) => Host(Some(replace_parent_on_selector(
- selector,
- parent,
- &mut specificity,
- &mut flags,
- SelectorFlags::for_nesting() - SelectorFlags::HAS_NON_FEATURELESS_COMPONENT,
- ))),
- NthOf(ref data) => {
- let selectors = replace_parent_on_selector_list(
- data.selectors(),
+ .unwrap_or_else(|| selectors.clone()))
+ },
+ Where(ref selectors) => {
+ Where(
+ replace_parent_on_selector_list(
+ selectors.slice(),
parent,
&mut specificity,
&mut flags,
- /* propagate_specificity = */ true,
+ /* propagate_specificity = */ false,
SelectorFlags::for_nesting(),
- );
- NthOf(match selectors {
- Some(s) => {
- NthOfSelectorData::new(data.nth_data(), s.slice().iter().cloned())
- },
- None => data.clone(),
- })
- },
- Slotted(ref selector) => Slotted(replace_parent_on_selector(
- selector,
+ )
+ .unwrap_or_else(|| selectors.clone()),
+ )
+ },
+ Has(ref selectors) => Has(replace_parent_on_relative_selector_list(
+ selectors,
+ parent,
+ &mut specificity,
+ &mut flags,
+ SelectorFlags::for_nesting(),
+ )
+ .into_boxed_slice()),
+
+ Host(Some(ref selector)) => Host(Some(replace_parent_on_selector(
+ selector,
+ parent,
+ &mut specificity,
+ &mut flags,
+ SelectorFlags::for_nesting() - SelectorFlags::HAS_NON_FEATURELESS_COMPONENT,
+ ))),
+ NthOf(ref data) => {
+ let selectors = replace_parent_on_selector_list(
+ data.selectors(),
parent,
&mut specificity,
&mut flags,
+ /* propagate_specificity = */ true,
SelectorFlags::for_nesting(),
- )),
- }
- });
- UniqueArc::from_header_and_iter(Default::default(), iter)
- };
+ );
+ NthOf(match selectors {
+ Some(s) => {
+ NthOfSelectorData::new(data.nth_data(), s.slice().iter().cloned())
+ },
+ None => data.clone(),
+ })
+ },
+ Slotted(ref selector) => Slotted(replace_parent_on_selector(
+ selector,
+ parent,
+ &mut specificity,
+ &mut flags,
+ SelectorFlags::for_nesting(),
+ )),
+ }
+ });
+ let mut items = UniqueArc::from_header_and_iter(Default::default(), iter);
*items.header_mut() = SpecificityAndFlags {
specificity: specificity.into(),
flags,
@@ -2602,9 +2608,14 @@ where
// combinator.
builder.push_combinator(combinator.unwrap_or(Combinator::Descendant));
},
- ParseRelative::ForNesting => {
+ ParseRelative::ForNesting | ParseRelative::ForScope => {
if let Ok(combinator) = combinator {
- builder.push_simple_selector(Component::ParentSelector);
+ let selector = match parse_relative {
+ ParseRelative::ForHas | ParseRelative::No => unreachable!(),
+ ParseRelative::ForNesting => Component::ParentSelector,
+ ParseRelative::ForScope => Component::Scope,
+ };
+ builder.push_simple_selector(selector);
builder.push_combinator(combinator);
}
},
@@ -2643,7 +2654,7 @@ where
builder.push_combinator(combinator);
}
- return Ok(Selector(builder.build()));
+ return Ok(Selector(builder.build(parse_relative)));
}
fn try_parse_combinator<'i, 't, P, Impl>(input: &mut CssParser<'i, 't>) -> Result<Combinator, ()> {
@@ -4375,7 +4386,7 @@ pub mod tests {
parse("#foo:has(:is(.bar, div .baz).bar)").unwrap()
);
- let child = parse("#foo").unwrap();
+ let child = parse_relative_expected("#foo", ParseRelative::ForNesting, Some("& #foo")).unwrap();
assert_eq!(
child.replace_parent_selector(&parent),
parse(":is(.bar, div .baz) #foo").unwrap()
diff --git a/servo/components/selectors/tree.rs b/servo/components/selectors/tree.rs
index c1ea8ff5ae..1c440a124d 100644
--- a/servo/components/selectors/tree.rs
+++ b/servo/components/selectors/tree.rs
@@ -134,6 +134,11 @@ pub trait Element: Sized + Clone + Debug {
case_sensitivity: CaseSensitivity,
) -> bool;
+ fn has_custom_state(
+ &self,
+ name: &<Self::Impl as SelectorImpl>::Identifier,
+ ) -> bool;
+
/// Returns the mapping from the `exportparts` attribute in the reverse
/// direction, that is, in an outer-tree -> inner-tree direction.
fn imported_part(
diff --git a/servo/components/servo_arc/Cargo.toml b/servo/components/servo_arc/Cargo.toml
index 03b1004bac..51aeb0e1f3 100644
--- a/servo/components/servo_arc/Cargo.toml
+++ b/servo/components/servo_arc/Cargo.toml
@@ -12,7 +12,8 @@ path = "lib.rs"
[features]
gecko_refcount_logging = []
-servo = ["serde"]
+servo = ["serde", "track_alloc_size"]
+track_alloc_size = []
[dependencies]
serde = { version = "1.0", optional = true }
diff --git a/servo/components/servo_arc/lib.rs b/servo/components/servo_arc/lib.rs
index 1438ccebfd..c3d27c5bb5 100644
--- a/servo/components/servo_arc/lib.rs
+++ b/servo/components/servo_arc/lib.rs
@@ -35,10 +35,8 @@ use stable_deref_trait::{CloneStableDeref, StableDeref};
use std::alloc::{self, Layout};
use std::borrow;
use std::cmp::Ordering;
-use std::convert::From;
use std::fmt;
use std::hash::{Hash, Hasher};
-use std::iter::{ExactSizeIterator, Iterator};
use std::marker::PhantomData;
use std::mem::{self, align_of, size_of};
use std::ops::{Deref, DerefMut};
@@ -122,6 +120,8 @@ impl<T> UniqueArc<T> {
.unwrap_or_else(|| alloc::handle_alloc_error(layout))
.cast::<ArcInner<mem::MaybeUninit<T>>>();
ptr::write(&mut p.as_mut().count, atomic::AtomicUsize::new(1));
+ #[cfg(feature = "track_alloc_size")]
+ ptr::write(&mut p.as_mut().alloc_size, layout.size());
#[cfg(feature = "gecko_refcount_logging")]
{
@@ -171,9 +171,24 @@ unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}
unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
/// The object allocated by an Arc<T>
+///
+/// See https://github.com/mozilla/cbindgen/issues/937 for the derive-{eq,neq}=false. But we don't
+/// use those anyways so we can just disable them.
+/// cbindgen:derive-eq=false
+/// cbindgen:derive-neq=false
#[repr(C)]
struct ArcInner<T: ?Sized> {
count: atomic::AtomicUsize,
+ // NOTE(emilio): This needs to be here so that HeaderSlice<> is deallocated properly if the
+ // allocator relies on getting the right Layout. We don't need to track the right alignment,
+ // since we know that statically.
+ //
+ // This member could be completely avoided once min_specialization feature is stable (by
+ // implementing a trait for HeaderSlice that gives you the right layout). For now, servo-only
+ // since Gecko doesn't need it (its allocator doesn't need the size for the alignments we care
+ // about). See https://github.com/rust-lang/rust/issues/31844.
+ #[cfg(feature = "track_alloc_size")]
+ alloc_size: usize,
data: T,
}
@@ -192,24 +207,31 @@ impl<T> Arc<T> {
/// Construct an `Arc<T>`
#[inline]
pub fn new(data: T) -> Self {
- let ptr = Box::into_raw(Box::new(ArcInner {
- count: atomic::AtomicUsize::new(1),
- data,
- }));
+ let layout = Layout::new::<ArcInner<T>>();
+ let p = unsafe {
+ let ptr = ptr::NonNull::new(alloc::alloc(layout))
+ .unwrap_or_else(|| alloc::handle_alloc_error(layout))
+ .cast::<ArcInner<T>>();
+ ptr::write(ptr.as_ptr(), ArcInner {
+ count: atomic::AtomicUsize::new(1),
+ #[cfg(feature = "track_alloc_size")]
+ alloc_size: layout.size(),
+ data,
+ });
+ ptr
+ };
#[cfg(feature = "gecko_refcount_logging")]
unsafe {
// FIXME(emilio): Would be so amazing to have
// std::intrinsics::type_name() around, so that we could also report
// a real size.
- NS_LogCtor(ptr as *mut _, b"ServoArc\0".as_ptr() as *const _, 8);
+ NS_LogCtor(p.as_ptr() as *mut _, b"ServoArc\0".as_ptr() as *const _, 8);
}
- unsafe {
- Arc {
- p: ptr::NonNull::new_unchecked(ptr),
- phantom: PhantomData,
- }
+ Arc {
+ p,
+ phantom: PhantomData,
}
}
@@ -266,10 +288,13 @@ impl<T> Arc<T> {
where
F: FnOnce(Layout) -> *mut u8,
{
- let ptr = alloc(Layout::new::<ArcInner<T>>()) as *mut ArcInner<T>;
+ let layout = Layout::new::<ArcInner<T>>();
+ let ptr = alloc(layout) as *mut ArcInner<T>;
let x = ArcInner {
count: atomic::AtomicUsize::new(STATIC_REFCOUNT),
+ #[cfg(feature = "track_alloc_size")]
+ alloc_size: layout.size(),
data,
};
@@ -337,7 +362,14 @@ impl<T: ?Sized> Arc<T> {
#[inline(never)]
unsafe fn drop_slow(&mut self) {
self.record_drop();
- let _ = Box::from_raw(self.ptr());
+ let inner = self.ptr();
+
+ let layout = Layout::for_value(&*inner);
+ #[cfg(feature = "track_alloc_size")]
+ let layout = Layout::from_size_align_unchecked((*inner).alloc_size, layout.align());
+
+ std::ptr::drop_in_place(inner);
+ alloc::dealloc(inner as *mut _, layout);
}
/// Test pointer equality between the two Arcs, i.e. they must be the _same_
@@ -711,11 +743,6 @@ impl<H, T> HeaderSlice<H, T> {
}
}
-#[inline(always)]
-fn divide_rounding_up(dividend: usize, divisor: usize) -> usize {
- (dividend + divisor - 1) / divisor
-}
-
impl<H, T> Arc<HeaderSlice<H, T>> {
/// Creates an Arc for a HeaderSlice using the given header struct and
/// iterator to generate the slice.
@@ -742,26 +769,17 @@ impl<H, T> Arc<HeaderSlice<H, T>> {
{
assert_ne!(size_of::<T>(), 0, "Need to think about ZST");
- let size = size_of::<ArcInner<HeaderSlice<H, T>>>() + size_of::<T>() * num_items;
- let inner_align = align_of::<ArcInner<HeaderSlice<H, T>>>();
- debug_assert!(inner_align >= align_of::<T>());
-
- let ptr: *mut ArcInner<HeaderSlice<H, T>>;
- unsafe {
+ let layout = Layout::new::<ArcInner<HeaderSlice<H, T>>>();
+ debug_assert!(layout.align() >= align_of::<T>());
+ debug_assert!(layout.align() >= align_of::<usize>());
+ let array_layout = Layout::array::<T>(num_items).expect("Overflow");
+ let (layout, _offset) = layout.extend(array_layout).expect("Overflow");
+ let p = unsafe {
// Allocate the buffer.
- let layout = if inner_align <= align_of::<usize>() {
- Layout::from_size_align_unchecked(size, align_of::<usize>())
- } else if inner_align <= align_of::<u64>() {
- // On 32-bit platforms <T> may have 8 byte alignment while usize
- // has 4 byte aligment. Use u64 to avoid over-alignment.
- // This branch will compile away in optimized builds.
- Layout::from_size_align_unchecked(size, align_of::<u64>())
- } else {
- panic!("Over-aligned type not handled");
- };
-
let buffer = alloc(layout);
- ptr = buffer as *mut ArcInner<HeaderSlice<H, T>>;
+ let mut p = ptr::NonNull::new(buffer)
+ .unwrap_or_else(|| alloc::handle_alloc_error(layout))
+ .cast::<ArcInner<HeaderSlice<H, T>>>();
// Write the data.
//
@@ -772,11 +790,13 @@ impl<H, T> Arc<HeaderSlice<H, T>> {
} else {
atomic::AtomicUsize::new(1)
};
- ptr::write(&mut ((*ptr).count), count);
- ptr::write(&mut ((*ptr).data.header), header);
- ptr::write(&mut ((*ptr).data.len), num_items);
+ ptr::write(&mut p.as_mut().count, count);
+ #[cfg(feature = "track_alloc_size")]
+ ptr::write(&mut p.as_mut().alloc_size, layout.size());
+ ptr::write(&mut p.as_mut().data.header, header);
+ ptr::write(&mut p.as_mut().data.len, num_items);
if num_items != 0 {
- let mut current = std::ptr::addr_of_mut!((*ptr).data.data) as *mut T;
+ let mut current = std::ptr::addr_of_mut!(p.as_mut().data.data) as *mut T;
for _ in 0..num_items {
ptr::write(
current,
@@ -789,20 +809,21 @@ impl<H, T> Arc<HeaderSlice<H, T>> {
// We should have consumed the buffer exactly, maybe accounting
// for some padding from the alignment.
debug_assert!(
- (buffer.add(size) as usize - current as *mut u8 as usize) < inner_align
+ (buffer.add(layout.size()) as usize - current as *mut u8 as usize) < layout.align()
);
}
assert!(
items.next().is_none(),
"ExactSizeIterator under-reported length"
);
- }
+ p
+ };
#[cfg(feature = "gecko_refcount_logging")]
unsafe {
if !is_static {
// FIXME(emilio): Would be so amazing to have
// std::intrinsics::type_name() around.
- NS_LogCtor(ptr as *mut _, b"ServoArc\0".as_ptr() as *const _, 8)
+ NS_LogCtor(p.as_ptr() as *mut _, b"ServoArc\0".as_ptr() as *const _, 8)
}
}
@@ -812,11 +833,10 @@ impl<H, T> Arc<HeaderSlice<H, T>> {
size_of::<usize>(),
"The Arc should be thin"
);
- unsafe {
- Arc {
- p: ptr::NonNull::new_unchecked(ptr),
- phantom: PhantomData,
- }
+
+ Arc {
+ p,
+ phantom: PhantomData,
}
}
@@ -828,18 +848,7 @@ impl<H, T> Arc<HeaderSlice<H, T>> {
I: Iterator<Item = T>,
{
Arc::from_header_and_iter_alloc(
- |layout| {
- // align will only ever be align_of::<usize>() or align_of::<u64>()
- let align = layout.align();
- unsafe {
- if align == mem::align_of::<usize>() {
- Self::allocate_buffer::<usize>(layout.size())
- } else {
- assert_eq!(align, mem::align_of::<u64>());
- Self::allocate_buffer::<u64>(layout.size())
- }
- }
- },
+ |layout| unsafe { alloc::alloc(layout) },
header,
items,
num_items,
@@ -857,17 +866,6 @@ impl<H, T> Arc<HeaderSlice<H, T>> {
let len = items.len();
Self::from_header_and_iter_with_size(header, items, len)
}
-
- #[inline]
- unsafe fn allocate_buffer<W>(size: usize) -> *mut u8 {
- // We use Vec because the underlying allocation machinery isn't
- // available in stable Rust. To avoid alignment issues, we allocate
- // words rather than bytes, rounding up to the nearest word size.
- let words_to_allocate = divide_rounding_up(size, mem::size_of::<W>());
- let mut vec = Vec::<W>::with_capacity(words_to_allocate);
- vec.set_len(words_to_allocate);
- Box::into_raw(vec.into_boxed_slice()) as *mut W as *mut u8
- }
}
/// This is functionally equivalent to Arc<(H, [T])>
diff --git a/servo/components/style/Cargo.toml b/servo/components/style/Cargo.toml
index acf1bcf6fe..d6e37d4903 100644
--- a/servo/components/style/Cargo.toml
+++ b/servo/components/style/Cargo.toml
@@ -21,8 +21,6 @@ gecko = ["nsstring", "serde", "style_traits/gecko", "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", "to_shmem/servo", "servo_arc/servo"]
-servo-layout-2013 = []
-servo-layout-2020 = []
gecko_debug = []
gecko_refcount_logging = []
diff --git a/servo/components/style/animation.rs b/servo/components/style/animation.rs
index b865120aba..1cab5da84e 100644
--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -1273,9 +1273,10 @@ pub fn start_transitions_if_applicable(
) -> PropertyDeclarationIdSet {
let mut properties_that_transition = PropertyDeclarationIdSet::default();
for transition in new_style.transition_properties() {
- let physical_property = PropertyDeclarationId::Longhand(
- transition.longhand_id.to_physical(new_style.writing_mode),
- );
+ let physical_property = transition
+ .property
+ .as_borrowed()
+ .to_physical(new_style.writing_mode);
if properties_that_transition.contains(physical_property) {
continue;
}
diff --git a/servo/components/style/applicable_declarations.rs b/servo/components/style/applicable_declarations.rs
index 96049b76e3..b2fb05b0b8 100644
--- a/servo/components/style/applicable_declarations.rs
+++ b/servo/components/style/applicable_declarations.rs
@@ -142,8 +142,9 @@ pub struct ApplicableDeclarationBlock {
/// The style source, either a style rule, or a property declaration block.
#[ignore_malloc_size_of = "Arc"]
pub source: StyleSource,
- /// The bits containing the source order, cascade level, and shadow cascade
- /// order.
+ /// Order of appearance in which this rule appears - Set to 0 if not relevant
+ /// (e.g. Declaration from `style="/*...*/"`, presentation hints, animations
+ /// - See `CascadePriority` instead).
source_order: u32,
/// The specificity of the selector.
pub specificity: u32,
diff --git a/servo/components/style/bloom.rs b/servo/components/style/bloom.rs
index 824acb7114..63be881505 100644
--- a/servo/components/style/bloom.rs
+++ b/servo/components/style/bloom.rs
@@ -8,6 +8,7 @@
#![deny(missing_docs)]
use crate::dom::{SendElement, TElement};
+use crate::LocalName;
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
use owning_ref::OwningHandle;
use selectors::bloom::BloomFilter;
@@ -107,8 +108,8 @@ impl<E: TElement> PushedElement<E> {
/// We do this for attributes that are very common but not commonly used in
/// selectors.
#[inline]
-pub fn is_attr_name_excluded_from_filter(atom: &crate::Atom) -> bool {
- *atom == atom!("class") || *atom == atom!("id") || *atom == atom!("style")
+pub fn is_attr_name_excluded_from_filter(name: &LocalName) -> bool {
+ *name == local_name!("class") || *name == local_name!("id") || *name == local_name!("style")
}
/// Gather all relevant hash for fast-reject filters from an element.
diff --git a/servo/components/style/build.rs b/servo/components/style/build.rs
index 2247e87618..4b27edbe2c 100644
--- a/servo/components/style/build.rs
+++ b/servo/components/style/build.rs
@@ -71,16 +71,12 @@ fn generate_properties(engine: &str) {
fn main() {
let gecko = cfg!(feature = "gecko");
let servo = cfg!(feature = "servo");
- let l2013 = cfg!(feature = "servo-layout-2013");
- let l2020 = cfg!(feature = "servo-layout-2020");
- let engine = match (gecko, servo, l2013, l2020) {
- (true, false, false, false) => "gecko",
- (false, true, true, false) => "servo-2013",
- (false, true, false, true) => "servo-2020",
+ let engine = match (gecko, servo) {
+ (true, false) => "gecko",
+ (false, true) => "servo",
_ => panic!(
"\n\n\
- The style crate requires enabling one of its 'servo' or 'gecko' feature flags \
- and, in the 'servo' case, one of 'servo-layout-2013' or 'servo-layout-2020'.\
+ The style crate requires enabling one of its 'servo' or 'gecko' feature flags. \
\n\n"
),
};
diff --git a/servo/components/style/color/color_function.rs b/servo/components/style/color/color_function.rs
new file mode 100644
index 0000000000..2edb4fff3b
--- /dev/null
+++ b/servo/components/style/color/color_function.rs
@@ -0,0 +1,198 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Output of parsing a color function, e.g. rgb(..), hsl(..), color(..)
+
+use crate::values::normalize;
+use cssparser::color::{PredefinedColorSpace, OPAQUE};
+
+use super::{
+ component::ColorComponent,
+ convert::normalize_hue,
+ parsing::{NumberOrAngle, NumberOrPercentage},
+ AbsoluteColor, ColorSpace,
+};
+
+/// Represents a specified color function.
+#[derive(Debug)]
+pub enum ColorFunction {
+ /// <https://drafts.csswg.org/css-color-4/#rgb-functions>
+ Rgb(
+ ColorComponent<u8>, // red
+ ColorComponent<u8>, // green
+ ColorComponent<u8>, // blue
+ ColorComponent<NumberOrPercentage>, // alpha
+ ),
+ /// <https://drafts.csswg.org/css-color-4/#the-hsl-notation>
+ Hsl(
+ ColorComponent<NumberOrAngle>, // hue
+ ColorComponent<NumberOrPercentage>, // saturation
+ ColorComponent<NumberOrPercentage>, // lightness
+ ColorComponent<NumberOrPercentage>, // alpha
+ ),
+ /// <https://drafts.csswg.org/css-color-4/#the-hwb-notation>
+ Hwb(
+ ColorComponent<NumberOrAngle>, // hue
+ ColorComponent<NumberOrPercentage>, // whiteness
+ ColorComponent<NumberOrPercentage>, // blackness
+ ColorComponent<NumberOrPercentage>, // alpha
+ ),
+ /// <https://drafts.csswg.org/css-color-4/#specifying-lab-lch>
+ Lab(
+ ColorComponent<NumberOrPercentage>, // lightness
+ ColorComponent<NumberOrPercentage>, // a
+ ColorComponent<NumberOrPercentage>, // b
+ ColorComponent<NumberOrPercentage>, // alpha
+ ),
+ /// <https://drafts.csswg.org/css-color-4/#specifying-lab-lch>
+ Lch(
+ ColorComponent<NumberOrPercentage>, // lightness
+ ColorComponent<NumberOrPercentage>, // chroma
+ ColorComponent<NumberOrAngle>, // hue
+ ColorComponent<NumberOrPercentage>, // alpha
+ ),
+ /// <https://drafts.csswg.org/css-color-4/#specifying-oklab-oklch>
+ Oklab(
+ ColorComponent<NumberOrPercentage>, // lightness
+ ColorComponent<NumberOrPercentage>, // a
+ ColorComponent<NumberOrPercentage>, // b
+ ColorComponent<NumberOrPercentage>, // alpha
+ ),
+ /// <https://drafts.csswg.org/css-color-4/#specifying-oklab-oklch>
+ Oklch(
+ ColorComponent<NumberOrPercentage>, // lightness
+ ColorComponent<NumberOrPercentage>, // chroma
+ ColorComponent<NumberOrAngle>, // hue
+ ColorComponent<NumberOrPercentage>, // alpha
+ ),
+ /// <https://drafts.csswg.org/css-color-4/#color-function>
+ Color(
+ PredefinedColorSpace,
+ ColorComponent<NumberOrPercentage>, // red / x
+ ColorComponent<NumberOrPercentage>, // green / y
+ ColorComponent<NumberOrPercentage>, // blue / z
+ ColorComponent<NumberOrPercentage>, // alpha
+ ),
+}
+
+impl ColorFunction {
+ /// Try to resolve the color function to an [`AbsoluteColor`] that does not
+ /// contain any variables (currentcolor, color components, etc.).
+ pub fn resolve_to_absolute(&self) -> AbsoluteColor {
+ macro_rules! value {
+ ($v:expr) => {{
+ match $v {
+ ColorComponent::None => None,
+ // value should be Copy.
+ ColorComponent::Value(value) => Some(*value),
+ }
+ }};
+ }
+
+ macro_rules! alpha {
+ ($alpha:expr) => {{
+ value!($alpha).map(|value| normalize(value.to_number(1.0)).clamp(0.0, OPAQUE))
+ }};
+ }
+
+ match self {
+ ColorFunction::Rgb(r, g, b, alpha) => {
+ let r = value!(r).unwrap_or(0);
+ let g = value!(g).unwrap_or(0);
+ let b = value!(b).unwrap_or(0);
+
+ AbsoluteColor::srgb_legacy(r, g, b, alpha!(alpha).unwrap_or(0.0))
+ },
+ ColorFunction::Hsl(h, s, l, alpha) => {
+ // Percent reference range for S and L: 0% = 0.0, 100% = 100.0
+ const LIGHTNESS_RANGE: f32 = 100.0;
+ const SATURATION_RANGE: f32 = 100.0;
+
+ AbsoluteColor::new(
+ ColorSpace::Hsl,
+ value!(h).map(|angle| normalize_hue(angle.degrees())),
+ value!(s).map(|s| s.to_number(SATURATION_RANGE).clamp(0.0, SATURATION_RANGE)),
+ value!(l).map(|l| l.to_number(LIGHTNESS_RANGE).clamp(0.0, LIGHTNESS_RANGE)),
+ alpha!(alpha),
+ )
+ },
+ ColorFunction::Hwb(h, w, b, alpha) => {
+ // Percent reference range for W and B: 0% = 0.0, 100% = 100.0
+ const WHITENESS_RANGE: f32 = 100.0;
+ const BLACKNESS_RANGE: f32 = 100.0;
+
+ AbsoluteColor::new(
+ ColorSpace::Hwb,
+ value!(h).map(|angle| normalize_hue(angle.degrees())),
+ value!(w).map(|w| w.to_number(WHITENESS_RANGE).clamp(0.0, WHITENESS_RANGE)),
+ value!(b).map(|b| b.to_number(BLACKNESS_RANGE).clamp(0.0, BLACKNESS_RANGE)),
+ alpha!(alpha),
+ )
+ },
+ ColorFunction::Lab(l, a, b, alpha) => {
+ // for L: 0% = 0.0, 100% = 100.0
+ // for a and b: -100% = -125, 100% = 125
+ const LIGHTNESS_RANGE: f32 = 100.0;
+ const A_B_RANGE: f32 = 125.0;
+
+ AbsoluteColor::new(
+ ColorSpace::Lab,
+ value!(l).map(|l| l.to_number(LIGHTNESS_RANGE)),
+ value!(a).map(|a| a.to_number(A_B_RANGE)),
+ value!(b).map(|b| b.to_number(A_B_RANGE)),
+ alpha!(alpha),
+ )
+ },
+ ColorFunction::Lch(l, c, h, alpha) => {
+ // for L: 0% = 0.0, 100% = 100.0
+ // for C: 0% = 0, 100% = 150
+ const LIGHTNESS_RANGE: f32 = 100.0;
+ const CHROMA_RANGE: f32 = 150.0;
+
+ AbsoluteColor::new(
+ ColorSpace::Lch,
+ value!(l).map(|l| l.to_number(LIGHTNESS_RANGE)),
+ value!(c).map(|c| c.to_number(CHROMA_RANGE)),
+ value!(h).map(|angle| normalize_hue(angle.degrees())),
+ alpha!(alpha),
+ )
+ },
+ ColorFunction::Oklab(l, a, b, alpha) => {
+ // for L: 0% = 0.0, 100% = 1.0
+ // for a and b: -100% = -0.4, 100% = 0.4
+ const LIGHTNESS_RANGE: f32 = 1.0;
+ const A_B_RANGE: f32 = 0.4;
+
+ AbsoluteColor::new(
+ ColorSpace::Oklab,
+ value!(l).map(|l| l.to_number(LIGHTNESS_RANGE)),
+ value!(a).map(|a| a.to_number(A_B_RANGE)),
+ value!(b).map(|b| b.to_number(A_B_RANGE)),
+ alpha!(alpha),
+ )
+ },
+ ColorFunction::Oklch(l, c, h, alpha) => {
+ // for L: 0% = 0.0, 100% = 1.0
+ // for C: 0% = 0.0 100% = 0.4
+ const LIGHTNESS_RANGE: f32 = 1.0;
+ const CHROMA_RANGE: f32 = 0.4;
+
+ AbsoluteColor::new(
+ ColorSpace::Oklch,
+ value!(l).map(|l| l.to_number(LIGHTNESS_RANGE)),
+ value!(c).map(|c| c.to_number(CHROMA_RANGE)),
+ value!(h).map(|angle| normalize_hue(angle.degrees())),
+ alpha!(alpha),
+ )
+ },
+ ColorFunction::Color(color_space, r, g, b, alpha) => AbsoluteColor::new(
+ (*color_space).into(),
+ value!(r).map(|c| c.to_number(1.0)),
+ value!(g).map(|c| c.to_number(1.0)),
+ value!(b).map(|c| c.to_number(1.0)),
+ alpha!(alpha),
+ ),
+ }
+ }
+}
diff --git a/servo/components/style/color/component.rs b/servo/components/style/color/component.rs
index 9f101a460c..5f9d8a137e 100644
--- a/servo/components/style/color/component.rs
+++ b/servo/components/style/color/component.rs
@@ -4,8 +4,18 @@
//! Parse/serialize and resolve a single color component.
+use crate::{
+ parser::ParserContext,
+ values::{
+ generics::calc::CalcUnits,
+ specified::calc::{CalcNode as SpecifiedCalcNode, Leaf as SpecifiedLeaf},
+ },
+};
+use cssparser::{Parser, Token};
+use style_traits::{ParseError, StyleParseErrorKind};
+
/// A single color component.
-#[derive(Clone, MallocSizeOf, PartialEq, ToShmem)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
pub enum ColorComponent<ValueType> {
/// The "none" keyword.
None,
@@ -46,3 +56,49 @@ impl<ValueType> ColorComponent<ValueType> {
}
}
}
+
+/// An utility trait that allows the construction of [ColorComponent]
+/// `ValueType`'s after parsing a color component.
+pub trait ColorComponentType: Sized {
+ /// Return the [CalcUnits] flags that the impl can handle.
+ fn units() -> CalcUnits;
+
+ /// Try to create a new component from the given token.
+ fn try_from_token(token: &Token) -> Result<Self, ()>;
+
+ /// Try to create a new component from the given [CalcNodeLeaf] that was
+ /// resolved from a [CalcNode].
+ fn try_from_leaf(leaf: &SpecifiedLeaf) -> Result<Self, ()>;
+}
+
+impl<ValueType: ColorComponentType> ColorComponent<ValueType> {
+ /// Parse a single [ColorComponent].
+ pub fn parse<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ allow_none: bool,
+ ) -> Result<Self, ParseError<'i>> {
+ let location = input.current_source_location();
+
+ match *input.next()? {
+ Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => {
+ Ok(ColorComponent::None)
+ },
+ Token::Function(ref name) => {
+ let function = SpecifiedCalcNode::math_function(context, name, location)?;
+ let node = SpecifiedCalcNode::parse(context, input, function, ValueType::units())?;
+
+ let Ok(resolved_leaf) = node.resolve() else {
+ return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
+ };
+
+ ValueType::try_from_leaf(&resolved_leaf)
+ .map(Self::Value)
+ .map_err(|_| location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
+ },
+ ref t => ValueType::try_from_token(t)
+ .map(Self::Value)
+ .map_err(|_| location.new_unexpected_token_error(t.clone())),
+ }
+ }
+}
diff --git a/servo/components/style/color/mix.rs b/servo/components/style/color/mix.rs
index bcc4628575..3b3970e1e0 100644
--- a/servo/components/style/color/mix.rs
+++ b/servo/components/style/color/mix.rs
@@ -402,7 +402,8 @@ fn adjust_hue(left: &mut f32, right: &mut f32, hue_interpolation: HueInterpolati
// https://drafts.csswg.org/css-color/#longer
HueInterpolationMethod::Longer => {
let delta = *right - *left;
- if 0. < delta && delta < 180. {
+ // In the specific case of delta == 0 we need to use Decreasing
+ if 0. <= delta && delta < 180. {
*left += 360.;
} else if -180. < delta && delta <= 0. {
*right += 360.;
diff --git a/servo/components/style/color/mod.rs b/servo/components/style/color/mod.rs
index 6f35daaa8a..f7e661cddf 100644
--- a/servo/components/style/color/mod.rs
+++ b/servo/components/style/color/mod.rs
@@ -4,9 +4,13 @@
//! Color support functions.
-pub mod component;
+/// cbindgen:ignore
+mod color_function;
+
/// cbindgen:ignore
pub mod convert;
+
+pub mod component;
pub mod mix;
pub mod parsing;
mod to_css;
diff --git a/servo/components/style/color/parsing.rs b/servo/components/style/color/parsing.rs
index 1ae32c4ad7..68bcee6c56 100644
--- a/servo/components/style/color/parsing.rs
+++ b/servo/components/style/color/parsing.rs
@@ -8,14 +8,34 @@
//! Relative colors, color-mix, system colors, and other such things require better calc() support
//! and integration.
-use crate::color::component::ColorComponent;
-use cssparser::color::{
- clamp_floor_256_f32, clamp_unit_f32, parse_hash_color, PredefinedColorSpace, OPAQUE,
+use super::{
+ color_function::ColorFunction,
+ component::{ColorComponent, ColorComponentType},
+ AbsoluteColor,
+};
+use crate::{
+ parser::ParserContext,
+ values::{
+ generics::calc::CalcUnits,
+ specified::{
+ angle::Angle as SpecifiedAngle, calc::Leaf as SpecifiedLeaf,
+ color::Color as SpecifiedColor,
+ },
+ },
+};
+use cssparser::{
+ color::{clamp_floor_256_f32, clamp_unit_f32, parse_hash_color, PredefinedColorSpace, OPAQUE},
+ match_ignore_ascii_case, CowRcStr, Parser, Token,
};
-use cssparser::{match_ignore_ascii_case, CowRcStr, Parser, Token};
use std::str::FromStr;
use style_traits::{ParseError, StyleParseErrorKind};
+/// Returns true if the relative color syntax pref is enabled.
+#[inline]
+pub fn rcs_enabled() -> bool {
+ static_prefs::pref!("layout.css.relative-color-syntax.enabled")
+}
+
impl From<u8> for ColorComponent<u8> {
#[inline]
fn from(value: u8) -> Self {
@@ -29,56 +49,39 @@ impl From<u8> for ColorComponent<u8> {
/// CSS escaping (if relevant) should be resolved before calling this function.
/// (For example, the value of an `Ident` token is fine.)
#[inline]
-pub fn parse_color_keyword<Output>(ident: &str) -> Result<Output, ()>
-where
- Output: FromParsedColor,
-{
+pub fn parse_color_keyword(ident: &str) -> Result<SpecifiedColor, ()> {
Ok(match_ignore_ascii_case! { ident,
- "transparent" => Output::from_rgba(
- 0u8.into(),
- 0u8.into(),
- 0u8.into(),
- ColorComponent::Value(NumberOrPercentage::Number { value: 0.0 }),
- ),
- "currentcolor" => Output::from_current_color(),
+ "transparent" => {
+ SpecifiedColor::from_absolute_color(AbsoluteColor::srgb_legacy(0u8, 0u8, 0u8, 0.0))
+ },
+ "currentcolor" => SpecifiedColor::CurrentColor,
_ => {
let (r, g, b) = cssparser::color::parse_named_color(ident)?;
- Output::from_rgba(
- r.into(),
- g.into(),
- b.into(),
- ColorComponent::Value(NumberOrPercentage::Number { value: OPAQUE }),
- )
+ SpecifiedColor::from_absolute_color(AbsoluteColor::srgb_legacy(r, g, b, OPAQUE))
},
})
}
/// Parse a CSS color using the specified [`ColorParser`] and return a new color
/// value on success.
-pub fn parse_color_with<'i, 't, P>(
- color_parser: &P,
+pub fn parse_color_with<'i, 't>(
+ color_parser: &ColorParser<'_, '_>,
input: &mut Parser<'i, 't>,
-) -> Result<P::Output, ParseError<'i>>
-where
- P: ColorParser<'i>,
-{
+) -> Result<SpecifiedColor, ParseError<'i>> {
let location = input.current_source_location();
let token = input.next()?;
match *token {
Token::Hash(ref value) | Token::IDHash(ref value) => parse_hash_color(value.as_bytes())
.map(|(r, g, b, a)| {
- P::Output::from_rgba(
- r.into(),
- g.into(),
- b.into(),
- ColorComponent::Value(NumberOrPercentage::Number { value: a }),
- )
+ SpecifiedColor::from_absolute_color(AbsoluteColor::srgb_legacy(r, g, b, a))
}),
Token::Ident(ref value) => parse_color_keyword(value),
Token::Function(ref name) => {
let name = name.clone();
return input.parse_nested_block(|arguments| {
- parse_color_function(color_parser, name, arguments)
+ Ok(SpecifiedColor::from_absolute_color(
+ parse_color_function(color_parser, name, arguments)?.resolve_to_absolute(),
+ ))
});
},
_ => Err(()),
@@ -88,22 +91,19 @@ where
/// Parse one of the color functions: rgba(), lab(), color(), etc.
#[inline]
-fn parse_color_function<'i, 't, P>(
- color_parser: &P,
+fn parse_color_function<'i, 't>(
+ color_parser: &ColorParser<'_, '_>,
name: CowRcStr<'i>,
arguments: &mut Parser<'i, 't>,
-) -> Result<P::Output, ParseError<'i>>
-where
- P: ColorParser<'i>,
-{
+) -> Result<ColorFunction, ParseError<'i>> {
let color = match_ignore_ascii_case! { &name,
"rgb" | "rgba" => parse_rgb(color_parser, arguments),
"hsl" | "hsla" => parse_hsl(color_parser, arguments),
"hwb" => parse_hwb(color_parser, arguments),
- "lab" => parse_lab_like(color_parser, arguments, P::Output::from_lab),
- "lch" => parse_lch_like(color_parser, arguments, P::Output::from_lch),
- "oklab" => parse_lab_like(color_parser, arguments, P::Output::from_oklab),
- "oklch" => parse_lch_like(color_parser, arguments, P::Output::from_oklch),
+ "lab" => parse_lab_like(color_parser, arguments, ColorFunction::Lab),
+ "lch" => parse_lch_like(color_parser, arguments, ColorFunction::Lch),
+ "oklab" => parse_lab_like(color_parser, arguments, ColorFunction::Oklab),
+ "oklch" => parse_lch_like(color_parser, arguments, ColorFunction::Oklch),
"color" => parse_color_with_color_space(color_parser, arguments),
_ => return Err(arguments.new_unexpected_token_error(Token::Ident(name))),
}?;
@@ -113,13 +113,10 @@ where
Ok(color)
}
-fn parse_legacy_alpha<'i, 't, P>(
- color_parser: &P,
+fn parse_legacy_alpha<'i, 't>(
+ color_parser: &ColorParser<'_, '_>,
arguments: &mut Parser<'i, 't>,
-) -> Result<ColorComponent<NumberOrPercentage>, ParseError<'i>>
-where
- P: ColorParser<'i>,
-{
+) -> Result<ColorComponent<NumberOrPercentage>, ParseError<'i>> {
if !arguments.is_exhausted() {
arguments.expect_comma()?;
color_parser.parse_number_or_percentage(arguments, false)
@@ -130,13 +127,10 @@ where
}
}
-fn parse_modern_alpha<'i, 't, P>(
- color_parser: &P,
+fn parse_modern_alpha<'i, 't>(
+ color_parser: &ColorParser<'_, '_>,
arguments: &mut Parser<'i, 't>,
-) -> Result<ColorComponent<NumberOrPercentage>, ParseError<'i>>
-where
- P: ColorParser<'i>,
-{
+) -> Result<ColorComponent<NumberOrPercentage>, ParseError<'i>> {
if !arguments.is_exhausted() {
arguments.expect_delim('/')?;
color_parser.parse_number_or_percentage(arguments, true)
@@ -157,22 +151,45 @@ impl ColorComponent<NumberOrPercentage> {
}
}
+/// Parse the relative color syntax "from" syntax `from <color>`.
+fn parse_origin_color<'i, 't>(
+ color_parser: &ColorParser<'_, '_>,
+ arguments: &mut Parser<'i, 't>,
+) -> Result<Option<SpecifiedColor>, ParseError<'i>> {
+ if !rcs_enabled() {
+ return Ok(None);
+ }
+
+ // Not finding the from keyword is not an error, it just means we don't
+ // have an origin color.
+ if arguments
+ .try_parse(|p| p.expect_ident_matching("from"))
+ .is_err()
+ {
+ return Ok(None);
+ }
+
+ // We still fail if we can't parse the origin color.
+ parse_color_with(color_parser, arguments).map(|color| Some(color))
+}
+
#[inline]
-fn parse_rgb<'i, 't, P>(
- color_parser: &P,
+fn parse_rgb<'i, 't>(
+ color_parser: &ColorParser<'_, '_>,
arguments: &mut Parser<'i, 't>,
-) -> Result<P::Output, ParseError<'i>>
-where
- P: ColorParser<'i>,
-{
+) -> Result<ColorFunction, ParseError<'i>> {
+ let origin_color = parse_origin_color(color_parser, arguments)?;
+
let location = arguments.current_source_location();
let maybe_red = color_parser.parse_number_or_percentage(arguments, true)?;
// If the first component is not "none" and is followed by a comma, then we
- // are parsing the legacy syntax.
- let is_legacy_syntax =
- !maybe_red.is_none() && arguments.try_parse(|p| p.expect_comma()).is_ok();
+ // are parsing the legacy syntax. Legacy syntax also doesn't support an
+ // origin color.
+ let is_legacy_syntax = origin_color.is_none() &&
+ !maybe_red.is_none() &&
+ arguments.try_parse(|p| p.expect_comma()).is_ok();
let (red, green, blue, alpha) = if is_legacy_syntax {
let Ok(is_percentage) = maybe_red.is_percentage() else {
@@ -217,25 +234,26 @@ where
(red, green, blue, alpha)
};
- Ok(P::Output::from_rgba(red, green, blue, alpha))
+ Ok(ColorFunction::Rgb(red, green, blue, alpha))
}
/// Parses hsl syntax.
///
/// <https://drafts.csswg.org/css-color/#the-hsl-notation>
#[inline]
-fn parse_hsl<'i, 't, P>(
- color_parser: &P,
+fn parse_hsl<'i, 't>(
+ color_parser: &ColorParser<'_, '_>,
arguments: &mut Parser<'i, 't>,
-) -> Result<P::Output, ParseError<'i>>
-where
- P: ColorParser<'i>,
-{
+) -> Result<ColorFunction, ParseError<'i>> {
+ let origin_color = parse_origin_color(color_parser, arguments)?;
+
let hue = color_parser.parse_number_or_angle(arguments, true)?;
// If the hue is not "none" and is followed by a comma, then we are parsing
- // the legacy syntax.
- let is_legacy_syntax = !hue.is_none() && arguments.try_parse(|p| p.expect_comma()).is_ok();
+ // the legacy syntax. Legacy syntax also doesn't support an origin color.
+ let is_legacy_syntax = origin_color.is_none() &&
+ !hue.is_none() &&
+ arguments.try_parse(|p| p.expect_comma()).is_ok();
let (saturation, lightness, alpha) = if is_legacy_syntax {
let saturation = color_parser
@@ -260,29 +278,28 @@ where
)
};
- Ok(P::Output::from_hsl(hue, saturation, lightness, alpha))
+ Ok(ColorFunction::Hsl(hue, saturation, lightness, alpha))
}
/// Parses hwb syntax.
///
/// <https://drafts.csswg.org/css-color/#the-hbw-notation>
#[inline]
-fn parse_hwb<'i, 't, P>(
- color_parser: &P,
+fn parse_hwb<'i, 't>(
+ color_parser: &ColorParser<'_, '_>,
arguments: &mut Parser<'i, 't>,
-) -> Result<P::Output, ParseError<'i>>
-where
- P: ColorParser<'i>,
-{
+) -> Result<ColorFunction, ParseError<'i>> {
+ let _origin_color = parse_origin_color(color_parser, arguments)?;
+
let (hue, whiteness, blackness, alpha) = parse_components(
color_parser,
arguments,
- P::parse_number_or_angle,
- P::parse_number_or_percentage,
- P::parse_number_or_percentage,
+ ColorParser::parse_number_or_angle,
+ ColorParser::parse_number_or_percentage,
+ ColorParser::parse_number_or_percentage,
)?;
- Ok(P::Output::from_hwb(hue, whiteness, blackness, alpha))
+ Ok(ColorFunction::Hwb(hue, whiteness, blackness, alpha))
}
type IntoLabFn<Output> = fn(
@@ -293,20 +310,19 @@ type IntoLabFn<Output> = fn(
) -> Output;
#[inline]
-fn parse_lab_like<'i, 't, P>(
- color_parser: &P,
+fn parse_lab_like<'i, 't>(
+ color_parser: &ColorParser<'_, '_>,
arguments: &mut Parser<'i, 't>,
- into_color: IntoLabFn<P::Output>,
-) -> Result<P::Output, ParseError<'i>>
-where
- P: ColorParser<'i>,
-{
+ into_color: IntoLabFn<ColorFunction>,
+) -> Result<ColorFunction, ParseError<'i>> {
+ let _origin_color = parse_origin_color(color_parser, arguments)?;
+
let (lightness, a, b, alpha) = parse_components(
color_parser,
arguments,
- P::parse_number_or_percentage,
- P::parse_number_or_percentage,
- P::parse_number_or_percentage,
+ ColorParser::parse_number_or_percentage,
+ ColorParser::parse_number_or_percentage,
+ ColorParser::parse_number_or_percentage,
)?;
Ok(into_color(lightness, a, b, alpha))
@@ -320,20 +336,19 @@ type IntoLchFn<Output> = fn(
) -> Output;
#[inline]
-fn parse_lch_like<'i, 't, P>(
- color_parser: &P,
+fn parse_lch_like<'i, 't>(
+ color_parser: &ColorParser<'_, '_>,
arguments: &mut Parser<'i, 't>,
- into_color: IntoLchFn<P::Output>,
-) -> Result<P::Output, ParseError<'i>>
-where
- P: ColorParser<'i>,
-{
+ into_color: IntoLchFn<ColorFunction>,
+) -> Result<ColorFunction, ParseError<'i>> {
+ let _origin_color = parse_origin_color(color_parser, arguments)?;
+
let (lightness, chroma, hue, alpha) = parse_components(
color_parser,
arguments,
- P::parse_number_or_percentage,
- P::parse_number_or_percentage,
- P::parse_number_or_angle,
+ ColorParser::parse_number_or_percentage,
+ ColorParser::parse_number_or_percentage,
+ ColorParser::parse_number_or_angle,
)?;
Ok(into_color(lightness, chroma, hue, alpha))
@@ -341,13 +356,12 @@ where
/// Parse the color() function.
#[inline]
-fn parse_color_with_color_space<'i, 't, P>(
- color_parser: &P,
+fn parse_color_with_color_space<'i, 't>(
+ color_parser: &ColorParser<'_, '_>,
arguments: &mut Parser<'i, 't>,
-) -> Result<P::Output, ParseError<'i>>
-where
- P: ColorParser<'i>,
-{
+) -> Result<ColorFunction, ParseError<'i>> {
+ let _origin_color = parse_origin_color(color_parser, arguments)?;
+
let color_space = {
let location = arguments.current_source_location();
@@ -359,18 +373,12 @@ where
let (c1, c2, c3, alpha) = parse_components(
color_parser,
arguments,
- P::parse_number_or_percentage,
- P::parse_number_or_percentage,
- P::parse_number_or_percentage,
+ ColorParser::parse_number_or_percentage,
+ ColorParser::parse_number_or_percentage,
+ ColorParser::parse_number_or_percentage,
)?;
- Ok(P::Output::from_color_function(
- color_space,
- c1,
- c2,
- c3,
- alpha,
- ))
+ Ok(ColorFunction::Color(color_space, c1, c2, c3, alpha))
}
type ComponentParseResult<'i, R1, R2, R3> = Result<
@@ -384,18 +392,29 @@ type ComponentParseResult<'i, R1, R2, R3> = Result<
>;
/// Parse the color components and alpha with the modern [color-4] syntax.
-pub fn parse_components<'i, 't, P, F1, F2, F3, R1, R2, R3>(
- color_parser: &P,
+pub fn parse_components<'a, 'b: 'a, 'i, 't, F1, F2, F3, R1, R2, R3>(
+ color_parser: &ColorParser<'a, 'b>,
input: &mut Parser<'i, 't>,
f1: F1,
f2: F2,
f3: F3,
) -> ComponentParseResult<'i, R1, R2, R3>
where
- P: ColorParser<'i>,
- F1: FnOnce(&P, &mut Parser<'i, 't>, bool) -> Result<ColorComponent<R1>, ParseError<'i>>,
- F2: FnOnce(&P, &mut Parser<'i, 't>, bool) -> Result<ColorComponent<R2>, ParseError<'i>>,
- F3: FnOnce(&P, &mut Parser<'i, 't>, bool) -> Result<ColorComponent<R3>, ParseError<'i>>,
+ F1: FnOnce(
+ &ColorParser<'a, 'b>,
+ &mut Parser<'i, 't>,
+ bool,
+ ) -> Result<ColorComponent<R1>, ParseError<'i>>,
+ F2: FnOnce(
+ &ColorParser<'a, 'b>,
+ &mut Parser<'i, 't>,
+ bool,
+ ) -> Result<ColorComponent<R2>, ParseError<'i>>,
+ F3: FnOnce(
+ &ColorParser<'a, 'b>,
+ &mut Parser<'i, 't>,
+ bool,
+ ) -> Result<ColorComponent<R3>, ParseError<'i>>,
{
let r1 = f1(color_parser, input, true)?;
let r2 = f2(color_parser, input, true)?;
@@ -407,6 +426,7 @@ where
}
/// Either a number or a percentage.
+#[derive(Clone, Copy, Debug)]
pub enum NumberOrPercentage {
/// `<number>`.
Number {
@@ -432,7 +452,32 @@ impl NumberOrPercentage {
}
}
+impl ColorComponentType for NumberOrPercentage {
+ fn units() -> CalcUnits {
+ CalcUnits::PERCENTAGE
+ }
+
+ fn try_from_token(token: &Token) -> Result<Self, ()> {
+ Ok(match *token {
+ Token::Number { value, .. } => Self::Number { value },
+ Token::Percentage { unit_value, .. } => Self::Percentage { unit_value },
+ _ => {
+ return Err(());
+ },
+ })
+ }
+
+ fn try_from_leaf(leaf: &SpecifiedLeaf) -> Result<Self, ()> {
+ Ok(match *leaf {
+ SpecifiedLeaf::Percentage(unit_value) => Self::Percentage { unit_value },
+ SpecifiedLeaf::Number(value) => Self::Number { value },
+ _ => return Err(()),
+ })
+ }
+}
+
/// Either an angle or a number.
+#[derive(Clone, Copy, Debug)]
pub enum NumberOrAngle {
/// `<number>`.
Number {
@@ -457,45 +502,118 @@ impl NumberOrAngle {
}
}
-/// A trait that can be used to hook into how `cssparser` parses color
-/// components, with the intention of implementing more complicated behavior.
-///
-/// For example, this is used by Servo to support calc() in color.
-pub trait ColorParser<'i> {
- /// The type that the parser will construct on a successful parse.
- type Output: FromParsedColor;
-
- /// Parse an `<angle>` or `<number>`.
- ///
- /// Returns the result in degrees.
- fn parse_number_or_angle<'t>(
+impl ColorComponentType for NumberOrAngle {
+ fn units() -> CalcUnits {
+ CalcUnits::ANGLE
+ }
+
+ fn try_from_token(token: &Token) -> Result<Self, ()> {
+ Ok(match *token {
+ Token::Number { value, .. } => Self::Number { value },
+ Token::Dimension {
+ value, ref unit, ..
+ } => {
+ let degrees =
+ SpecifiedAngle::parse_dimension(value, unit, /* from_calc = */ false)
+ .map(|angle| angle.degrees())?;
+
+ NumberOrAngle::Angle { degrees }
+ },
+ _ => {
+ return Err(());
+ },
+ })
+ }
+
+ fn try_from_leaf(leaf: &SpecifiedLeaf) -> Result<Self, ()> {
+ Ok(match *leaf {
+ SpecifiedLeaf::Angle(angle) => Self::Angle {
+ degrees: angle.degrees(),
+ },
+ SpecifiedLeaf::Number(value) => Self::Number { value },
+ _ => return Err(()),
+ })
+ }
+}
+
+/// The raw f32 here is for <number>.
+impl ColorComponentType for f32 {
+ fn units() -> CalcUnits {
+ CalcUnits::empty()
+ }
+
+ fn try_from_token(token: &Token) -> Result<Self, ()> {
+ if let Token::Number { value, .. } = *token {
+ Ok(value)
+ } else {
+ Err(())
+ }
+ }
+
+ fn try_from_leaf(leaf: &SpecifiedLeaf) -> Result<Self, ()> {
+ if let SpecifiedLeaf::Number(value) = *leaf {
+ Ok(value)
+ } else {
+ Err(())
+ }
+ }
+}
+
+/// Used to parse the components of a color.
+pub struct ColorParser<'a, 'b: 'a> {
+ /// Parser context used for parsing the colors.
+ pub context: &'a ParserContext<'b>,
+}
+
+impl<'a, 'b: 'a> ColorParser<'a, 'b> {
+ /// Parse an `<number>` or `<angle>` value.
+ fn parse_number_or_angle<'i, 't>(
&self,
input: &mut Parser<'i, 't>,
allow_none: bool,
- ) -> Result<ColorComponent<NumberOrAngle>, ParseError<'i>>;
+ ) -> Result<ColorComponent<NumberOrAngle>, ParseError<'i>> {
+ ColorComponent::parse(self.context, input, allow_none)
+ }
/// Parse a `<percentage>` value.
- ///
- /// Returns the result in a number from 0.0 to 1.0.
- fn parse_percentage<'t>(
+ fn parse_percentage<'i, 't>(
&self,
input: &mut Parser<'i, 't>,
allow_none: bool,
- ) -> Result<ColorComponent<f32>, ParseError<'i>>;
+ ) -> Result<ColorComponent<f32>, ParseError<'i>> {
+ let location = input.current_source_location();
+
+ // We can use the [NumberOrPercentage] type here, because parsing it
+ // doesn't have any more overhead than just parsing a percentage on its
+ // own.
+ Ok(
+ match ColorComponent::<NumberOrPercentage>::parse(self.context, input, allow_none)? {
+ ColorComponent::None => ColorComponent::None,
+ ColorComponent::Value(NumberOrPercentage::Percentage { unit_value }) => {
+ ColorComponent::Value(unit_value)
+ },
+ _ => return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
+ },
+ )
+ }
/// Parse a `<number>` value.
- fn parse_number<'t>(
+ fn parse_number<'i, 't>(
&self,
input: &mut Parser<'i, 't>,
allow_none: bool,
- ) -> Result<ColorComponent<f32>, ParseError<'i>>;
+ ) -> Result<ColorComponent<f32>, ParseError<'i>> {
+ ColorComponent::parse(self.context, input, allow_none)
+ }
- /// Parse a `<number>` value or a `<percentage>` value.
- fn parse_number_or_percentage<'t>(
+ /// Parse a `<number>` or `<percentage>` value.
+ fn parse_number_or_percentage<'i, 't>(
&self,
input: &mut Parser<'i, 't>,
allow_none: bool,
- ) -> Result<ColorComponent<NumberOrPercentage>, ParseError<'i>>;
+ ) -> Result<ColorComponent<NumberOrPercentage>, ParseError<'i>> {
+ ColorComponent::parse(self.context, input, allow_none)
+ }
}
/// This trait is used by the [`ColorParser`] to construct colors of any type.
diff --git a/servo/components/style/context.rs b/servo/components/style/context.rs
index a2c020475b..8229721006 100644
--- a/servo/components/style/context.rs
+++ b/servo/components/style/context.rs
@@ -41,7 +41,6 @@ use style_traits::CSSPixel;
use style_traits::DevicePixel;
#[cfg(feature = "servo")]
use style_traits::SpeculativePainter;
-use time;
pub use selectors::matching::QuirksMode;
diff --git a/servo/components/style/custom_properties.rs b/servo/components/style/custom_properties.rs
index b6523dd489..766fe530d9 100644
--- a/servo/components/style/custom_properties.rs
+++ b/servo/components/style/custom_properties.rs
@@ -125,7 +125,12 @@ macro_rules! lnf_int_variable {
}};
}
-static CHROME_ENVIRONMENT_VARIABLES: [EnvironmentVariable; 8] = [
+static CHROME_ENVIRONMENT_VARIABLES: [EnvironmentVariable; 9] = [
+ lnf_int_variable!(
+ atom!("-moz-mac-titlebar-height"),
+ MacTitlebarHeight,
+ int_pixels
+ ),
lnf_int_variable!(
atom!("-moz-gtk-csd-titlebar-button-spacing"),
TitlebarButtonSpacing,
diff --git a/servo/components/style/dom.rs b/servo/components/style/dom.rs
index 358d788845..ec99a796c1 100644
--- a/servo/components/style/dom.rs
+++ b/servo/components/style/dom.rs
@@ -14,12 +14,12 @@ use crate::context::{PostAnimationTasks, UpdateAnimationsTasks};
use crate::data::ElementData;
use crate::media_queries::Device;
use crate::properties::{AnimationDeclarations, ComputedValues, PropertyDeclarationBlock};
-use crate::selector_parser::{AttrValue, CustomState, Lang, PseudoElement, SelectorImpl};
+use crate::selector_parser::{AttrValue, Lang, PseudoElement, SelectorImpl};
use crate::shared_lock::{Locked, SharedRwLock};
use crate::stylist::CascadeData;
use crate::values::computed::Display;
use crate::values::AtomIdent;
-use crate::WeakAtom;
+use crate::{LocalName, WeakAtom};
use atomic_refcell::{AtomicRef, AtomicRefMut};
use dom::ElementState;
use selectors::matching::{ElementSelectorFlags, QuirksMode, VisitedHandlingMode};
@@ -511,11 +511,6 @@ pub trait TElement:
/// Get this element's state, for non-tree-structural pseudos.
fn state(&self) -> ElementState;
- /// Returns whether this element's CustomStateSet contains a given state.
- fn has_custom_state(&self, _state: &CustomState) -> bool {
- false
- }
-
/// Returns whether this element has a `part` attribute.
fn has_part_attr(&self) -> bool;
@@ -530,6 +525,11 @@ pub trait TElement:
where
F: FnMut(&AtomIdent);
+ /// Internal iterator for the classes of this element.
+ fn each_custom_state<F>(&self, callback: F)
+ where
+ F: FnMut(&AtomIdent);
+
/// Internal iterator for the part names of this element.
fn each_part<F>(&self, _callback: F)
where
@@ -540,7 +540,7 @@ pub trait TElement:
/// Internal iterator for the attribute names of this element.
fn each_attr_name<F>(&self, callback: F)
where
- F: FnMut(&AtomIdent);
+ F: FnMut(&LocalName);
/// Internal iterator for the part names that this element exports for a
/// given part name.
@@ -906,7 +906,7 @@ pub trait TElement:
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool;
/// Returns the search direction for relative selector invalidation, if it is on the search path.
- fn relative_selector_search_direction(&self) -> Option<ElementSelectorFlags>;
+ fn relative_selector_search_direction(&self) -> ElementSelectorFlags;
}
/// TNode and TElement aren't Send because we want to be careful and explicit
diff --git a/servo/components/style/dom_apis.rs b/servo/components/style/dom_apis.rs
index cdc106e1ad..34ea73e5e6 100644
--- a/servo/components/style/dom_apis.rs
+++ b/servo/components/style/dom_apis.rs
@@ -368,7 +368,7 @@ fn collect_elements_with_id<E, Q, F>(
}
}
-fn has_attr<E>(element: E, local_name: &AtomIdent) -> bool
+fn has_attr<E>(element: E, local_name: &crate::LocalName) -> bool
where
E: TElement,
{
@@ -396,7 +396,7 @@ where
element.local_name() == &**chosen_name
}
-fn get_attr_name(component: &Component<SelectorImpl>) -> Option<&AtomIdent> {
+fn get_attr_name(component: &Component<SelectorImpl>) -> Option<&crate::LocalName> {
let (name, name_lower) = match component {
Component::AttributeInNoNamespace { ref local_name, .. } => return Some(local_name),
Component::AttributeInNoNamespaceExists {
@@ -512,7 +512,7 @@ where
enum SimpleFilter<'a> {
Class(&'a AtomIdent),
- Attr(&'a AtomIdent),
+ Attr(&'a crate::LocalName),
LocalName(&'a LocalName<SelectorImpl>),
}
diff --git a/servo/components/style/driver.rs b/servo/components/style/driver.rs
index a2407cb209..59f401895a 100644
--- a/servo/components/style/driver.rs
+++ b/servo/components/style/driver.rs
@@ -13,9 +13,7 @@ use crate::dom::{SendNode, TElement, TNode};
use crate::parallel;
use crate::scoped_tls::ScopedTLS;
use crate::traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};
-use rayon;
use std::collections::VecDeque;
-use time;
#[cfg(feature = "servo")]
fn should_report_statistics() -> bool {
@@ -36,14 +34,17 @@ fn report_statistics(_stats: &PerThreadTraversalStatistics) {
fn report_statistics(stats: &PerThreadTraversalStatistics) {
// This should only be called in the main thread, or it may be racy
// to update the statistics in a global variable.
- debug_assert!(unsafe { crate::gecko_bindings::bindings::Gecko_IsMainThread() });
- let gecko_stats =
- unsafe { &mut crate::gecko_bindings::structs::ServoTraversalStatistics_sSingleton };
- gecko_stats.mElementsTraversed += stats.elements_traversed;
- gecko_stats.mElementsStyled += stats.elements_styled;
- gecko_stats.mElementsMatched += stats.elements_matched;
- gecko_stats.mStylesShared += stats.styles_shared;
- gecko_stats.mStylesReused += stats.styles_reused;
+ unsafe {
+ debug_assert!(crate::gecko_bindings::bindings::Gecko_IsMainThread());
+ let gecko_stats = std::ptr::addr_of_mut!(
+ crate::gecko_bindings::structs::ServoTraversalStatistics_sSingleton
+ );
+ (*gecko_stats).mElementsTraversed += stats.elements_traversed;
+ (*gecko_stats).mElementsStyled += stats.elements_styled;
+ (*gecko_stats).mElementsMatched += stats.elements_matched;
+ (*gecko_stats).mStylesShared += stats.styles_shared;
+ (*gecko_stats).mStylesReused += stats.styles_reused;
+ }
}
fn with_pool_in_place_scope<'scope>(
diff --git a/servo/components/style/gecko/arc_types.rs b/servo/components/style/gecko/arc_types.rs
index 24bf22d69a..420b86d332 100644
--- a/servo/components/style/gecko/arc_types.rs
+++ b/servo/components/style/gecko/arc_types.rs
@@ -16,7 +16,8 @@ use crate::stylesheets::keyframes_rule::Keyframe;
use crate::stylesheets::{
ContainerRule, CounterStyleRule, CssRules, DocumentRule, FontFaceRule, FontFeatureValuesRule,
FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule,
- MediaRule, NamespaceRule, PageRule, PropertyRule, StyleRule, StylesheetContents, SupportsRule,
+ MediaRule, NamespaceRule, PageRule, PropertyRule, ScopeRule, StartingStyleRule, StyleRule,
+ StylesheetContents, SupportsRule,
};
use servo_arc::Arc;
@@ -169,3 +170,13 @@ impl_simple_arc_ffi!(
Servo_AnimationValue_AddRef,
Servo_AnimationValue_Release
);
+impl_simple_arc_ffi!(
+ ScopeRule,
+ Servo_ScopeRule_AddRef,
+ Servo_ScopeRule_Release
+);
+impl_simple_arc_ffi!(
+ StartingStyleRule,
+ Servo_StartingStyleRule_AddRef,
+ Servo_StartingStyleRule_Release
+);
diff --git a/servo/components/style/gecko/media_features.rs b/servo/components/style/gecko/media_features.rs
index 8de45d95c2..df1c5e464b 100644
--- a/servo/components/style/gecko/media_features.rs
+++ b/servo/components/style/gecko/media_features.rs
@@ -8,13 +8,14 @@ use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs;
use crate::gecko_bindings::structs::ScreenColorGamut;
use crate::media_queries::{Device, MediaType};
-use crate::queries::condition::KleeneValue;
+use crate::parser::ParserContext;
use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription};
use crate::queries::values::Orientation;
use crate::values::computed::{CSSPixelLength, Context, Ratio, Resolution};
use crate::values::AtomString;
use app_units::Au;
use euclid::default::Size2D;
+use selectors::kleene_value::KleeneValue;
fn device_size(device: &Device) -> Size2D<Au> {
let mut width = 0;
@@ -286,16 +287,26 @@ fn eval_prefers_contrast(context: &Context, query_value: Option<PrefersContrast>
pub enum ForcedColors {
/// Page colors are not being forced.
None,
+ /// Page colors would be forced in content.
+ #[parse(condition = "ParserContext::chrome_rules_enabled")]
+ Requested,
/// Page colors are being forced.
Active,
}
+impl ForcedColors {
+ /// Returns whether forced-colors is active for this page.
+ pub fn is_active(self) -> bool {
+ matches!(self, Self::Active)
+ }
+}
+
/// https://drafts.csswg.org/mediaqueries-5/#forced-colors
fn eval_forced_colors(context: &Context, query_value: Option<ForcedColors>) -> bool {
- let forced = !context.device().use_document_colors();
+ let forced = context.device().forced_colors();
match query_value {
- Some(query_value) => forced == (query_value == ForcedColors::Active),
- None => forced,
+ Some(query_value) => query_value == forced,
+ None => forced != ForcedColors::None,
}
}
diff --git a/servo/components/style/gecko/media_queries.rs b/servo/components/style/gecko/media_queries.rs
index ef156ab380..ded66027f2 100644
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -29,6 +29,8 @@ use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
use std::{cmp, fmt};
use style_traits::{CSSPixel, DevicePixel};
+use super::media_features::ForcedColors;
+
/// The `Device` in Gecko wraps a pres context, has a default values computed,
/// and contains all the viewport rule state.
pub struct Device {
@@ -172,10 +174,9 @@ impl Device {
Length::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed)))
}
- /// Set the font size of the root element (for rem)
- pub fn set_root_font_size(&self, size: Length) {
- self.root_font_size
- .store(size.px().to_bits(), Ordering::Relaxed)
+ /// Set the font size of the root element (for rem), in zoom-independent CSS pixels.
+ pub fn set_root_font_size(&self, size: f32) {
+ self.root_font_size.store(size.to_bits(), Ordering::Relaxed)
}
/// Get the line height of the root element (for rlh)
@@ -186,10 +187,9 @@ impl Device {
))
}
- /// Set the line height of the root element (for rlh)
- pub fn set_root_line_height(&self, size: Length) {
- self.root_line_height
- .store(size.px().to_bits(), Ordering::Relaxed);
+ /// Set the line height of the root element (for rlh), in zoom-independent CSS pixels.
+ pub fn set_root_line_height(&self, size: f32) {
+ self.root_line_height.store(size.to_bits(), Ordering::Relaxed);
}
/// The quirks mode of the document.
@@ -498,12 +498,27 @@ impl Device {
/// Returns whether document colors are enabled.
#[inline]
- pub fn use_document_colors(&self) -> bool {
- let doc = self.document();
- if doc.mIsBeingUsedAsImage() {
- return true;
+ pub fn forced_colors(&self) -> ForcedColors {
+ if self.document().mIsBeingUsedAsImage() {
+ // SVG images never force colors.
+ return ForcedColors::None
+ }
+ let prefs = self.pref_sheet_prefs();
+ if !prefs.mUseDocumentColors {
+ return ForcedColors::Active
+ }
+ // On Windows, having a high contrast theme also means that the OS is requesting the
+ // colors to be forced. This is mostly convenience for the front-end, which wants to
+ // reuse the forced-colors styles for chrome in this case as well, and it's a lot
+ // more convenient to use `(forced-colors)` than
+ // `(forced-colors) or ((-moz-platform: windows) and (prefers-contrast))`.
+ //
+ // TODO(emilio): We might want to factor in here the lwtheme attribute in the root element
+ // and so on.
+ if cfg!(target_os = "windows") && prefs.mUseAccessibilityTheme && prefs.mIsChrome {
+ return ForcedColors::Requested;
}
- self.pref_sheet_prefs().mUseDocumentColors
+ ForcedColors::None
}
/// Computes a system color and returns it as an nscolor.
diff --git a/servo/components/style/gecko/non_ts_pseudo_class_list.rs b/servo/components/style/gecko/non_ts_pseudo_class_list.rs
index cc7495dd9c..f628c77ad5 100644
--- a/servo/components/style/gecko/non_ts_pseudo_class_list.rs
+++ b/servo/components/style/gecko/non_ts_pseudo_class_list.rs
@@ -98,7 +98,6 @@ macro_rules! apply_non_ts_list {
// media query results are more expensive than document state changes. So for now
// making them pseudo-classes is probably the right trade-off.
("-moz-is-html", MozIsHTML, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
- ("-moz-lwtheme", MozLWTheme, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("-moz-window-inactive", MozWindowInactive, _, _),
]
}
diff --git a/servo/components/style/gecko/pseudo_element.rs b/servo/components/style/gecko/pseudo_element.rs
index 3bcd873455..f0e79a8acd 100644
--- a/servo/components/style/gecko/pseudo_element.rs
+++ b/servo/components/style/gecko/pseudo_element.rs
@@ -159,6 +159,11 @@ impl PseudoElement {
matches!(*self, Self::Highlight(_))
}
+ /// Whether this pseudo-element is the ::target-text pseudo.
+ #[inline]
+ pub fn is_target_text(&self) -> bool {
+ *self == PseudoElement::TargetText
+ }
/// Whether this pseudo-element supports user action selectors.
pub fn supports_user_action_state(&self) -> bool {
(self.flags() & structs::CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) != 0
@@ -168,6 +173,7 @@ impl PseudoElement {
pub fn enabled_in_content(&self) -> bool {
match *self {
Self::Highlight(..) => pref!("dom.customHighlightAPI.enabled"),
+ Self::TargetText => pref!("dom.text_fragments.enabled"),
Self::SliderFill | Self::SliderTrack | Self::SliderThumb => {
pref!("layout.css.modern-range-pseudos.enabled")
},
diff --git a/servo/components/style/gecko/selector_parser.rs b/servo/components/style/gecko/selector_parser.rs
index 203e6a3609..100bb45e81 100644
--- a/servo/components/style/gecko/selector_parser.rs
+++ b/servo/components/style/gecko/selector_parser.rs
@@ -196,7 +196,6 @@ impl NonTSPseudoClass {
None => DocumentState::empty(),
},
NonTSPseudoClass::MozWindowInactive => DocumentState::WINDOW_INACTIVE,
- NonTSPseudoClass::MozLWTheme => DocumentState::LWTHEME,
_ => DocumentState::empty(),
}
}
@@ -214,11 +213,10 @@ impl NonTSPseudoClass {
NonTSPseudoClass::MozNativeAnonymous |
// :-moz-placeholder is parsed but never matches.
NonTSPseudoClass::MozPlaceholder |
- // :-moz-is-html, :-moz-lwtheme, :-moz-locale-dir and :-moz-window-inactive
+ // :-moz-is-html, :-moz-locale-dir and :-moz-window-inactive
// depend only on the state of the document, which is invariant across all
// elements involved in a given style cache.
NonTSPseudoClass::MozIsHTML |
- NonTSPseudoClass::MozLWTheme |
NonTSPseudoClass::MozLocaleDir(_) |
NonTSPseudoClass::MozWindowInactive
)
diff --git a/servo/components/style/gecko/snapshot.rs b/servo/components/style/gecko/snapshot.rs
index 2ff04406ac..8f6eb120d5 100644
--- a/servo/components/style/gecko/snapshot.rs
+++ b/servo/components/style/gecko/snapshot.rs
@@ -171,4 +171,33 @@ impl ElementSnapshot for GeckoElementSnapshot {
Some(AtomString(unsafe { Atom::from_addrefed(ptr) }))
}
}
+
+ /// Returns true if the snapshot has stored state for custom states
+ #[inline]
+ fn has_custom_states(&self) -> bool {
+ self.has_any(Flags::CustomState)
+ }
+
+ /// Returns true if the snapshot has a given CustomState
+ #[inline]
+ fn has_custom_state(&self, state: &AtomIdent) -> bool {
+ unsafe {
+ self.mCustomStates.iter().any(|setstate| {
+ AtomIdent::with(setstate.mRawPtr, |setstate| state == setstate)
+ })
+ }
+ }
+
+ #[inline]
+ fn each_custom_state<F>(&self, mut callback: F)
+ where
+ F: FnMut(&AtomIdent),
+ {
+ unsafe {
+ for atom in self.mCustomStates.iter() {
+ AtomIdent::with(atom.mRawPtr, &mut callback)
+ }
+ }
+ }
+
}
diff --git a/servo/components/style/gecko/url.rs b/servo/components/style/gecko/url.rs
index 7fe32acc20..fa8d22adb6 100644
--- a/servo/components/style/gecko/url.rs
+++ b/servo/components/style/gecko/url.rs
@@ -18,7 +18,7 @@ use std::fmt::{self, Write};
use std::mem::ManuallyDrop;
use std::sync::RwLock;
use style_traits::{CssWriter, ParseError, ToCss};
-use to_shmem::{self, SharedMemoryBuilder, ToShmem};
+use to_shmem::{SharedMemoryBuilder, ToShmem};
/// A CSS url() value for gecko.
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
diff --git a/servo/components/style/gecko/wrapper.rs b/servo/components/style/gecko/wrapper.rs
index 43cf6e7941..eab968149c 100644
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -19,7 +19,7 @@ use crate::bloom::each_relevant_element_hash;
use crate::context::{PostAnimationTasks, QuirksMode, SharedStyleContext, UpdateAnimationsTasks};
use crate::data::ElementData;
use crate::dom::{LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode, TShadowRoot};
-use crate::gecko::selector_parser::{CustomState, NonTSPseudoClass, PseudoElement, SelectorImpl};
+use crate::gecko::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl};
use crate::gecko::snapshot_helpers;
use crate::gecko_bindings::bindings;
use crate::gecko_bindings::bindings::Gecko_ElementHasAnimations;
@@ -889,7 +889,11 @@ impl<'le> GeckoElement<'le> {
AnimationValue::from_computed_values(property_declaration_id, before_change_style);
let to = AnimationValue::from_computed_values(property_declaration_id, after_change_style);
- debug_assert_eq!(to.is_some(), from.is_some());
+ // If the declaration contains a custom property and getComputedValue was previously called
+ // before that custom property was defined, `from` will be `None` here.
+ debug_assert!(
+ to.is_some() == from.is_some() || matches!(to, Some(AnimationValue::Custom(..)))
+ );
from != to
}
@@ -1237,20 +1241,6 @@ impl<'le> TElement for GeckoElement<'le> {
}
#[inline]
- fn has_custom_state(&self, state: &CustomState) -> bool {
- if !self.is_html_element() {
- return false;
- }
- let check_state_ptr: *const nsAtom = state.0.as_ptr();
- self.extended_slots().map_or(false, |slot| {
- (&slot.mCustomStates).iter().any(|setstate| {
- let setstate_ptr: *const nsAtom = setstate.mRawPtr;
- setstate_ptr == check_state_ptr
- })
- })
- }
-
- #[inline]
fn has_part_attr(&self) -> bool {
self.as_node()
.get_bool_flag(nsINode_BooleanFlag::ElementHasPart)
@@ -1292,6 +1282,20 @@ impl<'le> TElement for GeckoElement<'le> {
}
#[inline]
+ fn each_custom_state<F>(&self, mut callback: F)
+ where
+ F: FnMut(&AtomIdent),
+ {
+ if let Some(slots) = self.extended_slots() {
+ unsafe {
+ for atom in slots.mCustomStates.iter() {
+ AtomIdent::with(atom.mRawPtr, &mut callback)
+ }
+ }
+ }
+ }
+
+ #[inline]
fn each_exported_part<F>(&self, name: &AtomIdent, callback: F)
where
F: FnMut(&AtomIdent),
@@ -1551,14 +1555,13 @@ impl<'le> TElement for GeckoElement<'le> {
let mut transitions_to_keep = PropertyDeclarationIdSet::default();
for transition_property in after_change_style.transition_properties() {
- let physical_longhand = PropertyDeclarationId::Longhand(
- transition_property
- .longhand_id
- .to_physical(after_change_style.writing_mode),
- );
- transitions_to_keep.insert(physical_longhand);
+ let physical_property = transition_property
+ .property
+ .as_borrowed()
+ .to_physical(after_change_style.writing_mode);
+ transitions_to_keep.insert(physical_property);
if self.needs_transitions_update_per_property(
- physical_longhand,
+ physical_property,
after_change_ui_style
.transition_combined_duration_at(transition_property.index)
.seconds(),
@@ -1791,17 +1794,17 @@ impl<'le> TElement for GeckoElement<'le> {
self.as_node().selector_flags() & node_flags == node_flags
}
- fn relative_selector_search_direction(&self) -> Option<ElementSelectorFlags> {
+ fn relative_selector_search_direction(&self) -> ElementSelectorFlags {
use crate::gecko_bindings::structs::NodeSelectorFlags;
let flags = self.as_node().selector_flags();
if (flags & NodeSelectorFlags::RelativeSelectorSearchDirectionAncestorSibling.0) != 0 {
- Some(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING)
+ ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING
} else if (flags & NodeSelectorFlags::RelativeSelectorSearchDirectionAncestor.0) != 0 {
- Some(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR)
+ ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR
} else if (flags & NodeSelectorFlags::RelativeSelectorSearchDirectionSibling.0) != 0 {
- Some(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING)
+ ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_SIBLING
} else {
- None
+ ElementSelectorFlags::empty()
}
}
}
@@ -2042,15 +2045,14 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
NonTSPseudoClass::MozAutofillPreview |
NonTSPseudoClass::MozRevealed |
NonTSPseudoClass::MozValueEmpty => self.state().intersects(pseudo_class.state_flag()),
- // TODO: This applying only to HTML elements is weird.
NonTSPseudoClass::Dir(ref dir) => {
- self.is_html_element() && self.state().intersects(dir.element_state())
+ self.state().intersects(dir.element_state())
},
NonTSPseudoClass::AnyLink => self.is_link(),
NonTSPseudoClass::Link => {
self.is_link() && context.visited_handling().matches_unvisited()
},
- NonTSPseudoClass::CustomState(ref state) => self.has_custom_state(state),
+ NonTSPseudoClass::CustomState(ref state) => self.has_custom_state(&state.0),
NonTSPseudoClass::Visited => {
self.is_link() && context.visited_handling().matches_visited()
},
@@ -2101,7 +2103,6 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
bindings::Gecko_IsSelectListBox(self.0)
},
NonTSPseudoClass::MozIsHTML => self.as_node().owner_doc().is_html_document(),
- NonTSPseudoClass::MozLWTheme |
NonTSPseudoClass::MozLocaleDir(..) |
NonTSPseudoClass::MozWindowInactive => {
let state_bit = pseudo_class.document_state_flag();
@@ -2186,6 +2187,20 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
}
#[inline]
+ fn has_custom_state(&self, state: &AtomIdent) -> bool {
+ if !self.is_html_element() {
+ return false;
+ }
+ let check_state_ptr: *const nsAtom = state.as_ptr();
+ self.extended_slots().map_or(false, |slot| {
+ (&slot.mCustomStates).iter().any(|setstate| {
+ let setstate_ptr: *const nsAtom = setstate.mRawPtr;
+ setstate_ptr == check_state_ptr
+ })
+ })
+ }
+
+ #[inline]
fn is_html_element_in_html_document(&self) -> bool {
self.is_html_element() && self.as_node().owner_doc().is_html_document()
}
diff --git a/servo/components/style/gecko_bindings/sugar/ns_style_auto_array.rs b/servo/components/style/gecko_bindings/sugar/ns_style_auto_array.rs
index b5772a6c77..350d578001 100644
--- a/servo/components/style/gecko_bindings/sugar/ns_style_auto_array.rs
+++ b/servo/components/style/gecko_bindings/sugar/ns_style_auto_array.rs
@@ -11,7 +11,7 @@ use crate::gecko_bindings::bindings::Gecko_EnsureStyleViewTimelineArrayLength;
use crate::gecko_bindings::structs::nsStyleAutoArray;
use crate::gecko_bindings::structs::{StyleAnimation, StyleTransition};
use crate::gecko_bindings::structs::{StyleScrollTimeline, StyleViewTimeline};
-use std::iter::{once, Chain, IntoIterator, Once};
+use std::iter::{once, Chain, Once};
use std::ops::{Index, IndexMut};
use std::slice::{Iter, IterMut};
diff --git a/servo/components/style/gecko_string_cache/mod.rs b/servo/components/style/gecko_string_cache/mod.rs
index 79a5d46525..3640b61d11 100644
--- a/servo/components/style/gecko_string_cache/mod.rs
+++ b/servo/components/style/gecko_string_cache/mod.rs
@@ -28,7 +28,7 @@ use std::num::NonZeroUsize;
use std::ops::Deref;
use std::{slice, str};
use style_traits::SpecifiedValueInfo;
-use to_shmem::{self, SharedMemoryBuilder, ToShmem};
+use to_shmem::{SharedMemoryBuilder, ToShmem};
#[macro_use]
#[allow(improper_ctypes, non_camel_case_types, missing_docs)]
diff --git a/servo/components/style/global_style_data.rs b/servo/components/style/global_style_data.rs
index 38d72b2c74..d6e49e7da6 100644
--- a/servo/components/style/global_style_data.rs
+++ b/servo/components/style/global_style_data.rs
@@ -10,9 +10,7 @@ use crate::gecko_bindings::bindings;
use crate::parallel::STYLE_THREAD_STACK_SIZE_KB;
use crate::shared_lock::SharedRwLock;
use crate::thread_state;
-use gecko_profiler;
use parking_lot::{Mutex, RwLock, RwLockReadGuard};
-use rayon;
#[cfg(unix)]
use std::os::unix::thread::{JoinHandleExt, RawPthread};
#[cfg(windows)]
diff --git a/servo/components/style/invalidation/element/element_wrapper.rs b/servo/components/style/invalidation/element/element_wrapper.rs
index e17afd7774..9a39b7d5a0 100644
--- a/servo/components/style/invalidation/element/element_wrapper.rs
+++ b/servo/components/style/invalidation/element/element_wrapper.rs
@@ -73,6 +73,18 @@ pub trait ElementSnapshot: Sized {
where
F: FnMut(&AtomIdent);
+
+ /// If this snapshot contains CustomStateSet information.
+ fn has_custom_states(&self) -> bool;
+
+ /// A callback that should be called for each CustomState of the snapshot.
+ fn has_custom_state(&self, state: &AtomIdent) -> bool;
+
+ /// A callback that should be called for each CustomState of the snapshot.
+ fn each_custom_state<F>(&self, callback: F)
+ where
+ F: FnMut(&AtomIdent);
+
/// The `xml:lang=""` or `lang=""` attribute value per this snapshot.
fn lang_attr(&self) -> Option<AttrValue>;
}
@@ -213,6 +225,11 @@ where
.match_element_lang(Some(self.get_lang()), lang_arg);
},
+ // CustomStateSet should match against the snapshot before element
+ NonTSPseudoClass::CustomState(ref state) => {
+ return self.has_custom_state(&state.0)
+ },
+
_ => {},
}
@@ -357,6 +374,13 @@ where
}
}
+ fn has_custom_state(&self, state: &AtomIdent) -> bool {
+ match self.snapshot() {
+ Some(snapshot) if snapshot.has_custom_states() => snapshot.has_custom_state(state),
+ _ => self.element.has_custom_state(state),
+ }
+ }
+
fn is_empty(&self) -> bool {
self.element.is_empty()
}
diff --git a/servo/components/style/invalidation/element/invalidation_map.rs b/servo/components/style/invalidation/element/invalidation_map.rs
index cb03862740..f42f542dd2 100644
--- a/servo/components/style/invalidation/element/invalidation_map.rs
+++ b/servo/components/style/invalidation/element/invalidation_map.rs
@@ -10,6 +10,7 @@ use crate::selector_map::{
};
use crate::selector_parser::{NonTSPseudoClass, SelectorImpl};
use crate::AllocErr;
+use crate::values::AtomIdent;
use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded};
use dom::{DocumentState, ElementState};
use selectors::attr::NamespaceConstraint;
@@ -259,6 +260,8 @@ pub type IdOrClassDependencyMap = MaybeCaseInsensitiveHashMap<Atom, SmallVec<[De
pub type StateDependencyMap = SelectorMap<StateDependency>;
/// Dependency mapping for local names.
pub type LocalNameDependencyMap = PrecomputedHashMap<LocalName, SmallVec<[Dependency; 1]>>;
+/// Dependency mapping for customstates
+pub type CustomStateDependencyMap = PrecomputedHashMap<AtomIdent, SmallVec<[Dependency; 1]>>;
/// A map where we store invalidations.
///
@@ -282,6 +285,8 @@ pub struct InvalidationMap {
pub document_state_selectors: Vec<DocumentStateDependency>,
/// A map of other attribute affecting selectors.
pub other_attribute_affecting_selectors: LocalNameDependencyMap,
+ /// A map of CSS custom states
+ pub custom_state_affecting_selectors: CustomStateDependencyMap,
}
/// Tree-structural pseudoclasses that we care about for (Relative selector) invalidation.
@@ -383,6 +388,7 @@ impl InvalidationMap {
state_affecting_selectors: StateDependencyMap::new(),
document_state_selectors: Vec::new(),
other_attribute_affecting_selectors: LocalNameDependencyMap::default(),
+ custom_state_affecting_selectors: CustomStateDependencyMap::default(),
}
}
@@ -398,6 +404,9 @@ impl InvalidationMap {
.fold(0, |accum, (_, ref v)| accum + v.len()) +
self.class_to_selector
.iter()
+ .fold(0, |accum, (_, ref v)| accum + v.len()) +
+ self.custom_state_affecting_selectors
+ .iter()
.fold(0, |accum, (_, ref v)| accum + v.len())
}
@@ -408,6 +417,7 @@ impl InvalidationMap {
self.state_affecting_selectors.clear();
self.document_state_selectors.clear();
self.other_attribute_affecting_selectors.clear();
+ self.custom_state_affecting_selectors.clear();
}
/// Shrink the capacity of hash maps if needed.
@@ -416,6 +426,7 @@ impl InvalidationMap {
self.id_to_selector.shrink_if_needed();
self.state_affecting_selectors.shrink_if_needed();
self.other_attribute_affecting_selectors.shrink_if_needed();
+ self.custom_state_affecting_selectors.shrink_if_needed();
}
}
@@ -489,6 +500,7 @@ trait Collector {
fn class_map(&mut self) -> &mut IdOrClassDependencyMap;
fn state_map(&mut self) -> &mut StateDependencyMap;
fn attribute_map(&mut self) -> &mut LocalNameDependencyMap;
+ fn custom_state_map(&mut self) -> &mut LocalNameDependencyMap;
fn update_states(&mut self, element_state: ElementState, document_state: DocumentState);
// In normal invalidations, type-based dependencies don't need to be explicitly tracked;
@@ -551,6 +563,16 @@ fn add_attr_dependency<C: Collector>(name: LocalName, collector: &mut C) -> Resu
add_local_name(name, dependency, map)
}
+fn add_custom_state_dependency<C: Collector>(name: AtomIdent, collector: &mut C) -> Result<(), AllocErr> {
+ let dependency = collector.dependency();
+ let map = collector.custom_state_map();
+ map.try_reserve(1)?;
+ let vec = map.entry(name).or_default();
+ vec.try_reserve(1)?;
+ vec.push(dependency);
+ Ok(())
+}
+
fn add_local_name(
name: LocalName,
dependency: Dependency,
@@ -576,6 +598,9 @@ fn on_pseudo_class<C: Collector>(pc: &NonTSPseudoClass, collector: &mut C) -> Re
return add_attr_dependency(local_name!("size"), collector);
},
NonTSPseudoClass::Lang(..) => local_name!("lang"),
+ NonTSPseudoClass::CustomState(ref name) => {
+ return add_custom_state_dependency(name.0.clone(), collector);
+ },
_ => return Ok(()),
};
@@ -702,6 +727,10 @@ impl<'a> Collector for SelectorDependencyCollector<'a> {
self.compound_state.element_state |= element_state;
*self.document_state |= document_state;
}
+
+ fn custom_state_map(&mut self) -> &mut CustomStateDependencyMap {
+ &mut self.map.custom_state_affecting_selectors
+ }
}
impl<'a> SelectorDependencyCollector<'a> {
@@ -1074,6 +1103,10 @@ impl<'a> Collector for RelativeSelectorDependencyCollector<'a> {
&mut self.map.map.other_attribute_affecting_selectors
}
+ fn custom_state_map(&mut self) -> &mut CustomStateDependencyMap {
+ &mut self.map.map.custom_state_affecting_selectors
+ }
+
fn update_states(&mut self, element_state: ElementState, document_state: DocumentState) {
self.compound_state.state.element_state |= element_state;
*self.document_state |= document_state;
@@ -1255,6 +1288,10 @@ impl<'a, 'b> Collector for RelativeSelectorInnerDependencyCollector<'a, 'b> {
&mut self.map.map.other_attribute_affecting_selectors
}
+ fn custom_state_map(&mut self) -> &mut CustomStateDependencyMap {
+ &mut self.map.map.custom_state_affecting_selectors
+ }
+
fn update_states(&mut self, element_state: ElementState, document_state: DocumentState) {
self.compound_state.state.element_state |= element_state;
*self.document_state |= document_state;
diff --git a/servo/components/style/invalidation/element/relative_selector.rs b/servo/components/style/invalidation/element/relative_selector.rs
index ccb48e349f..41222304be 100644
--- a/servo/components/style/invalidation/element/relative_selector.rs
+++ b/servo/components/style/invalidation/element/relative_selector.rs
@@ -53,12 +53,12 @@ impl DomMutationOperation {
fn accept<E: TElement>(&self, d: &Dependency, e: E) -> bool {
match self {
Self::Insert | Self::Append | Self::Remove => {
- e.relative_selector_search_direction().is_some()
+ !e.relative_selector_search_direction().is_empty()
},
// `:has(+ .a + .b)` with `.anchor + .a + .remove + .b` - `.a` would be present
// in the search path.
Self::SideEffectPrevSibling => {
- e.relative_selector_search_direction().is_some() &&
+ !e.relative_selector_search_direction().is_empty() &&
d.right_combinator_is_next_sibling()
},
// If an element is being removed and would cause next-sibling match to happen,
@@ -499,6 +499,19 @@ where
},
None => (),
});
+ element.each_custom_state(|v| {
+ match map.map.custom_state_affecting_selectors.get(v) {
+ Some(v) => {
+ for dependency in v {
+ if !operation.accept(dependency, element) {
+ continue;
+ }
+ self.add_dependency(dependency, element, scope);
+ }
+ },
+ None => (),
+ }
+ });
element.each_attr_name(
|v| match map.map.other_attribute_affecting_selectors.get(v) {
Some(v) => {
@@ -782,11 +795,7 @@ where
/// Is this element in the direction of the given relative selector search path?
fn in_search_direction(element: &E, desired: ElementSelectorFlags) -> bool {
- if let Some(direction) = element.relative_selector_search_direction() {
- direction.intersects(desired)
- } else {
- false
- }
+ element.relative_selector_search_direction().intersects(desired)
}
/// Handle a potential relative selector anchor.
@@ -1145,7 +1154,7 @@ where
dep: &'a Dependency,
) {
debug_assert!(dep.parent.is_some(), "Orphaned inners selector?");
- if element.relative_selector_search_direction().is_none() {
+ if element.relative_selector_search_direction().is_empty() {
return;
}
self.invalidations.push((
diff --git a/servo/components/style/invalidation/element/state_and_attributes.rs b/servo/components/style/invalidation/element/state_and_attributes.rs
index 1c58cddf1e..d5f0723d66 100644
--- a/servo/components/style/invalidation/element/state_and_attributes.rs
+++ b/servo/components/style/invalidation/element/state_and_attributes.rs
@@ -19,11 +19,12 @@ use crate::selector_map::SelectorMap;
use crate::selector_parser::Snapshot;
use crate::stylesheets::origin::OriginSet;
use crate::{Atom, WeakAtom};
+use crate::values::AtomIdent;
use dom::ElementState;
use selectors::attr::CaseSensitivity;
+use selectors::kleene_value::KleeneValue;
use selectors::matching::{
- matches_selector, MatchingContext, MatchingForInvalidation, MatchingMode, NeedsSelectorFlags,
- SelectorCaches, VisitedHandlingMode,
+ matches_selector_kleene, MatchingContext, MatchingForInvalidation, MatchingMode, NeedsSelectorFlags, SelectorCaches, VisitedHandlingMode
};
use smallvec::SmallVec;
@@ -41,6 +42,8 @@ where
added_id: Option<&'a WeakAtom>,
classes_removed: &'a SmallVec<[Atom; 8]>,
classes_added: &'a SmallVec<[Atom; 8]>,
+ custom_states_removed: &'a SmallVec<[AtomIdent; 8]>,
+ custom_states_added: &'a SmallVec<[AtomIdent; 8]>,
state_changes: ElementState,
descendant_invalidations: &'a mut DescendantInvalidationLists<'selectors>,
sibling_invalidations: &'a mut InvalidationVector<'selectors>,
@@ -97,23 +100,25 @@ where
E: TElement,
W: selectors::Element<Impl = E::Impl>,
{
- let matches_now = matches_selector(
- &dependency.selector,
- dependency.selector_offset,
- None,
- element,
- context,
- );
+ context.for_invalidation_comparison(|context| {
+ let matches_now = matches_selector_kleene(
+ &dependency.selector,
+ dependency.selector_offset,
+ None,
+ element,
+ context,
+ );
- let matched_then = matches_selector(
- &dependency.selector,
- dependency.selector_offset,
- None,
- wrapper,
- context,
- );
+ let matched_then = matches_selector_kleene(
+ &dependency.selector,
+ dependency.selector_offset,
+ None,
+ wrapper,
+ context,
+ );
- matched_then != matches_now
+ matched_then != matches_now || matches_now == KleeneValue::Unknown
+ })
}
/// Whether we should process the descendants of a given element for style
@@ -243,7 +248,7 @@ where
return false;
};
- if !snapshot.has_attrs() && state_changes.is_empty() {
+ if !snapshot.has_attrs() && !snapshot.has_custom_states() && state_changes.is_empty() {
return false;
}
@@ -264,6 +269,21 @@ where
})
}
+ let mut custom_states_removed = SmallVec::<[AtomIdent; 8]>::new();
+ let mut custom_states_added = SmallVec::<[AtomIdent; 8]>::new();
+ if snapshot.has_custom_states() {
+ snapshot.each_custom_state(|s| {
+ if !element.has_custom_state(s) {
+ custom_states_removed.push(s.clone())
+ }
+ });
+ element.each_custom_state(|s| {
+ if !snapshot.has_custom_state(s) {
+ custom_states_added.push(s.clone())
+ }
+ })
+ }
+
let mut id_removed = None;
let mut id_added = None;
if snapshot.id_changed() {
@@ -326,6 +346,8 @@ where
added_id: id_added,
classes_removed: &classes_removed,
classes_added: &classes_added,
+ custom_states_removed: &custom_states_removed,
+ custom_states_added: &custom_states_added,
descendant_invalidations,
sibling_invalidations,
invalidates_self: false,
@@ -443,6 +465,14 @@ where
}
}
+ for state in self.custom_states_added.iter().chain(self.custom_states_removed.iter()) {
+ if let Some(deps) = map.custom_state_affecting_selectors.get(state) {
+ for dep in deps {
+ self.scan_dependency(dep);
+ }
+ }
+ }
+
self.snapshot.each_attr_changed(|attribute| {
if let Some(deps) = map.other_attribute_affecting_selectors.get(attribute) {
for dep in deps {
diff --git a/servo/components/style/invalidation/stylesheets.rs b/servo/components/style/invalidation/stylesheets.rs
index d845897aa4..48ef8c1b5d 100644
--- a/servo/components/style/invalidation/stylesheets.rs
+++ b/servo/components/style/invalidation/stylesheets.rs
@@ -616,7 +616,7 @@ impl StylesheetInvalidationSet {
return self.invalidate_fully();
},
Document(..) | Import(..) | Media(..) | Supports(..) | Container(..) |
- LayerBlock(..) => {
+ LayerBlock(..) | StartingStyle(..) => {
// Do nothing, relevant nested rules are visited as part of rule iteration.
},
FontFace(..) => {
@@ -646,6 +646,11 @@ impl StylesheetInvalidationSet {
debug!(" > Found unsupported rule, marking the whole subtree invalid.");
self.invalidate_fully();
},
+ Scope(..) => {
+ // Addition/removal of @scope requires re-evaluation of scope proximity to properly
+ // figure out the styling order.
+ self.invalidate_fully();
+ },
}
}
}
diff --git a/servo/components/style/matching.rs b/servo/components/style/matching.rs
index 646d08a9e3..e9d754aa10 100644
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -934,7 +934,8 @@ pub trait MatchMethods: TElement {
if old_font_size != Some(new_font_size) {
if is_root {
debug_assert!(self.owner_doc_matches_for_testing(device));
- device.set_root_font_size(new_font_size.computed_size().into());
+ let size = new_font_size.computed_size();
+ device.set_root_font_size(new_primary_style.effective_zoom.unzoom(size.px()));
if device.used_root_font_size() {
// If the root font-size changed since last time, and something
// in the document did use rem units, ensure we recascade the
@@ -954,7 +955,8 @@ pub trait MatchMethods: TElement {
}
}
- // For line-height, we want the fully resolved value, as `normal` also depends on other font properties.
+ // For line-height, we want the fully resolved value, as `normal` also depends on other
+ // font properties.
let new_line_height = device
.calc_line_height(
&new_primary_style.get_font(),
@@ -968,14 +970,12 @@ pub trait MatchMethods: TElement {
.0
});
- if restyle_requirement != ChildRestyleRequirement::MustMatchDescendants &&
- old_line_height != Some(new_line_height)
- {
+ if old_line_height != Some(new_line_height) {
if is_root {
debug_assert!(self.owner_doc_matches_for_testing(device));
- device.set_root_line_height(new_line_height.into());
+ device.set_root_line_height(new_primary_style.effective_zoom.unzoom(new_line_height.px()));
if device.used_root_line_height() {
- restyle_requirement = ChildRestyleRequirement::MustCascadeDescendants;
+ restyle_requirement = std::cmp::max(restyle_requirement, ChildRestyleRequirement::MustCascadeDescendants);
}
}
diff --git a/servo/components/style/media_queries/media_list.rs b/servo/components/style/media_queries/media_list.rs
index 3c2ba9ee5c..7cbbeb5e3e 100644
--- a/servo/components/style/media_queries/media_list.rs
+++ b/servo/components/style/media_queries/media_list.rs
@@ -10,10 +10,10 @@ use super::{Device, MediaQuery, Qualifier};
use crate::context::QuirksMode;
use crate::error_reporting::ContextualParseError;
use crate::parser::ParserContext;
-use crate::queries::condition::KleeneValue;
use crate::values::computed;
use cssparser::{Delimiter, Parser};
use cssparser::{ParserInput, Token};
+use selectors::kleene_value::KleeneValue;
/// A type that encapsulates a media query list.
#[derive(Clone, MallocSizeOf, ToCss, ToShmem)]
diff --git a/servo/components/style/parallel.rs b/servo/components/style/parallel.rs
index 0b2ccf46d1..0e1c509ab7 100644
--- a/servo/components/style/parallel.rs
+++ b/servo/components/style/parallel.rs
@@ -26,7 +26,6 @@ use crate::context::{StyleContext, ThreadLocalStyleContext};
use crate::dom::{OpaqueNode, SendNode, TElement};
use crate::scoped_tls::ScopedTLS;
use crate::traversal::{DomTraversal, PerLevelTraversalData};
-use rayon;
use std::collections::VecDeque;
/// The minimum stack size for a thread in the styling pool, in kilobytes.
diff --git a/servo/components/style/parser.rs b/servo/components/style/parser.rs
index 893625854f..1e339841a9 100644
--- a/servo/components/style/parser.rs
+++ b/servo/components/style/parser.rs
@@ -9,9 +9,64 @@ use crate::error_reporting::{ContextualParseError, ParseErrorReporter};
use crate::stylesheets::{CssRuleType, CssRuleTypes, Namespaces, Origin, UrlExtraData};
use crate::use_counters::UseCounters;
use cssparser::{Parser, SourceLocation, UnicodeRange};
+use selectors::parser::ParseRelative;
use std::borrow::Cow;
use style_traits::{OneOrMoreSeparated, ParseError, ParsingMode, Separator};
+/// Nesting context for parsing rules.
+#[derive(Clone, Copy)]
+pub struct NestingContext {
+ /// All rule types we've nested into, if any.
+ pub rule_types: CssRuleTypes,
+ /// Whether or not parsing relative selector syntax should be allowed.
+ pub parse_relative: ParseRelative,
+}
+
+impl NestingContext {
+ fn parse_relative_for(rule_type: CssRuleType) -> ParseRelative {
+ match rule_type {
+ CssRuleType::Scope => ParseRelative::ForScope,
+ CssRuleType::Style => ParseRelative::ForNesting,
+ _ => ParseRelative::No,
+ }
+ }
+
+ /// Create a new nesting context.
+ pub fn new(rule_types: CssRuleTypes, parse_nested_rule_type: Option<CssRuleType>) -> Self {
+ Self {
+ rule_types,
+ parse_relative: parse_nested_rule_type
+ .map_or(ParseRelative::No, Self::parse_relative_for)
+ }
+ }
+
+ /// Create a new nesting context based on the given rule.
+ pub fn new_from_rule(rule_type: Option<CssRuleType>) -> Self {
+ Self {
+ rule_types: rule_type.map(CssRuleTypes::from).unwrap_or_default(),
+ parse_relative: rule_type
+ .map(Self::parse_relative_for)
+ .unwrap_or(ParseRelative::No),
+ }
+ }
+
+ /// Save the current nesting context.
+ pub fn save(&mut self, rule_type: CssRuleType) -> Self {
+ let old = *self;
+ self.rule_types.insert(rule_type);
+ let new_parse_relative = Self::parse_relative_for(rule_type);
+ if new_parse_relative != ParseRelative::No {
+ self.parse_relative = new_parse_relative;
+ }
+ old
+ }
+
+ /// Load the saved nesting context.
+ pub fn restore(&mut self, saved: Self) {
+ *self = saved;
+ }
+}
+
/// The data that the parser needs from outside in order to parse a stylesheet.
pub struct ParserContext<'a> {
/// The `Origin` of the stylesheet, whether it's a user, author or
@@ -19,8 +74,6 @@ pub struct ParserContext<'a> {
pub stylesheet_origin: Origin,
/// The extra data we need for resolving url values.
pub url_data: &'a UrlExtraData,
- /// The current rule types, if any.
- pub rule_types: CssRuleTypes,
/// The mode to use when parsing.
pub parsing_mode: ParsingMode,
/// The quirks mode of this stylesheet.
@@ -31,6 +84,8 @@ pub struct ParserContext<'a> {
pub namespaces: Cow<'a, Namespaces>,
/// The use counters we want to record while parsing style rules, if any.
pub use_counters: Option<&'a UseCounters>,
+ /// Current nesting context.
+ pub nesting_context: NestingContext,
}
impl<'a> ParserContext<'a> {
@@ -49,12 +104,12 @@ impl<'a> ParserContext<'a> {
Self {
stylesheet_origin,
url_data,
- rule_types: rule_type.map(CssRuleTypes::from).unwrap_or_default(),
parsing_mode,
quirks_mode,
error_reporter,
namespaces,
use_counters,
+ nesting_context: NestingContext::new_from_rule(rule_type),
}
}
@@ -64,22 +119,21 @@ impl<'a> ParserContext<'a> {
rule_type: CssRuleType,
cb: impl FnOnce(&mut Self) -> R,
) -> R {
- let old_rule_types = self.rule_types;
- self.rule_types.insert(rule_type);
+ let old = self.nesting_context.save(rule_type);
let r = cb(self);
- self.rule_types = old_rule_types;
+ self.nesting_context.restore(old);
r
}
/// Whether we're in a @page rule.
#[inline]
pub fn in_page_rule(&self) -> bool {
- self.rule_types.contains(CssRuleType::Page)
+ self.nesting_context.rule_types.contains(CssRuleType::Page)
}
/// Get the rule type, which assumes that one is available.
pub fn rule_types(&self) -> CssRuleTypes {
- self.rule_types
+ self.nesting_context.rule_types
}
/// Returns whether CSS error reporting is enabled.
diff --git a/servo/components/style/properties/build.py b/servo/components/style/properties/build.py
index 42121a4eae..d1f76d4a25 100644
--- a/servo/components/style/properties/build.py
+++ b/servo/components/style/properties/build.py
@@ -20,36 +20,10 @@ RE_PYTHON_ADDR = re.compile(r"<.+? object at 0x[0-9a-fA-F]+>")
OUT_DIR = os.environ.get("OUT_DIR", "")
-STYLE_STRUCT_LIST = [
- "background",
- "border",
- "box",
- "column",
- "counters",
- "effects",
- "font",
- "inherited_box",
- "inherited_svg",
- "inherited_table",
- "inherited_text",
- "inherited_ui",
- "list",
- "margin",
- "outline",
- "page",
- "padding",
- "position",
- "svg",
- "table",
- "text",
- "ui",
- "xul",
-]
-
def main():
usage = (
- "Usage: %s [ servo-2013 | servo-2020 | gecko ] [ style-crate | geckolib <template> | html ]"
+ "Usage: %s [ servo | gecko ] [ style-crate | geckolib <template> ]"
% sys.argv[0]
)
if len(sys.argv) < 3:
@@ -57,29 +31,15 @@ def main():
engine = sys.argv[1]
output = sys.argv[2]
- if engine not in ["servo-2013", "servo-2020", "gecko"] or output not in [
+ if engine not in ["servo", "gecko"] or output not in [
"style-crate",
"geckolib",
- "html",
]:
abort(usage)
properties = data.PropertiesData(engine=engine)
- files = {}
- for kind in ["longhands", "shorthands"]:
- files[kind] = {}
- for struct in STYLE_STRUCT_LIST:
- file_name = os.path.join(BASE, kind, "{}.mako.rs".format(struct))
- if kind == "shorthands" and not os.path.exists(file_name):
- files[kind][struct] = ""
- continue
- files[kind][struct] = render(
- file_name,
- engine=engine,
- data=properties,
- )
properties_template = os.path.join(BASE, "properties.mako.rs")
- files["properties"] = render(
+ properties_file = render(
properties_template,
engine=engine,
data=properties,
@@ -87,31 +47,15 @@ def main():
OUT_DIR=OUT_DIR,
)
if output == "style-crate":
- write(OUT_DIR, "properties.rs", files["properties"])
- for kind in ["longhands", "shorthands"]:
- for struct in files[kind]:
- write(
- os.path.join(OUT_DIR, kind),
- "{}.rs".format(struct),
- files[kind][struct],
- )
-
- if engine == "gecko":
- template = os.path.join(BASE, "gecko.mako.rs")
- rust = render(template, data=properties)
- write(OUT_DIR, "gecko_properties.rs", rust)
-
- if engine in ["servo-2013", "servo-2020"]:
- if engine == "servo-2013":
- pref_attr = "servo_2013_pref"
- if engine == "servo-2020":
- pref_attr = "servo_2020_pref"
+ write(OUT_DIR, "properties.rs", properties_file)
+
+ if engine == "servo":
properties_dict = {
kind: {
- p.name: {"pref": getattr(p, pref_attr)}
+ p.name: {"pref": getattr(p, "servo_pref")}
for prop in properties_list
if prop.enabled_in_content()
- for p in [prop] + prop.alias
+ for p in [prop] + prop.aliases
}
for kind, properties_list in [
("longhands", properties.longhands),
diff --git a/servo/components/style/properties/cascade.rs b/servo/components/style/properties/cascade.rs
index d4d48eaeb8..0f264a9249 100644
--- a/servo/components/style/properties/cascade.rs
+++ b/servo/components/style/properties/cascade.rs
@@ -297,7 +297,7 @@ where
context.style().add_flags(cascade_input_flags);
let using_cached_reset_properties;
- let ignore_colors = !context.builder.device.use_document_colors();
+ let ignore_colors = context.builder.device.forced_colors().is_active();
let mut cascade = Cascade::new(first_line_reparenting, ignore_colors);
let mut declarations = Default::default();
let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
@@ -763,6 +763,11 @@ impl<'b> Cascade<'b> {
if apply!(Zoom) {
self.compute_zoom(context);
+ // NOTE(emilio): This is a bit of a hack, but matches the shipped WebKit and Blink
+ // behavior for now. Ideally, in the future, we have a pass over all
+ // implicitly-or-explicitly-inherited properties that can contain lengths and
+ // re-compute them properly, see https://github.com/w3c/csswg-drafts/issues/9397.
+ self.recompute_font_size_for_zoom_change(&mut context.builder);
}
// Compute font-family.
@@ -1226,8 +1231,6 @@ impl<'b> Cascade<'b> {
/// <svg:text> is not affected by text zoom, and it uses a preshint to disable it. We fix up
/// the struct when this happens by unzooming its contained font values, which will have been
/// zoomed in the parent.
- ///
- /// FIXME(emilio): Why doing this _before_ handling font-size? That sounds wrong.
#[cfg(feature = "gecko")]
fn unzoom_fonts_if_needed(&self, builder: &mut StyleBuilder) {
debug_assert!(self.seen.contains(LonghandId::XTextScale));
@@ -1250,6 +1253,19 @@ impl<'b> Cascade<'b> {
builder.mutate_font().unzoom_fonts(device);
}
+ fn recompute_font_size_for_zoom_change(&self, builder: &mut StyleBuilder) {
+ debug_assert!(self.seen.contains(LonghandId::Zoom));
+ // NOTE(emilio): Intentionally not using the effective zoom here, since all the inherited
+ // zooms are already applied.
+ let zoom = builder.get_box().clone_zoom();
+ let old_size = builder.get_font().clone_font_size();
+ let new_size = old_size.zoom(zoom);
+ if old_size == new_size {
+ return;
+ }
+ builder.mutate_font().set_font_size(new_size);
+ }
+
/// Special handling of font-size: math (used for MathML).
/// https://w3c.github.io/mathml-core/#the-math-script-level-property
/// TODO: Bug: 1548471: MathML Core also does not specify a script min size
diff --git a/servo/components/style/properties/data.py b/servo/components/style/properties/data.py
index 093f1cb75f..2a29a7ed58 100644
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -176,11 +176,9 @@ class Keyword(object):
gecko_enum_prefix=None,
custom_consts=None,
extra_gecko_values=None,
- extra_servo_2013_values=None,
- extra_servo_2020_values=None,
+ extra_servo_values=None,
gecko_aliases=None,
- servo_2013_aliases=None,
- servo_2020_aliases=None,
+ servo_aliases=None,
gecko_strip_moz_prefix=None,
gecko_inexhaustive=None,
):
@@ -196,11 +194,9 @@ class Keyword(object):
)
self.gecko_enum_prefix = gecko_enum_prefix
self.extra_gecko_values = (extra_gecko_values or "").split()
- self.extra_servo_2013_values = (extra_servo_2013_values or "").split()
- self.extra_servo_2020_values = (extra_servo_2020_values or "").split()
+ self.extra_servo_values = (extra_servo_values or "").split()
self.gecko_aliases = parse_aliases(gecko_aliases or "")
- self.servo_2013_aliases = parse_aliases(servo_2013_aliases or "")
- self.servo_2020_aliases = parse_aliases(servo_2020_aliases or "")
+ self.servo_aliases = parse_aliases(servo_aliases or "")
self.consts_map = {} if custom_consts is None else custom_consts
self.gecko_strip_moz_prefix = (
True if gecko_strip_moz_prefix is None else gecko_strip_moz_prefix
@@ -210,20 +206,16 @@ class Keyword(object):
def values_for(self, engine):
if engine == "gecko":
return self.values + self.extra_gecko_values
- elif engine == "servo-2013":
- return self.values + self.extra_servo_2013_values
- elif engine == "servo-2020":
- return self.values + self.extra_servo_2020_values
+ elif engine == "servo":
+ return self.values + self.extra_servo_values
else:
raise Exception("Bad engine: " + engine)
def aliases_for(self, engine):
if engine == "gecko":
return self.gecko_aliases
- elif engine == "servo-2013":
- return self.servo_2013_aliases
- elif engine == "servo-2020":
- return self.servo_2020_aliases
+ elif engine == "servo":
+ return self.servo_aliases
else:
raise Exception("Bad engine: " + engine)
@@ -289,8 +281,7 @@ class Property(object):
self,
name,
spec,
- servo_2013_pref,
- servo_2020_pref,
+ servo_pref,
gecko_pref,
enabled_in,
rule_types_allowed,
@@ -304,8 +295,7 @@ class Property(object):
self.spec = spec
self.ident = to_rust_ident(name)
self.camel_case = to_camel_case(self.ident)
- self.servo_2013_pref = servo_2013_pref
- self.servo_2020_pref = servo_2020_pref
+ self.servo_pref = servo_pref
self.gecko_pref = gecko_pref
self.rule_types_allowed = rule_values_from_arg(rule_types_allowed)
# For enabled_in, the setup is as follows:
@@ -329,10 +319,8 @@ class Property(object):
def experimental(self, engine):
if engine == "gecko":
return bool(self.gecko_pref)
- elif engine == "servo-2013":
- return bool(self.servo_2013_pref)
- elif engine == "servo-2020":
- return bool(self.servo_2020_pref)
+ elif engine == "servo":
+ return bool(self.servo_pref)
else:
raise Exception("Bad engine: " + engine)
@@ -364,8 +352,7 @@ class Longhand(Property):
animation_value_type=None,
keyword=None,
predefined_type=None,
- servo_2013_pref=None,
- servo_2020_pref=None,
+ servo_pref=None,
gecko_pref=None,
enabled_in="content",
need_index=False,
@@ -390,8 +377,7 @@ class Longhand(Property):
self,
name=name,
spec=spec,
- servo_2013_pref=servo_2013_pref,
- servo_2020_pref=servo_2020_pref,
+ servo_pref=servo_pref,
gecko_pref=gecko_pref,
enabled_in=enabled_in,
rule_types_allowed=rule_types_allowed,
@@ -518,16 +504,8 @@ class Longhand(Property):
def may_be_disabled_in(self, shorthand, engine):
if engine == "gecko":
return self.gecko_pref and self.gecko_pref != shorthand.gecko_pref
- elif engine == "servo-2013":
- return (
- self.servo_2013_pref
- and self.servo_2013_pref != shorthand.servo_2013_pref
- )
- elif engine == "servo-2020":
- return (
- self.servo_2020_pref
- and self.servo_2020_pref != shorthand.servo_2020_pref
- )
+ elif engine == "servo":
+ return self.servo_pref and self.servo_pref != shorthand.servo_pref
else:
raise Exception("Bad engine: " + engine)
@@ -659,8 +637,7 @@ class Shorthand(Property):
name,
sub_properties,
spec=None,
- servo_2013_pref=None,
- servo_2020_pref=None,
+ servo_pref=None,
gecko_pref=None,
enabled_in="content",
rule_types_allowed=DEFAULT_RULES,
@@ -672,8 +649,7 @@ class Shorthand(Property):
self,
name=name,
spec=spec,
- servo_2013_pref=servo_2013_pref,
- servo_2020_pref=servo_2020_pref,
+ servo_pref=servo_pref,
gecko_pref=gecko_pref,
enabled_in=enabled_in,
rule_types_allowed=rule_types_allowed,
@@ -704,8 +680,7 @@ class Alias(object):
self.original = original
self.enabled_in = original.enabled_in
self.animatable = original.animatable
- self.servo_2013_pref = original.servo_2013_pref
- self.servo_2020_pref = original.servo_2020_pref
+ self.servo_pref = original.servo_pref
self.gecko_pref = gecko_pref
self.rule_types_allowed = original.rule_types_allowed
self.flags = original.flags
@@ -722,10 +697,8 @@ class Alias(object):
def experimental(self, engine):
if engine == "gecko":
return bool(self.gecko_pref)
- elif engine == "servo-2013":
- return bool(self.servo_2013_pref)
- elif engine == "servo-2020":
- return bool(self.servo_2020_pref)
+ elif engine == "servo":
+ return bool(self.servo_pref)
else:
raise Exception("Bad engine: " + engine)
@@ -768,7 +741,7 @@ class Method(object):
class StyleStruct(object):
- def __init__(self, name, inherited, gecko_name=None, additional_methods=None):
+ def __init__(self, name, inherited, gecko_name=None):
self.gecko_struct_name = "Gecko" + name
self.name = name
self.name_lower = to_snake_case(name)
@@ -777,15 +750,12 @@ class StyleStruct(object):
self.inherited = inherited
self.gecko_name = gecko_name or name
self.gecko_ffi_name = "nsStyle" + self.gecko_name
- self.additional_methods = additional_methods or []
self.document_dependent = self.gecko_name in ["Font", "Visibility", "Text"]
class PropertiesData(object):
def __init__(self, engine):
self.engine = engine
- self.style_structs = []
- self.current_style_struct = None
self.longhands = []
self.longhands_by_name = {}
self.longhands_by_logical_group = {}
@@ -797,13 +767,35 @@ class PropertiesData(object):
CountedUnknownProperty(p) for p in COUNTED_UNKNOWN_PROPERTIES
]
- def new_style_struct(self, *args, **kwargs):
- style_struct = StyleStruct(*args, **kwargs)
- self.style_structs.append(style_struct)
- self.current_style_struct = style_struct
+ self.style_structs = [
+ StyleStruct("Background", inherited=False),
+ StyleStruct("Border", inherited=False),
+ StyleStruct("Box", inherited=False, gecko_name="Display"),
+ StyleStruct("Column", inherited=False),
+ StyleStruct("Counters", inherited=False, gecko_name="Content"),
+ StyleStruct("Effects", inherited=False),
+ StyleStruct("Font", inherited=True),
+ StyleStruct("InheritedBox", inherited=True, gecko_name="Visibility"),
+ StyleStruct("InheritedSVG", inherited=True, gecko_name="SVG"),
+ StyleStruct("InheritedTable", inherited=True, gecko_name="TableBorder"),
+ StyleStruct("InheritedText", inherited=True, gecko_name="Text"),
+ StyleStruct("InheritedUI", inherited=True, gecko_name="UI"),
+ StyleStruct("List", inherited=True),
+ StyleStruct("Margin", inherited=False),
+ StyleStruct("Outline", inherited=False),
+ StyleStruct("Padding", inherited=False),
+ StyleStruct("Page", inherited=False),
+ StyleStruct("Position", inherited=False),
+ StyleStruct("SVG", inherited=False, gecko_name="SVGReset"),
+ StyleStruct("Table", inherited=False),
+ StyleStruct("Text", inherited=False, gecko_name="TextReset"),
+ StyleStruct("UI", inherited=False, gecko_name="UIReset"),
+ StyleStruct("XUL", inherited=False),
+ ]
+ self.current_style_struct = None
def active_style_structs(self):
- return [s for s in self.style_structs if s.additional_methods or s.longhands]
+ return [s for s in self.style_structs if s.longhands]
def add_prefixed_aliases(self, property):
# FIXME Servo's DOM architecture doesn't support vendor-prefixed properties.
@@ -856,7 +848,7 @@ def _add_logical_props(data, props):
groups = set()
for prop in props:
if prop not in data.longhands_by_name:
- assert data.engine in ["servo-2013", "servo-2020"]
+ assert data.engine == "servo"
continue
prop = data.longhands_by_name[prop]
if prop.logical_group:
diff --git a/servo/components/style/properties/declaration_block.rs b/servo/components/style/properties/declaration_block.rs
index 81d7148e62..94945ab8c0 100644
--- a/servo/components/style/properties/declaration_block.rs
+++ b/servo/components/style/properties/declaration_block.rs
@@ -36,10 +36,10 @@ use cssparser::{
use itertools::Itertools;
use selectors::SelectorList;
use servo_arc::Arc;
-use smallbitvec::{self, SmallBitVec};
+use smallbitvec::SmallBitVec;
use smallvec::SmallVec;
use std::fmt::{self, Write};
-use std::iter::{DoubleEndedIterator, Zip};
+use std::iter::Zip;
use std::slice::Iter;
use style_traits::{CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss};
use thin_vec::ThinVec;
diff --git a/servo/components/style/properties/helpers.mako.rs b/servo/components/style/properties/helpers.mako.rs
index 968a97aa00..82998632ca 100644
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -594,11 +594,9 @@
'gecko_constant_prefix',
'gecko_enum_prefix',
'extra_gecko_values',
- 'extra_servo_2013_values',
- 'extra_servo_2020_values',
+ 'extra_servo_values',
'gecko_aliases',
- 'servo_2013_aliases',
- 'servo_2020_aliases',
+ 'servo_aliases',
'custom_consts',
'gecko_inexhaustive',
'gecko_strip_moz_prefix',
diff --git a/servo/components/style/properties/helpers/animated_properties.mako.rs b/servo/components/style/properties/helpers/animated_properties.mako.rs
index 290684cdab..171413b243 100644
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -737,7 +737,7 @@ impl<'a> TransitionPropertyIterator<'a> {
/// A single iteration of the TransitionPropertyIterator.
pub struct TransitionPropertyIteration {
/// The id of the longhand for this property.
- pub longhand_id: LonghandId,
+ pub property: OwnedPropertyDeclarationId,
/// The index of this property in the list of transition properties for this
/// iterator's style.
@@ -753,7 +753,7 @@ impl<'a> Iterator for TransitionPropertyIterator<'a> {
if let Some(ref mut longhand_iterator) = self.longhand_iterator {
if let Some(longhand_id) = longhand_iterator.next() {
return Some(TransitionPropertyIteration {
- longhand_id,
+ property: OwnedPropertyDeclarationId::Longhand(longhand_id),
index: self.index_range.start - 1,
});
}
@@ -766,7 +766,7 @@ impl<'a> Iterator for TransitionPropertyIterator<'a> {
match id.longhand_or_shorthand() {
Ok(longhand_id) => {
return Some(TransitionPropertyIteration {
- longhand_id,
+ property: OwnedPropertyDeclarationId::Longhand(longhand_id),
index,
});
},
@@ -778,7 +778,13 @@ impl<'a> Iterator for TransitionPropertyIterator<'a> {
},
}
}
- TransitionProperty::Custom(..) | TransitionProperty::Unsupported(..) => {}
+ TransitionProperty::Custom(name) => {
+ return Some(TransitionPropertyIteration {
+ property: OwnedPropertyDeclarationId::Custom(name),
+ index,
+ })
+ },
+ TransitionProperty::Unsupported(..) => {},
}
}
}
diff --git a/servo/components/style/properties/longhands/background.mako.rs b/servo/components/style/properties/longhands/background.mako.rs
index 48270f748e..535dd64e20 100644
--- a/servo/components/style/properties/longhands/background.mako.rs
+++ b/servo/components/style/properties/longhands/background.mako.rs
@@ -4,13 +4,11 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% data.new_style_struct("Background", inherited=False) %>
-
${helpers.predefined_type(
"background-color",
"Color",
"computed::Color::TRANSPARENT_BLACK",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="SpecifiedValue::transparent()",
spec="https://drafts.csswg.org/css-backgrounds/#background-color",
animation_value_type="AnimatedColor",
@@ -23,7 +21,7 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"background-image",
"Image",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_value="computed::Image::None",
initial_specified_value="specified::Image::None",
spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
@@ -38,7 +36,7 @@ ${helpers.predefined_type(
"background-position-" + axis,
"position::" + direction + "Position",
"computed::LengthPercentage::zero_percent()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="SpecifiedValue::initial_specified_value()",
spec="https://drafts.csswg.org/css-backgrounds-4/#propdef-background-position-" + axis,
animation_value_type="ComputedValue",
@@ -52,7 +50,7 @@ ${helpers.predefined_type(
"background-repeat",
"BackgroundRepeat",
"computed::BackgroundRepeat::repeat()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::BackgroundRepeat::repeat()",
animation_value_type="discrete",
vector=True,
@@ -62,8 +60,8 @@ ${helpers.predefined_type(
${helpers.single_keyword(
"background-attachment",
- "scroll" + (" fixed" if engine in ["gecko", "servo-2013"] else "") + (" local" if engine == "gecko" else ""),
- engines="gecko servo-2013 servo-2020",
+ "scroll fixed" + (" local" if engine == "gecko" else ""),
+ engines="gecko servo",
vector=True,
gecko_enum_prefix="StyleImageLayerAttachment",
spec="https://drafts.csswg.org/css-backgrounds/#the-background-attachment",
@@ -74,7 +72,7 @@ ${helpers.single_keyword(
${helpers.single_keyword(
"background-clip",
"border-box padding-box content-box",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
extra_gecko_values="text",
vector=True, extra_prefixes="webkit",
gecko_enum_prefix="StyleGeometryBox",
@@ -87,7 +85,7 @@ ${helpers.single_keyword(
${helpers.single_keyword(
"background-origin",
"padding-box border-box content-box",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
vector=True, extra_prefixes="webkit",
gecko_enum_prefix="StyleGeometryBox",
gecko_inexhaustive=True,
@@ -99,7 +97,7 @@ ${helpers.single_keyword(
${helpers.predefined_type(
"background-size",
"BackgroundSize",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_value="computed::BackgroundSize::auto()",
initial_specified_value="specified::BackgroundSize::auto()",
spec="https://drafts.csswg.org/css-backgrounds/#the-background-size",
diff --git a/servo/components/style/properties/longhands/border.mako.rs b/servo/components/style/properties/longhands/border.mako.rs
index 4d0676f678..59a0fee94a 100644
--- a/servo/components/style/properties/longhands/border.mako.rs
+++ b/servo/components/style/properties/longhands/border.mako.rs
@@ -3,11 +3,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Keyword, Method, ALL_CORNERS, PHYSICAL_SIDES, ALL_SIDES, maybe_moz_logical_alias %>
+<% from data import ALL_CORNERS, ALL_SIDES, maybe_moz_logical_alias %>
-<% data.new_style_struct("Border", inherited=False,
- additional_methods=[Method("border_" + side + "_has_nonzero_width",
- "bool") for side in ["top", "right", "bottom", "left"]]) %>
<%
def maybe_logical_spec(side, kind):
if side[1]: # if it is logical
@@ -23,7 +20,7 @@
${helpers.predefined_type(
"border-%s-color" % side_name, "Color",
"computed_value::T::currentcolor()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
aliases=maybe_moz_logical_alias(engine, side, "-moz-border-%s-color"),
spec=maybe_logical_spec(side, "color"),
animation_value_type="AnimatedColor",
@@ -37,7 +34,7 @@
${helpers.predefined_type(
"border-%s-style" % side_name, "BorderStyle",
"specified::BorderStyle::None",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
aliases=maybe_moz_logical_alias(engine, side, "-moz-border-%s-style"),
spec=maybe_logical_spec(side, "style"),
animation_value_type="discrete" if not is_logical else "none",
@@ -50,7 +47,7 @@
"border-%s-width" % side_name,
"BorderSideWidth",
"app_units::Au::from_px(3)",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
aliases=maybe_moz_logical_alias(engine, side, "-moz-border-%s-width"),
spec=maybe_logical_spec(side, "width"),
animation_value_type="NonNegativeLength",
@@ -76,7 +73,7 @@
"BorderCornerRadius",
"computed::BorderCornerRadius::zero()",
"parse",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
extra_prefixes=prefixes,
spec=maybe_logical_spec(corner, "radius"),
boxed=True,
@@ -111,13 +108,13 @@ ${helpers.single_keyword(
${helpers.predefined_type(
"border-image-source",
"Image",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_value="computed::Image::None",
initial_specified_value="specified::Image::None",
spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
vector=False,
animation_value_type="discrete",
- boxed=engine == "servo-2013",
+ boxed=engine == "servo",
ignored_when_colors_disabled=True,
affects="paint",
)}
@@ -125,7 +122,7 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"border-image-outset",
"NonNegativeLengthOrNumberRect",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_value="generics::rect::Rect::all(computed::NonNegativeLengthOrNumber::zero())",
initial_specified_value="generics::rect::Rect::all(specified::NonNegativeLengthOrNumber::zero())",
spec="https://drafts.csswg.org/css-backgrounds/#border-image-outset",
@@ -138,7 +135,7 @@ ${helpers.predefined_type(
"border-image-repeat",
"BorderImageRepeat",
"computed::BorderImageRepeat::stretch()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::BorderImageRepeat::stretch()",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-backgrounds/#the-border-image-repeat",
@@ -148,7 +145,7 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"border-image-width",
"BorderImageWidth",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_value="computed::BorderImageWidth::all(computed::BorderImageSideWidth::one())",
initial_specified_value="specified::BorderImageWidth::all(specified::BorderImageSideWidth::one())",
spec="https://drafts.csswg.org/css-backgrounds/#border-image-width",
@@ -160,7 +157,7 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"border-image-slice",
"BorderImageSlice",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_value="computed::BorderImageSlice::hundred_percent()",
initial_specified_value="specified::BorderImageSlice::hundred_percent()",
spec="https://drafts.csswg.org/css-backgrounds/#border-image-slice",
diff --git a/servo/components/style/properties/longhands/box.mako.rs b/servo/components/style/properties/longhands/box.mako.rs
index 017bef38ea..187e8f8169 100644
--- a/servo/components/style/properties/longhands/box.mako.rs
+++ b/servo/components/style/properties/longhands/box.mako.rs
@@ -3,17 +3,13 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import ALL_AXES, Keyword, Method, to_rust_ident, to_camel_case%>
-
-<% data.new_style_struct("Box",
- inherited=False,
- gecko_name="Display") %>
+<% from data import ALL_AXES %>
${helpers.predefined_type(
"display",
"Display",
"computed::Display::inline()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::Display::inline()",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-display/#propdef-display",
@@ -38,7 +34,7 @@ ${helpers.single_keyword(
${helpers.single_keyword(
"-servo-top-layer",
"none top",
- engines="servo-2013 servo-2020",
+ engines="servo",
animation_value_type="none",
enabled_in="ua",
spec="Internal (not web-exposed)",
@@ -47,8 +43,8 @@ ${helpers.single_keyword(
<%helpers:single_keyword
name="position"
- values="static absolute relative fixed ${'sticky' if engine in ['gecko', 'servo-2013'] else ''}"
- engines="gecko servo-2013 servo-2020"
+ values="static absolute relative fixed sticky"
+ engines="gecko servo"
animation_value_type="discrete"
gecko_enum_prefix="StylePositionProperty"
spec="https://drafts.csswg.org/css-position/#position-property"
@@ -69,8 +65,7 @@ ${helpers.predefined_type(
"float",
"Float",
"computed::Float::None",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.2020.unimplemented",
+ engines="gecko servo",
initial_specified_value="specified::Float::None",
spec="https://drafts.csswg.org/css-box/#propdef-float",
animation_value_type="discrete",
@@ -83,7 +78,7 @@ ${helpers.predefined_type(
"clear",
"Clear",
"computed::Clear::None",
- engines="gecko servo-2013",
+ engines="gecko servo",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css2/#propdef-clear",
servo_restyle_damage="rebuild_and_reflow",
@@ -94,7 +89,7 @@ ${helpers.predefined_type(
"vertical-align",
"VerticalAlign",
"computed::VerticalAlign::baseline()",
- engines="gecko servo-2013",
+ engines="gecko servo",
animation_value_type="ComputedValue",
spec="https://www.w3.org/TR/CSS2/visudet.html#propdef-vertical-align",
servo_restyle_damage = "reflow",
@@ -105,7 +100,7 @@ ${helpers.predefined_type(
"baseline-source",
"BaselineSource",
"computed::BaselineSource::Auto",
- engines="gecko servo-2013",
+ engines="gecko servo",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-inline-3/#baseline-source",
servo_restyle_damage = "reflow",
@@ -117,7 +112,8 @@ ${helpers.predefined_type(
${helpers.single_keyword(
"-servo-overflow-clip-box",
"padding-box content-box",
- engines="servo-2013",
+ engines="servo",
+ servo_pref="layout.legacy_layout",
animation_value_type="none",
enabled_in="ua",
spec="Internal, not web-exposed, \
@@ -146,7 +142,7 @@ ${helpers.single_keyword(
full_name,
"Overflow",
"computed::Overflow::Visible",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
logical_group="overflow",
logical=logical,
animation_value_type="discrete",
@@ -174,7 +170,7 @@ ${helpers.predefined_type(
"transform",
"Transform",
"generics::transform::Transform::none()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
extra_prefixes=transform_extra_prefixes,
animation_value_type="ComputedValue",
flags="CAN_ANIMATE_ON_COMPOSITOR",
@@ -187,7 +183,7 @@ ${helpers.predefined_type(
"rotate",
"Rotate",
"generics::transform::Rotate::None",
- engines="gecko servo-2013",
+ engines="gecko servo",
animation_value_type="ComputedValue",
boxed=True,
flags="CAN_ANIMATE_ON_COMPOSITOR",
@@ -201,7 +197,7 @@ ${helpers.predefined_type(
"scale",
"Scale",
"generics::transform::Scale::None",
- engines="gecko servo-2013",
+ engines="gecko servo",
animation_value_type="ComputedValue",
boxed=True,
flags="CAN_ANIMATE_ON_COMPOSITOR",
@@ -215,7 +211,7 @@ ${helpers.predefined_type(
"translate",
"Translate",
"generics::transform::Translate::None",
- engines="gecko servo-2013",
+ engines="gecko servo",
animation_value_type="ComputedValue",
boxed=True,
flags="CAN_ANIMATE_ON_COMPOSITOR",
@@ -285,7 +281,6 @@ ${helpers.predefined_type(
"computed::OffsetPosition::normal()",
engines="gecko",
animation_value_type="ComputedValue",
- gecko_pref="layout.css.motion-path-offset-position.enabled",
flags="CAN_ANIMATE_ON_COMPOSITOR",
spec="https://drafts.fxtf.org/motion-1/#offset-position-property",
servo_restyle_damage="reflow_out_of_flow",
@@ -409,7 +404,7 @@ ${helpers.predefined_type(
"perspective",
"Perspective",
"computed::Perspective::none()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
gecko_ffi_name="mChildPerspective",
spec="https://drafts.csswg.org/css-transforms/#perspective",
extra_prefixes=transform_extra_prefixes,
@@ -422,7 +417,7 @@ ${helpers.predefined_type(
"perspective-origin",
"Position",
"computed::position::Position::center()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
boxed=True,
extra_prefixes=transform_extra_prefixes,
spec="https://drafts.csswg.org/css-transforms-2/#perspective-origin-property",
@@ -434,7 +429,7 @@ ${helpers.predefined_type(
${helpers.single_keyword(
"backface-visibility",
"visible hidden",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
gecko_enum_prefix="StyleBackfaceVisibility",
spec="https://drafts.csswg.org/css-transforms/#backface-visibility-property",
extra_prefixes=transform_extra_prefixes,
@@ -456,7 +451,7 @@ ${helpers.predefined_type(
"transform-style",
"TransformStyle",
"computed::TransformStyle::Flat",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property",
extra_prefixes=transform_extra_prefixes,
animation_value_type="discrete",
@@ -468,7 +463,7 @@ ${helpers.predefined_type(
"transform-origin",
"TransformOrigin",
"computed::TransformOrigin::initial_value()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
animation_value_type="ComputedValue",
extra_prefixes=transform_extra_prefixes,
gecko_ffi_name="mTransformOrigin",
@@ -503,10 +498,11 @@ ${helpers.predefined_type(
"container-type",
"ContainerType",
"computed::ContainerType::Normal",
- engines="gecko",
+ engines="gecko servo",
animation_value_type="none",
enabled_in="ua",
gecko_pref="layout.css.container-queries.enabled",
+ servo_pref="layout.container-queries.enabled",
spec="https://drafts.csswg.org/css-contain-3/#container-type",
affects="layout",
)}
@@ -515,10 +511,11 @@ ${helpers.predefined_type(
"container-name",
"ContainerName",
"computed::ContainerName::none()",
- engines="gecko",
+ engines="gecko servo",
animation_value_type="none",
enabled_in="ua",
gecko_pref="layout.css.container-queries.enabled",
+ servo_pref="layout.container-queries.enabled",
spec="https://drafts.csswg.org/css-contain-3/#container-name",
affects="",
)}
diff --git a/servo/components/style/properties/longhands/column.mako.rs b/servo/components/style/properties/longhands/column.mako.rs
index 38c32938c6..ca97f36dba 100644
--- a/servo/components/style/properties/longhands/column.mako.rs
+++ b/servo/components/style/properties/longhands/column.mako.rs
@@ -4,17 +4,14 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% data.new_style_struct("Column", inherited=False) %>
-
${helpers.predefined_type(
"column-width",
"length::NonNegativeLengthOrAuto",
"computed::length::NonNegativeLengthOrAuto::auto()",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.2020.unimplemented",
+ engines="gecko servo",
initial_specified_value="specified::length::NonNegativeLengthOrAuto::auto()",
animation_value_type="NonNegativeLengthOrAuto",
- servo_2013_pref="layout.columns.enabled",
+ servo_pref="layout.columns.enabled",
spec="https://drafts.csswg.org/css-multicol/#propdef-column-width",
servo_restyle_damage="rebuild_and_reflow",
affects="layout",
@@ -24,10 +21,9 @@ ${helpers.predefined_type(
"column-count",
"ColumnCount",
"computed::ColumnCount::auto()",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.2020.unimplemented",
+ engines="gecko servo",
initial_specified_value="specified::ColumnCount::auto()",
- servo_2013_pref="layout.columns.enabled",
+ servo_pref="layout.columns.enabled",
animation_value_type="AnimatedColumnCount",
spec="https://drafts.csswg.org/css-multicol/#propdef-column-count",
servo_restyle_damage="rebuild_and_reflow",
@@ -71,7 +67,8 @@ ${helpers.predefined_type(
${helpers.single_keyword(
"column-span",
"none all",
- engines="gecko",
+ engines="gecko servo",
+ servo_pref="layout.columns.enabled",
animation_value_type="discrete",
gecko_enum_prefix="StyleColumnSpan",
spec="https://drafts.csswg.org/css-multicol/#propdef-column-span",
diff --git a/servo/components/style/properties/longhands/counters.mako.rs b/servo/components/style/properties/longhands/counters.mako.rs
index 6c844c3567..5991bd416c 100644
--- a/servo/components/style/properties/longhands/counters.mako.rs
+++ b/servo/components/style/properties/longhands/counters.mako.rs
@@ -4,13 +4,11 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% data.new_style_struct("Counters", inherited=False, gecko_name="Content") %>
-
${helpers.predefined_type(
"content",
"Content",
"computed::Content::normal()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::Content::normal()",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-content/#propdef-content",
@@ -21,7 +19,8 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"counter-increment",
"CounterIncrement",
- engines="gecko servo-2013",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
initial_value="Default::default()",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-lists/#propdef-counter-increment",
@@ -32,7 +31,8 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"counter-reset",
"CounterReset",
- engines="gecko servo-2013",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
initial_value="Default::default()",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset",
diff --git a/servo/components/style/properties/longhands/effects.mako.rs b/servo/components/style/properties/longhands/effects.mako.rs
index b301aab5dd..742844a670 100644
--- a/servo/components/style/properties/longhands/effects.mako.rs
+++ b/servo/components/style/properties/longhands/effects.mako.rs
@@ -4,14 +4,11 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
-// Box-shadow, etc.
-<% data.new_style_struct("Effects", inherited=False) %>
-
${helpers.predefined_type(
"opacity",
"Opacity",
"1.0",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
animation_value_type="ComputedValue",
flags="CAN_ANIMATE_ON_COMPOSITOR",
spec="https://drafts.csswg.org/css-color/#transparency",
@@ -23,8 +20,8 @@ ${helpers.predefined_type(
"box-shadow",
"BoxShadow",
None,
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.2020.unimplemented",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
vector=True,
simple_vector_bindings=True,
animation_value_type="AnimatedBoxShadowList",
@@ -39,7 +36,7 @@ ${helpers.predefined_type(
"clip",
"ClipRectOrAuto",
"computed::ClipRectOrAuto::auto()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
animation_value_type="ComputedValue",
boxed=True,
allow_quirks="Yes",
@@ -51,7 +48,7 @@ ${helpers.predefined_type(
"filter",
"Filter",
None,
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
vector=True,
simple_vector_bindings=True,
gecko_ffi_name="mFilters",
@@ -84,7 +81,7 @@ ${helpers.single_keyword(
"""normal multiply screen overlay darken lighten color-dodge
color-burn hard-light soft-light difference exclusion hue
saturation color luminosity plus-lighter""",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
gecko_enum_prefix="StyleBlend",
animation_value_type="discrete",
spec="https://drafts.fxtf.org/compositing/#propdef-mix-blend-mode",
diff --git a/servo/components/style/properties/longhands/font.mako.rs b/servo/components/style/properties/longhands/font.mako.rs
index 4583de0bd7..3f7a2f0657 100644
--- a/servo/components/style/properties/longhands/font.mako.rs
+++ b/servo/components/style/properties/longhands/font.mako.rs
@@ -3,14 +3,12 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Method, to_camel_case, to_rust_ident, to_camel_case_lower, SYSTEM_FONT_LONGHANDS %>
-
-<% data.new_style_struct("Font", inherited=True) %>
+<% from data import SYSTEM_FONT_LONGHANDS %>
${helpers.predefined_type(
"font-family",
"FontFamily",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_value="computed::FontFamily::serif()",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-fonts/#propdef-font-family",
@@ -21,7 +19,7 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"font-style",
"FontStyle",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_value="computed::FontStyle::normal()",
initial_specified_value="specified::FontStyle::normal()",
animation_value_type="FontStyle",
@@ -39,7 +37,7 @@ ${helpers.predefined_type(
${helpers.single_keyword(
"font-variant-caps",
"normal small-caps",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
extra_gecko_values="all-small-caps petite-caps all-petite-caps unicase titling-caps",
gecko_constant_prefix="NS_FONT_VARIANT_CAPS",
gecko_ffi_name="mFont.variantCaps",
@@ -53,7 +51,7 @@ ${helpers.single_keyword(
${helpers.predefined_type(
"font-weight",
"FontWeight",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_value="computed::FontWeight::normal()",
initial_specified_value="specified::FontWeight::normal()",
animation_value_type="Number",
@@ -65,7 +63,7 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"font-size",
"FontSize",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_value="computed::FontSize::medium()",
initial_specified_value="specified::FontSize::medium()",
animation_value_type="NonNegativeLength",
@@ -137,7 +135,7 @@ ${helpers.predefined_type(
${helpers.predefined_type(
"font-stretch",
"FontStretch",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_value="computed::FontStretch::hundred()",
initial_specified_value="specified::FontStretch::normal()",
animation_value_type="Percentage",
@@ -354,7 +352,7 @@ ${helpers.predefined_type(
"line-height",
"LineHeight",
"computed::LineHeight::normal()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
animation_value_type="LineHeight",
spec="https://drafts.csswg.org/css2/visudet.html#propdef-line-height",
servo_restyle_damage="reflow",
diff --git a/servo/components/style/properties/longhands/inherited_box.mako.rs b/servo/components/style/properties/longhands/inherited_box.mako.rs
index 7fd94d1a1f..add367798b 100644
--- a/servo/components/style/properties/longhands/inherited_box.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_box.mako.rs
@@ -4,13 +4,11 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% data.new_style_struct("InheritedBox", inherited=True, gecko_name="Visibility") %>
-
// TODO: collapse. Well, do tables first.
${helpers.single_keyword(
"visibility",
"visible hidden collapse",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
gecko_ffi_name="mVisible",
animation_value_type="ComputedValue",
spec="https://drafts.csswg.org/css-box/#propdef-visibility",
@@ -23,13 +21,12 @@ ${helpers.single_keyword(
${helpers.single_keyword(
"writing-mode",
"horizontal-tb vertical-rl vertical-lr",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
extra_gecko_values="sideways-rl sideways-lr",
gecko_aliases="lr=horizontal-tb lr-tb=horizontal-tb \
rl=horizontal-tb rl-tb=horizontal-tb \
tb=vertical-rl tb-rl=vertical-rl",
- servo_2013_pref="layout.writing-mode.enabled",
- servo_2020_pref="layout.writing-mode.enabled",
+ servo_pref="layout.writing-mode.enabled",
animation_value_type="none",
spec="https://drafts.csswg.org/css-writing-modes/#propdef-writing-mode",
gecko_enum_prefix="StyleWritingModeProperty",
@@ -40,8 +37,8 @@ ${helpers.single_keyword(
${helpers.single_keyword(
"direction",
"ltr rtl",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.2020.unimplemented",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
animation_value_type="none",
spec="https://drafts.csswg.org/css-writing-modes/#propdef-direction",
gecko_enum_prefix="StyleDirection",
@@ -88,7 +85,7 @@ ${helpers.predefined_type(
"image-rendering",
"ImageRendering",
"computed::ImageRendering::Auto",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-images/#propdef-image-rendering",
animation_value_type="discrete",
affects="paint",
diff --git a/servo/components/style/properties/longhands/inherited_svg.mako.rs b/servo/components/style/properties/longhands/inherited_svg.mako.rs
index 90443f962a..d01d5a4a80 100644
--- a/servo/components/style/properties/longhands/inherited_svg.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_svg.mako.rs
@@ -4,10 +4,6 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
-// SVG 2
-// https://svgwg.org/svg2-draft/
-<% data.new_style_struct("InheritedSVG", inherited=True, gecko_name="SVG") %>
-
// Section 10 - Text
${helpers.single_keyword(
diff --git a/servo/components/style/properties/longhands/inherited_table.mako.rs b/servo/components/style/properties/longhands/inherited_table.mako.rs
index 7eb42a6eb2..c7ada525b8 100644
--- a/servo/components/style/properties/longhands/inherited_table.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_table.mako.rs
@@ -4,12 +4,11 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% data.new_style_struct("InheritedTable", inherited=True, gecko_name="TableBorder") %>
-
${helpers.single_keyword(
"border-collapse",
"separate collapse",
- engines="gecko servo-2013",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
gecko_enum_prefix="StyleBorderCollapse",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-tables/#propdef-border-collapse",
@@ -20,7 +19,8 @@ ${helpers.single_keyword(
${helpers.single_keyword(
"empty-cells",
"show hide",
- engines="gecko servo-2013",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
gecko_enum_prefix="StyleEmptyCells",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-tables/#propdef-empty-cells",
@@ -32,7 +32,8 @@ ${helpers.predefined_type(
"caption-side",
"table::CaptionSide",
"computed::table::CaptionSide::Top",
- engines="gecko servo-2013",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-tables/#propdef-caption-side",
servo_restyle_damage="rebuild_and_reflow",
@@ -43,8 +44,7 @@ ${helpers.predefined_type(
"border-spacing",
"BorderSpacing",
"computed::BorderSpacing::zero()",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.2020.unimplemented",
+ engines="gecko servo",
animation_value_type="BorderSpacing",
boxed=True,
spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing",
diff --git a/servo/components/style/properties/longhands/inherited_text.mako.rs b/servo/components/style/properties/longhands/inherited_text.mako.rs
index 544ba99bf7..c6ad907b23 100644
--- a/servo/components/style/properties/longhands/inherited_text.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_text.mako.rs
@@ -3,14 +3,12 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Keyword %>
-<% data.new_style_struct("InheritedText", inherited=True, gecko_name="Text") %>
${helpers.predefined_type(
"color",
"ColorPropertyValue",
"crate::color::AbsoluteColor::BLACK",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
animation_value_type="AbsoluteColor",
ignored_when_colors_disabled="True",
spec="https://drafts.csswg.org/css-color/#color",
@@ -23,7 +21,8 @@ ${helpers.predefined_type(
"text-transform",
"TextTransform",
"computed::TextTransform::none()",
- engines="gecko servo-2013",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-text-transform",
servo_restyle_damage="rebuild_and_reflow",
@@ -58,8 +57,7 @@ ${helpers.predefined_type(
"text-indent",
"TextIndent",
"computed::TextIndent::zero()",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.2020.unimplemented",
+ engines="gecko servo",
animation_value_type="ComputedValue",
spec="https://drafts.csswg.org/css-text/#propdef-text-indent",
servo_restyle_damage = "reflow",
@@ -72,8 +70,8 @@ ${helpers.predefined_type(
"overflow-wrap",
"OverflowWrap",
"computed::OverflowWrap::Normal",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.2020.unimplemented",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-overflow-wrap",
aliases="word-wrap",
@@ -85,8 +83,8 @@ ${helpers.predefined_type(
"word-break",
"WordBreak",
"computed::WordBreak::Normal",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.2020.unimplemented",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-word-break",
servo_restyle_damage="rebuild_and_reflow",
@@ -97,8 +95,7 @@ ${helpers.predefined_type(
"text-justify",
"TextJustify",
"computed::TextJustify::Auto",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.2020.unimplemented",
+ engines="gecko servo",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-text-justify",
servo_restyle_damage="rebuild_and_reflow",
@@ -109,7 +106,7 @@ ${helpers.predefined_type(
"text-align-last",
"TextAlignLast",
"computed::text::TextAlignLast::Auto",
- engines="gecko",
+ engines="gecko servo",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-text-align-last",
affects="layout",
@@ -120,7 +117,7 @@ ${helpers.predefined_type(
"text-align",
"TextAlign",
"computed::TextAlign::Start",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-text-align",
servo_restyle_damage = "reflow",
@@ -131,7 +128,7 @@ ${helpers.predefined_type(
"letter-spacing",
"LetterSpacing",
"computed::LetterSpacing::normal()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
animation_value_type="ComputedValue",
spec="https://drafts.csswg.org/css-text/#propdef-letter-spacing",
servo_restyle_damage="rebuild_and_reflow",
@@ -142,7 +139,7 @@ ${helpers.predefined_type(
"word-spacing",
"WordSpacing",
"computed::WordSpacing::zero()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
animation_value_type="ComputedValue",
spec="https://drafts.csswg.org/css-text/#propdef-word-spacing",
servo_restyle_damage="rebuild_and_reflow",
@@ -164,7 +161,8 @@ ${helpers.predefined_type(
"text-shadow",
"SimpleShadow",
None,
- engines="gecko servo-2013",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
vector=True,
vector_animation_type="with_zero",
animation_value_type="AnimatedTextShadowList",
@@ -304,7 +302,7 @@ ${helpers.single_keyword(
${helpers.single_keyword(
"text-rendering",
"auto optimizespeed optimizelegibility geometricprecision",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
gecko_enum_prefix="StyleTextRendering",
animation_value_type="discrete",
spec="https://svgwg.org/svg2-draft/painting.html#TextRenderingProperty",
diff --git a/servo/components/style/properties/longhands/inherited_ui.mako.rs b/servo/components/style/properties/longhands/inherited_ui.mako.rs
index 6cdf721336..7ee1adc602 100644
--- a/servo/components/style/properties/longhands/inherited_ui.mako.rs
+++ b/servo/components/style/properties/longhands/inherited_ui.mako.rs
@@ -4,13 +4,11 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% data.new_style_struct("InheritedUI", inherited=True, gecko_name="UI") %>
-
${helpers.predefined_type(
"cursor",
"Cursor",
"computed::Cursor::auto()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::Cursor::auto()",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-ui/#cursor",
@@ -23,7 +21,7 @@ ${helpers.predefined_type(
${helpers.single_keyword(
"pointer-events",
"auto none",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
animation_value_type="discrete",
extra_gecko_values="visiblepainted visiblefill visiblestroke visible painted fill stroke all",
spec="https://svgwg.org/svg2-draft/interact.html#PointerEventsProperty",
diff --git a/servo/components/style/properties/longhands/list.mako.rs b/servo/components/style/properties/longhands/list.mako.rs
index 619724bd32..0a3f8e36ec 100644
--- a/servo/components/style/properties/longhands/list.mako.rs
+++ b/servo/components/style/properties/longhands/list.mako.rs
@@ -4,13 +4,11 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% data.new_style_struct("List", inherited=True) %>
-
${helpers.single_keyword(
"list-style-position",
"outside inside",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.2020.unimplemented",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
gecko_enum_prefix="StyleListStylePosition",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-lists/#propdef-list-style-position",
@@ -24,17 +22,16 @@ ${helpers.single_keyword(
// upper-roman
//
// [1]: http://dev.w3.org/csswg/css-counter-styles/
-% if engine in ["servo-2013", "servo-2020"]:
+% if engine == "servo":
${helpers.single_keyword(
"list-style-type",
- "disc none circle square disclosure-open disclosure-closed",
- extra_servo_2013_values="""
+ """disc none circle square disclosure-open disclosure-closed
decimal lower-alpha upper-alpha arabic-indic bengali cambodian cjk-decimal devanagari
gujarati gurmukhi kannada khmer lao malayalam mongolian myanmar oriya persian telugu
thai tibetan cjk-earthly-branch cjk-heavenly-stem lower-greek hiragana hiragana-iroha
katakana katakana-iroha
""",
- engines="servo-2013 servo-2020",
+ engines="servo",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-lists/#propdef-list-style-type",
servo_restyle_damage="rebuild_and_reflow",
@@ -59,11 +56,12 @@ ${helpers.single_keyword(
${helpers.predefined_type(
"list-style-image",
"Image",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_value="computed::Image::None",
initial_specified_value="specified::Image::None",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image",
+ boxed=engine == "servo",
servo_restyle_damage="rebuild_and_reflow",
affects="layout",
)}
@@ -72,7 +70,8 @@ ${helpers.predefined_type(
"quotes",
"Quotes",
"computed::Quotes::get_initial_value()",
- engines="gecko servo-2013",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-content/#propdef-quotes",
servo_restyle_damage="rebuild_and_reflow",
diff --git a/servo/components/style/properties/longhands/margin.mako.rs b/servo/components/style/properties/longhands/margin.mako.rs
index b5a87f9683..1000704140 100644
--- a/servo/components/style/properties/longhands/margin.mako.rs
+++ b/servo/components/style/properties/longhands/margin.mako.rs
@@ -4,7 +4,6 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import ALL_SIDES, DEFAULT_RULES_AND_PAGE, maybe_moz_logical_alias %>
-<% data.new_style_struct("Margin", inherited=False) %>
% for side in ALL_SIDES:
<%
@@ -16,7 +15,7 @@
"margin-%s" % side[0],
"LengthPercentageOrAuto",
"computed::LengthPercentageOrAuto::zero()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
aliases=maybe_moz_logical_alias(engine, side, "-moz-margin-%s"),
allow_quirks="No" if side[1] else "Yes",
animation_value_type="ComputedValue",
diff --git a/servo/components/style/properties/longhands/outline.mako.rs b/servo/components/style/properties/longhands/outline.mako.rs
index 8e7f956bf5..d366cbc996 100644
--- a/servo/components/style/properties/longhands/outline.mako.rs
+++ b/servo/components/style/properties/longhands/outline.mako.rs
@@ -3,18 +3,12 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Method %>
-<% data.new_style_struct("Outline",
- inherited=False,
- additional_methods=[Method("outline_has_nonzero_width", "bool")]) %>
-
-// TODO(pcwalton): `invert`
${helpers.predefined_type(
"outline-color",
"Color",
"computed_value::T::currentcolor()",
- engines="gecko servo-2013",
+ engines="gecko servo",
initial_specified_value="specified::Color::currentcolor()",
animation_value_type="AnimatedColor",
ignored_when_colors_disabled=True,
@@ -26,8 +20,7 @@ ${helpers.predefined_type(
"outline-style",
"OutlineStyle",
"computed::OutlineStyle::none()",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.2020.unimplemented",
+ engines="gecko servo",
initial_specified_value="specified::OutlineStyle::none()",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-ui/#propdef-outline-style",
@@ -38,8 +31,7 @@ ${helpers.predefined_type(
"outline-width",
"BorderSideWidth",
"app_units::Au::from_px(3)",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.2020.unimplemented",
+ engines="gecko servo",
initial_specified_value="specified::BorderSideWidth::medium()",
animation_value_type="NonNegativeLength",
spec="https://drafts.csswg.org/css-ui/#propdef-outline-width",
@@ -50,7 +42,7 @@ ${helpers.predefined_type(
"outline-offset",
"Length",
"crate::values::computed::Length::new(0.)",
- engines="gecko servo-2013",
+ engines="gecko servo",
animation_value_type="ComputedValue",
spec="https://drafts.csswg.org/css-ui/#propdef-outline-offset",
affects="overflow",
diff --git a/servo/components/style/properties/longhands/padding.mako.rs b/servo/components/style/properties/longhands/padding.mako.rs
index a165e2cd34..d3984b71d2 100644
--- a/servo/components/style/properties/longhands/padding.mako.rs
+++ b/servo/components/style/properties/longhands/padding.mako.rs
@@ -4,7 +4,6 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import ALL_SIDES, maybe_moz_logical_alias %>
-<% data.new_style_struct("Padding", inherited=False) %>
% for side in ALL_SIDES:
<%
@@ -16,7 +15,7 @@
"padding-%s" % side[0],
"NonNegativeLengthPercentage",
"computed::NonNegativeLengthPercentage::zero()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
aliases=maybe_moz_logical_alias(engine, side, "-moz-padding-%s"),
animation_value_type="NonNegativeLengthPercentage",
logical=side[1],
diff --git a/servo/components/style/properties/longhands/page.mako.rs b/servo/components/style/properties/longhands/page.mako.rs
index 86cd284e18..bb7729c722 100644
--- a/servo/components/style/properties/longhands/page.mako.rs
+++ b/servo/components/style/properties/longhands/page.mako.rs
@@ -5,8 +5,6 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import PAGE_RULE %>
-<% data.new_style_struct("Page", inherited=False) %>
-
${helpers.predefined_type(
"size",
"PageSize",
diff --git a/servo/components/style/properties/longhands/position.mako.rs b/servo/components/style/properties/longhands/position.mako.rs
index fb68baa6b4..e71803e026 100644
--- a/servo/components/style/properties/longhands/position.mako.rs
+++ b/servo/components/style/properties/longhands/position.mako.rs
@@ -2,19 +2,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-<%! from data import to_rust_ident %>
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import ALL_SIZES, PHYSICAL_SIDES, LOGICAL_SIDES %>
-<% data.new_style_struct("Position", inherited=False) %>
-
// "top" / "left" / "bottom" / "right"
% for side in PHYSICAL_SIDES:
${helpers.predefined_type(
side,
"LengthPercentageOrAuto",
"computed::LengthPercentageOrAuto::auto()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
spec="https://www.w3.org/TR/CSS2/visuren.html#propdef-%s" % side,
animation_value_type="ComputedValue",
allow_quirks="Yes",
@@ -29,7 +26,7 @@
"inset-%s" % side,
"LengthPercentageOrAuto",
"computed::LengthPercentageOrAuto::auto()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-logical-props/#propdef-inset-%s" % side,
animation_value_type="ComputedValue",
logical=True,
@@ -42,7 +39,7 @@ ${helpers.predefined_type(
"z-index",
"ZIndex",
"computed::ZIndex::auto()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
spec="https://www.w3.org/TR/CSS2/visuren.html#z-index",
animation_value_type="ComputedValue",
affects="paint",
@@ -55,8 +52,8 @@ ${helpers.predefined_type(
${helpers.single_keyword(
"flex-direction",
"row row-reverse column column-reverse",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.flexbox.enabled",
+ engines="gecko servo",
+ servo_pref="layout.flexbox.enabled",
spec="https://drafts.csswg.org/css-flexbox/#flex-direction-property",
extra_prefixes="webkit",
animation_value_type="discrete",
@@ -68,8 +65,8 @@ ${helpers.single_keyword(
${helpers.single_keyword(
"flex-wrap",
"nowrap wrap wrap-reverse",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.flexbox.enabled",
+ engines="gecko servo",
+ servo_pref="layout.flexbox.enabled",
spec="https://drafts.csswg.org/css-flexbox/#flex-wrap-property",
extra_prefixes="webkit",
animation_value_type="discrete",
@@ -78,12 +75,13 @@ ${helpers.single_keyword(
affects="layout",
)}
-% if engine == "servo-2013":
+% if engine in "servo":
// FIXME: Update Servo to support the same Syntax as Gecko.
${helpers.single_keyword(
"justify-content",
"flex-start stretch flex-end center space-between space-around",
- engines="servo-2013",
+ engines="servo",
+ servo_pref="layout.flexbox.enabled",
extra_prefixes="webkit",
spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
animation_value_type="discrete",
@@ -117,12 +115,13 @@ ${helpers.single_keyword(
)}
% endif
-% if engine in ["servo-2013", "servo-2020"]:
+% if engine == "servo":
// FIXME: Update Servo to support the same Syntax as Gecko.
${helpers.single_keyword(
"align-content",
"stretch flex-start flex-end center space-between space-around",
- engines="servo-2013",
+ engines="servo",
+ servo_pref="layout.flexbox.enabled",
extra_prefixes="webkit",
spec="https://drafts.csswg.org/css-align/#propdef-align-content",
animation_value_type="discrete",
@@ -133,8 +132,8 @@ ${helpers.single_keyword(
${helpers.single_keyword(
"align-items",
"stretch flex-start flex-end center baseline",
- engines="servo-2013 servo-2020",
- servo_2020_pref="layout.flexbox.enabled",
+ engines="servo",
+ servo_pref="layout.flexbox.enabled",
extra_prefixes="webkit",
spec="https://drafts.csswg.org/css-flexbox/#align-items-property",
animation_value_type="discrete",
@@ -195,8 +194,8 @@ ${helpers.predefined_type(
"flex-grow",
"NonNegativeNumber",
"From::from(0.0)",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.flexbox.enabled",
+ engines="gecko servo",
+ servo_pref="layout.flexbox.enabled",
spec="https://drafts.csswg.org/css-flexbox/#flex-grow-property",
extra_prefixes="webkit",
animation_value_type="NonNegativeNumber",
@@ -208,8 +207,8 @@ ${helpers.predefined_type(
"flex-shrink",
"NonNegativeNumber",
"From::from(1.0)",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.flexbox.enabled",
+ engines="gecko servo",
+ servo_pref="layout.flexbox.enabled",
spec="https://drafts.csswg.org/css-flexbox/#flex-shrink-property",
extra_prefixes="webkit",
animation_value_type="NonNegativeNumber",
@@ -218,13 +217,13 @@ ${helpers.predefined_type(
)}
// https://drafts.csswg.org/css-align/#align-self-property
-% if engine in ["servo-2013", "servo-2020"]:
+% if engine == "servo":
// FIXME: Update Servo to support the same syntax as Gecko.
${helpers.single_keyword(
"align-self",
"auto stretch flex-start flex-end center baseline",
- engines="servo-2013 servo-2020",
- servo_2020_pref="layout.flexbox.enabled",
+ engines="servo",
+ servo_pref="layout.flexbox.enabled",
extra_prefixes="webkit",
spec="https://drafts.csswg.org/css-flexbox/#propdef-align-self",
animation_value_type="discrete",
@@ -260,8 +259,8 @@ ${helpers.predefined_type(
"order",
"Integer",
"0",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.flexbox.enabled",
+ engines="gecko servo",
+ servo_pref="layout.flexbox.enabled",
extra_prefixes="webkit",
animation_value_type="ComputedValue",
spec="https://drafts.csswg.org/css-flexbox/#order-property",
@@ -273,8 +272,8 @@ ${helpers.predefined_type(
"flex-basis",
"FlexBasis",
"computed::FlexBasis::auto()",
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.flexbox.enabled",
+ engines="gecko servo",
+ servo_pref="layout.flexbox.enabled",
spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property",
extra_prefixes="webkit",
animation_value_type="FlexBasis",
@@ -294,7 +293,7 @@ ${helpers.predefined_type(
size,
"Size",
"computed::Size::auto()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
logical=logical,
logical_group="size",
allow_quirks="No" if logical else "Yes",
@@ -308,7 +307,7 @@ ${helpers.predefined_type(
"min-%s" % size,
"Size",
"computed::Size::auto()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
logical=logical,
logical_group="min-size",
allow_quirks="No" if logical else "Yes",
@@ -321,7 +320,7 @@ ${helpers.predefined_type(
"max-%s" % size,
"MaxSize",
"computed::MaxSize::none()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
logical=logical,
logical_group="max-size",
allow_quirks="No" if logical else "Yes",
@@ -335,7 +334,7 @@ ${helpers.predefined_type(
${helpers.single_keyword(
"box-sizing",
"content-box border-box",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
extra_prefixes="moz:layout.css.prefixes.box-sizing webkit",
spec="https://drafts.csswg.org/css-ui/#propdef-box-sizing",
gecko_enum_prefix="StyleBoxSizing",
@@ -436,9 +435,9 @@ ${helpers.predefined_type(
"column-gap",
"length::NonNegativeLengthPercentageOrNormal",
"computed::length::NonNegativeLengthPercentageOrNormal::normal()",
- engines="gecko servo-2013",
+ engines="gecko servo",
aliases="grid-column-gap" if engine == "gecko" else "",
- servo_2013_pref="layout.columns.enabled",
+ servo_pref="layout.columns.enabled",
spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap",
animation_value_type="NonNegativeLengthPercentageOrNormal",
servo_restyle_damage="reflow",
@@ -462,7 +461,8 @@ ${helpers.predefined_type(
"aspect-ratio",
"AspectRatio",
"computed::AspectRatio::auto()",
- engines="gecko servo-2013",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
animation_value_type="ComputedValue",
spec="https://drafts.csswg.org/css-sizing-4/#aspect-ratio",
servo_restyle_damage="reflow",
diff --git a/servo/components/style/properties/longhands/svg.mako.rs b/servo/components/style/properties/longhands/svg.mako.rs
index 10788d4802..12998d553b 100644
--- a/servo/components/style/properties/longhands/svg.mako.rs
+++ b/servo/components/style/properties/longhands/svg.mako.rs
@@ -4,8 +4,6 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% data.new_style_struct("SVG", inherited=False, gecko_name="SVGReset") %>
-
${helpers.single_keyword(
"vector-effect",
"none non-scaling-stroke",
diff --git a/servo/components/style/properties/longhands/table.mako.rs b/servo/components/style/properties/longhands/table.mako.rs
index 3a756636ad..059838bac1 100644
--- a/servo/components/style/properties/longhands/table.mako.rs
+++ b/servo/components/style/properties/longhands/table.mako.rs
@@ -4,12 +4,11 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% data.new_style_struct("Table", inherited=False) %>
-
${helpers.single_keyword(
"table-layout",
"auto fixed",
- engines="gecko servo-2013",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
gecko_ffi_name="mLayoutStrategy",
animation_value_type="discrete",
gecko_enum_prefix="StyleTableLayout",
diff --git a/servo/components/style/properties/longhands/text.mako.rs b/servo/components/style/properties/longhands/text.mako.rs
index 0ee8ba3168..68b8ff3ceb 100644
--- a/servo/components/style/properties/longhands/text.mako.rs
+++ b/servo/components/style/properties/longhands/text.mako.rs
@@ -3,15 +3,13 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Method %>
-
-<% data.new_style_struct("Text", inherited=False, gecko_name="TextReset") %>
${helpers.predefined_type(
"text-overflow",
"TextOverflow",
"computed::TextOverflow::get_initial_value()",
- engines="gecko servo-2013",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
animation_value_type="discrete",
boxed=True,
spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow",
@@ -22,7 +20,8 @@ ${helpers.predefined_type(
${helpers.single_keyword(
"unicode-bidi",
"normal embed isolate bidi-override isolate-override plaintext",
- engines="gecko servo-2013",
+ engines="gecko servo",
+ servo_pref="layout.legacy_layout",
gecko_enum_prefix="StyleUnicodeBidi",
animation_value_type="none",
spec="https://drafts.csswg.org/css-writing-modes/#propdef-unicode-bidi",
@@ -34,7 +33,7 @@ ${helpers.predefined_type(
"text-decoration-line",
"TextDecorationLine",
"specified::TextDecorationLine::none()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::TextDecorationLine::none()",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line",
@@ -45,7 +44,7 @@ ${helpers.predefined_type(
${helpers.single_keyword(
"text-decoration-style",
"solid double dotted dashed wavy -moz-none",
- engines="gecko servo-2020",
+ engines="gecko servo",
gecko_enum_prefix="StyleTextDecorationStyle",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style",
@@ -56,7 +55,7 @@ ${helpers.predefined_type(
"text-decoration-color",
"Color",
"computed_value::T::currentcolor()",
- engines="gecko servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::Color::currentcolor()",
animation_value_type="AnimatedColor",
ignored_when_colors_disabled=True,
diff --git a/servo/components/style/properties/longhands/ui.mako.rs b/servo/components/style/properties/longhands/ui.mako.rs
index 58006e0d65..762cb64e9a 100644
--- a/servo/components/style/properties/longhands/ui.mako.rs
+++ b/servo/components/style/properties/longhands/ui.mako.rs
@@ -3,11 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import DEFAULT_RULES_EXCEPT_KEYFRAME, Method %>
-
-// CSS Basic User Interface Module Level 1
-// https://drafts.csswg.org/css-ui-3/
-<% data.new_style_struct("UI", inherited=False, gecko_name="UIReset") %>
+<% from data import DEFAULT_RULES_EXCEPT_KEYFRAME %>
// TODO spec says that UAs should not support this
// we should probably remove from gecko (https://bugzilla.mozilla.org/show_bug.cgi?id=1328331)
@@ -145,7 +141,7 @@ ${helpers.predefined_type(
"transition-duration",
"Time",
"computed::Time::zero()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::Time::zero()",
parse_method="parse_non_negative",
vector=True,
@@ -160,7 +156,7 @@ ${helpers.predefined_type(
"transition-timing-function",
"TimingFunction",
"computed::TimingFunction::ease()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::TimingFunction::ease()",
vector=True,
need_index=True,
@@ -174,7 +170,7 @@ ${helpers.predefined_type(
"transition-property",
"TransitionProperty",
"computed::TransitionProperty::all()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::TransitionProperty::all()",
vector=True,
none_value="computed::TransitionProperty::none()",
@@ -189,7 +185,7 @@ ${helpers.predefined_type(
"transition-delay",
"Time",
"computed::Time::zero()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::Time::zero()",
vector=True,
need_index=True,
@@ -219,7 +215,7 @@ ${helpers.predefined_type(
"animation-name",
"AnimationName",
"computed::AnimationName::none()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::AnimationName::none()",
vector=True,
need_index=True,
@@ -234,7 +230,7 @@ ${helpers.predefined_type(
"animation-duration",
"Time",
"computed::Time::zero()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::Time::zero()",
parse_method="parse_non_negative",
vector=True,
@@ -251,7 +247,7 @@ ${helpers.predefined_type(
"animation-timing-function",
"TimingFunction",
"computed::TimingFunction::ease()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::TimingFunction::ease()",
vector=True,
need_index=True,
@@ -265,7 +261,7 @@ ${helpers.predefined_type(
"animation-iteration-count",
"AnimationIterationCount",
"computed::AnimationIterationCount::one()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::AnimationIterationCount::one()",
vector=True,
need_index=True,
@@ -280,7 +276,7 @@ ${helpers.predefined_type(
"animation-direction",
"AnimationDirection",
"computed::AnimationDirection::Normal",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::AnimationDirection::Normal",
vector=True,
need_index=True,
@@ -295,7 +291,7 @@ ${helpers.predefined_type(
"animation-play-state",
"AnimationPlayState",
"computed::AnimationPlayState::Running",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="computed::AnimationPlayState::Running",
vector=True,
need_index=True,
@@ -310,7 +306,7 @@ ${helpers.predefined_type(
"animation-fill-mode",
"AnimationFillMode",
"computed::AnimationFillMode::None",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="computed::AnimationFillMode::None",
vector=True,
need_index=True,
@@ -325,12 +321,13 @@ ${helpers.predefined_type(
"animation-composition",
"AnimationComposition",
"computed::AnimationComposition::Replace",
- engines="gecko",
+ engines="gecko servo",
initial_specified_value="computed::AnimationComposition::Replace",
vector=True,
need_index=True,
animation_value_type="none",
gecko_pref="layout.css.animation-composition.enabled",
+ servo_pref="layout.unimplemented",
spec="https://drafts.csswg.org/css-animations-2/#animation-composition",
affects="",
)}
@@ -339,7 +336,7 @@ ${helpers.predefined_type(
"animation-delay",
"Time",
"computed::Time::zero()",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
initial_specified_value="specified::Time::zero()",
vector=True,
need_index=True,
@@ -354,7 +351,8 @@ ${helpers.predefined_type(
"animation-timeline",
"AnimationTimeline",
"computed::AnimationTimeline::auto()",
- engines="gecko",
+ engines="gecko servo",
+ servo_pref="layout.unimplemented",
initial_specified_value="specified::AnimationTimeline::auto()",
vector=True,
need_index=True,
diff --git a/servo/components/style/properties/longhands/xul.mako.rs b/servo/components/style/properties/longhands/xul.mako.rs
index 8974ac30dc..f9de8eb239 100644
--- a/servo/components/style/properties/longhands/xul.mako.rs
+++ b/servo/components/style/properties/longhands/xul.mako.rs
@@ -3,10 +3,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" />
-<% from data import Method %>
-
-// Non-standard properties that Gecko uses for XUL elements.
-<% data.new_style_struct("XUL", inherited=False) %>
${helpers.single_keyword(
"-moz-box-align",
diff --git a/servo/components/style/properties/mod.rs b/servo/components/style/properties/mod.rs
index 7adb6d4ae6..4a08d67f77 100644
--- a/servo/components/style/properties/mod.rs
+++ b/servo/components/style/properties/mod.rs
@@ -17,12 +17,6 @@ pub use self::generated::*;
#[deny(missing_docs)]
pub mod generated {
include!(concat!(env!("OUT_DIR"), "/properties.rs"));
-
- #[cfg(feature = "gecko")]
- #[allow(unsafe_code, missing_docs)]
- pub mod gecko {
- include!(concat!(env!("OUT_DIR"), "/gecko_properties.rs"));
- }
}
use crate::custom_properties::{self, ComputedCustomProperties};
@@ -1040,7 +1034,7 @@ impl<'a> PropertyDeclarationId<'a> {
pub fn is_discrete_animatable(&self) -> bool {
match self {
Self::Longhand(longhand) => longhand.is_discrete_animatable(),
- // TODO(bug 1846516): Refine this?
+ // TODO(bug 1885995): Refine this.
Self::Custom(_) => true,
}
}
diff --git a/servo/components/style/properties/properties.mako.rs b/servo/components/style/properties/properties.mako.rs
index b08314d7d5..e438967c35 100644
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -87,10 +87,19 @@ macro_rules! expanded {
#[allow(missing_docs)]
pub mod longhands {
% for style_struct in data.style_structs:
- include!("${repr(os.path.join(OUT_DIR, 'longhands/{}.rs'.format(style_struct.name_lower)))[1:-1]}");
+ <% data.current_style_struct = style_struct %>
+ <%include file="/longhands/${style_struct.name_lower}.mako.rs" />
% endfor
}
+
+#[cfg(feature = "gecko")]
+#[allow(unsafe_code, missing_docs)]
+pub mod gecko {
+ <%include file="/gecko.mako.rs" />
+}
+
+
macro_rules! unwrap_or_initial {
($prop: ident) => (unwrap_or_initial!($prop, $prop));
($prop: ident, $expr: expr) =>
@@ -107,7 +116,7 @@ pub mod shorthands {
use crate::values::specified;
% for style_struct in data.style_structs:
- include!("${repr(os.path.join(OUT_DIR, 'shorthands/{}.rs'.format(style_struct.name_lower)))[1:-1]}");
+ <%include file="/shorthands/${style_struct.name_lower}.mako.rs" />
% endfor
// We didn't define the 'all' shorthand using the regular helpers:shorthand
@@ -142,7 +151,7 @@ pub mod shorthands {
data.declare_shorthand(
"all",
logical_longhands + other_longhands,
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-cascade-3/#all-shorthand"
)
ALL_SHORTHAND_LEN = len(logical_longhands) + len(other_longhands);
@@ -505,8 +514,7 @@ impl NonCustomPropertyId {
}] = [
% for property in data.longhands + data.shorthands + data.all_aliases():
<%
- attrs = {"servo-2013": "servo_2013_pref", "servo-2020": "servo_2020_pref"}
- pref = getattr(property, attrs[engine])
+ pref = getattr(property, "servo_pref")
%>
% if pref:
Some("${pref}"),
@@ -2482,7 +2490,7 @@ impl<'a> StyleBuilder<'a> {
}
% endif
- % if not property.is_vector or property.simple_vector_bindings or engine in ["servo-2013", "servo-2020"]:
+ % if not property.is_vector or property.simple_vector_bindings or engine == "servo":
/// Set the `${property.ident}` to the computed value `value`.
#[allow(non_snake_case)]
pub fn set_${property.ident}(
@@ -2736,7 +2744,13 @@ impl<'a> StyleBuilder<'a> {
if matches!(line_height, computed::LineHeight::Normal) {
self.add_flags(flag);
}
- device.calc_line_height(&font, writing_mode, None)
+ let lh = device.calc_line_height(&font, writing_mode, None);
+ if line_height_base == LineHeightBase::InheritedStyle {
+ // Apply our own zoom if our style source is the parent style.
+ computed::NonNegativeLength::new(self.get_box().clone_zoom().zoom(lh.px()))
+ } else {
+ lh
+ }
}
/// And access to inherited style structs.
@@ -2932,7 +2946,7 @@ const_assert!(std::mem::size_of::<longhands::${longhand.ident}::SpecifiedValue>(
% endif
% endfor
-% if engine in ["servo-2013", "servo-2020"]:
+% if engine == "servo":
% for effect_name in ["repaint", "reflow_out_of_flow", "reflow", "rebuild_and_reflow_inline", "rebuild_and_reflow"]:
macro_rules! restyle_damage_${effect_name} {
($old: ident, $new: ident, $damage: ident, [ $($effect:expr),* ]) => ({
diff --git a/servo/components/style/properties/shorthands/background.mako.rs b/servo/components/style/properties/shorthands/background.mako.rs
index 08838233f6..5fee5cb2b0 100644
--- a/servo/components/style/properties/shorthands/background.mako.rs
+++ b/servo/components/style/properties/shorthands/background.mako.rs
@@ -6,7 +6,7 @@
// TODO: other background-* properties
<%helpers:shorthand name="background"
- engines="gecko servo-2013 servo-2020"
+ engines="gecko servo"
sub_properties="background-color background-position-x background-position-y background-repeat
background-attachment background-image background-size background-origin
background-clip"
@@ -230,7 +230,7 @@
</%helpers:shorthand>
<%helpers:shorthand name="background-position"
- engines="gecko servo-2013 servo-2020"
+ engines="gecko servo"
flags="SHORTHAND_IN_GETCS"
sub_properties="background-position-x background-position-y"
spec="https://drafts.csswg.org/css-backgrounds-4/#the-background-position">
diff --git a/servo/components/style/properties/shorthands/border.mako.rs b/servo/components/style/properties/shorthands/border.mako.rs
index c6a87f3197..2eeb691e24 100644
--- a/servo/components/style/properties/shorthands/border.mako.rs
+++ b/servo/components/style/properties/shorthands/border.mako.rs
@@ -9,7 +9,7 @@ ${helpers.four_sides_shorthand(
"border-color",
"border-%s-color",
"specified::Color::parse",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-backgrounds/#border-color",
allow_quirks="Yes",
)}
@@ -17,13 +17,13 @@ ${helpers.four_sides_shorthand(
${helpers.four_sides_shorthand(
"border-style",
"border-%s-style",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-backgrounds/#border-style",
)}
<%helpers:shorthand
name="border-width"
- engines="gecko servo-2013 servo-2020"
+ engines="gecko servo"
sub_properties="${
' '.join('border-%s-width' % side
for side in PHYSICAL_SIDES)}"
@@ -104,7 +104,7 @@ pub fn parse_border<'i, 't>(
%>
<%helpers:shorthand
name="border-${side}"
- engines="gecko servo-2013 servo-2020"
+ engines="gecko servo"
sub_properties="${' '.join(
'border-%s-%s' % (side, prop)
for prop in ['width', 'style', 'color']
@@ -139,7 +139,7 @@ pub fn parse_border<'i, 't>(
% endfor
<%helpers:shorthand name="border"
- engines="gecko servo-2013 servo-2020"
+ engines="gecko servo"
sub_properties="${' '.join('border-%s-%s' % (side, prop)
for side in PHYSICAL_SIDES for prop in ['width', 'style', 'color']
)}
@@ -232,7 +232,7 @@ pub fn parse_border<'i, 't>(
<%helpers:shorthand
name="border-radius"
- engines="gecko servo-2013 servo-2020"
+ engines="gecko servo"
sub_properties="${' '.join(
'border-%s-radius' % (corner)
for corner in ['top-left', 'top-right', 'bottom-right', 'bottom-left']
@@ -278,7 +278,8 @@ pub fn parse_border<'i, 't>(
<%helpers:shorthand
name="border-image"
- engines="gecko servo-2013"
+ engines="gecko servo"
+ servo_pref="layout.legacy_layout",
sub_properties="border-image-outset
border-image-repeat border-image-slice border-image-source border-image-width"
extra_prefixes="moz:layout.css.prefixes.border-image webkit"
@@ -402,7 +403,7 @@ pub fn parse_border<'i, 't>(
spec = "https://drafts.csswg.org/css-logical/#propdef-border-%s-%s" % (axis, prop)
%>
<%helpers:shorthand
- engines="gecko servo-2013 servo-2020"
+ engines="gecko servo"
name="border-${axis}-${prop}"
sub_properties="${' '.join(
'border-%s-%s-%s' % (axis, side, prop)
@@ -448,7 +449,7 @@ pub fn parse_border<'i, 't>(
%>
<%helpers:shorthand
name="border-${axis}"
- engines="gecko servo-2013 servo-2020"
+ engines="gecko servo"
sub_properties="${' '.join(
'border-%s-%s-width' % (axis, side)
for side in ['start', 'end']
diff --git a/servo/components/style/properties/shorthands/box.mako.rs b/servo/components/style/properties/shorthands/box.mako.rs
index f644687dc0..078a511ae9 100644
--- a/servo/components/style/properties/shorthands/box.mako.rs
+++ b/servo/components/style/properties/shorthands/box.mako.rs
@@ -8,7 +8,7 @@ ${helpers.two_properties_shorthand(
"overflow",
"overflow-x",
"overflow-y",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
flags="SHORTHAND_IN_GETCS",
spec="https://drafts.csswg.org/css-overflow/#propdef-overflow",
)}
@@ -159,13 +159,7 @@ ${helpers.two_properties_shorthand(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> {
- let offset_position =
- if static_prefs::pref!("layout.css.motion-path-offset-position.enabled") {
- input.try_parse(|i| OffsetPosition::parse(context, i)).ok()
- } else {
- None
- };
-
+ let offset_position = input.try_parse(|i| OffsetPosition::parse(context, i)).ok();
let offset_path = input.try_parse(|i| OffsetPath::parse(context, i)).ok();
// Must have one of [offset-position, offset-path].
@@ -212,24 +206,19 @@ ${helpers.two_properties_shorthand(
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
- if let Some(offset_position) = self.offset_position {
- // The basic concept is: we must serialize offset-position or offset-path group.
- // offset-path group means "offset-path offset-distance offset-rotate".
- let must_serialize_path = *self.offset_path != OffsetPath::None
- || (!self.offset_distance.is_zero() || !self.offset_rotate.is_auto());
- let position_is_default = matches!(offset_position, OffsetPosition::Normal);
- if !position_is_default || !must_serialize_path {
- offset_position.to_css(dest)?;
- }
+ // The basic concept is: we must serialize offset-position or offset-path group.
+ // offset-path group means "offset-path offset-distance offset-rotate".
+ let must_serialize_path = *self.offset_path != OffsetPath::None
+ || (!self.offset_distance.is_zero() || !self.offset_rotate.is_auto());
+ let position_is_default = matches!(self.offset_position, OffsetPosition::Normal);
+ if !position_is_default || !must_serialize_path {
+ self.offset_position.to_css(dest)?;
+ }
- if must_serialize_path {
- if !position_is_default {
- dest.write_char(' ')?;
- }
- self.offset_path.to_css(dest)?;
+ if must_serialize_path {
+ if !position_is_default {
+ dest.write_char(' ')?;
}
- } else {
- // If the pref is off, we always show offset-path.
self.offset_path.to_css(dest)?;
}
diff --git a/servo/components/style/properties/shorthands/column.mako.rs b/servo/components/style/properties/shorthands/column.mako.rs
index 3740775a7e..4cf9a8d786 100644
--- a/servo/components/style/properties/shorthands/column.mako.rs
+++ b/servo/components/style/properties/shorthands/column.mako.rs
@@ -5,9 +5,9 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
<%helpers:shorthand name="columns"
- engines="gecko servo-2013"
+ engines="gecko servo"
sub_properties="column-width column-count"
- servo_2013_pref="layout.columns.enabled",
+ servo_pref="layout.columns.enabled"
spec="https://drafts.csswg.org/css-multicol/#propdef-columns">
use crate::properties::longhands::{column_count, column_width};
diff --git a/servo/components/style/properties/shorthands/counters.mako.rs b/servo/components/style/properties/shorthands/counters.mako.rs
new file mode 100644
index 0000000000..daa3e8897c
--- /dev/null
+++ b/servo/components/style/properties/shorthands/counters.mako.rs
@@ -0,0 +1,3 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
diff --git a/servo/components/style/properties/shorthands/effects.mako.rs b/servo/components/style/properties/shorthands/effects.mako.rs
new file mode 100644
index 0000000000..daa3e8897c
--- /dev/null
+++ b/servo/components/style/properties/shorthands/effects.mako.rs
@@ -0,0 +1,3 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
diff --git a/servo/components/style/properties/shorthands/font.mako.rs b/servo/components/style/properties/shorthands/font.mako.rs
index 17dcf9d926..aaf0895aa5 100644
--- a/servo/components/style/properties/shorthands/font.mako.rs
+++ b/servo/components/style/properties/shorthands/font.mako.rs
@@ -7,7 +7,7 @@
<%helpers:shorthand
name="font"
- engines="gecko servo-2013 servo-2020"
+ engines="gecko servo"
sub_properties="
font-style
font-variant-caps
@@ -321,7 +321,8 @@
</%helpers:shorthand>
<%helpers:shorthand name="font-variant"
- engines="gecko servo-2013"
+ engines="gecko servo"
+ servo_pref="layout.legacy_layout",
flags="SHORTHAND_IN_GETCS"
sub_properties="font-variant-caps
${'font-variant-alternates' if engine == 'gecko' else ''}
diff --git a/servo/components/style/properties/shorthands/inherited_box.mako.rs b/servo/components/style/properties/shorthands/inherited_box.mako.rs
new file mode 100644
index 0000000000..daa3e8897c
--- /dev/null
+++ b/servo/components/style/properties/shorthands/inherited_box.mako.rs
@@ -0,0 +1,3 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
diff --git a/servo/components/style/properties/shorthands/inherited_table.mako.rs b/servo/components/style/properties/shorthands/inherited_table.mako.rs
new file mode 100644
index 0000000000..daa3e8897c
--- /dev/null
+++ b/servo/components/style/properties/shorthands/inherited_table.mako.rs
@@ -0,0 +1,3 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
diff --git a/servo/components/style/properties/shorthands/inherited_ui.mako.rs b/servo/components/style/properties/shorthands/inherited_ui.mako.rs
new file mode 100644
index 0000000000..daa3e8897c
--- /dev/null
+++ b/servo/components/style/properties/shorthands/inherited_ui.mako.rs
@@ -0,0 +1,3 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
diff --git a/servo/components/style/properties/shorthands/list.mako.rs b/servo/components/style/properties/shorthands/list.mako.rs
index 2e234e3d8f..183c5ab5da 100644
--- a/servo/components/style/properties/shorthands/list.mako.rs
+++ b/servo/components/style/properties/shorthands/list.mako.rs
@@ -5,7 +5,7 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
<%helpers:shorthand name="list-style"
- engines="gecko servo-2013 servo-2020"
+ engines="gecko servo"
sub_properties="list-style-position list-style-image list-style-type"
spec="https://drafts.csswg.org/css-lists/#propdef-list-style">
use crate::properties::longhands::{list_style_image, list_style_position, list_style_type};
@@ -110,7 +110,11 @@
use longhands::list_style_type::SpecifiedValue as ListStyleType;
use longhands::list_style_image::SpecifiedValue as ListStyleImage;
let mut have_one_non_initial_value = false;
- if self.list_style_position != &ListStylePosition::Outside {
+ #[cfg(feature = "gecko")]
+ let position_is_initial = self.list_style_position == &ListStylePosition::Outside;
+ #[cfg(feature = "servo")]
+ let position_is_initial = matches!(self.list_style_position, None | Some(&ListStylePosition::Outside));
+ if !position_is_initial {
self.list_style_position.to_css(dest)?;
have_one_non_initial_value = true;
}
@@ -121,7 +125,11 @@
self.list_style_image.to_css(dest)?;
have_one_non_initial_value = true;
}
- if self.list_style_type != &ListStyleType::disc() {
+ #[cfg(feature = "gecko")]
+ let type_is_initial = self.list_style_type == &ListStyleType::disc();
+ #[cfg(feature = "servo")]
+ let type_is_initial = self.list_style_type == &ListStyleType::Disc;
+ if !type_is_initial {
if have_one_non_initial_value {
dest.write_char(' ')?;
}
@@ -129,7 +137,14 @@
have_one_non_initial_value = true;
}
if !have_one_non_initial_value {
+ #[cfg(feature = "gecko")]
self.list_style_position.to_css(dest)?;
+ #[cfg(feature = "servo")]
+ if let Some(position) = self.list_style_position {
+ position.to_css(dest)?;
+ } else {
+ self.list_style_type.to_css(dest)?;
+ }
}
Ok(())
}
diff --git a/servo/components/style/properties/shorthands/margin.mako.rs b/servo/components/style/properties/shorthands/margin.mako.rs
index 6b5bf7e467..ba994316a2 100644
--- a/servo/components/style/properties/shorthands/margin.mako.rs
+++ b/servo/components/style/properties/shorthands/margin.mako.rs
@@ -9,7 +9,7 @@ ${helpers.four_sides_shorthand(
"margin",
"margin-%s",
"specified::LengthPercentageOrAuto::parse",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-box/#propdef-margin",
rule_types_allowed=DEFAULT_RULES_AND_PAGE,
allow_quirks="Yes",
@@ -20,7 +20,7 @@ ${helpers.two_properties_shorthand(
"margin-block-start",
"margin-block-end",
"specified::LengthPercentageOrAuto::parse",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-logical/#propdef-margin-block"
)}
@@ -29,7 +29,7 @@ ${helpers.two_properties_shorthand(
"margin-inline-start",
"margin-inline-end",
"specified::LengthPercentageOrAuto::parse",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-logical/#propdef-margin-inline"
)}
diff --git a/servo/components/style/properties/shorthands/outline.mako.rs b/servo/components/style/properties/shorthands/outline.mako.rs
index 6ee8ed22c9..ff77e1175e 100644
--- a/servo/components/style/properties/shorthands/outline.mako.rs
+++ b/servo/components/style/properties/shorthands/outline.mako.rs
@@ -5,7 +5,7 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
<%helpers:shorthand name="outline"
- engines="gecko servo-2013"
+ engines="gecko servo"
sub_properties="outline-color outline-style outline-width"
spec="https://drafts.csswg.org/css-ui/#propdef-outline">
use crate::properties::longhands::{outline_color, outline_width, outline_style};
diff --git a/servo/components/style/properties/shorthands/padding.mako.rs b/servo/components/style/properties/shorthands/padding.mako.rs
index 11ddfed3b1..dad0193708 100644
--- a/servo/components/style/properties/shorthands/padding.mako.rs
+++ b/servo/components/style/properties/shorthands/padding.mako.rs
@@ -8,7 +8,7 @@ ${helpers.four_sides_shorthand(
"padding",
"padding-%s",
"specified::NonNegativeLengthPercentage::parse",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-box-3/#propdef-padding",
allow_quirks="Yes",
)}
@@ -18,7 +18,7 @@ ${helpers.two_properties_shorthand(
"padding-block-start",
"padding-block-end",
"specified::NonNegativeLengthPercentage::parse",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-logical/#propdef-padding-block"
)}
@@ -27,7 +27,7 @@ ${helpers.two_properties_shorthand(
"padding-inline-start",
"padding-inline-end",
"specified::NonNegativeLengthPercentage::parse",
- engines="gecko servo-2013 servo-2020",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-logical/#propdef-padding-inline"
)}
diff --git a/servo/components/style/properties/shorthands/page.mako.rs b/servo/components/style/properties/shorthands/page.mako.rs
new file mode 100644
index 0000000000..daa3e8897c
--- /dev/null
+++ b/servo/components/style/properties/shorthands/page.mako.rs
@@ -0,0 +1,3 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
diff --git a/servo/components/style/properties/shorthands/position.mako.rs b/servo/components/style/properties/shorthands/position.mako.rs
index ed7df5e27a..ac09bb27d6 100644
--- a/servo/components/style/properties/shorthands/position.mako.rs
+++ b/servo/components/style/properties/shorthands/position.mako.rs
@@ -5,8 +5,8 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
<%helpers:shorthand name="flex-flow"
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.flexbox.enabled",
+ engines="gecko servo",
+ servo_pref="layout.flexbox.enabled",
sub_properties="flex-direction flex-wrap"
extra_prefixes="webkit"
spec="https://drafts.csswg.org/css-flexbox/#flex-flow-property">
@@ -60,8 +60,8 @@
</%helpers:shorthand>
<%helpers:shorthand name="flex"
- engines="gecko servo-2013 servo-2020",
- servo_2020_pref="layout.flexbox.enabled",
+ engines="gecko servo",
+ servo_pref="layout.flexbox.enabled",
sub_properties="flex-grow flex-shrink flex-basis"
extra_prefixes="webkit"
derive_serialize="True"
@@ -858,7 +858,7 @@ ${helpers.four_sides_shorthand(
"inset",
"%s",
"specified::LengthPercentageOrAuto::parse",
- engines="gecko servo-2013",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-logical/#propdef-inset",
allow_quirks="No",
)}
@@ -868,7 +868,7 @@ ${helpers.two_properties_shorthand(
"inset-block-start",
"inset-block-end",
"specified::LengthPercentageOrAuto::parse",
- engines="gecko servo-2013",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-logical/#propdef-inset-block"
)}
@@ -877,7 +877,7 @@ ${helpers.two_properties_shorthand(
"inset-inline-start",
"inset-inline-end",
"specified::LengthPercentageOrAuto::parse",
- engines="gecko servo-2013",
+ engines="gecko servo",
spec="https://drafts.csswg.org/css-logical/#propdef-inset-inline"
)}
diff --git a/servo/components/style/properties/shorthands/table.mako.rs b/servo/components/style/properties/shorthands/table.mako.rs
new file mode 100644
index 0000000000..daa3e8897c
--- /dev/null
+++ b/servo/components/style/properties/shorthands/table.mako.rs
@@ -0,0 +1,3 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
diff --git a/servo/components/style/properties/shorthands/text.mako.rs b/servo/components/style/properties/shorthands/text.mako.rs
index 5b071be2c4..a913f81875 100644
--- a/servo/components/style/properties/shorthands/text.mako.rs
+++ b/servo/components/style/properties/shorthands/text.mako.rs
@@ -5,7 +5,7 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
<%helpers:shorthand name="text-decoration"
- engines="gecko servo-2013 servo-2020"
+ engines="gecko servo"
flags="SHORTHAND_IN_GETCS"
sub_properties="text-decoration-line
${' text-decoration-style text-decoration-color text-decoration-thickness' if engine == 'gecko' else ''}"
diff --git a/servo/components/style/properties/shorthands/ui.mako.rs b/servo/components/style/properties/shorthands/ui.mako.rs
index 4a27d5e003..1915c65a63 100644
--- a/servo/components/style/properties/shorthands/ui.mako.rs
+++ b/servo/components/style/properties/shorthands/ui.mako.rs
@@ -18,7 +18,7 @@ macro_rules! try_parse_one {
}
<%helpers:shorthand name="transition"
- engines="gecko servo-2013 servo-2020"
+ engines="gecko servo"
extra_prefixes="moz:layout.css.prefixes.transitions webkit"
sub_properties="transition-property transition-duration
transition-timing-function
@@ -204,7 +204,7 @@ macro_rules! try_parse_one {
</%helpers:shorthand>
<%helpers:shorthand name="animation"
- engines="gecko servo-2013 servo-2020"
+ engines="gecko servo"
extra_prefixes="moz:layout.css.prefixes.animations webkit"
sub_properties="animation-name animation-duration
animation-timing-function animation-delay
@@ -295,6 +295,13 @@ macro_rules! try_parse_one {
impl<'a> ToCss for LonghandsToSerialize<'a> {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
+ use crate::values::specified::easing::TimingFunction;
+ use crate::values::specified::{
+ AnimationDirection, AnimationFillMode, AnimationPlayState,
+ };
+ use crate::Zero;
+ use style_traits::values::SequenceWriter;
+
let len = self.animation_name.0.len();
// There should be at least one declared value
if len == 0 {
@@ -320,28 +327,83 @@ macro_rules! try_parse_one {
dest.write_str(", ")?;
}
- % for name in props[2:]:
- self.animation_${name}.0[i].to_css(dest)?;
- dest.write_char(' ')?;
+ // We follow the order of this syntax:
+ // <single-animation> =
+ // <animation-duration> ||
+ // <easing-function> ||
+ // <animation-delay> ||
+ // <single-animation-iteration-count> ||
+ // <single-animation-direction> ||
+ // <single-animation-fill-mode> ||
+ // <single-animation-play-state> ||
+ // [ none | <keyframes-name> ] ||
+ // <single-animation-timeline>
+ //
+ // https://drafts.csswg.org/css-animations-2/#animation-shorthand
+ //
+ // FIXME: Bug 1804574. The initial value of duration should be auto, per
+ // css-animations-2.
+ let has_duration = !self.animation_duration.0[i].is_zero();
+ let has_timing_function = !self.animation_timing_function.0[i].is_ease();
+ let has_delay = !self.animation_delay.0[i].is_zero();
+ let has_iteration_count = !self.animation_iteration_count.0[i].is_one();
+ let has_direction =
+ !matches!(self.animation_direction.0[i], AnimationDirection::Normal);
+ let has_fill_mode =
+ !matches!(self.animation_fill_mode.0[i], AnimationFillMode::None);
+ let has_play_state =
+ !matches!(self.animation_play_state.0[i], AnimationPlayState::Running);
+ let animation_name = &self.animation_name.0[i];
+ let has_name = !animation_name.is_none();
+ let has_timeline = match self.animation_timeline {
+ Some(timeline) => !timeline.0[i].is_auto(),
+ _ => false,
+ };
+
+ let mut writer = SequenceWriter::new(dest, " ");
+
+ // To avoid ambiguity, we have to serialize duration if both duration is initial
+ // but delay is not. (In other words, it's ambiguous if we serialize delay only.)
+ if has_duration || has_delay {
+ writer.item(&self.animation_duration.0[i])?;
+ }
+
+ if has_timing_function || TimingFunction::match_keywords(animation_name) {
+ writer.item(&self.animation_timing_function.0[i])?;
+ }
+
+ // For animation-delay and animation-iteration-count.
+ % for name in props[4:6]:
+ if has_${name} {
+ writer.item(&self.animation_${name}.0[i])?;
+ }
% endfor
- self.animation_name.0[i].to_css(dest)?;
+ if has_direction || AnimationDirection::match_keywords(animation_name) {
+ writer.item(&self.animation_direction.0[i])?;
+ }
+
+ if has_fill_mode || AnimationFillMode::match_keywords(animation_name) {
+ writer.item(&self.animation_fill_mode.0[i])?;
+ }
- // Based on the spec, the default values of other properties must be output in at
- // least the cases necessary to distinguish an animation-name. The serialization
- // order of animation-timeline is always later than animation-name, so it's fine
- // to not serialize it if it is the default value. It's still possible to
- // distinguish them (because we always serialize animation-name).
- // https://drafts.csswg.org/css-animations-1/#animation
- // https://drafts.csswg.org/css-animations-2/#typedef-single-animation
- //
- // Note: it's also fine to always serialize this. However, it seems Blink
- // doesn't serialize default animation-timeline now, so we follow the same rule.
- if let Some(ref timeline) = self.animation_timeline {
- if !timeline.0[i].is_auto() {
- dest.write_char(' ')?;
- timeline.0[i].to_css(dest)?;
- }
+ if has_play_state || AnimationPlayState::match_keywords(animation_name) {
+ writer.item(&self.animation_play_state.0[i])?;
+ }
+
+ // If all values are initial, we must serialize animation-name.
+ let has_any = {
+ has_timeline
+ % for name in props[2:]:
+ || has_${name}
+ % endfor
+ };
+ if has_name || !has_any {
+ writer.item(animation_name)?;
+ }
+
+ if has_timeline {
+ writer.item(&self.animation_timeline.unwrap().0[i])?;
}
}
Ok(())
diff --git a/servo/components/style/properties/shorthands/xul.mako.rs b/servo/components/style/properties/shorthands/xul.mako.rs
new file mode 100644
index 0000000000..daa3e8897c
--- /dev/null
+++ b/servo/components/style/properties/shorthands/xul.mako.rs
@@ -0,0 +1,3 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
diff --git a/servo/components/style/queries/condition.rs b/servo/components/style/queries/condition.rs
index e17e6abd2e..25af4cdb01 100644
--- a/servo/components/style/queries/condition.rs
+++ b/servo/components/style/queries/condition.rs
@@ -13,6 +13,7 @@ use crate::{error_reporting::ContextualParseError, parser::ParserContext};
use cssparser::{Parser, Token};
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
+use selectors::kleene_value::KleeneValue;
/// A binary `and` or `or` operator.
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)]
@@ -29,93 +30,6 @@ enum AllowOr {
No,
}
-/// https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics
-#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToCss)]
-pub enum KleeneValue {
- /// False
- False = 0,
- /// True
- True = 1,
- /// Either true or false, but we’re not sure which yet.
- Unknown,
-}
-
-impl From<bool> for KleeneValue {
- fn from(b: bool) -> Self {
- if b {
- Self::True
- } else {
- Self::False
- }
- }
-}
-
-impl KleeneValue {
- /// Turns this Kleene value to a bool, taking the unknown value as an
- /// argument.
- pub fn to_bool(self, unknown: bool) -> bool {
- match self {
- Self::True => true,
- Self::False => false,
- Self::Unknown => unknown,
- }
- }
-}
-
-impl std::ops::Not for KleeneValue {
- type Output = Self;
-
- fn not(self) -> Self {
- match self {
- Self::True => Self::False,
- Self::False => Self::True,
- Self::Unknown => Self::Unknown,
- }
- }
-}
-
-// Implements the logical and operation.
-impl std::ops::BitAnd for KleeneValue {
- type Output = Self;
-
- fn bitand(self, other: Self) -> Self {
- if self == Self::False || other == Self::False {
- return Self::False;
- }
- if self == Self::Unknown || other == Self::Unknown {
- return Self::Unknown;
- }
- Self::True
- }
-}
-
-// Implements the logical or operation.
-impl std::ops::BitOr for KleeneValue {
- type Output = Self;
-
- fn bitor(self, other: Self) -> Self {
- if self == Self::True || other == Self::True {
- return Self::True;
- }
- if self == Self::Unknown || other == Self::Unknown {
- return Self::Unknown;
- }
- Self::False
- }
-}
-
-impl std::ops::BitOrAssign for KleeneValue {
- fn bitor_assign(&mut self, other: Self) {
- *self = *self | other;
- }
-}
-
-impl std::ops::BitAndAssign for KleeneValue {
- fn bitand_assign(&mut self, other: Self) {
- *self = *self & other;
- }
-}
-
/// Represents a condition.
#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)]
pub enum QueryCondition {
diff --git a/servo/components/style/queries/feature.rs b/servo/components/style/queries/feature.rs
index 83ff7e7522..2d4c60c362 100644
--- a/servo/components/style/queries/feature.rs
+++ b/servo/components/style/queries/feature.rs
@@ -4,7 +4,6 @@
//! Query features.
-use super::condition::KleeneValue;
use crate::parser::ParserContext;
use crate::values::computed::{self, CSSPixelLength, Ratio, Resolution};
use crate::values::AtomString;
@@ -12,6 +11,7 @@ use crate::Atom;
use cssparser::Parser;
use std::fmt;
use style_traits::ParseError;
+use selectors::kleene_value::KleeneValue;
/// A generic discriminant for an enum value.
pub type KeywordDiscriminant = u8;
@@ -87,12 +87,12 @@ macro_rules! keyword_evaluator {
fn __evaluate(
context: &$crate::values::computed::Context,
value: Option<$crate::queries::feature::KeywordDiscriminant>,
- ) -> $crate::queries::condition::KleeneValue {
+ ) -> selectors::kleene_value::KleeneValue {
// This unwrap is ok because the only discriminants that get
// back to us is the ones that `parse` produces.
let value: Option<$keyword_type> =
value.map(|kw| ::num_traits::cast::FromPrimitive::from_u8(kw).unwrap());
- $crate::queries::condition::KleeneValue::from($actual_evaluator(context, value))
+ selectors::kleene_value::KleeneValue::from($actual_evaluator(context, value))
}
$crate::queries::feature::Evaluator::Enumerated {
diff --git a/servo/components/style/queries/feature_expression.rs b/servo/components/style/queries/feature_expression.rs
index c0171c2058..3450f85cba 100644
--- a/servo/components/style/queries/feature_expression.rs
+++ b/servo/components/style/queries/feature_expression.rs
@@ -8,16 +8,16 @@
use super::feature::{Evaluator, QueryFeatureDescription};
use super::feature::{FeatureFlags, KeywordDiscriminant};
use crate::parser::{Parse, ParserContext};
-use crate::queries::condition::KleeneValue;
use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
use crate::values::computed::{self, Ratio, ToComputedValue};
use crate::values::specified::{Integer, Length, Number, Resolution};
use crate::values::{AtomString, CSSFloat};
use crate::{Atom, Zero};
use cssparser::{Parser, Token};
-use std::cmp::{Ordering, PartialOrd};
+use std::cmp::Ordering;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
+use selectors::kleene_value::KleeneValue;
/// Whether we're parsing a media or container query feature.
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
diff --git a/servo/components/style/scoped_tls.rs b/servo/components/style/scoped_tls.rs
index 0d3267397a..6fb8730d18 100644
--- a/servo/components/style/scoped_tls.rs
+++ b/servo/components/style/scoped_tls.rs
@@ -8,7 +8,6 @@
#![deny(missing_docs)]
use crate::global_style_data::STYLO_MAX_THREADS;
-use rayon;
use std::cell::{Ref, RefCell, RefMut};
use std::ops::DerefMut;
diff --git a/servo/components/style/str.rs b/servo/components/style/str.rs
index 9badcdf413..fe598f546e 100644
--- a/servo/components/style/str.rs
+++ b/servo/components/style/str.rs
@@ -8,7 +8,6 @@
use num_traits::ToPrimitive;
use std::borrow::Cow;
-use std::convert::AsRef;
use std::iter::{Filter, Peekable};
use std::str::Split;
diff --git a/servo/components/style/style_adjuster.rs b/servo/components/style/style_adjuster.rs
index a993d79d6a..78a631ec14 100644
--- a/servo/components/style/style_adjuster.rs
+++ b/servo/components/style/style_adjuster.rs
@@ -294,11 +294,9 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
.add_flags(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE);
}
- #[cfg(feature = "servo-layout-2013")]
- {
- if self.style.get_parent_column().is_multicol() {
- self.style.add_flags(ComputedValueFlags::CAN_BE_FRAGMENTED);
- }
+ #[cfg(feature = "servo")]
+ if self.style.get_parent_column().is_multicol() {
+ self.style.add_flags(ComputedValueFlags::CAN_BE_FRAGMENTED);
}
}
diff --git a/servo/components/style/stylesheets/container_rule.rs b/servo/components/style/stylesheets/container_rule.rs
index 28c387fde2..77dfa0e179 100644
--- a/servo/components/style/stylesheets/container_rule.rs
+++ b/servo/components/style/stylesheets/container_rule.rs
@@ -11,7 +11,6 @@ use crate::dom::TElement;
use crate::logical_geometry::{LogicalSize, WritingMode};
use crate::parser::ParserContext;
use crate::properties::ComputedValues;
-use crate::queries::condition::KleeneValue;
use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription};
use crate::queries::values::Orientation;
use crate::queries::{FeatureType, QueryCondition};
@@ -31,6 +30,7 @@ use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
use servo_arc::Arc;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, ToCss};
+use selectors::kleene_value::KleeneValue;
/// A container rule.
#[derive(Debug, ToShmem)]
diff --git a/servo/components/style/stylesheets/import_rule.rs b/servo/components/style/stylesheets/import_rule.rs
index e96134b436..d539471590 100644
--- a/servo/components/style/stylesheets/import_rule.rs
+++ b/servo/components/style/stylesheets/import_rule.rs
@@ -20,7 +20,7 @@ use crate::values::CssUrl;
use cssparser::{Parser, SourceLocation};
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
-use to_shmem::{self, SharedMemoryBuilder, ToShmem};
+use to_shmem::{SharedMemoryBuilder, ToShmem};
/// A sheet that is held from an import rule.
#[cfg(feature = "gecko")]
diff --git a/servo/components/style/stylesheets/mod.rs b/servo/components/style/stylesheets/mod.rs
index 2bf75565de..6bb8e19fb4 100644
--- a/servo/components/style/stylesheets/mod.rs
+++ b/servo/components/style/stylesheets/mod.rs
@@ -23,15 +23,17 @@ mod property_rule;
mod rule_list;
mod rule_parser;
mod rules_iterator;
+mod starting_style_rule;
mod style_rule;
mod stylesheet;
pub mod supports_rule;
+mod scope_rule;
#[cfg(feature = "gecko")]
use crate::gecko_bindings::sugar::refptr::RefCounted;
#[cfg(feature = "gecko")]
use crate::gecko_bindings::{bindings, structs};
-use crate::parser::ParserContext;
+use crate::parser::{NestingContext, ParserContext};
use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use crate::str::CssStringWriter;
@@ -45,7 +47,7 @@ use std::fmt;
use std::mem::{self, ManuallyDrop};
use style_traits::ParsingMode;
#[cfg(feature = "gecko")]
-use to_shmem::{self, SharedMemoryBuilder, ToShmem};
+use to_shmem::{SharedMemoryBuilder, ToShmem};
pub use self::container_rule::ContainerRule;
pub use self::counter_style_rule::CounterStyleRule;
@@ -69,7 +71,9 @@ pub use self::rules_iterator::{AllRules, EffectiveRules};
pub use self::rules_iterator::{
EffectiveRulesIterator, NestedRuleIterationCondition, RulesIterator,
};
+pub use self::starting_style_rule::StartingStyleRule;
pub use self::style_rule::StyleRule;
+pub use self::scope_rule::ScopeRule;
pub use self::stylesheet::{AllowImportRules, SanitizationData, SanitizationKind};
pub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet};
pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentStylesheets};
@@ -132,7 +136,11 @@ impl Drop for UrlExtraData {
impl ToShmem for UrlExtraData {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
if self.0 & 1 == 0 {
- let shared_extra_datas = unsafe { &structs::URLExtraData_sShared };
+ let shared_extra_datas = unsafe {
+ std::ptr::addr_of!(structs::URLExtraData_sShared)
+ .as_ref()
+ .unwrap()
+ };
let self_ptr = self.as_ref() as *const _ as *mut _;
let sheet_id = shared_extra_datas
.iter()
@@ -265,6 +273,8 @@ pub enum CssRule {
Document(Arc<DocumentRule>),
LayerBlock(Arc<LayerBlockRule>),
LayerStatement(Arc<LayerStatementRule>),
+ Scope(Arc<ScopeRule>),
+ StartingStyle(Arc<StartingStyleRule>),
}
impl CssRule {
@@ -309,8 +319,14 @@ impl CssRule {
CssRule::Document(ref arc) => {
arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)
},
+ CssRule::StartingStyle(ref arc) => {
+ arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops)
+ }
// TODO(emilio): Add memory reporting for these rules.
CssRule::LayerBlock(_) | CssRule::LayerStatement(_) => 0,
+ CssRule::Scope(ref rule) => {
+ rule.unconditional_shallow_size_of(ops) + rule.size_of(guard, ops)
+ }
}
}
}
@@ -349,6 +365,9 @@ pub enum CssRuleType {
FontPaletteValues = 19,
// 20 is an arbitrary number to use for Property.
Property = 20,
+ Scope = 21,
+ // https://drafts.csswg.org/css-transitions-2/#the-cssstartingstylerule-interface
+ StartingStyle = 22,
}
impl CssRuleType {
@@ -436,6 +455,8 @@ impl CssRule {
CssRule::LayerBlock(_) => CssRuleType::LayerBlock,
CssRule::LayerStatement(_) => CssRuleType::LayerStatement,
CssRule::Container(_) => CssRuleType::Container,
+ CssRule::Scope(_) => CssRuleType::Scope,
+ CssRule::StartingStyle(_) => CssRuleType::StartingStyle,
}
}
@@ -464,7 +485,11 @@ impl CssRule {
None,
None,
);
- context.rule_types = insert_rule_context.containing_rule_types;
+ // Override the nesting context with existing data.
+ context.nesting_context = NestingContext::new(
+ insert_rule_context.containing_rule_types,
+ insert_rule_context.parse_relative_rule_type
+ );
let state = if !insert_rule_context.containing_rule_types.is_empty() {
State::Body
@@ -567,6 +592,12 @@ impl DeepCloneWithLock for CssRule {
CssRule::LayerBlock(ref arc) => {
CssRule::LayerBlock(Arc::new(arc.deep_clone_with_lock(lock, guard, params)))
},
+ CssRule::Scope(ref arc) => {
+ CssRule::Scope(Arc::new(arc.deep_clone_with_lock(lock, guard, params)))
+ },
+ CssRule::StartingStyle(ref arc) => {
+ CssRule::StartingStyle(Arc::new(arc.deep_clone_with_lock(lock, guard, params)))
+ },
}
}
}
@@ -592,6 +623,8 @@ impl ToCssWithGuard for CssRule {
CssRule::LayerBlock(ref rule) => rule.to_css(guard, dest),
CssRule::LayerStatement(ref rule) => rule.to_css(guard, dest),
CssRule::Container(ref rule) => rule.to_css(guard, dest),
+ CssRule::Scope(ref rule) => rule.to_css(guard, dest),
+ CssRule::StartingStyle(ref rule) => rule.to_css(guard, dest),
}
}
}
diff --git a/servo/components/style/stylesheets/page_rule.rs b/servo/components/style/stylesheets/page_rule.rs
index a1618309a3..cdcfe0b8a7 100644
--- a/servo/components/style/stylesheets/page_rule.rs
+++ b/servo/components/style/stylesheets/page_rule.rs
@@ -221,7 +221,7 @@ impl Parse for PageSelector {
) -> Result<Self, ParseError<'i>> {
let name = input
.try_parse(parse_page_name)
- .unwrap_or(AtomIdent(atom!("")));
+ .unwrap_or(AtomIdent::new(atom!("")));
let mut pseudos = PagePseudoClasses::default();
while let Ok(pc) = input.try_parse(PagePseudoClass::parse) {
pseudos.push(pc);
diff --git a/servo/components/style/stylesheets/rule_list.rs b/servo/components/style/stylesheets/rule_list.rs
index 1b9f330185..9fb7629d3d 100644
--- a/servo/components/style/stylesheets/rule_list.rs
+++ b/servo/components/style/stylesheets/rule_list.rs
@@ -16,6 +16,8 @@ use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps};
use servo_arc::Arc;
use std::fmt::{self, Write};
+use super::CssRuleType;
+
/// A list of CSS rules.
#[derive(Debug, ToShmem)]
pub struct CssRules(pub Vec<CssRule>);
@@ -136,6 +138,7 @@ pub trait CssRulesHelpers {
parent_stylesheet_contents: &StylesheetContents,
index: usize,
nested: CssRuleTypes,
+ parse_relative_rule_type: Option<CssRuleType>,
loader: Option<&dyn StylesheetLoader>,
allow_import_rules: AllowImportRules,
) -> Result<CssRule, RulesMutateError>;
@@ -149,6 +152,7 @@ impl CssRulesHelpers for Locked<CssRules> {
parent_stylesheet_contents: &StylesheetContents,
index: usize,
containing_rule_types: CssRuleTypes,
+ parse_relative_rule_type: Option<CssRuleType>,
loader: Option<&dyn StylesheetLoader>,
allow_import_rules: AllowImportRules,
) -> Result<CssRule, RulesMutateError> {
@@ -165,6 +169,7 @@ impl CssRulesHelpers for Locked<CssRules> {
rule_list: &rules.0,
index,
containing_rule_types,
+ parse_relative_rule_type,
};
// Steps 3, 4, 5, 6
diff --git a/servo/components/style/stylesheets/rule_parser.rs b/servo/components/style/stylesheets/rule_parser.rs
index 742ad5d250..634f7c1af3 100644
--- a/servo/components/style/stylesheets/rule_parser.rs
+++ b/servo/components/style/stylesheets/rule_parser.rs
@@ -23,6 +23,8 @@ use crate::stylesheets::font_feature_values_rule::parse_family_name_list;
use crate::stylesheets::import_rule::{ImportLayer, ImportRule, ImportSupportsCondition};
use crate::stylesheets::keyframes_rule::parse_keyframe_list;
use crate::stylesheets::layer_rule::{LayerBlockRule, LayerName, LayerStatementRule};
+use crate::stylesheets::scope_rule::{ScopeBounds, ScopeRule};
+use crate::stylesheets::starting_style_rule::StartingStyleRule;
use crate::stylesheets::supports_rule::SupportsCondition;
use crate::stylesheets::{
AllowImportRules, CorsMode, CssRule, CssRuleType, CssRuleTypes, CssRules, DocumentRule,
@@ -50,6 +52,8 @@ pub struct InsertRuleContext<'a> {
pub index: usize,
/// The containing rule types of our ancestors.
pub containing_rule_types: CssRuleTypes,
+ /// Rule type determining if and how we parse relative selector syntax.
+ pub parse_relative_rule_type: Option<CssRuleType>,
}
impl<'a> InsertRuleContext<'a> {
@@ -231,6 +235,10 @@ pub enum AtRulePrelude {
Namespace(Option<Prefix>, Namespace),
/// A @layer rule prelude.
Layer(Vec<LayerName>),
+ /// A @scope rule prelude.
+ Scope(ScopeBounds),
+ /// A @starting-style prelude.
+ StartingStyle,
}
impl AtRulePrelude {
@@ -251,6 +259,8 @@ impl AtRulePrelude {
Self::Margin(..) => "margin",
Self::Namespace(..) => "namespace",
Self::Layer(..) => "layer",
+ Self::Scope(..) => "scope",
+ Self::StartingStyle => "starting-style",
}
}
}
@@ -483,18 +493,29 @@ impl NestedParseResult {
impl<'a, 'i> NestedRuleParser<'a, 'i> {
#[inline]
fn in_style_rule(&self) -> bool {
- self.context.rule_types.contains(CssRuleType::Style)
+ self.context
+ .nesting_context
+ .rule_types
+ .contains(CssRuleType::Style)
}
#[inline]
fn in_page_rule(&self) -> bool {
- self.context.rule_types.contains(CssRuleType::Page)
+ self.context
+ .nesting_context
+ .rule_types
+ .contains(CssRuleType::Page)
}
#[inline]
fn in_style_or_page_rule(&self) -> bool {
let types = CssRuleTypes::from_bits(CssRuleType::Style.bit() | CssRuleType::Page.bit());
- self.context.rule_types.intersects(types)
+ self.context.nesting_context.rule_types.intersects(types)
+ }
+
+ #[inline]
+ fn parse_relative(&self) -> ParseRelative {
+ self.context.nesting_context.parse_relative
}
// https://drafts.csswg.org/css-nesting/#conditionals
@@ -507,7 +528,9 @@ impl<'a, 'i> NestedRuleParser<'a, 'i> {
AtRulePrelude::Supports(..) |
AtRulePrelude::Container(..) |
AtRulePrelude::Document(..) |
- AtRulePrelude::Layer(..) => true,
+ AtRulePrelude::Layer(..) |
+ AtRulePrelude::Scope(..) |
+ AtRulePrelude::StartingStyle => true,
AtRulePrelude::Namespace(..) |
AtRulePrelude::FontFace |
@@ -523,10 +546,9 @@ impl<'a, 'i> NestedRuleParser<'a, 'i> {
}
fn nest_for_rule<R>(&mut self, rule_type: CssRuleType, cb: impl FnOnce(&mut Self) -> R) -> R {
- let old_rule_types = self.context.rule_types;
- self.context.rule_types.insert(rule_type);
+ let old = self.context.nesting_context.save(rule_type);
let r = cb(self);
- self.context.rule_types = old_rule_types;
+ self.context.nesting_context.restore(old);
r
}
@@ -701,6 +723,13 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
let cond = DocumentCondition::parse(&self.context, input)?;
AtRulePrelude::Document(cond)
},
+ "scope" if static_prefs::pref!("layout.css.at-scope.enabled") => {
+ let bounds = ScopeBounds::parse(&self.context, input, self.in_style_rule());
+ AtRulePrelude::Scope(bounds)
+ },
+ "starting-style" if static_prefs::pref!("layout.css.starting-style-at-rules.enabled") => {
+ AtRulePrelude::StartingStyle
+ },
_ => {
if static_prefs::pref!("layout.css.margin-rules.enabled") {
if let Some(margin_rule_type) = MarginRuleType::match_name(&name) {
@@ -862,11 +891,30 @@ impl<'a, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'i> {
block: Arc::new(self.shared_lock.wrap(declarations)),
source_location: start.source_location(),
}))
- }
+ },
AtRulePrelude::Import(..) | AtRulePrelude::Namespace(..) => {
// These rules don't have blocks.
return Err(input.new_unexpected_token_error(cssparser::Token::CurlyBracketBlock));
},
+ AtRulePrelude::Scope(bounds) => {
+ let source_location = start.source_location();
+ CssRule::Scope(Arc::new(ScopeRule {
+ bounds,
+ rules: self
+ .parse_nested(input, CssRuleType::Scope)
+ .into_rules(self.shared_lock, source_location),
+ source_location,
+ }))
+ },
+ AtRulePrelude::StartingStyle => {
+ let source_location = start.source_location();
+ CssRule::StartingStyle(Arc::new(StartingStyleRule {
+ rules: self
+ .parse_nested(input, CssRuleType::StartingStyle)
+ .into_rules(self.shared_lock, source_location),
+ source_location,
+ }))
+ },
};
self.rules.push(rule);
Ok(())
@@ -913,12 +961,7 @@ impl<'a, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'i> {
url_data: self.context.url_data,
for_supports_rule: false,
};
- let parse_relative = if self.in_style_rule() {
- ParseRelative::ForNesting
- } else {
- ParseRelative::No
- };
- SelectorList::parse(&selector_parser, input, parse_relative)
+ SelectorList::parse(&selector_parser, input, self.parse_relative())
}
fn parse_block<'t>(
diff --git a/servo/components/style/stylesheets/rules_iterator.rs b/servo/components/style/stylesheets/rules_iterator.rs
index 76d41c8184..60f3df1ea9 100644
--- a/servo/components/style/stylesheets/rules_iterator.rs
+++ b/servo/components/style/stylesheets/rules_iterator.rs
@@ -116,6 +116,8 @@ where
Some(supports_rule.rules.read_with(guard).0.iter())
},
CssRule::LayerBlock(ref layer_rule) => Some(layer_rule.rules.read_with(guard).0.iter()),
+ CssRule::Scope(ref rule) => Some(rule.rules.read_with(guard).0.iter()),
+ CssRule::StartingStyle(ref rule) => Some(rule.rules.read_with(guard).0.iter()),
}
}
}
diff --git a/servo/components/style/stylesheets/scope_rule.rs b/servo/components/style/stylesheets/scope_rule.rs
new file mode 100644
index 0000000000..6816c433a7
--- /dev/null
+++ b/servo/components/style/stylesheets/scope_rule.rs
@@ -0,0 +1,161 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! A [`@scope`][scope] rule.
+//!
+//! [scope]: https://drafts.csswg.org/css-cascade-6/#scoped-styles
+
+use crate::parser::ParserContext;
+use crate::selector_parser::{SelectorImpl, SelectorParser};
+use crate::shared_lock::{
+ DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,
+};
+use crate::str::CssStringWriter;
+use crate::stylesheets::CssRules;
+use cssparser::{Parser, SourceLocation, ToCss};
+#[cfg(feature = "gecko")]
+use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalSizeOf, MallocUnconditionalShallowSizeOf};
+use selectors::parser::{ParseRelative, SelectorList};
+use servo_arc::Arc;
+use std::fmt::{self, Write};
+use style_traits::CssWriter;
+
+/// A scoped rule.
+#[derive(Debug, ToShmem)]
+pub struct ScopeRule {
+ /// Bounds at which this rule applies.
+ pub bounds: ScopeBounds,
+ /// The nested rules inside the block.
+ pub rules: Arc<Locked<CssRules>>,
+ /// The source position where this rule was found.
+ pub source_location: SourceLocation,
+}
+
+impl DeepCloneWithLock for ScopeRule {
+ fn deep_clone_with_lock(
+ &self,
+ lock: &SharedRwLock,
+ guard: &SharedRwLockReadGuard,
+ params: &DeepCloneParams,
+ ) -> Self {
+ let rules = self.rules.read_with(guard);
+ Self {
+ bounds: self.bounds.clone(),
+ rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
+
+impl ToCssWithGuard for ScopeRule {
+ fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
+ dest.write_str("@scope")?;
+ {
+ let mut writer = CssWriter::new(dest);
+ if let Some(start) = self.bounds.start.as_ref() {
+ writer.write_str(" (")?;
+ start.to_css(&mut writer)?;
+ writer.write_char(')')?;
+ }
+ if let Some(end) = self.bounds.end.as_ref() {
+ writer.write_str(" to (")?;
+ end.to_css(&mut writer)?;
+ writer.write_char(')')?;
+ }
+ }
+ self.rules.read_with(guard).to_css_block(guard, dest)
+ }
+}
+
+impl ScopeRule {
+ /// Measure heap usage.
+ #[cfg(feature = "gecko")]
+ pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
+ self.rules.unconditional_shallow_size_of(ops) +
+ self.rules.read_with(guard).size_of(guard, ops) +
+ self.bounds.size_of(ops)
+ }
+}
+
+/// Bounds of the scope.
+#[derive(Debug, Clone, ToShmem)]
+pub struct ScopeBounds {
+ /// Start of the scope.
+ pub start: Option<SelectorList<SelectorImpl>>,
+ /// End of the scope.
+ pub end: Option<SelectorList<SelectorImpl>>,
+}
+
+impl ScopeBounds {
+ #[cfg(feature = "gecko")]
+ fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
+ fn bound_size_of(bound: &Option<SelectorList<SelectorImpl>>, ops: &mut MallocSizeOfOps) -> usize {
+ bound.as_ref().map(|list| list.unconditional_size_of(ops)).unwrap_or(0)
+ }
+ bound_size_of(&self.start, ops) + bound_size_of(&self.end, ops)
+ }
+}
+
+fn parse_scope<'a>(
+ context: &ParserContext,
+ input: &mut Parser<'a, '_>,
+ in_style_rule: bool,
+ for_end: bool
+) -> Option<SelectorList<SelectorImpl>> {
+ input.try_parse(|input| {
+ if for_end {
+ input.expect_ident_matching("to")?;
+ }
+ input.expect_parenthesis_block()?;
+ input.parse_nested_block(|input| {
+ if input.is_exhausted() {
+ return Ok(None);
+ }
+ let selector_parser = SelectorParser {
+ stylesheet_origin: context.stylesheet_origin,
+ namespaces: &context.namespaces,
+ url_data: context.url_data,
+ for_supports_rule: false,
+ };
+ let parse_relative = if for_end {
+ ParseRelative::ForScope
+ } else if in_style_rule {
+ ParseRelative::ForNesting
+ } else {
+ ParseRelative::No
+ };
+ Ok(Some(SelectorList::parse_forgiving(
+ &selector_parser,
+ input,
+ parse_relative,
+ )?))
+ })
+ })
+ .ok()
+ .flatten()
+}
+
+impl ScopeBounds {
+ /// Parse a container condition.
+ pub fn parse<'a>(
+ context: &ParserContext,
+ input: &mut Parser<'a, '_>,
+ in_style_rule: bool,
+ ) -> Self {
+ let start = parse_scope(
+ context,
+ input,
+ in_style_rule,
+ false
+ );
+
+ let end = parse_scope(
+ context,
+ input,
+ in_style_rule,
+ true
+ );
+ Self { start, end }
+ }
+}
diff --git a/servo/components/style/stylesheets/starting_style_rule.rs b/servo/components/style/stylesheets/starting_style_rule.rs
new file mode 100644
index 0000000000..3c2627582b
--- /dev/null
+++ b/servo/components/style/stylesheets/starting_style_rule.rs
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! before-change style: the `@starting-style` rules.
+//! https://drafts.csswg.org/css-transitions-2/#defining-before-change-style
+
+use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
+use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
+use crate::str::CssStringWriter;
+use crate::stylesheets::CssRules;
+use cssparser::SourceLocation;
+use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
+use servo_arc::Arc;
+use std::fmt::{self, Debug, Write};
+
+/// A [`@starting-style`][starting-style] rule.
+///
+/// [starting-style]: https://drafts.csswg.org/css-transitions-2/#at-ruledef-starting-style
+#[derive(Debug, ToShmem)]
+pub struct StartingStyleRule {
+ /// The nested rules to this starting-style rule.
+ pub rules: Arc<Locked<CssRules>>,
+ /// The source position where this starting-style rule was found.
+ pub source_location: SourceLocation,
+}
+
+impl StartingStyleRule {
+ /// Measure heap usage.
+ #[cfg(feature = "gecko")]
+ pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
+ self.rules.unconditional_shallow_size_of(ops) +
+ self.rules.read_with(guard).size_of(guard, ops)
+ }
+}
+
+impl ToCssWithGuard for StartingStyleRule {
+ fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
+ dest.write_str("@starting-style")?;
+ self.rules.read_with(guard).to_css_block(guard, dest)
+ }
+}
+
+impl DeepCloneWithLock for StartingStyleRule {
+ fn deep_clone_with_lock(
+ &self,
+ lock: &SharedRwLock,
+ guard: &SharedRwLockReadGuard,
+ params: &DeepCloneParams,
+ ) -> Self {
+ let rules = self.rules.read_with(guard);
+ StartingStyleRule {
+ rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))),
+ source_location: self.source_location.clone(),
+ }
+ }
+}
diff --git a/servo/components/style/stylesheets/stylesheet.rs b/servo/components/style/stylesheets/stylesheet.rs
index 1604022871..84d84b0d46 100644
--- a/servo/components/style/stylesheets/stylesheet.rs
+++ b/servo/components/style/stylesheets/stylesheet.rs
@@ -333,7 +333,11 @@ impl SanitizationKind {
// TODO(emilio): Perhaps Layer should not be always sanitized? But
// we sanitize @media and co, so this seems safer for now.
CssRule::LayerStatement(..) |
- CssRule::LayerBlock(..) => false,
+ CssRule::LayerBlock(..) |
+ // TODO(dshin): Same comment as Layer applies - shouldn't give away
+ // something like display size - erring on the side of "safe" for now.
+ CssRule::Scope(..) |
+ CssRule::StartingStyle(..) => false,
CssRule::FontFace(..) | CssRule::Namespace(..) | CssRule::Style(..) => true,
diff --git a/servo/components/style/stylist.rs b/servo/components/style/stylist.rs
index cc1cb75689..06cbe6276b 100644
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -31,7 +31,7 @@ use crate::rule_collector::RuleCollector;
use crate::rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
use crate::sharing::RevalidationResult;
use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet, SelectorMap, SelectorMapEntry};
-use crate::selector_parser::{PerPseudoElementMap, PseudoElement, SelectorImpl, SnapshotMap};
+use crate::selector_parser::{PerPseudoElementMap, PseudoElement, SelectorImpl, NonTSPseudoClass, SnapshotMap};
use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
use crate::stylesheet_set::{DataValidity, DocumentStylesheetSet, SheetRebuildKind};
use crate::stylesheet_set::{DocumentStylesheetFlusher, SheetCollectionFlusher};
@@ -48,7 +48,7 @@ use crate::stylesheets::{
PerOriginIter,
};
use crate::stylesheets::{StyleRule, StylesheetContents, StylesheetInDocument};
-use crate::values::computed;
+use crate::values::{computed, AtomIdent};
use crate::AllocErr;
use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom};
use dom::{DocumentState, ElementState};
@@ -113,8 +113,6 @@ unsafe impl Send for CascadeDataCacheKey {}
unsafe impl Sync for CascadeDataCacheKey {}
trait CascadeDataCacheEntry: Sized {
- /// Returns a reference to the cascade data.
- fn cascade_data(&self) -> &CascadeData;
/// Rebuilds the cascade data for the new stylesheet collection. The
/// collection is guaranteed to be dirty.
fn rebuild<S>(
@@ -269,10 +267,6 @@ lazy_static! {
}
impl CascadeDataCacheEntry for UserAgentCascadeData {
- fn cascade_data(&self) -> &CascadeData {
- &self.cascade_data
- }
-
fn rebuild<S>(
device: &Device,
quirks_mode: QuirksMode,
@@ -2023,6 +2017,11 @@ struct StylistSelectorVisitor<'a> {
/// selectors.
nth_of_attribute_dependencies: &'a mut PrecomputedHashSet<LocalName>,
+ /// The filter with the local names of custom states in selectors for
+ /// within the selector list of :nth-child(... of <selector list>)
+ /// selectors.
+ nth_of_custom_state_dependencies: &'a mut PrecomputedHashSet<AtomIdent>,
+
/// All the states selectors in the page reference.
state_dependencies: &'a mut ElementState,
@@ -2144,6 +2143,16 @@ impl<'a> SelectorVisitor for StylistSelectorVisitor<'a> {
component_needs_revalidation(s, self.passed_rightmost_selector);
match *s {
+ Component::NonTSPseudoClass(NonTSPseudoClass::CustomState(ref name)) => {
+ // CustomStateSet is special cased as it is a functional pseudo
+ // class with unbounded inner values. This is different to
+ // other psuedo class like :emtpy or :dir() which can be packed
+ // into the ElementState bitflags. For CustomState, however,
+ // the state name should be checked for presence in the selector.
+ if self.in_selector_list_of.relevant_to_nth_of_dependencies() {
+ self.nth_of_custom_state_dependencies.insert(name.0.clone());
+ }
+ },
Component::NonTSPseudoClass(ref p) => {
self.state_dependencies.insert(p.state_flag());
self.document_state_dependencies
@@ -2377,6 +2386,11 @@ pub struct CascadeData {
/// an element when an irrelevant attribute changes.
nth_of_attribute_dependencies: PrecomputedHashSet<LocalName>,
+ /// The custom states that appear in the selector list of
+ /// :nth-child(... of <selector list>). Used to avoid restyling siblings of
+ /// an element when an irrelevant custom state changes.
+ nth_of_custom_state_dependencies: PrecomputedHashSet<AtomIdent>,
+
/// The element state bits that are relied on by selectors. Like
/// `attribute_dependencies`, this is used to avoid taking element snapshots
/// when an irrelevant element state bit changes.
@@ -2456,6 +2470,7 @@ impl CascadeData {
nth_of_mapped_ids: PrecomputedHashSet::default(),
nth_of_class_dependencies: PrecomputedHashSet::default(),
nth_of_attribute_dependencies: PrecomputedHashSet::default(),
+ nth_of_custom_state_dependencies: PrecomputedHashSet::default(),
nth_of_state_dependencies: ElementState::empty(),
attribute_dependencies: PrecomputedHashSet::default(),
state_dependencies: ElementState::empty(),
@@ -2535,6 +2550,13 @@ impl CascadeData {
self.state_dependencies.intersects(state)
}
+ /// Returns whether the given Custom State is relied upon by a selector
+ /// of some rule in the selector list of :nth-child(... of <selector list>).
+ #[inline]
+ pub fn has_nth_of_custom_state_dependency(&self, state: &AtomIdent) -> bool {
+ self.nth_of_custom_state_dependencies.contains(state)
+ }
+
/// Returns whether the given ElementState bit is relied upon by a selector
/// of some rule in the selector list of :nth-child(... of <selector list>).
#[inline]
@@ -2664,6 +2686,7 @@ impl CascadeData {
self.relative_selector_invalidation_map.shrink_if_needed();
self.attribute_dependencies.shrink_if_needed();
self.nth_of_attribute_dependencies.shrink_if_needed();
+ self.nth_of_custom_state_dependencies.shrink_if_needed();
self.nth_of_class_dependencies.shrink_if_needed();
self.nth_of_mapped_ids.shrink_if_needed();
self.mapped_ids.shrink_if_needed();
@@ -2859,6 +2882,8 @@ impl CascadeData {
nth_of_class_dependencies: &mut self.nth_of_class_dependencies,
nth_of_attribute_dependencies: &mut self
.nth_of_attribute_dependencies,
+ nth_of_custom_state_dependencies: &mut self
+ .nth_of_custom_state_dependencies,
state_dependencies: &mut self.state_dependencies,
nth_of_state_dependencies: &mut self.nth_of_state_dependencies,
document_state_dependencies: &mut self.document_state_dependencies,
@@ -3234,7 +3259,9 @@ impl CascadeData {
CssRule::LayerBlock(..) |
CssRule::LayerStatement(..) |
CssRule::FontPaletteValues(..) |
- CssRule::FontFeatureValues(..) => {
+ CssRule::FontFeatureValues(..) |
+ CssRule::Scope(..) |
+ CssRule::StartingStyle(..) => {
// Not affected by device changes.
continue;
},
@@ -3323,6 +3350,7 @@ impl CascadeData {
self.relative_selector_invalidation_map.clear();
self.attribute_dependencies.clear();
self.nth_of_attribute_dependencies.clear();
+ self.nth_of_custom_state_dependencies.clear();
self.nth_of_class_dependencies.clear();
self.state_dependencies = ElementState::empty();
self.nth_of_state_dependencies = ElementState::empty();
@@ -3335,10 +3363,6 @@ impl CascadeData {
}
impl CascadeDataCacheEntry for CascadeData {
- fn cascade_data(&self) -> &CascadeData {
- self
- }
-
fn rebuild<S>(
device: &Device,
quirks_mode: QuirksMode,
@@ -3482,6 +3506,7 @@ pub fn needs_revalidation_for_testing(s: &Selector<SelectorImpl>) -> bool {
let mut attribute_dependencies = Default::default();
let mut nth_of_class_dependencies = Default::default();
let mut nth_of_attribute_dependencies = Default::default();
+ let mut nth_of_custom_state_dependencies = Default::default();
let mut state_dependencies = ElementState::empty();
let mut nth_of_state_dependencies = ElementState::empty();
let mut document_state_dependencies = DocumentState::empty();
@@ -3494,6 +3519,7 @@ pub fn needs_revalidation_for_testing(s: &Selector<SelectorImpl>) -> bool {
attribute_dependencies: &mut attribute_dependencies,
nth_of_class_dependencies: &mut nth_of_class_dependencies,
nth_of_attribute_dependencies: &mut nth_of_attribute_dependencies,
+ nth_of_custom_state_dependencies: &mut nth_of_custom_state_dependencies,
state_dependencies: &mut state_dependencies,
nth_of_state_dependencies: &mut nth_of_state_dependencies,
document_state_dependencies: &mut document_state_dependencies,
diff --git a/servo/components/style/traversal.rs b/servo/components/style/traversal.rs
index d63c3cb965..fa256d0f60 100644
--- a/servo/components/style/traversal.rs
+++ b/servo/components/style/traversal.rs
@@ -688,7 +688,7 @@ where
element.finish_restyle(context, data, new_styles, important_rules_changed)
}
-#[cfg(feature = "servo-layout-2013")]
+#[cfg(feature = "servo")]
fn notify_paint_worklet<E>(context: &StyleContext<E>, data: &ElementData)
where
E: TElement,
@@ -726,7 +726,7 @@ where
}
}
-#[cfg(not(feature = "servo-layout-2013"))]
+#[cfg(not(feature = "servo"))]
fn notify_paint_worklet<E>(_context: &StyleContext<E>, _data: &ElementData)
where
E: TElement,
diff --git a/servo/components/style/values/computed/basic_shape.rs b/servo/components/style/values/computed/basic_shape.rs
index d39110ec1c..21df7baf93 100644
--- a/servo/components/style/values/computed/basic_shape.rs
+++ b/servo/components/style/values/computed/basic_shape.rs
@@ -7,9 +7,12 @@
//!
//! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape
+use crate::values::animated::{Animate, Procedure};
+use crate::values::computed::angle::Angle;
use crate::values::computed::url::ComputedUrl;
use crate::values::computed::{Image, LengthPercentage, NonNegativeLengthPercentage, Position};
use crate::values::generics::basic_shape as generic;
+use crate::values::specified::svg_path::{CoordPair, PathCommand};
/// A computed alias for FillRule.
pub use crate::values::generics::basic_shape::FillRule;
@@ -21,8 +24,13 @@ pub type ClipPath = generic::GenericClipPath<BasicShape, ComputedUrl>;
pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>;
/// A computed basic shape.
-pub type BasicShape =
- generic::GenericBasicShape<Position, LengthPercentage, NonNegativeLengthPercentage, InsetRect>;
+pub type BasicShape = generic::GenericBasicShape<
+ Angle,
+ Position,
+ LengthPercentage,
+ NonNegativeLengthPercentage,
+ InsetRect,
+>;
/// The computed value of `inset()`.
pub type InsetRect = generic::GenericInsetRect<LengthPercentage, NonNegativeLengthPercentage>;
@@ -35,3 +43,179 @@ pub type Ellipse = generic::Ellipse<Position, NonNegativeLengthPercentage>;
/// The computed value of `ShapeRadius`.
pub type ShapeRadius = generic::GenericShapeRadius<NonNegativeLengthPercentage>;
+
+/// The computed value of `shape()`.
+pub type Shape = generic::Shape<Angle, LengthPercentage>;
+
+/// The computed value of `ShapeCommand`.
+pub type ShapeCommand = generic::GenericShapeCommand<Angle, LengthPercentage>;
+
+/// The computed value of `PathOrShapeFunction`.
+pub type PathOrShapeFunction = generic::GenericPathOrShapeFunction<Angle, LengthPercentage>;
+
+/// The computed value of `CoordinatePair`.
+pub type CoordinatePair = generic::CoordinatePair<LengthPercentage>;
+
+/// Animate from `Shape` to `Path`, and vice versa.
+macro_rules! animate_shape {
+ (
+ $from:ident,
+ $to:ident,
+ $procedure:ident,
+ $from_as_shape:tt,
+ $to_as_shape:tt
+ ) => {{
+ // Check fill-rule.
+ if $from.fill != $to.fill {
+ return Err(());
+ }
+
+ // Check the list of commands. (This is a specialized lists::by_computed_value::animate().)
+ let from_cmds = $from.commands();
+ let to_cmds = $to.commands();
+ if from_cmds.len() != to_cmds.len() {
+ return Err(());
+ }
+ let commands = from_cmds
+ .iter()
+ .zip(to_cmds.iter())
+ .map(|(from_cmd, to_cmd)| {
+ $from_as_shape(from_cmd).animate(&$to_as_shape(to_cmd), $procedure)
+ })
+ .collect::<Result<Vec<ShapeCommand>, ()>>()?;
+
+ Ok(Shape {
+ fill: $from.fill,
+ commands: commands.into(),
+ })
+ }};
+}
+
+impl Animate for PathOrShapeFunction {
+ #[inline]
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ // Per spec, commands are "the same" if they use the same command keyword, and use the same
+ // <by-to> keyword. For curve and smooth, they also must have the same number of control
+ // points. Therefore, we don't have to do normalization here. (Note that we do
+ // normalization if we animate from path() to path(). See svg_path.rs for more details.)
+ //
+ // https://drafts.csswg.org/css-shapes-2/#interpolating-shape
+ match (self, other) {
+ (Self::Path(ref from), Self::Path(ref to)) => {
+ from.animate(to, procedure).map(Self::Path)
+ },
+ (Self::Shape(ref from), Self::Shape(ref to)) => {
+ from.animate(to, procedure).map(Self::Shape)
+ },
+ (Self::Shape(ref from), Self::Path(ref to)) => {
+ // Animate from shape() to path(). We convert each PathCommand into ShapeCommand,
+ // and return shape().
+ animate_shape!(
+ from,
+ to,
+ procedure,
+ (|shape_cmd| shape_cmd),
+ (|path_cmd| ShapeCommand::from(path_cmd))
+ )
+ .map(Self::Shape)
+ },
+ (Self::Path(ref from), Self::Shape(ref to)) => {
+ // Animate from path() to shape(). We convert each PathCommand into ShapeCommand,
+ // and return shape().
+ animate_shape!(
+ from,
+ to,
+ procedure,
+ (|path_cmd| ShapeCommand::from(path_cmd)),
+ (|shape_cmd| shape_cmd)
+ )
+ .map(Self::Shape)
+ },
+ }
+ }
+}
+
+impl From<&PathCommand> for ShapeCommand {
+ #[inline]
+ fn from(path: &PathCommand) -> Self {
+ use crate::values::computed::CSSPixelLength;
+ match path {
+ &PathCommand::Close => Self::Close,
+ &PathCommand::Move { by_to, ref point } => Self::Move {
+ by_to,
+ point: point.into(),
+ },
+ &PathCommand::Line { by_to, ref point } => Self::Move {
+ by_to,
+ point: point.into(),
+ },
+ &PathCommand::HLine { by_to, x } => Self::HLine {
+ by_to,
+ x: LengthPercentage::new_length(CSSPixelLength::new(x)),
+ },
+ &PathCommand::VLine { by_to, y } => Self::VLine {
+ by_to,
+ y: LengthPercentage::new_length(CSSPixelLength::new(y)),
+ },
+ &PathCommand::CubicCurve {
+ by_to,
+ ref point,
+ ref control1,
+ ref control2,
+ } => Self::CubicCurve {
+ by_to,
+ point: point.into(),
+ control1: control1.into(),
+ control2: control2.into(),
+ },
+ &PathCommand::QuadCurve {
+ by_to,
+ ref point,
+ ref control1,
+ } => Self::QuadCurve {
+ by_to,
+ point: point.into(),
+ control1: control1.into(),
+ },
+ &PathCommand::SmoothCubic {
+ by_to,
+ ref point,
+ ref control2,
+ } => Self::SmoothCubic {
+ by_to,
+ point: point.into(),
+ control2: control2.into(),
+ },
+ &PathCommand::SmoothQuad { by_to, ref point } => Self::SmoothQuad {
+ by_to,
+ point: point.into(),
+ },
+ &PathCommand::Arc {
+ by_to,
+ ref point,
+ ref radii,
+ arc_sweep,
+ arc_size,
+ rotate,
+ } => Self::Arc {
+ by_to,
+ point: point.into(),
+ radii: radii.into(),
+ arc_sweep,
+ arc_size,
+ rotate: Angle::from_degrees(rotate),
+ },
+ }
+ }
+}
+
+impl From<&CoordPair> for CoordinatePair {
+ #[inline]
+ fn from(p: &CoordPair) -> Self {
+ use crate::values::computed::CSSPixelLength;
+ Self::new(
+ LengthPercentage::new_length(CSSPixelLength::new(p.x)),
+ LengthPercentage::new_length(CSSPixelLength::new(p.y)),
+ )
+ }
+}
diff --git a/servo/components/style/values/computed/box.rs b/servo/components/style/values/computed/box.rs
index 62811d9851..0dea1c7ab9 100644
--- a/servo/components/style/values/computed/box.rs
+++ b/servo/components/style/values/computed/box.rs
@@ -383,6 +383,15 @@ impl Zoom {
if self == Self::ONE {
return value;
}
- self.value() * value
+ value * self.value()
+ }
+
+ /// Returns the un-zoomed value.
+ #[inline]
+ pub fn unzoom(self, value: f32) -> f32 {
+ if self == Self::ONE {
+ return value;
+ }
+ value / self.value()
}
}
diff --git a/servo/components/style/values/computed/font.rs b/servo/components/style/values/computed/font.rs
index de0a5e372b..e492584f75 100644
--- a/servo/components/style/values/computed/font.rs
+++ b/servo/components/style/values/computed/font.rs
@@ -8,7 +8,7 @@ use crate::parser::{Parse, ParserContext};
use crate::values::animated::ToAnimatedValue;
use crate::values::computed::{
Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, Number, Percentage,
- ToComputedValue,
+ ToComputedValue, Zoom,
};
use crate::values::generics::font::{
FeatureTagValue, FontSettings, TaggedFontValue, VariationValue,
@@ -278,6 +278,16 @@ impl FontSize {
self.used_size.0
}
+ /// Apply zoom to the font-size. This is usually done by ToComputedValue.
+ #[inline]
+ pub fn zoom(&self, zoom: Zoom) -> Self {
+ Self {
+ computed_size: NonNegative(Length::new(zoom.zoom(self.computed_size.0.px()))),
+ used_size: NonNegative(Length::new(zoom.zoom(self.used_size.0.px()))),
+ keyword_info: self.keyword_info,
+ }
+ }
+
#[inline]
/// Get default value of font size.
pub fn medium() -> Self {
@@ -1359,7 +1369,7 @@ impl ToResolvedValue for LineHeight {
context.style.get_font(),
wm,
Some(context.element_info.element),
- ))
+ ).to_resolved_value(context))
}
#[inline]
diff --git a/servo/components/style/values/computed/length.rs b/servo/components/style/values/computed/length.rs
index e75676a76d..881efed126 100644
--- a/servo/components/style/values/computed/length.rs
+++ b/servo/components/style/values/computed/length.rs
@@ -6,12 +6,13 @@
use super::{Context, Number, ToComputedValue};
use crate::values::animated::ToAnimatedValue;
-use crate::values::computed::NonNegativeNumber;
+use crate::values::computed::{NonNegativeNumber, Zoom};
use crate::values::generics::length as generics;
use crate::values::generics::length::{
GenericLengthOrNumber, GenericLengthPercentageOrNormal, GenericMaxSize, GenericSize,
};
use crate::values::generics::NonNegative;
+use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
use crate::values::specified::length::{AbsoluteLength, FontBaseSize, LineHeightBase};
use crate::values::{specified, CSSFloat};
use crate::Zero;
@@ -227,12 +228,24 @@ impl Size {
ToAnimatedValue,
ToAnimatedZero,
ToComputedValue,
- ToResolvedValue,
ToShmem,
)]
#[repr(C)]
pub struct CSSPixelLength(CSSFloat);
+impl ToResolvedValue for CSSPixelLength {
+ type ResolvedValue = Self;
+
+ fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
+ Self(context.style.effective_zoom.unzoom(self.0))
+ }
+
+ #[inline]
+ fn from_resolved_value(value: Self::ResolvedValue) -> Self {
+ value
+ }
+}
+
impl fmt::Debug for CSSPixelLength {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)?;
@@ -271,6 +284,12 @@ impl CSSPixelLength {
self.0
}
+ /// Zooms a particular length.
+ #[inline]
+ pub fn zoom(self, zoom: Zoom) -> Self {
+ Self::new(zoom.zoom(self.px()))
+ }
+
/// Return the length with app_unit i32 type.
#[inline]
pub fn to_i32_au(self) -> i32 {
diff --git a/servo/components/style/values/computed/length_percentage.rs b/servo/components/style/values/computed/length_percentage.rs
index 0dbd2de76d..c448025dd1 100644
--- a/servo/components/style/values/computed/length_percentage.rs
+++ b/servo/components/style/values/computed/length_percentage.rs
@@ -30,6 +30,7 @@ use crate::values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZer
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::calc::{CalcUnits, PositivePercentageBasis};
use crate::values::generics::{calc, NonNegative};
+use crate::values::resolved::{Context as ResolvedContext, ToResolvedValue};
use crate::values::specified::length::{FontBaseSize, LineHeightBase};
use crate::values::{specified, CSSFloat};
use crate::{Zero, ZeroNoPercent};
@@ -164,6 +165,25 @@ impl MallocSizeOf for LengthPercentage {
}
}
+impl ToResolvedValue for LengthPercentage {
+ type ResolvedValue = Self;
+
+ fn to_resolved_value(self, context: &ResolvedContext) -> Self::ResolvedValue {
+ if context.style.effective_zoom.is_one() {
+ return self;
+ }
+ match self.unpack() {
+ Unpacked::Length(l) => Self::new_length(l.to_resolved_value(context)),
+ Unpacked::Percentage(..) | Unpacked::Calc(..) => self,
+ }
+ }
+
+ #[inline]
+ fn from_resolved_value(value: Self::ResolvedValue) -> Self {
+ value
+ }
+}
+
/// An unpacked `<length-percentage>` that borrows the `calc()` variant.
#[derive(Clone, Debug, PartialEq, ToCss)]
enum Unpacked<'a> {
diff --git a/servo/components/style/values/computed/ratio.rs b/servo/components/style/values/computed/ratio.rs
index ae8997cfc0..6964eec3e4 100644
--- a/servo/components/style/values/computed/ratio.rs
+++ b/servo/components/style/values/computed/ratio.rs
@@ -9,7 +9,7 @@ use crate::values::computed::NonNegativeNumber;
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::ratio::Ratio as GenericRatio;
use crate::{One, Zero};
-use std::cmp::{Ordering, PartialOrd};
+use std::cmp::Ordering;
/// A computed <ratio> value.
pub type Ratio = GenericRatio<NonNegativeNumber>;
diff --git a/servo/components/style/values/generics/basic_shape.rs b/servo/components/style/values/generics/basic_shape.rs
index 13d27995c1..ca7646fb13 100644
--- a/servo/components/style/values/generics/basic_shape.rs
+++ b/servo/components/style/values/generics/basic_shape.rs
@@ -10,7 +10,7 @@ use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
use crate::values::generics::border::GenericBorderRadius;
use crate::values::generics::position::GenericPositionOrAuto;
use crate::values::generics::rect::Rect;
-use crate::values::specified::SVGPathData;
+use crate::values::specified::svg_path::{PathCommand, SVGPathData};
use crate::Zero;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
@@ -181,8 +181,13 @@ pub use self::GenericShapeOutside as ShapeOutside;
ToShmem,
)]
#[repr(C, u8)]
-pub enum GenericBasicShape<Position, LengthPercentage, NonNegativeLengthPercentage, BasicShapeRect>
-{
+pub enum GenericBasicShape<
+ Angle,
+ Position,
+ LengthPercentage,
+ NonNegativeLengthPercentage,
+ BasicShapeRect,
+> {
/// The <basic-shape-rect>.
Rect(BasicShapeRect),
/// Defines a circle with a center and a radius.
@@ -199,10 +204,11 @@ pub enum GenericBasicShape<Position, LengthPercentage, NonNegativeLengthPercenta
),
/// Defines a polygon with pair arguments.
Polygon(GenericPolygon<LengthPercentage>),
- /// Defines a path with SVG path syntax.
- Path(Path),
- // TODO: Bug 1823463. Add shape().
- // https://drafts.csswg.org/css-shapes-2/#shape-function
+ /// Defines a path() or shape().
+ PathOrShape(
+ #[animation(field_bound)]
+ #[css(field_bound)]
+ GenericPathOrShapeFunction<Angle, LengthPercentage>),
}
pub use self::GenericBasicShape as BasicShape;
@@ -366,6 +372,30 @@ pub use self::GenericPolygon as Polygon;
#[repr(C)]
pub struct PolygonCoord<LengthPercentage>(pub LengthPercentage, pub LengthPercentage);
+/// path() function or shape() function.
+#[derive(
+ Clone,
+ ComputeSquaredDistance,
+ Debug,
+ Deserialize,
+ MallocSizeOf,
+ PartialEq,
+ Serialize,
+ SpecifiedValueInfo,
+ ToAnimatedValue,
+ ToComputedValue,
+ ToCss,
+ ToResolvedValue,
+ ToShmem,
+)]
+#[repr(C, u8)]
+pub enum GenericPathOrShapeFunction<Angle, LengthPercentage> {
+ /// Defines a path with SVG path syntax.
+ Path(Path),
+ /// Defines a shape function, which is identical to path() but it uses the CSS syntax.
+ Shape(#[css(field_bound)] Shape<Angle, LengthPercentage>),
+}
+
// https://drafts.csswg.org/css-shapes/#typedef-fill-rule
// NOTE: Basic shapes spec says that these are the only two values, however
// https://www.w3.org/TR/SVG/painting.html#FillRuleProperty
@@ -397,9 +427,9 @@ pub enum FillRule {
Evenodd,
}
-/// The path function defined in css-shape-2.
+/// The path function.
///
-/// https://drafts.csswg.org/css-shapes-2/#funcdef-path
+/// https://drafts.csswg.org/css-shapes-1/#funcdef-basic-shape-path
#[derive(
Animate,
Clone,
@@ -426,6 +456,14 @@ pub struct Path {
pub path: SVGPathData,
}
+impl Path {
+ /// Returns the slice of PathCommand.
+ #[inline]
+ pub fn commands(&self) -> &[PathCommand] {
+ self.path.commands()
+ }
+}
+
impl<B, U> ToAnimatedZero for ClipPath<B, U> {
fn to_animated_zero(&self) -> Result<Self, ()> {
Err(())
@@ -565,3 +603,479 @@ impl Default for FillRule {
fn is_default<T: Default + PartialEq>(fill: &T) -> bool {
*fill == Default::default()
}
+
+/// The shape function defined in css-shape-2.
+/// shape() = shape(<fill-rule>? from <coordinate-pair>, <shape-command>#)
+///
+/// https://drafts.csswg.org/css-shapes-2/#shape-function
+#[derive(
+ Clone,
+ Debug,
+ Deserialize,
+ MallocSizeOf,
+ PartialEq,
+ Serialize,
+ SpecifiedValueInfo,
+ ToAnimatedValue,
+ ToComputedValue,
+ ToResolvedValue,
+ ToShmem,
+)]
+#[repr(C)]
+pub struct Shape<Angle, LengthPercentage> {
+ /// The filling rule for this shape.
+ pub fill: FillRule,
+ /// The shape command data. Note that the starting point will be the first command in this
+ /// slice.
+ // Note: The first command is always GenericShapeCommand::Move.
+ pub commands: crate::OwnedSlice<GenericShapeCommand<Angle, LengthPercentage>>,
+}
+
+impl<Angle, LengthPercentage> Shape<Angle, LengthPercentage> {
+ /// Returns the slice of GenericShapeCommand<..>.
+ #[inline]
+ pub fn commands(&self) -> &[GenericShapeCommand<Angle, LengthPercentage>] {
+ &self.commands
+ }
+}
+
+impl<Angle, LengthPercentage> Animate for Shape<Angle, LengthPercentage>
+where
+ Angle: Animate,
+ LengthPercentage: Animate,
+{
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ if self.fill != other.fill {
+ return Err(());
+ }
+ let commands =
+ lists::by_computed_value::animate(&self.commands, &other.commands, procedure)?;
+ Ok(Self {
+ fill: self.fill,
+ commands,
+ })
+ }
+}
+
+impl<Angle, LengthPercentage> ComputeSquaredDistance for Shape<Angle, LengthPercentage>
+where
+ Angle: ComputeSquaredDistance,
+ LengthPercentage: ComputeSquaredDistance,
+{
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
+ if self.fill != other.fill {
+ return Err(());
+ }
+ lists::by_computed_value::squared_distance(&self.commands, &other.commands)
+ }
+}
+
+impl<Angle, LengthPercentage> ToCss for Shape<Angle, LengthPercentage>
+where
+ Angle: ToCss + Zero,
+ LengthPercentage: PartialEq + ToCss,
+{
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: Write,
+ {
+ use style_traits::values::SequenceWriter;
+
+ // Per spec, we must have the first move command and at least one following command.
+ debug_assert!(self.commands.len() > 1);
+
+ dest.write_str("shape(")?;
+ if !is_default(&self.fill) {
+ self.fill.to_css(dest)?;
+ dest.write_char(' ')?;
+ }
+ dest.write_str("from ")?;
+ match self.commands[0] {
+ ShapeCommand::Move {
+ by_to: _,
+ ref point,
+ } => point.to_css(dest)?,
+ _ => unreachable!("The first command must be move"),
+ }
+ dest.write_str(", ")?;
+ {
+ let mut writer = SequenceWriter::new(dest, ", ");
+ for command in self.commands.iter().skip(1) {
+ writer.item(command)?;
+ }
+ }
+ dest.write_char(')')
+ }
+}
+
+/// This is a more general shape(path) command type, for both shape() and path().
+///
+/// https://www.w3.org/TR/SVG11/paths.html#PathData
+/// https://drafts.csswg.org/css-shapes-2/#shape-function
+#[derive(
+ Animate,
+ Clone,
+ ComputeSquaredDistance,
+ Copy,
+ Debug,
+ Deserialize,
+ MallocSizeOf,
+ PartialEq,
+ Serialize,
+ SpecifiedValueInfo,
+ ToAnimatedValue,
+ ToAnimatedZero,
+ ToComputedValue,
+ ToResolvedValue,
+ ToShmem,
+)]
+#[allow(missing_docs)]
+#[repr(C, u8)]
+pub enum GenericShapeCommand<Angle, LengthPercentage> {
+ /// The move command.
+ Move {
+ by_to: ByTo,
+ point: CoordinatePair<LengthPercentage>,
+ },
+ /// The line command.
+ Line {
+ by_to: ByTo,
+ point: CoordinatePair<LengthPercentage>,
+ },
+ /// The hline command.
+ HLine { by_to: ByTo, x: LengthPercentage },
+ /// The vline command.
+ VLine { by_to: ByTo, y: LengthPercentage },
+ /// The cubic Bézier curve command.
+ CubicCurve {
+ by_to: ByTo,
+ point: CoordinatePair<LengthPercentage>,
+ control1: CoordinatePair<LengthPercentage>,
+ control2: CoordinatePair<LengthPercentage>,
+ },
+ /// The quadratic Bézier curve command.
+ QuadCurve {
+ by_to: ByTo,
+ point: CoordinatePair<LengthPercentage>,
+ control1: CoordinatePair<LengthPercentage>,
+ },
+ /// The smooth command.
+ SmoothCubic {
+ by_to: ByTo,
+ point: CoordinatePair<LengthPercentage>,
+ control2: CoordinatePair<LengthPercentage>,
+ },
+ /// The smooth quadratic Bézier curve command.
+ SmoothQuad {
+ by_to: ByTo,
+ point: CoordinatePair<LengthPercentage>,
+ },
+ /// The arc command.
+ Arc {
+ by_to: ByTo,
+ point: CoordinatePair<LengthPercentage>,
+ radii: CoordinatePair<LengthPercentage>,
+ arc_sweep: ArcSweep,
+ arc_size: ArcSize,
+ rotate: Angle,
+ },
+ /// The closepath command.
+ Close,
+}
+
+pub use self::GenericShapeCommand as ShapeCommand;
+
+impl<Angle, LengthPercentage> ToCss for ShapeCommand<Angle, LengthPercentage>
+where
+ Angle: ToCss + Zero,
+ LengthPercentage: PartialEq + ToCss,
+{
+ fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ use self::ShapeCommand::*;
+ match *self {
+ Move { by_to, ref point } => {
+ dest.write_str("move ")?;
+ by_to.to_css(dest)?;
+ dest.write_char(' ')?;
+ point.to_css(dest)
+ },
+ Line { by_to, ref point } => {
+ dest.write_str("line ")?;
+ by_to.to_css(dest)?;
+ dest.write_char(' ')?;
+ point.to_css(dest)
+ },
+ HLine { by_to, ref x } => {
+ dest.write_str("hline ")?;
+ by_to.to_css(dest)?;
+ dest.write_char(' ')?;
+ x.to_css(dest)
+ },
+ VLine { by_to, ref y } => {
+ dest.write_str("vline ")?;
+ by_to.to_css(dest)?;
+ dest.write_char(' ')?;
+ y.to_css(dest)
+ },
+ CubicCurve {
+ by_to,
+ ref point,
+ ref control1,
+ ref control2,
+ } => {
+ dest.write_str("curve ")?;
+ by_to.to_css(dest)?;
+ dest.write_char(' ')?;
+ point.to_css(dest)?;
+ dest.write_str(" via ")?;
+ control1.to_css(dest)?;
+ dest.write_char(' ')?;
+ control2.to_css(dest)
+ },
+ QuadCurve {
+ by_to,
+ ref point,
+ ref control1,
+ } => {
+ dest.write_str("curve ")?;
+ by_to.to_css(dest)?;
+ dest.write_char(' ')?;
+ point.to_css(dest)?;
+ dest.write_str(" via ")?;
+ control1.to_css(dest)
+ },
+ SmoothCubic {
+ by_to,
+ ref point,
+ ref control2,
+ } => {
+ dest.write_str("smooth ")?;
+ by_to.to_css(dest)?;
+ dest.write_char(' ')?;
+ point.to_css(dest)?;
+ dest.write_str(" via ")?;
+ control2.to_css(dest)
+ },
+ SmoothQuad { by_to, ref point } => {
+ dest.write_str("smooth ")?;
+ by_to.to_css(dest)?;
+ dest.write_char(' ')?;
+ point.to_css(dest)
+ },
+ Arc {
+ by_to,
+ ref point,
+ ref radii,
+ arc_sweep,
+ arc_size,
+ ref rotate,
+ } => {
+ dest.write_str("arc ")?;
+ by_to.to_css(dest)?;
+ dest.write_char(' ')?;
+ point.to_css(dest)?;
+ dest.write_str(" of ")?;
+ radii.x.to_css(dest)?;
+ if radii.x != radii.y {
+ dest.write_char(' ')?;
+ radii.y.to_css(dest)?;
+ }
+
+ if matches!(arc_sweep, ArcSweep::Cw) {
+ dest.write_str(" cw")?;
+ }
+
+ if matches!(arc_size, ArcSize::Large) {
+ dest.write_str(" large")?;
+ }
+
+ if !rotate.is_zero() {
+ dest.write_str(" rotate ")?;
+ rotate.to_css(dest)?;
+ }
+ Ok(())
+ },
+ Close => dest.write_str("close"),
+ }
+ }
+}
+
+/// This indicates the command is absolute or relative.
+/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-by-to
+#[derive(
+ Animate,
+ Clone,
+ ComputeSquaredDistance,
+ Copy,
+ Debug,
+ Deserialize,
+ MallocSizeOf,
+ Parse,
+ PartialEq,
+ Serialize,
+ SpecifiedValueInfo,
+ ToAnimatedValue,
+ ToAnimatedZero,
+ ToComputedValue,
+ ToCss,
+ ToResolvedValue,
+ ToShmem,
+)]
+#[repr(u8)]
+pub enum ByTo {
+ /// This indicates that the <coordinate-pair>s are relative to the command’s starting point.
+ By,
+ /// This relative to the top-left corner of the reference box.
+ To,
+}
+
+impl ByTo {
+ /// Return true if it is absolute, i.e. it is To.
+ #[inline]
+ pub fn is_abs(&self) -> bool {
+ matches!(self, ByTo::To)
+ }
+
+ /// Create ByTo based on the flag if it is absolute.
+ #[inline]
+ pub fn new(is_abs: bool) -> Self {
+ if is_abs {
+ Self::To
+ } else {
+ Self::By
+ }
+ }
+}
+
+/// Defines a pair of coordinates, representing a rightward and downward offset, respectively, from
+/// a specified reference point. Percentages are resolved against the width or height,
+/// respectively, of the reference box.
+/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-coordinate-pair
+#[allow(missing_docs)]
+#[derive(
+ AddAssign,
+ Animate,
+ Clone,
+ ComputeSquaredDistance,
+ Copy,
+ Debug,
+ Deserialize,
+ MallocSizeOf,
+ PartialEq,
+ Serialize,
+ SpecifiedValueInfo,
+ ToAnimatedValue,
+ ToAnimatedZero,
+ ToComputedValue,
+ ToCss,
+ ToResolvedValue,
+ ToShmem,
+)]
+#[repr(C)]
+pub struct CoordinatePair<LengthPercentage> {
+ pub x: LengthPercentage,
+ pub y: LengthPercentage,
+}
+
+impl<LengthPercentage> CoordinatePair<LengthPercentage> {
+ /// Create a CoordinatePair.
+ #[inline]
+ pub fn new(x: LengthPercentage, y: LengthPercentage) -> Self {
+ Self { x, y }
+ }
+}
+
+/// This indicates that the arc that is traced around the ellipse clockwise or counter-clockwise
+/// from the center.
+/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-arc-sweep
+#[derive(
+ Clone,
+ Copy,
+ Debug,
+ Deserialize,
+ FromPrimitive,
+ MallocSizeOf,
+ Parse,
+ PartialEq,
+ Serialize,
+ SpecifiedValueInfo,
+ ToAnimatedValue,
+ ToAnimatedZero,
+ ToComputedValue,
+ ToCss,
+ ToResolvedValue,
+ ToShmem,
+)]
+#[repr(u8)]
+pub enum ArcSweep {
+ /// Counter-clockwise. The default value. (This also represents 0 in the svg path.)
+ Ccw = 0,
+ /// Clockwise. (This also represents 1 in the svg path.)
+ Cw = 1,
+}
+
+impl Animate for ArcSweep {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ use num_traits::FromPrimitive;
+ // If an arc command has different <arc-sweep> between its starting and ending list, then
+ // the interpolated result uses cw for any progress value between 0 and 1.
+ (*self as i32)
+ .animate(&(*other as i32), procedure)
+ .map(|v| ArcSweep::from_u8((v > 0) as u8).unwrap_or(ArcSweep::Ccw))
+ }
+}
+
+impl ComputeSquaredDistance for ArcSweep {
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
+ (*self as i32).compute_squared_distance(&(*other as i32))
+ }
+}
+
+/// This indicates that the larger or smaller, respectively, of the two possible arcs must be
+/// chosen.
+/// https://drafts.csswg.org/css-shapes-2/#typedef-shape-arc-size
+#[derive(
+ Clone,
+ Copy,
+ Debug,
+ Deserialize,
+ FromPrimitive,
+ MallocSizeOf,
+ Parse,
+ PartialEq,
+ Serialize,
+ SpecifiedValueInfo,
+ ToAnimatedValue,
+ ToAnimatedZero,
+ ToComputedValue,
+ ToCss,
+ ToResolvedValue,
+ ToShmem,
+)]
+#[repr(u8)]
+pub enum ArcSize {
+ /// Choose the small one. The default value. (This also represents 0 in the svg path.)
+ Small = 0,
+ /// Choose the large one. (This also represents 1 in the svg path.)
+ Large = 1,
+}
+
+impl Animate for ArcSize {
+ fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
+ use num_traits::FromPrimitive;
+ // If it has different <arc-size> keywords, then the interpolated result uses large for any
+ // progress value between 0 and 1.
+ (*self as i32)
+ .animate(&(*other as i32), procedure)
+ .map(|v| ArcSize::from_u8((v > 0) as u8).unwrap_or(ArcSize::Small))
+ }
+}
+
+impl ComputeSquaredDistance for ArcSize {
+ fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
+ (*self as i32).compute_squared_distance(&(*other as i32))
+ }
+}
diff --git a/servo/components/style/values/generics/counters.rs b/servo/components/style/values/generics/counters.rs
index 1d4518c57b..3f23c74b33 100644
--- a/servo/components/style/values/generics/counters.rs
+++ b/servo/components/style/values/generics/counters.rs
@@ -4,11 +4,10 @@
//! Generic types for counters-related CSS values.
-#[cfg(feature = "servo-layout-2013")]
+#[cfg(feature = "servo")]
use crate::computed_values::list_style_type::T as ListStyleType;
#[cfg(feature = "gecko")]
use crate::values::generics::CounterStyle;
-#[cfg(any(feature = "gecko", feature = "servo-layout-2020"))]
use crate::values::specified::Attr;
use crate::values::CustomIdent;
use std::fmt::{self, Write};
@@ -186,13 +185,13 @@ pub struct GenericCounters<I>(
);
pub use self::GenericCounters as Counters;
-#[cfg(feature = "servo-layout-2013")]
+#[cfg(feature = "servo")]
type CounterStyleType = ListStyleType;
#[cfg(feature = "gecko")]
type CounterStyleType = CounterStyle;
-#[cfg(feature = "servo-layout-2013")]
+#[cfg(feature = "servo")]
#[inline]
fn is_decimal(counter_type: &CounterStyleType) -> bool {
*counter_type == ListStyleType::Decimal
@@ -254,11 +253,9 @@ pub enum GenericContentItem<I> {
/// Literal string content.
String(crate::OwnedStr),
/// `counter(name, style)`.
- #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
#[css(comma, function)]
Counter(CustomIdent, #[css(skip_if = "is_decimal")] CounterStyleType),
/// `counters(name, separator, style)`.
- #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
#[css(comma, function)]
Counters(
CustomIdent,
@@ -266,16 +263,12 @@ pub enum GenericContentItem<I> {
#[css(skip_if = "is_decimal")] CounterStyleType,
),
/// `open-quote`.
- #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
OpenQuote,
/// `close-quote`.
- #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
CloseQuote,
/// `no-open-quote`.
- #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
NoOpenQuote,
/// `no-close-quote`.
- #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
NoCloseQuote,
/// `-moz-alt-content`.
#[cfg(feature = "gecko")]
@@ -286,7 +279,6 @@ pub enum GenericContentItem<I> {
#[cfg(feature = "gecko")]
MozLabelContent,
/// `attr([namespace? `|`]? ident)`
- #[cfg(any(feature = "gecko", feature = "servo-layout-2020"))]
Attr(Attr),
/// image-set(url) | url(url)
Image(I),
diff --git a/servo/components/style/values/generics/image.rs b/servo/components/style/values/generics/image.rs
index 6fc0870e15..ca1c716052 100644
--- a/servo/components/style/values/generics/image.rs
+++ b/servo/components/style/values/generics/image.rs
@@ -41,7 +41,7 @@ pub enum GenericImage<G, ImageUrl, Color, Percentage, Resolution> {
/// A paint worklet image.
/// <https://drafts.css-houdini.org/css-paint-api/>
- #[cfg(feature = "servo-layout-2013")]
+ #[cfg(feature = "servo")]
PaintWorklet(PaintWorklet),
/// A `<cross-fade()>` image. Storing this directly inside of
@@ -416,7 +416,7 @@ where
Image::None => dest.write_str("none"),
Image::Url(ref url) => url.to_css(dest),
Image::Gradient(ref gradient) => gradient.to_css(dest),
- #[cfg(feature = "servo-layout-2013")]
+ #[cfg(feature = "servo")]
Image::PaintWorklet(ref paint_worklet) => paint_worklet.to_css(dest),
#[cfg(feature = "gecko")]
Image::Element(ref selector) => {
diff --git a/servo/components/style/values/generics/transform.rs b/servo/components/style/values/generics/transform.rs
index 3a65c460a7..4d70e4465c 100644
--- a/servo/components/style/values/generics/transform.rs
+++ b/servo/components/style/values/generics/transform.rs
@@ -11,7 +11,6 @@ use crate::values::specified::length::Length as SpecifiedLength;
use crate::values::specified::length::LengthPercentage as SpecifiedLengthPercentage;
use crate::values::{computed, CSSFloat};
use crate::{Zero, ZeroNoPercent};
-use euclid;
use euclid::default::{Rect, Transform3D};
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
diff --git a/servo/components/style/values/resolved/mod.rs b/servo/components/style/values/resolved/mod.rs
index 675f3cca68..d830474fe6 100644
--- a/servo/components/style/values/resolved/mod.rs
+++ b/servo/components/style/values/resolved/mod.rs
@@ -95,7 +95,6 @@ trivial_to_resolved_value!(computed::url::ComputedImageUrl);
trivial_to_resolved_value!(crate::Namespace);
#[cfg(feature = "servo")]
trivial_to_resolved_value!(crate::Prefix);
-trivial_to_resolved_value!(computed::LengthPercentage);
trivial_to_resolved_value!(style_traits::values::specified::AllowedNumericType);
trivial_to_resolved_value!(computed::TimingFunction);
diff --git a/servo/components/style/values/specified/animation.rs b/servo/components/style/values/specified/animation.rs
index 5a1f5003f3..552521711c 100644
--- a/servo/components/style/values/specified/animation.rs
+++ b/servo/components/style/values/specified/animation.rs
@@ -165,6 +165,12 @@ impl AnimationIterationCount {
pub fn one() -> Self {
Self::Number(NonNegativeNumber::new(1.0))
}
+
+ /// Returns true if it's `1.0`.
+ #[inline]
+ pub fn is_one(&self) -> bool {
+ *self == Self::one()
+ }
}
/// A value for the `animation-name` property.
@@ -230,6 +236,17 @@ pub enum AnimationDirection {
AlternateReverse,
}
+impl AnimationDirection {
+ /// Returns true if the name matches any animation-direction keyword.
+ #[inline]
+ pub fn match_keywords(name: &AnimationName) -> bool {
+ if let Some(name) = name.as_atom() {
+ return name.with_str(|n| Self::from_ident(n).is_ok());
+ }
+ false
+ }
+}
+
/// https://drafts.csswg.org/css-animations/#animation-play-state
#[derive(Copy, Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)]
#[repr(u8)]
@@ -239,6 +256,17 @@ pub enum AnimationPlayState {
Paused,
}
+impl AnimationPlayState {
+ /// Returns true if the name matches any animation-play-state keyword.
+ #[inline]
+ pub fn match_keywords(name: &AnimationName) -> bool {
+ if let Some(name) = name.as_atom() {
+ return name.with_str(|n| Self::from_ident(n).is_ok());
+ }
+ false
+ }
+}
+
/// https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode
#[derive(Copy, Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)]
#[repr(u8)]
@@ -250,6 +278,18 @@ pub enum AnimationFillMode {
Both,
}
+impl AnimationFillMode {
+ /// Returns true if the name matches any animation-fill-mode keyword.
+ /// Note: animation-name:none is its initial value, so we don't have to match none here.
+ #[inline]
+ pub fn match_keywords(name: &AnimationName) -> bool {
+ if let Some(atom) = name.as_atom() {
+ return !name.is_none() && atom.with_str(|n| Self::from_ident(n).is_ok());
+ }
+ false
+ }
+}
+
/// https://drafts.csswg.org/css-animations-2/#animation-composition
#[derive(Copy, Clone, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, ToResolvedValue, ToShmem)]
#[repr(u8)]
diff --git a/servo/components/style/values/specified/basic_shape.rs b/servo/components/style/values/specified/basic_shape.rs
index 526296b735..8ed32c40c5 100644
--- a/servo/components/style/values/specified/basic_shape.rs
+++ b/servo/components/style/values/specified/basic_shape.rs
@@ -14,6 +14,7 @@ use crate::values::generics::basic_shape as generic;
use crate::values::generics::basic_shape::{Path, PolygonCoord};
use crate::values::generics::position::{GenericPosition, GenericPositionOrAuto};
use crate::values::generics::rect::Rect;
+use crate::values::specified::angle::Angle;
use crate::values::specified::border::BorderRadius;
use crate::values::specified::image::Image;
use crate::values::specified::length::LengthPercentageOrAuto;
@@ -40,6 +41,7 @@ pub type ShapePosition = GenericPosition<LengthPercentage, LengthPercentage>;
/// A specified basic shape.
pub type BasicShape = generic::GenericBasicShape<
+ Angle,
ShapePosition,
LengthPercentage,
NonNegativeLengthPercentage,
@@ -61,6 +63,12 @@ pub type ShapeRadius = generic::ShapeRadius<NonNegativeLengthPercentage>;
/// The specified value of `Polygon`.
pub type Polygon = generic::GenericPolygon<LengthPercentage>;
+/// The specified value of `PathOrShapeFunction`.
+pub type PathOrShapeFunction = generic::GenericPathOrShapeFunction<Angle, LengthPercentage>;
+
+/// The specified value of `ShapeCommand`.
+pub type ShapeCommand = generic::GenericShapeCommand<Angle, LengthPercentage>;
+
/// The specified value of `xywh()`.
/// Defines a rectangle via offsets from the top and left edge of the reference box, and a
/// specified width and height.
@@ -168,8 +176,8 @@ bitflags! {
const POLYGON = 1 << 5;
/// path().
const PATH = 1 << 6;
- // TODO: Bug 1823463. Add shape().
- // const SHAPE = 1 << 7;
+ /// shape().
+ const SHAPE = 1 << 7;
/// All flags.
const ALL =
@@ -179,7 +187,8 @@ bitflags! {
Self::CIRCLE.bits() |
Self::ELLIPSE.bits() |
Self::POLYGON.bits() |
- Self::PATH.bits();
+ Self::PATH.bits() |
+ Self::SHAPE.bits();
/// For shape-outside.
const SHAPE_OUTSIDE =
@@ -329,7 +338,17 @@ impl BasicShape {
.map(BasicShape::Polygon)
},
"path" if flags.contains(AllowedBasicShapes::PATH) => {
- Path::parse_function_arguments(i, shape_type).map(BasicShape::Path)
+ Path::parse_function_arguments(i, shape_type)
+ .map(PathOrShapeFunction::Path)
+ .map(BasicShape::PathOrShape)
+ },
+ "shape"
+ if flags.contains(AllowedBasicShapes::SHAPE)
+ && static_prefs::pref!("layout.css.basic-shape-shape.enabled") =>
+ {
+ generic::Shape::parse_function_arguments(context, i, shape_type)
+ .map(PathOrShapeFunction::Shape)
+ .map(BasicShape::PathOrShape)
},
_ => Err(location
.new_custom_error(StyleParseErrorKind::UnexpectedFunction(function.clone()))),
@@ -490,7 +509,11 @@ impl Ellipse {
}
}
-fn parse_fill_rule<'i, 't>(input: &mut Parser<'i, 't>, shape_type: ShapeType) -> FillRule {
+fn parse_fill_rule<'i, 't>(
+ input: &mut Parser<'i, 't>,
+ shape_type: ShapeType,
+ expect_comma: bool,
+) -> FillRule {
match shape_type {
// Per [1] and [2], we ignore `<fill-rule>` for outline shapes, so always use a default
// value.
@@ -508,7 +531,9 @@ fn parse_fill_rule<'i, 't>(input: &mut Parser<'i, 't>, shape_type: ShapeType) ->
ShapeType::Filled => input
.try_parse(|i| -> Result<_, ParseError> {
let fill = FillRule::parse(i)?;
- i.expect_comma()?;
+ if expect_comma {
+ i.expect_comma()?;
+ }
Ok(fill)
})
.unwrap_or_default(),
@@ -532,7 +557,7 @@ impl Polygon {
input: &mut Parser<'i, 't>,
shape_type: ShapeType,
) -> Result<Self, ParseError<'i>> {
- let fill = parse_fill_rule(input, shape_type);
+ let fill = parse_fill_rule(input, shape_type, true /* has comma */);
let coordinates = input
.parse_comma_separated(|i| {
Ok(PolygonCoord(
@@ -554,7 +579,7 @@ impl Path {
) -> Result<Self, ParseError<'i>> {
use crate::values::specified::svg_path::AllowEmpty;
- let fill = parse_fill_rule(input, shape_type);
+ let fill = parse_fill_rule(input, shape_type, true /* has comma */);
let path = SVGPathData::parse(input, AllowEmpty::No)?;
Ok(Path { fill, path })
}
@@ -717,3 +742,168 @@ impl ToComputedValue for BasicShapeRect {
Self::Inset(ToComputedValue::from_computed_value(computed))
}
}
+
+impl generic::Shape<Angle, LengthPercentage> {
+ /// Parse the inner arguments of a `shape` function.
+ /// shape() = shape(<fill-rule>? from <coordinate-pair>, <shape-command>#)
+ fn parse_function_arguments<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ shape_type: ShapeType,
+ ) -> Result<Self, ParseError<'i>> {
+ let fill = parse_fill_rule(input, shape_type, false /* no following comma */);
+
+ let mut first = true;
+ let commands = input.parse_comma_separated(|i| {
+ if first {
+ first = false;
+
+ // The starting point for the first shape-command. It adds an initial absolute
+ // moveto to the list of path data commands, with the <coordinate-pair> measured
+ // from the top-left corner of the reference
+ i.expect_ident_matching("from")?;
+ Ok(ShapeCommand::Move {
+ by_to: generic::ByTo::To,
+ point: generic::CoordinatePair::parse(context, i)?,
+ })
+ } else {
+ // The further path data commands.
+ ShapeCommand::parse(context, i)
+ }
+ })?;
+
+ // We must have one starting point and at least one following <shape-command>.
+ if commands.len() < 2 {
+ return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
+ }
+
+ Ok(Self {
+ fill,
+ commands: commands.into(),
+ })
+ }
+}
+
+impl Parse for ShapeCommand {
+ fn parse<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ use crate::values::generics::basic_shape::{ArcSize, ArcSweep, ByTo, CoordinatePair};
+
+ // <shape-command> = <move-command> | <line-command> | <hv-line-command> |
+ // <curve-command> | <smooth-command> | <arc-command> | close
+ Ok(try_match_ident_ignore_ascii_case! { input,
+ "close" => Self::Close,
+ "move" => {
+ let by_to = ByTo::parse(input)?;
+ let point = CoordinatePair::parse(context, input)?;
+ Self::Move { by_to, point }
+ },
+ "line" => {
+ let by_to = ByTo::parse(input)?;
+ let point = CoordinatePair::parse(context, input)?;
+ Self::Line { by_to, point }
+ },
+ "hline" => {
+ let by_to = ByTo::parse(input)?;
+ let x = LengthPercentage::parse(context, input)?;
+ Self::HLine { by_to, x }
+ },
+ "vline" => {
+ let by_to = ByTo::parse(input)?;
+ let y = LengthPercentage::parse(context, input)?;
+ Self::VLine { by_to, y }
+ },
+ "curve" => {
+ let by_to = ByTo::parse(input)?;
+ let point = CoordinatePair::parse(context, input)?;
+ input.expect_ident_matching("via")?;
+ let control1 = CoordinatePair::parse(context, input)?;
+ match input.try_parse(|i| CoordinatePair::parse(context, i)) {
+ Ok(control2) => Self::CubicCurve {
+ by_to,
+ point,
+ control1,
+ control2,
+ },
+ Err(_) => Self::QuadCurve {
+ by_to,
+ point,
+ control1,
+ },
+ }
+ },
+ "smooth" => {
+ let by_to = ByTo::parse(input)?;
+ let point = CoordinatePair::parse(context, input)?;
+ if input.try_parse(|i| i.expect_ident_matching("via")).is_ok() {
+ let control2 = CoordinatePair::parse(context, input)?;
+ Self::SmoothCubic {
+ by_to,
+ point,
+ control2,
+ }
+ } else {
+ Self::SmoothQuad { by_to, point }
+ }
+ },
+ "arc" => {
+ let by_to = ByTo::parse(input)?;
+ let point = CoordinatePair::parse(context, input)?;
+ input.expect_ident_matching("of")?;
+ let rx = LengthPercentage::parse(context, input)?;
+ let ry = input
+ .try_parse(|i| LengthPercentage::parse(context, i))
+ .unwrap_or(rx.clone());
+ let radii = CoordinatePair::new(rx, ry);
+
+ // [<arc-sweep> || <arc-size> || rotate <angle>]?
+ let mut arc_sweep = None;
+ let mut arc_size = None;
+ let mut rotate = None;
+ loop {
+ if arc_sweep.is_none() {
+ arc_sweep = input.try_parse(ArcSweep::parse).ok();
+ }
+
+ if arc_size.is_none() {
+ arc_size = input.try_parse(ArcSize::parse).ok();
+ if arc_size.is_some() {
+ continue;
+ }
+ }
+
+ if rotate.is_none()
+ && input
+ .try_parse(|i| i.expect_ident_matching("rotate"))
+ .is_ok()
+ {
+ rotate = Some(Angle::parse(context, input)?);
+ continue;
+ }
+ break;
+ }
+ Self::Arc {
+ by_to,
+ point,
+ radii,
+ arc_sweep: arc_sweep.unwrap_or(ArcSweep::Ccw),
+ arc_size: arc_size.unwrap_or(ArcSize::Small),
+ rotate: rotate.unwrap_or(Angle::zero()),
+ }
+ },
+ })
+ }
+}
+
+impl Parse for generic::CoordinatePair<LengthPercentage> {
+ fn parse<'i, 't>(
+ context: &ParserContext,
+ input: &mut Parser<'i, 't>,
+ ) -> Result<Self, ParseError<'i>> {
+ let x = LengthPercentage::parse(context, input)?;
+ let y = LengthPercentage::parse(context, input)?;
+ Ok(Self::new(x, y))
+ }
+}
diff --git a/servo/components/style/values/specified/box.rs b/servo/components/style/values/specified/box.rs
index 8414591c2b..ee50227504 100644
--- a/servo/components/style/values/specified/box.rs
+++ b/servo/components/style/values/specified/box.rs
@@ -19,12 +19,12 @@ use std::fmt::{self, Write};
use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
-#[cfg(not(feature = "servo-layout-2020"))]
+#[cfg(not(feature = "servo"))]
fn flexbox_enabled() -> bool {
true
}
-#[cfg(feature = "servo-layout-2020")]
+#[cfg(feature = "servo")]
fn flexbox_enabled() -> bool {
servo_config::prefs::pref_map()
.get("layout.flexbox.enabled")
@@ -42,9 +42,7 @@ pub enum DisplayOutside {
None = 0,
Inline,
Block,
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableCaption,
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
InternalTable,
#[cfg(feature = "gecko")]
InternalRuby,
@@ -55,28 +53,19 @@ pub enum DisplayOutside {
#[repr(u8)]
pub enum DisplayInside {
None = 0,
- #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
Contents,
Flow,
FlowRoot,
Flex,
#[cfg(feature = "gecko")]
Grid,
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
Table,
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableRowGroup,
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableColumn,
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableColumnGroup,
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableHeaderGroup,
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableFooterGroup,
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableRow,
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
TableCell,
#[cfg(feature = "gecko")]
Ruby,
@@ -145,7 +134,6 @@ impl Display {
/// ::new() inlined so cbindgen can use it
pub const None: Self =
Self(((DisplayOutside::None as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::None as u16);
- #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
pub const Contents: Self = Self(
((DisplayOutside::None as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Contents as u16,
);
@@ -170,14 +158,11 @@ impl Display {
#[cfg(feature = "gecko")]
pub const InlineGrid: Self =
Self(((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Grid as u16);
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const Table: Self =
Self(((DisplayOutside::Block as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Table as u16);
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const InlineTable: Self = Self(
((DisplayOutside::Inline as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Table as u16,
);
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableCaption: Self = Self(
((DisplayOutside::TableCaption as u16) << Self::OUTSIDE_SHIFT) | DisplayInside::Flow as u16,
);
@@ -195,37 +180,30 @@ impl Display {
// Internal table boxes.
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableRowGroup: Self = Self(
((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::TableRowGroup as u16,
);
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableHeaderGroup: Self = Self(
((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::TableHeaderGroup as u16,
);
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableFooterGroup: Self = Self(
((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::TableFooterGroup as u16,
);
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableColumn: Self = Self(
((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::TableColumn as u16,
);
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableColumnGroup: Self = Self(
((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::TableColumnGroup as u16,
);
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableRow: Self = Self(
((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::TableRow as u16,
);
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
pub const TableCell: Self = Self(
((DisplayOutside::InternalTable as u16) << Self::OUTSIDE_SHIFT) |
DisplayInside::TableCell as u16,
@@ -336,7 +314,6 @@ impl Display {
pub fn is_atomic_inline_level(&self) -> bool {
match *self {
Display::InlineBlock | Display::InlineFlex => true,
- #[cfg(any(feature = "servo-layout-2013"))]
Display::InlineTable => true,
_ => false,
}
@@ -373,7 +350,6 @@ impl Display {
///
/// Also used for :root style adjustments.
pub fn equivalent_block_display(&self, _is_root_element: bool) -> Self {
- #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
{
// Special handling for `contents` and `list-item`s on the root element.
if _is_root_element && (self.is_contents() || self.is_list_item()) {
@@ -392,7 +368,6 @@ impl Display {
Display::from3(DisplayOutside::Block, inside, self.is_list_item())
},
DisplayOutside::Block | DisplayOutside::None => *self,
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
_ => Display::Block,
}
}
@@ -419,7 +394,6 @@ impl Display {
#[inline]
pub fn is_contents(&self) -> bool {
match *self {
- #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
Display::Contents => true,
_ => false,
}
@@ -444,30 +418,20 @@ impl DisplayKeyword {
use self::DisplayKeyword::*;
Ok(try_match_ident_ignore_ascii_case! { input,
"none" => Full(Display::None),
- #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
"contents" => Full(Display::Contents),
"inline-block" => Full(Display::InlineBlock),
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"inline-table" => Full(Display::InlineTable),
"-webkit-flex" if flexbox_enabled() => Full(Display::Flex),
"inline-flex" | "-webkit-inline-flex" if flexbox_enabled() => Full(Display::InlineFlex),
#[cfg(feature = "gecko")]
"inline-grid" => Full(Display::InlineGrid),
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-caption" => Full(Display::TableCaption),
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-row-group" => Full(Display::TableRowGroup),
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-header-group" => Full(Display::TableHeaderGroup),
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-footer-group" => Full(Display::TableFooterGroup),
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-column" => Full(Display::TableColumn),
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-column-group" => Full(Display::TableColumnGroup),
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-row" => Full(Display::TableRow),
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table-cell" => Full(Display::TableCell),
#[cfg(feature = "gecko")]
"ruby-base" => Full(Display::RubyBase),
@@ -493,9 +457,7 @@ impl DisplayKeyword {
/// https://drafts.csswg.org/css-display/#typedef-display-inside
"flow" => Inside(DisplayInside::Flow),
"flex" if flexbox_enabled() => Inside(DisplayInside::Flex),
- #[cfg(any(feature = "servo-layout-2020", feature = "gecko"))]
"flow-root" => Inside(DisplayInside::FlowRoot),
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
"table" => Inside(DisplayInside::Table),
#[cfg(feature = "gecko")]
"grid" => Inside(DisplayInside::Grid),
@@ -517,13 +479,11 @@ impl ToCss for Display {
Display::InlineBlock => dest.write_str("inline-block"),
#[cfg(feature = "gecko")]
Display::WebkitInlineBox => dest.write_str("-webkit-inline-box"),
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
Display::TableCaption => dest.write_str("table-caption"),
_ => match (outside, inside) {
#[cfg(feature = "gecko")]
(DisplayOutside::Inline, DisplayInside::Grid) => dest.write_str("inline-grid"),
(DisplayOutside::Inline, DisplayInside::Flex) => dest.write_str("inline-flex"),
- #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))]
(DisplayOutside::Inline, DisplayInside::Table) => dest.write_str("inline-table"),
#[cfg(feature = "gecko")]
(DisplayOutside::Block, DisplayInside::Ruby) => dest.write_str("block ruby"),
@@ -1607,18 +1567,12 @@ pub enum Appearance {
TabScrollArrowBack,
#[parse(condition = "ParserContext::chrome_rules_enabled")]
TabScrollArrowForward,
- /// A toolbar in an application window.
- #[parse(condition = "ParserContext::chrome_rules_enabled")]
- Toolbar,
/// A single toolbar button (with no associated dropdown).
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Toolbarbutton,
/// The dropdown portion of a toolbar button
#[parse(condition = "ParserContext::chrome_rules_enabled")]
ToolbarbuttonDropdown,
- /// The toolbox that contains the toolbars.
- #[parse(condition = "ParserContext::chrome_rules_enabled")]
- Toolbox,
/// A tooltip.
#[parse(condition = "ParserContext::chrome_rules_enabled")]
Tooltip,
diff --git a/servo/components/style/values/specified/color.rs b/servo/components/style/values/specified/color.rs
index 3694b4e9bc..f823ba7d30 100644
--- a/servo/components/style/values/specified/color.rs
+++ b/servo/components/style/values/specified/color.rs
@@ -7,21 +7,20 @@
use super::AllowQuirks;
use crate::color::component::ColorComponent;
use crate::color::convert::normalize_hue;
-use crate::color::parsing::{self, FromParsedColor, NumberOrAngle, NumberOrPercentage};
+use crate::color::parsing::{
+ self, ColorParser, FromParsedColor, NumberOrAngle, NumberOrPercentage,
+};
use crate::color::{mix::ColorInterpolationMethod, AbsoluteColor, ColorSpace};
use crate::media_queries::Device;
use crate::parser::{Parse, ParserContext};
use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue};
-use crate::values::generics::calc::CalcUnits;
use crate::values::generics::color::{
ColorMixFlags, GenericCaretColor, GenericColorMix, GenericColorOrAuto,
};
-use crate::values::specified::calc::{CalcNode, Leaf};
use crate::values::specified::Percentage;
use crate::values::{normalize, CustomIdent};
use cssparser::color::OPAQUE;
use cssparser::{color::PredefinedColorSpace, BasicParseErrorKind, ParseErrorKind, Parser, Token};
-use itoa;
use std::fmt::{self, Write};
use std::io::Write as IoWrite;
use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind};
@@ -623,156 +622,6 @@ impl FromParsedColor for Color {
}
}
-struct ColorParser<'a, 'b: 'a>(&'a ParserContext<'b>);
-
-impl<'a, 'b: 'a, 'i: 'a> parsing::ColorParser<'i> for ColorParser<'a, 'b> {
- type Output = Color;
-
- fn parse_number_or_angle<'t>(
- &self,
- input: &mut Parser<'i, 't>,
- allow_none: bool,
- ) -> Result<ColorComponent<NumberOrAngle>, ParseError<'i>> {
- use crate::values::specified::Angle;
-
- let location = input.current_source_location();
- let token = input.next()?.clone();
- Ok(match token {
- Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => {
- ColorComponent::None
- },
- Token::Dimension {
- value, ref unit, ..
- } => {
- let angle = Angle::parse_dimension(value, unit, /* from_calc = */ false);
-
- let degrees = match angle {
- Ok(angle) => angle.degrees(),
- Err(()) => return Err(location.new_unexpected_token_error(token.clone())),
- };
-
- ColorComponent::Value(NumberOrAngle::Angle { degrees })
- },
- Token::Number { value, .. } => ColorComponent::Value(NumberOrAngle::Number { value }),
- Token::Function(ref name) => {
- let function = CalcNode::math_function(self.0, name, location)?;
- let node = CalcNode::parse(self.0, input, function, CalcUnits::ANGLE)?;
-
- // If we can resolve the calc node, then use the value.
- match node.resolve() {
- Ok(Leaf::Number(value)) => {
- ColorComponent::Value(NumberOrAngle::Number { value })
- },
- Ok(Leaf::Angle(angle)) => ColorComponent::Value(NumberOrAngle::Angle {
- degrees: angle.degrees(),
- }),
- _ => {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
- },
- }
- },
- t => return Err(location.new_unexpected_token_error(t)),
- })
- }
-
- fn parse_percentage<'t>(
- &self,
- input: &mut Parser<'i, 't>,
- allow_none: bool,
- ) -> Result<ColorComponent<f32>, ParseError<'i>> {
- let location = input.current_source_location();
-
- Ok(match *input.next()? {
- Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => {
- ColorComponent::None
- },
- Token::Percentage { unit_value, .. } => ColorComponent::Value(unit_value),
- Token::Function(ref name) => {
- let function = CalcNode::math_function(self.0, name, location)?;
- let node = CalcNode::parse(self.0, input, function, CalcUnits::PERCENTAGE)?;
-
- // If we can resolve the calc node, then use the value.
- let Ok(resolved_leaf) = node.resolve() else {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- };
- if let Leaf::Percentage(value) = resolved_leaf {
- ColorComponent::Value(value)
- } else {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- },
- ref t => return Err(location.new_unexpected_token_error(t.clone())),
- })
- }
-
- fn parse_number<'t>(
- &self,
- input: &mut Parser<'i, 't>,
- allow_none: bool,
- ) -> Result<ColorComponent<f32>, ParseError<'i>> {
- let location = input.current_source_location();
-
- Ok(match *input.next()? {
- Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => {
- ColorComponent::None
- },
- Token::Number { value, .. } => ColorComponent::Value(value),
- Token::Function(ref name) => {
- let function = CalcNode::math_function(self.0, name, location)?;
- let node = CalcNode::parse(self.0, input, function, CalcUnits::empty())?;
-
- // If we can resolve the calc node, then use the value.
- let Ok(resolved_leaf) = node.resolve() else {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- };
- if let Leaf::Number(value) = resolved_leaf {
- ColorComponent::Value(value)
- } else {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- },
- ref t => return Err(location.new_unexpected_token_error(t.clone())),
- })
- }
-
- fn parse_number_or_percentage<'t>(
- &self,
- input: &mut Parser<'i, 't>,
- allow_none: bool,
- ) -> Result<ColorComponent<NumberOrPercentage>, ParseError<'i>> {
- let location = input.current_source_location();
-
- Ok(match *input.next()? {
- Token::Ident(ref value) if allow_none && value.eq_ignore_ascii_case("none") => {
- ColorComponent::None
- },
- Token::Number { value, .. } => {
- ColorComponent::Value(NumberOrPercentage::Number { value })
- },
- Token::Percentage { unit_value, .. } => {
- ColorComponent::Value(NumberOrPercentage::Percentage { unit_value })
- },
- Token::Function(ref name) => {
- let function = CalcNode::math_function(self.0, name, location)?;
- let node = CalcNode::parse(self.0, input, function, CalcUnits::PERCENTAGE)?;
-
- // If we can resolve the calc node, then use the value.
- let Ok(resolved_leaf) = node.resolve() else {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- };
- if let Leaf::Percentage(unit_value) = resolved_leaf {
- ColorComponent::Value(NumberOrPercentage::Percentage { unit_value })
- } else if let Leaf::Number(value) = resolved_leaf {
- ColorComponent::Value(NumberOrPercentage::Number { value })
- } else {
- return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
- },
- ref t => return Err(location.new_unexpected_token_error(t.clone())),
- })
- }
-}
-
/// Whether to preserve authored colors during parsing. That's useful only if we
/// plan to serialize the color back.
#[derive(Copy, Clone)]
@@ -809,7 +658,7 @@ impl Color {
},
};
- let color_parser = ColorParser(&*context);
+ let color_parser = ColorParser { context: &context };
match input.try_parse(|i| parsing::parse_color_with(&color_parser, i)) {
Ok(mut color) => {
if let Color::Absolute(ref mut absolute) = color {
diff --git a/servo/components/style/values/specified/counters.rs b/servo/components/style/values/specified/counters.rs
index 9d8261ce6c..7760be91d7 100644
--- a/servo/components/style/values/specified/counters.rs
+++ b/servo/components/style/values/specified/counters.rs
@@ -4,7 +4,7 @@
//! Specified types for counter properties.
-#[cfg(feature = "servo-layout-2013")]
+#[cfg(feature = "servo")]
use crate::computed_values::list_style_type::T as ListStyleType;
use crate::parser::{Parse, ParserContext};
use crate::values::generics::counters as generics;
@@ -12,12 +12,10 @@ use crate::values::generics::counters::CounterPair;
#[cfg(feature = "gecko")]
use crate::values::generics::CounterStyle;
use crate::values::specified::image::Image;
-#[cfg(any(feature = "gecko", feature = "servo-layout-2020"))]
use crate::values::specified::Attr;
use crate::values::specified::Integer;
use crate::values::CustomIdent;
use cssparser::{Parser, Token};
-#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
use selectors::parser::SelectorParseErrorKind;
use style_traits::{ParseError, StyleParseErrorKind};
@@ -151,7 +149,7 @@ pub type Content = generics::GenericContent<Image>;
pub type ContentItem = generics::GenericContentItem<Image>;
impl Content {
- #[cfg(feature = "servo-layout-2013")]
+ #[cfg(feature = "servo")]
fn parse_counter_style(_: &ParserContext, input: &mut Parser) -> ListStyleType {
input
.try_parse(|input| {
@@ -197,7 +195,6 @@ impl Parse for Content {
let mut content = vec![];
let mut has_alt_content = false;
loop {
- #[cfg(any(feature = "gecko", feature = "servo-layout-2020"))]
{
if let Ok(image) = input.try_parse(|i| Image::parse_forbid_none(context, i)) {
content.push(generics::ContentItem::Image(image));
@@ -212,13 +209,11 @@ impl Parse for Content {
},
Ok(&Token::Function(ref name)) => {
let result = match_ignore_ascii_case! { &name,
- #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
"counter" => input.parse_nested_block(|input| {
let name = CustomIdent::parse(input, &[])?;
let style = Content::parse_counter_style(context, input);
Ok(generics::ContentItem::Counter(name, style))
}),
- #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
"counters" => input.parse_nested_block(|input| {
let name = CustomIdent::parse(input, &[])?;
input.expect_comma()?;
@@ -226,7 +221,6 @@ impl Parse for Content {
let style = Content::parse_counter_style(context, input);
Ok(generics::ContentItem::Counters(name, separator, style))
}),
- #[cfg(any(feature = "gecko", feature = "servo-layout-2020"))]
"attr" => input.parse_nested_block(|input| {
Ok(generics::ContentItem::Attr(Attr::parse_function(context, input)?))
}),
@@ -240,7 +234,6 @@ impl Parse for Content {
}?;
content.push(result);
},
- #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
Ok(&Token::Ident(ref ident)) => {
content.push(match_ignore_ascii_case! { &ident,
"open-quote" => generics::ContentItem::OpenQuote,
diff --git a/servo/components/style/values/specified/easing.rs b/servo/components/style/values/specified/easing.rs
index 5e4d8ae1ea..7697e6a5d7 100644
--- a/servo/components/style/values/specified/easing.rs
+++ b/servo/components/style/values/specified/easing.rs
@@ -9,7 +9,7 @@ use crate::values::computed::easing::TimingFunction as ComputedTimingFunction;
use crate::values::computed::{Context, ToComputedValue};
use crate::values::generics::easing::TimingFunction as GenericTimingFunction;
use crate::values::generics::easing::{StepPosition, TimingKeyword};
-use crate::values::specified::{Integer, Number, Percentage};
+use crate::values::specified::{AnimationName, Integer, Number, Percentage};
use cssparser::{Delimiter, Parser, Token};
use selectors::parser::SelectorParseErrorKind;
use style_traits::{ParseError, StyleParseErrorKind};
@@ -140,6 +140,15 @@ impl TimingFunction {
Ok(GenericTimingFunction::LinearFunction(builder.build()))
}
+
+ /// Returns true if the name matches any keyword.
+ #[inline]
+ pub fn match_keywords(name: &AnimationName) -> bool {
+ if let Some(name) = name.as_atom() {
+ return name.with_str(|n| TimingKeyword::from_ident(n).is_ok());
+ }
+ false
+ }
}
// We need this for converting the specified TimingFunction into computed TimingFunction without
diff --git a/servo/components/style/values/specified/effects.rs b/servo/components/style/values/specified/effects.rs
index 0453582768..a32390e148 100644
--- a/servo/components/style/values/specified/effects.rs
+++ b/servo/components/style/values/specified/effects.rs
@@ -28,7 +28,7 @@ use crate::values::specified::{Angle, Number, NumberOrPercentage};
#[cfg(feature = "servo")]
use crate::values::Impossible;
use crate::Zero;
-use cssparser::{self, BasicParseErrorKind, Parser, Token};
+use cssparser::{BasicParseErrorKind, Parser, Token};
use style_traits::{ParseError, StyleParseErrorKind, ValueParseErrorKind};
/// A specified value for a single shadow of the `box-shadow` property.
diff --git a/servo/components/style/values/specified/image.rs b/servo/components/style/values/specified/image.rs
index 76bbbf85df..bedced1a27 100644
--- a/servo/components/style/values/specified/image.rs
+++ b/servo/components/style/values/specified/image.rs
@@ -241,7 +241,7 @@ impl Image {
let function = input.expect_function()?.clone();
input.parse_nested_block(|input| {
Ok(match_ignore_ascii_case! { &function,
- #[cfg(feature = "servo-layout-2013")]
+ #[cfg(feature = "servo")]
"paint" => Self::PaintWorklet(PaintWorklet::parse_args(context, input)?),
"cross-fade" if cross_fade_enabled() => Self::CrossFade(Box::new(CrossFade::parse_args(context, input, cors_mode, flags)?)),
#[cfg(feature = "gecko")]
diff --git a/servo/components/style/values/specified/length.rs b/servo/components/style/values/specified/length.rs
index d2e1d7d346..17ff43cd6f 100644
--- a/servo/components/style/values/specified/length.rs
+++ b/servo/components/style/values/specified/length.rs
@@ -97,9 +97,15 @@ pub enum LineHeightBase {
impl FontBaseSize {
/// Calculate the actual size for a given context
pub fn resolve(&self, context: &Context) -> computed::FontSize {
+ let style = context.style();
match *self {
- Self::CurrentStyle => context.style().get_font().clone_font_size(),
- Self::InheritedStyle => context.style().get_parent_font().clone_font_size(),
+ Self::CurrentStyle => style.get_font().clone_font_size(),
+ Self::InheritedStyle => {
+ // If we're using the size from our inherited style, we still need to apply our
+ // own zoom.
+ let zoom = style.get_box().clone_zoom();
+ style.get_parent_font().clone_font_size().zoom(zoom)
+ },
}
}
}
@@ -351,7 +357,7 @@ impl FontRelativeLength {
let reference_size = if context.builder.is_root_element || context.in_media_query {
reference_font_size.computed_size()
} else {
- context.device().root_font_size()
+ context.device().root_font_size().zoom(context.builder.effective_zoom)
};
(reference_size, length)
},
@@ -394,19 +400,19 @@ impl FontRelativeLength {
// When specified on the root element, the rlh units refer
// to the initial values of font and line-height properties.
//
- let reference_size: CSSPixelLength =
- if context.builder.is_root_element || context.in_media_query {
- context
- .device()
- .calc_line_height(
- &context.default_style().get_font(),
- context.style().writing_mode,
- None,
- )
- .0
- } else {
- context.device().root_line_height()
- };
+ let reference_size = if context.builder.is_root_element || context.in_media_query {
+ context
+ .device()
+ .calc_line_height(
+ &context.default_style().get_font(),
+ context.style().writing_mode,
+ None,
+ )
+ .0
+ } else {
+ context.device().root_line_height()
+ };
+ let reference_size = reference_size.zoom(context.builder.effective_zoom);
(reference_size, length)
},
}
@@ -668,7 +674,7 @@ impl ViewportPercentageLength {
pub fn to_computed_value(&self, context: &Context) -> CSSPixelLength {
let (variant, unit, factor) = self.unpack();
let size = context.viewport_size_for_viewport_unit_resolution(variant);
- let length = match unit {
+ let length: app_units::Au = match unit {
ViewportUnit::Vw => size.width,
ViewportUnit::Vh => size.height,
ViewportUnit::Vmin => cmp::min(size.width, size.height),
@@ -686,13 +692,15 @@ impl ViewportPercentageLength {
},
};
+ // NOTE: This is in app units!
+ let length = context.builder.effective_zoom.zoom(length.0 as f32);
+
// FIXME: Bug 1396535, we need to fix the extremely small viewport length for transform.
- // See bug 989802. We truncate so that adding multiple viewport units
- // that add up to 100 does not overflow due to rounding differences.
- // We convert appUnits to CSS px manually here to avoid premature clamping by
- // going through the Au type.
+ // See bug 989802. We truncate so that adding multiple viewport units that add up to 100
+ // does not overflow due to rounding differences. We convert appUnits to CSS px manually
+ // here to avoid premature clamping by going through the Au type.
let trunc_scaled =
- ((length.0 as f64 * factor as f64 / 100.).trunc() / AU_PER_PX as f64) as f32;
+ ((length as f64 * factor as f64 / 100.).trunc() / AU_PER_PX as f64) as f32;
CSSPixelLength::new(crate::values::normalize(trunc_scaled))
}
}
@@ -797,7 +805,7 @@ impl ToComputedValue for AbsoluteLength {
type ComputedValue = CSSPixelLength;
fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
- CSSPixelLength::new(context.builder.effective_zoom().zoom(self.to_px())).finite()
+ CSSPixelLength::new(self.to_px()).zoom(context.builder.effective_zoom).finite()
}
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
@@ -910,6 +918,9 @@ impl ContainerRelativeLength {
.builder
.add_flags(ComputedValueFlags::USES_CONTAINER_UNITS);
+ // TODO(emilio, bug 1894104): Need to handle zoom here, probably something like
+ // container_zoom - effective_zoom or so. See
+ // https://github.com/w3c/csswg-drafts/issues/10268
let size = context.get_container_size_query();
let (factor, container_length) = match *self {
Self::Cqw(v) => (v, size.get_container_width(context)),
diff --git a/servo/components/style/values/specified/motion.rs b/servo/components/style/values/specified/motion.rs
index 98858c712c..15c76bc640 100644
--- a/servo/components/style/values/specified/motion.rs
+++ b/servo/components/style/values/specified/motion.rs
@@ -76,10 +76,6 @@ impl Parse for RayFunction {
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
- if !static_prefs::pref!("layout.css.motion-path-ray.enabled") {
- return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
- }
-
input.expect_function_matching("ray")?;
input.parse_nested_block(|i| Self::parse_function_arguments(context, i))
}
@@ -154,11 +150,8 @@ impl Parse for OffsetPathFunction {
// <offset-path> = <ray()> | <url> | <basic-shape>
// https://drafts.fxtf.org/motion-1/#typedef-offset-path
-
- if static_prefs::pref!("layout.css.motion-path-ray.enabled") {
- if let Ok(ray) = input.try_parse(|i| RayFunction::parse(context, i)) {
- return Ok(OffsetPathFunction::Ray(ray));
- }
+ if let Ok(ray) = input.try_parse(|i| RayFunction::parse(context, i)) {
+ return Ok(OffsetPathFunction::Ray(ray));
}
if static_prefs::pref!("layout.css.motion-path-url.enabled") {
@@ -167,13 +160,7 @@ impl Parse for OffsetPathFunction {
}
}
- let allowed_shapes = if static_prefs::pref!("layout.css.motion-path-basic-shapes.enabled") {
- AllowedBasicShapes::ALL
- } else {
- AllowedBasicShapes::PATH
- };
-
- BasicShape::parse(context, input, allowed_shapes, ShapeType::Outline)
+ BasicShape::parse(context, input, AllowedBasicShapes::ALL, ShapeType::Outline)
.map(OffsetPathFunction::Shape)
}
}
@@ -197,9 +184,7 @@ impl Parse for OffsetPath {
.ok();
}
- if static_prefs::pref!("layout.css.motion-path-coord-box.enabled") &&
- coord_box.is_none()
- {
+ if coord_box.is_none() {
coord_box = input.try_parse(CoordBox::parse).ok();
if coord_box.is_some() {
continue;
diff --git a/servo/components/style/values/specified/svg_path.rs b/servo/components/style/values/specified/svg_path.rs
index 1eb9866dd1..56342b48bb 100644
--- a/servo/components/style/values/specified/svg_path.rs
+++ b/servo/components/style/values/specified/svg_path.rs
@@ -5,10 +5,13 @@
//! Specified types for SVG Path.
use crate::parser::{Parse, ParserContext};
-use crate::values::animated::{lists, Animate, Procedure, ToAnimatedZero};
+use crate::values::animated::{lists, Animate, Procedure};
use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
+use crate::values::generics::basic_shape::GenericShapeCommand;
+use crate::values::generics::basic_shape::{ArcSize, ArcSweep, ByTo, CoordinatePair};
use crate::values::CSSFloat;
use cssparser::Parser;
+use num_traits::FromPrimitive;
use std::fmt::{self, Write};
use std::iter::{Cloned, Peekable};
use std::slice;
@@ -70,6 +73,7 @@ impl SVGPathData {
#[cfg(feature = "gecko")]
pub fn decode_from_f32_array(path: &[f32]) -> Result<Self, ()> {
use crate::gecko_bindings::structs::dom::SVGPathSeg_Binding::*;
+ use crate::values::generics::basic_shape::GenericShapeCommand::*;
let mut result: Vec<PathCommand> = Vec::new();
let mut i: usize = 0;
@@ -80,85 +84,84 @@ impl SVGPathData {
let seg_type = path[i].to_bits() as u16;
i = i + 1;
match seg_type {
- PATHSEG_CLOSEPATH => result.push(PathCommand::ClosePath),
+ PATHSEG_CLOSEPATH => result.push(Close),
PATHSEG_MOVETO_ABS | PATHSEG_MOVETO_REL => {
debug_assert!(i + 1 < path.len());
- result.push(PathCommand::MoveTo {
+ result.push(Move {
point: CoordPair::new(path[i], path[i + 1]),
- absolute: IsAbsolute::new(seg_type == PATHSEG_MOVETO_ABS),
+ by_to: ByTo::new(seg_type == PATHSEG_MOVETO_ABS),
});
i = i + 2;
},
PATHSEG_LINETO_ABS | PATHSEG_LINETO_REL => {
debug_assert!(i + 1 < path.len());
- result.push(PathCommand::LineTo {
+ result.push(Line {
point: CoordPair::new(path[i], path[i + 1]),
- absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_ABS),
+ by_to: ByTo::new(seg_type == PATHSEG_LINETO_ABS),
});
i = i + 2;
},
PATHSEG_CURVETO_CUBIC_ABS | PATHSEG_CURVETO_CUBIC_REL => {
debug_assert!(i + 5 < path.len());
- result.push(PathCommand::CurveTo {
+ result.push(CubicCurve {
control1: CoordPair::new(path[i], path[i + 1]),
control2: CoordPair::new(path[i + 2], path[i + 3]),
point: CoordPair::new(path[i + 4], path[i + 5]),
- absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_ABS),
+ by_to: ByTo::new(seg_type == PATHSEG_CURVETO_CUBIC_ABS),
});
i = i + 6;
},
PATHSEG_CURVETO_QUADRATIC_ABS | PATHSEG_CURVETO_QUADRATIC_REL => {
debug_assert!(i + 3 < path.len());
- result.push(PathCommand::QuadBezierCurveTo {
+ result.push(QuadCurve {
control1: CoordPair::new(path[i], path[i + 1]),
point: CoordPair::new(path[i + 2], path[i + 3]),
- absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_ABS),
+ by_to: ByTo::new(seg_type == PATHSEG_CURVETO_QUADRATIC_ABS),
});
i = i + 4;
},
PATHSEG_ARC_ABS | PATHSEG_ARC_REL => {
debug_assert!(i + 6 < path.len());
- result.push(PathCommand::EllipticalArc {
- rx: path[i],
- ry: path[i + 1],
- angle: path[i + 2],
- large_arc_flag: ArcFlag(path[i + 3] != 0.0f32),
- sweep_flag: ArcFlag(path[i + 4] != 0.0f32),
+ result.push(Arc {
+ radii: CoordPair::new(path[i], path[i + 1]),
+ rotate: path[i + 2],
+ arc_size: ArcSize::from_u8((path[i + 3] != 0.0f32) as u8).unwrap(),
+ arc_sweep: ArcSweep::from_u8((path[i + 4] != 0.0f32) as u8).unwrap(),
point: CoordPair::new(path[i + 5], path[i + 6]),
- absolute: IsAbsolute::new(seg_type == PATHSEG_ARC_ABS),
+ by_to: ByTo::new(seg_type == PATHSEG_ARC_ABS),
});
i = i + 7;
},
PATHSEG_LINETO_HORIZONTAL_ABS | PATHSEG_LINETO_HORIZONTAL_REL => {
debug_assert!(i < path.len());
- result.push(PathCommand::HorizontalLineTo {
+ result.push(HLine {
x: path[i],
- absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_HORIZONTAL_ABS),
+ by_to: ByTo::new(seg_type == PATHSEG_LINETO_HORIZONTAL_ABS),
});
i = i + 1;
},
PATHSEG_LINETO_VERTICAL_ABS | PATHSEG_LINETO_VERTICAL_REL => {
debug_assert!(i < path.len());
- result.push(PathCommand::VerticalLineTo {
+ result.push(VLine {
y: path[i],
- absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_VERTICAL_ABS),
+ by_to: ByTo::new(seg_type == PATHSEG_LINETO_VERTICAL_ABS),
});
i = i + 1;
},
PATHSEG_CURVETO_CUBIC_SMOOTH_ABS | PATHSEG_CURVETO_CUBIC_SMOOTH_REL => {
debug_assert!(i + 3 < path.len());
- result.push(PathCommand::SmoothCurveTo {
+ result.push(SmoothCubic {
control2: CoordPair::new(path[i], path[i + 1]),
point: CoordPair::new(path[i + 2], path[i + 3]),
- absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS),
+ by_to: ByTo::new(seg_type == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS),
});
i = i + 4;
},
PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS | PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL => {
debug_assert!(i + 1 < path.len());
- result.push(PathCommand::SmoothQuadBezierCurveTo {
+ result.push(SmoothQuad {
point: CoordPair::new(path[i], path[i + 1]),
- absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS),
+ by_to: ByTo::new(seg_type == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS),
});
i = i + 2;
},
@@ -215,7 +218,7 @@ impl ToCss for SVGPathData {
{
let mut writer = SequenceWriter::new(dest, " ");
for command in self.commands() {
- writer.item(command)?;
+ writer.write_item(|inner| command.to_css_for_svg(inner))?;
}
}
dest.write_char('"')
@@ -268,79 +271,7 @@ impl ComputeSquaredDistance for SVGPathData {
/// points of the Bézier curve in the spec.
///
/// https://www.w3.org/TR/SVG11/paths.html#PathData
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[allow(missing_docs)]
-#[repr(C, u8)]
-pub enum PathCommand {
- /// The unknown type.
- /// https://www.w3.org/TR/SVG/paths.html#__svg__SVGPathSeg__PATHSEG_UNKNOWN
- Unknown,
- /// The "moveto" command.
- MoveTo {
- point: CoordPair,
- absolute: IsAbsolute,
- },
- /// The "lineto" command.
- LineTo {
- point: CoordPair,
- absolute: IsAbsolute,
- },
- /// The horizontal "lineto" command.
- HorizontalLineTo { x: CSSFloat, absolute: IsAbsolute },
- /// The vertical "lineto" command.
- VerticalLineTo { y: CSSFloat, absolute: IsAbsolute },
- /// The cubic Bézier curve command.
- CurveTo {
- control1: CoordPair,
- control2: CoordPair,
- point: CoordPair,
- absolute: IsAbsolute,
- },
- /// The smooth curve command.
- SmoothCurveTo {
- control2: CoordPair,
- point: CoordPair,
- absolute: IsAbsolute,
- },
- /// The quadratic Bézier curve command.
- QuadBezierCurveTo {
- control1: CoordPair,
- point: CoordPair,
- absolute: IsAbsolute,
- },
- /// The smooth quadratic Bézier curve command.
- SmoothQuadBezierCurveTo {
- point: CoordPair,
- absolute: IsAbsolute,
- },
- /// The elliptical arc curve command.
- EllipticalArc {
- rx: CSSFloat,
- ry: CSSFloat,
- angle: CSSFloat,
- large_arc_flag: ArcFlag,
- sweep_flag: ArcFlag,
- point: CoordPair,
- absolute: IsAbsolute,
- },
- /// The "closepath" command.
- ClosePath,
-}
+pub type PathCommand = GenericShapeCommand<CSSFloat, CSSFloat>;
/// For internal SVGPath normalization.
#[allow(missing_docs)]
@@ -355,177 +286,157 @@ impl PathCommand {
///
/// See discussion: https://github.com/w3c/svgwg/issues/321
fn normalize(&self, state: &mut PathTraversalState) -> Self {
- use self::PathCommand::*;
+ use crate::values::generics::basic_shape::GenericShapeCommand::*;
match *self {
- Unknown => Unknown,
- ClosePath => {
+ Close => {
state.pos = state.subpath_start;
- ClosePath
+ Close
},
- MoveTo {
- mut point,
- absolute,
- } => {
- if !absolute.is_yes() {
+ Move { by_to, mut point } => {
+ if !by_to.is_abs() {
point += state.pos;
}
state.pos = point;
state.subpath_start = point;
- MoveTo {
+ Move {
+ by_to: ByTo::To,
point,
- absolute: IsAbsolute::Yes,
}
},
- LineTo {
- mut point,
- absolute,
- } => {
- if !absolute.is_yes() {
+ Line { by_to, mut point } => {
+ if !by_to.is_abs() {
point += state.pos;
}
state.pos = point;
- LineTo {
+ Line {
+ by_to: ByTo::To,
point,
- absolute: IsAbsolute::Yes,
}
},
- HorizontalLineTo { mut x, absolute } => {
- if !absolute.is_yes() {
+ HLine { by_to, mut x } => {
+ if !by_to.is_abs() {
x += state.pos.x;
}
state.pos.x = x;
- HorizontalLineTo {
- x,
- absolute: IsAbsolute::Yes,
- }
+ HLine { by_to: ByTo::To, x }
},
- VerticalLineTo { mut y, absolute } => {
- if !absolute.is_yes() {
+ VLine { by_to, mut y } => {
+ if !by_to.is_abs() {
y += state.pos.y;
}
state.pos.y = y;
- VerticalLineTo {
- y,
- absolute: IsAbsolute::Yes,
- }
+ VLine { by_to: ByTo::To, y }
},
- CurveTo {
+ CubicCurve {
+ by_to,
+ mut point,
mut control1,
mut control2,
- mut point,
- absolute,
} => {
- if !absolute.is_yes() {
+ if !by_to.is_abs() {
+ point += state.pos;
control1 += state.pos;
control2 += state.pos;
- point += state.pos;
}
state.pos = point;
- CurveTo {
+ CubicCurve {
+ by_to: ByTo::To,
+ point,
control1,
control2,
- point,
- absolute: IsAbsolute::Yes,
}
},
- SmoothCurveTo {
- mut control2,
+ QuadCurve {
+ by_to,
mut point,
- absolute,
+ mut control1,
} => {
- if !absolute.is_yes() {
- control2 += state.pos;
+ if !by_to.is_abs() {
point += state.pos;
+ control1 += state.pos;
}
state.pos = point;
- SmoothCurveTo {
- control2,
+ QuadCurve {
+ by_to: ByTo::To,
point,
- absolute: IsAbsolute::Yes,
+ control1,
}
},
- QuadBezierCurveTo {
- mut control1,
+ SmoothCubic {
+ by_to,
mut point,
- absolute,
+ mut control2,
} => {
- if !absolute.is_yes() {
- control1 += state.pos;
+ if !by_to.is_abs() {
point += state.pos;
+ control2 += state.pos;
}
state.pos = point;
- QuadBezierCurveTo {
- control1,
+ SmoothCubic {
+ by_to: ByTo::To,
point,
- absolute: IsAbsolute::Yes,
+ control2,
}
},
- SmoothQuadBezierCurveTo {
- mut point,
- absolute,
- } => {
- if !absolute.is_yes() {
+ SmoothQuad { by_to, mut point } => {
+ if !by_to.is_abs() {
point += state.pos;
}
state.pos = point;
- SmoothQuadBezierCurveTo {
+ SmoothQuad {
+ by_to: ByTo::To,
point,
- absolute: IsAbsolute::Yes,
}
},
- EllipticalArc {
- rx,
- ry,
- angle,
- large_arc_flag,
- sweep_flag,
+ Arc {
+ by_to,
mut point,
- absolute,
+ radii,
+ arc_sweep,
+ arc_size,
+ rotate,
} => {
- if !absolute.is_yes() {
+ if !by_to.is_abs() {
point += state.pos;
}
state.pos = point;
- EllipticalArc {
- rx,
- ry,
- angle,
- large_arc_flag,
- sweep_flag,
+ Arc {
+ by_to: ByTo::To,
point,
- absolute: IsAbsolute::Yes,
+ radii,
+ arc_sweep,
+ arc_size,
+ rotate,
}
},
}
}
-}
-impl ToCss for PathCommand {
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
+ /// The serialization of the svg path.
+ fn to_css_for_svg<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: fmt::Write,
{
- use self::PathCommand::*;
+ use crate::values::generics::basic_shape::GenericShapeCommand::*;
match *self {
- Unknown => dest.write_char('X'),
- ClosePath => dest.write_char('Z'),
- MoveTo { point, absolute } => {
- dest.write_char(if absolute.is_yes() { 'M' } else { 'm' })?;
+ Close => dest.write_char('Z'),
+ Move { by_to, point } => {
+ dest.write_char(if by_to.is_abs() { 'M' } else { 'm' })?;
dest.write_char(' ')?;
point.to_css(dest)
},
- LineTo { point, absolute } => {
- dest.write_char(if absolute.is_yes() { 'L' } else { 'l' })?;
+ Line { by_to, point } => {
+ dest.write_char(if by_to.is_abs() { 'L' } else { 'l' })?;
dest.write_char(' ')?;
point.to_css(dest)
},
- CurveTo {
+ CubicCurve {
+ by_to,
+ point,
control1,
control2,
- point,
- absolute,
} => {
- dest.write_char(if absolute.is_yes() { 'C' } else { 'c' })?;
+ dest.write_char(if by_to.is_abs() { 'C' } else { 'c' })?;
dest.write_char(' ')?;
control1.to_css(dest)?;
dest.write_char(' ')?;
@@ -533,63 +444,60 @@ impl ToCss for PathCommand {
dest.write_char(' ')?;
point.to_css(dest)
},
- QuadBezierCurveTo {
- control1,
+ QuadCurve {
+ by_to,
point,
- absolute,
+ control1,
} => {
- dest.write_char(if absolute.is_yes() { 'Q' } else { 'q' })?;
+ dest.write_char(if by_to.is_abs() { 'Q' } else { 'q' })?;
dest.write_char(' ')?;
control1.to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)
},
- EllipticalArc {
- rx,
- ry,
- angle,
- large_arc_flag,
- sweep_flag,
+ Arc {
+ by_to,
point,
- absolute,
+ radii,
+ arc_sweep,
+ arc_size,
+ rotate,
} => {
- dest.write_char(if absolute.is_yes() { 'A' } else { 'a' })?;
- dest.write_char(' ')?;
- rx.to_css(dest)?;
+ dest.write_char(if by_to.is_abs() { 'A' } else { 'a' })?;
dest.write_char(' ')?;
- ry.to_css(dest)?;
+ radii.to_css(dest)?;
dest.write_char(' ')?;
- angle.to_css(dest)?;
+ rotate.to_css(dest)?;
dest.write_char(' ')?;
- large_arc_flag.to_css(dest)?;
+ (arc_size as i32).to_css(dest)?;
dest.write_char(' ')?;
- sweep_flag.to_css(dest)?;
+ (arc_sweep as i32).to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)
},
- HorizontalLineTo { x, absolute } => {
- dest.write_char(if absolute.is_yes() { 'H' } else { 'h' })?;
+ HLine { by_to, x } => {
+ dest.write_char(if by_to.is_abs() { 'H' } else { 'h' })?;
dest.write_char(' ')?;
x.to_css(dest)
},
- VerticalLineTo { y, absolute } => {
- dest.write_char(if absolute.is_yes() { 'V' } else { 'v' })?;
+ VLine { by_to, y } => {
+ dest.write_char(if by_to.is_abs() { 'V' } else { 'v' })?;
dest.write_char(' ')?;
y.to_css(dest)
},
- SmoothCurveTo {
- control2,
+ SmoothCubic {
+ by_to,
point,
- absolute,
+ control2,
} => {
- dest.write_char(if absolute.is_yes() { 'S' } else { 's' })?;
+ dest.write_char(if by_to.is_abs() { 'S' } else { 's' })?;
dest.write_char(' ')?;
control2.to_css(dest)?;
dest.write_char(' ')?;
point.to_css(dest)
},
- SmoothQuadBezierCurveTo { point, absolute } => {
- dest.write_char(if absolute.is_yes() { 'T' } else { 't' })?;
+ SmoothQuad { by_to, point } => {
+ dest.write_char(if by_to.is_abs() { 'T' } else { 't' })?;
dest.write_char(' ')?;
point.to_css(dest)
},
@@ -597,135 +505,8 @@ impl ToCss for PathCommand {
}
}
-/// The path command absolute type.
-#[allow(missing_docs)]
-#[derive(
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(u8)]
-pub enum IsAbsolute {
- Yes,
- No,
-}
-
-impl IsAbsolute {
- /// Return true if this is IsAbsolute::Yes.
- #[inline]
- pub fn is_yes(&self) -> bool {
- *self == IsAbsolute::Yes
- }
-
- /// Return Yes if value is true. Otherwise, return No.
- #[inline]
- fn new(value: bool) -> Self {
- if value {
- IsAbsolute::Yes
- } else {
- IsAbsolute::No
- }
- }
-}
-
/// The path coord type.
-#[allow(missing_docs)]
-#[derive(
- AddAssign,
- Animate,
- Clone,
- ComputeSquaredDistance,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToAnimatedZero,
- ToComputedValue,
- ToCss,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct CoordPair {
- x: CSSFloat,
- y: CSSFloat,
-}
-
-impl CoordPair {
- /// Create a CoordPair.
- #[inline]
- pub fn new(x: CSSFloat, y: CSSFloat) -> Self {
- CoordPair { x, y }
- }
-}
-
-/// The EllipticalArc flag type.
-#[derive(
- Clone,
- Copy,
- Debug,
- Deserialize,
- MallocSizeOf,
- PartialEq,
- Serialize,
- SpecifiedValueInfo,
- ToComputedValue,
- ToResolvedValue,
- ToShmem,
-)]
-#[repr(C)]
-pub struct ArcFlag(bool);
-
-impl ToCss for ArcFlag {
- #[inline]
- fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
- where
- W: fmt::Write,
- {
- (self.0 as i32).to_css(dest)
- }
-}
-
-impl Animate for ArcFlag {
- #[inline]
- fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
- (self.0 as i32)
- .animate(&(other.0 as i32), procedure)
- .map(|v| ArcFlag(v > 0))
- }
-}
-
-impl ComputeSquaredDistance for ArcFlag {
- #[inline]
- fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
- (self.0 as i32).compute_squared_distance(&(other.0 as i32))
- }
-}
-
-impl ToAnimatedZero for ArcFlag {
- #[inline]
- fn to_animated_zero(&self) -> Result<Self, ()> {
- // The 2 ArcFlags in EllipticalArc determine which one of the 4 different arcs will be
- // used. (i.e. From 4 combinations). In other words, if we change the flag, we get a
- // different arc. Therefore, we return *self.
- // https://svgwg.org/svg2-draft/paths.html#PathDataEllipticalArcCommands
- Ok(*self)
- }
-}
+pub type CoordPair = CoordinatePair<CSSFloat>;
/// SVG Path parser.
struct PathParser<'a> {
@@ -736,7 +517,7 @@ struct PathParser<'a> {
macro_rules! parse_arguments {
(
$parser:ident,
- $abs:ident,
+ $by_to:ident,
$enum:ident,
[ $para:ident => $func:ident $(, $other_para:ident => $other_func:ident)* ]
) => {
@@ -747,7 +528,9 @@ macro_rules! parse_arguments {
skip_comma_wsp(&mut $parser.chars);
let $other_para = $other_func(&mut $parser.chars)?;
)*
- $parser.path.push(PathCommand::$enum { $para $(, $other_para)*, $abs });
+ $parser.path.push(
+ PathCommand::$enum { $by_to, $para $(, $other_para)* }
+ );
// End of string or the next character is a possible new command.
if !skip_wsp(&mut $parser.chars) ||
@@ -785,23 +568,23 @@ impl<'a> PathParser<'a> {
}
let command = self.chars.next().unwrap();
- let abs = if command.is_ascii_uppercase() {
- IsAbsolute::Yes
+ let by_to = if command.is_ascii_uppercase() {
+ ByTo::To
} else {
- IsAbsolute::No
+ ByTo::By
};
skip_wsp(&mut self.chars);
match command {
b'Z' | b'z' => self.parse_closepath(),
- b'L' | b'l' => self.parse_lineto(abs),
- b'H' | b'h' => self.parse_h_lineto(abs),
- b'V' | b'v' => self.parse_v_lineto(abs),
- b'C' | b'c' => self.parse_curveto(abs),
- b'S' | b's' => self.parse_smooth_curveto(abs),
- b'Q' | b'q' => self.parse_quadratic_bezier_curveto(abs),
- b'T' | b't' => self.parse_smooth_quadratic_bezier_curveto(abs),
- b'A' | b'a' => self.parse_elliptical_arc(abs),
+ b'L' | b'l' => self.parse_lineto(by_to),
+ b'H' | b'h' => self.parse_h_lineto(by_to),
+ b'V' | b'v' => self.parse_v_lineto(by_to),
+ b'C' | b'c' => self.parse_curveto(by_to),
+ b'S' | b's' => self.parse_smooth_curveto(by_to),
+ b'Q' | b'q' => self.parse_quadratic_bezier_curveto(by_to),
+ b'T' | b't' => self.parse_smooth_quadratic_bezier_curveto(by_to),
+ b'A' | b'a' => self.parse_elliptical_arc(by_to),
_ => return Err(()),
}?;
}
@@ -817,12 +600,8 @@ impl<'a> PathParser<'a> {
skip_wsp(&mut self.chars);
let point = parse_coord(&mut self.chars)?;
- let absolute = if command == b'M' {
- IsAbsolute::Yes
- } else {
- IsAbsolute::No
- };
- self.path.push(PathCommand::MoveTo { point, absolute });
+ let by_to = if command == b'M' { ByTo::To } else { ByTo::By };
+ self.path.push(PathCommand::Move { by_to, point });
// End of string or the next character is a possible new command.
if !skip_wsp(&mut self.chars) || self.chars.peek().map_or(true, |c| c.is_ascii_alphabetic())
@@ -833,69 +612,74 @@ impl<'a> PathParser<'a> {
// If a moveto is followed by multiple pairs of coordinates, the subsequent
// pairs are treated as implicit lineto commands.
- self.parse_lineto(absolute)
+ self.parse_lineto(by_to)
}
/// Parse "closepath" command.
fn parse_closepath(&mut self) -> Result<(), ()> {
- self.path.push(PathCommand::ClosePath);
+ self.path.push(PathCommand::Close);
Ok(())
}
/// Parse "lineto" command.
- fn parse_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- parse_arguments!(self, absolute, LineTo, [ point => parse_coord ])
+ fn parse_lineto(&mut self, by_to: ByTo) -> Result<(), ()> {
+ parse_arguments!(self, by_to, Line, [ point => parse_coord ])
}
/// Parse horizontal "lineto" command.
- fn parse_h_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- parse_arguments!(self, absolute, HorizontalLineTo, [ x => parse_number ])
+ fn parse_h_lineto(&mut self, by_to: ByTo) -> Result<(), ()> {
+ parse_arguments!(self, by_to, HLine, [ x => parse_number ])
}
/// Parse vertical "lineto" command.
- fn parse_v_lineto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- parse_arguments!(self, absolute, VerticalLineTo, [ y => parse_number ])
+ fn parse_v_lineto(&mut self, by_to: ByTo) -> Result<(), ()> {
+ parse_arguments!(self, by_to, VLine, [ y => parse_number ])
}
/// Parse cubic Bézier curve command.
- fn parse_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- parse_arguments!(self, absolute, CurveTo, [
+ fn parse_curveto(&mut self, by_to: ByTo) -> Result<(), ()> {
+ parse_arguments!(self, by_to, CubicCurve, [
control1 => parse_coord, control2 => parse_coord, point => parse_coord
])
}
/// Parse smooth "curveto" command.
- fn parse_smooth_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- parse_arguments!(self, absolute, SmoothCurveTo, [
+ fn parse_smooth_curveto(&mut self, by_to: ByTo) -> Result<(), ()> {
+ parse_arguments!(self, by_to, SmoothCubic, [
control2 => parse_coord, point => parse_coord
])
}
/// Parse quadratic Bézier curve command.
- fn parse_quadratic_bezier_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- parse_arguments!(self, absolute, QuadBezierCurveTo, [
+ fn parse_quadratic_bezier_curveto(&mut self, by_to: ByTo) -> Result<(), ()> {
+ parse_arguments!(self, by_to, QuadCurve, [
control1 => parse_coord, point => parse_coord
])
}
/// Parse smooth quadratic Bézier curveto command.
- fn parse_smooth_quadratic_bezier_curveto(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
- parse_arguments!(self, absolute, SmoothQuadBezierCurveTo, [ point => parse_coord ])
+ fn parse_smooth_quadratic_bezier_curveto(&mut self, by_to: ByTo) -> Result<(), ()> {
+ parse_arguments!(self, by_to, SmoothQuad, [ point => parse_coord ])
}
/// Parse elliptical arc curve command.
- fn parse_elliptical_arc(&mut self, absolute: IsAbsolute) -> Result<(), ()> {
+ fn parse_elliptical_arc(&mut self, by_to: ByTo) -> Result<(), ()> {
// Parse a flag whose value is '0' or '1'; otherwise, return Err(()).
- let parse_flag = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| match iter.next() {
- Some(c) if c == b'0' || c == b'1' => Ok(ArcFlag(c == b'1')),
+ let parse_arc_size = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| match iter.next() {
+ Some(c) if c == b'1' => Ok(ArcSize::Large),
+ Some(c) if c == b'0' => Ok(ArcSize::Small),
+ _ => Err(()),
+ };
+ let parse_arc_sweep = |iter: &mut Peekable<Cloned<slice::Iter<u8>>>| match iter.next() {
+ Some(c) if c == b'1' => Ok(ArcSweep::Cw),
+ Some(c) if c == b'0' => Ok(ArcSweep::Ccw),
_ => Err(()),
};
- parse_arguments!(self, absolute, EllipticalArc, [
- rx => parse_number,
- ry => parse_number,
- angle => parse_number,
- large_arc_flag => parse_flag,
- sweep_flag => parse_flag,
+ parse_arguments!(self, by_to, Arc, [
+ radii => parse_coord,
+ rotate => parse_number,
+ arc_size => parse_arc_size,
+ arc_sweep => parse_arc_sweep,
point => parse_coord
])
}
diff --git a/servo/components/style/values/specified/text.rs b/servo/components/style/values/specified/text.rs
index 0e70bd26ac..e26a17ba27 100644
--- a/servo/components/style/values/specified/text.rs
+++ b/servo/components/style/values/specified/text.rs
@@ -454,7 +454,6 @@ pub enum TextAlignKeyword {
Left,
Right,
Center,
- #[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
Justify,
#[css(skip)]
#[cfg(feature = "gecko")]
@@ -466,11 +465,11 @@ pub enum TextAlignKeyword {
MozLeft,
#[cfg(feature = "gecko")]
MozRight,
- #[cfg(feature = "servo-layout-2013")]
+ #[cfg(feature = "servo")]
ServoCenter,
- #[cfg(feature = "servo-layout-2013")]
+ #[cfg(feature = "servo")]
ServoLeft,
- #[cfg(feature = "servo-layout-2013")]
+ #[cfg(feature = "servo")]
ServoRight,
}
diff --git a/servo/components/style_traits/owned_slice.rs b/servo/components/style_traits/owned_slice.rs
index 36ba3162e5..c0d4ceff8d 100644
--- a/servo/components/style_traits/owned_slice.rs
+++ b/servo/components/style_traits/owned_slice.rs
@@ -13,7 +13,7 @@ use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::ptr::NonNull;
use std::{fmt, iter, mem, slice};
-use to_shmem::{self, SharedMemoryBuilder, ToShmem};
+use to_shmem::{SharedMemoryBuilder, ToShmem};
/// A struct that basically replaces a `Box<[T]>`, but which cbindgen can
/// understand.
diff --git a/servo/components/style_traits/values.rs b/servo/components/style_traits/values.rs
index 58b50d6576..e2154f606a 100644
--- a/servo/components/style_traits/values.rs
+++ b/servo/components/style_traits/values.rs
@@ -243,8 +243,9 @@ where
Self { inner, separator }
}
+ /// Serialize the CSS Value with the specific serialization function.
#[inline]
- fn write_item<F>(&mut self, f: F) -> fmt::Result
+ pub fn write_item<F>(&mut self, f: F) -> fmt::Result
where
F: FnOnce(&mut CssWriter<'b, W>) -> fmt::Result,
{