aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/audiocontext.rs16
-rw-r--r--components/script/dom/bindings/codegen/CodegenRust.py17
-rw-r--r--components/script/dom/bindings/trace.rs14
-rw-r--r--components/script/dom/dedicatedworkerglobalscope.rs12
-rw-r--r--components/script/dom/document.rs115
-rw-r--r--components/script/dom/htmlmediaelement.rs122
-rw-r--r--components/script/dom/htmlvideoelement.rs6
-rw-r--r--components/script/dom/mediaelementaudiosourcenode.rs80
-rw-r--r--components/script/dom/mediametadata.rs97
-rw-r--r--components/script/dom/mediasession.rs213
-rw-r--r--components/script/dom/mod.rs3
-rw-r--r--components/script/dom/navigator.rs19
-rw-r--r--components/script/dom/promiserejectionevent.rs11
-rw-r--r--components/script/dom/serviceworkerglobalscope.rs2
-rw-r--r--components/script/dom/webidls/AudioContext.webidl2
-rw-r--r--components/script/dom/webidls/MediaElementAudioSourceNode.webidl17
-rw-r--r--components/script/dom/webidls/MediaMetadata.webidl30
-rw-r--r--components/script/dom/webidls/MediaSession.webidl57
-rw-r--r--components/script/dom/webidls/PromiseRejectionEvent.webidl4
-rw-r--r--components/script/dom/worklet.rs2
20 files changed, 724 insertions, 115 deletions
diff --git a/components/script/dom/audiocontext.rs b/components/script/dom/audiocontext.rs
index 2b6f04b421b..0212cb78ffa 100644
--- a/components/script/dom/audiocontext.rs
+++ b/components/script/dom/audiocontext.rs
@@ -20,6 +20,8 @@ use crate::dom::bindings::num::Finite;
use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::DomRoot;
+use crate::dom::htmlmediaelement::HTMLMediaElement;
+use crate::dom::mediaelementaudiosourcenode::MediaElementAudioSourceNode;
use crate::dom::promise::Promise;
use crate::dom::window::Window;
use crate::task_source::TaskSource;
@@ -97,6 +99,10 @@ impl AudioContext {
self.context.resume();
}
}
+
+ pub fn base(&self) -> DomRoot<BaseAudioContext> {
+ DomRoot::from_ref(&self.context)
+ }
}
impl AudioContextMethods for AudioContext {
@@ -240,6 +246,16 @@ impl AudioContextMethods for AudioContext {
// Step 6.
promise
}
+
+ /// https://webaudio.github.io/web-audio-api/#dom-audiocontext-createmediaelementsource
+ fn CreateMediaElementSource(
+ &self,
+ media_element: &HTMLMediaElement,
+ ) -> Fallible<DomRoot<MediaElementAudioSourceNode>> {
+ let global = self.global();
+ let window = global.as_window();
+ MediaElementAudioSourceNode::new(window, self, media_element)
+ }
}
impl From<AudioContextLatencyCategory> for LatencyCategory {
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py
index 7610b8442b4..fe3398417b2 100644
--- a/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/components/script/dom/bindings/codegen/CodegenRust.py
@@ -6425,7 +6425,8 @@ class CGDictionary(CGThing):
mustRoot = ""
if self.membersNeedTracing():
mustRoot = "#[unrooted_must_root_lint::must_root]\n"
- derive += ["Default"]
+ if not self.hasRequiredFields(self.dictionary):
+ derive += ["Default"]
return (string.Template(
"#[derive(${derive})]\n"
@@ -6485,16 +6486,14 @@ class CGDictionary(CGThing):
selfName = self.makeClassName(d)
if self.membersNeedTracing():
actualType = "RootedTraceableBox<%s>" % selfName
- preInitial = "let mut dictionary = RootedTraceableBox::new(%s::default());\n" % selfName
- initParent = initParent = ("dictionary.parent = %s;\n" % initParent) if initParent else ""
- memberInits = CGList([memberInit(m, False) for m in self.memberInfo])
- postInitial = ""
+ preInitial = "let dictionary = RootedTraceableBox::new(%s {\n" % selfName
+ postInitial = "});\n"
else:
actualType = selfName
preInitial = "let dictionary = %s {\n" % selfName
postInitial = "};\n"
- initParent = ("parent: %s,\n" % initParent) if initParent else ""
- memberInits = CGList([memberInit(m, True) for m in self.memberInfo])
+ initParent = ("parent: %s,\n" % initParent) if initParent else ""
+ memberInits = CGList([memberInit(m, True) for m in self.memberInfo])
return string.Template(
"impl ${selfName} {\n"
@@ -6540,8 +6539,8 @@ class CGDictionary(CGThing):
"initParent": CGIndenter(CGGeneric(initParent), indentLevel=16).define(),
"initMembers": CGIndenter(memberInits, indentLevel=16).define(),
"insertMembers": CGIndenter(memberInserts, indentLevel=8).define(),
- "preInitial": CGIndenter(CGGeneric(preInitial), indentLevel=16).define(),
- "postInitial": CGIndenter(CGGeneric(postInitial), indentLevel=16).define(),
+ "preInitial": CGIndenter(CGGeneric(preInitial), indentLevel=8).define(),
+ "postInitial": CGIndenter(CGGeneric(postInitial), indentLevel=8).define(),
})
def membersNeedTracing(self):
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 65faac1bb95..2051a1a19d8 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -57,7 +57,7 @@ use content_security_policy::CspList;
use crossbeam_channel::{Receiver, Sender};
use cssparser::RGBA;
use devtools_traits::{CSSError, TimelineMarkerType, WorkerId};
-use embedder_traits::EventLoopWaker;
+use embedder_traits::{EventLoopWaker, MediaMetadata};
use encoding_rs::{Decoder, Encoding};
use euclid::default::{Point2D, Rect, Rotation3D, Transform2D, Transform3D};
use euclid::Length as EuclidLength;
@@ -94,8 +94,8 @@ use profile_traits::time::ProfilerChan as TimeProfilerChan;
use script_layout_interface::rpc::LayoutRPC;
use script_layout_interface::OpaqueStyleAndLayoutData;
use script_traits::transferable::MessagePortImpl;
-use script_traits::DrawAPaintImageResult;
-use script_traits::{DocumentActivity, ScriptToConstellationChan, TimerEventId, TimerSource};
+use script_traits::{DocumentActivity, DrawAPaintImageResult};
+use script_traits::{MediaSessionActionType, ScriptToConstellationChan, TimerEventId, TimerSource};
use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType};
use selectors::matching::ElementSelectorFlags;
use serde::{Deserialize, Serialize};
@@ -107,7 +107,8 @@ use servo_media::audio::context::AudioContext;
use servo_media::audio::graph::NodeId;
use servo_media::audio::panner_node::{DistanceModel, PanningModel};
use servo_media::audio::param::ParamType;
-use servo_media::player::frame::Frame;
+use servo_media::player::audio::AudioRenderer;
+use servo_media::player::video::VideoFrame;
use servo_media::player::Player;
use servo_media::streams::registry::MediaStreamId;
use servo_media::streams::MediaStreamType;
@@ -532,8 +533,11 @@ unsafe_no_jsmanaged_fields!(Point2D<f32>, Rect<Au>);
unsafe_no_jsmanaged_fields!(Rect<f32>);
unsafe_no_jsmanaged_fields!(CascadeData);
unsafe_no_jsmanaged_fields!(WindowGLContext);
-unsafe_no_jsmanaged_fields!(Frame);
+unsafe_no_jsmanaged_fields!(VideoFrame);
unsafe_no_jsmanaged_fields!(WebGLContextId);
+unsafe_no_jsmanaged_fields!(Arc<Mutex<dyn AudioRenderer>>);
+unsafe_no_jsmanaged_fields!(MediaSessionActionType);
+unsafe_no_jsmanaged_fields!(MediaMetadata);
unsafe impl<'a> JSTraceable for &'a str {
#[inline]
diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs
index f5895379003..9b16b0348d7 100644
--- a/components/script/dom/dedicatedworkerglobalscope.rs
+++ b/components/script/dom/dedicatedworkerglobalscope.rs
@@ -32,6 +32,7 @@ use crate::script_runtime::{
new_child_runtime, CommonScriptMsg, JSContext as SafeJSContext, Runtime, ScriptChan, ScriptPort,
};
use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
+use crate::task_source::networking::NetworkingTaskSource;
use crate::task_source::TaskSourceName;
use crossbeam_channel::{unbounded, Receiver, Sender};
use devtools_traits::DevtoolScriptControlMsg;
@@ -344,7 +345,16 @@ impl DedicatedWorkerGlobalScope {
.referrer_policy(referrer_policy)
.origin(origin);
- let runtime = unsafe { new_child_runtime(parent) };
+ let runtime = unsafe {
+ if let Some(pipeline_id) = pipeline_id {
+ new_child_runtime(
+ parent,
+ Some(NetworkingTaskSource(parent_sender.clone(), pipeline_id)),
+ )
+ } else {
+ new_child_runtime(parent, None)
+ }
+ };
let (devtools_mpsc_chan, devtools_mpsc_port) = unbounded();
ROUTER.route_ipc_receiver_to_crossbeam_sender(
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 5a344879005..a7a56665bf1 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -506,62 +506,69 @@ impl Document {
pub fn set_activity(&self, activity: DocumentActivity) {
// This function should only be called on documents with a browsing context
assert!(self.has_browsing_context);
+ if activity == self.activity.get() {
+ return;
+ }
+
// Set the document's activity level, reflow if necessary, and suspend or resume timers.
- if activity != self.activity.get() {
- self.activity.set(activity);
- let media = ServoMedia::get().unwrap();
- let pipeline_id = self.window().pipeline_id().expect("doc with no pipeline");
- let client_context_id =
- ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
- if activity == DocumentActivity::FullyActive {
- self.title_changed();
- self.dirty_all_nodes();
- self.window()
- .reflow(ReflowGoal::Full, ReflowReason::CachedPageNeededReflow);
- self.window().resume();
- media.resume(&client_context_id);
- // html.spec.whatwg.org/multipage/#history-traversal
- // Step 4.6
- if self.ready_state.get() == DocumentReadyState::Complete {
- let document = Trusted::new(self);
- self.window
- .task_manager()
- .dom_manipulation_task_source()
- .queue(
- task!(fire_pageshow_event: move || {
- let document = document.root();
- let window = document.window();
- // Step 4.6.1
- if document.page_showing.get() {
- return;
- }
- // Step 4.6.2
- document.page_showing.set(true);
- // Step 4.6.4
- let event = PageTransitionEvent::new(
- window,
- atom!("pageshow"),
- false, // bubbles
- false, // cancelable
- true, // persisted
- );
- let event = event.upcast::<Event>();
- event.set_trusted(true);
- // FIXME(nox): Why are errors silenced here?
- let _ = window.upcast::<EventTarget>().dispatch_event_with_target(
- document.upcast(),
- &event,
- );
- }),
- self.window.upcast(),
- )
- .unwrap();
- }
- } else {
- self.window().suspend();
- media.suspend(&client_context_id);
- }
+ self.activity.set(activity);
+ let media = ServoMedia::get().unwrap();
+ let pipeline_id = self.window().pipeline_id().expect("doc with no pipeline");
+ let client_context_id =
+ ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
+
+ if activity != DocumentActivity::FullyActive {
+ self.window().suspend();
+ media.suspend(&client_context_id);
+ return;
}
+
+ self.title_changed();
+ self.dirty_all_nodes();
+ self.window()
+ .reflow(ReflowGoal::Full, ReflowReason::CachedPageNeededReflow);
+ self.window().resume();
+ media.resume(&client_context_id);
+
+ if self.ready_state.get() != DocumentReadyState::Complete {
+ return;
+ }
+
+ // html.spec.whatwg.org/multipage/#history-traversal
+ // Step 4.6
+ let document = Trusted::new(self);
+ self.window
+ .task_manager()
+ .dom_manipulation_task_source()
+ .queue(
+ task!(fire_pageshow_event: move || {
+ let document = document.root();
+ let window = document.window();
+ // Step 4.6.1
+ if document.page_showing.get() {
+ return;
+ }
+ // Step 4.6.2
+ document.page_showing.set(true);
+ // Step 4.6.4
+ let event = PageTransitionEvent::new(
+ window,
+ atom!("pageshow"),
+ false, // bubbles
+ false, // cancelable
+ true, // persisted
+ );
+ let event = event.upcast::<Event>();
+ event.set_trusted(true);
+ // FIXME(nox): Why are errors silenced here?
+ let _ = window.upcast::<EventTarget>().dispatch_event_with_target(
+ document.upcast(),
+ &event,
+ );
+ }),
+ self.window.upcast(),
+ )
+ .unwrap();
}
pub fn origin(&self) -> &MutableOrigin {
diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs
index 5afc2deb381..f8f707a0dd9 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::{
@@ -65,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::{MediaSessionEvent, MediaSessionPlaybackState};
use euclid::default::Size2D;
use headers::{ContentLength, ContentRange, HeaderMapExt};
use html5ever::{LocalName, Prefix};
@@ -79,7 +82,8 @@ use net_traits::{CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseLis
use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType};
use script_layout_interface::HTMLMediaData;
use servo_config::pref;
-use servo_media::player::frame::{Frame, FrameRenderer};
+use servo_media::player::audio::AudioRenderer;
+use servo_media::player::video::{VideoFrame, VideoFrameRenderer};
use servo_media::player::{PlaybackState, Player, PlayerError, PlayerEvent, SeekLock, StreamType};
use servo_media::{ClientContextId, ServoMedia, SupportsMediaType};
use servo_url::ServoUrl;
@@ -100,10 +104,10 @@ enum FrameStatus {
Unlocked,
}
-struct FrameHolder(FrameStatus, Frame);
+struct FrameHolder(FrameStatus, VideoFrame);
impl FrameHolder {
- fn new(frame: Frame) -> FrameHolder {
+ fn new(frame: VideoFrame) -> FrameHolder {
FrameHolder(FrameStatus::Unlocked, frame)
}
@@ -119,7 +123,7 @@ impl FrameHolder {
};
}
- fn set(&mut self, new_frame: Frame) {
+ fn set(&mut self, new_frame: VideoFrame) {
if self.0 == FrameStatus::Unlocked {
self.1 = new_frame
};
@@ -137,7 +141,7 @@ impl FrameHolder {
}
}
- fn get_frame(&self) -> Frame {
+ fn get_frame(&self) -> VideoFrame {
self.1.clone()
}
}
@@ -170,8 +174,8 @@ impl MediaFrameRenderer {
}
}
-impl FrameRenderer for MediaFrameRenderer {
- fn render(&mut self, frame: Frame) {
+impl VideoFrameRenderer for MediaFrameRenderer {
+ fn render(&mut self, frame: VideoFrame) {
let mut txn = Transaction::new();
if let Some(old_image_key) = mem::replace(&mut self.very_old_frame, self.old_frame.take()) {
@@ -325,7 +329,9 @@ pub struct HTMLMediaElement {
#[ignore_malloc_size_of = "servo_media"]
player: DomRefCell<Option<Arc<Mutex<dyn Player>>>>,
#[ignore_malloc_size_of = "Arc"]
- frame_renderer: Arc<Mutex<MediaFrameRenderer>>,
+ video_renderer: Arc<Mutex<MediaFrameRenderer>>,
+ #[ignore_malloc_size_of = "Arc"]
+ audio_renderer: DomRefCell<Option<Arc<Mutex<dyn AudioRenderer>>>>,
/// https://html.spec.whatwg.org/multipage/#show-poster-flag
show_poster: Cell<bool>,
/// https://html.spec.whatwg.org/multipage/#dom-media-duration
@@ -410,9 +416,10 @@ impl HTMLMediaElement {
pending_play_promises: Default::default(),
in_flight_play_promises_queue: Default::default(),
player: Default::default(),
- frame_renderer: Arc::new(Mutex::new(MediaFrameRenderer::new(
+ video_renderer: Arc::new(Mutex::new(MediaFrameRenderer::new(
document.window().get_webrender_api_sender(),
))),
+ audio_renderer: Default::default(),
show_poster: Cell::new(true),
duration: Cell::new(f64::NAN),
playback_position: Cell::new(0.),
@@ -588,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;
},
@@ -1293,7 +1299,7 @@ impl HTMLMediaElement {
// Step 6.
if let ImageResponse::Loaded(image, _) = image {
- self.frame_renderer
+ self.video_renderer
.lock()
.unwrap()
.render_poster_frame(image);
@@ -1325,11 +1331,14 @@ impl HTMLMediaElement {
let window = window_from_node(self);
let (action_sender, action_receiver) = ipc::channel::<PlayerEvent>().unwrap();
- let renderer: Option<Arc<Mutex<dyn FrameRenderer>>> = match self.media_type_id() {
+ let video_renderer: Option<Arc<Mutex<dyn VideoFrameRenderer>>> = match self.media_type_id()
+ {
HTMLMediaElementTypeId::HTMLAudioElement => None,
- HTMLMediaElementTypeId::HTMLVideoElement => Some(self.frame_renderer.clone()),
+ HTMLMediaElementTypeId::HTMLVideoElement => Some(self.video_renderer.clone()),
};
+ let audio_renderer = self.audio_renderer.borrow().as_ref().map(|r| r.clone());
+
let pipeline_id = window
.pipeline_id()
.expect("Cannot create player outside of a pipeline");
@@ -1339,7 +1348,8 @@ impl HTMLMediaElement {
&client_context_id,
stream_type,
action_sender,
- renderer,
+ video_renderer,
+ audio_renderer,
Box::new(window.get_player_context()),
);
@@ -1385,7 +1395,7 @@ impl HTMLMediaElement {
.unwrap_or((0, None));
self.id.set(player_id);
- self.frame_renderer.lock().unwrap().player_id = Some(player_id);
+ self.video_renderer.lock().unwrap().player_id = Some(player_id);
if let Some(image_receiver) = image_receiver {
let trusted_node = Trusted::new(self);
@@ -1400,11 +1410,11 @@ impl HTMLMediaElement {
if let Err(err) = task_source.queue_with_canceller(
task!(handle_glplayer_message: move || {
trace!("GLPlayer message {:?}", msg);
- let frame_renderer = this.root().frame_renderer.clone();
+ let video_renderer = this.root().video_renderer.clone();
match msg {
GLPlayerMsgForward::Lock(sender) => {
- frame_renderer
+ video_renderer
.lock()
.unwrap()
.current_frame_holder
@@ -1415,7 +1425,7 @@ impl HTMLMediaElement {
});
},
GLPlayerMsgForward::Unlock() => {
- frame_renderer
+ video_renderer
.lock()
.unwrap()
.current_frame_holder
@@ -1527,7 +1537,7 @@ impl HTMLMediaElement {
)));
self.upcast::<EventTarget>().fire_event(atom!("error"));
},
- PlayerEvent::FrameUpdated => {
+ PlayerEvent::VideoFrameUpdated => {
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
},
PlayerEvent::MetadataUpdated(ref metadata) => {
@@ -1717,6 +1727,17 @@ impl HTMLMediaElement {
if self.Controls() {
self.render_controls();
}
+
+ let global = self.global();
+ let window = global.as_window();
+
+ // Update the media session metadata title with the obtained metadata.
+ window.Navigator().MediaSession().update_title(
+ metadata
+ .title
+ .clone()
+ .unwrap_or(window.get_url().into_string()),
+ );
},
PlayerEvent::NeedData => {
// The player needs more data.
@@ -1774,13 +1795,33 @@ impl HTMLMediaElement {
};
ScriptThread::await_stable_state(Microtask::MediaElement(task));
},
- PlayerEvent::StateChanged(ref state) => match *state {
- PlaybackState::Paused => {
- if self.ready_state.get() == ReadyState::HaveMetadata {
- self.change_ready_state(ReadyState::HaveEnoughData);
- }
- },
- _ => {},
+ PlayerEvent::StateChanged(ref state) => {
+ let mut media_session_playback_state = MediaSessionPlaybackState::None_;
+ match *state {
+ PlaybackState::Paused => {
+ media_session_playback_state = MediaSessionPlaybackState::Paused;
+ if self.ready_state.get() == ReadyState::HaveMetadata {
+ self.change_ready_state(ReadyState::HaveEnoughData);
+ }
+ },
+ PlaybackState::Playing => {
+ media_session_playback_state = MediaSessionPlaybackState::Playing;
+ },
+ PlaybackState::Buffering => {
+ // Do not send the media session playback state change event
+ // in this case as a None_ state is expected to clean up the
+ // session.
+ return;
+ },
+ _ => {},
+ };
+ debug!(
+ "Sending media session event playback state changed to {:?}",
+ media_session_playback_state
+ );
+ self.send_media_session_event(MediaSessionEvent::PlaybackStateChange(
+ media_session_playback_state,
+ ));
},
}
}
@@ -1855,12 +1896,35 @@ impl HTMLMediaElement {
}
}
- pub fn get_current_frame(&self) -> Option<Frame> {
- match self.frame_renderer.lock().unwrap().current_frame_holder {
+ pub fn get_current_frame(&self) -> Option<VideoFrame> {
+ match self.video_renderer.lock().unwrap().current_frame_holder {
Some(ref holder) => Some(holder.get_frame()),
None => return None,
}
}
+
+ /// By default the audio is rendered through the audio sink automatically
+ /// selected by the servo-media Player instance. However, in some cases, like
+ /// the WebAudio MediaElementAudioSourceNode, we need to set a custom audio
+ /// renderer.
+ pub fn set_audio_renderer(&self, audio_renderer: Arc<Mutex<dyn AudioRenderer>>) {
+ *self.audio_renderer.borrow_mut() = Some(audio_renderer);
+ if let Some(ref player) = *self.player.borrow() {
+ if let Err(e) = player.lock().unwrap().stop() {
+ eprintln!("Could not stop player {:?}", e);
+ }
+ 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.register_media_instance(&self);
+
+ media_session.send_event(event);
+ }
}
// XXX Placeholder for [https://github.com/servo/servo/issues/22293]
@@ -2365,7 +2429,7 @@ impl LayoutHTMLMediaElementHelpers for LayoutDom<HTMLMediaElement> {
fn data(&self) -> HTMLMediaData {
let media = unsafe { &*self.unsafe_get() };
HTMLMediaData {
- current_frame: media.frame_renderer.lock().unwrap().current_frame.clone(),
+ current_frame: media.video_renderer.lock().unwrap().current_frame.clone(),
}
}
}
diff --git a/components/script/dom/htmlvideoelement.rs b/components/script/dom/htmlvideoelement.rs
index 55990c1ffe5..69f8b9a85e6 100644
--- a/components/script/dom/htmlvideoelement.rs
+++ b/components/script/dom/htmlvideoelement.rs
@@ -35,7 +35,7 @@ use net_traits::{
CoreResourceMsg, FetchChannels, FetchMetadata, FetchResponseListener, FetchResponseMsg,
};
use net_traits::{NetworkError, ResourceFetchTiming, ResourceTimingType};
-use servo_media::player::frame::Frame;
+use servo_media::player::video::VideoFrame;
use servo_url::ServoUrl;
use std::cell::Cell;
use std::sync::{Arc, Mutex};
@@ -58,8 +58,8 @@ pub struct HTMLVideoElement {
/// is being fetched.
load_blocker: DomRefCell<Option<LoadBlocker>>,
/// A copy of the last frame
- #[ignore_malloc_size_of = "Frame"]
- last_frame: DomRefCell<Option<Frame>>,
+ #[ignore_malloc_size_of = "VideoFrame"]
+ last_frame: DomRefCell<Option<VideoFrame>>,
}
impl HTMLVideoElement {
diff --git a/components/script/dom/mediaelementaudiosourcenode.rs b/components/script/dom/mediaelementaudiosourcenode.rs
new file mode 100644
index 00000000000..07ca70bd80e
--- /dev/null
+++ b/components/script/dom/mediaelementaudiosourcenode.rs
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use crate::dom::audiocontext::AudioContext;
+use crate::dom::audionode::AudioNode;
+use crate::dom::bindings::codegen::Bindings::MediaElementAudioSourceNodeBinding;
+use crate::dom::bindings::codegen::Bindings::MediaElementAudioSourceNodeBinding::MediaElementAudioSourceNodeMethods;
+use crate::dom::bindings::codegen::Bindings::MediaElementAudioSourceNodeBinding::MediaElementAudioSourceOptions;
+use crate::dom::bindings::error::Fallible;
+use crate::dom::bindings::reflector::reflect_dom_object;
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::htmlmediaelement::HTMLMediaElement;
+use crate::dom::window::Window;
+use dom_struct::dom_struct;
+use servo_media::audio::media_element_source_node::MediaElementSourceNodeMessage;
+use servo_media::audio::node::{AudioNodeInit, AudioNodeMessage};
+use std::sync::mpsc;
+
+#[dom_struct]
+pub struct MediaElementAudioSourceNode {
+ node: AudioNode,
+ media_element: Dom<HTMLMediaElement>,
+}
+
+impl MediaElementAudioSourceNode {
+ #[allow(unrooted_must_root)]
+ fn new_inherited(
+ context: &AudioContext,
+ media_element: &HTMLMediaElement,
+ ) -> Fallible<MediaElementAudioSourceNode> {
+ let node = AudioNode::new_inherited(
+ AudioNodeInit::MediaElementSourceNode,
+ &*context.base(),
+ Default::default(),
+ 0,
+ 1,
+ )?;
+ let (sender, receiver) = mpsc::channel();
+ node.message(AudioNodeMessage::MediaElementSourceNode(
+ MediaElementSourceNodeMessage::GetAudioRenderer(sender),
+ ));
+ let audio_renderer = receiver.recv().unwrap();
+ media_element.set_audio_renderer(audio_renderer);
+ let media_element = Dom::from_ref(media_element);
+ Ok(MediaElementAudioSourceNode {
+ node,
+ media_element,
+ })
+ }
+
+ #[allow(unrooted_must_root)]
+ pub fn new(
+ window: &Window,
+ context: &AudioContext,
+ media_element: &HTMLMediaElement,
+ ) -> Fallible<DomRoot<MediaElementAudioSourceNode>> {
+ let node = MediaElementAudioSourceNode::new_inherited(context, media_element)?;
+ Ok(reflect_dom_object(
+ Box::new(node),
+ window,
+ MediaElementAudioSourceNodeBinding::Wrap,
+ ))
+ }
+
+ pub fn Constructor(
+ window: &Window,
+ context: &AudioContext,
+ options: &MediaElementAudioSourceOptions,
+ ) -> Fallible<DomRoot<MediaElementAudioSourceNode>> {
+ MediaElementAudioSourceNode::new(window, context, &*options.mediaElement)
+ }
+}
+
+impl MediaElementAudioSourceNodeMethods for MediaElementAudioSourceNode {
+ /// https://webaudio.github.io/web-audio-api/#dom-mediaelementaudiosourcenode-mediaelement
+ fn MediaElement(&self) -> DomRoot<HTMLMediaElement> {
+ DomRoot::from_ref(&*self.media_element)
+ }
+}
diff --git a/components/script/dom/mediametadata.rs b/components/script/dom/mediametadata.rs
new file mode 100644
index 00000000000..f2e94abfaa1
--- /dev/null
+++ b/components/script/dom/mediametadata.rs
@@ -0,0 +1,97 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::MediaMetadataBinding;
+use crate::dom::bindings::codegen::Bindings::MediaMetadataBinding::MediaMetadataInit;
+use crate::dom::bindings::codegen::Bindings::MediaMetadataBinding::MediaMetadataMethods;
+use crate::dom::bindings::error::Fallible;
+use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
+use crate::dom::bindings::root::{DomRoot, MutNullableDom};
+use crate::dom::bindings::str::DOMString;
+use crate::dom::mediasession::MediaSession;
+use crate::dom::window::Window;
+use dom_struct::dom_struct;
+
+#[dom_struct]
+pub struct MediaMetadata {
+ reflector_: Reflector,
+ session: MutNullableDom<MediaSession>,
+ title: DomRefCell<DOMString>,
+ artist: DomRefCell<DOMString>,
+ album: DomRefCell<DOMString>,
+}
+
+impl MediaMetadata {
+ fn new_inherited(init: &MediaMetadataInit) -> MediaMetadata {
+ MediaMetadata {
+ reflector_: Reflector::new(),
+ session: Default::default(),
+ title: DomRefCell::new(init.title.clone()),
+ artist: DomRefCell::new(init.artist.clone()),
+ album: DomRefCell::new(init.album.clone()),
+ }
+ }
+
+ pub fn new(global: &Window, init: &MediaMetadataInit) -> DomRoot<MediaMetadata> {
+ reflect_dom_object(
+ Box::new(MediaMetadata::new_inherited(init)),
+ global,
+ MediaMetadataBinding::Wrap,
+ )
+ }
+
+ /// https://w3c.github.io/mediasession/#dom-mediametadata-mediametadata
+ pub fn Constructor(
+ window: &Window,
+ init: &MediaMetadataInit,
+ ) -> Fallible<DomRoot<MediaMetadata>> {
+ Ok(MediaMetadata::new(window, init))
+ }
+
+ fn queue_update_metadata_algorithm(&self) {
+ if self.session.get().is_none() {
+ return;
+ }
+ }
+
+ pub fn set_session(&self, session: &MediaSession) {
+ self.session.set(Some(&session));
+ }
+}
+
+impl MediaMetadataMethods for MediaMetadata {
+ /// https://w3c.github.io/mediasession/#dom-mediametadata-title
+ fn Title(&self) -> DOMString {
+ self.title.borrow().clone()
+ }
+
+ /// https://w3c.github.io/mediasession/#dom-mediametadata-title
+ fn SetTitle(&self, value: DOMString) {
+ *self.title.borrow_mut() = value;
+ self.queue_update_metadata_algorithm();
+ }
+
+ /// https://w3c.github.io/mediasession/#dom-mediametadata-artist
+ fn Artist(&self) -> DOMString {
+ self.artist.borrow().clone()
+ }
+
+ /// https://w3c.github.io/mediasession/#dom-mediametadata-artist
+ fn SetArtist(&self, value: DOMString) {
+ *self.artist.borrow_mut() = value;
+ self.queue_update_metadata_algorithm();
+ }
+
+ /// https://w3c.github.io/mediasession/#dom-mediametadata-album
+ fn Album(&self) -> DOMString {
+ self.album.borrow().clone()
+ }
+
+ /// https://w3c.github.io/mediasession/#dom-mediametadata-album
+ fn SetAlbum(&self, value: DOMString) {
+ *self.album.borrow_mut() = value;
+ self.queue_update_metadata_algorithm();
+ }
+}
diff --git a/components/script/dom/mediasession.rs b/components/script/dom/mediasession.rs
new file mode 100644
index 00000000000..1523e9a0ae6
--- /dev/null
+++ b/components/script/dom/mediasession.rs
@@ -0,0 +1,213 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use crate::compartments::{AlreadyInCompartment, InCompartment};
+use crate::dom::bindings::callback::ExceptionHandling;
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaElementMethods;
+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::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::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::root::{DomRoot, MutNullableDom};
+use crate::dom::bindings::str::DOMString;
+use crate::dom::htmlmediaelement::HTMLMediaElement;
+use crate::dom::mediametadata::MediaMetadata;
+use crate::dom::window::Window;
+use dom_struct::dom_struct;
+use embedder_traits::MediaMetadata as EmbedderMediaMetadata;
+use embedder_traits::MediaSessionEvent;
+use script_traits::MediaSessionActionType;
+use script_traits::ScriptMsg;
+use std::collections::HashMap;
+use std::rc::Rc;
+
+#[dom_struct]
+pub struct MediaSession {
+ reflector_: Reflector,
+ /// https://w3c.github.io/mediasession/#dom-mediasession-metadata
+ #[ignore_malloc_size_of = "defined in embedder_traits"]
+ metadata: DomRefCell<Option<EmbedderMediaMetadata>>,
+ /// https://w3c.github.io/mediasession/#dom-mediasession-playbackstate
+ playback_state: DomRefCell<MediaSessionPlaybackState>,
+ /// 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 {
+ #[allow(unrooted_must_root)]
+ fn new_inherited() -> MediaSession {
+ let media_session = MediaSession {
+ reflector_: Reflector::new(),
+ metadata: DomRefCell::new(None),
+ playback_state: DomRefCell::new(MediaSessionPlaybackState::None),
+ action_handlers: DomRefCell::new(HashMap::new()),
+ media_instance: Default::default(),
+ };
+ media_session
+ }
+
+ pub fn new(window: &Window) -> DomRoot<MediaSession> {
+ reflect_dom_object(
+ Box::new(MediaSession::new_inherited()),
+ window,
+ MediaSessionBinding::Wrap,
+ )
+ }
+
+ pub fn register_media_instance(&self, media_instance: &HTMLMediaElement) {
+ self.media_instance.set(Some(media_instance));
+ }
+
+ pub fn handle_action(&self, action: MediaSessionActionType) {
+ debug!("Handle media session action {:?}", action);
+
+ if let Some(handler) = self.action_handlers.borrow().get(&action) {
+ if handler.Call__(ExceptionHandling::Report).is_err() {
+ warn!("Error calling MediaSessionActionHandler callback");
+ }
+ return;
+ }
+
+ // Default action.
+ if let Some(media) = self.media_instance.get() {
+ match action {
+ MediaSessionActionType::Play => {
+ let in_compartment_proof = AlreadyInCompartment::assert(&self.global());
+ media.Play(InCompartment::Already(&in_compartment_proof));
+ },
+ MediaSessionActionType::Pause => {
+ media.Pause();
+ },
+ MediaSessionActionType::SeekBackward => {},
+ MediaSessionActionType::SeekForward => {},
+ MediaSessionActionType::PreviousTrack => {},
+ MediaSessionActionType::NextTrack => {},
+ MediaSessionActionType::SkipAd => {},
+ MediaSessionActionType::Stop => {},
+ MediaSessionActionType::SeekTo => {},
+ }
+ }
+ }
+
+ pub fn send_event(&self, event: MediaSessionEvent) {
+ let global = self.global();
+ let window = global.as_window();
+ let pipeline_id = window
+ .pipeline_id()
+ .expect("Cannot send media session event outside of a pipeline");
+ window.send_to_constellation(ScriptMsg::MediaSessionEvent(pipeline_id, event));
+ }
+
+ pub fn update_title(&self, title: String) {
+ let mut metadata = self.metadata.borrow_mut();
+ if let Some(ref mut metadata) = *metadata {
+ // We only update the title with the data provided by the media
+ // player and iff the user did not provide a title.
+ if !metadata.title.is_empty() {
+ return;
+ }
+ metadata.title = title;
+ } else {
+ *metadata = Some(EmbedderMediaMetadata::new(title));
+ }
+ self.send_event(MediaSessionEvent::SetMetadata(
+ metadata.as_ref().unwrap().clone(),
+ ));
+ }
+}
+
+impl MediaSessionMethods for MediaSession {
+ /// https://w3c.github.io/mediasession/#dom-mediasession-metadata
+ fn GetMetadata(&self) -> Option<DomRoot<MediaMetadata>> {
+ if let Some(ref metadata) = *self.metadata.borrow() {
+ let mut init = MediaMetadataInit::empty();
+ init.title = DOMString::from_string(metadata.title.clone());
+ init.artist = DOMString::from_string(metadata.artist.clone());
+ init.album = DOMString::from_string(metadata.album.clone());
+ let global = self.global();
+ Some(MediaMetadata::new(&global.as_window(), &init))
+ } else {
+ None
+ }
+ }
+
+ /// https://w3c.github.io/mediasession/#dom-mediasession-metadata
+ fn SetMetadata(&self, metadata: Option<&MediaMetadata>) {
+ if let Some(ref metadata) = metadata {
+ metadata.set_session(self);
+ }
+
+ let global = self.global();
+ let window = global.as_window();
+ let _metadata = match metadata {
+ Some(m) => {
+ let title = if m.Title().is_empty() {
+ window.get_url().into_string()
+ } else {
+ m.Title().into()
+ };
+ EmbedderMediaMetadata {
+ title,
+ artist: m.Artist().into(),
+ album: m.Album().into(),
+ }
+ },
+ None => EmbedderMediaMetadata::new(window.get_url().into_string()),
+ };
+
+ *self.metadata.borrow_mut() = Some(_metadata.clone());
+
+ self.send_event(MediaSessionEvent::SetMetadata(_metadata));
+ }
+
+ /// https://w3c.github.io/mediasession/#dom-mediasession-playbackstate
+ fn PlaybackState(&self) -> MediaSessionPlaybackState {
+ *self.playback_state.borrow()
+ }
+
+ /// https://w3c.github.io/mediasession/#dom-mediasession-playbackstate
+ fn SetPlaybackState(&self, state: MediaSessionPlaybackState) {
+ *self.playback_state.borrow_mut() = state;
+ }
+
+ /// https://w3c.github.io/mediasession/#update-action-handler-algorithm
+ fn SetActionHandler(
+ &self,
+ action: MediaSessionAction,
+ handler: Option<Rc<MediaSessionActionHandler>>,
+ ) {
+ match handler {
+ Some(handler) => self
+ .action_handlers
+ .borrow_mut()
+ .insert(action.into(), handler.clone()),
+ None => self.action_handlers.borrow_mut().remove(&action.into()),
+ };
+ }
+}
+
+impl From<MediaSessionAction> for MediaSessionActionType {
+ fn from(action: MediaSessionAction) -> MediaSessionActionType {
+ match action {
+ MediaSessionAction::Play => MediaSessionActionType::Play,
+ MediaSessionAction::Pause => MediaSessionActionType::Pause,
+ MediaSessionAction::Seekbackward => MediaSessionActionType::SeekBackward,
+ MediaSessionAction::Seekforward => MediaSessionActionType::SeekForward,
+ MediaSessionAction::Previoustrack => MediaSessionActionType::PreviousTrack,
+ MediaSessionAction::Nexttrack => MediaSessionActionType::NextTrack,
+ MediaSessionAction::Skipad => MediaSessionActionType::SkipAd,
+ MediaSessionAction::Stop => MediaSessionActionType::Stop,
+ MediaSessionAction::Seekto => MediaSessionActionType::SeekTo,
+ }
+ }
+}
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index 16d2c6fb686..ef562931bee 100644
--- a/components/script/dom/mod.rs
+++ b/components/script/dom/mod.rs
@@ -394,11 +394,14 @@ pub mod inputevent;
pub mod keyboardevent;
pub mod location;
pub mod mediadevices;
+pub mod mediaelementaudiosourcenode;
pub mod mediaerror;
pub mod mediafragmentparser;
pub mod medialist;
+pub mod mediametadata;
pub mod mediaquerylist;
pub mod mediaquerylistevent;
+pub mod mediasession;
pub mod mediastream;
pub mod mediastreamtrack;
pub mod messagechannel;
diff --git a/components/script/dom/navigator.rs b/components/script/dom/navigator.rs
index 8a0f6a21d98..127883dd956 100644
--- a/components/script/dom/navigator.rs
+++ b/components/script/dom/navigator.rs
@@ -12,6 +12,7 @@ use crate::dom::bindings::str::DOMString;
use crate::dom::bluetooth::Bluetooth;
use crate::dom::gamepadlist::GamepadList;
use crate::dom::mediadevices::MediaDevices;
+use crate::dom::mediasession::MediaSession;
use crate::dom::mimetypearray::MimeTypeArray;
use crate::dom::navigatorinfo;
use crate::dom::permissions::Permissions;
@@ -34,6 +35,7 @@ pub struct Navigator {
mediadevices: MutNullableDom<MediaDevices>,
gamepads: MutNullableDom<GamepadList>,
permissions: MutNullableDom<Permissions>,
+ mediasession: MutNullableDom<MediaSession>,
}
impl Navigator {
@@ -48,6 +50,7 @@ impl Navigator {
mediadevices: Default::default(),
gamepads: Default::default(),
permissions: Default::default(),
+ mediasession: Default::default(),
}
}
@@ -186,4 +189,20 @@ impl NavigatorMethods for Navigator {
self.mediadevices
.or_init(|| MediaDevices::new(&self.global()))
}
+
+ /// https://w3c.github.io/mediasession/#dom-navigator-mediasession
+ fn MediaSession(&self) -> DomRoot<MediaSession> {
+ self.mediasession.or_init(|| {
+ // There is a single MediaSession instance per Pipeline
+ // 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.
+ let global = self.global();
+ let window = global.as_window();
+ MediaSession::new(window)
+ })
+ }
}
diff --git a/components/script/dom/promiserejectionevent.rs b/components/script/dom/promiserejectionevent.rs
index 4e4a148905a..19bf96b9b12 100644
--- a/components/script/dom/promiserejectionevent.rs
+++ b/components/script/dom/promiserejectionevent.rs
@@ -5,7 +5,7 @@
use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use crate::dom::bindings::codegen::Bindings::PromiseRejectionEventBinding;
use crate::dom::bindings::codegen::Bindings::PromiseRejectionEventBinding::PromiseRejectionEventMethods;
-use crate::dom::bindings::error::{Error, Fallible};
+use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::DomRoot;
@@ -72,14 +72,7 @@ impl PromiseRejectionEvent {
init: RootedTraceableBox<PromiseRejectionEventBinding::PromiseRejectionEventInit>,
) -> Fallible<DomRoot<Self>> {
let reason = init.reason.handle();
- let promise = match init.promise.as_ref() {
- Some(promise) => promise.clone(),
- None => {
- return Err(Error::Type(
- "required member promise is undefined.".to_string(),
- ));
- },
- };
+ let promise = init.promise.clone();
let bubbles = EventBubbles::from(init.parent.bubbles);
let cancelable = EventCancelable::from(init.parent.cancelable);
diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs
index 657f43627ed..cb72a4a25e4 100644
--- a/components/script/dom/serviceworkerglobalscope.rs
+++ b/components/script/dom/serviceworkerglobalscope.rs
@@ -315,7 +315,7 @@ impl ServiceWorkerGlobalScope {
},
};
- let runtime = new_rt_and_cx();
+ let runtime = new_rt_and_cx(None);
let (devtools_mpsc_chan, devtools_mpsc_port) = unbounded();
ROUTER
diff --git a/components/script/dom/webidls/AudioContext.webidl b/components/script/dom/webidls/AudioContext.webidl
index 9e5dd6bd556..cd9e18edfa0 100644
--- a/components/script/dom/webidls/AudioContext.webidl
+++ b/components/script/dom/webidls/AudioContext.webidl
@@ -33,7 +33,7 @@ interface AudioContext : BaseAudioContext {
Promise<void> suspend();
Promise<void> close();
- // MediaElementAudioSourceNode createMediaElementSource(HTMLMediaElement mediaElement);
+ [Throws] MediaElementAudioSourceNode createMediaElementSource(HTMLMediaElement mediaElement);
// MediaStreamAudioSourceNode createMediaStreamSource(MediaStream mediaStream);
// MediaStreamTrackAudioSourceNode createMediaStreamTrackSource(MediaStreamTrack mediaStreamTrack);
// MediaStreamAudioDestinationNode createMediaStreamDestination();
diff --git a/components/script/dom/webidls/MediaElementAudioSourceNode.webidl b/components/script/dom/webidls/MediaElementAudioSourceNode.webidl
new file mode 100644
index 00000000000..5afe7775caa
--- /dev/null
+++ b/components/script/dom/webidls/MediaElementAudioSourceNode.webidl
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+/*
+ * The origin of this IDL file is
+ * https://webaudio.github.io/web-audio-api/#mediaelementaudiosourcenode
+ */
+
+dictionary MediaElementAudioSourceOptions {
+ required HTMLMediaElement mediaElement;
+};
+
+[Exposed=Window]
+interface MediaElementAudioSourceNode : AudioNode {
+ [Throws] constructor (AudioContext context, MediaElementAudioSourceOptions options);
+ [SameObject] readonly attribute HTMLMediaElement mediaElement;
+};
diff --git a/components/script/dom/webidls/MediaMetadata.webidl b/components/script/dom/webidls/MediaMetadata.webidl
new file mode 100644
index 00000000000..495aeef8e35
--- /dev/null
+++ b/components/script/dom/webidls/MediaMetadata.webidl
@@ -0,0 +1,30 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+/*
+ * The origin of this IDL file is
+ * https://w3c.github.io/mediasession/#mediametadata
+ */
+
+dictionary MediaImage {
+ required USVString src;
+ DOMString sizes = "";
+ DOMString type = "";
+};
+
+[Exposed=Window]
+interface MediaMetadata {
+ [Throws] constructor(optional MediaMetadataInit init = {});
+ attribute DOMString title;
+ attribute DOMString artist;
+ attribute DOMString album;
+ // TODO: https://github.com/servo/servo/issues/10072
+ // attribute FrozenArray<MediaImage> artwork;
+};
+
+dictionary MediaMetadataInit {
+ DOMString title = "";
+ DOMString artist = "";
+ DOMString album = "";
+ sequence<MediaImage> artwork = [];
+};
diff --git a/components/script/dom/webidls/MediaSession.webidl b/components/script/dom/webidls/MediaSession.webidl
new file mode 100644
index 00000000000..12b3fe062ba
--- /dev/null
+++ b/components/script/dom/webidls/MediaSession.webidl
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+/*
+ * The origin of this IDL file is
+ * https://w3c.github.io/mediasession/#mediasession
+ */
+
+[Exposed=Window]
+partial interface Navigator {
+ [SameObject] readonly attribute MediaSession mediaSession;
+};
+
+enum MediaSessionPlaybackState {
+ "none",
+ "paused",
+ "playing"
+};
+
+enum MediaSessionAction {
+ "play",
+ "pause",
+ "seekbackward",
+ "seekforward",
+ "previoustrack",
+ "nexttrack",
+ "skipad",
+ "stop",
+ "seekto"
+};
+
+dictionary MediaSessionActionDetails {
+ required MediaSessionAction action;
+};
+
+dictionary MediaSessionSeekActionDetails : MediaSessionActionDetails {
+ double? seekOffset;
+};
+
+dictionary MediaSessionSeekToActionDetails : MediaSessionActionDetails {
+ required double seekTime;
+ boolean? fastSeek;
+};
+
+callback MediaSessionActionHandler = void(/*MediaSessionActionDetails details*/);
+
+[Exposed=Window]
+interface MediaSession {
+ attribute MediaMetadata? metadata;
+
+ attribute MediaSessionPlaybackState playbackState;
+
+ void setActionHandler(MediaSessionAction action, MediaSessionActionHandler? handler);
+
+ //void setPositionState(optional MediaPositionState? state);
+};
+
diff --git a/components/script/dom/webidls/PromiseRejectionEvent.webidl b/components/script/dom/webidls/PromiseRejectionEvent.webidl
index 6ef93b8b1a7..70d11b1ff33 100644
--- a/components/script/dom/webidls/PromiseRejectionEvent.webidl
+++ b/components/script/dom/webidls/PromiseRejectionEvent.webidl
@@ -6,12 +6,12 @@
[Exposed=(Window,Worker)]
interface PromiseRejectionEvent : Event {
- [Throws] constructor(DOMString type, optional PromiseRejectionEventInit eventInitDict = {});
+ [Throws] constructor(DOMString type, PromiseRejectionEventInit eventInitDict);
readonly attribute Promise<any> promise;
readonly attribute any reason;
};
dictionary PromiseRejectionEventInit : EventInit {
- /* required */ Promise<any> promise;
+ required Promise<any> promise;
any reason;
};
diff --git a/components/script/dom/worklet.rs b/components/script/dom/worklet.rs
index e882566e07f..196864f5527 100644
--- a/components/script/dom/worklet.rs
+++ b/components/script/dom/worklet.rs
@@ -477,7 +477,7 @@ impl WorkletThread {
global_init: init.global_init,
global_scopes: HashMap::new(),
control_buffer: None,
- runtime: new_rt_and_cx(),
+ runtime: new_rt_and_cx(None),
should_gc: false,
gc_threshold: MIN_GC_THRESHOLD,
});