Bug 1242874 - part3 : implement different suspended methods. r=baku, r=jwwang
authorAlastor Wu <alwu@mozilla.com>
Tue, 03 May 2016 09:52:38 +0800
changeset 295998 9f43f3fe639247aede82f30c122e30c8c884ec17
parent 295997 f06def2d44cb3159c3402089fee29de07e02e232
child 295999 7e329206ebadef5ebb9ae586b4debff97d565501
push id76173
push usercbook@mozilla.com
push dateWed, 04 May 2016 07:38:26 +0000
treeherdermozilla-inbound@ea6a844dc6b9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, jwwang
bugs1242874
milestone49.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 1242874 - part3 : implement different suspended methods. r=baku, r=jwwang MozReview-Commit-ID: HHYX29gbLwk
dom/audiochannel/AudioChannelAgent.cpp
dom/audiochannel/AudioChannelAgent.h
dom/audiochannel/AudioChannelService.cpp
dom/audiochannel/AudioChannelService.h
dom/base/nsGlobalWindow.cpp
dom/fmradio/FMRadio.cpp
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
dom/media/webaudio/AudioDestinationNode.cpp
dom/media/webaudio/AudioDestinationNode.h
dom/media/webspeech/synth/nsSpeechTask.cpp
dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
dom/plugins/base/nsNPAPIPluginInstance.cpp
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -218,21 +218,26 @@ NS_IMETHODIMP AudioChannelAgent::NotifyS
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       service == nullptr || mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
   service->RegisterAudioChannelAgent(this,
     static_cast<AudioChannel>(mAudioChannelType));
 
-  service->GetState(mWindow, mAudioChannelType, aVolume, aMuted);
+  AudioPlaybackConfig config = service->GetMediaConfig(mWindow,
+                                                       mAudioChannelType);
 
   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
-         ("AudioChannelAgent, NotifyStartedPlaying, this = %p, mute = %d, "
-          "volume = %f\n", this, *aMuted, *aVolume));
+         ("AudioChannelAgent, NotifyStartedPlaying, this = %p, "
+          "mute = %d, volume = %f, suspend = %d\n", this,
+          config.mMuted, config.mVolume, config.mSuspend));
+
+  *aVolume = config.mVolume;
+  *aMuted = config.mMuted;
 
   mIsRegToService = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP AudioChannelAgent::NotifyStoppedPlaying()
 {
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
@@ -265,29 +270,59 @@ AudioChannelAgent::GetCallback()
 void
 AudioChannelAgent::WindowVolumeChanged()
 {
   nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
   if (!callback) {
     return;
   }
 
-  float volume = 1.0;
-  bool muted = false;
+  AudioPlaybackConfig config = GetMediaConfig();
+  MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+         ("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %d, "
+          "volume = %f\n", this, config.mMuted, config.mVolume));
+
+  callback->WindowVolumeChanged(config.mVolume, config.mMuted);
+}
 
-  RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
-  if (service) {
-    service->GetState(mWindow, mAudioChannelType, &volume, &muted);
+void
+AudioChannelAgent::WindowSuspendChanged(nsSuspendedTypes aSuspend)
+{
+  nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
+  if (!callback) {
+    return;
+  }
+
+  if (!IsDisposableSuspend(aSuspend)) {
+    aSuspend = GetMediaConfig().mSuspend;
   }
 
   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
-         ("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %d, "
-          "volume = %f\n", this, muted, volume));
+         ("AudioChannelAgent, WindowSuspendChanged, this = %p, "
+          "suspended = %d\n", this, aSuspend));
+
+  callback->WindowSuspendChanged(aSuspend);
+}
 
-  callback->WindowVolumeChanged(volume, muted);
+AudioPlaybackConfig
+AudioChannelAgent::GetMediaConfig()
+{
+  RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+  AudioPlaybackConfig config(1.0, false, nsISuspendedTypes::NONE_SUSPENDED);
+  if (service) {
+    config = service->GetMediaConfig(mWindow, mAudioChannelType);
+  }
+  return config;
+}
+
+bool
+AudioChannelAgent::IsDisposableSuspend(nsSuspendedTypes aSuspend) const
+{
+  return (aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
+          aSuspend == nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE);
 }
 
 uint64_t
 AudioChannelAgent::WindowID() const
 {
   return mWindow ? mWindow->WindowID() : 0;
 }
 
--- a/dom/audiochannel/AudioChannelAgent.h
+++ b/dom/audiochannel/AudioChannelAgent.h
@@ -18,41 +18,47 @@
       {0x90, 0x4e, 0x10, 0xbf, 0x48, 0xd6, 0x4b, 0xd4}}
 
 class nsPIDOMWindowInner;
 class nsPIDOMWindowOuter;
 
 namespace mozilla {
 namespace dom {
 
+class AudioPlaybackConfig;
+
 /* Header file */
 class AudioChannelAgent : public nsIAudioChannelAgent
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIAUDIOCHANNELAGENT
 
   NS_DECL_CYCLE_COLLECTION_CLASS(AudioChannelAgent)
 
   AudioChannelAgent();
 
   void WindowVolumeChanged();
+  void WindowSuspendChanged(nsSuspendedTypes aSuspend);
   void WindowAudioCaptureChanged(uint64_t aInnerWindowID, bool aCapture);
 
   nsPIDOMWindowOuter* Window() const
   {
     return mWindow;
   }
 
   uint64_t WindowID() const;
   uint64_t InnerWindowID() const;
 
 private:
   virtual ~AudioChannelAgent();
 
+  AudioPlaybackConfig GetMediaConfig();
+  bool IsDisposableSuspend(nsSuspendedTypes aSuspend) const;
+
   // Returns mCallback if that's non-null, or otherwise tries to get an
   // nsIAudioChannelAgentCallback out of mWeakCallback.
   already_AddRefed<nsIAudioChannelAgentCallback> GetCallback();
 
   nsresult InitInternal(nsPIDOMWindowInner* aWindow, int32_t aAudioAgentType,
                         nsIAudioChannelAgentCallback* aCallback,
                         bool aUseWeakRef);
 
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -348,59 +348,62 @@ AudioChannelService::RegisterTabParent(T
 
 void
 AudioChannelService::UnregisterTabParent(TabParent* aTabParent)
 {
   MOZ_ASSERT(aTabParent);
   mTabParents.RemoveElement(aTabParent);
 }
 
-void
-AudioChannelService::GetState(nsPIDOMWindowOuter* aWindow, uint32_t aAudioChannel,
-                              float* aVolume, bool* aMuted)
+AudioPlaybackConfig
+AudioChannelService::GetMediaConfig(nsPIDOMWindowOuter* aWindow,
+                                    uint32_t aAudioChannel) const
 {
   MOZ_ASSERT(!aWindow || aWindow->IsOuterWindow());
-  MOZ_ASSERT(aVolume && aMuted);
   MOZ_ASSERT(aAudioChannel < NUMBER_OF_AUDIO_CHANNELS);
 
+  AudioPlaybackConfig config(1.0, false,
+                             nsISuspendedTypes::NONE_SUSPENDED);
 
   if (!aWindow || !aWindow->IsOuterWindow()) {
-    *aVolume = 0.0;
-    *aMuted = true;
-    return;
+    config.SetConfig(0.0, true,
+                     nsISuspendedTypes::SUSPENDED_BLOCK);
+    return config;
   }
 
-  *aVolume = 1.0;
-  *aMuted = false;
-
   AudioChannelWindow* winData = nullptr;
   nsCOMPtr<nsPIDOMWindowOuter> window = aWindow;
 
   // The volume must be calculated based on the window hierarchy. Here we go up
   // to the top window and we calculate the volume and the muted flag.
   do {
     winData = GetWindowData(window->WindowID());
     if (winData) {
-      *aVolume *= winData->mChannels[aAudioChannel].mVolume;
-      *aMuted = *aMuted || winData->mChannels[aAudioChannel].mMuted;
+      config.mVolume *= winData->mChannels[aAudioChannel].mVolume;
+      config.mMuted = config.mMuted || winData->mChannels[aAudioChannel].mMuted;
     }
 
-    *aVolume *= window->GetAudioVolume();
-    // TODO : distiguish between suspend and mute, it would be done in bug1242874.
-    *aMuted = *aMuted || window->GetMediaSuspended() || window->GetAudioMuted();
+    config.mVolume *= window->GetAudioVolume();
+    config.mMuted = config.mMuted || window->GetAudioMuted();
+
+    // If the mSuspend is already suspended, we don't need to set it again.
+    config.mSuspend = (config.mSuspend == nsISuspendedTypes::NONE_SUSPENDED) ?
+      window->GetMediaSuspend() : config.mSuspend;
 
     nsCOMPtr<nsPIDOMWindowOuter> win = window->GetScriptableParentOrNull();
     if (!win) {
       break;
     }
 
     window = do_QueryInterface(win);
 
     // If there is no parent, or we are the toplevel we don't continue.
   } while (window && window != aWindow);
+
+  return config;
 }
 
 bool
 AudioChannelService::TelephonyChannelIsActive()
 {
   nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator windowsIter(mWindows);
   while (windowsIter.HasMore()) {
     AudioChannelWindow* next = windowsIter.GetNext();
@@ -595,17 +598,18 @@ AudioChannelService::RefreshAgentsVolume
                                                    winData->mChannels[(uint32_t)aAudioChannel].mVolume,
                                                    winData->mChannels[(uint32_t)aAudioChannel].mMuted);
   }
 
   RefreshAgentsVolume(aWindow);
 }
 
 void
-AudioChannelService::RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow)
+AudioChannelService::RefreshAgents(nsPIDOMWindowOuter* aWindow,
+                                   mozilla::function<void(AudioChannelAgent*)> aFunc)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aWindow->IsOuterWindow());
 
   nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetScriptableTop();
   if (!topWindow) {
     return;
   }
@@ -613,21 +617,38 @@ AudioChannelService::RefreshAgentsVolume
   AudioChannelWindow* winData = GetWindowData(topWindow->WindowID());
   if (!winData) {
     return;
   }
 
   nsTObserverArray<AudioChannelAgent*>::ForwardIterator
     iter(winData->mAgents);
   while (iter.HasMore()) {
-    iter.GetNext()->WindowVolumeChanged();
+    aFunc(iter.GetNext());
   }
 }
 
 void
+AudioChannelService::RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow)
+{
+  RefreshAgents(aWindow, [] (AudioChannelAgent* agent) {
+    agent->WindowVolumeChanged();
+  });
+}
+
+void
+AudioChannelService::RefreshAgentsSuspend(nsPIDOMWindowOuter* aWindow,
+                                          nsSuspendedTypes aSuspend)
+{
+  RefreshAgents(aWindow, [aSuspend] (AudioChannelAgent* agent) {
+    agent->WindowSuspendChanged(aSuspend);
+  });
+}
+
+void
 AudioChannelService::SetWindowAudioCaptured(nsPIDOMWindowOuter* aWindow,
                                             uint64_t aInnerWindowID,
                                             bool aCapture)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aWindow->IsOuterWindow());
 
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -11,32 +11,60 @@
 #include "nsAutoPtr.h"
 #include "nsIObserver.h"
 #include "nsTObserverArray.h"
 #include "nsTArray.h"
 
 #include "AudioChannelAgent.h"
 #include "nsAttrValue.h"
 #include "mozilla/dom/AudioChannelBinding.h"
+#include "mozilla/Function.h"
 
 class nsIRunnable;
 class nsPIDOMWindowOuter;
 struct PRLogModuleInfo;
 
 namespace mozilla {
 namespace dom {
 
 #ifdef MOZ_WIDGET_GONK
 class SpeakerManagerService;
 #endif
 
 class TabParent;
 
 #define NUMBER_OF_AUDIO_CHANNELS (uint32_t)AudioChannel::EndGuard_
 
+class AudioPlaybackConfig
+{
+public:
+  AudioPlaybackConfig()
+    : mVolume(1.0)
+    , mMuted(false)
+    , mSuspend(nsISuspendedTypes::NONE_SUSPENDED)
+  {}
+
+  AudioPlaybackConfig(float aVolume, bool aMuted, uint32_t aSuspended)
+    : mVolume(aVolume)
+    , mMuted(aMuted)
+    , mSuspend(aSuspended)
+  {}
+
+  void SetConfig(float aVolume, bool aMuted, uint32_t aSuspended)
+  {
+    mVolume = aVolume;
+    mMuted = aMuted;
+    mSuspend = aSuspended;
+  }
+
+  float mVolume;
+  bool mMuted;
+  uint32_t mSuspend;
+};
+
 class AudioChannelService final : public nsIAudioChannelService
                                 , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIAUDIOCHANNELSERVICE
 
@@ -67,20 +95,20 @@ public:
   /**
    * For nested iframes.
    */
   void RegisterTabParent(TabParent* aTabParent);
   void UnregisterTabParent(TabParent* aTabParent);
 
   /**
    * Return the state to indicate this audioChannel for his window should keep
-   * playing/muted.
+   * playing/muted/suspended.
    */
-  void GetState(nsPIDOMWindowOuter* aWindow, uint32_t aChannel,
-                float* aVolume, bool* aMuted);
+  AudioPlaybackConfig GetMediaConfig(nsPIDOMWindowOuter* aWindow,
+                                     uint32_t aAudioChannel) const;
 
   /* Methods for the BrowserElementAudioChannel */
   float GetAudioChannelVolume(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel);
 
   void SetAudioChannelVolume(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel,
                              float aVolume);
 
   bool GetAudioChannelMuted(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel);
@@ -109,16 +137,18 @@ public:
    * AudioChannel enum.
    */
   virtual void SetDefaultVolumeControlChannel(int32_t aChannel,
                                               bool aVisible);
 
   bool AnyAudioChannelIsActive();
 
   void RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow);
+  void RefreshAgentsSuspend(nsPIDOMWindowOuter* aWindow,
+                            nsSuspendedTypes aSuspend);
 
   void RefreshAgentsVolumeAndPropagate(AudioChannel aAudioChannel,
                                        nsPIDOMWindowOuter* aWindow);
 
   // This method needs to know the inner window that wants to capture audio. We
   // group agents per top outer window, but we can have multiple innerWindow per
   // top outerWindow (subiframes, etc.) and we have to identify all the agents
   // just for a particular innerWindow.
@@ -150,42 +180,43 @@ public:
 
   void ChildStatusReceived(uint64_t aChildID, bool aTelephonyChannel,
                            bool aContentOrNormalChannel, bool aAnyChannel);
 
 private:
   AudioChannelService();
   ~AudioChannelService();
 
+  void RefreshAgents(nsPIDOMWindowOuter* aWindow,
+                     mozilla::function<void(AudioChannelAgent*)> aFunc);
+
   static void CreateServiceIfNeeded();
 
   /**
    * Shutdown the singleton.
    */
   static void Shutdown();
 
   void MaybeSendStatusUpdate();
 
   bool ContentOrNormalChannelIsActive();
 
   /* Send the default-volume-channel-changed notification */
   void SetDefaultVolumeControlChannelInternal(int32_t aChannel,
                                               bool aVisible, uint64_t aChildID);
 
-  struct AudioChannelConfig final
+  class AudioChannelConfig final : public AudioPlaybackConfig
   {
+  public:
     AudioChannelConfig()
-      : mVolume(1.0)
-      , mMuted(IsAudioChannelMutedByDefault())
+      : AudioPlaybackConfig(1.0, IsAudioChannelMutedByDefault(),
+                            nsISuspendedTypes::NONE_SUSPENDED)
       , mNumberOfAgents(0)
     {}
 
-    float mVolume;
-    bool mMuted;
-
     uint32_t mNumberOfAgents;
   };
 
   struct AudioChannelWindow final
   {
     explicit AudioChannelWindow(uint64_t aWindowID)
       : mWindowID(aWindowID),
         mIsAudioCaptured(false)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3981,17 +3981,17 @@ nsPIDOMWindowOuter::RefreshMediaElements
   }
 }
 
 void
 nsPIDOMWindowOuter::RefreshMediaElementsSuspend(SuspendTypes aSuspend)
 {
   RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (service) {
-    // TODO : Impelement in next patch.
+    service->RefreshAgentsSuspend(GetOuterWindow(), aSuspend);
   }
 }
 
 bool
 nsPIDOMWindowOuter::IsDisposableSuspend(SuspendTypes aSuspend) const
 {
   return (aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
           aSuspend == nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE);
--- a/dom/fmradio/FMRadio.cpp
+++ b/dom/fmradio/FMRadio.cpp
@@ -458,25 +458,26 @@ FMRadio::EnableAudioChannelAgent()
   WindowVolumeChanged(volume, muted);
 
   mAudioChannelAgentEnabled = true;
 }
 
 NS_IMETHODIMP
 FMRadio::WindowVolumeChanged(float aVolume, bool aMuted)
 {
+  // TODO : Not support to change volume now, so we just close it.
   IFMRadioService::Singleton()->EnableAudio(!aMuted);
-  // TODO: what about the volume?
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FMRadio::WindowSuspendChanged(nsSuspendedTypes aSuspend)
 {
-  // TODO : implementation.
+  bool enable = (aSuspend == nsISuspendedTypes::NONE_SUSPENDED);
+  IFMRadioService::Singleton()->EnableAudio(enable);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FMRadio::WindowAudioCaptureChanged(bool aCapture)
 {
   return NS_OK;
 }
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1157,21 +1157,16 @@ void HTMLMediaElement::ResumeLoad(Preloa
   }
 }
 
 static bool IsAutoplayEnabled()
 {
   return Preferences::GetBool("media.autoplay.enabled");
 }
 
-static bool UseAudioChannelAPI()
-{
-  return Preferences::GetBool("media.useAudioChannelAPI");
-}
-
 void HTMLMediaElement::UpdatePreloadAction()
 {
   PreloadAction nextAction = PRELOAD_UNDEFINED;
   // If autoplay is set, or we're playing, we should always preload data,
   // as we'll need it to play.
   if ((IsAutoplayEnabled() && HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay)) ||
       !mPaused)
   {
@@ -2239,16 +2234,17 @@ HTMLMediaElement::HTMLMediaElement(alrea
     mPlayed(new TimeRanges(ToSupports(OwnerDoc()))),
     mCurrentPlayRangeStart(-1.0),
     mBegun(false),
     mLoadedDataFired(false),
     mAutoplaying(true),
     mAutoplayEnabled(true),
     mPaused(true),
     mMuted(0),
+    mAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED),
     mStatsShowing(false),
     mAllowCasting(false),
     mIsCasting(false),
     mAudioCaptured(false),
     mAudioCapturedByWindow(false),
     mPlayingBeforeSeek(false),
     mPlayingThroughTheAudioChannelBeforeSeek(false),
     mPausedForInactiveDocumentOrChannel(false),
@@ -2390,30 +2386,17 @@ HTMLMediaElement::Play(ErrorResult& aRv)
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
   }
 }
 
 nsresult
 HTMLMediaElement::PlayInternal(bool aCallerIsChrome)
 {
-  // Prevent media element from being auto-started by a script when
-  // media.autoplay.enabled=false
-  if (!mHasUserInteraction
-      && !IsAutoplayEnabled()
-      && !EventStateManager::IsHandlingUserInput()
-      && !aCallerIsChrome) {
-    LOG(LogLevel::Debug, ("%p Blocked attempt to autoplay media.", this));
-#if defined(MOZ_WIDGET_ANDROID)
-    nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
-                                         static_cast<nsIContent*>(this),
-                                         NS_LITERAL_STRING("MozAutoplayMediaBlocked"),
-                                         false,
-                                         false);
-#endif
+  if (!IsAllowedToPlay()) {
     return NS_OK;
   }
 
   // Play was not blocked so assume user interacted with the element.
   mHasUserInteraction = true;
 
   StopSuspendingAfterFirstFrame();
   SetPlayedOrSeeked(true);
@@ -2468,16 +2451,18 @@ HTMLMediaElement::PlayInternal(bool aCal
     case nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA:
       DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
       break;
     }
   }
 
   mPaused = false;
   mAutoplaying = false;
+  SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
+
   // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
   // and our preload status.
   AddRemoveSelfReference();
   UpdatePreloadAction();
   UpdateSrcMediaStreamPlaying();
   UpdateAudioChannelPlayingState();
 
   return NS_OK;
@@ -4533,22 +4518,17 @@ bool HTMLMediaElement::IsBeingDestroyed(
     docShell->IsBeingDestroyed(&isBeingDestroyed);
   }
   return isBeingDestroyed;
 }
 
 void HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
 {
   bool pauseElement = NotifyOwnerDocumentActivityChangedInternal();
-  if (pauseElement && mAudioChannelAgent &&
-      // On B2G, NotifyOwnerDocumentActivityChangedInternal may return true for
-      // two reasons: the document no longer being active, or the element being
-      // paused by the audio channel.  However we are only interested in the
-      // first case here, so we need to filter out the second case.
-      (!UseAudioChannelAPI() || !ComputedMuted())) {
+  if (pauseElement && mAudioChannelAgent) {
     // If the element is being paused since we are navigating away from the
     // document, notify the audio channel agent.
     // Be careful to ignore this event during a docshell frame swap.
     auto docShell = static_cast<nsDocShell*>(OwnerDoc()->GetDocShell());
     if (!docShell) {
       return;
     }
     if (!docShell->InFrameSwap()) {
@@ -4562,24 +4542,16 @@ HTMLMediaElement::NotifyOwnerDocumentAct
 {
   nsIDocument* ownerDoc = OwnerDoc();
   if (mDecoder && !IsBeingDestroyed()) {
     mDecoder->SetElementVisibility(!ownerDoc->Hidden());
     mDecoder->NotifyOwnerActivityChanged();
   }
 
   bool pauseElement = !IsActive();
-  // Only pause the element when we start playing. If we pause without playing
-  // audio, the resource loading would be affected unexpectedly. For example,
-  // the media element is muted by default, but we don't want this behavior
-  // interrupting the loading process.
-  if (UseAudioChannelAPI() && mAudioChannelAgent) {
-    pauseElement |= ComputedMuted();
-  }
-
   SuspendOrResumeElement(pauseElement, !IsActive());
 
   if (!mPausedForInactiveDocumentOrChannel &&
       mPlayBlockedBecauseHidden &&
       !OwnerDoc()->Hidden()) {
     LOG(LogLevel::Debug, ("%p Resuming playback now that owner doc is visble.", this));
     mPlayBlockedBecauseHidden = false;
     Play();
@@ -4983,43 +4955,16 @@ NS_IMETHODIMP HTMLMediaElement::SetMozPr
 }
 
 ImageContainer* HTMLMediaElement::GetImageContainer()
 {
   VideoFrameContainer* container = GetVideoFrameContainer();
   return container ? container->GetImageContainer() : nullptr;
 }
 
-nsresult HTMLMediaElement::UpdateChannelMuteState(float aVolume, bool aMuted)
-{
-  if (mAudioChannelVolume != aVolume) {
-    mAudioChannelVolume = aVolume;
-    SetVolumeInternal();
-  }
-
-  // We have to mute this channel.
-  if (aMuted && !ComputedMuted()) {
-    SetMutedInternal(mMuted | MUTED_BY_AUDIO_CHANNEL);
-    if (UseAudioChannelAPI()) {
-      DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptbegin"));
-    }
-  } else if (!aMuted && ComputedMuted()) {
-    SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_CHANNEL);
-    if (UseAudioChannelAPI()) {
-      DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptend"));
-    }
-  }
-
-  if (UseAudioChannelAPI()) {
-    SuspendOrResumeElement(ComputedMuted(), false);
-  }
-
-  return NS_OK;
-}
-
 bool
 HTMLMediaElement::MaybeCreateAudioChannelAgent()
 {
   if (!mAudioChannelAgent) {
     nsresult rv;
     mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return false;
@@ -5030,16 +4975,21 @@ HTMLMediaElement::MaybeCreateAudioChanne
                                              this);
   }
   return true;
 }
 
 bool
 HTMLMediaElement::IsPlayingThroughTheAudioChannel() const
 {
+  // It might be resumed from remote, we should keep the audio channel agent.
+  if (IsSuspendedByAudioChannel()) {
+    return true;
+  }
+
   // Are we paused or muted
   if (mPaused || Muted()) {
     return false;
   }
 
   // If we have an error, we are not playing.
   if (mError) {
     return false;
@@ -5116,36 +5066,186 @@ HTMLMediaElement::NotifyAudioChannelAgen
     mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
     WindowVolumeChanged(volume, muted);
   } else {
     mAudioChannelAgent->NotifyStoppedPlaying();
     mAudioChannelAgent = nullptr;
   }
 }
 
-NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged(float aVolume, bool aMuted)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  UpdateChannelMuteState(aVolume, aMuted);
-
-  if (UseAudioChannelAPI()) {
-    mPaused.SetCanPlay(!aMuted);
+NS_IMETHODIMP
+HTMLMediaElement::WindowVolumeChanged(float aVolume, bool aMuted)
+{
+  MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+         ("HTMLMediaElement, WindowVolumeChanged, this = %p, "
+          "aVolume = %f, aMuted = %d\n", this, aVolume, aMuted));
+
+  if (mAudioChannelVolume != aVolume) {
+    mAudioChannelVolume = aVolume;
+    SetVolumeInternal();
+  }
+
+  if (aMuted && !ComputedMuted()) {
+    SetMutedInternal(mMuted | MUTED_BY_AUDIO_CHANNEL);
+  } else if (!aMuted && ComputedMuted()) {
+    SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_CHANNEL);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-HTMLMediaElement::WindowSuspendChanged(nsSuspendedTypes aSuspend)
-{
-  // TODO : implementation.
+HTMLMediaElement::WindowSuspendChanged(SuspendTypes aSuspend)
+{
+  MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+         ("HTMLMediaElement, WindowSuspendChanged, this = %p, "
+          "aSuspend = %d\n", this, aSuspend));
+
+  switch (aSuspend) {
+    case nsISuspendedTypes::NONE_SUSPENDED:
+      ResumeFromAudioChannel();
+      break;
+    case nsISuspendedTypes::SUSPENDED_PAUSE:
+    case nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE:
+      PauseByAudioChannel(aSuspend);
+      break;
+    case nsISuspendedTypes::SUSPENDED_BLOCK:
+      BlockByAudioChannel();
+      break;
+    case nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE:
+      Pause();
+      break;
+    default:
+      MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+             ("HTMLMediaElement, WindowSuspendChanged, this = %p, "
+              "Error : unknown suspended type!\n", this));
+  }
+
   return NS_OK;
 }
 
+void
+HTMLMediaElement::ResumeFromAudioChannel()
+{
+  if (!IsSuspendedByAudioChannel()) {
+    return;
+  }
+
+  switch (mAudioChannelSuspended) {
+    case nsISuspendedTypes::SUSPENDED_PAUSE:
+    case nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE:
+      ResumeFromAudioChannelPaused(mAudioChannelSuspended);
+      break;
+    case nsISuspendedTypes::SUSPENDED_BLOCK:
+      ResumeFromAudioChannelBlocked();
+      break;
+    default:
+      MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+             ("HTMLMediaElement, ResumeFromAudioChannel, this = %p, "
+              "Error : resume without suspended!\n", this));
+  }
+}
+
+void
+HTMLMediaElement::ResumeFromAudioChannelPaused(SuspendTypes aSuspend)
+{
+  MOZ_ASSERT(mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE ||
+             mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE);
+
+  SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
+  nsresult rv = PlayInternal(nsContentUtils::IsCallerChrome());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+  DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptend"));
+}
+
+void
+HTMLMediaElement::ResumeFromAudioChannelBlocked()
+{
+  MOZ_ASSERT(mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK);
+
+  SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
+  mPaused.SetCanPlay(true);
+  SuspendOrResumeElement(false /* resume */, false);
+}
+
+void
+HTMLMediaElement::PauseByAudioChannel(SuspendTypes aSuspend)
+{
+  if (IsSuspendedByAudioChannel()) {
+    return;
+  }
+
+  SetAudioChannelSuspended(aSuspend);
+  Pause();
+  DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptbegin"));
+}
+
+void
+HTMLMediaElement::BlockByAudioChannel()
+{
+  if (IsSuspendedByAudioChannel()) {
+    return;
+  }
+
+  SetAudioChannelSuspended(nsISuspendedTypes::SUSPENDED_BLOCK);
+  SuspendOrResumeElement(true /* suspend */, false);
+  mPaused.SetCanPlay(false);
+}
+
+void
+HTMLMediaElement::SetAudioChannelSuspended(SuspendTypes aSuspend)
+{
+  if (mAudioChannelSuspended == aSuspend) {
+    return;
+  }
+
+  mAudioChannelSuspended = aSuspend;
+  MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+         ("HTMLMediaElement, SetAudioChannelSuspended, this = %p, "
+          "aSuspend = %d\n", this, aSuspend));
+}
+
+bool
+HTMLMediaElement::IsSuspendedByAudioChannel() const
+{
+  return (mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE ||
+          mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
+          mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK);
+}
+
+bool
+HTMLMediaElement::IsAllowedToPlay()
+{
+  // Prevent media element from being auto-started by a script when
+  // media.autoplay.enabled=false
+  if (!mHasUserInteraction &&
+      !IsAutoplayEnabled() &&
+      !EventStateManager::IsHandlingUserInput() &&
+      !nsContentUtils::IsCallerChrome()) {
+#if defined(MOZ_WIDGET_ANDROID)
+    nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
+                                         static_cast<nsIContent*>(this),
+                                         NS_LITERAL_STRING("MozAutoplayMediaBlocked"),
+                                         false,
+                                         false);
+#endif
+    return false;
+  }
+
+  // The MediaElement can't start playback until it's resumed by audio channel.
+  if (mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_PAUSE ||
+      mAudioChannelSuspended == nsISuspendedTypes::SUSPENDED_BLOCK) {
+    return false;
+  }
+
+  return true;
+}
+
 #ifdef MOZ_EME
 MediaKeys*
 HTMLMediaElement::GetMediaKeys() const
 {
   return mMediaKeys;
 }
 
 bool
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -32,16 +32,17 @@
 
 #include "mozilla/dom/HTMLMediaElementBinding.h"
 
 // Define to output information on decoding and painting framerate
 /* #define DEBUG_FRAME_RATE 1 */
 
 typedef uint16_t nsMediaNetworkState;
 typedef uint16_t nsMediaReadyState;
+typedef uint32_t SuspendTypes;
 
 namespace mozilla {
 class DecoderDoctorDiagnostics;
 class DOMMediaStream;
 class ErrorResult;
 class MediaResource;
 class MediaDecoder;
 class VideoFrameContainer;
@@ -1109,19 +1110,16 @@ protected:
 #ifdef MOZ_EME
   void ReportEMETelemetry();
 #endif
   void ReportMSETelemetry();
 
   // Check the permissions for audiochannel.
   bool CheckAudioChannelPermissions(const nsAString& aType);
 
-  // This method does the check for muting/nmuting the audio channel.
-  nsresult UpdateChannelMuteState(float aVolume, bool aMuted);
-
   // Seeks to aTime seconds. aSeekType can be Exact to seek to exactly the
   // seek target, or PrevSyncPoint if a quicker but less precise seek is
   // desired, and we'll seek to the sync point (keyframe and/or start of the
   // next block of audio samples) preceeding seek target.
   void Seek(double aTime, SeekTarget::Type aSeekType, ErrorResult& aRv);
 
   // A method to check if we are playing through the AudioChannel.
   bool IsPlayingThroughTheAudioChannel() const;
@@ -1146,16 +1144,48 @@ protected:
 
   // Notifies the audio channel agent when the element starts or stops playing.
   void NotifyAudioChannelAgent(bool aPlaying);
 
   // Creates the audio channel agent if needed.  Returns true if the audio
   // channel agent is ready to be used.
   bool MaybeCreateAudioChannelAgent();
 
+  /**
+   * We have different kinds of suspended cases,
+   * - SUSPENDED_PAUSE
+   * It's used when we temporary lost platform audio focus. MediaElement can
+   * only be resumed when we gain the audio focus again.
+   *
+   * - SUSPENDED_PAUSE_DISPOSABLE
+   * It's used when user press the pause botton on the remote media-control.
+   * MediaElement can be resumed by reomte media-control or via play().
+   *
+   * - SUSPENDED_BLOCK
+   * It's used to reduce the power comsuption, we won't play the auto-play
+   * audio/video in the page we have never visited before. MediaElement would
+   * be resumed when the page is active. See bug647429 for more details.
+   *
+   * - SUSPENDED_STOP_DISPOSABLE
+   * When we permanently lost platform audio focus, we shuold stop playing
+   * and stop the audio channel agent. MediaElement can only be restarted by
+   * play().
+   */
+  void PauseByAudioChannel(SuspendTypes aSuspend);
+  void BlockByAudioChannel();
+
+  void ResumeFromAudioChannel();
+  void ResumeFromAudioChannelPaused(SuspendTypes aSuspend);
+  void ResumeFromAudioChannelBlocked();
+
+  bool IsSuspendedByAudioChannel() const;
+  void SetAudioChannelSuspended(SuspendTypes aSuspend);
+
+  bool IsAllowedToPlay();
+
   class nsAsyncEventRunner;
   using nsGenericHTMLElement::DispatchEvent;
   // For nsAsyncEventRunner.
   nsresult DispatchEvent(const nsAString& aName);
 
   // The current decoder. Load() has been called on this decoder.
   // At most one of mDecoder and mSrcStream can be non-null.
   RefPtr<MediaDecoder> mDecoder;
@@ -1365,16 +1395,17 @@ protected:
   enum MutedReasons {
     MUTED_BY_CONTENT               = 0x01,
     MUTED_BY_INVALID_PLAYBACK_RATE = 0x02,
     MUTED_BY_AUDIO_CHANNEL         = 0x04,
     MUTED_BY_AUDIO_TRACK           = 0x08
   };
 
   uint32_t mMuted;
+  SuspendTypes mAudioChannelSuspended;
 
   // True if the media statistics are currently being shown by the builtin
   // video controls
   bool mStatsShowing;
 
   // The following two fields are here for the private storage of the builtin
   // video controls, and control 'casting' of the video to external devices
   // (TVs, projectors etc.)
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -300,21 +300,16 @@ public:
   }
 
 private:
   float mVolume;
   bool mLastInputMuted;
   bool mSuspended;
 };
 
-static bool UseAudioChannelAPI()
-{
-  return Preferences::GetBool("media.useAudioChannelAPI");
-}
-
 NS_IMPL_CYCLE_COLLECTION_INHERITED(AudioDestinationNode, AudioNode,
                                    mAudioChannelAgent,
                                    mOfflineRenderingPromise)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioDestinationNode)
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
@@ -326,17 +321,17 @@ AudioDestinationNode::AudioDestinationNo
                                            AudioChannel aChannel,
                                            uint32_t aNumberOfChannels,
                                            uint32_t aLength, float aSampleRate)
   : AudioNode(aContext, aIsOffline ? aNumberOfChannels : 2,
               ChannelCountMode::Explicit, ChannelInterpretation::Speakers)
   , mFramesToProduce(aLength)
   , mAudioChannel(AudioChannel::Normal)
   , mIsOffline(aIsOffline)
-  , mAudioChannelAgentPlaying(false)
+  , mAudioChannelSuspended(false)
   , mCaptured(false)
 {
   MediaStreamGraph* graph = aIsOffline ?
                             MediaStreamGraph::CreateNonRealtimeInstance(aSampleRate) :
                             MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER, aChannel);
   AudioNodeEngine* engine = aIsOffline ?
                             new OfflineDestinationNodeEngine(this, aNumberOfChannels,
                                                              aLength, aSampleRate) :
@@ -496,48 +491,46 @@ AudioDestinationNode::WrapObject(JSConte
 void
 AudioDestinationNode::StartRendering(Promise* aPromise)
 {
   mOfflineRenderingPromise = aPromise;
   mOfflineRenderingRef.Take(this);
   mStream->Graph()->StartNonRealtimeProcessing(mFramesToProduce);
 }
 
-void
-AudioDestinationNode::SetCanPlay(float aVolume, bool aMuted)
-{
-  if (!mStream) {
-    return;
-  }
-
-  mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, !aMuted);
-  mStream->SetAudioOutputVolume(&gWebAudioOutputKey, aVolume);
-}
-
 NS_IMETHODIMP
 AudioDestinationNode::WindowVolumeChanged(float aVolume, bool aMuted)
 {
-  if (aMuted != mAudioChannelAgentPlaying) {
-    mAudioChannelAgentPlaying = aMuted;
-
-    if (UseAudioChannelAPI()) {
-      Context()->DispatchTrustedEvent(
-        !aMuted ? NS_LITERAL_STRING("mozinterruptend")
-                : NS_LITERAL_STRING("mozinterruptbegin"));
-    }
+  if (!mStream) {
+    return NS_OK;
   }
 
-  SetCanPlay(aVolume, aMuted);
+  float volume = aMuted ? 0.0 : aVolume;
+  mStream->SetAudioOutputVolume(&gWebAudioOutputKey, volume);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AudioDestinationNode::WindowSuspendChanged(nsSuspendedTypes aSuspend)
 {
-  // TODO : implementation.
+  if (!mStream) {
+    return NS_OK;
+  }
+
+  bool suspended = (aSuspend != nsISuspendedTypes::NONE_SUSPENDED);
+  if (mAudioChannelSuspended == suspended) {
+    return NS_OK;
+  }
+
+  mAudioChannelSuspended = suspended;
+  Context()->DispatchTrustedEvent(!suspended ?
+    NS_LITERAL_STRING("mozinterruptend") :
+    NS_LITERAL_STRING("mozinterruptbegin"));
+
+  mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, !suspended);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AudioDestinationNode::WindowAudioCaptureChanged(bool aCapture)
 {
   MOZ_ASSERT(mAudioChannelAgent);
 
--- a/dom/media/webaudio/AudioDestinationNode.h
+++ b/dom/media/webaudio/AudioDestinationNode.h
@@ -87,30 +87,28 @@ public:
 
 protected:
   virtual ~AudioDestinationNode();
 
 private:
   void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
   bool CheckAudioChannelPermissions(AudioChannel aValue);
 
-  void SetCanPlay(float aVolume, bool aMuted);
-
   SelfReference<AudioDestinationNode> mOfflineRenderingRef;
   uint32_t mFramesToProduce;
 
   nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
   RefPtr<MediaInputPort> mCaptureStreamPort;
 
   RefPtr<Promise> mOfflineRenderingPromise;
 
   // Audio Channel Type.
   AudioChannel mAudioChannel;
   bool mIsOffline;
-  bool mAudioChannelAgentPlaying;
+  bool mAudioChannelSuspended;
 
   bool mCaptured;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp
+++ b/dom/media/webspeech/synth/nsSpeechTask.cpp
@@ -731,17 +731,21 @@ nsSpeechTask::WindowVolumeChanged(float 
 {
   SetAudioOutputVolume(aMuted ? 0.0 : mVolume * aVolume);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSpeechTask::WindowSuspendChanged(nsSuspendedTypes aSuspend)
 {
-  // TODO : implementation.
+  if (aSuspend == nsISuspendedTypes::NONE_SUSPENDED) {
+    Resume();
+  } else {
+    Pause();
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSpeechTask::WindowAudioCaptureChanged(bool aCapture)
 {
   // This is not supported yet.
   return NS_OK;
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
+++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
@@ -658,22 +658,21 @@ nsSynthVoiceRegistry::SpeakUtterance(Spe
     aUtterance.mVoice->GetVoiceURI(uri);
   }
 
   // Get current audio volume to apply speech call
   float volume = aUtterance.Volume();
   RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (service) {
     if (nsCOMPtr<nsPIDOMWindowInner> topWindow = aUtterance.GetOwner()) {
-      float audioVolume = 1.0f;
-      bool muted = false;
-      service->GetState(topWindow->GetOuterWindow(),
-                        static_cast<uint32_t>(AudioChannelService::GetDefaultAudioChannel()),
-                        &audioVolume, &muted);
-      volume = muted ? 0.0f : audioVolume * volume; 
+      // TODO : use audio channel agent, open new bug to fix it.
+      uint32_t channel = static_cast<uint32_t>(AudioChannelService::GetDefaultAudioChannel());
+      AudioPlaybackConfig config = service->GetMediaConfig(topWindow->GetOuterWindow(),
+                                                           channel);
+      volume = config.mMuted ? 0.0f : config.mVolume * volume;
     }
   }
 
   RefPtr<nsSpeechTask> task;
   if (XRE_IsContentProcess()) {
     task = new SpeechTaskChild(&aUtterance);
     SpeechSynthesisRequestChild* actor =
       new SpeechSynthesisRequestChild(static_cast<SpeechTaskChild*>(task.get()));
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -1846,17 +1846,19 @@ nsNPAPIPluginInstance::WindowVolumeChang
   nsresult rv = SetMuted(aMuted);
   NS_WARN_IF(NS_FAILED(rv));
   return rv;
 }
 
 NS_IMETHODIMP
 nsNPAPIPluginInstance::WindowSuspendChanged(nsSuspendedTypes aSuspend)
 {
-  // TODO : implementation.
+  // It doesn't support suspended, so we just do something like mute/unmute.
+  WindowVolumeChanged(1.0, /* useless */
+                      aSuspend != nsISuspendedTypes::NONE_SUSPENDED);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNPAPIPluginInstance::WindowAudioCaptureChanged(bool aCapture)
 {
   return NS_OK;
 }