Add more specification step comments and update to the latest specification.

This commit is contained in:
Josh Matthews 2016-04-27 20:16:24 -04:00
parent 99f6fb34fc
commit f16c05423c
2 changed files with 136 additions and 47 deletions

View file

@ -10,6 +10,7 @@ use dom::bindings::codegen::Bindings::HTMLMediaElementBinding::CanPlayTypeResult
use dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaElementConstants::*; use dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaElementConstants::*;
use dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaElementMethods; use dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaElementMethods;
use dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConstants::*; use dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConstants::*;
use dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods;
use dom::bindings::global::GlobalRef; use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable; use dom::bindings::inheritance::Castable;
use dom::bindings::js::{Root, MutNullableHeap, JS}; use dom::bindings::js::{Root, MutNullableHeap, JS};
@ -31,8 +32,8 @@ use script_thread::{Runnable, ScriptThread};
use std::cell::Cell; use std::cell::Cell;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use string_cache::Atom; use string_cache::Atom;
use task_source::dom_manipulation::DOMManipulationTask;
use task_source::TaskSource; use task_source::TaskSource;
use task_source::dom_manipulation::DOMManipulationTask;
use time::{self, Timespec, Duration}; use time::{self, Timespec, Duration};
use url::Url; use url::Url;
use util::str::DOMString; use util::str::DOMString;
@ -56,7 +57,6 @@ struct HTMLMediaElementContext {
ignore_response: bool, ignore_response: bool,
} }
// https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list
impl AsyncResponseListener for HTMLMediaElementContext { impl AsyncResponseListener 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 headers_available(&mut self, metadata: Result<Metadata, NetworkError>) { fn headers_available(&mut self, metadata: Result<Metadata, NetworkError>) {
@ -87,14 +87,20 @@ impl AsyncResponseListener for HTMLMediaElementContext {
let elem = self.elem.root(); let elem = self.elem.root();
// https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list
// => "Once enough of the media data has been fetched to determine the duration..."
if !self.have_metadata { if !self.have_metadata {
//TODO: actually check if the payload contains the full metadata //TODO: actually check if the payload contains the full metadata
// Step 6
elem.change_ready_state(HAVE_METADATA); elem.change_ready_state(HAVE_METADATA);
self.have_metadata = true; self.have_metadata = true;
} else { } else {
elem.change_ready_state(HAVE_CURRENT_DATA); elem.change_ready_state(HAVE_CURRENT_DATA);
} }
// https://html.spec.whatwg.org/multipage/#concept-media-load-resource step 4,
// => "If mode is remote" step 2
if time::get_time() > self.next_progress_event { if time::get_time() > self.next_progress_event {
elem.queue_fire_simple_event("progress"); elem.queue_fire_simple_event("progress");
self.next_progress_event = time::get_time() + Duration::milliseconds(350); self.next_progress_event = time::get_time() + Duration::milliseconds(350);
@ -105,6 +111,7 @@ impl AsyncResponseListener for HTMLMediaElementContext {
fn response_complete(&mut self, status: Result<(), NetworkError>) { fn response_complete(&mut self, status: Result<(), NetworkError>) {
let elem = self.elem.root(); let elem = self.elem.root();
// => "Once the entire media resource has been fetched..."
if status.is_ok() { if status.is_ok() {
elem.change_ready_state(HAVE_ENOUGH_DATA); elem.change_ready_state(HAVE_ENOUGH_DATA);
@ -113,16 +120,23 @@ impl AsyncResponseListener for HTMLMediaElementContext {
elem.network_state.set(NETWORK_IDLE); elem.network_state.set(NETWORK_IDLE);
elem.fire_simple_event("suspend"); elem.fire_simple_event("suspend");
} else if elem.ready_state.get() != HAVE_NOTHING { }
// => "If the connection is interrupted after some media data has been received..."
else if elem.ready_state.get() != HAVE_NOTHING {
// Step 2
elem.error.set(Some(&*MediaError::new(&*window_from_node(&*elem), elem.error.set(Some(&*MediaError::new(&*window_from_node(&*elem),
MEDIA_ERR_NETWORK))); MEDIA_ERR_NETWORK)));
// Step 3
elem.network_state.set(NETWORK_IDLE); elem.network_state.set(NETWORK_IDLE);
// TODO: update delay load flag // TODO: Step 4 - update delay load flag
// Step 5
elem.fire_simple_event("error"); elem.fire_simple_event("error");
} else { }
// => "If the media data cannot be fetched at all..."
else {
elem.queue_dedicated_media_source_failure_steps(); elem.queue_dedicated_media_source_failure_steps();
} }
@ -189,7 +203,7 @@ impl HTMLMediaElement {
&self.htmlelement &self.htmlelement
} }
// https://html.spec.whatwg.org/multipage/#playing-the-media-resource:internal-pause-steps // https://html.spec.whatwg.org/multipage/#internal-pause-steps
fn internal_pause_steps(&self) { fn internal_pause_steps(&self) {
// Step 1 // Step 1
self.autoplaying.set(false); self.autoplaying.set(false);
@ -200,17 +214,63 @@ impl HTMLMediaElement {
self.paused.set(true); self.paused.set(true);
// 2.2 // 2.2
self.queue_fire_simple_event("timeupdate"); self.queue_internal_pause_steps_task();
// 2.3 // TODO 2.3 (official playback position)
self.queue_fire_simple_event("pause");
// TODO 2.4 (official playback position)
} }
// TODO step 3 (media controller) // TODO step 3 (media controller)
} }
// https://html.spec.whatwg.org/multipage/#notify-about-playing
fn notify_about_playing(&self) {
// Step 1
self.fire_simple_event("playing");
// TODO Step 2
}
fn queue_notify_about_playing(&self) {
struct Task {
elem: Trusted<HTMLMediaElement>,
}
impl Runnable for Task {
fn handler(self: Box<Task>) {
self.elem.root().notify_about_playing();
}
}
let task = Task {
elem: Trusted::new(self),
};
let win = window_from_node(self);
let _ = win.dom_manipulation_task_source().queue(DOMManipulationTask::MediaTask(box task));
}
// https://html.spec.whatwg.org/multipage/#internal-pause-steps step 2.2
fn queue_internal_pause_steps_task(&self) {
struct Task {
elem: Trusted<HTMLMediaElement>,
}
impl Runnable for Task {
fn handler(self: Box<Task>) {
let elem = self.elem.root();
// 2.2.1
elem.fire_simple_event("timeupdate");
// 2.2.2
elem.fire_simple_event("pause");
// TODO 2.2.3
}
}
let task = Task {
elem: Trusted::new(self),
};
let win = window_from_node(self);
let _ = win.dom_manipulation_task_source().queue(DOMManipulationTask::MediaTask(box task));
}
fn queue_fire_simple_event(&self, type_: &'static str) { fn queue_fire_simple_event(&self, type_: &'static str) {
let win = window_from_node(self); let win = window_from_node(self);
let task = FireSimpleEventTask::new(self, type_); let task = FireSimpleEventTask::new(self, type_);
@ -235,6 +295,7 @@ impl HTMLMediaElement {
return; return;
} }
// Step 1
match (old_ready_state, ready_state) { match (old_ready_state, ready_state) {
// previous ready state was HAVE_NOTHING, and the new ready state is // previous ready state was HAVE_NOTHING, and the new ready state is
// HAVE_METADATA // HAVE_METADATA
@ -267,6 +328,7 @@ impl HTMLMediaElement {
_ => (), _ => (),
} }
// Step 1
// If the new ready state is HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA, // If the new ready state is HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA,
// then the relevant steps below must then be run also. // then the relevant steps below must then be run also.
match (old_ready_state, ready_state) { match (old_ready_state, ready_state) {
@ -277,8 +339,9 @@ impl HTMLMediaElement {
(HAVE_NOTHING, HAVE_FUTURE_DATA) => { (HAVE_NOTHING, HAVE_FUTURE_DATA) => {
self.queue_fire_simple_event("canplay"); self.queue_fire_simple_event("canplay");
// TODO: check paused state if !self.Paused() {
self.queue_fire_simple_event("playing"); self.queue_notify_about_playing();
}
} }
// new ready state is HAVE_ENOUGH_DATA // new ready state is HAVE_ENOUGH_DATA
@ -287,17 +350,23 @@ impl HTMLMediaElement {
self.queue_fire_simple_event("canplay"); self.queue_fire_simple_event("canplay");
if !self.Paused() { if !self.Paused() {
self.queue_fire_simple_event("playing"); self.queue_notify_about_playing();
} }
} }
//TODO: check sandboxed automatic features browsing context flag
if self.autoplaying.get() && if self.autoplaying.get() &&
self.Paused() && self.Paused() &&
self.Autoplay() { self.Autoplay() {
// Step 1
self.paused.set(false); self.paused.set(false);
// TODO: show poster // TODO step 2: show poster
// Step 3
self.queue_fire_simple_event("play"); self.queue_fire_simple_event("play");
self.queue_fire_simple_event("playing"); // Step 4
self.queue_notify_about_playing();
// Step 5
self.autoplaying.set(false);
} }
self.queue_fire_simple_event("canplaythrough"); self.queue_fire_simple_event("canplaythrough");
@ -305,6 +374,8 @@ impl HTMLMediaElement {
_ => (), _ => (),
} }
// TODO Step 2: media controller
} }
// https://html.spec.whatwg.org/multipage/#concept-media-load-algorithm // https://html.spec.whatwg.org/multipage/#concept-media-load-algorithm
@ -447,7 +518,8 @@ impl HTMLMediaElement {
// Step 5 // Step 5
self.fire_simple_event("error"); self.fire_simple_event("error");
// TODO step 6 (delay load event) // TODO step 6 (resolve pending play promises)
// TODO step 7 (delay load event)
} }
// https://html.spec.whatwg.org/multipage/#media-element-load-algorithm // https://html.spec.whatwg.org/multipage/#media-element-load-algorithm
@ -458,15 +530,12 @@ impl HTMLMediaElement {
// Step 2 // Step 2
self.generation_id.set(self.generation_id.get() + 1); self.generation_id.set(self.generation_id.get() + 1);
// TODO reject pending play promises
// Step 3 // Step 3
let network_state = self.NetworkState(); let network_state = self.NetworkState();
match network_state { if network_state == NETWORK_LOADING || network_state == NETWORK_IDLE {
NETWORK_LOADING | self.queue_fire_simple_event("abort");
NETWORK_IDLE => {
self.queue_fire_simple_event("abort");
}
_ => (),
} }
// Step 4 // Step 4
@ -476,18 +545,22 @@ impl HTMLMediaElement {
// TODO 4.2 (abort in-progress fetch) // TODO 4.2 (abort in-progress fetch)
// TODO 4.3 (forget resource tracks) // TODO 4.3 (detach media provider object)
// TODO 4.4 (forget resource tracks)
// 4.4 // 4.5
self.change_ready_state(HAVE_NOTHING); if self.ready_state.get() != HAVE_NOTHING {
self.change_ready_state(HAVE_NOTHING);
}
// 4.6
if !self.Paused() { if !self.Paused() {
self.paused.set(true); self.paused.set(true);
} }
// TODO 4.6 (seeking) // TODO 4.7 (seeking)
// TODO 4.7 (playback position) // TODO 4.8 (playback position)
// TODO 4.8 (timeline offset) // TODO 4.9 (timeline offset)
// TODO 4.9 (duration) // TODO 4.10 (duration)
} }
// TODO step 5 (playback rate) // TODO step 5 (playback rate)
@ -552,40 +625,57 @@ impl HTMLMediaElementMethods for HTMLMediaElement {
// https://html.spec.whatwg.org/multipage/#dom-media-play // https://html.spec.whatwg.org/multipage/#dom-media-play
fn Play(&self) { fn Play(&self) {
// Step 1 // TODO step 1
// Step 2
if self.error.get().map_or(false, |e| e.Code() == MEDIA_ERR_SRC_NOT_SUPPORTED) {
// TODO return rejected promise
return;
}
// TODO step 3
// Step 4
if self.network_state.get() == NETWORK_EMPTY { if self.network_state.get() == NETWORK_EMPTY {
self.invoke_resource_selection_algorithm(); self.invoke_resource_selection_algorithm();
} }
// TODO step 2 (seek backwards) // TODO step 5 (seek backwards)
// TODO step 3 (media controller) // TODO step 6 (media controller)
// Step 4 let state = self.ready_state.get();
// Step 7
if self.Paused() { if self.Paused() {
// 4.1 // 7.1
self.paused.set(false); self.paused.set(false);
// TODO 4.2 (show poster) // TODO 7.2 (show poster)
// 4.3 // 7.3
self.queue_fire_simple_event("play"); self.queue_fire_simple_event("play");
// 4.4 // 7.4
let state = self.ready_state.get();
if state == HAVE_NOTHING || if state == HAVE_NOTHING ||
state == HAVE_METADATA || state == HAVE_METADATA ||
state == HAVE_CURRENT_DATA { state == HAVE_CURRENT_DATA {
self.queue_fire_simple_event("waiting"); self.queue_fire_simple_event("waiting");
} else { } else {
self.queue_fire_simple_event("playing"); self.queue_notify_about_playing();
} }
// 4.5
self.autoplaying.set(false);
// TODO 4.6 (media controller)
} }
// Step 8
else if state == HAVE_FUTURE_DATA || state == HAVE_ENOUGH_DATA {
// TODO resolve pending play promises
}
// Step 9
self.autoplaying.set(false);
// TODO step 10 (media controller)
// TODO return promise
} }
// https://html.spec.whatwg.org/multipage/#dom-media-pause // https://html.spec.whatwg.org/multipage/#dom-media-pause
@ -623,7 +713,7 @@ impl VirtualMethods for HTMLMediaElement {
}; };
} }
// https://html.spec.whatwg.org/multipage/#playing-the-media-resource:media-element-75 // https://html.spec.whatwg.org/multipage/#playing-the-media-resource:remove-an-element-from-a-document
fn unbind_from_tree(&self, context: &UnbindContext) { fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context); self.super_type().unwrap().unbind_from_tree(context);

View file

@ -1,4 +1,3 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */