diff options
author | Delan Azabani <dazabani@igalia.com> | 2024-02-27 23:39:06 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-27 15:39:06 +0000 |
commit | faf754dfa655f0b9a28f62bc47a78fbf78ebcaf4 (patch) | |
tree | 4725e1446680d036797b1fc258733ae6b2c9f354 /components/style/stylesheets | |
parent | b07505417e629bbb081be9683630f2d7a5f50544 (diff) | |
download | servo-faf754dfa655f0b9a28f62bc47a78fbf78ebcaf4.tar.gz servo-faf754dfa655f0b9a28f62bc47a78fbf78ebcaf4.zip |
Move Stylo to its own repo (#31350)
* Remove packages that were moved to external repo
* Add workspace dependencies pointing to 2023-06-14 branch
* Fix servo-tidy.toml errors
* Update commit to include #31346
* Update commit to include servo/stylo#2
* Move css-properties.json lookup to target/doc/stylo
* Remove dependency on vendored mako in favour of pypi dependency
This also removes etc/ci/generate_workflow.py, which has been unused
since at least 9e71bd6a7010d6e5723831696ae0ebe26b47682f.
* Add temporary code to debug Windows test failures
* Fix failures on Windows due to custom target dir
* Update commit to include servo/stylo#3
* Fix license in tests/unit/style/build.rs
* Document how to build with local Stylo in Cargo.toml
Diffstat (limited to 'components/style/stylesheets')
22 files changed, 0 insertions, 6632 deletions
diff --git a/components/style/stylesheets/container_rule.rs b/components/style/stylesheets/container_rule.rs deleted file mode 100644 index f9d488b9b49..00000000000 --- a/components/style/stylesheets/container_rule.rs +++ /dev/null @@ -1,632 +0,0 @@ -/* 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 [`@container`][container] rule. -//! -//! [container]: https://drafts.csswg.org/css-contain-3/#container-rule - -use crate::computed_value_flags::ComputedValueFlags; -use crate::dom::TElement; -use crate::logical_geometry::{LogicalSize, WritingMode}; -use crate::media_queries::Device; -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}; -use crate::shared_lock::{ - DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard, -}; -use crate::str::CssStringWriter; -use crate::stylesheets::CssRules; -use crate::values::computed::{CSSPixelLength, ContainerType, Context, Ratio}; -use crate::values::specified::ContainerName; -use app_units::Au; -use cssparser::{Parser, SourceLocation}; -use euclid::default::Size2D; -#[cfg(feature = "gecko")] -use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; -use servo_arc::Arc; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ParseError, ToCss}; - -/// A container rule. -#[derive(Debug, ToShmem)] -pub struct ContainerRule { - /// The container query and name. - pub condition: Arc<ContainerCondition>, - /// The nested rules inside the block. - pub rules: Arc<Locked<CssRules>>, - /// The source position where this rule was found. - pub source_location: SourceLocation, -} - -impl ContainerRule { - /// Returns the query condition. - pub fn query_condition(&self) -> &QueryCondition { - &self.condition.condition - } - - /// Returns the query name filter. - pub fn container_name(&self) -> &ContainerName { - &self.condition.name - } - - /// Measure heap usage. - #[cfg(feature = "gecko")] - pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { - // Measurement of other fields may be added later. - self.rules.unconditional_shallow_size_of(ops) + - self.rules.read_with(guard).size_of(guard, ops) - } -} - -impl DeepCloneWithLock for ContainerRule { - fn deep_clone_with_lock( - &self, - lock: &SharedRwLock, - guard: &SharedRwLockReadGuard, - params: &DeepCloneParams, - ) -> Self { - let rules = self.rules.read_with(guard); - Self { - condition: self.condition.clone(), - rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))), - source_location: self.source_location.clone(), - } - } -} - -impl ToCssWithGuard for ContainerRule { - fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { - dest.write_str("@container ")?; - { - let mut writer = CssWriter::new(dest); - if !self.condition.name.is_none() { - self.condition.name.to_css(&mut writer)?; - writer.write_char(' ')?; - } - self.condition.condition.to_css(&mut writer)?; - } - self.rules.read_with(guard).to_css_block(guard, dest) - } -} - -/// A container condition and filter, combined. -#[derive(Debug, ToShmem, ToCss)] -pub struct ContainerCondition { - #[css(skip_if = "ContainerName::is_none")] - name: ContainerName, - condition: QueryCondition, - #[css(skip)] - flags: FeatureFlags, -} - -/// The result of a successful container query lookup. -pub struct ContainerLookupResult<E> { - /// The relevant container. - pub element: E, - /// The sizing / writing-mode information of the container. - pub info: ContainerInfo, - /// The style of the element. - pub style: Arc<ComputedValues>, -} - -fn container_type_axes(ty_: ContainerType, wm: WritingMode) -> FeatureFlags { - match ty_ { - ContainerType::Size => FeatureFlags::all_container_axes(), - ContainerType::InlineSize => { - let physical_axis = if wm.is_vertical() { - FeatureFlags::CONTAINER_REQUIRES_HEIGHT_AXIS - } else { - FeatureFlags::CONTAINER_REQUIRES_WIDTH_AXIS - }; - FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS | physical_axis - }, - ContainerType::Normal => FeatureFlags::empty(), - } -} - -enum TraversalResult<T> { - InProgress, - StopTraversal, - Done(T), -} - -fn traverse_container<E, F, R>( - mut e: E, - originating_element_style: Option<&ComputedValues>, - evaluator: F, -) -> Option<(E, R)> -where - E: TElement, - F: Fn(E, Option<&ComputedValues>) -> TraversalResult<R>, -{ - if originating_element_style.is_some() { - match evaluator(e, originating_element_style) { - TraversalResult::InProgress => {}, - TraversalResult::StopTraversal => return None, - TraversalResult::Done(result) => return Some((e, result)), - } - } - while let Some(element) = e.traversal_parent() { - match evaluator(element, None) { - TraversalResult::InProgress => {}, - TraversalResult::StopTraversal => return None, - TraversalResult::Done(result) => return Some((element, result)), - } - e = element; - } - - None -} - -impl ContainerCondition { - /// Parse a container condition. - pub fn parse<'a>( - context: &ParserContext, - input: &mut Parser<'a, '_>, - ) -> Result<Self, ParseError<'a>> { - let name = input - .try_parse(|input| ContainerName::parse_for_query(context, input)) - .ok() - .unwrap_or_else(ContainerName::none); - let condition = QueryCondition::parse(context, input, FeatureType::Container)?; - let flags = condition.cumulative_flags(); - Ok(Self { - name, - condition, - flags, - }) - } - - fn valid_container_info<E>( - &self, - potential_container: E, - originating_element_style: Option<&ComputedValues>, - ) -> TraversalResult<ContainerLookupResult<E>> - where - E: TElement, - { - let data; - let style = match originating_element_style { - Some(s) => s, - None => { - data = match potential_container.borrow_data() { - Some(d) => d, - None => return TraversalResult::InProgress, - }; - &**data.styles.primary() - }, - }; - let wm = style.writing_mode; - let box_style = style.get_box(); - - // Filter by container-type. - let container_type = box_style.clone_container_type(); - let available_axes = container_type_axes(container_type, wm); - if !available_axes.contains(self.flags.container_axes()) { - return TraversalResult::InProgress; - } - - // Filter by container-name. - let container_name = box_style.clone_container_name(); - for filter_name in self.name.0.iter() { - if !container_name.0.contains(filter_name) { - return TraversalResult::InProgress; - } - } - - let size = potential_container.query_container_size(&box_style.clone_display()); - let style = style.to_arc(); - TraversalResult::Done(ContainerLookupResult { - element: potential_container, - info: ContainerInfo { size, wm }, - style, - }) - } - - /// Performs container lookup for a given element. - pub fn find_container<E>( - &self, - e: E, - originating_element_style: Option<&ComputedValues>, - ) -> Option<ContainerLookupResult<E>> - where - E: TElement, - { - match traverse_container( - e, - originating_element_style, - |element, originating_element_style| { - self.valid_container_info(element, originating_element_style) - }, - ) { - Some((_, result)) => Some(result), - None => None, - } - } - - /// Tries to match a container query condition for a given element. - pub(crate) fn matches<E>( - &self, - device: &Device, - element: E, - originating_element_style: Option<&ComputedValues>, - invalidation_flags: &mut ComputedValueFlags, - ) -> KleeneValue - where - E: TElement, - { - let result = self.find_container(element, originating_element_style); - let (container, info) = match result { - Some(r) => (Some(r.element), Some((r.info, r.style))), - None => (None, None), - }; - // Set up the lookup for the container in question, as the condition may be using container query lengths. - let size_query_container_lookup = ContainerSizeQuery::for_option_element(container, None); - Context::for_container_query_evaluation( - device, - info, - size_query_container_lookup, - |context| { - let matches = self.condition.matches(context); - if context - .style() - .flags() - .contains(ComputedValueFlags::USES_VIEWPORT_UNITS) - { - // TODO(emilio): Might need something similar to improve - // invalidation of font relative container-query lengths. - invalidation_flags - .insert(ComputedValueFlags::USES_VIEWPORT_UNITS_ON_CONTAINER_QUERIES); - } - matches - }, - ) - } -} - -/// Information needed to evaluate an individual container query. -#[derive(Copy, Clone)] -pub struct ContainerInfo { - size: Size2D<Option<Au>>, - wm: WritingMode, -} - -impl ContainerInfo { - fn size(&self) -> Option<Size2D<Au>> { - Some(Size2D::new(self.size.width?, self.size.height?)) - } -} - -fn eval_width(context: &Context) -> Option<CSSPixelLength> { - let info = context.container_info.as_ref()?; - Some(CSSPixelLength::new(info.size.width?.to_f32_px())) -} - -fn eval_height(context: &Context) -> Option<CSSPixelLength> { - let info = context.container_info.as_ref()?; - Some(CSSPixelLength::new(info.size.height?.to_f32_px())) -} - -fn eval_inline_size(context: &Context) -> Option<CSSPixelLength> { - let info = context.container_info.as_ref()?; - Some(CSSPixelLength::new( - LogicalSize::from_physical(info.wm, info.size) - .inline? - .to_f32_px(), - )) -} - -fn eval_block_size(context: &Context) -> Option<CSSPixelLength> { - let info = context.container_info.as_ref()?; - Some(CSSPixelLength::new( - LogicalSize::from_physical(info.wm, info.size) - .block? - .to_f32_px(), - )) -} - -fn eval_aspect_ratio(context: &Context) -> Option<Ratio> { - let info = context.container_info.as_ref()?; - Some(Ratio::new( - info.size.width?.0 as f32, - info.size.height?.0 as f32, - )) -} - -fn eval_orientation(context: &Context, value: Option<Orientation>) -> KleeneValue { - let size = match context.container_info.as_ref().and_then(|info| info.size()) { - Some(size) => size, - None => return KleeneValue::Unknown, - }; - KleeneValue::from(Orientation::eval(size, value)) -} - -/// https://drafts.csswg.org/css-contain-3/#container-features -/// -/// TODO: Support style queries, perhaps. -pub static CONTAINER_FEATURES: [QueryFeatureDescription; 6] = [ - feature!( - atom!("width"), - AllowsRanges::Yes, - Evaluator::OptionalLength(eval_width), - FeatureFlags::CONTAINER_REQUIRES_WIDTH_AXIS, - ), - feature!( - atom!("height"), - AllowsRanges::Yes, - Evaluator::OptionalLength(eval_height), - FeatureFlags::CONTAINER_REQUIRES_HEIGHT_AXIS, - ), - feature!( - atom!("inline-size"), - AllowsRanges::Yes, - Evaluator::OptionalLength(eval_inline_size), - FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS, - ), - feature!( - atom!("block-size"), - AllowsRanges::Yes, - Evaluator::OptionalLength(eval_block_size), - FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS, - ), - feature!( - atom!("aspect-ratio"), - AllowsRanges::Yes, - Evaluator::OptionalNumberRatio(eval_aspect_ratio), - // XXX from_bits_truncate is const, but the pipe operator isn't, so this - // works around it. - FeatureFlags::from_bits_truncate( - FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS.bits() | - FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS.bits() - ), - ), - feature!( - atom!("orientation"), - AllowsRanges::No, - keyword_evaluator!(eval_orientation, Orientation), - FeatureFlags::from_bits_truncate( - FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS.bits() | - FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS.bits() - ), - ), -]; - -/// Result of a container size query, signifying the hypothetical containment boundary in terms of physical axes. -/// Defined by up to two size containers. Queries on logical axes are resolved with respect to the querying -/// element's writing mode. -#[derive(Copy, Clone, Default)] -pub struct ContainerSizeQueryResult { - width: Option<Au>, - height: Option<Au>, -} - -impl ContainerSizeQueryResult { - fn get_viewport_size(context: &Context) -> Size2D<Au> { - use crate::values::specified::ViewportVariant; - context.viewport_size_for_viewport_unit_resolution(ViewportVariant::Small) - } - - fn get_logical_viewport_size(context: &Context) -> LogicalSize<Au> { - LogicalSize::from_physical( - context.builder.writing_mode, - Self::get_viewport_size(context), - ) - } - - /// Get the inline-size of the query container. - pub fn get_container_inline_size(&self, context: &Context) -> Au { - if context.builder.writing_mode.is_horizontal() { - if let Some(w) = self.width { - return w; - } - } else { - if let Some(h) = self.height { - return h; - } - } - Self::get_logical_viewport_size(context).inline - } - - /// Get the block-size of the query container. - pub fn get_container_block_size(&self, context: &Context) -> Au { - if context.builder.writing_mode.is_horizontal() { - self.get_container_height(context) - } else { - self.get_container_width(context) - } - } - - /// Get the width of the query container. - pub fn get_container_width(&self, context: &Context) -> Au { - if let Some(w) = self.width { - return w; - } - Self::get_viewport_size(context).width - } - - /// Get the height of the query container. - pub fn get_container_height(&self, context: &Context) -> Au { - if let Some(h) = self.height { - return h; - } - Self::get_viewport_size(context).height - } - - // Merge the result of a subsequent lookup, preferring the initial result. - fn merge(self, new_result: Self) -> Self { - let mut result = self; - if let Some(width) = new_result.width { - result.width.get_or_insert(width); - } - if let Some(height) = new_result.height { - result.height.get_or_insert(height); - } - result - } - - fn is_complete(&self) -> bool { - self.width.is_some() && self.height.is_some() - } -} - -/// Unevaluated lazy container size query. -pub enum ContainerSizeQuery<'a> { - /// Query prior to evaluation. - NotEvaluated(Box<dyn Fn() -> ContainerSizeQueryResult + 'a>), - /// Cached evaluated result. - Evaluated(ContainerSizeQueryResult), -} - -impl<'a> ContainerSizeQuery<'a> { - fn evaluate_potential_size_container<E>( - e: E, - originating_element_style: Option<&ComputedValues>, - ) -> TraversalResult<ContainerSizeQueryResult> - where - E: TElement, - { - let data; - let style = match originating_element_style { - Some(s) => s, - None => { - data = match e.borrow_data() { - Some(d) => d, - None => return TraversalResult::InProgress, - }; - &**data.styles.primary() - }, - }; - if !style - .flags - .contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE) - { - // We know we won't find a size container. - return TraversalResult::StopTraversal; - } - - let wm = style.writing_mode; - let box_style = style.get_box(); - - let container_type = box_style.clone_container_type(); - let size = e.query_container_size(&box_style.clone_display()); - match container_type { - ContainerType::Size => TraversalResult::Done(ContainerSizeQueryResult { - width: size.width, - height: size.height, - }), - ContainerType::InlineSize => { - if wm.is_horizontal() { - TraversalResult::Done(ContainerSizeQueryResult { - width: size.width, - height: None, - }) - } else { - TraversalResult::Done(ContainerSizeQueryResult { - width: None, - height: size.height, - }) - } - }, - ContainerType::Normal => TraversalResult::InProgress, - } - } - - /// Find the query container size for a given element. Meant to be used as a callback for new(). - fn lookup<E>( - element: E, - originating_element_style: Option<&ComputedValues>, - ) -> ContainerSizeQueryResult - where - E: TElement + 'a, - { - match traverse_container( - element, - originating_element_style, - |e, originating_element_style| { - Self::evaluate_potential_size_container(e, originating_element_style) - }, - ) { - Some((container, result)) => { - if result.is_complete() { - result - } else { - // Traverse up from the found size container to see if we can get a complete containment. - result.merge(Self::lookup(container, None)) - } - }, - None => ContainerSizeQueryResult::default(), - } - } - - /// Create a new instance of the container size query for given element, with a deferred lookup callback. - pub fn for_element<E>(element: E, originating_element_style: Option<&'a ComputedValues>) -> Self - where - E: TElement + 'a, - { - let parent; - let data; - let style = match originating_element_style { - Some(s) => Some(s), - None => { - // No need to bother if we're the top element. - parent = match element.traversal_parent() { - Some(parent) => parent, - None => return Self::none(), - }; - data = parent.borrow_data(); - data.as_ref().map(|data| &**data.styles.primary()) - }, - }; - let should_traverse = match style { - Some(style) => style - .flags - .contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_SIZE_CONTAINER_TYPE), - None => true, // `display: none`, still want to show a correct computed value, so give it a try. - }; - if should_traverse { - return Self::NotEvaluated(Box::new(move || { - Self::lookup(element, originating_element_style) - })); - } - Self::none() - } - - /// Create a new instance, but with optional element. - pub fn for_option_element<E>( - element: Option<E>, - originating_element_style: Option<&'a ComputedValues>, - ) -> Self - where - E: TElement + 'a, - { - if let Some(e) = element { - Self::for_element(e, originating_element_style) - } else { - Self::none() - } - } - - /// Create a query that evaluates to empty, for cases where container size query is not required. - pub fn none() -> Self { - ContainerSizeQuery::Evaluated(ContainerSizeQueryResult::default()) - } - - /// Get the result of the container size query, doing the lookup if called for the first time. - pub fn get(&mut self) -> ContainerSizeQueryResult { - match self { - Self::NotEvaluated(lookup) => { - *self = Self::Evaluated((lookup)()); - match self { - Self::Evaluated(info) => *info, - _ => unreachable!("Just evaluated but not set?"), - } - }, - Self::Evaluated(info) => *info, - } - } -} diff --git a/components/style/stylesheets/counter_style_rule.rs b/components/style/stylesheets/counter_style_rule.rs deleted file mode 100644 index 974b76b8060..00000000000 --- a/components/style/stylesheets/counter_style_rule.rs +++ /dev/null @@ -1,7 +0,0 @@ -/* 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/. */ - -#![allow(missing_docs)] - -pub use crate::counter_style::CounterStyleRuleData as CounterStyleRule; diff --git a/components/style/stylesheets/document_rule.rs b/components/style/stylesheets/document_rule.rs deleted file mode 100644 index 75edab308db..00000000000 --- a/components/style/stylesheets/document_rule.rs +++ /dev/null @@ -1,305 +0,0 @@ -/* 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/. */ - -//! [@document rules](https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document) -//! initially in CSS Conditional Rules Module Level 3, @document has been postponed to the level 4. -//! We implement the prefixed `@-moz-document`. - -use crate::media_queries::Device; -use crate::parser::{Parse, ParserContext}; -use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked}; -use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; -use crate::str::CssStringWriter; -use crate::stylesheets::CssRules; -use crate::values::CssUrl; -use cssparser::{BasicParseErrorKind, Parser, SourceLocation}; -#[cfg(feature = "gecko")] -use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; -use servo_arc::Arc; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; - -#[derive(Debug, ToShmem)] -/// A @-moz-document rule -pub struct DocumentRule { - /// The parsed condition - pub condition: DocumentCondition, - /// Child rules - pub rules: Arc<Locked<CssRules>>, - /// The line and column of the rule's source code. - pub source_location: SourceLocation, -} - -impl DocumentRule { - /// Measure heap usage. - #[cfg(feature = "gecko")] - pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { - // Measurement of other fields may be added later. - self.rules.unconditional_shallow_size_of(ops) + - self.rules.read_with(guard).size_of(guard, ops) - } -} - -impl ToCssWithGuard for DocumentRule { - fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { - dest.write_str("@-moz-document ")?; - self.condition.to_css(&mut CssWriter::new(dest))?; - dest.write_str(" {")?; - for rule in self.rules.read_with(guard).0.iter() { - dest.write_char(' ')?; - rule.to_css(guard, dest)?; - } - dest.write_str(" }") - } -} - -impl DeepCloneWithLock for DocumentRule { - /// Deep clones this DocumentRule. - fn deep_clone_with_lock( - &self, - lock: &SharedRwLock, - guard: &SharedRwLockReadGuard, - params: &DeepCloneParams, - ) -> Self { - let rules = self.rules.read_with(guard); - DocumentRule { - condition: self.condition.clone(), - rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))), - source_location: self.source_location.clone(), - } - } -} - -/// The kind of media document that the rule will match. -#[derive(Clone, Copy, Debug, Parse, PartialEq, ToCss, ToShmem)] -#[allow(missing_docs)] -pub enum MediaDocumentKind { - All, - Plugin, - Image, - Video, -} - -/// A matching function for a `@document` rule's condition. -#[derive(Clone, Debug, ToCss, ToShmem)] -pub enum DocumentMatchingFunction { - /// Exact URL matching function. It evaluates to true whenever the - /// URL of the document being styled is exactly the URL given. - Url(CssUrl), - /// URL prefix matching function. It evaluates to true whenever the - /// URL of the document being styled has the argument to the - /// function as an initial substring (which is true when the two - /// strings are equal). When the argument is the empty string, - /// it evaluates to true for all documents. - #[css(function)] - UrlPrefix(String), - /// Domain matching function. It evaluates to true whenever the URL - /// of the document being styled has a host subcomponent and that - /// host subcomponent is exactly the argument to the ‘domain()’ - /// function or a final substring of the host component is a - /// period (U+002E) immediately followed by the argument to the - /// ‘domain()’ function. - #[css(function)] - Domain(String), - /// Regular expression matching function. It evaluates to true - /// whenever the regular expression matches the entirety of the URL - /// of the document being styled. - #[css(function)] - Regexp(String), - /// Matching function for a media document. - #[css(function)] - MediaDocument(MediaDocumentKind), - /// Matching function for a plain-text document. - #[css(function)] - PlainTextDocument(()), - /// Matching function for a document that can be observed by other content - /// documents. - #[css(function)] - UnobservableDocument(()), -} - -macro_rules! parse_quoted_or_unquoted_string { - ($input:ident, $url_matching_function:expr) => { - $input.parse_nested_block(|input| { - let start = input.position(); - input - .parse_entirely(|input| { - let string = input.expect_string()?; - Ok($url_matching_function(string.as_ref().to_owned())) - }) - .or_else(|_: ParseError| { - while let Ok(_) = input.next() {} - Ok($url_matching_function(input.slice_from(start).to_string())) - }) - }) - }; -} - -impl DocumentMatchingFunction { - /// Parse a URL matching function for a`@document` rule's condition. - pub fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result<Self, ParseError<'i>> { - if let Ok(url) = input.try_parse(|input| CssUrl::parse(context, input)) { - return Ok(DocumentMatchingFunction::Url(url)); - } - - let location = input.current_source_location(); - let function = input.expect_function()?.clone(); - match_ignore_ascii_case! { &function, - "url-prefix" => { - parse_quoted_or_unquoted_string!(input, DocumentMatchingFunction::UrlPrefix) - }, - "domain" => { - parse_quoted_or_unquoted_string!(input, DocumentMatchingFunction::Domain) - }, - "regexp" => { - input.parse_nested_block(|input| { - Ok(DocumentMatchingFunction::Regexp( - input.expect_string()?.as_ref().to_owned(), - )) - }) - }, - "media-document" => { - input.parse_nested_block(|input| { - let kind = MediaDocumentKind::parse(input)?; - Ok(DocumentMatchingFunction::MediaDocument(kind)) - }) - }, - - "plain-text-document" => { - input.parse_nested_block(|input| { - input.expect_exhausted()?; - Ok(DocumentMatchingFunction::PlainTextDocument(())) - }) - }, - - "unobservable-document" => { - input.parse_nested_block(|input| { - input.expect_exhausted()?; - Ok(DocumentMatchingFunction::UnobservableDocument(())) - }) - }, - - _ => { - Err(location.new_custom_error( - StyleParseErrorKind::UnexpectedFunction(function.clone()) - )) - }, - } - } - - #[cfg(feature = "gecko")] - /// Evaluate a URL matching function. - pub fn evaluate(&self, device: &Device) -> bool { - use crate::gecko_bindings::bindings::Gecko_DocumentRule_UseForPresentation; - use crate::gecko_bindings::structs::DocumentMatchingFunction as GeckoDocumentMatchingFunction; - use nsstring::nsCStr; - - let func = match *self { - DocumentMatchingFunction::Url(_) => GeckoDocumentMatchingFunction::URL, - DocumentMatchingFunction::UrlPrefix(_) => GeckoDocumentMatchingFunction::URLPrefix, - DocumentMatchingFunction::Domain(_) => GeckoDocumentMatchingFunction::Domain, - DocumentMatchingFunction::Regexp(_) => GeckoDocumentMatchingFunction::RegExp, - DocumentMatchingFunction::MediaDocument(_) => { - GeckoDocumentMatchingFunction::MediaDocument - }, - DocumentMatchingFunction::PlainTextDocument(..) => { - GeckoDocumentMatchingFunction::PlainTextDocument - }, - DocumentMatchingFunction::UnobservableDocument(..) => { - GeckoDocumentMatchingFunction::UnobservableDocument - }, - }; - - let pattern = nsCStr::from(match *self { - DocumentMatchingFunction::Url(ref url) => url.as_str(), - DocumentMatchingFunction::UrlPrefix(ref pat) | - DocumentMatchingFunction::Domain(ref pat) | - DocumentMatchingFunction::Regexp(ref pat) => pat, - DocumentMatchingFunction::MediaDocument(kind) => match kind { - MediaDocumentKind::All => "all", - MediaDocumentKind::Image => "image", - MediaDocumentKind::Plugin => "plugin", - MediaDocumentKind::Video => "video", - }, - DocumentMatchingFunction::PlainTextDocument(()) | - DocumentMatchingFunction::UnobservableDocument(()) => "", - }); - unsafe { Gecko_DocumentRule_UseForPresentation(device.document(), &*pattern, func) } - } - - #[cfg(not(feature = "gecko"))] - /// Evaluate a URL matching function. - pub fn evaluate(&self, _: &Device) -> bool { - false - } -} - -/// A `@document` rule's condition. -/// -/// <https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document> -/// -/// The `@document` rule's condition is written as a comma-separated list of -/// URL matching functions, and the condition evaluates to true whenever any -/// one of those functions evaluates to true. -#[derive(Clone, Debug, ToCss, ToShmem)] -#[css(comma)] -pub struct DocumentCondition(#[css(iterable)] Vec<DocumentMatchingFunction>); - -impl DocumentCondition { - /// Parse a document condition. - pub fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result<Self, ParseError<'i>> { - let conditions = - input.parse_comma_separated(|input| DocumentMatchingFunction::parse(context, input))?; - - let condition = DocumentCondition(conditions); - if !condition.allowed_in(context) { - return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid("-moz-document".into()))); - } - Ok(condition) - } - - /// Evaluate a document condition. - pub fn evaluate(&self, device: &Device) -> bool { - self.0 - .iter() - .any(|url_matching_function| url_matching_function.evaluate(device)) - } - - #[cfg(feature = "servo")] - fn allowed_in(&self, _: &ParserContext) -> bool { - false - } - - #[cfg(feature = "gecko")] - fn allowed_in(&self, context: &ParserContext) -> bool { - use static_prefs::pref; - - if context.in_ua_or_chrome_sheet() { - return true; - } - - if pref!("layout.css.moz-document.content.enabled") { - return true; - } - - // Allow a single url-prefix() for compatibility. - // - // See bug 1446470 and dependencies. - if self.0.len() != 1 { - return false; - } - - // NOTE(emilio): This technically allows url-prefix("") too, but... - match self.0[0] { - DocumentMatchingFunction::UrlPrefix(ref prefix) => prefix.is_empty(), - _ => false, - } - } -} diff --git a/components/style/stylesheets/font_face_rule.rs b/components/style/stylesheets/font_face_rule.rs deleted file mode 100644 index 78f3b338b22..00000000000 --- a/components/style/stylesheets/font_face_rule.rs +++ /dev/null @@ -1,7 +0,0 @@ -/* 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/. */ - -#![allow(missing_docs)] - -pub use crate::font_face::FontFaceRuleData as FontFaceRule; diff --git a/components/style/stylesheets/font_feature_values_rule.rs b/components/style/stylesheets/font_feature_values_rule.rs deleted file mode 100644 index 06016ec2bd9..00000000000 --- a/components/style/stylesheets/font_feature_values_rule.rs +++ /dev/null @@ -1,490 +0,0 @@ -/* 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/. */ - -//! The [`@font-feature-values`][font-feature-values] at-rule. -//! -//! [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule - -use crate::error_reporting::ContextualParseError; -#[cfg(feature = "gecko")] -use crate::gecko_bindings::bindings::Gecko_AppendFeatureValueHashEntry; -#[cfg(feature = "gecko")] -use crate::gecko_bindings::structs::{self, gfxFontFeatureValueSet, nsTArray}; -use crate::parser::{Parse, ParserContext}; -use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; -use crate::str::CssStringWriter; -use crate::stylesheets::CssRuleType; -use crate::values::computed::font::FamilyName; -use crate::values::serialize_atom_identifier; -use crate::Atom; -use cssparser::{ - AtRuleParser, BasicParseErrorKind, CowRcStr, DeclarationParser, Parser, ParserState, - QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, Token, -}; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; - -/// A @font-feature-values block declaration. -/// It is `<ident>: <integer>+`. -/// This struct can take 3 value types. -/// - `SingleValue` is to keep just one unsigned integer value. -/// - `PairValues` is to keep one or two unsigned integer values. -/// - `VectorValues` is to keep a list of unsigned integer values. -#[derive(Clone, Debug, PartialEq, ToShmem)] -pub struct FFVDeclaration<T> { - /// An `<ident>` for declaration name. - pub name: Atom, - /// An `<integer>+` for declaration value. - pub value: T, -} - -impl<T: ToCss> ToCss for FFVDeclaration<T> { - fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result - where - W: Write, - { - serialize_atom_identifier(&self.name, dest)?; - dest.write_str(": ")?; - self.value.to_css(dest)?; - dest.write_char(';') - } -} - -/// A trait for @font-feature-values rule to gecko values conversion. -#[cfg(feature = "gecko")] -pub trait ToGeckoFontFeatureValues { - /// Sets the equivalent of declaration to gecko `nsTArray<u32>` array. - fn to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>); -} - -/// A @font-feature-values block declaration value that keeps one value. -#[derive(Clone, Debug, PartialEq, ToCss, ToShmem)] -pub struct SingleValue(pub u32); - -impl Parse for SingleValue { - fn parse<'i, 't>( - _context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result<SingleValue, ParseError<'i>> { - let location = input.current_source_location(); - match *input.next()? { - Token::Number { - int_value: Some(v), .. - } if v >= 0 => Ok(SingleValue(v as u32)), - ref t => Err(location.new_unexpected_token_error(t.clone())), - } - } -} - -#[cfg(feature = "gecko")] -impl ToGeckoFontFeatureValues for SingleValue { - fn to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>) { - unsafe { - array.set_len_pod(1); - } - array[0] = self.0 as u32; - } -} - -/// A @font-feature-values block declaration value that keeps one or two values. -#[derive(Clone, Debug, PartialEq, ToCss, ToShmem)] -pub struct PairValues(pub u32, pub Option<u32>); - -impl Parse for PairValues { - fn parse<'i, 't>( - _context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result<PairValues, ParseError<'i>> { - let location = input.current_source_location(); - let first = match *input.next()? { - Token::Number { - int_value: Some(a), .. - } if a >= 0 => a as u32, - ref t => return Err(location.new_unexpected_token_error(t.clone())), - }; - let location = input.current_source_location(); - match input.next() { - Ok(&Token::Number { - int_value: Some(b), .. - }) if b >= 0 => Ok(PairValues(first, Some(b as u32))), - // It can't be anything other than number. - Ok(t) => Err(location.new_unexpected_token_error(t.clone())), - // It can be just one value. - Err(_) => Ok(PairValues(first, None)), - } - } -} - -#[cfg(feature = "gecko")] -impl ToGeckoFontFeatureValues for PairValues { - fn to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>) { - let len = if self.1.is_some() { 2 } else { 1 }; - - unsafe { - array.set_len_pod(len); - } - array[0] = self.0 as u32; - if let Some(second) = self.1 { - array[1] = second as u32; - }; - } -} - -/// A @font-feature-values block declaration value that keeps a list of values. -#[derive(Clone, Debug, PartialEq, ToCss, ToShmem)] -pub struct VectorValues(#[css(iterable)] pub Vec<u32>); - -impl Parse for VectorValues { - fn parse<'i, 't>( - _context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result<VectorValues, ParseError<'i>> { - let mut vec = vec![]; - loop { - let location = input.current_source_location(); - match input.next() { - Ok(&Token::Number { - int_value: Some(a), .. - }) if a >= 0 => { - vec.push(a as u32); - }, - // It can't be anything other than number. - Ok(t) => return Err(location.new_unexpected_token_error(t.clone())), - Err(_) => break, - } - } - - if vec.len() == 0 { - return Err(input.new_error(BasicParseErrorKind::EndOfInput)); - } - - Ok(VectorValues(vec)) - } -} - -#[cfg(feature = "gecko")] -impl ToGeckoFontFeatureValues for VectorValues { - fn to_gecko_font_feature_values(&self, array: &mut nsTArray<u32>) { - array.assign_from_iter_pod(self.0.iter().map(|v| *v)); - } -} - -/// Parses a list of `FamilyName`s. -pub fn parse_family_name_list<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, -) -> Result<Vec<FamilyName>, ParseError<'i>> { - input - .parse_comma_separated(|i| FamilyName::parse(context, i)) - .map_err(|e| e.into()) -} - -/// @font-feature-values inside block parser. Parses a list of `FFVDeclaration`. -/// (`<ident>: <integer>+`) -struct FFVDeclarationsParser<'a, 'b: 'a, T: 'a> { - context: &'a ParserContext<'b>, - declarations: &'a mut Vec<FFVDeclaration<T>>, -} - -/// Default methods reject all at rules. -impl<'a, 'b, 'i, T> AtRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> { - type Prelude = (); - type AtRule = (); - type Error = StyleParseErrorKind<'i>; -} - -impl<'a, 'b, 'i, T> QualifiedRuleParser<'i> for FFVDeclarationsParser<'a, 'b, T> { - type Prelude = (); - type QualifiedRule = (); - type Error = StyleParseErrorKind<'i>; -} - -impl<'a, 'b, 'i, T> DeclarationParser<'i> for FFVDeclarationsParser<'a, 'b, T> -where - T: Parse, -{ - type Declaration = (); - type Error = StyleParseErrorKind<'i>; - - fn parse_value<'t>( - &mut self, - name: CowRcStr<'i>, - input: &mut Parser<'i, 't>, - ) -> Result<(), ParseError<'i>> { - let value = input.parse_entirely(|i| T::parse(self.context, i))?; - let new = FFVDeclaration { - name: Atom::from(&*name), - value, - }; - update_or_push(&mut self.declarations, new); - Ok(()) - } -} - -impl<'a, 'b, 'i, T> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> - for FFVDeclarationsParser<'a, 'b, T> -where - T: Parse, -{ - fn parse_declarations(&self) -> bool { - true - } - fn parse_qualified(&self) -> bool { - false - } -} - -macro_rules! font_feature_values_blocks { - ( - blocks = [ - $( #[$doc: meta] $name: tt $ident: ident / $ident_camel: ident / $gecko_enum: ident: $ty: ty, )* - ] - ) => { - /// The [`@font-feature-values`][font-feature-values] at-rule. - /// - /// [font-feature-values]: https://drafts.csswg.org/css-fonts-3/#at-font-feature-values-rule - #[derive(Clone, Debug, PartialEq, ToShmem)] - pub struct FontFeatureValuesRule { - /// Font family list for @font-feature-values rule. - /// Family names cannot contain generic families. FamilyName - /// also accepts only non-generic names. - pub family_names: Vec<FamilyName>, - $( - #[$doc] - pub $ident: Vec<FFVDeclaration<$ty>>, - )* - /// The line and column of the rule's source code. - pub source_location: SourceLocation, - } - - impl FontFeatureValuesRule { - /// Creates an empty FontFeatureValuesRule with given location and family name list. - fn new(family_names: Vec<FamilyName>, location: SourceLocation) -> Self { - FontFeatureValuesRule { - family_names: family_names, - $( - $ident: vec![], - )* - source_location: location, - } - } - - /// Parses a `FontFeatureValuesRule`. - pub fn parse( - context: &ParserContext, - input: &mut Parser, - family_names: Vec<FamilyName>, - location: SourceLocation, - ) -> Self { - let mut rule = FontFeatureValuesRule::new(family_names, location); - let mut parser = FontFeatureValuesRuleParser { - context, - rule: &mut rule, - }; - let mut iter = RuleBodyParser::new(input, &mut parser); - while let Some(result) = iter.next() { - if let Err((error, slice)) = result { - let location = error.location; - let error = ContextualParseError::UnsupportedRule(slice, error); - context.log_css_error(location, error); - } - } - rule - } - - /// Prints inside of `@font-feature-values` block. - pub fn value_to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result - where - W: Write, - { - $( - if self.$ident.len() > 0 { - dest.write_str(concat!("@", $name, " {\n"))?; - let iter = self.$ident.iter(); - for val in iter { - val.to_css(dest)?; - dest.write_str("\n")? - } - dest.write_str("}\n")? - } - )* - Ok(()) - } - - /// Returns length of all at-rules. - pub fn len(&self) -> usize { - let mut len = 0; - $( - len += self.$ident.len(); - )* - len - } - - /// Convert to Gecko gfxFontFeatureValueSet. - #[cfg(feature = "gecko")] - pub fn set_at_rules(&self, dest: *mut gfxFontFeatureValueSet) { - for ref family in self.family_names.iter() { - let family = family.name.to_ascii_lowercase(); - $( - if self.$ident.len() > 0 { - for val in self.$ident.iter() { - let array = unsafe { - Gecko_AppendFeatureValueHashEntry( - dest, - family.as_ptr(), - structs::$gecko_enum, - val.name.as_ptr() - ) - }; - unsafe { - val.value.to_gecko_font_feature_values(&mut *array); - } - } - } - )* - } - } - } - - impl ToCssWithGuard for FontFeatureValuesRule { - fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { - dest.write_str("@font-feature-values ")?; - self.family_names.to_css(&mut CssWriter::new(dest))?; - dest.write_str(" {\n")?; - self.value_to_css(&mut CssWriter::new(dest))?; - dest.write_char('}') - } - } - - /// Updates with new value if same `ident` exists, otherwise pushes to the vector. - fn update_or_push<T>(vec: &mut Vec<FFVDeclaration<T>>, element: FFVDeclaration<T>) { - if let Some(item) = vec.iter_mut().find(|item| item.name == element.name) { - item.value = element.value; - } else { - vec.push(element); - } - } - - /// Keeps the information about block type like @swash, @styleset etc. - enum BlockType { - $( - $ident_camel, - )* - } - - /// Parser for `FontFeatureValuesRule`. Parses all blocks - /// <feature-type> { - /// <feature-value-declaration-list> - /// } - /// <feature-type> = @stylistic | @historical-forms | @styleset | - /// @character-variant | @swash | @ornaments | @annotation - struct FontFeatureValuesRuleParser<'a> { - context: &'a ParserContext<'a>, - rule: &'a mut FontFeatureValuesRule, - } - - /// Default methods reject all qualified rules. - impl<'a, 'i> QualifiedRuleParser<'i> for FontFeatureValuesRuleParser<'a> { - type Prelude = (); - type QualifiedRule = (); - type Error = StyleParseErrorKind<'i>; - } - - impl<'a, 'i> AtRuleParser<'i> for FontFeatureValuesRuleParser<'a> { - type Prelude = BlockType; - type AtRule = (); - type Error = StyleParseErrorKind<'i>; - - fn parse_prelude<'t>( - &mut self, - name: CowRcStr<'i>, - input: &mut Parser<'i, 't>, - ) -> Result<BlockType, ParseError<'i>> { - match_ignore_ascii_case! { &*name, - $( - $name => Ok(BlockType::$ident_camel), - )* - _ => Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)), - } - } - - fn parse_block<'t>( - &mut self, - prelude: BlockType, - _: &ParserState, - input: &mut Parser<'i, 't> - ) -> Result<Self::AtRule, ParseError<'i>> { - debug_assert!(self.context.rule_types().contains(CssRuleType::FontFeatureValues)); - match prelude { - $( - BlockType::$ident_camel => { - let mut parser = FFVDeclarationsParser { - context: &self.context, - declarations: &mut self.rule.$ident, - }; - - let mut iter = RuleBodyParser::new(input, &mut parser); - while let Some(declaration) = iter.next() { - if let Err((error, slice)) = declaration { - let location = error.location; - let error = ContextualParseError::UnsupportedKeyframePropertyDeclaration( - slice, error - ); - self.context.log_css_error(location, error); - } - } - }, - )* - } - - Ok(()) - } - } - - impl<'a, 'i> DeclarationParser<'i> for FontFeatureValuesRuleParser<'a> { - type Declaration = (); - type Error = StyleParseErrorKind<'i>; - } - - impl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> for FontFeatureValuesRuleParser<'a> { - fn parse_declarations(&self) -> bool { false } - fn parse_qualified(&self) -> bool { true } - } - } -} - -font_feature_values_blocks! { - blocks = [ - #[doc = "A @swash blocksck. \ - Specifies a feature name that will work with the swash() \ - functional notation of font-variant-alternates."] - "swash" swash / Swash / NS_FONT_VARIANT_ALTERNATES_SWASH: SingleValue, - - #[doc = "A @stylistic block. \ - Specifies a feature name that will work with the annotation() \ - functional notation of font-variant-alternates."] - "stylistic" stylistic / Stylistic / NS_FONT_VARIANT_ALTERNATES_STYLISTIC: SingleValue, - - #[doc = "A @ornaments block. \ - Specifies a feature name that will work with the ornaments() ] \ - functional notation of font-variant-alternates."] - "ornaments" ornaments / Ornaments / NS_FONT_VARIANT_ALTERNATES_ORNAMENTS: SingleValue, - - #[doc = "A @annotation block. \ - Specifies a feature name that will work with the stylistic() \ - functional notation of font-variant-alternates."] - "annotation" annotation / Annotation / NS_FONT_VARIANT_ALTERNATES_ANNOTATION: SingleValue, - - #[doc = "A @character-variant block. \ - Specifies a feature name that will work with the styleset() \ - functional notation of font-variant-alternates. The value can be a pair."] - "character-variant" character_variant / CharacterVariant / NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT: - PairValues, - - #[doc = "A @styleset block. \ - Specifies a feature name that will work with the character-variant() \ - functional notation of font-variant-alternates. The value can be a list."] - "styleset" styleset / Styleset / NS_FONT_VARIANT_ALTERNATES_STYLESET: VectorValues, - ] -} diff --git a/components/style/stylesheets/font_palette_values_rule.rs b/components/style/stylesheets/font_palette_values_rule.rs deleted file mode 100644 index c604f9b3c6e..00000000000 --- a/components/style/stylesheets/font_palette_values_rule.rs +++ /dev/null @@ -1,268 +0,0 @@ -/* 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/. */ - -//! The [`@font-palette-values`][font-palette-values] at-rule. -//! -//! [font-palette-values]: https://drafts.csswg.org/css-fonts/#font-palette-values - -use crate::error_reporting::ContextualParseError; -#[cfg(feature = "gecko")] -use crate::gecko_bindings::{ - bindings::Gecko_AppendPaletteValueHashEntry, - bindings::{Gecko_SetFontPaletteBase, Gecko_SetFontPaletteOverride}, - structs::gfx::FontPaletteValueSet, - structs::gfx::FontPaletteValueSet_PaletteValues_kDark, - structs::gfx::FontPaletteValueSet_PaletteValues_kLight, -}; -use crate::parser::{Parse, ParserContext}; -use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; -use crate::str::CssStringWriter; -use crate::stylesheets::font_feature_values_rule::parse_family_name_list; -use crate::values::computed::font::FamilyName; -use crate::values::specified::Color as SpecifiedColor; -use crate::values::specified::NonNegativeInteger; -use crate::values::DashedIdent; -use cssparser::{ - AtRuleParser, CowRcStr, DeclarationParser, Parser, QualifiedRuleParser, RuleBodyItemParser, - RuleBodyParser, SourceLocation, -}; -use selectors::parser::SelectorParseErrorKind; -use std::fmt::{self, Write}; -use style_traits::{Comma, OneOrMoreSeparated}; -use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; - -#[allow(missing_docs)] -#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] -pub struct FontPaletteOverrideColor { - index: NonNegativeInteger, - color: SpecifiedColor, -} - -impl Parse for FontPaletteOverrideColor { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result<FontPaletteOverrideColor, ParseError<'i>> { - let index = NonNegativeInteger::parse(context, input)?; - let location = input.current_source_location(); - let color = SpecifiedColor::parse(context, input)?; - // Only absolute colors are accepted here. - if let SpecifiedColor::Absolute { .. } = color { - Ok(FontPaletteOverrideColor { index, color }) - } else { - Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - } - } -} - -impl ToCss for FontPaletteOverrideColor { - fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result - where - W: fmt::Write, - { - self.index.to_css(dest)?; - dest.write_char(' ')?; - self.color.to_css(dest) - } -} - -impl OneOrMoreSeparated for FontPaletteOverrideColor { - type S = Comma; -} - -impl OneOrMoreSeparated for FamilyName { - type S = Comma; -} - -#[allow(missing_docs)] -#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)] -pub enum FontPaletteBase { - Light, - Dark, - Index(NonNegativeInteger), -} - -/// The [`@font-palette-values`][font-palette-values] at-rule. -/// -/// [font-palette-values]: https://drafts.csswg.org/css-fonts/#font-palette-values -#[derive(Clone, Debug, PartialEq, ToShmem)] -pub struct FontPaletteValuesRule { - /// Palette name. - pub name: DashedIdent, - /// Font family list for @font-palette-values rule. - /// Family names cannot contain generic families. FamilyName - /// also accepts only non-generic names. - pub family_names: Vec<FamilyName>, - /// The base palette. - pub base_palette: Option<FontPaletteBase>, - /// The list of override colors. - pub override_colors: Vec<FontPaletteOverrideColor>, - /// The line and column of the rule's source code. - pub source_location: SourceLocation, -} - -impl FontPaletteValuesRule { - /// Creates an empty FontPaletteValuesRule with given location and name. - fn new(name: DashedIdent, location: SourceLocation) -> Self { - FontPaletteValuesRule { - name, - family_names: vec![], - base_palette: None, - override_colors: vec![], - source_location: location, - } - } - - /// Parses a `FontPaletteValuesRule`. - pub fn parse( - context: &ParserContext, - input: &mut Parser, - name: DashedIdent, - location: SourceLocation, - ) -> Self { - let mut rule = FontPaletteValuesRule::new(name, location); - let mut parser = FontPaletteValuesDeclarationParser { - context, - rule: &mut rule, - }; - let mut iter = RuleBodyParser::new(input, &mut parser); - while let Some(declaration) = iter.next() { - if let Err((error, slice)) = declaration { - let location = error.location; - let error = - ContextualParseError::UnsupportedFontPaletteValuesDescriptor(slice, error); - context.log_css_error(location, error); - } - } - rule - } - - /// Prints inside of `@font-palette-values` block. - fn value_to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result - where - W: Write, - { - if !self.family_names.is_empty() { - dest.write_str("font-family: ")?; - self.family_names.to_css(dest)?; - dest.write_str("; ")?; - } - if let Some(base) = &self.base_palette { - dest.write_str("base-palette: ")?; - base.to_css(dest)?; - dest.write_str("; ")?; - } - if !self.override_colors.is_empty() { - dest.write_str("override-colors: ")?; - self.override_colors.to_css(dest)?; - dest.write_str("; ")?; - } - Ok(()) - } - - /// Convert to Gecko FontPaletteValueSet. - #[cfg(feature = "gecko")] - pub fn to_gecko_palette_value_set(&self, dest: *mut FontPaletteValueSet) { - for ref family in self.family_names.iter() { - let family = family.name.to_ascii_lowercase(); - let palette_values = unsafe { - Gecko_AppendPaletteValueHashEntry(dest, family.as_ptr(), self.name.0.as_ptr()) - }; - if let Some(base_palette) = &self.base_palette { - unsafe { - Gecko_SetFontPaletteBase( - palette_values, - match &base_palette { - FontPaletteBase::Light => FontPaletteValueSet_PaletteValues_kLight, - FontPaletteBase::Dark => FontPaletteValueSet_PaletteValues_kDark, - FontPaletteBase::Index(i) => i.0.value() as i32, - }, - ); - } - } - for c in &self.override_colors { - if let SpecifiedColor::Absolute(ref absolute) = c.color { - unsafe { - Gecko_SetFontPaletteOverride( - palette_values, - c.index.0.value(), - (&absolute.color) as *const _ as *mut _, - ); - } - } - } - } - } -} - -impl ToCssWithGuard for FontPaletteValuesRule { - fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { - dest.write_str("@font-palette-values ")?; - self.name.to_css(&mut CssWriter::new(dest))?; - dest.write_str(" { ")?; - self.value_to_css(&mut CssWriter::new(dest))?; - dest.write_char('}') - } -} - -/// Parser for declarations in `FontPaletteValuesRule`. -struct FontPaletteValuesDeclarationParser<'a> { - context: &'a ParserContext<'a>, - rule: &'a mut FontPaletteValuesRule, -} - -impl<'a, 'i> AtRuleParser<'i> for FontPaletteValuesDeclarationParser<'a> { - type Prelude = (); - type AtRule = (); - type Error = StyleParseErrorKind<'i>; -} - -impl<'a, 'i> QualifiedRuleParser<'i> for FontPaletteValuesDeclarationParser<'a> { - type Prelude = (); - type QualifiedRule = (); - type Error = StyleParseErrorKind<'i>; -} - -fn parse_override_colors<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, -) -> Result<Vec<FontPaletteOverrideColor>, ParseError<'i>> { - input.parse_comma_separated(|i| FontPaletteOverrideColor::parse(context, i)) -} - -impl<'a, 'b, 'i> DeclarationParser<'i> for FontPaletteValuesDeclarationParser<'a> { - type Declaration = (); - type Error = StyleParseErrorKind<'i>; - - fn parse_value<'t>( - &mut self, - name: CowRcStr<'i>, - input: &mut Parser<'i, 't>, - ) -> Result<(), ParseError<'i>> { - match_ignore_ascii_case! { &*name, - "font-family" => { - self.rule.family_names = parse_family_name_list(self.context, input)? - }, - "base-palette" => { - self.rule.base_palette = Some(input.parse_entirely(|i| FontPaletteBase::parse(self.context, i))?) - }, - "override-colors" => { - self.rule.override_colors = parse_override_colors(self.context, input)? - }, - _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))), - } - Ok(()) - } -} - -impl<'a, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> - for FontPaletteValuesDeclarationParser<'a> -{ - fn parse_declarations(&self) -> bool { - true - } - fn parse_qualified(&self) -> bool { - false - } -} diff --git a/components/style/stylesheets/import_rule.rs b/components/style/stylesheets/import_rule.rs deleted file mode 100644 index e7ea2748465..00000000000 --- a/components/style/stylesheets/import_rule.rs +++ /dev/null @@ -1,332 +0,0 @@ -/* 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/. */ - -//! The [`@import`][import] at-rule. -//! -//! [import]: https://drafts.csswg.org/css-cascade-3/#at-import - -use crate::media_queries::MediaList; -use crate::parser::{Parse, ParserContext}; -use crate::shared_lock::{ - DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard, -}; -use crate::str::CssStringWriter; -use crate::stylesheets::{ - layer_rule::LayerName, supports_rule::SupportsCondition, CssRule, CssRuleType, - StylesheetInDocument, -}; -use crate::values::CssUrl; -use cssparser::{Parser, SourceLocation}; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ToCss}; -use to_shmem::{self, SharedMemoryBuilder, ToShmem}; - -/// A sheet that is held from an import rule. -#[cfg(feature = "gecko")] -#[derive(Debug)] -pub enum ImportSheet { - /// A bonafide stylesheet. - Sheet(crate::gecko::data::GeckoStyleSheet), - - /// An @import created while parsing off-main-thread, whose Gecko sheet has - /// yet to be created and attached. - Pending, - - /// An @import created with a false <supports-condition>, so will never be fetched. - Refused, -} - -#[cfg(feature = "gecko")] -impl ImportSheet { - /// Creates a new ImportSheet from a GeckoStyleSheet. - pub fn new(sheet: crate::gecko::data::GeckoStyleSheet) -> Self { - ImportSheet::Sheet(sheet) - } - - /// Creates a pending ImportSheet for a load that has not started yet. - pub fn new_pending() -> Self { - ImportSheet::Pending - } - - /// Creates a refused ImportSheet for a load that will not happen. - pub fn new_refused() -> Self { - ImportSheet::Refused - } - - /// Returns a reference to the GeckoStyleSheet in this ImportSheet, if it - /// exists. - pub fn as_sheet(&self) -> Option<&crate::gecko::data::GeckoStyleSheet> { - match *self { - ImportSheet::Sheet(ref s) => { - debug_assert!(!s.hack_is_null()); - if s.hack_is_null() { - return None; - } - Some(s) - }, - ImportSheet::Refused | ImportSheet::Pending => None, - } - } - - /// Returns the media list for this import rule. - pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { - self.as_sheet().and_then(|s| s.media(guard)) - } - - /// Returns the rule list for this import rule. - pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] { - match self.as_sheet() { - Some(s) => s.rules(guard), - None => &[], - } - } -} - -#[cfg(feature = "gecko")] -impl DeepCloneWithLock for ImportSheet { - fn deep_clone_with_lock( - &self, - _lock: &SharedRwLock, - _guard: &SharedRwLockReadGuard, - params: &DeepCloneParams, - ) -> Self { - use crate::gecko::data::GeckoStyleSheet; - use crate::gecko_bindings::bindings; - match *self { - ImportSheet::Sheet(ref s) => { - let clone = unsafe { - bindings::Gecko_StyleSheet_Clone(s.raw() as *const _, params.reference_sheet) - }; - ImportSheet::Sheet(unsafe { GeckoStyleSheet::from_addrefed(clone) }) - }, - ImportSheet::Pending => ImportSheet::Pending, - ImportSheet::Refused => ImportSheet::Refused, - } - } -} - -/// A sheet that is held from an import rule. -#[cfg(feature = "servo")] -#[derive(Debug)] -pub enum ImportSheet { - /// A bonafide stylesheet. - Sheet(::servo_arc::Arc<crate::stylesheets::Stylesheet>), - - /// An @import created with a false <supports-condition>, so will never be fetched. - Refused, -} - -#[cfg(feature = "servo")] -impl ImportSheet { - /// Creates a new ImportSheet from a stylesheet. - pub fn new(sheet: ::servo_arc::Arc<crate::stylesheets::Stylesheet>) -> Self { - ImportSheet::Sheet(sheet) - } - - /// Creates a refused ImportSheet for a load that will not happen. - pub fn new_refused() -> Self { - ImportSheet::Refused - } - - /// Returns a reference to the stylesheet in this ImportSheet, if it exists. - pub fn as_sheet(&self) -> Option<&::servo_arc::Arc<crate::stylesheets::Stylesheet>> { - match *self { - ImportSheet::Sheet(ref s) => Some(s), - ImportSheet::Refused => None, - } - } - - /// Returns the media list for this import rule. - pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { - self.as_sheet().and_then(|s| s.media(guard)) - } - - /// Returns the rules for this import rule. - pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] { - match self.as_sheet() { - Some(s) => s.rules(guard), - None => &[], - } - } -} - -#[cfg(feature = "servo")] -impl DeepCloneWithLock for ImportSheet { - fn deep_clone_with_lock( - &self, - _lock: &SharedRwLock, - _guard: &SharedRwLockReadGuard, - _params: &DeepCloneParams, - ) -> Self { - match *self { - ImportSheet::Sheet(ref s) => { - use servo_arc::Arc; - ImportSheet::Sheet(Arc::new((&**s).clone())) - }, - ImportSheet::Refused => ImportSheet::Refused, - } - } -} - -/// The layer specified in an import rule (can be none, anonymous, or named). -#[derive(Debug, Clone)] -pub enum ImportLayer { - /// No layer specified - None, - - /// Anonymous layer (`layer`) - Anonymous, - - /// Named layer (`layer(name)`) - Named(LayerName), -} - -/// The supports condition in an import rule. -#[derive(Debug, Clone)] -pub struct ImportSupportsCondition { - /// The supports condition. - pub condition: SupportsCondition, - - /// If the import is enabled, from the result of the import condition. - pub enabled: bool, -} - -impl ToCss for ImportLayer { - fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result - where - W: Write, - { - match *self { - ImportLayer::None => Ok(()), - ImportLayer::Anonymous => dest.write_str("layer"), - ImportLayer::Named(ref name) => { - dest.write_str("layer(")?; - name.to_css(dest)?; - dest.write_char(')') - }, - } - } -} - -/// The [`@import`][import] at-rule. -/// -/// [import]: https://drafts.csswg.org/css-cascade-3/#at-import -#[derive(Debug)] -pub struct ImportRule { - /// The `<url>` this `@import` rule is loading. - pub url: CssUrl, - - /// The stylesheet is always present. However, in the case of gecko async - /// parsing, we don't actually have a Gecko sheet at first, and so the - /// ImportSheet just has stub behavior until it appears. - pub stylesheet: ImportSheet, - - /// A <supports-condition> for the rule. - pub supports: Option<ImportSupportsCondition>, - - /// A `layer()` function name. - pub layer: ImportLayer, - - /// The line and column of the rule's source code. - pub source_location: SourceLocation, -} - -impl ImportRule { - /// Parses the layer() / layer / supports() part of the import header, as per - /// https://drafts.csswg.org/css-cascade-5/#at-import: - /// - /// [ layer | layer(<layer-name>) ]? - /// [ supports([ <supports-condition> | <declaration> ]) ]? - /// - /// We do this here so that the import preloader can look at this without having to parse the - /// whole import rule or parse the media query list or what not. - pub fn parse_layer_and_supports<'i, 't>( - input: &mut Parser<'i, 't>, - context: &mut ParserContext, - ) -> (ImportLayer, Option<ImportSupportsCondition>) { - let layer = if input - .try_parse(|input| input.expect_ident_matching("layer")) - .is_ok() - { - ImportLayer::Anonymous - } else { - input - .try_parse(|input| { - input.expect_function_matching("layer")?; - input - .parse_nested_block(|input| LayerName::parse(context, input)) - .map(|name| ImportLayer::Named(name)) - }) - .ok() - .unwrap_or(ImportLayer::None) - }; - - let supports = if !static_prefs::pref!("layout.css.import-supports.enabled") { - None - } else { - input - .try_parse(SupportsCondition::parse_for_import) - .map(|condition| { - let enabled = context - .nest_for_rule(CssRuleType::Style, |context| condition.eval(context)); - ImportSupportsCondition { condition, enabled } - }) - .ok() - }; - - (layer, supports) - } -} - -impl ToShmem for ImportRule { - fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> { - Err(String::from( - "ToShmem failed for ImportRule: cannot handle imported style sheets", - )) - } -} - -impl DeepCloneWithLock for ImportRule { - fn deep_clone_with_lock( - &self, - lock: &SharedRwLock, - guard: &SharedRwLockReadGuard, - params: &DeepCloneParams, - ) -> Self { - ImportRule { - url: self.url.clone(), - stylesheet: self.stylesheet.deep_clone_with_lock(lock, guard, params), - supports: self.supports.clone(), - layer: self.layer.clone(), - source_location: self.source_location.clone(), - } - } -} - -impl ToCssWithGuard for ImportRule { - fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { - dest.write_str("@import ")?; - self.url.to_css(&mut CssWriter::new(dest))?; - - if !matches!(self.layer, ImportLayer::None) { - dest.write_char(' ')?; - self.layer.to_css(&mut CssWriter::new(dest))?; - } - - if let Some(ref supports) = self.supports { - dest.write_str(" supports(")?; - supports.condition.to_css(&mut CssWriter::new(dest))?; - dest.write_char(')')?; - } - - if let Some(media) = self.stylesheet.media(guard) { - if !media.is_empty() { - dest.write_char(' ')?; - media.to_css(&mut CssWriter::new(dest))?; - } - } - - dest.write_char(';') - } -} diff --git a/components/style/stylesheets/keyframes_rule.rs b/components/style/stylesheets/keyframes_rule.rs deleted file mode 100644 index 6e5016080e9..00000000000 --- a/components/style/stylesheets/keyframes_rule.rs +++ /dev/null @@ -1,691 +0,0 @@ -/* 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/. */ - -//! Keyframes: https://drafts.csswg.org/css-animations/#keyframes - -use crate::error_reporting::ContextualParseError; -use crate::parser::ParserContext; -use crate::properties::longhands::animation_composition::single_value::SpecifiedValue as SpecifiedComposition; -use crate::properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction; -use crate::properties::LonghandIdSet; -use crate::properties::{Importance, PropertyDeclaration}; -use crate::properties::{LonghandId, PropertyDeclarationBlock, PropertyId}; -use crate::properties::{PropertyDeclarationId, SourcePropertyDeclaration}; -use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, SharedRwLock, SharedRwLockReadGuard}; -use crate::shared_lock::{Locked, ToCssWithGuard}; -use crate::str::CssStringWriter; -use crate::stylesheets::rule_parser::VendorPrefix; -use crate::stylesheets::{CssRuleType, StylesheetContents}; -use crate::values::{serialize_percentage, KeyframesName}; -use cssparser::{ - parse_one_rule, AtRuleParser, CowRcStr, DeclarationParser, Parser, ParserInput, ParserState, - QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, Token, -}; -use servo_arc::Arc; -use std::borrow::Cow; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ParseError, ParsingMode, StyleParseErrorKind, ToCss}; - -/// A [`@keyframes`][keyframes] rule. -/// -/// [keyframes]: https://drafts.csswg.org/css-animations/#keyframes -#[derive(Debug, ToShmem)] -pub struct KeyframesRule { - /// The name of the current animation. - pub name: KeyframesName, - /// The keyframes specified for this CSS rule. - pub keyframes: Vec<Arc<Locked<Keyframe>>>, - /// Vendor prefix type the @keyframes has. - pub vendor_prefix: Option<VendorPrefix>, - /// The line and column of the rule's source code. - pub source_location: SourceLocation, -} - -impl ToCssWithGuard for KeyframesRule { - // Serialization of KeyframesRule is not specced. - fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { - dest.write_str("@keyframes ")?; - self.name.to_css(&mut CssWriter::new(dest))?; - dest.write_str(" {")?; - let iter = self.keyframes.iter(); - for lock in iter { - dest.write_str("\n")?; - let keyframe = lock.read_with(&guard); - keyframe.to_css(guard, dest)?; - } - dest.write_str("\n}") - } -} - -impl KeyframesRule { - /// Returns the index of the last keyframe that matches the given selector. - /// If the selector is not valid, or no keyframe is found, returns None. - /// - /// Related spec: - /// <https://drafts.csswg.org/css-animations-1/#interface-csskeyframesrule-findrule> - pub fn find_rule(&self, guard: &SharedRwLockReadGuard, selector: &str) -> Option<usize> { - let mut input = ParserInput::new(selector); - if let Ok(selector) = Parser::new(&mut input).parse_entirely(KeyframeSelector::parse) { - for (i, keyframe) in self.keyframes.iter().enumerate().rev() { - if keyframe.read_with(guard).selector == selector { - return Some(i); - } - } - } - None - } -} - -impl DeepCloneWithLock for KeyframesRule { - fn deep_clone_with_lock( - &self, - lock: &SharedRwLock, - guard: &SharedRwLockReadGuard, - params: &DeepCloneParams, - ) -> Self { - KeyframesRule { - name: self.name.clone(), - keyframes: self - .keyframes - .iter() - .map(|x| { - Arc::new( - lock.wrap(x.read_with(guard).deep_clone_with_lock(lock, guard, params)), - ) - }) - .collect(), - vendor_prefix: self.vendor_prefix.clone(), - source_location: self.source_location.clone(), - } - } -} - -/// A number from 0 to 1, indicating the percentage of the animation when this -/// keyframe should run. -#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToShmem)] -pub struct KeyframePercentage(pub f32); - -impl ::std::cmp::Ord for KeyframePercentage { - #[inline] - fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { - // We know we have a number from 0 to 1, so unwrap() here is safe. - self.0.partial_cmp(&other.0).unwrap() - } -} - -impl ::std::cmp::Eq for KeyframePercentage {} - -impl ToCss for KeyframePercentage { - fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result - where - W: Write, - { - serialize_percentage(self.0, dest) - } -} - -impl KeyframePercentage { - /// Trivially constructs a new `KeyframePercentage`. - #[inline] - pub fn new(value: f32) -> KeyframePercentage { - debug_assert!(value >= 0. && value <= 1.); - KeyframePercentage(value) - } - - fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<KeyframePercentage, ParseError<'i>> { - let token = input.next()?.clone(); - match token { - Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case("from") => { - Ok(KeyframePercentage::new(0.)) - }, - Token::Ident(ref identifier) if identifier.as_ref().eq_ignore_ascii_case("to") => { - Ok(KeyframePercentage::new(1.)) - }, - Token::Percentage { - unit_value: percentage, - .. - } if percentage >= 0. && percentage <= 1. => Ok(KeyframePercentage::new(percentage)), - _ => Err(input.new_unexpected_token_error(token)), - } - } -} - -/// A keyframes selector is a list of percentages or from/to symbols, which are -/// converted at parse time to percentages. -#[derive(Clone, Debug, Eq, PartialEq, ToCss, ToShmem)] -#[css(comma)] -pub struct KeyframeSelector(#[css(iterable)] Vec<KeyframePercentage>); - -impl KeyframeSelector { - /// Return the list of percentages this selector contains. - #[inline] - pub fn percentages(&self) -> &[KeyframePercentage] { - &self.0 - } - - /// A dummy public function so we can write a unit test for this. - pub fn new_for_unit_testing(percentages: Vec<KeyframePercentage>) -> KeyframeSelector { - KeyframeSelector(percentages) - } - - /// Parse a keyframe selector from CSS input. - pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> { - input - .parse_comma_separated(KeyframePercentage::parse) - .map(KeyframeSelector) - } -} - -/// A keyframe. -#[derive(Debug, ToShmem)] -pub struct Keyframe { - /// The selector this keyframe was specified from. - pub selector: KeyframeSelector, - - /// The declaration block that was declared inside this keyframe. - /// - /// Note that `!important` rules in keyframes don't apply, but we keep this - /// `Arc` just for convenience. - pub block: Arc<Locked<PropertyDeclarationBlock>>, - - /// The line and column of the rule's source code. - pub source_location: SourceLocation, -} - -impl ToCssWithGuard for Keyframe { - fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { - self.selector.to_css(&mut CssWriter::new(dest))?; - dest.write_str(" { ")?; - self.block.read_with(guard).to_css(dest)?; - dest.write_str(" }")?; - Ok(()) - } -} - -impl Keyframe { - /// Parse a CSS keyframe. - pub fn parse<'i>( - css: &'i str, - parent_stylesheet_contents: &StylesheetContents, - lock: &SharedRwLock, - ) -> Result<Arc<Locked<Self>>, ParseError<'i>> { - let url_data = parent_stylesheet_contents.url_data.read(); - let namespaces = parent_stylesheet_contents.namespaces.read(); - let mut context = ParserContext::new( - parent_stylesheet_contents.origin, - &url_data, - Some(CssRuleType::Keyframe), - ParsingMode::DEFAULT, - parent_stylesheet_contents.quirks_mode, - Cow::Borrowed(&*namespaces), - None, - None, - ); - let mut input = ParserInput::new(css); - let mut input = Parser::new(&mut input); - - let mut declarations = SourcePropertyDeclaration::default(); - let mut rule_parser = KeyframeListParser { - context: &mut context, - shared_lock: &lock, - declarations: &mut declarations, - }; - parse_one_rule(&mut input, &mut rule_parser) - } -} - -impl DeepCloneWithLock for Keyframe { - /// Deep clones this Keyframe. - fn deep_clone_with_lock( - &self, - lock: &SharedRwLock, - guard: &SharedRwLockReadGuard, - _params: &DeepCloneParams, - ) -> Keyframe { - Keyframe { - selector: self.selector.clone(), - block: Arc::new(lock.wrap(self.block.read_with(guard).clone())), - source_location: self.source_location.clone(), - } - } -} - -/// A keyframes step value. This can be a synthetised keyframes animation, that -/// is, one autogenerated from the current computed values, or a list of -/// declarations to apply. -/// -/// TODO: Find a better name for this? -#[derive(Clone, Debug, MallocSizeOf)] -pub enum KeyframesStepValue { - /// A step formed by a declaration block specified by the CSS. - Declarations { - /// The declaration block per se. - #[cfg_attr( - feature = "gecko", - ignore_malloc_size_of = "XXX: Primary ref, measure if DMD says it's worthwhile" - )] - #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] - block: Arc<Locked<PropertyDeclarationBlock>>, - }, - /// A synthetic step computed from the current computed values at the time - /// of the animation. - ComputedValues, -} - -/// A single step from a keyframe animation. -#[derive(Clone, Debug, MallocSizeOf)] -pub struct KeyframesStep { - /// The percentage of the animation duration when this step starts. - pub start_percentage: KeyframePercentage, - /// Declarations that will determine the final style during the step, or - /// `ComputedValues` if this is an autogenerated step. - pub value: KeyframesStepValue, - /// Whether an animation-timing-function declaration exists in the list of - /// declarations. - /// - /// This is used to know when to override the keyframe animation style. - pub declared_timing_function: bool, - /// Whether an animation-composition declaration exists in the list of - /// declarations. - /// - /// This is used to know when to override the keyframe animation style. - pub declared_composition: bool, -} - -impl KeyframesStep { - #[inline] - fn new( - start_percentage: KeyframePercentage, - value: KeyframesStepValue, - guard: &SharedRwLockReadGuard, - ) -> Self { - let mut declared_timing_function = false; - let mut declared_composition = false; - if let KeyframesStepValue::Declarations { ref block } = value { - for prop_decl in block.read_with(guard).declarations().iter() { - match *prop_decl { - PropertyDeclaration::AnimationTimingFunction(..) => { - declared_timing_function = true; - }, - PropertyDeclaration::AnimationComposition(..) => { - declared_composition = true; - }, - _ => continue, - } - // Don't need to continue the loop if both are found. - if declared_timing_function && declared_composition { - break; - } - } - } - - KeyframesStep { - start_percentage, - value, - declared_timing_function, - declared_composition, - } - } - - /// Return specified PropertyDeclaration. - #[inline] - fn get_declared_property<'a>( - &'a self, - guard: &'a SharedRwLockReadGuard, - property: LonghandId, - ) -> Option<&'a PropertyDeclaration> { - match self.value { - KeyframesStepValue::Declarations { ref block } => { - let guard = block.read_with(guard); - let (declaration, _) = guard - .get(PropertyDeclarationId::Longhand(property)) - .unwrap(); - match *declaration { - PropertyDeclaration::CSSWideKeyword(..) => None, - // FIXME: Bug 1710735: Support css variable in @keyframes rule. - PropertyDeclaration::WithVariables(..) => None, - _ => Some(declaration), - } - }, - KeyframesStepValue::ComputedValues => { - panic!("Shouldn't happen to set this property in missing keyframes") - }, - } - } - - /// Return specified TransitionTimingFunction if this KeyframesSteps has - /// 'animation-timing-function'. - pub fn get_animation_timing_function( - &self, - guard: &SharedRwLockReadGuard, - ) -> Option<SpecifiedTimingFunction> { - if !self.declared_timing_function { - return None; - } - - self.get_declared_property(guard, LonghandId::AnimationTimingFunction) - .map(|decl| { - match *decl { - PropertyDeclaration::AnimationTimingFunction(ref value) => { - // Use the first value - value.0[0].clone() - }, - _ => unreachable!("Unexpected PropertyDeclaration"), - } - }) - } - - /// Return CompositeOperation if this KeyframesSteps has 'animation-composition'. - pub fn get_animation_composition( - &self, - guard: &SharedRwLockReadGuard, - ) -> Option<SpecifiedComposition> { - if !self.declared_composition { - return None; - } - - self.get_declared_property(guard, LonghandId::AnimationComposition) - .map(|decl| { - match *decl { - PropertyDeclaration::AnimationComposition(ref value) => { - // Use the first value - value.0[0].clone() - }, - _ => unreachable!("Unexpected PropertyDeclaration"), - } - }) - } -} - -/// This structure represents a list of animation steps computed from the list -/// of keyframes, in order. -/// -/// It only takes into account animable properties. -#[derive(Clone, Debug, MallocSizeOf)] -pub struct KeyframesAnimation { - /// The difference steps of the animation. - pub steps: Vec<KeyframesStep>, - /// The properties that change in this animation. - pub properties_changed: LonghandIdSet, - /// Vendor prefix type the @keyframes has. - pub vendor_prefix: Option<VendorPrefix>, -} - -/// Get all the animated properties in a keyframes animation. -fn get_animated_properties( - keyframes: &[Arc<Locked<Keyframe>>], - guard: &SharedRwLockReadGuard, -) -> LonghandIdSet { - let mut ret = LonghandIdSet::new(); - // NB: declarations are already deduplicated, so we don't have to check for - // it here. - for keyframe in keyframes { - let keyframe = keyframe.read_with(&guard); - let block = keyframe.block.read_with(guard); - // CSS Animations spec clearly defines that properties with !important - // in keyframe rules are invalid and ignored, but it's still ambiguous - // whether we should drop the !important properties or retain the - // properties when they are set via CSSOM. So we assume there might - // be properties with !important in keyframe rules here. - // See the spec issue https://github.com/w3c/csswg-drafts/issues/1824 - for declaration in block.normal_declaration_iter() { - let longhand_id = match declaration.id() { - PropertyDeclarationId::Longhand(id) => id, - _ => continue, - }; - - if longhand_id == LonghandId::Display { - continue; - } - - if !longhand_id.is_animatable() { - continue; - } - - ret.insert(longhand_id); - } - } - - ret -} - -impl KeyframesAnimation { - /// Create a keyframes animation from a given list of keyframes. - /// - /// This will return a keyframe animation with empty steps and - /// properties_changed if the list of keyframes is empty, or there are no - /// animated properties obtained from the keyframes. - /// - /// Otherwise, this will compute and sort the steps used for the animation, - /// and return the animation object. - pub fn from_keyframes( - keyframes: &[Arc<Locked<Keyframe>>], - vendor_prefix: Option<VendorPrefix>, - guard: &SharedRwLockReadGuard, - ) -> Self { - let mut result = KeyframesAnimation { - steps: vec![], - properties_changed: LonghandIdSet::new(), - vendor_prefix, - }; - - if keyframes.is_empty() { - return result; - } - - result.properties_changed = get_animated_properties(keyframes, guard); - if result.properties_changed.is_empty() { - return result; - } - - for keyframe in keyframes { - let keyframe = keyframe.read_with(&guard); - for percentage in keyframe.selector.0.iter() { - result.steps.push(KeyframesStep::new( - *percentage, - KeyframesStepValue::Declarations { - block: keyframe.block.clone(), - }, - guard, - )); - } - } - - // Sort by the start percentage, so we can easily find a frame. - result.steps.sort_by_key(|step| step.start_percentage); - - // Prepend autogenerated keyframes if appropriate. - if result.steps[0].start_percentage.0 != 0. { - result.steps.insert( - 0, - KeyframesStep::new( - KeyframePercentage::new(0.), - KeyframesStepValue::ComputedValues, - guard, - ), - ); - } - - if result.steps.last().unwrap().start_percentage.0 != 1. { - result.steps.push(KeyframesStep::new( - KeyframePercentage::new(1.), - KeyframesStepValue::ComputedValues, - guard, - )); - } - - result - } -} - -/// Parses a keyframes list, like: -/// 0%, 50% { -/// width: 50%; -/// } -/// -/// 40%, 60%, 100% { -/// width: 100%; -/// } -struct KeyframeListParser<'a, 'b> { - context: &'a mut ParserContext<'b>, - shared_lock: &'a SharedRwLock, - declarations: &'a mut SourcePropertyDeclaration, -} - -/// Parses a keyframe list from CSS input. -pub fn parse_keyframe_list<'a>( - context: &mut ParserContext<'a>, - input: &mut Parser, - shared_lock: &SharedRwLock, -) -> Vec<Arc<Locked<Keyframe>>> { - let mut declarations = SourcePropertyDeclaration::default(); - let mut parser = KeyframeListParser { - context, - shared_lock, - declarations: &mut declarations, - }; - RuleBodyParser::new(input, &mut parser) - .filter_map(Result::ok) - .collect() -} - -impl<'a, 'b, 'i> AtRuleParser<'i> for KeyframeListParser<'a, 'b> { - type Prelude = (); - type AtRule = Arc<Locked<Keyframe>>; - type Error = StyleParseErrorKind<'i>; -} - -impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeListParser<'a, 'b> { - type Declaration = Arc<Locked<Keyframe>>; - type Error = StyleParseErrorKind<'i>; -} - -impl<'a, 'b, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a, 'b> { - type Prelude = KeyframeSelector; - type QualifiedRule = Arc<Locked<Keyframe>>; - type Error = StyleParseErrorKind<'i>; - - fn parse_prelude<'t>( - &mut self, - input: &mut Parser<'i, 't>, - ) -> Result<Self::Prelude, ParseError<'i>> { - let start_position = input.position(); - KeyframeSelector::parse(input).map_err(|e| { - let location = e.location; - let error = ContextualParseError::InvalidKeyframeRule( - input.slice_from(start_position), - e.clone(), - ); - self.context.log_css_error(location, error); - e - }) - } - - fn parse_block<'t>( - &mut self, - selector: Self::Prelude, - start: &ParserState, - input: &mut Parser<'i, 't>, - ) -> Result<Self::QualifiedRule, ParseError<'i>> { - let mut block = PropertyDeclarationBlock::new(); - let declarations = &mut self.declarations; - self.context - .nest_for_rule(CssRuleType::Keyframe, |context| { - let mut parser = KeyframeDeclarationParser { - context: &context, - declarations, - }; - let mut iter = RuleBodyParser::new(input, &mut parser); - while let Some(declaration) = iter.next() { - match declaration { - Ok(()) => { - block.extend(iter.parser.declarations.drain(), Importance::Normal); - }, - Err((error, slice)) => { - iter.parser.declarations.clear(); - let location = error.location; - let error = - ContextualParseError::UnsupportedKeyframePropertyDeclaration( - slice, error, - ); - context.log_css_error(location, error); - }, - } - // `parse_important` is not called here, `!important` is not allowed in keyframe blocks. - } - }); - Ok(Arc::new(self.shared_lock.wrap(Keyframe { - selector, - block: Arc::new(self.shared_lock.wrap(block)), - source_location: start.source_location(), - }))) - } -} - -impl<'a, 'b, 'i> RuleBodyItemParser<'i, Arc<Locked<Keyframe>>, StyleParseErrorKind<'i>> - for KeyframeListParser<'a, 'b> -{ - fn parse_qualified(&self) -> bool { - true - } - fn parse_declarations(&self) -> bool { - false - } -} - -struct KeyframeDeclarationParser<'a, 'b: 'a> { - context: &'a ParserContext<'b>, - declarations: &'a mut SourcePropertyDeclaration, -} - -/// Default methods reject all at rules. -impl<'a, 'b, 'i> AtRuleParser<'i> for KeyframeDeclarationParser<'a, 'b> { - type Prelude = (); - type AtRule = (); - type Error = StyleParseErrorKind<'i>; -} - -impl<'a, 'b, 'i> QualifiedRuleParser<'i> for KeyframeDeclarationParser<'a, 'b> { - type Prelude = (); - type QualifiedRule = (); - type Error = StyleParseErrorKind<'i>; -} - -impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeDeclarationParser<'a, 'b> { - type Declaration = (); - type Error = StyleParseErrorKind<'i>; - - fn parse_value<'t>( - &mut self, - name: CowRcStr<'i>, - input: &mut Parser<'i, 't>, - ) -> Result<(), ParseError<'i>> { - let id = match PropertyId::parse(&name, self.context) { - Ok(id) => id, - Err(()) => { - return Err(input.new_custom_error(StyleParseErrorKind::UnknownProperty(name))); - }, - }; - - // TODO(emilio): Shouldn't this use parse_entirely? - PropertyDeclaration::parse_into(self.declarations, id, self.context, input)?; - - // In case there is still unparsed text in the declaration, we should - // roll back. - input.expect_exhausted()?; - - Ok(()) - } -} - -impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> - for KeyframeDeclarationParser<'a, 'b> -{ - fn parse_qualified(&self) -> bool { - false - } - fn parse_declarations(&self) -> bool { - true - } -} diff --git a/components/style/stylesheets/layer_rule.rs b/components/style/stylesheets/layer_rule.rs deleted file mode 100644 index 3ebe6bb34f8..00000000000 --- a/components/style/stylesheets/layer_rule.rs +++ /dev/null @@ -1,228 +0,0 @@ -/* 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 [`@layer`][layer] rule. -//! -//! [layer]: https://drafts.csswg.org/css-cascade-5/#layering - -use crate::parser::{Parse, ParserContext}; -use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked}; -use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; -use crate::values::AtomIdent; - -use super::CssRules; - -use cssparser::{Parser, SourceLocation, Token}; -use servo_arc::Arc; -use smallvec::SmallVec; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ParseError, ToCss}; - -/// The order of a given layer. We use 16 bits so that we can pack LayerOrder -/// and CascadeLevel in a single 32-bit struct. If we need more bits we can go -/// back to packing CascadeLevel in a single byte as we did before. -#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd, Ord)] -pub struct LayerOrder(u16); - -impl LayerOrder { - /// The order of the root layer. - pub const fn root() -> Self { - Self(std::u16::MAX - 1) - } - - /// The order of the style attribute layer. - pub const fn style_attribute() -> Self { - Self(std::u16::MAX) - } - - /// Returns whether this layer is for the style attribute, which behaves - /// differently in terms of !important, see - /// https://github.com/w3c/csswg-drafts/issues/6872 - /// - /// (This is a bit silly, mind-you, but it's needed so that revert-layer - /// behaves correctly). - #[inline] - pub fn is_style_attribute_layer(&self) -> bool { - *self == Self::style_attribute() - } - - /// The first cascade layer order. - pub const fn first() -> Self { - Self(0) - } - - /// Increment the cascade layer order. - #[inline] - pub fn inc(&mut self) { - if self.0 != std::u16::MAX - 1 { - self.0 += 1; - } - } -} - -/// A `<layer-name>`: https://drafts.csswg.org/css-cascade-5/#typedef-layer-name -#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)] -pub struct LayerName(pub SmallVec<[AtomIdent; 1]>); - -impl LayerName { - /// Returns an empty layer name (which isn't a valid final state, so caller - /// is responsible to fill up the name before use). - pub fn new_empty() -> Self { - Self(Default::default()) - } - - /// Returns a synthesized name for an anonymous layer. - pub fn new_anonymous() -> Self { - use std::sync::atomic::{AtomicUsize, Ordering}; - static NEXT_ANONYMOUS_LAYER_NAME: AtomicUsize = AtomicUsize::new(0); - - let mut name = SmallVec::new(); - let next_id = NEXT_ANONYMOUS_LAYER_NAME.fetch_add(1, Ordering::Relaxed); - // The parens don't _technically_ prevent conflicts with authors, as - // authors could write escaped parens as part of the identifier, I - // think, but highly reduces the possibility. - name.push(AtomIdent::from(&*format!("-moz-anon-layer({})", next_id))); - - LayerName(name) - } - - /// Returns the names of the layers. That is, for a layer like `foo.bar`, - /// it'd return [foo, bar]. - pub fn layer_names(&self) -> &[AtomIdent] { - &self.0 - } -} - -impl Parse for LayerName { - fn parse<'i, 't>( - _: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result<Self, ParseError<'i>> { - let mut result = SmallVec::new(); - result.push(AtomIdent::from(&**input.expect_ident()?)); - loop { - let next_name = input.try_parse(|input| -> Result<AtomIdent, ParseError<'i>> { - match input.next_including_whitespace()? { - Token::Delim('.') => {}, - other => { - let t = other.clone(); - return Err(input.new_unexpected_token_error(t)); - }, - } - - let name = match input.next_including_whitespace()? { - Token::Ident(ref ident) => ident, - other => { - let t = other.clone(); - return Err(input.new_unexpected_token_error(t)); - }, - }; - - Ok(AtomIdent::from(&**name)) - }); - - match next_name { - Ok(name) => result.push(name), - Err(..) => break, - } - } - Ok(LayerName(result)) - } -} - -impl ToCss for LayerName { - fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result - where - W: Write, - { - let mut first = true; - for name in self.0.iter() { - if !first { - dest.write_char('.')?; - } - first = false; - name.to_css(dest)?; - } - Ok(()) - } -} - -#[derive(Debug, ToShmem)] -/// A block `@layer <name>? { ... }` -/// https://drafts.csswg.org/css-cascade-5/#layer-block -pub struct LayerBlockRule { - /// The layer name, or `None` if anonymous. - pub name: Option<LayerName>, - /// The nested rules. - pub rules: Arc<Locked<CssRules>>, - /// The source position where this rule was found. - pub source_location: SourceLocation, -} - -impl ToCssWithGuard for LayerBlockRule { - fn to_css( - &self, - guard: &SharedRwLockReadGuard, - dest: &mut crate::str::CssStringWriter, - ) -> fmt::Result { - dest.write_str("@layer")?; - if let Some(ref name) = self.name { - dest.write_char(' ')?; - name.to_css(&mut CssWriter::new(dest))?; - } - self.rules.read_with(guard).to_css_block(guard, dest) - } -} - -impl DeepCloneWithLock for LayerBlockRule { - fn deep_clone_with_lock( - &self, - lock: &SharedRwLock, - guard: &SharedRwLockReadGuard, - params: &DeepCloneParams, - ) -> Self { - Self { - name: self.name.clone(), - rules: Arc::new( - lock.wrap( - self.rules - .read_with(guard) - .deep_clone_with_lock(lock, guard, params), - ), - ), - source_location: self.source_location.clone(), - } - } -} - -/// A statement `@layer <name>, <name>, <name>;` -/// -/// https://drafts.csswg.org/css-cascade-5/#layer-empty -#[derive(Clone, Debug, ToShmem)] -pub struct LayerStatementRule { - /// The list of layers to sort. - pub names: Vec<LayerName>, - /// The source position where this rule was found. - pub source_location: SourceLocation, -} - -impl ToCssWithGuard for LayerStatementRule { - fn to_css( - &self, - _: &SharedRwLockReadGuard, - dest: &mut crate::str::CssStringWriter, - ) -> fmt::Result { - let mut writer = CssWriter::new(dest); - writer.write_str("@layer ")?; - let mut first = true; - for name in &*self.names { - if !first { - writer.write_str(", ")?; - } - first = false; - name.to_css(&mut writer)?; - } - writer.write_char(';') - } -} diff --git a/components/style/stylesheets/loader.rs b/components/style/stylesheets/loader.rs deleted file mode 100644 index f987cf95972..00000000000 --- a/components/style/stylesheets/loader.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* 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/. */ - -//! The stylesheet loader is the abstraction used to trigger network requests -//! for `@import` rules. - -use crate::media_queries::MediaList; -use crate::parser::ParserContext; -use crate::shared_lock::{Locked, SharedRwLock}; -use crate::stylesheets::import_rule::{ImportLayer, ImportRule, ImportSupportsCondition}; -use crate::values::CssUrl; -use cssparser::SourceLocation; -use servo_arc::Arc; - -/// The stylesheet loader is the abstraction used to trigger network requests -/// for `@import` rules. -pub trait StylesheetLoader { - /// Request a stylesheet after parsing a given `@import` rule, and return - /// the constructed `@import` rule. - fn request_stylesheet( - &self, - url: CssUrl, - location: SourceLocation, - context: &ParserContext, - lock: &SharedRwLock, - media: Arc<Locked<MediaList>>, - supports: Option<ImportSupportsCondition>, - layer: ImportLayer, - ) -> Arc<Locked<ImportRule>>; -} diff --git a/components/style/stylesheets/media_rule.rs b/components/style/stylesheets/media_rule.rs deleted file mode 100644 index cde60a16bf7..00000000000 --- a/components/style/stylesheets/media_rule.rs +++ /dev/null @@ -1,71 +0,0 @@ -/* 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/. */ - -//! An [`@media`][media] rule. -//! -//! [media]: https://drafts.csswg.org/css-conditional/#at-ruledef-media - -use crate::media_queries::MediaList; -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; -#[cfg(feature = "gecko")] -use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; -use servo_arc::Arc; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ToCss}; - -/// An [`@media`][media] rule. -/// -/// [media]: https://drafts.csswg.org/css-conditional/#at-ruledef-media -#[derive(Debug, ToShmem)] -pub struct MediaRule { - /// The list of media queries used by this media rule. - pub media_queries: Arc<Locked<MediaList>>, - /// The nested rules to this media rule. - pub rules: Arc<Locked<CssRules>>, - /// The source position where this media rule was found. - pub source_location: SourceLocation, -} - -impl MediaRule { - /// Measure heap usage. - #[cfg(feature = "gecko")] - pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { - // Measurement of other fields may be added later. - self.rules.unconditional_shallow_size_of(ops) + - self.rules.read_with(guard).size_of(guard, ops) - } -} - -impl ToCssWithGuard for MediaRule { - // Serialization of MediaRule is not specced. - // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSMediaRule - fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { - dest.write_str("@media ")?; - self.media_queries - .read_with(guard) - .to_css(&mut CssWriter::new(dest))?; - self.rules.read_with(guard).to_css_block(guard, dest) - } -} - -impl DeepCloneWithLock for MediaRule { - fn deep_clone_with_lock( - &self, - lock: &SharedRwLock, - guard: &SharedRwLockReadGuard, - params: &DeepCloneParams, - ) -> Self { - let media_queries = self.media_queries.read_with(guard); - let rules = self.rules.read_with(guard); - MediaRule { - media_queries: Arc::new(lock.wrap(media_queries.clone())), - rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))), - source_location: self.source_location.clone(), - } - } -} diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs deleted file mode 100644 index 85d6619d135..00000000000 --- a/components/style/stylesheets/mod.rs +++ /dev/null @@ -1,582 +0,0 @@ -/* 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/. */ - -//! Style sheets and their CSS rules. - -pub mod container_rule; -mod counter_style_rule; -mod document_rule; -mod font_face_rule; -pub mod font_feature_values_rule; -pub mod font_palette_values_rule; -pub mod import_rule; -pub mod keyframes_rule; -pub mod layer_rule; -mod loader; -mod media_rule; -mod namespace_rule; -pub mod origin; -mod page_rule; -mod property_rule; -mod rule_list; -mod rule_parser; -mod rules_iterator; -mod style_rule; -mod stylesheet; -pub mod supports_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::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked}; -use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; -use crate::str::CssStringWriter; -use cssparser::{parse_one_rule, Parser, ParserInput}; -#[cfg(feature = "gecko")] -use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; -use servo_arc::Arc; -use std::borrow::Cow; -use std::fmt; -#[cfg(feature = "gecko")] -use std::mem::{self, ManuallyDrop}; -use style_traits::ParsingMode; -use to_shmem::{self, SharedMemoryBuilder, ToShmem}; - -pub use self::container_rule::ContainerRule; -pub use self::counter_style_rule::CounterStyleRule; -pub use self::document_rule::DocumentRule; -pub use self::font_face_rule::FontFaceRule; -pub use self::font_feature_values_rule::FontFeatureValuesRule; -pub use self::font_palette_values_rule::FontPaletteValuesRule; -pub use self::import_rule::ImportRule; -pub use self::keyframes_rule::KeyframesRule; -pub use self::layer_rule::{LayerBlockRule, LayerStatementRule}; -pub use self::loader::StylesheetLoader; -pub use self::media_rule::MediaRule; -pub use self::namespace_rule::NamespaceRule; -pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter}; -pub use self::page_rule::{PageRule, PageSelector, PageSelectors}; -pub use self::property_rule::PropertyRule; -pub use self::rule_list::{CssRules, CssRulesHelpers}; -pub use self::rule_parser::{InsertRuleContext, State, TopLevelRuleParser}; -pub use self::rules_iterator::{AllRules, EffectiveRules}; -pub use self::rules_iterator::{ - EffectiveRulesIterator, NestedRuleIterationCondition, RulesIterator, -}; -pub use self::style_rule::StyleRule; -pub use self::stylesheet::{AllowImportRules, SanitizationData, SanitizationKind}; -pub use self::stylesheet::{DocumentStyleSheet, Namespaces, Stylesheet}; -pub use self::stylesheet::{StylesheetContents, StylesheetInDocument, UserAgentStylesheets}; -pub use self::supports_rule::SupportsRule; - -/// The CORS mode used for a CSS load. -#[repr(u8)] -#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)] -pub enum CorsMode { - /// No CORS mode, so cross-origin loads can be done. - None, - /// Anonymous CORS request. - Anonymous, -} - -/// Extra data that the backend may need to resolve url values. -/// -/// If the usize's lowest bit is 0, then this is a strong reference to a -/// structs::URLExtraData object. -/// -/// Otherwise, shifting the usize's bits the right by one gives the -/// UserAgentStyleSheetID value corresponding to the style sheet whose -/// URLExtraData this is, which is stored in URLExtraData_sShared. We don't -/// hold a strong reference to that object from here, but we rely on that -/// array's objects being held alive until shutdown. -/// -/// We use this packed representation rather than an enum so that -/// `from_ptr_ref` can work. -#[cfg(feature = "gecko")] -#[derive(PartialEq)] -#[repr(C)] -pub struct UrlExtraData(usize); - -/// Extra data that the backend may need to resolve url values. -#[cfg(feature = "servo")] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct UrlExtraData(pub Arc<::url::Url>); - -#[cfg(feature = "servo")] -impl UrlExtraData { - /// True if this URL scheme is chrome. - pub fn chrome_rules_enabled(&self) -> bool { - self.0.scheme() == "chrome" - } - - /// Get the interior Url as a string. - pub fn as_str(&self) -> &str { - self.0.as_str() - } -} - -#[cfg(feature = "servo")] -impl From<::url::Url> for UrlExtraData { - fn from(url: ::url::Url) -> Self { - Self(Arc::new(url)) - } -} - -#[cfg(not(feature = "gecko"))] -impl ToShmem for UrlExtraData { - fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> { - unimplemented!("If servo wants to share stylesheets across processes, ToShmem for Url must be implemented"); - } -} - -#[cfg(feature = "gecko")] -impl Clone for UrlExtraData { - fn clone(&self) -> UrlExtraData { - UrlExtraData::new(self.ptr()) - } -} - -#[cfg(feature = "gecko")] -impl Drop for UrlExtraData { - fn drop(&mut self) { - // No need to release when we have an index into URLExtraData_sShared. - if self.0 & 1 == 0 { - unsafe { - self.as_ref().release(); - } - } - } -} - -#[cfg(feature = "gecko")] -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 self_ptr = self.as_ref() as *const _ as *mut _; - let sheet_id = shared_extra_datas - .iter() - .position(|r| r.mRawPtr == self_ptr); - let sheet_id = match sheet_id { - Some(id) => id, - None => { - return Err(String::from( - "ToShmem failed for UrlExtraData: expected sheet's URLExtraData to be in \ - URLExtraData::sShared", - )); - }, - }; - Ok(ManuallyDrop::new(UrlExtraData((sheet_id << 1) | 1))) - } else { - Ok(ManuallyDrop::new(UrlExtraData(self.0))) - } - } -} - -#[cfg(feature = "gecko")] -impl UrlExtraData { - /// Create a new UrlExtraData wrapping a pointer to the specified Gecko - /// URLExtraData object. - pub fn new(ptr: *mut structs::URLExtraData) -> UrlExtraData { - unsafe { - (*ptr).addref(); - } - UrlExtraData(ptr as usize) - } - - /// True if this URL scheme is chrome. - #[inline] - pub fn chrome_rules_enabled(&self) -> bool { - self.as_ref().mChromeRulesEnabled - } - - /// Create a reference to this `UrlExtraData` from a reference to pointer. - /// - /// The pointer must be valid and non null. - /// - /// This method doesn't touch refcount. - #[inline] - pub unsafe fn from_ptr_ref(ptr: &*mut structs::URLExtraData) -> &Self { - mem::transmute(ptr) - } - - /// Returns a pointer to the Gecko URLExtraData object. - pub fn ptr(&self) -> *mut structs::URLExtraData { - if self.0 & 1 == 0 { - self.0 as *mut structs::URLExtraData - } else { - unsafe { - let sheet_id = self.0 >> 1; - structs::URLExtraData_sShared[sheet_id].mRawPtr - } - } - } - - fn as_ref(&self) -> &structs::URLExtraData { - unsafe { &*(self.ptr() as *const structs::URLExtraData) } - } -} - -#[cfg(feature = "gecko")] -impl fmt::Debug for UrlExtraData { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - macro_rules! define_debug_struct { - ($struct_name:ident, $gecko_class:ident, $debug_fn:ident) => { - struct $struct_name(*mut structs::$gecko_class); - impl fmt::Debug for $struct_name { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - use nsstring::nsCString; - let mut spec = nsCString::new(); - unsafe { - bindings::$debug_fn(self.0, &mut spec); - } - spec.fmt(formatter) - } - } - }; - } - - define_debug_struct!(DebugURI, nsIURI, Gecko_nsIURI_Debug); - define_debug_struct!( - DebugReferrerInfo, - nsIReferrerInfo, - Gecko_nsIReferrerInfo_Debug - ); - - formatter - .debug_struct("URLExtraData") - .field("chrome_rules_enabled", &self.chrome_rules_enabled()) - .field("base", &DebugURI(self.as_ref().mBaseURI.raw())) - .field( - "referrer", - &DebugReferrerInfo(self.as_ref().mReferrerInfo.raw()), - ) - .finish() - } -} - -// XXX We probably need to figure out whether we should mark Eq here. -// It is currently marked so because properties::UnparsedValue wants Eq. -#[cfg(feature = "gecko")] -impl Eq for UrlExtraData {} - -/// A CSS rule. -/// -/// TODO(emilio): Lots of spec links should be around. -#[derive(Clone, Debug, ToShmem)] -#[allow(missing_docs)] -pub enum CssRule { - // No Charset here, CSSCharsetRule has been removed from CSSOM - // https://drafts.csswg.org/cssom/#changes-from-5-december-2013 - Namespace(Arc<NamespaceRule>), - Import(Arc<Locked<ImportRule>>), - Style(Arc<Locked<StyleRule>>), - Media(Arc<MediaRule>), - Container(Arc<ContainerRule>), - FontFace(Arc<Locked<FontFaceRule>>), - FontFeatureValues(Arc<FontFeatureValuesRule>), - FontPaletteValues(Arc<FontPaletteValuesRule>), - CounterStyle(Arc<Locked<CounterStyleRule>>), - Keyframes(Arc<Locked<KeyframesRule>>), - Supports(Arc<SupportsRule>), - Page(Arc<Locked<PageRule>>), - Property(Arc<PropertyRule>), - Document(Arc<DocumentRule>), - LayerBlock(Arc<LayerBlockRule>), - LayerStatement(Arc<LayerStatementRule>), -} - -impl CssRule { - /// Measure heap usage. - #[cfg(feature = "gecko")] - fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { - match *self { - // Not all fields are currently fully measured. Extra measurement - // may be added later. - CssRule::Namespace(_) => 0, - - // We don't need to measure ImportRule::stylesheet because we measure - // it on the C++ side in the child list of the ServoStyleSheet. - CssRule::Import(_) => 0, - - CssRule::Style(ref lock) => { - lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) - }, - CssRule::Media(ref arc) => { - arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops) - }, - CssRule::Container(ref arc) => { - arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops) - }, - CssRule::FontFace(_) => 0, - CssRule::FontFeatureValues(_) => 0, - CssRule::FontPaletteValues(_) => 0, - CssRule::CounterStyle(_) => 0, - CssRule::Keyframes(_) => 0, - CssRule::Supports(ref arc) => { - arc.unconditional_shallow_size_of(ops) + arc.size_of(guard, ops) - }, - CssRule::Page(ref lock) => { - lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) - }, - CssRule::Property(ref rule) => { - rule.unconditional_shallow_size_of(ops) + rule.size_of(guard, ops) - }, - CssRule::Document(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, - } - } -} - -/// https://drafts.csswg.org/cssom-1/#dom-cssrule-type -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, Eq, FromPrimitive, PartialEq)] -#[repr(u8)] -pub enum CssRuleType { - // https://drafts.csswg.org/cssom/#the-cssrule-interface - Style = 1, - // Charset = 2, // Historical - Import = 3, - Media = 4, - FontFace = 5, - Page = 6, - // https://drafts.csswg.org/css-animations-1/#interface-cssrule-idl - Keyframes = 7, - Keyframe = 8, - // https://drafts.csswg.org/cssom/#the-cssrule-interface - // Margin = 9, // Not implemented yet. - Namespace = 10, - // https://drafts.csswg.org/css-counter-styles-3/#extentions-to-cssrule-interface - CounterStyle = 11, - // https://drafts.csswg.org/css-conditional-3/#extentions-to-cssrule-interface - Supports = 12, - // https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#extentions-to-cssrule-interface - Document = 13, - // https://drafts.csswg.org/css-fonts/#om-fontfeaturevalues - FontFeatureValues = 14, - // After viewport, all rules should return 0 from the API, but we still need - // a constant somewhere. - LayerBlock = 16, - LayerStatement = 17, - Container = 18, - FontPaletteValues = 19, - // 20 is an arbitrary number to use for Property. - Property = 20, -} - -impl CssRuleType { - /// Returns a bit that identifies this rule type. - #[inline] - pub const fn bit(self) -> u32 { - 1 << self as u32 - } -} - -/// Set of rule types. -#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -pub struct CssRuleTypes(u32); - -impl From<CssRuleType> for CssRuleTypes { - fn from(ty: CssRuleType) -> Self { - Self(ty.bit()) - } -} - -impl CssRuleTypes { - /// Returns whether the rule is in the current set. - #[inline] - pub fn contains(self, ty: CssRuleType) -> bool { - self.0 & ty.bit() != 0 - } - - /// Returns all the rules specified in the set. - pub fn bits(self) -> u32 { - self.0 - } - - /// Inserts a rule type into the set. - #[inline] - pub fn insert(&mut self, ty: CssRuleType) { - self.0 |= ty.bit() - } -} - -#[allow(missing_docs)] -pub enum RulesMutateError { - Syntax, - IndexSize, - HierarchyRequest, - InvalidState, -} - -impl CssRule { - /// Returns the CSSOM rule type of this rule. - pub fn rule_type(&self) -> CssRuleType { - match *self { - CssRule::Style(_) => CssRuleType::Style, - CssRule::Import(_) => CssRuleType::Import, - CssRule::Media(_) => CssRuleType::Media, - CssRule::FontFace(_) => CssRuleType::FontFace, - CssRule::FontFeatureValues(_) => CssRuleType::FontFeatureValues, - CssRule::FontPaletteValues(_) => CssRuleType::FontPaletteValues, - CssRule::CounterStyle(_) => CssRuleType::CounterStyle, - CssRule::Keyframes(_) => CssRuleType::Keyframes, - CssRule::Namespace(_) => CssRuleType::Namespace, - CssRule::Supports(_) => CssRuleType::Supports, - CssRule::Page(_) => CssRuleType::Page, - CssRule::Property(_) => CssRuleType::Property, - CssRule::Document(_) => CssRuleType::Document, - CssRule::LayerBlock(_) => CssRuleType::LayerBlock, - CssRule::LayerStatement(_) => CssRuleType::LayerStatement, - CssRule::Container(_) => CssRuleType::Container, - } - } - - /// Parse a CSS rule. - /// - /// Returns a parsed CSS rule and the final state of the parser. - /// - /// Input state is None for a nested rule - pub fn parse( - css: &str, - insert_rule_context: InsertRuleContext, - parent_stylesheet_contents: &StylesheetContents, - shared_lock: &SharedRwLock, - state: State, - loader: Option<&dyn StylesheetLoader>, - allow_import_rules: AllowImportRules, - ) -> Result<Self, RulesMutateError> { - let url_data = parent_stylesheet_contents.url_data.read(); - let namespaces = parent_stylesheet_contents.namespaces.read(); - let context = ParserContext::new( - parent_stylesheet_contents.origin, - &url_data, - None, - ParsingMode::DEFAULT, - parent_stylesheet_contents.quirks_mode, - Cow::Borrowed(&*namespaces), - None, - None, - ); - - let mut input = ParserInput::new(css); - let mut input = Parser::new(&mut input); - - // nested rules are in the body state - let mut rule_parser = TopLevelRuleParser { - context, - shared_lock: &shared_lock, - loader, - state, - dom_error: None, - insert_rule_context: Some(insert_rule_context), - allow_import_rules, - declaration_parser_state: Default::default(), - rules: Default::default(), - }; - - match parse_one_rule(&mut input, &mut rule_parser) { - Ok(_) => Ok(rule_parser.rules.pop().unwrap()), - Err(_) => Err(rule_parser.dom_error.unwrap_or(RulesMutateError::Syntax)), - } - } -} - -impl DeepCloneWithLock for CssRule { - /// Deep clones this CssRule. - fn deep_clone_with_lock( - &self, - lock: &SharedRwLock, - guard: &SharedRwLockReadGuard, - params: &DeepCloneParams, - ) -> CssRule { - match *self { - CssRule::Namespace(ref arc) => CssRule::Namespace(arc.clone()), - CssRule::Import(ref arc) => { - let rule = arc - .read_with(guard) - .deep_clone_with_lock(lock, guard, params); - CssRule::Import(Arc::new(lock.wrap(rule))) - }, - CssRule::Style(ref arc) => { - let rule = arc.read_with(guard); - CssRule::Style(Arc::new( - lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), - )) - }, - CssRule::Container(ref arc) => { - CssRule::Container(Arc::new(arc.deep_clone_with_lock(lock, guard, params))) - }, - CssRule::Media(ref arc) => { - CssRule::Media(Arc::new(arc.deep_clone_with_lock(lock, guard, params))) - }, - CssRule::FontFace(ref arc) => { - let rule = arc.read_with(guard); - CssRule::FontFace(Arc::new(lock.wrap(rule.clone()))) - }, - CssRule::FontFeatureValues(ref arc) => CssRule::FontFeatureValues(arc.clone()), - CssRule::FontPaletteValues(ref arc) => CssRule::FontPaletteValues(arc.clone()), - CssRule::CounterStyle(ref arc) => { - let rule = arc.read_with(guard); - CssRule::CounterStyle(Arc::new(lock.wrap(rule.clone()))) - }, - CssRule::Keyframes(ref arc) => { - let rule = arc.read_with(guard); - CssRule::Keyframes(Arc::new( - lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), - )) - }, - CssRule::Supports(ref arc) => { - CssRule::Supports(Arc::new(arc.deep_clone_with_lock(lock, guard, params))) - }, - CssRule::Page(ref arc) => { - let rule = arc.read_with(guard); - CssRule::Page(Arc::new( - lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), - )) - }, - CssRule::Property(ref arc) => { - // @property rules are immutable, so we don't need any of the `Locked` - // shenanigans, actually, and can just share the rule. - CssRule::Property(arc.clone()) - }, - CssRule::Document(ref arc) => { - CssRule::Document(Arc::new(arc.deep_clone_with_lock(lock, guard, params))) - }, - CssRule::LayerStatement(ref arc) => CssRule::LayerStatement(arc.clone()), - CssRule::LayerBlock(ref arc) => { - CssRule::LayerBlock(Arc::new(arc.deep_clone_with_lock(lock, guard, params))) - }, - } - } -} - -impl ToCssWithGuard for CssRule { - // https://drafts.csswg.org/cssom/#serialize-a-css-rule - fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { - match *self { - CssRule::Namespace(ref rule) => rule.to_css(guard, dest), - CssRule::Import(ref lock) => lock.read_with(guard).to_css(guard, dest), - CssRule::Style(ref lock) => lock.read_with(guard).to_css(guard, dest), - CssRule::FontFace(ref lock) => lock.read_with(guard).to_css(guard, dest), - CssRule::FontFeatureValues(ref rule) => rule.to_css(guard, dest), - CssRule::FontPaletteValues(ref rule) => rule.to_css(guard, dest), - CssRule::CounterStyle(ref lock) => lock.read_with(guard).to_css(guard, dest), - CssRule::Keyframes(ref lock) => lock.read_with(guard).to_css(guard, dest), - CssRule::Media(ref rule) => rule.to_css(guard, dest), - CssRule::Supports(ref rule) => rule.to_css(guard, dest), - CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest), - CssRule::Property(ref rule) => rule.to_css(guard, dest), - CssRule::Document(ref rule) => rule.to_css(guard, dest), - 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), - } - } -} diff --git a/components/style/stylesheets/namespace_rule.rs b/components/style/stylesheets/namespace_rule.rs deleted file mode 100644 index ad980b70a8b..00000000000 --- a/components/style/stylesheets/namespace_rule.rs +++ /dev/null @@ -1,43 +0,0 @@ -/* 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/. */ - -//! The `@namespace` at-rule. - -use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; -use crate::str::CssStringWriter; -use crate::{Namespace, Prefix}; -use cssparser::SourceLocation; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ToCss}; - -/// A `@namespace` rule. -#[derive(Clone, Debug, PartialEq, ToShmem)] -#[allow(missing_docs)] -pub struct NamespaceRule { - /// The namespace prefix, and `None` if it's the default Namespace - pub prefix: Option<Prefix>, - /// The actual namespace url. - pub url: Namespace, - /// The source location this rule was found at. - pub source_location: SourceLocation, -} - -impl ToCssWithGuard for NamespaceRule { - // https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSNamespaceRule - fn to_css( - &self, - _guard: &SharedRwLockReadGuard, - dest_str: &mut CssStringWriter, - ) -> fmt::Result { - let mut dest = CssWriter::new(dest_str); - dest.write_str("@namespace ")?; - if let Some(ref prefix) = self.prefix { - prefix.to_css(&mut dest)?; - dest.write_char(' ')?; - } - dest.write_str("url(")?; - self.url.to_string().to_css(&mut dest)?; - dest.write_str(");") - } -} diff --git a/components/style/stylesheets/origin.rs b/components/style/stylesheets/origin.rs deleted file mode 100644 index 27ad3fa184a..00000000000 --- a/components/style/stylesheets/origin.rs +++ /dev/null @@ -1,247 +0,0 @@ -/* 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/. */ - -//! [CSS cascade origins](https://drafts.csswg.org/css-cascade/#cascading-origins). - -use std::marker::PhantomData; -use std::ops::BitOrAssign; - -/// Each style rule has an origin, which determines where it enters the cascade. -/// -/// <https://drafts.csswg.org/css-cascade/#cascading-origins> -#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem, PartialOrd, Ord)] -#[repr(u8)] -pub enum Origin { - /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent> - UserAgent = 0x1, - - /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user> - User = 0x2, - - /// <https://drafts.csswg.org/css-cascade/#cascade-origin-author> - Author = 0x4, -} - -impl Origin { - /// Returns an origin that goes in order for `index`. - /// - /// This is used for iterating across origins. - fn from_index(index: i8) -> Option<Self> { - Some(match index { - 0 => Origin::Author, - 1 => Origin::User, - 2 => Origin::UserAgent, - _ => return None, - }) - } - - fn to_index(self) -> i8 { - match self { - Origin::Author => 0, - Origin::User => 1, - Origin::UserAgent => 2, - } - } - - /// Returns an iterator from this origin, towards all the less specific - /// origins. So for `UserAgent`, it'd iterate through all origins. - #[inline] - pub fn following_including(self) -> OriginSetIterator { - OriginSetIterator { - set: OriginSet::ORIGIN_USER | OriginSet::ORIGIN_AUTHOR | OriginSet::ORIGIN_USER_AGENT, - cur: self.to_index(), - rev: true, - } - } -} - -bitflags! { - /// A set of origins. This is equivalent to Gecko's OriginFlags. - #[derive(MallocSizeOf)] - pub struct OriginSet: u8 { - /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user-agent> - const ORIGIN_USER_AGENT = Origin::UserAgent as u8; - /// <https://drafts.csswg.org/css-cascade/#cascade-origin-user> - const ORIGIN_USER = Origin::User as u8; - /// <https://drafts.csswg.org/css-cascade/#cascade-origin-author> - const ORIGIN_AUTHOR = Origin::Author as u8; - } -} - -impl OriginSet { - /// Returns an iterator over the origins present in this `OriginSet`. - /// - /// See the `OriginSet` documentation for information about the order - /// origins are iterated. - pub fn iter(&self) -> OriginSetIterator { - OriginSetIterator { - set: *self, - cur: 0, - rev: false, - } - } -} - -impl From<Origin> for OriginSet { - fn from(origin: Origin) -> Self { - Self::from_bits_truncate(origin as u8) - } -} - -impl BitOrAssign<Origin> for OriginSet { - fn bitor_assign(&mut self, origin: Origin) { - *self |= OriginSet::from(origin); - } -} - -/// Iterates over the origins present in an `OriginSet`, in order from -/// highest priority (author) to lower (user agent). -#[derive(Clone)] -pub struct OriginSetIterator { - set: OriginSet, - cur: i8, - rev: bool, -} - -impl Iterator for OriginSetIterator { - type Item = Origin; - - fn next(&mut self) -> Option<Origin> { - loop { - let origin = Origin::from_index(self.cur)?; - - if self.rev { - self.cur -= 1; - } else { - self.cur += 1; - } - - if self.set.contains(origin.into()) { - return Some(origin); - } - } - } -} - -/// An object that stores a `T` for each origin of the CSS cascade. -#[derive(Debug, Default, MallocSizeOf)] -pub struct PerOrigin<T> { - /// Data for `Origin::UserAgent`. - pub user_agent: T, - - /// Data for `Origin::User`. - pub user: T, - - /// Data for `Origin::Author`. - pub author: T, -} - -impl<T> PerOrigin<T> { - /// Returns a reference to the per-origin data for the specified origin. - #[inline] - pub fn borrow_for_origin(&self, origin: &Origin) -> &T { - match *origin { - Origin::UserAgent => &self.user_agent, - Origin::User => &self.user, - Origin::Author => &self.author, - } - } - - /// Returns a mutable reference to the per-origin data for the specified - /// origin. - #[inline] - pub fn borrow_mut_for_origin(&mut self, origin: &Origin) -> &mut T { - match *origin { - Origin::UserAgent => &mut self.user_agent, - Origin::User => &mut self.user, - Origin::Author => &mut self.author, - } - } - - /// Iterates over references to per-origin extra style data, from highest - /// level (author) to lowest (user agent). - pub fn iter_origins(&self) -> PerOriginIter<T> { - PerOriginIter { - data: &self, - cur: 0, - rev: false, - } - } - - /// Iterates over references to per-origin extra style data, from lowest - /// level (user agent) to highest (author). - pub fn iter_origins_rev(&self) -> PerOriginIter<T> { - PerOriginIter { - data: &self, - cur: 2, - rev: true, - } - } - - /// Iterates over mutable references to per-origin extra style data, from - /// highest level (author) to lowest (user agent). - pub fn iter_mut_origins(&mut self) -> PerOriginIterMut<T> { - PerOriginIterMut { - data: self, - cur: 0, - _marker: PhantomData, - } - } -} - -/// Iterator over `PerOrigin<T>`, from highest level (author) to lowest -/// (user agent). -/// -/// We rely on this specific order for correctly looking up @font-face, -/// @counter-style and @keyframes rules. -pub struct PerOriginIter<'a, T: 'a> { - data: &'a PerOrigin<T>, - cur: i8, - rev: bool, -} - -impl<'a, T> Iterator for PerOriginIter<'a, T> -where - T: 'a, -{ - type Item = (&'a T, Origin); - - fn next(&mut self) -> Option<Self::Item> { - let origin = Origin::from_index(self.cur)?; - - self.cur += if self.rev { -1 } else { 1 }; - - Some((self.data.borrow_for_origin(&origin), origin)) - } -} - -/// Like `PerOriginIter<T>`, but iterates over mutable references to the -/// per-origin data. -/// -/// We must use unsafe code here since it's not possible for the borrow -/// checker to know that we are safely returning a different reference -/// each time from `next()`. -pub struct PerOriginIterMut<'a, T: 'a> { - data: *mut PerOrigin<T>, - cur: i8, - _marker: PhantomData<&'a mut PerOrigin<T>>, -} - -impl<'a, T> Iterator for PerOriginIterMut<'a, T> -where - T: 'a, -{ - type Item = (&'a mut T, Origin); - - fn next(&mut self) -> Option<Self::Item> { - let origin = Origin::from_index(self.cur)?; - - self.cur += 1; - - Some(( - unsafe { (*self.data).borrow_mut_for_origin(&origin) }, - origin, - )) - } -} diff --git a/components/style/stylesheets/page_rule.rs b/components/style/stylesheets/page_rule.rs deleted file mode 100644 index 5cd2458aa25..00000000000 --- a/components/style/stylesheets/page_rule.rs +++ /dev/null @@ -1,145 +0,0 @@ -/* 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 [`@page`][page] rule. -//! -//! [page]: https://drafts.csswg.org/css2/page.html#page-box - -use crate::parser::{Parse, ParserContext}; -use crate::properties::PropertyDeclarationBlock; -use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked}; -use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; -use crate::str::CssStringWriter; -use crate::values::{AtomIdent, CustomIdent}; -use cssparser::{Parser, SourceLocation}; -#[cfg(feature = "gecko")] -use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; -use servo_arc::Arc; -use std::fmt::{self, Write}; -use style_traits::{CssWriter, ParseError, ToCss}; - -/// Type of a single [`@page`][page selector] -/// -/// We do not support pseudo selectors yet. -/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors -#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] -pub struct PageSelector(pub AtomIdent); - -impl PageSelector { - /// Checks if the ident matches a page-name's ident. - /// - /// This does not currently take pseudo selectors into account. - #[inline] - pub fn ident_matches(&self, other: &CustomIdent) -> bool { - self.0 .0 == other.0 - } -} - -impl Parse for PageSelector { - fn parse<'i, 't>( - _context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result<Self, ParseError<'i>> { - let s = input.expect_ident()?; - Ok(PageSelector(AtomIdent::from(&**s))) - } -} - -/// A list of [`@page`][page selectors] -/// -/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors -#[derive(Clone, Debug, Default, MallocSizeOf, ToCss, ToShmem)] -#[css(comma)] -pub struct PageSelectors(#[css(iterable)] pub Box<[PageSelector]>); - -impl PageSelectors { - /// Creates a new PageSelectors from a Vec, as from parse_comma_separated - #[inline] - pub fn new(s: Vec<PageSelector>) -> Self { - PageSelectors(s.into()) - } - /// Returns true iff there are any page selectors - #[inline] - pub fn is_empty(&self) -> bool { - self.as_slice().is_empty() - } - /// Get the underlying PageSelector data as a slice - #[inline] - pub fn as_slice(&self) -> &[PageSelector] { - &*self.0 - } -} - -impl Parse for PageSelectors { - fn parse<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result<Self, ParseError<'i>> { - Ok(PageSelectors::new(input.parse_comma_separated(|i| { - PageSelector::parse(context, i) - })?)) - } -} - -/// A [`@page`][page] rule. -/// -/// This implements only a limited subset of the CSS -/// 2.2 syntax. -/// -/// [page]: https://drafts.csswg.org/css2/page.html#page-box -/// [page-selectors]: https://drafts.csswg.org/css2/page.html#page-selectors -#[derive(Clone, Debug, ToShmem)] -pub struct PageRule { - /// Selectors of the page-rule - pub selectors: PageSelectors, - /// The declaration block this page rule contains. - pub block: Arc<Locked<PropertyDeclarationBlock>>, - /// The source position this rule was found at. - pub source_location: SourceLocation, -} - -impl PageRule { - /// Measure heap usage. - #[cfg(feature = "gecko")] - pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { - // Measurement of other fields may be added later. - self.block.unconditional_shallow_size_of(ops) + - self.block.read_with(guard).size_of(ops) + - self.selectors.size_of(ops) - } -} - -impl ToCssWithGuard for PageRule { - /// Serialization of PageRule is not specced, adapted from steps for - /// StyleRule. - fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { - dest.write_str("@page ")?; - if !self.selectors.is_empty() { - self.selectors.to_css(&mut CssWriter::new(dest))?; - dest.write_char(' ')?; - } - dest.write_str("{ ")?; - let declaration_block = self.block.read_with(guard); - declaration_block.to_css(dest)?; - if !declaration_block.declarations().is_empty() { - dest.write_char(' ')?; - } - dest.write_char('}') - } -} - -impl DeepCloneWithLock for PageRule { - fn deep_clone_with_lock( - &self, - lock: &SharedRwLock, - guard: &SharedRwLockReadGuard, - _params: &DeepCloneParams, - ) -> Self { - PageRule { - selectors: self.selectors.clone(), - block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())), - source_location: self.source_location.clone(), - } - } -} diff --git a/components/style/stylesheets/property_rule.rs b/components/style/stylesheets/property_rule.rs deleted file mode 100644 index 1d1c1c982e9..00000000000 --- a/components/style/stylesheets/property_rule.rs +++ /dev/null @@ -1,5 +0,0 @@ -/* 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/. */ - -pub use crate::properties_and_values::rule::PropertyRuleData as PropertyRule; diff --git a/components/style/stylesheets/rule_list.rs b/components/style/stylesheets/rule_list.rs deleted file mode 100644 index ab747565ffe..00000000000 --- a/components/style/stylesheets/rule_list.rs +++ /dev/null @@ -1,198 +0,0 @@ -/* 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 list of CSS rules. - -use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked}; -use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; -use crate::str::CssStringWriter; -use crate::stylesheets::loader::StylesheetLoader; -use crate::stylesheets::rule_parser::{InsertRuleContext, State}; -use crate::stylesheets::stylesheet::StylesheetContents; -use crate::stylesheets::{AllowImportRules, CssRule, RulesMutateError}; -#[cfg(feature = "gecko")] -use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps}; -use servo_arc::Arc; -use std::fmt::{self, Write}; - -/// A list of CSS rules. -#[derive(Debug, ToShmem)] -pub struct CssRules(pub Vec<CssRule>); - -impl CssRules { - /// Whether this CSS rules is empty. - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } -} - -impl DeepCloneWithLock for CssRules { - fn deep_clone_with_lock( - &self, - lock: &SharedRwLock, - guard: &SharedRwLockReadGuard, - params: &DeepCloneParams, - ) -> Self { - CssRules( - self.0 - .iter() - .map(|x| x.deep_clone_with_lock(lock, guard, params)) - .collect(), - ) - } -} - -impl CssRules { - /// Measure heap usage. - #[cfg(feature = "gecko")] - pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { - let mut n = self.0.shallow_size_of(ops); - for rule in self.0.iter() { - n += rule.size_of(guard, ops); - } - n - } - - /// Trivially construct a new set of CSS rules. - pub fn new(rules: Vec<CssRule>, shared_lock: &SharedRwLock) -> Arc<Locked<CssRules>> { - Arc::new(shared_lock.wrap(CssRules(rules))) - } - - /// Returns whether all the rules in this list are namespace or import - /// rules. - fn only_ns_or_import(&self) -> bool { - self.0.iter().all(|r| match *r { - CssRule::Namespace(..) | CssRule::Import(..) => true, - _ => false, - }) - } - - /// <https://drafts.csswg.org/cssom/#remove-a-css-rule> - pub fn remove_rule(&mut self, index: usize) -> Result<(), RulesMutateError> { - // Step 1, 2 - if index >= self.0.len() { - return Err(RulesMutateError::IndexSize); - } - - { - // Step 3 - let ref rule = self.0[index]; - - // Step 4 - if let CssRule::Namespace(..) = *rule { - if !self.only_ns_or_import() { - return Err(RulesMutateError::InvalidState); - } - } - } - - // Step 5, 6 - self.0.remove(index); - Ok(()) - } - - /// Serializes this CSSRules to CSS text as a block of rules. - /// - /// This should be speced into CSSOM spec at some point. See - /// <https://github.com/w3c/csswg-drafts/issues/1985> - pub fn to_css_block( - &self, - guard: &SharedRwLockReadGuard, - dest: &mut CssStringWriter, - ) -> fmt::Result { - dest.write_str(" {")?; - self.to_css_block_without_opening(guard, dest) - } - - /// As above, but without the opening curly bracket. That's needed for nesting. - pub fn to_css_block_without_opening( - &self, - guard: &SharedRwLockReadGuard, - dest: &mut CssStringWriter, - ) -> fmt::Result { - for rule in self.0.iter() { - dest.write_str("\n ")?; - rule.to_css(guard, dest)?; - } - dest.write_str("\n}") - } -} - -/// A trait to implement helpers for `Arc<Locked<CssRules>>`. -pub trait CssRulesHelpers { - /// <https://drafts.csswg.org/cssom/#insert-a-css-rule> - /// - /// Written in this funky way because parsing an @import rule may cause us - /// to clone a stylesheet from the same document due to caching in the CSS - /// loader. - /// - /// TODO(emilio): We could also pass the write guard down into the loader - /// instead, but that seems overkill. - fn insert_rule( - &self, - lock: &SharedRwLock, - rule: &str, - parent_stylesheet_contents: &StylesheetContents, - index: usize, - nested: bool, - loader: Option<&dyn StylesheetLoader>, - allow_import_rules: AllowImportRules, - ) -> Result<CssRule, RulesMutateError>; -} - -impl CssRulesHelpers for Locked<CssRules> { - fn insert_rule( - &self, - lock: &SharedRwLock, - rule: &str, - parent_stylesheet_contents: &StylesheetContents, - index: usize, - nested: bool, - loader: Option<&dyn StylesheetLoader>, - allow_import_rules: AllowImportRules, - ) -> Result<CssRule, RulesMutateError> { - let new_rule = { - let read_guard = lock.read(); - let rules = self.read_with(&read_guard); - - // Step 1, 2 - if index > rules.0.len() { - return Err(RulesMutateError::IndexSize); - } - - // Computes the parser state at the given index - let insert_rule_context = InsertRuleContext { - rule_list: &rules.0, - index, - }; - - let state = if nested { - State::Body - } else if index == 0 { - State::Start - } else { - insert_rule_context.max_rule_state_at_index(index - 1) - }; - - // Steps 3, 4, 5, 6 - CssRule::parse( - &rule, - insert_rule_context, - parent_stylesheet_contents, - lock, - state, - loader, - allow_import_rules, - )? - }; - - { - let mut write_guard = lock.write(); - let rules = self.write_with(&mut write_guard); - rules.0.insert(index, new_rule.clone()); - } - - Ok(new_rule) - } -} diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs deleted file mode 100644 index 71b27f2c943..00000000000 --- a/components/style/stylesheets/rule_parser.rs +++ /dev/null @@ -1,867 +0,0 @@ -/* 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/. */ - -//! Parsing of the stylesheet contents. - -use crate::counter_style::{parse_counter_style_body, parse_counter_style_name_definition}; -use crate::custom_properties::parse_name as parse_custom_property_name; -use crate::error_reporting::ContextualParseError; -use crate::font_face::parse_font_face_block; -use crate::media_queries::MediaList; -use crate::parser::{Parse, ParserContext}; -use crate::properties::declaration_block::{ - parse_property_declaration_list, DeclarationParserState, PropertyDeclarationBlock, -}; -use crate::properties_and_values::rule::{parse_property_block, PropertyRuleName}; -use crate::selector_parser::{SelectorImpl, SelectorParser}; -use crate::shared_lock::{Locked, SharedRwLock}; -use crate::str::starts_with_ignore_ascii_case; -use crate::stylesheets::container_rule::{ContainerCondition, ContainerRule}; -use crate::stylesheets::document_rule::DocumentCondition; -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::supports_rule::SupportsCondition; -use crate::stylesheets::{ - AllowImportRules, CorsMode, CssRule, CssRuleType, CssRules, DocumentRule, - FontFeatureValuesRule, FontPaletteValuesRule, KeyframesRule, MediaRule, NamespaceRule, - PageRule, PageSelectors, RulesMutateError, StyleRule, StylesheetLoader, SupportsRule, -}; -use crate::values::computed::font::FamilyName; -use crate::values::{CssUrl, CustomIdent, DashedIdent, KeyframesName}; -use crate::{Atom, Namespace, Prefix}; -use cssparser::{ - AtRuleParser, BasicParseError, BasicParseErrorKind, CowRcStr, DeclarationParser, Parser, - ParserState, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, - SourcePosition, -}; -use selectors::SelectorList; -use servo_arc::Arc; -use style_traits::{ParseError, StyleParseErrorKind}; - -/// The information we need particularly to do CSSOM insertRule stuff. -pub struct InsertRuleContext<'a> { - /// The rule list we're about to insert into. - pub rule_list: &'a [CssRule], - /// The index we're about to get inserted at. - pub index: usize, -} - -impl<'a> InsertRuleContext<'a> { - /// Returns the max rule state allowable for insertion at a given index in - /// the rule list. - pub fn max_rule_state_at_index(&self, index: usize) -> State { - let rule = match self.rule_list.get(index) { - Some(rule) => rule, - None => return State::Body, - }; - match rule { - CssRule::Import(..) => State::Imports, - CssRule::Namespace(..) => State::Namespaces, - CssRule::LayerStatement(..) => { - // If there are @import / @namespace after this layer, then - // we're in the early-layers phase, otherwise we're in the body - // and everything is fair game. - let next_non_layer_statement_rule = self.rule_list[index + 1..] - .iter() - .find(|r| !matches!(*r, CssRule::LayerStatement(..))); - if let Some(non_layer) = next_non_layer_statement_rule { - if matches!(*non_layer, CssRule::Import(..) | CssRule::Namespace(..)) { - return State::EarlyLayers; - } - } - State::Body - }, - _ => State::Body, - } - } -} - -/// The parser for the top-level rules in a stylesheet. -pub struct TopLevelRuleParser<'a, 'i> { - /// A reference to the lock we need to use to create rules. - pub shared_lock: &'a SharedRwLock, - /// A reference to a stylesheet loader if applicable, for `@import` rules. - pub loader: Option<&'a dyn StylesheetLoader>, - /// The top-level parser context. - pub context: ParserContext<'a>, - /// The current state of the parser. - pub state: State, - /// Whether we have tried to parse was invalid due to being in the wrong - /// place (e.g. an @import rule was found while in the `Body` state). Reset - /// to `false` when `take_had_hierarchy_error` is called. - pub dom_error: Option<RulesMutateError>, - /// The info we need insert a rule in a list. - pub insert_rule_context: Option<InsertRuleContext<'a>>, - /// Whether @import rules will be allowed. - pub allow_import_rules: AllowImportRules, - /// Parser state for declaration blocks in either nested rules or style rules. - pub declaration_parser_state: DeclarationParserState<'i>, - /// The rules we've parsed so far. - pub rules: Vec<CssRule>, -} - -impl<'a, 'i> TopLevelRuleParser<'a, 'i> { - fn nested<'b>(&'b mut self) -> NestedRuleParser<'b, 'a, 'i> { - NestedRuleParser { - shared_lock: self.shared_lock, - context: &mut self.context, - declaration_parser_state: &mut self.declaration_parser_state, - rules: &mut self.rules, - } - } - - /// Returns the current state of the parser. - pub fn state(&self) -> State { - self.state - } - - /// Checks whether we can parse a rule that would transition us to - /// `new_state`. - /// - /// This is usually a simple branch, but we may need more bookkeeping if - /// doing `insertRule` from CSSOM. - fn check_state(&mut self, new_state: State) -> bool { - if self.state > new_state { - self.dom_error = Some(RulesMutateError::HierarchyRequest); - return false; - } - - let ctx = match self.insert_rule_context { - Some(ref ctx) => ctx, - None => return true, - }; - - let max_rule_state = ctx.max_rule_state_at_index(ctx.index); - if new_state > max_rule_state { - self.dom_error = Some(RulesMutateError::HierarchyRequest); - return false; - } - - // If there's anything that isn't a namespace rule (or import rule, but - // we checked that already at the beginning), reject with a - // StateError. - if new_state == State::Namespaces && - ctx.rule_list[ctx.index..] - .iter() - .any(|r| !matches!(*r, CssRule::Namespace(..))) - { - self.dom_error = Some(RulesMutateError::InvalidState); - return false; - } - - true - } -} - -/// The current state of the parser. -#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] -pub enum State { - /// We haven't started parsing rules. - Start = 1, - /// We're parsing early `@layer` statement rules. - EarlyLayers = 2, - /// We're parsing `@import` and early `@layer` statement rules. - Imports = 3, - /// We're parsing `@namespace` rules. - Namespaces = 4, - /// We're parsing the main body of the stylesheet. - Body = 5, -} - -#[derive(Clone, Debug, MallocSizeOf, ToShmem)] -/// Vendor prefix. -pub enum VendorPrefix { - /// -moz prefix. - Moz, - /// -webkit prefix. - WebKit, -} - -/// A rule prelude for at-rule with block. -pub enum AtRulePrelude { - /// A @font-face rule prelude. - FontFace, - /// A @font-feature-values rule prelude, with its FamilyName list. - FontFeatureValues(Vec<FamilyName>), - /// A @font-palette-values rule prelude, with its identifier. - FontPaletteValues(DashedIdent), - /// A @counter-style rule prelude, with its counter style name. - CounterStyle(CustomIdent), - /// A @media rule prelude, with its media queries. - Media(Arc<Locked<MediaList>>), - /// A @container rule prelude. - Container(Arc<ContainerCondition>), - /// An @supports rule, with its conditional - Supports(SupportsCondition), - /// A @keyframes rule, with its animation name and vendor prefix if exists. - Keyframes(KeyframesName, Option<VendorPrefix>), - /// A @page rule prelude, with its page name if it exists. - Page(PageSelectors), - /// A @property rule prelude. - Property(PropertyRuleName), - /// A @document rule, with its conditional. - Document(DocumentCondition), - /// A @import rule prelude. - Import( - CssUrl, - Arc<Locked<MediaList>>, - Option<ImportSupportsCondition>, - ImportLayer, - ), - /// A @namespace rule prelude. - Namespace(Option<Prefix>, Namespace), - /// A @layer rule prelude. - Layer(Vec<LayerName>), -} - -impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a, 'i> { - type Prelude = AtRulePrelude; - type AtRule = SourcePosition; - type Error = StyleParseErrorKind<'i>; - - fn parse_prelude<'t>( - &mut self, - name: CowRcStr<'i>, - input: &mut Parser<'i, 't>, - ) -> Result<AtRulePrelude, ParseError<'i>> { - match_ignore_ascii_case! { &*name, - "import" => { - if !self.check_state(State::Imports) { - return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule)) - } - - if let AllowImportRules::No = self.allow_import_rules { - return Err(input.new_custom_error(StyleParseErrorKind::DisallowedImportRule)) - } - - // FIXME(emilio): We should always be able to have a loader - // around! See bug 1533783. - if self.loader.is_none() { - error!("Saw @import rule, but no way to trigger the load"); - return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule)) - } - - let url_string = input.expect_url_or_string()?.as_ref().to_owned(); - let url = CssUrl::parse_from_string(url_string, &self.context, CorsMode::None); - - let (layer, supports) = ImportRule::parse_layer_and_supports(input, &mut self.context); - - let media = MediaList::parse(&self.context, input); - let media = Arc::new(self.shared_lock.wrap(media)); - - return Ok(AtRulePrelude::Import(url, media, supports, layer)); - }, - "namespace" => { - if !self.check_state(State::Namespaces) { - return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedNamespaceRule)) - } - - let prefix = input.try_parse(|i| i.expect_ident_cloned()) - .map(|s| Prefix::from(s.as_ref())).ok(); - let maybe_namespace = match input.expect_url_or_string() { - Ok(url_or_string) => url_or_string, - Err(BasicParseError { kind: BasicParseErrorKind::UnexpectedToken(t), location }) => { - return Err(location.new_custom_error(StyleParseErrorKind::UnexpectedTokenWithinNamespace(t))) - } - Err(e) => return Err(e.into()), - }; - let url = Namespace::from(maybe_namespace.as_ref()); - return Ok(AtRulePrelude::Namespace(prefix, url)); - }, - // @charset is removed by rust-cssparser if it’s the first rule in the stylesheet - // anything left is invalid. - "charset" => { - self.dom_error = Some(RulesMutateError::HierarchyRequest); - return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedCharsetRule)) - }, - "layer" => { - let state_to_check = if self.state <= State::EarlyLayers { - // The real state depends on whether there's a block or not. - // We don't know that yet, but the parse_block check deals - // with that. - State::EarlyLayers - } else { - State::Body - }; - if !self.check_state(state_to_check) { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - }, - _ => { - // All other rules have blocks, so we do this check early in - // parse_block instead. - } - } - - AtRuleParser::parse_prelude(&mut self.nested(), name, input) - } - - #[inline] - fn parse_block<'t>( - &mut self, - prelude: AtRulePrelude, - start: &ParserState, - input: &mut Parser<'i, 't>, - ) -> Result<Self::AtRule, ParseError<'i>> { - if !self.check_state(State::Body) { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - AtRuleParser::parse_block(&mut self.nested(), prelude, start, input)?; - self.state = State::Body; - Ok(start.position()) - } - - #[inline] - fn rule_without_block( - &mut self, - prelude: AtRulePrelude, - start: &ParserState, - ) -> Result<Self::AtRule, ()> { - match prelude { - AtRulePrelude::Import(url, media, supports, layer) => { - let loader = self - .loader - .expect("Expected a stylesheet loader for @import"); - - let import_rule = loader.request_stylesheet( - url, - start.source_location(), - &self.context, - &self.shared_lock, - media, - supports, - layer, - ); - - self.state = State::Imports; - self.rules.push(CssRule::Import(import_rule)) - }, - AtRulePrelude::Namespace(prefix, url) => { - let namespaces = self.context.namespaces.to_mut(); - let prefix = if let Some(prefix) = prefix { - namespaces.prefixes.insert(prefix.clone(), url.clone()); - Some(prefix) - } else { - namespaces.default = Some(url.clone()); - None - }; - - self.state = State::Namespaces; - self.rules.push(CssRule::Namespace(Arc::new(NamespaceRule { - prefix, - url, - source_location: start.source_location(), - }))); - }, - AtRulePrelude::Layer(..) => { - AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)?; - if self.state <= State::EarlyLayers { - self.state = State::EarlyLayers; - } else { - self.state = State::Body; - } - }, - _ => AtRuleParser::rule_without_block(&mut self.nested(), prelude, start)?, - }; - - Ok(start.position()) - } -} - -impl<'a, 'i> QualifiedRuleParser<'i> for TopLevelRuleParser<'a, 'i> { - type Prelude = SelectorList<SelectorImpl>; - type QualifiedRule = SourcePosition; - type Error = StyleParseErrorKind<'i>; - - #[inline] - fn parse_prelude<'t>( - &mut self, - input: &mut Parser<'i, 't>, - ) -> Result<Self::Prelude, ParseError<'i>> { - if !self.check_state(State::Body) { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } - - QualifiedRuleParser::parse_prelude(&mut self.nested(), input) - } - - #[inline] - fn parse_block<'t>( - &mut self, - prelude: Self::Prelude, - start: &ParserState, - input: &mut Parser<'i, 't>, - ) -> Result<Self::QualifiedRule, ParseError<'i>> { - QualifiedRuleParser::parse_block(&mut self.nested(), prelude, start, input)?; - self.state = State::Body; - Ok(start.position()) - } -} - -struct NestedRuleParser<'a, 'b: 'a, 'i> { - shared_lock: &'a SharedRwLock, - context: &'a mut ParserContext<'b>, - declaration_parser_state: &'a mut DeclarationParserState<'i>, - rules: &'a mut Vec<CssRule>, -} - -struct NestedParseResult { - rules: Vec<CssRule>, - declarations: PropertyDeclarationBlock, -} - -impl NestedParseResult { - fn into_rules( - mut self, - shared_lock: &SharedRwLock, - source_location: SourceLocation, - ) -> Arc<Locked<CssRules>> { - lazy_static! { - static ref AMPERSAND: SelectorList<SelectorImpl> = { - let list = SelectorList::ampersand(); - list.0 - .iter() - .for_each(|selector| selector.mark_as_intentionally_leaked()); - list - }; - }; - - if !self.declarations.is_empty() { - self.rules.insert( - 0, - CssRule::Style(Arc::new(shared_lock.wrap(StyleRule { - selectors: AMPERSAND.clone(), - block: Arc::new(shared_lock.wrap(self.declarations)), - rules: None, - source_location, - }))), - ) - } - - CssRules::new(self.rules, shared_lock) - } -} - -impl<'a, 'b, 'i> NestedRuleParser<'a, 'b, 'i> { - /// When nesting is disabled, we prevent parsing at rules and qualified rules inside style - /// rules. - fn allow_at_and_qualified_rules(&self) -> bool { - if !self.context.rule_types.contains(CssRuleType::Style) { - return true; - } - static_prefs::pref!("layout.css.nesting.enabled") - } - - 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 r = cb(self); - self.context.rule_types = old_rule_types; - r - } - - fn parse_nested( - &mut self, - input: &mut Parser<'i, '_>, - rule_type: CssRuleType, - selectors: Option<&SelectorList<SelectorImpl>>, - ) -> NestedParseResult { - self.nest_for_rule(rule_type, |parser| { - let parse_declarations = parser.parse_declarations(); - let mut old_declaration_state = std::mem::take(parser.declaration_parser_state); - let mut rules = std::mem::take(parser.rules); - let mut iter = RuleBodyParser::new(input, parser); - while let Some(result) = iter.next() { - match result { - Ok(()) => {}, - Err((error, slice)) => { - if parse_declarations { - iter.parser.declaration_parser_state.did_error( - iter.parser.context, - error, - slice, - ); - } else { - let location = error.location; - let error = ContextualParseError::InvalidRule(slice, error); - iter.parser.context.log_css_error(location, error); - } - }, - } - } - let declarations = if parse_declarations { - parser - .declaration_parser_state - .report_errors_if_needed(parser.context, selectors); - parser.declaration_parser_state.take_declarations() - } else { - PropertyDeclarationBlock::default() - }; - debug_assert!( - !parser.declaration_parser_state.has_parsed_declarations(), - "Parsed but didn't consume declarations" - ); - std::mem::swap(parser.declaration_parser_state, &mut old_declaration_state); - std::mem::swap(parser.rules, &mut rules); - NestedParseResult { - rules, - declarations, - } - }) - } -} - -impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b, 'i> { - type Prelude = AtRulePrelude; - type AtRule = (); - type Error = StyleParseErrorKind<'i>; - - fn parse_prelude<'t>( - &mut self, - name: CowRcStr<'i>, - input: &mut Parser<'i, 't>, - ) -> Result<Self::Prelude, ParseError<'i>> { - if !self.allow_at_and_qualified_rules() { - return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name))); - } - Ok(match_ignore_ascii_case! { &*name, - "media" => { - let media_queries = MediaList::parse(self.context, input); - let arc = Arc::new(self.shared_lock.wrap(media_queries)); - AtRulePrelude::Media(arc) - }, - "supports" => { - let cond = SupportsCondition::parse(input)?; - AtRulePrelude::Supports(cond) - }, - "font-face" => { - AtRulePrelude::FontFace - }, - "container" if static_prefs::pref!("layout.css.container-queries.enabled") => { - let condition = Arc::new(ContainerCondition::parse(self.context, input)?); - AtRulePrelude::Container(condition) - }, - "layer" => { - let names = input.try_parse(|input| { - input.parse_comma_separated(|input| { - LayerName::parse(self.context, input) - }) - }).unwrap_or_default(); - AtRulePrelude::Layer(names) - }, - "font-feature-values" if cfg!(feature = "gecko") => { - let family_names = parse_family_name_list(self.context, input)?; - AtRulePrelude::FontFeatureValues(family_names) - }, - "font-palette-values" if static_prefs::pref!("layout.css.font-palette.enabled") => { - let name = DashedIdent::parse(self.context, input)?; - AtRulePrelude::FontPaletteValues(name) - }, - "counter-style" if cfg!(feature = "gecko") => { - let name = parse_counter_style_name_definition(input)?; - AtRulePrelude::CounterStyle(name) - }, - "keyframes" | "-webkit-keyframes" | "-moz-keyframes" => { - let prefix = if starts_with_ignore_ascii_case(&*name, "-webkit-") { - Some(VendorPrefix::WebKit) - } else if starts_with_ignore_ascii_case(&*name, "-moz-") { - Some(VendorPrefix::Moz) - } else { - None - }; - if cfg!(feature = "servo") && - prefix.as_ref().map_or(false, |p| matches!(*p, VendorPrefix::Moz)) { - // Servo should not support @-moz-keyframes. - return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone()))) - } - let name = KeyframesName::parse(self.context, input)?; - AtRulePrelude::Keyframes(name, prefix) - }, - "page" if cfg!(feature = "gecko") => { - AtRulePrelude::Page( - input.try_parse(|i| PageSelectors::parse(self.context, i)).unwrap_or_default() - ) - }, - "property" if static_prefs::pref!("layout.css.properties-and-values.enabled") => { - let name = input.expect_ident_cloned()?; - let name = parse_custom_property_name(&name).map_err(|_| { - input.new_custom_error(StyleParseErrorKind::UnexpectedIdent(name.clone())) - })?; - AtRulePrelude::Property(PropertyRuleName(Arc::new(Atom::from(name)))) - }, - "-moz-document" if cfg!(feature = "gecko") => { - let cond = DocumentCondition::parse(self.context, input)?; - AtRulePrelude::Document(cond) - }, - _ => return Err(input.new_error(BasicParseErrorKind::AtRuleInvalid(name.clone()))) - }) - } - - fn parse_block<'t>( - &mut self, - prelude: AtRulePrelude, - start: &ParserState, - input: &mut Parser<'i, 't>, - ) -> Result<(), ParseError<'i>> { - let rule = match prelude { - AtRulePrelude::FontFace => self.nest_for_rule(CssRuleType::FontFace, |p| { - CssRule::FontFace(Arc::new(p.shared_lock.wrap( - parse_font_face_block(&p.context, input, start.source_location()).into(), - ))) - }), - AtRulePrelude::FontFeatureValues(family_names) => { - self.nest_for_rule(CssRuleType::FontFeatureValues, |p| { - CssRule::FontFeatureValues(Arc::new(FontFeatureValuesRule::parse( - &p.context, - input, - family_names, - start.source_location(), - ))) - }) - }, - AtRulePrelude::FontPaletteValues(name) => { - self.nest_for_rule(CssRuleType::FontPaletteValues, |p| { - CssRule::FontPaletteValues(Arc::new(FontPaletteValuesRule::parse( - &p.context, - input, - name, - start.source_location(), - ))) - }) - }, - AtRulePrelude::CounterStyle(name) => { - let body = self.nest_for_rule(CssRuleType::CounterStyle, |p| { - parse_counter_style_body(name, &p.context, input, start.source_location()) - })?; - CssRule::CounterStyle(Arc::new(self.shared_lock.wrap(body))) - }, - AtRulePrelude::Media(media_queries) => { - let source_location = start.source_location(); - CssRule::Media(Arc::new(MediaRule { - media_queries, - rules: self - .parse_nested(input, CssRuleType::Media, None) - .into_rules(self.shared_lock, source_location), - source_location, - })) - }, - AtRulePrelude::Supports(condition) => { - let enabled = - self.nest_for_rule(CssRuleType::Style, |p| condition.eval(&p.context)); - let source_location = start.source_location(); - CssRule::Supports(Arc::new(SupportsRule { - condition, - rules: self - .parse_nested(input, CssRuleType::Supports, None) - .into_rules(self.shared_lock, source_location), - enabled, - source_location, - })) - }, - AtRulePrelude::Keyframes(name, vendor_prefix) => { - self.nest_for_rule(CssRuleType::Keyframe, |p| { - CssRule::Keyframes(Arc::new(p.shared_lock.wrap(KeyframesRule { - name, - keyframes: parse_keyframe_list(&mut p.context, input, p.shared_lock), - vendor_prefix, - source_location: start.source_location(), - }))) - }) - }, - AtRulePrelude::Page(selectors) => { - let declarations = self.nest_for_rule(CssRuleType::Page, |p| { - // TODO: Support nesting in @page rules? - parse_property_declaration_list(&p.context, input, None) - }); - CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule { - selectors, - block: Arc::new(self.shared_lock.wrap(declarations)), - source_location: start.source_location(), - }))) - }, - AtRulePrelude::Property(name) => self.nest_for_rule(CssRuleType::Property, |p| { - CssRule::Property(Arc::new(parse_property_block( - &p.context, - input, - name, - start.source_location(), - ))) - }), - AtRulePrelude::Document(condition) => { - if !cfg!(feature = "gecko") { - unreachable!() - } - let source_location = start.source_location(); - CssRule::Document(Arc::new(DocumentRule { - condition, - rules: self - .parse_nested(input, CssRuleType::Document, None) - .into_rules(self.shared_lock, source_location), - source_location, - })) - }, - AtRulePrelude::Container(condition) => { - let source_location = start.source_location(); - CssRule::Container(Arc::new(ContainerRule { - condition, - rules: self - .parse_nested(input, CssRuleType::Container, None) - .into_rules(self.shared_lock, source_location), - source_location, - })) - }, - AtRulePrelude::Layer(names) => { - let name = match names.len() { - 0 | 1 => names.into_iter().next(), - _ => return Err(input.new_error(BasicParseErrorKind::AtRuleBodyInvalid)), - }; - let source_location = start.source_location(); - CssRule::LayerBlock(Arc::new(LayerBlockRule { - name, - rules: self - .parse_nested(input, CssRuleType::LayerBlock, None) - .into_rules(self.shared_lock, source_location), - source_location, - })) - }, - AtRulePrelude::Import(..) | AtRulePrelude::Namespace(..) => { - // These rules don't have blocks. - return Err(input.new_unexpected_token_error(cssparser::Token::CurlyBracketBlock)); - }, - }; - self.rules.push(rule); - Ok(()) - } - - #[inline] - fn rule_without_block( - &mut self, - prelude: AtRulePrelude, - start: &ParserState, - ) -> Result<(), ()> { - let rule = match prelude { - AtRulePrelude::Layer(names) => { - if names.is_empty() { - return Err(()); - } - CssRule::LayerStatement(Arc::new(LayerStatementRule { - names, - source_location: start.source_location(), - })) - }, - _ => return Err(()), - }; - self.rules.push(rule); - Ok(()) - } -} - -#[inline(never)] -fn check_for_useless_selector( - input: &mut Parser, - context: &ParserContext, - selectors: &SelectorList<SelectorImpl>, -) { - use cssparser::ToCss; - - 'selector_loop: for selector in selectors.0.iter() { - let mut current = selector.iter(); - loop { - let mut found_host = false; - let mut found_non_host = false; - for component in &mut current { - if component.is_host() { - found_host = true; - } else { - found_non_host = true; - } - if found_host && found_non_host { - let location = input.current_source_location(); - context.log_css_error( - location, - ContextualParseError::NeverMatchingHostSelector(selector.to_css_string()), - ); - continue 'selector_loop; - } - } - if current.next_sequence().is_none() { - break; - } - } - } -} - -impl<'a, 'b, 'i> QualifiedRuleParser<'i> for NestedRuleParser<'a, 'b, 'i> { - type Prelude = SelectorList<SelectorImpl>; - type QualifiedRule = (); - type Error = StyleParseErrorKind<'i>; - - fn parse_prelude<'t>( - &mut self, - input: &mut Parser<'i, 't>, - ) -> Result<Self::Prelude, ParseError<'i>> { - let selector_parser = SelectorParser { - stylesheet_origin: self.context.stylesheet_origin, - namespaces: &self.context.namespaces, - url_data: self.context.url_data, - for_supports_rule: false, - }; - let selectors = SelectorList::parse(&selector_parser, input)?; - if self.context.error_reporting_enabled() { - check_for_useless_selector(input, &self.context, &selectors); - } - Ok(selectors) - } - - fn parse_block<'t>( - &mut self, - selectors: Self::Prelude, - start: &ParserState, - input: &mut Parser<'i, 't>, - ) -> Result<(), ParseError<'i>> { - let result = self.parse_nested(input, CssRuleType::Style, Some(&selectors)); - let block = Arc::new(self.shared_lock.wrap(result.declarations)); - self.rules - .push(CssRule::Style(Arc::new(self.shared_lock.wrap(StyleRule { - selectors, - block, - rules: if result.rules.is_empty() { - None - } else { - Some(CssRules::new(result.rules, self.shared_lock)) - }, - source_location: start.source_location(), - })))); - Ok(()) - } -} - -impl<'a, 'b, 'i> DeclarationParser<'i> for NestedRuleParser<'a, 'b, 'i> { - type Declaration = (); - type Error = StyleParseErrorKind<'i>; - fn parse_value<'t>( - &mut self, - name: CowRcStr<'i>, - input: &mut Parser<'i, 't>, - ) -> Result<(), ParseError<'i>> { - self.declaration_parser_state - .parse_value(self.context, name, input) - } -} - -impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> - for NestedRuleParser<'a, 'b, 'i> -{ - fn parse_qualified(&self) -> bool { - self.allow_at_and_qualified_rules() - } - - /// If nesting is disabled, we can't get there for a non-style-rule. If it's enabled, we parse - /// raw declarations there. - fn parse_declarations(&self) -> bool { - self.context.rule_types.contains(CssRuleType::Style) - } -} diff --git a/components/style/stylesheets/rules_iterator.rs b/components/style/stylesheets/rules_iterator.rs deleted file mode 100644 index 0ab2b42c0bc..00000000000 --- a/components/style/stylesheets/rules_iterator.rs +++ /dev/null @@ -1,326 +0,0 @@ -/* 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/. */ - -//! An iterator over a list of rules. - -use crate::context::QuirksMode; -use crate::media_queries::Device; -use crate::shared_lock::SharedRwLockReadGuard; -use crate::stylesheets::{CssRule, DocumentRule, ImportRule, MediaRule, SupportsRule}; -use smallvec::SmallVec; -use std::slice; - -/// An iterator over a list of rules. -pub struct RulesIterator<'a, 'b, C> -where - 'b: 'a, - C: NestedRuleIterationCondition + 'static, -{ - device: &'a Device, - quirks_mode: QuirksMode, - guard: &'a SharedRwLockReadGuard<'b>, - stack: SmallVec<[slice::Iter<'a, CssRule>; 3]>, - _phantom: ::std::marker::PhantomData<C>, -} - -impl<'a, 'b, C> RulesIterator<'a, 'b, C> -where - 'b: 'a, - C: NestedRuleIterationCondition + 'static, -{ - /// Creates a new `RulesIterator` to iterate over `rules`. - pub fn new( - device: &'a Device, - quirks_mode: QuirksMode, - guard: &'a SharedRwLockReadGuard<'b>, - rules: slice::Iter<'a, CssRule>, - ) -> Self { - let mut stack = SmallVec::new(); - stack.push(rules); - Self { - device, - quirks_mode, - guard, - stack, - _phantom: ::std::marker::PhantomData, - } - } - - /// Skips all the remaining children of the last nested rule processed. - pub fn skip_children(&mut self) { - self.stack.pop(); - } - - /// Returns the children of `rule`, and whether `rule` is effective. - pub fn children( - rule: &'a CssRule, - device: &'a Device, - quirks_mode: QuirksMode, - guard: &'a SharedRwLockReadGuard<'_>, - effective: &mut bool, - ) -> Option<slice::Iter<'a, CssRule>> { - *effective = true; - match *rule { - CssRule::Namespace(_) | - CssRule::FontFace(_) | - CssRule::CounterStyle(_) | - CssRule::Keyframes(_) | - CssRule::Page(_) | - CssRule::Property(_) | - CssRule::LayerStatement(_) | - CssRule::FontFeatureValues(_) | - CssRule::FontPaletteValues(_) => None, - CssRule::Style(ref style_rule) => { - let style_rule = style_rule.read_with(guard); - style_rule - .rules - .as_ref() - .map(|r| r.read_with(guard).0.iter()) - }, - CssRule::Import(ref import_rule) => { - let import_rule = import_rule.read_with(guard); - if !C::process_import(guard, device, quirks_mode, import_rule) { - *effective = false; - return None; - } - Some(import_rule.stylesheet.rules(guard).iter()) - }, - CssRule::Document(ref doc_rule) => { - if !C::process_document(guard, device, quirks_mode, doc_rule) { - *effective = false; - return None; - } - Some(doc_rule.rules.read_with(guard).0.iter()) - }, - CssRule::Container(ref container_rule) => { - Some(container_rule.rules.read_with(guard).0.iter()) - }, - CssRule::Media(ref media_rule) => { - if !C::process_media(guard, device, quirks_mode, media_rule) { - *effective = false; - return None; - } - Some(media_rule.rules.read_with(guard).0.iter()) - }, - CssRule::Supports(ref supports_rule) => { - if !C::process_supports(guard, device, quirks_mode, supports_rule) { - *effective = false; - return None; - } - Some(supports_rule.rules.read_with(guard).0.iter()) - }, - CssRule::LayerBlock(ref layer_rule) => Some(layer_rule.rules.read_with(guard).0.iter()), - } - } -} - -impl<'a, 'b, C> Iterator for RulesIterator<'a, 'b, C> -where - 'b: 'a, - C: NestedRuleIterationCondition + 'static, -{ - type Item = &'a CssRule; - - fn next(&mut self) -> Option<Self::Item> { - while !self.stack.is_empty() { - let rule = { - let nested_iter = self.stack.last_mut().unwrap(); - match nested_iter.next() { - Some(r) => r, - None => { - self.stack.pop(); - continue; - }, - } - }; - - let mut effective = true; - let children = Self::children( - rule, - self.device, - self.quirks_mode, - self.guard, - &mut effective, - ); - if !effective { - continue; - } - - if let Some(children) = children { - // NOTE: It's important that `children` gets pushed even if - // empty, so that `skip_children()` works as expected. - self.stack.push(children); - } - - return Some(rule); - } - - None - } -} - -/// RulesIterator. -pub trait NestedRuleIterationCondition { - /// Whether we should process the nested rules in a given `@import` rule. - fn process_import( - guard: &SharedRwLockReadGuard, - device: &Device, - quirks_mode: QuirksMode, - rule: &ImportRule, - ) -> bool; - - /// Whether we should process the nested rules in a given `@media` rule. - fn process_media( - guard: &SharedRwLockReadGuard, - device: &Device, - quirks_mode: QuirksMode, - rule: &MediaRule, - ) -> bool; - - /// Whether we should process the nested rules in a given `@-moz-document` - /// rule. - fn process_document( - guard: &SharedRwLockReadGuard, - device: &Device, - quirks_mode: QuirksMode, - rule: &DocumentRule, - ) -> bool; - - /// Whether we should process the nested rules in a given `@supports` rule. - fn process_supports( - guard: &SharedRwLockReadGuard, - device: &Device, - quirks_mode: QuirksMode, - rule: &SupportsRule, - ) -> bool; -} - -/// A struct that represents the condition that a rule applies to the document. -pub struct EffectiveRules; - -impl EffectiveRules { - /// Returns whether a given rule is effective. - pub fn is_effective( - guard: &SharedRwLockReadGuard, - device: &Device, - quirks_mode: QuirksMode, - rule: &CssRule, - ) -> bool { - match *rule { - CssRule::Import(ref import_rule) => { - let import_rule = import_rule.read_with(guard); - Self::process_import(guard, device, quirks_mode, import_rule) - }, - CssRule::Document(ref doc_rule) => { - Self::process_document(guard, device, quirks_mode, doc_rule) - }, - CssRule::Media(ref media_rule) => { - Self::process_media(guard, device, quirks_mode, media_rule) - }, - CssRule::Supports(ref supports_rule) => { - Self::process_supports(guard, device, quirks_mode, supports_rule) - }, - _ => true, - } - } -} - -impl NestedRuleIterationCondition for EffectiveRules { - fn process_import( - guard: &SharedRwLockReadGuard, - device: &Device, - quirks_mode: QuirksMode, - rule: &ImportRule, - ) -> bool { - match rule.stylesheet.media(guard) { - Some(m) => m.evaluate(device, quirks_mode), - None => true, - } - } - - fn process_media( - guard: &SharedRwLockReadGuard, - device: &Device, - quirks_mode: QuirksMode, - rule: &MediaRule, - ) -> bool { - rule.media_queries - .read_with(guard) - .evaluate(device, quirks_mode) - } - - fn process_document( - _: &SharedRwLockReadGuard, - device: &Device, - _: QuirksMode, - rule: &DocumentRule, - ) -> bool { - rule.condition.evaluate(device) - } - - fn process_supports( - _: &SharedRwLockReadGuard, - _: &Device, - _: QuirksMode, - rule: &SupportsRule, - ) -> bool { - rule.enabled - } -} - -/// A filter that processes all the rules in a rule list. -pub struct AllRules; - -impl NestedRuleIterationCondition for AllRules { - fn process_import( - _: &SharedRwLockReadGuard, - _: &Device, - _: QuirksMode, - _: &ImportRule, - ) -> bool { - true - } - - fn process_media(_: &SharedRwLockReadGuard, _: &Device, _: QuirksMode, _: &MediaRule) -> bool { - true - } - - fn process_document( - _: &SharedRwLockReadGuard, - _: &Device, - _: QuirksMode, - _: &DocumentRule, - ) -> bool { - true - } - - fn process_supports( - _: &SharedRwLockReadGuard, - _: &Device, - _: QuirksMode, - _: &SupportsRule, - ) -> bool { - true - } -} - -/// An iterator over all the effective rules of a stylesheet. -/// -/// NOTE: This iterator recurses into `@import` rules. -pub type EffectiveRulesIterator<'a, 'b> = RulesIterator<'a, 'b, EffectiveRules>; - -impl<'a, 'b> EffectiveRulesIterator<'a, 'b> { - /// Returns an iterator over the effective children of a rule, even if - /// `rule` itself is not effective. - pub fn effective_children( - device: &'a Device, - quirks_mode: QuirksMode, - guard: &'a SharedRwLockReadGuard<'b>, - rule: &'a CssRule, - ) -> Self { - let children = - RulesIterator::<AllRules>::children(rule, device, quirks_mode, guard, &mut false); - EffectiveRulesIterator::new(device, quirks_mode, guard, children.unwrap_or([].iter())) - } -} diff --git a/components/style/stylesheets/style_rule.rs b/components/style/stylesheets/style_rule.rs deleted file mode 100644 index d5a22d6fc20..00000000000 --- a/components/style/stylesheets/style_rule.rs +++ /dev/null @@ -1,104 +0,0 @@ -/* 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 style rule. - -use crate::properties::PropertyDeclarationBlock; -use crate::selector_parser::SelectorImpl; -use crate::shared_lock::{ - DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard, -}; -use crate::str::CssStringWriter; -use crate::stylesheets::CssRules; -use cssparser::SourceLocation; -#[cfg(feature = "gecko")] -use malloc_size_of::MallocUnconditionalShallowSizeOf; -#[cfg(feature = "gecko")] -use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; -use selectors::SelectorList; -use servo_arc::Arc; -use std::fmt::{self, Write}; - -/// A style rule, with selectors and declarations. -#[derive(Debug, ToShmem)] -pub struct StyleRule { - /// The list of selectors in this rule. - pub selectors: SelectorList<SelectorImpl>, - /// The declaration block with the properties it contains. - pub block: Arc<Locked<PropertyDeclarationBlock>>, - /// The nested rules to this style rule. Only non-`None` when nesting is enabled. - pub rules: Option<Arc<Locked<CssRules>>>, - /// The location in the sheet where it was found. - pub source_location: SourceLocation, -} - -impl DeepCloneWithLock for StyleRule { - /// Deep clones this StyleRule. - fn deep_clone_with_lock( - &self, - lock: &SharedRwLock, - guard: &SharedRwLockReadGuard, - params: &DeepCloneParams, - ) -> StyleRule { - StyleRule { - selectors: self.selectors.clone(), - block: Arc::new(lock.wrap(self.block.read_with(guard).clone())), - rules: self.rules.as_ref().map(|rules| { - let rules = rules.read_with(guard); - Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))) - }), - source_location: self.source_location.clone(), - } - } -} - -impl StyleRule { - /// Measure heap usage. - #[cfg(feature = "gecko")] - pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { - let mut n = 0; - n += self.selectors.0.size_of(ops); - n += self.block.unconditional_shallow_size_of(ops) + - self.block.read_with(guard).size_of(ops); - if let Some(ref rules) = self.rules { - n += rules.unconditional_shallow_size_of(ops) + - rules.read_with(guard).size_of(guard, ops) - } - n - } -} - -impl ToCssWithGuard for StyleRule { - /// https://drafts.csswg.org/cssom/#serialize-a-css-rule CSSStyleRule - fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { - use cssparser::ToCss; - // Step 1 - self.selectors.to_css(dest)?; - dest.write_str(" {")?; - - // Step 2 - let declaration_block = self.block.read_with(guard); - let has_declarations = !declaration_block.declarations().is_empty(); - - // Step 3 - if let Some(ref rules) = self.rules { - let rules = rules.read_with(guard); - // Step 6 (here because it's more convenient) - if !rules.is_empty() { - if has_declarations { - dest.write_str("\n ")?; - declaration_block.to_css(dest)?; - } - return rules.to_css_block_without_opening(guard, dest); - } - } - - // Steps 4 & 5 - if has_declarations { - dest.write_char(' ')?; - declaration_block.to_css(dest)?; - } - dest.write_str(" }") - } -} diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs deleted file mode 100644 index 6c205e03c66..00000000000 --- a/components/style/stylesheets/stylesheet.rs +++ /dev/null @@ -1,605 +0,0 @@ -/* 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/. */ - -use crate::context::QuirksMode; -use crate::error_reporting::{ContextualParseError, ParseErrorReporter}; -use crate::media_queries::{Device, MediaList}; -use crate::parser::ParserContext; -use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked}; -use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard}; -use crate::stylesheets::loader::StylesheetLoader; -use crate::stylesheets::rule_parser::{State, TopLevelRuleParser}; -use crate::stylesheets::rules_iterator::{EffectiveRules, EffectiveRulesIterator}; -use crate::stylesheets::rules_iterator::{NestedRuleIterationCondition, RulesIterator}; -use crate::stylesheets::{CssRule, CssRules, Origin, UrlExtraData}; -use crate::use_counters::UseCounters; -use crate::{Namespace, Prefix}; -use cssparser::{Parser, ParserInput, StyleSheetParser}; -use fxhash::FxHashMap; -#[cfg(feature = "gecko")] -use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; -use parking_lot::RwLock; -use servo_arc::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; -use style_traits::ParsingMode; - -/// This structure holds the user-agent and user stylesheets. -pub struct UserAgentStylesheets { - /// The lock used for user-agent stylesheets. - pub shared_lock: SharedRwLock, - /// The user or user agent stylesheets. - pub user_or_user_agent_stylesheets: Vec<DocumentStyleSheet>, - /// The quirks mode stylesheet. - pub quirks_mode_stylesheet: DocumentStyleSheet, -} - -/// A set of namespaces applying to a given stylesheet. -/// -/// The namespace id is used in gecko -#[derive(Clone, Debug, Default, MallocSizeOf)] -#[allow(missing_docs)] -pub struct Namespaces { - pub default: Option<Namespace>, - pub prefixes: FxHashMap<Prefix, Namespace>, -} - -/// The contents of a given stylesheet. This effectively maps to a -/// StyleSheetInner in Gecko. -#[derive(Debug)] -pub struct StylesheetContents { - /// List of rules in the order they were found (important for - /// cascading order) - pub rules: Arc<Locked<CssRules>>, - /// The origin of this stylesheet. - pub origin: Origin, - /// The url data this stylesheet should use. - pub url_data: RwLock<UrlExtraData>, - /// The namespaces that apply to this stylesheet. - pub namespaces: RwLock<Namespaces>, - /// The quirks mode of this stylesheet. - pub quirks_mode: QuirksMode, - /// This stylesheet's source map URL. - pub source_map_url: RwLock<Option<String>>, - /// This stylesheet's source URL. - pub source_url: RwLock<Option<String>>, - - /// We don't want to allow construction outside of this file, to guarantee - /// that all contents are created with Arc<>. - _forbid_construction: (), -} - -impl StylesheetContents { - /// Parse a given CSS string, with a given url-data, origin, and - /// quirks mode. - pub fn from_str( - css: &str, - url_data: UrlExtraData, - origin: Origin, - shared_lock: &SharedRwLock, - stylesheet_loader: Option<&dyn StylesheetLoader>, - error_reporter: Option<&dyn ParseErrorReporter>, - quirks_mode: QuirksMode, - line_number_offset: u32, - use_counters: Option<&UseCounters>, - allow_import_rules: AllowImportRules, - sanitization_data: Option<&mut SanitizationData>, - ) -> Arc<Self> { - let (namespaces, rules, source_map_url, source_url) = Stylesheet::parse_rules( - css, - &url_data, - origin, - &shared_lock, - stylesheet_loader, - error_reporter, - quirks_mode, - line_number_offset, - use_counters, - allow_import_rules, - sanitization_data, - ); - - Arc::new(Self { - rules: CssRules::new(rules, &shared_lock), - origin, - url_data: RwLock::new(url_data), - namespaces: RwLock::new(namespaces), - quirks_mode, - source_map_url: RwLock::new(source_map_url), - source_url: RwLock::new(source_url), - _forbid_construction: (), - }) - } - - /// Creates a new StylesheetContents with the specified pre-parsed rules, - /// origin, URL data, and quirks mode. - /// - /// Since the rules have already been parsed, and the intention is that - /// this function is used for read only User Agent style sheets, an empty - /// namespace map is used, and the source map and source URLs are set to - /// None. - /// - /// An empty namespace map should be fine, as it is only used for parsing, - /// not serialization of existing selectors. Since UA sheets are read only, - /// we should never need the namespace map. - pub fn from_data( - rules: Arc<Locked<CssRules>>, - origin: Origin, - url_data: UrlExtraData, - quirks_mode: QuirksMode, - ) -> Arc<Self> { - Arc::new(Self { - rules, - origin, - url_data: RwLock::new(url_data), - namespaces: RwLock::new(Namespaces::default()), - quirks_mode, - source_map_url: RwLock::new(None), - source_url: RwLock::new(None), - _forbid_construction: (), - }) - } - - /// Same as above, but ensuring that the rules are static. - pub fn from_shared_data( - rules: Arc<Locked<CssRules>>, - origin: Origin, - url_data: UrlExtraData, - quirks_mode: QuirksMode, - ) -> Arc<Self> { - debug_assert!(rules.is_static()); - Self::from_data(rules, origin, url_data, quirks_mode) - } - - /// Returns a reference to the list of rules. - #[inline] - pub fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { - &self.rules.read_with(guard).0 - } - - /// Measure heap usage. - #[cfg(feature = "gecko")] - pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { - if self.rules.is_static() { - return 0; - } - // Measurement of other fields may be added later. - self.rules.unconditional_shallow_size_of(ops) + - self.rules.read_with(guard).size_of(guard, ops) - } -} - -impl DeepCloneWithLock for StylesheetContents { - fn deep_clone_with_lock( - &self, - lock: &SharedRwLock, - guard: &SharedRwLockReadGuard, - params: &DeepCloneParams, - ) -> Self { - // Make a deep clone of the rules, using the new lock. - let rules = self - .rules - .read_with(guard) - .deep_clone_with_lock(lock, guard, params); - - Self { - rules: Arc::new(lock.wrap(rules)), - quirks_mode: self.quirks_mode, - origin: self.origin, - url_data: RwLock::new((*self.url_data.read()).clone()), - namespaces: RwLock::new((*self.namespaces.read()).clone()), - source_map_url: RwLock::new((*self.source_map_url.read()).clone()), - source_url: RwLock::new((*self.source_url.read()).clone()), - _forbid_construction: (), - } - } -} - -/// The structure servo uses to represent a stylesheet. -#[derive(Debug)] -pub struct Stylesheet { - /// The contents of this stylesheet. - pub contents: Arc<StylesheetContents>, - /// The lock used for objects inside this stylesheet - pub shared_lock: SharedRwLock, - /// List of media associated with the Stylesheet. - pub media: Arc<Locked<MediaList>>, - /// Whether this stylesheet should be disabled. - pub disabled: AtomicBool, -} - -macro_rules! rule_filter { - ($( $method: ident($variant:ident => $rule_type: ident), )+) => { - $( - #[allow(missing_docs)] - fn $method<F>(&self, device: &Device, guard: &SharedRwLockReadGuard, mut f: F) - where F: FnMut(&crate::stylesheets::$rule_type), - { - use crate::stylesheets::CssRule; - - for rule in self.effective_rules(device, guard) { - if let CssRule::$variant(ref lock) = *rule { - let rule = lock.read_with(guard); - f(&rule) - } - } - } - )+ - } -} - -/// A trait to represent a given stylesheet in a document. -pub trait StylesheetInDocument: ::std::fmt::Debug { - /// Get whether this stylesheet is enabled. - fn enabled(&self) -> bool; - - /// Get the media associated with this stylesheet. - fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList>; - - /// Returns a reference to the list of rules in this stylesheet. - fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { - self.contents().rules(guard) - } - - /// Returns a reference to the contents of the stylesheet. - fn contents(&self) -> &StylesheetContents; - - /// Return an iterator using the condition `C`. - #[inline] - fn iter_rules<'a, 'b, C>( - &'a self, - device: &'a Device, - guard: &'a SharedRwLockReadGuard<'b>, - ) -> RulesIterator<'a, 'b, C> - where - C: NestedRuleIterationCondition, - { - let contents = self.contents(); - RulesIterator::new( - device, - contents.quirks_mode, - guard, - contents.rules(guard).iter(), - ) - } - - /// Returns whether the style-sheet applies for the current device. - fn is_effective_for_device(&self, device: &Device, guard: &SharedRwLockReadGuard) -> bool { - match self.media(guard) { - Some(medialist) => medialist.evaluate(device, self.contents().quirks_mode), - None => true, - } - } - - /// Return an iterator over the effective rules within the style-sheet, as - /// according to the supplied `Device`. - #[inline] - fn effective_rules<'a, 'b>( - &'a self, - device: &'a Device, - guard: &'a SharedRwLockReadGuard<'b>, - ) -> EffectiveRulesIterator<'a, 'b> { - self.iter_rules::<EffectiveRules>(device, guard) - } - - rule_filter! { - effective_font_face_rules(FontFace => FontFaceRule), - } -} - -impl StylesheetInDocument for Stylesheet { - fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { - Some(self.media.read_with(guard)) - } - - fn enabled(&self) -> bool { - !self.disabled() - } - - #[inline] - fn contents(&self) -> &StylesheetContents { - &self.contents - } -} - -/// A simple wrapper over an `Arc<Stylesheet>`, with pointer comparison, and -/// suitable for its use in a `StylesheetSet`. -#[derive(Clone, Debug)] -#[cfg_attr(feature = "servo", derive(MallocSizeOf))] -pub struct DocumentStyleSheet( - #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] pub Arc<Stylesheet>, -); - -impl PartialEq for DocumentStyleSheet { - fn eq(&self, other: &Self) -> bool { - Arc::ptr_eq(&self.0, &other.0) - } -} - -impl StylesheetInDocument for DocumentStyleSheet { - fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { - self.0.media(guard) - } - - fn enabled(&self) -> bool { - self.0.enabled() - } - - #[inline] - fn contents(&self) -> &StylesheetContents { - self.0.contents() - } -} - -/// The kind of sanitization to use when parsing a stylesheet. -#[repr(u8)] -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum SanitizationKind { - /// Perform no sanitization. - None, - /// Allow only @font-face, style rules, and @namespace. - Standard, - /// Allow everything but conditional rules. - NoConditionalRules, -} - -/// Whether @import rules are allowed. -#[repr(u8)] -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum AllowImportRules { - /// @import rules will be parsed. - Yes, - /// @import rules will not be parsed. - No, -} - -impl SanitizationKind { - fn allows(self, rule: &CssRule) -> bool { - debug_assert_ne!(self, SanitizationKind::None); - // NOTE(emilio): If this becomes more complex (not filtering just by - // top-level rules), we should thread all the data through nested rules - // and such. But this doesn't seem necessary at the moment. - let is_standard = matches!(self, SanitizationKind::Standard); - match *rule { - CssRule::Document(..) | - CssRule::Media(..) | - CssRule::Supports(..) | - CssRule::Import(..) | - CssRule::Container(..) | - // 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::FontFace(..) | CssRule::Namespace(..) | CssRule::Style(..) => true, - - CssRule::Keyframes(..) | - CssRule::Page(..) | - CssRule::Property(..) | - CssRule::FontFeatureValues(..) | - CssRule::FontPaletteValues(..) | - CssRule::CounterStyle(..) => !is_standard, - } - } -} - -/// A struct to hold the data relevant to style sheet sanitization. -#[derive(Debug)] -pub struct SanitizationData { - kind: SanitizationKind, - output: String, -} - -impl SanitizationData { - /// Create a new input for sanitization. - #[inline] - pub fn new(kind: SanitizationKind) -> Option<Self> { - if matches!(kind, SanitizationKind::None) { - return None; - } - Some(Self { - kind, - output: String::new(), - }) - } - - /// Take the sanitized output. - #[inline] - pub fn take(self) -> String { - self.output - } -} - -impl Stylesheet { - /// Updates an empty stylesheet from a given string of text. - pub fn update_from_str( - existing: &Stylesheet, - css: &str, - url_data: UrlExtraData, - stylesheet_loader: Option<&dyn StylesheetLoader>, - error_reporter: Option<&dyn ParseErrorReporter>, - line_number_offset: u32, - allow_import_rules: AllowImportRules, - ) { - // FIXME: Consider adding use counters to Servo? - let (namespaces, rules, source_map_url, source_url) = Self::parse_rules( - css, - &url_data, - existing.contents.origin, - &existing.shared_lock, - stylesheet_loader, - error_reporter, - existing.contents.quirks_mode, - line_number_offset, - /* use_counters = */ None, - allow_import_rules, - /* sanitization_data = */ None, - ); - - *existing.contents.url_data.write() = url_data; - *existing.contents.namespaces.write() = namespaces; - - // Acquire the lock *after* parsing, to minimize the exclusive section. - let mut guard = existing.shared_lock.write(); - *existing.contents.rules.write_with(&mut guard) = CssRules(rules); - *existing.contents.source_map_url.write() = source_map_url; - *existing.contents.source_url.write() = source_url; - } - - fn parse_rules( - css: &str, - url_data: &UrlExtraData, - origin: Origin, - shared_lock: &SharedRwLock, - stylesheet_loader: Option<&dyn StylesheetLoader>, - error_reporter: Option<&dyn ParseErrorReporter>, - quirks_mode: QuirksMode, - line_number_offset: u32, - use_counters: Option<&UseCounters>, - allow_import_rules: AllowImportRules, - mut sanitization_data: Option<&mut SanitizationData>, - ) -> (Namespaces, Vec<CssRule>, Option<String>, Option<String>) { - let mut input = ParserInput::new_with_line_number_offset(css, line_number_offset); - let mut input = Parser::new(&mut input); - - let context = ParserContext::new( - origin, - url_data, - None, - ParsingMode::DEFAULT, - quirks_mode, - /* namespaces = */ Default::default(), - error_reporter, - use_counters, - ); - - let mut rule_parser = TopLevelRuleParser { - shared_lock, - loader: stylesheet_loader, - context, - state: State::Start, - dom_error: None, - insert_rule_context: None, - allow_import_rules, - declaration_parser_state: Default::default(), - rules: Vec::new(), - }; - - { - let mut iter = StyleSheetParser::new(&mut input, &mut rule_parser); - while let Some(result) = iter.next() { - match result { - Ok(rule_start) => { - // TODO(emilio, nesting): sanitize nested CSS rules, probably? - if let Some(ref mut data) = sanitization_data { - if let Some(ref rule) = iter.parser.rules.last() { - if !data.kind.allows(rule) { - iter.parser.rules.pop(); - continue; - } - } - let end = iter.input.position().byte_index(); - data.output.push_str(&css[rule_start.byte_index()..end]); - } - }, - Err((error, slice)) => { - let location = error.location; - let error = ContextualParseError::InvalidRule(slice, error); - iter.parser.context.log_css_error(location, error); - }, - } - } - } - - let source_map_url = input.current_source_map_url().map(String::from); - let source_url = input.current_source_url().map(String::from); - ( - rule_parser.context.namespaces.into_owned(), - rule_parser.rules, - source_map_url, - source_url, - ) - } - - /// Creates an empty stylesheet and parses it with a given base url, origin - /// and media. - /// - /// Effectively creates a new stylesheet and forwards the hard work to - /// `Stylesheet::update_from_str`. - pub fn from_str( - css: &str, - url_data: UrlExtraData, - origin: Origin, - media: Arc<Locked<MediaList>>, - shared_lock: SharedRwLock, - stylesheet_loader: Option<&dyn StylesheetLoader>, - error_reporter: Option<&dyn ParseErrorReporter>, - quirks_mode: QuirksMode, - line_number_offset: u32, - allow_import_rules: AllowImportRules, - ) -> Self { - // FIXME: Consider adding use counters to Servo? - let contents = StylesheetContents::from_str( - css, - url_data, - origin, - &shared_lock, - stylesheet_loader, - error_reporter, - quirks_mode, - line_number_offset, - /* use_counters = */ None, - allow_import_rules, - /* sanitized_output = */ None, - ); - - Stylesheet { - contents, - shared_lock, - media, - disabled: AtomicBool::new(false), - } - } - - /// Returns whether the stylesheet has been explicitly disabled through the - /// CSSOM. - pub fn disabled(&self) -> bool { - self.disabled.load(Ordering::SeqCst) - } - - /// Records that the stylesheet has been explicitly disabled through the - /// CSSOM. - /// - /// Returns whether the the call resulted in a change in disabled state. - /// - /// Disabled stylesheets remain in the document, but their rules are not - /// added to the Stylist. - pub fn set_disabled(&self, disabled: bool) -> bool { - self.disabled.swap(disabled, Ordering::SeqCst) != disabled - } -} - -#[cfg(feature = "servo")] -impl Clone for Stylesheet { - fn clone(&self) -> Self { - // Create a new lock for our clone. - let lock = self.shared_lock.clone(); - let guard = self.shared_lock.read(); - - // Make a deep clone of the media, using the new lock. - let media = self.media.read_with(&guard).clone(); - let media = Arc::new(lock.wrap(media)); - let contents = Arc::new(self.contents.deep_clone_with_lock( - &lock, - &guard, - &DeepCloneParams, - )); - - Stylesheet { - contents, - media, - shared_lock: lock, - disabled: AtomicBool::new(self.disabled.load(Ordering::SeqCst)), - } - } -} diff --git a/components/style/stylesheets/supports_rule.rs b/components/style/stylesheets/supports_rule.rs deleted file mode 100644 index 936bcdb385f..00000000000 --- a/components/style/stylesheets/supports_rule.rs +++ /dev/null @@ -1,448 +0,0 @@ -/* 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/. */ - -//! [@supports rules](https://drafts.csswg.org/css-conditional-3/#at-supports) - -use crate::font_face::{FontFaceSourceFormatKeyword, FontFaceSourceTechFlags}; -use crate::parser::ParserContext; -use crate::properties::{PropertyDeclaration, PropertyId, SourcePropertyDeclaration}; -use crate::selector_parser::{SelectorImpl, SelectorParser}; -use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked}; -use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; -use crate::str::CssStringWriter; -use crate::stylesheets::{CssRuleType, CssRules}; -use cssparser::parse_important; -use cssparser::{Delimiter, Parser, SourceLocation, Token}; -use cssparser::{ParseError as CssParseError, ParserInput}; -#[cfg(feature = "gecko")] -use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; -use selectors::parser::{Selector, SelectorParseErrorKind}; -use servo_arc::Arc; -use std::ffi::{CStr, CString}; -use std::fmt::{self, Write}; -use std::str; -use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; - -/// An [`@supports`][supports] rule. -/// -/// [supports]: https://drafts.csswg.org/css-conditional-3/#at-supports -#[derive(Debug, ToShmem)] -pub struct SupportsRule { - /// The parsed condition - pub condition: SupportsCondition, - /// Child rules - pub rules: Arc<Locked<CssRules>>, - /// The result of evaluating the condition - pub enabled: bool, - /// The line and column of the rule's source code. - pub source_location: SourceLocation, -} - -impl SupportsRule { - /// Measure heap usage. - #[cfg(feature = "gecko")] - pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { - // Measurement of other fields may be added later. - self.rules.unconditional_shallow_size_of(ops) + - self.rules.read_with(guard).size_of(guard, ops) - } -} - -impl ToCssWithGuard for SupportsRule { - fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { - dest.write_str("@supports ")?; - self.condition.to_css(&mut CssWriter::new(dest))?; - self.rules.read_with(guard).to_css_block(guard, dest) - } -} - -impl DeepCloneWithLock for SupportsRule { - fn deep_clone_with_lock( - &self, - lock: &SharedRwLock, - guard: &SharedRwLockReadGuard, - params: &DeepCloneParams, - ) -> Self { - let rules = self.rules.read_with(guard); - SupportsRule { - condition: self.condition.clone(), - rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))), - enabled: self.enabled, - source_location: self.source_location.clone(), - } - } -} - -/// An @supports condition -/// -/// <https://drafts.csswg.org/css-conditional-3/#at-supports> -#[derive(Clone, Debug, ToShmem)] -pub enum SupportsCondition { - /// `not (condition)` - Not(Box<SupportsCondition>), - /// `(condition)` - Parenthesized(Box<SupportsCondition>), - /// `(condition) and (condition) and (condition) ..` - And(Vec<SupportsCondition>), - /// `(condition) or (condition) or (condition) ..` - Or(Vec<SupportsCondition>), - /// `property-ident: value` (value can be any tokens) - Declaration(Declaration), - /// A `selector()` function. - Selector(RawSelector), - /// `-moz-bool-pref("pref-name")` - /// Since we need to pass it through FFI to get the pref value, - /// we store it as CString directly. - MozBoolPref(CString), - /// `font-format(<font-format>)` - FontFormat(FontFaceSourceFormatKeyword), - /// `font-tech(<font-tech>)` - FontTech(FontFaceSourceTechFlags), - /// `(any tokens)` or `func(any tokens)` - FutureSyntax(String), -} - -impl SupportsCondition { - /// Parse a condition - /// - /// <https://drafts.csswg.org/css-conditional/#supports_condition> - pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> { - if input.try_parse(|i| i.expect_ident_matching("not")).is_ok() { - let inner = SupportsCondition::parse_in_parens(input)?; - return Ok(SupportsCondition::Not(Box::new(inner))); - } - - let in_parens = SupportsCondition::parse_in_parens(input)?; - - let location = input.current_source_location(); - let (keyword, wrapper) = match input.next() { - // End of input - Err(..) => return Ok(in_parens), - Ok(&Token::Ident(ref ident)) => { - match_ignore_ascii_case! { &ident, - "and" => ("and", SupportsCondition::And as fn(_) -> _), - "or" => ("or", SupportsCondition::Or as fn(_) -> _), - _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))) - } - }, - Ok(t) => return Err(location.new_unexpected_token_error(t.clone())), - }; - - let mut conditions = Vec::with_capacity(2); - conditions.push(in_parens); - loop { - conditions.push(SupportsCondition::parse_in_parens(input)?); - if input - .try_parse(|input| input.expect_ident_matching(keyword)) - .is_err() - { - // Did not find the expected keyword. - // If we found some other token, it will be rejected by - // `Parser::parse_entirely` somewhere up the stack. - return Ok(wrapper(conditions)); - } - } - } - - /// Parses a functional supports condition. - fn parse_functional<'i, 't>( - function: &str, - input: &mut Parser<'i, 't>, - ) -> Result<Self, ParseError<'i>> { - match_ignore_ascii_case! { function, - // Although this is an internal syntax, it is not necessary - // to check parsing context as far as we accept any - // unexpected token as future syntax, and evaluate it to - // false when not in chrome / ua sheet. - // See https://drafts.csswg.org/css-conditional-3/#general_enclosed - "-moz-bool-pref" => { - let name = { - let name = input.expect_string()?; - CString::new(name.as_bytes()) - }.map_err(|_| input.new_custom_error(StyleParseErrorKind::UnspecifiedError))?; - Ok(SupportsCondition::MozBoolPref(name)) - }, - "selector" => { - let pos = input.position(); - consume_any_value(input)?; - Ok(SupportsCondition::Selector(RawSelector( - input.slice_from(pos).to_owned() - ))) - }, - "font-format" if static_prefs::pref!("layout.css.font-tech.enabled") => { - let kw = FontFaceSourceFormatKeyword::parse(input)?; - Ok(SupportsCondition::FontFormat(kw)) - }, - "font-tech" if static_prefs::pref!("layout.css.font-tech.enabled") => { - let flag = FontFaceSourceTechFlags::parse_one(input)?; - Ok(SupportsCondition::FontTech(flag)) - }, - _ => { - Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - }, - } - } - - /// Parses an `@import` condition as per - /// https://drafts.csswg.org/css-cascade-5/#typedef-import-conditions - pub fn parse_for_import<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> { - input.expect_function_matching("supports")?; - input.parse_nested_block(parse_condition_or_declaration) - } - - /// <https://drafts.csswg.org/css-conditional-3/#supports_condition_in_parens> - fn parse_in_parens<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> { - // Whitespace is normally taken care of in `Parser::next`, but we want to not include it in - // `pos` for the SupportsCondition::FutureSyntax cases. - input.skip_whitespace(); - let pos = input.position(); - let location = input.current_source_location(); - match *input.next()? { - Token::ParenthesisBlock => { - let nested = input - .try_parse(|input| input.parse_nested_block(parse_condition_or_declaration)); - if let Ok(nested) = nested { - return Ok(Self::Parenthesized(Box::new(nested))); - } - }, - Token::Function(ref ident) => { - let ident = ident.clone(); - let nested = input.try_parse(|input| { - input.parse_nested_block(|input| { - SupportsCondition::parse_functional(&ident, input) - }) - }); - if nested.is_ok() { - return nested; - } - }, - ref t => return Err(location.new_unexpected_token_error(t.clone())), - } - input.parse_nested_block(consume_any_value)?; - Ok(SupportsCondition::FutureSyntax( - input.slice_from(pos).to_owned(), - )) - } - - /// Evaluate a supports condition - pub fn eval(&self, cx: &ParserContext) -> bool { - match *self { - SupportsCondition::Not(ref cond) => !cond.eval(cx), - SupportsCondition::Parenthesized(ref cond) => cond.eval(cx), - SupportsCondition::And(ref vec) => vec.iter().all(|c| c.eval(cx)), - SupportsCondition::Or(ref vec) => vec.iter().any(|c| c.eval(cx)), - SupportsCondition::Declaration(ref decl) => decl.eval(cx), - SupportsCondition::MozBoolPref(ref name) => eval_moz_bool_pref(name, cx), - SupportsCondition::Selector(ref selector) => selector.eval(cx), - SupportsCondition::FontFormat(ref format) => eval_font_format(format), - SupportsCondition::FontTech(ref tech) => eval_font_tech(tech), - SupportsCondition::FutureSyntax(_) => false, - } - } -} - -#[cfg(feature = "gecko")] -fn eval_moz_bool_pref(name: &CStr, cx: &ParserContext) -> bool { - use crate::gecko_bindings::bindings; - if !cx.in_ua_or_chrome_sheet() { - return false; - } - unsafe { bindings::Gecko_GetBoolPrefValue(name.as_ptr()) } -} - -#[cfg(feature = "gecko")] -fn eval_font_format(kw: &FontFaceSourceFormatKeyword) -> bool { - use crate::gecko_bindings::bindings; - unsafe { bindings::Gecko_IsFontFormatSupported(*kw) } -} - -#[cfg(feature = "gecko")] -fn eval_font_tech(flag: &FontFaceSourceTechFlags) -> bool { - use crate::gecko_bindings::bindings; - unsafe { bindings::Gecko_IsFontTechSupported(*flag) } -} - -#[cfg(feature = "servo")] -fn eval_moz_bool_pref(_: &CStr, _: &ParserContext) -> bool { - false -} - -#[cfg(feature = "servo")] -fn eval_font_format(_: &FontFaceSourceFormatKeyword) -> bool { - false -} - -#[cfg(feature = "servo")] -fn eval_font_tech(_: &FontFaceSourceTechFlags) -> bool { - false -} - -/// supports_condition | declaration -/// <https://drafts.csswg.org/css-conditional/#dom-css-supports-conditiontext-conditiontext> -pub fn parse_condition_or_declaration<'i, 't>( - input: &mut Parser<'i, 't>, -) -> Result<SupportsCondition, ParseError<'i>> { - if let Ok(condition) = input.try_parse(SupportsCondition::parse) { - Ok(condition) - } else { - Declaration::parse(input).map(SupportsCondition::Declaration) - } -} - -impl ToCss for SupportsCondition { - fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result - where - W: Write, - { - match *self { - SupportsCondition::Not(ref cond) => { - dest.write_str("not ")?; - cond.to_css(dest) - }, - SupportsCondition::Parenthesized(ref cond) => { - dest.write_char('(')?; - cond.to_css(dest)?; - dest.write_char(')') - }, - SupportsCondition::And(ref vec) => { - let mut first = true; - for cond in vec { - if !first { - dest.write_str(" and ")?; - } - first = false; - cond.to_css(dest)?; - } - Ok(()) - }, - SupportsCondition::Or(ref vec) => { - let mut first = true; - for cond in vec { - if !first { - dest.write_str(" or ")?; - } - first = false; - cond.to_css(dest)?; - } - Ok(()) - }, - SupportsCondition::Declaration(ref decl) => decl.to_css(dest), - SupportsCondition::Selector(ref selector) => { - dest.write_str("selector(")?; - selector.to_css(dest)?; - dest.write_char(')') - }, - SupportsCondition::MozBoolPref(ref name) => { - dest.write_str("-moz-bool-pref(")?; - let name = - str::from_utf8(name.as_bytes()).expect("Should be parsed from valid UTF-8"); - name.to_css(dest)?; - dest.write_char(')') - }, - SupportsCondition::FontFormat(ref kw) => { - dest.write_str("font-format(")?; - kw.to_css(dest)?; - dest.write_char(')') - }, - SupportsCondition::FontTech(ref flag) => { - dest.write_str("font-tech(")?; - flag.to_css(dest)?; - dest.write_char(')') - }, - SupportsCondition::FutureSyntax(ref s) => dest.write_str(&s), - } - } -} - -#[derive(Clone, Debug, ToShmem)] -/// A possibly-invalid CSS selector. -pub struct RawSelector(pub String); - -impl ToCss for RawSelector { - fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result - where - W: Write, - { - dest.write_str(&self.0) - } -} - -impl RawSelector { - /// Tries to evaluate a `selector()` function. - pub fn eval(&self, context: &ParserContext) -> bool { - let mut input = ParserInput::new(&self.0); - let mut input = Parser::new(&mut input); - input - .parse_entirely(|input| -> Result<(), CssParseError<()>> { - let parser = SelectorParser { - namespaces: &context.namespaces, - stylesheet_origin: context.stylesheet_origin, - url_data: context.url_data, - for_supports_rule: true, - }; - - Selector::<SelectorImpl>::parse(&parser, input) - .map_err(|_| input.new_custom_error(()))?; - - Ok(()) - }) - .is_ok() - } -} - -#[derive(Clone, Debug, ToShmem)] -/// A possibly-invalid property declaration -pub struct Declaration(pub String); - -impl ToCss for Declaration { - fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result - where - W: Write, - { - dest.write_str(&self.0) - } -} - -/// <https://drafts.csswg.org/css-syntax-3/#typedef-any-value> -fn consume_any_value<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<'i>> { - input.expect_no_error_token().map_err(|err| err.into()) -} - -impl Declaration { - /// Parse a declaration - pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Declaration, ParseError<'i>> { - let pos = input.position(); - input.expect_ident()?; - input.expect_colon()?; - consume_any_value(input)?; - Ok(Declaration(input.slice_from(pos).to_owned())) - } - - /// Determine if a declaration parses - /// - /// <https://drafts.csswg.org/css-conditional-3/#support-definition> - pub fn eval(&self, context: &ParserContext) -> bool { - debug_assert!(context.rule_types().contains(CssRuleType::Style)); - - let mut input = ParserInput::new(&self.0); - let mut input = Parser::new(&mut input); - input - .parse_entirely(|input| -> Result<(), CssParseError<()>> { - let prop = input.expect_ident_cloned().unwrap(); - input.expect_colon().unwrap(); - - let id = - PropertyId::parse(&prop, context).map_err(|_| input.new_custom_error(()))?; - - let mut declarations = SourcePropertyDeclaration::default(); - input.parse_until_before(Delimiter::Bang, |input| { - PropertyDeclaration::parse_into(&mut declarations, id, &context, input) - .map_err(|_| input.new_custom_error(())) - })?; - let _ = input.try_parse(parse_important); - Ok(()) - }) - .is_ok() - } -} |