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::css::parser_context_for_document;
17use crate::dom::bindings::codegen::Bindings::MediaListBinding::MediaListMethods;
18use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
19use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
20use crate::dom::bindings::root::{Dom, DomRoot};
21use crate::dom::bindings::str::DOMString;
22use crate::dom::css::cssstylesheet::CSSStyleSheet;
23use crate::dom::document::Document;
24use crate::dom::node::NodeTraits;
25use crate::dom::window::Window;
26use crate::script_runtime::CanGc;
27
28#[dom_struct]
29pub(crate) struct MediaList {
30 reflector_: Reflector,
31 parent_stylesheet: Dom<CSSStyleSheet>,
32 #[ignore_malloc_size_of = "Stylo"]
33 #[no_trace]
34 media_queries: RefCell<Arc<Locked<StyleMediaList>>>,
35}
36
37impl MediaList {
38 pub(crate) fn new_inherited(
39 parent_stylesheet: &CSSStyleSheet,
40 media_queries: Arc<Locked<StyleMediaList>>,
41 ) -> MediaList {
42 MediaList {
43 parent_stylesheet: Dom::from_ref(parent_stylesheet),
44 reflector_: Reflector::new(),
45 media_queries: RefCell::new(media_queries),
46 }
47 }
48
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 document = window.Document();
74 let url_data = UrlExtraData(document.owner_global().api_base_url().get_arc());
75 let context = parser_context_for_document(
79 &document,
80 CssRuleType::Media,
81 ParsingMode::DEFAULT,
82 &url_data,
83 );
84 StyleMediaList::parse(&context, &mut parser)
85 }
86
87 pub(crate) fn parse_media_query<'i>(
89 value: &'i str,
90 window: &Window,
91 ) -> Result<MediaQuery, ParseError<'i>> {
92 let mut input = ParserInput::new(value);
93 let mut parser = Parser::new(&mut input);
94 let document = window.Document();
95 let url_data = UrlExtraData(document.owner_global().api_base_url().get_arc());
96 let context = parser_context_for_document(
97 &document,
98 CssRuleType::Media,
99 ParsingMode::DEFAULT,
100 &url_data,
101 );
102 MediaQuery::parse(&context, &mut parser)
103 }
104
105 pub(crate) fn clone_media_list(&self) -> StyleMediaList {
106 let guard = self.shared_lock().read();
107 self.media_queries.borrow().read_with(&guard).clone()
108 }
109
110 pub(crate) fn update_media_list(&self, media_queries: Arc<Locked<StyleMediaList>>) {
111 *self.media_queries.borrow_mut() = media_queries;
112 }
113
114 pub(crate) fn matches_environment(document: &Document, media_query: &str) -> bool {
116 let quirks_mode = document.quirks_mode();
117 let url_data = UrlExtraData(document.owner_global().api_base_url().get_arc());
118 let context = ParserContext::new(
124 Origin::Author,
125 &url_data,
126 Some(CssRuleType::Style),
127 ParsingMode::all(),
128 quirks_mode,
129 Default::default(),
130 None,
131 None,
132 );
133 let mut parser_input = ParserInput::new(media_query);
134 let mut parser = Parser::new(&mut parser_input);
135 let media_list = StyleMediaList::parse(&context, &mut parser);
136 media_list.evaluate(
137 document.window().layout().device(),
138 quirks_mode,
139 &mut CustomMediaEvaluator::none(),
140 )
141 }
142}
143
144impl MediaListMethods<crate::DomTypeHolder> for MediaList {
145 fn MediaText(&self) -> DOMString {
147 let guard = self.shared_lock().read();
148 DOMString::from(
149 self.media_queries
150 .borrow()
151 .read_with(&guard)
152 .to_css_string(),
153 )
154 }
155
156 fn SetMediaText(&self, value: DOMString) {
158 self.parent_stylesheet.will_modify();
159 let global = self.global();
160 let mut guard = self.shared_lock().write();
161 let media_queries_borrowed = self.media_queries.borrow();
162 let media_queries = media_queries_borrowed.write_with(&mut guard);
163 *media_queries = Self::parse_media_list(&value.str(), global.as_window());
164 self.parent_stylesheet.notify_invalidations();
165 }
166
167 fn Length(&self) -> u32 {
169 let guard = self.shared_lock().read();
170 self.media_queries
171 .borrow()
172 .read_with(&guard)
173 .media_queries
174 .len() as u32
175 }
176
177 fn Item(&self, index: u32) -> Option<DOMString> {
179 let guard = self.shared_lock().read();
180 self.media_queries
181 .borrow()
182 .read_with(&guard)
183 .media_queries
184 .get(index as usize)
185 .map(|query| query.to_css_string().into())
186 }
187
188 fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
190 self.Item(index)
191 }
192
193 fn AppendMedium(&self, medium: DOMString) {
195 let global = self.global();
197 let medium = medium.str();
198 let m = Self::parse_media_query(&medium, global.as_window());
199 if m.is_err() {
201 return;
202 }
203 {
205 let m_serialized = m.clone().unwrap().to_css_string();
206 let guard = self.shared_lock().read();
207 let any = self
208 .media_queries
209 .borrow()
210 .read_with(&guard)
211 .media_queries
212 .iter()
213 .any(|q| m_serialized == q.to_css_string());
214 if any {
215 return;
216 }
217 }
218 self.parent_stylesheet.will_modify();
220 let mut guard = self.shared_lock().write();
221 self.media_queries
222 .borrow()
223 .write_with(&mut guard)
224 .media_queries
225 .push(m.unwrap());
226 self.parent_stylesheet.notify_invalidations();
227 }
228
229 fn DeleteMedium(&self, medium: DOMString) {
231 let global = self.global();
233 let medium = medium.str();
234 let m = Self::parse_media_query(&medium, global.as_window());
235 if m.is_err() {
237 return;
238 }
239 self.parent_stylesheet.will_modify();
241 let m_serialized = m.unwrap().to_css_string();
242 let mut guard = self.shared_lock().write();
243 let media_queries_borrowed = self.media_queries.borrow();
244 let media_list = media_queries_borrowed.write_with(&mut guard);
245 let new_vec = media_list
246 .media_queries
247 .drain(..)
248 .filter(|q| m_serialized != q.to_css_string())
249 .collect();
250 media_list.media_queries = new_vec;
251 self.parent_stylesheet.notify_invalidations();
252 }
253}