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 430572 004c28e9a39e1d804e7d4ce62a7cbfcc231c89b9
parent 430571 f36106f3f52a9bb2769860d20568c589fc2498d0
child 430573 eae22389b267d0603db9162d878881505dd94519
push id7768
push userryanvm@gmail.com
push dateSat, 16 Sep 2017 16:13:49 +0000
treeherdermozilla-beta@3b375d85383a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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;
             }
             _ => {}
         }
     }
 }