script/dom/media/
medialist.rs1use std::cell::RefCell;
6
7use cssparser::{Parser, ParserInput};
8use dom_struct::dom_struct;
9use servo_arc::Arc;
10use style::media_queries::{MediaList as StyleMediaList, MediaQuery};
11use style::parser::ParserContext;
12use style::shared_lock::{Locked, SharedRwLock};
13use style::stylesheets::{CssRuleType, CustomMediaEvaluator, Origin, UrlExtraData};
14use style_traits::{ParseError, ParsingMode, ToCss};
15
16use crate::dom::bindings::codegen::Bindings::MediaListBinding::MediaListMethods;
17use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
18use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
19use crate::dom::bindings::root::{Dom, DomRoot};
20use crate::dom::bindings::str::DOMString;
21use crate::dom::css::cssstylesheet::CSSStyleSheet;
22use crate::dom::document::Document;
23use crate::dom::window::Window;
24use crate::script_runtime::CanGc;
25
26#[dom_struct]
27pub(crate) struct MediaList {
28 reflector_: Reflector,
29 parent_stylesheet: Dom<CSSStyleSheet>,
30 #[ignore_malloc_size_of = "Stylo"]
31 #[no_trace]
32 media_queries: RefCell<Arc<Locked<StyleMediaList>>>,
33}
34
35impl MediaList {
36 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
37 pub(crate) fn new_inherited(
38 parent_stylesheet: &CSSStyleSheet,
39 media_queries: Arc<Locked<StyleMediaList>>,
40 ) -> MediaList {
41 MediaList {
42 parent_stylesheet: Dom::from_ref(parent_stylesheet),
43 reflector_: Reflector::new(),
44 media_queries: RefCell::new(media_queries),
45 }
46 }
47
48 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
49 pub(crate) fn new(
50 window: &Window,
51 parent_stylesheet: &CSSStyleSheet,
52 media_queries: Arc<Locked<StyleMediaList>>,
53 can_gc: CanGc,
54 ) -> DomRoot<MediaList> {
55 reflect_dom_object(
56 Box::new(MediaList::new_inherited(parent_stylesheet, media_queries)),
57 window,
58 can_gc,
59 )
60 }
61
62 fn shared_lock(&self) -> &SharedRwLock {
63 self.parent_stylesheet.shared_lock()
64 }
65
66 pub(crate) fn parse_media_list(value: &str, window: &Window) -> StyleMediaList {
68 if value.is_empty() {
69 return StyleMediaList::empty();
70 }
71 let mut input = ParserInput::new(value);
72 let mut parser = Parser::new(&mut input);
73 let url_data = UrlExtraData(window.get_url().get_arc());
74 let quirks_mode = window.Document().quirks_mode();
75 let context = ParserContext::new(
79 Origin::Author,
80 &url_data,
81 Some(CssRuleType::Media),
82 ParsingMode::DEFAULT,
83 quirks_mode,
84 Default::default(),
85 window.css_error_reporter(),
86 None,
87 );
88 StyleMediaList::parse(&context, &mut parser)
89 }
90
91 pub(crate) fn parse_media_query<'i>(
93 value: &'i str,
94 window: &Window,
95 ) -> Result<MediaQuery, ParseError<'i>> {
96 let mut input = ParserInput::new(value);
97 let mut parser = Parser::new(&mut input);
98 let url_data = UrlExtraData(window.get_url().get_arc());
99 let quirks_mode = window.Document().quirks_mode();
100 let context = ParserContext::new(
101 Origin::Author,
102 &url_data,
103 Some(CssRuleType::Media),
104 ParsingMode::DEFAULT,
105 quirks_mode,
106 Default::default(),
107 window.css_error_reporter(),
108 None,
109 );
110 MediaQuery::parse(&context, &mut parser)
111 }
112
113 pub(crate) fn clone_media_list(&self) -> StyleMediaList {
114 let guard = self.shared_lock().read();
115 self.media_queries.borrow().read_with(&guard).clone()
116 }
117
118 pub(crate) fn update_media_list(&self, media_queries: Arc<Locked<StyleMediaList>>) {
119 *self.media_queries.borrow_mut() = media_queries;
120 }
121
122 pub(crate) fn matches_environment(document: &Document, media_query: &str) -> bool {
124 let quirks_mode = document.quirks_mode();
125 let document_url_data = UrlExtraData(document.url().get_arc());
126 let context = ParserContext::new(
132 Origin::Author,
133 &document_url_data,
134 Some(CssRuleType::Style),
135 ParsingMode::all(),
136 quirks_mode,
137 Default::default(),
138 None,
139 None,
140 );
141 let mut parser_input = ParserInput::new(media_query);
142 let mut parser = Parser::new(&mut parser_input);
143 let media_list = StyleMediaList::parse(&context, &mut parser);
144 media_list.evaluate(
145 document.window().layout().device(),
146 quirks_mode,
147 &mut CustomMediaEvaluator::none(),
148 )
149 }
150}
151
152impl MediaListMethods<crate::DomTypeHolder> for MediaList {
153 fn MediaText(&self) -> DOMString {
155 let guard = self.shared_lock().read();
156 DOMString::from(
157 self.media_queries
158 .borrow()
159 .read_with(&guard)
160 .to_css_string(),
161 )
162 }
163
164 fn SetMediaText(&self, value: DOMString) {
166 self.parent_stylesheet.will_modify();
167 let global = self.global();
168 let mut guard = self.shared_lock().write();
169 let media_queries_borrowed = self.media_queries.borrow();
170 let media_queries = media_queries_borrowed.write_with(&mut guard);
171 *media_queries = Self::parse_media_list(&value.str(), global.as_window());
172 self.parent_stylesheet.notify_invalidations();
173 }
174
175 fn Length(&self) -> u32 {
177 let guard = self.shared_lock().read();
178 self.media_queries
179 .borrow()
180 .read_with(&guard)
181 .media_queries
182 .len() as u32
183 }
184
185 fn Item(&self, index: u32) -> Option<DOMString> {
187 let guard = self.shared_lock().read();
188 self.media_queries
189 .borrow()
190 .read_with(&guard)
191 .media_queries
192 .get(index as usize)
193 .map(|query| query.to_css_string().into())
194 }
195
196 fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
198 self.Item(index)
199 }
200
201 fn AppendMedium(&self, medium: DOMString) {
203 let global = self.global();
205 let medium = medium.str();
206 let m = Self::parse_media_query(&medium, global.as_window());
207 if m.is_err() {
209 return;
210 }
211 {
213 let m_serialized = m.clone().unwrap().to_css_string();
214 let guard = self.shared_lock().read();
215 let any = self
216 .media_queries
217 .borrow()
218 .read_with(&guard)
219 .media_queries
220 .iter()
221 .any(|q| m_serialized == q.to_css_string());
222 if any {
223 return;
224 }
225 }
226 self.parent_stylesheet.will_modify();
228 let mut guard = self.shared_lock().write();
229 self.media_queries
230 .borrow()
231 .write_with(&mut guard)
232 .media_queries
233 .push(m.unwrap());
234 self.parent_stylesheet.notify_invalidations();
235 }
236
237 fn DeleteMedium(&self, medium: DOMString) {
239 let global = self.global();
241 let medium = medium.str();
242 let m = Self::parse_media_query(&medium, global.as_window());
243 if m.is_err() {
245 return;
246 }
247 self.parent_stylesheet.will_modify();
249 let m_serialized = m.unwrap().to_css_string();
250 let mut guard = self.shared_lock().write();
251 let media_queries_borrowed = self.media_queries.borrow();
252 let media_list = media_queries_borrowed.write_with(&mut guard);
253 let new_vec = media_list
254 .media_queries
255 .drain(..)
256 .filter(|q| m_serialized != q.to_css_string())
257 .collect();
258 media_list.media_queries = new_vec;
259 self.parent_stylesheet.notify_invalidations();
260 }
261}