mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
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:
parent
da392e3524
commit
40a72f3e83
2 changed files with 56 additions and 29 deletions
|
@ -20,18 +20,18 @@ pub enum LoadType {
|
||||||
Subframe(ServoUrl),
|
Subframe(ServoUrl),
|
||||||
Stylesheet(ServoUrl),
|
Stylesheet(ServoUrl),
|
||||||
PageSource(ServoUrl),
|
PageSource(ServoUrl),
|
||||||
Media(ServoUrl),
|
Media,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LoadType {
|
impl LoadType {
|
||||||
fn url(&self) -> &ServoUrl {
|
fn url(&self) -> Option<&ServoUrl> {
|
||||||
match *self {
|
match *self {
|
||||||
LoadType::Image(ref url) |
|
LoadType::Image(ref url) |
|
||||||
LoadType::Script(ref url) |
|
LoadType::Script(ref url) |
|
||||||
LoadType::Subframe(ref url) |
|
LoadType::Subframe(ref url) |
|
||||||
LoadType::Stylesheet(ref url) |
|
LoadType::Stylesheet(ref url) |
|
||||||
LoadType::Media(ref url) |
|
LoadType::PageSource(ref url) => Some(url),
|
||||||
LoadType::PageSource(ref url) => url,
|
LoadType::Media => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ impl LoadBlocker {
|
||||||
|
|
||||||
/// Return the url associated with this load.
|
/// Return the url associated with this load.
|
||||||
pub fn url(&self) -> Option<&ServoUrl> {
|
pub fn url(&self) -> Option<&ServoUrl> {
|
||||||
self.load.as_ref().map(LoadType::url)
|
self.load.as_ref().and_then(LoadType::url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use audio_video_metadata;
|
use audio_video_metadata;
|
||||||
use document_loader::LoadType;
|
use document_loader::{LoadBlocker, LoadType};
|
||||||
use dom::attr::Attr;
|
use dom::attr::Attr;
|
||||||
use dom::bindings::cell::DOMRefCell;
|
use dom::bindings::cell::DOMRefCell;
|
||||||
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
|
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
|
||||||
|
@ -71,6 +71,8 @@ pub struct HTMLMediaElement {
|
||||||
paused: Cell<bool>,
|
paused: Cell<bool>,
|
||||||
/// https://html.spec.whatwg.org/multipage/#attr-media-autoplay
|
/// https://html.spec.whatwg.org/multipage/#attr-media-autoplay
|
||||||
autoplaying: Cell<bool>,
|
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
|
/// https://html.spec.whatwg.org/multipage/#list-of-pending-play-promises
|
||||||
#[ignore_heap_size_of = "promises are hard"]
|
#[ignore_heap_size_of = "promises are hard"]
|
||||||
pending_play_promises: DOMRefCell<Vec<Rc<Promise>>>,
|
pending_play_promises: DOMRefCell<Vec<Rc<Promise>>>,
|
||||||
|
@ -131,6 +133,7 @@ impl HTMLMediaElement {
|
||||||
paused: Cell::new(true),
|
paused: Cell::new(true),
|
||||||
// FIXME(nox): Why is this initialised to true?
|
// FIXME(nox): Why is this initialised to true?
|
||||||
autoplaying: Cell::new(true),
|
autoplaying: Cell::new(true),
|
||||||
|
delaying_the_load_event_flag: Default::default(),
|
||||||
pending_play_promises: Default::default(),
|
pending_play_promises: Default::default(),
|
||||||
in_flight_play_promises_queue: Default::default(),
|
in_flight_play_promises_queue: Default::default(),
|
||||||
video: DOMRefCell::new(None),
|
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
|
/// https://html.spec.whatwg.org/multipage/#dom-media-play
|
||||||
// FIXME(nox): Move this back to HTMLMediaElementMethods::Play once
|
// FIXME(nox): Move this back to HTMLMediaElementMethods::Play once
|
||||||
// Rc<Promise> doesn't require #[allow(unrooted_must_root)] anymore.
|
// Rc<Promise> doesn't require #[allow(unrooted_must_root)] anymore.
|
||||||
|
@ -335,10 +353,15 @@ impl HTMLMediaElement {
|
||||||
(ReadyState::HaveMetadata, new) if new >= ReadyState::HaveCurrentData => {
|
(ReadyState::HaveMetadata, new) if new >= ReadyState::HaveCurrentData => {
|
||||||
if !self.fired_loadeddata_event.get() {
|
if !self.fired_loadeddata_event.get() {
|
||||||
self.fired_loadeddata_event.set(true);
|
self.fired_loadeddata_event.set(true);
|
||||||
task_source.queue_simple_event(
|
let this = Trusted::new(self);
|
||||||
self.upcast(),
|
// FIXME(nox): Why are errors silenced here?
|
||||||
atom!("loadeddata"),
|
let _ = task_source.queue(
|
||||||
&window,
|
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.
|
// FIXME(nox): Set show poster flag to true.
|
||||||
|
|
||||||
// Step 3.
|
// Step 3.
|
||||||
// FIXME(nox): Set the delaying-the-load-event flag to true.
|
self.delay_load_event(true);
|
||||||
|
|
||||||
// Step 4.
|
// Step 4.
|
||||||
// If the resource selection mode in the synchronous section is
|
// If the resource selection mode in the synchronous section is
|
||||||
|
@ -462,6 +485,8 @@ impl HTMLMediaElement {
|
||||||
mode
|
mode
|
||||||
} else {
|
} else {
|
||||||
self.network_state.set(NetworkState::Empty);
|
self.network_state.set(NetworkState::Empty);
|
||||||
|
// https://github.com/whatwg/html/issues/3065
|
||||||
|
self.delay_load_event(false);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -549,8 +574,13 @@ impl HTMLMediaElement {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Step 4.remote.1.3.
|
// Step 4.remote.1.3.
|
||||||
// FIXME(nox): Queue a task to set the delaying-the-load-event
|
let this = Trusted::new(self);
|
||||||
// flag to false.
|
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.
|
// Steps 4.remote.1.4.
|
||||||
// FIXME(nox): Somehow we should wait for the task from previous
|
// FIXME(nox): Somehow we should wait for the task from previous
|
||||||
|
@ -571,7 +601,7 @@ impl HTMLMediaElement {
|
||||||
HTMLMediaElementTypeId::HTMLVideoElement => RequestType::Video,
|
HTMLMediaElementTypeId::HTMLVideoElement => RequestType::Video,
|
||||||
};
|
};
|
||||||
let request = RequestInit {
|
let request = RequestInit {
|
||||||
url: url.clone(),
|
url,
|
||||||
type_,
|
type_,
|
||||||
destination: Destination::Media,
|
destination: Destination::Media,
|
||||||
credentials_mode: CredentialsMode::Include,
|
credentials_mode: CredentialsMode::Include,
|
||||||
|
@ -583,7 +613,7 @@ impl HTMLMediaElement {
|
||||||
.. RequestInit::default()
|
.. 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 (action_sender, action_receiver) = ipc::channel().unwrap();
|
||||||
let window = window_from_node(self);
|
let window = window_from_node(self);
|
||||||
let listener = NetworkListener {
|
let listener = NetworkListener {
|
||||||
|
@ -594,7 +624,7 @@ impl HTMLMediaElement {
|
||||||
ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
|
ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
|
||||||
listener.notify_fetch(message.to().unwrap());
|
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 => {
|
Resource::Object => {
|
||||||
// FIXME(nox): Use the current media resource.
|
// FIXME(nox): Use the current media resource.
|
||||||
|
@ -645,7 +675,7 @@ impl HTMLMediaElement {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Step 7.
|
// Step 7.
|
||||||
// FIXME(nox): Set the delaying-the-load-event flag to false.
|
this.delay_load_event(false);
|
||||||
}),
|
}),
|
||||||
window.upcast(),
|
window.upcast(),
|
||||||
);
|
);
|
||||||
|
@ -952,8 +982,6 @@ struct HTMLMediaElementContext {
|
||||||
generation_id: u32,
|
generation_id: u32,
|
||||||
/// Time of last progress notification.
|
/// Time of last progress notification.
|
||||||
next_progress_event: Timespec,
|
next_progress_event: Timespec,
|
||||||
/// Url of resource requested.
|
|
||||||
url: ServoUrl,
|
|
||||||
/// Whether the media metadata has been completely received.
|
/// Whether the media metadata has been completely received.
|
||||||
have_metadata: bool,
|
have_metadata: bool,
|
||||||
/// True if this response is invalid and should be ignored.
|
/// 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 the media data cannot be fetched at all..."
|
||||||
if !status_is_ok {
|
if !status_is_ok {
|
||||||
// Ensure that the element doesn't receive any further notifications
|
// Ensure that the element doesn't receive any further notifications
|
||||||
// of the aborted fetch. The dedicated failure steps will be
|
// of the aborted fetch.
|
||||||
// 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.
|
|
||||||
self.ignore_response = true;
|
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
|
// https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list
|
||||||
fn process_response_eof(&mut self, status: Result<(), NetworkError>) {
|
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();
|
let elem = self.elem.root();
|
||||||
|
|
||||||
// => "If the media data can be fetched but is found by inspection to be in an unsupported
|
// => "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
|
// Step 3
|
||||||
elem.network_state.set(NetworkState::Idle);
|
elem.network_state.set(NetworkState::Idle);
|
||||||
|
|
||||||
// TODO: Step 4 - update delay load flag
|
// Step 4.
|
||||||
|
elem.delay_load_event(false);
|
||||||
|
|
||||||
// Step 5
|
// Step 5
|
||||||
elem.upcast::<EventTarget>().fire_event(atom!("error"));
|
elem.upcast::<EventTarget>().fire_event(atom!("error"));
|
||||||
|
@ -1056,9 +1087,6 @@ impl FetchResponseListener for HTMLMediaElementContext {
|
||||||
// => "If the media data cannot be fetched at all..."
|
// => "If the media data cannot be fetched at all..."
|
||||||
elem.queue_dedicated_media_source_failure_steps();
|
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 {
|
impl HTMLMediaElementContext {
|
||||||
fn new(elem: &HTMLMediaElement, url: ServoUrl) -> HTMLMediaElementContext {
|
fn new(elem: &HTMLMediaElement) -> HTMLMediaElementContext {
|
||||||
HTMLMediaElementContext {
|
HTMLMediaElementContext {
|
||||||
elem: Trusted::new(elem),
|
elem: Trusted::new(elem),
|
||||||
data: vec![],
|
data: vec![],
|
||||||
metadata: None,
|
metadata: None,
|
||||||
generation_id: elem.generation_id.get(),
|
generation_id: elem.generation_id.get(),
|
||||||
next_progress_event: time::get_time() + Duration::milliseconds(350),
|
next_progress_event: time::get_time() + Duration::milliseconds(350),
|
||||||
url: url,
|
|
||||||
have_metadata: false,
|
have_metadata: false,
|
||||||
ignore_response: false,
|
ignore_response: false,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue