diff options
author | Fernando Jiménez Moreno <ferjmoreno@gmail.com> | 2019-10-08 10:18:13 +0200 |
---|---|---|
committer | Fernando Jiménez Moreno <ferjmoreno@gmail.com> | 2019-11-20 13:33:14 +0100 |
commit | 4d147d2c56465405e7c3281073ef57fe1bd1c062 (patch) | |
tree | 7098eaa3bcccf06c44696023352eec78736b7027 | |
parent | 9c329a79354cb8b6c1000aa7c78364a5541de421 (diff) | |
download | servo-4d147d2c56465405e7c3281073ef57fe1bd1c062.tar.gz servo-4d147d2c56465405e7c3281073ef57fe1bd1c062.zip |
Register media instance with session and prepare communication with embedder
-rw-r--r-- | components/constellation/constellation.rs | 7 | ||||
-rw-r--r-- | components/script/dom/htmlmediaelement.rs | 10 | ||||
-rw-r--r-- | components/script/dom/mediasession.rs | 19 | ||||
-rw-r--r-- | components/script/dom/navigator.rs | 22 | ||||
-rw-r--r-- | components/script/script_thread.rs | 13 | ||||
-rw-r--r-- | components/script_traits/lib.rs | 31 | ||||
-rw-r--r-- | components/script_traits/script_msg.rs | 5 | ||||
-rw-r--r-- | ports/glutin/browser.rs | 4 |
8 files changed, 107 insertions, 4 deletions
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 8f549ea685e..8b0a7b073a3 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -474,6 +474,9 @@ pub struct Constellation<Message, LTF, STF> { /// Mechanism to force the compositor to process events. event_loop_waker: Option<Box<dyn EventLoopWaker>>, + + /// Browser ID of the active media session, if any. + active_media_session: Option<TopLevelBrowsingContextId>, } /// State needed to construct a constellation. @@ -843,6 +846,7 @@ where glplayer_threads: state.glplayer_threads, player_context: state.player_context, event_loop_waker: state.event_loop_waker, + active_media_session: None, }; constellation.run(); @@ -1774,6 +1778,9 @@ where new_value, ); }, + FromScriptMsg::MediaSessionEventMsg(browser_id, event) => { + // TODO + }, } } diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index d10801c5779..c816a69ca1a 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -15,8 +15,10 @@ use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaE use crate::dom::bindings::codegen::Bindings::HTMLSourceElementBinding::HTMLSourceElementMethods; use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConstants::*; use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods; +use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorBinding::NavigatorMethods; use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeBinding::NodeMethods; use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode}; +use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use crate::dom::bindings::codegen::InheritTypes::{ElementTypeId, HTMLElementTypeId}; use crate::dom::bindings::codegen::InheritTypes::{HTMLMediaElementTypeId, NodeTypeId}; use crate::dom::bindings::codegen::UnionTypes::{ @@ -78,6 +80,7 @@ use net_traits::request::{Destination, Referrer}; use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, Metadata}; use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType}; use script_layout_interface::HTMLMediaData; +use script_traits::MediaSessionEvent; use servo_config::pref; use servo_media::player::audio::AudioRenderer; use servo_media::player::video::{VideoFrame, VideoFrameRenderer}; @@ -592,7 +595,6 @@ impl HTMLMediaElement { match (old_ready_state, ready_state) { (ReadyState::HaveNothing, ReadyState::HaveMetadata) => { task_source.queue_simple_event(self.upcast(), atom!("loadedmetadata"), &window); - // No other steps are applicable in this case. return; }, @@ -1883,6 +1885,12 @@ impl HTMLMediaElement { self.media_element_load_algorithm(); } } + + fn send_media_session_event(&self, event: MediaSessionEvent) { + let global = self.global(); + let media_session = global.as_window().Navigator().MediaSession(); + media_session.send_event(event); + } } // XXX Placeholder for [https://github.com/servo/servo/issues/22293] diff --git a/components/script/dom/mediasession.rs b/components/script/dom/mediasession.rs index 139264953ca..f87b0dbee63 100644 --- a/components/script/dom/mediasession.rs +++ b/components/script/dom/mediasession.rs @@ -11,12 +11,13 @@ use crate::dom::bindings::codegen::Bindings::MediaSessionBinding::MediaSessionMe use crate::dom::bindings::codegen::Bindings::MediaSessionBinding::MediaSessionPlaybackState; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::{DomRoot, MutNullableDom}; +use crate::dom::htmlmediaelement::HTMLMediaElement; use crate::dom::mediametadata::MediaMetadata; use crate::dom::window::Window; use crate::script_thread::ScriptThread; use dom_struct::dom_struct; use msg::constellation_msg::TopLevelBrowsingContextId; -use script_traits::MediaSessionActionType; +use script_traits::{MediaSessionActionType, MediaSessionEvent, ScriptMsg}; use std::collections::HashMap; use std::rc::Rc; @@ -30,6 +31,9 @@ pub struct MediaSession { /// https://w3c.github.io/mediasession/#supported-media-session-actions #[ignore_malloc_size_of = "Rc"] action_handlers: DomRefCell<HashMap<MediaSessionActionType, Rc<MediaSessionActionHandler>>>, + /// The media instance controlled by this media session. + /// For now only HTMLMediaElements are controlled by media sessions. + media_instance: MutNullableDom<HTMLMediaElement>, } impl MediaSession { @@ -40,6 +44,7 @@ impl MediaSession { metadata: Default::default(), playback_state: DomRefCell::new(MediaSessionPlaybackState::None), action_handlers: DomRefCell::new(HashMap::new()), + media_instance: Default::default(), }; ScriptThread::register_media_session(&media_session, browsing_context_id); media_session @@ -63,6 +68,18 @@ impl MediaSession { } // TODO default action. } + + pub fn send_event(&self, event: MediaSessionEvent) { + let global = self.global(); + let browser_id = global + .as_window() + .window_proxy() + .top_level_browsing_context_id(); + let _ = global + .script_to_constellation_chan() + .send(ScriptMsg::MediaSessionEventMsg(browser_id, event)) + .unwrap(); + } } impl MediaSessionMethods for MediaSession { diff --git a/components/script/dom/navigator.rs b/components/script/dom/navigator.rs index 2b0d0ab128b..a759d3dbe54 100644 --- a/components/script/dom/navigator.rs +++ b/components/script/dom/navigator.rs @@ -21,6 +21,7 @@ use crate::dom::promise::Promise; use crate::dom::serviceworkercontainer::ServiceWorkerContainer; use crate::dom::window::Window; use crate::dom::xr::XR; +use crate::script_thread::ScriptThread; use dom_struct::dom_struct; use std::rc::Rc; @@ -192,7 +193,24 @@ impl NavigatorMethods for Navigator { /// https://w3c.github.io/mediasession/#dom-navigator-mediasession fn MediaSession(&self) -> DomRoot<MediaSession> { - self.mediasession - .or_init(|| MediaSession::new(self.global().as_window())) + self.mediasession.or_init(|| { + // There is a single MediaSession instance per top level browsing context + // and only one active MediaSession globally. + // + // MediaSession creation can happen in two cases: + // + // - If content gets `navigator.mediaSession` + // - If a media instance (HTMLMediaElement so far) starts playing media. + // + // The MediaSession constructor is in charge of registering itself with + // the script thread. + let global = self.global(); + let window = global.as_window(); + let browsing_context_id = window.window_proxy().top_level_browsing_context_id(); + match ScriptThread::get_media_session(browsing_context_id) { + Some(session) => session, + None => MediaSession::new(window), + } + }) } } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index ec1f5852060..e7b39d26420 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -3993,6 +3993,19 @@ impl ScriptThread { .remove(&browsing_context_id); }) } + + pub fn get_media_session( + browsing_context_id: TopLevelBrowsingContextId, + ) -> Option<DomRoot<MediaSession>> { + SCRIPT_THREAD_ROOT.with(|root| { + let script_thread = unsafe { &*root.get().unwrap() }; + script_thread + .media_sessions + .borrow() + .get(&browsing_context_id) + .map(|s| DomRoot::from_ref(&**s)) + }) + } } impl Drop for ScriptThread { diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index b4b85889ea0..f5006f1df6f 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -1087,3 +1087,34 @@ pub enum MediaSessionActionType { /// The action intent is to move the playback time to a specific time. SeekTo, } + +/// https://w3c.github.io/mediasession/#mediametadata +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct MediaMetadata { + /// Title + pub title: String, + /// Artist + pub artist: Option<String>, + /// Album + pub album: Option<String>, +} + +/// https://w3c.github.io/mediasession/#enumdef-mediasessionplaybackstate +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum MediaSessionPlaybackState { + /// The browsing context does not specify whether it’s playing or paused. + None_, + /// The browsing context has paused media and it can be resumed. + Playing, + /// The browsing context is currently playing media and it can be paused. + Paused, +} + +/// Type of events sent from script to the constellation about the media session. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub enum MediaSessionEvent { + /// Indicates that the media metadata is available. + SetMetadata(MediaMetadata), + /// Indicates that the playback state has changed. + PlaybackStateChange(MediaSessionPlaybackState), +} diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index a12101380c9..df1889ab6b2 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -8,6 +8,7 @@ use crate::DocumentState; use crate::IFrameLoadInfoWithData; use crate::LayoutControlMsg; use crate::LoadData; +use crate::MediaSessionEvent; use crate::MessagePortMsg; use crate::PortMessageTask; use crate::StructuredSerializedData; @@ -254,6 +255,9 @@ pub enum ScriptMsg { GetScreenSize(IpcSender<DeviceIntSize>), /// Get the available screen size (pixel) GetScreenAvailSize(IpcSender<DeviceIntSize>), + /// Notifies the constellation about media session events + /// (i.e. when there is metadata for the active media session, playback state changes...). + MediaSessionEventMsg(TopLevelBrowsingContextId, MediaSessionEvent), } impl fmt::Debug for ScriptMsg { @@ -305,6 +309,7 @@ impl fmt::Debug for ScriptMsg { GetClientWindow(..) => "GetClientWindow", GetScreenSize(..) => "GetScreenSize", GetScreenAvailSize(..) => "GetScreenAvailSize", + MediaSessionEventMsg(..) => "MediaSessionEventMsg", }; write!(formatter, "ScriptMsg::{}", variant) } diff --git a/ports/glutin/browser.rs b/ports/glutin/browser.rs index e3da4d13d61..95bea1e72bb 100644 --- a/ports/glutin/browser.rs +++ b/ports/glutin/browser.rs @@ -449,6 +449,10 @@ where error!("Failed to store profile: {}", e); } }, + EmbedderMsg::MediaSession(_) => { + debug!("MediaSession received"); + // TODO(ferjm) + }, } } } |