aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/mediaquerylist.rs
diff options
context:
space:
mode:
authorJack Moffitt <jack@metajack.im>2016-09-26 02:41:09 -0600
committerJack Moffitt <jack@metajack.im>2016-11-02 13:40:22 -0600
commit138a0480fee8e6f0a338d85160aca49f14e245c9 (patch)
tree3a1fd2588c8431859e010a31172d04dda809618b /components/script/dom/mediaquerylist.rs
parent99ad3678fa6490b83d209fd1648d49f074bb8c16 (diff)
downloadservo-138a0480fee8e6f0a338d85160aca49f14e245c9.tar.gz
servo-138a0480fee8e6f0a338d85160aca49f14e245c9.zip
Implement matchMedia and MediaQueryList
Fixes #13376.
Diffstat (limited to 'components/script/dom/mediaquerylist.rs')
-rw-r--r--components/script/dom/mediaquerylist.rs156
1 files changed, 156 insertions, 0 deletions
diff --git a/components/script/dom/mediaquerylist.rs b/components/script/dom/mediaquerylist.rs
new file mode 100644
index 00000000000..30e510171b4
--- /dev/null
+++ b/components/script/dom/mediaquerylist.rs
@@ -0,0 +1,156 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use cssparser::ToCss;
+use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
+use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
+use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
+use dom::bindings::codegen::Bindings::MediaQueryListBinding::{self, MediaQueryListMethods};
+use dom::bindings::inheritance::Castable;
+use dom::bindings::js::{JS, Root};
+use dom::bindings::reflector::reflect_dom_object;
+use dom::bindings::str::DOMString;
+use dom::bindings::trace::JSTraceable;
+use dom::bindings::weakref::{WeakRef, WeakRefVec};
+use dom::document::Document;
+use dom::eventtarget::EventTarget;
+use euclid::scale_factor::ScaleFactor;
+use js::jsapi::JSTracer;
+use std::cell::Cell;
+use std::rc::Rc;
+use style;
+use style::media_queries::{Device, MediaType};
+use style_traits::{PagePx, ViewportPx};
+
+pub enum MediaQueryListMatchState {
+ Same(bool),
+ Changed(bool),
+}
+
+#[dom_struct]
+pub struct MediaQueryList {
+ eventtarget: EventTarget,
+ document: JS<Document>,
+ media_query_list: style::media_queries::MediaQueryList,
+ last_match_state: Cell<Option<bool>>
+}
+
+impl MediaQueryList {
+ fn new_inherited(document: &Document,
+ media_query_list: style::media_queries::MediaQueryList) -> MediaQueryList {
+ MediaQueryList {
+ eventtarget: EventTarget::new_inherited(),
+ document: JS::from_ref(document),
+ media_query_list: media_query_list,
+ last_match_state: Cell::new(None),
+ }
+ }
+
+ pub fn new(document: &Document,
+ media_query_list: style::media_queries::MediaQueryList) -> Root<MediaQueryList> {
+ reflect_dom_object(box MediaQueryList::new_inherited(document, media_query_list),
+ document.window(),
+ MediaQueryListBinding::Wrap)
+ }
+}
+
+impl MediaQueryList {
+ fn evaluate_changes(&self) -> MediaQueryListMatchState {
+ let matches = self.evaluate();
+
+ let result = if let Some(old_matches) = self.last_match_state.get() {
+ if old_matches == matches {
+ MediaQueryListMatchState::Same(matches)
+ } else {
+ MediaQueryListMatchState::Changed(matches)
+ }
+ } else {
+ MediaQueryListMatchState::Changed(matches)
+ };
+
+ self.last_match_state.set(Some(matches));
+ result
+ }
+
+ pub fn evaluate(&self) -> bool {
+ if let Some(window_size) = self.document.window().window_size() {
+ let viewport_size = window_size.visible_viewport;
+ // TODO: support real ViewportPx, including zoom level
+ // This information seems not to be tracked currently, so we assume
+ // ViewportPx == PagePx
+ let page_to_viewport: ScaleFactor<f32, PagePx, ViewportPx> = ScaleFactor::new(1.0);
+ let device = Device::new(MediaType::Screen, viewport_size * page_to_viewport);
+ self.media_query_list.evaluate(&device)
+ } else {
+ false
+ }
+ }
+}
+
+impl MediaQueryListMethods for MediaQueryList {
+ // https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-media
+ fn Media(&self) -> DOMString {
+ let mut s = String::new();
+ self.media_query_list.to_css(&mut s).unwrap();
+ DOMString::from_string(s)
+ }
+
+ // https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-matches
+ fn Matches(&self) -> bool {
+ match self.last_match_state.get() {
+ None => self.evaluate(),
+ Some(state) => state,
+ }
+ }
+
+ // https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-addlistener
+ fn AddListener(&self, listener: Option<Rc<EventListener>>) {
+ self.upcast::<EventTarget>().AddEventListener(DOMString::from_string("change".to_owned()),
+ listener, false);
+ }
+
+ // https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-removelistener
+ fn RemoveListener(&self, listener: Option<Rc<EventListener>>) {
+ self.upcast::<EventTarget>().RemoveEventListener(DOMString::from_string("change".to_owned()),
+ listener, false);
+ }
+
+ // https://drafts.csswg.org/cssom-view/#dom-mediaquerylist-onchange
+ event_handler!(change, GetOnchange, SetOnchange);
+}
+
+#[derive(HeapSizeOf)]
+pub struct WeakMediaQueryListVec {
+ cell: DOMRefCell<WeakRefVec<MediaQueryList>>,
+}
+
+#[allow(unsafe_code)]
+impl WeakMediaQueryListVec {
+ /// Create a new vector of weak references to MediaQueryList
+ pub fn new() -> Self {
+ WeakMediaQueryListVec { cell: DOMRefCell::new(WeakRefVec::new()) }
+ }
+
+ pub fn push(&self, mql: &MediaQueryList) {
+ self.cell.borrow_mut().push(WeakRef::new(mql));
+ }
+
+ /// Evaluate media query lists and report changes
+ /// https://drafts.csswg.org/cssom-view/#evaluate-media-queries-and-report-changes
+ pub fn evaluate_and_report_changes(&self) {
+ for mql in self.cell.borrow().iter() {
+ if let MediaQueryListMatchState::Changed(_) = mql.root().unwrap().evaluate_changes() {
+ mql.root().unwrap().upcast::<EventTarget>().fire_simple_event("change");
+ }
+ }
+ }
+}
+
+#[allow(unsafe_code)]
+impl JSTraceable for WeakMediaQueryListVec {
+ fn trace(&self, _: *mut JSTracer) {
+ self.cell.borrow_mut().retain_alive()
+ }
+}