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::assert::assert_in_script;
33use script_bindings::cell::DomRefCell;
34use script_bindings::codegen::InheritTypes::{
35 ElementTypeId, HTMLElementTypeId, HTMLMediaElementTypeId, NodeTypeId,
36};
37use script_bindings::weakref::WeakRef;
38use servo_base::generic_channel::GenericSharedMemory;
39use servo_base::id::WebViewId;
40use servo_config::pref;
41use servo_media::player::audio::AudioRenderer;
42use servo_media::player::video::{VideoFrame, VideoFrameRenderer};
43use servo_media::player::{PlaybackState, Player, PlayerError, PlayerEvent, SeekLock, StreamType};
44use servo_media::{ClientContextId, ServoMedia, SupportsMediaType};
45use servo_url::ServoUrl;
46use stylo_atoms::Atom;
47use uuid::Uuid;
48use webrender_api::{
49 ExternalImageData, ExternalImageId, ExternalImageType, ImageBufferKind, ImageDescriptor,
50 ImageDescriptorFlags, ImageFormat, ImageKey,
51};
52
53use crate::document_loader::{LoadBlocker, LoadType};
54use crate::dom::audio::audiotrack::AudioTrack;
55use crate::dom::audio::audiotracklist::AudioTrackList;
56use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::{
57 CanPlayTypeResult, HTMLMediaElementConstants, HTMLMediaElementMethods,
58};
59use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConstants::*;
60use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods;
61use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
62use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
63use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode};
64use crate::dom::bindings::codegen::Bindings::URLBinding::URLMethods;
65use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
66use crate::dom::bindings::codegen::UnionTypes::{
67 MediaStreamOrBlob, VideoTrackOrAudioTrackOrTextTrack,
68};
69use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
70use crate::dom::bindings::inheritance::Castable;
71use crate::dom::bindings::num::Finite;
72use crate::dom::bindings::refcounted::Trusted;
73use crate::dom::bindings::reflector::DomGlobal;
74use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom, UnrootedDom};
75use crate::dom::bindings::str::{DOMString, USVString};
76use crate::dom::blob::Blob;
77use crate::dom::csp::{GlobalCspReporting, Violation};
78use crate::dom::document::Document;
79use crate::dom::element::attributes::storage::AttrRef;
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 = self
386 .glplayer_id
387 .filter(|_| frame.is_gl_texture())
388 .map(|glplayer_id| {
389 let texture_target = if frame.is_external_oes() {
390 ImageBufferKind::TextureExternal
391 } else {
392 ImageBufferKind::Texture2D
393 };
394
395 SerializableImageData::External(ExternalImageData {
396 id: ExternalImageId(glplayer_id),
397 channel_index: 0,
398 image_type: ExternalImageType::TextureHandle(texture_target),
399 normalized_uvs: false,
400 })
401 })
402 .unwrap_or_else(|| {
403 SerializableImageData::Raw(GenericSharedMemory::from_bytes(
404 &frame.get_data(),
405 ))
406 });
407
408 self.current_frame_holder
409 .get_or_insert_with(|| FrameHolder::new(frame.clone()))
410 .set(frame);
411
412 updates.push(ImageUpdate::AddImage(
413 new_image_key,
414 descriptor,
415 image_data,
416 false,
417 ));
418 },
419 None => {
420 let Some(image_key) = self.paint_api.generate_image_key_blocking(self.webview_id)
421 else {
422 return;
423 };
424
425 self.current_frame = Some(MediaFrame {
426 image_key,
427 width: frame.get_width(),
428 height: frame.get_height(),
429 });
430
431 let image_data = self
432 .glplayer_id
433 .filter(|_| frame.is_gl_texture())
434 .map(|glplayer_id| {
435 let texture_target = if frame.is_external_oes() {
436 ImageBufferKind::TextureExternal
437 } else {
438 ImageBufferKind::Texture2D
439 };
440
441 SerializableImageData::External(ExternalImageData {
442 id: ExternalImageId(glplayer_id),
443 channel_index: 0,
444 image_type: ExternalImageType::TextureHandle(texture_target),
445 normalized_uvs: false,
446 })
447 })
448 .unwrap_or_else(|| {
449 SerializableImageData::Raw(GenericSharedMemory::from_bytes(
450 &frame.get_data(),
451 ))
452 });
453
454 self.current_frame_holder = Some(FrameHolder::new(frame));
455
456 updates.push(ImageUpdate::AddImage(
457 image_key, descriptor, image_data, false,
458 ));
459 },
460 }
461 self.paint_api
462 .update_images(self.webview_id.into(), updates);
463 }
464}
465
466#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
467#[derive(JSTraceable, MallocSizeOf)]
468enum SrcObject {
469 MediaStream(Dom<MediaStream>),
470 Blob(Dom<Blob>),
471}
472
473impl From<MediaStreamOrBlob> for SrcObject {
474 fn from(src_object: MediaStreamOrBlob) -> SrcObject {
475 match src_object {
476 MediaStreamOrBlob::Blob(blob) => SrcObject::Blob(Dom::from_ref(&*blob)),
477 MediaStreamOrBlob::MediaStream(stream) => {
478 SrcObject::MediaStream(Dom::from_ref(&*stream))
479 },
480 }
481 }
482}
483
484#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
485enum LoadState {
486 NotLoaded,
487 LoadingFromSrcObject,
488 LoadingFromSrcAttribute,
489 LoadingFromSourceChild,
490 WaitingForSource,
491}
492
493#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
495#[derive(JSTraceable, MallocSizeOf)]
496struct SourceChildrenPointer {
497 source_before_pointer: Dom<HTMLSourceElement>,
498 inclusive: bool,
499}
500
501impl SourceChildrenPointer {
502 fn new(source_before_pointer: DomRoot<HTMLSourceElement>, inclusive: bool) -> Self {
503 Self {
504 source_before_pointer: source_before_pointer.as_traced(),
505 inclusive,
506 }
507 }
508}
509
510#[derive(Clone, Copy, Debug, PartialEq)]
514enum LoopCondition {
515 Included,
516 Ignored,
517}
518
519#[dom_struct]
520pub(crate) struct HTMLMediaElement {
521 htmlelement: HTMLElement,
522 network_state: Cell<NetworkState>,
524 ready_state: Cell<ReadyState>,
526 src_object: DomRefCell<Option<SrcObject>>,
528 current_src: DomRefCell<String>,
530 generation_id: Cell<u32>,
532 fired_loadeddata_event: Cell<bool>,
536 error: MutNullableDom<MediaError>,
538 paused: Cell<bool>,
540 default_playback_rate: Cell<f64>,
542 playback_rate: Cell<f64>,
544 autoplaying: Cell<bool>,
546 delaying_the_load_event_flag: DomRefCell<Option<LoadBlocker>>,
548 #[conditional_malloc_size_of]
550 pending_play_promises: DomRefCell<Vec<Rc<Promise>>>,
551 #[expect(clippy::type_complexity)]
553 #[conditional_malloc_size_of]
554 in_flight_play_promises_queue: DomRefCell<VecDeque<(Box<[Rc<Promise>]>, ErrorResult)>>,
555 #[ignore_malloc_size_of = "servo_media"]
556 #[no_trace]
557 player: DomRefCell<Option<Arc<Mutex<dyn Player>>>>,
558 #[conditional_malloc_size_of]
559 #[no_trace]
560 video_renderer: Arc<Mutex<MediaFrameRenderer>>,
561 #[ignore_malloc_size_of = "servo_media"]
562 #[no_trace]
563 audio_renderer: DomRefCell<Option<Arc<Mutex<dyn AudioRenderer>>>>,
564 #[conditional_malloc_size_of]
565 #[no_trace]
566 event_handler: RefCell<Option<Arc<Mutex<HTMLMediaElementEventHandler>>>>,
567 show_poster: Cell<bool>,
569 duration: Cell<f64>,
571 current_playback_position: Cell<f64>,
573 official_playback_position: Cell<f64>,
575 default_playback_start_position: Cell<f64>,
577 volume: Cell<f64>,
579 seeking: Cell<bool>,
581 current_seek_position: Cell<f64>,
585 muted: Cell<bool>,
587 load_state: Cell<LoadState>,
589 source_children_pointer: DomRefCell<Option<SourceChildrenPointer>>,
590 current_source_child: MutNullableDom<HTMLSourceElement>,
591 #[no_trace]
593 resource_url: DomRefCell<Option<ServoUrl>>,
594 #[no_trace]
597 blob_url: DomRefCell<Option<ServoUrl>>,
598 played: DomRefCell<TimeRangesContainer>,
600 audio_tracks_list: MutNullableDom<AudioTrackList>,
602 video_tracks_list: MutNullableDom<VideoTrackList>,
604 text_tracks_list: MutNullableDom<TextTrackList>,
606 #[ignore_malloc_size_of = "Defined in std::time"]
608 next_timeupdate_event: Cell<Instant>,
609 current_fetch_context: RefCell<Option<HTMLMediaElementFetchContext>>,
611 media_controls_id: DomRefCell<Option<String>>,
616}
617
618#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
620#[repr(u8)]
621pub(crate) enum NetworkState {
622 Empty = HTMLMediaElementConstants::NETWORK_EMPTY as u8,
623 Idle = HTMLMediaElementConstants::NETWORK_IDLE as u8,
624 Loading = HTMLMediaElementConstants::NETWORK_LOADING as u8,
625 NoSource = HTMLMediaElementConstants::NETWORK_NO_SOURCE as u8,
626}
627
628#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq, PartialOrd)]
630#[repr(u8)]
631#[expect(clippy::enum_variant_names)] pub(crate) enum ReadyState {
633 HaveNothing = HTMLMediaElementConstants::HAVE_NOTHING as u8,
634 HaveMetadata = HTMLMediaElementConstants::HAVE_METADATA as u8,
635 HaveCurrentData = HTMLMediaElementConstants::HAVE_CURRENT_DATA as u8,
636 HaveFutureData = HTMLMediaElementConstants::HAVE_FUTURE_DATA as u8,
637 HaveEnoughData = HTMLMediaElementConstants::HAVE_ENOUGH_DATA as u8,
638}
639
640#[derive(Clone, Copy, PartialEq)]
642enum PlaybackDirection {
643 Forwards,
644 Backwards,
645}
646
647impl HTMLMediaElement {
648 pub(crate) fn new_inherited(
649 tag_name: LocalName,
650 prefix: Option<Prefix>,
651 document: &Document,
652 ) -> Self {
653 Self {
654 htmlelement: HTMLElement::new_inherited(tag_name, prefix, document),
655 network_state: Cell::new(NetworkState::Empty),
656 ready_state: Cell::new(ReadyState::HaveNothing),
657 src_object: Default::default(),
658 current_src: DomRefCell::new("".to_owned()),
659 generation_id: Cell::new(0),
660 fired_loadeddata_event: Cell::new(false),
661 error: Default::default(),
662 paused: Cell::new(true),
663 default_playback_rate: Cell::new(1.0),
664 playback_rate: Cell::new(1.0),
665 muted: Cell::new(false),
666 load_state: Cell::new(LoadState::NotLoaded),
667 source_children_pointer: DomRefCell::new(None),
668 current_source_child: Default::default(),
669 autoplaying: Cell::new(true),
671 delaying_the_load_event_flag: Default::default(),
672 pending_play_promises: Default::default(),
673 in_flight_play_promises_queue: Default::default(),
674 player: Default::default(),
675 video_renderer: Arc::new(Mutex::new(MediaFrameRenderer::new(
676 document.webview_id(),
677 document.window().paint_api().clone(),
678 document.window().get_player_context(),
679 ))),
680 audio_renderer: Default::default(),
681 event_handler: Default::default(),
682 show_poster: Cell::new(true),
683 duration: Cell::new(f64::NAN),
684 current_playback_position: Cell::new(0.),
685 official_playback_position: Cell::new(0.),
686 default_playback_start_position: Cell::new(0.),
687 volume: Cell::new(1.0),
688 seeking: Cell::new(false),
689 current_seek_position: Cell::new(f64::NAN),
690 resource_url: DomRefCell::new(None),
691 blob_url: DomRefCell::new(None),
692 played: DomRefCell::new(TimeRangesContainer::default()),
693 audio_tracks_list: Default::default(),
694 video_tracks_list: Default::default(),
695 text_tracks_list: Default::default(),
696 next_timeupdate_event: Cell::new(Instant::now() + Duration::from_millis(250)),
697 current_fetch_context: RefCell::new(None),
698 media_controls_id: DomRefCell::new(None),
699 }
700 }
701
702 pub(crate) fn network_state(&self) -> NetworkState {
703 self.network_state.get()
704 }
705
706 pub(crate) fn get_ready_state(&self) -> ReadyState {
707 self.ready_state.get()
708 }
709
710 fn media_type_id(&self) -> HTMLMediaElementTypeId {
711 match self.upcast::<Node>().type_id() {
712 NodeTypeId::Element(ElementTypeId::HTMLElement(
713 HTMLElementTypeId::HTMLMediaElement(media_type_id),
714 )) => media_type_id,
715 _ => unreachable!(),
716 }
717 }
718
719 fn update_media_state(&self) {
720 let is_playing = self
721 .player
722 .borrow()
723 .as_ref()
724 .is_some_and(|player| !player.lock().unwrap().paused());
725
726 if self.is_potentially_playing() && !is_playing {
727 if let Some(ref player) = *self.player.borrow() {
728 let player = player.lock().unwrap();
729
730 if let Err(error) = player.set_playback_rate(self.playback_rate.get()) {
731 warn!("Could not set the playback rate: {error:?}");
732 }
733 if let Err(error) = player.set_volume(self.volume.get()) {
734 warn!("Could not set the volume: {error:?}");
735 }
736 if let Err(error) = player.play() {
737 error!("Could not play media: {error:?}");
738 }
739 }
740 } else if is_playing &&
741 let Some(ref player) = *self.player.borrow() &&
742 let Err(error) = player.lock().unwrap().pause()
743 {
744 error!("Could not pause player: {error:?}");
745 }
746 }
747
748 pub(crate) fn delay_load_event(&self, delay: bool, cx: &mut js::context::JSContext) {
755 let blocker = &self.delaying_the_load_event_flag;
756 if delay && blocker.borrow().is_none() {
757 *blocker.borrow_mut() = Some(LoadBlocker::new(&self.owner_document(), LoadType::Media));
758 } else if !delay && blocker.borrow().is_some() {
759 LoadBlocker::terminate(blocker, cx);
760 }
761 }
762
763 fn time_marches_on(&self) {
765 if Instant::now() > self.next_timeupdate_event.get() {
771 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
772 self.next_timeupdate_event
773 .set(Instant::now() + Duration::from_millis(250));
774 }
775 }
776
777 fn internal_play_steps(&self, cx: &mut js::context::JSContext) {
779 if self.network_state.get() == NetworkState::Empty {
782 self.invoke_resource_selection_algorithm(cx);
783 }
784
785 if self.ended_playback(LoopCondition::Ignored) &&
791 self.direction_of_playback() == PlaybackDirection::Forwards
792 {
793 self.seek(
794 self.earliest_possible_position(),
795 false,
796 );
797 }
798
799 let state = self.ready_state.get();
800
801 if self.Paused() {
803 self.paused.set(false);
805
806 if self.show_poster.get() {
809 self.show_poster.set(false);
810 self.time_marches_on();
811 }
812
813 self.queue_media_element_task_to_fire_event(atom!("play"));
816
817 match state {
823 ReadyState::HaveNothing |
824 ReadyState::HaveMetadata |
825 ReadyState::HaveCurrentData => {
826 self.queue_media_element_task_to_fire_event(atom!("waiting"));
827 },
828 ReadyState::HaveFutureData | ReadyState::HaveEnoughData => {
829 self.notify_about_playing();
830 },
831 }
832 }
833 else if state == ReadyState::HaveFutureData || state == ReadyState::HaveEnoughData {
838 self.take_pending_play_promises(Ok(()));
839
840 let this = Trusted::new(self);
841 let generation_id = self.generation_id.get();
842
843 self.owner_global()
844 .task_manager()
845 .media_element_task_source()
846 .queue(task!(resolve_pending_play_promises: move || {
847 let this = this.root();
848 if generation_id != this.generation_id.get() {
849 return;
850 }
851
852 this.fulfill_in_flight_play_promises(|| {});
853 }));
854 }
855
856 self.autoplaying.set(false);
858
859 self.update_media_state();
860 }
861
862 fn internal_pause_steps(&self) {
864 self.autoplaying.set(false);
866
867 if !self.Paused() {
869 self.paused.set(true);
871
872 self.take_pending_play_promises(Err(Error::Abort(None)));
874
875 let this = Trusted::new(self);
877 let generation_id = self.generation_id.get();
878
879 self.owner_global()
880 .task_manager()
881 .media_element_task_source()
882 .queue(task!(internal_pause_steps: move |cx| {
883 let this = this.root();
884 if generation_id != this.generation_id.get() {
885 return;
886 }
887
888 this.fulfill_in_flight_play_promises(|| {
889 this.upcast::<EventTarget>().fire_event(cx, atom!("timeupdate"));
891
892 this.upcast::<EventTarget>().fire_event(cx, atom!("pause"));
894
895 });
899 }));
900
901 self.official_playback_position
903 .set(self.current_playback_position.get());
904 }
905
906 self.update_media_state();
907 }
908
909 fn is_allowed_to_play(&self) -> bool {
911 true
912 }
913
914 fn notify_about_playing(&self) {
916 self.take_pending_play_promises(Ok(()));
918
919 let this = Trusted::new(self);
921 let generation_id = self.generation_id.get();
922
923 self.owner_global()
924 .task_manager()
925 .media_element_task_source()
926 .queue(task!(notify_about_playing: move |cx| {
927 let this = this.root();
928 if generation_id != this.generation_id.get() {
929 return;
930 }
931
932 this.fulfill_in_flight_play_promises(|| {
933 this.upcast::<EventTarget>().fire_event(cx, atom!("playing"));
935
936 });
939 }));
940 }
941
942 #[expect(
944 clippy::collapsible_match,
945 reason = "This way follows the spec more closely"
946 )]
947 fn change_ready_state(&self, ready_state: ReadyState) {
948 let old_ready_state = self.ready_state.get();
949 self.ready_state.set(ready_state);
950
951 if self.network_state.get() == NetworkState::Empty {
952 return;
953 }
954
955 if old_ready_state == ready_state {
956 return;
957 }
958
959 match (old_ready_state, ready_state) {
961 (ReadyState::HaveNothing, ReadyState::HaveMetadata) => {
964 self.queue_media_element_task_to_fire_event(atom!("loadedmetadata"));
967 return;
969 },
970 (ReadyState::HaveMetadata, new) if new >= ReadyState::HaveCurrentData => {
973 if !self.fired_loadeddata_event.get() {
977 self.fired_loadeddata_event.set(true);
978
979 let this = Trusted::new(self);
980 let generation_id = self.generation_id.get();
981
982 self.owner_global()
983 .task_manager()
984 .media_element_task_source()
985 .queue(task!(media_reached_current_data: move |cx| {
986 let this = this.root();
987 if generation_id != this.generation_id.get() {
988 return;
989 }
990
991 this.upcast::<EventTarget>().fire_event(cx, atom!("loadeddata"));
992 this.delay_load_event(false, cx);
996 }));
997 }
998
999 },
1003 (ReadyState::HaveFutureData, new) if new <= ReadyState::HaveCurrentData => {
1004 return;
1009 },
1010
1011 _ => (),
1012 }
1013
1014 if old_ready_state <= ReadyState::HaveCurrentData &&
1017 ready_state >= ReadyState::HaveFutureData
1018 {
1019 self.queue_media_element_task_to_fire_event(atom!("canplay"));
1022
1023 if !self.Paused() {
1026 self.notify_about_playing();
1027 }
1028 }
1029
1030 if ready_state == ReadyState::HaveEnoughData {
1032 self.queue_media_element_task_to_fire_event(atom!("canplaythrough"));
1035
1036 if self.eligible_for_autoplay() {
1039 self.paused.set(false);
1041
1042 if self.show_poster.get() {
1045 self.show_poster.set(false);
1046 self.time_marches_on();
1047 }
1048
1049 self.queue_media_element_task_to_fire_event(atom!("play"));
1052
1053 self.notify_about_playing();
1055 }
1056 }
1057
1058 self.update_media_state();
1059 }
1060
1061 fn invoke_resource_selection_algorithm(&self, cx: &mut js::context::JSContext) {
1063 self.network_state.set(NetworkState::NoSource);
1065
1066 self.show_poster.set(true);
1068
1069 self.delay_load_event(true, cx);
1072
1073 let task = MediaElementMicrotask::ResourceSelection {
1080 elem: DomRoot::from_ref(self),
1081 generation_id: self.generation_id.get(),
1082 base_url: self.owner_document().base_url(),
1083 };
1084
1085 ScriptThread::await_stable_state(Microtask::MediaElement(task));
1090 }
1091
1092 fn resource_selection_algorithm_sync(
1094 &self,
1095 base_url: ServoUrl,
1096 cx: &mut js::context::JSContext,
1097 ) {
1098 enum Mode {
1105 Object,
1106 Attribute(String),
1107 Children(DomRoot<HTMLSourceElement>),
1108 }
1109
1110 let mode = if self.src_object.borrow().is_some() {
1112 Mode::Object
1114 } else if let Some(attribute) = self.upcast::<Element>().get_attribute(&local_name!("src"))
1115 {
1116 Mode::Attribute((**attribute.value()).to_owned())
1119 } else if let Some(source) = self
1120 .upcast::<Node>()
1121 .children_unrooted(cx.no_gc())
1122 .find_map(UnrootedDom::downcast::<HTMLSourceElement>)
1123 {
1124 Mode::Children(source.as_rooted())
1128 } else {
1129 self.load_state.set(LoadState::NotLoaded);
1132
1133 self.network_state.set(NetworkState::Empty);
1135
1136 self.delay_load_event(false, cx);
1139
1140 return;
1142 };
1143
1144 self.network_state.set(NetworkState::Loading);
1146
1147 self.queue_media_element_task_to_fire_event(atom!("loadstart"));
1150
1151 match mode {
1153 Mode::Object => {
1154 self.load_from_src_object();
1156 },
1157 Mode::Attribute(src) => {
1158 self.load_from_src_attribute(base_url, &src);
1160 },
1161 Mode::Children(source) => {
1162 self.load_from_source_child(&source);
1164 },
1165 }
1166 }
1167
1168 fn load_from_src_object(&self) {
1170 self.load_state.set(LoadState::LoadingFromSrcObject);
1171
1172 "".clone_into(&mut self.current_src.borrow_mut());
1174
1175 self.resource_fetch_algorithm(Resource::Object);
1181 }
1182
1183 fn load_from_src_attribute(&self, base_url: ServoUrl, src: &str) {
1185 self.load_state.set(LoadState::LoadingFromSrcAttribute);
1186
1187 if src.is_empty() {
1190 self.queue_dedicated_media_source_failure_steps();
1191 return;
1192 }
1193
1194 let Ok(url_record) = base_url.join(src) else {
1198 self.queue_dedicated_media_source_failure_steps();
1199 return;
1200 };
1201
1202 *self.current_src.borrow_mut() = url_record.as_str().into();
1205
1206 self.resource_fetch_algorithm(Resource::Url(url_record));
1212 }
1213
1214 fn load_from_source_child(&self, source: &HTMLSourceElement) {
1216 self.load_state.set(LoadState::LoadingFromSourceChild);
1217
1218 *self.source_children_pointer.borrow_mut() =
1225 Some(SourceChildrenPointer::new(DomRoot::from_ref(source), false));
1226
1227 let element = source.upcast::<Element>();
1228
1229 let Some(src) = element
1233 .get_attribute(&local_name!("src"))
1234 .filter(|attribute| !attribute.value().is_empty())
1235 else {
1236 self.load_from_source_child_failure_steps(source);
1237 return;
1238 };
1239
1240 if let Some(media) = element.get_attribute(&local_name!("media")) &&
1244 !MediaList::matches_environment(&element.owner_document(), &media.value())
1245 {
1246 self.load_from_source_child_failure_steps(source);
1247 return;
1248 }
1249
1250 let Ok(url_record) = source.owner_document().base_url().join(&src.value()) else {
1254 self.load_from_source_child_failure_steps(source);
1257 return;
1258 };
1259
1260 if let Some(type_) = element.get_attribute(&local_name!("type")) &&
1265 ServoMedia::get().can_play_type(&type_.value()) == SupportsMediaType::No
1266 {
1267 self.load_from_source_child_failure_steps(source);
1268 return;
1269 }
1270
1271 self.reset_media_player();
1273
1274 self.current_source_child.set(Some(source));
1275
1276 *self.current_src.borrow_mut() = url_record.as_str().into();
1279
1280 self.resource_fetch_algorithm(Resource::Url(url_record));
1285 }
1286
1287 fn load_from_source_child_failure_steps(&self, source: &HTMLSourceElement) {
1289 let trusted_this = Trusted::new(self);
1292 let trusted_source = Trusted::new(source);
1293 let generation_id = self.generation_id.get();
1294
1295 self.owner_global()
1296 .task_manager()
1297 .media_element_task_source()
1298 .queue(task!(queue_error_event: move |cx| {
1299 let this = trusted_this.root();
1300 if generation_id != this.generation_id.get() {
1301 return;
1302 }
1303
1304 let source = trusted_source.root();
1305 source.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1306 }));
1307
1308 let task = MediaElementMicrotask::SelectNextSourceChild {
1310 elem: DomRoot::from_ref(self),
1311 generation_id: self.generation_id.get(),
1312 };
1313
1314 ScriptThread::await_stable_state(Microtask::MediaElement(task));
1315 }
1316
1317 fn select_next_source_child(&self, can_gc: CanGc) {
1319 self.AudioTracks(can_gc).clear();
1321 self.VideoTracks(can_gc).clear();
1322
1323 let mut source_candidate = None;
1325
1326 if let Some(ref source_children_pointer) = *self.source_children_pointer.borrow() {
1334 if source_children_pointer.inclusive {
1338 for next_sibling in source_children_pointer
1339 .source_before_pointer
1340 .upcast::<Node>()
1341 .inclusively_following_siblings()
1342 {
1343 if let Some(next_source) = DomRoot::downcast::<HTMLSourceElement>(next_sibling)
1344 {
1345 source_candidate = Some(next_source);
1346 break;
1347 }
1348 }
1349 } else {
1350 for next_sibling in source_children_pointer
1351 .source_before_pointer
1352 .upcast::<Node>()
1353 .following_siblings()
1354 {
1355 if let Some(next_source) = DomRoot::downcast::<HTMLSourceElement>(next_sibling)
1356 {
1357 source_candidate = Some(next_source);
1358 break;
1359 }
1360 }
1361 };
1362 }
1363
1364 if let Some(source_candidate) = source_candidate {
1367 self.load_from_source_child(&source_candidate);
1368 return;
1369 }
1370
1371 self.load_state.set(LoadState::WaitingForSource);
1372
1373 *self.source_children_pointer.borrow_mut() = None;
1374
1375 self.network_state.set(NetworkState::NoSource);
1378
1379 self.show_poster.set(true);
1381
1382 let this = Trusted::new(self);
1385 let generation_id = self.generation_id.get();
1386
1387 self.owner_global()
1388 .task_manager()
1389 .media_element_task_source()
1390 .queue(task!(queue_delay_load_event: move |cx| {
1391 let this = this.root();
1392 if generation_id != this.generation_id.get() {
1393 return;
1394 }
1395
1396 this.delay_load_event(false, cx);
1397 }));
1398
1399 }
1402
1403 fn resource_selection_algorithm_failure_steps(&self) {
1405 match self.load_state.get() {
1406 LoadState::LoadingFromSrcObject => {
1407 self.queue_dedicated_media_source_failure_steps();
1412 },
1413 LoadState::LoadingFromSrcAttribute => {
1414 self.queue_dedicated_media_source_failure_steps();
1419 },
1420 LoadState::LoadingFromSourceChild => {
1421 if let Some(source) = self.current_source_child.take() {
1424 self.load_from_source_child_failure_steps(&source);
1425 }
1426 },
1427 _ => {},
1428 }
1429 }
1430
1431 fn fetch_request(&self, offset: Option<u64>, seek_lock: Option<SeekLock>) {
1432 if self.resource_url.borrow().is_none() && self.blob_url.borrow().is_none() {
1433 error!("Missing request url");
1434 if let Some(seek_lock) = seek_lock {
1435 seek_lock.unlock(false);
1436 }
1437 self.resource_selection_algorithm_failure_steps();
1438 return;
1439 }
1440
1441 let document = self.owner_document();
1442 let destination = match self.media_type_id() {
1443 HTMLMediaElementTypeId::HTMLAudioElement => Destination::Audio,
1444 HTMLMediaElementTypeId::HTMLVideoElement => Destination::Video,
1445 };
1446 let mut headers = HeaderMap::new();
1447 headers.insert(
1449 header::RANGE,
1450 HeaderValue::from_str(&format!("bytes={}-", offset.unwrap_or(0))).unwrap(),
1451 );
1452 let url = match self.resource_url.borrow().as_ref() {
1453 Some(url) => url.clone(),
1454 None => self.blob_url.borrow().as_ref().unwrap().clone(),
1455 };
1456
1457 let cors_setting = cors_setting_for_element(self.upcast());
1458 let global = self.global();
1459 let request = create_a_potential_cors_request(
1460 Some(document.webview_id()),
1461 url.clone(),
1462 destination,
1463 cors_setting,
1464 None,
1465 global.get_referrer(),
1466 )
1467 .with_global_scope(&global)
1468 .headers(headers)
1469 .referrer_policy(document.get_referrer_policy());
1470
1471 let mut current_fetch_context = self.current_fetch_context.borrow_mut();
1472 if let Some(ref mut current_fetch_context) = *current_fetch_context {
1473 current_fetch_context.cancel(CancelReason::Abort);
1474 }
1475
1476 *current_fetch_context = Some(HTMLMediaElementFetchContext::new(
1477 request.id,
1478 global.core_resource_thread(),
1479 ));
1480 let listener =
1481 HTMLMediaElementFetchListener::new(self, request.id, url, offset.unwrap_or(0));
1482
1483 self.owner_document().fetch_background(request, listener);
1484
1485 if let Some(seek_lock) = seek_lock {
1490 seek_lock.unlock(true);
1491 }
1492 }
1493
1494 fn eligible_for_autoplay(&self) -> bool {
1496 self.autoplaying.get() &&
1498
1499 self.Paused() &&
1501
1502 self.Autoplay() &&
1504
1505 {
1508 let document = self.owner_document();
1509
1510 !document.has_active_sandboxing_flag(
1511 SandboxingFlagSet::SANDBOXED_AUTOMATIC_FEATURES_BROWSING_CONTEXT_FLAG,
1512 )
1513 }
1514
1515 }
1518
1519 fn resource_fetch_algorithm(&self, resource: Resource) {
1521 if let Err(e) = self.create_media_player(&resource) {
1522 error!("Create media player error {:?}", e);
1523 self.resource_selection_algorithm_failure_steps();
1524 return;
1525 }
1526
1527 match resource {
1536 Resource::Url(url) => {
1537 if self.Preload() == "none" && !self.autoplaying.get() {
1542 self.network_state.set(NetworkState::Idle);
1544
1545 self.queue_media_element_task_to_fire_event(atom!("suspend"));
1548
1549 let this = Trusted::new(self);
1553 let generation_id = self.generation_id.get();
1554
1555 self.owner_global()
1556 .task_manager()
1557 .media_element_task_source()
1558 .queue(task!(queue_delay_load_event: move |cx| {
1559 let this = this.root();
1560 if generation_id != this.generation_id.get() {
1561 return;
1562 }
1563
1564 this.delay_load_event(false, cx);
1565 }));
1566
1567 return;
1576 }
1577
1578 *self.resource_url.borrow_mut() = Some(url);
1579
1580 self.fetch_request(None, None);
1582 },
1583 Resource::Object => {
1584 if let Some(ref src_object) = *self.src_object.borrow() {
1585 match src_object {
1586 SrcObject::Blob(blob) => {
1587 let blob_url = URL::CreateObjectURL(&self.global(), blob);
1588 *self.blob_url.borrow_mut() =
1589 Some(ServoUrl::parse(&blob_url.str()).expect("infallible"));
1590 self.fetch_request(None, None);
1591 },
1592 SrcObject::MediaStream(stream) => {
1593 let tracks = &*stream.get_tracks();
1594 for (pos, track) in tracks.iter().enumerate() {
1595 if self
1596 .player
1597 .borrow()
1598 .as_ref()
1599 .unwrap()
1600 .lock()
1601 .unwrap()
1602 .set_stream(&track.id(), pos == tracks.len() - 1)
1603 .is_err()
1604 {
1605 self.resource_selection_algorithm_failure_steps();
1606 }
1607 }
1608 },
1609 }
1610 }
1611 },
1612 }
1613 }
1614
1615 fn queue_dedicated_media_source_failure_steps(&self) {
1619 let this = Trusted::new(self);
1620 let generation_id = self.generation_id.get();
1621 self.take_pending_play_promises(Err(Error::NotSupported(None)));
1622 self.owner_global()
1623 .task_manager()
1624 .media_element_task_source()
1625 .queue(task!(dedicated_media_source_failure_steps: move |cx| {
1626 let this = this.root();
1627 if generation_id != this.generation_id.get() {
1628 return;
1629 }
1630
1631 this.fulfill_in_flight_play_promises(|| {
1632 this.error.set(Some(&*MediaError::new(
1635 &this.owner_window(),
1636 MEDIA_ERR_SRC_NOT_SUPPORTED, CanGc::from_cx(cx))));
1637
1638 this.AudioTracks(CanGc::from_cx(cx)).clear();
1640 this.VideoTracks(CanGc::from_cx(cx)).clear();
1641
1642 this.network_state.set(NetworkState::NoSource);
1645
1646 this.show_poster.set(true);
1648
1649 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1651
1652 if let Some(ref player) = *this.player.borrow()
1653 && let Err(error) = player.lock().unwrap().stop() {
1654 error!("Could not stop player: {error:?}");
1655 }
1656
1657 });
1661
1662 this.delay_load_event(false, cx);
1665 }));
1666 }
1667
1668 fn in_error_state(&self) -> bool {
1669 self.error.get().is_some()
1670 }
1671
1672 fn is_potentially_playing(&self) -> bool {
1674 !self.paused.get() &&
1675 !self.ended_playback(LoopCondition::Included) &&
1676 self.error.get().is_none() &&
1677 !self.is_blocked_media_element()
1678 }
1679
1680 fn is_blocked_media_element(&self) -> bool {
1682 self.ready_state.get() <= ReadyState::HaveCurrentData ||
1683 self.is_paused_for_user_interaction() ||
1684 self.is_paused_for_in_band_content()
1685 }
1686
1687 fn is_paused_for_user_interaction(&self) -> bool {
1689 false
1692 }
1693
1694 fn is_paused_for_in_band_content(&self) -> bool {
1696 false
1699 }
1700
1701 fn media_element_load_algorithm(&self, cx: &mut js::context::JSContext) {
1703 self.fired_loadeddata_event.set(false);
1706
1707 self.generation_id.set(self.generation_id.get() + 1);
1712
1713 self.load_state.set(LoadState::NotLoaded);
1714 *self.source_children_pointer.borrow_mut() = None;
1715 self.current_source_child.set(None);
1716
1717 while !self.in_flight_play_promises_queue.borrow().is_empty() {
1724 self.fulfill_in_flight_play_promises(|| ());
1725 }
1726
1727 let network_state = self.network_state.get();
1732
1733 if network_state == NetworkState::Loading || network_state == NetworkState::Idle {
1737 self.queue_media_element_task_to_fire_event(atom!("abort"));
1738 }
1739
1740 self.reset_media_player();
1742
1743 if network_state != NetworkState::Empty {
1745 self.queue_media_element_task_to_fire_event(atom!("emptied"));
1748
1749 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1752 current_fetch_context.cancel(CancelReason::Abort);
1753 }
1754
1755 self.AudioTracks(CanGc::from_cx(cx)).clear();
1760 self.VideoTracks(CanGc::from_cx(cx)).clear();
1761
1762 if self.ready_state.get() != ReadyState::HaveNothing {
1764 self.change_ready_state(ReadyState::HaveNothing);
1765 }
1766
1767 if !self.Paused() {
1769 self.paused.set(true);
1771
1772 self.take_pending_play_promises(Err(Error::Abort(None)));
1775 self.fulfill_in_flight_play_promises(|| ());
1776 }
1777
1778 self.seeking.set(false);
1780
1781 self.current_seek_position.set(f64::NAN);
1782
1783 self.current_playback_position.set(0.);
1788 if self.official_playback_position.get() != 0. {
1789 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
1790 }
1791 self.official_playback_position.set(0.);
1792
1793 self.duration.set(f64::NAN);
1797 }
1798
1799 self.playback_rate.set(self.default_playback_rate.get());
1801
1802 self.error.set(None);
1804 self.autoplaying.set(true);
1805
1806 self.invoke_resource_selection_algorithm(cx);
1808
1809 }
1811
1812 fn queue_media_element_task_to_fire_event(&self, name: Atom) {
1815 let this = Trusted::new(self);
1816 let generation_id = self.generation_id.get();
1817
1818 self.owner_global()
1819 .task_manager()
1820 .media_element_task_source()
1821 .queue(task!(queue_event: move |cx| {
1822 let this = this.root();
1823 if generation_id != this.generation_id.get() {
1824 return;
1825 }
1826
1827 this.upcast::<EventTarget>().fire_event(cx, name);
1828 }));
1829 }
1830
1831 fn push_pending_play_promise(&self, promise: &Rc<Promise>) {
1833 self.pending_play_promises
1834 .borrow_mut()
1835 .push(promise.clone());
1836 }
1837
1838 fn take_pending_play_promises(&self, result: ErrorResult) {
1849 let pending_play_promises = std::mem::take(&mut *self.pending_play_promises.borrow_mut());
1850 self.in_flight_play_promises_queue
1851 .borrow_mut()
1852 .push_back((pending_play_promises.into(), result));
1853 }
1854
1855 fn fulfill_in_flight_play_promises<F>(&self, f: F)
1864 where
1865 F: FnOnce(),
1866 {
1867 let (promises, result) = self
1868 .in_flight_play_promises_queue
1869 .borrow_mut()
1870 .pop_front()
1871 .expect("there should be at least one list of in flight play promises");
1872 f();
1873 for promise in &*promises {
1874 match result {
1875 Ok(ref value) => promise.resolve_native(value, CanGc::deprecated_note()),
1876 Err(ref error) => promise.reject_error(error.clone(), CanGc::deprecated_note()),
1877 }
1878 }
1879 }
1880
1881 pub(crate) fn handle_source_child_insertion(
1882 &self,
1883 source: &HTMLSourceElement,
1884 cx: &mut js::context::JSContext,
1885 ) {
1886 if self.upcast::<Element>().has_attribute(&local_name!("src")) {
1890 return;
1891 }
1892
1893 if self.network_state.get() == NetworkState::Empty {
1894 self.invoke_resource_selection_algorithm(cx);
1895 return;
1896 }
1897
1898 if self.load_state.get() != LoadState::WaitingForSource {
1902 return;
1903 }
1904
1905 self.load_state.set(LoadState::LoadingFromSourceChild);
1906
1907 *self.source_children_pointer.borrow_mut() =
1908 Some(SourceChildrenPointer::new(DomRoot::from_ref(source), true));
1909
1910 let task = MediaElementMicrotask::SelectNextSourceChildAfterWait {
1912 elem: DomRoot::from_ref(self),
1913 generation_id: self.generation_id.get(),
1914 };
1915
1916 ScriptThread::await_stable_state(Microtask::MediaElement(task));
1917 }
1918
1919 fn select_next_source_child_after_wait(&self, cx: &mut js::context::JSContext) {
1921 self.delay_load_event(true, cx);
1924
1925 self.network_state.set(NetworkState::Loading);
1927
1928 self.select_next_source_child(CanGc::from_cx(cx));
1930 }
1931
1932 fn media_data_processing_failure_steps(&self) {
1937 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1939 current_fetch_context.cancel(CancelReason::Error);
1940 }
1941
1942 self.resource_selection_algorithm_failure_steps();
1944 }
1945
1946 fn media_data_processing_fatal_steps(&self, error: u16, cx: &mut js::context::JSContext) {
1950 *self.source_children_pointer.borrow_mut() = None;
1951 self.current_source_child.set(None);
1952
1953 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1955 current_fetch_context.cancel(CancelReason::Error);
1956 }
1957
1958 self.error.set(Some(&*MediaError::new(
1961 &self.owner_window(),
1962 error,
1963 CanGc::from_cx(cx),
1964 )));
1965
1966 self.network_state.set(NetworkState::Idle);
1968
1969 self.delay_load_event(false, cx);
1972
1973 self.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1975
1976 }
1978
1979 fn seek(&self, time: f64, _approximate_for_speed: bool) {
1981 self.show_poster.set(false);
1983
1984 if self.ready_state.get() == ReadyState::HaveNothing {
1986 return;
1987 }
1988
1989 self.current_seek_position.set(f64::NAN);
1993
1994 self.seeking.set(true);
1996
1997 let time = f64::min(time, self.Duration());
2003
2004 let time = f64::max(time, self.earliest_possible_position());
2007
2008 let seekable = self.seekable();
2014
2015 if seekable.is_empty() {
2016 self.seeking.set(false);
2017 return;
2018 }
2019
2020 let mut nearest_seekable_position = 0.0;
2021 let mut in_seekable_range = false;
2022 let mut nearest_seekable_distance = f64::MAX;
2023 for i in 0..seekable.len() {
2024 let start = seekable.start(i).unwrap().abs();
2025 let end = seekable.end(i).unwrap().abs();
2026 if time >= start && time <= end {
2027 nearest_seekable_position = time;
2028 in_seekable_range = true;
2029 break;
2030 } else if time < start {
2031 let distance = start - time;
2032 if distance < nearest_seekable_distance {
2033 nearest_seekable_distance = distance;
2034 nearest_seekable_position = start;
2035 }
2036 } else {
2037 let distance = time - end;
2038 if distance < nearest_seekable_distance {
2039 nearest_seekable_distance = distance;
2040 nearest_seekable_position = end;
2041 }
2042 }
2043 }
2044 let time = if in_seekable_range {
2045 time
2046 } else {
2047 nearest_seekable_position
2048 };
2049
2050 self.queue_media_element_task_to_fire_event(atom!("seeking"));
2061
2062 self.current_playback_position.set(time);
2064
2065 if let Some(ref player) = *self.player.borrow() &&
2066 let Err(error) = player.lock().unwrap().seek(time)
2067 {
2068 error!("Could not seek player: {error:?}");
2069 }
2070
2071 self.current_seek_position.set(time);
2072
2073 }
2079
2080 fn seek_end(&self) {
2082 self.official_playback_position
2085 .set(self.current_playback_position.get());
2086
2087 self.seeking.set(false);
2089
2090 self.current_seek_position.set(f64::NAN);
2091
2092 self.time_marches_on();
2094
2095 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
2098
2099 self.queue_media_element_task_to_fire_event(atom!("seeked"));
2102 }
2103
2104 pub(crate) fn set_poster_frame(&self, image: Option<Arc<RasterImage>>) {
2106 if pref!(media_testing_enabled) && image.is_some() {
2107 self.queue_media_element_task_to_fire_event(atom!("postershown"));
2108 }
2109
2110 self.video_renderer.lock().unwrap().set_poster_frame(image);
2111
2112 self.upcast::<Node>().dirty(NodeDamage::Other);
2113 }
2114
2115 fn player_id(&self) -> Option<usize> {
2116 self.player
2117 .borrow()
2118 .as_ref()
2119 .map(|player| player.lock().unwrap().get_id())
2120 }
2121
2122 fn create_media_player(&self, resource: &Resource) -> Result<(), ()> {
2123 let stream_type = match *resource {
2124 Resource::Object => {
2125 if let Some(ref src_object) = *self.src_object.borrow() {
2126 match src_object {
2127 SrcObject::MediaStream(_) => StreamType::Stream,
2128 _ => StreamType::Seekable,
2129 }
2130 } else {
2131 return Err(());
2132 }
2133 },
2134 _ => StreamType::Seekable,
2135 };
2136
2137 let window = self.owner_window();
2138 let (action_sender, action_receiver) = ipc::channel::<PlayerEvent>().unwrap();
2139 let video_renderer: Option<Arc<Mutex<dyn VideoFrameRenderer>>> = match self.media_type_id()
2140 {
2141 HTMLMediaElementTypeId::HTMLAudioElement => None,
2142 HTMLMediaElementTypeId::HTMLVideoElement => Some(self.video_renderer.clone()),
2143 };
2144
2145 let audio_renderer = self.audio_renderer.borrow().as_ref().cloned();
2146
2147 let pipeline_id = window.pipeline_id();
2148 let client_context_id =
2149 ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
2150 let player = ServoMedia::get().create_player(
2151 &client_context_id,
2152 stream_type,
2153 action_sender,
2154 video_renderer,
2155 audio_renderer,
2156 Box::new(window.get_player_context()),
2157 );
2158 let player_id = {
2159 let player_guard = player.lock().unwrap();
2160
2161 if let Err(error) = player_guard.set_mute(self.muted.get()) {
2162 warn!("Could not set mute state: {error:?}");
2163 }
2164
2165 player_guard.get_id()
2166 };
2167
2168 *self.player.borrow_mut() = Some(player);
2169
2170 let event_handler = Arc::new(Mutex::new(HTMLMediaElementEventHandler::new(self)));
2171 let weak_event_handler = Arc::downgrade(&event_handler);
2172 *self.event_handler.borrow_mut() = Some(event_handler);
2173
2174 let task_source = self
2175 .owner_global()
2176 .task_manager()
2177 .media_element_task_source()
2178 .to_sendable();
2179 ROUTER.add_typed_route(
2180 action_receiver,
2181 Box::new(move |message| {
2182 let event = message.unwrap();
2183 let weak_event_handler = weak_event_handler.clone();
2184
2185 task_source.queue(task!(handle_player_event: move |cx| {
2186 trace!("HTMLMediaElement event: {event:?}");
2187
2188 let Some(event_handler) = weak_event_handler.upgrade() else {
2189 return;
2190 };
2191
2192 event_handler.lock().unwrap().handle_player_event(player_id, event, cx);
2193 }));
2194 }),
2195 );
2196
2197 let task_source = self
2198 .owner_global()
2199 .task_manager()
2200 .media_element_task_source()
2201 .to_sendable();
2202 let weak_video_renderer = Arc::downgrade(&self.video_renderer);
2203
2204 self.video_renderer
2205 .lock()
2206 .unwrap()
2207 .setup(player_id, task_source, weak_video_renderer);
2208
2209 Ok(())
2210 }
2211
2212 fn reset_media_player(&self) {
2213 if self.player.borrow().is_none() {
2214 return;
2215 }
2216
2217 if let Some(ref player) = *self.player.borrow() &&
2218 let Err(error) = player.lock().unwrap().stop()
2219 {
2220 error!("Could not stop player: {error:?}");
2221 }
2222
2223 *self.player.borrow_mut() = None;
2224 self.video_renderer.lock().unwrap().reset();
2225 *self.event_handler.borrow_mut() = None;
2226
2227 if let Some(video_element) = self.downcast::<HTMLVideoElement>() {
2228 video_element.set_natural_dimensions(None, None);
2229 }
2230 }
2231
2232 pub(crate) fn set_audio_track(&self, idx: usize, enabled: bool) {
2233 if let Some(ref player) = *self.player.borrow() &&
2234 let Err(error) = player.lock().unwrap().set_audio_track(idx as i32, enabled)
2235 {
2236 warn!("Could not set audio track {error:?}");
2237 }
2238 }
2239
2240 pub(crate) fn set_video_track(&self, idx: usize, enabled: bool) {
2241 if let Some(ref player) = *self.player.borrow() &&
2242 let Err(error) = player.lock().unwrap().set_video_track(idx as i32, enabled)
2243 {
2244 warn!("Could not set video track: {error:?}");
2245 }
2246 }
2247
2248 fn direction_of_playback(&self) -> PlaybackDirection {
2250 if self.playback_rate.get() >= 0. {
2253 PlaybackDirection::Forwards
2254 } else {
2255 PlaybackDirection::Backwards
2256 }
2257 }
2258
2259 fn ended_playback(&self, loop_condition: LoopCondition) -> bool {
2261 if self.ready_state.get() < ReadyState::HaveMetadata {
2265 return false;
2266 }
2267
2268 let playback_position = self.current_playback_position.get();
2269
2270 match self.direction_of_playback() {
2271 PlaybackDirection::Forwards => {
2275 playback_position >= self.Duration() &&
2276 (loop_condition == LoopCondition::Ignored || !self.Loop())
2277 },
2278 PlaybackDirection::Backwards => playback_position <= self.earliest_possible_position(),
2281 }
2282 }
2283
2284 fn end_of_playback_in_forwards_direction(&self) {
2286 if self.Loop() {
2292 self.seek(
2293 self.earliest_possible_position(),
2294 false,
2295 );
2296 return;
2297 }
2298
2299 let this = Trusted::new(self);
2304 let generation_id = self.generation_id.get();
2305
2306 self.owner_global()
2307 .task_manager()
2308 .media_element_task_source()
2309 .queue(task!(reaches_the_end_steps: move |cx| {
2310 let this = this.root();
2311 if generation_id != this.generation_id.get() {
2312 return;
2313 }
2314
2315 this.upcast::<EventTarget>().fire_event(cx, atom!("timeupdate"));
2317
2318 if this.ended_playback(LoopCondition::Included) &&
2321 this.direction_of_playback() == PlaybackDirection::Forwards &&
2322 !this.Paused() {
2323 this.paused.set(true);
2325
2326 this.upcast::<EventTarget>().fire_event(cx, atom!("pause"));
2328
2329 this.take_pending_play_promises(Err(Error::Abort(None)));
2332 this.fulfill_in_flight_play_promises(|| ());
2333 }
2334
2335 this.upcast::<EventTarget>().fire_event(cx, atom!("ended"));
2337 }));
2338
2339 self.change_ready_state(ReadyState::HaveCurrentData);
2341 }
2342
2343 fn end_of_playback_in_backwards_direction(&self) {
2345 if self.current_playback_position.get() <= self.earliest_possible_position() {
2350 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
2351 }
2352 }
2353
2354 fn playback_end(&self) {
2355 if self.seeking.get() {
2357 return;
2358 }
2359
2360 match self.direction_of_playback() {
2361 PlaybackDirection::Forwards => self.end_of_playback_in_forwards_direction(),
2362 PlaybackDirection::Backwards => self.end_of_playback_in_backwards_direction(),
2363 }
2364 }
2365
2366 fn playback_error(&self, error: &str, cx: &mut js::context::JSContext) {
2367 error!("Player error: {:?}", error);
2368
2369 if self.in_error_state() {
2373 return;
2374 }
2375
2376 if self.ready_state.get() == ReadyState::HaveNothing {
2378 self.media_data_processing_failure_steps();
2381 } else {
2382 self.media_data_processing_fatal_steps(MEDIA_ERR_DECODE, cx);
2384 }
2385 }
2386
2387 fn playback_metadata_updated(
2388 &self,
2389 metadata: &servo_media::player::metadata::Metadata,
2390 can_gc: CanGc,
2391 ) {
2392 if self.ready_state.get() != ReadyState::HaveNothing {
2395 return;
2396 }
2397
2398 for (i, _track) in metadata.audio_tracks.iter().enumerate() {
2401 let audio_track_list = self.AudioTracks(can_gc);
2402
2403 let kind = match i {
2405 0 => DOMString::from("main"),
2406 _ => DOMString::new(),
2407 };
2408
2409 let audio_track = AudioTrack::new(
2410 self.global().as_window(),
2411 DOMString::new(),
2412 kind,
2413 DOMString::new(),
2414 DOMString::new(),
2415 Some(&*audio_track_list),
2416 can_gc,
2417 );
2418
2419 audio_track_list.add(&audio_track);
2422
2423 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
2430 let fragment = MediaFragmentParser::from(servo_url);
2431 if let Some(id) = fragment.id() &&
2432 audio_track.id() == id
2433 {
2434 audio_track_list.set_enabled(audio_track_list.len() - 1, true);
2435 }
2436
2437 if fragment.tracks().contains(&audio_track.kind().into()) {
2438 audio_track_list.set_enabled(audio_track_list.len() - 1, true);
2439 }
2440 }
2441
2442 if audio_track_list.enabled_index().is_none() {
2447 audio_track_list.set_enabled(audio_track_list.len() - 1, true);
2448 }
2449
2450 let event = TrackEvent::new(
2453 self.global().as_window(),
2454 atom!("addtrack"),
2455 false,
2456 false,
2457 &Some(VideoTrackOrAudioTrackOrTextTrack::AudioTrack(audio_track)),
2458 can_gc,
2459 );
2460
2461 event
2462 .upcast::<Event>()
2463 .fire(audio_track_list.upcast::<EventTarget>(), can_gc);
2464 }
2465
2466 for (i, _track) in metadata.video_tracks.iter().enumerate() {
2468 let video_track_list = self.VideoTracks(can_gc);
2469
2470 let kind = match i {
2472 0 => DOMString::from("main"),
2473 _ => DOMString::new(),
2474 };
2475
2476 let video_track = VideoTrack::new(
2477 self.global().as_window(),
2478 DOMString::new(),
2479 kind,
2480 DOMString::new(),
2481 DOMString::new(),
2482 Some(&*video_track_list),
2483 can_gc,
2484 );
2485
2486 video_track_list.add(&video_track);
2489
2490 if let Some(track) = video_track_list.item(0) &&
2497 let Some(servo_url) = self.resource_url.borrow().as_ref()
2498 {
2499 let fragment = MediaFragmentParser::from(servo_url);
2500 if let Some(id) = fragment.id() {
2501 if track.id() == id {
2502 video_track_list.set_selected(0, true);
2503 }
2504 } else if fragment.tracks().contains(&track.kind().into()) {
2505 video_track_list.set_selected(0, true);
2506 }
2507 }
2508
2509 if video_track_list.selected_index().is_none() {
2515 video_track_list.set_selected(video_track_list.len() - 1, true);
2516 }
2517
2518 let event = TrackEvent::new(
2521 self.global().as_window(),
2522 atom!("addtrack"),
2523 false,
2524 false,
2525 &Some(VideoTrackOrAudioTrackOrTextTrack::VideoTrack(video_track)),
2526 can_gc,
2527 );
2528
2529 event
2530 .upcast::<Event>()
2531 .fire(video_track_list.upcast::<EventTarget>(), can_gc);
2532 }
2533
2534 let earliest_possible_position = self.earliest_possible_position();
2547 self.current_playback_position
2548 .set(earliest_possible_position);
2549 self.official_playback_position
2550 .set(earliest_possible_position);
2551
2552 self.duration.set(
2558 metadata
2559 .duration
2560 .map_or(f64::INFINITY, |duration| duration.as_secs_f64()),
2561 );
2562 self.queue_media_element_task_to_fire_event(atom!("durationchange"));
2563
2564 if let Some(video_element) = self.downcast::<HTMLVideoElement>() {
2568 video_element.set_natural_dimensions(Some(metadata.width), Some(metadata.height));
2569 self.queue_media_element_task_to_fire_event(atom!("resize"));
2570 }
2571
2572 self.change_ready_state(ReadyState::HaveMetadata);
2574
2575 let mut jumped = false;
2577
2578 if self.default_playback_start_position.get() > 0. {
2581 self.seek(
2582 self.default_playback_start_position.get(),
2583 false,
2584 );
2585 jumped = true;
2586 }
2587
2588 self.default_playback_start_position.set(0.);
2590
2591 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
2596 let fragment = MediaFragmentParser::from(servo_url);
2597 if let Some(initial_playback_position) = fragment.start() &&
2598 initial_playback_position > 0. &&
2599 initial_playback_position < self.duration.get() &&
2600 !jumped
2601 {
2602 self.seek(
2603 initial_playback_position,
2604 false,
2605 )
2606 }
2607 }
2608
2609 let global = self.global();
2616 let window = global.as_window();
2617
2618 window.Navigator().MediaSession().update_title(
2620 metadata
2621 .title
2622 .clone()
2623 .unwrap_or(window.get_url().into_string()),
2624 );
2625 }
2626
2627 fn playback_duration_changed(&self, duration: Option<Duration>) {
2628 let duration = duration.map_or(f64::INFINITY, |duration| duration.as_secs_f64());
2629
2630 if self.duration.get() == duration {
2631 return;
2632 }
2633
2634 self.duration.set(duration);
2635
2636 self.queue_media_element_task_to_fire_event(atom!("durationchange"));
2642
2643 if self.current_playback_position.get() > duration {
2647 self.seek(duration, false);
2648 }
2649 }
2650
2651 fn playback_video_frame_updated(&self) {
2652 let Some(video_element) = self.downcast::<HTMLVideoElement>() else {
2653 return;
2654 };
2655
2656 if self.ready_state.get() == ReadyState::HaveNothing {
2665 return;
2666 }
2667
2668 if let Some(frame) = self.video_renderer.lock().unwrap().current_frame {
2669 if video_element
2670 .set_natural_dimensions(Some(frame.width as u32), Some(frame.height as u32))
2671 {
2672 self.queue_media_element_task_to_fire_event(atom!("resize"));
2673 } else {
2674 self.upcast::<Node>().dirty(NodeDamage::Other);
2677 }
2678 }
2679 }
2680
2681 fn playback_need_data(&self) {
2682 if let Some(ref current_fetch_context) = *self.current_fetch_context.borrow() &&
2686 let Some(reason) = current_fetch_context.cancel_reason()
2687 {
2688 if *reason == CancelReason::Backoff {
2694 self.seek(
2695 self.current_playback_position.get(),
2696 false,
2697 );
2698 }
2699 return;
2700 }
2701
2702 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() &&
2703 let Err(e) = {
2704 let mut data_source = current_fetch_context.data_source().borrow_mut();
2705 data_source.set_locked(false);
2706 data_source.process_into_player_from_queue(self.player.borrow().as_ref().unwrap())
2707 }
2708 {
2709 if e == PlayerError::EnoughData {
2714 current_fetch_context.cancel(CancelReason::Backoff);
2715 }
2716 }
2717 }
2718
2719 fn playback_enough_data(&self) {
2720 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() &&
2725 current_fetch_context.is_seekable()
2726 {
2727 current_fetch_context.cancel(CancelReason::Backoff);
2728 }
2729 }
2730
2731 fn playback_position_changed(&self, position: f64) {
2732 if self.seeking.get() {
2734 return;
2735 }
2736
2737 let _ = self
2738 .played
2739 .borrow_mut()
2740 .add(self.current_playback_position.get(), position);
2741 self.current_playback_position.set(position);
2742 self.official_playback_position.set(position);
2743 self.time_marches_on();
2744
2745 let media_position_state =
2746 MediaPositionState::new(self.duration.get(), self.playback_rate.get(), position);
2747 debug!(
2748 "Sending media session event set position state {:?}",
2749 media_position_state
2750 );
2751 self.send_media_session_event(MediaSessionEvent::SetPositionState(media_position_state));
2752 }
2753
2754 fn playback_seek_done(&self, position: f64) {
2755 let delta = (position - self.current_seek_position.get()).abs();
2758 if !self.seeking.get() || delta > SEEK_POSITION_THRESHOLD {
2759 return;
2760 }
2761
2762 let task = MediaElementMicrotask::Seeked {
2765 elem: DomRoot::from_ref(self),
2766 generation_id: self.generation_id.get(),
2767 };
2768
2769 ScriptThread::await_stable_state(Microtask::MediaElement(task));
2770 }
2771
2772 fn playback_state_changed(&self, state: &PlaybackState) {
2773 let mut media_session_playback_state = MediaSessionPlaybackState::None_;
2774 match *state {
2775 PlaybackState::Paused => {
2776 media_session_playback_state = MediaSessionPlaybackState::Paused;
2777 if self.ready_state.get() == ReadyState::HaveMetadata {
2778 self.change_ready_state(ReadyState::HaveEnoughData);
2779 }
2780 },
2781 PlaybackState::Playing => {
2782 media_session_playback_state = MediaSessionPlaybackState::Playing;
2783 if self.ready_state.get() == ReadyState::HaveMetadata {
2784 self.change_ready_state(ReadyState::HaveEnoughData);
2785 }
2786 },
2787 PlaybackState::Buffering => {
2788 return;
2792 },
2793 _ => {},
2794 };
2795 debug!(
2796 "Sending media session event playback state changed to {:?}",
2797 media_session_playback_state
2798 );
2799 self.send_media_session_event(MediaSessionEvent::PlaybackStateChange(
2800 media_session_playback_state,
2801 ));
2802 }
2803
2804 fn seekable(&self) -> TimeRangesContainer {
2805 let mut seekable = TimeRangesContainer::default();
2806 if let Some(ref player) = *self.player.borrow() {
2807 let ranges = player.lock().unwrap().seekable();
2808 for range in ranges {
2809 let _ = seekable.add(range.start, range.end);
2810 }
2811 }
2812 seekable
2813 }
2814
2815 fn earliest_possible_position(&self) -> f64 {
2817 self.seekable()
2818 .start(0)
2819 .unwrap_or_else(|_| self.current_playback_position.get())
2820 }
2821
2822 fn render_controls(&self, cx: &mut JSContext) {
2823 if self.upcast::<Element>().is_shadow_host() {
2824 return;
2826 }
2827
2828 let shadow_root = self.upcast::<Element>().attach_ua_shadow_root(cx, false);
2831 let document = self.owner_document();
2832 let script = Element::create(
2833 cx,
2834 QualName::new(None, ns!(html), local_name!("script")),
2835 None,
2836 &document,
2837 ElementCreator::ScriptCreated,
2838 CustomElementCreationMode::Asynchronous,
2839 None,
2840 );
2841 let id = Uuid::new_v4().to_string();
2847 document.register_media_controls(&id, &shadow_root);
2848 let media_controls_script = MEDIA_CONTROL_JS.replace("@@@id@@@", &id);
2849 *self.media_controls_id.borrow_mut() = Some(id);
2850 script
2851 .upcast::<Node>()
2852 .set_text_content_for_element(cx, Some(DOMString::from(media_controls_script)));
2853 if let Err(e) = shadow_root
2854 .upcast::<Node>()
2855 .AppendChild(cx, script.upcast::<Node>())
2856 {
2857 warn!("Could not render media controls {:?}", e);
2858 return;
2859 }
2860
2861 let style = Element::create(
2862 cx,
2863 QualName::new(None, ns!(html), local_name!("style")),
2864 None,
2865 &document,
2866 ElementCreator::ScriptCreated,
2867 CustomElementCreationMode::Asynchronous,
2868 None,
2869 );
2870
2871 style
2872 .upcast::<Node>()
2873 .set_text_content_for_element(cx, Some(DOMString::from(MEDIA_CONTROL_CSS)));
2874
2875 if let Err(e) = shadow_root
2876 .upcast::<Node>()
2877 .AppendChild(cx, style.upcast::<Node>())
2878 {
2879 warn!("Could not render media controls {:?}", e);
2880 }
2881
2882 self.upcast::<Node>().dirty(NodeDamage::Other);
2883 }
2884
2885 fn remove_controls(&self) {
2886 if let Some(id) = self.media_controls_id.borrow_mut().take() {
2887 self.owner_document().unregister_media_controls(&id);
2888 }
2889 }
2890
2891 pub(crate) fn get_current_frame(&self) -> Option<VideoFrame> {
2893 self.video_renderer
2894 .lock()
2895 .unwrap()
2896 .current_frame_holder
2897 .as_ref()
2898 .map(|holder| holder.get_frame())
2899 }
2900
2901 pub(crate) fn get_current_frame_to_present(&self) -> Option<MediaFrame> {
2904 let (current_frame, poster_frame) = {
2905 let renderer = self.video_renderer.lock().unwrap();
2906 (renderer.current_frame, renderer.poster_frame)
2907 };
2908
2909 if (self.show_poster.get() || current_frame.is_none()) && poster_frame.is_some() {
2912 return poster_frame;
2913 }
2914
2915 current_frame
2916 }
2917
2918 pub(crate) fn set_audio_renderer(
2923 &self,
2924 audio_renderer: Option<Arc<Mutex<dyn AudioRenderer>>>,
2925 cx: &mut js::context::JSContext,
2926 ) {
2927 *self.audio_renderer.borrow_mut() = audio_renderer;
2928
2929 let had_player = {
2930 if let Some(ref player) = *self.player.borrow() {
2931 if let Err(error) = player.lock().unwrap().stop() {
2932 error!("Could not stop player: {error:?}");
2933 }
2934 true
2935 } else {
2936 false
2937 }
2938 };
2939
2940 if had_player {
2941 self.media_element_load_algorithm(cx);
2942 }
2943 }
2944
2945 fn send_media_session_event(&self, event: MediaSessionEvent) {
2946 let global = self.global();
2947 let media_session = global.as_window().Navigator().MediaSession();
2948
2949 media_session.register_media_instance(self);
2950
2951 media_session.send_event(event);
2952 }
2953
2954 pub(crate) fn origin_is_clean(&self) -> bool {
2956 if self.src_object.borrow().is_some() {
2958 return true;
2961 }
2962
2963 if self.resource_url.borrow().is_some() {
2965 if let Some(ref current_fetch_context) = *self.current_fetch_context.borrow() {
2969 return current_fetch_context.origin_is_clean();
2970 }
2971 }
2972
2973 true
2974 }
2975}
2976
2977impl HTMLMediaElementMethods<crate::DomTypeHolder> for HTMLMediaElement {
2978 fn NetworkState(&self) -> u16 {
2980 self.network_state.get() as u16
2981 }
2982
2983 fn ReadyState(&self) -> u16 {
2985 self.ready_state.get() as u16
2986 }
2987
2988 make_bool_getter!(Autoplay, "autoplay");
2990 make_bool_setter!(SetAutoplay, "autoplay");
2992
2993 make_bool_getter!(Loop, "loop");
2995 make_bool_setter!(SetLoop, "loop");
2997
2998 make_bool_getter!(DefaultMuted, "muted");
3000 make_bool_setter!(SetDefaultMuted, "muted");
3002
3003 make_bool_getter!(Controls, "controls");
3005 make_bool_setter!(SetControls, "controls");
3007
3008 make_url_getter!(Src, "src");
3010
3011 make_url_setter!(SetSrc, "src");
3013
3014 fn GetCrossOrigin(&self) -> Option<DOMString> {
3016 reflect_cross_origin_attribute(self.upcast::<Element>())
3017 }
3018 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
3020 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
3021 }
3022
3023 fn Muted(&self) -> bool {
3025 self.muted.get()
3026 }
3027
3028 fn SetMuted(&self, _cx: &mut JSContext, value: bool) {
3030 if self.muted.get() == value {
3031 return;
3032 }
3033
3034 self.muted.set(value);
3035
3036 if let Some(ref player) = *self.player.borrow() &&
3037 let Err(error) = player.lock().unwrap().set_mute(value)
3038 {
3039 warn!("Could not set mute state: {error:?}");
3040 }
3041
3042 self.queue_media_element_task_to_fire_event(atom!("volumechange"));
3045
3046 if !self.is_allowed_to_play() {
3049 self.internal_pause_steps();
3050 }
3051 }
3052
3053 fn GetSrcObject(&self) -> Option<MediaStreamOrBlob> {
3055 (*self.src_object.borrow())
3056 .as_ref()
3057 .map(|src_object| match src_object {
3058 SrcObject::Blob(blob) => MediaStreamOrBlob::Blob(DomRoot::from_ref(blob)),
3059 SrcObject::MediaStream(stream) => {
3060 MediaStreamOrBlob::MediaStream(DomRoot::from_ref(stream))
3061 },
3062 })
3063 }
3064
3065 fn SetSrcObject(&self, cx: &mut js::context::JSContext, value: Option<MediaStreamOrBlob>) {
3067 *self.src_object.borrow_mut() = value.map(|value| value.into());
3068 self.media_element_load_algorithm(cx);
3069 }
3070
3071 make_enumerated_getter!(
3074 Preload,
3075 "preload",
3076 "none" | "metadata" | "auto",
3077 missing => "auto",
3078 invalid => "auto"
3079 );
3080
3081 make_setter!(SetPreload, "preload");
3083
3084 fn CurrentSrc(&self) -> USVString {
3086 USVString(self.current_src.borrow().clone())
3087 }
3088
3089 fn Load(&self, cx: &mut js::context::JSContext) {
3091 self.media_element_load_algorithm(cx);
3092 }
3093
3094 fn CanPlayType(&self, type_: DOMString) -> CanPlayTypeResult {
3096 match ServoMedia::get().can_play_type(&type_.str()) {
3097 SupportsMediaType::No => CanPlayTypeResult::_empty,
3098 SupportsMediaType::Maybe => CanPlayTypeResult::Maybe,
3099 SupportsMediaType::Probably => CanPlayTypeResult::Probably,
3100 }
3101 }
3102
3103 fn GetError(&self) -> Option<DomRoot<MediaError>> {
3105 self.error.get()
3106 }
3107
3108 fn Play(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
3110 let promise = Promise::new_in_realm(cx);
3111
3112 if self
3119 .error
3120 .get()
3121 .is_some_and(|e| e.Code() == MEDIA_ERR_SRC_NOT_SUPPORTED)
3122 {
3123 promise.reject_error(Error::NotSupported(None), CanGc::from_cx(cx));
3124 return promise;
3125 }
3126
3127 self.push_pending_play_promise(&promise);
3130
3131 self.internal_play_steps(cx);
3133
3134 promise
3136 }
3137
3138 fn Pause(&self, cx: &mut js::context::JSContext) {
3140 if self.network_state.get() == NetworkState::Empty {
3143 self.invoke_resource_selection_algorithm(cx);
3144 }
3145
3146 self.internal_pause_steps();
3148 }
3149
3150 fn Paused(&self) -> bool {
3152 self.paused.get()
3153 }
3154
3155 fn GetDefaultPlaybackRate(&self) -> Fallible<Finite<f64>> {
3157 Ok(Finite::wrap(self.default_playback_rate.get()))
3158 }
3159
3160 fn SetDefaultPlaybackRate(&self, _cx: &mut JSContext, value: Finite<f64>) -> ErrorResult {
3162 let min_allowed = -64.0;
3165 let max_allowed = 64.0;
3166 if *value < min_allowed || *value > max_allowed {
3167 return Err(Error::NotSupported(None));
3168 }
3169
3170 if self.default_playback_rate.get() == *value {
3171 return Ok(());
3172 }
3173
3174 self.default_playback_rate.set(*value);
3175
3176 self.queue_media_element_task_to_fire_event(atom!("ratechange"));
3179
3180 Ok(())
3181 }
3182
3183 fn GetPlaybackRate(&self) -> Fallible<Finite<f64>> {
3185 Ok(Finite::wrap(self.playback_rate.get()))
3186 }
3187
3188 fn SetPlaybackRate(&self, _cx: &mut JSContext, value: Finite<f64>) -> ErrorResult {
3190 let min_allowed = -64.0;
3195 let max_allowed = 64.0;
3196 if *value < min_allowed || *value > max_allowed {
3197 return Err(Error::NotSupported(None));
3198 }
3199
3200 if self.playback_rate.get() == *value {
3201 return Ok(());
3202 }
3203
3204 self.playback_rate.set(*value);
3207
3208 if self.is_potentially_playing() &&
3209 let Some(ref player) = *self.player.borrow() &&
3210 let Err(error) = player.lock().unwrap().set_playback_rate(*value)
3211 {
3212 warn!("Could not set the playback rate: {error:?}");
3213 }
3214
3215 self.queue_media_element_task_to_fire_event(atom!("ratechange"));
3218
3219 Ok(())
3220 }
3221
3222 fn Duration(&self) -> f64 {
3224 self.duration.get()
3225 }
3226
3227 fn CurrentTime(&self) -> Finite<f64> {
3229 Finite::wrap(if self.default_playback_start_position.get() != 0. {
3230 self.default_playback_start_position.get()
3231 } else if self.seeking.get() {
3232 self.current_seek_position.get()
3237 } else {
3238 self.official_playback_position.get()
3239 })
3240 }
3241
3242 fn SetCurrentTime(&self, _cx: &mut JSContext, time: Finite<f64>) {
3244 if self.ready_state.get() == ReadyState::HaveNothing {
3245 self.default_playback_start_position.set(*time);
3246 } else {
3247 self.official_playback_position.set(*time);
3248 self.seek(*time, false);
3249 }
3250 }
3251
3252 fn Seeking(&self) -> bool {
3254 self.seeking.get()
3255 }
3256
3257 fn Ended(&self) -> bool {
3259 self.ended_playback(LoopCondition::Included) &&
3260 self.direction_of_playback() == PlaybackDirection::Forwards
3261 }
3262
3263 fn FastSeek(&self, time: Finite<f64>) {
3265 self.seek(*time, true);
3266 }
3267
3268 fn Played(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
3270 TimeRanges::new(
3271 self.global().as_window(),
3272 self.played.borrow().clone(),
3273 can_gc,
3274 )
3275 }
3276
3277 fn Seekable(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
3279 TimeRanges::new(self.global().as_window(), self.seekable(), can_gc)
3280 }
3281
3282 fn Buffered(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
3284 let mut buffered = TimeRangesContainer::default();
3285 if let Some(ref player) = *self.player.borrow() {
3286 let ranges = player.lock().unwrap().buffered();
3287 for range in ranges {
3288 let _ = buffered.add(range.start, range.end);
3289 }
3290 }
3291 TimeRanges::new(self.global().as_window(), buffered, can_gc)
3292 }
3293
3294 fn AudioTracks(&self, can_gc: CanGc) -> DomRoot<AudioTrackList> {
3296 let window = self.owner_window();
3297 self.audio_tracks_list
3298 .or_init(|| AudioTrackList::new(&window, &[], Some(self), can_gc))
3299 }
3300
3301 fn VideoTracks(&self, can_gc: CanGc) -> DomRoot<VideoTrackList> {
3303 let window = self.owner_window();
3304 self.video_tracks_list
3305 .or_init(|| VideoTrackList::new(&window, &[], Some(self), can_gc))
3306 }
3307
3308 fn TextTracks(&self, can_gc: CanGc) -> DomRoot<TextTrackList> {
3310 let window = self.owner_window();
3311 self.text_tracks_list
3312 .or_init(|| TextTrackList::new(&window, &[], can_gc))
3313 }
3314
3315 fn AddTextTrack(
3317 &self,
3318 kind: TextTrackKind,
3319 label: DOMString,
3320 language: DOMString,
3321 can_gc: CanGc,
3322 ) -> DomRoot<TextTrack> {
3323 let window = self.owner_window();
3324 let track = TextTrack::new(
3327 &window,
3328 "".into(),
3329 kind,
3330 label,
3331 language,
3332 TextTrackMode::Hidden,
3333 None,
3334 can_gc,
3335 );
3336 self.TextTracks(can_gc).add(&track);
3338 DomRoot::from_ref(&track)
3340 }
3341
3342 fn GetVolume(&self) -> Fallible<Finite<f64>> {
3344 Ok(Finite::wrap(self.volume.get()))
3345 }
3346
3347 fn SetVolume(&self, _cx: &mut JSContext, value: Finite<f64>) -> ErrorResult {
3349 let minimum_volume = 0.0;
3352 let maximum_volume = 1.0;
3353 if *value < minimum_volume || *value > maximum_volume {
3354 return Err(Error::IndexSize(None));
3355 }
3356
3357 if self.volume.get() == *value {
3358 return Ok(());
3359 }
3360
3361 self.volume.set(*value);
3362
3363 if let Some(ref player) = *self.player.borrow() &&
3364 let Err(error) = player.lock().unwrap().set_volume(*value)
3365 {
3366 warn!("Could not set the volume: {error:?}");
3367 }
3368
3369 self.queue_media_element_task_to_fire_event(atom!("volumechange"));
3372
3373 if !self.is_allowed_to_play() {
3376 self.internal_pause_steps();
3377 }
3378
3379 Ok(())
3380 }
3381}
3382
3383impl VirtualMethods for HTMLMediaElement {
3384 fn super_type(&self) -> Option<&dyn VirtualMethods> {
3385 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
3386 }
3387
3388 fn attribute_mutated(
3389 &self,
3390 cx: &mut js::context::JSContext,
3391 attr: AttrRef<'_>,
3392 mutation: AttributeMutation,
3393 ) {
3394 self.super_type()
3395 .unwrap()
3396 .attribute_mutated(cx, attr, mutation);
3397
3398 match *attr.local_name() {
3399 local_name!("muted") => {
3400 if let AttributeMutation::Set(
3404 _,
3405 AttributeMutationReason::ByCloning | AttributeMutationReason::ByParser,
3406 ) = mutation
3407 {
3408 self.SetMuted(cx, true);
3409 }
3410 },
3411 local_name!("src") => {
3412 if !mutation.is_removal() {
3417 self.media_element_load_algorithm(cx);
3418 }
3419 },
3420 local_name!("controls") => {
3421 if mutation.new_value(attr).is_some() {
3422 self.render_controls(cx);
3423 } else {
3424 self.remove_controls();
3425 }
3426 },
3427 _ => (),
3428 };
3429 }
3430
3431 fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
3433 self.super_type().unwrap().unbind_from_tree(cx, context);
3434
3435 self.remove_controls();
3436
3437 if context.tree_connected {
3438 let task = MediaElementMicrotask::PauseIfNotInDocument {
3439 elem: DomRoot::from_ref(self),
3440 };
3441 ScriptThread::await_stable_state(Microtask::MediaElement(task));
3442 }
3443 }
3444
3445 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
3446 self.super_type().unwrap().adopting_steps(cx, old_doc);
3447
3448 if let Some(id) = &*self.media_controls_id.borrow() {
3452 let Some(shadow_root) = self.upcast::<Element>().shadow_root() else {
3453 error!("Missing media controls shadow root");
3454 return;
3455 };
3456
3457 old_doc.unregister_media_controls(id);
3458 self.owner_document()
3459 .register_media_controls(id, &shadow_root);
3460 }
3461 }
3462}
3463
3464#[derive(JSTraceable, MallocSizeOf)]
3465pub(crate) enum MediaElementMicrotask {
3466 ResourceSelection {
3467 elem: DomRoot<HTMLMediaElement>,
3468 generation_id: u32,
3469 #[no_trace]
3470 base_url: ServoUrl,
3471 },
3472 PauseIfNotInDocument {
3473 elem: DomRoot<HTMLMediaElement>,
3474 },
3475 Seeked {
3476 elem: DomRoot<HTMLMediaElement>,
3477 generation_id: u32,
3478 },
3479 SelectNextSourceChild {
3480 elem: DomRoot<HTMLMediaElement>,
3481 generation_id: u32,
3482 },
3483 SelectNextSourceChildAfterWait {
3484 elem: DomRoot<HTMLMediaElement>,
3485 generation_id: u32,
3486 },
3487}
3488
3489impl MicrotaskRunnable for MediaElementMicrotask {
3490 fn handler(&self, cx: &mut js::context::JSContext) {
3491 match self {
3492 &MediaElementMicrotask::ResourceSelection {
3493 ref elem,
3494 generation_id,
3495 ref base_url,
3496 } => {
3497 if generation_id == elem.generation_id.get() {
3498 elem.resource_selection_algorithm_sync(base_url.clone(), cx);
3499 }
3500 },
3501 MediaElementMicrotask::PauseIfNotInDocument { elem } => {
3502 if !elem.upcast::<Node>().is_connected() {
3503 elem.internal_pause_steps();
3504 }
3505 },
3506 &MediaElementMicrotask::Seeked {
3507 ref elem,
3508 generation_id,
3509 } => {
3510 if generation_id == elem.generation_id.get() {
3511 elem.seek_end();
3512 }
3513 },
3514 &MediaElementMicrotask::SelectNextSourceChild {
3515 ref elem,
3516 generation_id,
3517 } => {
3518 if generation_id == elem.generation_id.get() {
3519 elem.select_next_source_child(CanGc::from_cx(cx));
3520 }
3521 },
3522 &MediaElementMicrotask::SelectNextSourceChildAfterWait {
3523 ref elem,
3524 generation_id,
3525 } => {
3526 if generation_id == elem.generation_id.get() {
3527 elem.select_next_source_child_after_wait(cx);
3528 }
3529 },
3530 }
3531 }
3532
3533 fn enter_realm<'cx>(&self, cx: &'cx mut js::context::JSContext) -> AutoRealm<'cx> {
3534 match self {
3535 &MediaElementMicrotask::ResourceSelection { ref elem, .. } |
3536 &MediaElementMicrotask::PauseIfNotInDocument { ref elem } |
3537 &MediaElementMicrotask::Seeked { ref elem, .. } |
3538 &MediaElementMicrotask::SelectNextSourceChild { ref elem, .. } |
3539 &MediaElementMicrotask::SelectNextSourceChildAfterWait { ref elem, .. } => {
3540 enter_auto_realm(cx, &**elem)
3541 },
3542 }
3543 }
3544}
3545
3546enum Resource {
3547 Object,
3548 Url(ServoUrl),
3549}
3550
3551#[derive(Debug, MallocSizeOf, PartialEq)]
3552enum DataBuffer {
3553 Payload(Vec<u8>),
3554 EndOfStream,
3555}
3556
3557#[derive(MallocSizeOf)]
3558struct BufferedDataSource {
3559 locked: Cell<bool>,
3564 buffers: VecDeque<DataBuffer>,
3566}
3567
3568impl BufferedDataSource {
3569 fn new() -> BufferedDataSource {
3570 BufferedDataSource {
3571 locked: Cell::new(true),
3572 buffers: VecDeque::default(),
3573 }
3574 }
3575
3576 fn set_locked(&self, locked: bool) {
3577 self.locked.set(locked)
3578 }
3579
3580 fn add_buffer_to_queue(&mut self, buffer: DataBuffer) {
3581 debug_assert_ne!(
3582 self.buffers.back(),
3583 Some(&DataBuffer::EndOfStream),
3584 "The media backend not expects any further data after end of stream"
3585 );
3586
3587 self.buffers.push_back(buffer);
3588 }
3589
3590 fn process_into_player_from_queue(
3591 &mut self,
3592 player: &Arc<Mutex<dyn Player>>,
3593 ) -> Result<(), PlayerError> {
3594 if self.locked.get() {
3596 return Ok(());
3597 }
3598
3599 while let Some(buffer) = self.buffers.pop_front() {
3600 match buffer {
3601 DataBuffer::Payload(payload) => {
3602 if let Err(error) = player.lock().unwrap().push_data(payload) {
3603 warn!("Could not push input data to player: {error:?}");
3604 return Err(error);
3605 }
3606 },
3607 DataBuffer::EndOfStream => {
3608 if let Err(error) = player.lock().unwrap().end_of_stream() {
3609 warn!("Could not signal EOS to player: {error:?}");
3610 return Err(error);
3611 }
3612 },
3613 }
3614 }
3615
3616 Ok(())
3617 }
3618
3619 fn reset(&mut self) {
3620 self.locked.set(true);
3621 self.buffers.clear();
3622 }
3623}
3624
3625#[derive(Debug, MallocSizeOf, PartialEq)]
3627enum CancelReason {
3628 Backoff,
3630 Error,
3632 Abort,
3634}
3635
3636#[derive(MallocSizeOf)]
3637pub(crate) struct HTMLMediaElementFetchContext {
3638 request_id: RequestId,
3640 cancel_reason: Option<CancelReason>,
3642 is_seekable: bool,
3644 origin_clean: bool,
3646 data_source: RefCell<BufferedDataSource>,
3648 fetch_canceller: FetchCanceller,
3651}
3652
3653impl HTMLMediaElementFetchContext {
3654 fn new(
3655 request_id: RequestId,
3656 core_resource_thread: CoreResourceThread,
3657 ) -> HTMLMediaElementFetchContext {
3658 HTMLMediaElementFetchContext {
3659 request_id,
3660 cancel_reason: None,
3661 is_seekable: false,
3662 origin_clean: true,
3663 data_source: RefCell::new(BufferedDataSource::new()),
3664 fetch_canceller: FetchCanceller::new(request_id, false, core_resource_thread),
3665 }
3666 }
3667
3668 fn request_id(&self) -> RequestId {
3669 self.request_id
3670 }
3671
3672 fn is_seekable(&self) -> bool {
3673 self.is_seekable
3674 }
3675
3676 fn set_seekable(&mut self, seekable: bool) {
3677 self.is_seekable = seekable;
3678 }
3679
3680 fn origin_is_clean(&self) -> bool {
3681 self.origin_clean
3682 }
3683
3684 fn set_origin_clean(&mut self, origin_clean: bool) {
3685 self.origin_clean = origin_clean;
3686 }
3687
3688 fn data_source(&self) -> &RefCell<BufferedDataSource> {
3689 &self.data_source
3690 }
3691
3692 fn cancel(&mut self, reason: CancelReason) {
3693 if self.cancel_reason.is_some() {
3694 return;
3695 }
3696 self.cancel_reason = Some(reason);
3697 self.data_source.borrow_mut().reset();
3698 self.fetch_canceller.abort();
3699 }
3700
3701 fn cancel_reason(&self) -> &Option<CancelReason> {
3702 &self.cancel_reason
3703 }
3704}
3705
3706struct HTMLMediaElementFetchListener {
3707 element: Trusted<HTMLMediaElement>,
3709 generation_id: u32,
3711 request_id: RequestId,
3713 next_progress_event: Instant,
3715 url: ServoUrl,
3717 expected_content_length: Option<u64>,
3719 fetched_content_length: u64,
3721 content_length_to_discard: u64,
3725}
3726
3727impl FetchResponseListener for HTMLMediaElementFetchListener {
3728 fn process_request_body(&mut self, _: RequestId) {}
3729
3730 fn process_response(
3731 &mut self,
3732 cx: &mut js::context::JSContext,
3733 _: RequestId,
3734 metadata: Result<FetchMetadata, NetworkError>,
3735 ) {
3736 let element = self.element.root();
3737
3738 let (metadata, origin_clean) = match metadata {
3739 Ok(fetch_metadata) => match fetch_metadata {
3740 FetchMetadata::Unfiltered(metadata) => (Some(metadata), true),
3741 FetchMetadata::Filtered { filtered, unsafe_ } => (
3742 Some(unsafe_),
3743 matches!(
3744 filtered,
3745 FilteredMetadata::Basic(_) | FilteredMetadata::Cors(_)
3746 ),
3747 ),
3748 },
3749 Err(_) => (None, true),
3750 };
3751
3752 let (status_is_success, is_seekable) =
3753 metadata.as_ref().map_or((false, false), |metadata| {
3754 let status = &metadata.status;
3755 (status.is_success(), *status == StatusCode::PARTIAL_CONTENT)
3756 });
3757
3758 if !status_is_success {
3760 if element.ready_state.get() == ReadyState::HaveNothing {
3761 element.media_data_processing_failure_steps();
3763 } else {
3764 element.media_data_processing_fatal_steps(MEDIA_ERR_NETWORK, cx);
3766 }
3767 return;
3768 }
3769
3770 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut() {
3771 current_fetch_context.set_seekable(is_seekable);
3772 current_fetch_context.set_origin_clean(origin_clean);
3773 }
3774
3775 if let Some(metadata) = metadata.as_ref() &&
3776 let Some(headers) = metadata.headers.as_ref()
3777 {
3778 let content_length = if let Some(content_range) = headers.typed_get::<ContentRange>() {
3781 content_range.bytes_len()
3782 } else {
3783 headers
3784 .typed_get::<ContentLength>()
3785 .map(|content_length| content_length.0)
3786 };
3787
3788 if content_length != self.expected_content_length &&
3790 let Some(content_length) = content_length
3791 {
3792 self.expected_content_length = Some(content_length);
3793 }
3794 }
3795
3796 if let Some(expected_content_length) = self.expected_content_length &&
3798 let Err(e) = element
3799 .player
3800 .borrow()
3801 .as_ref()
3802 .unwrap()
3803 .lock()
3804 .unwrap()
3805 .set_input_size(expected_content_length)
3806 {
3807 warn!("Could not set player input size {:?}", e);
3808 }
3809 }
3810
3811 fn process_response_chunk(
3812 &mut self,
3813 _: &mut js::context::JSContext,
3814 _: RequestId,
3815 chunk: Vec<u8>,
3816 ) {
3817 let element = self.element.root();
3818
3819 self.fetched_content_length += chunk.len() as u64;
3820
3821 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut() {
3823 if let Some(CancelReason::Backoff) = current_fetch_context.cancel_reason() {
3824 return;
3825 }
3826
3827 let payload = if !current_fetch_context.is_seekable() &&
3829 self.content_length_to_discard != 0
3830 {
3831 if chunk.len() as u64 > self.content_length_to_discard {
3832 let shrink_chunk = chunk[self.content_length_to_discard as usize..].to_vec();
3833 self.content_length_to_discard = 0;
3834 shrink_chunk
3835 } else {
3836 self.content_length_to_discard -= chunk.len() as u64;
3838 return;
3839 }
3840 } else {
3841 chunk
3842 };
3843
3844 if let Err(e) = {
3845 let mut data_source = current_fetch_context.data_source().borrow_mut();
3846 data_source.add_buffer_to_queue(DataBuffer::Payload(payload));
3847 data_source
3848 .process_into_player_from_queue(element.player.borrow().as_ref().unwrap())
3849 } {
3850 if e == PlayerError::EnoughData {
3855 current_fetch_context.cancel(CancelReason::Backoff);
3856 }
3857 return;
3858 }
3859 }
3860
3861 if Instant::now() > self.next_progress_event {
3866 element.queue_media_element_task_to_fire_event(atom!("progress"));
3867 self.next_progress_event = Instant::now() + Duration::from_millis(350);
3868 }
3869 }
3870
3871 fn process_response_eof(
3872 self,
3873 cx: &mut js::context::JSContext,
3874 _: RequestId,
3875 status: Result<(), NetworkError>,
3876 timing: ResourceFetchTiming,
3877 ) {
3878 let element = self.element.root();
3879
3880 if status.is_ok() && self.fetched_content_length != 0 {
3882 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut()
3887 {
3888 if self.expected_content_length.is_none() &&
3895 let Err(e) = element
3896 .player
3897 .borrow()
3898 .as_ref()
3899 .unwrap()
3900 .lock()
3901 .unwrap()
3902 .set_input_size(self.fetched_content_length)
3903 {
3904 warn!("Could not set player input size {:?}", e);
3905 }
3906
3907 let mut data_source = current_fetch_context.data_source().borrow_mut();
3908
3909 data_source.add_buffer_to_queue(DataBuffer::EndOfStream);
3910 let _ = data_source
3911 .process_into_player_from_queue(element.player.borrow().as_ref().unwrap());
3912 }
3913
3914 element
3916 .upcast::<EventTarget>()
3917 .fire_event(cx, atom!("progress"));
3918
3919 element.network_state.set(NetworkState::Idle);
3922
3923 element
3924 .upcast::<EventTarget>()
3925 .fire_event(cx, atom!("suspend"));
3926 } else if status.is_err() && element.ready_state.get() != ReadyState::HaveNothing {
3927 element.media_data_processing_fatal_steps(MEDIA_ERR_NETWORK, cx);
3929 } else {
3930 element.media_data_processing_failure_steps();
3933 }
3934
3935 network_listener::submit_timing(cx, &self, &status, &timing);
3936 }
3937
3938 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
3939 let global = &self.resource_timing_global();
3940 global.report_csp_violations(violations, None, None);
3941 }
3942
3943 fn should_invoke(&self) -> bool {
3944 let element = self.element.root();
3945
3946 if element.generation_id.get() != self.generation_id || element.player.borrow().is_none() {
3947 return false;
3948 }
3949
3950 let Some(ref current_fetch_context) = *element.current_fetch_context.borrow() else {
3951 return false;
3952 };
3953
3954 if current_fetch_context.request_id() != self.request_id {
3956 return false;
3957 }
3958
3959 if let Some(cancel_reason) = current_fetch_context.cancel_reason() &&
3962 matches!(*cancel_reason, CancelReason::Error | CancelReason::Abort)
3963 {
3964 return false;
3965 }
3966
3967 true
3968 }
3969}
3970
3971impl ResourceTimingListener for HTMLMediaElementFetchListener {
3972 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
3973 let initiator_type = InitiatorType::LocalName(
3974 self.element
3975 .root()
3976 .upcast::<Element>()
3977 .local_name()
3978 .to_string(),
3979 );
3980 (initiator_type, self.url.clone())
3981 }
3982
3983 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
3984 self.element.root().owner_document().global()
3985 }
3986}
3987
3988impl HTMLMediaElementFetchListener {
3989 fn new(element: &HTMLMediaElement, request_id: RequestId, url: ServoUrl, offset: u64) -> Self {
3990 Self {
3991 element: Trusted::new(element),
3992 generation_id: element.generation_id.get(),
3993 request_id,
3994 next_progress_event: Instant::now() + Duration::from_millis(350),
3995 url,
3996 expected_content_length: None,
3997 fetched_content_length: 0,
3998 content_length_to_discard: offset,
3999 }
4000 }
4001}
4002
4003#[derive(JSTraceable, MallocSizeOf)]
4007struct HTMLMediaElementEventHandler {
4008 element: WeakRef<HTMLMediaElement>,
4009}
4010
4011#[expect(unsafe_code)]
4012unsafe impl Send for HTMLMediaElementEventHandler {}
4013
4014impl HTMLMediaElementEventHandler {
4015 fn new(element: &HTMLMediaElement) -> Self {
4016 Self {
4017 element: WeakRef::new(element),
4018 }
4019 }
4020
4021 fn handle_player_event(
4022 &self,
4023 player_id: usize,
4024 event: PlayerEvent,
4025 cx: &mut js::context::JSContext,
4026 ) {
4027 let Some(element) = self.element.root() else {
4028 return;
4029 };
4030
4031 if element.player_id().is_none_or(|id| id != player_id) {
4033 return;
4034 }
4035
4036 match event {
4037 PlayerEvent::DurationChanged(duration) => element.playback_duration_changed(duration),
4038 PlayerEvent::EndOfStream => element.playback_end(),
4039 PlayerEvent::EnoughData => element.playback_enough_data(),
4040 PlayerEvent::Error(ref error) => element.playback_error(error, cx),
4041 PlayerEvent::MetadataUpdated(ref metadata) => {
4042 element.playback_metadata_updated(metadata, CanGc::from_cx(cx))
4043 },
4044 PlayerEvent::NeedData => element.playback_need_data(),
4045 PlayerEvent::PositionChanged(position) => element.playback_position_changed(position),
4046 PlayerEvent::SeekData(offset, seek_lock) => {
4047 element.fetch_request(Some(offset), Some(seek_lock))
4048 },
4049 PlayerEvent::SeekDone(position) => element.playback_seek_done(position),
4050 PlayerEvent::StateChanged(ref state) => element.playback_state_changed(state),
4051 PlayerEvent::VideoFrameUpdated => element.playback_video_frame_updated(),
4052 }
4053 }
4054}
4055
4056impl Drop for HTMLMediaElementEventHandler {
4057 fn drop(&mut self) {
4058 assert_in_script();
4062 }
4063}