--- a/servo/components/script/dom/htmlmediaelement.rs
+++ b/servo/components/script/dom/htmlmediaelement.rs
@@ -3,17 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use audio_video_metadata;
use document_loader::LoadType;
use dom::attr::Attr;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
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::MediaErrorBinding::MediaErrorConstants::*;
use dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{MutNullableJS, Root};
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::DomObject;
use dom::bindings::str::DOMString;
@@ -40,41 +40,62 @@ use servo_url::ServoUrl;
use std::cell::Cell;
use std::sync::{Arc, Mutex};
use task_source::TaskSource;
use time::{self, Timespec, Duration};
#[dom_struct]
pub struct HTMLMediaElement {
htmlelement: HTMLElement,
- /// https://html.spec.whatwg.org/multipage/#dom-media-networkstate-2
+ /// https://html.spec.whatwg.org/multipage/#dom-media-networkstate
// FIXME(nox): Use an enum.
- network_state: Cell<u16>,
- /// https://html.spec.whatwg.org/multipage/#dom-media-readystate-2
+ network_state: Cell<NetworkState>,
+ /// https://html.spec.whatwg.org/multipage/#dom-media-readystate
// FIXME(nox): Use an enum.
- ready_state: Cell<u16>,
- /// https://html.spec.whatwg.org/multipage/#dom-media-currentsrc-2
+ ready_state: Cell<ReadyState>,
+ /// https://html.spec.whatwg.org/multipage/#dom-media-currentsrc
current_src: DOMRefCell<String>,
// FIXME(nox): Document this one, I have no idea what it is used for.
generation_id: Cell<u32>,
/// https://html.spec.whatwg.org/multipage/#fire-loadeddata
///
/// Reset to false every time the load algorithm is invoked.
fired_loadeddata_event: Cell<bool>,
- /// https://html.spec.whatwg.org/multipage/#dom-media-error-2
+ /// https://html.spec.whatwg.org/multipage/#dom-media-error
error: MutNullableJS<MediaError>,
- /// https://html.spec.whatwg.org/multipage/#dom-media-paused-2
+ /// https://html.spec.whatwg.org/multipage/#dom-media-paused
paused: Cell<bool>,
/// https://html.spec.whatwg.org/multipage/#attr-media-autoplay
autoplaying: Cell<bool>,
/// The details of the video currently related to this media element.
// FIXME(nox): Why isn't this in HTMLVideoElement?
video: DOMRefCell<Option<VideoMedia>>,
}
+/// https://html.spec.whatwg.org/multipage/#dom-media-networkstate
+#[derive(Clone, Copy, HeapSizeOf, JSTraceable, PartialEq)]
+#[repr(u8)]
+enum NetworkState {
+ Empty = HTMLMediaElementConstants::NETWORK_EMPTY as u8,
+ Idle = HTMLMediaElementConstants::NETWORK_IDLE as u8,
+ Loading = HTMLMediaElementConstants::NETWORK_LOADING as u8,
+ NoSource = HTMLMediaElementConstants::NETWORK_NO_SOURCE as u8,
+}
+
+/// https://html.spec.whatwg.org/multipage/#dom-media-readystate
+#[derive(Clone, Copy, HeapSizeOf, JSTraceable, PartialEq, PartialOrd)]
+#[repr(u8)]
+enum ReadyState {
+ HaveNothing = HTMLMediaElementConstants::HAVE_NOTHING as u8,
+ HaveMetadata = HTMLMediaElementConstants::HAVE_METADATA as u8,
+ HaveCurrentData = HTMLMediaElementConstants::HAVE_CURRENT_DATA as u8,
+ HaveFutureData = HTMLMediaElementConstants::HAVE_FUTURE_DATA as u8,
+ HaveEnoughData = HTMLMediaElementConstants::HAVE_ENOUGH_DATA as u8,
+}
+
#[derive(HeapSizeOf, JSTraceable)]
pub struct VideoMedia {
format: String,
#[ignore_heap_size_of = "defined in time"]
duration: Duration,
width: u32,
height: u32,
video: String,
@@ -84,18 +105,18 @@ pub struct VideoMedia {
impl HTMLMediaElement {
pub fn new_inherited(
tag_name: LocalName,
prefix: Option<Prefix>,
document: &Document,
) -> Self {
Self {
htmlelement: HTMLElement::new_inherited(tag_name, prefix, document),
- network_state: Cell::new(NETWORK_EMPTY),
- ready_state: Cell::new(HAVE_NOTHING),
+ network_state: Cell::new(NetworkState::Empty),
+ ready_state: Cell::new(ReadyState::HaveNothing),
current_src: DOMRefCell::new("".to_owned()),
generation_id: Cell::new(0),
fired_loadeddata_event: Cell::new(false),
error: Default::default(),
paused: Cell::new(true),
// FIXME(nox): Why is this initialised to true?
autoplaying: Cell::new(true),
video: DOMRefCell::new(None),
@@ -168,91 +189,91 @@ impl HTMLMediaElement {
// Step 2.2.
// FIXME(nox): Resolve pending play promises with promises.
}
}
}
// https://html.spec.whatwg.org/multipage/#ready-states
- fn change_ready_state(&self, ready_state: u16) {
+ fn change_ready_state(&self, ready_state: ReadyState) {
let old_ready_state = self.ready_state.get();
self.ready_state.set(ready_state);
- if self.network_state.get() == NETWORK_EMPTY {
+ if self.network_state.get() == NetworkState::Empty {
return;
}
let window = window_from_node(self);
let task_source = window.dom_manipulation_task_source();
// Step 1
match (old_ready_state, ready_state) {
- // previous ready state was HAVE_NOTHING, and the new ready state is
- // HAVE_METADATA
- (HAVE_NOTHING, HAVE_METADATA) => {
+ // Previous ready state was ReadyState::HaveNothing,
+ // and the new ready state is ReadyState::HaveMetadata.
+ (ReadyState::HaveNothing, ReadyState::HaveMetadata) => {
task_source.queue_simple_event(
self.upcast(),
atom!("loadedmetadata"),
&window,
);
}
- // previous ready state was HAVE_METADATA and the new ready state is
- // HAVE_CURRENT_DATA or greater
- (HAVE_METADATA, HAVE_CURRENT_DATA) |
- (HAVE_METADATA, HAVE_FUTURE_DATA) |
- (HAVE_METADATA, HAVE_ENOUGH_DATA) => {
+ // Previous ready state was ReadyState::HaveMetadata, and the new
+ // ready state is ReadyState::HaveCurrentData or greater.
+ (ReadyState::HaveMetadata, ReadyState::HaveCurrentData) |
+ (ReadyState::HaveMetadata, ReadyState::HaveFutureData) |
+ (ReadyState::HaveMetadata, ReadyState::HaveEnoughData) => {
if !self.fired_loadeddata_event.get() {
self.fired_loadeddata_event.set(true);
task_source.queue_simple_event(
self.upcast(),
atom!("loadeddata"),
&window,
);
}
}
- // previous ready state was HAVE_FUTURE_DATA or more, and the new ready
- // state is HAVE_CURRENT_DATA or less
- (HAVE_FUTURE_DATA, HAVE_CURRENT_DATA) |
- (HAVE_ENOUGH_DATA, HAVE_CURRENT_DATA) |
- (HAVE_FUTURE_DATA, HAVE_METADATA) |
- (HAVE_ENOUGH_DATA, HAVE_METADATA) |
- (HAVE_FUTURE_DATA, HAVE_NOTHING) |
- (HAVE_ENOUGH_DATA, HAVE_NOTHING) => {
+ // previous ready state was ReadyState::HaveFutureData or more,
+ // and the new ready state is ReadyState::HaveCurrentData or less.
+ (ReadyState::HaveFutureData, ReadyState::HaveCurrentData) |
+ (ReadyState::HaveEnoughData, ReadyState::HaveCurrentData) |
+ (ReadyState::HaveFutureData, ReadyState::HaveMetadata) |
+ (ReadyState::HaveEnoughData, ReadyState::HaveMetadata) |
+ (ReadyState::HaveFutureData, ReadyState::HaveNothing) |
+ (ReadyState::HaveEnoughData, ReadyState::HaveNothing) => {
// TODO: timeupdate event logic + waiting
}
_ => (),
}
- // Step 1
- // If the new ready state is HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA,
+ // Step 1.
+ // If the new ready state is ReadyState::HaveFutureData or ReadyState::HaveEnoughData,
// then the relevant steps below must then be run also.
match (old_ready_state, ready_state) {
- // previous ready state was HAVE_CURRENT_DATA or less, and the new ready
- // state is HAVE_FUTURE_DATA
- (HAVE_CURRENT_DATA, HAVE_FUTURE_DATA) |
- (HAVE_METADATA, HAVE_FUTURE_DATA) |
- (HAVE_NOTHING, HAVE_FUTURE_DATA) => {
+ // Previous ready state was ReadyState::HaveCurrentData or less,
+ // and the new ready state is ReadyState::HaveFutureData.
+ (ReadyState::HaveCurrentData, ReadyState::HaveFutureData) |
+ (ReadyState::HaveMetadata, ReadyState::HaveFutureData) |
+ (ReadyState::HaveNothing, ReadyState::HaveFutureData) => {
task_source.queue_simple_event(
self.upcast(),
atom!("canplay"),
&window,
);
if !self.Paused() {
self.notify_about_playing();
}
}
- // new ready state is HAVE_ENOUGH_DATA
- (_, HAVE_ENOUGH_DATA) => {
- if old_ready_state <= HAVE_CURRENT_DATA {
+ // New ready state is ReadyState::HaveEnoughData.
+ (_, ReadyState::HaveEnoughData) => {
+ if old_ready_state <= ReadyState::HaveCurrentData {
task_source.queue_simple_event(
self.upcast(),
atom!("canplay"),
&window,
);
if !self.Paused() {
self.notify_about_playing();
@@ -289,17 +310,17 @@ impl HTMLMediaElement {
}
// TODO Step 2: media controller
}
// https://html.spec.whatwg.org/multipage/#concept-media-load-algorithm
fn invoke_resource_selection_algorithm(&self) {
// Step 1
- self.network_state.set(NETWORK_NO_SOURCE);
+ self.network_state.set(NetworkState::NoSource);
// TODO step 2 (show poster)
// TODO step 3 (delay load event)
// Step 4
let doc = document_from_node(self);
let task = MediaElementMicrotask::ResourceSelectionTask {
elem: Root::from_ref(self),
@@ -318,22 +339,22 @@ impl HTMLMediaElement {
// TODO media provider object
ResourceSelectionMode::Object
} else if let Some(attr) = self.upcast::<Element>().get_attribute(&ns!(), &local_name!("src")) {
ResourceSelectionMode::Attribute(attr.Value().to_string())
} else if false { // TODO: when implementing this remove #[allow(unreachable_code)] above.
// TODO <source> child
ResourceSelectionMode::Children(panic!())
} else {
- self.network_state.set(NETWORK_EMPTY);
+ self.network_state.set(NetworkState::Empty);
return;
};
// Step 7
- self.network_state.set(NETWORK_LOADING);
+ self.network_state.set(NetworkState::Loading);
// Step 8
let window = window_from_node(self);
window.dom_manipulation_task_source().queue_simple_event(
self.upcast(),
atom!("loadstart"),
&window,
);
@@ -379,17 +400,17 @@ impl HTMLMediaElement {
fn resource_fetch_algorithm(&self, resource: Resource) {
// TODO step 3 (remove text tracks)
// Step 4
if let Resource::Url(url) = resource {
// 4.1
if self.Preload() == "none" && !self.autoplaying.get() {
// 4.1.1
- self.network_state.set(NETWORK_IDLE);
+ self.network_state.set(NetworkState::Idle);
// 4.1.2
let window = window_from_node(self);
window.dom_manipulation_task_source().queue_simple_event(
self.upcast(),
atom!("suspend"),
&window,
);
@@ -455,17 +476,17 @@ impl HTMLMediaElement {
fn dedicated_media_source_failure(&self) {
// Step 1
self.error.set(Some(&*MediaError::new(&*window_from_node(self),
MEDIA_ERR_SRC_NOT_SUPPORTED)));
// TODO step 2 (forget resource tracks)
// Step 3
- self.network_state.set(NETWORK_NO_SOURCE);
+ self.network_state.set(NetworkState::NoSource);
// TODO step 4 (show poster)
// Step 5
self.upcast::<EventTarget>().fire_event(atom!("error"));
// TODO step 6 (resolve pending play promises)
// TODO step 7 (delay load event)
@@ -482,38 +503,38 @@ impl HTMLMediaElement {
// Step 2
self.generation_id.set(self.generation_id.get() + 1);
// TODO reject pending play promises
let window = window_from_node(self);
let task_source = window.dom_manipulation_task_source();
// Step 3
- let network_state = self.NetworkState();
- if network_state == NETWORK_LOADING || network_state == NETWORK_IDLE {
+ let network_state = self.network_state.get();
+ if network_state == NetworkState::Loading || network_state == NetworkState::Idle {
task_source.queue_simple_event(
self.upcast(),
atom!("abort"),
&window,
);
}
// Step 4
- if network_state != NETWORK_EMPTY {
+ if network_state != NetworkState::Empty {
// 4.1
task_source.queue_simple_event(self.upcast(), atom!("emptied"), &window);
// TODO 4.2 (abort in-progress fetch)
// TODO 4.3 (detach media provider object)
// TODO 4.4 (forget resource tracks)
// 4.5
- if self.ready_state.get() != HAVE_NOTHING {
- self.change_ready_state(HAVE_NOTHING);
+ if self.ready_state.get() != ReadyState::HaveNothing {
+ self.change_ready_state(ReadyState::HaveNothing);
}
// 4.6
if !self.Paused() {
self.paused.set(true);
}
// TODO 4.7 (seeking)
// TODO 4.8 (playback position)
@@ -531,22 +552,22 @@ impl HTMLMediaElement {
// TODO step 8 (stop previously playing resource)
}
}
impl HTMLMediaElementMethods for HTMLMediaElement {
// https://html.spec.whatwg.org/multipage/#dom-media-networkstate
fn NetworkState(&self) -> u16 {
- self.network_state.get()
+ self.network_state.get() as u16
}
// https://html.spec.whatwg.org/multipage/#dom-media-readystate
fn ReadyState(&self) -> u16 {
- self.ready_state.get()
+ self.ready_state.get() as u16
}
// https://html.spec.whatwg.org/multipage/#dom-media-autoplay
make_bool_getter!(Autoplay, "autoplay");
// https://html.spec.whatwg.org/multipage/#dom-media-autoplay
make_bool_setter!(SetAutoplay, "autoplay");
// https://html.spec.whatwg.org/multipage/#dom-media-src
@@ -589,17 +610,17 @@ impl HTMLMediaElementMethods for HTMLMed
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() == NetworkState::Empty {
self.invoke_resource_selection_algorithm();
}
// TODO step 5 (seek backwards)
// TODO step 6 (media controller)
let state = self.ready_state.get();
@@ -613,45 +634,45 @@ impl HTMLMediaElementMethods for HTMLMed
let window = window_from_node(self);
let task_source = window.dom_manipulation_task_source();
// 7.3
task_source.queue_simple_event(self.upcast(), atom!("play"), &window);
// 7.4
- if state == HAVE_NOTHING ||
- state == HAVE_METADATA ||
- state == HAVE_CURRENT_DATA {
+ if state == ReadyState::HaveNothing ||
+ state == ReadyState::HaveMetadata ||
+ state == ReadyState::HaveCurrentData {
task_source.queue_simple_event(
self.upcast(),
atom!("waiting"),
&window,
);
} else {
self.notify_about_playing();
}
}
// Step 8
- else if state == HAVE_FUTURE_DATA || state == HAVE_ENOUGH_DATA {
+ else if state == ReadyState::HaveFutureData || state == ReadyState::HaveEnoughData {
// 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
fn Pause(&self) {
// Step 1
- if self.network_state.get() == NETWORK_EMPTY {
+ if self.network_state.get() == NetworkState::Empty {
self.invoke_resource_selection_algorithm();
}
// Step 2
self.internal_pause_steps();
}
// https://html.spec.whatwg.org/multipage/#dom-media-paused
@@ -803,17 +824,17 @@ impl FetchResponseListener for HTMLMedia
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 {
self.check_metadata(&elem);
} else {
- elem.change_ready_state(HAVE_CURRENT_DATA);
+ elem.change_ready_state(ReadyState::HaveCurrentData);
}
// 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 {
let window = window_from_node(&*elem);
window.dom_manipulation_task_source().queue_simple_event(
elem.upcast(),
@@ -830,32 +851,32 @@ impl FetchResponseListener for HTMLMedia
// => "If the media data can be fetched but is found by inspection to be in an unsupported
// format, or can otherwise not be rendered at all"
if !self.have_metadata {
elem.queue_dedicated_media_source_failure_steps();
}
// => "Once the entire media resource has been fetched..."
else if status.is_ok() {
- elem.change_ready_state(HAVE_ENOUGH_DATA);
+ elem.change_ready_state(ReadyState::HaveEnoughData);
elem.upcast::<EventTarget>().fire_event(atom!("progress"));
- elem.network_state.set(NETWORK_IDLE);
+ elem.network_state.set(NetworkState::Idle);
elem.upcast::<EventTarget>().fire_event(atom!("suspend"));
}
// => "If the connection is interrupted after some media data has been received..."
- else if elem.ready_state.get() != HAVE_NOTHING {
+ else if elem.ready_state.get() != ReadyState::HaveNothing {
// Step 2
elem.error.set(Some(&*MediaError::new(&*window_from_node(&*elem),
MEDIA_ERR_NETWORK)));
// Step 3
- elem.network_state.set(NETWORK_IDLE);
+ elem.network_state.set(NetworkState::Idle);
// TODO: Step 4 - update delay load flag
// Step 5
elem.upcast::<EventTarget>().fire_event(atom!("error"));
} else {
// => "If the media data cannot be fetched at all..."
elem.queue_dedicated_media_source_failure_steps();
@@ -896,15 +917,15 @@ impl HTMLMediaElementContext {
duration: Duration::seconds(dur.as_secs() as i64) +
Duration::nanoseconds(dur.subsec_nanos() as i64),
width: meta.dimensions.width,
height: meta.dimensions.height,
video: meta.video.unwrap_or("".to_owned()),
audio: meta.audio.audio,
});
// Step 6
- elem.change_ready_state(HAVE_METADATA);
+ elem.change_ready_state(ReadyState::HaveMetadata);
self.have_metadata = true;
}
_ => {}
}
}
}