Introduce embedder MediaSessionEvent and move active session to Servo

This commit is contained in:
Fernando Jiménez Moreno 2019-10-09 12:16:35 +02:00
parent 4d147d2c56
commit 89d9e3ad78
9 changed files with 90 additions and 61 deletions

View file

@ -1778,9 +1778,6 @@ where
new_value, new_value,
); );
}, },
FromScriptMsg::MediaSessionEventMsg(browser_id, event) => {
// TODO
},
} }
} }

View file

@ -162,9 +162,9 @@ pub enum EmbedderMsg {
Shutdown, Shutdown,
/// Report a complete sampled profile /// Report a complete sampled profile
ReportProfile(Vec<u8>), ReportProfile(Vec<u8>),
/// Sent when a media session is activated or deactivated. /// Notifies the embedder about media session events
/// There can only be a single active media session. /// (i.e. when there is metadata for the active media session, playback state changes...).
MediaSession(bool), MediaSessionEvent(MediaSessionEvent),
} }
impl Debug for EmbedderMsg { impl Debug for EmbedderMsg {
@ -197,7 +197,7 @@ impl Debug for EmbedderMsg {
EmbedderMsg::AllowOpeningBrowser(..) => write!(f, "AllowOpeningBrowser"), EmbedderMsg::AllowOpeningBrowser(..) => write!(f, "AllowOpeningBrowser"),
EmbedderMsg::BrowserCreated(..) => write!(f, "BrowserCreated"), EmbedderMsg::BrowserCreated(..) => write!(f, "BrowserCreated"),
EmbedderMsg::ReportProfile(..) => write!(f, "ReportProfile"), 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 ".") /// the `String` content is expected to be extension (e.g, "doc", without the prefixing ".")
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct FilterPattern(pub String); 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<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 its 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),
}

View file

@ -67,6 +67,7 @@ use crate::script_thread::ScriptThread;
use crate::task_source::TaskSource; use crate::task_source::TaskSource;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use embedder_traits::resources::{self, Resource as EmbedderResource}; use embedder_traits::resources::{self, Resource as EmbedderResource};
use embedder_traits::{MediaMetadata, MediaSessionEvent};
use euclid::default::Size2D; use euclid::default::Size2D;
use headers::{ContentLength, ContentRange, HeaderMapExt}; use headers::{ContentLength, ContentRange, HeaderMapExt};
use html5ever::{LocalName, Prefix}; 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::{CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, Metadata};
use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType}; use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType};
use script_layout_interface::HTMLMediaData; use script_layout_interface::HTMLMediaData;
use script_traits::MediaSessionEvent;
use servo_config::pref; use servo_config::pref;
use servo_media::player::audio::AudioRenderer; use servo_media::player::audio::AudioRenderer;
use servo_media::player::video::{VideoFrame, VideoFrameRenderer}; use servo_media::player::video::{VideoFrame, VideoFrameRenderer};
@ -1727,6 +1727,14 @@ impl HTMLMediaElement {
if self.Controls() { if self.Controls() {
self.render_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 => { PlayerEvent::NeedData => {
// The player needs more data. // The player needs more data.

View file

@ -16,8 +16,9 @@ use crate::dom::mediametadata::MediaMetadata;
use crate::dom::window::Window; use crate::dom::window::Window;
use crate::script_thread::ScriptThread; use crate::script_thread::ScriptThread;
use dom_struct::dom_struct; use dom_struct::dom_struct;
use embedder_traits::{EmbedderMsg, MediaSessionEvent};
use msg::constellation_msg::TopLevelBrowsingContextId; use msg::constellation_msg::TopLevelBrowsingContextId;
use script_traits::{MediaSessionActionType, MediaSessionEvent, ScriptMsg}; use script_traits::MediaSessionActionType;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
@ -71,14 +72,8 @@ impl MediaSession {
pub fn send_event(&self, event: MediaSessionEvent) { pub fn send_event(&self, event: MediaSessionEvent) {
let global = self.global(); let global = self.global();
let browser_id = global let window = global.as_window();
.as_window() window.send_to_embedder(EmbedderMsg::MediaSessionEvent(event));
.window_proxy()
.top_level_browsing_context_id();
let _ = global
.script_to_constellation_chan()
.send(ScriptMsg::MediaSessionEventMsg(browser_id, event))
.unwrap();
} }
} }

View file

@ -1087,34 +1087,3 @@ pub enum MediaSessionActionType {
/// The action intent is to move the playback time to a specific time. /// The action intent is to move the playback time to a specific time.
SeekTo, 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 its 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),
}

View file

@ -255,9 +255,6 @@ pub enum ScriptMsg {
GetScreenSize(IpcSender<DeviceIntSize>), GetScreenSize(IpcSender<DeviceIntSize>),
/// Get the available screen size (pixel) /// Get the available screen size (pixel)
GetScreenAvailSize(IpcSender<DeviceIntSize>), 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 { impl fmt::Debug for ScriptMsg {
@ -309,7 +306,6 @@ impl fmt::Debug for ScriptMsg {
GetClientWindow(..) => "GetClientWindow", GetClientWindow(..) => "GetClientWindow",
GetScreenSize(..) => "GetScreenSize", GetScreenSize(..) => "GetScreenSize",
GetScreenAvailSize(..) => "GetScreenAvailSize", GetScreenAvailSize(..) => "GetScreenAvailSize",
MediaSessionEventMsg(..) => "MediaSessionEventMsg",
}; };
write!(formatter, "ScriptMsg::{}", variant) write!(formatter, "ScriptMsg::{}", variant)
} }

View file

@ -83,6 +83,7 @@ use constellation::{Constellation, InitialConstellationState, UnprivilegedPipeli
use constellation::{FromCompositorLogger, FromScriptLogger}; use constellation::{FromCompositorLogger, FromScriptLogger};
use crossbeam_channel::{unbounded, Sender}; use crossbeam_channel::{unbounded, Sender};
use embedder_traits::{EmbedderMsg, EmbedderProxy, EmbedderReceiver, EventLoopWaker}; use embedder_traits::{EmbedderMsg, EmbedderProxy, EmbedderReceiver, EventLoopWaker};
use embedder_traits::{MediaSessionEvent, MediaSessionPlaybackState};
use env_logger::Builder as EnvLoggerBuilder; use env_logger::Builder as EnvLoggerBuilder;
use euclid::{Scale, Size2D}; use euclid::{Scale, Size2D};
#[cfg(all( #[cfg(all(
@ -270,6 +271,9 @@ pub struct Servo<Window: WindowMethods + 'static + ?Sized> {
embedder_receiver: EmbedderReceiver, embedder_receiver: EmbedderReceiver,
embedder_events: Vec<(Option<BrowserId>, EmbedderMsg)>, embedder_events: Vec<(Option<BrowserId>, EmbedderMsg)>,
profiler_enabled: bool, profiler_enabled: bool,
webgl_thread_data: Option<Rc<WebGLMainThread>>,
/// Browser ID of the active media session, if any.
active_media_session: Option<BrowserId>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -555,6 +559,8 @@ where
embedder_receiver: embedder_receiver, embedder_receiver: embedder_receiver,
embedder_events: Vec::new(), embedder_events: Vec::new(),
profiler_enabled: false, profiler_enabled: false,
webgl_thread_data,
active_media_session: None,
} }
} }
@ -744,6 +750,31 @@ where
self.embedder_events.push(event); 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) => { (msg, ShutdownState::NotShuttingDown) => {
self.embedder_events.push((top_level_browsing_context, msg)); self.embedder_events.push((top_level_browsing_context, msg));
}, },

View file

@ -42,6 +42,7 @@ thread_local! {
/// It will be called to notify embedder that some events are available, /// It will be called to notify embedder that some events are available,
/// and that perform_updates need to be called /// and that perform_updates need to be called
pub use servo::embedder_traits::EventLoopWaker; pub use servo::embedder_traits::EventLoopWaker;
pub use servo::embedder_traits::MediaSessionEvent;
pub struct InitOptions { pub struct InitOptions {
pub args: Vec<String>, pub args: Vec<String>,
@ -130,8 +131,8 @@ pub trait HostTrait {
fn get_clipboard_contents(&self) -> Option<String>; fn get_clipboard_contents(&self) -> Option<String>;
/// Sets system clipboard contents /// Sets system clipboard contents
fn set_clipboard_contents(&self, contents: String); fn set_clipboard_contents(&self, contents: String);
/// Called when a media session is activated or deactivated. /// Called when there is a new media session event.
fn on_media_session(&self, active: bool); fn on_media_session_event(&self, event: MediaSessionEvent);
} }
pub struct ServoGlue { pub struct ServoGlue {
@ -583,8 +584,8 @@ impl ServoGlue {
EmbedderMsg::HideIME => { EmbedderMsg::HideIME => {
self.callbacks.host_callbacks.on_ime_state_changed(false); self.callbacks.host_callbacks.on_ime_state_changed(false);
}, },
EmbedderMsg::MediaSession(active) => { EmbedderMsg::MediaSessionEvent(event) => {
self.callbacks.host_callbacks.on_media_session(active); self.callbacks.host_callbacks.on_media_session_event(event);
}, },
EmbedderMsg::Status(..) | EmbedderMsg::Status(..) |
EmbedderMsg::SelectFiles(..) | EmbedderMsg::SelectFiles(..) |

View file

@ -17,7 +17,7 @@ use env_logger;
use log::LevelFilter; use log::LevelFilter;
use simpleservo::{self, gl_glue, ServoGlue, SERVO}; use simpleservo::{self, gl_glue, ServoGlue, SERVO};
use simpleservo::{ use simpleservo::{
Coordinates, EventLoopWaker, HostTrait, InitOptions, MouseButton, VRInitOptions, Coordinates, EventLoopWaker, HostTrait, InitOptions, MediaSessionEvent, MouseButton, VRInitOptions,
}; };
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
@ -216,7 +216,8 @@ pub struct CHostCallbacks {
pub on_ime_state_changed: extern "C" fn(show: bool), pub on_ime_state_changed: extern "C" fn(show: bool),
pub get_clipboard_contents: extern "C" fn() -> *const c_char, pub get_clipboard_contents: extern "C" fn() -> *const c_char,
pub set_clipboard_contents: extern "C" fn(contents: *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 /// Servo options
@ -710,8 +711,8 @@ impl HostTrait for HostCallbacks {
(self.0.set_clipboard_contents)(contents.as_ptr()); (self.0.set_clipboard_contents)(contents.as_ptr());
} }
fn on_media_session(&self, active: bool) { fn on_media_session_event(&self, event: MediaSessionEvent) {
debug!("on_media_session (active: {:?})", active); debug!("on_media_session_event (event: {:?})", event);
(self.0.on_media_session)(active); (self.0.on_media_session_event)();
} }
} }