aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2016-11-02 14:51:12 -0500
committerGitHub <noreply@github.com>2016-11-02 14:51:12 -0500
commit6ef46ab9e4ec08d5f5226d41f0cac77c3584bae9 (patch)
tree1eecad9e272742998500a50421130c9131bbe4b8
parent021cabd3e6242b3537addec32010ee8cee556121 (diff)
parent138a0480fee8e6f0a338d85160aca49f14e245c9 (diff)
downloadservo-6ef46ab9e4ec08d5f5226d41f0cac77c3584bae9.tar.gz
servo-6ef46ab9e4ec08d5f5226d41f0cac77c3584bae9.zip
Auto merge of #13453 - metajack:media-query-list, r=jdm
Implement matchMedia and MediaQueryList <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #13376 (github issue number if applicable). <!-- Either: --> - [x] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Fixes #13376. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13453) <!-- Reviewable:end -->
-rw-r--r--components/script/Cargo.toml1
-rw-r--r--components/script/dom/bindings/codegen/Bindings.conf6
-rw-r--r--components/script/dom/bindings/trace.rs3
-rw-r--r--components/script/dom/mediaquerylist.rs156
-rw-r--r--components/script/dom/mod.rs1
-rw-r--r--components/script/dom/webidls/MediaQueryList.webidl14
-rw-r--r--components/script/dom/webidls/Window.webidl2
-rw-r--r--components/script/dom/window.rs25
-rw-r--r--components/script/lib.rs1
-rw-r--r--components/script/script_thread.rs5
-rw-r--r--components/servo/Cargo.lock1
-rw-r--r--components/style/lib.rs15
-rw-r--r--components/style/media_queries.rs63
-rw-r--r--ports/cef/Cargo.lock1
-rw-r--r--tests/unit/style/media_queries.rs9
-rw-r--r--tests/wpt/metadata-css/cssom-view-1_dev/html/window-interface.htm.ini3
-rw-r--r--tests/wpt/metadata/html/browsers/the-window-object/window-properties.html.ini3
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json6
-rw-r--r--tests/wpt/mozilla/meta/css/matchMedia.html.ini15
-rw-r--r--tests/wpt/mozilla/tests/css/blank.html1
-rw-r--r--tests/wpt/mozilla/tests/css/matchMedia.html188
-rw-r--r--tests/wpt/mozilla/tests/mozilla/interfaces.html1
22 files changed, 500 insertions, 20 deletions
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml
index f8f0b8f0d4b..7d98caec983 100644
--- a/components/script/Cargo.toml
+++ b/components/script/Cargo.toml
@@ -68,6 +68,7 @@ serde = "0.8"
smallvec = "0.1"
string_cache = {version = "0.2.26", features = ["heap_size", "unstable"]}
style = {path = "../style"}
+style_traits = {path = "../style_traits"}
time = "0.1.12"
url = {version = "1.2", features = ["heap_size", "query_encoding"]}
util = {path = "../util"}
diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf
index 434450d2bcc..5647679f446 100644
--- a/components/script/dom/bindings/codegen/Bindings.conf
+++ b/components/script/dom/bindings/codegen/Bindings.conf
@@ -14,12 +14,16 @@
DOMInterfaces = {
+'MediaQueryList': {
+ 'weakReferenceable': True,
+},
+
'Promise': {
'spiderMonkeyInterface': True,
},
'Range': {
- 'weakReferenceable': True,
+ 'weakReferenceable': True,
},
#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 9ac91f7078c..e0f65d9f6e1 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -91,6 +91,7 @@ use std::time::{SystemTime, Instant};
use string_cache::{Atom, Namespace, QualName};
use style::attr::{AttrIdentifier, AttrValue, LengthOrPercentageOrAuto};
use style::element_state::*;
+use style::media_queries::MediaQueryList;
use style::properties::PropertyDeclarationBlock;
use style::selector_impl::{ElementSnapshot, PseudoElement};
use style::values::specified::Length;
@@ -367,7 +368,7 @@ no_jsmanaged_fields!(WebGLProgramId);
no_jsmanaged_fields!(WebGLRenderbufferId);
no_jsmanaged_fields!(WebGLShaderId);
no_jsmanaged_fields!(WebGLTextureId);
-
+no_jsmanaged_fields!(MediaQueryList);
impl JSTraceable for Box<ScriptChan + Send> {
#[inline]
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()
+ }
+}
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index 4c6c7c941c4..14498823424 100644
--- a/components/script/dom/mod.rs
+++ b/components/script/dom/mod.rs
@@ -357,6 +357,7 @@ pub mod imagedata;
pub mod keyboardevent;
pub mod location;
pub mod mediaerror;
+pub mod mediaquerylist;
pub mod messageevent;
pub mod mimetype;
pub mod mimetypearray;
diff --git a/components/script/dom/webidls/MediaQueryList.webidl b/components/script/dom/webidls/MediaQueryList.webidl
new file mode 100644
index 00000000000..2991efa89a8
--- /dev/null
+++ b/components/script/dom/webidls/MediaQueryList.webidl
@@ -0,0 +1,14 @@
+/* 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/. */
+
+// https://drafts.csswg.org/cssom-view/#mediaquerylist
+
+[Exposed=(Window)]
+interface MediaQueryList : EventTarget {
+ readonly attribute DOMString media;
+ readonly attribute boolean matches;
+ void addListener(EventListener? listener);
+ void removeListener(EventListener? listener);
+ attribute EventHandler onchange;
+};
diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl
index 87cc3f3e486..ad3494b6f5f 100644
--- a/components/script/dom/webidls/Window.webidl
+++ b/components/script/dom/webidls/Window.webidl
@@ -120,7 +120,7 @@ dictionary ScrollToOptions : ScrollOptions {
// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-window-interface
partial interface Window {
- //MediaQueryList matchMedia(DOMString query);
+ [Exposed=(Window), NewObject] MediaQueryList matchMedia(DOMString query);
[SameObject] readonly attribute Screen screen;
// browsing context
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 5afbf475cc9..099e50f4820 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use app_units::Au;
+use cssparser::Parser;
use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType};
use dom::bindings::callback::ExceptionHandling;
use dom::bindings::cell::DOMRefCell;
@@ -35,6 +36,7 @@ use dom::globalscope::GlobalScope;
use dom::history::History;
use dom::htmliframeelement::build_mozbrowser_custom_event;
use dom::location::Location;
+use dom::mediaquerylist::{MediaQueryList, WeakMediaQueryListVec};
use dom::messageevent::MessageEvent;
use dom::navigator::Navigator;
use dom::node::{Node, from_untrusted_node_address, window_from_node};
@@ -86,6 +88,7 @@ use std::sync::mpsc::TryRecvError::{Disconnected, Empty};
use string_cache::Atom;
use style::context::ReflowGoal;
use style::error_reporting::ParseErrorReporter;
+use style::media_queries;
use style::properties::longhands::overflow_x;
use style::selector_impl::PseudoElement;
use style::str::HTML_SPACE_CHARACTERS;
@@ -233,6 +236,9 @@ pub struct Window {
/// A list of scroll offsets for each scrollable element.
scroll_offsets: DOMRefCell<HashMap<UntrustedNodeAddress, Point2D<f32>>>,
+
+ /// All the MediaQueryLists we need to update
+ media_query_lists: WeakMediaQueryListVec,
}
impl Window {
@@ -309,6 +315,10 @@ impl Window {
pub fn set_scroll_offsets(&self, offsets: HashMap<UntrustedNodeAddress, Point2D<f32>>) {
*self.scroll_offsets.borrow_mut() = offsets
}
+
+ pub fn current_viewport(&self) -> Rect<Au> {
+ self.current_viewport.clone().get()
+ }
}
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
@@ -856,6 +866,16 @@ impl WindowMethods for Window {
}
}
+ // https://drafts.csswg.org/cssom-view/#dom-window-matchmedia
+ fn MatchMedia(&self, query: DOMString) -> Root<MediaQueryList> {
+ let mut parser = Parser::new(&query);
+ let media_query_list = media_queries::parse_media_query_list(&mut parser);
+ let document = self.Document();
+ let mql = MediaQueryList::new(&document, media_query_list);
+ self.media_query_lists.push(&*mql);
+ mql
+ }
+
#[allow(unrooted_must_root)]
// https://fetch.spec.whatwg.org/#fetch-method
fn Fetch(&self, input: RequestOrUSVString, init: &RequestInit) -> Rc<Promise> {
@@ -1477,6 +1497,10 @@ impl Window {
let custom_event = build_mozbrowser_custom_event(&self, event);
custom_event.upcast::<Event>().fire(self.upcast());
}
+
+ pub fn evaluate_media_queries_and_report_changes(&self) {
+ self.media_query_lists.evaluate_and_report_changes();
+ }
}
impl Window {
@@ -1563,6 +1587,7 @@ impl Window {
ignore_further_async_events: Arc::new(AtomicBool::new(false)),
error_reporter: error_reporter,
scroll_offsets: DOMRefCell::new(HashMap::new()),
+ media_query_lists: WeakMediaQueryListVec::new(),
};
WindowBinding::Wrap(runtime.cx(), win)
diff --git a/components/script/lib.rs b/components/script/lib.rs
index f2227fc363a..c679a5950d3 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -81,6 +81,7 @@ extern crate smallvec;
#[macro_use(atom, ns)] extern crate string_cache;
#[macro_use]
extern crate style;
+extern crate style_traits;
extern crate time;
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
extern crate tinyfiledialogs;
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index ec3bc5b1b34..a028cd66ad5 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -2099,6 +2099,11 @@ impl ScriptThread {
0i32);
uievent.upcast::<Event>().fire(window.upcast());
}
+
+ // https://html.spec.whatwg.org/multipage/#event-loop-processing-model
+ // Step 7.7 - evaluate media queries and report changes
+ // Since we have resized, we need to re-evaluate MQLs
+ window.evaluate_media_queries_and_report_changes();
}
/// Initiate a non-blocking fetch for a specified resource. Stores the InProgressLoad
diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock
index 240cd6170da..a851a6ae48e 100644
--- a/components/servo/Cargo.lock
+++ b/components/servo/Cargo.lock
@@ -1976,6 +1976,7 @@ dependencies = [
"smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"string_cache 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
"style 0.0.1",
+ "style_traits 0.0.1",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"tinyfiledialogs 0.1.0 (git+https://github.com/jdm/tinyfiledialogs)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/components/style/lib.rs b/components/style/lib.rs
index 53d8102b6fa..de708d7a637 100644
--- a/components/style/lib.rs
+++ b/components/style/lib.rs
@@ -137,6 +137,8 @@ pub mod values;
pub mod viewport;
pub mod workqueue;
+use cssparser::ToCss;
+use std::fmt;
use std::sync::Arc;
/// The CSS properties supported by the style system.
@@ -176,3 +178,16 @@ pub fn arc_ptr_eq<T: 'static>(a: &Arc<T>, b: &Arc<T>) -> bool {
let b: &T = &**b;
(a as *const T) == (b as *const T)
}
+
+pub fn serialize_comma_separated_list<W, T>(dest: &mut W, list: &[T])
+ -> fmt::Result where W: fmt::Write, T: ToCss {
+ if list.len() > 0 {
+ for item in &list[..list.len()-1] {
+ try!(item.to_css(dest));
+ try!(write!(dest, ", "));
+ }
+ list[list.len()-1].to_css(dest)
+ } else {
+ Ok(())
+ }
+}
diff --git a/components/style/media_queries.rs b/components/style/media_queries.rs
index 0537fc9fdac..216e1c33a42 100644
--- a/components/style/media_queries.rs
+++ b/components/style/media_queries.rs
@@ -7,9 +7,12 @@
//! [mq]: https://drafts.csswg.org/mediaqueries/
use app_units::Au;
-use cssparser::{Delimiter, Parser, Token};
+use cssparser::{Delimiter, Parser, ToCss, Token};
use euclid::size::{Size2D, TypedSize2D};
use properties::longhands;
+use serialize_comma_separated_list;
+use std::fmt::{self, Write};
+use string_cache::Atom;
use style_traits::ViewportPx;
use values::specified;
@@ -20,6 +23,14 @@ pub struct MediaQueryList {
pub media_queries: Vec<MediaQuery>
}
+impl ToCss for MediaQueryList {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+ where W: fmt::Write
+ {
+ serialize_comma_separated_list(dest, &self.media_queries)
+ }
+}
+
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Range<T> {
@@ -104,20 +115,58 @@ impl MediaQuery {
}
}
+impl ToCss for MediaQuery {
+ fn to_css<W>(&self, dest: &mut W) -> fmt::Result
+ where W: fmt::Write
+ {
+ if self.qualifier == Some(Qualifier::Not) {
+ try!(write!(dest, "not "));
+ }
+
+ let mut type_ = String::new();
+ match self.media_type {
+ MediaQueryType::All => try!(write!(type_, "all")),
+ MediaQueryType::MediaType(MediaType::Screen) => try!(write!(type_, "screen")),
+ MediaQueryType::MediaType(MediaType::Print) => try!(write!(type_, "print")),
+ MediaQueryType::MediaType(MediaType::Unknown(ref desc)) => try!(write!(type_, "{}", desc)),
+ };
+ if self.expressions.is_empty() {
+ return write!(dest, "{}", type_)
+ } else if type_ != "all" || self.qualifier == Some(Qualifier::Not) {
+ try!(write!(dest, "{} and ", type_));
+ }
+ for (i, &e) in self.expressions.iter().enumerate() {
+ try!(write!(dest, "("));
+ let (mm, l) = match e {
+ Expression::Width(Range::Min(ref l)) => ("min", l),
+ Expression::Width(Range::Max(ref l)) => ("max", l),
+ };
+ try!(write!(dest, "{}-width: ", mm));
+ try!(l.to_css(dest));
+ if i == self.expressions.len() - 1 {
+ try!(write!(dest, ")"));
+ } else {
+ try!(write!(dest, ") and "));
+ }
+ }
+ Ok(())
+ }
+}
+
/// http://dev.w3.org/csswg/mediaqueries-3/#media0
-#[derive(PartialEq, Eq, Copy, Clone, Debug)]
+#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum MediaQueryType {
All, // Always true
MediaType(MediaType),
}
-#[derive(PartialEq, Eq, Copy, Clone, Debug)]
+#[derive(PartialEq, Eq, Clone, Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum MediaType {
Screen,
Print,
- Unknown,
+ Unknown(Atom),
}
#[derive(Debug)]
@@ -181,7 +230,7 @@ impl MediaQuery {
"screen" => MediaQueryType::MediaType(MediaType::Screen),
"print" => MediaQueryType::MediaType(MediaType::Print),
"all" => MediaQueryType::All,
- _ => MediaQueryType::MediaType(MediaType::Unknown)
+ _ => MediaQueryType::MediaType(MediaType::Unknown(Atom::from(&*ident)))
}
} else {
// Media type is only optional if qualifier is not specified.
@@ -233,8 +282,8 @@ impl MediaQueryList {
self.media_queries.iter().any(|mq| {
// Check if media matches. Unknown media never matches.
let media_match = match mq.media_type {
- MediaQueryType::MediaType(MediaType::Unknown) => false,
- MediaQueryType::MediaType(media_type) => media_type == device.media_type,
+ MediaQueryType::MediaType(MediaType::Unknown(_)) => false,
+ MediaQueryType::MediaType(ref media_type) => *media_type == device.media_type,
MediaQueryType::All => true,
};
diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock
index 47af71d1852..26207e9bba1 100644
--- a/ports/cef/Cargo.lock
+++ b/ports/cef/Cargo.lock
@@ -1827,6 +1827,7 @@ dependencies = [
"smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"string_cache 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
"style 0.0.1",
+ "style_traits 0.0.1",
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
"tinyfiledialogs 0.1.0 (git+https://github.com/jdm/tinyfiledialogs)",
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/tests/unit/style/media_queries.rs b/tests/unit/style/media_queries.rs
index 4565b57ae1c..46757bb9bf0 100644
--- a/tests/unit/style/media_queries.rs
+++ b/tests/unit/style/media_queries.rs
@@ -6,6 +6,7 @@ use app_units::Au;
use cssparser::{Parser, SourcePosition};
use euclid::size::TypedSize2D;
use std::borrow::ToOwned;
+use string_cache::Atom;
use style::error_reporting::ParseErrorReporter;
use style::media_queries::*;
use style::parser::ParserContextExtraData;
@@ -126,7 +127,7 @@ fn test_mq_unknown() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
- assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
+ assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown(Atom::from("fridge"))), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
@@ -134,7 +135,7 @@ fn test_mq_unknown() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Only), css.to_owned());
- assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
+ assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown(Atom::from("glass"))), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
@@ -142,7 +143,7 @@ fn test_mq_unknown() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == Some(Qualifier::Not), css.to_owned());
- assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
+ assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown(Atom::from("wood"))), css.to_owned());
assert!(q.expressions.len() == 0, css.to_owned());
});
}
@@ -247,7 +248,7 @@ fn test_mq_expressions() {
assert!(list.media_queries.len() == 1, css.to_owned());
let q = &list.media_queries[0];
assert!(q.qualifier == None, css.to_owned());
- assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown), css.to_owned());
+ assert!(q.media_type == MediaQueryType::MediaType(MediaType::Unknown(Atom::from("fridge"))), css.to_owned());
assert!(q.expressions.len() == 1, css.to_owned());
match q.expressions[0] {
Expression::Width(Range::Max(w)) => assert!(w == specified::Length::Absolute(Au::from_px(52))),
diff --git a/tests/wpt/metadata-css/cssom-view-1_dev/html/window-interface.htm.ini b/tests/wpt/metadata-css/cssom-view-1_dev/html/window-interface.htm.ini
index fb15f5287a8..b980676f43e 100644
--- a/tests/wpt/metadata-css/cssom-view-1_dev/html/window-interface.htm.ini
+++ b/tests/wpt/metadata-css/cssom-view-1_dev/html/window-interface.htm.ini
@@ -3,6 +3,3 @@
[window_inherited_functions]
expected: FAIL
- [window_functions]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/html/browsers/the-window-object/window-properties.html.ini b/tests/wpt/metadata/html/browsers/the-window-object/window-properties.html.ini
index 9af9ef4196b..905abfcd492 100644
--- a/tests/wpt/metadata/html/browsers/the-window-object/window-properties.html.ini
+++ b/tests/wpt/metadata/html/browsers/the-window-object/window-properties.html.ini
@@ -24,9 +24,6 @@
[Window method: getSelection]
expected: FAIL
- [Window method: matchMedia]
- expected: FAIL
-
[Window readonly attribute: applicationCache]
expected: FAIL
diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json
index fe55e10d9b2..9a3d311ad01 100644
--- a/tests/wpt/mozilla/meta/MANIFEST.json
+++ b/tests/wpt/mozilla/meta/MANIFEST.json
@@ -6566,6 +6566,12 @@
"url": "/_mozilla/css/float_relative_to_position.html"
}
],
+ "css/matchMedia.html": [
+ {
+ "path": "css/matchMedia.html",
+ "url": "/_mozilla/css/matchMedia.html"
+ }
+ ],
"css/media_calc_crash.html": [
{
"path": "css/media_calc_crash.html",
diff --git a/tests/wpt/mozilla/meta/css/matchMedia.html.ini b/tests/wpt/mozilla/meta/css/matchMedia.html.ini
new file mode 100644
index 00000000000..00c4a685473
--- /dev/null
+++ b/tests/wpt/mozilla/meta/css/matchMedia.html.ini
@@ -0,0 +1,15 @@
+[matchMedia.html]
+ type: testharness
+ expected: OK
+
+ [window.matchMedia exists]
+ expected: FAIL
+
+ [MediaQueryList.matches for "(min-aspect-ratio: 1/1)"]
+ expected: FAIL
+
+ [MediaQueryList.matches for "(width: 200px)"]
+ expected: FAIL
+
+ [Resize iframe from 200x100 to 200x50, then to 100x50]
+ expected: FAIL
diff --git a/tests/wpt/mozilla/tests/css/blank.html b/tests/wpt/mozilla/tests/css/blank.html
new file mode 100644
index 00000000000..82055fc37ee
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/blank.html
@@ -0,0 +1 @@
+<!--intentionally blank-->
diff --git a/tests/wpt/mozilla/tests/css/matchMedia.html b/tests/wpt/mozilla/tests/css/matchMedia.html
new file mode 100644
index 00000000000..0eeb007a49a
--- /dev/null
+++ b/tests/wpt/mozilla/tests/css/matchMedia.html
@@ -0,0 +1,188 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+ <head>
+ <title>CSS Test: CSSOM View matchMedia and MediaQueryList</title>
+ <link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#dom-window-matchmedia">
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#the-mediaquerylist-interface">
+ <link rel="help" href="http://www.w3.org/TR/cssom-1/#serializing-media-queries">
+ <meta name="flags" content="dom">
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+ <style type="text/css">
+ iframe { border: none; }
+ </style>
+ </head>
+ <body>
+ <noscript>Test not run - javascript required.</noscript>
+ <div id="log"></div>
+ <iframe width="200" height="100" src="blank.html"></iframe>
+ <script type="text/javascript">
+ function reflow(doc) {
+ doc.body.offsetWidth;
+ }
+
+ var iframe = document.querySelector("iframe");
+ iframe.onload = function(){
+ var iframe_window = iframe.contentWindow;
+
+ reflow(iframe_window.document);
+
+ test(function(){
+ assert_inherits(window, "matchMedia");
+ }, "window.matchMedia exists");
+
+ test(function(){
+ assert_true(window.matchMedia instanceof Function, "FATAL ERROR: The window.matchMedia function is not present. The rest of the testsuite will fail to run.");
+ }, "window.matchMedia is a Function");
+
+ var mql, mql1, mql2, mql3;
+
+ test(function(){
+ mql = window.matchMedia("all");
+ assert_true(mql instanceof MediaQueryList, "matchMedia(\"all\") returned MediaQueryList object.");
+ }, "window.matchMedia(\"all\")");
+
+ test(function(){
+ assert_idl_attribute(mql, "media", "Check that MediaQueryList.media exists.");
+ }, "MediaQueryList.media exists");
+
+ test(function(){
+ assert_readonly(mql, "media", "Check that MediaQueryList.media is readonly.");
+ }, "MediaQueryList.media is readonly");
+
+ test(function(){
+ assert_equals(mql.media, "all");
+ }, "MediaQueryList.media for \"all\"");
+
+ test(function(){
+ assert_idl_attribute(mql, "matches", "Check that MediaQueryList.matches exists.");
+ }, "MediaQueryList.matches exists");
+
+ test(function(){
+ assert_readonly(mql, "matches", "Check that MediaQueryList.matches is readonly.");
+ }, "MediaQueryList.matches is readonly");
+
+ test(function(){
+ assert_true(mql.matches);
+ }, "MediaQueryList.matches for \"all\"");
+
+ test(function(){
+ assert_inherits(mql, "addListener");
+ }, "MediaQueryList.addListener exists");
+
+ test(function(){
+ assert_true(mql.addListener instanceof Function);
+ }, "MediaQueryList.addListener is a Function");
+
+ test(function(){
+ assert_inherits(mql, "removeListener");
+ }, "MediaQueryList.removeListener exists");
+
+ test(function(){
+ assert_true(mql.removeListener instanceof Function);
+ }, "MediaQueryList.removeListener is a Function");
+
+ test(function(){
+ mql = window.matchMedia("::");
+ assert_true(mql instanceof MediaQueryList, "window.matchMedia(\"::\") returned MediaQueryList object.");
+ assert_equals(mql.media, "not all", "MediaQueryList.media serialized as \"not all\" from original string with syntax error.");
+ }, "MediaQueryList.media syntax error");
+
+ test(function(){
+ assert_false(mql.matches);
+ }, "MediaQueryList.matches for \"not all\"");
+
+ test(function(){
+ mql = iframe_window.matchMedia("(max-width: 199px), all and (min-width: 200px)");
+ assert_equals(mql.media, "(max-width: 199px), (min-width: 200px)");
+ assert_true(mql.matches);
+ }, "MediaQueryList.matches for \"(max-width: 199px), all and (min-width: 200px)\"")
+
+ test(function(){
+ mql = iframe_window.matchMedia("(min-aspect-ratio: 1/1)");
+ assert_true(mql.matches);
+ }, "MediaQueryList.matches for \"(min-aspect-ratio: 1/1)\"");
+
+ test(function(){
+ mql = iframe_window.matchMedia("(width: 200px)");
+ assert_true(mql.matches);
+ }, "MediaQueryList.matches for \"(width: 200px)\"");
+
+ test(function(){
+ mql1 = iframe_window.matchMedia("(max-height: 50px)");
+ assert_false(mql1.matches);
+ }, "MediaQueryList.matches for \"(max-height: 50px)\"");
+
+ test(function(){
+ mql2 = iframe_window.matchMedia("(min-width: 150px)");
+ assert_true(mql2.matches);
+ }, "MediaQueryList.matches for \"(min-width: 150px)\"");
+
+ var resizeTest = async_test("Resize iframe from 200x100 to 200x50, then to 100x50");
+ var listenerOrderTest = async_test("Listeners are called in the order which they have been added");
+ var duplicateListenerTest = async_test("Listener added twice is only called once.");
+
+ window.onload = function(){
+
+ var rmListener = function(x){
+ resizeTest.step(function(){
+ assert_unreached("removeListener was not successful.");
+ });
+ };
+
+ var dupListener = function(x){
+ duplicateListenerTest.step(function(){
+ assert_false(mql1.dupListenerCalled, "Check that this listener has not been called before.");
+ mql1.dupListenerCalled = true;
+ });
+ };
+
+ mql1.firstListenerCalled = false;
+ mql1.dupListenerCalled = false;
+ // Add listener twice and remove it below. Should not be called.
+ mql1.addListener(rmListener);
+ mql1.addListener(rmListener);
+ // Add listener twice. Should only be called once.
+ mql1.addListener(dupListener);
+ mql1.addListener(dupListener);
+
+ mql1.addListener(function(x){
+ resizeTest.step(function(){
+ assert_equals(x, mql1, "Check that the MediaQueryList passed to the handler is the same that addListener was invoked on.");
+ assert_true(x.matches, "(max-height: 50px) should now pass.");
+ assert_true(mql2.matches, "(min-width: 150px) should still pass.");
+ iframe.width = "100";
+ });
+
+ listenerOrderTest.step(function(){
+ assert_false(mql1.firstListenerCalled, "Check that this listener is only called once.");
+ mql1.firstListenerCalled = true;
+ });
+ });
+
+ mql1.addListener(function(x){
+ listenerOrderTest.step(function(){
+ assert_true(mql1.firstListenerCalled, "Check that the listener added last is called last.");
+ });
+ listenerOrderTest.done();
+ });
+
+ mql1.removeListener(rmListener);
+
+ mql2.addListener(function(x){
+ duplicateListenerTest.done();
+ resizeTest.step(function(){
+ assert_equals(x, mql2, "Check that the MediaQueryList passed to the handler is the same that addListener was invoked on.");
+ assert_true(mql1.matches, "(max-height: 50px) should still pass.");
+ assert_false(x.matches, "(min-width: 150px) should now fail.");
+ });
+ resizeTest.done();
+ });
+
+ iframe.height = "50";
+ };
+ };
+ </script>
+ </body>
+</html>
diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.html b/tests/wpt/mozilla/tests/mozilla/interfaces.html
index 8c318e4d7d3..65dc554e274 100644
--- a/tests/wpt/mozilla/tests/mozilla/interfaces.html
+++ b/tests/wpt/mozilla/tests/mozilla/interfaces.html
@@ -128,6 +128,7 @@ test_interfaces([
"KeyboardEvent",
"Location",
"MediaError",
+ "MediaQueryList",
"MessageEvent",
"MimeType",
"MimeTypeArray",