diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 8b0a7b073a3..db9d94e0c88 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -1778,9 +1778,6 @@ where new_value, ); }, - FromScriptMsg::MediaSessionEventMsg(browser_id, event) => { - // TODO - }, } } diff --git a/components/embedder_traits/lib.rs b/components/embedder_traits/lib.rs index da526d5f478..7d967e46277 100644 --- a/components/embedder_traits/lib.rs +++ b/components/embedder_traits/lib.rs @@ -162,9 +162,9 @@ pub enum EmbedderMsg { Shutdown, /// Report a complete sampled profile ReportProfile(Vec), - /// Sent when a media session is activated or deactivated. - /// There can only be a single active media session. - MediaSession(bool), + /// Notifies the embedder about media session events + /// (i.e. when there is metadata for the active media session, playback state changes...). + MediaSessionEvent(MediaSessionEvent), } impl Debug for EmbedderMsg { @@ -197,7 +197,7 @@ impl Debug for EmbedderMsg { EmbedderMsg::AllowOpeningBrowser(..) => write!(f, "AllowOpeningBrowser"), EmbedderMsg::BrowserCreated(..) => write!(f, "BrowserCreated"), EmbedderMsg::ReportProfile(..) => write!(f, "ReportProfile"), - EmbedderMsg::MediaSession(..) => write!(f, "MediaSession"), + EmbedderMsg::MediaSessionEvent(..) => write!(f, "MediaSessionEvent"), } } } @@ -206,3 +206,34 @@ impl Debug for EmbedderMsg { /// the `String` content is expected to be extension (e.g, "doc", without the prefixing ".") #[derive(Clone, Debug, Deserialize, Serialize)] pub struct FilterPattern(pub String); + +/// https://w3c.github.io/mediasession/#mediametadata +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct MediaMetadata { + /// Title + pub title: String, + /// Artist + pub artist: Option, + /// Album + pub album: Option, +} + +/// 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 embedder 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/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index c816a69ca1a..5b6c0d35e93 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -67,6 +67,7 @@ use crate::script_thread::ScriptThread; use crate::task_source::TaskSource; use dom_struct::dom_struct; use embedder_traits::resources::{self, Resource as EmbedderResource}; +use embedder_traits::{MediaMetadata, MediaSessionEvent}; use euclid::default::Size2D; use headers::{ContentLength, ContentRange, HeaderMapExt}; use html5ever::{LocalName, Prefix}; @@ -80,7 +81,6 @@ 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}; @@ -1727,6 +1727,14 @@ impl HTMLMediaElement { if self.Controls() { self.render_controls(); } + + // Send a media session event with the obtained metadata. + self.send_media_session_event(MediaSessionEvent::SetMetadata(MediaMetadata { + // TODO(ferjm) set url if no title. + title: metadata.title.clone().unwrap_or("".to_string()), + artist: None, + album: None, + })); }, PlayerEvent::NeedData => { // The player needs more data. diff --git a/components/script/dom/mediasession.rs b/components/script/dom/mediasession.rs index f87b0dbee63..ef64170f312 100644 --- a/components/script/dom/mediasession.rs +++ b/components/script/dom/mediasession.rs @@ -16,8 +16,9 @@ use crate::dom::mediametadata::MediaMetadata; use crate::dom::window::Window; use crate::script_thread::ScriptThread; use dom_struct::dom_struct; +use embedder_traits::{EmbedderMsg, MediaSessionEvent}; use msg::constellation_msg::TopLevelBrowsingContextId; -use script_traits::{MediaSessionActionType, MediaSessionEvent, ScriptMsg}; +use script_traits::MediaSessionActionType; use std::collections::HashMap; use std::rc::Rc; @@ -71,14 +72,8 @@ impl MediaSession { 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(); + let window = global.as_window(); + window.send_to_embedder(EmbedderMsg::MediaSessionEvent(event)); } } diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index f5006f1df6f..b4b85889ea0 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -1087,34 +1087,3 @@ 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, - /// Album - pub album: Option, -} - -/// 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 df1889ab6b2..64d6b1f5389 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -255,9 +255,6 @@ pub enum ScriptMsg { GetScreenSize(IpcSender), /// Get the available screen size (pixel) GetScreenAvailSize(IpcSender), - /// 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 { @@ -309,7 +306,6 @@ impl fmt::Debug for ScriptMsg { GetClientWindow(..) => "GetClientWindow", GetScreenSize(..) => "GetScreenSize", GetScreenAvailSize(..) => "GetScreenAvailSize", - MediaSessionEventMsg(..) => "MediaSessionEventMsg", }; write!(formatter, "ScriptMsg::{}", variant) } diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 67751234111..3da82d299fd 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -83,6 +83,7 @@ use constellation::{Constellation, InitialConstellationState, UnprivilegedPipeli use constellation::{FromCompositorLogger, FromScriptLogger}; use crossbeam_channel::{unbounded, Sender}; use embedder_traits::{EmbedderMsg, EmbedderProxy, EmbedderReceiver, EventLoopWaker}; +use embedder_traits::{MediaSessionEvent, MediaSessionPlaybackState}; use env_logger::Builder as EnvLoggerBuilder; use euclid::{Scale, Size2D}; #[cfg(all( @@ -270,6 +271,9 @@ pub struct Servo { embedder_receiver: EmbedderReceiver, embedder_events: Vec<(Option, EmbedderMsg)>, profiler_enabled: bool, + webgl_thread_data: Option>, + /// Browser ID of the active media session, if any. + active_media_session: Option, } #[derive(Clone)] @@ -555,6 +559,8 @@ where embedder_receiver: embedder_receiver, embedder_events: Vec::new(), profiler_enabled: false, + webgl_thread_data, + active_media_session: None, } } @@ -744,6 +750,31 @@ where self.embedder_events.push(event); }, + (EmbedderMsg::MediaSessionEvent(event), ShutdownState::NotShuttingDown) => { + // Unlikely at this point, but we may receive events coming from + // different media sessions, so we set the active media session based + // on Playing events. + // The last media session claiming to be in playing state is set to + // the active media session. + // Events coming from inactive media sessions are discarded. + if self.active_media_session.is_some() { + match event { + MediaSessionEvent::PlaybackStateChange(ref state) => { + match state { + MediaSessionPlaybackState::Playing => (), + _ => return, + }; + }, + _ => (), + }; + } + self.active_media_session = top_level_browsing_context; + self.embedder_events.push(( + top_level_browsing_context, + EmbedderMsg::MediaSessionEvent(event), + )); + }, + (msg, ShutdownState::NotShuttingDown) => { self.embedder_events.push((top_level_browsing_context, msg)); }, diff --git a/ports/libsimpleservo/api/src/lib.rs b/ports/libsimpleservo/api/src/lib.rs index 4f1d0aa6a22..b7f0d3587b4 100644 --- a/ports/libsimpleservo/api/src/lib.rs +++ b/ports/libsimpleservo/api/src/lib.rs @@ -42,6 +42,7 @@ thread_local! { /// It will be called to notify embedder that some events are available, /// and that perform_updates need to be called pub use servo::embedder_traits::EventLoopWaker; +pub use servo::embedder_traits::MediaSessionEvent; pub struct InitOptions { pub args: Vec, @@ -130,8 +131,8 @@ pub trait HostTrait { fn get_clipboard_contents(&self) -> Option; /// Sets system clipboard contents fn set_clipboard_contents(&self, contents: String); - /// Called when a media session is activated or deactivated. - fn on_media_session(&self, active: bool); + /// Called when there is a new media session event. + fn on_media_session_event(&self, event: MediaSessionEvent); } pub struct ServoGlue { @@ -583,8 +584,8 @@ impl ServoGlue { EmbedderMsg::HideIME => { self.callbacks.host_callbacks.on_ime_state_changed(false); }, - EmbedderMsg::MediaSession(active) => { - self.callbacks.host_callbacks.on_media_session(active); + EmbedderMsg::MediaSessionEvent(event) => { + self.callbacks.host_callbacks.on_media_session_event(event); }, EmbedderMsg::Status(..) | EmbedderMsg::SelectFiles(..) | diff --git a/ports/libsimpleservo/capi/src/lib.rs b/ports/libsimpleservo/capi/src/lib.rs index 4fbd239000e..57043407879 100644 --- a/ports/libsimpleservo/capi/src/lib.rs +++ b/ports/libsimpleservo/capi/src/lib.rs @@ -17,7 +17,7 @@ use env_logger; use log::LevelFilter; use simpleservo::{self, gl_glue, ServoGlue, SERVO}; use simpleservo::{ - Coordinates, EventLoopWaker, HostTrait, InitOptions, MouseButton, VRInitOptions, + Coordinates, EventLoopWaker, HostTrait, InitOptions, MediaSessionEvent, MouseButton, VRInitOptions, }; use std::ffi::{CStr, CString}; #[cfg(target_os = "windows")] @@ -216,7 +216,8 @@ pub struct CHostCallbacks { pub on_ime_state_changed: extern "C" fn(show: bool), pub get_clipboard_contents: extern "C" fn() -> *const c_char, pub set_clipboard_contents: extern "C" fn(contents: *const c_char), - pub on_media_session: extern "C" fn(active: bool), + // TODO(ferjm) pass C representation of media event. + pub on_media_session_event: extern "C" fn(), } /// Servo options @@ -710,8 +711,8 @@ impl HostTrait for HostCallbacks { (self.0.set_clipboard_contents)(contents.as_ptr()); } - fn on_media_session(&self, active: bool) { - debug!("on_media_session (active: {:?})", active); - (self.0.on_media_session)(active); + fn on_media_session_event(&self, event: MediaSessionEvent) { + debug!("on_media_session_event (event: {:?})", event); + (self.0.on_media_session_event)(); } }