diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2016-12-09 09:52:34 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-12-09 09:52:34 -0800 |
commit | 8b69e73594647319e95bd0fd36c2addabcee1e5d (patch) | |
tree | fb8f23cab824af1e9731c3bc1acb754ee443c77b | |
parent | c3c086e521b73d191d5de9239a6748691b0f96a3 (diff) | |
parent | 55f0e56224be03dcbd17d5cba1c45f8b0c6bb390 (diff) | |
download | servo-8b69e73594647319e95bd0fd36c2addabcee1e5d.tar.gz servo-8b69e73594647319e95bd0fd36c2addabcee1e5d.zip |
Auto merge of #13489 - farodin91:fullscreen, r=jdm
Add support for fullscreen #10102
<!-- Please describe your changes on the following line: -->
I'm start working on fullscreen support.
@jdm Should be the entry_point in ScriptReflow a Option if fullscreen is enabled or point on the entry_node? For example the RootNode.
---
<!-- 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
- [x] These changes fix #10102 (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. -->
<!-- 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/13489)
<!-- Reviewable:end -->
33 files changed, 454 insertions, 30 deletions
diff --git a/Cargo.lock b/Cargo.lock index 3e90dbb14e4..679a7b7fe2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1075,7 +1075,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mac 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1088,7 +1088,7 @@ dependencies = [ [[package]] name = "html5ever-atoms" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1272,7 +1272,7 @@ dependencies = [ "gfx_traits 0.0.1", "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2189,7 +2189,7 @@ dependencies = [ "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "html5ever 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)", "hyper_serde 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2247,7 +2247,7 @@ dependencies = [ "gfx_traits 0.0.1", "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "ipc-channel 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2598,7 +2598,7 @@ dependencies = [ "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "heapsize_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2635,7 +2635,7 @@ dependencies = [ "app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "cssparser 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3192,7 +3192,7 @@ name = "xml5ever" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "mac 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3290,7 +3290,7 @@ dependencies = [ "checksum heartbeats-simple-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53c4b67617665d7f4172f381f9843c1bec6a4fccc9a9226529e5b1be40dc1301" "checksum hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2da7d3a34cf6406d9d700111b8eafafe9a251de41ae71d8052748259343b58" "checksum html5ever 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "45815593feb142cf01121b9f413d8630c9902192d160e494a579c50628eef498" -"checksum html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "daefa106438c66af58309c84842b5db1df2733fe35849f39adde6fdf63583d40" +"checksum html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fd3fc831590ee7fcf693c673e4e3cbe14fbda44dc0f26d9bdc79cfc9f551dc05" "checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae" "checksum hyper 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)" = "edd47c66782933e546a32ae89ca3c49263b2ba9bc29f3a0d5c52fff48e0ac67c" "checksum hyper_serde 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "572d2168173019de312a050a24f2ad33ac2ac7895a2139fbf21ee6b6f470a24e" diff --git a/components/atoms/static_atoms.txt b/components/atoms/static_atoms.txt index 153c0962eeb..2ac84aa2f4e 100644 --- a/components/atoms/static_atoms.txt +++ b/components/atoms/static_atoms.txt @@ -78,3 +78,5 @@ statechange controllerchange fetch characteristicvaluechanged +fullscreenchange +fullscreenerror diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 1fc3a5b27f5..a721c2b19c8 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -633,6 +633,10 @@ impl<Window: WindowMethods> IOCompositor<Window> { func(); } + (Msg::SetFullscreenState(state), ShutdownState::NotShuttingDown) => { + self.window.set_fullscreen_state(state); + } + // When we are shutting_down, we need to avoid performing operations // such as Paint that may crash because we have begun tearing down // the rest of our resources. diff --git a/components/compositing/compositor_thread.rs b/components/compositing/compositor_thread.rs index 0a7962c7917..8a78a7e8882 100644 --- a/components/compositing/compositor_thread.rs +++ b/components/compositing/compositor_thread.rs @@ -128,7 +128,9 @@ pub enum Msg { /// Runs a closure in the compositor thread. /// It's used to dispatch functions from webrender to the main thread's event loop. /// Required to allow WGL GLContext sharing in Windows. - Dispatch(Box<Fn() + Send>) + Dispatch(Box<Fn() + Send>), + /// Enter or exit fullscreen + SetFullscreenState(bool), } impl Debug for Msg { @@ -161,6 +163,7 @@ impl Debug for Msg { Msg::PipelineExited(..) => write!(f, "PipelineExited"), Msg::NewScrollFrameReady(..) => write!(f, "NewScrollFrameReady"), Msg::Dispatch(..) => write!(f, "Dispatch"), + Msg::SetFullscreenState(..) => write!(f, "SetFullscreenState"), } } } diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index d905b53a39f..33eed7630cc 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -119,6 +119,8 @@ pub trait WindowMethods { fn set_inner_size(&self, size: Size2D<u32>); /// Set the window position fn set_position(&self, point: Point2D<i32>); + /// Set fullscreen state + fn set_fullscreen_state(&self, state: bool); /// Sets the page title for the current page. fn set_page_title(&self, title: Option<String>); diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index c64e3ee3811..c34f9b1597c 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -1090,6 +1090,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> FromScriptMsg::BroadcastStorageEvent(pipeline_id, storage, url, key, old_value, new_value) => { self.handle_broadcast_storage_event(pipeline_id, storage, url, key, old_value, new_value); } + FromScriptMsg::SetFullscreenState(state) => { + self.compositor_proxy.send(ToCompositorMsg::SetFullscreenState(state)); + } } } diff --git a/components/profile/time.rs b/components/profile/time.rs index 62f85b362d2..648c31f83d6 100644 --- a/components/profile/time.rs +++ b/components/profile/time.rs @@ -150,6 +150,8 @@ impl Formattable for ProfilerCategory { ProfilerCategory::ScriptWebSocketEvent => "Script Web Socket Event", ProfilerCategory::ScriptWorkerEvent => "Script Worker Event", ProfilerCategory::ScriptServiceWorkerEvent => "Script Service Worker Event", + ProfilerCategory::ScriptEnterFullscreen => "Script Enter Fullscreen", + ProfilerCategory::ScriptExitFullscreen => "Script Exit Fullscreen", ProfilerCategory::ApplicationHeartbeat => "Application Heartbeat", }; format!("{}{}", padding, name) diff --git a/components/profile_traits/time.rs b/components/profile_traits/time.rs index c8cf4913f1e..ae114f98bc0 100644 --- a/components/profile_traits/time.rs +++ b/components/profile_traits/time.rs @@ -86,6 +86,8 @@ pub enum ProfilerCategory { ScriptWorkerEvent = 0x74, ScriptServiceWorkerEvent = 0x75, ScriptParseXML = 0x76, + ScriptEnterFullscreen = 0x77, + ScriptExitFullscreen = 0x78, ApplicationHeartbeat = 0x90, } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index d0dbe75ebd3..0e16ca16f81 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -27,7 +27,7 @@ use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, Nod use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root}; use dom::bindings::js::RootedReference; use dom::bindings::num::Finite; -use dom::bindings::refcounted::Trusted; +use dom::bindings::refcounted::{Trusted, TrustedPromise}; use dom::bindings::reflector::{DomObject, reflect_dom_object}; use dom::bindings::str::{DOMString, USVString}; use dom::bindings::xmlname::{namespace_from_domstring, validate_and_extract, xml_name_type}; @@ -39,7 +39,7 @@ use dom::customevent::CustomEvent; use dom::documentfragment::DocumentFragment; use dom::documenttype::DocumentType; use dom::domimplementation::DOMImplementation; -use dom::element::{Element, ElementCreator}; +use dom::element::{Element, ElementCreator, ElementPerformFullscreenEnter, ElementPerformFullscreenExit}; use dom::errorevent::ErrorEvent; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::eventdispatcher::EventStatus; @@ -74,6 +74,7 @@ use dom::pagetransitionevent::PageTransitionEvent; use dom::popstateevent::PopStateEvent; use dom::processinginstruction::ProcessingInstruction; use dom::progressevent::ProgressEvent; +use dom::promise::Promise; use dom::range::Range; use dom::servoparser::ServoParser; use dom::storageevent::StorageEvent; @@ -105,6 +106,7 @@ use net_traits::response::HttpsState; use num_traits::ToPrimitive; use origin::Origin; use script_layout_interface::message::{Msg, ReflowQueryType}; +use script_runtime::{CommonScriptMsg, ScriptThreadEventCategory}; use script_thread::{MainThreadScriptMsg, Runnable}; use script_traits::{AnimationState, CompositorEvent, MouseButton, MouseEventType, MozBrowserEvent}; use script_traits::{ScriptMsg as ConstellationMsg, TouchpadPressurePhase}; @@ -290,6 +292,8 @@ pub struct Document { /// /// See also: https://github.com/servo/servo/issues/10110 dom_count: Cell<u32>, + /// Entry node for fullscreen. + fullscreen_element: MutNullableHeap<JS<Element>>, } #[derive(JSTraceable, HeapSizeOf)] @@ -1907,6 +1911,7 @@ impl Document { last_click_info: DOMRefCell::new(None), ignore_destructive_writes_counter: Default::default(), dom_count: Cell::new(1), + fullscreen_element: MutNullableHeap::new(None), } } @@ -2092,6 +2097,110 @@ impl Document { self.ignore_destructive_writes_counter.set( self.ignore_destructive_writes_counter.get() - 1); } + + // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen + #[allow(unrooted_must_root)] + pub fn enter_fullscreen(&self, pending: &Element) -> Rc<Promise> { + // Step 1 + let promise = Promise::new(self.global().r()); + let mut error = false; + + // Step 4 + // check namespace + match *pending.namespace() { + ns!(mathml) => { + if pending.local_name().as_ref() != "math" { + error = true; + } + } + ns!(svg) => { + if pending.local_name().as_ref() != "svg" { + error = true; + } + } + ns!(html) => (), + _ => error = true, + } + // fullscreen element ready check + if !pending.fullscreen_element_ready_check() { + error = true; + } + // TODO fullscreen is supported + // TODO This algorithm is allowed to request fullscreen. + + // Step 5 Parallel start + + let window = self.window(); + // Step 6 + if !error { + let event = ConstellationMsg::SetFullscreenState(true); + window.upcast::<GlobalScope>().constellation_chan().send(event).unwrap(); + } + + // Step 7 + let trusted_pending = Trusted::new(pending); + let trusted_promise = TrustedPromise::new(promise.clone()); + let handler = ElementPerformFullscreenEnter::new(trusted_pending, trusted_promise, error); + let script_msg = CommonScriptMsg::RunnableMsg(ScriptThreadEventCategory::EnterFullscreen, handler); + let msg = MainThreadScriptMsg::Common(script_msg); + window.main_thread_script_chan().send(msg).unwrap(); + + promise + } + + // https://fullscreen.spec.whatwg.org/#exit-fullscreen + #[allow(unrooted_must_root)] + pub fn exit_fullscreen(&self) -> Rc<Promise> { + let global = self.global(); + // Step 1 + let promise = Promise::new(global.r()); + // Step 2 + if self.fullscreen_element.get().is_none() { + promise.reject_error(global.get_cx(), Error::Type(String::from("fullscreen is null"))); + return promise + } + // TODO Step 3-6 + let element = self.fullscreen_element.get().unwrap(); + + // Step 7 Parallel start + + let window = self.window(); + // Step 8 + let event = ConstellationMsg::SetFullscreenState(false); + window.upcast::<GlobalScope>().constellation_chan().send(event).unwrap(); + + // Step 9 + let trusted_element = Trusted::new(element.r()); + let trusted_promise = TrustedPromise::new(promise.clone()); + let handler = ElementPerformFullscreenExit::new(trusted_element, trusted_promise); + let script_msg = CommonScriptMsg::RunnableMsg(ScriptThreadEventCategory::ExitFullscreen, handler); + let msg = MainThreadScriptMsg::Common(script_msg); + window.main_thread_script_chan().send(msg).unwrap(); + + promise + } + + pub fn set_fullscreen_element(&self, element: Option<&Element>) { + self.fullscreen_element.set(element); + } + + pub fn get_allow_fullscreen(&self) -> bool { + // https://html.spec.whatwg.org/multipage/#allowed-to-use + match self.browsing_context() { + // Step 1 + None => false, + Some(_) => { + // Step 2 + let window = self.window(); + if window.is_top_level() { + true + } else { + // Step 3 + window.GetFrameElement().map_or(false, |el| el.has_attribute(&local_name!("allowfullscreen"))) + } + } + } + } } @@ -3109,6 +3218,34 @@ impl DocumentMethods for Document { // https://html.spec.whatwg.org/multipage/#documentandelementeventhandlers document_and_element_event_handlers!(); + + // https://fullscreen.spec.whatwg.org/#handler-document-onfullscreenerror + event_handler!(fullscreenerror, GetOnfullscreenerror, SetOnfullscreenerror); + + // https://fullscreen.spec.whatwg.org/#handler-document-onfullscreenchange + event_handler!(fullscreenchange, GetOnfullscreenchange, SetOnfullscreenchange); + + // https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled + fn FullscreenEnabled(&self) -> bool { + self.get_allow_fullscreen() + } + + // https://fullscreen.spec.whatwg.org/#dom-document-fullscreen + fn Fullscreen(&self) -> bool { + self.fullscreen_element.get().is_some() + } + + // https://fullscreen.spec.whatwg.org/#dom-document-fullscreenelement + fn GetFullscreenElement(&self) -> Option<Root<Element>> { + // TODO ShadowRoot + self.fullscreen_element.get() + } + + #[allow(unrooted_must_root)] + // https://fullscreen.spec.whatwg.org/#dom-document-exitfullscreen + fn ExitFullscreen(&self) -> Rc<Promise> { + self.exit_fullscreen() + } } fn update_with_current_time_ms(marker: &Cell<u64>) { diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 6df2122e7b3..d9831d79a69 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -24,6 +24,8 @@ use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId}; use dom::bindings::js::{JS, LayoutJS, MutNullableHeap}; use dom::bindings::js::{Root, RootedReference}; +use dom::bindings::refcounted::{Trusted, TrustedPromise}; +use dom::bindings::reflector::DomObject; use dom::bindings::str::DOMString; use dom::bindings::xmlname::{namespace_from_domstring, validate_and_extract, xml_name_type}; use dom::bindings::xmlname::XMLName::InvalidXMLName; @@ -35,6 +37,7 @@ use dom::domrect::DOMRect; use dom::domrectlist::DOMRectList; use dom::domtokenlist::DOMTokenList; use dom::event::Event; +use dom::eventtarget::EventTarget; use dom::htmlanchorelement::HTMLAnchorElement; use dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers}; use dom::htmlbuttonelement::HTMLButtonElement; @@ -62,18 +65,23 @@ use dom::node::{CLICK_IN_PROGRESS, ChildrenMutation, LayoutNodeHelpers, Node}; use dom::node::{NodeDamage, SEQUENTIALLY_FOCUSABLE, UnbindContext}; use dom::node::{document_from_node, window_from_node}; use dom::nodelist::NodeList; +use dom::promise::Promise; use dom::servoparser::ServoParser; use dom::text::Text; use dom::validation::Validatable; use dom::virtualmethods::{VirtualMethods, vtable_for}; +use dom::window::ReflowReason; use html5ever::serialize; use html5ever::serialize::SerializeOpts; use html5ever::serialize::TraversalScope; use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode}; use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks}; use html5ever_atoms::{Prefix, LocalName, Namespace, QualName}; +use js::jsapi::{HandleValue, JSAutoCompartment}; use parking_lot::RwLock; use ref_filter_map::ref_filter_map; +use script_layout_interface::message::ReflowQueryType; +use script_thread::Runnable; use selectors::matching::{ElementFlags, MatchingReason, matches}; use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS}; use selectors::parser::{AttrSelector, NamespaceConstraint}; @@ -84,9 +92,11 @@ use std::cell::{Cell, Ref}; use std::convert::TryFrom; use std::default::Default; use std::fmt; +use std::rc::Rc; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use style::attr::{AttrValue, LengthOrPercentageOrAuto}; +use style::context::ReflowGoal; use style::dom::TRestyleDamage; use style::element_state::*; use style::matching::{common_style_affecting_attributes, rare_style_affecting_attributes}; @@ -1300,6 +1310,15 @@ impl Element { } } } + + // https://fullscreen.spec.whatwg.org/#fullscreen-element-ready-check + pub fn fullscreen_element_ready_check(&self) -> bool { + if !self.is_connected() { + return false + } + let document = document_from_node(self); + document.get_allow_fullscreen() + } } impl ElementMethods for Element { @@ -2056,6 +2075,13 @@ impl ElementMethods for Element { None => return Err(Error::NotSupported) } } + + // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen + #[allow(unrooted_must_root)] + fn RequestFullscreen(&self) -> Rc<Promise> { + let doc = document_from_node(self); + doc.enter_fullscreen(self) + } } pub fn fragment_affecting_attributes() -> [LocalName; 3] { @@ -2167,6 +2193,10 @@ impl VirtualMethods for Element { } let doc = document_from_node(self); + let fullscreen = doc.GetFullscreenElement(); + if fullscreen.r() == Some(self) { + doc.exit_fullscreen(); + } if let Some(ref value) = *self.id_attribute.borrow() { doc.unregister_named_element(self, value.clone()); } @@ -2307,6 +2337,7 @@ impl<'a> ::selectors::Element for Root<Element> { NonTSPseudoClass::Active | NonTSPseudoClass::Focus | + NonTSPseudoClass::Fullscreen | NonTSPseudoClass::Hover | NonTSPseudoClass::Enabled | NonTSPseudoClass::Disabled | @@ -2582,7 +2613,22 @@ impl Element { } pub fn set_target_state(&self, value: bool) { - self.set_state(IN_TARGET_STATE, value) + self.set_state(IN_TARGET_STATE, value) + } + + pub fn fullscreen_state(&self) -> bool { + self.state.get().contains(IN_FULLSCREEN_STATE) + } + + pub fn set_fullscreen_state(&self, value: bool) { + self.set_state(IN_FULLSCREEN_STATE, value) + } + + /// https://dom.spec.whatwg.org/#connected + pub fn is_connected(&self) -> bool { + let node = self.upcast::<Node>(); + let root = node.GetRootNode(); + root.is::<Document>() } } @@ -2713,3 +2759,104 @@ impl TagName { *self.ptr.borrow_mut() = None; } } + +pub struct ElementPerformFullscreenEnter { + element: Trusted<Element>, + promise: TrustedPromise, + error: bool, +} + +impl ElementPerformFullscreenEnter { + pub fn new(element: Trusted<Element>, promise: TrustedPromise, error: bool) -> Box<ElementPerformFullscreenEnter> { + box ElementPerformFullscreenEnter { + element: element, + promise: promise, + error: error, + } + } +} + +impl Runnable for ElementPerformFullscreenEnter { + fn name(&self) -> &'static str { "ElementPerformFullscreenEnter" } + + #[allow(unrooted_must_root)] + fn handler(self: Box<ElementPerformFullscreenEnter>) { + let element = self.element.root(); + let document = document_from_node(element.r()); + + // Step 7.1 + if self.error || !element.fullscreen_element_ready_check() { + // JSAutoCompartment needs to be manually made. + // Otherwise, Servo will crash. + let promise = self.promise.root(); + let promise_cx = promise.global().get_cx(); + let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get()); + document.upcast::<EventTarget>().fire_event(atom!("fullscreenerror")); + promise.reject_error(promise.global().get_cx(), Error::Type(String::from("fullscreen is not connected"))); + return + } + + // TODO Step 7.2-4 + // Step 7.5 + element.set_fullscreen_state(true); + document.set_fullscreen_element(Some(&element)); + document.window().reflow(ReflowGoal::ForDisplay, + ReflowQueryType::NoQuery, + ReflowReason::ElementStateChanged); + + // Step 7.6 + document.upcast::<EventTarget>().fire_event(atom!("fullscreenchange")); + + // Step 7.7 + // JSAutoCompartment needs to be manually made. + // Otherwise, Servo will crash. + let promise = self.promise.root(); + let promise_cx = promise.global().get_cx(); + let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get()); + promise.resolve(promise.global().get_cx(), HandleValue::undefined()); + } +} + +pub struct ElementPerformFullscreenExit { + element: Trusted<Element>, + promise: TrustedPromise, +} + +impl ElementPerformFullscreenExit { + pub fn new(element: Trusted<Element>, promise: TrustedPromise) -> Box<ElementPerformFullscreenExit> { + box ElementPerformFullscreenExit { + element: element, + promise: promise, + } + } +} + +impl Runnable for ElementPerformFullscreenExit { + fn name(&self) -> &'static str { "ElementPerformFullscreenExit" } + + #[allow(unrooted_must_root)] + fn handler(self: Box<ElementPerformFullscreenExit>) { + let element = self.element.root(); + let document = document_from_node(element.r()); + // TODO Step 9.1-5 + // Step 9.6 + element.set_fullscreen_state(false); + + document.window().reflow(ReflowGoal::ForDisplay, + ReflowQueryType::NoQuery, + ReflowReason::ElementStateChanged); + + document.set_fullscreen_element(None); + + // Step 9.8 + document.upcast::<EventTarget>().fire_event(atom!("fullscreenchange")); + + // Step 9.10 + let promise = self.promise.root(); + // JSAutoCompartment needs to be manually made. + // Otherwise, Servo will crash. + let promise_cx = promise.global().get_cx(); + let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get()); + promise.resolve(promise.global().get_cx(), HandleValue::undefined()); + } +} diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 0f55f3c6be3..44323e1c1ef 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -15,7 +15,7 @@ use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserElementVisib use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserShowModalPromptEventDetail; use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding; use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods; -use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; +use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use dom::bindings::conversions::ToJSValConvertible; use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::inheritance::Castable; @@ -544,6 +544,11 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement { element.set_bool_attribute(&local_name!("mozbrowser"), value); } + // https://html.spec.whatwg.org/multipage/#attr-iframe-allowfullscreen + make_bool_getter!(AllowFullscreen, "allowfullscreen"); + // https://html.spec.whatwg.org/multipage/#attr-iframe-allowfullscreen + make_bool_setter!(SetAllowFullscreen, "allowfullscreen"); + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/goBack fn GoBack(&self) -> ErrorResult { Navigate(self, TraversalDirection::Back(1)) diff --git a/components/script/dom/webidls/Document.webidl b/components/script/dom/webidls/Document.webidl index 1e8725d8b86..4955bf6e956 100644 --- a/components/script/dom/webidls/Document.webidl +++ b/components/script/dom/webidls/Document.webidl @@ -190,3 +190,15 @@ partial interface Document { partial interface Document { [SameObject] readonly attribute StyleSheetList styleSheets; }; + +// https://fullscreen.spec.whatwg.org/#api +partial interface Document { + [LenientSetter] readonly attribute boolean fullscreenEnabled; + [LenientSetter] readonly attribute Element? fullscreenElement; + [LenientSetter] readonly attribute boolean fullscreen; // historical + + Promise<void> exitFullscreen(); + + attribute EventHandler onfullscreenchange; + attribute EventHandler onfullscreenerror; +}; diff --git a/components/script/dom/webidls/Element.webidl b/components/script/dom/webidls/Element.webidl index 4e79166e945..61a54e180d0 100644 --- a/components/script/dom/webidls/Element.webidl +++ b/components/script/dom/webidls/Element.webidl @@ -110,6 +110,11 @@ partial interface Element { attribute DOMString outerHTML; }; +// https://fullscreen.spec.whatwg.org/#api +partial interface Element { + Promise<void> requestFullscreen(); +}; + Element implements ChildNode; Element implements NonDocumentTypeChildNode; Element implements ParentNode; diff --git a/components/script/dom/webidls/HTMLIFrameElement.webidl b/components/script/dom/webidls/HTMLIFrameElement.webidl index 330453d9f8c..f02c7c35a1b 100644 --- a/components/script/dom/webidls/HTMLIFrameElement.webidl +++ b/components/script/dom/webidls/HTMLIFrameElement.webidl @@ -13,7 +13,7 @@ interface HTMLIFrameElement : HTMLElement { [SameObject, PutForwards=value] readonly attribute DOMTokenList sandbox; // attribute boolean seamless; - // attribute boolean allowFullscreen; + attribute boolean allowFullscreen; attribute DOMString width; attribute DOMString height; readonly attribute Document? contentDocument; diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index a6d4815090f..338edc19730 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -666,6 +666,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { NonTSPseudoClass::Active | NonTSPseudoClass::Focus | + NonTSPseudoClass::Fullscreen | NonTSPseudoClass::Hover | NonTSPseudoClass::Enabled | NonTSPseudoClass::Disabled | diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index f65b145c4ec..00e7d3a198e 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -76,7 +76,9 @@ pub enum ScriptThreadEventCategory { UpdateReplacedElement, WebSocketEvent, WorkerEvent, - ServiceWorkerEvent + ServiceWorkerEvent, + EnterFullscreen, + ExitFullscreen, } /// An interface for receiving ScriptMsg values in an event loop. Used for synchronous DOM diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index ad8c3d09e57..f55d9279138 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -946,7 +946,9 @@ impl ScriptThread { ScriptThreadEventCategory::TimerEvent => ProfilerCategory::ScriptTimerEvent, ScriptThreadEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent, ScriptThreadEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent, - ScriptThreadEventCategory::ServiceWorkerEvent => ProfilerCategory::ScriptServiceWorkerEvent + ScriptThreadEventCategory::ServiceWorkerEvent => ProfilerCategory::ScriptServiceWorkerEvent, + ScriptThreadEventCategory::EnterFullscreen => ProfilerCategory::ScriptEnterFullscreen, + ScriptThreadEventCategory::ExitFullscreen => ProfilerCategory::ScriptExitFullscreen, }; profile(profiler_cat, None, self.time_profiler_chan.clone(), f) } else { diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index e91a034fec9..80dbc3f4165 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -145,6 +145,8 @@ pub enum ScriptMsg { ForwardDOMMessage(DOMMessage, ServoUrl), /// Store the data required to activate a service worker for the given scope RegisterServiceWorker(ScopeThings, ServoUrl), + /// Enter or exit fullscreen + SetFullscreenState(bool), /// Requests that the compositor shut down. Exit } diff --git a/components/style/element_state.rs b/components/style/element_state.rs index f394a7003ef..ef6e9704170 100644 --- a/components/style/element_state.rs +++ b/components/style/element_state.rs @@ -35,5 +35,7 @@ bitflags! { const IN_PLACEHOLDER_SHOWN_STATE = 0x0100, #[doc = "https://html.spec.whatwg.org/multipage/#selector-target"] const IN_TARGET_STATE = 0x0200, + #[doc = "https://fullscreen.spec.whatwg.org/#%3Afullscreen-pseudo-class"] + const IN_FULLSCREEN_STATE = 0x0400, } } diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index 608295af954..d9fdf6a4431 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -109,6 +109,7 @@ pub enum NonTSPseudoClass { Visited, Active, Focus, + Fullscreen, Hover, Enabled, Disabled, @@ -127,6 +128,7 @@ impl ToCss for NonTSPseudoClass { Visited => ":visited", Active => ":active", Focus => ":focus", + Fullscreen => ":fullscreen", Hover => ":hover", Enabled => ":enabled", Disabled => ":disabled", @@ -145,6 +147,7 @@ impl NonTSPseudoClass { match *self { Active => IN_ACTIVE_STATE, Focus => IN_FOCUS_STATE, + Fullscreen => IN_FULLSCREEN_STATE, Hover => IN_HOVER_STATE, Enabled => IN_ENABLED_STATE, Disabled => IN_DISABLED_STATE, @@ -196,6 +199,7 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> { "visited" => Visited, "active" => Active, "focus" => Focus, + "fullscreen" => Fullscreen, "hover" => Hover, "enabled" => Enabled, "disabled" => Disabled, diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 6b124d55d20..4d9683241a8 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -481,6 +481,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { NonTSPseudoClass::Disabled | NonTSPseudoClass::Checked | NonTSPseudoClass::ReadWrite | + NonTSPseudoClass::Fullscreen | NonTSPseudoClass::Indeterminate => { self.get_state().contains(pseudo_class.state_flag()) }, diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index 8dc844456bd..34eb8c2df7c 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -90,6 +90,7 @@ pub enum NonTSPseudoClass { Visited, Active, Focus, + Fullscreen, Hover, Enabled, Disabled, @@ -111,6 +112,7 @@ impl ToCss for NonTSPseudoClass { Visited => ":visited", Active => ":active", Focus => ":focus", + Fullscreen => ":fullscreen", Hover => ":hover", Enabled => ":enabled", Disabled => ":disabled", @@ -132,6 +134,7 @@ impl NonTSPseudoClass { match *self { Active => IN_ACTIVE_STATE, Focus => IN_FOCUS_STATE, + Fullscreen => IN_FULLSCREEN_STATE, Hover => IN_HOVER_STATE, Enabled => IN_ENABLED_STATE, Disabled => IN_DISABLED_STATE, @@ -187,6 +190,7 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> { "visited" => Visited, "active" => Active, "focus" => Focus, + "fullscreen" => Fullscreen, "hover" => Hover, "enabled" => Enabled, "disabled" => Disabled, diff --git a/ports/cef/window.rs b/ports/cef/window.rs index 16ea145bf0c..33a1c5749c6 100644 --- a/ports/cef/window.rs +++ b/ports/cef/window.rs @@ -238,6 +238,9 @@ impl WindowMethods for Window { } + fn set_fullscreen_state(&self, _state: bool) { + } + fn present(&self) { let browser = self.cef_browser.borrow(); match *browser { diff --git a/ports/glutin/window.rs b/ports/glutin/window.rs index 11953f64b92..c19836cb4f4 100644 --- a/ports/glutin/window.rs +++ b/ports/glutin/window.rs @@ -841,6 +841,15 @@ impl WindowMethods for Window { } } + fn set_fullscreen_state(&self, _state: bool) { + match self.kind { + WindowKind::Window(..) => { + warn!("Fullscreen is not implemented!") + }, + WindowKind::Headless(..) => {} + } + } + fn present(&self) { match self.kind { WindowKind::Window(ref window) => { diff --git a/resources/user-agent.css b/resources/user-agent.css index 9d2c2508d98..141b9b21de9 100644 --- a/resources/user-agent.css +++ b/resources/user-agent.css @@ -286,3 +286,25 @@ video { object-fit: contain; } textarea { white-space: pre-wrap; } + +*|*:not(:root):fullscreen { + position:fixed !important; + top:0 !important; right:0 !important; bottom:0 !important; left:0 !important; + margin:0 !important; + box-sizing:border-box !important; + min-width:0 !important; + max-width:none !important; + min-height:0 !important; + max-height:none !important; + width:100% !important; + height:100% !important; + transform:none !important; + + /* intentionally not !important */ + object-fit:contain; +} + +iframe:fullscreen { + border:none !important; + padding:0 !important; +} diff --git a/tests/wpt/include.ini b/tests/wpt/include.ini index c784afdaad7..63631c662cb 100644 --- a/tests/wpt/include.ini +++ b/tests/wpt/include.ini @@ -21,6 +21,8 @@ skip: true skip: false [FileAPI] skip: false +[fullscreen] + skip: false [hr-time] skip: false [html] diff --git a/tests/wpt/metadata/html/dom/interfaces.html.ini b/tests/wpt/metadata/html/dom/interfaces.html.ini index b602f533384..4961fca1f35 100644 --- a/tests/wpt/metadata/html/dom/interfaces.html.ini +++ b/tests/wpt/metadata/html/dom/interfaces.html.ini @@ -1305,9 +1305,6 @@ [HTMLIFrameElement interface: attribute seamless] expected: FAIL - [HTMLIFrameElement interface: attribute allowFullscreen] - expected: FAIL - [HTMLIFrameElement interface: attribute align] expected: FAIL diff --git a/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe-allowfullscreen.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe-allowfullscreen.html.ini index b7750351f19..021d2d2fa06 100644 --- a/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe-allowfullscreen.html.ini +++ b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/iframe-allowfullscreen.html.ini @@ -1,8 +1,5 @@ [iframe-allowfullscreen.html] type: testharness - [iframe-allowfullscreen] - expected: FAIL - [iframe-sandbox-allowfullscreen] expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index bc3a420f79c..23b77f9ed85 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -8132,6 +8132,12 @@ "url": "/_mozilla/mozilla/form_tab_keyevent.html" } ], + "mozilla/fullscreen-remove-single.html": [ + { + "path": "mozilla/fullscreen-remove-single.html", + "url": "/_mozilla/mozilla/fullscreen-remove-single.html" + } + ], "mozilla/getBoundingClientRect.html": [ { "path": "mozilla/getBoundingClientRect.html", diff --git a/tests/wpt/mozilla/tests/mozilla/fullscreen-remove-single.html b/tests/wpt/mozilla/tests/mozilla/fullscreen-remove-single.html new file mode 100644 index 00000000000..1e3246f791d --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/fullscreen-remove-single.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<title>Remove the single element on the fullscreen element stack</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="log"></div> +<div id="single"></div> +<script> + async_test(function(t) + { + var single = document.getElementById("single"); + document.onfullscreenchange = t.step_func(function() + { + assert_equals(document.fullscreenElement, single); + document.onfullscreenchange = t.step_func(function() + { + assert_equals(document.fullscreenElement, null); + t.done(); + }); + single.remove(); + }); + single.requestFullscreen(); + }); +</script> diff --git a/tests/wpt/web-platform-tests/fullscreen/api/blank.html b/tests/wpt/web-platform-tests/fullscreen/api/blank.html new file mode 100644 index 00000000000..e149a394349 --- /dev/null +++ b/tests/wpt/web-platform-tests/fullscreen/api/blank.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Title</title> +</head> +<body> + +</body> +</html> diff --git a/tests/wpt/web-platform-tests/fullscreen/api/document-fullscreen-enabled.html b/tests/wpt/web-platform-tests/fullscreen/api/document-fullscreen-enabled.html index d45bb1576e9..f6382e64083 100644 --- a/tests/wpt/web-platform-tests/fullscreen/api/document-fullscreen-enabled.html +++ b/tests/wpt/web-platform-tests/fullscreen/api/document-fullscreen-enabled.html @@ -3,15 +3,23 @@ <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <div id="log"></div> -<iframe></iframe> -<iframe allowfullscreen></iframe> +<iframe src="blank.html"></iframe> +<iframe allowfullscreen src="blank.html"></iframe> <script> -test(function() +async_test(function(t) { assert_true(document.fullscreenEnabled, "top-level document"); var iframes = document.getElementsByTagName("iframe"); - assert_false(iframes[0].contentDocument.fullscreenEnabled, "iframe without allowfullscreen"); - assert_true(iframes[1].contentDocument.fullscreenEnabled, "iframe with allowfullscreen"); + + var loaded = 0; + iframes[0].onload = t.step_func(function() { + assert_false(iframes[0].contentDocument.fullscreenEnabled, "iframe without allowfullscreen"); + if (++loaded == 2) t.done(); + }); + iframes[1].onload = t.step_func(function() { + assert_true(iframes[1].contentDocument.fullscreenEnabled, "iframe with allowfullscreen"); + if (++loaded == 2) t.done(); + }); }); </script> diff --git a/tests/wpt/web-platform-tests/fullscreen/api/document-onfullscreenerror.html b/tests/wpt/web-platform-tests/fullscreen/api/document-onfullscreenerror.html index f5eaaf44146..9bbcda9bba1 100644 --- a/tests/wpt/web-platform-tests/fullscreen/api/document-onfullscreenerror.html +++ b/tests/wpt/web-platform-tests/fullscreen/api/document-onfullscreenerror.html @@ -6,8 +6,13 @@ <script> async_test(function(t) { + var sync = true; assert_equals(document.onfullscreenerror, null, "initial onfullscreenerror"); - document.onfullscreenerror = t.step_func_done(); - document.createElement("a").requestFullscreen(); + document.onfullscreenerror = t.step_func_done(function(event) { + assert_false(sync); + }); + var e = document.createElement('span'); + e.requestFullscreen(); + sync = false; }); </script> |