1use 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, 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::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(document.window().layout().device(), quirks_mode)
145 }
146}
147
148impl MediaListMethods<crate::DomTypeHolder> for MediaList {
149 fn MediaText(&self) -> DOMString {
151 let guard = self.shared_lock().read();
152 DOMString::from(
153 self.media_queries
154 .borrow()
155 .read_with(&guard)
156 .to_css_string(),
157 )
158 }
159
160 fn SetMediaText(&self, value: DOMString) {
162 self.parent_stylesheet.will_modify();
163 let global = self.global();
164 let mut guard = self.shared_lock().write();
165 let media_queries_borrowed = self.media_queries.borrow();
166 let media_queries = media_queries_borrowed.write_with(&mut guard);
167 *media_queries = Self::parse_media_list(&value.str(), global.as_window());
168 self.parent_stylesheet.notify_invalidations();
169 }
170
171 fn Length(&self) -> u32 {
173 let guard = self.shared_lock().read();
174 self.media_queries
175 .borrow()
176 .read_with(&guard)
177 .media_queries
178 .len() as u32
179 }
180
181 fn Item(&self, index: u32) -> Option<DOMString> {
183 let guard = self.shared_lock().read();
184 self.media_queries
185 .borrow()
186 .read_with(&guard)
187 .media_queries
188 .get(index as usize)
189 .map(|query| query.to_css_string().into())
190 }
191
192 fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
194 self.Item(index)
195 }
196
197 fn AppendMedium(&self, medium: DOMString) {
199 let global = self.global();
201 let medium = medium.str();
202 let m = Self::parse_media_query(&medium, global.as_window());
203 if m.is_err() {
205 return;
206 }
207 {
209 let m_serialized = m.clone().unwrap().to_css_string();
210 let guard = self.shared_lock().read();
211 let any = self
212 .media_queries
213 .borrow()
214 .read_with(&guard)
215 .media_queries
216 .iter()
217 .any(|q| m_serialized == q.to_css_string());
218 if any {
219 return;
220 }
221 }
222 self.parent_stylesheet.will_modify();
224 let mut guard = self.shared_lock().write();
225 self.media_queries
226 .borrow()
227 .write_with(&mut guard)
228 .media_queries
229 .push(m.unwrap());
230 self.parent_stylesheet.notify_invalidations();
231 }
232
233 fn DeleteMedium(&self, medium: DOMString) {
235 let global = self.global();
237 let medium = medium.str();
238 let m = Self::parse_media_query(&medium, global.as_window());
239 if m.is_err() {
241 return;
242 }
243 self.parent_stylesheet.will_modify();
245 let m_serialized = m.unwrap().to_css_string();
246 let mut guard = self.shared_lock().write();
247 let media_queries_borrowed = self.media_queries.borrow();
248 let media_list = media_queries_borrowed.write_with(&mut guard);
249 let new_vec = media_list
250 .media_queries
251 .drain(..)
252 .filter(|q| m_serialized != q.to_css_string())
253 .collect();
254 media_list.media_queries = new_vec;
255 self.parent_stylesheet.notify_invalidations();
256 }
257}