Bug 1513039 - part5 : log warning when autoplay is blocked. r=cpearce,karlt
authoralwu <alwu@mozilla.com>
Mon, 07 Jan 2019 19:57:48 +0000
changeset 512757 0e229bbdaae5c8fa9f83796c628a4fdf88f057ba
parent 512756 dba8b9714294f04b19675a10c972bdd72ab7f9ef
child 512758 54817f010dde7ba75d4b506be0e55eac3a890471
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, karlt
bugs1513039
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1513039 - part5 : log warning when autoplay is blocked. r=cpearce,karlt Wrap 'nsContentUtils::ReportToConsole()' to reduce necessary input parameters and call it when we need to log error or warning message. Show the warning when autoplay is blocked. For web audio, this restores the console messages removed in part4 and also reports the same message when the AudioContext is blocked in the constructor. Differential Revision: https://phabricator.services.mozilla.com/D14330
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
dom/media/webaudio/AudioContext.cpp
dom/media/webaudio/AudioContext.h
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -752,19 +752,25 @@ NS_IMETHODIMP
 HTMLMediaElement::MediaLoadListener::GetInterface(const nsIID& aIID,
                                                   void** aResult) {
   return QueryInterface(aIID, aResult);
 }
 
 void HTMLMediaElement::ReportLoadError(const char* aMsg,
                                        const char16_t** aParams,
                                        uint32_t aParamCount) {
-  nsContentUtils::ReportToConsole(
-      nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Media"), OwnerDoc(),
-      nsContentUtils::eDOM_PROPERTIES, aMsg, aParams, aParamCount);
+  ReportToConsole(nsIScriptError::warningFlag, aMsg, aParams, aParamCount);
+}
+
+void HTMLMediaElement::ReportToConsole(uint32_t aErrorFlags, const char* aMsg,
+                                       const char16_t** aParams,
+                                       uint32_t aParamCount) const {
+  nsContentUtils::ReportToConsole(aErrorFlags, NS_LITERAL_CSTRING("Media"),
+                                  OwnerDoc(), nsContentUtils::eDOM_PROPERTIES,
+                                  aMsg, aParams, aParamCount);
 }
 
 class HTMLMediaElement::AudioChannelAgentCallback final
     : public nsIAudioChannelAgentCallback {
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(AudioChannelAgentCallback)
 
@@ -3109,20 +3115,18 @@ already_AddRefed<DOMMediaStream> HTMLMed
       aFinishBehavior == StreamCaptureBehavior::FINISH_WHEN_ENDED;
   out->mCapturingAudioOnly =
       aStreamCaptureType == StreamCaptureType::CAPTURE_AUDIO;
 
   if (aStreamCaptureType == StreamCaptureType::CAPTURE_AUDIO) {
     if (mSrcStream) {
       // We don't support applying volume and mute to the captured stream, when
       // capturing a MediaStream.
-      nsContentUtils::ReportToConsole(
-          nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Media"), OwnerDoc(),
-          nsContentUtils::eDOM_PROPERTIES,
-          "MediaElementAudioCaptureOfMediaStreamError");
+      ReportToConsole(nsIScriptError::errorFlag,
+                      "MediaElementAudioCaptureOfMediaStreamError");
       return nullptr;
     }
 
     // mAudioCaptured tells the user that the audio played by this media element
     // is being routed to the captureStreams *instead* of being played to
     // speakers.
     mAudioCaptured = true;
   }
@@ -3645,16 +3649,17 @@ void HTMLMediaElement::DispatchEventsWhe
   }
 #if defined(MOZ_WIDGET_ANDROID)
   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
       this, NS_LITERAL_STRING("MozAutoplayMediaBlocked"), CanBubble::eYes,
       ChromeOnlyDispatch::eYes);
   asyncDispatcher->PostDOMEvent();
 #endif
   OwnerDoc()->MaybeNotifyAutoplayBlocked();
+  ReportToConsole(nsIScriptError::warningFlag, "BlockAutoplayError");
 }
 
 void HTMLMediaElement::PlayInternal(bool aHandlingUserInput) {
   if (mPreloadAction == HTMLMediaElement::PRELOAD_NONE) {
     // The media load algorithm will be initiated by a user interaction.
     // We want to boost the channel priority for better responsiveness.
     // Note this must be done before UpdatePreloadAction() which will
     // update |mPreloadAction|.
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -784,16 +784,23 @@ class HTMLMediaElement : public nsGeneri
    * aMsg is the localized message identifier, aParams is the parameters to
    * be substituted into the localized message, and aParamCount is the number
    * of parameters in aParams.
    */
   void ReportLoadError(const char* aMsg, const char16_t** aParams = nullptr,
                        uint32_t aParamCount = 0);
 
   /**
+   * Log message to web console.
+   */
+  void ReportToConsole(uint32_t aErrorFlags, const char* aMsg,
+                       const char16_t** aParams = nullptr,
+                       uint32_t aParamCount = 0) const;
+
+  /**
    * Changes mHasPlayedOrSeeked to aValue. If mHasPlayedOrSeeked changes
    * we'll force a reflow so that the video frame gets reflowed to reflect
    * the poster hiding or showing immediately.
    */
   void SetPlayedOrSeeked(bool aValue);
 
   /**
    * Initialize the media element for playback of aStream
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -168,17 +168,17 @@ AudioContext::AudioContext(nsPIDOMWindow
 
   // If an AudioContext is not allowed to start, we would postpone its state
   // transition from `suspended` to `running` until sites explicitly call
   // AudioContext.resume() or AudioScheduledSourceNode.start().
   if (!allowedToStart) {
     AUTOPLAY_LOG("AudioContext %p is not allowed to start", this);
     mWasAllowedToStart = false;
     SuspendInternal(nullptr);
-    DispatchBlockedEvent();
+    ReportBlocked();
   }
 
   FFTBlock::MainThreadInit();
 }
 
 void AudioContext::NotifyScheduledSourceNodeStarted() {
   MOZ_ASSERT(NS_IsMainThread());
   // Only try to start AudioContext when AudioContext was not allowed to start.
@@ -187,17 +187,17 @@ void AudioContext::NotifyScheduledSource
   }
 
   const bool isAllowedToPlay = AutoplayPolicy::IsAllowedToPlay(*this);
   AUTOPLAY_LOG("Trying to start AudioContext %p, IsAllowedToPlay=%d", this,
                isAllowedToPlay);
   if (isAllowedToPlay) {
     ResumeInternal();
   } else {
-    DispatchBlockedEvent();
+    ReportBlocked();
   }
 }
 
 nsresult AudioContext::Init() {
   if (!mIsOffline) {
     nsresult rv = mDestination->CreateAudioChannelAgent();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
@@ -938,17 +938,17 @@ already_AddRefed<Promise> AudioContext::
 
   const bool isAllowedToPlay = AutoplayPolicy::IsAllowedToPlay(*this);
   AUTOPLAY_LOG("Trying to resume AudioContext %p, IsAllowedToPlay=%d", this,
                isAllowedToPlay);
   if (isAllowedToPlay) {
     mWasAllowedToStart = true;
     ResumeInternal();
   } else {
-    DispatchBlockedEvent();
+    ReportBlocked();
   }
 
   return promise.forget();
 }
 
 void AudioContext::ResumeInternal() {
   AUTOPLAY_LOG("Allow to resume AudioContext %p", this);
   Destination()->Resume();
@@ -961,17 +961,19 @@ void AudioContext::ResumeInternal() {
   if (mSuspendCalled) {
     streams = GetAllStreams();
   }
   Graph()->ApplyAudioContextOperation(DestinationStream(), streams,
                                       AudioContextOperation::Resume, nullptr);
   mSuspendCalled = false;
 }
 
-void AudioContext::DispatchBlockedEvent() {
+void AudioContext::ReportBlocked() {
+  ReportToConsole(nsIScriptError::warningFlag, "BlockAutoplayError");
+
   if (!StaticPrefs::MediaBlockEventEnabled()) {
     return;
   }
 
   RefPtr<AudioContext> self = this;
   RefPtr<nsIRunnable> r =
       NS_NewRunnableFunction("AudioContext::AutoplayBlocked", [self]() {
         nsPIDOMWindowInner* parent = self->GetParentObject();
@@ -1139,16 +1141,25 @@ AudioContext::CollectReports(nsIHandleRe
 BasicWaveFormCache* AudioContext::GetBasicWaveFormCache() {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mBasicWaveFormCache) {
     mBasicWaveFormCache = new BasicWaveFormCache(SampleRate());
   }
   return mBasicWaveFormCache;
 }
 
+void AudioContext::ReportToConsole(uint32_t aErrorFlags,
+                                   const char* aMsg) const {
+  MOZ_ASSERT(aMsg);
+  Document* doc =
+      GetParentObject() ? GetParentObject()->GetExtantDoc() : nullptr;
+  nsContentUtils::ReportToConsole(aErrorFlags, NS_LITERAL_CSTRING("Media"), doc,
+                                  nsContentUtils::eDOM_PROPERTIES, aMsg);
+}
+
 BasicWaveFormCache::BasicWaveFormCache(uint32_t aSampleRate)
     : mSampleRate(aSampleRate) {
   MOZ_ASSERT(NS_IsMainThread());
 }
 BasicWaveFormCache::~BasicWaveFormCache() {}
 
 WebCore::PeriodicWave* BasicWaveFormCache::GetBasicWaveForm(
     OscillatorType aType) {
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -317,18 +317,21 @@ class AudioContext final : public DOMEve
 
   friend struct ::mozilla::WebAudioDecodeJob;
 
   nsTArray<MediaStream*> GetAllStreams() const;
 
   void ResumeInternal();
   void SuspendInternal(void* aPromise);
 
-  // This event is used for testing only.
-  void DispatchBlockedEvent();
+  // Will report error message to console and dispatch testing event if needed
+  // when AudioContext is blocked by autoplay policy.
+  void ReportBlocked();
+
+  void ReportToConsole(uint32_t aErrorFlags, const char* aMsg) const;
 
  private:
   // Each AudioContext has an id, that is passed down the MediaStreams that
   // back the AudioNodes, so we can easily compute the set of all the
   // MediaStreams for a given context, on the MediasStreamGraph side.
   const AudioContextId mId;
   // Note that it's important for mSampleRate to be initialized before
   // mDestination, as mDestination's constructor needs to access it!