servo: Merge #18504 - Some minor HTMLMediaElement improvements (from servo:media); r=emilio
authorAnthony Ramine <n.oxyde@gmail.com>
Thu, 14 Sep 2017 20:51:51 -0500
changeset 665544 004c28e9a39e1d804e7d4ce62a7cbfcc231c89b9
parent 665384 f36106f3f52a9bb2769860d20568c589fc2498d0
child 665545 eae22389b267d0603db9162d878881505dd94519
push id80115
push userbmo:eoger@fastmail.com
push dateFri, 15 Sep 2017 18:29:01 +0000
reviewersemilio
milestone57.0a1
servo: Merge #18504 - Some minor HTMLMediaElement improvements (from servo:media); r=emilio Source-Repo: https://github.com/servo/servo Source-Revision: 1b9a5ea7197cb59f48b2010895ad2a98fef08bc4
servo/components/script/dom/htmlmediaelement.rs
--- 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;
             }
             _ => {}
         }
     }
 }