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 compositing_traits::{CrossProcessCompositorApi, ImageUpdate, SerializableImageData};
13use content_security_policy::sandboxing_directive::SandboxingFlagSet;
14use dom_struct::dom_struct;
15use embedder_traits::{MediaPositionState, MediaSessionEvent, MediaSessionPlaybackState};
16use euclid::default::Size2D;
17use headers::{ContentLength, ContentRange, HeaderMapExt};
18use html5ever::{LocalName, Prefix, QualName, local_name, ns};
19use http::StatusCode;
20use http::header::{self, HeaderMap, HeaderValue};
21use ipc_channel::ipc::{self, IpcSharedMemory};
22use ipc_channel::router::ROUTER;
23use js::jsapi::JSAutoRealm;
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 ResourceTimingType,
30};
31use pixels::RasterImage;
32use script_bindings::codegen::GenericBindings::TimeRangesBinding::TimeRangesMethods;
33use script_bindings::codegen::InheritTypes::{
34 ElementTypeId, HTMLElementTypeId, HTMLMediaElementTypeId, NodeTypeId,
35};
36use servo_config::pref;
37use servo_media::player::audio::AudioRenderer;
38use servo_media::player::video::{VideoFrame, VideoFrameRenderer};
39use servo_media::player::{PlaybackState, Player, PlayerError, PlayerEvent, SeekLock, StreamType};
40use servo_media::{ClientContextId, ServoMedia, SupportsMediaType};
41use servo_url::ServoUrl;
42use stylo_atoms::Atom;
43use uuid::Uuid;
44use webrender_api::{
45 ExternalImageData, ExternalImageId, ExternalImageType, ImageBufferKind, ImageDescriptor,
46 ImageDescriptorFlags, ImageFormat, ImageKey,
47};
48
49use crate::document_loader::{LoadBlocker, LoadType};
50use crate::dom::attr::Attr;
51use crate::dom::audio::audiotrack::AudioTrack;
52use crate::dom::audio::audiotracklist::AudioTrackList;
53use crate::dom::bindings::cell::DomRefCell;
54use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::{
55 CanPlayTypeResult, HTMLMediaElementConstants, HTMLMediaElementMethods,
56};
57use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConstants::*;
58use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods;
59use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
60use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
61use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode};
62use crate::dom::bindings::codegen::Bindings::URLBinding::URLMethods;
63use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
64use crate::dom::bindings::codegen::UnionTypes::{
65 MediaStreamOrBlob, VideoTrackOrAudioTrackOrTextTrack,
66};
67use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
68use crate::dom::bindings::inheritance::Castable;
69use crate::dom::bindings::num::Finite;
70use crate::dom::bindings::refcounted::Trusted;
71use crate::dom::bindings::reflector::DomGlobal;
72use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
73use crate::dom::bindings::str::{DOMString, USVString};
74use crate::dom::blob::Blob;
75use crate::dom::csp::{GlobalCspReporting, Violation};
76use crate::dom::document::Document;
77use crate::dom::element::{
78 AttributeMutation, CustomElementCreationMode, Element, ElementCreator,
79 cors_setting_for_element, reflect_cross_origin_attribute, set_cross_origin_attribute,
80};
81use crate::dom::event::Event;
82use crate::dom::eventtarget::EventTarget;
83use crate::dom::globalscope::GlobalScope;
84use crate::dom::html::htmlelement::HTMLElement;
85use crate::dom::html::htmlsourceelement::HTMLSourceElement;
86use crate::dom::html::htmlvideoelement::HTMLVideoElement;
87use crate::dom::mediaerror::MediaError;
88use crate::dom::mediafragmentparser::MediaFragmentParser;
89use crate::dom::medialist::MediaList;
90use crate::dom::mediastream::MediaStream;
91use crate::dom::node::{Node, NodeDamage, NodeTraits, UnbindContext};
92use crate::dom::performance::performanceresourcetiming::InitiatorType;
93use crate::dom::promise::Promise;
94use crate::dom::texttrack::TextTrack;
95use crate::dom::texttracklist::TextTrackList;
96use crate::dom::timeranges::{TimeRanges, TimeRangesContainer};
97use crate::dom::trackevent::TrackEvent;
98use crate::dom::url::URL;
99use crate::dom::videotrack::VideoTrack;
100use crate::dom::videotracklist::VideoTrackList;
101use crate::dom::virtualmethods::VirtualMethods;
102use crate::fetch::{FetchCanceller, create_a_potential_cors_request};
103use crate::microtask::{Microtask, MicrotaskRunnable};
104use crate::network_listener::{self, FetchResponseListener, ResourceTimingListener};
105use crate::realms::{InRealm, enter_realm};
106use crate::script_runtime::CanGc;
107use crate::script_thread::ScriptThread;
108use crate::task_source::SendableTaskSource;
109
110static MEDIA_CONTROL_CSS: &str = include_str!("../../resources/media-controls.css");
112
113static MEDIA_CONTROL_JS: &str = include_str!("../../resources/media-controls.js");
115
116#[derive(MallocSizeOf, PartialEq)]
117enum FrameStatus {
118 Locked,
119 Unlocked,
120}
121
122#[derive(MallocSizeOf)]
123struct FrameHolder(
124 FrameStatus,
125 #[ignore_malloc_size_of = "defined in servo-media"] VideoFrame,
126);
127
128impl FrameHolder {
129 fn new(frame: VideoFrame) -> FrameHolder {
130 FrameHolder(FrameStatus::Unlocked, frame)
131 }
132
133 fn lock(&mut self) {
134 if self.0 == FrameStatus::Unlocked {
135 self.0 = FrameStatus::Locked;
136 };
137 }
138
139 fn unlock(&mut self) {
140 if self.0 == FrameStatus::Locked {
141 self.0 = FrameStatus::Unlocked;
142 };
143 }
144
145 fn set(&mut self, new_frame: VideoFrame) {
146 if self.0 == FrameStatus::Unlocked {
147 self.1 = new_frame
148 };
149 }
150
151 fn get(&self) -> (u32, Size2D<i32>, usize) {
152 if self.0 == FrameStatus::Locked {
153 (
154 self.1.get_texture_id(),
155 Size2D::new(self.1.get_width(), self.1.get_height()),
156 0,
157 )
158 } else {
159 unreachable!();
160 }
161 }
162
163 fn get_frame(&self) -> VideoFrame {
164 self.1.clone()
165 }
166}
167
168#[derive(MallocSizeOf)]
169pub(crate) struct MediaFrameRenderer {
170 player_id: Option<usize>,
171 glplayer_id: Option<u64>,
172 compositor_api: CrossProcessCompositorApi,
173 #[ignore_malloc_size_of = "Defined in other crates"]
174 player_context: WindowGLContext,
175 current_frame: Option<MediaFrame>,
176 old_frame: Option<ImageKey>,
177 very_old_frame: Option<ImageKey>,
178 current_frame_holder: Option<FrameHolder>,
179 poster_frame: Option<MediaFrame>,
181}
182
183impl MediaFrameRenderer {
184 fn new(compositor_api: CrossProcessCompositorApi, player_context: WindowGLContext) -> Self {
185 Self {
186 player_id: None,
187 glplayer_id: None,
188 compositor_api,
189 player_context,
190 current_frame: None,
191 old_frame: None,
192 very_old_frame: None,
193 current_frame_holder: None,
194 poster_frame: None,
195 }
196 }
197
198 fn setup(
199 &mut self,
200 player_id: usize,
201 task_source: SendableTaskSource,
202 weak_video_renderer: Weak<Mutex<MediaFrameRenderer>>,
203 ) {
204 self.player_id = Some(player_id);
205
206 let (glplayer_id, image_receiver) = self
207 .player_context
208 .glplayer_thread_sender
209 .as_ref()
210 .map(|sender| {
211 let (image_sender, image_receiver) = ipc::channel::<GLPlayerMsgForward>().unwrap();
212 sender
213 .send(GLPlayerMsg::RegisterPlayer(image_sender))
214 .unwrap();
215 match image_receiver.recv().unwrap() {
216 GLPlayerMsgForward::PlayerId(id) => (Some(id), Some(image_receiver)),
217 _ => unreachable!(),
218 }
219 })
220 .unwrap_or((None, None));
221
222 self.glplayer_id = glplayer_id;
223
224 let Some(image_receiver) = image_receiver else {
225 return;
226 };
227
228 ROUTER.add_typed_route(
229 image_receiver,
230 Box::new(move |message| {
231 let message = message.unwrap();
232 let weak_video_renderer = weak_video_renderer.clone();
233
234 task_source.queue(task!(handle_glplayer_message: move || {
235 trace!("GLPlayer message {:?}", message);
236
237 let Some(video_renderer) = weak_video_renderer.upgrade() else {
238 return;
239 };
240
241 match message {
242 GLPlayerMsgForward::Lock(sender) => {
243 if let Some(holder) = video_renderer
244 .lock()
245 .unwrap()
246 .current_frame_holder
247 .as_mut() {
248 holder.lock();
249 sender.send(holder.get()).unwrap();
250 };
251 },
252 GLPlayerMsgForward::Unlock() => {
253 if let Some(holder) = video_renderer
254 .lock()
255 .unwrap()
256 .current_frame_holder
257 .as_mut() { holder.unlock() }
258 },
259 _ => (),
260 }
261 }));
262 }),
263 );
264 }
265
266 fn reset(&mut self) {
267 self.player_id = None;
268
269 if let Some(glplayer_id) = self.glplayer_id.take() {
270 self.player_context
271 .send(GLPlayerMsg::UnregisterPlayer(glplayer_id));
272 }
273
274 self.current_frame_holder = None;
275
276 let mut updates = smallvec::smallvec![];
277
278 if let Some(current_frame) = self.current_frame.take() {
279 updates.push(ImageUpdate::DeleteImage(current_frame.image_key));
280 }
281
282 if let Some(old_image_key) = self.old_frame.take() {
283 updates.push(ImageUpdate::DeleteImage(old_image_key));
284 }
285
286 if let Some(very_old_image_key) = self.very_old_frame.take() {
287 updates.push(ImageUpdate::DeleteImage(very_old_image_key));
288 }
289
290 if !updates.is_empty() {
291 self.compositor_api.update_images(updates);
292 }
293 }
294
295 fn set_poster_frame(&mut self, image: Option<Arc<RasterImage>>) {
296 self.poster_frame = image.and_then(|image| {
297 image.id.map(|image_key| MediaFrame {
298 image_key,
299 width: image.metadata.width as i32,
300 height: image.metadata.height as i32,
301 })
302 });
303 }
304}
305
306impl Drop for MediaFrameRenderer {
307 fn drop(&mut self) {
308 self.reset();
309 }
310}
311
312impl VideoFrameRenderer for MediaFrameRenderer {
313 fn render(&mut self, frame: VideoFrame) {
314 if self.player_id.is_none() || (frame.is_gl_texture() && self.glplayer_id.is_none()) {
315 return;
316 }
317
318 let mut updates = smallvec::smallvec![];
319
320 if let Some(old_image_key) = mem::replace(&mut self.very_old_frame, self.old_frame.take()) {
321 updates.push(ImageUpdate::DeleteImage(old_image_key));
322 }
323
324 let descriptor = ImageDescriptor::new(
325 frame.get_width(),
326 frame.get_height(),
327 ImageFormat::BGRA8,
328 ImageDescriptorFlags::empty(),
329 );
330
331 match &mut self.current_frame {
332 Some(current_frame)
333 if current_frame.width == frame.get_width() &&
334 current_frame.height == frame.get_height() =>
335 {
336 if !frame.is_gl_texture() {
337 updates.push(ImageUpdate::UpdateImage(
338 current_frame.image_key,
339 descriptor,
340 SerializableImageData::Raw(IpcSharedMemory::from_bytes(&frame.get_data())),
341 None,
342 ));
343 }
344
345 self.current_frame_holder
346 .get_or_insert_with(|| FrameHolder::new(frame.clone()))
347 .set(frame);
348
349 if let Some(old_image_key) = self.old_frame.take() {
350 updates.push(ImageUpdate::DeleteImage(old_image_key));
351 }
352 },
353 Some(current_frame) => {
354 self.old_frame = Some(current_frame.image_key);
355
356 let Some(new_image_key) = self.compositor_api.generate_image_key_blocking() else {
357 return;
358 };
359
360 current_frame.image_key = new_image_key;
362 current_frame.width = frame.get_width();
363 current_frame.height = frame.get_height();
364
365 let image_data = if frame.is_gl_texture() && self.glplayer_id.is_some() {
366 let texture_target = if frame.is_external_oes() {
367 ImageBufferKind::TextureExternal
368 } else {
369 ImageBufferKind::Texture2D
370 };
371
372 SerializableImageData::External(ExternalImageData {
373 id: ExternalImageId(self.glplayer_id.unwrap()),
374 channel_index: 0,
375 image_type: ExternalImageType::TextureHandle(texture_target),
376 normalized_uvs: false,
377 })
378 } else {
379 SerializableImageData::Raw(IpcSharedMemory::from_bytes(&frame.get_data()))
380 };
381
382 self.current_frame_holder
383 .get_or_insert_with(|| FrameHolder::new(frame.clone()))
384 .set(frame);
385
386 updates.push(ImageUpdate::AddImage(new_image_key, descriptor, image_data));
387 },
388 None => {
389 let Some(image_key) = self.compositor_api.generate_image_key_blocking() else {
390 return;
391 };
392
393 self.current_frame = Some(MediaFrame {
394 image_key,
395 width: frame.get_width(),
396 height: frame.get_height(),
397 });
398
399 let image_data = if frame.is_gl_texture() && self.glplayer_id.is_some() {
400 let texture_target = if frame.is_external_oes() {
401 ImageBufferKind::TextureExternal
402 } else {
403 ImageBufferKind::Texture2D
404 };
405
406 SerializableImageData::External(ExternalImageData {
407 id: ExternalImageId(self.glplayer_id.unwrap()),
408 channel_index: 0,
409 image_type: ExternalImageType::TextureHandle(texture_target),
410 normalized_uvs: false,
411 })
412 } else {
413 SerializableImageData::Raw(IpcSharedMemory::from_bytes(&frame.get_data()))
414 };
415
416 self.current_frame_holder = Some(FrameHolder::new(frame));
417
418 updates.push(ImageUpdate::AddImage(image_key, descriptor, image_data));
419 },
420 }
421 self.compositor_api.update_images(updates);
422 }
423}
424
425#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
426#[derive(JSTraceable, MallocSizeOf)]
427enum SrcObject {
428 MediaStream(Dom<MediaStream>),
429 Blob(Dom<Blob>),
430}
431
432impl From<MediaStreamOrBlob> for SrcObject {
433 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
434 fn from(src_object: MediaStreamOrBlob) -> SrcObject {
435 match src_object {
436 MediaStreamOrBlob::Blob(blob) => SrcObject::Blob(Dom::from_ref(&*blob)),
437 MediaStreamOrBlob::MediaStream(stream) => {
438 SrcObject::MediaStream(Dom::from_ref(&*stream))
439 },
440 }
441 }
442}
443
444#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq)]
445enum LoadState {
446 NotLoaded,
447 LoadingFromSrcObject,
448 LoadingFromSrcAttribute,
449 LoadingFromSourceChild,
450 WaitingForSource,
451}
452
453#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
455#[derive(JSTraceable, MallocSizeOf)]
456struct SourceChildrenPointer {
457 source_before_pointer: Dom<HTMLSourceElement>,
458 inclusive: bool,
459}
460
461impl SourceChildrenPointer {
462 fn new(source_before_pointer: DomRoot<HTMLSourceElement>, inclusive: bool) -> Self {
463 Self {
464 source_before_pointer: source_before_pointer.as_traced(),
465 inclusive,
466 }
467 }
468}
469
470#[dom_struct]
471#[allow(non_snake_case)]
472pub(crate) struct HTMLMediaElement {
473 htmlelement: HTMLElement,
474 network_state: Cell<NetworkState>,
476 ready_state: Cell<ReadyState>,
478 src_object: DomRefCell<Option<SrcObject>>,
480 current_src: DomRefCell<String>,
482 generation_id: Cell<u32>,
484 fired_loadeddata_event: Cell<bool>,
488 error: MutNullableDom<MediaError>,
490 paused: Cell<bool>,
492 defaultPlaybackRate: Cell<f64>,
494 playbackRate: Cell<f64>,
496 autoplaying: Cell<bool>,
498 delaying_the_load_event_flag: DomRefCell<Option<LoadBlocker>>,
500 #[conditional_malloc_size_of]
502 pending_play_promises: DomRefCell<Vec<Rc<Promise>>>,
503 #[allow(clippy::type_complexity)]
505 #[conditional_malloc_size_of]
506 in_flight_play_promises_queue: DomRefCell<VecDeque<(Box<[Rc<Promise>]>, ErrorResult)>>,
507 #[ignore_malloc_size_of = "servo_media"]
508 #[no_trace]
509 player: DomRefCell<Option<Arc<Mutex<dyn Player>>>>,
510 #[conditional_malloc_size_of]
511 #[no_trace]
512 video_renderer: Arc<Mutex<MediaFrameRenderer>>,
513 #[ignore_malloc_size_of = "servo_media"]
514 #[no_trace]
515 audio_renderer: DomRefCell<Option<Arc<Mutex<dyn AudioRenderer>>>>,
516 show_poster: Cell<bool>,
518 duration: Cell<f64>,
520 playback_position: Cell<f64>,
522 default_playback_start_position: Cell<f64>,
524 volume: Cell<f64>,
526 seeking: Cell<bool>,
528 muted: Cell<bool>,
530 load_state: Cell<LoadState>,
532 source_children_pointer: DomRefCell<Option<SourceChildrenPointer>>,
533 current_source_child: MutNullableDom<HTMLSourceElement>,
534 #[no_trace]
536 resource_url: DomRefCell<Option<ServoUrl>>,
537 #[no_trace]
540 blob_url: DomRefCell<Option<ServoUrl>>,
541 played: DomRefCell<TimeRangesContainer>,
543 audio_tracks_list: MutNullableDom<AudioTrackList>,
545 video_tracks_list: MutNullableDom<VideoTrackList>,
547 text_tracks_list: MutNullableDom<TextTrackList>,
549 #[ignore_malloc_size_of = "Defined in std::time"]
551 next_timeupdate_event: Cell<Instant>,
552 current_fetch_context: RefCell<Option<HTMLMediaElementFetchContext>>,
554 media_controls_id: DomRefCell<Option<String>>,
559}
560
561#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
563#[repr(u8)]
564pub(crate) enum NetworkState {
565 Empty = HTMLMediaElementConstants::NETWORK_EMPTY as u8,
566 Idle = HTMLMediaElementConstants::NETWORK_IDLE as u8,
567 Loading = HTMLMediaElementConstants::NETWORK_LOADING as u8,
568 NoSource = HTMLMediaElementConstants::NETWORK_NO_SOURCE as u8,
569}
570
571#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq, PartialOrd)]
573#[repr(u8)]
574#[allow(clippy::enum_variant_names)] pub(crate) enum ReadyState {
576 HaveNothing = HTMLMediaElementConstants::HAVE_NOTHING as u8,
577 HaveMetadata = HTMLMediaElementConstants::HAVE_METADATA as u8,
578 HaveCurrentData = HTMLMediaElementConstants::HAVE_CURRENT_DATA as u8,
579 HaveFutureData = HTMLMediaElementConstants::HAVE_FUTURE_DATA as u8,
580 HaveEnoughData = HTMLMediaElementConstants::HAVE_ENOUGH_DATA as u8,
581}
582
583impl HTMLMediaElement {
584 pub(crate) fn new_inherited(
585 tag_name: LocalName,
586 prefix: Option<Prefix>,
587 document: &Document,
588 ) -> Self {
589 Self {
590 htmlelement: HTMLElement::new_inherited(tag_name, prefix, document),
591 network_state: Cell::new(NetworkState::Empty),
592 ready_state: Cell::new(ReadyState::HaveNothing),
593 src_object: Default::default(),
594 current_src: DomRefCell::new("".to_owned()),
595 generation_id: Cell::new(0),
596 fired_loadeddata_event: Cell::new(false),
597 error: Default::default(),
598 paused: Cell::new(true),
599 defaultPlaybackRate: Cell::new(1.0),
600 playbackRate: Cell::new(1.0),
601 muted: Cell::new(false),
602 load_state: Cell::new(LoadState::NotLoaded),
603 source_children_pointer: DomRefCell::new(None),
604 current_source_child: Default::default(),
605 autoplaying: Cell::new(true),
607 delaying_the_load_event_flag: Default::default(),
608 pending_play_promises: Default::default(),
609 in_flight_play_promises_queue: Default::default(),
610 player: Default::default(),
611 video_renderer: Arc::new(Mutex::new(MediaFrameRenderer::new(
612 document.window().compositor_api().clone(),
613 document.window().get_player_context(),
614 ))),
615 audio_renderer: Default::default(),
616 show_poster: Cell::new(true),
617 duration: Cell::new(f64::NAN),
618 playback_position: Cell::new(0.),
619 default_playback_start_position: Cell::new(0.),
620 volume: Cell::new(1.0),
621 seeking: Cell::new(false),
622 resource_url: DomRefCell::new(None),
623 blob_url: DomRefCell::new(None),
624 played: DomRefCell::new(TimeRangesContainer::default()),
625 audio_tracks_list: Default::default(),
626 video_tracks_list: Default::default(),
627 text_tracks_list: Default::default(),
628 next_timeupdate_event: Cell::new(Instant::now() + Duration::from_millis(250)),
629 current_fetch_context: RefCell::new(None),
630 media_controls_id: DomRefCell::new(None),
631 }
632 }
633
634 pub(crate) fn network_state(&self) -> NetworkState {
635 self.network_state.get()
636 }
637
638 pub(crate) fn get_ready_state(&self) -> ReadyState {
639 self.ready_state.get()
640 }
641
642 fn media_type_id(&self) -> HTMLMediaElementTypeId {
643 match self.upcast::<Node>().type_id() {
644 NodeTypeId::Element(ElementTypeId::HTMLElement(
645 HTMLElementTypeId::HTMLMediaElement(media_type_id),
646 )) => media_type_id,
647 _ => unreachable!(),
648 }
649 }
650
651 fn play_media(&self) {
652 if let Some(ref player) = *self.player.borrow() {
653 if let Err(e) = player.lock().unwrap().set_rate(self.playbackRate.get()) {
654 warn!("Could not set the playback rate {:?}", e);
655 }
656 if let Err(e) = player.lock().unwrap().play() {
657 warn!("Could not play media {:?}", e);
658 }
659 }
660 }
661
662 pub(crate) fn delay_load_event(&self, delay: bool, can_gc: CanGc) {
669 let blocker = &self.delaying_the_load_event_flag;
670 if delay && blocker.borrow().is_none() {
671 *blocker.borrow_mut() = Some(LoadBlocker::new(&self.owner_document(), LoadType::Media));
672 } else if !delay && blocker.borrow().is_some() {
673 LoadBlocker::terminate(blocker, can_gc);
674 }
675 }
676
677 fn time_marches_on(&self) {
679 if Instant::now() > self.next_timeupdate_event.get() {
681 self.owner_global()
682 .task_manager()
683 .media_element_task_source()
684 .queue_simple_event(self.upcast(), atom!("timeupdate"));
685 self.next_timeupdate_event
686 .set(Instant::now() + Duration::from_millis(350));
687 }
688 }
689
690 fn internal_pause_steps(&self) {
692 self.autoplaying.set(false);
694
695 if !self.Paused() {
697 self.paused.set(true);
699
700 self.take_pending_play_promises(Err(Error::Abort));
702
703 let this = Trusted::new(self);
705 let generation_id = self.generation_id.get();
706 self.owner_global()
707 .task_manager()
708 .media_element_task_source()
709 .queue(task!(internal_pause_steps: move || {
710 let this = this.root();
711 if generation_id != this.generation_id.get() {
712 return;
713 }
714
715 this.fulfill_in_flight_play_promises(|| {
716 this.upcast::<EventTarget>().fire_event(atom!("timeupdate"), CanGc::note());
718
719 this.upcast::<EventTarget>().fire_event(atom!("pause"), CanGc::note());
721
722 if let Some(ref player) = *this.player.borrow() {
723 if let Err(e) = player.lock().unwrap().pause() {
724 error!("Could not pause player {:?}", e);
725 }
726 }
727
728 });
732 }));
733
734 }
738 }
739 fn is_allowed_to_play(&self) -> bool {
741 true
742 }
743
744 fn notify_about_playing(&self) {
746 self.take_pending_play_promises(Ok(()));
748
749 let this = Trusted::new(self);
751 let generation_id = self.generation_id.get();
752 self.owner_global()
753 .task_manager()
754 .media_element_task_source()
755 .queue(task!(notify_about_playing: move || {
756 let this = this.root();
757 if generation_id != this.generation_id.get() {
758 return;
759 }
760
761 this.fulfill_in_flight_play_promises(|| {
762 this.upcast::<EventTarget>().fire_event(atom!("playing"), CanGc::note());
764 this.play_media();
765
766 });
770
771 }));
772 }
773
774 fn change_ready_state(&self, ready_state: ReadyState) {
776 let old_ready_state = self.ready_state.get();
777 self.ready_state.set(ready_state);
778
779 if self.network_state.get() == NetworkState::Empty {
780 return;
781 }
782
783 if old_ready_state == ready_state {
784 return;
785 }
786
787 let owner_global = self.owner_global();
788 let task_manager = owner_global.task_manager();
789 let task_source = task_manager.media_element_task_source();
790
791 match (old_ready_state, ready_state) {
793 (ReadyState::HaveNothing, ReadyState::HaveMetadata) => {
794 task_source.queue_simple_event(self.upcast(), atom!("loadedmetadata"));
795 return;
797 },
798 (ReadyState::HaveMetadata, new) if new >= ReadyState::HaveCurrentData => {
799 if !self.fired_loadeddata_event.get() {
800 self.fired_loadeddata_event.set(true);
801 let this = Trusted::new(self);
802 task_source.queue(task!(media_reached_current_data: move || {
803 let this = this.root();
804 this.upcast::<EventTarget>().fire_event(atom!("loadeddata"), CanGc::note());
805 this.delay_load_event(false, CanGc::note());
806 }));
807 }
808
809 },
813 (ReadyState::HaveFutureData, new) if new <= ReadyState::HaveCurrentData => {
814 return;
819 },
820
821 _ => (),
822 }
823
824 if old_ready_state <= ReadyState::HaveCurrentData &&
825 ready_state >= ReadyState::HaveFutureData
826 {
827 task_source.queue_simple_event(self.upcast(), atom!("canplay"));
828
829 if !self.Paused() {
830 self.notify_about_playing();
831 }
832 }
833
834 if ready_state == ReadyState::HaveEnoughData {
835 if self.eligible_for_autoplay() {
837 self.paused.set(false);
839 if self.show_poster.get() {
841 self.show_poster.set(false);
842 self.time_marches_on();
843 }
844 task_source.queue_simple_event(self.upcast(), atom!("play"));
846 self.notify_about_playing();
848 self.autoplaying.set(false);
850 }
851
852 task_source.queue_simple_event(self.upcast(), atom!("canplaythrough"));
855 }
856 }
857
858 fn invoke_resource_selection_algorithm(&self, can_gc: CanGc) {
860 self.network_state.set(NetworkState::NoSource);
862
863 self.show_poster.set(true);
865
866 self.delay_load_event(true, can_gc);
869
870 let task = MediaElementMicrotask::ResourceSelection {
877 elem: DomRoot::from_ref(self),
878 generation_id: self.generation_id.get(),
879 base_url: self.owner_document().base_url(),
880 };
881
882 ScriptThread::await_stable_state(Microtask::MediaElement(task));
887 }
888
889 fn resource_selection_algorithm_sync(&self, base_url: ServoUrl, can_gc: CanGc) {
891 enum Mode {
898 Object,
899 Attribute(String),
900 Children(DomRoot<HTMLSourceElement>),
901 }
902
903 let mode = if self.src_object.borrow().is_some() {
905 Mode::Object
907 } else if let Some(attribute) = self
908 .upcast::<Element>()
909 .get_attribute(&ns!(), &local_name!("src"))
910 {
911 Mode::Attribute((**attribute.value()).to_owned())
914 } else if let Some(source) = self
915 .upcast::<Node>()
916 .children()
917 .filter_map(DomRoot::downcast::<HTMLSourceElement>)
918 .next()
919 {
920 Mode::Children(source)
924 } else {
925 self.load_state.set(LoadState::NotLoaded);
928
929 self.network_state.set(NetworkState::Empty);
931
932 self.delay_load_event(false, can_gc);
935
936 return;
938 };
939
940 self.network_state.set(NetworkState::Loading);
942
943 self.queue_media_element_task_to_fire_event(atom!("loadstart"));
946
947 match mode {
949 Mode::Object => {
950 self.load_from_src_object();
952 },
953 Mode::Attribute(src) => {
954 self.load_from_src_attribute(base_url, &src);
956 },
957 Mode::Children(source) => {
958 self.load_from_source_child(&source);
960 },
961 }
962 }
963
964 fn load_from_src_object(&self) {
966 self.load_state.set(LoadState::LoadingFromSrcObject);
967
968 "".clone_into(&mut self.current_src.borrow_mut());
970
971 self.resource_fetch_algorithm(Resource::Object);
977 }
978
979 fn load_from_src_attribute(&self, base_url: ServoUrl, src: &str) {
981 self.load_state.set(LoadState::LoadingFromSrcAttribute);
982
983 if src.is_empty() {
986 self.queue_dedicated_media_source_failure_steps();
987 return;
988 }
989
990 let Ok(url_record) = base_url.join(src) else {
994 self.queue_dedicated_media_source_failure_steps();
995 return;
996 };
997
998 *self.current_src.borrow_mut() = url_record.as_str().into();
1001
1002 self.resource_fetch_algorithm(Resource::Url(url_record));
1008 }
1009
1010 fn load_from_source_child(&self, source: &HTMLSourceElement) {
1012 self.load_state.set(LoadState::LoadingFromSourceChild);
1013
1014 *self.source_children_pointer.borrow_mut() =
1021 Some(SourceChildrenPointer::new(DomRoot::from_ref(source), false));
1022
1023 let element = source.upcast::<Element>();
1024
1025 let Some(src) = element
1029 .get_attribute(&ns!(), &local_name!("src"))
1030 .filter(|attribute| !attribute.value().is_empty())
1031 else {
1032 self.load_from_source_child_failure_steps(source);
1033 return;
1034 };
1035
1036 if let Some(media) = element.get_attribute(&ns!(), &local_name!("media")) {
1040 if !MediaList::matches_environment(&element.owner_document(), &media.value()) {
1041 self.load_from_source_child_failure_steps(source);
1042 return;
1043 }
1044 }
1045
1046 let Ok(url_record) = source.owner_document().base_url().join(&src.value()) else {
1050 self.load_from_source_child_failure_steps(source);
1053 return;
1054 };
1055
1056 if let Some(type_) = element.get_attribute(&ns!(), &local_name!("type")) {
1061 if ServoMedia::get().can_play_type(&type_.value()) == SupportsMediaType::No {
1062 self.load_from_source_child_failure_steps(source);
1063 return;
1064 }
1065 }
1066
1067 self.reset_media_player();
1069
1070 self.current_source_child.set(Some(source));
1071
1072 *self.current_src.borrow_mut() = url_record.as_str().into();
1075
1076 self.resource_fetch_algorithm(Resource::Url(url_record));
1081 }
1082
1083 fn load_from_source_child_failure_steps(&self, source: &HTMLSourceElement) {
1085 let trusted_this = Trusted::new(self);
1088 let trusted_source = Trusted::new(source);
1089 let generation_id = self.generation_id.get();
1090
1091 self.owner_global()
1092 .task_manager()
1093 .media_element_task_source()
1094 .queue(task!(queue_error_event: move || {
1095 let this = trusted_this.root();
1096 if generation_id != this.generation_id.get() {
1097 return;
1098 }
1099
1100 let source = trusted_source.root();
1101 source.upcast::<EventTarget>().fire_event(atom!("error"), CanGc::note());
1102 }));
1103
1104 let task = MediaElementMicrotask::SelectNextSourceChild {
1106 elem: DomRoot::from_ref(self),
1107 generation_id: self.generation_id.get(),
1108 };
1109
1110 ScriptThread::await_stable_state(Microtask::MediaElement(task));
1111 }
1112
1113 fn select_next_source_child(&self, can_gc: CanGc) {
1115 self.AudioTracks(can_gc).clear();
1117 self.VideoTracks(can_gc).clear();
1118
1119 let mut source_candidate = None;
1121
1122 if let Some(ref source_children_pointer) = *self.source_children_pointer.borrow() {
1130 if source_children_pointer.inclusive {
1134 for next_sibling in source_children_pointer
1135 .source_before_pointer
1136 .upcast::<Node>()
1137 .inclusively_following_siblings()
1138 {
1139 if let Some(next_source) = DomRoot::downcast::<HTMLSourceElement>(next_sibling)
1140 {
1141 source_candidate = Some(next_source);
1142 break;
1143 }
1144 }
1145 } else {
1146 for next_sibling in source_children_pointer
1147 .source_before_pointer
1148 .upcast::<Node>()
1149 .following_siblings()
1150 {
1151 if let Some(next_source) = DomRoot::downcast::<HTMLSourceElement>(next_sibling)
1152 {
1153 source_candidate = Some(next_source);
1154 break;
1155 }
1156 }
1157 };
1158 }
1159
1160 if let Some(source_candidate) = source_candidate {
1163 self.load_from_source_child(&source_candidate);
1164 return;
1165 }
1166
1167 self.load_state.set(LoadState::WaitingForSource);
1168
1169 *self.source_children_pointer.borrow_mut() = None;
1170
1171 self.network_state.set(NetworkState::NoSource);
1174
1175 self.show_poster.set(true);
1177
1178 let this = Trusted::new(self);
1181 let generation_id = self.generation_id.get();
1182
1183 self.owner_global()
1184 .task_manager()
1185 .media_element_task_source()
1186 .queue(task!(queue_delay_load_event: move || {
1187 let this = this.root();
1188 if generation_id != this.generation_id.get() {
1189 return;
1190 }
1191
1192 this.delay_load_event(false, CanGc::note());
1193 }));
1194
1195 }
1198
1199 fn resource_selection_algorithm_failure_steps(&self) {
1201 match self.load_state.get() {
1202 LoadState::LoadingFromSrcObject => {
1203 self.queue_dedicated_media_source_failure_steps();
1208 },
1209 LoadState::LoadingFromSrcAttribute => {
1210 self.queue_dedicated_media_source_failure_steps();
1215 },
1216 LoadState::LoadingFromSourceChild => {
1217 if let Some(source) = self.current_source_child.take() {
1220 self.load_from_source_child_failure_steps(&source);
1221 }
1222 },
1223 _ => {},
1224 }
1225 }
1226
1227 fn fetch_request(&self, offset: Option<u64>, seek_lock: Option<SeekLock>) {
1228 if self.resource_url.borrow().is_none() && self.blob_url.borrow().is_none() {
1229 error!("Missing request url");
1230 if let Some(seek_lock) = seek_lock {
1231 seek_lock.unlock(false);
1232 }
1233 self.resource_selection_algorithm_failure_steps();
1234 return;
1235 }
1236
1237 let document = self.owner_document();
1238 let destination = match self.media_type_id() {
1239 HTMLMediaElementTypeId::HTMLAudioElement => Destination::Audio,
1240 HTMLMediaElementTypeId::HTMLVideoElement => Destination::Video,
1241 };
1242 let mut headers = HeaderMap::new();
1243 headers.insert(
1245 header::RANGE,
1246 HeaderValue::from_str(&format!("bytes={}-", offset.unwrap_or(0))).unwrap(),
1247 );
1248 let url = match self.resource_url.borrow().as_ref() {
1249 Some(url) => url.clone(),
1250 None => self.blob_url.borrow().as_ref().unwrap().clone(),
1251 };
1252
1253 let cors_setting = cors_setting_for_element(self.upcast());
1254 let global = self.global();
1255 let request = create_a_potential_cors_request(
1256 Some(document.webview_id()),
1257 url.clone(),
1258 destination,
1259 cors_setting,
1260 None,
1261 global.get_referrer(),
1262 document.insecure_requests_policy(),
1263 document.has_trustworthy_ancestor_or_current_origin(),
1264 global.policy_container(),
1265 )
1266 .headers(headers)
1267 .origin(document.origin().immutable().clone())
1268 .pipeline_id(Some(self.global().pipeline_id()))
1269 .referrer_policy(document.get_referrer_policy());
1270
1271 let mut current_fetch_context = self.current_fetch_context.borrow_mut();
1272 if let Some(ref mut current_fetch_context) = *current_fetch_context {
1273 current_fetch_context.cancel(CancelReason::Abort);
1274 }
1275
1276 *current_fetch_context = Some(HTMLMediaElementFetchContext::new(
1277 request.id,
1278 global.core_resource_thread(),
1279 ));
1280 let listener =
1281 HTMLMediaElementFetchListener::new(self, request.id, url.clone(), offset.unwrap_or(0));
1282
1283 self.owner_document().fetch_background(request, listener);
1284
1285 if let Some(seek_lock) = seek_lock {
1290 seek_lock.unlock(true);
1291 }
1292 }
1293
1294 fn eligible_for_autoplay(&self) -> bool {
1296 self.autoplaying.get() &&
1298
1299 self.Paused() &&
1301
1302 self.Autoplay() &&
1304
1305 {
1308 let document = self.owner_document();
1309
1310 !document.has_active_sandboxing_flag(
1311 SandboxingFlagSet::SANDBOXED_AUTOMATIC_FEATURES_BROWSING_CONTEXT_FLAG,
1312 )
1313 }
1314
1315 }
1318
1319 fn resource_fetch_algorithm(&self, resource: Resource) {
1321 if let Err(e) = self.create_media_player(&resource) {
1322 error!("Create media player error {:?}", e);
1323 self.resource_selection_algorithm_failure_steps();
1324 return;
1325 }
1326
1327 match resource {
1336 Resource::Url(url) => {
1337 if self.Preload() == "none" && !self.autoplaying.get() {
1342 self.network_state.set(NetworkState::Idle);
1344
1345 self.queue_media_element_task_to_fire_event(atom!("suspend"));
1348
1349 let this = Trusted::new(self);
1353 let generation_id = self.generation_id.get();
1354
1355 self.owner_global()
1356 .task_manager()
1357 .media_element_task_source()
1358 .queue(task!(queue_delay_load_event: move || {
1359 let this = this.root();
1360 if generation_id != this.generation_id.get() {
1361 return;
1362 }
1363
1364 this.delay_load_event(false, CanGc::note());
1365 }));
1366
1367 return;
1376 }
1377
1378 *self.resource_url.borrow_mut() = Some(url);
1379
1380 self.fetch_request(None, None);
1382 },
1383 Resource::Object => {
1384 if let Some(ref src_object) = *self.src_object.borrow() {
1385 match src_object {
1386 SrcObject::Blob(blob) => {
1387 let blob_url = URL::CreateObjectURL(&self.global(), blob);
1388 *self.blob_url.borrow_mut() =
1389 Some(ServoUrl::parse(&blob_url.str()).expect("infallible"));
1390 self.fetch_request(None, None);
1391 },
1392 SrcObject::MediaStream(stream) => {
1393 let tracks = &*stream.get_tracks();
1394 for (pos, track) in tracks.iter().enumerate() {
1395 if self
1396 .player
1397 .borrow()
1398 .as_ref()
1399 .unwrap()
1400 .lock()
1401 .unwrap()
1402 .set_stream(&track.id(), pos == tracks.len() - 1)
1403 .is_err()
1404 {
1405 self.resource_selection_algorithm_failure_steps();
1406 }
1407 }
1408 },
1409 }
1410 }
1411 },
1412 }
1413 }
1414
1415 fn queue_dedicated_media_source_failure_steps(&self) {
1419 let this = Trusted::new(self);
1420 let generation_id = self.generation_id.get();
1421 self.take_pending_play_promises(Err(Error::NotSupported));
1422 self.owner_global()
1423 .task_manager()
1424 .media_element_task_source()
1425 .queue(task!(dedicated_media_source_failure_steps: move || {
1426 let this = this.root();
1427 if generation_id != this.generation_id.get() {
1428 return;
1429 }
1430
1431 this.fulfill_in_flight_play_promises(|| {
1432 this.error.set(Some(&*MediaError::new(
1435 &this.owner_window(),
1436 MEDIA_ERR_SRC_NOT_SUPPORTED, CanGc::note())));
1437
1438 this.AudioTracks(CanGc::note()).clear();
1440 this.VideoTracks(CanGc::note()).clear();
1441
1442 this.network_state.set(NetworkState::NoSource);
1445
1446 this.show_poster.set(true);
1448
1449 this.upcast::<EventTarget>().fire_event(atom!("error"), CanGc::note());
1451
1452 if let Some(ref player) = *this.player.borrow() {
1453 if let Err(e) = player.lock().unwrap().stop() {
1454 error!("Could not stop player {:?}", e);
1455 }
1456 }
1457
1458 });
1462
1463 this.delay_load_event(false, CanGc::note());
1466 }));
1467 }
1468
1469 fn in_error_state(&self) -> bool {
1470 self.error.get().is_some()
1471 }
1472
1473 fn is_potentially_playing(&self) -> bool {
1475 !self.paused.get() &&
1476 !self.Ended() &&
1477 self.error.get().is_none() &&
1478 !self.is_blocked_media_element()
1479 }
1480
1481 fn is_blocked_media_element(&self) -> bool {
1483 self.ready_state.get() <= ReadyState::HaveCurrentData ||
1484 self.is_paused_for_user_interaction() ||
1485 self.is_paused_for_in_band_content()
1486 }
1487
1488 fn is_paused_for_user_interaction(&self) -> bool {
1490 false
1493 }
1494
1495 fn is_paused_for_in_band_content(&self) -> bool {
1497 false
1500 }
1501
1502 fn media_element_load_algorithm(&self, can_gc: CanGc) {
1504 self.fired_loadeddata_event.set(false);
1507
1508 self.generation_id.set(self.generation_id.get() + 1);
1513
1514 self.load_state.set(LoadState::NotLoaded);
1515 *self.source_children_pointer.borrow_mut() = None;
1516 self.current_source_child.set(None);
1517
1518 while !self.in_flight_play_promises_queue.borrow().is_empty() {
1525 self.fulfill_in_flight_play_promises(|| ());
1526 }
1527
1528 let network_state = self.network_state.get();
1533
1534 if network_state == NetworkState::Loading || network_state == NetworkState::Idle {
1538 self.queue_media_element_task_to_fire_event(atom!("abort"));
1539 }
1540
1541 self.reset_media_player();
1543
1544 if network_state != NetworkState::Empty {
1546 self.queue_media_element_task_to_fire_event(atom!("emptied"));
1549
1550 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1553 current_fetch_context.cancel(CancelReason::Abort);
1554 }
1555
1556 self.AudioTracks(can_gc).clear();
1561 self.VideoTracks(can_gc).clear();
1562
1563 if self.ready_state.get() != ReadyState::HaveNothing {
1565 self.change_ready_state(ReadyState::HaveNothing);
1566 }
1567
1568 if !self.Paused() {
1570 self.paused.set(true);
1572
1573 self.take_pending_play_promises(Err(Error::Abort));
1576 self.fulfill_in_flight_play_promises(|| ());
1577 }
1578
1579 self.seeking.set(false);
1581
1582 if self.playback_position.get() != 0. {
1587 self.queue_media_element_task_to_fire_event(atom!("timeupdate"));
1588 }
1589 self.playback_position.set(0.);
1590
1591 self.duration.set(f64::NAN);
1595 }
1596
1597 self.playbackRate.set(self.defaultPlaybackRate.get());
1599
1600 self.error.set(None);
1602 self.autoplaying.set(true);
1603
1604 self.invoke_resource_selection_algorithm(can_gc);
1606
1607 }
1609
1610 fn queue_media_element_task_to_fire_event(&self, name: Atom) {
1613 let this = Trusted::new(self);
1614 let generation_id = self.generation_id.get();
1615
1616 self.owner_global()
1617 .task_manager()
1618 .media_element_task_source()
1619 .queue(task!(queue_event: move || {
1620 let this = this.root();
1621 if generation_id != this.generation_id.get() {
1622 return;
1623 }
1624
1625 this.upcast::<EventTarget>().fire_event(name, CanGc::note());
1626 }));
1627 }
1628
1629 fn push_pending_play_promise(&self, promise: &Rc<Promise>) {
1631 self.pending_play_promises
1632 .borrow_mut()
1633 .push(promise.clone());
1634 }
1635
1636 fn take_pending_play_promises(&self, result: ErrorResult) {
1647 let pending_play_promises = std::mem::take(&mut *self.pending_play_promises.borrow_mut());
1648 self.in_flight_play_promises_queue
1649 .borrow_mut()
1650 .push_back((pending_play_promises.into(), result));
1651 }
1652
1653 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
1662 fn fulfill_in_flight_play_promises<F>(&self, f: F)
1663 where
1664 F: FnOnce(),
1665 {
1666 let (promises, result) = self
1667 .in_flight_play_promises_queue
1668 .borrow_mut()
1669 .pop_front()
1670 .expect("there should be at least one list of in flight play promises");
1671 f();
1672 for promise in &*promises {
1673 match result {
1674 Ok(ref value) => promise.resolve_native(value, CanGc::note()),
1675 Err(ref error) => promise.reject_error(error.clone(), CanGc::note()),
1676 }
1677 }
1678 }
1679
1680 pub(crate) fn handle_source_child_insertion(&self, source: &HTMLSourceElement, can_gc: CanGc) {
1681 if self.upcast::<Element>().has_attribute(&local_name!("src")) {
1685 return;
1686 }
1687
1688 if self.network_state.get() == NetworkState::Empty {
1689 self.invoke_resource_selection_algorithm(can_gc);
1690 return;
1691 }
1692
1693 if self.load_state.get() != LoadState::WaitingForSource {
1697 return;
1698 }
1699
1700 self.load_state.set(LoadState::LoadingFromSourceChild);
1701
1702 *self.source_children_pointer.borrow_mut() =
1703 Some(SourceChildrenPointer::new(DomRoot::from_ref(source), true));
1704
1705 let task = MediaElementMicrotask::SelectNextSourceChildAfterWait {
1707 elem: DomRoot::from_ref(self),
1708 generation_id: self.generation_id.get(),
1709 };
1710
1711 ScriptThread::await_stable_state(Microtask::MediaElement(task));
1712 }
1713
1714 fn select_next_source_child_after_wait(&self, can_gc: CanGc) {
1716 self.delay_load_event(true, can_gc);
1719
1720 self.network_state.set(NetworkState::Loading);
1722
1723 self.select_next_source_child(can_gc);
1725 }
1726
1727 fn media_data_processing_failure_steps(&self) {
1732 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1734 current_fetch_context.cancel(CancelReason::Error);
1735 }
1736
1737 self.resource_selection_algorithm_failure_steps();
1739 }
1740
1741 fn media_data_processing_fatal_steps(&self, error: u16, can_gc: CanGc) {
1745 *self.source_children_pointer.borrow_mut() = None;
1746 self.current_source_child.set(None);
1747
1748 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1750 current_fetch_context.cancel(CancelReason::Error);
1751 }
1752
1753 self.error
1756 .set(Some(&*MediaError::new(&self.owner_window(), error, can_gc)));
1757
1758 self.network_state.set(NetworkState::Idle);
1760
1761 self.delay_load_event(false, can_gc);
1764
1765 self.upcast::<EventTarget>()
1767 .fire_event(atom!("error"), can_gc);
1768
1769 }
1771
1772 fn seek(&self, time: f64, _approximate_for_speed: bool, can_gc: CanGc) {
1774 self.show_poster.set(false);
1776
1777 if self.ready_state.get() == ReadyState::HaveNothing {
1779 return;
1780 }
1781
1782 self.seeking.set(true);
1789
1790 let time = f64::min(time, self.Duration());
1796
1797 let time = f64::max(time, 0.);
1799
1800 let seekable = self.Seekable(can_gc);
1802 if seekable.Length() == 0 {
1803 self.seeking.set(false);
1804 return;
1805 }
1806 let mut nearest_seekable_position = 0.0;
1807 let mut in_seekable_range = false;
1808 let mut nearest_seekable_distance = f64::MAX;
1809 for i in 0..seekable.Length() {
1810 let start = seekable.Start(i).unwrap().abs();
1811 let end = seekable.End(i).unwrap().abs();
1812 if time >= start && time <= end {
1813 nearest_seekable_position = time;
1814 in_seekable_range = true;
1815 break;
1816 } else if time < start {
1817 let distance = start - time;
1818 if distance < nearest_seekable_distance {
1819 nearest_seekable_distance = distance;
1820 nearest_seekable_position = start;
1821 }
1822 } else {
1823 let distance = time - end;
1824 if distance < nearest_seekable_distance {
1825 nearest_seekable_distance = distance;
1826 nearest_seekable_position = end;
1827 }
1828 }
1829 }
1830 let time = if in_seekable_range {
1831 time
1832 } else {
1833 nearest_seekable_position
1834 };
1835
1836 self.owner_global()
1841 .task_manager()
1842 .media_element_task_source()
1843 .queue_simple_event(self.upcast(), atom!("seeking"));
1844
1845 if let Some(ref player) = *self.player.borrow() {
1847 if let Err(e) = player.lock().unwrap().seek(time) {
1848 error!("Seek error {:?}", e);
1849 }
1850 }
1851
1852 }
1856
1857 fn seek_end(&self) {
1859 self.seeking.set(false);
1861
1862 self.time_marches_on();
1864
1865 let global = self.owner_global();
1867 let task_manager = global.task_manager();
1868 let task_source = task_manager.media_element_task_source();
1869 task_source.queue_simple_event(self.upcast(), atom!("timeupdate"));
1870
1871 task_source.queue_simple_event(self.upcast(), atom!("seeked"));
1873 }
1874
1875 pub(crate) fn set_poster_frame(&self, image: Option<Arc<RasterImage>>) {
1877 let queue_postershown_event = pref!(media_testing_enabled) && image.is_some();
1878
1879 self.video_renderer.lock().unwrap().set_poster_frame(image);
1880
1881 self.upcast::<Node>().dirty(NodeDamage::Other);
1882
1883 if queue_postershown_event {
1884 self.owner_global()
1885 .task_manager()
1886 .media_element_task_source()
1887 .queue_simple_event(self.upcast(), atom!("postershown"));
1888 }
1889 }
1890
1891 fn create_media_player(&self, resource: &Resource) -> Result<(), ()> {
1892 let stream_type = match *resource {
1893 Resource::Object => {
1894 if let Some(ref src_object) = *self.src_object.borrow() {
1895 match src_object {
1896 SrcObject::MediaStream(_) => StreamType::Stream,
1897 _ => StreamType::Seekable,
1898 }
1899 } else {
1900 return Err(());
1901 }
1902 },
1903 _ => StreamType::Seekable,
1904 };
1905
1906 let window = self.owner_window();
1907 let (action_sender, action_receiver) = ipc::channel::<PlayerEvent>().unwrap();
1908 let video_renderer: Option<Arc<Mutex<dyn VideoFrameRenderer>>> = match self.media_type_id()
1909 {
1910 HTMLMediaElementTypeId::HTMLAudioElement => None,
1911 HTMLMediaElementTypeId::HTMLVideoElement => Some(self.video_renderer.clone()),
1912 };
1913
1914 let audio_renderer = self.audio_renderer.borrow().as_ref().cloned();
1915
1916 let pipeline_id = window.pipeline_id();
1917 let client_context_id =
1918 ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
1919 let player = ServoMedia::get().create_player(
1920 &client_context_id,
1921 stream_type,
1922 action_sender,
1923 video_renderer,
1924 audio_renderer,
1925 Box::new(window.get_player_context()),
1926 );
1927 let player_id = {
1928 let player_guard = player.lock().unwrap();
1929
1930 if let Err(e) = player_guard.set_mute(self.muted.get()) {
1931 log::warn!("Could not set mute state: {:?}", e);
1932 }
1933
1934 player_guard.get_id()
1935 };
1936
1937 *self.player.borrow_mut() = Some(player);
1938
1939 let trusted_node = Trusted::new(self);
1940 let task_source = self
1941 .owner_global()
1942 .task_manager()
1943 .media_element_task_source()
1944 .to_sendable();
1945 ROUTER.add_typed_route(
1946 action_receiver,
1947 Box::new(move |message| {
1948 let event = message.unwrap();
1949 trace!("Player event {:?}", event);
1950 let this = trusted_node.clone();
1951 task_source.queue(task!(handle_player_event: move || {
1952 this.root().handle_player_event(player_id, &event, CanGc::note());
1953 }));
1954 }),
1955 );
1956
1957 let task_source = self
1958 .owner_global()
1959 .task_manager()
1960 .media_element_task_source()
1961 .to_sendable();
1962 let weak_video_renderer = Arc::downgrade(&self.video_renderer);
1963
1964 self.video_renderer
1965 .lock()
1966 .unwrap()
1967 .setup(player_id, task_source, weak_video_renderer);
1968
1969 Ok(())
1970 }
1971
1972 fn reset_media_player(&self) {
1973 if self.player.borrow().is_none() {
1974 return;
1975 }
1976
1977 if let Some(ref player) = *self.player.borrow() {
1978 if let Err(e) = player.lock().unwrap().stop() {
1979 error!("Could not stop player {:?}", e);
1980 }
1981 }
1982
1983 *self.player.borrow_mut() = None;
1984 self.video_renderer.lock().unwrap().reset();
1985 self.handle_resize(None, None);
1986 }
1987
1988 pub(crate) fn set_audio_track(&self, idx: usize, enabled: bool) {
1989 if let Some(ref player) = *self.player.borrow() {
1990 if let Err(err) = player.lock().unwrap().set_audio_track(idx as i32, enabled) {
1991 warn!("Could not set audio track {:#?}", err);
1992 }
1993 }
1994 }
1995
1996 pub(crate) fn set_video_track(&self, idx: usize, enabled: bool) {
1997 if let Some(ref player) = *self.player.borrow() {
1998 if let Err(err) = player.lock().unwrap().set_video_track(idx as i32, enabled) {
1999 warn!("Could not set video track {:#?}", err);
2000 }
2001 }
2002 }
2003
2004 fn end_of_playback_in_forwards_direction(&self, can_gc: CanGc) {
2005 if self.Loop() {
2008 self.seek(
2009 self.earliest_possible_position(),
2010 false,
2011 can_gc,
2012 );
2013 return;
2014 }
2015 let this = Trusted::new(self);
2022
2023 self.owner_global()
2024 .task_manager()
2025 .media_element_task_source()
2026 .queue(task!(reaches_the_end_steps: move || {
2027 let this = this.root();
2028 this.upcast::<EventTarget>().fire_event(atom!("timeupdate"), CanGc::note());
2030
2031 if this.Ended() && !this.Paused() {
2034 this.paused.set(true);
2036
2037 this.upcast::<EventTarget>().fire_event(atom!("pause"), CanGc::note());
2039
2040 this.take_pending_play_promises(Err(Error::Abort));
2043 this.fulfill_in_flight_play_promises(|| ());
2044 }
2045
2046 this.upcast::<EventTarget>().fire_event(atom!("ended"), CanGc::note());
2048 }));
2049
2050 self.change_ready_state(ReadyState::HaveCurrentData);
2052 }
2053
2054 fn playback_end(&self, can_gc: CanGc) {
2055 match self.direction_of_playback() {
2057 PlaybackDirection::Forwards => self.end_of_playback_in_forwards_direction(can_gc),
2058
2059 PlaybackDirection::Backwards => {
2060 if self.playback_position.get() <= self.earliest_possible_position() {
2061 self.owner_global()
2062 .task_manager()
2063 .media_element_task_source()
2064 .queue_simple_event(self.upcast(), atom!("ended"));
2065 }
2066 },
2067 }
2068 }
2069
2070 fn playback_error(&self, error: &str, can_gc: CanGc) {
2071 error!("Player error: {:?}", error);
2072
2073 if self.in_error_state() {
2077 return;
2078 }
2079
2080 if self.ready_state.get() == ReadyState::HaveNothing {
2082 self.media_data_processing_failure_steps();
2085 } else {
2086 self.media_data_processing_fatal_steps(MEDIA_ERR_DECODE, can_gc);
2088 }
2089 }
2090
2091 fn playback_metadata_updated(
2092 &self,
2093 metadata: &servo_media::player::metadata::Metadata,
2094 can_gc: CanGc,
2095 ) {
2096 if !metadata.audio_tracks.is_empty() {
2099 for (i, _track) in metadata.audio_tracks.iter().enumerate() {
2100 let kind = match i {
2102 0 => DOMString::from("main"),
2103 _ => DOMString::new(),
2104 };
2105 let window = self.owner_window();
2106 let audio_track = AudioTrack::new(
2107 &window,
2108 DOMString::new(),
2109 kind,
2110 DOMString::new(),
2111 DOMString::new(),
2112 Some(&*self.AudioTracks(can_gc)),
2113 can_gc,
2114 );
2115
2116 self.AudioTracks(can_gc).add(&audio_track);
2118
2119 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
2121 let fragment = MediaFragmentParser::from(servo_url);
2122 if let Some(id) = fragment.id() {
2123 if audio_track.id() == id {
2124 self.AudioTracks(can_gc)
2125 .set_enabled(self.AudioTracks(can_gc).len() - 1, true);
2126 }
2127 }
2128
2129 if fragment.tracks().contains(&audio_track.kind().into()) {
2130 self.AudioTracks(can_gc)
2131 .set_enabled(self.AudioTracks(can_gc).len() - 1, true);
2132 }
2133 }
2134
2135 if self.AudioTracks(can_gc).enabled_index().is_none() {
2137 self.AudioTracks(can_gc)
2138 .set_enabled(self.AudioTracks(can_gc).len() - 1, true);
2139 }
2140
2141 let event = TrackEvent::new(
2143 self.global().as_window(),
2144 atom!("addtrack"),
2145 false,
2146 false,
2147 &Some(VideoTrackOrAudioTrackOrTextTrack::AudioTrack(audio_track)),
2148 can_gc,
2149 );
2150
2151 event
2152 .upcast::<Event>()
2153 .fire(self.upcast::<EventTarget>(), can_gc);
2154 }
2155 }
2156
2157 if !metadata.video_tracks.is_empty() {
2159 for (i, _track) in metadata.video_tracks.iter().enumerate() {
2160 let kind = match i {
2162 0 => DOMString::from("main"),
2163 _ => DOMString::new(),
2164 };
2165 let window = self.owner_window();
2166 let video_track = VideoTrack::new(
2167 &window,
2168 DOMString::new(),
2169 kind,
2170 DOMString::new(),
2171 DOMString::new(),
2172 Some(&*self.VideoTracks(can_gc)),
2173 can_gc,
2174 );
2175
2176 self.VideoTracks(can_gc).add(&video_track);
2178
2179 if let Some(track) = self.VideoTracks(can_gc).item(0) {
2181 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
2182 let fragment = MediaFragmentParser::from(servo_url);
2183 if let Some(id) = fragment.id() {
2184 if track.id() == id {
2185 self.VideoTracks(can_gc).set_selected(0, true);
2186 }
2187 } else if fragment.tracks().contains(&track.kind().into()) {
2188 self.VideoTracks(can_gc).set_selected(0, true);
2189 }
2190 }
2191 }
2192
2193 if self.VideoTracks(can_gc).selected_index().is_none() {
2195 self.VideoTracks(can_gc)
2196 .set_selected(self.VideoTracks(can_gc).len() - 1, true);
2197 }
2198
2199 let event = TrackEvent::new(
2201 self.global().as_window(),
2202 atom!("addtrack"),
2203 false,
2204 false,
2205 &Some(VideoTrackOrAudioTrackOrTextTrack::VideoTrack(video_track)),
2206 can_gc,
2207 );
2208
2209 event
2210 .upcast::<Event>()
2211 .fire(self.upcast::<EventTarget>(), can_gc);
2212 }
2213 }
2214
2215 self.playback_position.set(0.);
2224
2225 let previous_duration = self.duration.get();
2227 if let Some(duration) = metadata.duration {
2228 self.duration.set(duration.as_secs() as f64);
2229 } else {
2230 self.duration.set(f64::INFINITY);
2231 }
2232 if previous_duration != self.duration.get() {
2233 self.owner_global()
2234 .task_manager()
2235 .media_element_task_source()
2236 .queue_simple_event(self.upcast(), atom!("durationchange"));
2237 }
2238
2239 self.handle_resize(Some(metadata.width), Some(metadata.height));
2241
2242 self.change_ready_state(ReadyState::HaveMetadata);
2244
2245 let mut jumped = false;
2247
2248 if self.default_playback_start_position.get() > 0. {
2250 self.seek(
2251 self.default_playback_start_position.get(),
2252 false,
2253 can_gc,
2254 );
2255 jumped = true;
2256 }
2257
2258 self.default_playback_start_position.set(0.);
2260
2261 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
2263 let fragment = MediaFragmentParser::from(servo_url);
2264 if let Some(start) = fragment.start() {
2265 if start > 0. && start < self.duration.get() {
2266 self.playback_position.set(start);
2267 if !jumped {
2268 self.seek(self.playback_position.get(), false, can_gc)
2269 }
2270 }
2271 }
2272 }
2273
2274 let global = self.global();
2277 let window = global.as_window();
2278
2279 window.Navigator().MediaSession().update_title(
2281 metadata
2282 .title
2283 .clone()
2284 .unwrap_or(window.get_url().into_string()),
2285 );
2286 }
2287
2288 fn playback_video_frame_updated(&self) {
2289 if let Some(frame) = self.video_renderer.lock().unwrap().current_frame {
2291 self.handle_resize(Some(frame.width as u32), Some(frame.height as u32));
2292 }
2293 }
2294
2295 fn playback_need_data(&self, can_gc: CanGc) {
2296 if let Some(ref current_fetch_context) = *self.current_fetch_context.borrow() {
2302 if let Some(reason) = current_fetch_context.cancel_reason() {
2303 if *reason == CancelReason::Backoff {
2309 self.seek(self.playback_position.get(), false, can_gc);
2310 }
2311 return;
2312 }
2313 }
2314
2315 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
2316 if let Err(e) = {
2317 let mut data_source = current_fetch_context.data_source().borrow_mut();
2318 data_source.set_locked(false);
2319 data_source.process_into_player_from_queue(self.player.borrow().as_ref().unwrap())
2320 } {
2321 if e == PlayerError::EnoughData {
2326 current_fetch_context.cancel(CancelReason::Backoff);
2327 }
2328 }
2329 }
2330 }
2331
2332 fn playback_enough_data(&self) {
2333 self.change_ready_state(ReadyState::HaveEnoughData);
2334
2335 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
2340 if current_fetch_context.is_seekable() {
2341 current_fetch_context.cancel(CancelReason::Backoff);
2342 }
2343 }
2344 }
2345
2346 fn playback_position_changed(&self, position: u64) {
2347 let position = position as f64;
2348 let _ = self
2349 .played
2350 .borrow_mut()
2351 .add(self.playback_position.get(), position);
2352 self.playback_position.set(position);
2353 self.time_marches_on();
2354 let media_position_state =
2355 MediaPositionState::new(self.duration.get(), self.playbackRate.get(), position);
2356 debug!(
2357 "Sending media session event set position state {:?}",
2358 media_position_state
2359 );
2360 self.send_media_session_event(MediaSessionEvent::SetPositionState(media_position_state));
2361 }
2362
2363 fn playback_seek_done(&self) {
2364 let task = MediaElementMicrotask::Seeked {
2369 elem: DomRoot::from_ref(self),
2370 generation_id: self.generation_id.get(),
2371 };
2372 ScriptThread::await_stable_state(Microtask::MediaElement(task));
2373 }
2374
2375 fn playback_state_changed(&self, state: &PlaybackState) {
2376 let mut media_session_playback_state = MediaSessionPlaybackState::None_;
2377 match *state {
2378 PlaybackState::Paused => {
2379 media_session_playback_state = MediaSessionPlaybackState::Paused;
2380 if self.ready_state.get() == ReadyState::HaveMetadata {
2381 self.change_ready_state(ReadyState::HaveEnoughData);
2382 }
2383 },
2384 PlaybackState::Playing => {
2385 media_session_playback_state = MediaSessionPlaybackState::Playing;
2386 },
2387 PlaybackState::Buffering => {
2388 return;
2392 },
2393 _ => {},
2394 };
2395 debug!(
2396 "Sending media session event playback state changed to {:?}",
2397 media_session_playback_state
2398 );
2399 self.send_media_session_event(MediaSessionEvent::PlaybackStateChange(
2400 media_session_playback_state,
2401 ));
2402 }
2403
2404 fn handle_player_event(&self, player_id: usize, event: &PlayerEvent, can_gc: CanGc) {
2405 if self
2407 .player
2408 .borrow()
2409 .as_ref()
2410 .is_none_or(|player| player.lock().unwrap().get_id() != player_id)
2411 {
2412 return;
2413 }
2414
2415 match *event {
2416 PlayerEvent::EndOfStream => self.playback_end(can_gc),
2417 PlayerEvent::Error(ref error) => self.playback_error(error, can_gc),
2418 PlayerEvent::VideoFrameUpdated => self.playback_video_frame_updated(),
2419 PlayerEvent::MetadataUpdated(ref metadata) => {
2420 self.playback_metadata_updated(metadata, can_gc)
2421 },
2422 PlayerEvent::NeedData => self.playback_need_data(can_gc),
2423 PlayerEvent::EnoughData => self.playback_enough_data(),
2424 PlayerEvent::PositionChanged(position) => self.playback_position_changed(position),
2425 PlayerEvent::SeekData(p, ref seek_lock) => {
2426 self.fetch_request(Some(p), Some(seek_lock.clone()))
2427 },
2428 PlayerEvent::SeekDone(_) => self.playback_seek_done(),
2429 PlayerEvent::StateChanged(ref state) => self.playback_state_changed(state),
2430 }
2431 }
2432
2433 fn earliest_possible_position(&self) -> f64 {
2435 self.played
2436 .borrow()
2437 .start(0)
2438 .unwrap_or_else(|_| self.playback_position.get())
2439 }
2440
2441 fn render_controls(&self, can_gc: CanGc) {
2442 if self.upcast::<Element>().is_shadow_host() {
2443 return;
2445 }
2446
2447 let shadow_root = self
2450 .upcast::<Element>()
2451 .attach_ua_shadow_root(false, can_gc);
2452 let document = self.owner_document();
2453 let script = Element::create(
2454 QualName::new(None, ns!(html), local_name!("script")),
2455 None,
2456 &document,
2457 ElementCreator::ScriptCreated,
2458 CustomElementCreationMode::Asynchronous,
2459 None,
2460 can_gc,
2461 );
2462 let id = Uuid::new_v4().to_string();
2468 document.register_media_controls(&id, &shadow_root);
2469 let media_controls_script = MEDIA_CONTROL_JS.replace("@@@id@@@", &id);
2470 *self.media_controls_id.borrow_mut() = Some(id);
2471 script
2472 .upcast::<Node>()
2473 .set_text_content_for_element(Some(DOMString::from(media_controls_script)), can_gc);
2474 if let Err(e) = shadow_root
2475 .upcast::<Node>()
2476 .AppendChild(script.upcast::<Node>(), can_gc)
2477 {
2478 warn!("Could not render media controls {:?}", e);
2479 return;
2480 }
2481
2482 let style = Element::create(
2483 QualName::new(None, ns!(html), local_name!("style")),
2484 None,
2485 &document,
2486 ElementCreator::ScriptCreated,
2487 CustomElementCreationMode::Asynchronous,
2488 None,
2489 can_gc,
2490 );
2491
2492 style
2493 .upcast::<Node>()
2494 .set_text_content_for_element(Some(DOMString::from(MEDIA_CONTROL_CSS)), can_gc);
2495
2496 if let Err(e) = shadow_root
2497 .upcast::<Node>()
2498 .AppendChild(style.upcast::<Node>(), can_gc)
2499 {
2500 warn!("Could not render media controls {:?}", e);
2501 }
2502
2503 self.upcast::<Node>().dirty(NodeDamage::Other);
2504 }
2505
2506 fn remove_controls(&self) {
2507 if let Some(id) = self.media_controls_id.borrow_mut().take() {
2508 self.owner_document().unregister_media_controls(&id);
2509 }
2510 }
2511
2512 pub(crate) fn get_current_frame(&self) -> Option<VideoFrame> {
2514 self.video_renderer
2515 .lock()
2516 .unwrap()
2517 .current_frame_holder
2518 .as_ref()
2519 .map(|holder| holder.get_frame())
2520 }
2521
2522 pub(crate) fn get_current_frame_to_present(&self) -> Option<MediaFrame> {
2525 let (current_frame, poster_frame) = {
2526 let renderer = self.video_renderer.lock().unwrap();
2527 (renderer.current_frame, renderer.poster_frame)
2528 };
2529
2530 if (self.show_poster.get() || current_frame.is_none()) && poster_frame.is_some() {
2533 return poster_frame;
2534 }
2535
2536 current_frame
2537 }
2538
2539 fn handle_resize(&self, width: Option<u32>, height: Option<u32>) {
2540 if let Some(video_elem) = self.downcast::<HTMLVideoElement>() {
2541 video_elem.resize(width, height);
2542 self.upcast::<Node>().dirty(NodeDamage::Other);
2543 }
2544 }
2545
2546 pub(crate) fn set_audio_renderer(
2551 &self,
2552 audio_renderer: Arc<Mutex<dyn AudioRenderer>>,
2553 can_gc: CanGc,
2554 ) {
2555 *self.audio_renderer.borrow_mut() = Some(audio_renderer);
2556 if let Some(ref player) = *self.player.borrow() {
2557 if let Err(e) = player.lock().unwrap().stop() {
2558 error!("Could not stop player {:?}", e);
2559 }
2560 self.media_element_load_algorithm(can_gc);
2561 }
2562 }
2563
2564 fn send_media_session_event(&self, event: MediaSessionEvent) {
2565 let global = self.global();
2566 let media_session = global.as_window().Navigator().MediaSession();
2567
2568 media_session.register_media_instance(self);
2569
2570 media_session.send_event(event);
2571 }
2572
2573 pub(crate) fn set_duration(&self, duration: f64) {
2574 self.duration.set(duration);
2575 }
2576
2577 pub(crate) fn reset(&self) {
2578 if let Some(ref player) = *self.player.borrow() {
2579 if let Err(e) = player.lock().unwrap().stop() {
2580 error!("Could not stop player {:?}", e);
2581 }
2582 }
2583 }
2584
2585 pub(crate) fn origin_is_clean(&self) -> bool {
2587 if self.src_object.borrow().is_some() {
2589 return true;
2592 }
2593
2594 if self.resource_url.borrow().is_some() {
2596 if let Some(ref current_fetch_context) = *self.current_fetch_context.borrow() {
2600 return current_fetch_context.origin_is_clean();
2601 }
2602 }
2603
2604 true
2605 }
2606}
2607
2608#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
2610enum PlaybackDirection {
2611 Forwards,
2612 #[expect(dead_code)]
2613 Backwards,
2614}
2615
2616impl HTMLMediaElement {
2620 fn direction_of_playback(&self) -> PlaybackDirection {
2622 PlaybackDirection::Forwards
2623 }
2624}
2625
2626impl HTMLMediaElementMethods<crate::DomTypeHolder> for HTMLMediaElement {
2627 fn NetworkState(&self) -> u16 {
2629 self.network_state.get() as u16
2630 }
2631
2632 fn ReadyState(&self) -> u16 {
2634 self.ready_state.get() as u16
2635 }
2636
2637 make_bool_getter!(Autoplay, "autoplay");
2639 make_bool_setter!(SetAutoplay, "autoplay");
2641
2642 make_bool_getter!(Loop, "loop");
2644 make_bool_setter!(SetLoop, "loop");
2646
2647 make_bool_getter!(DefaultMuted, "muted");
2649 make_bool_setter!(SetDefaultMuted, "muted");
2651
2652 make_bool_getter!(Controls, "controls");
2654 make_bool_setter!(SetControls, "controls");
2656
2657 make_url_getter!(Src, "src");
2659
2660 make_url_setter!(SetSrc, "src");
2662
2663 fn GetCrossOrigin(&self) -> Option<DOMString> {
2665 reflect_cross_origin_attribute(self.upcast::<Element>())
2666 }
2667 fn SetCrossOrigin(&self, value: Option<DOMString>, can_gc: CanGc) {
2669 set_cross_origin_attribute(self.upcast::<Element>(), value, can_gc);
2670 }
2671
2672 fn Muted(&self) -> bool {
2674 self.muted.get()
2675 }
2676
2677 fn SetMuted(&self, value: bool) {
2679 if self.muted.get() == value {
2680 return;
2681 }
2682
2683 if let Some(ref player) = *self.player.borrow() {
2684 let _ = player.lock().unwrap().set_mute(value);
2685 }
2686
2687 self.muted.set(value);
2688 self.owner_global()
2689 .task_manager()
2690 .media_element_task_source()
2691 .queue_simple_event(self.upcast(), atom!("volumechange"));
2692 if !self.is_allowed_to_play() {
2693 self.internal_pause_steps();
2694 }
2695 }
2696
2697 fn GetSrcObject(&self) -> Option<MediaStreamOrBlob> {
2699 (*self.src_object.borrow())
2700 .as_ref()
2701 .map(|src_object| match src_object {
2702 SrcObject::Blob(blob) => MediaStreamOrBlob::Blob(DomRoot::from_ref(blob)),
2703 SrcObject::MediaStream(stream) => {
2704 MediaStreamOrBlob::MediaStream(DomRoot::from_ref(stream))
2705 },
2706 })
2707 }
2708
2709 fn SetSrcObject(&self, value: Option<MediaStreamOrBlob>, can_gc: CanGc) {
2711 *self.src_object.borrow_mut() = value.map(|value| value.into());
2712 self.media_element_load_algorithm(can_gc);
2713 }
2714
2715 make_enumerated_getter!(
2718 Preload,
2719 "preload",
2720 "none" | "metadata" | "auto",
2721 missing => "auto",
2722 invalid => "auto"
2723 );
2724
2725 make_setter!(SetPreload, "preload");
2727
2728 fn CurrentSrc(&self) -> USVString {
2730 USVString(self.current_src.borrow().clone())
2731 }
2732
2733 fn Load(&self, can_gc: CanGc) {
2735 self.media_element_load_algorithm(can_gc);
2736 }
2737
2738 fn CanPlayType(&self, type_: DOMString) -> CanPlayTypeResult {
2740 match ServoMedia::get().can_play_type(&type_.str()) {
2741 SupportsMediaType::No => CanPlayTypeResult::_empty,
2742 SupportsMediaType::Maybe => CanPlayTypeResult::Maybe,
2743 SupportsMediaType::Probably => CanPlayTypeResult::Probably,
2744 }
2745 }
2746
2747 fn GetError(&self) -> Option<DomRoot<MediaError>> {
2749 self.error.get()
2750 }
2751
2752 fn Play(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
2754 let promise = Promise::new_in_current_realm(comp, can_gc);
2755 if self
2760 .error
2761 .get()
2762 .is_some_and(|e| e.Code() == MEDIA_ERR_SRC_NOT_SUPPORTED)
2763 {
2764 promise.reject_error(Error::NotSupported, can_gc);
2765 return promise;
2766 }
2767
2768 self.push_pending_play_promise(&promise);
2770
2771 if self.network_state.get() == NetworkState::Empty {
2773 self.invoke_resource_selection_algorithm(can_gc);
2774 }
2775
2776 if self.Ended() && self.direction_of_playback() == PlaybackDirection::Forwards {
2778 self.seek(
2779 self.earliest_possible_position(),
2780 false,
2781 can_gc,
2782 );
2783 }
2784
2785 let state = self.ready_state.get();
2786
2787 let global = self.owner_global();
2788 let task_manager = global.task_manager();
2789 let task_source = task_manager.media_element_task_source();
2790 if self.Paused() {
2791 self.paused.set(false);
2793
2794 if self.show_poster.get() {
2796 self.show_poster.set(false);
2797 self.time_marches_on();
2798 }
2799
2800 task_source.queue_simple_event(self.upcast(), atom!("play"));
2802
2803 match state {
2805 ReadyState::HaveNothing |
2806 ReadyState::HaveMetadata |
2807 ReadyState::HaveCurrentData => {
2808 task_source.queue_simple_event(self.upcast(), atom!("waiting"));
2809 },
2810 ReadyState::HaveFutureData | ReadyState::HaveEnoughData => {
2811 self.notify_about_playing();
2812 },
2813 }
2814 } else if state == ReadyState::HaveFutureData || state == ReadyState::HaveEnoughData {
2815 self.take_pending_play_promises(Ok(()));
2817 let this = Trusted::new(self);
2818 let generation_id = self.generation_id.get();
2819 task_source.queue(task!(resolve_pending_play_promises: move || {
2820 let this = this.root();
2821 if generation_id != this.generation_id.get() {
2822 return;
2823 }
2824
2825 this.fulfill_in_flight_play_promises(|| {
2826 this.play_media();
2827 });
2828 }));
2829 }
2830
2831 self.autoplaying.set(false);
2833
2834 promise
2836 }
2837
2838 fn Pause(&self, can_gc: CanGc) {
2840 if self.network_state.get() == NetworkState::Empty {
2842 self.invoke_resource_selection_algorithm(can_gc);
2843 }
2844
2845 self.internal_pause_steps();
2847 }
2848
2849 fn Paused(&self) -> bool {
2851 self.paused.get()
2852 }
2853
2854 fn GetDefaultPlaybackRate(&self) -> Fallible<Finite<f64>> {
2856 Ok(Finite::wrap(self.defaultPlaybackRate.get()))
2857 }
2858
2859 fn SetDefaultPlaybackRate(&self, value: Finite<f64>) -> ErrorResult {
2861 let min_allowed = -64.0;
2862 let max_allowed = 64.0;
2863 if *value < min_allowed || *value > max_allowed {
2864 return Err(Error::NotSupported);
2865 }
2866
2867 if *value != self.defaultPlaybackRate.get() {
2868 self.defaultPlaybackRate.set(*value);
2869 self.queue_media_element_task_to_fire_event(atom!("ratechange"));
2870 }
2871
2872 Ok(())
2873 }
2874
2875 fn GetPlaybackRate(&self) -> Fallible<Finite<f64>> {
2877 Ok(Finite::wrap(self.playbackRate.get()))
2878 }
2879
2880 fn SetPlaybackRate(&self, value: Finite<f64>) -> ErrorResult {
2882 let min_allowed = -64.0;
2883 let max_allowed = 64.0;
2884 if *value < min_allowed || *value > max_allowed {
2885 return Err(Error::NotSupported);
2886 }
2887
2888 if *value != self.playbackRate.get() {
2889 self.playbackRate.set(*value);
2890 self.queue_media_element_task_to_fire_event(atom!("ratechange"));
2891 if self.is_potentially_playing() {
2892 if let Some(ref player) = *self.player.borrow() {
2893 if let Err(e) = player.lock().unwrap().set_rate(*value) {
2894 warn!("Could not set the playback rate {:?}", e);
2895 }
2896 }
2897 }
2898 }
2899
2900 Ok(())
2901 }
2902
2903 fn Duration(&self) -> f64 {
2905 self.duration.get()
2906 }
2907
2908 fn CurrentTime(&self) -> Finite<f64> {
2910 Finite::wrap(if self.default_playback_start_position.get() != 0. {
2911 self.default_playback_start_position.get()
2912 } else {
2913 self.playback_position.get()
2914 })
2915 }
2916
2917 fn SetCurrentTime(&self, time: Finite<f64>, can_gc: CanGc) {
2919 if self.ready_state.get() == ReadyState::HaveNothing {
2920 self.default_playback_start_position.set(*time);
2921 } else {
2922 self.playback_position.set(*time);
2923 self.seek(*time, false, can_gc);
2924 }
2925 }
2926
2927 fn Seeking(&self) -> bool {
2929 self.seeking.get()
2930 }
2931
2932 fn Ended(&self) -> bool {
2934 if self.ready_state.get() < ReadyState::HaveMetadata {
2935 return false;
2936 }
2937
2938 let playback_pos = self.playback_position.get();
2939
2940 match self.direction_of_playback() {
2941 PlaybackDirection::Forwards => playback_pos >= self.Duration() && !self.Loop(),
2942 PlaybackDirection::Backwards => playback_pos <= self.earliest_possible_position(),
2943 }
2944 }
2945
2946 fn FastSeek(&self, time: Finite<f64>, can_gc: CanGc) {
2948 self.seek(*time, true, can_gc);
2949 }
2950
2951 fn Played(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
2953 TimeRanges::new(
2954 self.global().as_window(),
2955 self.played.borrow().clone(),
2956 can_gc,
2957 )
2958 }
2959
2960 fn Seekable(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
2962 let mut seekable = TimeRangesContainer::default();
2963 if let Some(ref player) = *self.player.borrow() {
2964 if let Ok(ranges) = player.lock().unwrap().seekable() {
2965 for range in ranges {
2966 let _ = seekable.add(range.start, range.end);
2967 }
2968 }
2969 }
2970 TimeRanges::new(self.global().as_window(), seekable, can_gc)
2971 }
2972
2973 fn Buffered(&self, can_gc: CanGc) -> DomRoot<TimeRanges> {
2975 let mut buffered = TimeRangesContainer::default();
2976 if let Some(ref player) = *self.player.borrow() {
2977 if let Ok(ranges) = player.lock().unwrap().buffered() {
2978 for range in ranges {
2979 let _ = buffered.add(range.start, range.end);
2980 }
2981 }
2982 }
2983 TimeRanges::new(self.global().as_window(), buffered, can_gc)
2984 }
2985
2986 fn AudioTracks(&self, can_gc: CanGc) -> DomRoot<AudioTrackList> {
2988 let window = self.owner_window();
2989 self.audio_tracks_list
2990 .or_init(|| AudioTrackList::new(&window, &[], Some(self), can_gc))
2991 }
2992
2993 fn VideoTracks(&self, can_gc: CanGc) -> DomRoot<VideoTrackList> {
2995 let window = self.owner_window();
2996 self.video_tracks_list
2997 .or_init(|| VideoTrackList::new(&window, &[], Some(self), can_gc))
2998 }
2999
3000 fn TextTracks(&self, can_gc: CanGc) -> DomRoot<TextTrackList> {
3002 let window = self.owner_window();
3003 self.text_tracks_list
3004 .or_init(|| TextTrackList::new(&window, &[], can_gc))
3005 }
3006
3007 fn AddTextTrack(
3009 &self,
3010 kind: TextTrackKind,
3011 label: DOMString,
3012 language: DOMString,
3013 can_gc: CanGc,
3014 ) -> DomRoot<TextTrack> {
3015 let window = self.owner_window();
3016 let track = TextTrack::new(
3019 &window,
3020 "".into(),
3021 kind,
3022 label,
3023 language,
3024 TextTrackMode::Hidden,
3025 None,
3026 can_gc,
3027 );
3028 self.TextTracks(can_gc).add(&track);
3030 DomRoot::from_ref(&track)
3032 }
3033
3034 fn GetVolume(&self) -> Fallible<Finite<f64>> {
3036 Ok(Finite::wrap(self.volume.get()))
3037 }
3038
3039 fn SetVolume(&self, value: Finite<f64>) -> ErrorResult {
3041 let minimum_volume = 0.0;
3042 let maximum_volume = 1.0;
3043 if *value < minimum_volume || *value > maximum_volume {
3044 return Err(Error::IndexSize(None));
3045 }
3046
3047 if *value != self.volume.get() {
3048 self.volume.set(*value);
3049 if let Some(player) = self.player.borrow().as_ref() {
3050 let _ = player.lock().unwrap().set_volume(*value);
3051 }
3052 self.owner_global()
3053 .task_manager()
3054 .media_element_task_source()
3055 .queue_simple_event(self.upcast(), atom!("volumechange"));
3056 if !self.is_allowed_to_play() {
3057 self.internal_pause_steps();
3058 }
3059 }
3060
3061 Ok(())
3062 }
3063}
3064
3065impl VirtualMethods for HTMLMediaElement {
3066 fn super_type(&self) -> Option<&dyn VirtualMethods> {
3067 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
3068 }
3069
3070 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
3071 self.super_type()
3072 .unwrap()
3073 .attribute_mutated(attr, mutation, can_gc);
3074
3075 match *attr.local_name() {
3076 local_name!("muted") => {
3077 self.SetMuted(mutation.new_value(attr).is_some());
3078 },
3079 local_name!("src") => {
3080 if !mutation.is_removal() {
3085 self.media_element_load_algorithm(can_gc);
3086 }
3087 },
3088 local_name!("controls") => {
3089 if mutation.new_value(attr).is_some() {
3090 self.render_controls(can_gc);
3091 } else {
3092 self.remove_controls();
3093 }
3094 },
3095 _ => (),
3096 };
3097 }
3098
3099 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
3101 self.super_type().unwrap().unbind_from_tree(context, can_gc);
3102
3103 self.remove_controls();
3104
3105 if context.tree_connected {
3106 let task = MediaElementMicrotask::PauseIfNotInDocument {
3107 elem: DomRoot::from_ref(self),
3108 };
3109 ScriptThread::await_stable_state(Microtask::MediaElement(task));
3110 }
3111 }
3112
3113 fn adopting_steps(&self, old_doc: &Document, can_gc: CanGc) {
3114 self.super_type().unwrap().adopting_steps(old_doc, can_gc);
3115
3116 if let Some(id) = &*self.media_controls_id.borrow() {
3120 let Some(shadow_root) = self.upcast::<Element>().shadow_root() else {
3121 error!("Missing media controls shadow root");
3122 return;
3123 };
3124
3125 old_doc.unregister_media_controls(id);
3126 self.owner_document()
3127 .register_media_controls(id, &shadow_root);
3128 }
3129 }
3130}
3131
3132#[derive(JSTraceable, MallocSizeOf)]
3133pub(crate) enum MediaElementMicrotask {
3134 ResourceSelection {
3135 elem: DomRoot<HTMLMediaElement>,
3136 generation_id: u32,
3137 #[no_trace]
3138 base_url: ServoUrl,
3139 },
3140 PauseIfNotInDocument {
3141 elem: DomRoot<HTMLMediaElement>,
3142 },
3143 Seeked {
3144 elem: DomRoot<HTMLMediaElement>,
3145 generation_id: u32,
3146 },
3147 SelectNextSourceChild {
3148 elem: DomRoot<HTMLMediaElement>,
3149 generation_id: u32,
3150 },
3151 SelectNextSourceChildAfterWait {
3152 elem: DomRoot<HTMLMediaElement>,
3153 generation_id: u32,
3154 },
3155}
3156
3157impl MicrotaskRunnable for MediaElementMicrotask {
3158 fn handler(&self, can_gc: CanGc) {
3159 match self {
3160 &MediaElementMicrotask::ResourceSelection {
3161 ref elem,
3162 generation_id,
3163 ref base_url,
3164 } => {
3165 if generation_id == elem.generation_id.get() {
3166 elem.resource_selection_algorithm_sync(base_url.clone(), can_gc);
3167 }
3168 },
3169 MediaElementMicrotask::PauseIfNotInDocument { elem } => {
3170 if !elem.upcast::<Node>().is_connected() {
3171 elem.internal_pause_steps();
3172 }
3173 },
3174 &MediaElementMicrotask::Seeked {
3175 ref elem,
3176 generation_id,
3177 } => {
3178 if generation_id == elem.generation_id.get() {
3179 elem.seek_end();
3180 }
3181 },
3182 &MediaElementMicrotask::SelectNextSourceChild {
3183 ref elem,
3184 generation_id,
3185 } => {
3186 if generation_id == elem.generation_id.get() {
3187 elem.select_next_source_child(can_gc);
3188 }
3189 },
3190 &MediaElementMicrotask::SelectNextSourceChildAfterWait {
3191 ref elem,
3192 generation_id,
3193 } => {
3194 if generation_id == elem.generation_id.get() {
3195 elem.select_next_source_child_after_wait(can_gc);
3196 }
3197 },
3198 }
3199 }
3200
3201 fn enter_realm(&self) -> JSAutoRealm {
3202 match self {
3203 &MediaElementMicrotask::ResourceSelection { ref elem, .. } |
3204 &MediaElementMicrotask::PauseIfNotInDocument { ref elem } |
3205 &MediaElementMicrotask::Seeked { ref elem, .. } |
3206 &MediaElementMicrotask::SelectNextSourceChild { ref elem, .. } |
3207 &MediaElementMicrotask::SelectNextSourceChildAfterWait { ref elem, .. } => {
3208 enter_realm(&**elem)
3209 },
3210 }
3211 }
3212}
3213
3214enum Resource {
3215 Object,
3216 Url(ServoUrl),
3217}
3218
3219#[derive(Debug, MallocSizeOf, PartialEq)]
3220enum DataBuffer {
3221 Payload(Vec<u8>),
3222 EndOfStream,
3223}
3224
3225#[derive(MallocSizeOf)]
3226struct BufferedDataSource {
3227 locked: Cell<bool>,
3232 buffers: VecDeque<DataBuffer>,
3234}
3235
3236impl BufferedDataSource {
3237 fn new() -> BufferedDataSource {
3238 BufferedDataSource {
3239 locked: Cell::new(true),
3240 buffers: VecDeque::default(),
3241 }
3242 }
3243
3244 fn set_locked(&self, locked: bool) {
3245 self.locked.set(locked)
3246 }
3247
3248 fn add_buffer_to_queue(&mut self, buffer: DataBuffer) {
3249 debug_assert_ne!(
3250 self.buffers.back(),
3251 Some(&DataBuffer::EndOfStream),
3252 "The media backend not expects any further data after end of stream"
3253 );
3254
3255 self.buffers.push_back(buffer);
3256 }
3257
3258 fn process_into_player_from_queue(
3259 &mut self,
3260 player: &Arc<Mutex<dyn Player>>,
3261 ) -> Result<(), PlayerError> {
3262 if self.locked.get() {
3264 return Ok(());
3265 }
3266
3267 while let Some(buffer) = self.buffers.pop_front() {
3268 match buffer {
3269 DataBuffer::Payload(payload) => {
3270 if let Err(e) = player.lock().unwrap().push_data(payload) {
3271 warn!("Could not push input data to player {:?}", e);
3272 return Err(e);
3273 }
3274 },
3275 DataBuffer::EndOfStream => {
3276 if let Err(e) = player.lock().unwrap().end_of_stream() {
3277 warn!("Could not signal EOS to player {:?}", e);
3278 return Err(e);
3279 }
3280 },
3281 }
3282 }
3283
3284 Ok(())
3285 }
3286
3287 fn reset(&mut self) {
3288 self.locked.set(true);
3289 self.buffers.clear();
3290 }
3291}
3292
3293#[derive(Debug, MallocSizeOf, PartialEq)]
3295enum CancelReason {
3296 Backoff,
3298 Error,
3300 Abort,
3302}
3303
3304#[derive(MallocSizeOf)]
3305pub(crate) struct HTMLMediaElementFetchContext {
3306 request_id: RequestId,
3308 cancel_reason: Option<CancelReason>,
3310 is_seekable: bool,
3312 origin_clean: bool,
3314 data_source: RefCell<BufferedDataSource>,
3316 fetch_canceller: FetchCanceller,
3319}
3320
3321impl HTMLMediaElementFetchContext {
3322 fn new(
3323 request_id: RequestId,
3324 core_resource_thread: CoreResourceThread,
3325 ) -> HTMLMediaElementFetchContext {
3326 HTMLMediaElementFetchContext {
3327 request_id,
3328 cancel_reason: None,
3329 is_seekable: false,
3330 origin_clean: true,
3331 data_source: RefCell::new(BufferedDataSource::new()),
3332 fetch_canceller: FetchCanceller::new(request_id, core_resource_thread.clone()),
3333 }
3334 }
3335
3336 fn request_id(&self) -> RequestId {
3337 self.request_id
3338 }
3339
3340 fn is_seekable(&self) -> bool {
3341 self.is_seekable
3342 }
3343
3344 fn set_seekable(&mut self, seekable: bool) {
3345 self.is_seekable = seekable;
3346 }
3347
3348 fn origin_is_clean(&self) -> bool {
3349 self.origin_clean
3350 }
3351
3352 fn set_origin_clean(&mut self, origin_clean: bool) {
3353 self.origin_clean = origin_clean;
3354 }
3355
3356 fn data_source(&self) -> &RefCell<BufferedDataSource> {
3357 &self.data_source
3358 }
3359
3360 fn cancel(&mut self, reason: CancelReason) {
3361 if self.cancel_reason.is_some() {
3362 return;
3363 }
3364 self.cancel_reason = Some(reason);
3365 self.data_source.borrow_mut().reset();
3366 self.fetch_canceller.cancel();
3367 }
3368
3369 fn cancel_reason(&self) -> &Option<CancelReason> {
3370 &self.cancel_reason
3371 }
3372}
3373
3374struct HTMLMediaElementFetchListener {
3375 element: Trusted<HTMLMediaElement>,
3377 generation_id: u32,
3379 request_id: RequestId,
3381 next_progress_event: Instant,
3383 resource_timing: ResourceFetchTiming,
3385 url: ServoUrl,
3387 expected_content_length: Option<u64>,
3389 fetched_content_length: u64,
3391 content_length_to_discard: u64,
3395}
3396
3397impl FetchResponseListener for HTMLMediaElementFetchListener {
3398 fn process_request_body(&mut self, _: RequestId) {}
3399
3400 fn process_request_eof(&mut self, _: RequestId) {}
3401
3402 fn process_response(&mut self, _: RequestId, metadata: Result<FetchMetadata, NetworkError>) {
3403 let element = self.element.root();
3404
3405 let (metadata, origin_clean) = match metadata {
3406 Ok(fetch_metadata) => match fetch_metadata {
3407 FetchMetadata::Unfiltered(metadata) => (Some(metadata), true),
3408 FetchMetadata::Filtered { filtered, unsafe_ } => (
3409 Some(unsafe_),
3410 matches!(
3411 filtered,
3412 FilteredMetadata::Basic(_) | FilteredMetadata::Cors(_)
3413 ),
3414 ),
3415 },
3416 Err(_) => (None, true),
3417 };
3418
3419 let (status_is_success, is_seekable) =
3420 metadata.as_ref().map_or((false, false), |metadata| {
3421 let status = &metadata.status;
3422 (status.is_success(), *status == StatusCode::PARTIAL_CONTENT)
3423 });
3424
3425 if !status_is_success {
3427 if element.ready_state.get() == ReadyState::HaveNothing {
3428 element.media_data_processing_failure_steps();
3430 } else {
3431 element.media_data_processing_fatal_steps(MEDIA_ERR_NETWORK, CanGc::note());
3433 }
3434 return;
3435 }
3436
3437 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut() {
3438 current_fetch_context.set_seekable(is_seekable);
3439 current_fetch_context.set_origin_clean(origin_clean);
3440 }
3441
3442 if let Some(metadata) = metadata.as_ref() {
3443 if let Some(headers) = metadata.headers.as_ref() {
3444 let content_length =
3447 if let Some(content_range) = headers.typed_get::<ContentRange>() {
3448 content_range.bytes_len()
3449 } else {
3450 headers
3451 .typed_get::<ContentLength>()
3452 .map(|content_length| content_length.0)
3453 };
3454
3455 if content_length != self.expected_content_length {
3457 if let Some(content_length) = content_length {
3458 self.expected_content_length = Some(content_length);
3459 }
3460 }
3461 }
3462 }
3463
3464 if let Some(expected_content_length) = self.expected_content_length {
3466 if let Err(e) = element
3467 .player
3468 .borrow()
3469 .as_ref()
3470 .unwrap()
3471 .lock()
3472 .unwrap()
3473 .set_input_size(expected_content_length)
3474 {
3475 warn!("Could not set player input size {:?}", e);
3476 }
3477 }
3478 }
3479
3480 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
3481 let element = self.element.root();
3482
3483 self.fetched_content_length += chunk.len() as u64;
3484
3485 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut() {
3487 if let Some(CancelReason::Backoff) = current_fetch_context.cancel_reason() {
3488 return;
3489 }
3490
3491 let payload = if !current_fetch_context.is_seekable() &&
3493 self.content_length_to_discard != 0
3494 {
3495 if chunk.len() as u64 > self.content_length_to_discard {
3496 let shrink_chunk = chunk[self.content_length_to_discard as usize..].to_vec();
3497 self.content_length_to_discard = 0;
3498 shrink_chunk
3499 } else {
3500 self.content_length_to_discard -= chunk.len() as u64;
3502 return;
3503 }
3504 } else {
3505 chunk
3506 };
3507
3508 if let Err(e) = {
3509 let mut data_source = current_fetch_context.data_source().borrow_mut();
3510 data_source.add_buffer_to_queue(DataBuffer::Payload(payload));
3511 data_source
3512 .process_into_player_from_queue(element.player.borrow().as_ref().unwrap())
3513 } {
3514 if e == PlayerError::EnoughData {
3519 current_fetch_context.cancel(CancelReason::Backoff);
3520 }
3521 return;
3522 }
3523 }
3524
3525 if Instant::now() > self.next_progress_event {
3528 element
3529 .owner_global()
3530 .task_manager()
3531 .media_element_task_source()
3532 .queue_simple_event(element.upcast(), atom!("progress"));
3533 self.next_progress_event = Instant::now() + Duration::from_millis(350);
3534 }
3535 }
3536
3537 fn process_response_eof(
3538 &mut self,
3539 _: RequestId,
3540 status: Result<ResourceFetchTiming, NetworkError>,
3541 ) {
3542 let element = self.element.root();
3543
3544 if status.is_ok() && self.fetched_content_length != 0 {
3546 if let Some(ref mut current_fetch_context) = *element.current_fetch_context.borrow_mut()
3551 {
3552 if self.expected_content_length.is_none() {
3559 if let Err(e) = element
3560 .player
3561 .borrow()
3562 .as_ref()
3563 .unwrap()
3564 .lock()
3565 .unwrap()
3566 .set_input_size(self.fetched_content_length)
3567 {
3568 warn!("Could not set player input size {:?}", e);
3569 }
3570 }
3571
3572 let mut data_source = current_fetch_context.data_source().borrow_mut();
3573
3574 data_source.add_buffer_to_queue(DataBuffer::EndOfStream);
3575 let _ = data_source
3576 .process_into_player_from_queue(element.player.borrow().as_ref().unwrap());
3577 }
3578
3579 element
3581 .upcast::<EventTarget>()
3582 .fire_event(atom!("progress"), CanGc::note());
3583
3584 element.network_state.set(NetworkState::Idle);
3587
3588 element
3589 .upcast::<EventTarget>()
3590 .fire_event(atom!("suspend"), CanGc::note());
3591 } else if status.is_err() && element.ready_state.get() != ReadyState::HaveNothing {
3592 element.media_data_processing_fatal_steps(MEDIA_ERR_NETWORK, CanGc::note());
3594 } else {
3595 element.media_data_processing_failure_steps();
3598 }
3599 }
3600
3601 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
3602 &mut self.resource_timing
3603 }
3604
3605 fn resource_timing(&self) -> &ResourceFetchTiming {
3606 &self.resource_timing
3607 }
3608
3609 fn submit_resource_timing(&mut self) {
3610 network_listener::submit_timing(self, CanGc::note())
3611 }
3612
3613 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
3614 let global = &self.resource_timing_global();
3615 global.report_csp_violations(violations, None, None);
3616 }
3617
3618 fn should_invoke(&self) -> bool {
3619 let element = self.element.root();
3620
3621 if element.generation_id.get() != self.generation_id || element.player.borrow().is_none() {
3622 return false;
3623 }
3624
3625 let Some(ref current_fetch_context) = *element.current_fetch_context.borrow() else {
3626 return false;
3627 };
3628
3629 if current_fetch_context.request_id() != self.request_id {
3631 return false;
3632 }
3633
3634 if let Some(cancel_reason) = current_fetch_context.cancel_reason() {
3637 if matches!(*cancel_reason, CancelReason::Error | CancelReason::Abort) {
3638 return false;
3639 }
3640 }
3641
3642 true
3643 }
3644}
3645
3646impl ResourceTimingListener for HTMLMediaElementFetchListener {
3647 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
3648 let initiator_type = InitiatorType::LocalName(
3649 self.element
3650 .root()
3651 .upcast::<Element>()
3652 .local_name()
3653 .to_string(),
3654 );
3655 (initiator_type, self.url.clone())
3656 }
3657
3658 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
3659 self.element.root().owner_document().global()
3660 }
3661}
3662
3663impl HTMLMediaElementFetchListener {
3664 fn new(element: &HTMLMediaElement, request_id: RequestId, url: ServoUrl, offset: u64) -> Self {
3665 Self {
3666 element: Trusted::new(element),
3667 generation_id: element.generation_id.get(),
3668 request_id,
3669 next_progress_event: Instant::now() + Duration::from_millis(350),
3670 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
3671 url,
3672 expected_content_length: None,
3673 fetched_content_length: 0,
3674 content_length_to_discard: offset,
3675 }
3676 }
3677}