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