commit 596b5c2cfe64d63c2ac5e62b0ddf2ec94ce8acb8
parent e6ebd3a140fedea61fe28872da89a91a1c9c3490
Author: az <azebrowski@mozilla.com>
Date: Mon, 1 Dec 2025 17:07:05 +0000
Bug 1993541 - Part 3: Add MediaCapabilitiesValidation.{h,cpp} to validate MediaCapabilities API types per spec. r=pehrsons,media-playback-reviewers,padenot
Differential Revision: https://phabricator.services.mozilla.com/D263717
Diffstat:
4 files changed, 558 insertions(+), 1 deletion(-)
diff --git a/dom/media/mediacapabilities/MediaCapabilities.cpp b/dom/media/mediacapabilities/MediaCapabilities.cpp
@@ -36,7 +36,7 @@
#include "mozilla/layers/KnowsCompositor.h"
#include "nsContentUtils.h"
-static mozilla::LazyLogModule sMediaCapabilitiesLog("MediaCapabilities");
+mozilla::LazyLogModule sMediaCapabilitiesLog("MediaCapabilities");
#define LOG(msg, ...) \
DDMOZ_LOG(sMediaCapabilitiesLog, LogLevel::Debug, msg, ##__VA_ARGS__)
diff --git a/dom/media/mediacapabilities/MediaCapabilitiesValidation.cpp b/dom/media/mediacapabilities/MediaCapabilitiesValidation.cpp
@@ -0,0 +1,495 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+#include "MediaCapabilitiesValidation.h"
+
+#include <algorithm>
+#include <array>
+#include <cmath>
+#include <utility>
+
+#include "MediaMIMETypes.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Result.h"
+#include "mozilla/Variant.h"
+#include "mozilla/dom/MediaCapabilitiesBinding.h"
+#include "mozilla/dom/Promise.h"
+#include "nsReadableUtils.h"
+
+extern mozilla::LazyLogModule sMediaCapabilitiesLog;
+#define LOG(args) MOZ_LOG(sMediaCapabilitiesLog, LogLevel::Debug, args)
+
+namespace mozilla::mediacaps {
+using dom::AudioConfiguration;
+using dom::MediaConfiguration;
+using dom::MediaDecodingConfiguration;
+using dom::MediaDecodingType;
+using dom::MediaEncodingConfiguration;
+using dom::MediaEncodingType;
+using dom::MSG_INVALID_MEDIA_AUDIO_CONFIGURATION;
+using dom::MSG_INVALID_MEDIA_VIDEO_CONFIGURATION;
+using dom::MSG_MISSING_REQUIRED_DICTIONARY_MEMBER;
+using dom::Promise;
+using dom::VideoConfiguration;
+
+static nsAutoCString GetMIMEDebugString(const MediaConfiguration& aConfig);
+static bool IsContainerType(const MediaExtendedMIMEType& aMime);
+static bool IsSingleCodecType(const MediaExtendedMIMEType& aMime);
+
+// If encodingOrDecodingType is webrtc (MediaEncodingType) or webrtc
+// (MediaDecodingType) and mimeType is not one that is used with RTP
+// (as defined in the specifications of the corresponding RTP payload formats
+// [IANA-MEDIA-TYPES] [RFC6838]), return unsupported.
+//
+// Unsupported: iLBC, iSAC (Chrome, Safari)
+// https://developer.mozilla.org/en-US/docs/Web/Media/Guides/Formats/WebRTC_codecs
+static const std::array kSingleWebRTCCodecTypes = {
+ // "audio/ilbc"_ns, "audio/isac"_ns,
+ "audio/g711-alaw"_ns, "audio/g711-mlaw"_ns, "audio/g722"_ns,
+ "audio/opus"_ns, "audio/pcma"_ns, "audio/pcmu"_ns,
+ "video/av1"_ns, "video/h264"_ns, "video/vp8"_ns,
+ "video/vp9"_ns,
+};
+
+static const std::array kContainerTypes = {"video/mkv"_ns, "video/mp4"_ns,
+ "video/webm"_ns, "audio/ogg"_ns,
+ "audio/mp4"_ns, "audio/webm"_ns};
+
+// https://w3c.github.io/media-capabilities/#check-mime-type-support
+ValidationResult CheckMIMETypeSupport(const MediaExtendedMIMEType& aMime,
+ const AVType& aAVType,
+ const MediaType& aMediaType) {
+ // Step 1: If encodingOrDecodingType is webrtc (MediaEncodingType) or
+ // webrtc (MediaDecodingType) and mimeType is not one that is used with
+ // RTP (as defined in the specifications of the corresponding RTP payload
+ // formats [IANA-MEDIA-TYPES] [RFC6838]), return unsupported.
+ // TODO bug 1825286
+
+ // Step 2: If colorGamut is present and is not valid for mimeType, return
+ // unsupported.
+ // TODO bug 1825286
+ return Ok();
+}
+
+// Checks MIME type validity as per:
+// https://w3c.github.io/media-capabilities/#check-mime-type-validity
+// NOTE: Open issue, https://github.com/w3c/media-capabilities/issues/238
+// "Do WebRTC encoding/decoding types have the single-codec restrictions?"
+static ValidationResult CheckMIMETypeValidity(
+ const MediaExtendedMIMEType& aMime, const AVType& aAVType,
+ const MediaType& aMediaType) {
+ // Step 1: If the type of mimeType per [RFC9110] is neither
+ // media nor application, return false.
+ const MediaMIMEType& mimetype = aMime.Type();
+ if (!mimetype.HasAudioMajorType() && !mimetype.HasVideoMajorType() &&
+ !mimetype.HasApplicationMajorType()) {
+ ValidationResult err =
+ Err(aAVType == AVType::AUDIO ? ValidationError::InvalidAudioType
+ : ValidationError::InvalidVideoType);
+ LOG(
+ ("[Invalid MIME Validity #1, %s] Rejecting - not media, not "
+ "application %s",
+ EnumValueToString(err.unwrapErr()), aMime.OriginalString().get()));
+ return err;
+ }
+
+ // The following two steps don't appear to be explicitly defined in the spec
+ // but are required for some WPT passes and seem like they'd make the most
+ // sense to have here. The tests in question can be found here:
+ // https://searchfox.org/firefox-main/rev/cd639e07f74b203d72b0f4a2bea757ae9e10401a/testing/web-platform/tests/media-capabilities/decodingInfo.any.js#140-161
+
+ // Step 1a?: Test that decodingInfo rejects if the audio configuration
+ // contentType is of type video
+ if (aAVType == AVType::AUDIO && !aMime.Type().HasAudioMajorType()) {
+ ValidationResult err = Err(ValidationError::InvalidAudioType);
+ LOG(("[Invalid MIME Validity #1a?, %s] Rejecting '%s'",
+ EnumValueToString(err.unwrapErr()), aMime.OriginalString().get()));
+ return err;
+ }
+
+ // Step 1b?: Test that decodingInfo rejects if the video configuration
+ // contentType is of type audio
+ if (aAVType == AVType::VIDEO && !aMime.Type().HasVideoMajorType()) {
+ ValidationResult err = Err(ValidationError::InvalidVideoType);
+ LOG(("[Invalid MIME Validity #1b?, %s] Rejecting '%s'",
+ EnumValueToString(err.unwrapErr()), aMime.OriginalString().get()));
+ return err;
+ }
+
+ // Step 2: If the combined type and subtype members of mimeType allow a
+ // single media codec and the parameters member of mimeType is not
+ // empty, return false.
+ //
+ // (NOTE: WEBRTC EXCEPTION, SEE ISSUE)
+ // https://github.com/w3c/media-capabilities/issues/238
+ // TODO bug 1825286 (WebRTC)
+ const size_t numParams = aMime.GetParameterCount();
+ if (IsSingleCodecType(aMime) && numParams != 0) {
+ ValidationResult err = Err(ValidationError::SingleCodecHasParams);
+ LOG(("[Invalid MIME Validity #2, %s] Rejecting '%s'",
+ EnumValueToString(err.unwrapErr()), aMime.OriginalString().get()));
+ return err;
+ }
+
+ // Step 3: If the combined type and subtype members of mimeType allow
+ // multiple media codecs, run the following steps:
+ if (IsContainerType(aMime)) {
+ // Step 3.1: If the parameters member of mimeType does not contain a single
+ // key named "codecs", return false.
+ if ((numParams != 1) || !aMime.HaveCodecs()) {
+ ValidationResult err = Err(ValidationError::ContainerMissingCodecsParam);
+ LOG(("[Invalid MIME Validity #3.1, %s] Rejecting '%s'",
+ EnumValueToString(err.unwrapErr()), aMime.OriginalString().get()));
+ return err;
+ }
+
+ // Step 3.2: If the value of mimeType.parameters["codecs"] does not
+ // describe a single media codec, return false.
+ const auto& codecs = aMime.Codecs();
+ if (!aMime.HaveCodecs() || codecs.IsEmpty() ||
+ codecs.AsString().FindChar(',') != kNotFound) {
+ ValidationResult err = Err(ValidationError::ContainerCodecsNotSingle);
+ LOG(("[Invalid MIME #3.2, %s] Rejecting '%s'",
+ EnumValueToString(err.unwrapErr()), aMime.OriginalString().get()));
+ return err;
+ }
+ }
+
+ // Step 4: Return true
+ return Ok();
+}
+
+// https://w3c.github.io/media-capabilities/#audioconfiguration
+ValidationResult IsValidAudioConfiguration(const AudioConfiguration& aConfig,
+ const MediaType& aType) {
+ // Step 1: Let mimeType be the result of running parse a MIME type with
+ // configuration’s contentType.
+ const Maybe<MediaExtendedMIMEType> mime =
+ MakeMediaExtendedMIMEType(aConfig.mContentType);
+
+ // Step 2: If mimeType is failure, return false.
+ if (!mime) {
+ ValidationResult err = Err(ValidationError::InvalidAudioType);
+ LOG(("[Invalid AudioConfiguration #2, %s] Rejecting '%s'\n",
+ EnumValueToString(err.unwrapErr()),
+ NS_ConvertUTF16toUTF8(aConfig.mContentType).get()));
+ return err;
+ }
+
+ // Return the result of running check MIME type validity with mimeType and
+ // audio. The channels member represents the audio channels used by the audio
+ // track. channels is only applicable to the decoding types media-source,
+ // file, and webrtc and the encoding type webrtc.
+ return CheckMIMETypeValidity(mime.ref(), AVType::AUDIO, aType);
+}
+
+// https://w3c.github.io/media-capabilities/#audioconfiguration
+// To check if a VideoConfiguration configuration is a valid video
+// configuration, the following steps MUST be run...
+template <typename CodingType>
+ValidationResult IsValidVideoConfiguration(const VideoConfiguration& aConfig,
+ const CodingType& aType) {
+ static_assert(std::is_same_v<std::decay_t<CodingType>, MediaEncodingType> ||
+ std::is_same_v<CodingType, MediaDecodingType>,
+ "tType must be MediaEncodingType or MediaDecodingType");
+
+ // Step 1: If framerate is not finite or is not greater than 0,
+ // return false and abort these steps.
+ if (!isfinite(aConfig.mFramerate) || !(aConfig.mFramerate > 0)) {
+ ValidationResult err = Err(ValidationError::FramerateInvalid);
+ LOG(("[Invalid VideoConfiguration (Framerate, %s) #1] Rejecting '%s'\n",
+ EnumValueToString(err.unwrapErr()),
+ NS_ConvertUTF16toUTF8(aConfig.mContentType).get()));
+ return err;
+ }
+
+ // Step 2: If an optional member is specified for a MediaDecodingType or
+ // MediaEncodingType to which it’s not applicable, return false and abort
+ // these steps. See applicability rules in the member definitions below.
+ if constexpr (std::is_same_v<CodingType, MediaDecodingType>) {
+ // hdrMetadataType is only applicable to MediaDecodingConfiguration
+ // for types media-source and file.
+ if (aConfig.mHdrMetadataType.WasPassed() &&
+ aType != MediaDecodingType::File &&
+ aType != MediaDecodingType::Media_source) {
+ ValidationResult err = Err(ValidationError::InapplicableMember);
+ LOG(("[Invalid VideoConfiguration (HDR, %s) #2] Rejecting '%s'\n",
+ EnumValueToString(err.unwrapErr()),
+ NS_ConvertUTF16toUTF8(aConfig.mContentType).get()));
+ return err;
+ }
+ // colorGamut is only applicable to
+ // MediaDecodingConfiguration for types media-source and file.
+ if (aConfig.mColorGamut.WasPassed() && aType != MediaDecodingType::File &&
+ aType != MediaDecodingType::Media_source) {
+ ValidationResult err = Err(ValidationError::InapplicableMember);
+ LOG(("[Invalid VideoConfiguration (Color Gamut, %s) #2] Rejecting '%s'\n",
+ EnumValueToString(err.unwrapErr()),
+ NS_ConvertUTF16toUTF8(aConfig.mContentType).get()));
+ return err;
+ }
+
+ // transferFunction is only
+ // applicable to MediaDecodingConfiguration for types media-source and file.
+ if (aConfig.mTransferFunction.WasPassed() &&
+ aType != MediaDecodingType::File &&
+ aType != MediaDecodingType::Media_source) {
+ ValidationResult err = Err(ValidationError::InapplicableMember);
+ LOG(
+ ("[Invalid VideoConfiguration (Transfer Function, %s) #2] Rejecting "
+ "'%s'\n",
+ EnumValueToString(err.unwrapErr()),
+ NS_ConvertUTF16toUTF8(aConfig.mContentType).get()));
+ return err;
+ }
+ }
+
+ // ScalabilityMode is only applicable to MediaEncodingConfiguration
+ // for type webrtc.
+ // TODO bug 1825286
+
+ // Step 3: Let mimeType be the result of running parse a MIME type with
+ // configuration’s contentType.
+ const Maybe<MediaExtendedMIMEType> mime =
+ MakeMediaExtendedMIMEType(aConfig.mContentType);
+
+ // Step 4: If mimeType is failure, return false.
+ if (!mime) {
+ ValidationResult err = Err(ValidationError::InvalidVideoType);
+ LOG(("[Invalid VideoConfiguration (MIME failure, %s) #4] Rejecting '%s'\n",
+ EnumValueToString(err.unwrapErr()),
+ NS_ConvertUTF16toUTF8(aConfig.mContentType).get()));
+ return err;
+ }
+
+ // Step 5: Return the result of running check MIME type validity
+ // with mimeType and video.
+ return CheckMIMETypeValidity(mime.ref(), AVType::VIDEO, AsVariant(aType));
+}
+
+template ValidationResult IsValidVideoConfiguration<MediaEncodingType>(
+ const VideoConfiguration&, const MediaEncodingType&);
+template ValidationResult IsValidVideoConfiguration<MediaDecodingType>(
+ const VideoConfiguration&, const MediaDecodingType&);
+
+ValidationResult IsValidVideoConfiguration(const VideoConfiguration& aConfig,
+ const MediaType& aType) {
+ return aType.match(
+ [&](const MediaEncodingType& t) {
+ return IsValidVideoConfiguration(aConfig, t);
+ },
+ [&](const MediaDecodingType& t) {
+ return IsValidVideoConfiguration(aConfig, t);
+ });
+}
+
+// https://w3c.github.io/media-capabilities/#mediaconfiguration
+ValidationResult IsValidMediaConfiguration(const MediaConfiguration& aConfig,
+ const MediaType& aType) {
+ // Step 1: audio and/or video MUST exist.
+ if (!aConfig.mVideo.WasPassed() && !aConfig.mAudio.WasPassed()) {
+ ValidationResult err = Err(ValidationError::MissingType);
+ LOG(("[Invalid Media Configuration (No A/V, %s) #1] '%s'",
+ EnumValueToString(err.unwrapErr()),
+ GetMIMEDebugString(aConfig).get()));
+ return err;
+ }
+
+ // Step 2: audio MUST be a valid audio configuration if it exists.
+ if (aConfig.mAudio.WasPassed()) {
+ auto rv = IsValidAudioConfiguration(aConfig.mAudio.Value(), aType);
+ if (rv.isErr()) {
+ LOG(("[Invalid Media Configuration (Invalid Audio, %s) #2] '%s'",
+ EnumValueToString(rv.unwrapErr()),
+ GetMIMEDebugString(aConfig).get()));
+ return rv;
+ }
+ }
+
+ // Step 3: video MUST be a valid video configuration if it exists.
+ if (aConfig.mVideo.WasPassed()) {
+ auto rv = IsValidVideoConfiguration(aConfig.mVideo.Value(), aType);
+ if (rv.isErr()) {
+ LOG(("[Invalid Media Configuration (Invalid Video, %s) #3] '%s'",
+ EnumValueToString(rv.unwrapErr()),
+ GetMIMEDebugString(aConfig).get()));
+ return rv;
+ }
+ }
+ return Ok();
+}
+
+// No specific validation steps in the spec...
+ValidationResult IsValidMediaEncodingConfiguration(
+ const MediaEncodingConfiguration& aConfig) {
+ return IsValidMediaConfiguration(aConfig, AsVariant(aConfig.mType));
+}
+
+// https://w3c.github.io/media-capabilities/#mediaconfiguration
+ValidationResult IsValidMediaDecodingConfiguration(
+ const MediaDecodingConfiguration& aConfig) {
+ // For a MediaDecodingConfiguration to be a valid MediaDecodingConfiguration,
+ // all of the following conditions MUST be true:
+
+ // Step 1: It MUST be a valid MediaConfiguration.
+ auto base = IsValidMediaConfiguration(aConfig, AsVariant(aConfig.mType));
+ if (base.isErr()) {
+ LOG(
+ ("[Invalid MediaDecodingConfiguration (Invalid MediaConfiguration, %s) "
+ "#1]",
+ EnumValueToString(base.unwrapErr())));
+ return base;
+ }
+ // Step 2: If keySystemConfiguration exists...
+ if (aConfig.mKeySystemConfiguration.WasPassed()) {
+ const auto& keySystemConfig = aConfig.mKeySystemConfiguration.Value();
+
+ // Step 2.1: The type MUST be media-source or file.
+ if (aConfig.mType != MediaDecodingType::File &&
+ aConfig.mType != MediaDecodingType::Media_source) {
+ ValidationResult err = Err(ValidationError::KeySystemWrongType);
+ LOG(("[Invalid MediaDecodingConfiguration (keysystem, %s) #2.1]",
+ EnumValueToString(err.unwrapErr())));
+ return err;
+ }
+
+ // Step 2.2: If keySystemConfiguration.audio exists, audio MUST also exist.
+ if (keySystemConfig.mAudio.WasPassed() && !aConfig.mAudio.WasPassed()) {
+ ValidationResult err = Err(ValidationError::KeySystemAudioMissing);
+ LOG(("[Invalid MediaDecodingConfiguration (keysystem, %s) #2.2]",
+ EnumValueToString(err.unwrapErr())));
+ return err;
+ }
+
+ // Step 2.3: If keySystemConfiguration.video exists, video MUST also exist.
+ if (keySystemConfig.mVideo.WasPassed() && !aConfig.mVideo.WasPassed()) {
+ ValidationResult err = Err(ValidationError::KeySystemVideoMissing);
+ LOG(("[Invalid MediaDecodingConfiguration (keysystem, %s) #2.3]",
+ EnumValueToString(err.unwrapErr())));
+ return err;
+ }
+ }
+ return Ok();
+}
+
+/////////////////////////////////
+// Helper functions begin here //
+/////////////////////////////////
+
+void RejectWithValidationResult(Promise* aPromise, const ValidationError aErr) {
+ switch (aErr) {
+ case ValidationError::MissingType:
+ aPromise->MaybeRejectWithTypeError(
+ "'audio' or 'video' member of argument of MediaCapabilities");
+ return;
+ case ValidationError::InvalidAudioConfiguration:
+ aPromise->MaybeRejectWithTypeError("Invalid AudioConfiguration!");
+ return;
+ case ValidationError::InvalidAudioType:
+ aPromise->MaybeRejectWithTypeError(
+ "Invalid AudioConfiguration MIME type");
+ return;
+ case ValidationError::InvalidVideoConfiguration:
+ aPromise->MaybeRejectWithTypeError("Invalid VideoConfiguration!");
+ return;
+ case ValidationError::InvalidVideoType:
+ aPromise->MaybeRejectWithTypeError("Invalid Video MIME type");
+ return;
+ case ValidationError::SingleCodecHasParams:
+ aPromise->MaybeRejectWithTypeError("Single codec has parameters");
+ return;
+ case ValidationError::ContainerMissingCodecsParam:
+ aPromise->MaybeRejectWithTypeError("Container missing codec parameters");
+ return;
+ case ValidationError::ContainerCodecsNotSingle:
+ aPromise->MaybeRejectWithTypeError("Container has more than one codec");
+ return;
+ case ValidationError::FramerateInvalid:
+ aPromise->MaybeRejectWithTypeError("Invalid frame rate");
+ return;
+ case ValidationError::InapplicableMember:
+ aPromise->MaybeRejectWithTypeError("Inapplicable member");
+ return;
+ case ValidationError::KeySystemWrongType:
+ case ValidationError::KeySystemAudioMissing:
+ case ValidationError::KeySystemVideoMissing:
+ aPromise->MaybeRejectWithTypeError("Invalid keysystem configuration");
+ return;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unhandled MediaCapabilities validation error!");
+ return;
+ }
+}
+
+void ThrowWithValidationResult(ErrorResult& aRv, const ValidationError aErr) {
+ switch (aErr) {
+ case ValidationError::MissingType:
+ aRv.ThrowTypeError<MSG_MISSING_REQUIRED_DICTIONARY_MEMBER>(
+ "'audio' or 'video' member of argument of MediaCapabilities");
+ return;
+ case ValidationError::InvalidAudioConfiguration:
+ aRv.ThrowTypeError<MSG_INVALID_MEDIA_AUDIO_CONFIGURATION>();
+ return;
+ case ValidationError::InvalidAudioType:
+ case ValidationError::KeySystemAudioMissing:
+ aRv.ThrowTypeError<MSG_INVALID_MEDIA_AUDIO_CONFIGURATION>();
+ return;
+ case ValidationError::InvalidVideoConfiguration:
+ case ValidationError::InvalidVideoType:
+ case ValidationError::SingleCodecHasParams:
+ case ValidationError::ContainerMissingCodecsParam:
+ case ValidationError::ContainerCodecsNotSingle:
+ case ValidationError::FramerateInvalid:
+ case ValidationError::InapplicableMember:
+ aRv.ThrowTypeError<MSG_INVALID_MEDIA_VIDEO_CONFIGURATION>();
+ return;
+ case ValidationError::KeySystemWrongType:
+ case ValidationError::KeySystemVideoMissing:
+ aRv.ThrowTypeError<MSG_INVALID_MEDIA_VIDEO_CONFIGURATION>();
+ return;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unhandled MediaCapabilities validation error!");
+ return;
+ }
+}
+
+template <size_t N>
+static bool MimePrefixStartsWith(
+ const MediaExtendedMIMEType& aMime,
+ const std::array<nsLiteralCString, N>& aPrefixes) {
+ const nsACString& s = aMime.OriginalString();
+ return std::any_of(aPrefixes.begin(), aPrefixes.end(), [&](const auto& p) {
+ return StringBeginsWith(s, p, nsCaseInsensitiveCStringComparator);
+ });
+}
+static bool IsContainerType(const MediaExtendedMIMEType& aMime) {
+ return MimePrefixStartsWith(aMime, kContainerTypes);
+}
+static bool IsSingleCodecType(const MediaExtendedMIMEType& aMime) {
+ return MimePrefixStartsWith(aMime, kSingleWebRTCCodecTypes);
+}
+
+static nsAutoCString GetMIMEDebugString(const MediaConfiguration& aConfig) {
+ nsAutoCString result;
+ result.SetCapacity(64);
+ result.AssignLiteral("Audio MIME: ");
+ if (aConfig.mAudio.WasPassed()) {
+ result.Append(NS_ConvertUTF16toUTF8(aConfig.mAudio.Value().mContentType));
+ } else {
+ result.AppendLiteral("(none)");
+ }
+ result.AppendLiteral(" Video MIME: ");
+ if (aConfig.mVideo.WasPassed()) {
+ result.Append(NS_ConvertUTF16toUTF8(aConfig.mVideo.Value().mContentType));
+ } else {
+ result.AppendLiteral("(none)");
+ }
+ return result;
+}
+
+} // namespace mozilla::mediacaps
+#undef LOG
diff --git a/dom/media/mediacapabilities/MediaCapabilitiesValidation.h b/dom/media/mediacapabilities/MediaCapabilitiesValidation.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+#ifndef DOM_MEDIA_MEDIACAPABILITIES_MEDIACAPABILITIESVALIDATION_H_
+#define DOM_MEDIA_MEDIACAPABILITIES_MEDIACAPABILITIESVALIDATION_H_
+#include "mozilla/DefineEnum.h"
+#include "mozilla/Result.h"
+#include "mozilla/Variant.h"
+// Media Capabilities API spec validation check functions.
+// These functions are used as a first pass to filter out non-compliant
+// configurations according to the spec. Lower-level SW/HW compability checks
+// are handled in MediaCapabilities.cpp.
+//
+// See also:
+// https://github.com/w3c/media-capabilities
+// commit: b9d2f30c00c534ac44179f546bc60f421ce78070
+namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+class Promise;
+struct MediaDecodingConfiguration;
+struct MediaEncodingConfiguration;
+enum class MediaEncodingType : uint8_t;
+enum class MediaDecodingType : uint8_t;
+} // namespace dom
+
+namespace mediacaps {
+enum class AVType { AUDIO, VIDEO };
+MOZ_DEFINE_ENUM_CLASS_WITH_TOSTRING(
+ ValidationError,
+ (MissingType, InvalidAudioConfiguration, InvalidVideoConfiguration,
+ InvalidMIMEType, InvalidAudioType, InvalidVideoType, SingleCodecHasParams,
+ ContainerMissingCodecsParam, ContainerCodecsNotSingle, FramerateInvalid,
+ InapplicableMember, KeySystemWrongType, KeySystemAudioMissing,
+ KeySystemVideoMissing, SENTINEL));
+
+// Variant to consolidate encoding/decoding checks into the same functions
+using MediaType = Variant<dom::MediaEncodingType, dom::MediaDecodingType>;
+using ValidationResult = mozilla::Result<mozilla::Ok, ValidationError>;
+
+// https://w3c.github.io/media-capabilities/#mediaconfiguration
+ValidationResult IsValidMediaDecodingConfiguration(
+ const dom::MediaDecodingConfiguration& aConfig);
+
+// NOTE: No formal validation steps in the spec.
+// Currently just calls IsValidMediaConfiguration.
+ValidationResult IsValidMediaEncodingConfiguration(
+ const dom::MediaEncodingConfiguration& aConfig);
+
+// Helpers which also convert the validation error to text
+void RejectWithValidationResult(dom::Promise* aPromise,
+ const ValidationError aErr);
+void ThrowWithValidationResult(ErrorResult& aRv, const ValidationError aErr);
+} // namespace mediacaps
+} // namespace mozilla
+
+#endif
diff --git a/dom/media/mediacapabilities/moz.build b/dom/media/mediacapabilities/moz.build
@@ -9,6 +9,7 @@ EXPORTS.mozilla.dom += [
UNIFIED_SOURCES += [
"MediaCapabilities.cpp",
+ "MediaCapabilitiesValidation.cpp",
]
include("/ipc/chromium/chromium-config.mozbuild")