From e4cd6be8311de48230b67ba4340b317a5d635a56 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 15 Sep 2017 02:59:09 +0200 Subject: [PATCH 1/6] Clean up HTMLMediaElement::change_ready_state --- components/script/dom/htmlmediaelement.rs | 138 +++++++++------------- 1 file changed, 58 insertions(+), 80 deletions(-) diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 59ebbd10aea..3bb5d0030cf 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -199,23 +199,19 @@ impl HTMLMediaElement { let window = window_from_node(self); let task_source = window.dom_manipulation_task_source(); - // Step 1 + // Step 1. match (old_ready_state, ready_state) { - // 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 ReadyState::HaveMetadata, and the new - // ready state is ReadyState::HaveCurrentData or greater. - (ReadyState::HaveMetadata, ReadyState::HaveCurrentData) | - (ReadyState::HaveMetadata, ReadyState::HaveFutureData) | - (ReadyState::HaveMetadata, ReadyState::HaveEnoughData) => { + // No other steps are applicable in this case. + return; + }, + (ReadyState::HaveMetadata, new) if new >= ReadyState::HaveCurrentData => { if !self.fired_loadeddata_event.get() { self.fired_loadeddata_event.set(true); task_source.queue_simple_event( @@ -224,86 +220,68 @@ impl HTMLMediaElement { &window, ); } - } - // 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 - } + // Steps for the transition from HaveMetadata to HaveCurrentData + // or HaveFutureData also apply here, as per the next match + // expression. + }, + (ReadyState::HaveFutureData, new) if new <= ReadyState::HaveCurrentData => { + // FIXME(nox): Queue a task to fire timeupdate and waiting + // events if the conditions call from the spec are met. + + // No other steps are applicable in this case. + return; + }, _ => (), } - // 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 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 old_ready_state <= ReadyState::HaveCurrentData && ready_state >= ReadyState::HaveFutureData { + task_source.queue_simple_event( + self.upcast(), + atom!("canplay"), + &window, + ); - if !self.Paused() { - self.notify_about_playing(); - } + if !self.Paused() { + self.notify_about_playing(); } - - // 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(); - } - } - - //TODO: check sandboxed automatic features browsing context flag - if self.autoplaying.get() && - self.Paused() && - self.Autoplay() { - // Step 1 - self.paused.set(false); - // TODO step 2: show poster - // Step 3 - task_source.queue_simple_event( - self.upcast(), - atom!("play"), - &window, - ); - // Step 4 - self.notify_about_playing(); - // Step 5 - self.autoplaying.set(false); - } - - task_source.queue_simple_event( - self.upcast(), - atom!("canplaythrough"), - &window, - ); - } - - _ => (), } - // TODO Step 2: media controller + if ready_state == ReadyState::HaveEnoughData { + // TODO: Check sandboxed automatic features browsing context flag. + // FIXME(nox): I have no idea what this TODO is about. + + // FIXME(nox): Review this block. + if self.autoplaying.get() && + self.Paused() && + self.Autoplay() { + // Step 1 + self.paused.set(false); + // TODO step 2: show poster + // Step 3 + task_source.queue_simple_event( + self.upcast(), + atom!("play"), + &window, + ); + // Step 4 + self.notify_about_playing(); + // Step 5 + self.autoplaying.set(false); + } + + // FIXME(nox): According to the spec, this should come *before* the + // "play" event. + task_source.queue_simple_event( + self.upcast(), + atom!("canplaythrough"), + &window, + ); + } + + // TODO Step 2: Media controller. + // FIXME(nox): There is no step 2 in the spec. } // https://html.spec.whatwg.org/multipage/#concept-media-load-algorithm From 5352697558a0ee4241067edfb483ff3f8f036c24 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 15 Sep 2017 03:31:53 +0200 Subject: [PATCH 2/6] Clean up the resource selection algorithm a bit --- components/script/dom/htmlmediaelement.rs | 111 +++++++++++++--------- 1 file changed, 66 insertions(+), 45 deletions(-) diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 3bb5d0030cf..7be4b56e1cc 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -286,44 +286,61 @@ impl HTMLMediaElement { // https://html.spec.whatwg.org/multipage/#concept-media-load-algorithm fn invoke_resource_selection_algorithm(&self) { - // Step 1 + // Step 1. self.network_state.set(NetworkState::NoSource); - // TODO step 2 (show poster) - // TODO step 3 (delay load event) + // Step 2. + // FIXME(nox): Set show poster flag to true. - // Step 4 + // Step 3. + // FIXME(nox): Set the delaying-the-load-event flag to true. + + // Step 4. + // 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: Root::from_ref(self), 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)); } // https://html.spec.whatwg.org/multipage/#concept-media-load-algorithm - #[allow(unreachable_code)] + // FIXME(nox): Why does this need to be passed the base URL? fn resource_selection_algorithm_sync(&self, base_url: ServoUrl) { - // TODO step 5 (populate pending text tracks) + // Step 5. + // FIXME(nox): Maybe populate the list of pending text tracks. - // Step 6 - let mode = if false { - // TODO media provider object - ResourceSelectionMode::Object - } else if let Some(attr) = self.upcast::().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 child - ResourceSelectionMode::Children(panic!()) + // Step 6. + enum Mode { + // FIXME(nox): Support media object provider. + #[allow(dead_code)] + Object, + Attribute(String), + // FIXME(nox): Support source element child. + #[allow(dead_code)] + Children(Root), + } + let mode = if let Some(attr) = self.upcast::().get_attribute(&ns!(), &local_name!("src")) { + Mode::Attribute(attr.Value().into()) } else { self.network_state.set(NetworkState::Empty); return; }; - // Step 7 + // Step 7. self.network_state.set(NetworkState::Loading); - // Step 8 + // Step 8. let window = window_from_node(self); window.dom_manipulation_task_source().queue_simple_event( self.upcast(), @@ -331,40 +348,50 @@ impl HTMLMediaElement { &window, ); - // Step 9 + // Step 9. match mode { - ResourceSelectionMode::Object => { - // Step 1 + // Step 9.obj. + Mode::Object => { + // Step 9.obj.1. *self.current_src.borrow_mut() = "".to_owned(); - // Step 4 - self.resource_fetch_algorithm(Resource::Object); - } + // Step 9.obj.2. + // FIXME(nox): The rest of the steps should be ran in parallel. - ResourceSelectionMode::Attribute(src) => { - // Step 1 + // 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; } - // Step 2 - let absolute_url = base_url.join(&src).map_err(|_| ()); + // Step 9.attr.2. + let url_record = match base_url.join(&src) { + Ok(url) => url, + Err(_) => { + self.queue_dedicated_media_source_failure_steps(); + return; + } + }; - // Step 3 - if let Ok(url) = absolute_url { - *self.current_src.borrow_mut() = url.as_str().into(); - // Step 4 - self.resource_fetch_algorithm(Resource::Url(url)); - } else { - self.queue_dedicated_media_source_failure_steps(); - } - } + // Step 9.attr.3. + *self.current_src.borrow_mut() = url_record.as_str().into(); - ResourceSelectionMode::Children(_child) => { - // TODO + // Step 9.attr.4. + // Note that the resource fetch algorithm itself takes care + // of the cleanup in case of failure itself. + self.resource_fetch_algorithm(Resource::Url(url_record)); + }, + Mode::Children(_source) => { + // Step 9.children. self.queue_dedicated_media_source_failure_steps() - } + }, } } @@ -723,12 +750,6 @@ impl MicrotaskRunnable for MediaElementMicrotask { } } -enum ResourceSelectionMode { - Object, - Attribute(String), - Children(Root), -} - enum Resource { Object, Url(ServoUrl), From e8d7eaf8a3a5302e861624b7c1c78f7eae86bcc3 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 15 Sep 2017 09:41:21 +0200 Subject: [PATCH 3/6] Clean up code of the resource fetch algorithm --- components/script/dom/htmlmediaelement.rs | 149 ++++++++++++---------- 1 file changed, 85 insertions(+), 64 deletions(-) diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 7be4b56e1cc..3358e74e862 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -12,6 +12,8 @@ use dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaElementC use dom::bindings::codegen::Bindings::HTMLMediaElementBinding::HTMLMediaElementMethods; use dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorConstants::*; use dom::bindings::codegen::Bindings::MediaErrorBinding::MediaErrorMethods; +use dom::bindings::codegen::InheritTypes::{ElementTypeId, HTMLElementTypeId}; +use dom::bindings::codegen::InheritTypes::{HTMLMediaElementTypeId, NodeTypeId}; use dom::bindings::inheritance::Castable; use dom::bindings::js::{MutNullableJS, Root}; use dom::bindings::refcounted::Trusted; @@ -20,10 +22,8 @@ use dom::bindings::str::DOMString; use dom::document::Document; use dom::element::{Element, AttributeMutation}; use dom::eventtarget::EventTarget; -use dom::htmlaudioelement::HTMLAudioElement; use dom::htmlelement::HTMLElement; use dom::htmlsourceelement::HTMLSourceElement; -use dom::htmlvideoelement::HTMLVideoElement; use dom::mediaerror::MediaError; use dom::node::{window_from_node, document_from_node, Node, UnbindContext}; use dom::virtualmethods::VirtualMethods; @@ -123,6 +123,17 @@ impl HTMLMediaElement { } } + fn media_type_id(&self) -> HTMLMediaElementTypeId { + match self.upcast::().type_id() { + NodeTypeId::Element(ElementTypeId::HTMLElement( + HTMLElementTypeId::HTMLMediaElement(media_type_id), + )) => { + media_type_id + }, + _ => unreachable!(), + } + } + /// https://html.spec.whatwg.org/multipage/#internal-pause-steps fn internal_pause_steps(&self) { // Step 1. @@ -397,71 +408,81 @@ impl HTMLMediaElement { // https://html.spec.whatwg.org/multipage/#concept-media-load-resource fn resource_fetch_algorithm(&self, resource: Resource) { - // TODO step 3 (remove text tracks) + // Steps 1-2. + // Unapplicable, the `resource` variable already conveys which mode + // is in use. - // Step 4 - if let Resource::Url(url) = resource { - // 4.1 - if self.Preload() == "none" && !self.autoplaying.get() { - // 4.1.1 - self.network_state.set(NetworkState::Idle); + // Step 3. + // FIXME(nox): Remove all media-resource-specific text tracks. - // 4.1.2 + // Step 4. + match resource { + Resource::Url(url) => { + // Step 4.remote.1. + if self.Preload() == "none" && !self.autoplaying.get() { + // Step 4.remote.1.1. + self.network_state.set(NetworkState::Idle); + + // Step 4.remote.1.2. + let window = window_from_node(self); + window.dom_manipulation_task_source().queue_simple_event( + self.upcast(), + atom!("suspend"), + &window, + ); + + // Step 4.remote.1.3. + // FIXME(nox): Queue a task to set the delaying-the-load-event + // flag to false. + + // Steps 4.remote.1.4. + // FIXME(nox): Somehow we should wait for the task from previous + // step to be ran before continuing. + + // Steps 4.remote.1.5-4.remote.1.7. + // FIXME(nox): Wait for an implementation-defined event and + // then continue with the normal set of steps instead of just + // returning. + return; + } + + // Step 4.remote.2. + // FIXME(nox): Handle CORS setting from crossorigin attribute. + let document = document_from_node(self); + let type_ = match self.media_type_id() { + HTMLMediaElementTypeId::HTMLAudioElement => RequestType::Audio, + HTMLMediaElementTypeId::HTMLVideoElement => RequestType::Video, + }; + let request = RequestInit { + url: url.clone(), + type_, + destination: Destination::Media, + credentials_mode: CredentialsMode::Include, + use_url_credentials: true, + origin: document.origin().immutable().clone(), + pipeline_id: Some(self.global().pipeline_id()), + referrer_url: Some(document.url()), + referrer_policy: document.get_referrer_policy(), + .. RequestInit::default() + }; + + let context = Arc::new(Mutex::new(HTMLMediaElementContext::new(self, url.clone()))); + let (action_sender, action_receiver) = ipc::channel().unwrap(); let window = window_from_node(self); - window.dom_manipulation_task_source().queue_simple_event( - self.upcast(), - atom!("suspend"), - &window, - ); - - // TODO 4.1.3 (delay load flag) - - // TODO 4.1.5-7 (state for load that initiates later) - return; - } - - // 4.2 - let context = Arc::new(Mutex::new(HTMLMediaElementContext::new(self, url.clone()))); - let (action_sender, action_receiver) = ipc::channel().unwrap(); - let window = window_from_node(self); - let listener = NetworkListener { - context: context, - task_source: window.networking_task_source(), - canceller: Some(window.task_canceller()) - }; - - ROUTER.add_route(action_receiver.to_opaque(), box move |message| { - listener.notify_fetch(message.to().unwrap()); - }); - - // FIXME: we're supposed to block the load event much earlier than now - let document = document_from_node(self); - - let ty = if self.is::() { - RequestType::Audio - } else if self.is::() { - RequestType::Video - } else { - unreachable!("Unexpected HTMLMediaElement") - }; - - let request = RequestInit { - url: url.clone(), - type_: ty, - destination: Destination::Media, - credentials_mode: CredentialsMode::Include, - use_url_credentials: true, - origin: document.origin().immutable().clone(), - pipeline_id: Some(self.global().pipeline_id()), - referrer_url: Some(document.url()), - referrer_policy: document.get_referrer_policy(), - .. RequestInit::default() - }; - - document.fetch_async(LoadType::Media(url), request, action_sender); - } else { - // TODO local resource fetch - self.queue_dedicated_media_source_failure_steps(); + let listener = NetworkListener { + context: context, + task_source: window.networking_task_source(), + canceller: Some(window.task_canceller()) + }; + ROUTER.add_route(action_receiver.to_opaque(), box move |message| { + listener.notify_fetch(message.to().unwrap()); + }); + document.fetch_async(LoadType::Media(url), request, action_sender); + }, + Resource::Object => { + // FIXME(nox): Use the current media resource. + self.queue_dedicated_media_source_failure_steps(); + }, } } From bd9486bbe2b82cd7afbff96a0df7d6fb9911545f Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 15 Sep 2017 10:09:01 +0200 Subject: [PATCH 4/6] Clean up code for the media element load algorithm --- components/script/dom/htmlmediaelement.rs | 67 +++++++++++++++-------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 3358e74e862..ef96a876356 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -531,59 +531,78 @@ impl HTMLMediaElement { // this invokation of the load algorithm. self.fired_loadeddata_event.set(false); - // TODO Step 1 (abort resource selection algorithm instances) + // Step 1. + // FIXME(nox): Abort any already-running instance of the + // resource selection algorithm. - // Step 2 + // Steps 2-4. + // FIXME(nox): Cancel all tasks related to this element and resolve or + // reject all pending play promises. 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 + // Step 5. 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, - ); + task_source.queue_simple_event(self.upcast(), atom!("abort"), &window); } - // Step 4 + // Step 6. if network_state != NetworkState::Empty { - // 4.1 + // Step 6.1. task_source.queue_simple_event(self.upcast(), atom!("emptied"), &window); - // TODO 4.2 (abort in-progress fetch) + // Step 6.2. + // FIXME(nox): Abort in-progress fetching process. - // TODO 4.3 (detach media provider object) - // TODO 4.4 (forget resource tracks) + // Step 6.3. + // FIXME(nox): Detach MediaSource media provider object. - // 4.5 + // Step 6.4. + // FIXME(nox): Forget the media-resource-specific tracks. + + // Step 6.5. if self.ready_state.get() != ReadyState::HaveNothing { self.change_ready_state(ReadyState::HaveNothing); } - // 4.6 + // Step 6.6. if !self.Paused() { + // Step 6.6.1. self.paused.set(true); + + // Step 6.6.2. + // FIXME(nox): Reject pending play promises. } - // TODO 4.7 (seeking) - // TODO 4.8 (playback position) - // TODO 4.9 (timeline offset) - // TODO 4.10 (duration) + + // Step 6.7. + // FIXME(nox): If seeking is true, set it to false. + + // Step 6.8. + // FIXME(nox): Set current and official playback position to 0 and + // maybe queue a task to fire a timeupdate event. + + // Step 6.9. + // FIXME(nox): Set timeline offset to NaN. + + // Step 6.10. + // FIXME(nox): Set duration to NaN. } - // TODO step 5 (playback rate) - // Step 6 + // Step 7. + // FIXME(nox): Set playbackRate to defaultPlaybackRate. + + // Step 8. self.error.set(None); self.autoplaying.set(true); - // Step 7 + // Step 9. self.invoke_resource_selection_algorithm(); - // TODO step 8 (stop previously playing resource) + // Step 10. + // FIXME(nox): Stop playback of any previously running media resource. } } From 7e835c351e44e7378784fdd4667cc151b3d602ae Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 15 Sep 2017 10:09:22 +0200 Subject: [PATCH 5/6] Handle invalid MIME types in HTMLMediaElement::CanPlayType --- components/script/dom/htmlmediaelement.rs | 12 +++++++++--- .../media-elements/mime-types/canPlayType.html.ini | 3 --- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index ef96a876356..80afcf2a585 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -32,6 +32,7 @@ use html5ever::{LocalName, Prefix}; use ipc_channel::ipc; use ipc_channel::router::ROUTER; use microtask::{Microtask, MicrotaskRunnable}; +use mime::{Mime, SubLevel, TopLevel}; use net_traits::{FetchResponseListener, FetchMetadata, Metadata, NetworkError}; use net_traits::request::{CredentialsMode, Destination, RequestInit, Type as RequestType}; use network_listener::{NetworkListener, PreInvoke}; @@ -644,9 +645,14 @@ impl HTMLMediaElementMethods for HTMLMediaElement { } // https://html.spec.whatwg.org/multipage/#dom-navigator-canplaytype - fn CanPlayType(&self, _type_: DOMString) -> CanPlayTypeResult { - // TODO: application/octet-stream - CanPlayTypeResult::Maybe + fn CanPlayType(&self, type_: DOMString) -> CanPlayTypeResult { + match type_.parse::() { + Ok(Mime(TopLevel::Application, SubLevel::OctetStream, _)) | + Err(_) => { + CanPlayTypeResult::_empty + }, + _ => CanPlayTypeResult::Maybe + } } // https://html.spec.whatwg.org/multipage/#dom-media-error diff --git a/tests/wpt/metadata/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html.ini index c5491bf470e..dc43bf6b602 100644 --- a/tests/wpt/metadata/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html.ini +++ b/tests/wpt/metadata/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html.ini @@ -1,8 +1,5 @@ [canPlayType.html] type: testharness - [application/octet-stream] - expected: FAIL - [video/x-new-fictional-format] expected: FAIL From 0e2249f07f6858ae61fc9b85a0d1d8908178884b Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 15 Sep 2017 10:24:20 +0200 Subject: [PATCH 6/6] Clean up HTMLMediaElement::Play --- components/script/dom/htmlmediaelement.rs | 90 ++++++++++++----------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 80afcf2a585..594c0ea221e 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -661,65 +661,70 @@ impl HTMLMediaElementMethods for HTMLMediaElement { } // https://html.spec.whatwg.org/multipage/#dom-media-play + // FIXME(nox): This should return a promise. fn Play(&self) { - // TODO step 1 + // Step 1. + // FIXME(nox): Return a rejected promise if not allowed to play. - // Step 2 + // Step 2. if self.error.get().map_or(false, |e| e.Code() == MEDIA_ERR_SRC_NOT_SUPPORTED) { - // TODO return rejected promise + // FIXME(nox): This should return a rejected promise. return; } - // TODO step 3 + // Step 3. + // Create promise and add it to list of pending play promises. - // Step 4 + // Step 4. if self.network_state.get() == NetworkState::Empty { self.invoke_resource_selection_algorithm(); } - // TODO step 5 (seek backwards) - - // TODO step 6 (media controller) + // Step 5. + // FIXME(nox): Seek to earliest possible position if playback has ended + // and direction of playback is forwards. let state = self.ready_state.get(); - // Step 7 if self.Paused() { - // 7.1 + // Step 6.1. self.paused.set(false); - // TODO 7.2 (show poster) + // Step 6.2. + // FIXME(nox): Set show poster flag to false and run time marches on + // steps if show poster flag is true. + // Step 6.3. 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 == ReadyState::HaveNothing || - state == ReadyState::HaveMetadata || - state == ReadyState::HaveCurrentData { - task_source.queue_simple_event( - self.upcast(), - atom!("waiting"), - &window, - ); - } else { - self.notify_about_playing(); + // Step 7.4. + match state { + ReadyState::HaveNothing | + ReadyState::HaveMetadata | + ReadyState::HaveCurrentData => { + task_source.queue_simple_event( + self.upcast(), + atom!("waiting"), + &window, + ); + }, + ReadyState::HaveFutureData | + ReadyState::HaveEnoughData => { + self.notify_about_playing(); + } } - } - // Step 8 - else if state == ReadyState::HaveFutureData || state == ReadyState::HaveEnoughData { - // TODO resolve pending play promises + } else if state == ReadyState::HaveFutureData || state == ReadyState::HaveEnoughData { + // Step 7. + // FIXME(nox): Queue a task to resolve pending play promises. } - // Step 9 + // Step 8. self.autoplaying.set(false); - // TODO step 10 (media controller) - - // TODO return promise + // Step 9. + // FIXME(nox): Return promise created in step 3. } // https://html.spec.whatwg.org/multipage/#dom-media-pause @@ -820,12 +825,12 @@ struct HTMLMediaElementContext { ignore_response: bool, } +// https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list impl FetchResponseListener for HTMLMediaElementContext { fn process_request_body(&mut self) {} fn process_request_eof(&mut self) {} - // https://html.spec.whatwg.org/multipage/#media-data-processing-steps-list fn process_response(&mut self, metadata: Result) { self.metadata = metadata.ok().map(|m| { match m { @@ -834,23 +839,24 @@ impl FetchResponseListener for HTMLMediaElementContext { } }); + let status_is_ok = self.metadata.as_ref() + .and_then(|m| m.status.as_ref()) + .map_or(true, |s| s.0 >= 200 && s.0 < 300); + // => "If the media data cannot be fetched at all..." - let is_failure = self.metadata - .as_ref() - .and_then(|m| m.status - .as_ref() - .map(|&(s, _)| s < 200 || s >= 300)) - .unwrap_or(false); - if is_failure { + if !status_is_ok { // Ensure that the element doesn't receive any further notifications - // of the aborted fetch. The dedicated failure steps will be executed - // when response_complete runs. + // of the aborted fetch. The dedicated failure steps will be + // executed when response_complete runs. + // FIXME(nox): According to the spec, we shouldn't wait to receive + // the whole response before running the dedicated failure steps. self.ignore_response = true; } } fn process_response_chunk(&mut self, mut payload: Vec) { if self.ignore_response { + // An error was received previously, skip processing the payload. return; }