2016-04-19 10:36:19 +03:00
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 http : //mozilla.org/MPL/2.0/. */
# include "DecoderDoctorDiagnostics.h"
2016-04-19 10:36:19 +03:00
# include "mozilla/dom/DecoderDoctorNotificationBinding.h"
2016-04-19 10:36:19 +03:00
# include "mozilla/Logging.h"
2016-04-22 06:42:11 +03:00
# include "mozilla/Preferences.h"
2016-04-27 14:03:44 +03:00
# include "nsContentUtils.h"
2016-04-19 10:36:19 +03:00
# include "nsGkAtoms.h"
# include "nsIDocument.h"
2016-04-19 10:36:19 +03:00
# include "nsIObserverService.h"
2016-04-27 14:03:44 +03:00
# include "nsIScriptError.h"
2016-04-19 10:36:19 +03:00
# include "nsITimer.h"
# include "nsIWeakReference.h"
2016-04-22 06:42:11 +03:00
# include "nsPluginHost.h"
2016-04-19 10:36:19 +03:00
static mozilla : : LazyLogModule sDecoderDoctorLog ( " DecoderDoctor " ) ;
# define DD_LOG(level, arg, ...) MOZ_LOG(sDecoderDoctorLog, level, (arg, ##__VA_ARGS__))
# define DD_DEBUG(arg, ...) DD_LOG(mozilla::LogLevel::Debug, arg, ##__VA_ARGS__)
2016-04-19 10:36:19 +03:00
# define DD_INFO(arg, ...) DD_LOG(mozilla::LogLevel::Info, arg, ##__VA_ARGS__)
2016-04-19 10:36:19 +03:00
# define DD_WARN(arg, ...) DD_LOG(mozilla::LogLevel::Warning, arg, ##__VA_ARGS__)
2016-05-16 14:35:14 +03:00
namespace mozilla {
2016-04-19 10:36:19 +03:00
2016-04-19 10:36:19 +03:00
// Class that collects a sequence of diagnostics from the same document over a
// small period of time, in order to provide a synthesized analysis.
//
// Referenced by the document through a nsINode property, mTimer, and
// inter-task captures.
// When notified that the document is dead, or when the timer expires but
// nothing new happened, StopWatching() will remove the document property and
// timer (if present), so no more work will happen and the watcher will be
// destroyed once all references are gone.
class DecoderDoctorDocumentWatcher : public nsITimerCallback
{
public :
2016-04-22 06:42:11 +03:00
static already_AddRefed < DecoderDoctorDocumentWatcher >
2016-04-19 10:36:19 +03:00
RetrieveOrCreate ( nsIDocument * aDocument ) ;
NS_DECL_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
2016-04-22 06:42:11 +03:00
void AddDiagnostics ( DecoderDoctorDiagnostics & & aDiagnostics ,
const char * aCallSite ) ;
2016-04-19 10:36:19 +03:00
private :
explicit DecoderDoctorDocumentWatcher ( nsIDocument * aDocument ) ;
virtual ~ DecoderDoctorDocumentWatcher ( ) ;
// This will prevent further work from happening, watcher will deregister
// itself from document (if requested) and cancel any timer, and soon die.
void StopWatching ( bool aRemoveProperty ) ;
// Remove property from document; will call DestroyPropertyCallback.
void RemovePropertyFromDocument ( ) ;
// Callback for property destructor, will be automatically called when the
// document (in aObject) is being destroyed.
static void DestroyPropertyCallback ( void * aObject ,
nsIAtom * aPropertyName ,
void * aPropertyValue ,
void * aData ) ;
static const uint32_t sAnalysisPeriod_ms = 1000 ;
void EnsureTimerIsStarted ( ) ;
2016-04-19 10:36:19 +03:00
void ReportAnalysis ( dom : : DecoderDoctorNotificationType aNotificationType ,
const char * aReportStringId ,
2016-04-19 10:36:19 +03:00
const nsAString & aFormats ) ;
void SynthesizeAnalysis ( ) ;
// Raw pointer to an nsIDocument.
// Must be non-null during construction.
// Nulled when we want to stop watching, because either:
// 1. The document has been destroyed (notified through
// DestroyPropertyCallback).
// 2. We have not received new diagnostic information within a short time
// period, so we just stop watching.
// Once nulled, no more actual work will happen, and the watcher will be
// destroyed soon.
nsIDocument * mDocument ;
struct Diagnostics
{
Diagnostics ( DecoderDoctorDiagnostics & & aDiagnostics ,
const char * aCallSite )
: mDecoderDoctorDiagnostics ( Move ( aDiagnostics ) )
, mCallSite ( aCallSite )
{ }
Diagnostics ( const Diagnostics & ) = delete ;
Diagnostics ( Diagnostics & & aOther )
: mDecoderDoctorDiagnostics ( Move ( aOther . mDecoderDoctorDiagnostics ) )
, mCallSite ( Move ( aOther . mCallSite ) )
{ }
const DecoderDoctorDiagnostics mDecoderDoctorDiagnostics ;
const nsCString mCallSite ;
} ;
typedef nsTArray < Diagnostics > DiagnosticsSequence ;
DiagnosticsSequence mDiagnosticsSequence ;
nsCOMPtr < nsITimer > mTimer ; // Keep timer alive until we run.
DiagnosticsSequence : : size_type mDiagnosticsHandled = 0 ;
} ;
NS_IMPL_ISUPPORTS ( DecoderDoctorDocumentWatcher , nsITimerCallback )
// static
2016-04-22 06:42:11 +03:00
already_AddRefed < DecoderDoctorDocumentWatcher >
2016-04-19 10:36:19 +03:00
DecoderDoctorDocumentWatcher : : RetrieveOrCreate ( nsIDocument * aDocument )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
MOZ_ASSERT ( aDocument ) ;
RefPtr < DecoderDoctorDocumentWatcher > watcher =
static_cast < DecoderDoctorDocumentWatcher * > (
aDocument - > GetProperty ( nsGkAtoms : : decoderDoctor ) ) ;
if ( ! watcher ) {
watcher = new DecoderDoctorDocumentWatcher ( aDocument ) ;
if ( NS_WARN_IF ( NS_FAILED (
aDocument - > SetProperty ( nsGkAtoms : : decoderDoctor ,
watcher . get ( ) ,
DestroyPropertyCallback ,
/*transfer*/ false ) ) ) ) {
DD_WARN ( " DecoderDoctorDocumentWatcher::RetrieveOrCreate(doc=%p) - Could not set property in document, will destroy new watcher[%p] " ,
aDocument , watcher . get ( ) ) ;
return nullptr ;
}
// Document owns watcher through this property.
// Released in DestroyPropertyCallback().
NS_ADDREF ( watcher . get ( ) ) ;
}
2016-04-22 06:42:11 +03:00
return watcher . forget ( ) ;
2016-04-19 10:36:19 +03:00
}
DecoderDoctorDocumentWatcher : : DecoderDoctorDocumentWatcher ( nsIDocument * aDocument )
: mDocument ( aDocument )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
MOZ_ASSERT ( mDocument ) ;
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p]::DecoderDoctorDocumentWatcher(doc=%p) " ,
this , mDocument ) ;
}
DecoderDoctorDocumentWatcher : : ~ DecoderDoctorDocumentWatcher ( )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p <- expect 0]::~DecoderDoctorDocumentWatcher() " ,
this , mDocument ) ;
// mDocument should have been reset through StopWatching()!
MOZ_ASSERT ( ! mDocument ) ;
}
void
DecoderDoctorDocumentWatcher : : RemovePropertyFromDocument ( )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
DecoderDoctorDocumentWatcher * watcher =
static_cast < DecoderDoctorDocumentWatcher * > (
mDocument - > GetProperty ( nsGkAtoms : : decoderDoctor ) ) ;
if ( ! watcher ) {
return ;
}
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::RemovePropertyFromDocument() \n " ,
watcher , watcher - > mDocument ) ;
// This will remove the property and call our DestroyPropertyCallback.
mDocument - > DeleteProperty ( nsGkAtoms : : decoderDoctor ) ;
}
// Callback for property destructors. |aObject| is the object
// the property is being removed for, |aPropertyName| is the property
// being removed, |aPropertyValue| is the value of the property, and |aData|
// is the opaque destructor data that was passed to SetProperty().
// static
void
DecoderDoctorDocumentWatcher : : DestroyPropertyCallback ( void * aObject ,
nsIAtom * aPropertyName ,
void * aPropertyValue ,
void * )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
# ifdef DEBUG
nsIDocument * document = static_cast < nsIDocument * > ( aObject ) ;
# endif
MOZ_ASSERT ( aPropertyName = = nsGkAtoms : : decoderDoctor ) ;
DecoderDoctorDocumentWatcher * watcher =
static_cast < DecoderDoctorDocumentWatcher * > ( aPropertyValue ) ;
MOZ_ASSERT ( watcher ) ;
MOZ_ASSERT ( watcher - > mDocument = = document ) ;
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::DestroyPropertyCallback() \n " ,
watcher , watcher - > mDocument ) ;
// 'false': StopWatching should not try and remove the property.
watcher - > StopWatching ( false ) ;
NS_RELEASE ( watcher ) ;
}
void
DecoderDoctorDocumentWatcher : : StopWatching ( bool aRemoveProperty )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
// StopWatching() shouldn't be called twice.
MOZ_ASSERT ( mDocument ) ;
if ( aRemoveProperty ) {
RemovePropertyFromDocument ( ) ;
}
// Forget document now, this will prevent more work from being started.
mDocument = nullptr ;
if ( mTimer ) {
mTimer - > Cancel ( ) ;
mTimer = nullptr ;
}
}
void
DecoderDoctorDocumentWatcher : : EnsureTimerIsStarted ( )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
if ( ! mTimer ) {
mTimer = do_CreateInstance ( NS_TIMER_CONTRACTID ) ;
if ( NS_WARN_IF ( ! mTimer ) ) {
return ;
}
if ( NS_WARN_IF ( NS_FAILED (
mTimer - > InitWithCallback (
this , sAnalysisPeriod_ms , nsITimer : : TYPE_ONE_SHOT ) ) ) ) {
mTimer = nullptr ;
}
}
}
2016-04-19 10:36:19 +03:00
static void
DispatchNotification ( nsISupports * aSubject ,
dom : : DecoderDoctorNotificationType aNotificationType ,
const nsAString & aFormats )
{
if ( ! aSubject ) {
return ;
}
dom : : DecoderDoctorNotification data ;
data . mType = aNotificationType ;
if ( ! aFormats . IsEmpty ( ) ) {
data . mFormats . Construct ( aFormats ) ;
}
nsAutoString json ;
data . ToJSON ( json ) ;
if ( json . IsEmpty ( ) ) {
DD_WARN ( " DecoderDoctorDiagnostics/DispatchEvent() - Could not create json for dispatch " ) ;
// No point in dispatching this notification without data, the front-end
// wouldn't know what to display.
return ;
}
DD_DEBUG ( " DecoderDoctorDiagnostics/DispatchEvent() %s " , NS_ConvertUTF16toUTF8 ( json ) . get ( ) ) ;
nsCOMPtr < nsIObserverService > obs = services : : GetObserverService ( ) ;
if ( obs ) {
obs - > NotifyObservers ( aSubject , " decoder-doctor-notification " , json . get ( ) ) ;
}
}
2016-04-19 10:36:19 +03:00
void
2016-04-19 10:36:19 +03:00
DecoderDoctorDocumentWatcher : : ReportAnalysis (
dom : : DecoderDoctorNotificationType aNotificationType ,
const char * aReportStringId ,
2016-04-22 06:42:11 +03:00
const nsAString & aParams )
2016-04-19 10:36:19 +03:00
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
if ( ! mDocument ) {
return ;
}
2016-04-22 06:42:11 +03:00
// 'params' will only be forwarded for non-empty strings.
const char16_t * params [ 1 ] = { aParams . Data ( ) } ;
2016-04-19 10:36:19 +03:00
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::ReportAnalysis() ReportToConsole - aMsg='%s' params[0]='%s' " ,
this , mDocument , aReportStringId ,
2016-04-22 06:42:11 +03:00
aParams . IsEmpty ( ) ? " <no params> " : NS_ConvertUTF16toUTF8 ( params [ 0 ] ) . get ( ) ) ;
2016-04-19 10:36:19 +03:00
nsContentUtils : : ReportToConsole ( nsIScriptError : : warningFlag ,
NS_LITERAL_CSTRING ( " Media " ) ,
mDocument ,
nsContentUtils : : eDOM_PROPERTIES ,
aReportStringId ,
2016-04-22 06:42:11 +03:00
aParams . IsEmpty ( ) ? nullptr : params ,
aParams . IsEmpty ( ) ? 0 : 1 ) ;
2016-04-19 10:36:19 +03:00
2016-04-22 06:42:11 +03:00
// "media.decoder-doctor.notifications-allowed" controls which notifications
// may be dispatched to the front-end. It either contains:
// - '*' -> Allow everything.
// - Comma-separater list of ids -> Allow if aReportStringId (from
// dom.properties) is one of them.
// - Nothing (missing or empty) -> Disable everything.
nsAdoptingCString filter =
Preferences : : GetCString ( " media.decoder-doctor.notifications-allowed " ) ;
filter . StripWhitespace ( ) ;
bool allowed = false ;
if ( ! filter | | filter . IsEmpty ( ) ) {
// Allow nothing.
} else if ( filter . EqualsLiteral ( " * " ) ) {
allowed = true ;
} else for ( uint32_t start = 0 ; start < filter . Length ( ) ; ) {
int32_t comma = filter . FindChar ( ' , ' , start ) ;
uint32_t end = ( comma > = 0 ) ? uint32_t ( comma ) : filter . Length ( ) ;
if ( strncmp ( aReportStringId , filter . Data ( ) + start , end - start ) = = 0 ) {
allowed = true ;
break ;
}
// Skip comma. End of line will be caught in for 'while' clause.
start = end + 1 ;
}
if ( allowed ) {
2016-04-19 10:36:20 +03:00
DispatchNotification (
2016-04-22 06:42:11 +03:00
mDocument - > GetInnerWindow ( ) , aNotificationType , aParams ) ;
2016-04-19 10:36:20 +03:00
}
2016-04-19 10:36:19 +03:00
}
2016-04-22 06:42:11 +03:00
enum SilverlightPresence {
eNoSilverlight ,
eSilverlightDisabled ,
eSilverlightEnabled
} ;
static SilverlightPresence
CheckSilverlight ( )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
RefPtr < nsPluginHost > pluginHost = nsPluginHost : : GetInst ( ) ;
if ( ! pluginHost ) {
return eNoSilverlight ;
}
nsTArray < nsCOMPtr < nsIInternalPluginTag > > plugins ;
pluginHost - > GetPlugins ( plugins , /*aIncludeDisabled*/ true ) ;
for ( const auto & plugin : plugins ) {
for ( const auto & mime : plugin - > MimeTypes ( ) ) {
if ( mime . LowerCaseEqualsLiteral ( " application/x-silverlight " )
| | mime . LowerCaseEqualsLiteral ( " application/x-silverlight-2 " ) ) {
return plugin - > IsEnabled ( ) ? eSilverlightEnabled : eSilverlightDisabled ;
}
}
}
return eNoSilverlight ;
}
2016-04-22 06:42:11 +03:00
static void AppendToStringList ( nsAString & list , const nsAString & item )
{
if ( ! list . IsEmpty ( ) ) {
list + = NS_LITERAL_STRING ( " , " ) ;
}
list + = item ;
}
2016-04-19 10:36:19 +03:00
void
DecoderDoctorDocumentWatcher : : SynthesizeAnalysis ( )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
bool canPlay = false ;
2016-04-22 06:42:11 +03:00
# if defined(XP_WIN)
bool WMFNeeded = false ;
# endif
2016-04-19 10:36:20 +03:00
# if defined(MOZ_FFMPEG)
2016-04-22 06:42:11 +03:00
bool FFMpegNeeded = false ;
2016-04-19 10:36:20 +03:00
# endif
2016-04-22 06:42:11 +03:00
nsAutoString playableFormats ;
2016-04-22 06:42:11 +03:00
nsAutoString unplayableFormats ;
2016-04-22 06:42:11 +03:00
nsAutoString supportedKeySystems ;
2016-04-22 06:42:11 +03:00
nsAutoString unsupportedKeySystems ;
2016-04-22 06:42:11 +03:00
DecoderDoctorDiagnostics : : KeySystemIssue lastKeySystemIssue =
DecoderDoctorDiagnostics : : eUnset ;
2016-04-22 06:42:11 +03:00
2016-04-19 10:36:19 +03:00
for ( auto & diag : mDiagnosticsSequence ) {
2016-04-22 06:42:11 +03:00
switch ( diag . mDecoderDoctorDiagnostics . Type ( ) ) {
2016-05-16 14:35:14 +03:00
case DecoderDoctorDiagnostics : : eFormatSupportCheck :
if ( diag . mDecoderDoctorDiagnostics . CanPlay ( ) ) {
canPlay = true ;
AppendToStringList ( playableFormats ,
diag . mDecoderDoctorDiagnostics . Format ( ) ) ;
} else {
2016-04-22 06:42:11 +03:00
# if defined(XP_WIN)
2016-05-16 14:35:14 +03:00
if ( diag . mDecoderDoctorDiagnostics . DidWMFFailToLoad ( ) ) {
WMFNeeded = true ;
}
2016-04-22 06:42:11 +03:00
# endif
2016-04-19 10:36:20 +03:00
# if defined(MOZ_FFMPEG)
2016-05-16 14:35:14 +03:00
if ( diag . mDecoderDoctorDiagnostics . DidFFmpegFailToLoad ( ) ) {
FFMpegNeeded = true ;
}
2016-04-19 10:36:20 +03:00
# endif
2016-05-16 14:35:14 +03:00
AppendToStringList ( unplayableFormats ,
diag . mDecoderDoctorDiagnostics . Format ( ) ) ;
2016-04-22 06:42:11 +03:00
}
2016-05-16 14:35:14 +03:00
break ;
case DecoderDoctorDiagnostics : : eMediaKeySystemAccessRequest :
if ( diag . mDecoderDoctorDiagnostics . IsKeySystemSupported ( ) ) {
AppendToStringList ( supportedKeySystems ,
diag . mDecoderDoctorDiagnostics . KeySystem ( ) ) ;
} else {
AppendToStringList ( unsupportedKeySystems ,
diag . mDecoderDoctorDiagnostics . KeySystem ( ) ) ;
DecoderDoctorDiagnostics : : KeySystemIssue issue =
diag . mDecoderDoctorDiagnostics . GetKeySystemIssue ( ) ;
if ( issue ! = DecoderDoctorDiagnostics : : eUnset ) {
lastKeySystemIssue = issue ;
}
}
break ;
default :
MOZ_ASSERT ( diag . mDecoderDoctorDiagnostics . Type ( )
= = DecoderDoctorDiagnostics : : eFormatSupportCheck
| | diag . mDecoderDoctorDiagnostics . Type ( )
= = DecoderDoctorDiagnostics : : eMediaKeySystemAccessRequest ) ;
break ;
2016-04-19 10:36:19 +03:00
}
}
2016-04-22 06:42:11 +03:00
2016-04-22 06:42:11 +03:00
// Look at Key System issues first, as they may influence format checks.
if ( ! unsupportedKeySystems . IsEmpty ( ) & & supportedKeySystems . IsEmpty ( ) ) {
// No supported key systems!
switch ( lastKeySystemIssue ) {
2016-05-16 14:35:14 +03:00
case DecoderDoctorDiagnostics : : eWidevineWithNoWMF :
if ( CheckSilverlight ( ) ! = eSilverlightEnabled ) {
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unsupported key systems: %s, widevine without WMF nor Silverlight " ,
this , mDocument , NS_ConvertUTF16toUTF8 ( unplayableFormats ) . get ( ) ) ;
ReportAnalysis ( dom : : DecoderDoctorNotificationType : : Platform_decoder_not_found ,
" MediaWidevineNoWMFNoSilverlight " , NS_LITERAL_STRING ( " " ) ) ;
return ;
}
break ;
default :
break ;
2016-04-22 06:42:11 +03:00
}
}
if ( ! canPlay & & ! unplayableFormats . IsEmpty ( ) ) {
# if defined(XP_WIN)
if ( WMFNeeded ) {
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - formats: %s -> Cannot play media because WMF was not found " ,
this , mDocument , NS_ConvertUTF16toUTF8 ( unplayableFormats ) . get ( ) ) ;
ReportAnalysis ( dom : : DecoderDoctorNotificationType : : Platform_decoder_not_found ,
" MediaWMFNeeded " , unplayableFormats ) ;
return ;
}
# endif
2016-04-19 10:36:20 +03:00
# if defined(MOZ_FFMPEG)
2016-04-22 06:42:11 +03:00
if ( FFMpegNeeded ) {
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - unplayable formats: %s -> Cannot play media because platform decoder was not found " ,
this , mDocument , NS_ConvertUTF16toUTF8 ( unplayableFormats ) . get ( ) ) ;
2016-04-19 10:36:20 +03:00
ReportAnalysis ( dom : : DecoderDoctorNotificationType : : Platform_decoder_not_found ,
2016-04-22 06:42:11 +03:00
" MediaPlatformDecoderNotFound " , unplayableFormats ) ;
2016-04-22 06:42:11 +03:00
return ;
2016-04-19 10:36:20 +03:00
}
2016-04-22 06:42:11 +03:00
# endif
DD_WARN ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Cannot play media, unplayable formats: %s " ,
this , mDocument , NS_ConvertUTF16toUTF8 ( unplayableFormats ) . get ( ) ) ;
ReportAnalysis ( dom : : DecoderDoctorNotificationType : : Cannot_play ,
" MediaCannotPlayNoDecoders " , unplayableFormats ) ;
return ;
}
if ( ! unplayableFormats . IsEmpty ( ) ) {
2016-04-22 06:42:11 +03:00
DD_INFO ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Can play media, but no decoders for some requested formats: %s " ,
this , mDocument , NS_ConvertUTF16toUTF8 ( unplayableFormats ) . get ( ) ) ;
2016-04-22 06:42:11 +03:00
if ( Preferences : : GetBool ( " media.decoder-doctor.verbose " , false ) ) {
2016-04-19 10:36:19 +03:00
ReportAnalysis (
dom : : DecoderDoctorNotificationType : : Can_play_but_some_missing_decoders ,
2016-04-22 06:42:11 +03:00
" MediaNoDecoders " , unplayableFormats ) ;
2016-04-19 10:36:19 +03:00
}
2016-04-22 06:42:11 +03:00
return ;
2016-04-19 10:36:19 +03:00
}
2016-04-22 06:42:11 +03:00
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::SynthesizeAnalysis() - Can play media, decoders available for all requested formats " ,
this , mDocument ) ;
2016-04-19 10:36:19 +03:00
}
void
2016-04-22 06:42:11 +03:00
DecoderDoctorDocumentWatcher : : AddDiagnostics ( DecoderDoctorDiagnostics & & aDiagnostics ,
const char * aCallSite )
2016-04-19 10:36:19 +03:00
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
if ( ! mDocument ) {
return ;
}
2016-04-22 06:42:11 +03:00
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::AddDiagnostics(DecoderDoctorDiagnostics{%s}, call site '%s') " ,
this , mDocument , aDiagnostics . GetDescription ( ) . Data ( ) , aCallSite ) ;
2016-04-19 10:36:19 +03:00
mDiagnosticsSequence . AppendElement (
2016-04-22 06:42:11 +03:00
Diagnostics ( Move ( aDiagnostics ) , aCallSite ) ) ;
2016-04-19 10:36:19 +03:00
EnsureTimerIsStarted ( ) ;
}
NS_IMETHODIMP
DecoderDoctorDocumentWatcher : : Notify ( nsITimer * timer )
{
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
MOZ_ASSERT ( timer = = mTimer ) ;
// Forget timer. (Assuming timer keeps itself and us alive during this call.)
mTimer = nullptr ;
if ( ! mDocument ) {
return NS_OK ;
}
if ( mDiagnosticsSequence . Length ( ) > mDiagnosticsHandled ) {
// We have new diagnostic data.
mDiagnosticsHandled = mDiagnosticsSequence . Length ( ) ;
SynthesizeAnalysis ( ) ;
// Restart timer, to redo analysis or stop watching this document,
// depending on whether anything new happens.
EnsureTimerIsStarted ( ) ;
} else {
DD_DEBUG ( " DecoderDoctorDocumentWatcher[%p, doc=%p]::Notify() - No new diagnostics to analyze -> Stop watching " ,
this , mDocument ) ;
// Stop watching this document, we don't expect more diagnostics for now.
// If more diagnostics come in, we'll treat them as another burst, separately.
// 'true' to remove the property from the document.
StopWatching ( true ) ;
}
return NS_OK ;
}
2016-04-19 10:36:19 +03:00
void
2016-04-22 06:42:11 +03:00
DecoderDoctorDiagnostics : : StoreFormatDiagnostics ( nsIDocument * aDocument ,
const nsAString & aFormat ,
bool aCanPlay ,
const char * aCallSite )
2016-04-19 10:36:19 +03:00
{
2016-04-19 10:36:19 +03:00
MOZ_ASSERT ( NS_IsMainThread ( ) ) ;
2016-04-22 06:42:11 +03:00
// Make sure we only store once.
MOZ_ASSERT ( mDiagnosticsType = = eUnsaved ) ;
mDiagnosticsType = eFormatSupportCheck ;
2016-04-19 10:36:19 +03:00
if ( NS_WARN_IF ( ! aDocument ) ) {
2016-04-22 06:42:11 +03:00
DD_WARN ( " DecoderDoctorDiagnostics[%p]::StoreFormatDiagnostics(nsIDocument* aDocument=nullptr, format='%s', can-play=%d, call site '%s') " ,
this , NS_ConvertUTF16toUTF8 ( aFormat ) . get ( ) , aCanPlay , aCallSite ) ;
return ;
}
if ( NS_WARN_IF ( aFormat . IsEmpty ( ) ) ) {
DD_WARN ( " DecoderDoctorDiagnostics[%p]::StoreFormatDiagnostics(nsIDocument* aDocument=%p, format=<empty>, can-play=%d, call site '%s') " ,
this , aDocument , aCanPlay , aCallSite ) ;
2016-04-19 10:36:19 +03:00
return ;
}
2016-04-19 10:36:19 +03:00
RefPtr < DecoderDoctorDocumentWatcher > watcher =
DecoderDoctorDocumentWatcher : : RetrieveOrCreate ( aDocument ) ;
if ( NS_WARN_IF ( ! watcher ) ) {
2016-04-22 06:42:11 +03:00
DD_WARN ( " DecoderDoctorDiagnostics[%p]::StoreFormatDiagnostics(nsIDocument* aDocument=%p, format='%s', can-play=%d, call site '%s') - Could not create document watcher " ,
this , aDocument , NS_ConvertUTF16toUTF8 ( aFormat ) . get ( ) , aCanPlay , aCallSite ) ;
2016-04-19 10:36:19 +03:00
return ;
}
2016-04-22 06:42:11 +03:00
mFormat = aFormat ;
mCanPlay = aCanPlay ;
2016-04-19 10:36:19 +03:00
// StoreDiagnostics should only be called once, after all data is available,
// so it is safe to Move() from this object.
2016-04-22 06:42:11 +03:00
watcher - > AddDiagnostics ( Move ( * this ) , aCallSite ) ;
2016-04-22 06:42:11 +03:00
// 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 ) ;
2016-04-22 06:42:11 +03:00
}
nsCString
DecoderDoctorDiagnostics : : GetDescription ( ) const
{
2016-04-22 06:42:11 +03:00
MOZ_ASSERT ( mDiagnosticsType = = eFormatSupportCheck
| | mDiagnosticsType = = eMediaKeySystemAccessRequest ) ;
2016-04-22 06:42:11 +03:00
nsCString s ;
2016-04-22 06:42:11 +03:00
switch ( mDiagnosticsType ) {
2016-05-16 14:35:14 +03:00
case eUnsaved :
s = " Unsaved diagnostics, cannot get accurate description " ;
2016-04-22 06:42:11 +03:00
break ;
2016-05-16 14:35:14 +03:00
case eFormatSupportCheck :
s = " format=' " ;
s + = NS_ConvertUTF16toUTF8 ( mFormat ) . get ( ) ;
s + = mCanPlay ? " ', can play " : " ', cannot play " ;
if ( mWMFFailedToLoad ) {
s + = " , Windows platform decoder failed to load " ;
}
if ( mFFmpegFailedToLoad ) {
s + = " , Linux platform decoder failed to load " ;
}
if ( mGMPPDMFailedToStartup ) {
s + = " , GMP PDM failed to startup " ;
} else if ( ! mGMP . IsEmpty ( ) ) {
s + = " , Using GMP ' " ;
s + = mGMP ;
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 ;
}
2016-04-22 06:42:11 +03:00
break ;
default :
s = " ? " ;
break ;
2016-04-22 06:42:11 +03:00
}
return s ;
2016-04-19 10:36:19 +03:00
}
} // namespace mozilla