Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement missing ready states transition #31706

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions components/script/dom/audiotracklist.rs
Expand Up @@ -107,6 +107,12 @@ impl AudioTrackList {
track.add_track_list(self);
}

pub fn remove(&self, index: usize) -> DomRoot<AudioTrack> {
let track: DomRoot<AudioTrack> = DomRoot::from_ref(&self.tracks.borrow_mut().remove(index));
track.remove_track_list();
track
}

pub fn clear(&self) {
self.tracks
.borrow()
Expand Down
99 changes: 92 additions & 7 deletions components/script/dom/htmlmediaelement.rs
Expand Up @@ -611,6 +611,11 @@ impl HTMLMediaElement {
let old_ready_state = self.ready_state.get();
self.ready_state.set(ready_state);

debug!(
"Changing ready state from {:?} to {:?}",
old_ready_state, ready_state
);

if self.network_state.get() == NetworkState::Empty {
return;
}
Expand Down Expand Up @@ -1488,6 +1493,7 @@ impl HTMLMediaElement {
}

fn handle_player_event(&self, event: &PlayerEvent) {
debug!("Handling player event {:?}", event);
match *event {
PlayerEvent::EndOfStream => {
// https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list
Expand Down Expand Up @@ -1538,6 +1544,39 @@ impl HTMLMediaElement {
}),
window.upcast(),
);

// If at any time the user agent learns that an audio or video
// track has ended..

if let Some(idx) = self.VideoTracks().selected_index() {
// 1. Remove the track..
let video_track = self.VideoTracks().remove(idx);
// 2. Fire an event named removetrack
let event = TrackEvent::new(
&self.global(),
atom!("removetrack"),
false,
false,
&Some(VideoTrackOrAudioTrackOrTextTrack::VideoTrack(
video_track,
)),
);
event.upcast::<Event>().fire(self.upcast::<EventTarget>());
}

if let Some(idx) = self.AudioTracks().enabled_index() {
let audio_track = self.AudioTracks().remove(idx);
let event = TrackEvent::new(
&self.global(),
atom!("removetrack"),
false,
false,
&Some(VideoTrackOrAudioTrackOrTextTrack::AudioTrack(
audio_track,
)),
);
event.upcast::<Event>().fire(self.upcast::<EventTarget>());
}
}
},

Expand All @@ -1549,6 +1588,39 @@ impl HTMLMediaElement {
.task_manager()
.media_element_task_source()
.queue_simple_event(self.upcast(), atom!("ended"), &window);

// If at any time the user agent learns that an audio or video
// track has ended..

if let Some(idx) = self.VideoTracks().selected_index() {
// 1. Remove the track..
let video_track = self.VideoTracks().remove(idx);
// 2. Fire an event named removetrack
let event = TrackEvent::new(
&self.global(),
atom!("removetrack"),
false,
false,
&Some(VideoTrackOrAudioTrackOrTextTrack::VideoTrack(
video_track,
)),
);
event.upcast::<Event>().fire(self.upcast::<EventTarget>());
}

if let Some(idx) = self.AudioTracks().enabled_index() {
let audio_track = self.AudioTracks().remove(idx);
let event = TrackEvent::new(
&self.global(),
atom!("removetrack"),
false,
false,
&Some(VideoTrackOrAudioTrackOrTextTrack::AudioTrack(
audio_track,
)),
);
event.upcast::<Event>().fire(self.upcast::<EventTarget>());
}
}
},
}
Expand Down Expand Up @@ -1857,6 +1929,9 @@ impl HTMLMediaElement {
}
},
PlaybackState::Playing => {
if self.ready_state.get() == ReadyState::HaveMetadata {
self.change_ready_state(ReadyState::HaveEnoughData);
}
media_session_playback_state = MediaSessionPlaybackState::Playing;
},
PlaybackState::Buffering => {
Expand Down Expand Up @@ -2663,6 +2738,8 @@ impl FetchResponseListener for HTMLMediaElementFetchListener {
fn process_request_eof(&mut self) {}

fn process_response(&mut self, metadata: Result<FetchMetadata, NetworkError>) {
debug!("process_response({:?})", metadata);

let elem = self.elem.root();

if elem.generation_id.get() != self.generation_id || elem.player.borrow().is_none() {
Expand Down Expand Up @@ -2736,6 +2813,7 @@ impl FetchResponseListener for HTMLMediaElementFetchListener {
}

fn process_response_chunk(&mut self, payload: Vec<u8>) {
debug!("process_response_chunk()");
let elem = self.elem.root();
// If an error was received previously or if we triggered a new fetch request,
// we skip processing the payload.
Expand Down Expand Up @@ -2795,6 +2873,7 @@ impl FetchResponseListener for HTMLMediaElementFetchListener {

// https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list
fn process_response_eof(&mut self, status: Result<ResourceFetchTiming, NetworkError>) {
debug!("process_response_eof({:?})", status);
if let Some(seek_lock) = self.seek_lock.take() {
seek_lock.unlock(/* successful seek */ false);
}
Expand Down Expand Up @@ -2827,14 +2906,8 @@ impl FetchResponseListener for HTMLMediaElementFetchListener {
}
}

// => "Once the entire media resource has been fetched..."
if status.is_ok() && self.latest_fetched_content != 0 {
if elem.ready_state.get() == ReadyState::HaveNothing {
// Make sure that we don't skip the HaveMetadata and HaveCurrentData
// states for short streams.
elem.change_ready_state(ReadyState::HaveMetadata);
}
elem.change_ready_state(ReadyState::HaveEnoughData);

elem.upcast::<EventTarget>().fire_event(atom!("progress"));

elem.network_state.set(NetworkState::Idle);
Expand Down Expand Up @@ -2868,6 +2941,18 @@ impl FetchResponseListener for HTMLMediaElementFetchListener {
// => "If the media data cannot be fetched at all..."
elem.queue_dedicated_media_source_failure_steps();
}

if let Err(e) = elem
.player
.borrow()
.as_ref()
.unwrap()
.lock()
.unwrap()
.end_of_stream()
{
warn!("Could not signal EOS to player {:?}", e);
};
}

fn resource_timing_mut(&mut self) -> &mut ResourceFetchTiming {
Expand Down
6 changes: 6 additions & 0 deletions components/script/dom/videotracklist.rs
Expand Up @@ -115,6 +115,12 @@ impl VideoTrackList {
track.add_track_list(self);
}

pub fn remove(&self, idx: usize) -> DomRoot<VideoTrack> {
let track: DomRoot<VideoTrack> = DomRoot::from_ref(&self.tracks.borrow_mut().remove(idx));
track.remove_track_list();
track
}

pub fn clear(&self) {
self.tracks
.borrow()
Expand Down
1 change: 1 addition & 0 deletions components/script/task_source/media_element.rs
Expand Up @@ -51,6 +51,7 @@ impl TaskSource for MediaElementTaskSource {
impl MediaElementTaskSource {
pub fn queue_simple_event(&self, target: &EventTarget, name: Atom, window: &Window) {
let target = Trusted::new(target);
debug!("queuing event: {}", name);
let _ = self.queue(SimpleEventTask { target, name }, window.upcast());
}
}
@@ -1,4 +1,5 @@
[track-cue-mutable-fragment.html]
expected: TIMEOUT
[Cue fragment is mutable]
expected: FAIL
expected: TIMEOUT

@@ -1,4 +1,5 @@
[track-mode-not-changed-by-new-track.html]
expected: TIMEOUT
[A track appended after the initial track configuration does not change other tracks]
expected: FAIL
expected: TIMEOUT

@@ -1,4 +1,5 @@
[track-cue-mutable-fragment.html]
expected: TIMEOUT
[Cue fragment is mutable]
expected: FAIL
expected: TIMEOUT

@@ -1,4 +1,5 @@
[track-mode-not-changed-by-new-track.html]
expected: TIMEOUT
[A track appended after the initial track configuration does not change other tracks]
expected: FAIL
expected: TIMEOUT

This file was deleted.