1use std::cell::Cell;
6use std::collections::VecDeque;
7use std::rc::Rc;
8use std::sync::{Arc, Mutex};
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, channel};
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, FetchResponseListener, FilteredMetadata, Metadata,
29 NetworkError, ResourceFetchTiming, 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 webrender_api::{
43 ExternalImageData, ExternalImageId, ExternalImageType, ImageBufferKind, ImageDescriptor,
44 ImageDescriptorFlags, ImageFormat, ImageKey,
45};
46
47use crate::document_loader::{LoadBlocker, LoadType};
48use crate::dom::attr::Attr;
49use crate::dom::audio::audiotrack::AudioTrack;
50use crate::dom::audio::audiotracklist::AudioTrackList;
51use crate::dom::bindings::cell::DomRefCell;
52use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
53use crate::dom::bindings::codegen::Bindings::HTMLMediaElementBinding::{
54 CanPlayTypeResult, HTMLMediaElementConstants, HTMLMediaElementMethods,
55};
56use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConstants::*;
57use crate::dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods;
58use crate::dom::bindings::codegen::Bindings::NavigatorBinding::Navigator_Binding::NavigatorMethods;
59use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
60use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode};
61use crate::dom::bindings::codegen::Bindings::URLBinding::URLMethods;
62use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
63use crate::dom::bindings::codegen::UnionTypes::{
64 MediaStreamOrBlob, VideoTrackOrAudioTrackOrTextTrack,
65};
66use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
67use crate::dom::bindings::inheritance::Castable;
68use crate::dom::bindings::num::Finite;
69use crate::dom::bindings::refcounted::Trusted;
70use crate::dom::bindings::reflector::DomGlobal;
71use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
72use crate::dom::bindings::str::{DOMString, USVString};
73use crate::dom::blob::Blob;
74use crate::dom::csp::{GlobalCspReporting, Violation};
75use crate::dom::document::Document;
76use crate::dom::element::{
77 AttributeMutation, CustomElementCreationMode, Element, ElementCreator,
78 cors_setting_for_element, reflect_cross_origin_attribute, set_cross_origin_attribute,
79};
80use crate::dom::event::Event;
81use crate::dom::eventtarget::EventTarget;
82use crate::dom::globalscope::GlobalScope;
83use crate::dom::html::htmlelement::HTMLElement;
84use crate::dom::html::htmlsourceelement::HTMLSourceElement;
85use crate::dom::html::htmlvideoelement::HTMLVideoElement;
86use crate::dom::mediaerror::MediaError;
87use crate::dom::mediafragmentparser::MediaFragmentParser;
88use crate::dom::mediastream::MediaStream;
89use crate::dom::node::{Node, NodeDamage, NodeTraits, UnbindContext};
90use crate::dom::performanceresourcetiming::InitiatorType;
91use crate::dom::promise::Promise;
92use crate::dom::texttrack::TextTrack;
93use crate::dom::texttracklist::TextTrackList;
94use crate::dom::timeranges::{TimeRanges, TimeRangesContainer};
95use crate::dom::trackevent::TrackEvent;
96use crate::dom::url::URL;
97use crate::dom::videotrack::VideoTrack;
98use crate::dom::videotracklist::VideoTrackList;
99use crate::dom::virtualmethods::VirtualMethods;
100use crate::fetch::{FetchCanceller, create_a_potential_cors_request};
101use crate::microtask::{Microtask, MicrotaskRunnable};
102use crate::network_listener::{self, PreInvoke, ResourceTimingListener};
103use crate::realms::{InRealm, enter_realm};
104use crate::script_runtime::CanGc;
105use crate::script_thread::ScriptThread;
106
107static MEDIA_CONTROL_CSS: &str = include_str!("../../resources/media-controls.css");
109
110static MEDIA_CONTROL_JS: &str = include_str!("../../resources/media-controls.js");
112
113#[derive(MallocSizeOf, PartialEq)]
114enum FrameStatus {
115 Locked,
116 Unlocked,
117}
118
119#[derive(MallocSizeOf)]
120struct FrameHolder(
121 FrameStatus,
122 #[ignore_malloc_size_of = "defined in servo-media"] VideoFrame,
123);
124
125impl FrameHolder {
126 fn new(frame: VideoFrame) -> FrameHolder {
127 FrameHolder(FrameStatus::Unlocked, frame)
128 }
129
130 fn lock(&mut self) {
131 if self.0 == FrameStatus::Unlocked {
132 self.0 = FrameStatus::Locked;
133 };
134 }
135
136 fn unlock(&mut self) {
137 if self.0 == FrameStatus::Locked {
138 self.0 = FrameStatus::Unlocked;
139 };
140 }
141
142 fn set(&mut self, new_frame: VideoFrame) {
143 if self.0 == FrameStatus::Unlocked {
144 self.1 = new_frame
145 };
146 }
147
148 fn get(&self) -> (u32, Size2D<i32>, usize) {
149 if self.0 == FrameStatus::Locked {
150 (
151 self.1.get_texture_id(),
152 Size2D::new(self.1.get_width(), self.1.get_height()),
153 0,
154 )
155 } else {
156 unreachable!();
157 }
158 }
159
160 fn get_frame(&self) -> VideoFrame {
161 self.1.clone()
162 }
163}
164
165#[derive(MallocSizeOf)]
166pub(crate) struct MediaFrameRenderer {
167 player_id: Option<u64>,
168 compositor_api: CrossProcessCompositorApi,
169 current_frame: Option<MediaFrame>,
170 old_frame: Option<ImageKey>,
171 very_old_frame: Option<ImageKey>,
172 current_frame_holder: Option<FrameHolder>,
173 poster_frame: Option<MediaFrame>,
175}
176
177impl MediaFrameRenderer {
178 fn new(compositor_api: CrossProcessCompositorApi) -> Self {
179 Self {
180 player_id: None,
181 compositor_api,
182 current_frame: None,
183 old_frame: None,
184 very_old_frame: None,
185 current_frame_holder: None,
186 poster_frame: None,
187 }
188 }
189
190 fn set_poster_frame(&mut self, image: Option<Arc<RasterImage>>) {
191 self.poster_frame = image.and_then(|image| {
192 image.id.map(|image_key| MediaFrame {
193 image_key,
194 width: image.metadata.width as i32,
195 height: image.metadata.height as i32,
196 })
197 });
198 }
199}
200
201impl VideoFrameRenderer for MediaFrameRenderer {
202 fn render(&mut self, frame: VideoFrame) {
203 let mut updates = smallvec::smallvec![];
204
205 if let Some(old_image_key) = mem::replace(&mut self.very_old_frame, self.old_frame.take()) {
206 updates.push(ImageUpdate::DeleteImage(old_image_key));
207 }
208
209 let descriptor = ImageDescriptor::new(
210 frame.get_width(),
211 frame.get_height(),
212 ImageFormat::BGRA8,
213 ImageDescriptorFlags::empty(),
214 );
215
216 match &mut self.current_frame {
217 Some(current_frame)
218 if current_frame.width == frame.get_width() &&
219 current_frame.height == frame.get_height() =>
220 {
221 if !frame.is_gl_texture() {
222 updates.push(ImageUpdate::UpdateImage(
223 current_frame.image_key,
224 descriptor,
225 SerializableImageData::Raw(IpcSharedMemory::from_bytes(&frame.get_data())),
226 None,
227 ));
228 }
229
230 self.current_frame_holder
231 .get_or_insert_with(|| FrameHolder::new(frame.clone()))
232 .set(frame);
233
234 if let Some(old_image_key) = self.old_frame.take() {
235 updates.push(ImageUpdate::DeleteImage(old_image_key));
236 }
237 },
238 Some(current_frame) => {
239 self.old_frame = Some(current_frame.image_key);
240
241 let Some(new_image_key) = self.compositor_api.generate_image_key_blocking() else {
242 return;
243 };
244
245 current_frame.image_key = new_image_key;
247 current_frame.width = frame.get_width();
248 current_frame.height = frame.get_height();
249
250 let image_data = if frame.is_gl_texture() && self.player_id.is_some() {
251 let texture_target = if frame.is_external_oes() {
252 ImageBufferKind::TextureExternal
253 } else {
254 ImageBufferKind::Texture2D
255 };
256
257 SerializableImageData::External(ExternalImageData {
258 id: ExternalImageId(self.player_id.unwrap()),
259 channel_index: 0,
260 image_type: ExternalImageType::TextureHandle(texture_target),
261 normalized_uvs: false,
262 })
263 } else {
264 SerializableImageData::Raw(IpcSharedMemory::from_bytes(&frame.get_data()))
265 };
266
267 self.current_frame_holder
268 .get_or_insert_with(|| FrameHolder::new(frame.clone()))
269 .set(frame);
270
271 updates.push(ImageUpdate::AddImage(new_image_key, descriptor, image_data));
272 },
273 None => {
274 let Some(image_key) = self.compositor_api.generate_image_key_blocking() else {
275 return;
276 };
277
278 self.current_frame = Some(MediaFrame {
279 image_key,
280 width: frame.get_width(),
281 height: frame.get_height(),
282 });
283
284 let image_data = if frame.is_gl_texture() && self.player_id.is_some() {
285 let texture_target = if frame.is_external_oes() {
286 ImageBufferKind::TextureExternal
287 } else {
288 ImageBufferKind::Texture2D
289 };
290
291 SerializableImageData::External(ExternalImageData {
292 id: ExternalImageId(self.player_id.unwrap()),
293 channel_index: 0,
294 image_type: ExternalImageType::TextureHandle(texture_target),
295 normalized_uvs: false,
296 })
297 } else {
298 SerializableImageData::Raw(IpcSharedMemory::from_bytes(&frame.get_data()))
299 };
300
301 self.current_frame_holder = Some(FrameHolder::new(frame));
302
303 updates.push(ImageUpdate::AddImage(image_key, descriptor, image_data));
304 },
305 }
306 self.compositor_api.update_images(updates);
307 }
308}
309
310#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
311#[derive(JSTraceable, MallocSizeOf)]
312enum SrcObject {
313 MediaStream(Dom<MediaStream>),
314 Blob(Dom<Blob>),
315}
316
317impl From<MediaStreamOrBlob> for SrcObject {
318 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
319 fn from(src_object: MediaStreamOrBlob) -> SrcObject {
320 match src_object {
321 MediaStreamOrBlob::Blob(blob) => SrcObject::Blob(Dom::from_ref(&*blob)),
322 MediaStreamOrBlob::MediaStream(stream) => {
323 SrcObject::MediaStream(Dom::from_ref(&*stream))
324 },
325 }
326 }
327}
328
329#[derive(JSTraceable, MallocSizeOf)]
330struct DroppableHtmlMediaElement {
331 player_id: Cell<u64>,
333 #[ignore_malloc_size_of = "Defined in other crates"]
334 #[no_trace]
335 player_context: WindowGLContext,
336}
337
338impl DroppableHtmlMediaElement {
339 fn new(player_id: Cell<u64>, player_context: WindowGLContext) -> Self {
340 Self {
341 player_id,
342 player_context,
343 }
344 }
345
346 pub(crate) fn set_player_id(&self, id: u64) {
347 self.player_id.set(id);
348 }
349}
350
351impl Drop for DroppableHtmlMediaElement {
352 fn drop(&mut self) {
353 self.player_context
354 .send(GLPlayerMsg::UnregisterPlayer(self.player_id.get()));
355 }
356}
357#[dom_struct]
358#[allow(non_snake_case)]
359pub(crate) struct HTMLMediaElement {
360 htmlelement: HTMLElement,
361 network_state: Cell<NetworkState>,
363 ready_state: Cell<ReadyState>,
365 src_object: DomRefCell<Option<SrcObject>>,
367 current_src: DomRefCell<String>,
369 generation_id: Cell<u32>,
371 fired_loadeddata_event: Cell<bool>,
375 error: MutNullableDom<MediaError>,
377 paused: Cell<bool>,
379 defaultPlaybackRate: Cell<f64>,
381 playbackRate: Cell<f64>,
383 autoplaying: Cell<bool>,
385 delaying_the_load_event_flag: DomRefCell<Option<LoadBlocker>>,
387 #[ignore_malloc_size_of = "promises are hard"]
389 pending_play_promises: DomRefCell<Vec<Rc<Promise>>>,
390 #[allow(clippy::type_complexity)]
392 #[ignore_malloc_size_of = "promises are hard"]
393 in_flight_play_promises_queue: DomRefCell<VecDeque<(Box<[Rc<Promise>]>, ErrorResult)>>,
394 #[ignore_malloc_size_of = "servo_media"]
395 #[no_trace]
396 player: DomRefCell<Option<Arc<Mutex<dyn Player>>>>,
397 #[conditional_malloc_size_of]
398 #[no_trace]
399 video_renderer: Arc<Mutex<MediaFrameRenderer>>,
400 #[ignore_malloc_size_of = "Arc"]
401 #[no_trace]
402 audio_renderer: DomRefCell<Option<Arc<Mutex<dyn AudioRenderer>>>>,
403 show_poster: Cell<bool>,
405 duration: Cell<f64>,
407 playback_position: Cell<f64>,
409 default_playback_start_position: Cell<f64>,
411 volume: Cell<f64>,
413 seeking: Cell<bool>,
415 muted: Cell<bool>,
417 #[no_trace]
419 resource_url: DomRefCell<Option<ServoUrl>>,
420 #[no_trace]
423 blob_url: DomRefCell<Option<ServoUrl>>,
424 played: DomRefCell<TimeRangesContainer>,
426 audio_tracks_list: MutNullableDom<AudioTrackList>,
428 video_tracks_list: MutNullableDom<VideoTrackList>,
430 text_tracks_list: MutNullableDom<TextTrackList>,
432 #[ignore_malloc_size_of = "Defined in std::time"]
434 next_timeupdate_event: Cell<Instant>,
435 current_fetch_context: DomRefCell<Option<HTMLMediaElementFetchContext>>,
437 media_controls_id: DomRefCell<Option<String>>,
442 droppable: DroppableHtmlMediaElement,
443}
444
445#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
447#[repr(u8)]
448pub(crate) enum NetworkState {
449 Empty = HTMLMediaElementConstants::NETWORK_EMPTY as u8,
450 Idle = HTMLMediaElementConstants::NETWORK_IDLE as u8,
451 Loading = HTMLMediaElementConstants::NETWORK_LOADING as u8,
452 NoSource = HTMLMediaElementConstants::NETWORK_NO_SOURCE as u8,
453}
454
455#[derive(Clone, Copy, Debug, JSTraceable, MallocSizeOf, PartialEq, PartialOrd)]
457#[repr(u8)]
458#[allow(clippy::enum_variant_names)] pub(crate) enum ReadyState {
460 HaveNothing = HTMLMediaElementConstants::HAVE_NOTHING as u8,
461 HaveMetadata = HTMLMediaElementConstants::HAVE_METADATA as u8,
462 HaveCurrentData = HTMLMediaElementConstants::HAVE_CURRENT_DATA as u8,
463 HaveFutureData = HTMLMediaElementConstants::HAVE_FUTURE_DATA as u8,
464 HaveEnoughData = HTMLMediaElementConstants::HAVE_ENOUGH_DATA as u8,
465}
466
467impl HTMLMediaElement {
468 pub(crate) fn new_inherited(
469 tag_name: LocalName,
470 prefix: Option<Prefix>,
471 document: &Document,
472 ) -> Self {
473 Self {
474 htmlelement: HTMLElement::new_inherited(tag_name, prefix, document),
475 network_state: Cell::new(NetworkState::Empty),
476 ready_state: Cell::new(ReadyState::HaveNothing),
477 src_object: Default::default(),
478 current_src: DomRefCell::new("".to_owned()),
479 generation_id: Cell::new(0),
480 fired_loadeddata_event: Cell::new(false),
481 error: Default::default(),
482 paused: Cell::new(true),
483 defaultPlaybackRate: Cell::new(1.0),
484 playbackRate: Cell::new(1.0),
485 muted: Cell::new(false),
486 autoplaying: Cell::new(true),
488 delaying_the_load_event_flag: Default::default(),
489 pending_play_promises: Default::default(),
490 in_flight_play_promises_queue: Default::default(),
491 player: Default::default(),
492 video_renderer: Arc::new(Mutex::new(MediaFrameRenderer::new(
493 document.window().compositor_api().clone(),
494 ))),
495 audio_renderer: Default::default(),
496 show_poster: Cell::new(true),
497 duration: Cell::new(f64::NAN),
498 playback_position: Cell::new(0.),
499 default_playback_start_position: Cell::new(0.),
500 volume: Cell::new(1.0),
501 seeking: Cell::new(false),
502 resource_url: DomRefCell::new(None),
503 blob_url: DomRefCell::new(None),
504 played: DomRefCell::new(TimeRangesContainer::default()),
505 audio_tracks_list: Default::default(),
506 video_tracks_list: Default::default(),
507 text_tracks_list: Default::default(),
508 next_timeupdate_event: Cell::new(Instant::now() + Duration::from_millis(250)),
509 current_fetch_context: DomRefCell::new(None),
510 media_controls_id: DomRefCell::new(None),
511 droppable: DroppableHtmlMediaElement::new(
512 Cell::new(0),
513 document.window().get_player_context(),
514 ),
515 }
516 }
517
518 pub(crate) fn network_state(&self) -> NetworkState {
519 self.network_state.get()
520 }
521
522 pub(crate) fn get_ready_state(&self) -> ReadyState {
523 self.ready_state.get()
524 }
525
526 fn media_type_id(&self) -> HTMLMediaElementTypeId {
527 match self.upcast::<Node>().type_id() {
528 NodeTypeId::Element(ElementTypeId::HTMLElement(
529 HTMLElementTypeId::HTMLMediaElement(media_type_id),
530 )) => media_type_id,
531 _ => unreachable!(),
532 }
533 }
534
535 fn play_media(&self) {
536 if let Some(ref player) = *self.player.borrow() {
537 if let Err(e) = player.lock().unwrap().set_rate(self.playbackRate.get()) {
538 warn!("Could not set the playback rate {:?}", e);
539 }
540 if let Err(e) = player.lock().unwrap().play() {
541 warn!("Could not play media {:?}", e);
542 }
543 }
544 }
545
546 pub(crate) fn delay_load_event(&self, delay: bool, can_gc: CanGc) {
553 let blocker = &self.delaying_the_load_event_flag;
554 if delay && blocker.borrow().is_none() {
555 *blocker.borrow_mut() = Some(LoadBlocker::new(&self.owner_document(), LoadType::Media));
556 } else if !delay && blocker.borrow().is_some() {
557 LoadBlocker::terminate(blocker, can_gc);
558 }
559 }
560
561 fn time_marches_on(&self) {
563 if Instant::now() > self.next_timeupdate_event.get() {
565 self.owner_global()
566 .task_manager()
567 .media_element_task_source()
568 .queue_simple_event(self.upcast(), atom!("timeupdate"));
569 self.next_timeupdate_event
570 .set(Instant::now() + Duration::from_millis(350));
571 }
572 }
573
574 fn internal_pause_steps(&self) {
576 self.autoplaying.set(false);
578
579 if !self.Paused() {
581 self.paused.set(true);
583
584 self.take_pending_play_promises(Err(Error::Abort));
586
587 let this = Trusted::new(self);
589 let generation_id = self.generation_id.get();
590 self.owner_global()
591 .task_manager()
592 .media_element_task_source()
593 .queue(task!(internal_pause_steps: move || {
594 let this = this.root();
595 if generation_id != this.generation_id.get() {
596 return;
597 }
598
599 this.fulfill_in_flight_play_promises(|| {
600 this.upcast::<EventTarget>().fire_event(atom!("timeupdate"), CanGc::note());
602
603 this.upcast::<EventTarget>().fire_event(atom!("pause"), CanGc::note());
605
606 if let Some(ref player) = *this.player.borrow() {
607 if let Err(e) = player.lock().unwrap().pause() {
608 eprintln!("Could not pause player {:?}", e);
609 }
610 }
611
612 });
616 }));
617
618 }
622 }
623 fn is_allowed_to_play(&self) -> bool {
625 true
626 }
627
628 fn notify_about_playing(&self) {
630 self.take_pending_play_promises(Ok(()));
632
633 let this = Trusted::new(self);
635 let generation_id = self.generation_id.get();
636 self.owner_global()
637 .task_manager()
638 .media_element_task_source()
639 .queue(task!(notify_about_playing: move || {
640 let this = this.root();
641 if generation_id != this.generation_id.get() {
642 return;
643 }
644
645 this.fulfill_in_flight_play_promises(|| {
646 this.upcast::<EventTarget>().fire_event(atom!("playing"), CanGc::note());
648 this.play_media();
649
650 });
654
655 }));
656 }
657
658 fn change_ready_state(&self, ready_state: ReadyState) {
660 let old_ready_state = self.ready_state.get();
661 self.ready_state.set(ready_state);
662
663 if self.network_state.get() == NetworkState::Empty {
664 return;
665 }
666
667 if old_ready_state == ready_state {
668 return;
669 }
670
671 let owner_global = self.owner_global();
672 let task_manager = owner_global.task_manager();
673 let task_source = task_manager.media_element_task_source();
674
675 match (old_ready_state, ready_state) {
677 (ReadyState::HaveNothing, ReadyState::HaveMetadata) => {
678 task_source.queue_simple_event(self.upcast(), atom!("loadedmetadata"));
679 return;
681 },
682 (ReadyState::HaveMetadata, new) if new >= ReadyState::HaveCurrentData => {
683 if !self.fired_loadeddata_event.get() {
684 self.fired_loadeddata_event.set(true);
685 let this = Trusted::new(self);
686 task_source.queue(task!(media_reached_current_data: move || {
687 let this = this.root();
688 this.upcast::<EventTarget>().fire_event(atom!("loadeddata"), CanGc::note());
689 this.delay_load_event(false, CanGc::note());
690 }));
691 }
692
693 },
697 (ReadyState::HaveFutureData, new) if new <= ReadyState::HaveCurrentData => {
698 return;
703 },
704
705 _ => (),
706 }
707
708 if old_ready_state <= ReadyState::HaveCurrentData &&
709 ready_state >= ReadyState::HaveFutureData
710 {
711 task_source.queue_simple_event(self.upcast(), atom!("canplay"));
712
713 if !self.Paused() {
714 self.notify_about_playing();
715 }
716 }
717
718 if ready_state == ReadyState::HaveEnoughData {
719 if self.eligible_for_autoplay() {
721 self.paused.set(false);
723 if self.show_poster.get() {
725 self.show_poster.set(false);
726 self.time_marches_on();
727 }
728 task_source.queue_simple_event(self.upcast(), atom!("play"));
730 self.notify_about_playing();
732 self.autoplaying.set(false);
734 }
735
736 task_source.queue_simple_event(self.upcast(), atom!("canplaythrough"));
739 }
740 }
741
742 fn invoke_resource_selection_algorithm(&self, can_gc: CanGc) {
744 self.network_state.set(NetworkState::NoSource);
746
747 self.show_poster.set(true);
749
750 self.delay_load_event(true, can_gc);
752
753 let doc = self.owner_document();
760 let task = MediaElementMicrotask::ResourceSelection {
761 elem: DomRoot::from_ref(self),
762 generation_id: self.generation_id.get(),
763 base_url: doc.base_url(),
764 };
765
766 ScriptThread::await_stable_state(Microtask::MediaElement(task));
771 }
772
773 fn resource_selection_algorithm_sync(&self, base_url: ServoUrl, can_gc: CanGc) {
775 enum Mode {
782 Object,
783 Attribute(String),
784 Children(DomRoot<HTMLSourceElement>),
785 }
786 fn mode(media: &HTMLMediaElement) -> Option<Mode> {
787 if media.src_object.borrow().is_some() {
788 return Some(Mode::Object);
789 }
790 if let Some(attr) = media
791 .upcast::<Element>()
792 .get_attribute(&ns!(), &local_name!("src"))
793 {
794 return Some(Mode::Attribute(attr.Value().into()));
795 }
796 let source_child_element = media
797 .upcast::<Node>()
798 .children()
799 .filter_map(DomRoot::downcast::<HTMLSourceElement>)
800 .next();
801 if let Some(element) = source_child_element {
802 return Some(Mode::Children(element));
803 }
804 None
805 }
806 let mode = if let Some(mode) = mode(self) {
807 mode
808 } else {
809 self.network_state.set(NetworkState::Empty);
810 self.delay_load_event(false, can_gc);
812 return;
813 };
814
815 self.network_state.set(NetworkState::Loading);
817
818 self.owner_global()
820 .task_manager()
821 .media_element_task_source()
822 .queue_simple_event(self.upcast(), atom!("loadstart"));
823
824 match mode {
826 Mode::Object => {
828 "".clone_into(&mut self.current_src.borrow_mut());
830
831 self.resource_fetch_algorithm(Resource::Object);
838 },
839 Mode::Attribute(src) => {
840 if src.is_empty() {
842 self.queue_dedicated_media_source_failure_steps();
843 return;
844 }
845
846 let url_record = match base_url.join(&src) {
848 Ok(url) => url,
849 Err(_) => {
850 self.queue_dedicated_media_source_failure_steps();
851 return;
852 },
853 };
854
855 *self.current_src.borrow_mut() = url_record.as_str().into();
857
858 self.resource_fetch_algorithm(Resource::Url(url_record));
862 },
863 Mode::Children(source) => {
865 let src = source
868 .upcast::<Element>()
869 .get_attribute(&ns!(), &local_name!("src"));
870 let src: String = match src {
872 Some(src) if !src.Value().is_empty() => src.Value().into(),
873 _ => {
874 source
875 .upcast::<EventTarget>()
876 .fire_event(atom!("error"), can_gc);
877 self.queue_dedicated_media_source_failure_steps();
878 return;
879 },
880 };
881 let url_record = match base_url.join(&src) {
883 Ok(url) => url,
884 Err(_) => {
885 source
886 .upcast::<EventTarget>()
887 .fire_event(atom!("error"), can_gc);
888 self.queue_dedicated_media_source_failure_steps();
889 return;
890 },
891 };
892 *self.current_src.borrow_mut() = url_record.as_str().into();
894 self.resource_fetch_algorithm(Resource::Url(url_record));
896 },
897 }
898 }
899
900 fn fetch_request(&self, offset: Option<u64>, seek_lock: Option<SeekLock>) {
901 if self.resource_url.borrow().is_none() && self.blob_url.borrow().is_none() {
902 eprintln!("Missing request url");
903 if let Some(seek_lock) = seek_lock {
904 seek_lock.unlock(false);
905 }
906 self.queue_dedicated_media_source_failure_steps();
907 return;
908 }
909
910 let document = self.owner_document();
911 let destination = match self.media_type_id() {
912 HTMLMediaElementTypeId::HTMLAudioElement => Destination::Audio,
913 HTMLMediaElementTypeId::HTMLVideoElement => Destination::Video,
914 };
915 let mut headers = HeaderMap::new();
916 headers.insert(
918 header::RANGE,
919 HeaderValue::from_str(&format!("bytes={}-", offset.unwrap_or(0))).unwrap(),
920 );
921 let url = match self.resource_url.borrow().as_ref() {
922 Some(url) => url.clone(),
923 None => self.blob_url.borrow().as_ref().unwrap().clone(),
924 };
925
926 let cors_setting = cors_setting_for_element(self.upcast());
927 let global = self.global();
928 let request = create_a_potential_cors_request(
929 Some(document.webview_id()),
930 url.clone(),
931 destination,
932 cors_setting,
933 None,
934 global.get_referrer(),
935 document.insecure_requests_policy(),
936 document.has_trustworthy_ancestor_or_current_origin(),
937 global.policy_container(),
938 )
939 .headers(headers)
940 .origin(document.origin().immutable().clone())
941 .pipeline_id(Some(self.global().pipeline_id()))
942 .referrer_policy(document.get_referrer_policy());
943
944 let mut current_fetch_context = self.current_fetch_context.borrow_mut();
945 if let Some(ref mut current_fetch_context) = *current_fetch_context {
946 current_fetch_context.cancel(CancelReason::Overridden);
947 }
948
949 *current_fetch_context = Some(HTMLMediaElementFetchContext::new(
950 request.id,
951 global.core_resource_thread(),
952 ));
953 let listener =
954 HTMLMediaElementFetchListener::new(self, request.id, url.clone(), offset.unwrap_or(0));
955
956 self.owner_document().fetch_background(request, listener);
957
958 if let Some(seek_lock) = seek_lock {
963 seek_lock.unlock(true);
964 }
965 }
966
967 fn eligible_for_autoplay(&self) -> bool {
969 self.autoplaying.get() &&
971
972 self.Paused() &&
974
975 self.Autoplay() &&
977
978 {
981 let document = self.owner_document();
982
983 !document.has_active_sandboxing_flag(
984 SandboxingFlagSet::SANDBOXED_AUTOMATIC_FEATURES_BROWSING_CONTEXT_FLAG,
985 )
986 }
987
988 }
991
992 fn resource_fetch_algorithm(&self, resource: Resource) {
994 if let Err(e) = self.setup_media_player(&resource) {
995 eprintln!("Setup media player error {:?}", e);
996 self.queue_dedicated_media_source_failure_steps();
997 return;
998 }
999
1000 match resource {
1009 Resource::Url(url) => {
1010 if self.Preload() == "none" && !self.autoplaying.get() {
1012 self.network_state.set(NetworkState::Idle);
1014
1015 let owner_global = self.owner_global();
1017 let task_manager = owner_global.task_manager();
1018 let task_source = task_manager.media_element_task_source();
1019 task_source.queue_simple_event(self.upcast(), atom!("suspend"));
1020
1021 let this = Trusted::new(self);
1023 task_source.queue(task!(set_media_delay_load_event_flag_to_false: move || {
1024 this.root().delay_load_event(false, CanGc::note());
1025 }));
1026
1027 return;
1036 }
1037
1038 *self.resource_url.borrow_mut() = Some(url);
1040 self.fetch_request(None, None);
1041 },
1042 Resource::Object => {
1043 if let Some(ref src_object) = *self.src_object.borrow() {
1044 match src_object {
1045 SrcObject::Blob(blob) => {
1046 let blob_url = URL::CreateObjectURL(&self.global(), blob);
1047 *self.blob_url.borrow_mut() =
1048 Some(ServoUrl::parse(&blob_url).expect("infallible"));
1049 self.fetch_request(None, None);
1050 },
1051 SrcObject::MediaStream(stream) => {
1052 let tracks = &*stream.get_tracks();
1053 for (pos, track) in tracks.iter().enumerate() {
1054 if self
1055 .player
1056 .borrow()
1057 .as_ref()
1058 .unwrap()
1059 .lock()
1060 .unwrap()
1061 .set_stream(&track.id(), pos == tracks.len() - 1)
1062 .is_err()
1063 {
1064 self.queue_dedicated_media_source_failure_steps();
1065 }
1066 }
1067 },
1068 }
1069 }
1070 },
1071 }
1072 }
1073
1074 fn queue_dedicated_media_source_failure_steps(&self) {
1078 let this = Trusted::new(self);
1079 let generation_id = self.generation_id.get();
1080 self.take_pending_play_promises(Err(Error::NotSupported));
1081 self.owner_global()
1082 .task_manager()
1083 .media_element_task_source()
1084 .queue(task!(dedicated_media_source_failure_steps: move || {
1085 let this = this.root();
1086 if generation_id != this.generation_id.get() {
1087 return;
1088 }
1089
1090 this.fulfill_in_flight_play_promises(|| {
1091 this.error.set(Some(&*MediaError::new(
1093 &this.owner_window(),
1094 MEDIA_ERR_SRC_NOT_SUPPORTED, CanGc::note())));
1095
1096 this.AudioTracks().clear();
1098 this.VideoTracks().clear();
1099
1100 this.network_state.set(NetworkState::NoSource);
1102
1103 this.show_poster.set(true);
1105
1106 this.upcast::<EventTarget>().fire_event(atom!("error"), CanGc::note());
1108
1109 if let Some(ref player) = *this.player.borrow() {
1110 if let Err(e) = player.lock().unwrap().stop() {
1111 eprintln!("Could not stop player {:?}", e);
1112 }
1113 }
1114
1115 });
1119
1120 this.delay_load_event(false, CanGc::note());
1122 }));
1123 }
1124
1125 fn queue_ratechange_event(&self) {
1126 self.owner_global()
1127 .task_manager()
1128 .media_element_task_source()
1129 .queue_simple_event(self.upcast(), atom!("ratechange"));
1130 }
1131
1132 fn in_error_state(&self) -> bool {
1133 self.error.get().is_some()
1134 }
1135
1136 fn is_potentially_playing(&self) -> bool {
1138 !self.paused.get() &&
1139 !self.Ended() &&
1140 self.error.get().is_none() &&
1141 !self.is_blocked_media_element()
1142 }
1143
1144 fn is_blocked_media_element(&self) -> bool {
1146 self.ready_state.get() <= ReadyState::HaveCurrentData ||
1147 self.is_paused_for_user_interaction() ||
1148 self.is_paused_for_in_band_content()
1149 }
1150
1151 fn is_paused_for_user_interaction(&self) -> bool {
1153 false
1156 }
1157
1158 fn is_paused_for_in_band_content(&self) -> bool {
1160 false
1163 }
1164
1165 fn media_element_load_algorithm(&self, can_gc: CanGc) {
1167 self.fired_loadeddata_event.set(false);
1170
1171 self.generation_id.set(self.generation_id.get() + 1);
1173
1174 while !self.in_flight_play_promises_queue.borrow().is_empty() {
1176 self.fulfill_in_flight_play_promises(|| ());
1177 }
1178
1179 let global = self.owner_global();
1180 let task_manager = global.task_manager();
1181 let task_source = task_manager.media_element_task_source();
1182
1183 let network_state = self.network_state.get();
1185 if network_state == NetworkState::Loading || network_state == NetworkState::Idle {
1186 task_source.queue_simple_event(self.upcast(), atom!("abort"));
1187 }
1188
1189 if network_state != NetworkState::Empty {
1191 task_source.queue_simple_event(self.upcast(), atom!("emptied"));
1193
1194 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1196 current_fetch_context.cancel(CancelReason::Error);
1197 }
1198
1199 self.AudioTracks().clear();
1204 self.VideoTracks().clear();
1205
1206 if self.ready_state.get() != ReadyState::HaveNothing {
1208 self.change_ready_state(ReadyState::HaveNothing);
1209 }
1210
1211 if !self.Paused() {
1213 self.paused.set(true);
1215
1216 self.take_pending_play_promises(Err(Error::Abort));
1218 self.fulfill_in_flight_play_promises(|| ());
1219 }
1220
1221 if self.seeking.get() {
1223 self.seeking.set(false);
1224 }
1225
1226 let queue_timeupdate_event = self.playback_position.get() != 0.;
1228 self.playback_position.set(0.);
1229 if queue_timeupdate_event {
1230 task_source.queue_simple_event(self.upcast(), atom!("timeupdate"));
1231 }
1232
1233 self.duration.set(f64::NAN);
1238 }
1239
1240 self.playbackRate.set(self.defaultPlaybackRate.get());
1242
1243 self.error.set(None);
1245 self.autoplaying.set(true);
1246
1247 self.invoke_resource_selection_algorithm(can_gc);
1249
1250 }
1253
1254 fn push_pending_play_promise(&self, promise: &Rc<Promise>) {
1256 self.pending_play_promises
1257 .borrow_mut()
1258 .push(promise.clone());
1259 }
1260
1261 fn take_pending_play_promises(&self, result: ErrorResult) {
1272 let pending_play_promises = std::mem::take(&mut *self.pending_play_promises.borrow_mut());
1273 self.in_flight_play_promises_queue
1274 .borrow_mut()
1275 .push_back((pending_play_promises.into(), result));
1276 }
1277
1278 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
1287 fn fulfill_in_flight_play_promises<F>(&self, f: F)
1288 where
1289 F: FnOnce(),
1290 {
1291 let (promises, result) = self
1292 .in_flight_play_promises_queue
1293 .borrow_mut()
1294 .pop_front()
1295 .expect("there should be at least one list of in flight play promises");
1296 f();
1297 for promise in &*promises {
1298 match result {
1299 Ok(ref value) => promise.resolve_native(value, CanGc::note()),
1300 Err(ref error) => promise.reject_error(error.clone(), CanGc::note()),
1301 }
1302 }
1303 }
1304
1305 pub(crate) fn handle_source_child_insertion(&self, can_gc: CanGc) {
1309 if self.upcast::<Element>().has_attribute(&local_name!("src")) {
1310 return;
1311 }
1312 if self.network_state.get() != NetworkState::Empty {
1313 return;
1314 }
1315 self.media_element_load_algorithm(can_gc);
1316 }
1317
1318 fn seek(&self, time: f64, _approximate_for_speed: bool) {
1320 self.show_poster.set(false);
1322
1323 if self.ready_state.get() == ReadyState::HaveNothing {
1325 return;
1326 }
1327
1328 self.seeking.set(true);
1335
1336 let time = f64::min(time, self.Duration());
1342
1343 let time = f64::max(time, 0.);
1345
1346 let seekable = self.Seekable();
1348 if seekable.Length() == 0 {
1349 self.seeking.set(false);
1350 return;
1351 }
1352 let mut nearest_seekable_position = 0.0;
1353 let mut in_seekable_range = false;
1354 let mut nearest_seekable_distance = f64::MAX;
1355 for i in 0..seekable.Length() {
1356 let start = seekable.Start(i).unwrap().abs();
1357 let end = seekable.End(i).unwrap().abs();
1358 if time >= start && time <= end {
1359 nearest_seekable_position = time;
1360 in_seekable_range = true;
1361 break;
1362 } else if time < start {
1363 let distance = start - time;
1364 if distance < nearest_seekable_distance {
1365 nearest_seekable_distance = distance;
1366 nearest_seekable_position = start;
1367 }
1368 } else {
1369 let distance = time - end;
1370 if distance < nearest_seekable_distance {
1371 nearest_seekable_distance = distance;
1372 nearest_seekable_position = end;
1373 }
1374 }
1375 }
1376 let time = if in_seekable_range {
1377 time
1378 } else {
1379 nearest_seekable_position
1380 };
1381
1382 self.owner_global()
1387 .task_manager()
1388 .media_element_task_source()
1389 .queue_simple_event(self.upcast(), atom!("seeking"));
1390
1391 if let Some(ref player) = *self.player.borrow() {
1393 if let Err(e) = player.lock().unwrap().seek(time) {
1394 eprintln!("Seek error {:?}", e);
1395 }
1396 }
1397
1398 }
1402
1403 fn seek_end(&self) {
1405 self.seeking.set(false);
1407
1408 self.time_marches_on();
1410
1411 let global = self.owner_global();
1413 let task_manager = global.task_manager();
1414 let task_source = task_manager.media_element_task_source();
1415 task_source.queue_simple_event(self.upcast(), atom!("timeupdate"));
1416
1417 task_source.queue_simple_event(self.upcast(), atom!("seeked"));
1419 }
1420
1421 fn set_player_id(&self, player_id: u64) {
1422 self.droppable.set_player_id(player_id);
1423 }
1424
1425 pub(crate) fn set_poster_frame(&self, image: Option<Arc<RasterImage>>) {
1427 let queue_postershown_event = pref!(media_testing_enabled) && image.is_some();
1428
1429 self.video_renderer.lock().unwrap().set_poster_frame(image);
1430
1431 self.upcast::<Node>().dirty(NodeDamage::Other);
1432
1433 if queue_postershown_event {
1434 self.owner_global()
1435 .task_manager()
1436 .media_element_task_source()
1437 .queue_simple_event(self.upcast(), atom!("postershown"));
1438 }
1439 }
1440
1441 fn setup_media_player(&self, resource: &Resource) -> Result<(), ()> {
1442 let stream_type = match *resource {
1443 Resource::Object => {
1444 if let Some(ref src_object) = *self.src_object.borrow() {
1445 match src_object {
1446 SrcObject::MediaStream(_) => StreamType::Stream,
1447 _ => StreamType::Seekable,
1448 }
1449 } else {
1450 return Err(());
1451 }
1452 },
1453 _ => StreamType::Seekable,
1454 };
1455
1456 let window = self.owner_window();
1457 let (action_sender, action_receiver) = ipc::channel::<PlayerEvent>().unwrap();
1458 let video_renderer: Option<Arc<Mutex<dyn VideoFrameRenderer>>> = match self.media_type_id()
1459 {
1460 HTMLMediaElementTypeId::HTMLAudioElement => None,
1461 HTMLMediaElementTypeId::HTMLVideoElement => Some(self.video_renderer.clone()),
1462 };
1463
1464 let audio_renderer = self.audio_renderer.borrow().as_ref().cloned();
1465
1466 let pipeline_id = window.pipeline_id();
1467 let client_context_id =
1468 ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get());
1469 let player = ServoMedia::get().create_player(
1470 &client_context_id,
1471 stream_type,
1472 action_sender,
1473 video_renderer,
1474 audio_renderer,
1475 Box::new(window.get_player_context()),
1476 );
1477 let player_id = {
1478 let player_guard = player.lock().unwrap();
1479
1480 if let Err(e) = player_guard.set_mute(self.muted.get()) {
1481 log::warn!("Could not set mute state: {:?}", e);
1482 }
1483
1484 player_guard.get_id()
1485 };
1486
1487 *self.player.borrow_mut() = Some(player);
1488
1489 let trusted_node = Trusted::new(self);
1490 let task_source = self
1491 .owner_global()
1492 .task_manager()
1493 .media_element_task_source()
1494 .to_sendable();
1495 ROUTER.add_typed_route(
1496 action_receiver,
1497 Box::new(move |message| {
1498 let event = message.unwrap();
1499 trace!("Player event {:?}", event);
1500 let this = trusted_node.clone();
1501 task_source.queue(task!(handle_player_event: move || {
1502 this.root().handle_player_event(player_id, &event, CanGc::note());
1503 }));
1504 }),
1505 );
1506
1507 let (player_id, image_receiver) = window
1509 .get_player_context()
1510 .glplayer_thread_sender
1511 .map(|pipeline| {
1512 let (image_sender, image_receiver) = channel().unwrap();
1513 pipeline
1514 .send(GLPlayerMsg::RegisterPlayer(image_sender))
1515 .unwrap();
1516 match image_receiver.recv().unwrap() {
1517 GLPlayerMsgForward::PlayerId(id) => (id, Some(image_receiver)),
1518 _ => unreachable!(),
1519 }
1520 })
1521 .unwrap_or((0, None));
1522
1523 self.set_player_id(player_id);
1524 self.video_renderer.lock().unwrap().player_id = Some(player_id);
1525
1526 if let Some(image_receiver) = image_receiver {
1527 let trusted_node = Trusted::new(self);
1528 let task_source = self
1529 .owner_global()
1530 .task_manager()
1531 .media_element_task_source()
1532 .to_sendable();
1533 ROUTER.add_typed_route(
1534 image_receiver,
1535 Box::new(move |message| {
1536 let msg = message.unwrap();
1537 let this = trusted_node.clone();
1538 task_source.queue(task!(handle_glplayer_message: move || {
1539 trace!("GLPlayer message {:?}", msg);
1540 let video_renderer = this.root().video_renderer.clone();
1541
1542 match msg {
1543 GLPlayerMsgForward::Lock(sender) => {
1544 if let Some(holder) = video_renderer
1545 .lock()
1546 .unwrap()
1547 .current_frame_holder
1548 .as_mut() {
1549 holder.lock();
1550 sender.send(holder.get()).unwrap();
1551 };
1552 },
1553 GLPlayerMsgForward::Unlock() => {
1554 if let Some(holder) = video_renderer
1555 .lock()
1556 .unwrap()
1557 .current_frame_holder
1558 .as_mut() { holder.unlock() }
1559 },
1560 _ => (),
1561 }
1562 }));
1563 }),
1564 );
1565 }
1566
1567 Ok(())
1568 }
1569
1570 pub(crate) fn set_audio_track(&self, idx: usize, enabled: bool) {
1571 if let Some(ref player) = *self.player.borrow() {
1572 if let Err(err) = player.lock().unwrap().set_audio_track(idx as i32, enabled) {
1573 warn!("Could not set audio track {:#?}", err);
1574 }
1575 }
1576 }
1577
1578 pub(crate) fn set_video_track(&self, idx: usize, enabled: bool) {
1579 if let Some(ref player) = *self.player.borrow() {
1580 if let Err(err) = player.lock().unwrap().set_video_track(idx as i32, enabled) {
1581 warn!("Could not set video track {:#?}", err);
1582 }
1583 }
1584 }
1585
1586 fn end_of_playback_in_forwards_direction(&self) {
1587 if self.Loop() {
1590 self.seek(
1591 self.earliest_possible_position(),
1592 false,
1593 );
1594 return;
1595 }
1596 let this = Trusted::new(self);
1603
1604 self.owner_global()
1605 .task_manager()
1606 .media_element_task_source()
1607 .queue(task!(reaches_the_end_steps: move || {
1608 let this = this.root();
1609 this.upcast::<EventTarget>().fire_event(atom!("timeupdate"), CanGc::note());
1611
1612 if this.Ended() && !this.Paused() {
1615 this.paused.set(true);
1617
1618 this.upcast::<EventTarget>().fire_event(atom!("pause"), CanGc::note());
1620
1621 this.take_pending_play_promises(Err(Error::Abort));
1624 this.fulfill_in_flight_play_promises(|| ());
1625 }
1626
1627 this.upcast::<EventTarget>().fire_event(atom!("ended"), CanGc::note());
1629 }));
1630
1631 self.change_ready_state(ReadyState::HaveCurrentData);
1633 }
1634
1635 fn playback_end(&self) {
1636 if self.ready_state.get() < ReadyState::HaveMetadata {
1640 self.queue_dedicated_media_source_failure_steps();
1641 return;
1642 }
1643
1644 match self.direction_of_playback() {
1646 PlaybackDirection::Forwards => self.end_of_playback_in_forwards_direction(),
1647
1648 PlaybackDirection::Backwards => {
1649 if self.playback_position.get() <= self.earliest_possible_position() {
1650 self.owner_global()
1651 .task_manager()
1652 .media_element_task_source()
1653 .queue_simple_event(self.upcast(), atom!("ended"));
1654 }
1655 },
1656 }
1657 }
1658
1659 fn playback_error(&self, error: &str, can_gc: CanGc) {
1660 error!("Player error: {:?}", error);
1661
1662 if self.in_error_state() {
1666 return;
1667 }
1668
1669 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1672 current_fetch_context.cancel(CancelReason::Error);
1673 }
1674 self.error.set(Some(&*MediaError::new(
1676 &self.owner_window(),
1677 MEDIA_ERR_DECODE,
1678 can_gc,
1679 )));
1680
1681 self.network_state.set(NetworkState::Idle);
1683
1684 self.delay_load_event(false, can_gc);
1686
1687 self.upcast::<EventTarget>()
1689 .fire_event(atom!("error"), can_gc);
1690
1691 }
1693
1694 fn playback_metadata_updated(
1695 &self,
1696 metadata: &servo_media::player::metadata::Metadata,
1697 can_gc: CanGc,
1698 ) {
1699 if !metadata.audio_tracks.is_empty() {
1702 for (i, _track) in metadata.audio_tracks.iter().enumerate() {
1703 let kind = match i {
1705 0 => DOMString::from("main"),
1706 _ => DOMString::new(),
1707 };
1708 let window = self.owner_window();
1709 let audio_track = AudioTrack::new(
1710 &window,
1711 DOMString::new(),
1712 kind,
1713 DOMString::new(),
1714 DOMString::new(),
1715 Some(&*self.AudioTracks()),
1716 can_gc,
1717 );
1718
1719 self.AudioTracks().add(&audio_track);
1721
1722 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
1724 let fragment = MediaFragmentParser::from(servo_url);
1725 if let Some(id) = fragment.id() {
1726 if audio_track.id() == DOMString::from(id) {
1727 self.AudioTracks()
1728 .set_enabled(self.AudioTracks().len() - 1, true);
1729 }
1730 }
1731
1732 if fragment.tracks().contains(&audio_track.kind().into()) {
1733 self.AudioTracks()
1734 .set_enabled(self.AudioTracks().len() - 1, true);
1735 }
1736 }
1737
1738 if self.AudioTracks().enabled_index().is_none() {
1740 self.AudioTracks()
1741 .set_enabled(self.AudioTracks().len() - 1, true);
1742 }
1743
1744 let event = TrackEvent::new(
1746 self.global().as_window(),
1747 atom!("addtrack"),
1748 false,
1749 false,
1750 &Some(VideoTrackOrAudioTrackOrTextTrack::AudioTrack(audio_track)),
1751 can_gc,
1752 );
1753
1754 event
1755 .upcast::<Event>()
1756 .fire(self.upcast::<EventTarget>(), can_gc);
1757 }
1758 }
1759
1760 if !metadata.video_tracks.is_empty() {
1762 for (i, _track) in metadata.video_tracks.iter().enumerate() {
1763 let kind = match i {
1765 0 => DOMString::from("main"),
1766 _ => DOMString::new(),
1767 };
1768 let window = self.owner_window();
1769 let video_track = VideoTrack::new(
1770 &window,
1771 DOMString::new(),
1772 kind,
1773 DOMString::new(),
1774 DOMString::new(),
1775 Some(&*self.VideoTracks()),
1776 can_gc,
1777 );
1778
1779 self.VideoTracks().add(&video_track);
1781
1782 if let Some(track) = self.VideoTracks().item(0) {
1784 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
1785 let fragment = MediaFragmentParser::from(servo_url);
1786 if let Some(id) = fragment.id() {
1787 if track.id() == DOMString::from(id) {
1788 self.VideoTracks().set_selected(0, true);
1789 }
1790 } else if fragment.tracks().contains(&track.kind().into()) {
1791 self.VideoTracks().set_selected(0, true);
1792 }
1793 }
1794 }
1795
1796 if self.VideoTracks().selected_index().is_none() {
1798 self.VideoTracks()
1799 .set_selected(self.VideoTracks().len() - 1, true);
1800 }
1801
1802 let event = TrackEvent::new(
1804 self.global().as_window(),
1805 atom!("addtrack"),
1806 false,
1807 false,
1808 &Some(VideoTrackOrAudioTrackOrTextTrack::VideoTrack(video_track)),
1809 can_gc,
1810 );
1811
1812 event
1813 .upcast::<Event>()
1814 .fire(self.upcast::<EventTarget>(), can_gc);
1815 }
1816 }
1817
1818 self.playback_position.set(0.);
1827
1828 let previous_duration = self.duration.get();
1830 if let Some(duration) = metadata.duration {
1831 self.duration.set(duration.as_secs() as f64);
1832 } else {
1833 self.duration.set(f64::INFINITY);
1834 }
1835 if previous_duration != self.duration.get() {
1836 self.owner_global()
1837 .task_manager()
1838 .media_element_task_source()
1839 .queue_simple_event(self.upcast(), atom!("durationchange"));
1840 }
1841
1842 self.handle_resize(Some(metadata.width), Some(metadata.height));
1844
1845 self.change_ready_state(ReadyState::HaveMetadata);
1847
1848 let mut jumped = false;
1850
1851 if self.default_playback_start_position.get() > 0. {
1853 self.seek(
1854 self.default_playback_start_position.get(),
1855 false,
1856 );
1857 jumped = true;
1858 }
1859
1860 self.default_playback_start_position.set(0.);
1862
1863 if let Some(servo_url) = self.resource_url.borrow().as_ref() {
1865 let fragment = MediaFragmentParser::from(servo_url);
1866 if let Some(start) = fragment.start() {
1867 if start > 0. && start < self.duration.get() {
1868 self.playback_position.set(start);
1869 if !jumped {
1870 self.seek(self.playback_position.get(), false)
1871 }
1872 }
1873 }
1874 }
1875
1876 if self.Controls() {
1881 self.render_controls(can_gc);
1882 }
1883
1884 let global = self.global();
1885 let window = global.as_window();
1886
1887 window.Navigator().MediaSession().update_title(
1889 metadata
1890 .title
1891 .clone()
1892 .unwrap_or(window.get_url().into_string()),
1893 );
1894 }
1895
1896 fn playback_video_frame_updated(&self) {
1897 if let Some(frame) = self.video_renderer.lock().unwrap().current_frame {
1899 self.handle_resize(Some(frame.width as u32), Some(frame.height as u32));
1900 }
1901 }
1902
1903 fn playback_need_data(&self) {
1904 if let Some(ref current_fetch_context) = *self.current_fetch_context.borrow() {
1910 if let Some(reason) = current_fetch_context.cancel_reason() {
1911 if *reason == CancelReason::Backoff {
1917 self.seek(self.playback_position.get(), false);
1918 }
1919 return;
1920 }
1921 }
1922
1923 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1924 if let Err(e) = {
1925 let mut data_source = current_fetch_context.data_source().borrow_mut();
1926 data_source.set_locked(false);
1927 data_source.process_into_player_from_queue(self.player.borrow().as_ref().unwrap())
1928 } {
1929 if e == PlayerError::EnoughData {
1934 current_fetch_context.cancel(CancelReason::Backoff);
1935 }
1936 }
1937 }
1938 }
1939
1940 fn playback_enough_data(&self) {
1941 self.change_ready_state(ReadyState::HaveEnoughData);
1942
1943 if let Some(ref mut current_fetch_context) = *self.current_fetch_context.borrow_mut() {
1948 if current_fetch_context.is_seekable() {
1949 current_fetch_context.cancel(CancelReason::Backoff);
1950 }
1951 }
1952 }
1953
1954 fn playback_position_changed(&self, position: u64) {
1955 let position = position as f64;
1956 let _ = self
1957 .played
1958 .borrow_mut()
1959 .add(self.playback_position.get(), position);
1960 self.playback_position.set(position);
1961 self.time_marches_on();
1962 let media_position_state =
1963 MediaPositionState::new(self.duration.get(), self.playbackRate.get(), position);
1964 debug!(
1965 "Sending media session event set position state {:?}",
1966 media_position_state
1967 );
1968 self.send_media_session_event(MediaSessionEvent::SetPositionState(media_position_state));
1969 }
1970
1971 fn playback_seek_done(&self) {
1972 let task = MediaElementMicrotask::Seeked {
1977 elem: DomRoot::from_ref(self),
1978 generation_id: self.generation_id.get(),
1979 };
1980 ScriptThread::await_stable_state(Microtask::MediaElement(task));
1981 }
1982
1983 fn playback_state_changed(&self, state: &PlaybackState) {
1984 let mut media_session_playback_state = MediaSessionPlaybackState::None_;
1985 match *state {
1986 PlaybackState::Paused => {
1987 media_session_playback_state = MediaSessionPlaybackState::Paused;
1988 if self.ready_state.get() == ReadyState::HaveMetadata {
1989 self.change_ready_state(ReadyState::HaveEnoughData);
1990 }
1991 },
1992 PlaybackState::Playing => {
1993 media_session_playback_state = MediaSessionPlaybackState::Playing;
1994 },
1995 PlaybackState::Buffering => {
1996 return;
2000 },
2001 _ => {},
2002 };
2003 debug!(
2004 "Sending media session event playback state changed to {:?}",
2005 media_session_playback_state
2006 );
2007 self.send_media_session_event(MediaSessionEvent::PlaybackStateChange(
2008 media_session_playback_state,
2009 ));
2010 }
2011
2012 fn handle_player_event(&self, player_id: usize, event: &PlayerEvent, can_gc: CanGc) {
2013 if self
2015 .player
2016 .borrow()
2017 .as_ref()
2018 .is_none_or(|player| player.lock().unwrap().get_id() != player_id)
2019 {
2020 return;
2021 }
2022
2023 match *event {
2024 PlayerEvent::EndOfStream => self.playback_end(),
2025 PlayerEvent::Error(ref error) => self.playback_error(error, can_gc),
2026 PlayerEvent::VideoFrameUpdated => self.playback_video_frame_updated(),
2027 PlayerEvent::MetadataUpdated(ref metadata) => {
2028 self.playback_metadata_updated(metadata, can_gc)
2029 },
2030 PlayerEvent::NeedData => self.playback_need_data(),
2031 PlayerEvent::EnoughData => self.playback_enough_data(),
2032 PlayerEvent::PositionChanged(position) => self.playback_position_changed(position),
2033 PlayerEvent::SeekData(p, ref seek_lock) => {
2034 self.fetch_request(Some(p), Some(seek_lock.clone()))
2035 },
2036 PlayerEvent::SeekDone(_) => self.playback_seek_done(),
2037 PlayerEvent::StateChanged(ref state) => self.playback_state_changed(state),
2038 }
2039 }
2040
2041 fn earliest_possible_position(&self) -> f64 {
2043 self.played
2044 .borrow()
2045 .start(0)
2046 .unwrap_or_else(|_| self.playback_position.get())
2047 }
2048
2049 fn render_controls(&self, can_gc: CanGc) {
2050 let element = self.htmlelement.upcast::<Element>();
2051 if self.ready_state.get() < ReadyState::HaveMetadata || element.is_shadow_host() {
2052 return;
2055 }
2056 let shadow_root = self
2059 .upcast::<Element>()
2060 .attach_ua_shadow_root(false, can_gc);
2061 let document = self.owner_document();
2062 let script = Element::create(
2063 QualName::new(None, ns!(html), local_name!("script")),
2064 None,
2065 &document,
2066 ElementCreator::ScriptCreated,
2067 CustomElementCreationMode::Asynchronous,
2068 None,
2069 can_gc,
2070 );
2071 let id = document.register_media_controls(&shadow_root);
2077 let media_controls_script = MEDIA_CONTROL_JS.replace("@@@id@@@", &id);
2078 *self.media_controls_id.borrow_mut() = Some(id);
2079 script
2080 .upcast::<Node>()
2081 .set_text_content_for_element(Some(DOMString::from(media_controls_script)), can_gc);
2082 if let Err(e) = shadow_root
2083 .upcast::<Node>()
2084 .AppendChild(script.upcast::<Node>(), can_gc)
2085 {
2086 warn!("Could not render media controls {:?}", e);
2087 return;
2088 }
2089
2090 let style = Element::create(
2091 QualName::new(None, ns!(html), local_name!("style")),
2092 None,
2093 &document,
2094 ElementCreator::ScriptCreated,
2095 CustomElementCreationMode::Asynchronous,
2096 None,
2097 can_gc,
2098 );
2099
2100 style
2101 .upcast::<Node>()
2102 .set_text_content_for_element(Some(DOMString::from(MEDIA_CONTROL_CSS)), can_gc);
2103
2104 if let Err(e) = shadow_root
2105 .upcast::<Node>()
2106 .AppendChild(style.upcast::<Node>(), can_gc)
2107 {
2108 warn!("Could not render media controls {:?}", e);
2109 }
2110
2111 self.upcast::<Node>().dirty(NodeDamage::Other);
2112 }
2113
2114 fn remove_controls(&self, can_gc: CanGc) {
2115 if let Some(id) = self.media_controls_id.borrow_mut().take() {
2116 self.owner_document().unregister_media_controls(&id, can_gc);
2117 }
2118 }
2119
2120 pub(crate) fn get_current_frame(&self) -> Option<VideoFrame> {
2122 self.video_renderer
2123 .lock()
2124 .unwrap()
2125 .current_frame_holder
2126 .as_ref()
2127 .map(|holder| holder.get_frame())
2128 }
2129
2130 pub(crate) fn get_current_frame_to_present(&self) -> Option<MediaFrame> {
2133 let (current_frame, poster_frame) = {
2134 let renderer = self.video_renderer.lock().unwrap();
2135 (renderer.current_frame, renderer.poster_frame)
2136 };
2137
2138 if (self.show_poster.get() || current_frame.is_none()) && poster_frame.is_some() {
2141 return poster_frame;
2142 }
2143
2144 current_frame
2145 }
2146
2147 pub(crate) fn clear_current_frame_data(&self) {
2148 self.handle_resize(None, None);
2149 self.video_renderer.lock().unwrap().current_frame = None;
2150 }
2151
2152 fn handle_resize(&self, width: Option<u32>, height: Option<u32>) {
2153 if let Some(video_elem) = self.downcast::<HTMLVideoElement>() {
2154 video_elem.resize(width, height);
2155 self.upcast::<Node>().dirty(NodeDamage::Other);
2156 }
2157 }
2158
2159 pub(crate) fn set_audio_renderer(
2164 &self,
2165 audio_renderer: Arc<Mutex<dyn AudioRenderer>>,
2166 can_gc: CanGc,
2167 ) {
2168 *self.audio_renderer.borrow_mut() = Some(audio_renderer);
2169 if let Some(ref player) = *self.player.borrow() {
2170 if let Err(e) = player.lock().unwrap().stop() {
2171 eprintln!("Could not stop player {:?}", e);
2172 }
2173 self.media_element_load_algorithm(can_gc);
2174 }
2175 }
2176
2177 fn send_media_session_event(&self, event: MediaSessionEvent) {
2178 let global = self.global();
2179 let media_session = global.as_window().Navigator().MediaSession();
2180
2181 media_session.register_media_instance(self);
2182
2183 media_session.send_event(event);
2184 }
2185
2186 pub(crate) fn set_duration(&self, duration: f64) {
2187 self.duration.set(duration);
2188 }
2189
2190 pub(crate) fn reset(&self) {
2191 if let Some(ref player) = *self.player.borrow() {
2192 if let Err(e) = player.lock().unwrap().stop() {
2193 eprintln!("Could not stop player {:?}", e);
2194 }
2195 }
2196 }
2197
2198 pub(crate) fn origin_is_clean(&self) -> bool {
2200 if self.src_object.borrow().is_some() {
2202 return true;
2205 }
2206
2207 if self.resource_url.borrow().is_some() {
2209 if let Some(ref current_fetch_context) = *self.current_fetch_context.borrow() {
2213 return current_fetch_context.origin_is_clean();
2214 }
2215 }
2216
2217 true
2218 }
2219}
2220
2221#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
2223enum PlaybackDirection {
2224 Forwards,
2225 #[allow(dead_code)]
2226 Backwards,
2227}
2228
2229impl HTMLMediaElement {
2233 fn direction_of_playback(&self) -> PlaybackDirection {
2235 PlaybackDirection::Forwards
2236 }
2237}
2238
2239impl HTMLMediaElementMethods<crate::DomTypeHolder> for HTMLMediaElement {
2240 fn NetworkState(&self) -> u16 {
2242 self.network_state.get() as u16
2243 }
2244
2245 fn ReadyState(&self) -> u16 {
2247 self.ready_state.get() as u16
2248 }
2249
2250 make_bool_getter!(Autoplay, "autoplay");
2252 make_bool_setter!(SetAutoplay, "autoplay");
2254
2255 make_bool_getter!(Loop, "loop");
2257 make_bool_setter!(SetLoop, "loop");
2259
2260 make_bool_getter!(DefaultMuted, "muted");
2262 make_bool_setter!(SetDefaultMuted, "muted");
2264
2265 make_bool_getter!(Controls, "controls");
2267 make_bool_setter!(SetControls, "controls");
2269
2270 make_url_getter!(Src, "src");
2272
2273 make_url_setter!(SetSrc, "src");
2275
2276 fn GetCrossOrigin(&self) -> Option<DOMString> {
2278 reflect_cross_origin_attribute(self.upcast::<Element>())
2279 }
2280 fn SetCrossOrigin(&self, value: Option<DOMString>, can_gc: CanGc) {
2282 set_cross_origin_attribute(self.upcast::<Element>(), value, can_gc);
2283 }
2284
2285 fn Muted(&self) -> bool {
2287 self.muted.get()
2288 }
2289
2290 fn SetMuted(&self, value: bool) {
2292 if self.muted.get() == value {
2293 return;
2294 }
2295
2296 if let Some(ref player) = *self.player.borrow() {
2297 let _ = player.lock().unwrap().set_mute(value);
2298 }
2299
2300 self.muted.set(value);
2301 self.owner_global()
2302 .task_manager()
2303 .media_element_task_source()
2304 .queue_simple_event(self.upcast(), atom!("volumechange"));
2305 if !self.is_allowed_to_play() {
2306 self.internal_pause_steps();
2307 }
2308 }
2309
2310 fn GetSrcObject(&self) -> Option<MediaStreamOrBlob> {
2312 (*self.src_object.borrow())
2313 .as_ref()
2314 .map(|src_object| match src_object {
2315 SrcObject::Blob(blob) => MediaStreamOrBlob::Blob(DomRoot::from_ref(blob)),
2316 SrcObject::MediaStream(stream) => {
2317 MediaStreamOrBlob::MediaStream(DomRoot::from_ref(stream))
2318 },
2319 })
2320 }
2321
2322 fn SetSrcObject(&self, value: Option<MediaStreamOrBlob>, can_gc: CanGc) {
2324 *self.src_object.borrow_mut() = value.map(|value| value.into());
2325 self.media_element_load_algorithm(can_gc);
2326 }
2327
2328 make_enumerated_getter!(
2331 Preload,
2332 "preload",
2333 "none" | "metadata" | "auto",
2334 missing => "auto",
2335 invalid => "auto"
2336 );
2337
2338 make_setter!(SetPreload, "preload");
2340
2341 fn CurrentSrc(&self) -> USVString {
2343 USVString(self.current_src.borrow().clone())
2344 }
2345
2346 fn Load(&self, can_gc: CanGc) {
2348 self.media_element_load_algorithm(can_gc);
2349 }
2350
2351 fn CanPlayType(&self, type_: DOMString) -> CanPlayTypeResult {
2353 match ServoMedia::get().can_play_type(&type_) {
2354 SupportsMediaType::No => CanPlayTypeResult::_empty,
2355 SupportsMediaType::Maybe => CanPlayTypeResult::Maybe,
2356 SupportsMediaType::Probably => CanPlayTypeResult::Probably,
2357 }
2358 }
2359
2360 fn GetError(&self) -> Option<DomRoot<MediaError>> {
2362 self.error.get()
2363 }
2364
2365 fn Play(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
2367 let promise = Promise::new_in_current_realm(comp, can_gc);
2368 if self
2373 .error
2374 .get()
2375 .is_some_and(|e| e.Code() == MEDIA_ERR_SRC_NOT_SUPPORTED)
2376 {
2377 promise.reject_error(Error::NotSupported, can_gc);
2378 return promise;
2379 }
2380
2381 self.push_pending_play_promise(&promise);
2383
2384 if self.network_state.get() == NetworkState::Empty {
2386 self.invoke_resource_selection_algorithm(can_gc);
2387 }
2388
2389 if self.Ended() && self.direction_of_playback() == PlaybackDirection::Forwards {
2391 self.seek(
2392 self.earliest_possible_position(),
2393 false,
2394 );
2395 }
2396
2397 let state = self.ready_state.get();
2398
2399 let global = self.owner_global();
2400 let task_manager = global.task_manager();
2401 let task_source = task_manager.media_element_task_source();
2402 if self.Paused() {
2403 self.paused.set(false);
2405
2406 if self.show_poster.get() {
2408 self.show_poster.set(false);
2409 self.time_marches_on();
2410 }
2411
2412 task_source.queue_simple_event(self.upcast(), atom!("play"));
2414
2415 match state {
2417 ReadyState::HaveNothing |
2418 ReadyState::HaveMetadata |
2419 ReadyState::HaveCurrentData => {
2420 task_source.queue_simple_event(self.upcast(), atom!("waiting"));
2421 },
2422 ReadyState::HaveFutureData | ReadyState::HaveEnoughData => {
2423 self.notify_about_playing();
2424 },
2425 }
2426 } else if state == ReadyState::HaveFutureData || state == ReadyState::HaveEnoughData {
2427 self.take_pending_play_promises(Ok(()));
2429 let this = Trusted::new(self);
2430 let generation_id = self.generation_id.get();
2431 task_source.queue(task!(resolve_pending_play_promises: move || {
2432 let this = this.root();
2433 if generation_id != this.generation_id.get() {
2434 return;
2435 }
2436
2437 this.fulfill_in_flight_play_promises(|| {
2438 this.play_media();
2439 });
2440 }));
2441 }
2442
2443 self.autoplaying.set(false);
2445
2446 promise
2448 }
2449
2450 fn Pause(&self, can_gc: CanGc) {
2452 if self.network_state.get() == NetworkState::Empty {
2454 self.invoke_resource_selection_algorithm(can_gc);
2455 }
2456
2457 self.internal_pause_steps();
2459 }
2460
2461 fn Paused(&self) -> bool {
2463 self.paused.get()
2464 }
2465
2466 fn GetDefaultPlaybackRate(&self) -> Fallible<Finite<f64>> {
2468 Ok(Finite::wrap(self.defaultPlaybackRate.get()))
2469 }
2470
2471 fn SetDefaultPlaybackRate(&self, value: Finite<f64>) -> ErrorResult {
2473 let min_allowed = -64.0;
2474 let max_allowed = 64.0;
2475 if *value < min_allowed || *value > max_allowed {
2476 return Err(Error::NotSupported);
2477 }
2478
2479 if *value != self.defaultPlaybackRate.get() {
2480 self.defaultPlaybackRate.set(*value);
2481 self.queue_ratechange_event();
2482 }
2483
2484 Ok(())
2485 }
2486
2487 fn GetPlaybackRate(&self) -> Fallible<Finite<f64>> {
2489 Ok(Finite::wrap(self.playbackRate.get()))
2490 }
2491
2492 fn SetPlaybackRate(&self, value: Finite<f64>) -> ErrorResult {
2494 let min_allowed = -64.0;
2495 let max_allowed = 64.0;
2496 if *value < min_allowed || *value > max_allowed {
2497 return Err(Error::NotSupported);
2498 }
2499
2500 if *value != self.playbackRate.get() {
2501 self.playbackRate.set(*value);
2502 self.queue_ratechange_event();
2503 if self.is_potentially_playing() {
2504 if let Some(ref player) = *self.player.borrow() {
2505 if let Err(e) = player.lock().unwrap().set_rate(*value) {
2506 warn!("Could not set the playback rate {:?}", e);
2507 }
2508 }
2509 }
2510 }
2511
2512 Ok(())
2513 }
2514
2515 fn Duration(&self) -> f64 {
2517 self.duration.get()
2518 }
2519
2520 fn CurrentTime(&self) -> Finite<f64> {
2522 Finite::wrap(if self.default_playback_start_position.get() != 0. {
2523 self.default_playback_start_position.get()
2524 } else {
2525 self.playback_position.get()
2526 })
2527 }
2528
2529 fn SetCurrentTime(&self, time: Finite<f64>) {
2531 if self.ready_state.get() == ReadyState::HaveNothing {
2532 self.default_playback_start_position.set(*time);
2533 } else {
2534 self.playback_position.set(*time);
2535 self.seek(*time, false);
2536 }
2537 }
2538
2539 fn Seeking(&self) -> bool {
2541 self.seeking.get()
2542 }
2543
2544 fn Ended(&self) -> bool {
2546 if self.ready_state.get() < ReadyState::HaveMetadata {
2547 return false;
2548 }
2549
2550 let playback_pos = self.playback_position.get();
2551
2552 match self.direction_of_playback() {
2553 PlaybackDirection::Forwards => playback_pos >= self.Duration() && !self.Loop(),
2554 PlaybackDirection::Backwards => playback_pos <= self.earliest_possible_position(),
2555 }
2556 }
2557
2558 fn FastSeek(&self, time: Finite<f64>) {
2560 self.seek(*time, true);
2561 }
2562
2563 fn Played(&self) -> DomRoot<TimeRanges> {
2565 TimeRanges::new(
2566 self.global().as_window(),
2567 self.played.borrow().clone(),
2568 CanGc::note(),
2569 )
2570 }
2571
2572 fn Seekable(&self) -> DomRoot<TimeRanges> {
2574 let mut seekable = TimeRangesContainer::default();
2575 if let Some(ref player) = *self.player.borrow() {
2576 if let Ok(ranges) = player.lock().unwrap().seekable() {
2577 for range in ranges {
2578 let _ = seekable.add(range.start, range.end);
2579 }
2580 }
2581 }
2582 TimeRanges::new(self.global().as_window(), seekable, CanGc::note())
2583 }
2584
2585 fn Buffered(&self) -> DomRoot<TimeRanges> {
2587 let mut buffered = TimeRangesContainer::default();
2588 if let Some(ref player) = *self.player.borrow() {
2589 if let Ok(ranges) = player.lock().unwrap().buffered() {
2590 for range in ranges {
2591 let _ = buffered.add(range.start, range.end);
2592 }
2593 }
2594 }
2595 TimeRanges::new(self.global().as_window(), buffered, CanGc::note())
2596 }
2597
2598 fn AudioTracks(&self) -> DomRoot<AudioTrackList> {
2600 let window = self.owner_window();
2601 self.audio_tracks_list
2602 .or_init(|| AudioTrackList::new(&window, &[], Some(self), CanGc::note()))
2603 }
2604
2605 fn VideoTracks(&self) -> DomRoot<VideoTrackList> {
2607 let window = self.owner_window();
2608 self.video_tracks_list
2609 .or_init(|| VideoTrackList::new(&window, &[], Some(self), CanGc::note()))
2610 }
2611
2612 fn TextTracks(&self) -> DomRoot<TextTrackList> {
2614 let window = self.owner_window();
2615 self.text_tracks_list
2616 .or_init(|| TextTrackList::new(&window, &[], CanGc::note()))
2617 }
2618
2619 fn AddTextTrack(
2621 &self,
2622 kind: TextTrackKind,
2623 label: DOMString,
2624 language: DOMString,
2625 ) -> DomRoot<TextTrack> {
2626 let window = self.owner_window();
2627 let track = TextTrack::new(
2630 &window,
2631 "".into(),
2632 kind,
2633 label,
2634 language,
2635 TextTrackMode::Hidden,
2636 None,
2637 CanGc::note(),
2638 );
2639 self.TextTracks().add(&track);
2641 DomRoot::from_ref(&track)
2643 }
2644
2645 fn GetVolume(&self) -> Fallible<Finite<f64>> {
2647 Ok(Finite::wrap(self.volume.get()))
2648 }
2649
2650 fn SetVolume(&self, value: Finite<f64>) -> ErrorResult {
2652 let minimum_volume = 0.0;
2653 let maximum_volume = 1.0;
2654 if *value < minimum_volume || *value > maximum_volume {
2655 return Err(Error::IndexSize);
2656 }
2657
2658 if *value != self.volume.get() {
2659 self.volume.set(*value);
2660
2661 self.owner_global()
2662 .task_manager()
2663 .media_element_task_source()
2664 .queue_simple_event(self.upcast(), atom!("volumechange"));
2665 if !self.is_allowed_to_play() {
2666 self.internal_pause_steps();
2667 }
2668 }
2669
2670 Ok(())
2671 }
2672}
2673
2674impl VirtualMethods for HTMLMediaElement {
2675 fn super_type(&self) -> Option<&dyn VirtualMethods> {
2676 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
2677 }
2678
2679 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
2680 self.super_type()
2681 .unwrap()
2682 .attribute_mutated(attr, mutation, can_gc);
2683
2684 match *attr.local_name() {
2685 local_name!("muted") => {
2686 self.SetMuted(mutation.new_value(attr).is_some());
2687 },
2688 local_name!("src") => {
2689 if mutation.new_value(attr).is_none() {
2690 self.clear_current_frame_data();
2691 return;
2692 }
2693 self.media_element_load_algorithm(CanGc::note());
2694 },
2695 local_name!("controls") => {
2696 if mutation.new_value(attr).is_some() {
2697 self.render_controls(can_gc);
2698 } else {
2699 self.remove_controls(can_gc);
2700 }
2701 },
2702 _ => (),
2703 };
2704 }
2705
2706 fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
2708 self.super_type().unwrap().unbind_from_tree(context, can_gc);
2709
2710 self.remove_controls(can_gc);
2711
2712 if context.tree_connected {
2713 let task = MediaElementMicrotask::PauseIfNotInDocument {
2714 elem: DomRoot::from_ref(self),
2715 };
2716 ScriptThread::await_stable_state(Microtask::MediaElement(task));
2717 }
2718 }
2719}
2720
2721#[derive(JSTraceable, MallocSizeOf)]
2722pub(crate) enum MediaElementMicrotask {
2723 ResourceSelection {
2724 elem: DomRoot<HTMLMediaElement>,
2725 generation_id: u32,
2726 #[no_trace]
2727 base_url: ServoUrl,
2728 },
2729 PauseIfNotInDocument {
2730 elem: DomRoot<HTMLMediaElement>,
2731 },
2732 Seeked {
2733 elem: DomRoot<HTMLMediaElement>,
2734 generation_id: u32,
2735 },
2736}
2737
2738impl MicrotaskRunnable for MediaElementMicrotask {
2739 fn handler(&self, can_gc: CanGc) {
2740 match self {
2741 &MediaElementMicrotask::ResourceSelection {
2742 ref elem,
2743 generation_id,
2744 ref base_url,
2745 } => {
2746 if generation_id == elem.generation_id.get() {
2747 elem.resource_selection_algorithm_sync(base_url.clone(), can_gc);
2748 }
2749 },
2750 MediaElementMicrotask::PauseIfNotInDocument { elem } => {
2751 if !elem.upcast::<Node>().is_connected() {
2752 elem.internal_pause_steps();
2753 }
2754 },
2755 &MediaElementMicrotask::Seeked {
2756 ref elem,
2757 generation_id,
2758 } => {
2759 if generation_id == elem.generation_id.get() {
2760 elem.seek_end();
2761 }
2762 },
2763 }
2764 }
2765
2766 fn enter_realm(&self) -> JSAutoRealm {
2767 match self {
2768 &MediaElementMicrotask::ResourceSelection { ref elem, .. } |
2769 &MediaElementMicrotask::PauseIfNotInDocument { ref elem } |
2770 &MediaElementMicrotask::Seeked { ref elem, .. } => enter_realm(&**elem),
2771 }
2772 }
2773}
2774
2775enum Resource {
2776 Object,
2777 Url(ServoUrl),
2778}
2779
2780#[derive(Debug, MallocSizeOf, PartialEq)]
2781enum DataBuffer {
2782 Payload(Vec<u8>),
2783 EndOfStream,
2784}
2785
2786#[derive(MallocSizeOf)]
2787struct BufferedDataSource {
2788 locked: Cell<bool>,
2793 buffers: VecDeque<DataBuffer>,
2795}
2796
2797impl BufferedDataSource {
2798 fn new() -> BufferedDataSource {
2799 BufferedDataSource {
2800 locked: Cell::new(true),
2801 buffers: VecDeque::default(),
2802 }
2803 }
2804
2805 fn set_locked(&self, locked: bool) {
2806 self.locked.set(locked)
2807 }
2808
2809 fn add_buffer_to_queue(&mut self, buffer: DataBuffer) {
2810 debug_assert_ne!(
2811 self.buffers.back(),
2812 Some(&DataBuffer::EndOfStream),
2813 "The media backend not expects any further data after end of stream"
2814 );
2815
2816 self.buffers.push_back(buffer);
2817 }
2818
2819 fn process_into_player_from_queue(
2820 &mut self,
2821 player: &Arc<Mutex<dyn Player>>,
2822 ) -> Result<(), PlayerError> {
2823 if self.locked.get() {
2825 return Ok(());
2826 }
2827
2828 while let Some(buffer) = self.buffers.pop_front() {
2829 match buffer {
2830 DataBuffer::Payload(payload) => {
2831 if let Err(e) = player.lock().unwrap().push_data(payload) {
2832 warn!("Could not push input data to player {:?}", e);
2833 return Err(e);
2834 }
2835 },
2836 DataBuffer::EndOfStream => {
2837 if let Err(e) = player.lock().unwrap().end_of_stream() {
2838 warn!("Could not signal EOS to player {:?}", e);
2839 return Err(e);
2840 }
2841 },
2842 }
2843 }
2844
2845 Ok(())
2846 }
2847
2848 fn reset(&mut self) {
2849 self.locked.set(true);
2850 self.buffers.clear();
2851 }
2852}
2853
2854#[derive(Debug, MallocSizeOf, PartialEq)]
2856enum CancelReason {
2857 Backoff,
2859 Error,
2861 Overridden,
2863}
2864
2865#[derive(MallocSizeOf)]
2866pub(crate) struct HTMLMediaElementFetchContext {
2867 request_id: RequestId,
2869 cancel_reason: Option<CancelReason>,
2871 is_seekable: bool,
2873 origin_clean: bool,
2875 data_source: DomRefCell<BufferedDataSource>,
2877 fetch_canceller: FetchCanceller,
2880}
2881
2882impl HTMLMediaElementFetchContext {
2883 fn new(
2884 request_id: RequestId,
2885 core_resource_thread: CoreResourceThread,
2886 ) -> HTMLMediaElementFetchContext {
2887 HTMLMediaElementFetchContext {
2888 request_id,
2889 cancel_reason: None,
2890 is_seekable: false,
2891 origin_clean: true,
2892 data_source: DomRefCell::new(BufferedDataSource::new()),
2893 fetch_canceller: FetchCanceller::new(request_id, core_resource_thread.clone()),
2894 }
2895 }
2896
2897 fn request_id(&self) -> RequestId {
2898 self.request_id
2899 }
2900
2901 fn is_seekable(&self) -> bool {
2902 self.is_seekable
2903 }
2904
2905 fn set_seekable(&mut self, seekable: bool) {
2906 self.is_seekable = seekable;
2907 }
2908
2909 pub(crate) fn origin_is_clean(&self) -> bool {
2910 self.origin_clean
2911 }
2912
2913 fn set_origin_unclean(&mut self) {
2914 self.origin_clean = false;
2915 }
2916
2917 fn data_source(&self) -> &DomRefCell<BufferedDataSource> {
2918 &self.data_source
2919 }
2920
2921 fn cancel(&mut self, reason: CancelReason) {
2922 if self.cancel_reason.is_some() {
2923 return;
2924 }
2925 self.cancel_reason = Some(reason);
2926 self.data_source.borrow_mut().reset();
2927 self.fetch_canceller.cancel();
2928 }
2929
2930 fn cancel_reason(&self) -> &Option<CancelReason> {
2931 &self.cancel_reason
2932 }
2933}
2934
2935struct HTMLMediaElementFetchListener {
2936 elem: Trusted<HTMLMediaElement>,
2938 metadata: Option<Metadata>,
2940 generation_id: u32,
2942 request_id: RequestId,
2944 next_progress_event: Instant,
2946 resource_timing: ResourceFetchTiming,
2948 url: ServoUrl,
2950 expected_content_length: Option<u64>,
2952 fetched_content_length: u64,
2954 content_length_to_discard: u64,
2958}
2959
2960impl FetchResponseListener for HTMLMediaElementFetchListener {
2962 fn process_request_body(&mut self, _: RequestId) {}
2963
2964 fn process_request_eof(&mut self, _: RequestId) {}
2965
2966 fn process_response(&mut self, _: RequestId, metadata: Result<FetchMetadata, NetworkError>) {
2967 let elem = self.elem.root();
2968
2969 if let Ok(FetchMetadata::Filtered {
2970 filtered: FilteredMetadata::Opaque | FilteredMetadata::OpaqueRedirect(_),
2971 ..
2972 }) = metadata
2973 {
2974 if let Some(ref mut current_fetch_context) = *elem.current_fetch_context.borrow_mut() {
2975 current_fetch_context.set_origin_unclean();
2976 }
2977 }
2978
2979 self.metadata = metadata.ok().map(|m| match m {
2980 FetchMetadata::Unfiltered(m) => m,
2981 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
2982 });
2983
2984 if let Some(metadata) = self.metadata.as_ref() {
2985 if let Some(headers) = metadata.headers.as_ref() {
2986 let content_length =
2989 if let Some(content_range) = headers.typed_get::<ContentRange>() {
2990 content_range.bytes_len()
2991 } else {
2992 headers
2993 .typed_get::<ContentLength>()
2994 .map(|content_length| content_length.0)
2995 };
2996
2997 if content_length != self.expected_content_length {
2999 if let Some(content_length) = content_length {
3000 self.expected_content_length = Some(content_length);
3001 }
3002 }
3003 }
3004 }
3005
3006 if let Err(e) = elem
3008 .player
3009 .borrow()
3010 .as_ref()
3011 .unwrap()
3012 .lock()
3013 .unwrap()
3014 .set_input_size(self.expected_content_length.unwrap_or_default())
3015 {
3016 warn!("Could not set player input size {:?}", e);
3017 }
3018
3019 let (status_is_ok, is_seekable) = self.metadata.as_ref().map_or((true, false), |s| {
3020 let status = &s.status;
3021 (
3022 status.is_success(),
3023 *status == StatusCode::PARTIAL_CONTENT ||
3024 *status == StatusCode::RANGE_NOT_SATISFIABLE,
3025 )
3026 });
3027
3028 if is_seekable {
3029 if let Some(ref mut current_fetch_context) = *elem.current_fetch_context.borrow_mut() {
3031 current_fetch_context.set_seekable(true);
3032 }
3033 }
3034
3035 if !status_is_ok {
3037 if let Some(ref mut current_fetch_context) = *elem.current_fetch_context.borrow_mut() {
3040 current_fetch_context.cancel(CancelReason::Error);
3041 }
3042 elem.queue_dedicated_media_source_failure_steps();
3043 }
3044 }
3045
3046 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
3047 let elem = self.elem.root();
3048
3049 self.fetched_content_length += chunk.len() as u64;
3050
3051 if let Some(ref mut current_fetch_context) = *elem.current_fetch_context.borrow_mut() {
3053 if current_fetch_context.cancel_reason().is_some() {
3054 return;
3055 }
3056
3057 let payload = if !current_fetch_context.is_seekable() &&
3060 self.content_length_to_discard != 0
3061 {
3062 if chunk.len() as u64 > self.content_length_to_discard {
3063 let shrink_chunk = chunk[self.content_length_to_discard as usize..].to_vec();
3064 self.content_length_to_discard = 0;
3065 shrink_chunk
3066 } else {
3067 self.content_length_to_discard -= chunk.len() as u64;
3069 return;
3070 }
3071 } else {
3072 chunk
3073 };
3074
3075 if let Err(e) = {
3076 let mut data_source = current_fetch_context.data_source().borrow_mut();
3077 data_source.add_buffer_to_queue(DataBuffer::Payload(payload));
3078 data_source.process_into_player_from_queue(elem.player.borrow().as_ref().unwrap())
3079 } {
3080 if e == PlayerError::EnoughData {
3085 current_fetch_context.cancel(CancelReason::Backoff);
3086 }
3087 return;
3088 }
3089 }
3090
3091 if Instant::now() > self.next_progress_event {
3094 elem.owner_global()
3095 .task_manager()
3096 .media_element_task_source()
3097 .queue_simple_event(elem.upcast(), atom!("progress"));
3098 self.next_progress_event = Instant::now() + Duration::from_millis(350);
3099 }
3100 }
3101
3102 fn process_response_eof(
3104 &mut self,
3105 _: RequestId,
3106 status: Result<ResourceFetchTiming, NetworkError>,
3107 ) {
3108 trace!("process response eof");
3109
3110 let elem = self.elem.root();
3111
3112 if let Some(ref mut current_fetch_context) = *elem.current_fetch_context.borrow_mut() {
3115 if self.expected_content_length.is_none() && self.fetched_content_length != 0 {
3122 if let Err(e) = elem
3123 .player
3124 .borrow()
3125 .as_ref()
3126 .unwrap()
3127 .lock()
3128 .unwrap()
3129 .set_input_size(self.fetched_content_length)
3130 {
3131 warn!("Could not set player input size {:?}", e);
3132 }
3133 }
3134
3135 let mut data_source = current_fetch_context.data_source().borrow_mut();
3136
3137 data_source.add_buffer_to_queue(DataBuffer::EndOfStream);
3138 let _ =
3139 data_source.process_into_player_from_queue(elem.player.borrow().as_ref().unwrap());
3140
3141 if let Some(CancelReason::Error) = current_fetch_context.cancel_reason() {
3143 return;
3144 }
3145 }
3146
3147 if status.is_ok() && self.fetched_content_length != 0 {
3148 elem.upcast::<EventTarget>()
3149 .fire_event(atom!("progress"), CanGc::note());
3150
3151 elem.network_state.set(NetworkState::Idle);
3152
3153 elem.upcast::<EventTarget>()
3154 .fire_event(atom!("suspend"), CanGc::note());
3155 }
3156 else if elem.ready_state.get() != ReadyState::HaveNothing {
3158 if elem.in_error_state() {
3161 return;
3162 }
3163
3164 if let Some(ref mut current_fetch_context) = *elem.current_fetch_context.borrow_mut() {
3166 current_fetch_context.cancel(CancelReason::Error);
3167 }
3168
3169 elem.error.set(Some(&*MediaError::new(
3171 &elem.owner_window(),
3172 MEDIA_ERR_NETWORK,
3173 CanGc::note(),
3174 )));
3175
3176 elem.network_state.set(NetworkState::Idle);
3178
3179 elem.delay_load_event(false, CanGc::note());
3181
3182 elem.upcast::<EventTarget>()
3184 .fire_event(atom!("error"), CanGc::note());
3185 } else {
3186 elem.queue_dedicated_media_source_failure_steps();
3188 }
3189 }
3190
3191 fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
3192 &mut self.resource_timing
3193 }
3194
3195 fn resource_timing(&self) -> &ResourceFetchTiming {
3196 &self.resource_timing
3197 }
3198
3199 fn submit_resource_timing(&mut self) {
3200 network_listener::submit_timing(self, CanGc::note())
3201 }
3202
3203 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
3204 let global = &self.resource_timing_global();
3205 global.report_csp_violations(violations, None, None);
3206 }
3207}
3208
3209impl ResourceTimingListener for HTMLMediaElementFetchListener {
3210 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
3211 let initiator_type = InitiatorType::LocalName(
3212 self.elem
3213 .root()
3214 .upcast::<Element>()
3215 .local_name()
3216 .to_string(),
3217 );
3218 (initiator_type, self.url.clone())
3219 }
3220
3221 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
3222 self.elem.root().owner_document().global()
3223 }
3224}
3225
3226impl PreInvoke for HTMLMediaElementFetchListener {
3227 fn should_invoke(&self) -> bool {
3228 let elem = self.elem.root();
3229
3230 if elem.generation_id.get() != self.generation_id || elem.player.borrow().is_none() {
3231 return false;
3232 }
3233
3234 elem.current_fetch_context
3236 .borrow()
3237 .as_ref()
3238 .is_some_and(|context| context.request_id() == self.request_id)
3239 }
3240}
3241
3242impl HTMLMediaElementFetchListener {
3243 fn new(elem: &HTMLMediaElement, request_id: RequestId, url: ServoUrl, offset: u64) -> Self {
3244 Self {
3245 elem: Trusted::new(elem),
3246 metadata: None,
3247 generation_id: elem.generation_id.get(),
3248 request_id,
3249 next_progress_event: Instant::now() + Duration::from_millis(350),
3250 resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
3251 url,
3252 expected_content_length: None,
3253 fetched_content_length: 0,
3254 content_length_to_discard: offset,
3255 }
3256 }
3257}