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 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 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 |cx| {
851 let this = this.root();
852 if generation_id != this.generation_id.get() {
853 return;
854 }
855
856 this.fulfill_in_flight_play_promises(cx, |_| {});
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(cx, |cx| {
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(cx, |cx| {
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 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(&self, base_url: ServoUrl, cx: &mut JSContext) {
1098 enum Mode {
1105 Object,
1106 Attribute(String),
1107 Children(DomRoot<HTMLSourceElement>),
1108 }
1109
1110 let mode = if self.src_object.borrow().is_some() {
1112 Mode::Object
1114 } else if let Some(src) = self
1115 .upcast::<Element>()
1116 .get_attribute_string_value(&local_name!("src"))
1117 {
1118 Mode::Attribute(src)
1121 } else if let Some(source) = self
1122 .upcast::<Node>()
1123 .children_unrooted(cx.no_gc())
1124 .find_map(UnrootedDom::downcast::<HTMLSourceElement>)
1125 {
1126 Mode::Children(source.as_rooted())
1130 } else {
1131 self.load_state.set(LoadState::NotLoaded);
1134
1135 self.network_state.set(NetworkState::Empty);
1137
1138 self.delay_load_event(false, cx);
1141
1142 return;
1144 };
1145
1146 self.network_state.set(NetworkState::Loading);
1148
1149 self.queue_media_element_task_to_fire_event(atom!("loadstart"));
1152
1153 match mode {
1155 Mode::Object => {
1156 self.load_from_src_object();
1158 },
1159 Mode::Attribute(src) => {
1160 self.load_from_src_attribute(base_url, &src);
1162 },
1163 Mode::Children(source) => {
1164 self.load_from_source_child(&source);
1166 },
1167 }
1168 }
1169
1170 fn load_from_src_object(&self) {
1172 self.load_state.set(LoadState::LoadingFromSrcObject);
1173
1174 "".clone_into(&mut self.current_src.borrow_mut());
1176
1177 self.resource_fetch_algorithm(Resource::Object);
1183 }
1184
1185 fn load_from_src_attribute(&self, base_url: ServoUrl, src: &str) {
1187 self.load_state.set(LoadState::LoadingFromSrcAttribute);
1188
1189 if src.is_empty() {
1192 self.queue_dedicated_media_source_failure_steps();
1193 return;
1194 }
1195
1196 let Ok(url_record) = base_url.join(src) else {
1200 self.queue_dedicated_media_source_failure_steps();
1201 return;
1202 };
1203
1204 *self.current_src.borrow_mut() = url_record.as_str().into();
1207
1208 self.resource_fetch_algorithm(Resource::Url(url_record));
1214 }
1215
1216 fn load_from_source_child(&self, source: &HTMLSourceElement) {
1218 self.load_state.set(LoadState::LoadingFromSourceChild);
1219
1220 *self.source_children_pointer.borrow_mut() =
1227 Some(SourceChildrenPointer::new(DomRoot::from_ref(source), false));
1228
1229 let element = source.upcast::<Element>();
1230
1231 let Some(src) = element
1235 .get_attribute_string_value(&local_name!("src"))
1236 .filter(|value| !value.is_empty())
1237 else {
1238 self.load_from_source_child_failure_steps(source);
1239 return;
1240 };
1241
1242 if let Some(media) = element.get_attribute_string_value(&local_name!("media")) &&
1246 !MediaList::matches_environment(&element.owner_document(), &media)
1247 {
1248 self.load_from_source_child_failure_steps(source);
1249 return;
1250 }
1251
1252 let Ok(url_record) = source.owner_document().base_url().join(&src) else {
1256 self.load_from_source_child_failure_steps(source);
1259 return;
1260 };
1261
1262 if let Some(type_) = element.get_attribute_string_value(&local_name!("type")) &&
1267 ServoMedia::get().can_play_type(&type_) == SupportsMediaType::No
1268 {
1269 self.load_from_source_child_failure_steps(source);
1270 return;
1271 }
1272
1273 self.reset_media_player();
1275
1276 self.current_source_child.set(Some(source));
1277
1278 *self.current_src.borrow_mut() = url_record.as_str().into();
1281
1282 self.resource_fetch_algorithm(Resource::Url(url_record));
1287 }
1288
1289 fn load_from_source_child_failure_steps(&self, source: &HTMLSourceElement) {
1291 let trusted_this = Trusted::new(self);
1294 let trusted_source = Trusted::new(source);
1295 let generation_id = self.generation_id.get();
1296
1297 self.owner_global()
1298 .task_manager()
1299 .media_element_task_source()
1300 .queue(task!(queue_error_event: move |cx| {
1301 let this = trusted_this.root();
1302 if generation_id != this.generation_id.get() {
1303 return;
1304 }
1305
1306 let source = trusted_source.root();
1307 source.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1308 }));
1309
1310 let task = MediaElementMicrotask::SelectNextSourceChild {
1312 elem: DomRoot::from_ref(self),
1313 generation_id: self.generation_id.get(),
1314 };
1315
1316 ScriptThread::await_stable_state(Microtask::MediaElement(task));
1317 }
1318
1319 fn select_next_source_child(&self, cx: &mut JSContext) {
1321 self.AudioTracks(cx).clear();
1323 self.VideoTracks(CanGc::from_cx(cx)).clear();
1324
1325 let mut source_candidate = None;
1327
1328 if let Some(ref source_children_pointer) = *self.source_children_pointer.borrow() {
1336 if source_children_pointer.inclusive {
1340 for next_sibling in source_children_pointer
1341 .source_before_pointer
1342 .upcast::<Node>()
1343 .inclusively_following_siblings()
1344 {
1345 if let Some(next_source) = DomRoot::downcast::<HTMLSourceElement>(next_sibling)
1346 {
1347 source_candidate = Some(next_source);
1348 break;
1349 }
1350 }
1351 } else {
1352 for next_sibling in source_children_pointer
1353 .source_before_pointer
1354 .upcast::<Node>()
1355 .following_siblings()
1356 {
1357 if let Some(next_source) = DomRoot::downcast::<HTMLSourceElement>(next_sibling)
1358 {
1359 source_candidate = Some(next_source);
1360 break;
1361 }
1362 }
1363 };
1364 }
1365
1366 if let Some(source_candidate) = source_candidate {
1369 self.load_from_source_child(&source_candidate);
1370 return;
1371 }
1372
1373 self.load_state.set(LoadState::WaitingForSource);
1374
1375 *self.source_children_pointer.borrow_mut() = None;
1376
1377 self.network_state.set(NetworkState::NoSource);
1380
1381 self.show_poster.set(true);
1383
1384 let this = Trusted::new(self);
1387 let generation_id = self.generation_id.get();
1388
1389 self.owner_global()
1390 .task_manager()
1391 .media_element_task_source()
1392 .queue(task!(queue_delay_load_event: move |cx| {
1393 let this = this.root();
1394 if generation_id != this.generation_id.get() {
1395 return;
1396 }
1397
1398 this.delay_load_event(false, cx);
1399 }));
1400
1401 }
1404
1405 fn resource_selection_algorithm_failure_steps(&self) {
1407 match self.load_state.get() {
1408 LoadState::LoadingFromSrcObject => {
1409 self.queue_dedicated_media_source_failure_steps();
1414 },
1415 LoadState::LoadingFromSrcAttribute => {
1416 self.queue_dedicated_media_source_failure_steps();
1421 },
1422 LoadState::LoadingFromSourceChild => {
1423 if let Some(source) = self.current_source_child.take() {
1426 self.load_from_source_child_failure_steps(&source);
1427 }
1428 },
1429 _ => {},
1430 }
1431 }
1432
1433 fn fetch_request(&self, offset: Option<u64>, seek_lock: Option<SeekLock>) {
1434 if self.resource_url.borrow().is_none() && self.blob_url.borrow().is_none() {
1435 error!("Missing request url");
1436 if let Some(seek_lock) = seek_lock {
1437 seek_lock.unlock(false);
1438 }
1439 self.resource_selection_algorithm_failure_steps();
1440 return;
1441 }
1442
1443 let document = self.owner_document();
1444 let destination = match self.media_type_id() {
1445 HTMLMediaElementTypeId::HTMLAudioElement => Destination::Audio,
1446 HTMLMediaElementTypeId::HTMLVideoElement => Destination::Video,
1447 };
1448 let mut headers = HeaderMap::new();
1449 headers.insert(
1451 header::RANGE,
1452 HeaderValue::from_str(&format!("bytes={}-", offset.unwrap_or(0))).unwrap(),
1453 );
1454 let url = match self.resource_url.borrow().as_ref() {
1455 Some(url) => url.clone(),
1456 None => self.blob_url.borrow().as_ref().unwrap().clone(),
1457 };
1458
1459 let cors_setting = cors_setting_for_element(self.upcast());
1460 let global = self.global();
1461 let request = create_a_potential_cors_request(
1462 Some(document.webview_id()),
1463 url.clone(),
1464 destination,
1465 cors_setting,
1466 None,
1467 global.get_referrer(),
1468 )
1469 .with_global_scope(&global)
1470 .headers(headers)
1471 .referrer_policy(document.get_referrer_policy());
1472
1473 let mut current_fetch_context = self.current_fetch_context.borrow_mut();
1474 if let Some(ref mut current_fetch_context) = *current_fetch_context {
1475 current_fetch_context.cancel(CancelReason::Abort);
1476 }
1477
1478 *current_fetch_context = Some(HTMLMediaElementFetchContext::new(
1479 request.id,
1480 global.core_resource_thread(),
1481 ));
1482 let listener =
1483 HTMLMediaElementFetchListener::new(self, request.id, url, offset.unwrap_or(0));
1484
1485 self.owner_document().fetch_background(request, listener);
1486
1487 if let Some(seek_lock) = seek_lock {
1492 seek_lock.unlock(true);
1493 }
1494 }
1495
1496 fn eligible_for_autoplay(&self) -> bool {
1498 self.autoplaying.get() &&
1500
1501 self.Paused() &&
1503
1504 self.Autoplay() &&
1506
1507 {
1510 let document = self.owner_document();
1511
1512 !document.has_active_sandboxing_flag(
1513 SandboxingFlagSet::SANDBOXED_AUTOMATIC_FEATURES_BROWSING_CONTEXT_FLAG,
1514 )
1515 }
1516
1517 }
1520
1521 fn resource_fetch_algorithm(&self, resource: Resource) {
1523 if let Err(e) = self.create_media_player(&resource) {
1524 error!("Create media player error {:?}", e);
1525 self.resource_selection_algorithm_failure_steps();
1526 return;
1527 }
1528
1529 match resource {
1538 Resource::Url(url) => {
1539 if self.Preload() == "none" && !self.autoplaying.get() {
1544 self.network_state.set(NetworkState::Idle);
1546
1547 self.queue_media_element_task_to_fire_event(atom!("suspend"));
1550
1551 let this = Trusted::new(self);
1555 let generation_id = self.generation_id.get();
1556
1557 self.owner_global()
1558 .task_manager()
1559 .media_element_task_source()
1560 .queue(task!(queue_delay_load_event: move |cx| {
1561 let this = this.root();
1562 if generation_id != this.generation_id.get() {
1563 return;
1564 }
1565
1566 this.delay_load_event(false, cx);
1567 }));
1568
1569 return;
1578 }
1579
1580 *self.resource_url.borrow_mut() = Some(url);
1581
1582 self.fetch_request(None, None);
1584 },
1585 Resource::Object => {
1586 if let Some(ref src_object) = *self.src_object.borrow() {
1587 match src_object {
1588 SrcObject::Blob(blob) => {
1589 let blob_url = URL::CreateObjectURL(&self.global(), blob);
1590 *self.blob_url.borrow_mut() =
1591 Some(ServoUrl::parse(&blob_url.str()).expect("infallible"));
1592 self.fetch_request(None, None);
1593 },
1594 SrcObject::MediaStream(stream) => {
1595 let tracks = &*stream.get_tracks();
1596 for (pos, track) in tracks.iter().enumerate() {
1597 if self
1598 .player
1599 .borrow()
1600 .as_ref()
1601 .unwrap()
1602 .lock()
1603 .unwrap()
1604 .set_stream(&track.id(), pos == tracks.len() - 1)
1605 .is_err()
1606 {
1607 self.resource_selection_algorithm_failure_steps();
1608 }
1609 }
1610 },
1611 }
1612 }
1613 },
1614 }
1615 }
1616
1617 fn queue_dedicated_media_source_failure_steps(&self) {
1621 let this = Trusted::new(self);
1622 let generation_id = self.generation_id.get();
1623 self.take_pending_play_promises(Err(Error::NotSupported(None)));
1624 self.owner_global()
1625 .task_manager()
1626 .media_element_task_source()
1627 .queue(task!(dedicated_media_source_failure_steps: move |cx| {
1628 let this = this.root();
1629 if generation_id != this.generation_id.get() {
1630 return;
1631 }
1632
1633 this.fulfill_in_flight_play_promises(cx, |cx| {
1634 this.error.set(Some(&*MediaError::new(
1637 &this.owner_window(),
1638 MEDIA_ERR_SRC_NOT_SUPPORTED, CanGc::from_cx(cx))));
1639
1640 this.AudioTracks(cx).clear();
1642 this.VideoTracks(CanGc::from_cx(cx)).clear();
1643
1644 this.network_state.set(NetworkState::NoSource);
1647
1648 this.show_poster.set(true);
1650
1651 this.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1653
1654 if let Some(ref player) = *this.player.borrow()
1655 && let Err(error) = player.lock().unwrap().stop() {
1656 error!("Could not stop player: {error:?}");
1657 }
1658
1659 });
1663
1664 this.delay_load_event(false, cx);
1667 }));
1668 }
1669
1670 fn in_error_state(&self) -> bool {
1671 self.error.get().is_some()
1672 }
1673
1674 fn is_potentially_playing(&self) -> bool {
1676 !self.paused.get() &&
1677 !self.ended_playback(LoopCondition::Included) &&
1678 self.error.get().is_none() &&
1679 !self.is_blocked_media_element()
1680 }
1681
1682 fn is_blocked_media_element(&self) -> bool {
1684 self.ready_state.get() <= ReadyState::HaveCurrentData ||
1685 self.is_paused_for_user_interaction() ||
1686 self.is_paused_for_in_band_content()
1687 }
1688
1689 fn is_paused_for_user_interaction(&self) -> bool {
1691 false
1694 }
1695
1696 fn is_paused_for_in_band_content(&self) -> bool {
1698 false
1701 }
1702
1703 fn media_element_load_algorithm(&self, cx: &mut JSContext) {
1705 self.fired_loadeddata_event.set(false);
1708
1709 self.generation_id.set(self.generation_id.get() + 1);
1714
1715 self.load_state.set(LoadState::NotLoaded);
1716 *self.source_children_pointer.borrow_mut() = None;
1717 self.current_source_child.set(None);
1718
1719 while !self.in_flight_play_promises_queue.borrow().is_empty() {
1726 self.fulfill_in_flight_play_promises(cx, |_| ());
1727 }
1728
1729 let network_state = self.network_state.get();
1734
1735 if network_state == NetworkState::Loading || network_state == NetworkState::Idle {
1739 self.queue_media_element_task_to_fire_event(atom!("abort"));
1740 }
1741
1742 self.reset_media_player();
1744
1745 if network_state != NetworkState::Empty {
1747 self.queue_media_element_task_to_fire_event(atom!("emptied"));
1750
1751 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1754 current_fetch_context.cancel(CancelReason::Abort);
1755 }
1756
1757 self.AudioTracks(cx).clear();
1762 self.VideoTracks(CanGc::from_cx(cx)).clear();
1763
1764 if self.ready_state.get() != ReadyState::HaveNothing {
1766 self.change_ready_state(ReadyState::HaveNothing);
1767 }
1768
1769 if !self.Paused() {
1771 self.paused.set(true);
1773
1774 self.take_pending_play_promises(Err(Error::Abort(None)));
1777 self.fulfill_in_flight_play_promises(cx, |_| ());
1778 }
1779
1780 self.seeking.set(false);
1782
1783 self.current_seek_position.set(f64::NAN);
1784
1785 self.current_playback_position.set(0.);
1790 if self.official_playback_position.get() != 0. {
1791 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
1792 }
1793 self.official_playback_position.set(0.);
1794
1795 self.duration.set(f64::NAN);
1799 }
1800
1801 self.playback_rate.set(self.default_playback_rate.get());
1803
1804 self.error.set(None);
1806 self.autoplaying.set(true);
1807
1808 self.invoke_resource_selection_algorithm(cx);
1810
1811 }
1813
1814 fn queue_media_element_task_to_fire_event(&self, name: Atom) {
1817 let this = Trusted::new(self);
1818 let generation_id = self.generation_id.get();
1819
1820 self.owner_global()
1821 .task_manager()
1822 .media_element_task_source()
1823 .queue(task!(queue_event: move |cx| {
1824 let this = this.root();
1825 if generation_id != this.generation_id.get() {
1826 return;
1827 }
1828
1829 this.upcast::<EventTarget>().fire_event(cx, name);
1830 }));
1831 }
1832
1833 fn push_pending_play_promise(&self, promise: &Rc<Promise>) {
1835 self.pending_play_promises
1836 .borrow_mut()
1837 .push(promise.clone());
1838 }
1839
1840 fn take_pending_play_promises(&self, result: ErrorResult) {
1851 let pending_play_promises = std::mem::take(&mut *self.pending_play_promises.borrow_mut());
1852 self.in_flight_play_promises_queue
1853 .borrow_mut()
1854 .push_back((pending_play_promises.into(), result));
1855 }
1856
1857 fn fulfill_in_flight_play_promises<F>(&self, cx: &mut JSContext, f: F)
1866 where
1867 F: FnOnce(&mut JSContext),
1868 {
1869 let (promises, result) = self
1870 .in_flight_play_promises_queue
1871 .borrow_mut()
1872 .pop_front()
1873 .expect("there should be at least one list of in flight play promises");
1874 f(cx);
1875 for promise in &*promises {
1876 match result {
1877 Ok(ref value) => promise.resolve_native_with_cx(cx, value),
1878 Err(ref error) => promise.reject_error_with_cx(cx, error.clone()),
1879 }
1880 }
1881 }
1882
1883 pub(crate) fn handle_source_child_insertion(
1884 &self,
1885 source: &HTMLSourceElement,
1886 cx: &mut JSContext,
1887 ) {
1888 if self.upcast::<Element>().has_attribute(&local_name!("src")) {
1892 return;
1893 }
1894
1895 if self.network_state.get() == NetworkState::Empty {
1896 self.invoke_resource_selection_algorithm(cx);
1897 return;
1898 }
1899
1900 if self.load_state.get() != LoadState::WaitingForSource {
1904 return;
1905 }
1906
1907 self.load_state.set(LoadState::LoadingFromSourceChild);
1908
1909 *self.source_children_pointer.borrow_mut() =
1910 Some(SourceChildrenPointer::new(DomRoot::from_ref(source), true));
1911
1912 let task = MediaElementMicrotask::SelectNextSourceChildAfterWait {
1914 elem: DomRoot::from_ref(self),
1915 generation_id: self.generation_id.get(),
1916 };
1917
1918 ScriptThread::await_stable_state(Microtask::MediaElement(task));
1919 }
1920
1921 fn select_next_source_child_after_wait(&self, cx: &mut JSContext) {
1923 self.delay_load_event(true, cx);
1926
1927 self.network_state.set(NetworkState::Loading);
1929
1930 self.select_next_source_child(cx);
1932 }
1933
1934 fn media_data_processing_failure_steps(&self) {
1939 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1941 current_fetch_context.cancel(CancelReason::Error);
1942 }
1943
1944 self.resource_selection_algorithm_failure_steps();
1946 }
1947
1948 fn media_data_processing_fatal_steps(&self, error: u16, cx: &mut JSContext) {
1952 *self.source_children_pointer.borrow_mut() = None;
1953 self.current_source_child.set(None);
1954
1955 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1957 current_fetch_context.cancel(CancelReason::Error);
1958 }
1959
1960 self.error.set(Some(&*MediaError::new(
1963 &self.owner_window(),
1964 error,
1965 CanGc::from_cx(cx),
1966 )));
1967
1968 self.network_state.set(NetworkState::Idle);
1970
1971 self.delay_load_event(false, cx);
1974
1975 self.upcast::<EventTarget>().fire_event(cx, atom!("error"));
1977
1978 }
1980
1981 fn seek(&self, time: f64, _approximate_for_speed: bool) {
1983 self.show_poster.set(false);
1985
1986 if self.ready_state.get() == ReadyState::HaveNothing {
1988 return;
1989 }
1990
1991 self.current_seek_position.set(f64::NAN);
1995
1996 self.seeking.set(true);
1998
1999 let time = f64::min(time, self.Duration());
2005
2006 let time = f64::max(time, self.earliest_possible_position());
2009
2010 let seekable = self.seekable();
2016
2017 if seekable.is_empty() {
2018 self.seeking.set(false);
2019 return;
2020 }
2021
2022 let mut nearest_seekable_position = 0.0;
2023 let mut in_seekable_range = false;
2024 let mut nearest_seekable_distance = f64::MAX;
2025 for i in 0..seekable.len() {
2026 let start = seekable.start(i).unwrap().abs();
2027 let end = seekable.end(i).unwrap().abs();
2028 if time >= start && time <= end {
2029 nearest_seekable_position = time;
2030 in_seekable_range = true;
2031 break;
2032 } else if time < start {
2033 let distance = start - time;
2034 if distance < nearest_seekable_distance {
2035 nearest_seekable_distance = distance;
2036 nearest_seekable_position = start;
2037 }
2038 } else {
2039 let distance = time - end;
2040 if distance < nearest_seekable_distance {
2041 nearest_seekable_distance = distance;
2042 nearest_seekable_position = end;
2043 }
2044 }
2045 }
2046 let time = if in_seekable_range {
2047 time
2048 } else {
2049 nearest_seekable_position
2050 };
2051
2052 self.queue_media_element_task_to_fire_event(atom!("seeking"));
2063
2064 self.current_playback_position.set(time);
2066
2067 if let Some(ref player) = *self.player.borrow() &&
2068 let Err(error) = player.lock().unwrap().seek(time)
2069 {
2070 error!("Could not seek player: {error:?}");
2071 }
2072
2073 self.current_seek_position.set(time);
2074
2075 }
2081
2082 fn seek_end(&self) {
2084 self.official_playback_position
2087 .set(self.current_playback_position.get());
2088
2089 self.seeking.set(false);
2091
2092 self.current_seek_position.set(f64::NAN);
2093
2094 self.time_marches_on();
2096
2097 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
2100
2101 self.queue_media_element_task_to_fire_event(atom!("seeked"));
2104 }
2105
2106 pub(crate) fn set_poster_frame(&self, image: Option<Arc<RasterImage>>) {
2108 if pref!(media_testing_enabled) && image.is_some() {
2109 self.queue_media_element_task_to_fire_event(atom!("postershown"));
2110 }
2111
2112 self.video_renderer.lock().unwrap().set_poster_frame(image);
2113
2114 self.upcast::<Node>().dirty(NodeDamage::Other);
2115 }
2116
2117 fn player_id(&self) -> Option<usize> {
2118 self.player
2119 .borrow()
2120 .as_ref()
2121 .map(|player| player.lock().unwrap().get_id())
2122 }
2123
2124 fn create_media_player(&self, resource: &Resource) -> Result<(), ()> {
2125 let stream_type = match *resource {
2126 Resource::Object => {
2127 if let Some(ref src_object) = *self.src_object.borrow() {
2128 match src_object {
2129 SrcObject::MediaStream(_) => StreamType::Stream,
2130 _ => StreamType::Seekable,
2131 }
2132 } else {
2133 return Err(());
2134 }
2135 },
2136 _ => StreamType::Seekable,
2137 };
2138
2139 let window = self.owner_window();
2140 let (action_sender, action_receiver) = ipc::channel::<PlayerEvent>().unwrap();
2141 let video_renderer: Option<Arc<Mutex<dyn VideoFrameRenderer>>> = match self.media_type_id()
2142 {
2143 HTMLMediaElementTypeId::HTMLAudioElement => None,
2144 HTMLMediaElementTypeId::HTMLVideoElement => Some(self.video_renderer.clone()),
2145 };
2146
2147 let audio_renderer = self.audio_renderer.borrow().as_ref().cloned();
2148
2149 let pipeline_id = window.pipeline_id();
2150 let client_context_id =
2151 ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
2152 let player = ServoMedia::get().create_player(
2153 &client_context_id,
2154 stream_type,
2155 action_sender,
2156 video_renderer,
2157 audio_renderer,
2158 Box::new(window.get_player_context()),
2159 );
2160 let player_id = {
2161 let player_guard = player.lock().unwrap();
2162
2163 if let Err(error) = player_guard.set_mute(self.muted.get()) {
2164 warn!("Could not set mute state: {error:?}");
2165 }
2166
2167 player_guard.get_id()
2168 };
2169
2170 *self.player.borrow_mut() = Some(player);
2171
2172 let event_handler = Arc::new(Mutex::new(HTMLMediaElementEventHandler::new(self)));
2173 let weak_event_handler = Arc::downgrade(&event_handler);
2174 *self.event_handler.borrow_mut() = Some(event_handler);
2175
2176 let task_source = self
2177 .owner_global()
2178 .task_manager()
2179 .media_element_task_source()
2180 .to_sendable();
2181 ROUTER.add_typed_route(
2182 action_receiver,
2183 Box::new(move |message| {
2184 let event = message.unwrap();
2185 let weak_event_handler = weak_event_handler.clone();
2186
2187 task_source.queue(task!(handle_player_event: move |cx| {
2188 trace!("HTMLMediaElement event: {event:?}");
2189
2190 let Some(event_handler) = weak_event_handler.upgrade() else {
2191 return;
2192 };
2193
2194 event_handler.lock().unwrap().handle_player_event(player_id, event, cx);
2195 }));
2196 }),
2197 );
2198
2199 let task_source = self
2200 .owner_global()
2201 .task_manager()
2202 .media_element_task_source()
2203 .to_sendable();
2204 let weak_video_renderer = Arc::downgrade(&self.video_renderer);
2205
2206 self.video_renderer
2207 .lock()
2208 .unwrap()
2209 .setup(player_id, task_source, weak_video_renderer);
2210
2211 Ok(())
2212 }
2213
2214 fn reset_media_player(&self) {
2215 if self.player.borrow().is_none() {
2216 return;
2217 }
2218
2219 if let Some(ref player) = *self.player.borrow() &&
2220 let Err(error) = player.lock().unwrap().stop()
2221 {
2222 error!("Could not stop player: {error:?}");
2223 }
2224
2225 *self.player.borrow_mut() = None;
2226 self.video_renderer.lock().unwrap().reset();
2227 *self.event_handler.borrow_mut() = None;
2228
2229 if let Some(video_element) = self.downcast::<HTMLVideoElement>() {
2230 video_element.set_natural_dimensions(None, None);
2231 }
2232 }
2233
2234 pub(crate) fn set_audio_track(&self, idx: usize, enabled: bool) {
2235 if let Some(ref player) = *self.player.borrow() &&
2236 let Err(error) = player.lock().unwrap().set_audio_track(idx as i32, enabled)
2237 {
2238 warn!("Could not set audio track {error:?}");
2239 }
2240 }
2241
2242 pub(crate) fn set_video_track(&self, idx: usize, enabled: bool) {
2243 if let Some(ref player) = *self.player.borrow() &&
2244 let Err(error) = player.lock().unwrap().set_video_track(idx as i32, enabled)
2245 {
2246 warn!("Could not set video track: {error:?}");
2247 }
2248 }
2249
2250 fn direction_of_playback(&self) -> PlaybackDirection {
2252 if self.playback_rate.get() >= 0. {
2255 PlaybackDirection::Forwards
2256 } else {
2257 PlaybackDirection::Backwards
2258 }
2259 }
2260
2261 fn ended_playback(&self, loop_condition: LoopCondition) -> bool {
2263 if self.ready_state.get() < ReadyState::HaveMetadata {
2267 return false;
2268 }
2269
2270 let playback_position = self.current_playback_position.get();
2271
2272 match self.direction_of_playback() {
2273 PlaybackDirection::Forwards => {
2277 playback_position >= self.Duration() &&
2278 (loop_condition == LoopCondition::Ignored || !self.Loop())
2279 },
2280 PlaybackDirection::Backwards => playback_position <= self.earliest_possible_position(),
2283 }
2284 }
2285
2286 fn end_of_playback_in_forwards_direction(&self) {
2288 if self.Loop() {
2294 self.seek(
2295 self.earliest_possible_position(),
2296 false,
2297 );
2298 return;
2299 }
2300
2301 let this = Trusted::new(self);
2306 let generation_id = self.generation_id.get();
2307
2308 self.owner_global()
2309 .task_manager()
2310 .media_element_task_source()
2311 .queue(task!(reaches_the_end_steps: move |cx| {
2312 let this = this.root();
2313 if generation_id != this.generation_id.get() {
2314 return;
2315 }
2316
2317 this.upcast::<EventTarget>().fire_event(cx, atom!("timeupdate"));
2319
2320 if this.ended_playback(LoopCondition::Included) &&
2323 this.direction_of_playback() == PlaybackDirection::Forwards &&
2324 !this.Paused() {
2325 this.paused.set(true);
2327
2328 this.upcast::<EventTarget>().fire_event(cx, atom!("pause"));
2330
2331 this.take_pending_play_promises(Err(Error::Abort(None)));
2334 this.fulfill_in_flight_play_promises(cx, |_| ());
2335 }
2336
2337 this.upcast::<EventTarget>().fire_event(cx, atom!("ended"));
2339 }));
2340
2341 self.change_ready_state(ReadyState::HaveCurrentData);
2343 }
2344
2345 fn end_of_playback_in_backwards_direction(&self) {
2347 if self.current_playback_position.get() <= self.earliest_possible_position() {
2352 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
2353 }
2354 }
2355
2356 fn playback_end(&self) {
2357 if self.seeking.get() {
2359 return;
2360 }
2361
2362 match self.direction_of_playback() {
2363 PlaybackDirection::Forwards => self.end_of_playback_in_forwards_direction(),
2364 PlaybackDirection::Backwards => self.end_of_playback_in_backwards_direction(),
2365 }
2366 }
2367
2368 fn playback_error(&self, error: &str, cx: &mut JSContext) {
2369 error!("Player error: {:?}", error);
2370
2371 if self.in_error_state() {
2375 return;
2376 }
2377
2378 if self.ready_state.get() == ReadyState::HaveNothing {
2380 self.media_data_processing_failure_steps();
2383 } else {
2384 self.media_data_processing_fatal_steps(MEDIA_ERR_DECODE, cx);
2386 }
2387 }
2388
2389 fn playback_metadata_updated(
2390 &self,
2391 cx: &mut JSContext,
2392 metadata: &servo_media::player::metadata::Metadata,
2393 ) {
2394 if self.ready_state.get() != ReadyState::HaveNothing {
2397 return;
2398 }
2399
2400 for (i, _track) in metadata.audio_tracks.iter().enumerate() {
2403 let audio_track_list = self.AudioTracks(cx);
2404
2405 let kind = match i {
2407 0 => DOMString::from("main"),
2408 _ => DOMString::new(),
2409 };
2410
2411 let audio_track = AudioTrack::new(
2412 cx,
2413 self.global().as_window(),
2414 DOMString::new(),
2415 kind,
2416 DOMString::new(),
2417 DOMString::new(),
2418 Some(&*audio_track_list),
2419 );
2420
2421 audio_track_list.add(&audio_track);
2424
2425 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
2432 let fragment = MediaFragmentParser::from(servo_url);
2433 if let Some(id) = fragment.id() &&
2434 audio_track.id() == id
2435 {
2436 audio_track_list.set_enabled(audio_track_list.len() - 1, true);
2437 }
2438
2439 if fragment.tracks().contains(&audio_track.kind().into()) {
2440 audio_track_list.set_enabled(audio_track_list.len() - 1, true);
2441 }
2442 }
2443
2444 if audio_track_list.enabled_index().is_none() {
2449 audio_track_list.set_enabled(audio_track_list.len() - 1, true);
2450 }
2451
2452 let event = TrackEvent::new(
2455 self.global().as_window(),
2456 atom!("addtrack"),
2457 false,
2458 false,
2459 &Some(VideoTrackOrAudioTrackOrTextTrack::AudioTrack(audio_track)),
2460 CanGc::from_cx(cx),
2461 );
2462
2463 event
2464 .upcast::<Event>()
2465 .fire(cx, audio_track_list.upcast::<EventTarget>());
2466 }
2467
2468 for (i, _track) in metadata.video_tracks.iter().enumerate() {
2470 let video_track_list = self.VideoTracks(CanGc::from_cx(cx));
2471
2472 let kind = match i {
2474 0 => DOMString::from("main"),
2475 _ => DOMString::new(),
2476 };
2477
2478 let video_track = VideoTrack::new(
2479 self.global().as_window(),
2480 DOMString::new(),
2481 kind,
2482 DOMString::new(),
2483 DOMString::new(),
2484 Some(&*video_track_list),
2485 CanGc::from_cx(cx),
2486 );
2487
2488 video_track_list.add(&video_track);
2491
2492 if let Some(track) = video_track_list.item(0) &&
2499 let Some(servo_url) = self.resource_url.borrow().as_ref()
2500 {
2501 let fragment = MediaFragmentParser::from(servo_url);
2502 if let Some(id) = fragment.id() {
2503 if track.id() == id {
2504 video_track_list.set_selected(0, true);
2505 }
2506 } else if fragment.tracks().contains(&track.kind().into()) {
2507 video_track_list.set_selected(0, true);
2508 }
2509 }
2510
2511 if video_track_list.selected_index().is_none() {
2517 video_track_list.set_selected(video_track_list.len() - 1, true);
2518 }
2519
2520 let event = TrackEvent::new(
2523 self.global().as_window(),
2524 atom!("addtrack"),
2525 false,
2526 false,
2527 &Some(VideoTrackOrAudioTrackOrTextTrack::VideoTrack(video_track)),
2528 CanGc::from_cx(cx),
2529 );
2530
2531 event
2532 .upcast::<Event>()
2533 .fire(cx, video_track_list.upcast::<EventTarget>());
2534 }
2535
2536 let earliest_possible_position = self.earliest_possible_position();
2549 self.current_playback_position
2550 .set(earliest_possible_position);
2551 self.official_playback_position
2552 .set(earliest_possible_position);
2553
2554 self.duration.set(
2560 metadata
2561 .duration
2562 .map_or(f64::INFINITY, |duration| duration.as_secs_f64()),
2563 );
2564 self.queue_media_element_task_to_fire_event(atom!("durationchange"));
2565
2566 if let Some(video_element) = self.downcast::<HTMLVideoElement>() {
2570 video_element.set_natural_dimensions(Some(metadata.width), Some(metadata.height));
2571 self.queue_media_element_task_to_fire_event(atom!("resize"));
2572 }
2573
2574 self.change_ready_state(ReadyState::HaveMetadata);
2576
2577 let mut jumped = false;
2579
2580 if self.default_playback_start_position.get() > 0. {
2583 self.seek(
2584 self.default_playback_start_position.get(),
2585 false,
2586 );
2587 jumped = true;
2588 }
2589
2590 self.default_playback_start_position.set(0.);
2592
2593 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
2598 let fragment = MediaFragmentParser::from(servo_url);
2599 if let Some(initial_playback_position) = fragment.start() &&
2600 initial_playback_position > 0. &&
2601 initial_playback_position < self.duration.get() &&
2602 !jumped
2603 {
2604 self.seek(
2605 initial_playback_position,
2606 false,
2607 )
2608 }
2609 }
2610
2611 let global = self.global();
2618 let window = global.as_window();
2619
2620 window.Navigator().MediaSession().update_title(
2622 metadata
2623 .title
2624 .clone()
2625 .unwrap_or(window.get_url().into_string()),
2626 );
2627 }
2628
2629 fn playback_duration_changed(&self, duration: Option<Duration>) {
2630 let duration = duration.map_or(f64::INFINITY, |duration| duration.as_secs_f64());
2631
2632 if self.duration.get() == duration {
2633 return;
2634 }
2635
2636 self.duration.set(duration);
2637
2638 self.queue_media_element_task_to_fire_event(atom!("durationchange"));
2644
2645 if self.current_playback_position.get() > duration {
2649 self.seek(duration, false);
2650 }
2651 }
2652
2653 fn playback_video_frame_updated(&self) {
2654 let Some(video_element) = self.downcast::<HTMLVideoElement>() else {
2655 return;
2656 };
2657
2658 if self.ready_state.get() == ReadyState::HaveNothing {
2667 return;
2668 }
2669
2670 if let Some(frame) = self.video_renderer.lock().unwrap().current_frame {
2671 if video_element
2672 .set_natural_dimensions(Some(frame.width as u32), Some(frame.height as u32))
2673 {
2674 self.queue_media_element_task_to_fire_event(atom!("resize"));
2675 } else {
2676 self.upcast::<Node>().dirty(NodeDamage::Other);
2679 }
2680 }
2681 }
2682
2683 fn playback_need_data(&self) {
2684 if let Some(ref current_fetch_context) = *self.current_fetch_context.borrow() &&
2688 let Some(reason) = current_fetch_context.cancel_reason()
2689 {
2690 if *reason == CancelReason::Backoff {
2696 self.seek(
2697 self.current_playback_position.get(),
2698 false,
2699 );
2700 }
2701 return;
2702 }
2703
2704 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() &&
2705 let Err(e) = {
2706 let mut data_source = current_fetch_context.data_source().borrow_mut();
2707 data_source.set_locked(false);
2708 data_source.process_into_player_from_queue(self.player.borrow().as_ref().unwrap())
2709 }
2710 {
2711 if e == PlayerError::EnoughData {
2716 current_fetch_context.cancel(CancelReason::Backoff);
2717 }
2718 }
2719 }
2720
2721 fn playback_enough_data(&self) {
2722 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() &&
2727 current_fetch_context.is_seekable()
2728 {
2729 current_fetch_context.cancel(CancelReason::Backoff);
2730 }
2731 }
2732
2733 fn playback_position_changed(&self, position: f64) {
2734 if self.seeking.get() {
2736 return;
2737 }
2738
2739 let _ = self
2740 .played
2741 .borrow_mut()
2742 .add(self.current_playback_position.get(), position);
2743 self.current_playback_position.set(position);
2744 self.official_playback_position.set(position);
2745 self.time_marches_on();
2746
2747 let media_position_state =
2748 MediaPositionState::new(self.duration.get(), self.playback_rate.get(), position);
2749 debug!(
2750 "Sending media session event set position state {:?}",
2751 media_position_state
2752 );
2753 self.send_media_session_event(MediaSessionEvent::SetPositionState(media_position_state));
2754 }
2755
2756 fn playback_seek_done(&self, position: f64) {
2757 let delta = (position - self.current_seek_position.get()).abs();
2760 if !self.seeking.get() || delta > SEEK_POSITION_THRESHOLD {
2761 return;
2762 }
2763
2764 let task = MediaElementMicrotask::Seeked {
2767 elem: DomRoot::from_ref(self),
2768 generation_id: self.generation_id.get(),
2769 };
2770
2771 ScriptThread::await_stable_state(Microtask::MediaElement(task));
2772 }
2773
2774 fn playback_state_changed(&self, state: &PlaybackState) {
2775 let mut media_session_playback_state = MediaSessionPlaybackState::None_;
2776 match *state {
2777 PlaybackState::Paused => {
2778 media_session_playback_state = MediaSessionPlaybackState::Paused;
2779 if self.ready_state.get() == ReadyState::HaveMetadata {
2780 self.change_ready_state(ReadyState::HaveEnoughData);
2781 }
2782 },
2783 PlaybackState::Playing => {
2784 media_session_playback_state = MediaSessionPlaybackState::Playing;
2785 if self.ready_state.get() == ReadyState::HaveMetadata {
2786 self.change_ready_state(ReadyState::HaveEnoughData);
2787 }
2788 },
2789 PlaybackState::Buffering => {
2790 return;
2794 },
2795 _ => {},
2796 };
2797 debug!(
2798 "Sending media session event playback state changed to {:?}",
2799 media_session_playback_state
2800 );
2801 self.send_media_session_event(MediaSessionEvent::PlaybackStateChange(
2802 media_session_playback_state,
2803 ));
2804 }
2805
2806 fn seekable(&self) -> TimeRangesContainer {
2807 let mut seekable = TimeRangesContainer::default();
2808 if let Some(ref player) = *self.player.borrow() {
2809 let ranges = player.lock().unwrap().seekable();
2810 for range in ranges {
2811 let _ = seekable.add(range.start, range.end);
2812 }
2813 }
2814 seekable
2815 }
2816
2817 fn earliest_possible_position(&self) -> f64 {
2819 self.seekable()
2820 .start(0)
2821 .unwrap_or_else(|_| self.current_playback_position.get())
2822 }
2823
2824 fn render_controls(&self, cx: &mut JSContext) {
2825 if self.upcast::<Element>().is_shadow_host() {
2826 return;
2828 }
2829
2830 let shadow_root = self.upcast::<Element>().attach_ua_shadow_root(cx, false);
2833 let document = self.owner_document();
2834 let script = Element::create(
2835 cx,
2836 QualName::new(None, ns!(html), local_name!("script")),
2837 None,
2838 &document,
2839 ElementCreator::ScriptCreated,
2840 CustomElementCreationMode::Asynchronous,
2841 None,
2842 );
2843 let id = Uuid::new_v4().to_string();
2849 document.register_media_controls(&id, &shadow_root);
2850 let media_controls_script = MEDIA_CONTROL_JS.replace("@@@id@@@", &id);
2851 *self.media_controls_id.borrow_mut() = Some(id);
2852 script
2853 .upcast::<Node>()
2854 .set_text_content_for_element(cx, Some(DOMString::from(media_controls_script)));
2855 if let Err(e) = shadow_root
2856 .upcast::<Node>()
2857 .AppendChild(cx, script.upcast::<Node>())
2858 {
2859 warn!("Could not render media controls {:?}", e);
2860 return;
2861 }
2862
2863 let style = Element::create(
2864 cx,
2865 QualName::new(None, ns!(html), local_name!("style")),
2866 None,
2867 &document,
2868 ElementCreator::ScriptCreated,
2869 CustomElementCreationMode::Asynchronous,
2870 None,
2871 );
2872
2873 style
2874 .upcast::<Node>()
2875 .set_text_content_for_element(cx, Some(DOMString::from(MEDIA_CONTROL_CSS)));
2876
2877 if let Err(e) = shadow_root
2878 .upcast::<Node>()
2879 .AppendChild(cx, style.upcast::<Node>())
2880 {
2881 warn!("Could not render media controls {:?}", e);
2882 }
2883
2884 self.upcast::<Node>().dirty(NodeDamage::Other);
2885 }
2886
2887 fn remove_controls(&self) {
2888 if let Some(id) = self.media_controls_id.borrow_mut().take() {
2889 self.owner_document().unregister_media_controls(&id);
2890 }
2891 }
2892
2893 pub(crate) fn get_current_frame(&self) -> Option<VideoFrame> {
2895 self.video_renderer
2896 .lock()
2897 .unwrap()
2898 .current_frame_holder
2899 .as_ref()
2900 .map(|holder| holder.get_frame())
2901 }
2902
2903 pub(crate) fn get_current_frame_to_present(&self) -> Option<MediaFrame> {
2906 let (current_frame, poster_frame) = {
2907 let renderer = self.video_renderer.lock().unwrap();
2908 (renderer.current_frame, renderer.poster_frame)
2909 };
2910
2911 if (self.show_poster.get() || current_frame.is_none()) && poster_frame.is_some() {
2914 return poster_frame;
2915 }
2916
2917 current_frame
2918 }
2919
2920 pub(crate) fn set_audio_renderer(
2925 &self,
2926 audio_renderer: Option<Arc<Mutex<dyn AudioRenderer>>>,
2927 cx: &mut JSContext,
2928 ) {
2929 *self.audio_renderer.borrow_mut() = audio_renderer;
2930
2931 let had_player = {
2932 if let Some(ref player) = *self.player.borrow() {
2933 if let Err(error) = player.lock().unwrap().stop() {
2934 error!("Could not stop player: {error:?}");
2935 }
2936 true
2937 } else {
2938 false
2939 }
2940 };
2941
2942 if had_player {
2943 self.media_element_load_algorithm(cx);
2944 }
2945 }
2946
2947 fn send_media_session_event(&self, event: MediaSessionEvent) {
2948 let global = self.global();
2949 let media_session = global.as_window().Navigator().MediaSession();
2950
2951 media_session.register_media_instance(self);
2952
2953 media_session.send_event(event);
2954 }
2955
2956 pub(crate) fn origin_is_clean(&self) -> bool {
2958 if self.src_object.borrow().is_some() {
2960 return true;
2963 }
2964
2965 if self.resource_url.borrow().is_some() {
2967 if let Some(ref current_fetch_context) = *self.current_fetch_context.borrow() {
2971 return current_fetch_context.origin_is_clean();
2972 }
2973 }
2974
2975 true
2976 }
2977}
2978
2979impl HTMLMediaElementMethods<crate::DomTypeHolder> for HTMLMediaElement {
2980 fn NetworkState(&self) -> u16 {
2982 self.network_state.get() as u16
2983 }
2984
2985 fn ReadyState(&self) -> u16 {
2987 self.ready_state.get() as u16
2988 }
2989
2990 make_bool_getter!(Autoplay, "autoplay");
2992 make_bool_setter!(SetAutoplay, "autoplay");
2994
2995 make_bool_getter!(Loop, "loop");
2997 make_bool_setter!(SetLoop, "loop");
2999
3000 make_bool_getter!(DefaultMuted, "muted");
3002 make_bool_setter!(SetDefaultMuted, "muted");
3004
3005 make_bool_getter!(Controls, "controls");
3007 make_bool_setter!(SetControls, "controls");
3009
3010 make_url_getter!(Src, "src");
3012
3013 make_url_setter!(SetSrc, "src");
3015
3016 fn GetCrossOrigin(&self) -> Option<DOMString> {
3018 reflect_cross_origin_attribute(self.upcast::<Element>())
3019 }
3020 fn SetCrossOrigin(&self, cx: &mut JSContext, value: Option<DOMString>) {
3022 set_cross_origin_attribute(cx, self.upcast::<Element>(), value);
3023 }
3024
3025 fn Muted(&self) -> bool {
3027 self.muted.get()
3028 }
3029
3030 fn SetMuted(&self, _cx: &mut JSContext, value: bool) {
3032 if self.muted.get() == value {
3033 return;
3034 }
3035
3036 self.muted.set(value);
3037
3038 if let Some(ref player) = *self.player.borrow() &&
3039 let Err(error) = player.lock().unwrap().set_mute(value)
3040 {
3041 warn!("Could not set mute state: {error:?}");
3042 }
3043
3044 self.queue_media_element_task_to_fire_event(atom!("volumechange"));
3047
3048 if !self.is_allowed_to_play() {
3051 self.internal_pause_steps();
3052 }
3053 }
3054
3055 fn GetSrcObject(&self) -> Option<MediaStreamOrBlob> {
3057 (*self.src_object.borrow())
3058 .as_ref()
3059 .map(|src_object| match src_object {
3060 SrcObject::Blob(blob) => MediaStreamOrBlob::Blob(DomRoot::from_ref(blob)),
3061 SrcObject::MediaStream(stream) => {
3062 MediaStreamOrBlob::MediaStream(DomRoot::from_ref(stream))
3063 },
3064 })
3065 }
3066
3067 fn SetSrcObject(&self, cx: &mut JSContext, value: Option<MediaStreamOrBlob>) {
3069 *self.src_object.borrow_mut() = value.map(|value| value.into());
3070 self.media_element_load_algorithm(cx);
3071 }
3072
3073 make_enumerated_getter!(
3076 Preload,
3077 "preload",
3078 "none" | "metadata" | "auto",
3079 missing => "auto",
3080 invalid => "auto"
3081 );
3082
3083 make_setter!(SetPreload, "preload");
3085
3086 fn CurrentSrc(&self) -> USVString {
3088 USVString(self.current_src.borrow().clone())
3089 }
3090
3091 fn Load(&self, cx: &mut JSContext) {
3093 self.media_element_load_algorithm(cx);
3094 }
3095
3096 fn CanPlayType(&self, type_: DOMString) -> CanPlayTypeResult {
3098 match ServoMedia::get().can_play_type(&type_.str()) {
3099 SupportsMediaType::No => CanPlayTypeResult::_empty,
3100 SupportsMediaType::Maybe => CanPlayTypeResult::Maybe,
3101 SupportsMediaType::Probably => CanPlayTypeResult::Probably,
3102 }
3103 }
3104
3105 fn GetError(&self) -> Option<DomRoot<MediaError>> {
3107 self.error.get()
3108 }
3109
3110 fn Play(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
3112 let promise = Promise::new_in_realm(cx);
3113
3114 if self
3121 .error
3122 .get()
3123 .is_some_and(|e| e.Code() == MEDIA_ERR_SRC_NOT_SUPPORTED)
3124 {
3125 promise.reject_error_with_cx(cx, Error::NotSupported(None));
3126 return promise;
3127 }
3128
3129 self.push_pending_play_promise(&promise);
3132
3133 self.internal_play_steps(cx);
3135
3136 promise
3138 }
3139
3140 fn Pause(&self, cx: &mut JSContext) {
3142 if self.network_state.get() == NetworkState::Empty {
3145 self.invoke_resource_selection_algorithm(cx);
3146 }
3147
3148 self.internal_pause_steps();
3150 }
3151
3152 fn Paused(&self) -> bool {
3154 self.paused.get()
3155 }
3156
3157 fn GetDefaultPlaybackRate(&self) -> Fallible<Finite<f64>> {
3159 Ok(Finite::wrap(self.default_playback_rate.get()))
3160 }
3161
3162 fn SetDefaultPlaybackRate(&self, _cx: &mut JSContext, value: Finite<f64>) -> ErrorResult {
3164 let min_allowed = -64.0;
3167 let max_allowed = 64.0;
3168 if *value < min_allowed || *value > max_allowed {
3169 return Err(Error::NotSupported(None));
3170 }
3171
3172 if self.default_playback_rate.get() == *value {
3173 return Ok(());
3174 }
3175
3176 self.default_playback_rate.set(*value);
3177
3178 self.queue_media_element_task_to_fire_event(atom!("ratechange"));
3181
3182 Ok(())
3183 }
3184
3185 fn GetPlaybackRate(&self) -> Fallible<Finite<f64>> {
3187 Ok(Finite::wrap(self.playback_rate.get()))
3188 }
3189
3190 fn SetPlaybackRate(&self, _cx: &mut JSContext, value: Finite<f64>) -> ErrorResult {
3192 let min_allowed = -64.0;
3197 let max_allowed = 64.0;
3198 if *value < min_allowed || *value > max_allowed {
3199 return Err(Error::NotSupported(None));
3200 }
3201
3202 if self.playback_rate.get() == *value {
3203 return Ok(());
3204 }
3205
3206 self.playback_rate.set(*value);
3209
3210 if self.is_potentially_playing() &&
3211 let Some(ref player) = *self.player.borrow() &&
3212 let Err(error) = player.lock().unwrap().set_playback_rate(*value)
3213 {
3214 warn!("Could not set the playback rate: {error:?}");
3215 }
3216
3217 self.queue_media_element_task_to_fire_event(atom!("ratechange"));
3220
3221 Ok(())
3222 }
3223
3224 fn Duration(&self) -> f64 {
3226 self.duration.get()
3227 }
3228
3229 fn CurrentTime(&self) -> Finite<f64> {
3231 Finite::wrap(if self.default_playback_start_position.get() != 0. {
3232 self.default_playback_start_position.get()
3233 } else if self.seeking.get() {
3234 self.current_seek_position.get()
3239 } else {
3240 self.official_playback_position.get()
3241 })
3242 }
3243
3244 fn SetCurrentTime(&self, _cx: &mut JSContext, time: Finite<f64>) {
3246 if self.ready_state.get() == ReadyState::HaveNothing {
3247 self.default_playback_start_position.set(*time);
3248 } else {
3249 self.official_playback_position.set(*time);
3250 self.seek(*time, false);
3251 }
3252 }
3253
3254 fn Seeking(&self) -> bool {
3256 self.seeking.get()
3257 }
3258
3259 fn Ended(&self) -> bool {
3261 self.ended_playback(LoopCondition::Included) &&
3262 self.direction_of_playback() == PlaybackDirection::Forwards
3263 }
3264
3265 fn FastSeek(&self, time: Finite<f64>) {
3267 self.seek(*time, true);
3268 }
3269
3270 fn Played(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
3272 TimeRanges::new(
3273 self.global().as_window(),
3274 self.played.borrow().clone(),
3275 can_gc,
3276 )
3277 }
3278
3279 fn Seekable(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
3281 TimeRanges::new(self.global().as_window(), self.seekable(), can_gc)
3282 }
3283
3284 fn Buffered(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
3286 let mut buffered = TimeRangesContainer::default();
3287 if let Some(ref player) = *self.player.borrow() {
3288 let ranges = player.lock().unwrap().buffered();
3289 for range in ranges {
3290 let _ = buffered.add(range.start, range.end);
3291 }
3292 }
3293 TimeRanges::new(self.global().as_window(), buffered, can_gc)
3294 }
3295
3296 fn AudioTracks(&self, cx: &mut JSContext) -> DomRoot<AudioTrackList> {
3298 let window = self.owner_window();
3299 self.audio_tracks_list
3300 .or_init(|| AudioTrackList::new(cx, &window, &[], Some(self)))
3301 }
3302
3303 fn VideoTracks(&self, can_gc: CanGc) -> DomRoot<VideoTrackList> {
3305 let window = self.owner_window();
3306 self.video_tracks_list
3307 .or_init(|| VideoTrackList::new(&window, &[], Some(self), can_gc))
3308 }
3309
3310 fn TextTracks(&self, can_gc: CanGc) -> DomRoot<TextTrackList> {
3312 let window = self.owner_window();
3313 self.text_tracks_list
3314 .or_init(|| TextTrackList::new(&window, &[], can_gc))
3315 }
3316
3317 fn AddTextTrack(
3319 &self,
3320 kind: TextTrackKind,
3321 label: DOMString,
3322 language: DOMString,
3323 can_gc: CanGc,
3324 ) -> DomRoot<TextTrack> {
3325 let window = self.owner_window();
3326 let track = TextTrack::new(
3329 &window,
3330 "".into(),
3331 kind,
3332 label,
3333 language,
3334 TextTrackMode::Hidden,
3335 None,
3336 can_gc,
3337 );
3338 self.TextTracks(can_gc).add(&track);
3340 DomRoot::from_ref(&track)
3342 }
3343
3344 fn GetVolume(&self) -> Fallible<Finite<f64>> {
3346 Ok(Finite::wrap(self.volume.get()))
3347 }
3348
3349 fn SetVolume(&self, _cx: &mut JSContext, value: Finite<f64>) -> ErrorResult {
3351 let minimum_volume = 0.0;
3354 let maximum_volume = 1.0;
3355 if *value < minimum_volume || *value > maximum_volume {
3356 return Err(Error::IndexSize(None));
3357 }
3358
3359 if self.volume.get() == *value {
3360 return Ok(());
3361 }
3362
3363 self.volume.set(*value);
3364
3365 if let Some(ref player) = *self.player.borrow() &&
3366 let Err(error) = player.lock().unwrap().set_volume(*value)
3367 {
3368 warn!("Could not set the volume: {error:?}");
3369 }
3370
3371 self.queue_media_element_task_to_fire_event(atom!("volumechange"));
3374
3375 if !self.is_allowed_to_play() {
3378 self.internal_pause_steps();
3379 }
3380
3381 Ok(())
3382 }
3383}
3384
3385impl VirtualMethods for HTMLMediaElement {
3386 fn super_type(&self) -> Option<&dyn VirtualMethods> {
3387 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
3388 }
3389
3390 fn attribute_mutated(
3391 &self,
3392 cx: &mut JSContext,
3393 attr: AttrRef<'_>,
3394 mutation: AttributeMutation,
3395 ) {
3396 self.super_type()
3397 .unwrap()
3398 .attribute_mutated(cx, attr, mutation);
3399
3400 match *attr.local_name() {
3401 local_name!("muted") => {
3402 if let AttributeMutation::Set(
3406 _,
3407 AttributeMutationReason::ByCloning | AttributeMutationReason::ByParser,
3408 ) = mutation
3409 {
3410 self.SetMuted(cx, true);
3411 }
3412 },
3413 local_name!("src") => {
3414 if !mutation.is_removal() {
3419 self.media_element_load_algorithm(cx);
3420 }
3421 },
3422 local_name!("controls") => {
3423 if mutation.new_value(attr).is_some() {
3424 self.render_controls(cx);
3425 } else {
3426 self.remove_controls();
3427 }
3428 },
3429 _ => (),
3430 };
3431 }
3432
3433 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
3435 self.super_type().unwrap().unbind_from_tree(cx, context);
3436
3437 self.remove_controls();
3438
3439 if context.tree_connected {
3443 let task = MediaElementMicrotask::PauseIfNotInDocument {
3444 elem: DomRoot::from_ref(self),
3445 };
3446 ScriptThread::await_stable_state(Microtask::MediaElement(task));
3447 }
3448 }
3449
3450 fn adopting_steps(&self, cx: &mut JSContext, old_doc: &Document) {
3451 self.super_type().unwrap().adopting_steps(cx, old_doc);
3452
3453 if let Some(id) = &*self.media_controls_id.borrow() {
3457 let Some(shadow_root) = self.upcast::<Element>().shadow_root() else {
3458 error!("Missing media controls shadow root");
3459 return;
3460 };
3461
3462 old_doc.unregister_media_controls(id);
3463 self.owner_document()
3464 .register_media_controls(id, &shadow_root);
3465 }
3466 }
3467}
3468
3469#[derive(JSTraceable, MallocSizeOf)]
3470pub(crate) enum MediaElementMicrotask {
3471 ResourceSelection {
3472 elem: DomRoot<HTMLMediaElement>,
3473 generation_id: u32,
3474 #[no_trace]
3475 base_url: ServoUrl,
3476 },
3477 PauseIfNotInDocument {
3478 elem: DomRoot<HTMLMediaElement>,
3479 },
3480 Seeked {
3481 elem: DomRoot<HTMLMediaElement>,
3482 generation_id: u32,
3483 },
3484 SelectNextSourceChild {
3485 elem: DomRoot<HTMLMediaElement>,
3486 generation_id: u32,
3487 },
3488 SelectNextSourceChildAfterWait {
3489 elem: DomRoot<HTMLMediaElement>,
3490 generation_id: u32,
3491 },
3492}
3493
3494impl MicrotaskRunnable for MediaElementMicrotask {
3495 fn handler(&self, cx: &mut JSContext) {
3496 match self {
3497 &MediaElementMicrotask::ResourceSelection {
3498 ref elem,
3499 generation_id,
3500 ref base_url,
3501 } => {
3502 if generation_id == elem.generation_id.get() {
3503 elem.resource_selection_algorithm_sync(base_url.clone(), cx);
3504 }
3505 },
3506 MediaElementMicrotask::PauseIfNotInDocument { elem } => {
3508 if elem.upcast::<Node>().is_connected() {
3510 return;
3511 }
3512 elem.internal_pause_steps();
3514 },
3515 &MediaElementMicrotask::Seeked {
3516 ref elem,
3517 generation_id,
3518 } => {
3519 if generation_id == elem.generation_id.get() {
3520 elem.seek_end();
3521 }
3522 },
3523 &MediaElementMicrotask::SelectNextSourceChild {
3524 ref elem,
3525 generation_id,
3526 } => {
3527 if generation_id == elem.generation_id.get() {
3528 elem.select_next_source_child(cx);
3529 }
3530 },
3531 &MediaElementMicrotask::SelectNextSourceChildAfterWait {
3532 ref elem,
3533 generation_id,
3534 } => {
3535 if generation_id == elem.generation_id.get() {
3536 elem.select_next_source_child_after_wait(cx);
3537 }
3538 },
3539 }
3540 }
3541
3542 fn enter_realm<'cx>(&self, cx: &'cx mut js::context::JSContext) -> AutoRealm<'cx> {
3543 match self {
3544 &MediaElementMicrotask::ResourceSelection { ref elem, .. } |
3545 &MediaElementMicrotask::PauseIfNotInDocument { ref elem } |
3546 &MediaElementMicrotask::Seeked { ref elem, .. } |
3547 &MediaElementMicrotask::SelectNextSourceChild { ref elem, .. } |
3548 &MediaElementMicrotask::SelectNextSourceChildAfterWait { ref elem, .. } => {
3549 enter_auto_realm(cx, &**elem)
3550 },
3551 }
3552 }
3553}
3554
3555enum Resource {
3556 Object,
3557 Url(ServoUrl),
3558}
3559
3560#[derive(Debug, MallocSizeOf, PartialEq)]
3561enum DataBuffer {
3562 Payload(Vec<u8>),
3563 EndOfStream,
3564}
3565
3566#[derive(MallocSizeOf)]
3567struct BufferedDataSource {
3568 locked: Cell<bool>,
3573 buffers: VecDeque<DataBuffer>,
3575}
3576
3577impl BufferedDataSource {
3578 fn new() -> BufferedDataSource {
3579 BufferedDataSource {
3580 locked: Cell::new(true),
3581 buffers: VecDeque::default(),
3582 }
3583 }
3584
3585 fn set_locked(&self, locked: bool) {
3586 self.locked.set(locked)
3587 }
3588
3589 fn add_buffer_to_queue(&mut self, buffer: DataBuffer) {
3590 debug_assert_ne!(
3591 self.buffers.back(),
3592 Some(&DataBuffer::EndOfStream),
3593 "The media backend not expects any further data after end of stream"
3594 );
3595
3596 self.buffers.push_back(buffer);
3597 }
3598
3599 fn process_into_player_from_queue(
3600 &mut self,
3601 player: &Arc<Mutex<dyn Player>>,
3602 ) -> Result<(), PlayerError> {
3603 if self.locked.get() {
3605 return Ok(());
3606 }
3607
3608 while let Some(buffer) = self.buffers.pop_front() {
3609 match buffer {
3610 DataBuffer::Payload(payload) => {
3611 if let Err(error) = player.lock().unwrap().push_data(payload) {
3612 warn!("Could not push input data to player: {error:?}");
3613 return Err(error);
3614 }
3615 },
3616 DataBuffer::EndOfStream => {
3617 if let Err(error) = player.lock().unwrap().end_of_stream() {
3618 warn!("Could not signal EOS to player: {error:?}");
3619 return Err(error);
3620 }
3621 },
3622 }
3623 }
3624
3625 Ok(())
3626 }
3627
3628 fn reset(&mut self) {
3629 self.locked.set(true);
3630 self.buffers.clear();
3631 }
3632}
3633
3634#[derive(Debug, MallocSizeOf, PartialEq)]
3636enum CancelReason {
3637 Backoff,
3639 Error,
3641 Abort,
3643}
3644
3645#[derive(MallocSizeOf)]
3646pub(crate) struct HTMLMediaElementFetchContext {
3647 request_id: RequestId,
3649 cancel_reason: Option<CancelReason>,
3651 is_seekable: bool,
3653 origin_clean: bool,
3655 data_source: RefCell<BufferedDataSource>,
3657 fetch_canceller: FetchCanceller,
3660}
3661
3662impl HTMLMediaElementFetchContext {
3663 fn new(
3664 request_id: RequestId,
3665 core_resource_thread: CoreResourceThread,
3666 ) -> HTMLMediaElementFetchContext {
3667 HTMLMediaElementFetchContext {
3668 request_id,
3669 cancel_reason: None,
3670 is_seekable: false,
3671 origin_clean: true,
3672 data_source: RefCell::new(BufferedDataSource::new()),
3673 fetch_canceller: FetchCanceller::new(request_id, false, core_resource_thread),
3674 }
3675 }
3676
3677 fn request_id(&self) -> RequestId {
3678 self.request_id
3679 }
3680
3681 fn is_seekable(&self) -> bool {
3682 self.is_seekable
3683 }
3684
3685 fn set_seekable(&mut self, seekable: bool) {
3686 self.is_seekable = seekable;
3687 }
3688
3689 fn origin_is_clean(&self) -> bool {
3690 self.origin_clean
3691 }
3692
3693 fn set_origin_clean(&mut self, origin_clean: bool) {
3694 self.origin_clean = origin_clean;
3695 }
3696
3697 fn data_source(&self) -> &RefCell<BufferedDataSource> {
3698 &self.data_source
3699 }
3700
3701 fn cancel(&mut self, reason: CancelReason) {
3702 if self.cancel_reason.is_some() {
3703 return;
3704 }
3705 self.cancel_reason = Some(reason);
3706 self.data_source.borrow_mut().reset();
3707 self.fetch_canceller.abort();
3708 }
3709
3710 fn cancel_reason(&self) -> &Option<CancelReason> {
3711 &self.cancel_reason
3712 }
3713}
3714
3715struct HTMLMediaElementFetchListener {
3716 element: Trusted<HTMLMediaElement>,
3718 generation_id: u32,
3720 request_id: RequestId,
3722 next_progress_event: Instant,
3724 url: ServoUrl,
3726 expected_content_length: Option<u64>,
3728 fetched_content_length: u64,
3730 content_length_to_discard: u64,
3734}
3735
3736impl FetchResponseListener for HTMLMediaElementFetchListener {
3737 fn process_request_body(&mut self, _: RequestId) {}
3738
3739 fn process_response(
3740 &mut self,
3741 cx: &mut JSContext,
3742 _: RequestId,
3743 metadata: Result<FetchMetadata, NetworkError>,
3744 ) {
3745 let element = self.element.root();
3746
3747 let (metadata, origin_clean) = match metadata {
3748 Ok(fetch_metadata) => match fetch_metadata {
3749 FetchMetadata::Unfiltered(metadata) => (Some(metadata), true),
3750 FetchMetadata::Filtered { filtered, unsafe_ } => (
3751 Some(unsafe_),
3752 matches!(
3753 filtered,
3754 FilteredMetadata::Basic(_) | FilteredMetadata::Cors(_)
3755 ),
3756 ),
3757 },
3758 Err(_) => (None, true),
3759 };
3760
3761 let (status_is_success, is_seekable) =
3762 metadata.as_ref().map_or((false, false), |metadata| {
3763 let status = &metadata.status;
3764 (status.is_success(), *status == StatusCode::PARTIAL_CONTENT)
3765 });
3766
3767 if !status_is_success {
3769 if element.ready_state.get() == ReadyState::HaveNothing {
3770 element.media_data_processing_failure_steps();
3772 } else {
3773 element.media_data_processing_fatal_steps(MEDIA_ERR_NETWORK, cx);
3775 }
3776 return;
3777 }
3778
3779 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut() {
3780 current_fetch_context.set_seekable(is_seekable);
3781 current_fetch_context.set_origin_clean(origin_clean);
3782 }
3783
3784 if let Some(metadata) = metadata.as_ref() &&
3785 let Some(headers) = metadata.headers.as_ref()
3786 {
3787 let content_length = if let Some(content_range) = headers.typed_get::<ContentRange>() {
3790 content_range.bytes_len()
3791 } else {
3792 headers
3793 .typed_get::<ContentLength>()
3794 .map(|content_length| content_length.0)
3795 };
3796
3797 if content_length != self.expected_content_length &&
3799 let Some(content_length) = content_length
3800 {
3801 self.expected_content_length = Some(content_length);
3802 }
3803 }
3804
3805 if let Err(e) = element
3807 .player
3808 .borrow()
3809 .as_ref()
3810 .unwrap()
3811 .lock()
3812 .unwrap()
3813 .set_seekable(is_seekable)
3814 {
3815 warn!("Could not set player seekable {:?}", e);
3816 }
3817
3818 if let Some(expected_content_length) = self.expected_content_length &&
3819 let Err(e) = element
3820 .player
3821 .borrow()
3822 .as_ref()
3823 .unwrap()
3824 .lock()
3825 .unwrap()
3826 .set_input_size(expected_content_length)
3827 {
3828 warn!("Could not set player input size {:?}", e);
3829 }
3830 }
3831
3832 fn process_response_chunk(&mut self, _: &mut JSContext, _: RequestId, chunk: Vec<u8>) {
3833 let element = self.element.root();
3834
3835 self.fetched_content_length += chunk.len() as u64;
3836
3837 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut() {
3839 if let Some(CancelReason::Backoff) = current_fetch_context.cancel_reason() {
3840 return;
3841 }
3842
3843 let payload = if !current_fetch_context.is_seekable() &&
3845 self.content_length_to_discard != 0
3846 {
3847 if chunk.len() as u64 > self.content_length_to_discard {
3848 let shrink_chunk = chunk[self.content_length_to_discard as usize..].to_vec();
3849 self.content_length_to_discard = 0;
3850 shrink_chunk
3851 } else {
3852 self.content_length_to_discard -= chunk.len() as u64;
3854 return;
3855 }
3856 } else {
3857 chunk
3858 };
3859
3860 if let Err(e) = {
3861 let mut data_source = current_fetch_context.data_source().borrow_mut();
3862 data_source.add_buffer_to_queue(DataBuffer::Payload(payload));
3863 data_source
3864 .process_into_player_from_queue(element.player.borrow().as_ref().unwrap())
3865 } {
3866 if e == PlayerError::EnoughData {
3871 current_fetch_context.cancel(CancelReason::Backoff);
3872 }
3873 return;
3874 }
3875 }
3876
3877 if Instant::now() > self.next_progress_event {
3882 element.queue_media_element_task_to_fire_event(atom!("progress"));
3883 self.next_progress_event = Instant::now() + Duration::from_millis(350);
3884 }
3885 }
3886
3887 fn process_response_eof(
3888 self,
3889 cx: &mut JSContext,
3890 _: RequestId,
3891 status: Result<(), NetworkError>,
3892 timing: ResourceFetchTiming,
3893 ) {
3894 let element = self.element.root();
3895
3896 if status.is_ok() && self.fetched_content_length != 0 {
3898 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut()
3903 {
3904 if self.expected_content_length.is_none() &&
3911 let Err(e) = element
3912 .player
3913 .borrow()
3914 .as_ref()
3915 .unwrap()
3916 .lock()
3917 .unwrap()
3918 .set_input_size(self.fetched_content_length)
3919 {
3920 warn!("Could not set player input size {:?}", e);
3921 }
3922
3923 let mut data_source = current_fetch_context.data_source().borrow_mut();
3924
3925 data_source.add_buffer_to_queue(DataBuffer::EndOfStream);
3926 let _ = data_source
3927 .process_into_player_from_queue(element.player.borrow().as_ref().unwrap());
3928 }
3929
3930 element
3932 .upcast::<EventTarget>()
3933 .fire_event(cx, atom!("progress"));
3934
3935 element.network_state.set(NetworkState::Idle);
3938
3939 element
3940 .upcast::<EventTarget>()
3941 .fire_event(cx, atom!("suspend"));
3942 } else if status.is_err() && element.ready_state.get() != ReadyState::HaveNothing {
3943 element.media_data_processing_fatal_steps(MEDIA_ERR_NETWORK, cx);
3945 } else {
3946 element.media_data_processing_failure_steps();
3949 }
3950
3951 network_listener::submit_timing(cx, &self, &status, &timing);
3952 }
3953
3954 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
3955 let global = &self.resource_timing_global();
3956 global.report_csp_violations(violations, None, None);
3957 }
3958
3959 fn should_invoke(&self) -> bool {
3960 let element = self.element.root();
3961
3962 if element.generation_id.get() != self.generation_id || element.player.borrow().is_none() {
3963 return false;
3964 }
3965
3966 let Some(ref current_fetch_context) = *element.current_fetch_context.borrow() else {
3967 return false;
3968 };
3969
3970 if current_fetch_context.request_id() != self.request_id {
3972 return false;
3973 }
3974
3975 if let Some(cancel_reason) = current_fetch_context.cancel_reason() &&
3978 matches!(*cancel_reason, CancelReason::Error | CancelReason::Abort)
3979 {
3980 return false;
3981 }
3982
3983 true
3984 }
3985}
3986
3987impl ResourceTimingListener for HTMLMediaElementFetchListener {
3988 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
3989 let initiator_type = InitiatorType::LocalName(
3990 self.element
3991 .root()
3992 .upcast::<Element>()
3993 .local_name()
3994 .to_string(),
3995 );
3996 (initiator_type, self.url.clone())
3997 }
3998
3999 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
4000 self.element.root().owner_document().global()
4001 }
4002}
4003
4004impl HTMLMediaElementFetchListener {
4005 fn new(element: &HTMLMediaElement, request_id: RequestId, url: ServoUrl, offset: u64) -> Self {
4006 Self {
4007 element: Trusted::new(element),
4008 generation_id: element.generation_id.get(),
4009 request_id,
4010 next_progress_event: Instant::now() + Duration::from_millis(350),
4011 url,
4012 expected_content_length: None,
4013 fetched_content_length: 0,
4014 content_length_to_discard: offset,
4015 }
4016 }
4017}
4018
4019#[derive(JSTraceable, MallocSizeOf)]
4023struct HTMLMediaElementEventHandler {
4024 element: WeakRef<HTMLMediaElement>,
4025}
4026
4027#[expect(unsafe_code)]
4028unsafe impl Send for HTMLMediaElementEventHandler {}
4029
4030impl HTMLMediaElementEventHandler {
4031 fn new(element: &HTMLMediaElement) -> Self {
4032 Self {
4033 element: WeakRef::new(element),
4034 }
4035 }
4036
4037 fn handle_player_event(&self, player_id: usize, event: PlayerEvent, cx: &mut JSContext) {
4038 let Some(element) = self.element.root() else {
4039 return;
4040 };
4041
4042 if element.player_id().is_none_or(|id| id != player_id) {
4044 return;
4045 }
4046
4047 match event {
4048 PlayerEvent::DurationChanged(duration) => element.playback_duration_changed(duration),
4049 PlayerEvent::EndOfStream => element.playback_end(),
4050 PlayerEvent::EnoughData => element.playback_enough_data(),
4051 PlayerEvent::Error(ref error) => element.playback_error(error, cx),
4052 PlayerEvent::MetadataUpdated(ref metadata) => {
4053 element.playback_metadata_updated(cx, metadata)
4054 },
4055 PlayerEvent::NeedData => element.playback_need_data(),
4056 PlayerEvent::PositionChanged(position) => element.playback_position_changed(position),
4057 PlayerEvent::SeekData(offset, seek_lock) => {
4058 element.fetch_request(Some(offset), Some(seek_lock))
4059 },
4060 PlayerEvent::SeekDone(position) => element.playback_seek_done(position),
4061 PlayerEvent::StateChanged(ref state) => element.playback_state_changed(state),
4062 PlayerEvent::VideoFrameUpdated => element.playback_video_frame_updated(),
4063 }
4064 }
4065}
4066
4067impl Drop for HTMLMediaElementEventHandler {
4068 fn drop(&mut self) {
4069 assert_in_script();
4073 }
4074}