Decouple media load blockers from their resource URL

A media element can delay the document's load event without having a resource URL,
and it can even block it while being inserted into a different document AFAIK.
This commit is contained in:
Anthony Ramine 2017-09-25 12:32:48 +02:00
parent da392e3524
commit 40a72f3e83
2 changed files with 56 additions and 29 deletions

View file

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use audio_video_metadata;
use document_loader::LoadType;
use document_loader::{LoadBlocker, LoadType};
use dom::attr::Attr;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
@ -71,6 +71,8 @@ pub struct HTMLMediaElement {
paused: Cell<bool>,
/// https://html.spec.whatwg.org/multipage/#attr-media-autoplay
autoplaying: Cell<bool>,
/// https://html.spec.whatwg.org/multipage/#delaying-the-load-event-flag
delaying_the_load_event_flag: DOMRefCell<Option<LoadBlocker>>,
/// https://html.spec.whatwg.org/multipage/#list-of-pending-play-promises
#[ignore_heap_size_of = "promises are hard"]
pending_play_promises: DOMRefCell<Vec<Rc<Promise>>>,
@ -131,6 +133,7 @@ impl HTMLMediaElement {
paused: Cell::new(true),
// FIXME(nox): Why is this initialised to true?
autoplaying: Cell::new(true),
delaying_the_load_event_flag: Default::default(),
pending_play_promises: Default::default(),
in_flight_play_promises_queue: Default::default(),
video: DOMRefCell::new(None),
@ -148,6 +151,21 @@ impl HTMLMediaElement {
}
}
/// Marks that element as delaying the load event or not.
///
/// Nothing happens if the element was already delaying the load event and
/// we pass true to that method again.
///
/// https://html.spec.whatwg.org/multipage/#delaying-the-load-event-flag
fn delay_load_event(&self, delay: bool) {
let mut blocker = self.delaying_the_load_event_flag.borrow_mut();
if delay && blocker.is_none() {
*blocker = Some(LoadBlocker::new(&document_from_node(self), LoadType::Media));
} else if !delay && blocker.is_some() {
LoadBlocker::terminate(&mut *blocker);
}
}
/// https://html.spec.whatwg.org/multipage/#dom-media-play
// FIXME(nox): Move this back to HTMLMediaElementMethods::Play once
// Rc<Promise> doesn't require #[allow(unrooted_must_root)] anymore.
@ -335,10 +353,15 @@ impl HTMLMediaElement {
(ReadyState::HaveMetadata, new) if new >= ReadyState::HaveCurrentData => {
if !self.fired_loadeddata_event.get() {
self.fired_loadeddata_event.set(true);
task_source.queue_simple_event(
self.upcast(),
atom!("loadeddata"),
&window,
let this = Trusted::new(self);
// FIXME(nox): Why are errors silenced here?
let _ = task_source.queue(
task!(media_reached_current_data: move || {
let this = this.root();
this.upcast::<EventTarget>().fire_event(atom!("loadeddata"));
this.delay_load_event(false);
}),
window.upcast(),
);
}
@ -411,7 +434,7 @@ impl HTMLMediaElement {
// FIXME(nox): Set show poster flag to true.
// Step 3.
// FIXME(nox): Set the delaying-the-load-event flag to true.
self.delay_load_event(true);
// Step 4.
// If the resource selection mode in the synchronous section is
@ -462,6 +485,8 @@ impl HTMLMediaElement {
mode
} else {
self.network_state.set(NetworkState::Empty);
// https://github.com/whatwg/html/issues/3065
self.delay_load_event(false);
return;
};
@ -549,8 +574,13 @@ impl HTMLMediaElement {
);
// Step 4.remote.1.3.
// FIXME(nox): Queue a task to set the delaying-the-load-event
// flag to false.
let this = Trusted::new(self);
window.dom_manipulation_task_source().queue(
task!(set_media_delay_load_event_flag_to_false: move || {
this.root().delay_load_event(false);
}),
window.upcast(),
).unwrap();
// Steps 4.remote.1.4.
// FIXME(nox): Somehow we should wait for the task from previous
@ -571,7 +601,7 @@ impl HTMLMediaElement {
HTMLMediaElementTypeId::HTMLVideoElement => RequestType::Video,
};
let request = RequestInit {
url: url.clone(),
url,
type_,
destination: Destination::Media,
credentials_mode: CredentialsMode::Include,
@ -583,7 +613,7 @@ impl HTMLMediaElement {
.. RequestInit::default()
};
let context = Arc::new(Mutex::new(HTMLMediaElementContext::new(self, url.clone())));
let context = Arc::new(Mutex::new(HTMLMediaElementContext::new(self)));
let (action_sender, action_receiver) = ipc::channel().unwrap();
let window = window_from_node(self);
let listener = NetworkListener {
@ -594,7 +624,7 @@ impl HTMLMediaElement {
ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
listener.notify_fetch(message.to().unwrap());
});
document.fetch_async(LoadType::Media(url), request, action_sender);
document.loader().fetch_async_background(request, action_sender);
},
Resource::Object => {
// FIXME(nox): Use the current media resource.
@ -645,7 +675,7 @@ impl HTMLMediaElement {
});
// Step 7.
// FIXME(nox): Set the delaying-the-load-event flag to false.
this.delay_load_event(false);
}),
window.upcast(),
);
@ -952,8 +982,6 @@ struct HTMLMediaElementContext {
generation_id: u32,
/// Time of last progress notification.
next_progress_event: Timespec,
/// Url of resource requested.
url: ServoUrl,
/// Whether the media metadata has been completely received.
have_metadata: bool,
/// True if this response is invalid and should be ignored.
@ -981,11 +1009,9 @@ impl FetchResponseListener for HTMLMediaElementContext {
// => "If the media data cannot be fetched at all..."
if !status_is_ok {
// Ensure that the element doesn't receive any further notifications
// of the aborted fetch. The dedicated failure steps will be
// executed when response_complete runs.
// FIXME(nox): According to the spec, we shouldn't wait to receive
// the whole response before running the dedicated failure steps.
// of the aborted fetch.
self.ignore_response = true;
self.elem.root().queue_dedicated_media_source_failure_steps();
}
}
@ -1022,6 +1048,10 @@ impl FetchResponseListener for HTMLMediaElementContext {
// https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list
fn process_response_eof(&mut self, status: Result<(), NetworkError>) {
if self.ignore_response {
// An error was received previously, skip processing the payload.
return;
}
let elem = self.elem.root();
// => "If the media data can be fetched but is found by inspection to be in an unsupported
@ -1048,7 +1078,8 @@ impl FetchResponseListener for HTMLMediaElementContext {
// Step 3
elem.network_state.set(NetworkState::Idle);
// TODO: Step 4 - update delay load flag
// Step 4.
elem.delay_load_event(false);
// Step 5
elem.upcast::<EventTarget>().fire_event(atom!("error"));
@ -1056,9 +1087,6 @@ impl FetchResponseListener for HTMLMediaElementContext {
// => "If the media data cannot be fetched at all..."
elem.queue_dedicated_media_source_failure_steps();
}
let document = document_from_node(&*elem);
document.finish_load(LoadType::Media(self.url.clone()));
}
}
@ -1070,14 +1098,13 @@ impl PreInvoke for HTMLMediaElementContext {
}
impl HTMLMediaElementContext {
fn new(elem: &HTMLMediaElement, url: ServoUrl) -> HTMLMediaElementContext {
fn new(elem: &HTMLMediaElement) -> HTMLMediaElementContext {
HTMLMediaElementContext {
elem: Trusted::new(elem),
data: vec![],
metadata: None,
generation_id: elem.generation_id.get(),
next_progress_event: time::get_time() + Duration::milliseconds(350),
url: url,
have_metadata: false,
ignore_response: false,
}