1use std::cell::{Cell, RefCell};
6use std::collections::VecDeque;
7use std::rc::Rc;
8use std::sync::{Arc, Mutex, Weak};
9use std::time::{Duration, Instant};
10use std::{f64, mem};
11
12use content_security_policy::sandboxing_directive::SandboxingFlagSet;
13use dom_struct::dom_struct;
14use embedder_traits::{MediaPositionState, MediaSessionEvent, MediaSessionPlaybackState};
15use euclid::default::Size2D;
16use headers::{ContentLength, ContentRange, HeaderMapExt};
17use html5ever::{LocalName, Prefix, QualName, local_name, ns};
18use http::StatusCode;
19use http::header::{self, HeaderMap, HeaderValue};
20use ipc_channel::ipc::{self};
21use ipc_channel::router::ROUTER;
22use js::context::JSContext;
23use js::realm::{AutoRealm, CurrentRealm};
24use layout_api::MediaFrame;
25use media::{GLPlayerMsg, GLPlayerMsgForward, WindowGLContext};
26use net_traits::request::{Destination, RequestId};
27use net_traits::{
28 CoreResourceThread, FetchMetadata, FilteredMetadata, NetworkError, ResourceFetchTiming,
29};
30use paint_api::{CrossProcessPaintApi, ImageUpdate, SerializableImageData};
31use pixels::RasterImage;
32use script_bindings::codegen::InheritTypes::{
33 ElementTypeId, HTMLElementTypeId, HTMLMediaElementTypeId, NodeTypeId,
34};
35use script_bindings::root::assert_in_script;
36use script_bindings::weakref::WeakRef;
37use servo_base::generic_channel::GenericSharedMemory;
38use servo_base::id::WebViewId;
39use servo_config::pref;
40use servo_media::player::audio::AudioRenderer;
41use servo_media::player::video::{VideoFrame, VideoFrameRenderer};
42use servo_media::player::{PlaybackState, Player, PlayerError, PlayerEvent, SeekLock, StreamType};
43use servo_media::{ClientContextId, ServoMedia, SupportsMediaType};
44use servo_url::ServoUrl;
45use stylo_atoms::Atom;
46use uuid::Uuid;
47use webrender_api::{
48 ExternalImageData, ExternalImageId, ExternalImageType, ImageBufferKind, ImageDescriptor,
49 ImageDescriptorFlags, ImageFormat, ImageKey,
50};
51
52use crate::document_loader::{LoadBlocker, LoadType};
53use crate::dom::attr::Attr;
54use crate::dom::audio::audiotrack::AudioTrack;
55use crate::dom::audio::audiotracklist::AudioTrackList;
56use crate::dom::bindings::cell::DomRefCell;
57use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::{
58 CanPlayTypeResult, HTMLMediaElementConstants, HTMLMediaElementMethods,
59};
60use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConstants::*;
61use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods;
62use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
63use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
64use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode};
65use crate::dom::bindings::codegen::Bindings::URLBinding::URLMethods;
66use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
67use crate::dom::bindings::codegen::UnionTypes::{
68 MediaStreamOrBlob, VideoTrackOrAudioTrackOrTextTrack,
69};
70use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
71use crate::dom::bindings::inheritance::Castable;
72use crate::dom::bindings::num::Finite;
73use crate::dom::bindings::refcounted::Trusted;
74use crate::dom::bindings::reflector::DomGlobal;
75use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
76use crate::dom::bindings::str::{DOMString, USVString};
77use crate::dom::blob::Blob;
78use crate::dom::csp::{GlobalCspReporting, Violation};
79use crate::dom::document::Document;
80use crate::dom::element::{
81 AttributeMutation, AttributeMutationReason, CustomElementCreationMode, Element, ElementCreator,
82 cors_setting_for_element, reflect_cross_origin_attribute, set_cross_origin_attribute,
83};
84use crate::dom::event::Event;
85use crate::dom::eventtarget::EventTarget;
86use crate::dom::globalscope::GlobalScope;
87use crate::dom::html::htmlelement::HTMLElement;
88use crate::dom::html::htmlsourceelement::HTMLSourceElement;
89use crate::dom::html::htmlvideoelement::HTMLVideoElement;
90use crate::dom::mediaerror::MediaError;
91use crate::dom::mediafragmentparser::MediaFragmentParser;
92use crate::dom::medialist::MediaList;
93use crate::dom::mediastream::MediaStream;
94use crate::dom::node::{Node, NodeDamage, NodeTraits, UnbindContext};
95use crate::dom::performance::performanceresourcetiming::InitiatorType;
96use crate::dom::promise::Promise;
97use crate::dom::texttrack::TextTrack;
98use crate::dom::texttracklist::TextTrackList;
99use crate::dom::timeranges::{TimeRanges, TimeRangesContainer};
100use crate::dom::trackevent::TrackEvent;
101use crate::dom::url::URL;
102use crate::dom::videotrack::VideoTrack;
103use crate::dom::videotracklist::VideoTrackList;
104use crate::dom::virtualmethods::VirtualMethods;
105use crate::fetch::{FetchCanceller, RequestWithGlobalScope, create_a_potential_cors_request};
106use crate::microtask::{Microtask, MicrotaskRunnable};
107use crate::network_listener::{self, FetchResponseListener, ResourceTimingListener};
108use crate::realms::enter_auto_realm;
109use crate::script_runtime::CanGc;
110use crate::script_thread::ScriptThread;
111use crate::task_source::SendableTaskSource;
112
113static MEDIA_CONTROL_CSS: &str = include_str!("../../resources/media-controls.css");
115
116static MEDIA_CONTROL_JS: &str = include_str!("../../resources/media-controls.js");
118
119const SEEK_POSITION_THRESHOLD: f64 = 0.5;
123
124#[derive(MallocSizeOf, PartialEq)]
125enum FrameStatus {
126 Locked,
127 Unlocked,
128}
129
130#[derive(MallocSizeOf)]
131struct FrameHolder(
132 FrameStatus,
133 #[ignore_malloc_size_of = "defined in servo-media"] VideoFrame,
134);
135
136impl FrameHolder {
137 fn new(frame: VideoFrame) -> FrameHolder {
138 FrameHolder(FrameStatus::Unlocked, frame)
139 }
140
141 fn lock(&mut self) {
142 if self.0 == FrameStatus::Unlocked {
143 self.0 = FrameStatus::Locked;
144 };
145 }
146
147 fn unlock(&mut self) {
148 if self.0 == FrameStatus::Locked {
149 self.0 = FrameStatus::Unlocked;
150 };
151 }
152
153 fn set(&mut self, new_frame: VideoFrame) {
154 if self.0 == FrameStatus::Unlocked {
155 self.1 = new_frame
156 };
157 }
158
159 fn get(&self) -> (u32, Size2D<i32>, usize) {
160 if self.0 == FrameStatus::Locked {
161 (
162 self.1.get_texture_id(),
163 Size2D::new(self.1.get_width(), self.1.get_height()),
164 0,
165 )
166 } else {
167 unreachable!();
168 }
169 }
170
171 fn get_frame(&self) -> VideoFrame {
172 self.1.clone()
173 }
174}
175
176#[derive(MallocSizeOf)]
177pub(crate) struct MediaFrameRenderer {
178 webview_id: WebViewId,
179 player_id: Option<usize>,
180 glplayer_id: Option<u64>,
181 paint_api: CrossProcessPaintApi,
182 #[ignore_malloc_size_of = "Defined in other crates"]
183 player_context: WindowGLContext,
184 current_frame: Option<MediaFrame>,
185 old_frame: Option<ImageKey>,
186 very_old_frame: Option<ImageKey>,
187 current_frame_holder: Option<FrameHolder>,
188 poster_frame: Option<MediaFrame>,
190}
191
192impl MediaFrameRenderer {
193 fn new(
194 webview_id: WebViewId,
195 paint_api: CrossProcessPaintApi,
196 player_context: WindowGLContext,
197 ) -> Self {
198 Self {
199 webview_id,
200 player_id: None,
201 glplayer_id: None,
202 paint_api,
203 player_context,
204 current_frame: None,
205 old_frame: None,
206 very_old_frame: None,
207 current_frame_holder: None,
208 poster_frame: None,
209 }
210 }
211
212 fn setup(
213 &mut self,
214 player_id: usize,
215 task_source: SendableTaskSource,
216 weak_video_renderer: Weak<Mutex<MediaFrameRenderer>>,
217 ) {
218 self.player_id = Some(player_id);
219
220 let (glplayer_id, image_receiver) = self
221 .player_context
222 .glplayer_thread_sender
223 .as_ref()
224 .map(|sender| {
225 let (image_sender, image_receiver) = ipc::channel::<GLPlayerMsgForward>().unwrap();
226 sender
227 .send(GLPlayerMsg::RegisterPlayer(image_sender))
228 .unwrap();
229 match image_receiver.recv().unwrap() {
230 GLPlayerMsgForward::PlayerId(id) => (Some(id), Some(image_receiver)),
231 _ => unreachable!(),
232 }
233 })
234 .unwrap_or((None, None));
235
236 self.glplayer_id = glplayer_id;
237
238 let Some(image_receiver) = image_receiver else {
239 return;
240 };
241
242 ROUTER.add_typed_route(
243 image_receiver,
244 Box::new(move |message| {
245 let message = message.unwrap();
246 let weak_video_renderer = weak_video_renderer.clone();
247
248 task_source.queue(task!(handle_glplayer_message: move || {
249 trace!("GLPlayer message {:?}", message);
250
251 let Some(video_renderer) = weak_video_renderer.upgrade() else {
252 return;
253 };
254
255 match message {
256 GLPlayerMsgForward::Lock(sender) => {
257 if let Some(holder) = video_renderer
258 .lock()
259 .unwrap()
260 .current_frame_holder
261 .as_mut() {
262 holder.lock();
263 sender.send(holder.get()).unwrap();
264 };
265 },
266 GLPlayerMsgForward::Unlock() => {
267 if let Some(holder) = video_renderer
268 .lock()
269 .unwrap()
270 .current_frame_holder
271 .as_mut() { holder.unlock() }
272 },
273 _ => (),
274 }
275 }));
276 }),
277 );
278 }
279
280 fn reset(&mut self) {
281 self.player_id = None;
282
283 if let Some(glplayer_id) = self.glplayer_id.take() {
284 self.player_context
285 .send(GLPlayerMsg::UnregisterPlayer(glplayer_id));
286 }
287
288 self.current_frame_holder = None;
289
290 let mut updates = smallvec::smallvec![];
291
292 if let Some(current_frame) = self.current_frame.take() {
293 updates.push(ImageUpdate::DeleteImage(current_frame.image_key));
294 }
295
296 if let Some(old_image_key) = self.old_frame.take() {
297 updates.push(ImageUpdate::DeleteImage(old_image_key));
298 }
299
300 if let Some(very_old_image_key) = self.very_old_frame.take() {
301 updates.push(ImageUpdate::DeleteImage(very_old_image_key));
302 }
303
304 if !updates.is_empty() {
305 self.paint_api
306 .update_images(self.webview_id.into(), updates);
307 }
308 }
309
310 fn set_poster_frame(&mut self, image: Option<Arc<RasterImage>>) {
311 self.poster_frame = image.and_then(|image| {
312 image.id.map(|image_key| MediaFrame {
313 image_key,
314 width: image.metadata.width as i32,
315 height: image.metadata.height as i32,
316 })
317 });
318 }
319}
320
321impl Drop for MediaFrameRenderer {
322 fn drop(&mut self) {
323 self.reset();
324 }
325}
326
327impl VideoFrameRenderer for MediaFrameRenderer {
328 fn render(&mut self, frame: VideoFrame) {
329 if self.player_id.is_none() || (frame.is_gl_texture() && self.glplayer_id.is_none()) {
330 return;
331 }
332
333 let mut updates = smallvec::smallvec![];
334
335 if let Some(old_image_key) = mem::replace(&mut self.very_old_frame, self.old_frame.take()) {
336 updates.push(ImageUpdate::DeleteImage(old_image_key));
337 }
338
339 let descriptor = ImageDescriptor::new(
340 frame.get_width(),
341 frame.get_height(),
342 ImageFormat::BGRA8,
343 ImageDescriptorFlags::empty(),
344 );
345
346 match &mut self.current_frame {
347 Some(current_frame)
348 if current_frame.width == frame.get_width() &&
349 current_frame.height == frame.get_height() =>
350 {
351 if !frame.is_gl_texture() {
352 updates.push(ImageUpdate::UpdateImage(
353 current_frame.image_key,
354 descriptor,
355 SerializableImageData::Raw(GenericSharedMemory::from_bytes(
356 &frame.get_data(),
357 )),
358 None,
359 ));
360 }
361
362 self.current_frame_holder
363 .get_or_insert_with(|| FrameHolder::new(frame.clone()))
364 .set(frame);
365
366 if let Some(old_image_key) = self.old_frame.take() {
367 updates.push(ImageUpdate::DeleteImage(old_image_key));
368 }
369 },
370 Some(current_frame) => {
371 self.old_frame = Some(current_frame.image_key);
372
373 let Some(new_image_key) =
374 self.paint_api.generate_image_key_blocking(self.webview_id)
375 else {
376 return;
377 };
378
379 current_frame.image_key = new_image_key;
381 current_frame.width = frame.get_width();
382 current_frame.height = frame.get_height();
383
384 let image_data = if frame.is_gl_texture() && self.glplayer_id.is_some() {
385 let texture_target = if frame.is_external_oes() {
386 ImageBufferKind::TextureExternal
387 } else {
388 ImageBufferKind::Texture2D
389 };
390
391 SerializableImageData::External(ExternalImageData {
392 id: ExternalImageId(self.glplayer_id.unwrap()),
393 channel_index: 0,
394 image_type: ExternalImageType::TextureHandle(texture_target),
395 normalized_uvs: false,
396 })
397 } else {
398 SerializableImageData::Raw(GenericSharedMemory::from_bytes(&frame.get_data()))
399 };
400
401 self.current_frame_holder
402 .get_or_insert_with(|| FrameHolder::new(frame.clone()))
403 .set(frame);
404
405 updates.push(ImageUpdate::AddImage(
406 new_image_key,
407 descriptor,
408 image_data,
409 false,
410 ));
411 },
412 None => {
413 let Some(image_key) = self.paint_api.generate_image_key_blocking(self.webview_id)
414 else {
415 return;
416 };
417
418 self.current_frame = Some(MediaFrame {
419 image_key,
420 width: frame.get_width(),
421 height: frame.get_height(),
422 });
423
424 let image_data = if frame.is_gl_texture() && self.glplayer_id.is_some() {
425 let texture_target = if frame.is_external_oes() {
426 ImageBufferKind::TextureExternal
427 } else {
428 ImageBufferKind::Texture2D
429 };
430
431 SerializableImageData::External(ExternalImageData {
432 id: ExternalImageId(self.glplayer_id.unwrap()),
433 channel_index: 0,
434 image_type: ExternalImageType::TextureHandle(texture_target),
435 normalized_uvs: false,
436 })
437 } else {
438 SerializableImageData::Raw(GenericSharedMemory::from_bytes(&frame.get_data()))
439 };
440
441 self.current_frame_holder = Some(FrameHolder::new(frame));
442
443 updates.push(ImageUpdate::AddImage(
444 image_key, descriptor, image_data, false,
445 ));
446 },
447 }
448 self.paint_api
449 .update_images(self.webview_id.into(), updates);
450 }
451}
452
453#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
454#[derive(JSTraceable, MallocSizeOf)]
455enum SrcObject {
456 MediaStream(Dom<MediaStream>),
457 Blob(Dom<Blob>),
458}
459
460impl From<MediaStreamOrBlob> for SrcObject {
461 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
462 fn from(src_object: MediaStreamOrBlob) -> SrcObject {
463 match src_object {
464 MediaStreamOrBlob::Blob(blob) => SrcObject::Blob(Dom::from_ref(&*blob)),
465 MediaStreamOrBlob::MediaStream(stream) => {
466 SrcObject::MediaStream(Dom::from_ref(&*stream))
467 },
468 }
469 }
470}
471
472#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
473enum LoadState {
474 NotLoaded,
475 LoadingFromSrcObject,
476 LoadingFromSrcAttribute,
477 LoadingFromSourceChild,
478 WaitingForSource,
479}
480
481#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
483#[derive(JSTraceable, MallocSizeOf)]
484struct SourceChildrenPointer {
485 source_before_pointer: Dom<HTMLSourceElement>,
486 inclusive: bool,
487}
488
489impl SourceChildrenPointer {
490 fn new(source_before_pointer: DomRoot<HTMLSourceElement>, inclusive: bool) -> Self {
491 Self {
492 source_before_pointer: source_before_pointer.as_traced(),
493 inclusive,
494 }
495 }
496}
497
498#[derive(Clone, Copy, Debug, PartialEq)]
502enum LoopCondition {
503 Included,
504 Ignored,
505}
506
507#[dom_struct]
508pub(crate) struct HTMLMediaElement {
509 htmlelement: HTMLElement,
510 network_state: Cell<NetworkState>,
512 ready_state: Cell<ReadyState>,
514 src_object: DomRefCell<Option<SrcObject>>,
516 current_src: DomRefCell<String>,
518 generation_id: Cell<u32>,
520 fired_loadeddata_event: Cell<bool>,
524 error: MutNullableDom<MediaError>,
526 paused: Cell<bool>,
528 default_playback_rate: Cell<f64>,
530 playback_rate: Cell<f64>,
532 autoplaying: Cell<bool>,
534 delaying_the_load_event_flag: DomRefCell<Option<LoadBlocker>>,
536 #[conditional_malloc_size_of]
538 pending_play_promises: DomRefCell<Vec<Rc<Promise>>>,
539 #[expect(clippy::type_complexity)]
541 #[conditional_malloc_size_of]
542 in_flight_play_promises_queue: DomRefCell<VecDeque<(Box<[Rc<Promise>]>, ErrorResult)>>,
543 #[ignore_malloc_size_of = "servo_media"]
544 #[no_trace]
545 player: DomRefCell<Option<Arc<Mutex<dyn Player>>>>,
546 #[conditional_malloc_size_of]
547 #[no_trace]
548 video_renderer: Arc<Mutex<MediaFrameRenderer>>,
549 #[ignore_malloc_size_of = "servo_media"]
550 #[no_trace]
551 audio_renderer: DomRefCell<Option<Arc<Mutex<dyn AudioRenderer>>>>,
552 #[conditional_malloc_size_of]
553 #[no_trace]
554 event_handler: RefCell<Option<Arc<Mutex<HTMLMediaElementEventHandler>>>>,
555 show_poster: Cell<bool>,
557 duration: Cell<f64>,
559 current_playback_position: Cell<f64>,
561 official_playback_position: Cell<f64>,
563 default_playback_start_position: Cell<f64>,
565 volume: Cell<f64>,
567 seeking: Cell<bool>,
569 current_seek_position: Cell<f64>,
573 muted: Cell<bool>,
575 load_state: Cell<LoadState>,
577 source_children_pointer: DomRefCell<Option<SourceChildrenPointer>>,
578 current_source_child: MutNullableDom<HTMLSourceElement>,
579 #[no_trace]
581 resource_url: DomRefCell<Option<ServoUrl>>,
582 #[no_trace]
585 blob_url: DomRefCell<Option<ServoUrl>>,
586 played: DomRefCell<TimeRangesContainer>,
588 audio_tracks_list: MutNullableDom<AudioTrackList>,
590 video_tracks_list: MutNullableDom<VideoTrackList>,
592 text_tracks_list: MutNullableDom<TextTrackList>,
594 #[ignore_malloc_size_of = "Defined in std::time"]
596 next_timeupdate_event: Cell<Instant>,
597 current_fetch_context: RefCell<Option<HTMLMediaElementFetchContext>>,
599 media_controls_id: DomRefCell<Option<String>>,
604}
605
606#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
608#[repr(u8)]
609pub(crate) enum NetworkState {
610 Empty = HTMLMediaElementConstants::NETWORK_EMPTY as u8,
611 Idle = HTMLMediaElementConstants::NETWORK_IDLE as u8,
612 Loading = HTMLMediaElementConstants::NETWORK_LOADING as u8,
613 NoSource = HTMLMediaElementConstants::NETWORK_NO_SOURCE as u8,
614}
615
616#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq, PartialOrd)]
618#[repr(u8)]
619#[expect(clippy::enum_variant_names)] pub(crate) enum ReadyState {
621 HaveNothing = HTMLMediaElementConstants::HAVE_NOTHING as u8,
622 HaveMetadata = HTMLMediaElementConstants::HAVE_METADATA as u8,
623 HaveCurrentData = HTMLMediaElementConstants::HAVE_CURRENT_DATA as u8,
624 HaveFutureData = HTMLMediaElementConstants::HAVE_FUTURE_DATA as u8,
625 HaveEnoughData = HTMLMediaElementConstants::HAVE_ENOUGH_DATA as u8,
626}
627
628#[derive(Clone, Copy, PartialEq)]
630enum PlaybackDirection {
631 Forwards,
632 Backwards,
633}
634
635impl HTMLMediaElement {
636 pub(crate) fn new_inherited(
637 tag_name: LocalName,
638 prefix: Option<Prefix>,
639 document: &Document,
640 ) -> Self {
641 Self {
642 htmlelement: HTMLElement::new_inherited(tag_name, prefix, document),
643 network_state: Cell::new(NetworkState::Empty),
644 ready_state: Cell::new(ReadyState::HaveNothing),
645 src_object: Default::default(),
646 current_src: DomRefCell::new("".to_owned()),
647 generation_id: Cell::new(0),
648 fired_loadeddata_event: Cell::new(false),
649 error: Default::default(),
650 paused: Cell::new(true),
651 default_playback_rate: Cell::new(1.0),
652 playback_rate: Cell::new(1.0),
653 muted: Cell::new(false),
654 load_state: Cell::new(LoadState::NotLoaded),
655 source_children_pointer: DomRefCell::new(None),
656 current_source_child: Default::default(),
657 autoplaying: Cell::new(true),
659 delaying_the_load_event_flag: Default::default(),
660 pending_play_promises: Default::default(),
661 in_flight_play_promises_queue: Default::default(),
662 player: Default::default(),
663 video_renderer: Arc::new(Mutex::new(MediaFrameRenderer::new(
664 document.webview_id(),
665 document.window().paint_api().clone(),
666 document.window().get_player_context(),
667 ))),
668 audio_renderer: Default::default(),
669 event_handler: Default::default(),
670 show_poster: Cell::new(true),
671 duration: Cell::new(f64::NAN),
672 current_playback_position: Cell::new(0.),
673 official_playback_position: Cell::new(0.),
674 default_playback_start_position: Cell::new(0.),
675 volume: Cell::new(1.0),
676 seeking: Cell::new(false),
677 current_seek_position: Cell::new(f64::NAN),
678 resource_url: DomRefCell::new(None),
679 blob_url: DomRefCell::new(None),
680 played: DomRefCell::new(TimeRangesContainer::default()),
681 audio_tracks_list: Default::default(),
682 video_tracks_list: Default::default(),
683 text_tracks_list: Default::default(),
684 next_timeupdate_event: Cell::new(Instant::now() + Duration::from_millis(250)),
685 current_fetch_context: RefCell::new(None),
686 media_controls_id: DomRefCell::new(None),
687 }
688 }
689
690 pub(crate) fn network_state(&self) -> NetworkState {
691 self.network_state.get()
692 }
693
694 pub(crate) fn get_ready_state(&self) -> ReadyState {
695 self.ready_state.get()
696 }
697
698 fn media_type_id(&self) -> HTMLMediaElementTypeId {
699 match self.upcast::<Node>().type_id() {
700 NodeTypeId::Element(ElementTypeId::HTMLElement(
701 HTMLElementTypeId::HTMLMediaElement(media_type_id),
702 )) => media_type_id,
703 _ => unreachable!(),
704 }
705 }
706
707 fn update_media_state(&self) {
708 let is_playing = self
709 .player
710 .borrow()
711 .as_ref()
712 .is_some_and(|player| !player.lock().unwrap().paused());
713
714 if self.is_potentially_playing() && !is_playing {
715 if let Some(ref player) = *self.player.borrow() {
716 let player = player.lock().unwrap();
717
718 if let Err(error) = player.set_playback_rate(self.playback_rate.get()) {
719 warn!("Could not set the playback rate: {error:?}");
720 }
721 if let Err(error) = player.set_volume(self.volume.get()) {
722 warn!("Could not set the volume: {error:?}");
723 }
724 if let Err(error) = player.play() {
725 error!("Could not play media: {error:?}");
726 }
727 }
728 } else if is_playing {
729 if let Some(ref player) = *self.player.borrow() {
730 if let Err(error) = player.lock().unwrap().pause() {
731 error!("Could not pause player: {error:?}");
732 }
733 }
734 }
735 }
736
737 pub(crate) fn delay_load_event(&self, delay: bool, cx: &mut js::context::JSContext) {
744 let blocker = &self.delaying_the_load_event_flag;
745 if delay && blocker.borrow().is_none() {
746 *blocker.borrow_mut() = Some(LoadBlocker::new(&self.owner_document(), LoadType::Media));
747 } else if !delay && blocker.borrow().is_some() {
748 LoadBlocker::terminate(blocker, cx);
749 }
750 }
751
752 fn time_marches_on(&self) {
754 if Instant::now() > self.next_timeupdate_event.get() {
760 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
761 self.next_timeupdate_event
762 .set(Instant::now() + Duration::from_millis(250));
763 }
764 }
765
766 fn internal_play_steps(&self, cx: &mut js::context::JSContext) {
768 if self.network_state.get() == NetworkState::Empty {
771 self.invoke_resource_selection_algorithm(cx);
772 }
773
774 if self.ended_playback(LoopCondition::Ignored) &&
780 self.direction_of_playback() == PlaybackDirection::Forwards
781 {
782 self.seek(
783 self.earliest_possible_position(),
784 false,
785 );
786 }
787
788 let state = self.ready_state.get();
789
790 if self.Paused() {
792 self.paused.set(false);
794
795 if self.show_poster.get() {
798 self.show_poster.set(false);
799 self.time_marches_on();
800 }
801
802 self.queue_media_element_task_to_fire_event(atom!("play"));
805
806 match state {
812 ReadyState::HaveNothing |
813 ReadyState::HaveMetadata |
814 ReadyState::HaveCurrentData => {
815 self.queue_media_element_task_to_fire_event(atom!("waiting"));
816 },
817 ReadyState::HaveFutureData | ReadyState::HaveEnoughData => {
818 self.notify_about_playing();
819 },
820 }
821 }
822 else if state == ReadyState::HaveFutureData || state == ReadyState::HaveEnoughData {
827 self.take_pending_play_promises(Ok(()));
828
829 let this = Trusted::new(self);
830 let generation_id = self.generation_id.get();
831
832 self.owner_global()
833 .task_manager()
834 .media_element_task_source()
835 .queue(task!(resolve_pending_play_promises: move || {
836 let this = this.root();
837 if generation_id != this.generation_id.get() {
838 return;
839 }
840
841 this.fulfill_in_flight_play_promises(|| {});
842 }));
843 }
844
845 self.autoplaying.set(false);
847
848 self.update_media_state();
849 }
850
851 fn internal_pause_steps(&self) {
853 self.autoplaying.set(false);
855
856 if !self.Paused() {
858 self.paused.set(true);
860
861 self.take_pending_play_promises(Err(Error::Abort(None)));
863
864 let this = Trusted::new(self);
866 let generation_id = self.generation_id.get();
867
868 self.owner_global()
869 .task_manager()
870 .media_element_task_source()
871 .queue(task!(internal_pause_steps: move || {
872 let this = this.root();
873 if generation_id != this.generation_id.get() {
874 return;
875 }
876
877 this.fulfill_in_flight_play_promises(|| {
878 this.upcast::<EventTarget>().fire_event(atom!("timeupdate"), CanGc::note());
880
881 this.upcast::<EventTarget>().fire_event(atom!("pause"), CanGc::note());
883
884 });
888 }));
889
890 self.official_playback_position
892 .set(self.current_playback_position.get());
893 }
894
895 self.update_media_state();
896 }
897
898 fn is_allowed_to_play(&self) -> bool {
900 true
901 }
902
903 fn notify_about_playing(&self) {
905 self.take_pending_play_promises(Ok(()));
907
908 let this = Trusted::new(self);
910 let generation_id = self.generation_id.get();
911
912 self.owner_global()
913 .task_manager()
914 .media_element_task_source()
915 .queue(task!(notify_about_playing: move || {
916 let this = this.root();
917 if generation_id != this.generation_id.get() {
918 return;
919 }
920
921 this.fulfill_in_flight_play_promises(|| {
922 this.upcast::<EventTarget>().fire_event(atom!("playing"), CanGc::note());
924
925 });
928 }));
929 }
930
931 fn change_ready_state(&self, ready_state: ReadyState) {
933 let old_ready_state = self.ready_state.get();
934 self.ready_state.set(ready_state);
935
936 if self.network_state.get() == NetworkState::Empty {
937 return;
938 }
939
940 if old_ready_state == ready_state {
941 return;
942 }
943
944 match (old_ready_state, ready_state) {
946 (ReadyState::HaveNothing, ReadyState::HaveMetadata) => {
949 self.queue_media_element_task_to_fire_event(atom!("loadedmetadata"));
952 return;
954 },
955 (ReadyState::HaveMetadata, new) if new >= ReadyState::HaveCurrentData => {
958 if !self.fired_loadeddata_event.get() {
962 self.fired_loadeddata_event.set(true);
963
964 let this = Trusted::new(self);
965 let generation_id = self.generation_id.get();
966
967 self.owner_global()
968 .task_manager()
969 .media_element_task_source()
970 .queue(task!(media_reached_current_data: move |cx| {
971 let this = this.root();
972 if generation_id != this.generation_id.get() {
973 return;
974 }
975
976 this.upcast::<EventTarget>().fire_event(atom!("loadeddata"), CanGc::from_cx(cx));
977 this.delay_load_event(false, cx);
981 }));
982 }
983
984 },
988 (ReadyState::HaveFutureData, new) if new <= ReadyState::HaveCurrentData => {
989 return;
994 },
995
996 _ => (),
997 }
998
999 if old_ready_state <= ReadyState::HaveCurrentData &&
1002 ready_state >= ReadyState::HaveFutureData
1003 {
1004 self.queue_media_element_task_to_fire_event(atom!("canplay"));
1007
1008 if !self.Paused() {
1011 self.notify_about_playing();
1012 }
1013 }
1014
1015 if ready_state == ReadyState::HaveEnoughData {
1017 self.queue_media_element_task_to_fire_event(atom!("canplaythrough"));
1020
1021 if self.eligible_for_autoplay() {
1024 self.paused.set(false);
1026
1027 if self.show_poster.get() {
1030 self.show_poster.set(false);
1031 self.time_marches_on();
1032 }
1033
1034 self.queue_media_element_task_to_fire_event(atom!("play"));
1037
1038 self.notify_about_playing();
1040 }
1041 }
1042
1043 self.update_media_state();
1044 }
1045
1046 fn invoke_resource_selection_algorithm(&self, cx: &mut js::context::JSContext) {
1048 self.network_state.set(NetworkState::NoSource);
1050
1051 self.show_poster.set(true);
1053
1054 self.delay_load_event(true, cx);
1057
1058 let task = MediaElementMicrotask::ResourceSelection {
1065 elem: DomRoot::from_ref(self),
1066 generation_id: self.generation_id.get(),
1067 base_url: self.owner_document().base_url(),
1068 };
1069
1070 ScriptThread::await_stable_state(Microtask::MediaElement(task));
1075 }
1076
1077 fn resource_selection_algorithm_sync(
1079 &self,
1080 base_url: ServoUrl,
1081 cx: &mut js::context::JSContext,
1082 ) {
1083 enum Mode {
1090 Object,
1091 Attribute(String),
1092 Children(DomRoot<HTMLSourceElement>),
1093 }
1094
1095 let mode = if self.src_object.borrow().is_some() {
1097 Mode::Object
1099 } else if let Some(attribute) = self.upcast::<Element>().get_attribute(&local_name!("src"))
1100 {
1101 Mode::Attribute((**attribute.value()).to_owned())
1104 } else if let Some(source) = self
1105 .upcast::<Node>()
1106 .children()
1107 .find_map(DomRoot::downcast::<HTMLSourceElement>)
1108 {
1109 Mode::Children(source)
1113 } else {
1114 self.load_state.set(LoadState::NotLoaded);
1117
1118 self.network_state.set(NetworkState::Empty);
1120
1121 self.delay_load_event(false, cx);
1124
1125 return;
1127 };
1128
1129 self.network_state.set(NetworkState::Loading);
1131
1132 self.queue_media_element_task_to_fire_event(atom!("loadstart"));
1135
1136 match mode {
1138 Mode::Object => {
1139 self.load_from_src_object();
1141 },
1142 Mode::Attribute(src) => {
1143 self.load_from_src_attribute(base_url, &src);
1145 },
1146 Mode::Children(source) => {
1147 self.load_from_source_child(&source);
1149 },
1150 }
1151 }
1152
1153 fn load_from_src_object(&self) {
1155 self.load_state.set(LoadState::LoadingFromSrcObject);
1156
1157 "".clone_into(&mut self.current_src.borrow_mut());
1159
1160 self.resource_fetch_algorithm(Resource::Object);
1166 }
1167
1168 fn load_from_src_attribute(&self, base_url: ServoUrl, src: &str) {
1170 self.load_state.set(LoadState::LoadingFromSrcAttribute);
1171
1172 if src.is_empty() {
1175 self.queue_dedicated_media_source_failure_steps();
1176 return;
1177 }
1178
1179 let Ok(url_record) = base_url.join(src) else {
1183 self.queue_dedicated_media_source_failure_steps();
1184 return;
1185 };
1186
1187 *self.current_src.borrow_mut() = url_record.as_str().into();
1190
1191 self.resource_fetch_algorithm(Resource::Url(url_record));
1197 }
1198
1199 fn load_from_source_child(&self, source: &HTMLSourceElement) {
1201 self.load_state.set(LoadState::LoadingFromSourceChild);
1202
1203 *self.source_children_pointer.borrow_mut() =
1210 Some(SourceChildrenPointer::new(DomRoot::from_ref(source), false));
1211
1212 let element = source.upcast::<Element>();
1213
1214 let Some(src) = element
1218 .get_attribute(&local_name!("src"))
1219 .filter(|attribute| !attribute.value().is_empty())
1220 else {
1221 self.load_from_source_child_failure_steps(source);
1222 return;
1223 };
1224
1225 if let Some(media) = element.get_attribute(&local_name!("media")) {
1229 if !MediaList::matches_environment(&element.owner_document(), &media.value()) {
1230 self.load_from_source_child_failure_steps(source);
1231 return;
1232 }
1233 }
1234
1235 let Ok(url_record) = source.owner_document().base_url().join(&src.value()) else {
1239 self.load_from_source_child_failure_steps(source);
1242 return;
1243 };
1244
1245 if let Some(type_) = element.get_attribute(&local_name!("type")) {
1250 if ServoMedia::get().can_play_type(&type_.value()) == SupportsMediaType::No {
1251 self.load_from_source_child_failure_steps(source);
1252 return;
1253 }
1254 }
1255
1256 self.reset_media_player();
1258
1259 self.current_source_child.set(Some(source));
1260
1261 *self.current_src.borrow_mut() = url_record.as_str().into();
1264
1265 self.resource_fetch_algorithm(Resource::Url(url_record));
1270 }
1271
1272 fn load_from_source_child_failure_steps(&self, source: &HTMLSourceElement) {
1274 let trusted_this = Trusted::new(self);
1277 let trusted_source = Trusted::new(source);
1278 let generation_id = self.generation_id.get();
1279
1280 self.owner_global()
1281 .task_manager()
1282 .media_element_task_source()
1283 .queue(task!(queue_error_event: move |cx| {
1284 let this = trusted_this.root();
1285 if generation_id != this.generation_id.get() {
1286 return;
1287 }
1288
1289 let source = trusted_source.root();
1290 source.upcast::<EventTarget>().fire_event(atom!("error"), CanGc::from_cx(cx));
1291 }));
1292
1293 let task = MediaElementMicrotask::SelectNextSourceChild {
1295 elem: DomRoot::from_ref(self),
1296 generation_id: self.generation_id.get(),
1297 };
1298
1299 ScriptThread::await_stable_state(Microtask::MediaElement(task));
1300 }
1301
1302 fn select_next_source_child(&self, can_gc: CanGc) {
1304 self.AudioTracks(can_gc).clear();
1306 self.VideoTracks(can_gc).clear();
1307
1308 let mut source_candidate = None;
1310
1311 if let Some(ref source_children_pointer) = *self.source_children_pointer.borrow() {
1319 if source_children_pointer.inclusive {
1323 for next_sibling in source_children_pointer
1324 .source_before_pointer
1325 .upcast::<Node>()
1326 .inclusively_following_siblings()
1327 {
1328 if let Some(next_source) = DomRoot::downcast::<HTMLSourceElement>(next_sibling)
1329 {
1330 source_candidate = Some(next_source);
1331 break;
1332 }
1333 }
1334 } else {
1335 for next_sibling in source_children_pointer
1336 .source_before_pointer
1337 .upcast::<Node>()
1338 .following_siblings()
1339 {
1340 if let Some(next_source) = DomRoot::downcast::<HTMLSourceElement>(next_sibling)
1341 {
1342 source_candidate = Some(next_source);
1343 break;
1344 }
1345 }
1346 };
1347 }
1348
1349 if let Some(source_candidate) = source_candidate {
1352 self.load_from_source_child(&source_candidate);
1353 return;
1354 }
1355
1356 self.load_state.set(LoadState::WaitingForSource);
1357
1358 *self.source_children_pointer.borrow_mut() = None;
1359
1360 self.network_state.set(NetworkState::NoSource);
1363
1364 self.show_poster.set(true);
1366
1367 let this = Trusted::new(self);
1370 let generation_id = self.generation_id.get();
1371
1372 self.owner_global()
1373 .task_manager()
1374 .media_element_task_source()
1375 .queue(task!(queue_delay_load_event: move |cx| {
1376 let this = this.root();
1377 if generation_id != this.generation_id.get() {
1378 return;
1379 }
1380
1381 this.delay_load_event(false, cx);
1382 }));
1383
1384 }
1387
1388 fn resource_selection_algorithm_failure_steps(&self) {
1390 match self.load_state.get() {
1391 LoadState::LoadingFromSrcObject => {
1392 self.queue_dedicated_media_source_failure_steps();
1397 },
1398 LoadState::LoadingFromSrcAttribute => {
1399 self.queue_dedicated_media_source_failure_steps();
1404 },
1405 LoadState::LoadingFromSourceChild => {
1406 if let Some(source) = self.current_source_child.take() {
1409 self.load_from_source_child_failure_steps(&source);
1410 }
1411 },
1412 _ => {},
1413 }
1414 }
1415
1416 fn fetch_request(&self, offset: Option<u64>, seek_lock: Option<SeekLock>) {
1417 if self.resource_url.borrow().is_none() && self.blob_url.borrow().is_none() {
1418 error!("Missing request url");
1419 if let Some(seek_lock) = seek_lock {
1420 seek_lock.unlock(false);
1421 }
1422 self.resource_selection_algorithm_failure_steps();
1423 return;
1424 }
1425
1426 let document = self.owner_document();
1427 let destination = match self.media_type_id() {
1428 HTMLMediaElementTypeId::HTMLAudioElement => Destination::Audio,
1429 HTMLMediaElementTypeId::HTMLVideoElement => Destination::Video,
1430 };
1431 let mut headers = HeaderMap::new();
1432 headers.insert(
1434 header::RANGE,
1435 HeaderValue::from_str(&format!("bytes={}-", offset.unwrap_or(0))).unwrap(),
1436 );
1437 let url = match self.resource_url.borrow().as_ref() {
1438 Some(url) => url.clone(),
1439 None => self.blob_url.borrow().as_ref().unwrap().clone(),
1440 };
1441
1442 let cors_setting = cors_setting_for_element(self.upcast());
1443 let global = self.global();
1444 let request = create_a_potential_cors_request(
1445 Some(document.webview_id()),
1446 url.clone(),
1447 destination,
1448 cors_setting,
1449 None,
1450 global.get_referrer(),
1451 )
1452 .with_global_scope(&global)
1453 .headers(headers)
1454 .referrer_policy(document.get_referrer_policy());
1455
1456 let mut current_fetch_context = self.current_fetch_context.borrow_mut();
1457 if let Some(ref mut current_fetch_context) = *current_fetch_context {
1458 current_fetch_context.cancel(CancelReason::Abort);
1459 }
1460
1461 *current_fetch_context = Some(HTMLMediaElementFetchContext::new(
1462 request.id,
1463 global.core_resource_thread(),
1464 ));
1465 let listener =
1466 HTMLMediaElementFetchListener::new(self, request.id, url, offset.unwrap_or(0));
1467
1468 self.owner_document().fetch_background(request, listener);
1469
1470 if let Some(seek_lock) = seek_lock {
1475 seek_lock.unlock(true);
1476 }
1477 }
1478
1479 fn eligible_for_autoplay(&self) -> bool {
1481 self.autoplaying.get() &&
1483
1484 self.Paused() &&
1486
1487 self.Autoplay() &&
1489
1490 {
1493 let document = self.owner_document();
1494
1495 !document.has_active_sandboxing_flag(
1496 SandboxingFlagSet::SANDBOXED_AUTOMATIC_FEATURES_BROWSING_CONTEXT_FLAG,
1497 )
1498 }
1499
1500 }
1503
1504 fn resource_fetch_algorithm(&self, resource: Resource) {
1506 if let Err(e) = self.create_media_player(&resource) {
1507 error!("Create media player error {:?}", e);
1508 self.resource_selection_algorithm_failure_steps();
1509 return;
1510 }
1511
1512 match resource {
1521 Resource::Url(url) => {
1522 if self.Preload() == "none" && !self.autoplaying.get() {
1527 self.network_state.set(NetworkState::Idle);
1529
1530 self.queue_media_element_task_to_fire_event(atom!("suspend"));
1533
1534 let this = Trusted::new(self);
1538 let generation_id = self.generation_id.get();
1539
1540 self.owner_global()
1541 .task_manager()
1542 .media_element_task_source()
1543 .queue(task!(queue_delay_load_event: move |cx| {
1544 let this = this.root();
1545 if generation_id != this.generation_id.get() {
1546 return;
1547 }
1548
1549 this.delay_load_event(false, cx);
1550 }));
1551
1552 return;
1561 }
1562
1563 *self.resource_url.borrow_mut() = Some(url);
1564
1565 self.fetch_request(None, None);
1567 },
1568 Resource::Object => {
1569 if let Some(ref src_object) = *self.src_object.borrow() {
1570 match src_object {
1571 SrcObject::Blob(blob) => {
1572 let blob_url = URL::CreateObjectURL(&self.global(), blob);
1573 *self.blob_url.borrow_mut() =
1574 Some(ServoUrl::parse(&blob_url.str()).expect("infallible"));
1575 self.fetch_request(None, None);
1576 },
1577 SrcObject::MediaStream(stream) => {
1578 let tracks = &*stream.get_tracks();
1579 for (pos, track) in tracks.iter().enumerate() {
1580 if self
1581 .player
1582 .borrow()
1583 .as_ref()
1584 .unwrap()
1585 .lock()
1586 .unwrap()
1587 .set_stream(&track.id(), pos == tracks.len() - 1)
1588 .is_err()
1589 {
1590 self.resource_selection_algorithm_failure_steps();
1591 }
1592 }
1593 },
1594 }
1595 }
1596 },
1597 }
1598 }
1599
1600 fn queue_dedicated_media_source_failure_steps(&self) {
1604 let this = Trusted::new(self);
1605 let generation_id = self.generation_id.get();
1606 self.take_pending_play_promises(Err(Error::NotSupported(None)));
1607 self.owner_global()
1608 .task_manager()
1609 .media_element_task_source()
1610 .queue(task!(dedicated_media_source_failure_steps: move |cx| {
1611 let this = this.root();
1612 if generation_id != this.generation_id.get() {
1613 return;
1614 }
1615
1616 this.fulfill_in_flight_play_promises(|| {
1617 this.error.set(Some(&*MediaError::new(
1620 &this.owner_window(),
1621 MEDIA_ERR_SRC_NOT_SUPPORTED, CanGc::from_cx(cx))));
1622
1623 this.AudioTracks(CanGc::from_cx(cx)).clear();
1625 this.VideoTracks(CanGc::from_cx(cx)).clear();
1626
1627 this.network_state.set(NetworkState::NoSource);
1630
1631 this.show_poster.set(true);
1633
1634 this.upcast::<EventTarget>().fire_event(atom!("error"), CanGc::from_cx(cx));
1636
1637 if let Some(ref player) = *this.player.borrow() {
1638 if let Err(error) = player.lock().unwrap().stop() {
1639 error!("Could not stop player: {error:?}");
1640 }
1641 }
1642
1643 });
1647
1648 this.delay_load_event(false, cx);
1651 }));
1652 }
1653
1654 fn in_error_state(&self) -> bool {
1655 self.error.get().is_some()
1656 }
1657
1658 fn is_potentially_playing(&self) -> bool {
1660 !self.paused.get() &&
1661 !self.ended_playback(LoopCondition::Included) &&
1662 self.error.get().is_none() &&
1663 !self.is_blocked_media_element()
1664 }
1665
1666 fn is_blocked_media_element(&self) -> bool {
1668 self.ready_state.get() <= ReadyState::HaveCurrentData ||
1669 self.is_paused_for_user_interaction() ||
1670 self.is_paused_for_in_band_content()
1671 }
1672
1673 fn is_paused_for_user_interaction(&self) -> bool {
1675 false
1678 }
1679
1680 fn is_paused_for_in_band_content(&self) -> bool {
1682 false
1685 }
1686
1687 fn media_element_load_algorithm(&self, cx: &mut js::context::JSContext) {
1689 self.fired_loadeddata_event.set(false);
1692
1693 self.generation_id.set(self.generation_id.get() + 1);
1698
1699 self.load_state.set(LoadState::NotLoaded);
1700 *self.source_children_pointer.borrow_mut() = None;
1701 self.current_source_child.set(None);
1702
1703 while !self.in_flight_play_promises_queue.borrow().is_empty() {
1710 self.fulfill_in_flight_play_promises(|| ());
1711 }
1712
1713 let network_state = self.network_state.get();
1718
1719 if network_state == NetworkState::Loading || network_state == NetworkState::Idle {
1723 self.queue_media_element_task_to_fire_event(atom!("abort"));
1724 }
1725
1726 self.reset_media_player();
1728
1729 if network_state != NetworkState::Empty {
1731 self.queue_media_element_task_to_fire_event(atom!("emptied"));
1734
1735 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1738 current_fetch_context.cancel(CancelReason::Abort);
1739 }
1740
1741 self.AudioTracks(CanGc::from_cx(cx)).clear();
1746 self.VideoTracks(CanGc::from_cx(cx)).clear();
1747
1748 if self.ready_state.get() != ReadyState::HaveNothing {
1750 self.change_ready_state(ReadyState::HaveNothing);
1751 }
1752
1753 if !self.Paused() {
1755 self.paused.set(true);
1757
1758 self.take_pending_play_promises(Err(Error::Abort(None)));
1761 self.fulfill_in_flight_play_promises(|| ());
1762 }
1763
1764 self.seeking.set(false);
1766
1767 self.current_seek_position.set(f64::NAN);
1768
1769 self.current_playback_position.set(0.);
1774 if self.official_playback_position.get() != 0. {
1775 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
1776 }
1777 self.official_playback_position.set(0.);
1778
1779 self.duration.set(f64::NAN);
1783 }
1784
1785 self.playback_rate.set(self.default_playback_rate.get());
1787
1788 self.error.set(None);
1790 self.autoplaying.set(true);
1791
1792 self.invoke_resource_selection_algorithm(cx);
1794
1795 }
1797
1798 fn queue_media_element_task_to_fire_event(&self, name: Atom) {
1801 let this = Trusted::new(self);
1802 let generation_id = self.generation_id.get();
1803
1804 self.owner_global()
1805 .task_manager()
1806 .media_element_task_source()
1807 .queue(task!(queue_event: move |cx| {
1808 let this = this.root();
1809 if generation_id != this.generation_id.get() {
1810 return;
1811 }
1812
1813 this.upcast::<EventTarget>().fire_event(name, CanGc::from_cx(cx));
1814 }));
1815 }
1816
1817 fn push_pending_play_promise(&self, promise: &Rc<Promise>) {
1819 self.pending_play_promises
1820 .borrow_mut()
1821 .push(promise.clone());
1822 }
1823
1824 fn take_pending_play_promises(&self, result: ErrorResult) {
1835 let pending_play_promises = std::mem::take(&mut *self.pending_play_promises.borrow_mut());
1836 self.in_flight_play_promises_queue
1837 .borrow_mut()
1838 .push_back((pending_play_promises.into(), result));
1839 }
1840
1841 fn fulfill_in_flight_play_promises<F>(&self, f: F)
1850 where
1851 F: FnOnce(),
1852 {
1853 let (promises, result) = self
1854 .in_flight_play_promises_queue
1855 .borrow_mut()
1856 .pop_front()
1857 .expect("there should be at least one list of in flight play promises");
1858 f();
1859 for promise in &*promises {
1860 match result {
1861 Ok(ref value) => promise.resolve_native(value, CanGc::note()),
1862 Err(ref error) => promise.reject_error(error.clone(), CanGc::note()),
1863 }
1864 }
1865 }
1866
1867 pub(crate) fn handle_source_child_insertion(
1868 &self,
1869 source: &HTMLSourceElement,
1870 cx: &mut js::context::JSContext,
1871 ) {
1872 if self.upcast::<Element>().has_attribute(&local_name!("src")) {
1876 return;
1877 }
1878
1879 if self.network_state.get() == NetworkState::Empty {
1880 self.invoke_resource_selection_algorithm(cx);
1881 return;
1882 }
1883
1884 if self.load_state.get() != LoadState::WaitingForSource {
1888 return;
1889 }
1890
1891 self.load_state.set(LoadState::LoadingFromSourceChild);
1892
1893 *self.source_children_pointer.borrow_mut() =
1894 Some(SourceChildrenPointer::new(DomRoot::from_ref(source), true));
1895
1896 let task = MediaElementMicrotask::SelectNextSourceChildAfterWait {
1898 elem: DomRoot::from_ref(self),
1899 generation_id: self.generation_id.get(),
1900 };
1901
1902 ScriptThread::await_stable_state(Microtask::MediaElement(task));
1903 }
1904
1905 fn select_next_source_child_after_wait(&self, cx: &mut js::context::JSContext) {
1907 self.delay_load_event(true, cx);
1910
1911 self.network_state.set(NetworkState::Loading);
1913
1914 self.select_next_source_child(CanGc::from_cx(cx));
1916 }
1917
1918 fn media_data_processing_failure_steps(&self) {
1923 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1925 current_fetch_context.cancel(CancelReason::Error);
1926 }
1927
1928 self.resource_selection_algorithm_failure_steps();
1930 }
1931
1932 fn media_data_processing_fatal_steps(&self, error: u16, cx: &mut js::context::JSContext) {
1936 *self.source_children_pointer.borrow_mut() = None;
1937 self.current_source_child.set(None);
1938
1939 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1941 current_fetch_context.cancel(CancelReason::Error);
1942 }
1943
1944 self.error.set(Some(&*MediaError::new(
1947 &self.owner_window(),
1948 error,
1949 CanGc::from_cx(cx),
1950 )));
1951
1952 self.network_state.set(NetworkState::Idle);
1954
1955 self.delay_load_event(false, cx);
1958
1959 self.upcast::<EventTarget>()
1961 .fire_event(atom!("error"), CanGc::from_cx(cx));
1962
1963 }
1965
1966 fn seek(&self, time: f64, _approximate_for_speed: bool) {
1968 self.show_poster.set(false);
1970
1971 if self.ready_state.get() == ReadyState::HaveNothing {
1973 return;
1974 }
1975
1976 self.current_seek_position.set(f64::NAN);
1980
1981 self.seeking.set(true);
1983
1984 let time = f64::min(time, self.Duration());
1990
1991 let time = f64::max(time, self.earliest_possible_position());
1994
1995 let seekable = self.seekable();
2001
2002 if seekable.is_empty() {
2003 self.seeking.set(false);
2004 return;
2005 }
2006
2007 let mut nearest_seekable_position = 0.0;
2008 let mut in_seekable_range = false;
2009 let mut nearest_seekable_distance = f64::MAX;
2010 for i in 0..seekable.len() {
2011 let start = seekable.start(i).unwrap().abs();
2012 let end = seekable.end(i).unwrap().abs();
2013 if time >= start && time <= end {
2014 nearest_seekable_position = time;
2015 in_seekable_range = true;
2016 break;
2017 } else if time < start {
2018 let distance = start - time;
2019 if distance < nearest_seekable_distance {
2020 nearest_seekable_distance = distance;
2021 nearest_seekable_position = start;
2022 }
2023 } else {
2024 let distance = time - end;
2025 if distance < nearest_seekable_distance {
2026 nearest_seekable_distance = distance;
2027 nearest_seekable_position = end;
2028 }
2029 }
2030 }
2031 let time = if in_seekable_range {
2032 time
2033 } else {
2034 nearest_seekable_position
2035 };
2036
2037 self.queue_media_element_task_to_fire_event(atom!("seeking"));
2048
2049 self.current_playback_position.set(time);
2051
2052 if let Some(ref player) = *self.player.borrow() {
2053 if let Err(error) = player.lock().unwrap().seek(time) {
2054 error!("Could not seek player: {error:?}");
2055 }
2056 }
2057
2058 self.current_seek_position.set(time);
2059
2060 }
2066
2067 fn seek_end(&self) {
2069 self.official_playback_position
2072 .set(self.current_playback_position.get());
2073
2074 self.seeking.set(false);
2076
2077 self.current_seek_position.set(f64::NAN);
2078
2079 self.time_marches_on();
2081
2082 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
2085
2086 self.queue_media_element_task_to_fire_event(atom!("seeked"));
2089 }
2090
2091 pub(crate) fn set_poster_frame(&self, image: Option<Arc<RasterImage>>) {
2093 if pref!(media_testing_enabled) && image.is_some() {
2094 self.queue_media_element_task_to_fire_event(atom!("postershown"));
2095 }
2096
2097 self.video_renderer.lock().unwrap().set_poster_frame(image);
2098
2099 self.upcast::<Node>().dirty(NodeDamage::Other);
2100 }
2101
2102 fn player_id(&self) -> Option<usize> {
2103 self.player
2104 .borrow()
2105 .as_ref()
2106 .map(|player| player.lock().unwrap().get_id())
2107 }
2108
2109 fn create_media_player(&self, resource: &Resource) -> Result<(), ()> {
2110 let stream_type = match *resource {
2111 Resource::Object => {
2112 if let Some(ref src_object) = *self.src_object.borrow() {
2113 match src_object {
2114 SrcObject::MediaStream(_) => StreamType::Stream,
2115 _ => StreamType::Seekable,
2116 }
2117 } else {
2118 return Err(());
2119 }
2120 },
2121 _ => StreamType::Seekable,
2122 };
2123
2124 let window = self.owner_window();
2125 let (action_sender, action_receiver) = ipc::channel::<PlayerEvent>().unwrap();
2126 let video_renderer: Option<Arc<Mutex<dyn VideoFrameRenderer>>> = match self.media_type_id()
2127 {
2128 HTMLMediaElementTypeId::HTMLAudioElement => None,
2129 HTMLMediaElementTypeId::HTMLVideoElement => Some(self.video_renderer.clone()),
2130 };
2131
2132 let audio_renderer = self.audio_renderer.borrow().as_ref().cloned();
2133
2134 let pipeline_id = window.pipeline_id();
2135 let client_context_id =
2136 ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
2137 let player = ServoMedia::get().create_player(
2138 &client_context_id,
2139 stream_type,
2140 action_sender,
2141 video_renderer,
2142 audio_renderer,
2143 Box::new(window.get_player_context()),
2144 );
2145 let player_id = {
2146 let player_guard = player.lock().unwrap();
2147
2148 if let Err(error) = player_guard.set_mute(self.muted.get()) {
2149 warn!("Could not set mute state: {error:?}");
2150 }
2151
2152 player_guard.get_id()
2153 };
2154
2155 *self.player.borrow_mut() = Some(player);
2156
2157 let event_handler = Arc::new(Mutex::new(HTMLMediaElementEventHandler::new(self)));
2158 let weak_event_handler = Arc::downgrade(&event_handler);
2159 *self.event_handler.borrow_mut() = Some(event_handler);
2160
2161 let task_source = self
2162 .owner_global()
2163 .task_manager()
2164 .media_element_task_source()
2165 .to_sendable();
2166 ROUTER.add_typed_route(
2167 action_receiver,
2168 Box::new(move |message| {
2169 let event = message.unwrap();
2170 let weak_event_handler = weak_event_handler.clone();
2171
2172 task_source.queue(task!(handle_player_event: move |cx| {
2173 trace!("HTMLMediaElement event: {event:?}");
2174
2175 let Some(event_handler) = weak_event_handler.upgrade() else {
2176 return;
2177 };
2178
2179 event_handler.lock().unwrap().handle_player_event(player_id, event, cx);
2180 }));
2181 }),
2182 );
2183
2184 let task_source = self
2185 .owner_global()
2186 .task_manager()
2187 .media_element_task_source()
2188 .to_sendable();
2189 let weak_video_renderer = Arc::downgrade(&self.video_renderer);
2190
2191 self.video_renderer
2192 .lock()
2193 .unwrap()
2194 .setup(player_id, task_source, weak_video_renderer);
2195
2196 Ok(())
2197 }
2198
2199 fn reset_media_player(&self) {
2200 if self.player.borrow().is_none() {
2201 return;
2202 }
2203
2204 if let Some(ref player) = *self.player.borrow() {
2205 if let Err(error) = player.lock().unwrap().stop() {
2206 error!("Could not stop player: {error:?}");
2207 }
2208 }
2209
2210 *self.player.borrow_mut() = None;
2211 self.video_renderer.lock().unwrap().reset();
2212 *self.event_handler.borrow_mut() = None;
2213
2214 if let Some(video_element) = self.downcast::<HTMLVideoElement>() {
2215 video_element.set_natural_dimensions(None, None);
2216 }
2217 }
2218
2219 pub(crate) fn set_audio_track(&self, idx: usize, enabled: bool) {
2220 if let Some(ref player) = *self.player.borrow() {
2221 if let Err(error) = player.lock().unwrap().set_audio_track(idx as i32, enabled) {
2222 warn!("Could not set audio track {error:?}");
2223 }
2224 }
2225 }
2226
2227 pub(crate) fn set_video_track(&self, idx: usize, enabled: bool) {
2228 if let Some(ref player) = *self.player.borrow() {
2229 if let Err(error) = player.lock().unwrap().set_video_track(idx as i32, enabled) {
2230 warn!("Could not set video track: {error:?}");
2231 }
2232 }
2233 }
2234
2235 fn direction_of_playback(&self) -> PlaybackDirection {
2237 if self.playback_rate.get() >= 0. {
2240 PlaybackDirection::Forwards
2241 } else {
2242 PlaybackDirection::Backwards
2243 }
2244 }
2245
2246 fn ended_playback(&self, loop_condition: LoopCondition) -> bool {
2248 if self.ready_state.get() < ReadyState::HaveMetadata {
2252 return false;
2253 }
2254
2255 let playback_position = self.current_playback_position.get();
2256
2257 match self.direction_of_playback() {
2258 PlaybackDirection::Forwards => {
2262 playback_position >= self.Duration() &&
2263 (loop_condition == LoopCondition::Ignored || !self.Loop())
2264 },
2265 PlaybackDirection::Backwards => playback_position <= self.earliest_possible_position(),
2268 }
2269 }
2270
2271 fn end_of_playback_in_forwards_direction(&self) {
2273 if self.Loop() {
2279 self.seek(
2280 self.earliest_possible_position(),
2281 false,
2282 );
2283 return;
2284 }
2285
2286 let this = Trusted::new(self);
2291 let generation_id = self.generation_id.get();
2292
2293 self.owner_global()
2294 .task_manager()
2295 .media_element_task_source()
2296 .queue(task!(reaches_the_end_steps: move || {
2297 let this = this.root();
2298 if generation_id != this.generation_id.get() {
2299 return;
2300 }
2301
2302 this.upcast::<EventTarget>().fire_event(atom!("timeupdate"), CanGc::note());
2304
2305 if this.ended_playback(LoopCondition::Included) &&
2308 this.direction_of_playback() == PlaybackDirection::Forwards &&
2309 !this.Paused() {
2310 this.paused.set(true);
2312
2313 this.upcast::<EventTarget>().fire_event(atom!("pause"), CanGc::note());
2315
2316 this.take_pending_play_promises(Err(Error::Abort(None)));
2319 this.fulfill_in_flight_play_promises(|| ());
2320 }
2321
2322 this.upcast::<EventTarget>().fire_event(atom!("ended"), CanGc::note());
2324 }));
2325
2326 self.change_ready_state(ReadyState::HaveCurrentData);
2328 }
2329
2330 fn end_of_playback_in_backwards_direction(&self) {
2332 if self.current_playback_position.get() <= self.earliest_possible_position() {
2337 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
2338 }
2339 }
2340
2341 fn playback_end(&self) {
2342 if self.seeking.get() {
2344 return;
2345 }
2346
2347 match self.direction_of_playback() {
2348 PlaybackDirection::Forwards => self.end_of_playback_in_forwards_direction(),
2349 PlaybackDirection::Backwards => self.end_of_playback_in_backwards_direction(),
2350 }
2351 }
2352
2353 fn playback_error(&self, error: &str, cx: &mut js::context::JSContext) {
2354 error!("Player error: {:?}", error);
2355
2356 if self.in_error_state() {
2360 return;
2361 }
2362
2363 if self.ready_state.get() == ReadyState::HaveNothing {
2365 self.media_data_processing_failure_steps();
2368 } else {
2369 self.media_data_processing_fatal_steps(MEDIA_ERR_DECODE, cx);
2371 }
2372 }
2373
2374 fn playback_metadata_updated(
2375 &self,
2376 metadata: &servo_media::player::metadata::Metadata,
2377 can_gc: CanGc,
2378 ) {
2379 if self.ready_state.get() != ReadyState::HaveNothing {
2382 return;
2383 }
2384
2385 for (i, _track) in metadata.audio_tracks.iter().enumerate() {
2388 let audio_track_list = self.AudioTracks(can_gc);
2389
2390 let kind = match i {
2392 0 => DOMString::from("main"),
2393 _ => DOMString::new(),
2394 };
2395
2396 let audio_track = AudioTrack::new(
2397 self.global().as_window(),
2398 DOMString::new(),
2399 kind,
2400 DOMString::new(),
2401 DOMString::new(),
2402 Some(&*audio_track_list),
2403 can_gc,
2404 );
2405
2406 audio_track_list.add(&audio_track);
2409
2410 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
2417 let fragment = MediaFragmentParser::from(servo_url);
2418 if let Some(id) = fragment.id() {
2419 if audio_track.id() == id {
2420 audio_track_list.set_enabled(audio_track_list.len() - 1, true);
2421 }
2422 }
2423
2424 if fragment.tracks().contains(&audio_track.kind().into()) {
2425 audio_track_list.set_enabled(audio_track_list.len() - 1, true);
2426 }
2427 }
2428
2429 if audio_track_list.enabled_index().is_none() {
2434 audio_track_list.set_enabled(audio_track_list.len() - 1, true);
2435 }
2436
2437 let event = TrackEvent::new(
2440 self.global().as_window(),
2441 atom!("addtrack"),
2442 false,
2443 false,
2444 &Some(VideoTrackOrAudioTrackOrTextTrack::AudioTrack(audio_track)),
2445 can_gc,
2446 );
2447
2448 event
2449 .upcast::<Event>()
2450 .fire(audio_track_list.upcast::<EventTarget>(), can_gc);
2451 }
2452
2453 for (i, _track) in metadata.video_tracks.iter().enumerate() {
2455 let video_track_list = self.VideoTracks(can_gc);
2456
2457 let kind = match i {
2459 0 => DOMString::from("main"),
2460 _ => DOMString::new(),
2461 };
2462
2463 let video_track = VideoTrack::new(
2464 self.global().as_window(),
2465 DOMString::new(),
2466 kind,
2467 DOMString::new(),
2468 DOMString::new(),
2469 Some(&*video_track_list),
2470 can_gc,
2471 );
2472
2473 video_track_list.add(&video_track);
2476
2477 if let Some(track) = video_track_list.item(0) {
2484 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
2485 let fragment = MediaFragmentParser::from(servo_url);
2486 if let Some(id) = fragment.id() {
2487 if track.id() == id {
2488 video_track_list.set_selected(0, true);
2489 }
2490 } else if fragment.tracks().contains(&track.kind().into()) {
2491 video_track_list.set_selected(0, true);
2492 }
2493 }
2494 }
2495
2496 if video_track_list.selected_index().is_none() {
2502 video_track_list.set_selected(video_track_list.len() - 1, true);
2503 }
2504
2505 let event = TrackEvent::new(
2508 self.global().as_window(),
2509 atom!("addtrack"),
2510 false,
2511 false,
2512 &Some(VideoTrackOrAudioTrackOrTextTrack::VideoTrack(video_track)),
2513 can_gc,
2514 );
2515
2516 event
2517 .upcast::<Event>()
2518 .fire(video_track_list.upcast::<EventTarget>(), can_gc);
2519 }
2520
2521 let earliest_possible_position = self.earliest_possible_position();
2534 self.current_playback_position
2535 .set(earliest_possible_position);
2536 self.official_playback_position
2537 .set(earliest_possible_position);
2538
2539 self.duration.set(
2545 metadata
2546 .duration
2547 .map_or(f64::INFINITY, |duration| duration.as_secs_f64()),
2548 );
2549 self.queue_media_element_task_to_fire_event(atom!("durationchange"));
2550
2551 if let Some(video_element) = self.downcast::<HTMLVideoElement>() {
2555 video_element.set_natural_dimensions(Some(metadata.width), Some(metadata.height));
2556 self.queue_media_element_task_to_fire_event(atom!("resize"));
2557 }
2558
2559 self.change_ready_state(ReadyState::HaveMetadata);
2561
2562 let mut jumped = false;
2564
2565 if self.default_playback_start_position.get() > 0. {
2568 self.seek(
2569 self.default_playback_start_position.get(),
2570 false,
2571 );
2572 jumped = true;
2573 }
2574
2575 self.default_playback_start_position.set(0.);
2577
2578 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
2583 let fragment = MediaFragmentParser::from(servo_url);
2584 if let Some(initial_playback_position) = fragment.start() {
2585 if initial_playback_position > 0. &&
2586 initial_playback_position < self.duration.get() &&
2587 !jumped
2588 {
2589 self.seek(
2590 initial_playback_position,
2591 false,
2592 )
2593 }
2594 }
2595 }
2596
2597 let global = self.global();
2604 let window = global.as_window();
2605
2606 window.Navigator().MediaSession().update_title(
2608 metadata
2609 .title
2610 .clone()
2611 .unwrap_or(window.get_url().into_string()),
2612 );
2613 }
2614
2615 fn playback_duration_changed(&self, duration: Option<Duration>) {
2616 let duration = duration.map_or(f64::INFINITY, |duration| duration.as_secs_f64());
2617
2618 if self.duration.get() == duration {
2619 return;
2620 }
2621
2622 self.duration.set(duration);
2623
2624 self.queue_media_element_task_to_fire_event(atom!("durationchange"));
2630
2631 if self.current_playback_position.get() > duration {
2635 self.seek(duration, false);
2636 }
2637 }
2638
2639 fn playback_video_frame_updated(&self) {
2640 let Some(video_element) = self.downcast::<HTMLVideoElement>() else {
2641 return;
2642 };
2643
2644 if self.ready_state.get() == ReadyState::HaveNothing {
2653 return;
2654 }
2655
2656 if let Some(frame) = self.video_renderer.lock().unwrap().current_frame {
2657 if video_element
2658 .set_natural_dimensions(Some(frame.width as u32), Some(frame.height as u32))
2659 {
2660 self.queue_media_element_task_to_fire_event(atom!("resize"));
2661 } else {
2662 self.upcast::<Node>().dirty(NodeDamage::Other);
2665 }
2666 }
2667 }
2668
2669 fn playback_need_data(&self) {
2670 if let Some(ref current_fetch_context) = *self.current_fetch_context.borrow() {
2674 if let Some(reason) = current_fetch_context.cancel_reason() {
2675 if *reason == CancelReason::Backoff {
2681 self.seek(
2682 self.current_playback_position.get(),
2683 false,
2684 );
2685 }
2686 return;
2687 }
2688 }
2689
2690 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
2691 if let Err(e) = {
2692 let mut data_source = current_fetch_context.data_source().borrow_mut();
2693 data_source.set_locked(false);
2694 data_source.process_into_player_from_queue(self.player.borrow().as_ref().unwrap())
2695 } {
2696 if e == PlayerError::EnoughData {
2701 current_fetch_context.cancel(CancelReason::Backoff);
2702 }
2703 }
2704 }
2705 }
2706
2707 fn playback_enough_data(&self) {
2708 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
2713 if current_fetch_context.is_seekable() {
2714 current_fetch_context.cancel(CancelReason::Backoff);
2715 }
2716 }
2717 }
2718
2719 fn playback_position_changed(&self, position: f64) {
2720 if self.seeking.get() {
2722 return;
2723 }
2724
2725 let _ = self
2726 .played
2727 .borrow_mut()
2728 .add(self.current_playback_position.get(), position);
2729 self.current_playback_position.set(position);
2730 self.official_playback_position.set(position);
2731 self.time_marches_on();
2732
2733 let media_position_state =
2734 MediaPositionState::new(self.duration.get(), self.playback_rate.get(), position);
2735 debug!(
2736 "Sending media session event set position state {:?}",
2737 media_position_state
2738 );
2739 self.send_media_session_event(MediaSessionEvent::SetPositionState(media_position_state));
2740 }
2741
2742 fn playback_seek_done(&self, position: f64) {
2743 let delta = (position - self.current_seek_position.get()).abs();
2746 if !self.seeking.get() || delta > SEEK_POSITION_THRESHOLD {
2747 return;
2748 }
2749
2750 let task = MediaElementMicrotask::Seeked {
2753 elem: DomRoot::from_ref(self),
2754 generation_id: self.generation_id.get(),
2755 };
2756
2757 ScriptThread::await_stable_state(Microtask::MediaElement(task));
2758 }
2759
2760 fn playback_state_changed(&self, state: &PlaybackState) {
2761 let mut media_session_playback_state = MediaSessionPlaybackState::None_;
2762 match *state {
2763 PlaybackState::Paused => {
2764 media_session_playback_state = MediaSessionPlaybackState::Paused;
2765 if self.ready_state.get() == ReadyState::HaveMetadata {
2766 self.change_ready_state(ReadyState::HaveEnoughData);
2767 }
2768 },
2769 PlaybackState::Playing => {
2770 media_session_playback_state = MediaSessionPlaybackState::Playing;
2771 if self.ready_state.get() == ReadyState::HaveMetadata {
2772 self.change_ready_state(ReadyState::HaveEnoughData);
2773 }
2774 },
2775 PlaybackState::Buffering => {
2776 return;
2780 },
2781 _ => {},
2782 };
2783 debug!(
2784 "Sending media session event playback state changed to {:?}",
2785 media_session_playback_state
2786 );
2787 self.send_media_session_event(MediaSessionEvent::PlaybackStateChange(
2788 media_session_playback_state,
2789 ));
2790 }
2791
2792 fn seekable(&self) -> TimeRangesContainer {
2793 let mut seekable = TimeRangesContainer::default();
2794 if let Some(ref player) = *self.player.borrow() {
2795 let ranges = player.lock().unwrap().seekable();
2796 for range in ranges {
2797 let _ = seekable.add(range.start, range.end);
2798 }
2799 }
2800 seekable
2801 }
2802
2803 fn earliest_possible_position(&self) -> f64 {
2805 self.seekable()
2806 .start(0)
2807 .unwrap_or_else(|_| self.current_playback_position.get())
2808 }
2809
2810 fn render_controls(&self, cx: &mut JSContext) {
2811 if self.upcast::<Element>().is_shadow_host() {
2812 return;
2814 }
2815
2816 let shadow_root = self.upcast::<Element>().attach_ua_shadow_root(cx, false);
2819 let document = self.owner_document();
2820 let script = Element::create(
2821 cx,
2822 QualName::new(None, ns!(html), local_name!("script")),
2823 None,
2824 &document,
2825 ElementCreator::ScriptCreated,
2826 CustomElementCreationMode::Asynchronous,
2827 None,
2828 );
2829 let id = Uuid::new_v4().to_string();
2835 document.register_media_controls(&id, &shadow_root);
2836 let media_controls_script = MEDIA_CONTROL_JS.replace("@@@id@@@", &id);
2837 *self.media_controls_id.borrow_mut() = Some(id);
2838 script
2839 .upcast::<Node>()
2840 .set_text_content_for_element(cx, Some(DOMString::from(media_controls_script)));
2841 if let Err(e) = shadow_root
2842 .upcast::<Node>()
2843 .AppendChild(cx, script.upcast::<Node>())
2844 {
2845 warn!("Could not render media controls {:?}", e);
2846 return;
2847 }
2848
2849 let style = Element::create(
2850 cx,
2851 QualName::new(None, ns!(html), local_name!("style")),
2852 None,
2853 &document,
2854 ElementCreator::ScriptCreated,
2855 CustomElementCreationMode::Asynchronous,
2856 None,
2857 );
2858
2859 style
2860 .upcast::<Node>()
2861 .set_text_content_for_element(cx, Some(DOMString::from(MEDIA_CONTROL_CSS)));
2862
2863 if let Err(e) = shadow_root
2864 .upcast::<Node>()
2865 .AppendChild(cx, style.upcast::<Node>())
2866 {
2867 warn!("Could not render media controls {:?}", e);
2868 }
2869
2870 self.upcast::<Node>().dirty(NodeDamage::Other);
2871 }
2872
2873 fn remove_controls(&self) {
2874 if let Some(id) = self.media_controls_id.borrow_mut().take() {
2875 self.owner_document().unregister_media_controls(&id);
2876 }
2877 }
2878
2879 pub(crate) fn get_current_frame(&self) -> Option<VideoFrame> {
2881 self.video_renderer
2882 .lock()
2883 .unwrap()
2884 .current_frame_holder
2885 .as_ref()
2886 .map(|holder| holder.get_frame())
2887 }
2888
2889 pub(crate) fn get_current_frame_to_present(&self) -> Option<MediaFrame> {
2892 let (current_frame, poster_frame) = {
2893 let renderer = self.video_renderer.lock().unwrap();
2894 (renderer.current_frame, renderer.poster_frame)
2895 };
2896
2897 if (self.show_poster.get() || current_frame.is_none()) && poster_frame.is_some() {
2900 return poster_frame;
2901 }
2902
2903 current_frame
2904 }
2905
2906 pub(crate) fn set_audio_renderer(
2911 &self,
2912 audio_renderer: Option<Arc<Mutex<dyn AudioRenderer>>>,
2913 cx: &mut js::context::JSContext,
2914 ) {
2915 *self.audio_renderer.borrow_mut() = audio_renderer;
2916
2917 let had_player = {
2918 if let Some(ref player) = *self.player.borrow() {
2919 if let Err(error) = player.lock().unwrap().stop() {
2920 error!("Could not stop player: {error:?}");
2921 }
2922 true
2923 } else {
2924 false
2925 }
2926 };
2927
2928 if had_player {
2929 self.media_element_load_algorithm(cx);
2930 }
2931 }
2932
2933 fn send_media_session_event(&self, event: MediaSessionEvent) {
2934 let global = self.global();
2935 let media_session = global.as_window().Navigator().MediaSession();
2936
2937 media_session.register_media_instance(self);
2938
2939 media_session.send_event(event);
2940 }
2941
2942 pub(crate) fn origin_is_clean(&self) -> bool {
2944 if self.src_object.borrow().is_some() {
2946 return true;
2949 }
2950
2951 if self.resource_url.borrow().is_some() {
2953 if let Some(ref current_fetch_context) = *self.current_fetch_context.borrow() {
2957 return current_fetch_context.origin_is_clean();
2958 }
2959 }
2960
2961 true
2962 }
2963}
2964
2965impl HTMLMediaElementMethods<crate::DomTypeHolder> for HTMLMediaElement {
2966 fn NetworkState(&self) -> u16 {
2968 self.network_state.get() as u16
2969 }
2970
2971 fn ReadyState(&self) -> u16 {
2973 self.ready_state.get() as u16
2974 }
2975
2976 make_bool_getter!(Autoplay, "autoplay");
2978 make_bool_setter!(SetAutoplay, "autoplay");
2980
2981 make_bool_getter!(Loop, "loop");
2983 make_bool_setter!(SetLoop, "loop");
2985
2986 make_bool_getter!(DefaultMuted, "muted");
2988 make_bool_setter!(SetDefaultMuted, "muted");
2990
2991 make_bool_getter!(Controls, "controls");
2993 make_bool_setter!(SetControls, "controls");
2995
2996 make_url_getter!(Src, "src");
2998
2999 make_url_setter!(SetSrc, "src");
3001
3002 fn GetCrossOrigin(&self) -> Option<DOMString> {
3004 reflect_cross_origin_attribute(self.upcast::<Element>())
3005 }
3006 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
3008 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
3009 }
3010
3011 fn Muted(&self) -> bool {
3013 self.muted.get()
3014 }
3015
3016 fn SetMuted(&self, value: bool) {
3018 if self.muted.get() == value {
3019 return;
3020 }
3021
3022 self.muted.set(value);
3023
3024 if let Some(ref player) = *self.player.borrow() {
3025 if let Err(error) = player.lock().unwrap().set_mute(value) {
3026 warn!("Could not set mute state: {error:?}");
3027 }
3028 }
3029
3030 self.queue_media_element_task_to_fire_event(atom!("volumechange"));
3033
3034 if !self.is_allowed_to_play() {
3037 self.internal_pause_steps();
3038 }
3039 }
3040
3041 fn GetSrcObject(&self) -> Option<MediaStreamOrBlob> {
3043 (*self.src_object.borrow())
3044 .as_ref()
3045 .map(|src_object| match src_object {
3046 SrcObject::Blob(blob) => MediaStreamOrBlob::Blob(DomRoot::from_ref(blob)),
3047 SrcObject::MediaStream(stream) => {
3048 MediaStreamOrBlob::MediaStream(DomRoot::from_ref(stream))
3049 },
3050 })
3051 }
3052
3053 fn SetSrcObject(&self, cx: &mut js::context::JSContext, value: Option<MediaStreamOrBlob>) {
3055 *self.src_object.borrow_mut() = value.map(|value| value.into());
3056 self.media_element_load_algorithm(cx);
3057 }
3058
3059 make_enumerated_getter!(
3062 Preload,
3063 "preload",
3064 "none" | "metadata" | "auto",
3065 missing => "auto",
3066 invalid => "auto"
3067 );
3068
3069 make_setter!(SetPreload, "preload");
3071
3072 fn CurrentSrc(&self) -> USVString {
3074 USVString(self.current_src.borrow().clone())
3075 }
3076
3077 fn Load(&self, cx: &mut js::context::JSContext) {
3079 self.media_element_load_algorithm(cx);
3080 }
3081
3082 fn CanPlayType(&self, type_: DOMString) -> CanPlayTypeResult {
3084 match ServoMedia::get().can_play_type(&type_.str()) {
3085 SupportsMediaType::No => CanPlayTypeResult::_empty,
3086 SupportsMediaType::Maybe => CanPlayTypeResult::Maybe,
3087 SupportsMediaType::Probably => CanPlayTypeResult::Probably,
3088 }
3089 }
3090
3091 fn GetError(&self) -> Option<DomRoot<MediaError>> {
3093 self.error.get()
3094 }
3095
3096 fn Play(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
3098 let promise = Promise::new_in_realm(cx);
3099
3100 if self
3107 .error
3108 .get()
3109 .is_some_and(|e| e.Code() == MEDIA_ERR_SRC_NOT_SUPPORTED)
3110 {
3111 promise.reject_error(Error::NotSupported(None), CanGc::from_cx(cx));
3112 return promise;
3113 }
3114
3115 self.push_pending_play_promise(&promise);
3118
3119 self.internal_play_steps(cx);
3121
3122 promise
3124 }
3125
3126 fn Pause(&self, cx: &mut js::context::JSContext) {
3128 if self.network_state.get() == NetworkState::Empty {
3131 self.invoke_resource_selection_algorithm(cx);
3132 }
3133
3134 self.internal_pause_steps();
3136 }
3137
3138 fn Paused(&self) -> bool {
3140 self.paused.get()
3141 }
3142
3143 fn GetDefaultPlaybackRate(&self) -> Fallible<Finite<f64>> {
3145 Ok(Finite::wrap(self.default_playback_rate.get()))
3146 }
3147
3148 fn SetDefaultPlaybackRate(&self, value: Finite<f64>) -> ErrorResult {
3150 let min_allowed = -64.0;
3153 let max_allowed = 64.0;
3154 if *value < min_allowed || *value > max_allowed {
3155 return Err(Error::NotSupported(None));
3156 }
3157
3158 if self.default_playback_rate.get() == *value {
3159 return Ok(());
3160 }
3161
3162 self.default_playback_rate.set(*value);
3163
3164 self.queue_media_element_task_to_fire_event(atom!("ratechange"));
3167
3168 Ok(())
3169 }
3170
3171 fn GetPlaybackRate(&self) -> Fallible<Finite<f64>> {
3173 Ok(Finite::wrap(self.playback_rate.get()))
3174 }
3175
3176 fn SetPlaybackRate(&self, value: Finite<f64>) -> ErrorResult {
3178 let min_allowed = -64.0;
3183 let max_allowed = 64.0;
3184 if *value < min_allowed || *value > max_allowed {
3185 return Err(Error::NotSupported(None));
3186 }
3187
3188 if self.playback_rate.get() == *value {
3189 return Ok(());
3190 }
3191
3192 self.playback_rate.set(*value);
3195
3196 if self.is_potentially_playing() {
3197 if let Some(ref player) = *self.player.borrow() {
3198 if let Err(error) = player.lock().unwrap().set_playback_rate(*value) {
3199 warn!("Could not set the playback rate: {error:?}");
3200 }
3201 }
3202 }
3203
3204 self.queue_media_element_task_to_fire_event(atom!("ratechange"));
3207
3208 Ok(())
3209 }
3210
3211 fn Duration(&self) -> f64 {
3213 self.duration.get()
3214 }
3215
3216 fn CurrentTime(&self) -> Finite<f64> {
3218 Finite::wrap(if self.default_playback_start_position.get() != 0. {
3219 self.default_playback_start_position.get()
3220 } else if self.seeking.get() {
3221 self.current_seek_position.get()
3226 } else {
3227 self.official_playback_position.get()
3228 })
3229 }
3230
3231 fn SetCurrentTime(&self, time: Finite<f64>) {
3233 if self.ready_state.get() == ReadyState::HaveNothing {
3234 self.default_playback_start_position.set(*time);
3235 } else {
3236 self.official_playback_position.set(*time);
3237 self.seek(*time, false);
3238 }
3239 }
3240
3241 fn Seeking(&self) -> bool {
3243 self.seeking.get()
3244 }
3245
3246 fn Ended(&self) -> bool {
3248 self.ended_playback(LoopCondition::Included) &&
3249 self.direction_of_playback() == PlaybackDirection::Forwards
3250 }
3251
3252 fn FastSeek(&self, time: Finite<f64>) {
3254 self.seek(*time, true);
3255 }
3256
3257 fn Played(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
3259 TimeRanges::new(
3260 self.global().as_window(),
3261 self.played.borrow().clone(),
3262 can_gc,
3263 )
3264 }
3265
3266 fn Seekable(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
3268 TimeRanges::new(self.global().as_window(), self.seekable(), can_gc)
3269 }
3270
3271 fn Buffered(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
3273 let mut buffered = TimeRangesContainer::default();
3274 if let Some(ref player) = *self.player.borrow() {
3275 let ranges = player.lock().unwrap().buffered();
3276 for range in ranges {
3277 let _ = buffered.add(range.start, range.end);
3278 }
3279 }
3280 TimeRanges::new(self.global().as_window(), buffered, can_gc)
3281 }
3282
3283 fn AudioTracks(&self, can_gc: CanGc) -> DomRoot<AudioTrackList> {
3285 let window = self.owner_window();
3286 self.audio_tracks_list
3287 .or_init(|| AudioTrackList::new(&window, &[], Some(self), can_gc))
3288 }
3289
3290 fn VideoTracks(&self, can_gc: CanGc) -> DomRoot<VideoTrackList> {
3292 let window = self.owner_window();
3293 self.video_tracks_list
3294 .or_init(|| VideoTrackList::new(&window, &[], Some(self), can_gc))
3295 }
3296
3297 fn TextTracks(&self, can_gc: CanGc) -> DomRoot<TextTrackList> {
3299 let window = self.owner_window();
3300 self.text_tracks_list
3301 .or_init(|| TextTrackList::new(&window, &[], can_gc))
3302 }
3303
3304 fn AddTextTrack(
3306 &self,
3307 kind: TextTrackKind,
3308 label: DOMString,
3309 language: DOMString,
3310 can_gc: CanGc,
3311 ) -> DomRoot<TextTrack> {
3312 let window = self.owner_window();
3313 let track = TextTrack::new(
3316 &window,
3317 "".into(),
3318 kind,
3319 label,
3320 language,
3321 TextTrackMode::Hidden,
3322 None,
3323 can_gc,
3324 );
3325 self.TextTracks(can_gc).add(&track);
3327 DomRoot::from_ref(&track)
3329 }
3330
3331 fn GetVolume(&self) -> Fallible<Finite<f64>> {
3333 Ok(Finite::wrap(self.volume.get()))
3334 }
3335
3336 fn SetVolume(&self, value: Finite<f64>) -> ErrorResult {
3338 let minimum_volume = 0.0;
3341 let maximum_volume = 1.0;
3342 if *value < minimum_volume || *value > maximum_volume {
3343 return Err(Error::IndexSize(None));
3344 }
3345
3346 if self.volume.get() == *value {
3347 return Ok(());
3348 }
3349
3350 self.volume.set(*value);
3351
3352 if let Some(ref player) = *self.player.borrow() {
3353 if let Err(error) = player.lock().unwrap().set_volume(*value) {
3354 warn!("Could not set the volume: {error:?}");
3355 }
3356 }
3357
3358 self.queue_media_element_task_to_fire_event(atom!("volumechange"));
3361
3362 if !self.is_allowed_to_play() {
3365 self.internal_pause_steps();
3366 }
3367
3368 Ok(())
3369 }
3370}
3371
3372impl VirtualMethods for HTMLMediaElement {
3373 fn super_type(&self) -> Option<&dyn VirtualMethods> {
3374 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
3375 }
3376
3377 fn attribute_mutated(
3378 &self,
3379 cx: &mut js::context::JSContext,
3380 attr: &Attr,
3381 mutation: AttributeMutation,
3382 ) {
3383 self.super_type()
3384 .unwrap()
3385 .attribute_mutated(cx, attr, mutation);
3386
3387 match *attr.local_name() {
3388 local_name!("muted") => {
3389 if let AttributeMutation::Set(
3393 _,
3394 AttributeMutationReason::ByCloning | AttributeMutationReason::ByParser,
3395 ) = mutation
3396 {
3397 self.SetMuted(true);
3398 }
3399 },
3400 local_name!("src") => {
3401 if !mutation.is_removal() {
3406 self.media_element_load_algorithm(cx);
3407 }
3408 },
3409 local_name!("controls") => {
3410 if mutation.new_value(attr).is_some() {
3411 self.render_controls(cx);
3412 } else {
3413 self.remove_controls();
3414 }
3415 },
3416 _ => (),
3417 };
3418 }
3419
3420 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
3422 self.super_type().unwrap().unbind_from_tree(context, can_gc);
3423
3424 self.remove_controls();
3425
3426 if context.tree_connected {
3427 let task = MediaElementMicrotask::PauseIfNotInDocument {
3428 elem: DomRoot::from_ref(self),
3429 };
3430 ScriptThread::await_stable_state(Microtask::MediaElement(task));
3431 }
3432 }
3433
3434 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
3435 self.super_type().unwrap().adopting_steps(cx, old_doc);
3436
3437 if let Some(id) = &*self.media_controls_id.borrow() {
3441 let Some(shadow_root) = self.upcast::<Element>().shadow_root() else {
3442 error!("Missing media controls shadow root");
3443 return;
3444 };
3445
3446 old_doc.unregister_media_controls(id);
3447 self.owner_document()
3448 .register_media_controls(id, &shadow_root);
3449 }
3450 }
3451}
3452
3453#[derive(JSTraceable, MallocSizeOf)]
3454pub(crate) enum MediaElementMicrotask {
3455 ResourceSelection {
3456 elem: DomRoot<HTMLMediaElement>,
3457 generation_id: u32,
3458 #[no_trace]
3459 base_url: ServoUrl,
3460 },
3461 PauseIfNotInDocument {
3462 elem: DomRoot<HTMLMediaElement>,
3463 },
3464 Seeked {
3465 elem: DomRoot<HTMLMediaElement>,
3466 generation_id: u32,
3467 },
3468 SelectNextSourceChild {
3469 elem: DomRoot<HTMLMediaElement>,
3470 generation_id: u32,
3471 },
3472 SelectNextSourceChildAfterWait {
3473 elem: DomRoot<HTMLMediaElement>,
3474 generation_id: u32,
3475 },
3476}
3477
3478impl MicrotaskRunnable for MediaElementMicrotask {
3479 fn handler(&self, cx: &mut js::context::JSContext) {
3480 match self {
3481 &MediaElementMicrotask::ResourceSelection {
3482 ref elem,
3483 generation_id,
3484 ref base_url,
3485 } => {
3486 if generation_id == elem.generation_id.get() {
3487 elem.resource_selection_algorithm_sync(base_url.clone(), cx);
3488 }
3489 },
3490 MediaElementMicrotask::PauseIfNotInDocument { elem } => {
3491 if !elem.upcast::<Node>().is_connected() {
3492 elem.internal_pause_steps();
3493 }
3494 },
3495 &MediaElementMicrotask::Seeked {
3496 ref elem,
3497 generation_id,
3498 } => {
3499 if generation_id == elem.generation_id.get() {
3500 elem.seek_end();
3501 }
3502 },
3503 &MediaElementMicrotask::SelectNextSourceChild {
3504 ref elem,
3505 generation_id,
3506 } => {
3507 if generation_id == elem.generation_id.get() {
3508 elem.select_next_source_child(CanGc::from_cx(cx));
3509 }
3510 },
3511 &MediaElementMicrotask::SelectNextSourceChildAfterWait {
3512 ref elem,
3513 generation_id,
3514 } => {
3515 if generation_id == elem.generation_id.get() {
3516 elem.select_next_source_child_after_wait(cx);
3517 }
3518 },
3519 }
3520 }
3521
3522 fn enter_realm<'cx>(&self, cx: &'cx mut js::context::JSContext) -> AutoRealm<'cx> {
3523 match self {
3524 &MediaElementMicrotask::ResourceSelection { ref elem, .. } |
3525 &MediaElementMicrotask::PauseIfNotInDocument { ref elem } |
3526 &MediaElementMicrotask::Seeked { ref elem, .. } |
3527 &MediaElementMicrotask::SelectNextSourceChild { ref elem, .. } |
3528 &MediaElementMicrotask::SelectNextSourceChildAfterWait { ref elem, .. } => {
3529 enter_auto_realm(cx, &**elem)
3530 },
3531 }
3532 }
3533}
3534
3535enum Resource {
3536 Object,
3537 Url(ServoUrl),
3538}
3539
3540#[derive(Debug, MallocSizeOf, PartialEq)]
3541enum DataBuffer {
3542 Payload(Vec<u8>),
3543 EndOfStream,
3544}
3545
3546#[derive(MallocSizeOf)]
3547struct BufferedDataSource {
3548 locked: Cell<bool>,
3553 buffers: VecDeque<DataBuffer>,
3555}
3556
3557impl BufferedDataSource {
3558 fn new() -> BufferedDataSource {
3559 BufferedDataSource {
3560 locked: Cell::new(true),
3561 buffers: VecDeque::default(),
3562 }
3563 }
3564
3565 fn set_locked(&self, locked: bool) {
3566 self.locked.set(locked)
3567 }
3568
3569 fn add_buffer_to_queue(&mut self, buffer: DataBuffer) {
3570 debug_assert_ne!(
3571 self.buffers.back(),
3572 Some(&DataBuffer::EndOfStream),
3573 "The media backend not expects any further data after end of stream"
3574 );
3575
3576 self.buffers.push_back(buffer);
3577 }
3578
3579 fn process_into_player_from_queue(
3580 &mut self,
3581 player: &Arc<Mutex<dyn Player>>,
3582 ) -> Result<(), PlayerError> {
3583 if self.locked.get() {
3585 return Ok(());
3586 }
3587
3588 while let Some(buffer) = self.buffers.pop_front() {
3589 match buffer {
3590 DataBuffer::Payload(payload) => {
3591 if let Err(error) = player.lock().unwrap().push_data(payload) {
3592 warn!("Could not push input data to player: {error:?}");
3593 return Err(error);
3594 }
3595 },
3596 DataBuffer::EndOfStream => {
3597 if let Err(error) = player.lock().unwrap().end_of_stream() {
3598 warn!("Could not signal EOS to player: {error:?}");
3599 return Err(error);
3600 }
3601 },
3602 }
3603 }
3604
3605 Ok(())
3606 }
3607
3608 fn reset(&mut self) {
3609 self.locked.set(true);
3610 self.buffers.clear();
3611 }
3612}
3613
3614#[derive(Debug, MallocSizeOf, PartialEq)]
3616enum CancelReason {
3617 Backoff,
3619 Error,
3621 Abort,
3623}
3624
3625#[derive(MallocSizeOf)]
3626pub(crate) struct HTMLMediaElementFetchContext {
3627 request_id: RequestId,
3629 cancel_reason: Option<CancelReason>,
3631 is_seekable: bool,
3633 origin_clean: bool,
3635 data_source: RefCell<BufferedDataSource>,
3637 fetch_canceller: FetchCanceller,
3640}
3641
3642impl HTMLMediaElementFetchContext {
3643 fn new(
3644 request_id: RequestId,
3645 core_resource_thread: CoreResourceThread,
3646 ) -> HTMLMediaElementFetchContext {
3647 HTMLMediaElementFetchContext {
3648 request_id,
3649 cancel_reason: None,
3650 is_seekable: false,
3651 origin_clean: true,
3652 data_source: RefCell::new(BufferedDataSource::new()),
3653 fetch_canceller: FetchCanceller::new(request_id, false, core_resource_thread),
3654 }
3655 }
3656
3657 fn request_id(&self) -> RequestId {
3658 self.request_id
3659 }
3660
3661 fn is_seekable(&self) -> bool {
3662 self.is_seekable
3663 }
3664
3665 fn set_seekable(&mut self, seekable: bool) {
3666 self.is_seekable = seekable;
3667 }
3668
3669 fn origin_is_clean(&self) -> bool {
3670 self.origin_clean
3671 }
3672
3673 fn set_origin_clean(&mut self, origin_clean: bool) {
3674 self.origin_clean = origin_clean;
3675 }
3676
3677 fn data_source(&self) -> &RefCell<BufferedDataSource> {
3678 &self.data_source
3679 }
3680
3681 fn cancel(&mut self, reason: CancelReason) {
3682 if self.cancel_reason.is_some() {
3683 return;
3684 }
3685 self.cancel_reason = Some(reason);
3686 self.data_source.borrow_mut().reset();
3687 self.fetch_canceller.abort();
3688 }
3689
3690 fn cancel_reason(&self) -> &Option<CancelReason> {
3691 &self.cancel_reason
3692 }
3693}
3694
3695struct HTMLMediaElementFetchListener {
3696 element: Trusted<HTMLMediaElement>,
3698 generation_id: u32,
3700 request_id: RequestId,
3702 next_progress_event: Instant,
3704 url: ServoUrl,
3706 expected_content_length: Option<u64>,
3708 fetched_content_length: u64,
3710 content_length_to_discard: u64,
3714}
3715
3716impl FetchResponseListener for HTMLMediaElementFetchListener {
3717 fn process_request_body(&mut self, _: RequestId) {}
3718
3719 fn process_response(
3720 &mut self,
3721 cx: &mut js::context::JSContext,
3722 _: RequestId,
3723 metadata: Result<FetchMetadata, NetworkError>,
3724 ) {
3725 let element = self.element.root();
3726
3727 let (metadata, origin_clean) = match metadata {
3728 Ok(fetch_metadata) => match fetch_metadata {
3729 FetchMetadata::Unfiltered(metadata) => (Some(metadata), true),
3730 FetchMetadata::Filtered { filtered, unsafe_ } => (
3731 Some(unsafe_),
3732 matches!(
3733 filtered,
3734 FilteredMetadata::Basic(_) | FilteredMetadata::Cors(_)
3735 ),
3736 ),
3737 },
3738 Err(_) => (None, true),
3739 };
3740
3741 let (status_is_success, is_seekable) =
3742 metadata.as_ref().map_or((false, false), |metadata| {
3743 let status = &metadata.status;
3744 (status.is_success(), *status == StatusCode::PARTIAL_CONTENT)
3745 });
3746
3747 if !status_is_success {
3749 if element.ready_state.get() == ReadyState::HaveNothing {
3750 element.media_data_processing_failure_steps();
3752 } else {
3753 element.media_data_processing_fatal_steps(MEDIA_ERR_NETWORK, cx);
3755 }
3756 return;
3757 }
3758
3759 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut() {
3760 current_fetch_context.set_seekable(is_seekable);
3761 current_fetch_context.set_origin_clean(origin_clean);
3762 }
3763
3764 if let Some(metadata) = metadata.as_ref() {
3765 if let Some(headers) = metadata.headers.as_ref() {
3766 let content_length =
3769 if let Some(content_range) = headers.typed_get::<ContentRange>() {
3770 content_range.bytes_len()
3771 } else {
3772 headers
3773 .typed_get::<ContentLength>()
3774 .map(|content_length| content_length.0)
3775 };
3776
3777 if content_length != self.expected_content_length {
3779 if let Some(content_length) = content_length {
3780 self.expected_content_length = Some(content_length);
3781 }
3782 }
3783 }
3784 }
3785
3786 if let Some(expected_content_length) = self.expected_content_length {
3788 if let Err(e) = element
3789 .player
3790 .borrow()
3791 .as_ref()
3792 .unwrap()
3793 .lock()
3794 .unwrap()
3795 .set_input_size(expected_content_length)
3796 {
3797 warn!("Could not set player input size {:?}", e);
3798 }
3799 }
3800 }
3801
3802 fn process_response_chunk(
3803 &mut self,
3804 _: &mut js::context::JSContext,
3805 _: RequestId,
3806 chunk: Vec<u8>,
3807 ) {
3808 let element = self.element.root();
3809
3810 self.fetched_content_length += chunk.len() as u64;
3811
3812 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut() {
3814 if let Some(CancelReason::Backoff) = current_fetch_context.cancel_reason() {
3815 return;
3816 }
3817
3818 let payload = if !current_fetch_context.is_seekable() &&
3820 self.content_length_to_discard != 0
3821 {
3822 if chunk.len() as u64 > self.content_length_to_discard {
3823 let shrink_chunk = chunk[self.content_length_to_discard as usize..].to_vec();
3824 self.content_length_to_discard = 0;
3825 shrink_chunk
3826 } else {
3827 self.content_length_to_discard -= chunk.len() as u64;
3829 return;
3830 }
3831 } else {
3832 chunk
3833 };
3834
3835 if let Err(e) = {
3836 let mut data_source = current_fetch_context.data_source().borrow_mut();
3837 data_source.add_buffer_to_queue(DataBuffer::Payload(payload));
3838 data_source
3839 .process_into_player_from_queue(element.player.borrow().as_ref().unwrap())
3840 } {
3841 if e == PlayerError::EnoughData {
3846 current_fetch_context.cancel(CancelReason::Backoff);
3847 }
3848 return;
3849 }
3850 }
3851
3852 if Instant::now() > self.next_progress_event {
3857 element.queue_media_element_task_to_fire_event(atom!("progress"));
3858 self.next_progress_event = Instant::now() + Duration::from_millis(350);
3859 }
3860 }
3861
3862 fn process_response_eof(
3863 self,
3864 cx: &mut js::context::JSContext,
3865 _: RequestId,
3866 status: Result<(), NetworkError>,
3867 timing: ResourceFetchTiming,
3868 ) {
3869 let element = self.element.root();
3870
3871 if status.is_ok() && self.fetched_content_length != 0 {
3873 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut()
3878 {
3879 if self.expected_content_length.is_none() {
3886 if let Err(e) = element
3887 .player
3888 .borrow()
3889 .as_ref()
3890 .unwrap()
3891 .lock()
3892 .unwrap()
3893 .set_input_size(self.fetched_content_length)
3894 {
3895 warn!("Could not set player input size {:?}", e);
3896 }
3897 }
3898
3899 let mut data_source = current_fetch_context.data_source().borrow_mut();
3900
3901 data_source.add_buffer_to_queue(DataBuffer::EndOfStream);
3902 let _ = data_source
3903 .process_into_player_from_queue(element.player.borrow().as_ref().unwrap());
3904 }
3905
3906 element
3908 .upcast::<EventTarget>()
3909 .fire_event(atom!("progress"), CanGc::from_cx(cx));
3910
3911 element.network_state.set(NetworkState::Idle);
3914
3915 element
3916 .upcast::<EventTarget>()
3917 .fire_event(atom!("suspend"), CanGc::from_cx(cx));
3918 } else if status.is_err() && element.ready_state.get() != ReadyState::HaveNothing {
3919 element.media_data_processing_fatal_steps(MEDIA_ERR_NETWORK, cx);
3921 } else {
3922 element.media_data_processing_failure_steps();
3925 }
3926
3927 network_listener::submit_timing(cx, &self, &status, &timing);
3928 }
3929
3930 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
3931 let global = &self.resource_timing_global();
3932 global.report_csp_violations(violations, None, None);
3933 }
3934
3935 fn should_invoke(&self) -> bool {
3936 let element = self.element.root();
3937
3938 if element.generation_id.get() != self.generation_id || element.player.borrow().is_none() {
3939 return false;
3940 }
3941
3942 let Some(ref current_fetch_context) = *element.current_fetch_context.borrow() else {
3943 return false;
3944 };
3945
3946 if current_fetch_context.request_id() != self.request_id {
3948 return false;
3949 }
3950
3951 if let Some(cancel_reason) = current_fetch_context.cancel_reason() {
3954 if matches!(*cancel_reason, CancelReason::Error | CancelReason::Abort) {
3955 return false;
3956 }
3957 }
3958
3959 true
3960 }
3961}
3962
3963impl ResourceTimingListener for HTMLMediaElementFetchListener {
3964 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
3965 let initiator_type = InitiatorType::LocalName(
3966 self.element
3967 .root()
3968 .upcast::<Element>()
3969 .local_name()
3970 .to_string(),
3971 );
3972 (initiator_type, self.url.clone())
3973 }
3974
3975 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
3976 self.element.root().owner_document().global()
3977 }
3978}
3979
3980impl HTMLMediaElementFetchListener {
3981 fn new(element: &HTMLMediaElement, request_id: RequestId, url: ServoUrl, offset: u64) -> Self {
3982 Self {
3983 element: Trusted::new(element),
3984 generation_id: element.generation_id.get(),
3985 request_id,
3986 next_progress_event: Instant::now() + Duration::from_millis(350),
3987 url,
3988 expected_content_length: None,
3989 fetched_content_length: 0,
3990 content_length_to_discard: offset,
3991 }
3992 }
3993}
3994
3995#[derive(JSTraceable, MallocSizeOf)]
3999struct HTMLMediaElementEventHandler {
4000 element: WeakRef<HTMLMediaElement>,
4001}
4002
4003#[expect(unsafe_code)]
4004unsafe impl Send for HTMLMediaElementEventHandler {}
4005
4006impl HTMLMediaElementEventHandler {
4007 fn new(element: &HTMLMediaElement) -> Self {
4008 Self {
4009 element: WeakRef::new(element),
4010 }
4011 }
4012
4013 fn handle_player_event(
4014 &self,
4015 player_id: usize,
4016 event: PlayerEvent,
4017 cx: &mut js::context::JSContext,
4018 ) {
4019 let Some(element) = self.element.root() else {
4020 return;
4021 };
4022
4023 if element.player_id().is_none_or(|id| id != player_id) {
4025 return;
4026 }
4027
4028 match event {
4029 PlayerEvent::DurationChanged(duration) => element.playback_duration_changed(duration),
4030 PlayerEvent::EndOfStream => element.playback_end(),
4031 PlayerEvent::EnoughData => element.playback_enough_data(),
4032 PlayerEvent::Error(ref error) => element.playback_error(error, cx),
4033 PlayerEvent::MetadataUpdated(ref metadata) => {
4034 element.playback_metadata_updated(metadata, CanGc::from_cx(cx))
4035 },
4036 PlayerEvent::NeedData => element.playback_need_data(),
4037 PlayerEvent::PositionChanged(position) => element.playback_position_changed(position),
4038 PlayerEvent::SeekData(offset, seek_lock) => {
4039 element.fetch_request(Some(offset), Some(seek_lock))
4040 },
4041 PlayerEvent::SeekDone(position) => element.playback_seek_done(position),
4042 PlayerEvent::StateChanged(ref state) => element.playback_state_changed(state),
4043 PlayerEvent::VideoFrameUpdated => element.playback_video_frame_updated(),
4044 }
4045 }
4046}
4047
4048impl Drop for HTMLMediaElementEventHandler {
4049 fn drop(&mut self) {
4050 assert_in_script();
4054 }
4055}