Bug 848994 - p3. Check MediaKeySystem requests - r=cpearce

Media Key System access requests are now recorded with their success/failure,
as well as accompanying issues of importance.
In this bug we focus on the Widevine-with-no-WMF case.

MozReview-Commit-ID: ElBN6cXKwAW
This commit is contained in:
Gerald Squelart 2016-04-22 13:42:11 +10:00
Родитель 6494d247f8
Коммит 5853b8739b
5 изменённых файлов: 211 добавлений и 44 удалений

Просмотреть файл

@ -303,9 +303,11 @@ DecoderDoctorDocumentWatcher::SynthesizeAnalysis()
bool FFMpegNeeded = false;
#endif
nsAutoString unplayableFormats;
nsAutoString unsupportedKeySystems;
for (auto& diag : mDiagnosticsSequence) {
if (!diag.mDecoderDoctorDiagnostics.Format().IsEmpty()) {
switch (diag.mDecoderDoctorDiagnostics.Type()) {
case DecoderDoctorDiagnostics::eFormatSupportCheck:
if (diag.mDecoderDoctorDiagnostics.CanPlay()) {
canPlay = true;
} else {
@ -319,6 +321,21 @@ DecoderDoctorDocumentWatcher::SynthesizeAnalysis()
}
unplayableFormats += diag.mDecoderDoctorDiagnostics.Format();
}
break;
case DecoderDoctorDiagnostics::eMediaKeySystemAccessRequest:
if (!diag.mDecoderDoctorDiagnostics.IsKeySystemSupported()) {
if (!unsupportedKeySystems.IsEmpty()) {
unsupportedKeySystems += NS_LITERAL_STRING(", ");
}
unsupportedKeySystems += diag.mDecoderDoctorDiagnostics.KeySystem();
}
break;
default:
MOZ_ASSERT(diag.mDecoderDoctorDiagnostics.Type()
== DecoderDoctorDiagnostics::eFormatSupportCheck
|| diag.mDecoderDoctorDiagnostics.Type()
== DecoderDoctorDiagnostics::eMediaKeySystemAccessRequest);
break;
}
}
@ -410,6 +427,10 @@ DecoderDoctorDiagnostics::StoreFormatDiagnostics(nsIDocument* aDocument,
const char* aCallSite)
{
MOZ_ASSERT(NS_IsMainThread());
// Make sure we only store once.
MOZ_ASSERT(mDiagnosticsType == eUnsaved);
mDiagnosticsType = eFormatSupportCheck;
if (NS_WARN_IF(!aDocument)) {
DD_WARN("DecoderDoctorDiagnostics[%p]::StoreFormatDiagnostics(nsIDocument* aDocument=nullptr, format='%s', can-play=%d, call site '%s')",
this, NS_ConvertUTF16toUTF8(aFormat).get(), aCanPlay, aCallSite);
@ -436,13 +457,64 @@ DecoderDoctorDiagnostics::StoreFormatDiagnostics(nsIDocument* aDocument,
// StoreDiagnostics should only be called once, after all data is available,
// so it is safe to Move() from this object.
watcher->AddDiagnostics(Move(*this), aCallSite);
// Even though it's moved-from, the type should stay set
// (Only used to ensure that we do store only once.)
MOZ_ASSERT(mDiagnosticsType == eFormatSupportCheck);
}
void
DecoderDoctorDiagnostics::StoreMediaKeySystemAccess(nsIDocument* aDocument,
const nsAString& aKeySystem,
bool aIsSupported,
const char* aCallSite)
{
MOZ_ASSERT(NS_IsMainThread());
// Make sure we only store once.
MOZ_ASSERT(mDiagnosticsType == eUnsaved);
mDiagnosticsType = eMediaKeySystemAccessRequest;
if (NS_WARN_IF(!aDocument)) {
DD_WARN("DecoderDoctorDiagnostics[%p]::StoreMediaKeySystemAccess(nsIDocument* aDocument=nullptr, keysystem='%s', supported=%d, call site '%s')",
this, NS_ConvertUTF16toUTF8(aKeySystem).get(), aIsSupported, aCallSite);
return;
}
if (NS_WARN_IF(aKeySystem.IsEmpty())) {
DD_WARN("DecoderDoctorDiagnostics[%p]::StoreMediaKeySystemAccess(nsIDocument* aDocument=%p, keysystem=<empty>, supported=%d, call site '%s')",
this, aDocument, aIsSupported, aCallSite);
return;
}
RefPtr<DecoderDoctorDocumentWatcher> watcher =
DecoderDoctorDocumentWatcher::RetrieveOrCreate(aDocument);
if (NS_WARN_IF(!watcher)) {
DD_WARN("DecoderDoctorDiagnostics[%p]::StoreMediaKeySystemAccess(nsIDocument* aDocument=%p, keysystem='%s', supported=%d, call site '%s') - Could not create document watcher",
this, NS_ConvertUTF16toUTF8(aKeySystem).get(), aIsSupported, aCallSite);
return;
}
mKeySystem = aKeySystem;
mIsKeySystemSupported = aIsSupported;
// StoreDiagnostics should only be called once, after all data is available,
// so it is safe to Move() from this object.
watcher->AddDiagnostics(Move(*this), aCallSite);
// Even though it's moved-from, the type should stay set
// (Only used to ensure that we do store only once.)
MOZ_ASSERT(mDiagnosticsType == eMediaKeySystemAccessRequest);
}
nsCString
DecoderDoctorDiagnostics::GetDescription() const
{
MOZ_ASSERT(mDiagnosticsType == eFormatSupportCheck
|| mDiagnosticsType == eMediaKeySystemAccessRequest);
nsCString s;
if (!mFormat.IsEmpty()) {
switch (mDiagnosticsType) {
case eUnsaved:
s = "Unsaved diagnostics, cannot get accurate description";
break;
case eFormatSupportCheck:
s = "format='";
s += NS_ConvertUTF16toUTF8(mFormat).get();
s += mCanPlay ? "', can play" : "', cannot play";
@ -452,8 +524,22 @@ DecoderDoctorDiagnostics::GetDescription() const
if (mFFmpegFailedToLoad) {
s += ", Linux platform decoder failed to load";
}
} else {
s = "?";
break;
case eMediaKeySystemAccessRequest:
s = "key system='";
s += NS_ConvertUTF16toUTF8(mKeySystem).get();
s += mIsKeySystemSupported ? "', supported" : "', not supported";
switch (mKeySystemIssue) {
case eUnset:
break;
case eWidevineWithNoWMF:
s += ", Widevine with no WMF";
break;
}
break;
default:
s = "?";
break;
}
return s;
}

Просмотреть файл

@ -7,8 +7,9 @@
#ifndef DecoderDoctorDiagnostics_h_
#define DecoderDoctorDiagnostics_h_
#include "nsString.h"
class nsIDocument;
class nsAString;
namespace mozilla {
@ -40,7 +41,19 @@ public:
bool aCanPlay,
const char* aCallSite);
// Description string, for logging purposes.
void StoreMediaKeySystemAccess(nsIDocument* aDocument,
const nsAString& aKeySystem,
bool aIsSupported,
const char* aCallSite);
enum DiagnosticsType {
eUnsaved,
eFormatSupportCheck,
eMediaKeySystemAccessRequest
};
DiagnosticsType Type() const { return mDiagnosticsType; }
// Description string, for logging purposes; only call on stored diags.
nsCString GetDescription() const;
// Methods to record diagnostic information:
@ -54,13 +67,37 @@ public:
void SetFFmpegFailedToLoad() { mFFmpegFailedToLoad = true; }
bool DidFFmpegFailToLoad() const { return mFFmpegFailedToLoad; }
const nsAString& KeySystem() const { return mKeySystem; }
bool IsKeySystemSupported() const { return mIsKeySystemSupported; }
enum KeySystemIssue {
eUnset,
eWidevineWithNoWMF
};
void SetKeySystemIssue(KeySystemIssue aKeySystemIssue)
{
mKeySystemIssue = aKeySystemIssue;
}
KeySystemIssue GetKeySystemIssue() const
{
return mKeySystemIssue;
}
private:
// Currently-known type of diagnostics. Set from one of the 'Store...' methods.
// This helps ensure diagnostics are only stored once,
// and makes it easy to know what information they contain.
DiagnosticsType mDiagnosticsType = eUnsaved;
nsString mFormat;
// True if there is at least one decoder that can play that format.
bool mCanPlay = false;
bool mWMFFailedToLoad = false;
bool mFFmpegFailedToLoad = false;
nsString mKeySystem;
bool mIsKeySystemSupported = false;
KeySystemIssue mKeySystemIssue = eUnset;
};
} // namespace mozilla

Просмотреть файл

@ -33,6 +33,7 @@
#include "nsXULAppAPI.h"
#include "gmp-audio-decode.h"
#include "gmp-video-decode.h"
#include "DecoderDoctorDiagnostics.h"
namespace mozilla {
namespace dom {
@ -321,7 +322,8 @@ MediaKeySystemAccess::GetKeySystemStatus(const nsAString& aKeySystem,
static bool
GMPDecryptsAndDecodesAAC(mozIGeckoMediaPluginService* aGMPS,
const nsAString& aKeySystem)
const nsAString& aKeySystem,
DecoderDoctorDiagnostics* aDiagnostics)
{
MOZ_ASSERT(HaveGMPFor(aGMPS,
NS_ConvertUTF16toUTF8(aKeySystem),
@ -334,7 +336,8 @@ GMPDecryptsAndDecodesAAC(mozIGeckoMediaPluginService* aGMPS,
static bool
GMPDecryptsAndDecodesH264(mozIGeckoMediaPluginService* aGMPS,
const nsAString& aKeySystem)
const nsAString& aKeySystem,
DecoderDoctorDiagnostics* aDiagnostics)
{
MOZ_ASSERT(HaveGMPFor(aGMPS,
NS_ConvertUTF16toUTF8(aKeySystem),
@ -351,7 +354,8 @@ GMPDecryptsAndDecodesH264(mozIGeckoMediaPluginService* aGMPS,
static bool
GMPDecryptsAndGeckoDecodesH264(mozIGeckoMediaPluginService* aGMPService,
const nsAString& aKeySystem,
const nsAString& aContentType)
const nsAString& aContentType,
DecoderDoctorDiagnostics* aDiagnostics)
{
MOZ_ASSERT(HaveGMPFor(aGMPService,
NS_ConvertUTF16toUTF8(aKeySystem),
@ -361,60 +365,72 @@ GMPDecryptsAndGeckoDecodesH264(mozIGeckoMediaPluginService* aGMPService,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
NS_LITERAL_CSTRING("h264")) &&
MP4Decoder::CanHandleMediaType(aContentType,
/* DecoderDoctorDiagnostics* */ nullptr);
MP4Decoder::CanHandleMediaType(aContentType, aDiagnostics);
}
static bool
GMPDecryptsAndGeckoDecodesAAC(mozIGeckoMediaPluginService* aGMPService,
const nsAString& aKeySystem,
const nsAString& aContentType)
const nsAString& aContentType,
DecoderDoctorDiagnostics* aDiagnostics)
{
MOZ_ASSERT(HaveGMPFor(aGMPService,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
MOZ_ASSERT(IsAACContentType(aContentType));
return !HaveGMPFor(aGMPService,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
NS_LITERAL_CSTRING("aac")) &&
if (HaveGMPFor(aGMPService,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
NS_LITERAL_CSTRING("aac"))) {
// We do have a GMP for AAC -> Gecko itself does *not* decode AAC.
return false;
}
#if defined(MOZ_WIDEVINE_EME) && defined(XP_WIN)
// Widevine CDM doesn't include an AAC decoder. So if WMF can't
// decode AAC, and a codec wasn't specified, be conservative
// and reject the MediaKeys request, since our policy is to prevent
// the Adobe GMP's unencrypted AAC decoding path being used to
// decode content decrypted by the Widevine CDM.
(!aKeySystem.EqualsLiteral("com.widevine.alpha") || WMFDecoderModule::HasAAC()) &&
// Widevine CDM doesn't include an AAC decoder. So if WMF can't
// decode AAC, and a codec wasn't specified, be conservative
// and reject the MediaKeys request, since our policy is to prevent
// the Adobe GMP's unencrypted AAC decoding path being used to
// decode content decrypted by the Widevine CDM.
if (aKeySystem.EqualsLiteral("com.widevine.alpha") &&
!WMFDecoderModule::HasAAC()) {
if (aDiagnostics) {
aDiagnostics->SetKeySystemIssue(
DecoderDoctorDiagnostics::eWidevineWithNoWMF);
}
return false;
}
#endif
MP4Decoder::CanHandleMediaType(aContentType,
/* DecoderDoctorDiagnostics* */ nullptr);
return MP4Decoder::CanHandleMediaType(aContentType, aDiagnostics);
}
static bool
IsSupportedAudio(mozIGeckoMediaPluginService* aGMPService,
const nsAString& aKeySystem,
const nsAString& aAudioType)
const nsAString& aAudioType,
DecoderDoctorDiagnostics* aDiagnostics)
{
return IsAACContentType(aAudioType) &&
(GMPDecryptsAndDecodesAAC(aGMPService, aKeySystem) ||
GMPDecryptsAndGeckoDecodesAAC(aGMPService, aKeySystem, aAudioType));
(GMPDecryptsAndDecodesAAC(aGMPService, aKeySystem, aDiagnostics) ||
GMPDecryptsAndGeckoDecodesAAC(aGMPService, aKeySystem, aAudioType, aDiagnostics));
}
static bool
IsSupportedVideo(mozIGeckoMediaPluginService* aGMPService,
const nsAString& aKeySystem,
const nsAString& aVideoType)
const nsAString& aVideoType,
DecoderDoctorDiagnostics* aDiagnostics)
{
return IsH264ContentType(aVideoType) &&
(GMPDecryptsAndDecodesH264(aGMPService, aKeySystem) ||
GMPDecryptsAndGeckoDecodesH264(aGMPService, aKeySystem, aVideoType));
(GMPDecryptsAndDecodesH264(aGMPService, aKeySystem, aDiagnostics) ||
GMPDecryptsAndGeckoDecodesH264(aGMPService, aKeySystem, aVideoType, aDiagnostics));
}
static bool
IsSupported(mozIGeckoMediaPluginService* aGMPService,
const nsAString& aKeySystem,
const MediaKeySystemConfiguration& aConfig)
const MediaKeySystemConfiguration& aConfig,
DecoderDoctorDiagnostics* aDiagnostics)
{
if (aConfig.mInitDataType.IsEmpty() &&
aConfig.mAudioType.IsEmpty() &&
@ -429,11 +445,11 @@ IsSupported(mozIGeckoMediaPluginService* aGMPService,
return false;
}
if (!aConfig.mAudioType.IsEmpty() &&
!IsSupportedAudio(aGMPService, aKeySystem, aConfig.mAudioType)) {
!IsSupportedAudio(aGMPService, aKeySystem, aConfig.mAudioType, aDiagnostics)) {
return false;
}
if (!aConfig.mVideoType.IsEmpty() &&
!IsSupportedVideo(aGMPService, aKeySystem, aConfig.mVideoType)) {
!IsSupportedVideo(aGMPService, aKeySystem, aConfig.mVideoType, aDiagnostics)) {
return false;
}
@ -458,7 +474,8 @@ static bool
GetSupportedConfig(mozIGeckoMediaPluginService* aGMPService,
const nsAString& aKeySystem,
const MediaKeySystemConfiguration& aCandidate,
MediaKeySystemConfiguration& aOutConfig)
MediaKeySystemConfiguration& aOutConfig,
DecoderDoctorDiagnostics* aDiagnostics)
{
MediaKeySystemConfiguration config;
config.mLabel = aCandidate.mLabel;
@ -478,7 +495,7 @@ GetSupportedConfig(mozIGeckoMediaPluginService* aGMPService,
if (aCandidate.mAudioCapabilities.WasPassed()) {
nsTArray<MediaKeySystemMediaCapability> caps;
for (const MediaKeySystemMediaCapability& cap : aCandidate.mAudioCapabilities.Value()) {
if (IsSupportedAudio(aGMPService, aKeySystem, cap.mContentType)) {
if (IsSupportedAudio(aGMPService, aKeySystem, cap.mContentType, aDiagnostics)) {
caps.AppendElement(cap);
}
}
@ -491,7 +508,7 @@ GetSupportedConfig(mozIGeckoMediaPluginService* aGMPService,
if (aCandidate.mVideoCapabilities.WasPassed()) {
nsTArray<MediaKeySystemMediaCapability> caps;
for (const MediaKeySystemMediaCapability& cap : aCandidate.mVideoCapabilities.Value()) {
if (IsSupportedVideo(aGMPService, aKeySystem, cap.mContentType)) {
if (IsSupportedVideo(aGMPService, aKeySystem, cap.mContentType, aDiagnostics)) {
caps.AppendElement(cap);
}
}
@ -509,6 +526,10 @@ GetSupportedConfig(mozIGeckoMediaPluginService* aGMPService,
(!aCandidate.mAudioCapabilities.WasPassed() ||
!aCandidate.mVideoCapabilities.WasPassed()) &&
!WMFDecoderModule::HasAAC()) {
if (aDiagnostics) {
aDiagnostics->SetKeySystemIssue(
DecoderDoctorDiagnostics::eWidevineWithNoWMF);
}
return false;
}
#endif
@ -523,7 +544,8 @@ GetSupportedConfig(mozIGeckoMediaPluginService* aGMPService,
/* static */
bool
MediaKeySystemAccess::IsSupported(const nsAString& aKeySystem,
const Sequence<MediaKeySystemConfiguration>& aConfigs)
const Sequence<MediaKeySystemConfiguration>& aConfigs,
DecoderDoctorDiagnostics* aDiagnostics)
{
nsCOMPtr<mozIGeckoMediaPluginService> mps =
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
@ -538,7 +560,7 @@ MediaKeySystemAccess::IsSupported(const nsAString& aKeySystem,
}
for (const MediaKeySystemConfiguration& config : aConfigs) {
if (mozilla::dom::IsSupported(mps, aKeySystem, config)) {
if (mozilla::dom::IsSupported(mps, aKeySystem, config, aDiagnostics)) {
return true;
}
}
@ -549,7 +571,8 @@ MediaKeySystemAccess::IsSupported(const nsAString& aKeySystem,
bool
MediaKeySystemAccess::GetSupportedConfig(const nsAString& aKeySystem,
const Sequence<MediaKeySystemConfiguration>& aConfigs,
MediaKeySystemConfiguration& aOutConfig)
MediaKeySystemConfiguration& aOutConfig,
DecoderDoctorDiagnostics* aDiagnostics)
{
nsCOMPtr<mozIGeckoMediaPluginService> mps =
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
@ -564,7 +587,8 @@ MediaKeySystemAccess::GetSupportedConfig(const nsAString& aKeySystem,
}
for (const MediaKeySystemConfiguration& config : aConfigs) {
if (mozilla::dom::GetSupportedConfig(mps, aKeySystem, config, aOutConfig)) {
if (mozilla::dom::GetSupportedConfig(
mps, aKeySystem, config, aOutConfig, aDiagnostics)) {
return true;
}
}

Просмотреть файл

@ -19,6 +19,9 @@
#include "js/TypeDecls.h"
namespace mozilla {
class DecoderDoctorDiagnostics;
namespace dom {
class MediaKeySystemAccess final : public nsISupports,
@ -56,7 +59,8 @@ public:
nsACString& aOutCdmVersion);
static bool IsSupported(const nsAString& aKeySystem,
const Sequence<MediaKeySystemConfiguration>& aConfigs);
const Sequence<MediaKeySystemConfiguration>& aConfigs,
DecoderDoctorDiagnostics* aDiagnostics);
static void NotifyObservers(nsPIDOMWindowInner* aWindow,
const nsAString& aKeySystem,
@ -68,7 +72,8 @@ public:
static bool GetSupportedConfig(const nsAString& aKeySystem,
const Sequence<MediaKeySystemConfiguration>& aConfigs,
MediaKeySystemConfiguration& aOutConfig);
MediaKeySystemConfiguration& aOutConfig,
DecoderDoctorDiagnostics* aDiagnostics);
private:
nsCOMPtr<nsPIDOMWindowInner> mParent;

Просмотреть файл

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MediaKeySystemAccessManager.h"
#include "DecoderDoctorDiagnostics.h"
#include "mozilla/Preferences.h"
#include "mozilla/EMEUtils.h"
#include "nsServiceManagerUtils.h"
@ -80,6 +81,8 @@ MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
{
EME_LOG("MediaKeySystemAccessManager::Request %s", NS_ConvertUTF16toUTF8(aKeySystem).get());
DecoderDoctorDiagnostics diagnostics;
// Parse keysystem, split it out into keySystem prefix, and version suffix.
nsAutoString keySystem;
int32_t minCdmVersion = NO_CDM_VERSION;
@ -89,6 +92,8 @@ MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
NS_LITERAL_CSTRING("Key system string is invalid,"
" or key system is unsupported"));
diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
aKeySystem, false, __func__);
return;
}
@ -99,6 +104,8 @@ MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
MediaKeySystemStatus::Api_disabled);
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
NS_LITERAL_CSTRING("EME has been preffed off"));
diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
aKeySystem, false, __func__);
return;
}
@ -141,6 +148,8 @@ MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
NS_LITERAL_CSTRING("Gave up while waiting for a CDM update"));
}
diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
aKeySystem, false, __func__);
return;
}
if (status != MediaKeySystemStatus::Available) {
@ -154,23 +163,29 @@ MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
}
aPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
NS_LITERAL_CSTRING("GetKeySystemAccess failed"));
diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
aKeySystem, false, __func__);
return;
}
MediaKeySystemConfiguration config;
// TODO: Remove IsSupported() check here once we remove backwards
// compatibility with initial implementation...
if (MediaKeySystemAccess::GetSupportedConfig(keySystem, aConfigs, config) ||
MediaKeySystemAccess::IsSupported(keySystem, aConfigs)) {
if (MediaKeySystemAccess::GetSupportedConfig(keySystem, aConfigs, config, &diagnostics) ||
MediaKeySystemAccess::IsSupported(keySystem, aConfigs, &diagnostics)) {
RefPtr<MediaKeySystemAccess> access(
new MediaKeySystemAccess(mWindow, keySystem, NS_ConvertUTF8toUTF16(cdmVersion), config));
aPromise->MaybeResolve(access);
diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
aKeySystem, true, __func__);
return;
}
// Not to inform user, because nothing to do if the corresponding keySystem
// configuration is not supported.
aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
NS_LITERAL_CSTRING("Key system configuration is not supported"));
diagnostics.StoreMediaKeySystemAccess(mWindow->GetExtantDoc(),
aKeySystem, false, __func__);
}
MediaKeySystemAccessManager::PendingRequest::PendingRequest(DetailedPromise* aPromise,