aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/embedder_traits/lib.rs20
-rw-r--r--components/script/dom/htmlmediaelement.rs23
-rw-r--r--components/script/dom/mediasession.rs59
-rw-r--r--components/script/dom/webidls/MediaSession.webidl9
-rw-r--r--ports/libsimpleservo/api/src/lib.rs12
-rw-r--r--ports/libsimpleservo/capi/src/lib.rs17
-rw-r--r--ports/libsimpleservo/jniapi/src/lib.rs24
-rw-r--r--support/android/apk/servoapp/src/main/java/org/mozilla/servo/MainActivity.java11
-rw-r--r--support/android/apk/servoapp/src/main/java/org/mozilla/servo/MediaSession.java6
-rw-r--r--support/android/apk/servoview/src/main/java/org/mozilla/servoview/JNIServo.java2
-rw-r--r--support/android/apk/servoview/src/main/java/org/mozilla/servoview/Servo.java6
-rw-r--r--tests/wpt/metadata/mediasession/idlharness.window.js.ini9
-rw-r--r--tests/wpt/metadata/mediasession/positionstate.html.ini19
13 files changed, 183 insertions, 34 deletions
diff --git a/components/embedder_traits/lib.rs b/components/embedder_traits/lib.rs
index 9d4e56dd297..4ac1b4f793d 100644
--- a/components/embedder_traits/lib.rs
+++ b/components/embedder_traits/lib.rs
@@ -240,6 +240,24 @@ pub enum MediaSessionPlaybackState {
Paused,
}
+/// https://w3c.github.io/mediasession/#dictdef-mediapositionstate
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub struct MediaPositionState {
+ pub duration: f64,
+ pub playback_rate: f64,
+ pub position: f64,
+}
+
+impl MediaPositionState {
+ pub fn new(duration: f64, playback_rate: f64, position: f64) -> Self {
+ Self {
+ duration,
+ playback_rate,
+ position,
+ }
+ }
+}
+
/// Type of events sent from script to the embedder about the media session.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum MediaSessionEvent {
@@ -247,4 +265,6 @@ pub enum MediaSessionEvent {
SetMetadata(MediaMetadata),
/// Indicates that the playback state has changed.
PlaybackStateChange(MediaSessionPlaybackState),
+ /// Indicates that the position state is set.
+ SetPositionState(MediaPositionState),
}
diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs
index 651084cfb84..91acd4b97e7 100644
--- a/components/script/dom/htmlmediaelement.rs
+++ b/components/script/dom/htmlmediaelement.rs
@@ -67,7 +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::{MediaSessionEvent, MediaSessionPlaybackState};
+use embedder_traits::{MediaPositionState, MediaSessionEvent, MediaSessionPlaybackState};
use euclid::default::Size2D;
use headers::{ContentLength, ContentRange, HeaderMapExt};
use html5ever::{LocalName, Prefix};
@@ -1780,6 +1780,15 @@ impl HTMLMediaElement {
.add(self.playback_position.get(), position);
self.playback_position.set(position);
self.time_marches_on();
+ let media_position_state =
+ MediaPositionState::new(self.duration.get(), self.playbackRate.get(), position);
+ debug!(
+ "Sending media session event set position state {:?}",
+ media_position_state
+ );
+ self.send_media_session_event(MediaSessionEvent::SetPositionState(
+ media_position_state,
+ ));
},
PlayerEvent::SeekData(p, ref seek_lock) => {
self.fetch_request(Some(p), Some(seek_lock.clone()));
@@ -1925,6 +1934,18 @@ impl HTMLMediaElement {
media_session.send_event(event);
}
+
+ pub fn set_duration(&self, duration: f64) {
+ self.duration.set(duration);
+ }
+
+ pub fn reset(&self) {
+ if let Some(ref player) = *self.player.borrow() {
+ if let Err(e) = player.lock().unwrap().stop() {
+ eprintln!("Could not stop player {:?}", e);
+ }
+ }
+ }
}
// 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 1523e9a0ae6..8dfc88a1c04 100644
--- a/components/script/dom/mediasession.rs
+++ b/components/script/dom/mediasession.rs
@@ -9,10 +9,13 @@ use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaE
use crate::dom::bindings::codegen::Bindings::MediaMetadataBinding::MediaMetadataInit;
use crate::dom::bindings::codegen::Bindings::MediaMetadataBinding::MediaMetadataMethods;
use crate::dom::bindings::codegen::Bindings::MediaSessionBinding;
+use crate::dom::bindings::codegen::Bindings::MediaSessionBinding::MediaPositionState;
use crate::dom::bindings::codegen::Bindings::MediaSessionBinding::MediaSessionAction;
use crate::dom::bindings::codegen::Bindings::MediaSessionBinding::MediaSessionActionHandler;
use crate::dom::bindings::codegen::Bindings::MediaSessionBinding::MediaSessionMethods;
use crate::dom::bindings::codegen::Bindings::MediaSessionBinding::MediaSessionPlaybackState;
+use crate::dom::bindings::error::{Error, Fallible};
+use crate::dom::bindings::num::Finite;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
@@ -194,6 +197,62 @@ impl MediaSessionMethods for MediaSession {
None => self.action_handlers.borrow_mut().remove(&action.into()),
};
}
+
+ /// https://w3c.github.io/mediasession/#dom-mediasession-setpositionstate
+ fn SetPositionState(&self, state: &MediaPositionState) -> Fallible<()> {
+ // If the state is an empty dictionary then clear the position state.
+ if state.duration.is_none() && state.position.is_none() && state.playbackRate.is_none() {
+ if let Some(media_instance) = self.media_instance.get() {
+ media_instance.reset();
+ }
+ return Ok(());
+ }
+
+ // If the duration is not present or its value is null, throw a TypeError.
+ if state.duration.is_none() {
+ return Err(Error::Type(
+ "duration is not present or its value is null".to_owned(),
+ ));
+ }
+
+ // If the duration is negative, throw a TypeError.
+ if let Some(state_duration) = state.duration {
+ if *state_duration < 0.0 {
+ return Err(Error::Type("duration is negative".to_owned()));
+ }
+ }
+
+ // If the position is negative or greater than duration, throw a TypeError.
+ if let Some(state_position) = state.position {
+ if *state_position < 0.0 {
+ return Err(Error::Type("position is negative".to_owned()));
+ }
+ if let Some(state_duration) = state.duration {
+ if *state_position > *state_duration {
+ return Err(Error::Type("position is greater than duration".to_owned()));
+ }
+ }
+ }
+
+ // If the playbackRate is zero throw a TypeError.
+ if let Some(state_playback_rate) = state.playbackRate {
+ if *state_playback_rate <= 0.0 {
+ return Err(Error::Type("playbackRate is zero".to_owned()));
+ }
+ }
+
+ // Update the position state and last position updated time.
+ if let Some(media_instance) = self.media_instance.get() {
+ media_instance.set_duration(state.duration.map(|v| *v).unwrap());
+ // If the playbackRate is not present or its value is null, set it to 1.0.
+ let _ =
+ media_instance.SetPlaybackRate(state.playbackRate.unwrap_or(Finite::wrap(1.0)))?;
+ // If the position is not present or its value is null, set it to zero.
+ media_instance.SetCurrentTime(state.position.unwrap_or(Finite::wrap(0.0)));
+ }
+
+ Ok(())
+ }
}
impl From<MediaSessionAction> for MediaSessionActionType {
diff --git a/components/script/dom/webidls/MediaSession.webidl b/components/script/dom/webidls/MediaSession.webidl
index 12b3fe062ba..2680ea0c40b 100644
--- a/components/script/dom/webidls/MediaSession.webidl
+++ b/components/script/dom/webidls/MediaSession.webidl
@@ -42,6 +42,12 @@ dictionary MediaSessionSeekToActionDetails : MediaSessionActionDetails {
boolean? fastSeek;
};
+dictionary MediaPositionState {
+ double duration;
+ double playbackRate;
+ double position;
+};
+
callback MediaSessionActionHandler = void(/*MediaSessionActionDetails details*/);
[Exposed=Window]
@@ -52,6 +58,5 @@ interface MediaSession {
void setActionHandler(MediaSessionAction action, MediaSessionActionHandler? handler);
- //void setPositionState(optional MediaPositionState? state);
+ [Throws] void setPositionState(optional MediaPositionState state = {});
};
-
diff --git a/ports/libsimpleservo/api/src/lib.rs b/ports/libsimpleservo/api/src/lib.rs
index c7986e05623..05a8bf9d32c 100644
--- a/ports/libsimpleservo/api/src/lib.rs
+++ b/ports/libsimpleservo/api/src/lib.rs
@@ -132,8 +132,10 @@ pub trait HostTrait {
fn set_clipboard_contents(&self, contents: String);
/// Called when we get the media session metadata/
fn on_media_session_metadata(&self, title: String, artist: String, album: String);
- /// Called when the media sessoin playback state changes.
+ /// Called when the media session playback state changes.
fn on_media_session_playback_state_change(&self, state: i32);
+ /// Called when the media session position state is set.
+ fn on_media_session_set_position_state(&self, duration: f64, position: f64, playback_rate: f64);
}
pub struct ServoGlue {
@@ -594,6 +596,14 @@ impl ServoGlue {
.callbacks
.host_callbacks
.on_media_session_playback_state_change(state as i32),
+ MediaSessionEvent::SetPositionState(position_state) => self
+ .callbacks
+ .host_callbacks
+ .on_media_session_set_position_state(
+ position_state.duration,
+ position_state.position,
+ position_state.playback_rate,
+ ),
};
},
EmbedderMsg::Status(..) |
diff --git a/ports/libsimpleservo/capi/src/lib.rs b/ports/libsimpleservo/capi/src/lib.rs
index 956c7e3a380..8ca727213a3 100644
--- a/ports/libsimpleservo/capi/src/lib.rs
+++ b/ports/libsimpleservo/capi/src/lib.rs
@@ -219,6 +219,8 @@ pub struct CHostCallbacks {
pub on_media_session_metadata:
extern "C" fn(title: *const c_char, album: *const c_char, artist: *const c_char),
pub on_media_session_playback_state_change: extern "C" fn(state: i32),
+ pub on_media_session_set_position_state:
+ extern "C" fn(duration: f64, position: f64, playback_rate: f64),
}
/// Servo options
@@ -648,7 +650,7 @@ impl HostTrait for HostCallbacks {
}
fn on_load_started(&self) {
- debug!("on_load_ended");
+ debug!("on_load_started");
(self.0.on_load_started)();
}
@@ -727,4 +729,17 @@ impl HostTrait for HostCallbacks {
debug!("on_media_session_playback_state_change {:?}", state);
(self.0.on_media_session_playback_state_change)(state);
}
+
+ fn on_media_session_set_position_state(
+ &self,
+ duration: f64,
+ position: f64,
+ playback_rate: f64,
+ ) {
+ debug!(
+ "on_media_session_set_position_state ({:?} {:?} {:?})",
+ duration, position, playback_rate
+ );
+ (self.0.on_media_session_set_position_state)(duration, position, playback_rate);
+ }
}
diff --git a/ports/libsimpleservo/jniapi/src/lib.rs b/ports/libsimpleservo/jniapi/src/lib.rs
index 1125f3a5680..74db8774a15 100644
--- a/ports/libsimpleservo/jniapi/src/lib.rs
+++ b/ports/libsimpleservo/jniapi/src/lib.rs
@@ -560,6 +560,30 @@ impl HostTrait for HostCallbacks {
)
.unwrap();
}
+
+ fn on_media_session_set_position_state(
+ &self,
+ duration: f64,
+ position: f64,
+ playback_rate: f64,
+ ) {
+ info!(
+ "on_media_session_playback_state_change ({:?}, {:?}, {:?})",
+ duration, position, playback_rate
+ );
+ let env = self.jvm.get_env().unwrap();
+ let duration = JValue::Float(duration as jfloat);
+ let position = JValue::Float(position as jfloat);
+ let playback_rate = JValue::Float(playback_rate as jfloat);
+
+ env.call_method(
+ self.callbacks.as_obj(),
+ "onMediaSessionSetPositionState",
+ "(FFF)V",
+ &[duration, position, playback_rate],
+ )
+ .unwrap();
+ }
}
fn initialize_android_glue(env: &JNIEnv, activity: JObject) {
diff --git a/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MainActivity.java b/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MainActivity.java
index 21a6e7f996f..1e1c9dbabeb 100644
--- a/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MainActivity.java
+++ b/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MainActivity.java
@@ -255,4 +255,15 @@ public class MainActivity extends Activity implements Servo.Client {
return;
}
}
+
+ @Override
+ public void onMediaSessionSetPositionState(float duration, float position, float playbackRate) {
+ Log.d("onMediaSessionSetPositionState", duration + " " + position + " " + playbackRate);
+ if (mMediaSession == null) {
+ mMediaSession = new MediaSession(mServoView, this, getApplicationContext());
+ }
+
+ mMediaSession.setPositionState(duration, position, playbackRate);
+ return;
+ }
}
diff --git a/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MediaSession.java b/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MediaSession.java
index 5b1852d5d3f..bb9f874b463 100644
--- a/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MediaSession.java
+++ b/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MediaSession.java
@@ -192,4 +192,8 @@ public class MediaSession {
showMediaSessionControls();
}
}
-}
+
+ // Not implemented
+ // see https://github.com/servo/servo/pull/24885#discussion_r352496117
+ public void setPositionState(float duration, float position, float playbackRate) {}
+} \ No newline at end of file
diff --git a/support/android/apk/servoview/src/main/java/org/mozilla/servoview/JNIServo.java b/support/android/apk/servoview/src/main/java/org/mozilla/servoview/JNIServo.java
index 6181a298f0a..5fa91fa34f6 100644
--- a/support/android/apk/servoview/src/main/java/org/mozilla/servoview/JNIServo.java
+++ b/support/android/apk/servoview/src/main/java/org/mozilla/servoview/JNIServo.java
@@ -115,6 +115,8 @@ public class JNIServo {
void onMediaSessionMetadata(String title, String artist, String album);
void onMediaSessionPlaybackStateChange(int state);
+
+ void onMediaSessionSetPositionState(float duration, float position, float playbackRate);
}
}
diff --git a/support/android/apk/servoview/src/main/java/org/mozilla/servoview/Servo.java b/support/android/apk/servoview/src/main/java/org/mozilla/servoview/Servo.java
index f45f55e79f5..7f8bd3fc211 100644
--- a/support/android/apk/servoview/src/main/java/org/mozilla/servoview/Servo.java
+++ b/support/android/apk/servoview/src/main/java/org/mozilla/servoview/Servo.java
@@ -192,6 +192,8 @@ public class Servo {
void onMediaSessionMetadata(String title, String artist, String album);
void onMediaSessionPlaybackStateChange(int state);
+
+ void onMediaSessionSetPositionState(float duration, float position, float playbackRate);
}
public interface RunCallback {
@@ -285,5 +287,9 @@ public class Servo {
public void onMediaSessionPlaybackStateChange(int state) {
mRunCallback.inUIThread(() -> mClient.onMediaSessionPlaybackStateChange(state));
}
+
+ public void onMediaSessionSetPositionState(float duration, float position, float playbackRate) {
+ mRunCallback.inUIThread(() -> mClient.onMediaSessionSetPositionState(duration, position, playbackRate));
+ }
}
}
diff --git a/tests/wpt/metadata/mediasession/idlharness.window.js.ini b/tests/wpt/metadata/mediasession/idlharness.window.js.ini
index 5e65e535b96..c6afb039eed 100644
--- a/tests/wpt/metadata/mediasession/idlharness.window.js.ini
+++ b/tests/wpt/metadata/mediasession/idlharness.window.js.ini
@@ -1,13 +1,4 @@
[idlharness.window.html]
- [MediaSession interface: calling setPositionState(MediaPositionState) on navigator.mediaSession with too few arguments must throw TypeError]
- expected: FAIL
-
- [MediaSession interface: navigator.mediaSession must inherit property "setPositionState(MediaPositionState)" with the proper type]
- expected: FAIL
-
- [MediaSession interface: operation setPositionState(MediaPositionState)]
- expected: FAIL
-
[MediaMetadata interface: attribute artwork]
expected: FAIL
diff --git a/tests/wpt/metadata/mediasession/positionstate.html.ini b/tests/wpt/metadata/mediasession/positionstate.html.ini
deleted file mode 100644
index ca73e31fb84..00000000000
--- a/tests/wpt/metadata/mediasession/positionstate.html.ini
+++ /dev/null
@@ -1,19 +0,0 @@
-[positionstate.html]
- [Test setPositionState with a null value]
- expected: FAIL
-
- [Test setPositionState with zero duration]
- expected: FAIL
-
- [Test setPositionState with a valid value for forward playback]
- expected: FAIL
-
- [Test setPositionState with optional position]
- expected: FAIL
-
- [Test setPositionState with only duration]
- expected: FAIL
-
- [Test setPositionState with optional playback rate]
- expected: FAIL
-