Bug 1329561 - MediaContentType is always valid - r?jya draft
authorGerald Squelart <gsquelart@mozilla.com>
Thu, 01 Dec 2016 12:56:11 +1100
changeset 460014 5a3de4fbf7c9672c6f594593d47ae79c36681d72
parent 459754 6e5f04db4c4a95ad509224b238fd3ad464281e6b
child 460015 096dcea5692ddbcae77e6736f29a0ea7241e7b5c
push id41345
push usergsquelart@mozilla.com
push dateThu, 12 Jan 2017 20:46:28 +0000
reviewersjya
bugs1329561
milestone53.0a1
Bug 1329561 - MediaContentType is always valid - r?jya MediaContentType can only be created through MakeMediaContentType(), which returns a Maybe<MediaContentType>. If the return value is Nothing, parsing failed. Otherwise the contained MediaContentType object is guaranteed to be valid; E.g., GetMIMEType() will always return a non-empty string. Note that this interface will change a lot in the following bugs&patches, so please don't worry about the 'Get' in the never-failing GetMIMEType(), it will be gone soon! MozReview-Commit-ID: IjGKkQ6RVd4
dom/html/HTMLMediaElement.cpp
dom/media/DecoderTraits.cpp
dom/media/MediaContentType.cpp
dom/media/MediaContentType.h
dom/media/mediasource/MediaSource.cpp
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4468,18 +4468,21 @@ void HTMLMediaElement::UnbindFromTree(bo
   RunInStableState(task);
 }
 
 /* static */
 CanPlayStatus
 HTMLMediaElement::GetCanPlay(const nsAString& aType,
                              DecoderDoctorDiagnostics* aDiagnostics)
 {
-  MediaContentType contentType{aType};
-  return DecoderTraits::CanHandleContentType(contentType, aDiagnostics);
+  Maybe<MediaContentType> contentType = MakeMediaContentType(aType);
+  if (!contentType) {
+    return CANPLAY_NO;
+  }
+  return DecoderTraits::CanHandleContentType(*contentType, aDiagnostics);
 }
 
 NS_IMETHODIMP
 HTMLMediaElement::CanPlayType(const nsAString& aType, nsAString& aResult)
 {
   DecoderDoctorDiagnostics diagnostics;
   CanPlayStatus canPlay = GetCanPlay(aType, &diagnostics);
   diagnostics.StoreFormatDiagnostics(
--- a/dom/media/DecoderTraits.cpp
+++ b/dom/media/DecoderTraits.cpp
@@ -139,18 +139,21 @@ IsMP4SupportedType(const MediaContentTyp
                    DecoderDoctorDiagnostics* aDiagnostics)
 {
   return MP4Decoder::CanHandleMediaType(aParsedType, aDiagnostics);
 }
 static bool
 IsMP4SupportedType(const nsACString& aType,
                    DecoderDoctorDiagnostics* aDiagnostics)
 {
-  MediaContentType contentType{aType};
-  return IsMP4SupportedType(contentType, aDiagnostics);
+  Maybe<MediaContentType> contentType = MakeMediaContentType(aType);
+  if (!contentType) {
+    return false;
+  }
+  return IsMP4SupportedType(*contentType, aDiagnostics);
 }
 #endif
 
 /* static */ bool
 DecoderTraits::IsMP4TypeAndEnabled(const nsACString& aType,
                                    DecoderDoctorDiagnostics* aDiagnostics)
 {
 #ifdef MOZ_FMP4
@@ -188,17 +191,16 @@ IsFlacSupportedType(const nsACString& aT
   return FlacDecoder::CanHandleMediaType(aType, aCodecs);
 }
 
 static
 CanPlayStatus
 CanHandleCodecsType(const MediaContentType& aType,
                     DecoderDoctorDiagnostics* aDiagnostics)
 {
-  MOZ_ASSERT(aType.IsValid());
   // We should have been given a codecs string, though it may be empty.
   MOZ_ASSERT(aType.HaveCodecs());
 
   char const* const* codecList = nullptr;
   if (IsOggTypeAndEnabled(aType.GetMIMEType())) {
     if (IsOggSupportedType(aType.GetMIMEType(), aType.GetCodecs())) {
       return CANPLAY_YES;
     } else {
@@ -334,20 +336,16 @@ CanHandleMediaType(const MediaContentTyp
   return CANPLAY_NO;
 }
 
 /* static */
 CanPlayStatus
 DecoderTraits::CanHandleContentType(const MediaContentType& aContentType,
                                     DecoderDoctorDiagnostics* aDiagnostics)
 {
-  if (!aContentType.IsValid()) {
-    return CANPLAY_NO;
-  }
-
   return CanHandleMediaType(aContentType, aDiagnostics);
 }
 
 /* static */
 bool DecoderTraits::ShouldHandleMediaType(const char* aMIMEType,
                                           DecoderDoctorDiagnostics* aDiagnostics)
 {
   if (IsWaveSupportedType(nsDependentCString(aMIMEType))) {
@@ -365,18 +363,21 @@ bool DecoderTraits::ShouldHandleMediaTyp
   if (nsDependentCString(aMIMEType).EqualsASCII("video/quicktime")) {
     RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
     if (pluginHost &&
         pluginHost->HavePluginForType(nsDependentCString(aMIMEType))) {
       return false;
     }
   }
 
-  MediaContentType parsed{nsDependentCString(aMIMEType)};
-  return CanHandleMediaType(parsed, aDiagnostics)
+  Maybe<MediaContentType> parsed = MakeMediaContentType(aMIMEType);
+  if (!parsed) {
+    return false;
+  }
+  return CanHandleMediaType(*parsed, aDiagnostics)
          != CANPLAY_NO;
 }
 
 // Instantiates but does not initialize decoder.
 static
 already_AddRefed<MediaDecoder>
 InstantiateDecoder(const nsACString& aType,
                    MediaDecoderOwner* aOwner,
--- a/dom/media/MediaContentType.cpp
+++ b/dom/media/MediaContentType.cpp
@@ -5,27 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaContentType.h"
 
 #include "nsContentTypeParser.h"
 
 namespace mozilla {
 
-MediaContentType::MediaContentType(const nsAString& aType)
-{
-  Populate(aType);
-}
-
-MediaContentType::MediaContentType(const nsACString& aType)
-{
-  NS_ConvertUTF8toUTF16 typeUTF16(aType);
-  Populate(typeUTF16);
-}
-
 static int32_t
 GetParameterAsNumber(const nsContentTypeParser& aParser,
                      const char* aParameter,
                      const int32_t aErrorReturn)
 {
   nsAutoString parameterString;
   nsresult rv = aParser.GetParameter(aParameter, parameterString);
   if (NS_FAILED_impl(rv)) {
@@ -33,28 +22,59 @@ GetParameterAsNumber(const nsContentType
   }
   int32_t number = parameterString.ToInteger(&rv);
   if (MOZ_UNLIKELY(NS_FAILED_impl(rv))) {
     return aErrorReturn;
   }
   return number;
 }
 
-void
+bool
 MediaContentType::Populate(const nsAString& aType)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsContentTypeParser parser(aType);
   nsAutoString mime;
   nsresult rv = parser.GetType(mime);
-  if (NS_SUCCEEDED(rv)) {
-    mMIMEType = NS_ConvertUTF16toUTF8(mime);
+  if (!NS_SUCCEEDED(rv) || mime.IsEmpty()) {
+    return false;
   }
 
+  mMIMEType = NS_ConvertUTF16toUTF8(mime);
+
   rv = parser.GetParameter("codecs", mCodecs);
   mHaveCodecs = NS_SUCCEEDED(rv);
 
   mWidth = GetParameterAsNumber(parser, "width", -1);
   mHeight = GetParameterAsNumber(parser, "height", -1);
   mFramerate = GetParameterAsNumber(parser, "framerate", -1);
   mBitrate = GetParameterAsNumber(parser, "bitrate", -1);
+
+  return true;
+}
+
+Maybe<MediaContentType>
+MakeMediaContentType(const nsAString& aType)
+{
+  Maybe<MediaContentType> type{Some(MediaContentType{})};
+  if (!type->Populate(aType)) {
+    type.reset();
+  }
+  return type;
+}
+
+Maybe<MediaContentType>
+MakeMediaContentType(const nsACString& aType)
+{
+  return MakeMediaContentType(NS_ConvertUTF8toUTF16(aType));
+}
+
+Maybe<MediaContentType>
+MakeMediaContentType(const char* aType)
+{
+  if (!aType) {
+    return Nothing();
+  }
+  return MakeMediaContentType(nsDependentCString(aType));
 }
 
 } // namespace mozilla
--- a/dom/media/MediaContentType.h
+++ b/dom/media/MediaContentType.h
@@ -12,49 +12,47 @@
 
 namespace mozilla {
 
 // Structure containing pre-parsed content type parameters, e.g.:
 // MIME type, optional codecs, etc.
 class MediaContentType
 {
 public:
-  // Parse UTF16 string to extract parameters.
-  explicit MediaContentType(const nsAString& aType);
-  // Parse UTF8 string to extract parameters.
-  explicit MediaContentType(const nsACString& aType);
-
-  bool IsValid() const { return !GetMIMEType().IsEmpty(); }
-
-  // MIME type. Empty if construction arguments could not be parsed.
+  // MIME type. Guaranteed not to be empty.
   const nsACString& GetMIMEType() const { return mMIMEType; }
 
   // Was there an explicit 'codecs' parameter provided?
   bool HaveCodecs() const { return mHaveCodecs; }
   // Codecs. May be empty if not provided or explicitly provided as empty.
   const nsAString& GetCodecs() const { return mCodecs; }
 
   // Sizes and rates.
   Maybe<int32_t> GetWidth() const { return GetMaybeNumber(mWidth); }
   Maybe<int32_t> GetHeight() const { return GetMaybeNumber(mHeight); }
   Maybe<int32_t> GetFramerate() const { return GetMaybeNumber(mFramerate); }
   Maybe<int32_t> GetBitrate() const { return GetMaybeNumber(mBitrate); }
 
 private:
-  void Populate(const nsAString& aType);
+  friend Maybe<MediaContentType> MakeMediaContentType(const nsAString& aType);
+  bool Populate(const nsAString& aType);
 
   Maybe<int32_t> GetMaybeNumber(int32_t aNumber) const
   {
     return (aNumber < 0) ? Maybe<int32_t>(Nothing()) : Some(int32_t(aNumber));
   }
 
-  nsCString mMIMEType; // UTF8 MIME type. Empty if parsing failed.
+  nsCString mMIMEType; // UTF8 MIME type.
   bool mHaveCodecs; // If false, mCodecs must be empty.
   nsString mCodecs;
   int32_t mWidth; // -1 if not provided.
   int32_t mHeight; // -1 if not provided.
   int32_t mFramerate; // -1 if not provided.
   int32_t mBitrate; // -1 if not provided.
 };
 
+Maybe<MediaContentType> MakeMediaContentType(const nsAString& aType);
+Maybe<MediaContentType> MakeMediaContentType(const nsACString& aType);
+Maybe<MediaContentType> MakeMediaContentType(const char* aType);
+
 } // namespace mozilla
 
 #endif // MediaContentType_h_
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -88,29 +88,29 @@ namespace dom {
 /* static */
 nsresult
 MediaSource::IsTypeSupported(const nsAString& aType, DecoderDoctorDiagnostics* aDiagnostics)
 {
   if (aType.IsEmpty()) {
     return NS_ERROR_DOM_TYPE_ERR;
   }
 
-  MediaContentType contentType{aType};
-  if (!contentType.IsValid()) {
+  Maybe<MediaContentType> contentType = MakeMediaContentType(aType);
+  if (!contentType) {
     return NS_ERROR_DOM_TYPE_ERR;
   }
 
-  if (DecoderTraits::CanHandleContentType(contentType, aDiagnostics)
+  if (DecoderTraits::CanHandleContentType(*contentType, aDiagnostics)
       == CANPLAY_NO) {
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   }
 
   // Now we know that this media type could be played.
   // MediaSource imposes extra restrictions, and some prefs.
-  const nsACString& mimeType = contentType.GetMIMEType();
+  const nsACString& mimeType = contentType->GetMIMEType();
   if (mimeType.EqualsASCII("video/mp4") || mimeType.EqualsASCII("audio/mp4")) {
     if (!Preferences::GetBool("media.mediasource.mp4.enabled", false)) {
       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     }
     return NS_OK;
   }
   if (mimeType.EqualsASCII("video/webm")) {
     if (!(Preferences::GetBool("media.mediasource.webm.enabled", false) ||
@@ -232,22 +232,22 @@ MediaSource::AddSourceBuffer(const nsASt
   if (mSourceBuffers->Length() >= MAX_SOURCE_BUFFERS) {
     aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
     return nullptr;
   }
   if (mReadyState != MediaSourceReadyState::Open) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
-  MediaContentType contentType{aType};
-  if (!contentType.IsValid()) {
+  Maybe<MediaContentType> contentType = MakeMediaContentType(aType);
+  if (!contentType) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
-  const nsACString& mimeType = contentType.GetMIMEType();
+  const nsACString& mimeType = contentType->GetMIMEType();
   RefPtr<SourceBuffer> sourceBuffer = new SourceBuffer(this, mimeType);
   if (!sourceBuffer) {
     aRv.Throw(NS_ERROR_FAILURE); // XXX need a better error here
     return nullptr;
   }
   mSourceBuffers->Append(sourceBuffer);
   MSE_DEBUG("sourceBuffer=%p", sourceBuffer.get());
   return sourceBuffer.forget();