script/dom/media/
medialist.rs1use std::cell::RefCell;
6
7use cssparser::{Parser, ParserInput};
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
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::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_with_cx(
57 Box::new(MediaList::new_inherited(parent_stylesheet, media_queries)),
58 window,
59 cx,
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 input = ParserInput::new(value);
73 let mut parser = Parser::new(&mut input);
74 let document = window.Document();
75 let url_data = UrlExtraData(document.owner_global().api_base_url().get_arc());
76 let context = parser_context_for_document(
80 &document,
81 CssRuleType::Media,
82 ParsingMode::DEFAULT,
83 &url_data,
84 );
85 StyleMediaList::parse(&context, &mut parser)
86 }
87
88 pub(crate) fn parse_media_query<'i>(
90 value: &'i str,
91 window: &Window,
92 ) -> Result<MediaQuery, ParseError<'i>> {
93 let mut input = ParserInput::new(value);
94 let mut parser = Parser::new(&mut input);
95 let document = window.Document();
96 let url_data = UrlExtraData(document.owner_global().api_base_url().get_arc());
97 let context = parser_context_for_document(
98 &document,
99 CssRuleType::Media,
100 ParsingMode::DEFAULT,
101 &url_data,
102 );
103 MediaQuery::parse(&context, &mut parser)
104 }
105
106 pub(crate) fn clone_media_list(&self) -> StyleMediaList {
107 let guard = self.shared_lock().read();
108 self.media_queries.borrow().read_with(&guard).clone()
109 }
110
111 pub(crate) fn update_media_list(&self, media_queries: Arc<Locked<StyleMediaList>>) {
112 *self.media_queries.borrow_mut() = media_queries;
113 }
114
115 pub(crate) fn matches_environment(document: &Document, media_query: &str) -> bool {
117 let quirks_mode = document.quirks_mode();
118 let url_data = UrlExtraData(document.owner_global().api_base_url().get_arc());
119 let context = ParserContext::new(
125 Origin::Author,
126 &url_data,
127 Some(CssRuleType::Style),
128 ParsingMode::all(),
129 quirks_mode,
130 Default::default(),
131 None,
132 None,
133 Default::default(),
134 );
135 let mut parser_input = ParserInput::new(media_query);
136 let mut parser = Parser::new(&mut parser_input);
137 let media_list = StyleMediaList::parse(&context, &mut parser);
138 media_list.evaluate(
139 document.window().layout().device(),
140 quirks_mode,
141 &mut CustomMediaEvaluator::none(),
142 )
143 }
144}
145
146impl MediaListMethods<crate::DomTypeHolder> for MediaList {
147 fn MediaText(&self) -> DOMString {
149 let guard = self.shared_lock().read();
150 DOMString::from(
151 self.media_queries
152 .borrow()
153 .read_with(&guard)
154 .to_css_string(),
155 )
156 }
157
158 fn SetMediaText(&self, value: DOMString) {
160 self.parent_stylesheet.will_modify();
161 let global = self.global();
162 let mut guard = self.shared_lock().write();
163 let media_queries_borrowed = self.media_queries.borrow();
164 let media_queries = media_queries_borrowed.write_with(&mut guard);
165 *media_queries = Self::parse_media_list(&value.str(), global.as_window());
166 self.parent_stylesheet.notify_invalidations();
167 }
168
169 fn Length(&self) -> u32 {
171 let guard = self.shared_lock().read();
172 self.media_queries
173 .borrow()
174 .read_with(&guard)
175 .media_queries
176 .len() as u32
177 }
178
179 fn Item(&self, index: u32) -> Option<DOMString> {
181 let guard = self.shared_lock().read();
182 self.media_queries
183 .borrow()
184 .read_with(&guard)
185 .media_queries
186 .get(index as usize)
187 .map(|query| query.to_css_string().into())
188 }
189
190 fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
192 self.Item(index)
193 }
194
195 fn AppendMedium(&self, medium: DOMString) {
197 let global = self.global();
199 let medium = medium.str();
200 let m = Self::parse_media_query(&medium, global.as_window());
201 if m.is_err() {
203 return;
204 }
205 {
207 let m_serialized = m.clone().unwrap().to_css_string();
208 let guard = self.shared_lock().read();
209 let any = self
210 .media_queries
211 .borrow()
212 .read_with(&guard)
213 .media_queries
214 .iter()
215 .any(|q| m_serialized == q.to_css_string());
216 if any {
217 return;
218 }
219 }
220 self.parent_stylesheet.will_modify();
222 let mut guard = self.shared_lock().write();
223 self.media_queries
224 .borrow()
225 .write_with(&mut guard)
226 .media_queries
227 .push(m.unwrap());
228 self.parent_stylesheet.notify_invalidations();
229 }
230
231 fn DeleteMedium(&self, medium: DOMString) {
233 let global = self.global();
235 let medium = medium.str();
236 let m = Self::parse_media_query(&medium, global.as_window());
237 if m.is_err() {
239 return;
240 }
241 self.parent_stylesheet.will_modify();
243 let m_serialized = m.unwrap().to_css_string();
244 let mut guard = self.shared_lock().write();
245 let media_queries_borrowed = self.media_queries.borrow();
246 let media_list = media_queries_borrowed.write_with(&mut guard);
247 let new_vec = media_list
248 .media_queries
249 .drain(..)
250 .filter(|q| m_serialized != q.to_css_string())
251 .collect();
252 media_list.media_queries = new_vec;
253 self.parent_stylesheet.notify_invalidations();
254 }
255}