use crate::shared_lock::{DeepCloneWithLock, Locked};
use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use crate::str::CssStringWriter;
use crate::stylesheets::loader::StylesheetLoader;
use crate::stylesheets::rule_parser::InsertRuleContext;
use crate::stylesheets::stylesheet::StylesheetContents;
use crate::stylesheets::{AllowImportRules, CssRule, CssRuleTypes, RulesMutateError};
#[cfg(feature = "gecko")]
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps};
use servo_arc::Arc;
use std::fmt::{self, Write};
use super::CssRuleType;
#[derive(Debug, ToShmem)]
pub struct CssRules(pub Vec<CssRule>);
impl CssRules {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl DeepCloneWithLock for CssRules {
fn deep_clone_with_lock(
&self,
lock: &SharedRwLock,
guard: &SharedRwLockReadGuard,
) -> Self {
CssRules(
self.0
.iter()
.map(|x| x.deep_clone_with_lock(lock, guard))
.collect(),
)
}
}
impl CssRules {
#[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
}
pub fn new(rules: Vec<CssRule>, shared_lock: &SharedRwLock) -> Arc<Locked<CssRules>> {
Arc::new(shared_lock.wrap(CssRules(rules)))
}
fn only_ns_or_import(&self) -> bool {
self.0.iter().all(|r| match *r {
CssRule::Namespace(..) | CssRule::Import(..) => true,
_ => false,
})
}
pub fn remove_rule(&mut self, index: usize) -> Result<(), RulesMutateError> {
if index >= self.0.len() {
return Err(RulesMutateError::IndexSize);
}
{
let ref rule = self.0[index];
if let CssRule::Namespace(..) = *rule {
if !self.only_ns_or_import() {
return Err(RulesMutateError::InvalidState);
}
}
}
self.0.remove(index);
Ok(())
}
pub fn to_css_block(
&self,
guard: &SharedRwLockReadGuard,
dest: &mut CssStringWriter,
) -> fmt::Result {
dest.write_str(" {")?;
self.to_css_block_without_opening(guard, dest)
}
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}")
}
}
pub trait CssRulesHelpers {
fn insert_rule(
&self,
lock: &SharedRwLock,
rule: &str,
parent_stylesheet_contents: &StylesheetContents,
index: usize,
nested: CssRuleTypes,
parse_relative_rule_type: Option<CssRuleType>,
loader: Option<&dyn StylesheetLoader>,
allow_import_rules: AllowImportRules,
) -> Result<CssRule, RulesMutateError>;
}
impl CssRulesHelpers for Locked<CssRules> {
fn insert_rule(
&self,
lock: &SharedRwLock,
rule: &str,
parent_stylesheet_contents: &StylesheetContents,
index: usize,
containing_rule_types: CssRuleTypes,
parse_relative_rule_type: Option<CssRuleType>,
loader: Option<&dyn StylesheetLoader>,
allow_import_rules: AllowImportRules,
) -> Result<CssRule, RulesMutateError> {
let new_rule = {
let read_guard = lock.read();
let rules = self.read_with(&read_guard);
if index > rules.0.len() {
return Err(RulesMutateError::IndexSize);
}
let insert_rule_context = InsertRuleContext {
rule_list: &rules.0,
index,
containing_rule_types,
parse_relative_rule_type,
};
CssRule::parse(
&rule,
insert_rule_context,
parent_stylesheet_contents,
lock,
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)
}
}