LCOV - code coverage report
Current view: top level - dom/audiochannel - AudioChannelService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 75 467 16.1 %
Date: 2017-07-14 16:53:18 Functions: 16 76 21.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       5             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "AudioChannelService.h"
       8             : 
       9             : #include "base/basictypes.h"
      10             : 
      11             : #include "mozilla/Services.h"
      12             : #include "mozilla/StaticPtr.h"
      13             : #include "mozilla/Unused.h"
      14             : 
      15             : #include "nsContentUtils.h"
      16             : #include "nsIScriptSecurityManager.h"
      17             : #include "nsISupportsPrimitives.h"
      18             : #include "nsThreadUtils.h"
      19             : #include "nsHashPropertyBag.h"
      20             : #include "nsComponentManagerUtils.h"
      21             : #include "nsGlobalWindow.h"
      22             : #include "nsPIDOMWindow.h"
      23             : #include "nsServiceManagerUtils.h"
      24             : 
      25             : #ifdef MOZ_WIDGET_GONK
      26             : #include "nsJSUtils.h"
      27             : #endif
      28             : 
      29             : #include "mozilla/Preferences.h"
      30             : 
      31             : using namespace mozilla;
      32             : using namespace mozilla::dom;
      33             : 
      34             : static mozilla::LazyLogModule gAudioChannelLog("AudioChannel");
      35             : 
      36             : namespace {
      37             : 
      38             : bool sAudioChannelCompeting = false;
      39             : bool sAudioChannelCompetingAllAgents = false;
      40             : bool sXPCOMShuttingDown = false;
      41             : 
      42           0 : class NotifyChannelActiveRunnable final : public Runnable
      43             : {
      44             : public:
      45           0 :   NotifyChannelActiveRunnable(uint64_t aWindowID, bool aActive)
      46           0 :     : Runnable("NotifyChannelActiveRunnable")
      47             :     , mWindowID(aWindowID)
      48           0 :     , mActive(aActive)
      49           0 :   {}
      50             : 
      51           0 :   NS_IMETHOD Run() override
      52             :   {
      53             :     nsCOMPtr<nsIObserverService> observerService =
      54           0 :       services::GetObserverService();
      55           0 :     if (NS_WARN_IF(!observerService)) {
      56           0 :       return NS_ERROR_FAILURE;
      57             :     }
      58             : 
      59             :     nsCOMPtr<nsISupportsPRUint64> wrapper =
      60           0 :       do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID);
      61           0 :     if (NS_WARN_IF(!wrapper)) {
      62           0 :        return NS_ERROR_FAILURE;
      63             :     }
      64             : 
      65           0 :     wrapper->SetData(mWindowID);
      66             : 
      67           0 :     observerService->NotifyObservers(wrapper,
      68             :                                      "media-playback",
      69           0 :                                      mActive
      70             :                                        ? u"active"
      71           0 :                                        : u"inactive");
      72             : 
      73           0 :     MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
      74             :            ("NotifyChannelActiveRunnable, active = %s\n",
      75             :             mActive ? "true" : "false"));
      76             : 
      77           0 :     return NS_OK;
      78             :   }
      79             : 
      80             : private:
      81             :   const uint64_t mWindowID;
      82             :   const bool mActive;
      83             : };
      84             : 
      85           0 : class AudioPlaybackRunnable final : public Runnable
      86             : {
      87             : public:
      88           0 :   AudioPlaybackRunnable(nsPIDOMWindowOuter* aWindow,
      89             :                         bool aActive,
      90             :                         AudioChannelService::AudibleChangedReasons aReason)
      91           0 :     : mozilla::Runnable("AudioPlaybackRunnable")
      92             :     , mWindow(aWindow)
      93             :     , mActive(aActive)
      94           0 :     , mReason(aReason)
      95           0 :   {}
      96             : 
      97           0 :  NS_IMETHOD Run() override
      98             :  {
      99             :     nsCOMPtr<nsIObserverService> observerService =
     100           0 :       services::GetObserverService();
     101           0 :     if (NS_WARN_IF(!observerService)) {
     102           0 :       return NS_ERROR_FAILURE;
     103             :     }
     104             : 
     105           0 :     nsAutoString state;
     106           0 :     GetActiveState(state);
     107             : 
     108           0 :     observerService->NotifyObservers(ToSupports(mWindow),
     109             :                                      "audio-playback",
     110           0 :                                      state.get());
     111             : 
     112           0 :     MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
     113             :            ("AudioPlaybackRunnable, active = %s, reason = %s\n",
     114             :             mActive ? "true" : "false", AudibleChangedReasonToStr(mReason)));
     115             : 
     116           0 :     return NS_OK;
     117             :   }
     118             : 
     119             : private:
     120           0 :   void GetActiveState(nsAString& astate)
     121             :   {
     122           0 :     if (mActive) {
     123           0 :       CopyASCIItoUTF16("active", astate);
     124             :     } else {
     125           0 :       if(mReason == AudioChannelService::AudibleChangedReasons::ePauseStateChanged) {
     126           0 :         CopyASCIItoUTF16("inactive-pause", astate);
     127             :       } else {
     128           0 :         CopyASCIItoUTF16("inactive-nonaudible", astate);
     129             :       }
     130             :     }
     131           0 :   }
     132             : 
     133             :   nsCOMPtr<nsPIDOMWindowOuter> mWindow;
     134             :   bool mActive;
     135             :   AudioChannelService::AudibleChangedReasons mReason;
     136             : };
     137             : 
     138             : bool
     139           0 : IsEnableAudioCompetingForAllAgents()
     140             : {
     141             :   // In general, the audio competing should only be for audible media and it
     142             :   // helps user can focus on one media at the same time. However, we hope to
     143             :   // treat all media as the same in the mobile device. First reason is we have
     144             :   // media control on fennec and we just want to control one media at once time.
     145             :   // Second reason is to reduce the bandwidth, avoiding to play any non-audible
     146             :   // media in background which user doesn't notice about.
     147             : #ifdef MOZ_WIDGET_ANDROID
     148             :   return true;
     149             : #else
     150           0 :   return sAudioChannelCompetingAllAgents;
     151             : #endif
     152             : }
     153             : 
     154             : } // anonymous namespace
     155             : 
     156             : namespace mozilla {
     157             : namespace dom {
     158             : 
     159             : const char*
     160           0 : SuspendTypeToStr(const nsSuspendedTypes& aSuspend)
     161             : {
     162           0 :   MOZ_ASSERT(aSuspend == nsISuspendedTypes::NONE_SUSPENDED ||
     163             :              aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE ||
     164             :              aSuspend == nsISuspendedTypes::SUSPENDED_BLOCK ||
     165             :              aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
     166             :              aSuspend == nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE);
     167             : 
     168           0 :   switch (aSuspend) {
     169             :     case nsISuspendedTypes::NONE_SUSPENDED:
     170           0 :       return "none";
     171             :     case nsISuspendedTypes::SUSPENDED_PAUSE:
     172           0 :       return "pause";
     173             :     case nsISuspendedTypes::SUSPENDED_BLOCK:
     174           0 :       return "block";
     175             :     case nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE:
     176           0 :       return "disposable-pause";
     177             :     case nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE:
     178           0 :       return "disposable-stop";
     179             :     default:
     180           0 :       return "unknown";
     181             :   }
     182             : }
     183             : 
     184             : const char*
     185           0 : AudibleStateToStr(const AudioChannelService::AudibleState& aAudible)
     186             : {
     187           0 :   MOZ_ASSERT(aAudible == AudioChannelService::AudibleState::eNotAudible ||
     188             :              aAudible == AudioChannelService::AudibleState::eMaybeAudible ||
     189             :              aAudible == AudioChannelService::AudibleState::eAudible);
     190             : 
     191           0 :   switch (aAudible) {
     192             :     case AudioChannelService::AudibleState::eNotAudible :
     193           0 :       return "not-audible";
     194             :     case AudioChannelService::AudibleState::eMaybeAudible :
     195           0 :       return "maybe-audible";
     196             :     case AudioChannelService::AudibleState::eAudible :
     197           0 :       return "audible";
     198             :     default:
     199           0 :       return "unknown";
     200             :   }
     201             : }
     202             : 
     203             : const char*
     204           0 : AudibleChangedReasonToStr(const AudioChannelService::AudibleChangedReasons& aReason)
     205             : {
     206           0 :   MOZ_ASSERT(aReason == AudioChannelService::AudibleChangedReasons::eVolumeChanged ||
     207             :              aReason == AudioChannelService::AudibleChangedReasons::eDataAudibleChanged ||
     208             :              aReason == AudioChannelService::AudibleChangedReasons::ePauseStateChanged);
     209             : 
     210           0 :   switch (aReason) {
     211             :     case AudioChannelService::AudibleChangedReasons::eVolumeChanged :
     212           0 :       return "volume";
     213             :     case AudioChannelService::AudibleChangedReasons::eDataAudibleChanged :
     214           0 :       return "data-audible";
     215             :     case AudioChannelService::AudibleChangedReasons::ePauseStateChanged :
     216           0 :       return "pause-state";
     217             :     default:
     218           0 :       return "unknown";
     219             :   }
     220             : }
     221             : 
     222           3 : StaticRefPtr<AudioChannelService> gAudioChannelService;
     223             : 
     224             : // Mappings from 'mozaudiochannel' attribute strings to an enumeration.
     225             : static const nsAttrValue::EnumTable kMozAudioChannelAttributeTable[] = {
     226             :   { "normal",             (int16_t)AudioChannel::Normal },
     227             :   { "content",            (int16_t)AudioChannel::Content },
     228             :   { "notification",       (int16_t)AudioChannel::Notification },
     229             :   { "alarm",              (int16_t)AudioChannel::Alarm },
     230             :   { "telephony",          (int16_t)AudioChannel::Telephony },
     231             :   { "ringer",             (int16_t)AudioChannel::Ringer },
     232             :   { "publicnotification", (int16_t)AudioChannel::Publicnotification },
     233             :   { "system",             (int16_t)AudioChannel::System },
     234             :   { nullptr,              0 }
     235             : };
     236             : 
     237             : /* static */ void
     238           9 : AudioChannelService::CreateServiceIfNeeded()
     239             : {
     240           9 :   MOZ_ASSERT(NS_IsMainThread());
     241             : 
     242           9 :   if (!gAudioChannelService) {
     243           2 :     gAudioChannelService = new AudioChannelService();
     244             :   }
     245           9 : }
     246             : 
     247             : /* static */ already_AddRefed<AudioChannelService>
     248           9 : AudioChannelService::GetOrCreate()
     249             : {
     250           9 :   if (sXPCOMShuttingDown) {
     251           0 :     return nullptr;
     252             :   }
     253             : 
     254           9 :   CreateServiceIfNeeded();
     255          18 :   RefPtr<AudioChannelService> service = gAudioChannelService.get();
     256           9 :   return service.forget();
     257             : }
     258             : 
     259             : /* static */ already_AddRefed<AudioChannelService>
     260           4 : AudioChannelService::Get()
     261             : {
     262           4 :   if (sXPCOMShuttingDown) {
     263           0 :     return nullptr;
     264             :   }
     265             : 
     266           8 :   RefPtr<AudioChannelService> service = gAudioChannelService.get();
     267           4 :   return service.forget();
     268             : }
     269             : 
     270             : /* static */ LogModule*
     271           5 : AudioChannelService::GetAudioChannelLog()
     272             : {
     273           5 :   return gAudioChannelLog;
     274             : }
     275             : 
     276             : /* static */ void
     277           0 : AudioChannelService::Shutdown()
     278             : {
     279           0 :   if (gAudioChannelService) {
     280           0 :     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     281           0 :     if (obs) {
     282           0 :       obs->RemoveObserver(gAudioChannelService, "xpcom-shutdown");
     283           0 :       obs->RemoveObserver(gAudioChannelService, "outer-window-destroyed");
     284             :     }
     285             : 
     286           0 :     gAudioChannelService->mWindows.Clear();
     287             : 
     288           0 :     gAudioChannelService = nullptr;
     289             :   }
     290           0 : }
     291             : 
     292             : /* static */ bool
     293           0 : AudioChannelService::IsEnableAudioCompeting()
     294             : {
     295           0 :   CreateServiceIfNeeded();
     296           0 :   return sAudioChannelCompeting;
     297             : }
     298             : 
     299           0 : NS_INTERFACE_MAP_BEGIN(AudioChannelService)
     300           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
     301           0 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
     302           0 : NS_INTERFACE_MAP_END
     303             : 
     304          21 : NS_IMPL_ADDREF(AudioChannelService)
     305          15 : NS_IMPL_RELEASE(AudioChannelService)
     306             : 
     307           2 : AudioChannelService::AudioChannelService()
     308             : {
     309           4 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     310           2 :   if (obs) {
     311           2 :     obs->AddObserver(this, "xpcom-shutdown", false);
     312           2 :     obs->AddObserver(this, "outer-window-destroyed", false);
     313             :   }
     314             : 
     315             :   Preferences::AddBoolVarCache(&sAudioChannelCompeting,
     316           2 :                                "dom.audiochannel.audioCompeting");
     317             :   Preferences::AddBoolVarCache(&sAudioChannelCompetingAllAgents,
     318           2 :                                "dom.audiochannel.audioCompeting.allAgents");
     319           2 : }
     320             : 
     321           0 : AudioChannelService::~AudioChannelService()
     322             : {
     323           0 : }
     324             : 
     325             : void
     326           0 : AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
     327             :                                                AudibleState aAudible)
     328             : {
     329           0 :   MOZ_ASSERT(aAgent);
     330             : 
     331           0 :   uint64_t windowID = aAgent->WindowID();
     332           0 :   AudioChannelWindow* winData = GetWindowData(windowID);
     333           0 :   if (!winData) {
     334           0 :     winData = new AudioChannelWindow(windowID);
     335           0 :     mWindows.AppendElement(winData);
     336             :   }
     337             : 
     338             :   // To make sure agent would be alive because AppendAgent() would trigger the
     339             :   // callback function of AudioChannelAgentOwner that means the agent might be
     340             :   // released in their callback.
     341           0 :   RefPtr<AudioChannelAgent> kungFuDeathGrip(aAgent);
     342           0 :   winData->AppendAgent(aAgent, aAudible);
     343           0 : }
     344             : 
     345             : void
     346           0 : AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
     347             : {
     348           0 :   MOZ_ASSERT(aAgent);
     349             : 
     350           0 :   AudioChannelWindow* winData = GetWindowData(aAgent->WindowID());
     351           0 :   if (!winData) {
     352           0 :     return;
     353             :   }
     354             : 
     355             :   // To make sure agent would be alive because AppendAgent() would trigger the
     356             :   // callback function of AudioChannelAgentOwner that means the agent might be
     357             :   // released in their callback.
     358           0 :   RefPtr<AudioChannelAgent> kungFuDeathGrip(aAgent);
     359           0 :   winData->RemoveAgent(aAgent);
     360             : }
     361             : 
     362             : AudioPlaybackConfig
     363           0 : AudioChannelService::GetMediaConfig(nsPIDOMWindowOuter* aWindow,
     364             :                                     uint32_t aAudioChannel) const
     365             : {
     366           0 :   MOZ_ASSERT(!aWindow || aWindow->IsOuterWindow());
     367           0 :   MOZ_ASSERT(aAudioChannel < NUMBER_OF_AUDIO_CHANNELS);
     368             : 
     369             :   AudioPlaybackConfig config(1.0, false,
     370           0 :                              nsISuspendedTypes::NONE_SUSPENDED);
     371             : 
     372           0 :   if (!aWindow || !aWindow->IsOuterWindow()) {
     373             :     config.SetConfig(0.0, true,
     374           0 :                      nsISuspendedTypes::SUSPENDED_BLOCK);
     375           0 :     return config;
     376             :   }
     377             : 
     378           0 :   AudioChannelWindow* winData = nullptr;
     379           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = aWindow;
     380             : 
     381             :   // The volume must be calculated based on the window hierarchy. Here we go up
     382             :   // to the top window and we calculate the volume and the muted flag.
     383           0 :   do {
     384           0 :     winData = GetWindowData(window->WindowID());
     385           0 :     if (winData) {
     386           0 :       config.mVolume *= winData->mChannels[aAudioChannel].mVolume;
     387           0 :       config.mMuted = config.mMuted || winData->mChannels[aAudioChannel].mMuted;
     388           0 :       config.mSuspend = winData->mOwningAudioFocus ?
     389             :         config.mSuspend : nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE;
     390             :     }
     391             : 
     392           0 :     config.mVolume *= window->GetAudioVolume();
     393           0 :     config.mMuted = config.mMuted || window->GetAudioMuted();
     394           0 :     if (window->GetMediaSuspend() != nsISuspendedTypes::NONE_SUSPENDED) {
     395           0 :       config.mSuspend = window->GetMediaSuspend();
     396             :     }
     397             : 
     398           0 :     nsCOMPtr<nsPIDOMWindowOuter> win = window->GetScriptableParentOrNull();
     399           0 :     if (!win) {
     400           0 :       break;
     401             :     }
     402             : 
     403           0 :     window = do_QueryInterface(win);
     404             : 
     405             :     // If there is no parent, or we are the toplevel we don't continue.
     406           0 :   } while (window && window != aWindow);
     407             : 
     408           0 :   return config;
     409             : }
     410             : 
     411             : void
     412           0 : AudioChannelService::AudioAudibleChanged(AudioChannelAgent* aAgent,
     413             :                                          AudibleState aAudible,
     414             :                                          AudibleChangedReasons aReason)
     415             : {
     416           0 :   MOZ_ASSERT(aAgent);
     417             : 
     418           0 :   uint64_t windowID = aAgent->WindowID();
     419           0 :   AudioChannelWindow* winData = GetWindowData(windowID);
     420           0 :   if (winData) {
     421           0 :     winData->AudioAudibleChanged(aAgent, aAudible, aReason);
     422             :   }
     423           0 : }
     424             : 
     425             : NS_IMETHODIMP
     426           1 : AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic,
     427             :                              const char16_t* aData)
     428             : {
     429           1 :   if (!strcmp(aTopic, "xpcom-shutdown")) {
     430           0 :     sXPCOMShuttingDown = true;
     431           0 :     Shutdown();
     432           1 :   } else if (!strcmp(aTopic, "outer-window-destroyed")) {
     433           2 :     nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
     434           1 :     NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
     435             : 
     436             :     uint64_t outerID;
     437           1 :     nsresult rv = wrapper->GetData(&outerID);
     438           1 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     439           0 :       return rv;
     440             :     }
     441             : 
     442           2 :     nsAutoPtr<AudioChannelWindow> winData;
     443             :     {
     444             :       nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator
     445           2 :         iter(mWindows);
     446           1 :       while (iter.HasMore()) {
     447           0 :         nsAutoPtr<AudioChannelWindow>& next = iter.GetNext();
     448           0 :         if (next->mWindowID == outerID) {
     449           0 :           uint32_t pos = mWindows.IndexOf(next);
     450           0 :           winData = next.forget();
     451           0 :           mWindows.RemoveElementAt(pos);
     452           0 :           break;
     453             :         }
     454             :       }
     455             :     }
     456             : 
     457           1 :     if (winData) {
     458             :       nsTObserverArray<AudioChannelAgent*>::ForwardIterator
     459           0 :         iter(winData->mAgents);
     460           0 :       while (iter.HasMore()) {
     461           0 :         iter.GetNext()->WindowVolumeChanged();
     462             :       }
     463             :     }
     464             :   }
     465             : 
     466           1 :   return NS_OK;
     467             : }
     468             : 
     469             : void
     470           4 : AudioChannelService::RefreshAgents(nsPIDOMWindowOuter* aWindow,
     471             :                                    const std::function<void(AudioChannelAgent*)>& aFunc)
     472             : {
     473           4 :   MOZ_ASSERT(aWindow);
     474           4 :   MOZ_ASSERT(aWindow->IsOuterWindow());
     475             : 
     476           4 :   nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetScriptableTop();
     477           4 :   if (!topWindow) {
     478           0 :     return;
     479             :   }
     480             : 
     481           4 :   AudioChannelWindow* winData = GetWindowData(topWindow->WindowID());
     482           4 :   if (!winData) {
     483           4 :     return;
     484             :   }
     485             : 
     486             :   nsTObserverArray<AudioChannelAgent*>::ForwardIterator
     487           0 :     iter(winData->mAgents);
     488           0 :   while (iter.HasMore()) {
     489           0 :     aFunc(iter.GetNext());
     490             :   }
     491             : }
     492             : 
     493             : void
     494           0 : AudioChannelService::RefreshAgentsVolume(nsPIDOMWindowOuter* aWindow)
     495             : {
     496           0 :   RefreshAgents(aWindow, [] (AudioChannelAgent* agent) {
     497           0 :     agent->WindowVolumeChanged();
     498           0 :   });
     499           0 : }
     500             : 
     501             : void
     502           4 : AudioChannelService::RefreshAgentsSuspend(nsPIDOMWindowOuter* aWindow,
     503             :                                           nsSuspendedTypes aSuspend)
     504             : {
     505           8 :   RefreshAgents(aWindow, [aSuspend] (AudioChannelAgent* agent) {
     506           0 :     agent->WindowSuspendChanged(aSuspend);
     507           4 :   });
     508           4 : }
     509             : 
     510             : void
     511           0 : AudioChannelService::SetWindowAudioCaptured(nsPIDOMWindowOuter* aWindow,
     512             :                                             uint64_t aInnerWindowID,
     513             :                                             bool aCapture)
     514             : {
     515           0 :   MOZ_ASSERT(NS_IsMainThread());
     516           0 :   MOZ_ASSERT(aWindow);
     517           0 :   MOZ_ASSERT(aWindow->IsOuterWindow());
     518             : 
     519           0 :   MOZ_LOG(GetAudioChannelLog(), LogLevel::Debug,
     520             :          ("AudioChannelService, SetWindowAudioCaptured, window = %p, "
     521             :           "aCapture = %d\n", aWindow, aCapture));
     522             : 
     523           0 :   nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetScriptableTop();
     524           0 :   if (!topWindow) {
     525           0 :     return;
     526             :   }
     527             : 
     528           0 :   AudioChannelWindow* winData = GetWindowData(topWindow->WindowID());
     529             : 
     530             :   // This can happen, but only during shutdown, because the the outer window
     531             :   // changes ScriptableTop, so that its ID is different.
     532             :   // In this case either we are capturing, and it's too late because the window
     533             :   // has been closed anyways, or we are un-capturing, and everything has already
     534             :   // been cleaned up by the HTMLMediaElements or the AudioContexts.
     535           0 :   if (!winData) {
     536           0 :     return;
     537             :   }
     538             : 
     539           0 :   if (aCapture != winData->mIsAudioCaptured) {
     540           0 :     winData->mIsAudioCaptured = aCapture;
     541             :     nsTObserverArray<AudioChannelAgent*>::ForwardIterator
     542           0 :       iter(winData->mAgents);
     543           0 :     while (iter.HasMore()) {
     544           0 :       iter.GetNext()->WindowAudioCaptureChanged(aInnerWindowID, aCapture);
     545             :     }
     546             :   }
     547             : }
     548             : 
     549             : /* static */ const nsAttrValue::EnumTable*
     550           0 : AudioChannelService::GetAudioChannelTable()
     551             : {
     552           0 :   return kMozAudioChannelAttributeTable;
     553             : }
     554             : 
     555             : /* static */ AudioChannel
     556           0 : AudioChannelService::GetAudioChannel(const nsAString& aChannel)
     557             : {
     558           0 :   for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) {
     559           0 :     if (aChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].tag)) {
     560           0 :       return static_cast<AudioChannel>(kMozAudioChannelAttributeTable[i].value);
     561             :     }
     562             :   }
     563             : 
     564           0 :   return AudioChannel::Normal;
     565             : }
     566             : 
     567             : /* static */ AudioChannel
     568           1 : AudioChannelService::GetDefaultAudioChannel()
     569             : {
     570           2 :   nsAutoString audioChannel(Preferences::GetString("media.defaultAudioChannel"));
     571           1 :   if (audioChannel.IsEmpty()) {
     572           1 :     return AudioChannel::Normal;
     573             :   }
     574             : 
     575           0 :   for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) {
     576           0 :     if (audioChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].tag)) {
     577           0 :       return static_cast<AudioChannel>(kMozAudioChannelAttributeTable[i].value);
     578             :     }
     579             :   }
     580             : 
     581           0 :   return AudioChannel::Normal;
     582             : }
     583             : 
     584             : AudioChannelService::AudioChannelWindow*
     585           0 : AudioChannelService::GetOrCreateWindowData(nsPIDOMWindowOuter* aWindow)
     586             : {
     587           0 :   MOZ_ASSERT(NS_IsMainThread());
     588           0 :   MOZ_ASSERT(aWindow);
     589           0 :   MOZ_ASSERT(aWindow->IsOuterWindow());
     590             : 
     591           0 :   AudioChannelWindow* winData = GetWindowData(aWindow->WindowID());
     592           0 :   if (!winData) {
     593           0 :     winData = new AudioChannelWindow(aWindow->WindowID());
     594           0 :     mWindows.AppendElement(winData);
     595             :   }
     596             : 
     597           0 :   return winData;
     598             : }
     599             : 
     600             : AudioChannelService::AudioChannelWindow*
     601          12 : AudioChannelService::GetWindowData(uint64_t aWindowID) const
     602             : {
     603             :   nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator
     604          24 :     iter(mWindows);
     605          12 :   while (iter.HasMore()) {
     606           0 :     AudioChannelWindow* next = iter.GetNext();
     607           0 :     if (next->mWindowID == aWindowID) {
     608           0 :       return next;
     609             :     }
     610             :   }
     611             : 
     612          12 :   return nullptr;
     613             : }
     614             : 
     615             : bool
     616           4 : AudioChannelService::IsWindowActive(nsPIDOMWindowOuter* aWindow)
     617             : {
     618           4 :   MOZ_ASSERT(NS_IsMainThread());
     619             : 
     620           4 :   auto* window = nsPIDOMWindowOuter::From(aWindow)->GetScriptableTop();
     621           4 :   if (!window) {
     622           0 :     return false;
     623             :   }
     624             : 
     625           4 :   AudioChannelWindow* winData = GetWindowData(window->WindowID());
     626           4 :   if (!winData) {
     627           4 :     return false;
     628             :   }
     629             : 
     630           0 :   return !winData->mAudibleAgents.IsEmpty();
     631             : }
     632             : 
     633             : void
     634           0 : AudioChannelService::RefreshAgentsAudioFocusChanged(AudioChannelAgent* aAgent)
     635             : {
     636           0 :   MOZ_ASSERT(aAgent);
     637             : 
     638             :   nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator
     639           0 :     iter(mWindows);
     640           0 :   while (iter.HasMore()) {
     641           0 :     AudioChannelWindow* winData = iter.GetNext();
     642           0 :     if (winData->mOwningAudioFocus) {
     643           0 :       winData->AudioFocusChanged(aAgent);
     644             :     }
     645             :   }
     646           0 : }
     647             : 
     648             : void
     649           4 : AudioChannelService::NotifyMediaResumedFromBlock(nsPIDOMWindowOuter* aWindow)
     650             : {
     651           4 :   MOZ_ASSERT(aWindow);
     652           4 :   MOZ_ASSERT(aWindow->IsOuterWindow());
     653             : 
     654           4 :   nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetScriptableTop();
     655           4 :   if (!topWindow) {
     656           0 :     return;
     657             :   }
     658             : 
     659           4 :   AudioChannelWindow* winData = GetWindowData(topWindow->WindowID());
     660           4 :   if (!winData) {
     661           4 :     return;
     662             :   }
     663             : 
     664           0 :   winData->NotifyMediaBlockStop(aWindow);
     665             : }
     666             : 
     667             : void
     668           0 : AudioChannelService::AudioChannelWindow::RequestAudioFocus(AudioChannelAgent* aAgent)
     669             : {
     670           0 :   MOZ_ASSERT(aAgent);
     671             : 
     672             :   // Don't need to check audio focus for window-less agent.
     673           0 :   if (!aAgent->Window()) {
     674           0 :     return;
     675             :   }
     676             : 
     677             :   // We already have the audio focus. No operation is needed.
     678           0 :   if (mOwningAudioFocus) {
     679           0 :     return;
     680             :   }
     681             : 
     682             :   // Only foreground window can request audio focus, but it would still own the
     683             :   // audio focus even it goes to background. Audio focus would be abandoned
     684             :   // only when other foreground window starts audio competing.
     685             :   // One exception is if the pref "media.block-autoplay-until-in-foreground"
     686             :   // is on and the background page is the non-visited before. Because the media
     687             :   // in that page would be blocked until the page is going to foreground.
     688           0 :   mOwningAudioFocus = (!(aAgent->Window()->IsBackground()) ||
     689           0 :                        aAgent->Window()->GetMediaSuspend() == nsISuspendedTypes::SUSPENDED_BLOCK) ;
     690             : 
     691           0 :   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
     692             :          ("AudioChannelWindow, RequestAudioFocus, this = %p, "
     693             :           "agent = %p, owning audio focus = %s\n",
     694             :           this, aAgent, mOwningAudioFocus ? "true" : "false"));
     695             : }
     696             : 
     697             : void
     698           0 : AudioChannelService::AudioChannelWindow::NotifyAudioCompetingChanged(AudioChannelAgent* aAgent)
     699             : {
     700             :   // This function may be called after RemoveAgentAndReduceAgentsNum(), so the
     701             :   // agent may be not contained in mAgent. In addition, the agent would still
     702             :   // be alive because we have kungFuDeathGrip in UnregisterAudioChannelAgent().
     703           0 :   MOZ_ASSERT(aAgent);
     704             : 
     705           0 :   RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
     706           0 :   MOZ_ASSERT(service);
     707             : 
     708           0 :   if (!service->IsEnableAudioCompeting()) {
     709           0 :     return;
     710             :   }
     711             : 
     712           0 :   if (!IsAgentInvolvingInAudioCompeting(aAgent)) {
     713           0 :     return;
     714             :   }
     715             : 
     716           0 :   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
     717             :          ("AudioChannelWindow, NotifyAudioCompetingChanged, this = %p, "
     718             :           "agent = %p\n",
     719             :           this, aAgent));
     720             : 
     721           0 :   service->RefreshAgentsAudioFocusChanged(aAgent);
     722             : }
     723             : 
     724             : bool
     725           0 : AudioChannelService::AudioChannelWindow::IsAgentInvolvingInAudioCompeting(AudioChannelAgent* aAgent) const
     726             : {
     727           0 :   MOZ_ASSERT(aAgent);
     728             : 
     729           0 :   if(!mOwningAudioFocus) {
     730           0 :     return false;
     731             :   }
     732             : 
     733           0 :   if (IsAudioCompetingInSameTab()) {
     734           0 :     return false;
     735             :   }
     736             : 
     737             :   // TODO : add MediaSession::ambient kind, because it doens't interact with
     738             :   // other kinds.
     739           0 :   return true;
     740             : }
     741             : 
     742             : bool
     743           0 : AudioChannelService::AudioChannelWindow::IsAudioCompetingInSameTab() const
     744             : {
     745           0 :   bool hasMultipleActiveAgents = IsEnableAudioCompetingForAllAgents() ?
     746           0 :     mAgents.Length() > 1 : mAudibleAgents.Length() > 1;
     747           0 :   return mOwningAudioFocus && hasMultipleActiveAgents;
     748             : }
     749             : 
     750             : void
     751           0 : AudioChannelService::AudioChannelWindow::AudioFocusChanged(AudioChannelAgent* aNewPlayingAgent)
     752             : {
     753             :   // This agent isn't always known for the current window, because it can comes
     754             :   // from other window.
     755           0 :   MOZ_ASSERT(aNewPlayingAgent);
     756             : 
     757           0 :   if (IsInactiveWindow()) {
     758             :     // These would happen in two situations,
     759             :     // (1) Audio in page A was ended, and another page B want to play audio.
     760             :     //     Page A should abandon its focus.
     761             :     // (2) Audio was paused by remote-control, page should still own the focus.
     762           0 :     mOwningAudioFocus = IsContainingPlayingAgent(aNewPlayingAgent);
     763             :   } else {
     764             :     nsTObserverArray<AudioChannelAgent*>::ForwardIterator
     765           0 :       iter(IsEnableAudioCompetingForAllAgents() ? mAgents : mAudibleAgents);
     766           0 :     while (iter.HasMore()) {
     767           0 :       AudioChannelAgent* agent = iter.GetNext();
     768           0 :       MOZ_ASSERT(agent);
     769             : 
     770             :       // Don't need to update the playing state of new playing agent.
     771           0 :       if (agent == aNewPlayingAgent) {
     772           0 :         continue;
     773             :       }
     774             : 
     775           0 :       uint32_t type = GetCompetingBehavior(agent,
     776           0 :                                            aNewPlayingAgent->AudioChannelType());
     777             : 
     778             :       // If window will be suspended, it needs to abandon the audio focus
     779             :       // because only one window can own audio focus at a time. However, we
     780             :       // would support multiple audio focus at the same time in the future.
     781           0 :       mOwningAudioFocus = (type == nsISuspendedTypes::NONE_SUSPENDED);
     782             : 
     783             :       // TODO : support other behaviors which are definded in MediaSession API.
     784           0 :       switch (type) {
     785             :         case nsISuspendedTypes::NONE_SUSPENDED:
     786             :         case nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE:
     787           0 :           agent->WindowSuspendChanged(type);
     788           0 :           break;
     789             :       }
     790             :     }
     791             :   }
     792             : 
     793           0 :   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
     794             :          ("AudioChannelWindow, AudioFocusChanged, this = %p, "
     795             :           "OwningAudioFocus = %s\n", this, mOwningAudioFocus ? "true" : "false"));
     796           0 : }
     797             : 
     798             : bool
     799           0 : AudioChannelService::AudioChannelWindow::IsContainingPlayingAgent(AudioChannelAgent* aAgent) const
     800             : {
     801           0 :   return (aAgent->WindowID() == mWindowID);
     802             : }
     803             : 
     804             : uint32_t
     805           0 : AudioChannelService::AudioChannelWindow::GetCompetingBehavior(AudioChannelAgent* aAgent,
     806             :                                                               int32_t aIncomingChannelType) const
     807             : {
     808           0 :   MOZ_ASSERT(aAgent);
     809           0 :   MOZ_ASSERT(IsEnableAudioCompetingForAllAgents() ?
     810             :     mAgents.Contains(aAgent) : mAudibleAgents.Contains(aAgent));
     811             : 
     812           0 :   uint32_t competingBehavior = nsISuspendedTypes::NONE_SUSPENDED;
     813           0 :   int32_t presentChannelType = aAgent->AudioChannelType();
     814             : 
     815             :   // TODO : add other competing cases for MediaSession API
     816           0 :   if (presentChannelType == int32_t(AudioChannel::Normal) &&
     817             :       aIncomingChannelType == int32_t(AudioChannel::Normal)) {
     818           0 :     competingBehavior = nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE;
     819             :   }
     820             : 
     821           0 :   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
     822             :          ("AudioChannelWindow, GetCompetingBehavior, this = %p, "
     823             :           "present type = %d, incoming channel = %d, behavior = %s\n",
     824             :           this, presentChannelType, aIncomingChannelType,
     825             :           SuspendTypeToStr(competingBehavior)));
     826             : 
     827           0 :   return competingBehavior;
     828             : }
     829             : 
     830             : void
     831           0 : AudioChannelService::AudioChannelWindow::AppendAgent(AudioChannelAgent* aAgent,
     832             :                                                      AudibleState aAudible)
     833             : {
     834           0 :   MOZ_ASSERT(aAgent);
     835             : 
     836           0 :   RequestAudioFocus(aAgent);
     837           0 :   AppendAgentAndIncreaseAgentsNum(aAgent);
     838           0 :   AudioCapturedChanged(aAgent, AudioCaptureState::eCapturing);
     839           0 :   if (aAudible == AudibleState::eAudible) {
     840             :     AudioAudibleChanged(aAgent,
     841             :                         AudibleState::eAudible,
     842           0 :                         AudibleChangedReasons::eDataAudibleChanged);
     843           0 :   } else if (IsEnableAudioCompetingForAllAgents() &&
     844             :              aAudible != AudibleState::eAudible) {
     845           0 :     NotifyAudioCompetingChanged(aAgent);
     846             :   }
     847           0 : }
     848             : 
     849             : void
     850           0 : AudioChannelService::AudioChannelWindow::RemoveAgent(AudioChannelAgent* aAgent)
     851             : {
     852           0 :   MOZ_ASSERT(aAgent);
     853             : 
     854           0 :   RemoveAgentAndReduceAgentsNum(aAgent);
     855           0 :   AudioCapturedChanged(aAgent, AudioCaptureState::eNotCapturing);
     856             :   AudioAudibleChanged(aAgent,
     857             :                       AudibleState::eNotAudible,
     858           0 :                       AudibleChangedReasons::ePauseStateChanged);
     859           0 : }
     860             : 
     861             : void
     862           0 : AudioChannelService::AudioChannelWindow::NotifyMediaBlockStop(nsPIDOMWindowOuter* aWindow)
     863             : {
     864             :   // Can't use raw pointer for lamba variable capturing, use smart ptr.
     865           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = aWindow;
     866           0 :   NS_DispatchToCurrentThread(NS_NewRunnableFunction(
     867             :     "dom::AudioChannelService::AudioChannelWindow::NotifyMediaBlockStop",
     868           0 :     [window]() -> void {
     869             :       nsCOMPtr<nsIObserverService> observerService =
     870           0 :         services::GetObserverService();
     871           0 :       if (NS_WARN_IF(!observerService)) {
     872           0 :         return;
     873             :       }
     874             : 
     875           0 :       observerService->NotifyObservers(ToSupports(window),
     876             :                                        "audio-playback",
     877           0 :                                        u"mediaBlockStop");
     878             :     })
     879           0 :   );
     880             : 
     881           0 :   if (mShouldSendActiveMediaBlockStopEvent) {
     882           0 :     mShouldSendActiveMediaBlockStopEvent = false;
     883           0 :     NS_DispatchToCurrentThread(NS_NewRunnableFunction(
     884             :       "dom::AudioChannelService::AudioChannelWindow::NotifyMediaBlockStop",
     885           0 :       [window]() -> void {
     886             :         nsCOMPtr<nsIObserverService> observerService =
     887           0 :           services::GetObserverService();
     888           0 :         if (NS_WARN_IF(!observerService)) {
     889           0 :           return;
     890             :         }
     891             : 
     892           0 :         observerService->NotifyObservers(ToSupports(window),
     893             :                                          "audio-playback",
     894           0 :                                          u"activeMediaBlockStop");
     895             :       })
     896           0 :     );
     897             :   }
     898           0 : }
     899             : 
     900             : void
     901           0 : AudioChannelService::AudioChannelWindow::AppendAgentAndIncreaseAgentsNum(AudioChannelAgent* aAgent)
     902             : {
     903           0 :   MOZ_ASSERT(aAgent);
     904           0 :   MOZ_ASSERT(!mAgents.Contains(aAgent));
     905             : 
     906           0 :   int32_t channel = aAgent->AudioChannelType();
     907           0 :   mAgents.AppendElement(aAgent);
     908             : 
     909           0 :   ++mChannels[channel].mNumberOfAgents;
     910             : 
     911             :   // TODO: Make NotifyChannelActiveRunnable irrelevant to BrowserElementAudioChannel
     912           0 :   if (mChannels[channel].mNumberOfAgents == 1) {
     913           0 :     NotifyChannelActive(aAgent->WindowID(), true);
     914             :   }
     915           0 : }
     916             : 
     917             : void
     918           0 : AudioChannelService::AudioChannelWindow::RemoveAgentAndReduceAgentsNum(AudioChannelAgent* aAgent)
     919             : {
     920           0 :   MOZ_ASSERT(aAgent);
     921           0 :   MOZ_ASSERT(mAgents.Contains(aAgent));
     922             : 
     923           0 :   int32_t channel = aAgent->AudioChannelType();
     924           0 :   mAgents.RemoveElement(aAgent);
     925             : 
     926           0 :   MOZ_ASSERT(mChannels[channel].mNumberOfAgents > 0);
     927           0 :   --mChannels[channel].mNumberOfAgents;
     928             : 
     929           0 :   if (mChannels[channel].mNumberOfAgents == 0) {
     930           0 :     NotifyChannelActive(aAgent->WindowID(), false);
     931             :   }
     932           0 : }
     933             : 
     934             : void
     935           0 : AudioChannelService::AudioChannelWindow::AudioCapturedChanged(AudioChannelAgent* aAgent,
     936             :                                                               AudioCaptureState aCapture)
     937             : {
     938           0 :   MOZ_ASSERT(aAgent);
     939             : 
     940           0 :   if (mIsAudioCaptured) {
     941           0 :     aAgent->WindowAudioCaptureChanged(aAgent->InnerWindowID(), aCapture);
     942             :   }
     943           0 : }
     944             : 
     945             : void
     946           0 : AudioChannelService::AudioChannelWindow::AudioAudibleChanged(AudioChannelAgent* aAgent,
     947             :                                                              AudibleState aAudible,
     948             :                                                              AudibleChangedReasons aReason)
     949             : {
     950           0 :   MOZ_ASSERT(aAgent);
     951             : 
     952           0 :   if (aAudible == AudibleState::eAudible) {
     953           0 :     AppendAudibleAgentIfNotContained(aAgent, aReason);
     954           0 :     NotifyAudioCompetingChanged(aAgent);
     955             :   } else {
     956           0 :     RemoveAudibleAgentIfContained(aAgent, aReason);
     957             :   }
     958             : 
     959           0 :   if (aAudible != AudibleState::eNotAudible) {
     960           0 :     MaybeNotifyMediaBlockStart(aAgent);
     961             :   }
     962           0 : }
     963             : 
     964             : void
     965           0 : AudioChannelService::AudioChannelWindow::AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent,
     966             :                                                                           AudibleChangedReasons aReason)
     967             : {
     968           0 :   MOZ_ASSERT(aAgent);
     969           0 :   MOZ_ASSERT(mAgents.Contains(aAgent));
     970             : 
     971           0 :   if (!mAudibleAgents.Contains(aAgent)) {
     972           0 :     mAudibleAgents.AppendElement(aAgent);
     973           0 :     if (IsFirstAudibleAgent()) {
     974           0 :       NotifyAudioAudibleChanged(aAgent->Window(), AudibleState::eAudible, aReason);
     975             :     }
     976             :   }
     977           0 : }
     978             : 
     979             : void
     980           0 : AudioChannelService::AudioChannelWindow::RemoveAudibleAgentIfContained(AudioChannelAgent* aAgent,
     981             :                                                                        AudibleChangedReasons aReason)
     982             : {
     983           0 :   MOZ_ASSERT(aAgent);
     984             : 
     985           0 :   if (mAudibleAgents.Contains(aAgent)) {
     986           0 :     mAudibleAgents.RemoveElement(aAgent);
     987           0 :     if (IsLastAudibleAgent()) {
     988           0 :       NotifyAudioAudibleChanged(aAgent->Window(), AudibleState::eNotAudible, aReason);
     989             :     }
     990             :   }
     991           0 : }
     992             : 
     993             : bool
     994           0 : AudioChannelService::AudioChannelWindow::IsFirstAudibleAgent() const
     995             : {
     996           0 :   return (mAudibleAgents.Length() == 1);
     997             : }
     998             : 
     999             : bool
    1000           0 : AudioChannelService::AudioChannelWindow::IsLastAudibleAgent() const
    1001             : {
    1002           0 :   return mAudibleAgents.IsEmpty();
    1003             : }
    1004             : 
    1005             : bool
    1006           0 : AudioChannelService::AudioChannelWindow::IsInactiveWindow() const
    1007             : {
    1008           0 :   return IsEnableAudioCompetingForAllAgents() ?
    1009           0 :     mAudibleAgents.IsEmpty() && mAgents.IsEmpty() : mAudibleAgents.IsEmpty();
    1010             : }
    1011             : 
    1012             : void
    1013           0 : AudioChannelService::AudioChannelWindow::NotifyAudioAudibleChanged(nsPIDOMWindowOuter* aWindow,
    1014             :                                                                    AudibleState aAudible,
    1015             :                                                                    AudibleChangedReasons aReason)
    1016             : {
    1017             :   RefPtr<AudioPlaybackRunnable> runnable =
    1018             :     new AudioPlaybackRunnable(aWindow,
    1019             :                               aAudible == AudibleState::eAudible,
    1020           0 :                               aReason);
    1021           0 :   DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(runnable);
    1022           0 :   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToCurrentThread failed");
    1023           0 : }
    1024             : 
    1025             : void
    1026           0 : AudioChannelService::AudioChannelWindow::NotifyChannelActive(uint64_t aWindowID,
    1027             :                                                              bool aActive)
    1028             : {
    1029             :   RefPtr<NotifyChannelActiveRunnable> runnable =
    1030           0 :     new NotifyChannelActiveRunnable(aWindowID, aActive);
    1031           0 :   DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(runnable);
    1032           0 :   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToCurrentThread failed");
    1033           0 : }
    1034             : 
    1035             : void
    1036           0 : AudioChannelService::AudioChannelWindow::MaybeNotifyMediaBlockStart(AudioChannelAgent* aAgent)
    1037             : {
    1038           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = aAgent->Window();
    1039           0 :   if (!window) {
    1040           0 :     return;
    1041             :   }
    1042             : 
    1043           0 :   MOZ_ASSERT(window->IsOuterWindow());
    1044           0 :   nsCOMPtr<nsPIDOMWindowInner> inner = window->GetCurrentInnerWindow();
    1045           0 :   if (!inner) {
    1046           0 :     return;
    1047             :   }
    1048             : 
    1049           0 :   nsCOMPtr<nsIDocument> doc = inner->GetExtantDoc();
    1050           0 :   if (!doc) {
    1051           0 :     return;
    1052             :   }
    1053             : 
    1054           0 :   if (window->GetMediaSuspend() != nsISuspendedTypes::SUSPENDED_BLOCK ||
    1055           0 :       !doc->Hidden()) {
    1056           0 :     return;
    1057             :   }
    1058             : 
    1059           0 :   if (!mShouldSendActiveMediaBlockStopEvent) {
    1060           0 :       mShouldSendActiveMediaBlockStopEvent = true;
    1061           0 :       NS_DispatchToCurrentThread(NS_NewRunnableFunction(
    1062             :         "dom::AudioChannelService::AudioChannelWindow::"
    1063             :         "MaybeNotifyMediaBlockStart",
    1064           0 :         [window]() -> void {
    1065             :           nsCOMPtr<nsIObserverService> observerService =
    1066           0 :             services::GetObserverService();
    1067           0 :           if (NS_WARN_IF(!observerService)) {
    1068           0 :             return;
    1069             :           }
    1070             : 
    1071           0 :           observerService->NotifyObservers(
    1072           0 :             ToSupports(window), "audio-playback", u"activeMediaBlockStart");
    1073           0 :         }));
    1074             :   }
    1075             : }
    1076             : 
    1077             : } // namespace dom
    1078           9 : } // namespace mozilla
    1079             : 

Generated by: LCOV version 1.13