servo: Merge #18804 - Another couple of low-key media improvements πŸ‘ΆπŸ‘£ (from servo:media); r=emilio
authorAnthony Ramine <n.oxyde@gmail.com>
Tue, 10 Oct 2017 06:17:15 -0500
changeset 428029 c02a1d8cfbe195fb7247ed6948fa2d6dabf3fd09
parent 428028 54c3651423b2242178b0839d72229cdbf5ebc900
child 428030 6bec02f9a646f11777dbc12ceeb90af76198242d
push id97
push userfmarier@mozilla.com
push dateSat, 14 Oct 2017 01:12:59 +0000
reviewersemilio
milestone58.0a1
servo: Merge #18804 - Another couple of low-key media improvements πŸ‘ΆπŸ‘£ (from servo:media); r=emilio Source-Repo: https://github.com/servo/servo Source-Revision: 826352ab4cae13f5154d13ab53885d80a8057337
servo/components/script/dom/htmlmediaelement.rs
servo/components/script/dom/webidls/HTMLMediaElement.webidl
--- a/servo/components/script/dom/htmlmediaelement.rs
+++ b/servo/components/script/dom/htmlmediaelement.rs
@@ -15,16 +15,17 @@ use dom::bindings::codegen::Bindings::Me
 use dom::bindings::codegen::InheritTypes::{ElementTypeId, HTMLElementTypeId};
 use dom::bindings::codegen::InheritTypes::{HTMLMediaElementTypeId, NodeTypeId};
 use dom::bindings::error::{Error, ErrorResult};
 use dom::bindings::inheritance::Castable;
 use dom::bindings::refcounted::Trusted;
 use dom::bindings::reflector::DomObject;
 use dom::bindings::root::{DomRoot, MutNullableDom};
 use dom::bindings::str::DOMString;
+use dom::blob::Blob;
 use dom::document::Document;
 use dom::element::{Element, AttributeMutation};
 use dom::eventtarget::EventTarget;
 use dom::htmlelement::HTMLElement;
 use dom::htmlsourceelement::HTMLSourceElement;
 use dom::mediaerror::MediaError;
 use dom::node::{window_from_node, document_from_node, Node, UnbindContext};
 use dom::promise::Promise;
@@ -52,16 +53,18 @@ use time::{self, Timespec, Duration};
 // FIXME(nox): A lot of tasks queued for this element should probably be in the
 // media element event task source.
 pub struct HTMLMediaElement {
     htmlelement: HTMLElement,
     /// https://html.spec.whatwg.org/multipage/#dom-media-networkstate
     network_state: Cell<NetworkState>,
     /// https://html.spec.whatwg.org/multipage/#dom-media-readystate
     ready_state: Cell<ReadyState>,
+    /// https://html.spec.whatwg.org/multipage/#dom-media-srcobject
+    src_object: MutNullableDom<Blob>,
     /// https://html.spec.whatwg.org/multipage/#dom-media-currentsrc
     current_src: DomRefCell<String>,
     /// Incremented whenever tasks associated with this element are cancelled.
     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>,
@@ -107,16 +110,17 @@ impl HTMLMediaElement {
         tag_name: LocalName,
         prefix: Option<Prefix>,
         document: &Document,
     ) -> Self {
         Self {
             htmlelement: HTMLElement::new_inherited(tag_name, prefix, document),
             network_state: Cell::new(NetworkState::Empty),
             ready_state: Cell::new(ReadyState::HaveNothing),
+            src_object: Default::default(),
             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),
             delaying_the_load_event_flag: Default::default(),
@@ -425,16 +429,17 @@ impl HTMLMediaElement {
         // If the resource selection mode in the synchronous section is
         // "attribute", the URL of the resource to fetch is relative to the
         // media element's node document when the src attribute was last
         // changed, which is why we need to pass the base URL in the task
         // right here.
         let doc = document_from_node(self);
         let task = MediaElementMicrotask::ResourceSelectionTask {
             elem: DomRoot::from_ref(self),
+            generation_id: self.generation_id.get(),
             base_url: doc.base_url()
         };
 
         // FIXME(nox): This will later call the resource_selection_algorith_sync
         // method from below, if microtasks were trait objects, we would be able
         // to put the code directly in this method, without the boilerplate
         // indirections.
         ScriptThread::await_stable_state(Microtask::MediaElement(task));
@@ -442,23 +447,24 @@ impl HTMLMediaElement {
 
     // https://html.spec.whatwg.org/multipage/#concept-media-load-algorithm
     fn resource_selection_algorithm_sync(&self, base_url: ServoUrl) {
         // Step 5.
         // FIXME(nox): Maybe populate the list of pending text tracks.
 
         // Step 6.
         enum Mode {
-            // FIXME(nox): Support media object provider.
-            #[allow(dead_code)]
             Object,
             Attribute(String),
             Children(DomRoot<HTMLSourceElement>),
         }
         fn mode(media: &HTMLMediaElement) -> Option<Mode> {
+            if media.src_object.get().is_some() {
+                return Some(Mode::Object);
+            }
             if let Some(attr) = media.upcast::<Element>().get_attribute(&ns!(), &local_name!("src")) {
                 return Some(Mode::Attribute(attr.Value().into()));
             }
             let source_child_element = media.upcast::<Node>()
                 .children()
                 .filter_map(DomRoot::downcast::<HTMLSourceElement>)
                 .next();
             if let Some(element) = source_child_element {
@@ -494,17 +500,16 @@ impl HTMLMediaElement {
                 *self.current_src.borrow_mut() = "".to_owned();
 
                 // Step 9.obj.2.
                 // FIXME(nox): The rest of the steps should be ran in parallel.
 
                 // Step 9.obj.3.
                 // Note that the resource fetch algorithm itself takes care
                 // of the cleanup in case of failure itself.
-                // FIXME(nox): Pass the assigned media provider here.
                 self.resource_fetch_algorithm(Resource::Object);
             },
             Mode::Attribute(src) => {
                 // Step 9.attr.1.
                 if src.is_empty() {
                     self.queue_dedicated_media_source_failure_steps();
                     return;
                 }
@@ -607,17 +612,17 @@ impl HTMLMediaElement {
                     canceller: Some(window.task_canceller())
                 };
                 ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
                     listener.notify_fetch(message.to().unwrap());
                 });
                 document.loader().fetch_async_background(request, action_sender);
             },
             Resource::Object => {
-                // FIXME(nox): Use the current media resource.
+                // FIXME(nox): Actually do something with the object.
                 self.queue_dedicated_media_source_failure_steps();
             },
         }
     }
 
     /// Queues a task to run the [dedicated media source failure steps][steps].
     ///
     /// [steps]: https://html.spec.whatwg.org/multipage/#dedicated-media-source-failure-steps
@@ -667,22 +672,20 @@ impl HTMLMediaElement {
     }
 
     // https://html.spec.whatwg.org/multipage/#media-element-load-algorithm
     fn media_element_load_algorithm(&self) {
         // Reset the flag that signals whether loadeddata was ever fired for
         // this invokation of the load algorithm.
         self.fired_loadeddata_event.set(false);
 
-        // Step 1.
-        // FIXME(nox): Abort any already-running instance of the
-        // resource selection algorithm.
+        // Step 1-2.
+        self.generation_id.set(self.generation_id.get() + 1);
 
-        // Steps 2-4.
-        self.generation_id.set(self.generation_id.get() + 1);
+        // Steps 3-4.
         while !self.in_flight_play_promises_queue.borrow().is_empty() {
             self.fulfill_in_flight_play_promises(|| ());
         }
 
         let window = window_from_node(self);
         let task_source = window.dom_manipulation_task_source();
 
         // Step 5.
@@ -832,16 +835,27 @@ impl HTMLMediaElementMethods for HTMLMed
     // https://html.spec.whatwg.org/multipage/#dom-media-autoplay
     make_bool_setter!(SetAutoplay, "autoplay");
 
     // https://html.spec.whatwg.org/multipage/#dom-media-src
     make_url_getter!(Src, "src");
     // https://html.spec.whatwg.org/multipage/#dom-media-src
     make_setter!(SetSrc, "src");
 
+    // https://html.spec.whatwg.org/multipage/#dom-media-srcobject
+    fn GetSrcObject(&self) -> Option<DomRoot<Blob>> {
+        self.src_object.get()
+    }
+
+    // https://html.spec.whatwg.org/multipage/#dom-media-srcobject
+    fn SetSrcObject(&self, value: Option<&Blob>) {
+        self.src_object.set(value);
+        self.media_element_load_algorithm();
+    }
+
     // https://html.spec.whatwg.org/multipage/#attr-media-preload
     // Missing value default is user-agent defined.
     make_enumerated_getter!(Preload, "preload", "", "none" | "metadata" | "auto");
     // https://html.spec.whatwg.org/multipage/#attr-media-preload
     make_setter!(SetPreload, "preload");
 
     // https://html.spec.whatwg.org/multipage/#dom-media-currentsrc
     fn CurrentSrc(&self) -> DOMString {
@@ -924,28 +938,31 @@ impl VirtualMethods for HTMLMediaElement
         }
     }
 }
 
 #[derive(HeapSizeOf, JSTraceable)]
 pub enum MediaElementMicrotask {
     ResourceSelectionTask {
         elem: DomRoot<HTMLMediaElement>,
-        base_url: ServoUrl
+        generation_id: u32,
+        base_url: ServoUrl,
     },
     PauseIfNotInDocumentTask {
         elem: DomRoot<HTMLMediaElement>,
     }
 }
 
 impl MicrotaskRunnable for MediaElementMicrotask {
     fn handler(&self) {
         match self {
-            &MediaElementMicrotask::ResourceSelectionTask { ref elem, ref base_url } => {
-                elem.resource_selection_algorithm_sync(base_url.clone());
+            &MediaElementMicrotask::ResourceSelectionTask { ref elem, generation_id, ref base_url } => {
+                if generation_id == elem.generation_id.get() {
+                    elem.resource_selection_algorithm_sync(base_url.clone());
+                }
             },
             &MediaElementMicrotask::PauseIfNotInDocumentTask { ref elem } => {
                 if !elem.upcast::<Node>().is_in_doc() {
                     elem.internal_pause_steps();
                 }
             },
         }
     }
--- a/servo/components/script/dom/webidls/HTMLMediaElement.webidl
+++ b/servo/components/script/dom/webidls/HTMLMediaElement.webidl
@@ -1,73 +1,66 @@
 /* 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
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // https://html.spec.whatwg.org/multipage/#htmlmediaelement
+
 enum CanPlayTypeResult { "" /* empty string */, "maybe", "probably" };
+typedef /* (MediaStream or MediaSource or */ Blob /* ) */ MediaProvider;
+
 [Abstract]
 interface HTMLMediaElement : HTMLElement {
   // error state
   readonly attribute MediaError? error;
 
   // network state
-  [CEReactions]
-           attribute DOMString src;
+  [CEReactions] attribute DOMString src;
+  attribute MediaProvider? srcObject;
   readonly attribute DOMString currentSrc;
-  // [CEReactions]
-  //          attribute DOMString crossOrigin;
+  // [CEReactions] attribute DOMString crossOrigin;
   const unsigned short NETWORK_EMPTY = 0;
   const unsigned short NETWORK_IDLE = 1;
   const unsigned short NETWORK_LOADING = 2;
   const unsigned short NETWORK_NO_SOURCE = 3;
   readonly attribute unsigned short networkState;
-  [CEReactions]
-           attribute DOMString preload;
+  [CEReactions] attribute DOMString preload;
   // readonly attribute TimeRanges buffered;
   void load();
   CanPlayTypeResult canPlayType(DOMString type);
 
   // ready state
   const unsigned short HAVE_NOTHING = 0;
   const unsigned short HAVE_METADATA = 1;
   const unsigned short HAVE_CURRENT_DATA = 2;
   const unsigned short HAVE_FUTURE_DATA = 3;
   const unsigned short HAVE_ENOUGH_DATA = 4;
   readonly attribute unsigned short readyState;
   // readonly attribute boolean seeking;
 
   // playback state
-  //          attribute double currentTime;
+  // attribute double currentTime;
   // void fastSeek(double time);
   // readonly attribute unrestricted double duration;
   // Date getStartDate();
   readonly attribute boolean paused;
-  //          attribute double defaultPlaybackRate;
-  //          attribute double playbackRate;
+  // attribute double defaultPlaybackRate;
+  // attribute double playbackRate;
   // readonly attribute TimeRanges played;
   // readonly attribute TimeRanges seekable;
   // readonly attribute boolean ended;
-  [CEReactions]
-           attribute boolean autoplay;
-  // [CEReactions]
-  //          attribute boolean loop;
+  [CEReactions] attribute boolean autoplay;
+  // [CEReactions] attribute boolean loop;
   Promise<void> play();
   void pause();
 
-  // media controller
-  //          attribute DOMString mediaGroup;
-  //          attribute MediaController? controller;
-
   // controls
-  // [CEReactions]
-  //          attribute boolean controls;
-  //          attribute double volume;
-  //          attribute boolean muted;
-  // [CEReactions]
-  //          attribute boolean defaultMuted;
+  // [CEReactions] attribute boolean controls;
+  // attribute double volume;
+  // attribute boolean muted;
+  // [CEReactions] attribute boolean defaultMuted;
 
   // tracks
   // readonly attribute AudioTrackList audioTracks;
   // readonly attribute VideoTrackList videoTracks;
   // readonly attribute TextTrackList textTracks;
   // TextTrack addTextTrack(TextTrackKind kind, optional DOMString label = "", optional DOMString language = "");
 };