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_arc_vec(
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_arc_vec(
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_arc_vec(
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
757 if delay {
758 if blocker.borrow().is_none() {
759 *blocker.borrow_mut() =
760 Some(LoadBlocker::new(&self.owner_document(), LoadType::Media));
761 }
762 } else {
763 LoadBlocker::terminate(blocker, cx);
764 }
765 }
766
767 fn time_marches_on(&self) {
769 if Instant::now() > self.next_timeupdate_event.get() {
775 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
776 self.next_timeupdate_event
777 .set(Instant::now() + Duration::from_millis(250));
778 }
779 }
780
781 fn internal_play_steps(&self, cx: &mut js::context::JSContext) {
783 if self.network_state.get() == NetworkState::Empty {
786 self.invoke_resource_selection_algorithm(cx);
787 }
788
789 if self.ended_playback(LoopCondition::Ignored) &&
795 self.direction_of_playback() == PlaybackDirection::Forwards
796 {
797 self.seek(
798 self.earliest_possible_position(),
799 false,
800 );
801 }
802
803 let state = self.ready_state.get();
804
805 if self.Paused() {
807 self.paused.set(false);
809
810 if self.show_poster.get() {
813 self.show_poster.set(false);
814 self.time_marches_on();
815 }
816
817 self.queue_media_element_task_to_fire_event(atom!("play"));
820
821 match state {
827 ReadyState::HaveNothing |
828 ReadyState::HaveMetadata |
829 ReadyState::HaveCurrentData => {
830 self.queue_media_element_task_to_fire_event(atom!("waiting"));
831 },
832 ReadyState::HaveFutureData | ReadyState::HaveEnoughData => {
833 self.notify_about_playing();
834 },
835 }
836 }
837 else if state == ReadyState::HaveFutureData || state == ReadyState::HaveEnoughData {
842 self.take_pending_play_promises(Ok(()));
843
844 let this = Trusted::new(self);
845 let generation_id = self.generation_id.get();
846
847 self.owner_global()
848 .task_manager()
849 .media_element_task_source()
850 .queue(task!(resolve_pending_play_promises: move || {
851 let this = this.root();
852 if generation_id != this.generation_id.get() {
853 return;
854 }
855
856 this.fulfill_in_flight_play_promises(|| {});
857 }));
858 }
859
860 self.autoplaying.set(false);
862
863 self.update_media_state();
864 }
865
866 fn internal_pause_steps(&self) {
868 self.autoplaying.set(false);
870
871 if !self.Paused() {
873 self.paused.set(true);
875
876 self.take_pending_play_promises(Err(Error::Abort(None)));
878
879 let this = Trusted::new(self);
881 let generation_id = self.generation_id.get();
882
883 self.owner_global()
884 .task_manager()
885 .media_element_task_source()
886 .queue(task!(internal_pause_steps: move |cx| {
887 let this = this.root();
888 if generation_id != this.generation_id.get() {
889 return;
890 }
891
892 this.fulfill_in_flight_play_promises(|| {
893 this.upcast::<EventTarget>().fire_event(cx, atom!("timeupdate"));
895
896 this.upcast::<EventTarget>().fire_event(cx, atom!("pause"));
898
899 });
903 }));
904
905 self.official_playback_position
907 .set(self.current_playback_position.get());
908 }
909
910 self.update_media_state();
911 }
912
913 fn is_allowed_to_play(&self) -> bool {
915 true
916 }
917
918 fn notify_about_playing(&self) {
920 self.take_pending_play_promises(Ok(()));
922
923 let this = Trusted::new(self);
925 let generation_id = self.generation_id.get();
926
927 self.owner_global()
928 .task_manager()
929 .media_element_task_source()
930 .queue(task!(notify_about_playing: move |cx| {
931 let this = this.root();
932 if generation_id != this.generation_id.get() {
933 return;
934 }
935
936 this.fulfill_in_flight_play_promises(|| {
937 this.upcast::<EventTarget>().fire_event(cx, atom!("playing"));
939
940 });
943 }));
944 }
945
946 #[expect(
948 clippy::collapsible_match,
949 reason = "This way follows the spec more closely"
950 )]
951 fn change_ready_state(&self, ready_state: ReadyState) {
952 let old_ready_state = self.ready_state.get();
953 self.ready_state.set(ready_state);
954
955 if self.network_state.get() == NetworkState::Empty {
956 return;
957 }
958
959 if old_ready_state == ready_state {
960 return;
961 }
962
963 match (old_ready_state, ready_state) {
965 (ReadyState::HaveNothing, ReadyState::HaveMetadata) => {
968 self.queue_media_element_task_to_fire_event(atom!("loadedmetadata"));
971 return;
973 },
974 (ReadyState::HaveMetadata, new) if new >= ReadyState::HaveCurrentData => {
977 if !self.fired_loadeddata_event.get() {
981 self.fired_loadeddata_event.set(true);
982
983 let this = Trusted::new(self);
984 let generation_id = self.generation_id.get();
985
986 self.owner_global()
987 .task_manager()
988 .media_element_task_source()
989 .queue(task!(media_reached_current_data: move |cx| {
990 let this = this.root();
991 if generation_id != this.generation_id.get() {
992 return;
993 }
994
995 this.upcast::<EventTarget>().fire_event(cx, atom!("loadeddata"));
996 this.delay_load_event(false, cx);
1000 }));
1001 }
1002
1003 },
1007 (ReadyState::HaveFutureData, new) if new <= ReadyState::HaveCurrentData => {
1008 return;
1013 },
1014
1015 _ => (),
1016 }
1017
1018 if old_ready_state <= ReadyState::HaveCurrentData &&
1021 ready_state >= ReadyState::HaveFutureData
1022 {
1023 self.queue_media_element_task_to_fire_event(atom!("canplay"));
1026
1027 if !self.Paused() {
1030 self.notify_about_playing();
1031 }
1032 }
1033
1034 if ready_state == ReadyState::HaveEnoughData {
1036 self.queue_media_element_task_to_fire_event(atom!("canplaythrough"));
1039
1040 if self.eligible_for_autoplay() {
1043 self.paused.set(false);
1045
1046 if self.show_poster.get() {
1049 self.show_poster.set(false);
1050 self.time_marches_on();
1051 }
1052
1053 self.queue_media_element_task_to_fire_event(atom!("play"));
1056
1057 self.notify_about_playing();
1059 }
1060 }
1061
1062 self.update_media_state();
1063 }
1064
1065 fn invoke_resource_selection_algorithm(&self, cx: &mut js::context::JSContext) {
1067 self.network_state.set(NetworkState::NoSource);
1069
1070 self.show_poster.set(true);
1072
1073 self.delay_load_event(true, cx);
1076
1077 let task = MediaElementMicrotask::ResourceSelection {
1084 elem: DomRoot::from_ref(self),
1085 generation_id: self.generation_id.get(),
1086 base_url: self.owner_document().base_url(),
1087 };
1088
1089 ScriptThread::await_stable_state(Microtask::MediaElement(task));
1094 }
1095
1096 fn resource_selection_algorithm_sync(
1098 &self,
1099 base_url: ServoUrl,
1100 cx: &mut js::context::JSContext,
1101 ) {
1102 enum Mode {
1109 Object,
1110 Attribute(String),
1111 Children(DomRoot<HTMLSourceElement>),
1112 }
1113
1114 let mode = if self.src_object.borrow().is_some() {
1116 Mode::Object
1118 } else if let Some(src) = self
1119 .upcast::<Element>()
1120 .get_attribute_string_value(&local_name!("src"))
1121 {
1122 Mode::Attribute(src)
1125 } else if let Some(source) = self
1126 .upcast::<Node>()
1127 .children_unrooted(cx.no_gc())
1128 .find_map(UnrootedDom::downcast::<HTMLSourceElement>)
1129 {
1130 Mode::Children(source.as_rooted())
1134 } else {
1135 self.load_state.set(LoadState::NotLoaded);
1138
1139 self.network_state.set(NetworkState::Empty);
1141
1142 self.delay_load_event(false, cx);
1145
1146 return;
1148 };
1149
1150 self.network_state.set(NetworkState::Loading);
1152
1153 self.queue_media_element_task_to_fire_event(atom!("loadstart"));
1156
1157 match mode {
1159 Mode::Object => {
1160 self.load_from_src_object();
1162 },
1163 Mode::Attribute(src) => {
1164 self.load_from_src_attribute(base_url, &src);
1166 },
1167 Mode::Children(source) => {
1168 self.load_from_source_child(&source);
1170 },
1171 }
1172 }
1173
1174 fn load_from_src_object(&self) {
1176 self.load_state.set(LoadState::LoadingFromSrcObject);
1177
1178 "".clone_into(&mut self.current_src.borrow_mut());
1180
1181 self.resource_fetch_algorithm(Resource::Object);
1187 }
1188
1189 fn load_from_src_attribute(&self, base_url: ServoUrl, src: &str) {
1191 self.load_state.set(LoadState::LoadingFromSrcAttribute);
1192
1193 if src.is_empty() {
1196 self.queue_dedicated_media_source_failure_steps();
1197 return;
1198 }
1199
1200 let Ok(url_record) = base_url.join(src) else {
1204 self.queue_dedicated_media_source_failure_steps();
1205 return;
1206 };
1207
1208 *self.current_src.borrow_mut() = url_record.as_str().into();
1211
1212 self.resource_fetch_algorithm(Resource::Url(url_record));
1218 }
1219
1220 fn load_from_source_child(&self, source: &HTMLSourceElement) {
1222 self.load_state.set(LoadState::LoadingFromSourceChild);
1223
1224 *self.source_children_pointer.borrow_mut() =
1231 Some(SourceChildrenPointer::new(DomRoot::from_ref(source), false));
1232
1233 let element = source.upcast::<Element>();
1234
1235 let Some(src) = element
1239 .get_attribute_string_value(&local_name!("src"))
1240 .filter(|value| !value.is_empty())
1241 else {
1242 self.load_from_source_child_failure_steps(source);
1243 return;
1244 };
1245
1246 if let Some(media) = element.get_attribute_string_value(&local_name!("media")) &&
1250 !MediaList::matches_environment(&element.owner_document(), &media)
1251 {
1252 self.load_from_source_child_failure_steps(source);
1253 return;
1254 }
1255
1256 let Ok(url_record) = source.owner_document().base_url().join(&src) else {
1260 self.load_from_source_child_failure_steps(source);
1263 return;
1264 };
1265
1266 if let Some(type_) = element.get_attribute_string_value(&local_name!("type")) &&
1271 ServoMedia::get().can_play_type(&type_) == SupportsMediaType::No
1272 {
1273 self.load_from_source_child_failure_steps(source);
1274 return;
1275 }
1276
1277 self.reset_media_player();
1279
1280 self.current_source_child.set(Some(source));
1281
1282 *self.current_src.borrow_mut() = url_record.as_str().into();
1285
1286 self.resource_fetch_algorithm(Resource::Url(url_record));
1291 }
1292
1293 fn load_from_source_child_failure_steps(&self, source: &HTMLSourceElement) {
1295 let trusted_this = Trusted::new(self);
1298 let trusted_source = Trusted::new(source);
1299 let generation_id = self.generation_id.get();
1300
1301 self.owner_global()
1302 .task_manager()
1303 .media_element_task_source()
1304 .queue(task!(queue_error_event: move |cx| {
1305 let this = trusted_this.root();
1306 if generation_id != this.generation_id.get() {
1307 return;
1308 }
1309
1310 let source = trusted_source.root();
1311 source.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1312 }));
1313
1314 let task = MediaElementMicrotask::SelectNextSourceChild {
1316 elem: DomRoot::from_ref(self),
1317 generation_id: self.generation_id.get(),
1318 };
1319
1320 ScriptThread::await_stable_state(Microtask::MediaElement(task));
1321 }
1322
1323 fn select_next_source_child(&self, cx: &mut js::context::JSContext) {
1325 self.AudioTracks(cx).clear();
1327 self.VideoTracks(CanGc::from_cx(cx)).clear();
1328
1329 let mut source_candidate = None;
1331
1332 if let Some(ref source_children_pointer) = *self.source_children_pointer.borrow() {
1340 if source_children_pointer.inclusive {
1344 for next_sibling in source_children_pointer
1345 .source_before_pointer
1346 .upcast::<Node>()
1347 .inclusively_following_siblings()
1348 {
1349 if let Some(next_source) = DomRoot::downcast::<HTMLSourceElement>(next_sibling)
1350 {
1351 source_candidate = Some(next_source);
1352 break;
1353 }
1354 }
1355 } else {
1356 for next_sibling in source_children_pointer
1357 .source_before_pointer
1358 .upcast::<Node>()
1359 .following_siblings()
1360 {
1361 if let Some(next_source) = DomRoot::downcast::<HTMLSourceElement>(next_sibling)
1362 {
1363 source_candidate = Some(next_source);
1364 break;
1365 }
1366 }
1367 };
1368 }
1369
1370 if let Some(source_candidate) = source_candidate {
1373 self.load_from_source_child(&source_candidate);
1374 return;
1375 }
1376
1377 self.load_state.set(LoadState::WaitingForSource);
1378
1379 *self.source_children_pointer.borrow_mut() = None;
1380
1381 self.network_state.set(NetworkState::NoSource);
1384
1385 self.show_poster.set(true);
1387
1388 let this = Trusted::new(self);
1391 let generation_id = self.generation_id.get();
1392
1393 self.owner_global()
1394 .task_manager()
1395 .media_element_task_source()
1396 .queue(task!(queue_delay_load_event: move |cx| {
1397 let this = this.root();
1398 if generation_id != this.generation_id.get() {
1399 return;
1400 }
1401
1402 this.delay_load_event(false, cx);
1403 }));
1404
1405 }
1408
1409 fn resource_selection_algorithm_failure_steps(&self) {
1411 match self.load_state.get() {
1412 LoadState::LoadingFromSrcObject => {
1413 self.queue_dedicated_media_source_failure_steps();
1418 },
1419 LoadState::LoadingFromSrcAttribute => {
1420 self.queue_dedicated_media_source_failure_steps();
1425 },
1426 LoadState::LoadingFromSourceChild => {
1427 if let Some(source) = self.current_source_child.take() {
1430 self.load_from_source_child_failure_steps(&source);
1431 }
1432 },
1433 _ => {},
1434 }
1435 }
1436
1437 fn fetch_request(&self, offset: Option<u64>, seek_lock: Option<SeekLock>) {
1438 if self.resource_url.borrow().is_none() && self.blob_url.borrow().is_none() {
1439 error!("Missing request url");
1440 if let Some(seek_lock) = seek_lock {
1441 seek_lock.unlock(false);
1442 }
1443 self.resource_selection_algorithm_failure_steps();
1444 return;
1445 }
1446
1447 let document = self.owner_document();
1448 let destination = match self.media_type_id() {
1449 HTMLMediaElementTypeId::HTMLAudioElement => Destination::Audio,
1450 HTMLMediaElementTypeId::HTMLVideoElement => Destination::Video,
1451 };
1452 let mut headers = HeaderMap::new();
1453 headers.insert(
1455 header::RANGE,
1456 HeaderValue::from_str(&format!("bytes={}-", offset.unwrap_or(0))).unwrap(),
1457 );
1458 let url = match self.resource_url.borrow().as_ref() {
1459 Some(url) => url.clone(),
1460 None => self.blob_url.borrow().as_ref().unwrap().clone(),
1461 };
1462
1463 let cors_setting = cors_setting_for_element(self.upcast());
1464 let global = self.global();
1465 let request = create_a_potential_cors_request(
1466 Some(document.webview_id()),
1467 url.clone(),
1468 destination,
1469 cors_setting,
1470 None,
1471 global.get_referrer(),
1472 )
1473 .with_global_scope(&global)
1474 .headers(headers)
1475 .referrer_policy(document.get_referrer_policy());
1476
1477 let mut current_fetch_context = self.current_fetch_context.borrow_mut();
1478 if let Some(ref mut current_fetch_context) = *current_fetch_context {
1479 current_fetch_context.cancel(CancelReason::Abort);
1480 }
1481
1482 *current_fetch_context = Some(HTMLMediaElementFetchContext::new(
1483 request.id,
1484 global.core_resource_thread(),
1485 ));
1486 let listener =
1487 HTMLMediaElementFetchListener::new(self, request.id, url, offset.unwrap_or(0));
1488
1489 self.owner_document().fetch_background(request, listener);
1490
1491 if let Some(seek_lock) = seek_lock {
1496 seek_lock.unlock(true);
1497 }
1498 }
1499
1500 fn eligible_for_autoplay(&self) -> bool {
1502 self.autoplaying.get() &&
1504
1505 self.Paused() &&
1507
1508 self.Autoplay() &&
1510
1511 {
1514 let document = self.owner_document();
1515
1516 !document.has_active_sandboxing_flag(
1517 SandboxingFlagSet::SANDBOXED_AUTOMATIC_FEATURES_BROWSING_CONTEXT_FLAG,
1518 )
1519 }
1520
1521 }
1524
1525 fn resource_fetch_algorithm(&self, resource: Resource) {
1527 if let Err(e) = self.create_media_player(&resource) {
1528 error!("Create media player error {:?}", e);
1529 self.resource_selection_algorithm_failure_steps();
1530 return;
1531 }
1532
1533 match resource {
1542 Resource::Url(url) => {
1543 if self.Preload() == "none" && !self.autoplaying.get() {
1548 self.network_state.set(NetworkState::Idle);
1550
1551 self.queue_media_element_task_to_fire_event(atom!("suspend"));
1554
1555 let this = Trusted::new(self);
1559 let generation_id = self.generation_id.get();
1560
1561 self.owner_global()
1562 .task_manager()
1563 .media_element_task_source()
1564 .queue(task!(queue_delay_load_event: move |cx| {
1565 let this = this.root();
1566 if generation_id != this.generation_id.get() {
1567 return;
1568 }
1569
1570 this.delay_load_event(false, cx);
1571 }));
1572
1573 return;
1582 }
1583
1584 *self.resource_url.borrow_mut() = Some(url);
1585
1586 self.fetch_request(None, None);
1588 },
1589 Resource::Object => {
1590 if let Some(ref src_object) = *self.src_object.borrow() {
1591 match src_object {
1592 SrcObject::Blob(blob) => {
1593 let blob_url = URL::CreateObjectURL(&self.global(), blob);
1594 *self.blob_url.borrow_mut() =
1595 Some(ServoUrl::parse(&blob_url.str()).expect("infallible"));
1596 self.fetch_request(None, None);
1597 },
1598 SrcObject::MediaStream(stream) => {
1599 let tracks = &*stream.get_tracks();
1600 for (pos, track) in tracks.iter().enumerate() {
1601 if self
1602 .player
1603 .borrow()
1604 .as_ref()
1605 .unwrap()
1606 .lock()
1607 .unwrap()
1608 .set_stream(&track.id(), pos == tracks.len() - 1)
1609 .is_err()
1610 {
1611 self.resource_selection_algorithm_failure_steps();
1612 }
1613 }
1614 },
1615 }
1616 }
1617 },
1618 }
1619 }
1620
1621 fn queue_dedicated_media_source_failure_steps(&self) {
1625 let this = Trusted::new(self);
1626 let generation_id = self.generation_id.get();
1627 self.take_pending_play_promises(Err(Error::NotSupported(None)));
1628 self.owner_global()
1629 .task_manager()
1630 .media_element_task_source()
1631 .queue(task!(dedicated_media_source_failure_steps: move |cx| {
1632 let this = this.root();
1633 if generation_id != this.generation_id.get() {
1634 return;
1635 }
1636
1637 this.fulfill_in_flight_play_promises(|| {
1638 this.error.set(Some(&*MediaError::new(
1641 &this.owner_window(),
1642 MEDIA_ERR_SRC_NOT_SUPPORTED, CanGc::from_cx(cx))));
1643
1644 this.AudioTracks(cx).clear();
1646 this.VideoTracks(CanGc::from_cx(cx)).clear();
1647
1648 this.network_state.set(NetworkState::NoSource);
1651
1652 this.show_poster.set(true);
1654
1655 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1657
1658 if let Some(ref player) = *this.player.borrow()
1659 && let Err(error) = player.lock().unwrap().stop() {
1660 error!("Could not stop player: {error:?}");
1661 }
1662
1663 });
1667
1668 this.delay_load_event(false, cx);
1671 }));
1672 }
1673
1674 fn in_error_state(&self) -> bool {
1675 self.error.get().is_some()
1676 }
1677
1678 fn is_potentially_playing(&self) -> bool {
1680 !self.paused.get() &&
1681 !self.ended_playback(LoopCondition::Included) &&
1682 self.error.get().is_none() &&
1683 !self.is_blocked_media_element()
1684 }
1685
1686 fn is_blocked_media_element(&self) -> bool {
1688 self.ready_state.get() <= ReadyState::HaveCurrentData ||
1689 self.is_paused_for_user_interaction() ||
1690 self.is_paused_for_in_band_content()
1691 }
1692
1693 fn is_paused_for_user_interaction(&self) -> bool {
1695 false
1698 }
1699
1700 fn is_paused_for_in_band_content(&self) -> bool {
1702 false
1705 }
1706
1707 fn media_element_load_algorithm(&self, cx: &mut js::context::JSContext) {
1709 self.fired_loadeddata_event.set(false);
1712
1713 self.generation_id.set(self.generation_id.get() + 1);
1718
1719 self.load_state.set(LoadState::NotLoaded);
1720 *self.source_children_pointer.borrow_mut() = None;
1721 self.current_source_child.set(None);
1722
1723 while !self.in_flight_play_promises_queue.borrow().is_empty() {
1730 self.fulfill_in_flight_play_promises(|| ());
1731 }
1732
1733 let network_state = self.network_state.get();
1738
1739 if network_state == NetworkState::Loading || network_state == NetworkState::Idle {
1743 self.queue_media_element_task_to_fire_event(atom!("abort"));
1744 }
1745
1746 self.reset_media_player();
1748
1749 if network_state != NetworkState::Empty {
1751 self.queue_media_element_task_to_fire_event(atom!("emptied"));
1754
1755 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1758 current_fetch_context.cancel(CancelReason::Abort);
1759 }
1760
1761 self.AudioTracks(cx).clear();
1766 self.VideoTracks(CanGc::from_cx(cx)).clear();
1767
1768 if self.ready_state.get() != ReadyState::HaveNothing {
1770 self.change_ready_state(ReadyState::HaveNothing);
1771 }
1772
1773 if !self.Paused() {
1775 self.paused.set(true);
1777
1778 self.take_pending_play_promises(Err(Error::Abort(None)));
1781 self.fulfill_in_flight_play_promises(|| ());
1782 }
1783
1784 self.seeking.set(false);
1786
1787 self.current_seek_position.set(f64::NAN);
1788
1789 self.current_playback_position.set(0.);
1794 if self.official_playback_position.get() != 0. {
1795 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
1796 }
1797 self.official_playback_position.set(0.);
1798
1799 self.duration.set(f64::NAN);
1803 }
1804
1805 self.playback_rate.set(self.default_playback_rate.get());
1807
1808 self.error.set(None);
1810 self.autoplaying.set(true);
1811
1812 self.invoke_resource_selection_algorithm(cx);
1814
1815 }
1817
1818 fn queue_media_element_task_to_fire_event(&self, name: Atom) {
1821 let this = Trusted::new(self);
1822 let generation_id = self.generation_id.get();
1823
1824 self.owner_global()
1825 .task_manager()
1826 .media_element_task_source()
1827 .queue(task!(queue_event: move |cx| {
1828 let this = this.root();
1829 if generation_id != this.generation_id.get() {
1830 return;
1831 }
1832
1833 this.upcast::<EventTarget>().fire_event(cx, name);
1834 }));
1835 }
1836
1837 fn push_pending_play_promise(&self, promise: &Rc<Promise>) {
1839 self.pending_play_promises
1840 .borrow_mut()
1841 .push(promise.clone());
1842 }
1843
1844 fn take_pending_play_promises(&self, result: ErrorResult) {
1855 let pending_play_promises = std::mem::take(&mut *self.pending_play_promises.borrow_mut());
1856 self.in_flight_play_promises_queue
1857 .borrow_mut()
1858 .push_back((pending_play_promises.into(), result));
1859 }
1860
1861 fn fulfill_in_flight_play_promises<F>(&self, f: F)
1870 where
1871 F: FnOnce(),
1872 {
1873 let (promises, result) = self
1874 .in_flight_play_promises_queue
1875 .borrow_mut()
1876 .pop_front()
1877 .expect("there should be at least one list of in flight play promises");
1878 f();
1879 for promise in &*promises {
1880 match result {
1881 Ok(ref value) => promise.resolve_native(value, CanGc::deprecated_note()),
1882 Err(ref error) => promise.reject_error(error.clone(), CanGc::deprecated_note()),
1883 }
1884 }
1885 }
1886
1887 pub(crate) fn handle_source_child_insertion(
1888 &self,
1889 source: &HTMLSourceElement,
1890 cx: &mut js::context::JSContext,
1891 ) {
1892 if self.upcast::<Element>().has_attribute(&local_name!("src")) {
1896 return;
1897 }
1898
1899 if self.network_state.get() == NetworkState::Empty {
1900 self.invoke_resource_selection_algorithm(cx);
1901 return;
1902 }
1903
1904 if self.load_state.get() != LoadState::WaitingForSource {
1908 return;
1909 }
1910
1911 self.load_state.set(LoadState::LoadingFromSourceChild);
1912
1913 *self.source_children_pointer.borrow_mut() =
1914 Some(SourceChildrenPointer::new(DomRoot::from_ref(source), true));
1915
1916 let task = MediaElementMicrotask::SelectNextSourceChildAfterWait {
1918 elem: DomRoot::from_ref(self),
1919 generation_id: self.generation_id.get(),
1920 };
1921
1922 ScriptThread::await_stable_state(Microtask::MediaElement(task));
1923 }
1924
1925 fn select_next_source_child_after_wait(&self, cx: &mut js::context::JSContext) {
1927 self.delay_load_event(true, cx);
1930
1931 self.network_state.set(NetworkState::Loading);
1933
1934 self.select_next_source_child(cx);
1936 }
1937
1938 fn media_data_processing_failure_steps(&self) {
1943 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1945 current_fetch_context.cancel(CancelReason::Error);
1946 }
1947
1948 self.resource_selection_algorithm_failure_steps();
1950 }
1951
1952 fn media_data_processing_fatal_steps(&self, error: u16, cx: &mut js::context::JSContext) {
1956 *self.source_children_pointer.borrow_mut() = None;
1957 self.current_source_child.set(None);
1958
1959 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1961 current_fetch_context.cancel(CancelReason::Error);
1962 }
1963
1964 self.error.set(Some(&*MediaError::new(
1967 &self.owner_window(),
1968 error,
1969 CanGc::from_cx(cx),
1970 )));
1971
1972 self.network_state.set(NetworkState::Idle);
1974
1975 self.delay_load_event(false, cx);
1978
1979 self.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1981
1982 }
1984
1985 fn seek(&self, time: f64, _approximate_for_speed: bool) {
1987 self.show_poster.set(false);
1989
1990 if self.ready_state.get() == ReadyState::HaveNothing {
1992 return;
1993 }
1994
1995 self.current_seek_position.set(f64::NAN);
1999
2000 self.seeking.set(true);
2002
2003 let time = f64::min(time, self.Duration());
2009
2010 let time = f64::max(time, self.earliest_possible_position());
2013
2014 let seekable = self.seekable();
2020
2021 if seekable.is_empty() {
2022 self.seeking.set(false);
2023 return;
2024 }
2025
2026 let mut nearest_seekable_position = 0.0;
2027 let mut in_seekable_range = false;
2028 let mut nearest_seekable_distance = f64::MAX;
2029 for i in 0..seekable.len() {
2030 let start = seekable.start(i).unwrap().abs();
2031 let end = seekable.end(i).unwrap().abs();
2032 if time >= start && time <= end {
2033 nearest_seekable_position = time;
2034 in_seekable_range = true;
2035 break;
2036 } else if time < start {
2037 let distance = start - time;
2038 if distance < nearest_seekable_distance {
2039 nearest_seekable_distance = distance;
2040 nearest_seekable_position = start;
2041 }
2042 } else {
2043 let distance = time - end;
2044 if distance < nearest_seekable_distance {
2045 nearest_seekable_distance = distance;
2046 nearest_seekable_position = end;
2047 }
2048 }
2049 }
2050 let time = if in_seekable_range {
2051 time
2052 } else {
2053 nearest_seekable_position
2054 };
2055
2056 self.queue_media_element_task_to_fire_event(atom!("seeking"));
2067
2068 self.current_playback_position.set(time);
2070
2071 if let Some(ref player) = *self.player.borrow() &&
2072 let Err(error) = player.lock().unwrap().seek(time)
2073 {
2074 error!("Could not seek player: {error:?}");
2075 }
2076
2077 self.current_seek_position.set(time);
2078
2079 }
2085
2086 fn seek_end(&self) {
2088 self.official_playback_position
2091 .set(self.current_playback_position.get());
2092
2093 self.seeking.set(false);
2095
2096 self.current_seek_position.set(f64::NAN);
2097
2098 self.time_marches_on();
2100
2101 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
2104
2105 self.queue_media_element_task_to_fire_event(atom!("seeked"));
2108 }
2109
2110 pub(crate) fn set_poster_frame(&self, image: Option<Arc<RasterImage>>) {
2112 if pref!(media_testing_enabled) && image.is_some() {
2113 self.queue_media_element_task_to_fire_event(atom!("postershown"));
2114 }
2115
2116 self.video_renderer.lock().unwrap().set_poster_frame(image);
2117
2118 self.upcast::<Node>().dirty(NodeDamage::Other);
2119 }
2120
2121 fn player_id(&self) -> Option<usize> {
2122 self.player
2123 .borrow()
2124 .as_ref()
2125 .map(|player| player.lock().unwrap().get_id())
2126 }
2127
2128 fn create_media_player(&self, resource: &Resource) -> Result<(), ()> {
2129 let stream_type = match *resource {
2130 Resource::Object => {
2131 if let Some(ref src_object) = *self.src_object.borrow() {
2132 match src_object {
2133 SrcObject::MediaStream(_) => StreamType::Stream,
2134 _ => StreamType::Seekable,
2135 }
2136 } else {
2137 return Err(());
2138 }
2139 },
2140 _ => StreamType::Seekable,
2141 };
2142
2143 let window = self.owner_window();
2144 let (action_sender, action_receiver) = ipc::channel::<PlayerEvent>().unwrap();
2145 let video_renderer: Option<Arc<Mutex<dyn VideoFrameRenderer>>> = match self.media_type_id()
2146 {
2147 HTMLMediaElementTypeId::HTMLAudioElement => None,
2148 HTMLMediaElementTypeId::HTMLVideoElement => Some(self.video_renderer.clone()),
2149 };
2150
2151 let audio_renderer = self.audio_renderer.borrow().as_ref().cloned();
2152
2153 let pipeline_id = window.pipeline_id();
2154 let client_context_id =
2155 ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
2156 let player = ServoMedia::get().create_player(
2157 &client_context_id,
2158 stream_type,
2159 action_sender,
2160 video_renderer,
2161 audio_renderer,
2162 Box::new(window.get_player_context()),
2163 );
2164 let player_id = {
2165 let player_guard = player.lock().unwrap();
2166
2167 if let Err(error) = player_guard.set_mute(self.muted.get()) {
2168 warn!("Could not set mute state: {error:?}");
2169 }
2170
2171 player_guard.get_id()
2172 };
2173
2174 *self.player.borrow_mut() = Some(player);
2175
2176 let event_handler = Arc::new(Mutex::new(HTMLMediaElementEventHandler::new(self)));
2177 let weak_event_handler = Arc::downgrade(&event_handler);
2178 *self.event_handler.borrow_mut() = Some(event_handler);
2179
2180 let task_source = self
2181 .owner_global()
2182 .task_manager()
2183 .media_element_task_source()
2184 .to_sendable();
2185 ROUTER.add_typed_route(
2186 action_receiver,
2187 Box::new(move |message| {
2188 let event = message.unwrap();
2189 let weak_event_handler = weak_event_handler.clone();
2190
2191 task_source.queue(task!(handle_player_event: move |cx| {
2192 trace!("HTMLMediaElement event: {event:?}");
2193
2194 let Some(event_handler) = weak_event_handler.upgrade() else {
2195 return;
2196 };
2197
2198 event_handler.lock().unwrap().handle_player_event(player_id, event, cx);
2199 }));
2200 }),
2201 );
2202
2203 let task_source = self
2204 .owner_global()
2205 .task_manager()
2206 .media_element_task_source()
2207 .to_sendable();
2208 let weak_video_renderer = Arc::downgrade(&self.video_renderer);
2209
2210 self.video_renderer
2211 .lock()
2212 .unwrap()
2213 .setup(player_id, task_source, weak_video_renderer);
2214
2215 Ok(())
2216 }
2217
2218 fn reset_media_player(&self) {
2219 if self.player.borrow().is_none() {
2220 return;
2221 }
2222
2223 if let Some(ref player) = *self.player.borrow() &&
2224 let Err(error) = player.lock().unwrap().stop()
2225 {
2226 error!("Could not stop player: {error:?}");
2227 }
2228
2229 *self.player.borrow_mut() = None;
2230 self.video_renderer.lock().unwrap().reset();
2231 *self.event_handler.borrow_mut() = None;
2232
2233 if let Some(video_element) = self.downcast::<HTMLVideoElement>() {
2234 video_element.set_natural_dimensions(None, None);
2235 }
2236 }
2237
2238 pub(crate) fn set_audio_track(&self, idx: usize, enabled: bool) {
2239 if let Some(ref player) = *self.player.borrow() &&
2240 let Err(error) = player.lock().unwrap().set_audio_track(idx as i32, enabled)
2241 {
2242 warn!("Could not set audio track {error:?}");
2243 }
2244 }
2245
2246 pub(crate) fn set_video_track(&self, idx: usize, enabled: bool) {
2247 if let Some(ref player) = *self.player.borrow() &&
2248 let Err(error) = player.lock().unwrap().set_video_track(idx as i32, enabled)
2249 {
2250 warn!("Could not set video track: {error:?}");
2251 }
2252 }
2253
2254 fn direction_of_playback(&self) -> PlaybackDirection {
2256 if self.playback_rate.get() >= 0. {
2259 PlaybackDirection::Forwards
2260 } else {
2261 PlaybackDirection::Backwards
2262 }
2263 }
2264
2265 fn ended_playback(&self, loop_condition: LoopCondition) -> bool {
2267 if self.ready_state.get() < ReadyState::HaveMetadata {
2271 return false;
2272 }
2273
2274 let playback_position = self.current_playback_position.get();
2275
2276 match self.direction_of_playback() {
2277 PlaybackDirection::Forwards => {
2281 playback_position >= self.Duration() &&
2282 (loop_condition == LoopCondition::Ignored || !self.Loop())
2283 },
2284 PlaybackDirection::Backwards => playback_position <= self.earliest_possible_position(),
2287 }
2288 }
2289
2290 fn end_of_playback_in_forwards_direction(&self) {
2292 if self.Loop() {
2298 self.seek(
2299 self.earliest_possible_position(),
2300 false,
2301 );
2302 return;
2303 }
2304
2305 let this = Trusted::new(self);
2310 let generation_id = self.generation_id.get();
2311
2312 self.owner_global()
2313 .task_manager()
2314 .media_element_task_source()
2315 .queue(task!(reaches_the_end_steps: move |cx| {
2316 let this = this.root();
2317 if generation_id != this.generation_id.get() {
2318 return;
2319 }
2320
2321 this.upcast::<EventTarget>().fire_event(cx, atom!("timeupdate"));
2323
2324 if this.ended_playback(LoopCondition::Included) &&
2327 this.direction_of_playback() == PlaybackDirection::Forwards &&
2328 !this.Paused() {
2329 this.paused.set(true);
2331
2332 this.upcast::<EventTarget>().fire_event(cx, atom!("pause"));
2334
2335 this.take_pending_play_promises(Err(Error::Abort(None)));
2338 this.fulfill_in_flight_play_promises(|| ());
2339 }
2340
2341 this.upcast::<EventTarget>().fire_event(cx, atom!("ended"));
2343 }));
2344
2345 self.change_ready_state(ReadyState::HaveCurrentData);
2347 }
2348
2349 fn end_of_playback_in_backwards_direction(&self) {
2351 if self.current_playback_position.get() <= self.earliest_possible_position() {
2356 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
2357 }
2358 }
2359
2360 fn playback_end(&self) {
2361 if self.seeking.get() {
2363 return;
2364 }
2365
2366 match self.direction_of_playback() {
2367 PlaybackDirection::Forwards => self.end_of_playback_in_forwards_direction(),
2368 PlaybackDirection::Backwards => self.end_of_playback_in_backwards_direction(),
2369 }
2370 }
2371
2372 fn playback_error(&self, error: &str, cx: &mut js::context::JSContext) {
2373 error!("Player error: {:?}", error);
2374
2375 if self.in_error_state() {
2379 return;
2380 }
2381
2382 if self.ready_state.get() == ReadyState::HaveNothing {
2384 self.media_data_processing_failure_steps();
2387 } else {
2388 self.media_data_processing_fatal_steps(MEDIA_ERR_DECODE, cx);
2390 }
2391 }
2392
2393 fn playback_metadata_updated(
2394 &self,
2395 cx: &mut JSContext,
2396 metadata: &servo_media::player::metadata::Metadata,
2397 ) {
2398 if self.ready_state.get() != ReadyState::HaveNothing {
2401 return;
2402 }
2403
2404 for (i, _track) in metadata.audio_tracks.iter().enumerate() {
2407 let audio_track_list = self.AudioTracks(cx);
2408
2409 let kind = match i {
2411 0 => DOMString::from("main"),
2412 _ => DOMString::new(),
2413 };
2414
2415 let audio_track = AudioTrack::new(
2416 cx,
2417 self.global().as_window(),
2418 DOMString::new(),
2419 kind,
2420 DOMString::new(),
2421 DOMString::new(),
2422 Some(&*audio_track_list),
2423 );
2424
2425 audio_track_list.add(&audio_track);
2428
2429 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
2436 let fragment = MediaFragmentParser::from(servo_url);
2437 if let Some(id) = fragment.id() &&
2438 audio_track.id() == id
2439 {
2440 audio_track_list.set_enabled(audio_track_list.len() - 1, true);
2441 }
2442
2443 if fragment.tracks().contains(&audio_track.kind().into()) {
2444 audio_track_list.set_enabled(audio_track_list.len() - 1, true);
2445 }
2446 }
2447
2448 if audio_track_list.enabled_index().is_none() {
2453 audio_track_list.set_enabled(audio_track_list.len() - 1, true);
2454 }
2455
2456 let event = TrackEvent::new(
2459 self.global().as_window(),
2460 atom!("addtrack"),
2461 false,
2462 false,
2463 &Some(VideoTrackOrAudioTrackOrTextTrack::AudioTrack(audio_track)),
2464 CanGc::from_cx(cx),
2465 );
2466
2467 event
2468 .upcast::<Event>()
2469 .fire(cx, audio_track_list.upcast::<EventTarget>());
2470 }
2471
2472 for (i, _track) in metadata.video_tracks.iter().enumerate() {
2474 let video_track_list = self.VideoTracks(CanGc::from_cx(cx));
2475
2476 let kind = match i {
2478 0 => DOMString::from("main"),
2479 _ => DOMString::new(),
2480 };
2481
2482 let video_track = VideoTrack::new(
2483 self.global().as_window(),
2484 DOMString::new(),
2485 kind,
2486 DOMString::new(),
2487 DOMString::new(),
2488 Some(&*video_track_list),
2489 CanGc::from_cx(cx),
2490 );
2491
2492 video_track_list.add(&video_track);
2495
2496 if let Some(track) = video_track_list.item(0) &&
2503 let Some(servo_url) = self.resource_url.borrow().as_ref()
2504 {
2505 let fragment = MediaFragmentParser::from(servo_url);
2506 if let Some(id) = fragment.id() {
2507 if track.id() == id {
2508 video_track_list.set_selected(0, true);
2509 }
2510 } else if fragment.tracks().contains(&track.kind().into()) {
2511 video_track_list.set_selected(0, true);
2512 }
2513 }
2514
2515 if video_track_list.selected_index().is_none() {
2521 video_track_list.set_selected(video_track_list.len() - 1, true);
2522 }
2523
2524 let event = TrackEvent::new(
2527 self.global().as_window(),
2528 atom!("addtrack"),
2529 false,
2530 false,
2531 &Some(VideoTrackOrAudioTrackOrTextTrack::VideoTrack(video_track)),
2532 CanGc::from_cx(cx),
2533 );
2534
2535 event
2536 .upcast::<Event>()
2537 .fire(cx, video_track_list.upcast::<EventTarget>());
2538 }
2539
2540 let earliest_possible_position = self.earliest_possible_position();
2553 self.current_playback_position
2554 .set(earliest_possible_position);
2555 self.official_playback_position
2556 .set(earliest_possible_position);
2557
2558 self.duration.set(
2564 metadata
2565 .duration
2566 .map_or(f64::INFINITY, |duration| duration.as_secs_f64()),
2567 );
2568 self.queue_media_element_task_to_fire_event(atom!("durationchange"));
2569
2570 if let Some(video_element) = self.downcast::<HTMLVideoElement>() {
2574 video_element.set_natural_dimensions(Some(metadata.width), Some(metadata.height));
2575 self.queue_media_element_task_to_fire_event(atom!("resize"));
2576 }
2577
2578 self.change_ready_state(ReadyState::HaveMetadata);
2580
2581 let mut jumped = false;
2583
2584 if self.default_playback_start_position.get() > 0. {
2587 self.seek(
2588 self.default_playback_start_position.get(),
2589 false,
2590 );
2591 jumped = true;
2592 }
2593
2594 self.default_playback_start_position.set(0.);
2596
2597 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
2602 let fragment = MediaFragmentParser::from(servo_url);
2603 if let Some(initial_playback_position) = fragment.start() &&
2604 initial_playback_position > 0. &&
2605 initial_playback_position < self.duration.get() &&
2606 !jumped
2607 {
2608 self.seek(
2609 initial_playback_position,
2610 false,
2611 )
2612 }
2613 }
2614
2615 let global = self.global();
2622 let window = global.as_window();
2623
2624 window.Navigator().MediaSession().update_title(
2626 metadata
2627 .title
2628 .clone()
2629 .unwrap_or(window.get_url().into_string()),
2630 );
2631 }
2632
2633 fn playback_duration_changed(&self, duration: Option<Duration>) {
2634 let duration = duration.map_or(f64::INFINITY, |duration| duration.as_secs_f64());
2635
2636 if self.duration.get() == duration {
2637 return;
2638 }
2639
2640 self.duration.set(duration);
2641
2642 self.queue_media_element_task_to_fire_event(atom!("durationchange"));
2648
2649 if self.current_playback_position.get() > duration {
2653 self.seek(duration, false);
2654 }
2655 }
2656
2657 fn playback_video_frame_updated(&self) {
2658 let Some(video_element) = self.downcast::<HTMLVideoElement>() else {
2659 return;
2660 };
2661
2662 if self.ready_state.get() == ReadyState::HaveNothing {
2671 return;
2672 }
2673
2674 if let Some(frame) = self.video_renderer.lock().unwrap().current_frame {
2675 if video_element
2676 .set_natural_dimensions(Some(frame.width as u32), Some(frame.height as u32))
2677 {
2678 self.queue_media_element_task_to_fire_event(atom!("resize"));
2679 } else {
2680 self.upcast::<Node>().dirty(NodeDamage::Other);
2683 }
2684 }
2685 }
2686
2687 fn playback_need_data(&self) {
2688 if let Some(ref current_fetch_context) = *self.current_fetch_context.borrow() &&
2692 let Some(reason) = current_fetch_context.cancel_reason()
2693 {
2694 if *reason == CancelReason::Backoff {
2700 self.seek(
2701 self.current_playback_position.get(),
2702 false,
2703 );
2704 }
2705 return;
2706 }
2707
2708 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() &&
2709 let Err(e) = {
2710 let mut data_source = current_fetch_context.data_source().borrow_mut();
2711 data_source.set_locked(false);
2712 data_source.process_into_player_from_queue(self.player.borrow().as_ref().unwrap())
2713 }
2714 {
2715 if e == PlayerError::EnoughData {
2720 current_fetch_context.cancel(CancelReason::Backoff);
2721 }
2722 }
2723 }
2724
2725 fn playback_enough_data(&self) {
2726 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() &&
2731 current_fetch_context.is_seekable()
2732 {
2733 current_fetch_context.cancel(CancelReason::Backoff);
2734 }
2735 }
2736
2737 fn playback_position_changed(&self, position: f64) {
2738 if self.seeking.get() {
2740 return;
2741 }
2742
2743 let _ = self
2744 .played
2745 .borrow_mut()
2746 .add(self.current_playback_position.get(), position);
2747 self.current_playback_position.set(position);
2748 self.official_playback_position.set(position);
2749 self.time_marches_on();
2750
2751 let media_position_state =
2752 MediaPositionState::new(self.duration.get(), self.playback_rate.get(), position);
2753 debug!(
2754 "Sending media session event set position state {:?}",
2755 media_position_state
2756 );
2757 self.send_media_session_event(MediaSessionEvent::SetPositionState(media_position_state));
2758 }
2759
2760 fn playback_seek_done(&self, position: f64) {
2761 let delta = (position - self.current_seek_position.get()).abs();
2764 if !self.seeking.get() || delta > SEEK_POSITION_THRESHOLD {
2765 return;
2766 }
2767
2768 let task = MediaElementMicrotask::Seeked {
2771 elem: DomRoot::from_ref(self),
2772 generation_id: self.generation_id.get(),
2773 };
2774
2775 ScriptThread::await_stable_state(Microtask::MediaElement(task));
2776 }
2777
2778 fn playback_state_changed(&self, state: &PlaybackState) {
2779 let mut media_session_playback_state = MediaSessionPlaybackState::None_;
2780 match *state {
2781 PlaybackState::Paused => {
2782 media_session_playback_state = MediaSessionPlaybackState::Paused;
2783 if self.ready_state.get() == ReadyState::HaveMetadata {
2784 self.change_ready_state(ReadyState::HaveEnoughData);
2785 }
2786 },
2787 PlaybackState::Playing => {
2788 media_session_playback_state = MediaSessionPlaybackState::Playing;
2789 if self.ready_state.get() == ReadyState::HaveMetadata {
2790 self.change_ready_state(ReadyState::HaveEnoughData);
2791 }
2792 },
2793 PlaybackState::Buffering => {
2794 return;
2798 },
2799 _ => {},
2800 };
2801 debug!(
2802 "Sending media session event playback state changed to {:?}",
2803 media_session_playback_state
2804 );
2805 self.send_media_session_event(MediaSessionEvent::PlaybackStateChange(
2806 media_session_playback_state,
2807 ));
2808 }
2809
2810 fn seekable(&self) -> TimeRangesContainer {
2811 let mut seekable = TimeRangesContainer::default();
2812 if let Some(ref player) = *self.player.borrow() {
2813 let ranges = player.lock().unwrap().seekable();
2814 for range in ranges {
2815 let _ = seekable.add(range.start, range.end);
2816 }
2817 }
2818 seekable
2819 }
2820
2821 fn earliest_possible_position(&self) -> f64 {
2823 self.seekable()
2824 .start(0)
2825 .unwrap_or_else(|_| self.current_playback_position.get())
2826 }
2827
2828 fn render_controls(&self, cx: &mut JSContext) {
2829 if self.upcast::<Element>().is_shadow_host() {
2830 return;
2832 }
2833
2834 let shadow_root = self.upcast::<Element>().attach_ua_shadow_root(cx, false);
2837 let document = self.owner_document();
2838 let script = Element::create(
2839 cx,
2840 QualName::new(None, ns!(html), local_name!("script")),
2841 None,
2842 &document,
2843 ElementCreator::ScriptCreated,
2844 CustomElementCreationMode::Asynchronous,
2845 None,
2846 );
2847 let id = Uuid::new_v4().to_string();
2853 document.register_media_controls(&id, &shadow_root);
2854 let media_controls_script = MEDIA_CONTROL_JS.replace("@@@id@@@", &id);
2855 *self.media_controls_id.borrow_mut() = Some(id);
2856 script
2857 .upcast::<Node>()
2858 .set_text_content_for_element(cx, Some(DOMString::from(media_controls_script)));
2859 if let Err(e) = shadow_root
2860 .upcast::<Node>()
2861 .AppendChild(cx, script.upcast::<Node>())
2862 {
2863 warn!("Could not render media controls {:?}", e);
2864 return;
2865 }
2866
2867 let style = Element::create(
2868 cx,
2869 QualName::new(None, ns!(html), local_name!("style")),
2870 None,
2871 &document,
2872 ElementCreator::ScriptCreated,
2873 CustomElementCreationMode::Asynchronous,
2874 None,
2875 );
2876
2877 style
2878 .upcast::<Node>()
2879 .set_text_content_for_element(cx, Some(DOMString::from(MEDIA_CONTROL_CSS)));
2880
2881 if let Err(e) = shadow_root
2882 .upcast::<Node>()
2883 .AppendChild(cx, style.upcast::<Node>())
2884 {
2885 warn!("Could not render media controls {:?}", e);
2886 }
2887
2888 self.upcast::<Node>().dirty(NodeDamage::Other);
2889 }
2890
2891 fn remove_controls(&self) {
2892 if let Some(id) = self.media_controls_id.borrow_mut().take() {
2893 self.owner_document().unregister_media_controls(&id);
2894 }
2895 }
2896
2897 pub(crate) fn get_current_frame(&self) -> Option<VideoFrame> {
2899 self.video_renderer
2900 .lock()
2901 .unwrap()
2902 .current_frame_holder
2903 .as_ref()
2904 .map(|holder| holder.get_frame())
2905 }
2906
2907 pub(crate) fn get_current_frame_to_present(&self) -> Option<MediaFrame> {
2910 let (current_frame, poster_frame) = {
2911 let renderer = self.video_renderer.lock().unwrap();
2912 (renderer.current_frame, renderer.poster_frame)
2913 };
2914
2915 if (self.show_poster.get() || current_frame.is_none()) && poster_frame.is_some() {
2918 return poster_frame;
2919 }
2920
2921 current_frame
2922 }
2923
2924 pub(crate) fn set_audio_renderer(
2929 &self,
2930 audio_renderer: Option<Arc<Mutex<dyn AudioRenderer>>>,
2931 cx: &mut js::context::JSContext,
2932 ) {
2933 *self.audio_renderer.borrow_mut() = audio_renderer;
2934
2935 let had_player = {
2936 if let Some(ref player) = *self.player.borrow() {
2937 if let Err(error) = player.lock().unwrap().stop() {
2938 error!("Could not stop player: {error:?}");
2939 }
2940 true
2941 } else {
2942 false
2943 }
2944 };
2945
2946 if had_player {
2947 self.media_element_load_algorithm(cx);
2948 }
2949 }
2950
2951 fn send_media_session_event(&self, event: MediaSessionEvent) {
2952 let global = self.global();
2953 let media_session = global.as_window().Navigator().MediaSession();
2954
2955 media_session.register_media_instance(self);
2956
2957 media_session.send_event(event);
2958 }
2959
2960 pub(crate) fn origin_is_clean(&self) -> bool {
2962 if self.src_object.borrow().is_some() {
2964 return true;
2967 }
2968
2969 if self.resource_url.borrow().is_some() {
2971 if let Some(ref current_fetch_context) = *self.current_fetch_context.borrow() {
2975 return current_fetch_context.origin_is_clean();
2976 }
2977 }
2978
2979 true
2980 }
2981}
2982
2983impl HTMLMediaElementMethods<crate::DomTypeHolder> for HTMLMediaElement {
2984 fn NetworkState(&self) -> u16 {
2986 self.network_state.get() as u16
2987 }
2988
2989 fn ReadyState(&self) -> u16 {
2991 self.ready_state.get() as u16
2992 }
2993
2994 make_bool_getter!(Autoplay, "autoplay");
2996 make_bool_setter!(SetAutoplay, "autoplay");
2998
2999 make_bool_getter!(Loop, "loop");
3001 make_bool_setter!(SetLoop, "loop");
3003
3004 make_bool_getter!(DefaultMuted, "muted");
3006 make_bool_setter!(SetDefaultMuted, "muted");
3008
3009 make_bool_getter!(Controls, "controls");
3011 make_bool_setter!(SetControls, "controls");
3013
3014 make_url_getter!(Src, "src");
3016
3017 make_url_setter!(SetSrc, "src");
3019
3020 fn GetCrossOrigin(&self) -> Option<DOMString> {
3022 reflect_cross_origin_attribute(self.upcast::<Element>())
3023 }
3024 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
3026 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
3027 }
3028
3029 fn Muted(&self) -> bool {
3031 self.muted.get()
3032 }
3033
3034 fn SetMuted(&self, _cx: &mut JSContext, value: bool) {
3036 if self.muted.get() == value {
3037 return;
3038 }
3039
3040 self.muted.set(value);
3041
3042 if let Some(ref player) = *self.player.borrow() &&
3043 let Err(error) = player.lock().unwrap().set_mute(value)
3044 {
3045 warn!("Could not set mute state: {error:?}");
3046 }
3047
3048 self.queue_media_element_task_to_fire_event(atom!("volumechange"));
3051
3052 if !self.is_allowed_to_play() {
3055 self.internal_pause_steps();
3056 }
3057 }
3058
3059 fn GetSrcObject(&self) -> Option<MediaStreamOrBlob> {
3061 (*self.src_object.borrow())
3062 .as_ref()
3063 .map(|src_object| match src_object {
3064 SrcObject::Blob(blob) => MediaStreamOrBlob::Blob(DomRoot::from_ref(blob)),
3065 SrcObject::MediaStream(stream) => {
3066 MediaStreamOrBlob::MediaStream(DomRoot::from_ref(stream))
3067 },
3068 })
3069 }
3070
3071 fn SetSrcObject(&self, cx: &mut js::context::JSContext, value: Option<MediaStreamOrBlob>) {
3073 *self.src_object.borrow_mut() = value.map(|value| value.into());
3074 self.media_element_load_algorithm(cx);
3075 }
3076
3077 make_enumerated_getter!(
3080 Preload,
3081 "preload",
3082 "none" | "metadata" | "auto",
3083 missing => "auto",
3084 invalid => "auto"
3085 );
3086
3087 make_setter!(SetPreload, "preload");
3089
3090 fn CurrentSrc(&self) -> USVString {
3092 USVString(self.current_src.borrow().clone())
3093 }
3094
3095 fn Load(&self, cx: &mut js::context::JSContext) {
3097 self.media_element_load_algorithm(cx);
3098 }
3099
3100 fn CanPlayType(&self, type_: DOMString) -> CanPlayTypeResult {
3102 match ServoMedia::get().can_play_type(&type_.str()) {
3103 SupportsMediaType::No => CanPlayTypeResult::_empty,
3104 SupportsMediaType::Maybe => CanPlayTypeResult::Maybe,
3105 SupportsMediaType::Probably => CanPlayTypeResult::Probably,
3106 }
3107 }
3108
3109 fn GetError(&self) -> Option<DomRoot<MediaError>> {
3111 self.error.get()
3112 }
3113
3114 fn Play(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
3116 let promise = Promise::new_in_realm(cx);
3117
3118 if self
3125 .error
3126 .get()
3127 .is_some_and(|e| e.Code() == MEDIA_ERR_SRC_NOT_SUPPORTED)
3128 {
3129 promise.reject_error_with_cx(cx, Error::NotSupported(None));
3130 return promise;
3131 }
3132
3133 self.push_pending_play_promise(&promise);
3136
3137 self.internal_play_steps(cx);
3139
3140 promise
3142 }
3143
3144 fn Pause(&self, cx: &mut js::context::JSContext) {
3146 if self.network_state.get() == NetworkState::Empty {
3149 self.invoke_resource_selection_algorithm(cx);
3150 }
3151
3152 self.internal_pause_steps();
3154 }
3155
3156 fn Paused(&self) -> bool {
3158 self.paused.get()
3159 }
3160
3161 fn GetDefaultPlaybackRate(&self) -> Fallible<Finite<f64>> {
3163 Ok(Finite::wrap(self.default_playback_rate.get()))
3164 }
3165
3166 fn SetDefaultPlaybackRate(&self, _cx: &mut JSContext, value: Finite<f64>) -> ErrorResult {
3168 let min_allowed = -64.0;
3171 let max_allowed = 64.0;
3172 if *value < min_allowed || *value > max_allowed {
3173 return Err(Error::NotSupported(None));
3174 }
3175
3176 if self.default_playback_rate.get() == *value {
3177 return Ok(());
3178 }
3179
3180 self.default_playback_rate.set(*value);
3181
3182 self.queue_media_element_task_to_fire_event(atom!("ratechange"));
3185
3186 Ok(())
3187 }
3188
3189 fn GetPlaybackRate(&self) -> Fallible<Finite<f64>> {
3191 Ok(Finite::wrap(self.playback_rate.get()))
3192 }
3193
3194 fn SetPlaybackRate(&self, _cx: &mut JSContext, value: Finite<f64>) -> ErrorResult {
3196 let min_allowed = -64.0;
3201 let max_allowed = 64.0;
3202 if *value < min_allowed || *value > max_allowed {
3203 return Err(Error::NotSupported(None));
3204 }
3205
3206 if self.playback_rate.get() == *value {
3207 return Ok(());
3208 }
3209
3210 self.playback_rate.set(*value);
3213
3214 if self.is_potentially_playing() &&
3215 let Some(ref player) = *self.player.borrow() &&
3216 let Err(error) = player.lock().unwrap().set_playback_rate(*value)
3217 {
3218 warn!("Could not set the playback rate: {error:?}");
3219 }
3220
3221 self.queue_media_element_task_to_fire_event(atom!("ratechange"));
3224
3225 Ok(())
3226 }
3227
3228 fn Duration(&self) -> f64 {
3230 self.duration.get()
3231 }
3232
3233 fn CurrentTime(&self) -> Finite<f64> {
3235 Finite::wrap(if self.default_playback_start_position.get() != 0. {
3236 self.default_playback_start_position.get()
3237 } else if self.seeking.get() {
3238 self.current_seek_position.get()
3243 } else {
3244 self.official_playback_position.get()
3245 })
3246 }
3247
3248 fn SetCurrentTime(&self, _cx: &mut JSContext, time: Finite<f64>) {
3250 if self.ready_state.get() == ReadyState::HaveNothing {
3251 self.default_playback_start_position.set(*time);
3252 } else {
3253 self.official_playback_position.set(*time);
3254 self.seek(*time, false);
3255 }
3256 }
3257
3258 fn Seeking(&self) -> bool {
3260 self.seeking.get()
3261 }
3262
3263 fn Ended(&self) -> bool {
3265 self.ended_playback(LoopCondition::Included) &&
3266 self.direction_of_playback() == PlaybackDirection::Forwards
3267 }
3268
3269 fn FastSeek(&self, time: Finite<f64>) {
3271 self.seek(*time, true);
3272 }
3273
3274 fn Played(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
3276 TimeRanges::new(
3277 self.global().as_window(),
3278 self.played.borrow().clone(),
3279 can_gc,
3280 )
3281 }
3282
3283 fn Seekable(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
3285 TimeRanges::new(self.global().as_window(), self.seekable(), can_gc)
3286 }
3287
3288 fn Buffered(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
3290 let mut buffered = TimeRangesContainer::default();
3291 if let Some(ref player) = *self.player.borrow() {
3292 let ranges = player.lock().unwrap().buffered();
3293 for range in ranges {
3294 let _ = buffered.add(range.start, range.end);
3295 }
3296 }
3297 TimeRanges::new(self.global().as_window(), buffered, can_gc)
3298 }
3299
3300 fn AudioTracks(&self, cx: &mut js::context::JSContext) -> DomRoot<AudioTrackList> {
3302 let window = self.owner_window();
3303 self.audio_tracks_list
3304 .or_init(|| AudioTrackList::new(cx, &window, &[], Some(self)))
3305 }
3306
3307 fn VideoTracks(&self, can_gc: CanGc) -> DomRoot<VideoTrackList> {
3309 let window = self.owner_window();
3310 self.video_tracks_list
3311 .or_init(|| VideoTrackList::new(&window, &[], Some(self), can_gc))
3312 }
3313
3314 fn TextTracks(&self, can_gc: CanGc) -> DomRoot<TextTrackList> {
3316 let window = self.owner_window();
3317 self.text_tracks_list
3318 .or_init(|| TextTrackList::new(&window, &[], can_gc))
3319 }
3320
3321 fn AddTextTrack(
3323 &self,
3324 kind: TextTrackKind,
3325 label: DOMString,
3326 language: DOMString,
3327 can_gc: CanGc,
3328 ) -> DomRoot<TextTrack> {
3329 let window = self.owner_window();
3330 let track = TextTrack::new(
3333 &window,
3334 "".into(),
3335 kind,
3336 label,
3337 language,
3338 TextTrackMode::Hidden,
3339 None,
3340 can_gc,
3341 );
3342 self.TextTracks(can_gc).add(&track);
3344 DomRoot::from_ref(&track)
3346 }
3347
3348 fn GetVolume(&self) -> Fallible<Finite<f64>> {
3350 Ok(Finite::wrap(self.volume.get()))
3351 }
3352
3353 fn SetVolume(&self, _cx: &mut JSContext, value: Finite<f64>) -> ErrorResult {
3355 let minimum_volume = 0.0;
3358 let maximum_volume = 1.0;
3359 if *value < minimum_volume || *value > maximum_volume {
3360 return Err(Error::IndexSize(None));
3361 }
3362
3363 if self.volume.get() == *value {
3364 return Ok(());
3365 }
3366
3367 self.volume.set(*value);
3368
3369 if let Some(ref player) = *self.player.borrow() &&
3370 let Err(error) = player.lock().unwrap().set_volume(*value)
3371 {
3372 warn!("Could not set the volume: {error:?}");
3373 }
3374
3375 self.queue_media_element_task_to_fire_event(atom!("volumechange"));
3378
3379 if !self.is_allowed_to_play() {
3382 self.internal_pause_steps();
3383 }
3384
3385 Ok(())
3386 }
3387}
3388
3389impl VirtualMethods for HTMLMediaElement {
3390 fn super_type(&self) -> Option<&dyn VirtualMethods> {
3391 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
3392 }
3393
3394 fn attribute_mutated(
3395 &self,
3396 cx: &mut js::context::JSContext,
3397 attr: AttrRef<'_>,
3398 mutation: AttributeMutation,
3399 ) {
3400 self.super_type()
3401 .unwrap()
3402 .attribute_mutated(cx, attr, mutation);
3403
3404 match *attr.local_name() {
3405 local_name!("muted") => {
3406 if let AttributeMutation::Set(
3410 _,
3411 AttributeMutationReason::ByCloning | AttributeMutationReason::ByParser,
3412 ) = mutation
3413 {
3414 self.SetMuted(cx, true);
3415 }
3416 },
3417 local_name!("src") => {
3418 if !mutation.is_removal() {
3423 self.media_element_load_algorithm(cx);
3424 }
3425 },
3426 local_name!("controls") => {
3427 if mutation.new_value(attr).is_some() {
3428 self.render_controls(cx);
3429 } else {
3430 self.remove_controls();
3431 }
3432 },
3433 _ => (),
3434 };
3435 }
3436
3437 fn unbind_from_tree(&self, cx: &mut js::context::JSContext, context: &UnbindContext) {
3439 self.super_type().unwrap().unbind_from_tree(cx, context);
3440
3441 self.remove_controls();
3442
3443 if context.tree_connected {
3447 let task = MediaElementMicrotask::PauseIfNotInDocument {
3448 elem: DomRoot::from_ref(self),
3449 };
3450 ScriptThread::await_stable_state(Microtask::MediaElement(task));
3451 }
3452 }
3453
3454 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
3455 self.super_type().unwrap().adopting_steps(cx, old_doc);
3456
3457 if let Some(id) = &*self.media_controls_id.borrow() {
3461 let Some(shadow_root) = self.upcast::<Element>().shadow_root() else {
3462 error!("Missing media controls shadow root");
3463 return;
3464 };
3465
3466 old_doc.unregister_media_controls(id);
3467 self.owner_document()
3468 .register_media_controls(id, &shadow_root);
3469 }
3470 }
3471}
3472
3473#[derive(JSTraceable, MallocSizeOf)]
3474pub(crate) enum MediaElementMicrotask {
3475 ResourceSelection {
3476 elem: DomRoot<HTMLMediaElement>,
3477 generation_id: u32,
3478 #[no_trace]
3479 base_url: ServoUrl,
3480 },
3481 PauseIfNotInDocument {
3482 elem: DomRoot<HTMLMediaElement>,
3483 },
3484 Seeked {
3485 elem: DomRoot<HTMLMediaElement>,
3486 generation_id: u32,
3487 },
3488 SelectNextSourceChild {
3489 elem: DomRoot<HTMLMediaElement>,
3490 generation_id: u32,
3491 },
3492 SelectNextSourceChildAfterWait {
3493 elem: DomRoot<HTMLMediaElement>,
3494 generation_id: u32,
3495 },
3496}
3497
3498impl MicrotaskRunnable for MediaElementMicrotask {
3499 fn handler(&self, cx: &mut js::context::JSContext) {
3500 match self {
3501 &MediaElementMicrotask::ResourceSelection {
3502 ref elem,
3503 generation_id,
3504 ref base_url,
3505 } => {
3506 if generation_id == elem.generation_id.get() {
3507 elem.resource_selection_algorithm_sync(base_url.clone(), cx);
3508 }
3509 },
3510 MediaElementMicrotask::PauseIfNotInDocument { elem } => {
3512 if elem.upcast::<Node>().is_connected() {
3514 return;
3515 }
3516 elem.internal_pause_steps();
3518 },
3519 &MediaElementMicrotask::Seeked {
3520 ref elem,
3521 generation_id,
3522 } => {
3523 if generation_id == elem.generation_id.get() {
3524 elem.seek_end();
3525 }
3526 },
3527 &MediaElementMicrotask::SelectNextSourceChild {
3528 ref elem,
3529 generation_id,
3530 } => {
3531 if generation_id == elem.generation_id.get() {
3532 elem.select_next_source_child(cx);
3533 }
3534 },
3535 &MediaElementMicrotask::SelectNextSourceChildAfterWait {
3536 ref elem,
3537 generation_id,
3538 } => {
3539 if generation_id == elem.generation_id.get() {
3540 elem.select_next_source_child_after_wait(cx);
3541 }
3542 },
3543 }
3544 }
3545
3546 fn enter_realm<'cx>(&self, cx: &'cx mut js::context::JSContext) -> AutoRealm<'cx> {
3547 match self {
3548 &MediaElementMicrotask::ResourceSelection { ref elem, .. } |
3549 &MediaElementMicrotask::PauseIfNotInDocument { ref elem } |
3550 &MediaElementMicrotask::Seeked { ref elem, .. } |
3551 &MediaElementMicrotask::SelectNextSourceChild { ref elem, .. } |
3552 &MediaElementMicrotask::SelectNextSourceChildAfterWait { ref elem, .. } => {
3553 enter_auto_realm(cx, &**elem)
3554 },
3555 }
3556 }
3557}
3558
3559enum Resource {
3560 Object,
3561 Url(ServoUrl),
3562}
3563
3564#[derive(Debug, MallocSizeOf, PartialEq)]
3565enum DataBuffer {
3566 Payload(Vec<u8>),
3567 EndOfStream,
3568}
3569
3570#[derive(MallocSizeOf)]
3571struct BufferedDataSource {
3572 locked: Cell<bool>,
3577 buffers: VecDeque<DataBuffer>,
3579}
3580
3581impl BufferedDataSource {
3582 fn new() -> BufferedDataSource {
3583 BufferedDataSource {
3584 locked: Cell::new(true),
3585 buffers: VecDeque::default(),
3586 }
3587 }
3588
3589 fn set_locked(&self, locked: bool) {
3590 self.locked.set(locked)
3591 }
3592
3593 fn add_buffer_to_queue(&mut self, buffer: DataBuffer) {
3594 debug_assert_ne!(
3595 self.buffers.back(),
3596 Some(&DataBuffer::EndOfStream),
3597 "The media backend not expects any further data after end of stream"
3598 );
3599
3600 self.buffers.push_back(buffer);
3601 }
3602
3603 fn process_into_player_from_queue(
3604 &mut self,
3605 player: &Arc<Mutex<dyn Player>>,
3606 ) -> Result<(), PlayerError> {
3607 if self.locked.get() {
3609 return Ok(());
3610 }
3611
3612 while let Some(buffer) = self.buffers.pop_front() {
3613 match buffer {
3614 DataBuffer::Payload(payload) => {
3615 if let Err(error) = player.lock().unwrap().push_data(payload) {
3616 warn!("Could not push input data to player: {error:?}");
3617 return Err(error);
3618 }
3619 },
3620 DataBuffer::EndOfStream => {
3621 if let Err(error) = player.lock().unwrap().end_of_stream() {
3622 warn!("Could not signal EOS to player: {error:?}");
3623 return Err(error);
3624 }
3625 },
3626 }
3627 }
3628
3629 Ok(())
3630 }
3631
3632 fn reset(&mut self) {
3633 self.locked.set(true);
3634 self.buffers.clear();
3635 }
3636}
3637
3638#[derive(Debug, MallocSizeOf, PartialEq)]
3640enum CancelReason {
3641 Backoff,
3643 Error,
3645 Abort,
3647}
3648
3649#[derive(MallocSizeOf)]
3650pub(crate) struct HTMLMediaElementFetchContext {
3651 request_id: RequestId,
3653 cancel_reason: Option<CancelReason>,
3655 is_seekable: bool,
3657 origin_clean: bool,
3659 data_source: RefCell<BufferedDataSource>,
3661 fetch_canceller: FetchCanceller,
3664}
3665
3666impl HTMLMediaElementFetchContext {
3667 fn new(
3668 request_id: RequestId,
3669 core_resource_thread: CoreResourceThread,
3670 ) -> HTMLMediaElementFetchContext {
3671 HTMLMediaElementFetchContext {
3672 request_id,
3673 cancel_reason: None,
3674 is_seekable: false,
3675 origin_clean: true,
3676 data_source: RefCell::new(BufferedDataSource::new()),
3677 fetch_canceller: FetchCanceller::new(request_id, false, core_resource_thread),
3678 }
3679 }
3680
3681 fn request_id(&self) -> RequestId {
3682 self.request_id
3683 }
3684
3685 fn is_seekable(&self) -> bool {
3686 self.is_seekable
3687 }
3688
3689 fn set_seekable(&mut self, seekable: bool) {
3690 self.is_seekable = seekable;
3691 }
3692
3693 fn origin_is_clean(&self) -> bool {
3694 self.origin_clean
3695 }
3696
3697 fn set_origin_clean(&mut self, origin_clean: bool) {
3698 self.origin_clean = origin_clean;
3699 }
3700
3701 fn data_source(&self) -> &RefCell<BufferedDataSource> {
3702 &self.data_source
3703 }
3704
3705 fn cancel(&mut self, reason: CancelReason) {
3706 if self.cancel_reason.is_some() {
3707 return;
3708 }
3709 self.cancel_reason = Some(reason);
3710 self.data_source.borrow_mut().reset();
3711 self.fetch_canceller.abort();
3712 }
3713
3714 fn cancel_reason(&self) -> &Option<CancelReason> {
3715 &self.cancel_reason
3716 }
3717}
3718
3719struct HTMLMediaElementFetchListener {
3720 element: Trusted<HTMLMediaElement>,
3722 generation_id: u32,
3724 request_id: RequestId,
3726 next_progress_event: Instant,
3728 url: ServoUrl,
3730 expected_content_length: Option<u64>,
3732 fetched_content_length: u64,
3734 content_length_to_discard: u64,
3738}
3739
3740impl FetchResponseListener for HTMLMediaElementFetchListener {
3741 fn process_request_body(&mut self, _: RequestId) {}
3742
3743 fn process_response(
3744 &mut self,
3745 cx: &mut js::context::JSContext,
3746 _: RequestId,
3747 metadata: Result<FetchMetadata, NetworkError>,
3748 ) {
3749 let element = self.element.root();
3750
3751 let (metadata, origin_clean) = match metadata {
3752 Ok(fetch_metadata) => match fetch_metadata {
3753 FetchMetadata::Unfiltered(metadata) => (Some(metadata), true),
3754 FetchMetadata::Filtered { filtered, unsafe_ } => (
3755 Some(unsafe_),
3756 matches!(
3757 filtered,
3758 FilteredMetadata::Basic(_) | FilteredMetadata::Cors(_)
3759 ),
3760 ),
3761 },
3762 Err(_) => (None, true),
3763 };
3764
3765 let (status_is_success, is_seekable) =
3766 metadata.as_ref().map_or((false, false), |metadata| {
3767 let status = &metadata.status;
3768 (status.is_success(), *status == StatusCode::PARTIAL_CONTENT)
3769 });
3770
3771 if !status_is_success {
3773 if element.ready_state.get() == ReadyState::HaveNothing {
3774 element.media_data_processing_failure_steps();
3776 } else {
3777 element.media_data_processing_fatal_steps(MEDIA_ERR_NETWORK, cx);
3779 }
3780 return;
3781 }
3782
3783 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut() {
3784 current_fetch_context.set_seekable(is_seekable);
3785 current_fetch_context.set_origin_clean(origin_clean);
3786 }
3787
3788 if let Some(metadata) = metadata.as_ref() &&
3789 let Some(headers) = metadata.headers.as_ref()
3790 {
3791 let content_length = if let Some(content_range) = headers.typed_get::<ContentRange>() {
3794 content_range.bytes_len()
3795 } else {
3796 headers
3797 .typed_get::<ContentLength>()
3798 .map(|content_length| content_length.0)
3799 };
3800
3801 if content_length != self.expected_content_length &&
3803 let Some(content_length) = content_length
3804 {
3805 self.expected_content_length = Some(content_length);
3806 }
3807 }
3808
3809 if let Err(e) = element
3811 .player
3812 .borrow()
3813 .as_ref()
3814 .unwrap()
3815 .lock()
3816 .unwrap()
3817 .set_seekable(is_seekable)
3818 {
3819 warn!("Could not set player seekable {:?}", e);
3820 }
3821
3822 if let Some(expected_content_length) = self.expected_content_length &&
3823 let Err(e) = element
3824 .player
3825 .borrow()
3826 .as_ref()
3827 .unwrap()
3828 .lock()
3829 .unwrap()
3830 .set_input_size(expected_content_length)
3831 {
3832 warn!("Could not set player input size {:?}", e);
3833 }
3834 }
3835
3836 fn process_response_chunk(
3837 &mut self,
3838 _: &mut js::context::JSContext,
3839 _: RequestId,
3840 chunk: Vec<u8>,
3841 ) {
3842 let element = self.element.root();
3843
3844 self.fetched_content_length += chunk.len() as u64;
3845
3846 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut() {
3848 if let Some(CancelReason::Backoff) = current_fetch_context.cancel_reason() {
3849 return;
3850 }
3851
3852 let payload = if !current_fetch_context.is_seekable() &&
3854 self.content_length_to_discard != 0
3855 {
3856 if chunk.len() as u64 > self.content_length_to_discard {
3857 let shrink_chunk = chunk[self.content_length_to_discard as usize..].to_vec();
3858 self.content_length_to_discard = 0;
3859 shrink_chunk
3860 } else {
3861 self.content_length_to_discard -= chunk.len() as u64;
3863 return;
3864 }
3865 } else {
3866 chunk
3867 };
3868
3869 if let Err(e) = {
3870 let mut data_source = current_fetch_context.data_source().borrow_mut();
3871 data_source.add_buffer_to_queue(DataBuffer::Payload(payload));
3872 data_source
3873 .process_into_player_from_queue(element.player.borrow().as_ref().unwrap())
3874 } {
3875 if e == PlayerError::EnoughData {
3880 current_fetch_context.cancel(CancelReason::Backoff);
3881 }
3882 return;
3883 }
3884 }
3885
3886 if Instant::now() > self.next_progress_event {
3891 element.queue_media_element_task_to_fire_event(atom!("progress"));
3892 self.next_progress_event = Instant::now() + Duration::from_millis(350);
3893 }
3894 }
3895
3896 fn process_response_eof(
3897 self,
3898 cx: &mut js::context::JSContext,
3899 _: RequestId,
3900 status: Result<(), NetworkError>,
3901 timing: ResourceFetchTiming,
3902 ) {
3903 let element = self.element.root();
3904
3905 if status.is_ok() && self.fetched_content_length != 0 {
3907 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut()
3912 {
3913 if self.expected_content_length.is_none() &&
3920 let Err(e) = element
3921 .player
3922 .borrow()
3923 .as_ref()
3924 .unwrap()
3925 .lock()
3926 .unwrap()
3927 .set_input_size(self.fetched_content_length)
3928 {
3929 warn!("Could not set player input size {:?}", e);
3930 }
3931
3932 let mut data_source = current_fetch_context.data_source().borrow_mut();
3933
3934 data_source.add_buffer_to_queue(DataBuffer::EndOfStream);
3935 let _ = data_source
3936 .process_into_player_from_queue(element.player.borrow().as_ref().unwrap());
3937 }
3938
3939 element
3941 .upcast::<EventTarget>()
3942 .fire_event(cx, atom!("progress"));
3943
3944 element.network_state.set(NetworkState::Idle);
3947
3948 element
3949 .upcast::<EventTarget>()
3950 .fire_event(cx, atom!("suspend"));
3951 } else if status.is_err() && element.ready_state.get() != ReadyState::HaveNothing {
3952 element.media_data_processing_fatal_steps(MEDIA_ERR_NETWORK, cx);
3954 } else {
3955 element.media_data_processing_failure_steps();
3958 }
3959
3960 network_listener::submit_timing(cx, &self, &status, &timing);
3961 }
3962
3963 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
3964 let global = &self.resource_timing_global();
3965 global.report_csp_violations(violations, None, None);
3966 }
3967
3968 fn should_invoke(&self) -> bool {
3969 let element = self.element.root();
3970
3971 if element.generation_id.get() != self.generation_id || element.player.borrow().is_none() {
3972 return false;
3973 }
3974
3975 let Some(ref current_fetch_context) = *element.current_fetch_context.borrow() else {
3976 return false;
3977 };
3978
3979 if current_fetch_context.request_id() != self.request_id {
3981 return false;
3982 }
3983
3984 if let Some(cancel_reason) = current_fetch_context.cancel_reason() &&
3987 matches!(*cancel_reason, CancelReason::Error | CancelReason::Abort)
3988 {
3989 return false;
3990 }
3991
3992 true
3993 }
3994}
3995
3996impl ResourceTimingListener for HTMLMediaElementFetchListener {
3997 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
3998 let initiator_type = InitiatorType::LocalName(
3999 self.element
4000 .root()
4001 .upcast::<Element>()
4002 .local_name()
4003 .to_string(),
4004 );
4005 (initiator_type, self.url.clone())
4006 }
4007
4008 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
4009 self.element.root().owner_document().global()
4010 }
4011}
4012
4013impl HTMLMediaElementFetchListener {
4014 fn new(element: &HTMLMediaElement, request_id: RequestId, url: ServoUrl, offset: u64) -> Self {
4015 Self {
4016 element: Trusted::new(element),
4017 generation_id: element.generation_id.get(),
4018 request_id,
4019 next_progress_event: Instant::now() + Duration::from_millis(350),
4020 url,
4021 expected_content_length: None,
4022 fetched_content_length: 0,
4023 content_length_to_discard: offset,
4024 }
4025 }
4026}
4027
4028#[derive(JSTraceable, MallocSizeOf)]
4032struct HTMLMediaElementEventHandler {
4033 element: WeakRef<HTMLMediaElement>,
4034}
4035
4036#[expect(unsafe_code)]
4037unsafe impl Send for HTMLMediaElementEventHandler {}
4038
4039impl HTMLMediaElementEventHandler {
4040 fn new(element: &HTMLMediaElement) -> Self {
4041 Self {
4042 element: WeakRef::new(element),
4043 }
4044 }
4045
4046 fn handle_player_event(
4047 &self,
4048 player_id: usize,
4049 event: PlayerEvent,
4050 cx: &mut js::context::JSContext,
4051 ) {
4052 let Some(element) = self.element.root() else {
4053 return;
4054 };
4055
4056 if element.player_id().is_none_or(|id| id != player_id) {
4058 return;
4059 }
4060
4061 match event {
4062 PlayerEvent::DurationChanged(duration) => element.playback_duration_changed(duration),
4063 PlayerEvent::EndOfStream => element.playback_end(),
4064 PlayerEvent::EnoughData => element.playback_enough_data(),
4065 PlayerEvent::Error(ref error) => element.playback_error(error, cx),
4066 PlayerEvent::MetadataUpdated(ref metadata) => {
4067 element.playback_metadata_updated(cx, metadata)
4068 },
4069 PlayerEvent::NeedData => element.playback_need_data(),
4070 PlayerEvent::PositionChanged(position) => element.playback_position_changed(position),
4071 PlayerEvent::SeekData(offset, seek_lock) => {
4072 element.fetch_request(Some(offset), Some(seek_lock))
4073 },
4074 PlayerEvent::SeekDone(position) => element.playback_seek_done(position),
4075 PlayerEvent::StateChanged(ref state) => element.playback_state_changed(state),
4076 PlayerEvent::VideoFrameUpdated => element.playback_video_frame_updated(),
4077 }
4078 }
4079}
4080
4081impl Drop for HTMLMediaElementEventHandler {
4082 fn drop(&mut self) {
4083 assert_in_script();
4087 }
4088}