mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
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 -->
This commit is contained in:
commit
6ef46ab9e4
22 changed files with 500 additions and 20 deletions
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
156
components/script/dom/mediaquerylist.rs
Normal file
156
components/script/dom/mediaquerylist.rs
Normal file
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
14
components/script/dom/webidls/MediaQueryList.webidl
Normal file
14
components/script/dom/webidls/MediaQueryList.webidl
Normal file
|
@ -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;
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue