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::window::Window;
23use crate::script_runtime::CanGc;
24
25#[dom_struct]
26pub(crate) struct MediaList {
27 reflector_: Reflector,
28 parent_stylesheet: Dom<CSSStyleSheet>,
29 #[ignore_malloc_size_of = "Arc"]
30 #[no_trace]
31 media_queries: RefCell<Arc<Locked<StyleMediaList>>>,
32}
33
34impl MediaList {
35 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
36 pub(crate) fn new_inherited(
37 parent_stylesheet: &CSSStyleSheet,
38 media_queries: Arc<Locked<StyleMediaList>>,
39 ) -> MediaList {
40 MediaList {
41 parent_stylesheet: Dom::from_ref(parent_stylesheet),
42 reflector_: Reflector::new(),
43 media_queries: RefCell::new(media_queries),
44 }
45 }
46
47 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
48 pub(crate) fn new(
49 window: &Window,
50 parent_stylesheet: &CSSStyleSheet,
51 media_queries: Arc<Locked<StyleMediaList>>,
52 can_gc: CanGc,
53 ) -> DomRoot<MediaList> {
54 reflect_dom_object(
55 Box::new(MediaList::new_inherited(parent_stylesheet, media_queries)),
56 window,
57 can_gc,
58 )
59 }
60
61 fn shared_lock(&self) -> &SharedRwLock {
62 self.parent_stylesheet.shared_lock()
63 }
64
65 pub(crate) fn parse_media_list(value: &str, window: &Window) -> StyleMediaList {
67 if value.is_empty() {
68 return StyleMediaList::empty();
69 }
70 let mut input = ParserInput::new(value);
71 let mut parser = Parser::new(&mut input);
72 let url_data = UrlExtraData(window.get_url().get_arc());
73 let quirks_mode = window.Document().quirks_mode();
74 let context = ParserContext::new(
78 Origin::Author,
79 &url_data,
80 Some(CssRuleType::Media),
81 ParsingMode::DEFAULT,
82 quirks_mode,
83 Default::default(),
84 window.css_error_reporter(),
85 None,
86 );
87 StyleMediaList::parse(&context, &mut parser)
88 }
89
90 pub(crate) fn parse_media_query<'i>(
92 value: &'i str,
93 window: &Window,
94 ) -> Result<MediaQuery, ParseError<'i>> {
95 let mut input = ParserInput::new(value);
96 let mut parser = Parser::new(&mut input);
97 let url_data = UrlExtraData(window.get_url().get_arc());
98 let quirks_mode = window.Document().quirks_mode();
99 let context = ParserContext::new(
100 Origin::Author,
101 &url_data,
102 Some(CssRuleType::Media),
103 ParsingMode::DEFAULT,
104 quirks_mode,
105 Default::default(),
106 window.css_error_reporter(),
107 None,
108 );
109 MediaQuery::parse(&context, &mut parser)
110 }
111
112 pub(crate) fn clone_media_list(&self) -> StyleMediaList {
113 let guard = self.shared_lock().read();
114 self.media_queries.borrow().read_with(&guard).clone()
115 }
116
117 pub(crate) fn update_media_list(&self, media_queries: Arc<Locked<StyleMediaList>>) {
118 *self.media_queries.borrow_mut() = media_queries;
119 }
120}
121
122impl MediaListMethods<crate::DomTypeHolder> for MediaList {
123 fn MediaText(&self) -> DOMString {
125 let guard = self.shared_lock().read();
126 DOMString::from(
127 self.media_queries
128 .borrow()
129 .read_with(&guard)
130 .to_css_string(),
131 )
132 }
133
134 fn SetMediaText(&self, value: DOMString) {
136 self.parent_stylesheet.will_modify();
137 let global = self.global();
138 let mut guard = self.shared_lock().write();
139 let media_queries_borrowed = self.media_queries.borrow();
140 let media_queries = media_queries_borrowed.write_with(&mut guard);
141 *media_queries = Self::parse_media_list(&value, global.as_window());
142 self.parent_stylesheet.notify_invalidations();
143 }
144
145 fn Length(&self) -> u32 {
147 let guard = self.shared_lock().read();
148 self.media_queries
149 .borrow()
150 .read_with(&guard)
151 .media_queries
152 .len() as u32
153 }
154
155 fn Item(&self, index: u32) -> Option<DOMString> {
157 let guard = self.shared_lock().read();
158 self.media_queries
159 .borrow()
160 .read_with(&guard)
161 .media_queries
162 .get(index as usize)
163 .map(|query| query.to_css_string().into())
164 }
165
166 fn IndexedGetter(&self, index: u32) -> Option<DOMString> {
168 self.Item(index)
169 }
170
171 fn AppendMedium(&self, medium: DOMString) {
173 let global = self.global();
175 let m = Self::parse_media_query(&medium, global.as_window());
176 if m.is_err() {
178 return;
179 }
180 {
182 let m_serialized = m.clone().unwrap().to_css_string();
183 let guard = self.shared_lock().read();
184 let any = self
185 .media_queries
186 .borrow()
187 .read_with(&guard)
188 .media_queries
189 .iter()
190 .any(|q| m_serialized == q.to_css_string());
191 if any {
192 return;
193 }
194 }
195 self.parent_stylesheet.will_modify();
197 let mut guard = self.shared_lock().write();
198 self.media_queries
199 .borrow()
200 .write_with(&mut guard)
201 .media_queries
202 .push(m.unwrap());
203 self.parent_stylesheet.notify_invalidations();
204 }
205
206 fn DeleteMedium(&self, medium: DOMString) {
208 let global = self.global();
210 let m = Self::parse_media_query(&medium, global.as_window());
211 if m.is_err() {
213 return;
214 }
215 self.parent_stylesheet.will_modify();
217 let m_serialized = m.unwrap().to_css_string();
218 let mut guard = self.shared_lock().write();
219 let media_queries_borrowed = self.media_queries.borrow();
220 let media_list = media_queries_borrowed.write_with(&mut guard);
221 let new_vec = media_list
222 .media_queries
223 .drain(..)
224 .filter(|q| m_serialized != q.to_css_string())
225 .collect();
226 media_list.media_queries = new_vec;
227 self.parent_stylesheet.notify_invalidations();
228 }
229}