style/stylesheets/
media_rule.rs1use crate::media_queries::MediaList;
10use crate::selector_map::{PrecomputedHashMap, PrecomputedHashSet};
11use crate::shared_lock::{DeepCloneWithLock, Locked};
12use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
13use crate::stylesheets::CssRules;
14use crate::values::{computed, DashedIdent};
15use crate::Atom;
16use cssparser::Parser;
17use cssparser::SourceLocation;
18#[cfg(feature = "gecko")]
19use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
20use selectors::kleene_value::KleeneValue;
21use servo_arc::Arc;
22use std::fmt::{self, Write};
23use style_traits::{CssStringWriter, CssWriter, ParseError, ToCss};
24
25#[derive(Debug, ToShmem)]
29pub struct MediaRule {
30 pub media_queries: Arc<Locked<MediaList>>,
32 pub rules: Arc<Locked<CssRules>>,
34 pub source_location: SourceLocation,
36}
37
38impl MediaRule {
39 #[cfg(feature = "gecko")]
41 pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
42 self.rules.unconditional_shallow_size_of(ops)
44 + self.rules.read_with(guard).size_of(guard, ops)
45 }
46}
47
48impl ToCssWithGuard for MediaRule {
49 fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
52 dest.write_str("@media ")?;
53 self.media_queries
54 .read_with(guard)
55 .to_css(&mut CssWriter::new(dest))?;
56 self.rules.read_with(guard).to_css_block(guard, dest)
57 }
58}
59
60impl DeepCloneWithLock for MediaRule {
61 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
62 let media_queries = self.media_queries.read_with(guard);
63 let rules = self.rules.read_with(guard);
64 MediaRule {
65 media_queries: Arc::new(lock.wrap(media_queries.clone())),
66 rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))),
67 source_location: self.source_location.clone(),
68 }
69 }
70}
71
72#[derive(Debug, ToShmem, Clone, MallocSizeOf)]
74pub enum CustomMediaCondition {
75 True,
77 False,
79 MediaList(#[ignore_malloc_size_of = "Arc"] Arc<Locked<MediaList>>),
81}
82
83impl CustomMediaCondition {
84 pub(crate) fn parse_keyword<'i>(input: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
86 Ok(try_match_ident_ignore_ascii_case! { input,
87 "true" => Self::True,
88 "false" => Self::False,
89 })
90 }
91}
92
93impl DeepCloneWithLock for CustomMediaCondition {
94 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
95 match self {
96 Self::True => Self::True,
97 Self::False => Self::False,
98 Self::MediaList(ref m) => {
99 Self::MediaList(Arc::new(lock.wrap(m.read_with(guard).clone())))
100 },
101 }
102 }
103}
104
105#[derive(Debug, ToShmem)]
108pub struct CustomMediaRule {
109 pub name: DashedIdent,
111 pub condition: CustomMediaCondition,
113 pub source_location: SourceLocation,
115}
116
117impl DeepCloneWithLock for CustomMediaRule {
118 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
119 Self {
120 name: self.name.clone(),
121 condition: self.condition.deep_clone_with_lock(lock, guard),
122 source_location: self.source_location.clone(),
123 }
124 }
125}
126
127impl ToCssWithGuard for CustomMediaRule {
128 fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
131 dest.write_str("@custom-media ")?;
132 self.name.to_css(&mut CssWriter::new(dest))?;
133 dest.write_char(' ')?;
134 match self.condition {
135 CustomMediaCondition::True => dest.write_str("true"),
136 CustomMediaCondition::False => dest.write_str("false"),
137 CustomMediaCondition::MediaList(ref m) => {
138 m.read_with(guard).to_css(&mut CssWriter::new(dest))
139 },
140 }
141 }
142}
143
144pub type CustomMediaMap = PrecomputedHashMap<Atom, CustomMediaCondition>;
146
147pub struct CustomMediaEvaluator<'a> {
149 map: Option<(&'a CustomMediaMap, &'a SharedRwLockReadGuard<'a>)>,
150 currently_evaluating: PrecomputedHashSet<Atom>,
152}
153
154impl<'a> CustomMediaEvaluator<'a> {
155 pub fn new(map: &'a CustomMediaMap, guard: &'a SharedRwLockReadGuard<'a>) -> Self {
157 Self {
158 map: Some((map, guard)),
159 currently_evaluating: Default::default(),
160 }
161 }
162
163 pub fn none() -> Self {
165 Self {
166 map: None,
167 currently_evaluating: Default::default(),
168 }
169 }
170
171 pub fn matches(&mut self, ident: &DashedIdent, context: &computed::Context) -> KleeneValue {
173 let Some((map, guard)) = self.map else {
174 return KleeneValue::Unknown;
175 };
176 let Some(condition) = map.get(&ident.0) else {
177 return KleeneValue::Unknown;
178 };
179 let media = match condition {
180 CustomMediaCondition::True => return KleeneValue::True,
181 CustomMediaCondition::False => return KleeneValue::False,
182 CustomMediaCondition::MediaList(ref m) => m,
183 };
184 if !self.currently_evaluating.insert(ident.0.clone()) {
185 return KleeneValue::False;
187 }
188 let result = media.read_with(guard).matches(context, self);
189 self.currently_evaluating.remove(&ident.0);
190 result.into()
191 }
192}