Skip to main content

script/dom/media/
mediaquerylist.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use script_bindings::callback::RootedCallback;
11use script_bindings::reflector::reflect_weak_referenceable_dom_object;
12use style::media_queries::MediaList;
13use style::stylesheets::CustomMediaEvaluator;
14use style_traits::ToCss;
15
16use crate::dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
17use crate::dom::bindings::codegen::Bindings::EventTargetBinding::{
18    AddEventListenerOptions, EventListenerOptions,
19};
20use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryListMethods;
21use crate::dom::bindings::inheritance::Castable;
22use crate::dom::bindings::root::{Dom, DomRoot};
23use crate::dom::bindings::str::DOMString;
24use crate::dom::document::Document;
25use crate::dom::eventtarget::EventTarget;
26
27pub(crate) enum MediaQueryListMatchState {
28    Same,
29    Changed,
30}
31
32#[dom_struct]
33pub(crate) struct MediaQueryList {
34    eventtarget: EventTarget,
35    document: Dom<Document>,
36    #[no_trace]
37    media_query_list: MediaList,
38    last_match_state: Cell<Option<bool>>,
39}
40
41impl MediaQueryList {
42    fn new_inherited(document: &Document, media_query_list: MediaList) -> MediaQueryList {
43        MediaQueryList {
44            eventtarget: EventTarget::new_inherited(),
45            document: Dom::from_ref(document),
46            media_query_list,
47            last_match_state: Cell::new(None),
48        }
49    }
50
51    pub(crate) fn new(
52        cx: &mut JSContext,
53        document: &Document,
54        media_query_list: MediaList,
55    ) -> DomRoot<MediaQueryList> {
56        reflect_weak_referenceable_dom_object(
57            cx,
58            Rc::new(MediaQueryList::new_inherited(document, media_query_list)),
59            document.window(),
60        )
61    }
62}
63
64impl MediaQueryList {
65    pub(crate) fn evaluate_changes(&self) -> MediaQueryListMatchState {
66        let matches = self.evaluate();
67
68        let result = if let Some(old_matches) = self.last_match_state.get() {
69            if old_matches == matches {
70                MediaQueryListMatchState::Same
71            } else {
72                MediaQueryListMatchState::Changed
73            }
74        } else {
75            MediaQueryListMatchState::Changed
76        };
77
78        self.last_match_state.set(Some(matches));
79        result
80    }
81
82    pub(crate) fn evaluate(&self) -> bool {
83        let quirks_mode = self.document.quirks_mode();
84        self.media_query_list.evaluate(
85            self.document.window().layout().device(),
86            quirks_mode,
87            &mut CustomMediaEvaluator::none(),
88        )
89    }
90}
91
92impl MediaQueryListMethods<crate::DomTypeHolder> for MediaQueryList {
93    /// <https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-media>
94    fn Media(&self) -> DOMString {
95        self.media_query_list.to_css_string().into()
96    }
97
98    /// <https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-matches>
99    fn Matches(&self) -> bool {
100        match self.last_match_state.get() {
101            None => self.evaluate(),
102            Some(state) => state,
103        }
104    }
105
106    /// <https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-addlistener>
107    fn AddListener(&self, listener: Option<RootedCallback<EventListener>>) {
108        self.upcast::<EventTarget>().add_event_listener(
109            DOMString::from_static("change"),
110            listener,
111            AddEventListenerOptions {
112                parent: EventListenerOptions { capture: false },
113                once: false,
114                passive: None,
115                signal: None,
116            },
117        );
118    }
119
120    /// <https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-removelistener>
121    fn RemoveListener(&self, listener: Option<RootedCallback<EventListener>>) {
122        self.upcast::<EventTarget>().remove_event_listener(
123            DOMString::from_static("change"),
124            listener.as_deref(),
125            &EventListenerOptions { capture: false },
126        );
127    }
128
129    // https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-onchange
130    event_handler!(change, GetOnchange, SetOnchange);
131}