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