Bug 1299072: P10. Pass decoding error details to media element's error attribute. r?jwwang draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Sun, 11 Sep 2016 00:56:09 +1000
changeset 412875 1b406b5bcbc20cbd7844cfbb71530debf6963e85
parent 412874 c7545fb792215ba95dac388d3683bef179877142
child 412876 9fb71c41e5273d96354bfb9097e5e66eee5b2137
push id29276
push userbmo:jyavenard@mozilla.com
push dateTue, 13 Sep 2016 03:29:20 +0000
reviewersjwwang
bugs1299072
milestone51.0a1
Bug 1299072: P10. Pass decoding error details to media element's error attribute. r?jwwang MozReview-Commit-ID: 49DurV9WI5S
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
dom/html/MediaError.cpp
dom/html/MediaError.h
dom/media/MediaDecoder.cpp
dom/media/MediaDecoderOwner.h
dom/media/eme/MediaKeys.cpp
dom/media/gtest/MockMediaDecoderOwner.h
dom/media/mediasource/MediaSource.cpp
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4336,17 +4336,17 @@ void HTMLMediaElement::MetadataLoaded(co
                "Video resolution must be known on 'loadedmetadata'");
   DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
   if (mDecoder && mDecoder->IsTransportSeekable() && mDecoder->IsMediaSeekable()) {
     ProcessMediaFragmentURI();
     mDecoder->SetFragmentEndTime(mFragmentEnd);
   }
   if (mIsEncrypted) {
     if (!mMediaSource && Preferences::GetBool("media.eme.mse-only", true)) {
-      DecodeError();
+      DecodeError(NS_ERROR_DOM_MEDIA_FATAL_ERR);
       return;
     }
 
 #ifdef MOZ_EME
     // Dispatch a distinct 'encrypted' event for each initData we have.
     for (const auto& initData : mPendingEncryptedInitData.mInitDatas) {
       DispatchEncrypted(initData.mInitData, initData.mType);
     }
@@ -4411,17 +4411,17 @@ void HTMLMediaElement::FirstFrameLoaded(
 void HTMLMediaElement::NetworkError()
 {
   if (mDecoder) {
     ShutdownDecoder();
   }
   Error(nsIDOMMediaError::MEDIA_ERR_NETWORK);
 }
 
-void HTMLMediaElement::DecodeError()
+void HTMLMediaElement::DecodeError(const MediaResult& aError)
 {
   nsAutoString src;
   GetCurrentSrc(src);
   const char16_t* params[] = { src.get() };
   ReportLoadError("MediaLoadDecodeError", params, ArrayLength(params));
 
   if (mDecoder) {
     ShutdownDecoder();
@@ -4435,45 +4435,50 @@ void HTMLMediaElement::DecodeError()
     mError = nullptr;
     if (mSourceLoadCandidate) {
       DispatchAsyncSourceError(mSourceLoadCandidate);
       QueueLoadFromSourceTask();
     } else {
       NS_WARNING("Should know the source we were loading from!");
     }
   } else {
-    Error(nsIDOMMediaError::MEDIA_ERR_DECODE);
+    Error(nsIDOMMediaError::MEDIA_ERR_DECODE, aError);
   }
 }
 
 bool HTMLMediaElement::HasError() const
 {
   return GetError();
 }
 
 void HTMLMediaElement::LoadAborted()
 {
   Error(nsIDOMMediaError::MEDIA_ERR_ABORTED);
 }
 
-void HTMLMediaElement::Error(uint16_t aErrorCode)
+void HTMLMediaElement::Error(uint16_t aErrorCode,
+                             const MediaResult& aErrorDetails)
 {
   NS_ASSERTION(aErrorCode == nsIDOMMediaError::MEDIA_ERR_DECODE ||
                aErrorCode == nsIDOMMediaError::MEDIA_ERR_NETWORK ||
                aErrorCode == nsIDOMMediaError::MEDIA_ERR_ABORTED,
                "Only use nsIDOMMediaError codes!");
 
   // Since we have multiple paths calling into DecodeError, e.g.
   // MediaKeys::Terminated and EMEH264Decoder::Error. We should take the 1st
   // one only in order not to fire multiple 'error' events.
   if (mError) {
     return;
   }
-
-  mError = new MediaError(this, aErrorCode);
+  nsCString message;
+  if (NS_FAILED(aErrorDetails)) {
+    message = aErrorDetails.Description();
+  }
+  mError = new MediaError(this, aErrorCode, message);
+
   DispatchAsyncEvent(NS_LITERAL_STRING("error"));
   if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
     ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
     DispatchAsyncEvent(NS_LITERAL_STRING("emptied"));
   } else {
     ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
   }
   ChangeDelayLoadStatus(false);
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -174,17 +174,17 @@ public:
   virtual void FirstFrameLoaded() final override;
 
   // Called by the video decoder object, on the main thread,
   // when the resource has a network error during loading.
   virtual void NetworkError() final override;
 
   // Called by the video decoder object, on the main thread, when the
   // resource has a decode error during metadata loading or decoding.
-  virtual void DecodeError() final override;
+  virtual void DecodeError(const MediaResult& aError) final override;
 
   // Return true if error attribute is not null.
   virtual bool HasError() const final override;
 
   // Called by the video decoder object, on the main thread, when the
   // resource load has been cancelled.
   virtual void LoadAborted() final override;
 
@@ -1111,17 +1111,17 @@ protected:
    * Dispatches an error event to a child source element.
    */
   void DispatchAsyncSourceError(nsIContent* aSourceElement);
 
   /**
    * Resets the media element for an error condition as per aErrorCode.
    * aErrorCode must be one of nsIDOMHTMLMediaError codes.
    */
-  void Error(uint16_t aErrorCode);
+  void Error(uint16_t aErrorCode, const MediaResult& aErrorDetails = NS_OK);
 
   /**
    * Returns the URL spec of the currentSrc.
    **/
   void GetCurrentSpec(nsCString& aString);
 
   /**
    * Process any media fragment entries in the URI
--- a/dom/html/MediaError.cpp
+++ b/dom/html/MediaError.cpp
@@ -16,32 +16,35 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaErr
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaError)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaError)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIDOMMediaError)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMediaError)
 NS_INTERFACE_MAP_END
 
-MediaError::MediaError(HTMLMediaElement* aParent, uint16_t aCode)
+MediaError::MediaError(HTMLMediaElement* aParent, uint16_t aCode,
+                       const nsACString& aMessage)
   : mParent(aParent)
   , mCode(aCode)
+  , mMessage(aMessage)
 {
 }
 
 NS_IMETHODIMP MediaError::GetCode(uint16_t* aCode)
 {
   if (aCode)
     *aCode = Code();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP MediaError::GetMessage(nsAString& aResult)
 {
+  CopyUTF8toUTF16(mMessage, aResult);
   return NS_OK;
 }
 
 JSObject*
 MediaError::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return MediaErrorBinding::Wrap(aCx, this, aGivenProto);
 }
--- a/dom/html/MediaError.h
+++ b/dom/html/MediaError.h
@@ -17,17 +17,18 @@ namespace mozilla {
 namespace dom {
 
 class MediaError final : public nsIDOMMediaError,
                          public nsWrapperCache
 {
   ~MediaError() {}
 
 public:
-  MediaError(HTMLMediaElement* aParent, uint16_t aCode);
+  MediaError(HTMLMediaElement* aParent, uint16_t aCode,
+             const nsACString& aMessage = nsCString());
 
   // nsISupports
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaError)
 
   // nsIDOMMediaError
   NS_DECL_NSIDOMMEDIAERROR
 
@@ -43,14 +44,16 @@ public:
     return mCode;
   }
 
 private:
   RefPtr<HTMLMediaElement> mParent;
 
   // Error code
   const uint16_t mCode;
+  // Error details;
+  const nsCString mMessage;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_MediaError_h
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1011,17 +1011,17 @@ MediaDecoder::NetworkError()
   MOZ_ASSERT(IsShutdown());
 }
 
 void
 MediaDecoder::DecodeError(const MediaResult& aError)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!IsShutdown());
-  mOwner->DecodeError();
+  mOwner->DecodeError(aError);
   MOZ_ASSERT(IsShutdown());
 }
 
 void
 MediaDecoder::UpdateSameOriginStatus(bool aSameOrigin)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mSameOriginMedia = aSameOrigin;
--- a/dom/media/MediaDecoderOwner.h
+++ b/dom/media/MediaDecoderOwner.h
@@ -6,16 +6,17 @@
 #ifndef MediaDecoderOwner_h_
 #define MediaDecoderOwner_h_
 #include "AbstractMediaDecoder.h"
 #include "nsAutoPtr.h"
 
 namespace mozilla {
 
 class VideoFrameContainer;
+class MediaResult;
 
 namespace dom {
 class HTMLMediaElement;
 } // namespace dom
 
 class MediaDecoderOwner
 {
 public:
@@ -62,17 +63,17 @@ public:
   // The decoder owner should call Shutdown() on the decoder and drop the
   // reference to the decoder to prevent further calls into the decoder.
   virtual void NetworkError() = 0;
 
   // Called by the decoder object, on the main thread, when the
   // resource has a decode error during metadata loading or decoding.
   // The decoder owner should call Shutdown() on the decoder and drop the
   // reference to the decoder to prevent further calls into the decoder.
-  virtual void DecodeError() = 0;
+  virtual void DecodeError(const MediaResult& aError) = 0;
 
   // Return true if media element error attribute is not null.
   virtual bool HasError() const = 0;
 
   // Called by the video decoder object, on the main thread, when the
   // resource load has been cancelled.
   virtual void LoadAborted() = 0;
 
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -83,17 +83,17 @@ MediaKeys::Terminated()
     RefPtr<MediaKeySession>& session = iter.Data();
     session->OnClosed();
   }
   keySessions.Clear();
   MOZ_ASSERT(mKeySessions.Count() == 0);
 
   // Notify the element about that CDM has terminated.
   if (mElement) {
-    mElement->DecodeError();
+    mElement->DecodeError(NS_ERROR_DOM_MEDIA_CDM_ERR);
   }
 
   Shutdown();
 }
 
 void
 MediaKeys::Shutdown()
 {
--- a/dom/media/gtest/MockMediaDecoderOwner.h
+++ b/dom/media/gtest/MockMediaDecoderOwner.h
@@ -20,17 +20,17 @@ public:
   }
   void FireTimeUpdate(bool aPeriodic) override {}
   bool GetPaused() override { return false; }
   void MetadataLoaded(const MediaInfo* aInfo,
                       nsAutoPtr<const MetadataTags> aTags) override
   {
   }
   void NetworkError() override {}
-  void DecodeError() override {}
+  void DecodeError(const MediaResult& aError) override {}
   bool HasError() const override { return false; }
   void LoadAborted() override {}
   void PlaybackEnded() override {}
   void SeekStarted() override {}
   void SeekCompleted() override {}
   void DownloadProgressed() override {}
   void UpdateReadyState() override {}
   void FirstFrameLoaded() override {}
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -325,17 +325,17 @@ MediaSource::EndOfStream(const Optional<
     mDecoder->Ended(true);
     return;
   }
   switch (aError.Value()) {
   case MediaSourceEndOfStreamError::Network:
     mDecoder->NetworkError();
     break;
   case MediaSourceEndOfStreamError::Decode:
-    mDecoder->DecodeError();
+    mDecoder->DecodeError(NS_ERROR_DOM_MEDIA_FATAL_ERR);
     break;
   default:
     aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
   }
 }
 
 /* static */ bool
 MediaSource::IsTypeSupported(const GlobalObject& aOwner, const nsAString& aType)