Bug 1315551 - part2 : check we have initialized agent when we want to call the agent's function. r=jwwang
authorAlastor Wu <alwu@mozilla.com>
Tue, 08 Nov 2016 11:08:02 +0800
changeset 435621 dd2c93bc1da35cbd72f15896222f5744b6ae04cc
parent 435620 967dd79df01ce7a8702c87bf568a0b4807bf1561
child 435622 00f8bca94661bc5be13c1635b63e086ba7f03ce6
push id35078
push usermbrubeck@mozilla.com
push dateTue, 08 Nov 2016 22:02:54 +0000
reviewersjwwang
bugs1315551
milestone52.0a1
Bug 1315551 - part2 : check we have initialized agent when we want to call the agent's function. r=jwwang Because the agent's initialization might fail if we don't get the valid inner window, we need to check whether the agent exists before calling the agent's method. MozReview-Commit-ID: IUuvyGh7CMd
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2947,17 +2947,17 @@ HTMLMediaElement::HTMLMediaElement(alrea
 
   MOZ_ASSERT(NS_IsMainThread());
   mWatchManager.Watch(mDownloadSuspendedByCache, &HTMLMediaElement::UpdateReadyStateInternal);
   // Paradoxically, there is a self-edge whereby UpdateReadyStateInternal refuses
   // to run until mReadyState reaches at least HAVE_METADATA by some other means.
   mWatchManager.Watch(mReadyState, &HTMLMediaElement::UpdateReadyStateInternal);
 
   mShutdownObserver->Subscribe(this);
-  CreateAudioChannelAgent();
+  MaybeCreateAudioChannelAgent();
 }
 
 HTMLMediaElement::~HTMLMediaElement()
 {
   NS_ASSERTION(!mHasSelfReference,
                "How can we be destroyed if we're still holding a self reference?");
 
   mShutdownObserver->Unsubscribe();
@@ -2988,16 +2988,17 @@ HTMLMediaElement::~HTMLMediaElement()
   NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 0,
     "Destroyed media element should no longer be in element table");
 
   if (mChannelLoader) {
     mChannelLoader->Cancel();
   }
 
   WakeLockRelease();
+  mAudioChannelAgent = nullptr;
 }
 
 void HTMLMediaElement::StopSuspendingAfterFirstFrame()
 {
   mAllowSuspendAfterFirstFrame = false;
   if (!mSuspendedAfterFirstFrame)
     return;
   mSuspendedAfterFirstFrame = false;
@@ -5750,27 +5751,36 @@ NS_IMETHODIMP HTMLMediaElement::SetMozPr
 }
 
 ImageContainer* HTMLMediaElement::GetImageContainer()
 {
   VideoFrameContainer* container = GetVideoFrameContainer();
   return container ? container->GetImageContainer() : nullptr;
 }
 
-void
-HTMLMediaElement::CreateAudioChannelAgent()
+bool
+HTMLMediaElement::MaybeCreateAudioChannelAgent()
 {
   if (mAudioChannelAgent) {
-    return;
+    return true;
   }
 
   mAudioChannelAgent = new AudioChannelAgent();
-  mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(),
+  nsresult rv = mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetInnerWindow(),
                                            static_cast<int32_t>(mAudioChannel),
                                            this);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mAudioChannelAgent = nullptr;
+    MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+           ("HTMLMediaElement, Fail to initialize the audio channel agent,"
+            " this = %p\n", this));
+    return false;
+  }
+
+  return true;
 }
 
 bool
 HTMLMediaElement::IsPlayingThroughTheAudioChannel() const
 {
   // If we have an error, we are not playing.
   if (mError) {
     return false;
@@ -5816,16 +5826,20 @@ HTMLMediaElement::IsPlayingThroughTheAud
 
 void
 HTMLMediaElement::UpdateAudioChannelPlayingState(bool aForcePlaying)
 {
   bool playingThroughTheAudioChannel =
     aForcePlaying || IsPlayingThroughTheAudioChannel();
 
   if (playingThroughTheAudioChannel != mPlayingThroughTheAudioChannel) {
+    if (!MaybeCreateAudioChannelAgent()) {
+      return;
+    }
+
     mPlayingThroughTheAudioChannel = playingThroughTheAudioChannel;
     NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel);
   }
 }
 
 void
 HTMLMediaElement::NotifyAudioChannelAgent(bool aPlaying)
 {
@@ -6026,29 +6040,30 @@ HTMLMediaElement::IsAllowedToPlay()
   if (mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE ||
       mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK) {
     return false;
   }
 
   // If the tab hasn't been activated yet, the media element in that tab can't
   // be playback now until the tab goes to foreground first time or user clicks
   // the unblocking tab icon.
-  if (!IsTabActivated()) {
+  if (MaybeCreateAudioChannelAgent() && !IsTabActivated()) {
     // Even we haven't start playing yet, we still need to notify the audio
     // channe system because we need to receive the resume notification later.
     UpdateAudioChannelPlayingState(true /* force to start */);
     return false;
   }
 
   return true;
 }
 
 bool
 HTMLMediaElement::IsTabActivated() const
 {
+  MOZ_ASSERT(mAudioChannelAgent);
   return !mAudioChannelAgent->ShouldBlockMedia();
 }
 
 static const char* VisibilityString(Visibility aVisibility) {
   switch(aVisibility) {
     case Visibility::UNTRACKED: {
       return "UNTRACKED";
     }
@@ -6450,19 +6465,18 @@ HTMLMediaElement::SetAudibleState(bool a
     NotifyAudioPlaybackChanged(
       AudioChannelService::AudibleChangedReasons::eDataAudibleChanged);
   }
 }
 
 void
 HTMLMediaElement::NotifyAudioPlaybackChanged(AudibleChangedReasons aReason)
 {
-  MOZ_ASSERT(mAudioChannelAgent);
-
-  if (!mAudioChannelAgent->IsPlayingStarted()) {
+  if (MaybeCreateAudioChannelAgent() &&
+      !mAudioChannelAgent->IsPlayingStarted()) {
     return;
   }
 
   if (mAudible == IsAudible()) {
     return;
   }
 
   mAudible = IsAudible();
@@ -6495,16 +6509,17 @@ HTMLMediaElement::MaybeNotifyMediaResume
 {
   // In fennec, we should send the notification when media is resumed from the
   // pause-disposable which was called by media control.
   if (mAudioChannelSuspended != nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE &&
       aSuspend != nsISuspendedTypes::NONE_SUSPENDED) {
     return;
   }
 
+  MOZ_ASSERT(mAudioChannelAgent);
   uint64_t windowID = mAudioChannelAgent->WindowID();
   NS_DispatchToMainThread(NS_NewRunnableFunction([windowID]() -> void {
     nsCOMPtr<nsIObserverService> observerService =
       services::GetObserverService();
     if (NS_WARN_IF(!observerService)) {
       return;
     }
 
@@ -6571,24 +6586,23 @@ HTMLMediaElement::SetMediaInfo(const Med
 {
   mMediaInfo = aInfo;
   AudioCaptureStreamChangeIfNeeded();
 }
 
 void
 HTMLMediaElement::AudioCaptureStreamChangeIfNeeded()
 {
-  MOZ_ASSERT(mAudioChannelAgent);
-
   // No need to capture a silence media element.
   if (!HasAudio()) {
     return;
   }
 
-  if (!mAudioChannelAgent->IsPlayingStarted()) {
+  if (MaybeCreateAudioChannelAgent() &&
+      !mAudioChannelAgent->IsPlayingStarted()) {
     return;
   }
 
   if (mAudioCapturedByWindow && !mCaptureStreamPort) {
     nsCOMPtr<nsPIDOMWindowInner> window = OwnerDoc()->GetInnerWindow();
     if (!OwnerDoc()->GetInnerWindow()) {
       return;
     }
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1224,19 +1224,21 @@ protected:
   TextTrackManager* GetOrCreateTextTrackManager();
 
   // Recomputes ready state and fires events as necessary based on current state.
   void UpdateReadyStateInternal();
 
   // Notifies the audio channel agent when the element starts or stops playing.
   void NotifyAudioChannelAgent(bool aPlaying);
 
-  // Creates the audio channel agent in the beginning and this agent would be
-  // used to communicate with the AudioChannelService.
-  void CreateAudioChannelAgent();
+  // True if we create the audio channel agent successfully or we already have
+  // one. The agent is used to communicate with the AudioChannelService. eg.
+  // notify we are playing/audible and receive muted/unmuted/suspend/resume
+  // commands from AudioChannelService.
+  bool MaybeCreateAudioChannelAgent();
 
   // Determine if the element should be paused because of suspend conditions.
   bool ShouldElementBePaused();
 
   // Create or destroy the captured stream depend on mAudioCapturedByWindow.
   void AudioCaptureStreamChangeIfNeeded();
 
   /**