diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /servo/components/style/stylesheets/rule_list.rs | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | servo/components/style/stylesheets/rule_list.rs | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/servo/components/style/stylesheets/rule_list.rs b/servo/components/style/stylesheets/rule_list.rs new file mode 100644 index 0000000000..ab747565ff --- /dev/null +++ b/servo/components/style/stylesheets/rule_list.rs @@ -0,0 +1,198 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! 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) + } +} |