From 80b5f477f2e3b5116225547f29160f4b8d30182c Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Fri, 10 Jul 2015 17:38:44 +0100 Subject: [PATCH] Bug 1113086 - AudioChannel policy in Browser API - patch 1 - BrowserElementAudioChannel, r=ehsan --- browser/installer/package-manifest.in | 2 + dom/audiochannel/AudioChannelAgent.cpp | 90 +- dom/audiochannel/AudioChannelAgent.h | 15 +- dom/audiochannel/AudioChannelCommon.h | 24 - dom/audiochannel/AudioChannelService.cpp | 1208 ++++++----------- dom/audiochannel/AudioChannelService.h | 220 +-- dom/audiochannel/AudioChannelServiceChild.cpp | 168 --- dom/audiochannel/AudioChannelServiceChild.h | 62 - dom/audiochannel/moz.build | 6 +- dom/audiochannel/nsIAudioChannelAgent.idl | 42 +- dom/audiochannel/nsIAudioChannelService.idl | 29 + .../tests/AudioChannelChromeScript.js | 18 - .../tests/TestAudioChannelService.cpp | 669 --------- dom/audiochannel/tests/audio.ogg | Bin 16521 -> 0 bytes dom/audiochannel/tests/file_audio.html | 99 -- .../tests/file_telephonyPolicy.html | 18 - dom/audiochannel/tests/mochitest.ini | 11 - dom/audiochannel/tests/moz.build | 16 - .../tests/test_audioChannelChange.html | 209 --- .../tests/test_telephonyPolicy.html | 87 -- dom/base/nsGkAtomList.h | 1 + dom/base/nsGlobalWindow.cpp | 31 +- dom/base/nsPIDOMWindow.h | 2 - .../BrowserElementAudioChannel.cpp | 489 +++++++ .../BrowserElementAudioChannel.h | 83 ++ .../BrowserElementChildPreload.js | 58 + dom/browser-element/BrowserElementParent.cpp | 1 + dom/browser-element/BrowserElementParent.js | 34 +- .../mochitest/browserElement_AudioChannel.js | 153 +++ dom/browser-element/mochitest/file_audio.html | 15 + .../mochitest/iframe_file_audio.html | 5 + .../mochitest/mochitest-oop.ini | 1 + dom/browser-element/mochitest/mochitest.ini | 4 + ...st_browserElement_inproc_AudioChannel.html | 13 + .../test_browserElement_oop_AudioChannel.html | 13 + dom/browser-element/moz.build | 5 + dom/browser-element/nsIBrowserElementAPI.idl | 10 +- dom/camera/DOMCameraControl.cpp | 8 +- dom/fmradio/FMRadio.cpp | 64 +- dom/fmradio/FMRadio.h | 5 - dom/html/HTMLAudioElement.cpp | 1 - dom/html/HTMLMediaElement.cpp | 78 +- dom/html/HTMLMediaElement.h | 9 +- dom/html/nsBrowserElement.cpp | 141 ++ dom/html/nsBrowserElement.h | 6 + dom/html/nsGenericHTMLFrameElement.cpp | 13 + dom/html/nsGenericHTMLFrameElement.h | 4 +- dom/ipc/ContentChild.cpp | 12 - dom/ipc/ContentChild.h | 3 - dom/ipc/ContentParent.cpp | 55 +- dom/ipc/ContentParent.h | 13 - dom/ipc/PBrowser.ipdl | 4 + dom/ipc/PContent.ipdl | 18 - dom/ipc/ProcessPriorityManager.cpp | 2 +- dom/ipc/TabChild.cpp | 48 + dom/ipc/TabMessageUtils.h | 9 - dom/ipc/TabParent.cpp | 24 + dom/ipc/TabParent.h | 3 + dom/media/webaudio/AudioDestinationNode.cpp | 149 +- dom/media/webaudio/AudioDestinationNode.h | 10 +- dom/media/webaudio/moz.build | 2 - dom/media/webaudio/test/browser.ini | 8 - .../test/browser_mozAudioChannel.html | 33 - .../webaudio/test/browser_mozAudioChannel.js | 80 -- .../test/browser_mozAudioChannel_muted.html | 21 - .../test/browser_mozAudioChannel_muted.js | 72 - dom/speakermanager/SpeakerManager.cpp | 4 +- dom/speakermanager/SpeakerManagerService.cpp | 15 +- .../SpeakerManagerServiceChild.cpp | 4 +- dom/system/gonk/AudioChannelManager.cpp | 2 +- dom/system/gonk/AudioManager.cpp | 11 +- dom/webidl/BrowserElementAudioChannel.webidl | 37 + dom/webidl/moz.build | 1 + layout/build/nsLayoutModule.cpp | 2 +- 74 files changed, 1773 insertions(+), 3109 deletions(-) delete mode 100644 dom/audiochannel/AudioChannelCommon.h delete mode 100644 dom/audiochannel/AudioChannelServiceChild.cpp delete mode 100644 dom/audiochannel/AudioChannelServiceChild.h create mode 100644 dom/audiochannel/nsIAudioChannelService.idl delete mode 100644 dom/audiochannel/tests/AudioChannelChromeScript.js delete mode 100644 dom/audiochannel/tests/TestAudioChannelService.cpp delete mode 100644 dom/audiochannel/tests/audio.ogg delete mode 100644 dom/audiochannel/tests/file_audio.html delete mode 100644 dom/audiochannel/tests/file_telephonyPolicy.html delete mode 100644 dom/audiochannel/tests/mochitest.ini delete mode 100644 dom/audiochannel/tests/moz.build delete mode 100644 dom/audiochannel/tests/test_audioChannelChange.html delete mode 100644 dom/audiochannel/tests/test_telephonyPolicy.html create mode 100644 dom/browser-element/BrowserElementAudioChannel.cpp create mode 100644 dom/browser-element/BrowserElementAudioChannel.h create mode 100644 dom/browser-element/mochitest/browserElement_AudioChannel.js create mode 100644 dom/browser-element/mochitest/file_audio.html create mode 100644 dom/browser-element/mochitest/iframe_file_audio.html create mode 100644 dom/browser-element/mochitest/test_browserElement_inproc_AudioChannel.html create mode 100644 dom/browser-element/mochitest/test_browserElement_oop_AudioChannel.html delete mode 100644 dom/media/webaudio/test/browser.ini delete mode 100644 dom/media/webaudio/test/browser_mozAudioChannel.html delete mode 100644 dom/media/webaudio/test/browser_mozAudioChannel.js delete mode 100644 dom/media/webaudio/test/browser_mozAudioChannel_muted.html delete mode 100644 dom/media/webaudio/test/browser_mozAudioChannel_muted.js create mode 100644 dom/webidl/BrowserElementAudioChannel.webidl diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 42710fc079c..52ea0892981 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -888,6 +888,8 @@ bin/libfreebl_32int64_3.so @RESPATH@/components/DataStoreImpl.js @RESPATH@/components/dom_datastore.xpt +@RESPATH@/components/dom_audiochannel.xpt + ; Shutdown Terminator @RESPATH@/components/nsTerminatorTelemetry.js @RESPATH@/components/terminator.manifest diff --git a/dom/audiochannel/AudioChannelAgent.cpp b/dom/audiochannel/AudioChannelAgent.cpp index aaef0c336e6..31b3146de12 100644 --- a/dom/audiochannel/AudioChannelAgent.cpp +++ b/dom/audiochannel/AudioChannelAgent.cpp @@ -5,7 +5,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "AudioChannelAgent.h" -#include "AudioChannelCommon.h" #include "AudioChannelService.h" #include "nsIDOMWindow.h" #include "nsPIDOMWindow.h" @@ -26,8 +25,6 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioChannelAgent) AudioChannelAgent::AudioChannelAgent() : mAudioChannelType(AUDIO_AGENT_CHANNEL_ERROR) , mIsRegToService(false) - , mVisible(true) - , mWithVideo(false) { } @@ -66,21 +63,10 @@ AudioChannelAgent::InitWithWeakCallback(nsIDOMWindow* aWindow, /* useWeakRef = */ true); } -/* void initWithVideo(in nsIDOMWindow window, in long channelType, - * in nsIAudioChannelAgentCallback callback, in boolean weak); */ -NS_IMETHODIMP -AudioChannelAgent::InitWithVideo(nsIDOMWindow* aWindow, int32_t aChannelType, - nsIAudioChannelAgentCallback *aCallback, - bool aUseWeakRef) -{ - return InitInternal(aWindow, aChannelType, aCallback, aUseWeakRef, - /* withVideo = */ true); -} - nsresult AudioChannelAgent::InitInternal(nsIDOMWindow* aWindow, int32_t aChannelType, nsIAudioChannelAgentCallback *aCallback, - bool aUseWeakRef, bool aWithVideo) + bool aUseWeakRef) { // We syncd the enum of channel type between nsIAudioChannelAgent.idl and // AudioChannelBinding.h the same. @@ -100,12 +86,12 @@ AudioChannelAgent::InitInternal(nsIDOMWindow* aWindow, int32_t aChannelType, } if (aWindow) { - nsCOMPtr pWindow = do_QueryInterface(aWindow); - if (!pWindow->IsInnerWindow()) { - pWindow = pWindow->GetCurrentInnerWindow(); - } + nsCOMPtr topWindow; + aWindow->GetScriptableTop(getter_AddRefs(topWindow)); + MOZ_ASSERT(topWindow); - mWindow = pWindow.forget(); + mWindow = do_QueryInterface(topWindow); + mWindow = mWindow->GetOuterWindow(); } mAudioChannelType = aChannelType; @@ -116,23 +102,26 @@ AudioChannelAgent::InitInternal(nsIDOMWindow* aWindow, int32_t aChannelType, mCallback = aCallback; } - mWithVideo = aWithVideo; - return NS_OK; } /* boolean startPlaying (); */ -NS_IMETHODIMP AudioChannelAgent::StartPlaying(int32_t *_retval) +NS_IMETHODIMP AudioChannelAgent::StartPlaying(float *aVolume, bool* aMuted) { - AudioChannelService *service = AudioChannelService::GetOrCreateAudioChannelService(); + MOZ_ASSERT(aVolume); + MOZ_ASSERT(aMuted); + + nsRefPtr service = AudioChannelService::GetOrCreate(); if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR || service == nullptr || mIsRegToService) { return NS_ERROR_FAILURE; } service->RegisterAudioChannelAgent(this, - static_cast(mAudioChannelType), mWithVideo); - *_retval = service->GetState(this, !mVisible); + static_cast(mAudioChannelType)); + + service->GetState(mWindow, mAudioChannelType, aVolume, aMuted); + mIsRegToService = true; return NS_OK; } @@ -145,36 +134,12 @@ NS_IMETHODIMP AudioChannelAgent::StopPlaying(void) return NS_ERROR_FAILURE; } - AudioChannelService *service = AudioChannelService::GetOrCreateAudioChannelService(); + nsRefPtr service = AudioChannelService::GetOrCreate(); service->UnregisterAudioChannelAgent(this); mIsRegToService = false; return NS_OK; } -/* void setVisibilityState (in boolean visible); */ -NS_IMETHODIMP AudioChannelAgent::SetVisibilityState(bool visible) -{ - bool oldVisibility = mVisible; - - nsCOMPtr callback = GetCallback(); - - mVisible = visible; - if (mIsRegToService && oldVisibility != mVisible && callback) { - AudioChannelService *service = AudioChannelService::GetOrCreateAudioChannelService(); - callback->CanPlayChanged(service->GetState(this, !mVisible)); - } - return NS_OK; -} - -void AudioChannelAgent::NotifyAudioChannelStateChanged() -{ - nsCOMPtr callback = GetCallback(); - if (callback) { - AudioChannelService *service = AudioChannelService::GetOrCreateAudioChannelService(); - callback->CanPlayChanged(service->GetState(this, !mVisible)); - } -} - already_AddRefed AudioChannelAgent::GetCallback() { @@ -193,20 +158,17 @@ AudioChannelAgent::WindowVolumeChanged() return; } - callback->WindowVolumeChanged(); + float volume = 1.0; + bool muted = false; + + nsRefPtr service = AudioChannelService::GetOrCreate(); + service->GetState(mWindow, mAudioChannelType, &volume, &muted); + + callback->WindowVolumeChanged(volume, muted); } -NS_IMETHODIMP -AudioChannelAgent::GetWindowVolume(float* aVolume) +uint64_t +AudioChannelAgent::WindowID() const { - NS_ENSURE_ARG_POINTER(aVolume); - - nsCOMPtr win = do_QueryInterface(mWindow); - if (!win) { - *aVolume = 1.0f; - return NS_OK; - } - - *aVolume = win->GetAudioGlobalVolume(); - return NS_OK; + return mWindow ? mWindow->WindowID() : 0; } diff --git a/dom/audiochannel/AudioChannelAgent.h b/dom/audiochannel/AudioChannelAgent.h index f2259386f6d..ed70c7e3b22 100644 --- a/dom/audiochannel/AudioChannelAgent.h +++ b/dom/audiochannel/AudioChannelAgent.h @@ -17,7 +17,7 @@ #define NS_AUDIOCHANNELAGENT_CID {0xf27688e2, 0x3dd7, 0x11e2, \ {0x90, 0x4e, 0x10, 0xbf, 0x48, 0xd6, 0x4b, 0xd4}} -class nsIDOMWindow; +class nsPIDOMWindow; namespace mozilla { namespace dom { @@ -32,15 +32,16 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS(AudioChannelAgent) AudioChannelAgent(); - virtual void NotifyAudioChannelStateChanged(); void WindowVolumeChanged(); - nsIDOMWindow* Window() const + nsPIDOMWindow* Window() const { return mWindow; } + uint64_t WindowID() const; + private: virtual ~AudioChannelAgent(); @@ -50,15 +51,15 @@ private: nsresult InitInternal(nsIDOMWindow* aWindow, int32_t aAudioAgentType, nsIAudioChannelAgentCallback* aCallback, - bool aUseWeakRef, bool aWithVideo=false); + bool aUseWeakRef); - nsCOMPtr mWindow; + nsCOMPtr mWindow; nsCOMPtr mCallback; + nsWeakPtr mWeakCallback; + int32_t mAudioChannelType; bool mIsRegToService; - bool mVisible; - bool mWithVideo; }; } // namespace dom diff --git a/dom/audiochannel/AudioChannelCommon.h b/dom/audiochannel/AudioChannelCommon.h deleted file mode 100644 index b0de2904140..00000000000 --- a/dom/audiochannel/AudioChannelCommon.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_audiochannelcommon_h__ -#define mozilla_dom_audiochannelcommon_h__ - -namespace mozilla { -namespace dom { - -enum AudioChannelState { - AUDIO_CHANNEL_STATE_NORMAL = 0, - AUDIO_CHANNEL_STATE_MUTED, - AUDIO_CHANNEL_STATE_FADED, - AUDIO_CHANNEL_STATE_LAST -}; - -} // namespace dom -} // namespace mozilla - -#endif - diff --git a/dom/audiochannel/AudioChannelService.cpp b/dom/audiochannel/AudioChannelService.cpp index 87eceb2a8ad..6ce82c4315e 100644 --- a/dom/audiochannel/AudioChannelService.cpp +++ b/dom/audiochannel/AudioChannelService.cpp @@ -5,7 +5,6 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "AudioChannelService.h" -#include "AudioChannelServiceChild.h" #include "base/basictypes.h" @@ -13,9 +12,11 @@ #include "mozilla/StaticPtr.h" #include "mozilla/unused.h" +#include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ContentParent.h" #include "nsContentUtils.h" +#include "nsIScriptSecurityManager.h" #include "nsISupportsPrimitives.h" #include "nsThreadUtils.h" #include "nsHashPropertyBag.h" @@ -37,18 +38,54 @@ using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::hal; -// When a inner-window is destroyed we have to mute all the related -// AudioChannelAgents. In order to do this we have to notify them after purging -// AudioChannelService::mAgents. -struct MOZ_STACK_CLASS WindowDestroyedEnumeratorData -{ - explicit WindowDestroyedEnumeratorData(uint64_t aInnerID) - : mInnerID(aInnerID) - {} +namespace { - nsTArray> mAgents; - uint64_t mInnerID; -}; +void +NotifyChannelActive(uint64_t aWindowID, AudioChannel aAudioChannel, + bool aActive) +{ + nsCOMPtr observerService = + services::GetObserverService(); + if (NS_WARN_IF(!observerService)) { + return; + } + + nsCOMPtr wrapper = + do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID); + if (!wrapper) { + return; + } + + wrapper->SetData(aWindowID); + + nsAutoString name; + AudioChannelService::GetAudioChannelString(aAudioChannel, name); + + nsAutoCString topic; + topic.Assign("audiochannel-activity-"); + topic.Append(NS_ConvertUTF16toUTF8(name)); + + observerService->NotifyObservers(wrapper, topic.get(), + aActive + ? MOZ_UTF16("active") : MOZ_UTF16("inactive")); +} + +already_AddRefed +GetTopWindow(nsIDOMWindow* aWindow) +{ + MOZ_ASSERT(aWindow); + + nsCOMPtr topWindow; + aWindow->GetScriptableTop(getter_AddRefs(topWindow)); + MOZ_ASSERT(topWindow); + + nsCOMPtr window = do_QueryInterface(topWindow); + window = window->GetOuterWindow(); + + return window.forget(); +} + +} // anonymous namespace StaticRefPtr gAudioChannelService; @@ -64,63 +101,52 @@ static const nsAttrValue::EnumTable kMozAudioChannelAttributeTable[] = { { nullptr } }; -// static -AudioChannelService* -AudioChannelService::GetAudioChannelService() +/* static */ already_AddRefed +AudioChannelService::GetOrCreate() { MOZ_ASSERT(NS_IsMainThread()); - if (!XRE_IsParentProcess()) { - return AudioChannelServiceChild::GetAudioChannelService(); + if (!gAudioChannelService) { + gAudioChannelService = new AudioChannelService(); } - return gAudioChannelService; - -} - -// static -AudioChannelService* -AudioChannelService::GetOrCreateAudioChannelService() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (!XRE_IsParentProcess()) { - return AudioChannelServiceChild::GetOrCreateAudioChannelService(); - } - - // If we already exist, exit early - if (gAudioChannelService) { - return gAudioChannelService; - } - - // Create new instance, register, return - nsRefPtr service = new AudioChannelService(); - MOZ_ASSERT(service); - - gAudioChannelService = service; - return gAudioChannelService; + nsRefPtr service = gAudioChannelService.get(); + return service.forget(); } void AudioChannelService::Shutdown() { - if (!XRE_IsParentProcess()) { - return AudioChannelServiceChild::Shutdown(); - } - if (gAudioChannelService) { + if (XRE_GetProcessType() == GeckoProcessType_Default) { + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + obs->RemoveObserver(gAudioChannelService, "ipc:content-shutdown"); + obs->RemoveObserver(gAudioChannelService, "xpcom-shutdown"); + obs->RemoveObserver(gAudioChannelService, "inner-window-destroyed"); +#ifdef MOZ_WIDGET_GONK + // To monitor the volume settings based on audio channel. + obs->RemoveObserver(gAudioChannelService, "mozsettings-changed"); +#endif + } + } + gAudioChannelService = nullptr; } } -NS_IMPL_ISUPPORTS(AudioChannelService, nsIObserver, nsITimerCallback) +NS_INTERFACE_MAP_BEGIN(AudioChannelService) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAudioChannelService) + NS_INTERFACE_MAP_ENTRY(nsIAudioChannelService) + NS_INTERFACE_MAP_ENTRY(nsIObserver) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(AudioChannelService) +NS_IMPL_RELEASE(AudioChannelService) AudioChannelService::AudioChannelService() -: mCurrentHigherChannel(-1) -, mCurrentVisibleHigherChannel(-1) -, mPlayableHiddenContentChildID(CONTENT_PROCESS_ID_UNKNOWN) -, mDisabled(false) -, mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN) + : mDisabled(false) + , mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN) { if (XRE_IsParentProcess()) { nsCOMPtr obs = mozilla::services::GetObserverService(); @@ -142,23 +168,29 @@ AudioChannelService::~AudioChannelService() void AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent, - AudioChannel aChannel, - bool aWithVideo) + AudioChannel aChannel) { if (mDisabled) { return; } - AudioChannelAgentData* data = new AudioChannelAgentData(aChannel, - true /* aElementHidden */, - AUDIO_CHANNEL_STATE_MUTED /* aState */, - aWithVideo); - mAgents.Put(aAgent, data); - RegisterType(aChannel, CONTENT_PROCESS_ID_MAIN, aWithVideo); + uint64_t windowID = aAgent->WindowID(); + AudioChannelWindow* winData = mWindows.LookupOrAdd(windowID); + + MOZ_ASSERT(!winData->mAgents.Get(aAgent)); + + AudioChannel* audioChannel = new AudioChannel(aChannel); + winData->mAgents.Put(aAgent, audioChannel); + + ++winData->mChannels[(uint32_t)aChannel].mNumberOfAgents; + + // The first one, we must inform the BrowserElementAudioChannel. + if (winData->mChannels[(uint32_t)aChannel].mNumberOfAgents == 1) { + NotifyChannelActive(aAgent->WindowID(), aChannel, true); + } // If this is the first agent for this window, we must notify the observers. - uint32_t count = CountWindow(aAgent->Window()); - if (count == 1) { + if (winData->mAgents.Count() == 1) { nsCOMPtr observerService = services::GetObserverService(); if (observerService) { @@ -169,61 +201,6 @@ AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent, } } -void -AudioChannelService::RegisterType(AudioChannel aChannel, uint64_t aChildID, - bool aWithVideo) -{ - if (mDisabled) { - return; - } - - AudioChannelInternalType type = GetInternalType(aChannel, true); - mChannelCounters[type].AppendElement(aChildID); - - if (XRE_IsParentProcess()) { - - // We must keep the childIds in order to decide which app is allowed to play - // with then telephony channel. - if (aChannel == AudioChannel::Telephony) { - RegisterTelephonyChild(aChildID); - } - - // Since there is another telephony registered, we can unregister old one - // immediately. - if (mDeferTelChannelTimer && aChannel == AudioChannel::Telephony) { - mDeferTelChannelTimer->Cancel(); - mDeferTelChannelTimer = nullptr; - UnregisterTypeInternal(aChannel, mTimerElementHidden, mTimerChildID, - false); - } - - if (aWithVideo) { - mWithVideoChildIDs.AppendElement(aChildID); - } - - // No hidden content channel can be playable if there is a content channel - // in foreground (bug 855208), nor if there is a normal channel with video - // in foreground (bug 894249). - if (type == AUDIO_CHANNEL_INT_CONTENT || - (type == AUDIO_CHANNEL_INT_NORMAL && - mWithVideoChildIDs.Contains(aChildID))) { - mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN; - } - // One hidden content channel can be playable only when there is no any - // content channel in the foreground, and no normal channel with video in - // foreground. - else if (type == AUDIO_CHANNEL_INT_CONTENT_HIDDEN && - mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) { - mPlayableHiddenContentChildID = aChildID; - } - - // In order to avoid race conditions, it's safer to notify any existing - // agent any time a new one is registered. - SendAudioChannelChangedNotification(aChildID); - SendNotification(); - } -} - void AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent) { @@ -231,12 +208,23 @@ AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent) return; } - nsAutoPtr data; - mAgents.RemoveAndForget(aAgent, data); + uint64_t windowID = aAgent->WindowID(); + AudioChannelWindow* winData = nullptr; + if (!mWindows.Get(windowID, &winData)) { + return; + } - if (data) { - UnregisterType(data->mChannel, data->mElementHidden, - CONTENT_PROCESS_ID_MAIN, data->mWithVideo); + nsAutoPtr audioChannel; + winData->mAgents.RemoveAndForget(aAgent, audioChannel); + if (audioChannel) { + MOZ_ASSERT(winData->mChannels[(uint32_t)*audioChannel].mNumberOfAgents > 0); + + --winData->mChannels[(uint32_t)*audioChannel].mNumberOfAgents; + + // The last one, we must inform the BrowserElementAudioChannel. + if (winData->mChannels[(uint32_t)*audioChannel].mNumberOfAgents == 0) { + NotifyChannelActive(aAgent->WindowID(), *audioChannel, false); + } } #ifdef MOZ_WIDGET_GONK @@ -247,8 +235,7 @@ AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent) #endif // If this is the last agent for this window, we must notify the observers. - uint32_t count = CountWindow(aAgent->Window()); - if (count == 0) { + if (winData->mAgents.Count() == 0) { nsCOMPtr observerService = services::GetObserverService(); if (observerService) { @@ -260,616 +247,118 @@ AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent) } void -AudioChannelService::UnregisterType(AudioChannel aChannel, - bool aElementHidden, - uint64_t aChildID, - bool aWithVideo) +AudioChannelService::GetState(nsPIDOMWindow* aWindow, uint32_t aAudioChannel, + float* aVolume, bool* aMuted) { - if (mDisabled) { + MOZ_ASSERT(!aWindow || aWindow->IsOuterWindow()); + MOZ_ASSERT(aVolume && aMuted); + MOZ_ASSERT(aAudioChannel < NUMBER_OF_AUDIO_CHANNELS); + + *aVolume = 1.0; + *aMuted = false; + + if (!aWindow || !aWindow->IsOuterWindow()) { return; } - // There are two reasons to defer the decrease of telephony channel. - // 1. User can have time to remove device from his ear before music resuming. - // 2. Give BT SCO to be disconnected before starting to connect A2DP. - if (XRE_IsParentProcess()) { + AudioChannelWindow* winData = nullptr; + nsCOMPtr window = aWindow; - if (aChannel == AudioChannel::Telephony) { - UnregisterTelephonyChild(aChildID); + // 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 { + if (mWindows.Get(window->WindowID(), &winData)) { + *aVolume *= winData->mChannels[aAudioChannel].mVolume; + *aMuted = *aMuted || winData->mChannels[aAudioChannel].mMuted; } - if (aChannel == AudioChannel::Telephony && - (mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN].Length() + - mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].Length()) == 1) { - mTimerElementHidden = aElementHidden; - mTimerChildID = aChildID; - mDeferTelChannelTimer = do_CreateInstance("@mozilla.org/timer;1"); - mDeferTelChannelTimer->InitWithCallback(this, 1500, nsITimer::TYPE_ONE_SHOT); - return; - } - } + *aVolume *= window->GetAudioVolume(); + *aMuted = *aMuted || window->GetAudioMuted(); - UnregisterTypeInternal(aChannel, aElementHidden, aChildID, aWithVideo); -} - -void -AudioChannelService::UnregisterTypeInternal(AudioChannel aChannel, - bool aElementHidden, - uint64_t aChildID, - bool aWithVideo) -{ - // The array may contain multiple occurrence of this appId but - // this should remove only the first one. - AudioChannelInternalType type = GetInternalType(aChannel, aElementHidden); - MOZ_ASSERT(mChannelCounters[type].Contains(aChildID)); - mChannelCounters[type].RemoveElement(aChildID); - - // In order to avoid race conditions, it's safer to notify any existing - // agent any time a new one is registered. - if (XRE_IsParentProcess()) { - // No hidden content channel is playable if the original playable hidden - // process does not need to play audio from background anymore. - if (aChannel == AudioChannel::Content && - mPlayableHiddenContentChildID == aChildID && - !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Contains(aChildID)) { - mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN; - } - - if (aWithVideo) { - MOZ_ASSERT(mWithVideoChildIDs.Contains(aChildID)); - mWithVideoChildIDs.RemoveElement(aChildID); - } - - SendAudioChannelChangedNotification(aChildID); - SendNotification(); - } -} - -void -AudioChannelService::UpdateChannelType(AudioChannel aChannel, - uint64_t aChildID, - bool aElementHidden, - bool aElementWasHidden) -{ - // Calculate the new and old internal type and update the hashtable if needed. - AudioChannelInternalType newType = GetInternalType(aChannel, aElementHidden); - AudioChannelInternalType oldType = GetInternalType(aChannel, aElementWasHidden); - - if (newType != oldType) { - mChannelCounters[newType].AppendElement(aChildID); - MOZ_ASSERT(mChannelCounters[oldType].Contains(aChildID)); - mChannelCounters[oldType].RemoveElement(aChildID); - } - - // No hidden content channel can be playable if there is a content channel - // in foreground (bug 855208), nor if there is a normal channel with video - // in foreground (bug 894249). - if (newType == AUDIO_CHANNEL_INT_CONTENT || - (newType == AUDIO_CHANNEL_INT_NORMAL && - mWithVideoChildIDs.Contains(aChildID))) { - mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN; - } - // If there is no content channel in foreground and no normal channel with - // video in foreground, the last content channel which goes from foreground - // to background can be playable. - else if (oldType == AUDIO_CHANNEL_INT_CONTENT && - newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN && - mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) { - mPlayableHiddenContentChildID = aChildID; - } -} - -AudioChannelState -AudioChannelService::GetState(AudioChannelAgent* aAgent, bool aElementHidden) -{ - AudioChannelAgentData* data; - if (!mAgents.Get(aAgent, &data)) { - return AUDIO_CHANNEL_STATE_MUTED; - } - - bool oldElementHidden = data->mElementHidden; - // Update visibility. - data->mElementHidden = aElementHidden; - - data->mState = GetStateInternal(data->mChannel, CONTENT_PROCESS_ID_MAIN, - aElementHidden, oldElementHidden); - #ifdef MOZ_WIDGET_GONK - /** Only modify the speaker status when - * (1) apps in the foreground. - * (2) apps in the backgrund and inactive. - * Notice : check the state when the visible status is stable, because there - * has lantency in passing the visibility events. - **/ - bool active = AnyAudioChannelIsActive(); - if (aElementHidden == oldElementHidden && - (!aElementHidden || (aElementHidden && !active))) { - for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) { - mSpeakerManager[i]->SetAudioChannelActive(active); - } - } - #endif - - return data->mState; -} - -AudioChannelState -AudioChannelService::GetStateInternal(AudioChannel aChannel, uint64_t aChildID, - bool aElementHidden, - bool aElementWasHidden) -{ - UpdateChannelType(aChannel, aChildID, aElementHidden, aElementWasHidden); - - // Calculating the new and old type and update the hashtable if needed. - AudioChannelInternalType newType = GetInternalType(aChannel, aElementHidden); - AudioChannelInternalType oldType = GetInternalType(aChannel, - aElementWasHidden); - - if (newType != oldType && - (aChannel == AudioChannel::Content || - (aChannel == AudioChannel::Normal && - mWithVideoChildIDs.Contains(aChildID)))) { - SendNotification(); - } - - SendAudioChannelChangedNotification(aChildID); - - // Let play any visible audio channel. - if (!aElementHidden) { - if (CheckVolumeFadedCondition(newType, aElementHidden)) { - return AUDIO_CHANNEL_STATE_FADED; - } - return CheckTelephonyPolicy(aChannel, aChildID); - } - - // We are not visible, maybe we have to mute. - if (newType == AUDIO_CHANNEL_INT_NORMAL_HIDDEN || - (newType == AUDIO_CHANNEL_INT_CONTENT_HIDDEN && - // One process can have multiple content channels; and during the - // transition from foreground to background, its content channels will be - // updated with correct visibility status one by one. All its content - // channels should remain playable until all of their visibility statuses - // have been updated as hidden. After all its content channels have been - // updated properly as hidden, mPlayableHiddenContentChildID is used to - // check whether this background process is playable or not. - !(mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID) || - (mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty() && - mPlayableHiddenContentChildID == aChildID)))) { - return AUDIO_CHANNEL_STATE_MUTED; - } - - // After checking the condition on normal & content channel, if the state - // is not on muted then checking other higher channels type here. - if (ChannelsActiveWithHigherPriorityThan(newType)) { - MOZ_ASSERT(newType != AUDIO_CHANNEL_INT_NORMAL_HIDDEN); - if (CheckVolumeFadedCondition(newType, aElementHidden)) { - return AUDIO_CHANNEL_STATE_FADED; - } - return AUDIO_CHANNEL_STATE_MUTED; - } - - return CheckTelephonyPolicy(aChannel, aChildID); -} - -AudioChannelState -AudioChannelService::CheckTelephonyPolicy(AudioChannel aChannel, - uint64_t aChildID) -{ - // Only the latest childID is allowed to play with telephony channel. - if (aChannel != AudioChannel::Telephony) { - return AUDIO_CHANNEL_STATE_NORMAL; - } - - MOZ_ASSERT(!mTelephonyChildren.IsEmpty()); - -#if DEBUG - bool found = false; - for (uint32_t i = 0, len = mTelephonyChildren.Length(); i < len; ++i) { - if (mTelephonyChildren[i].mChildID == aChildID) { - found = true; + nsCOMPtr win; + window->GetScriptableParent(getter_AddRefs(win)); + if (window == win) { break; } - } - MOZ_ASSERT(found); -#endif + window = do_QueryInterface(win); - return mTelephonyChildren.LastElement().mChildID == aChildID - ? AUDIO_CHANNEL_STATE_NORMAL : AUDIO_CHANNEL_STATE_MUTED; + // If there is no parent, or we are the toplevel we don't continue. + } while (window && window != aWindow); } -bool -AudioChannelService::CheckVolumeFadedCondition(AudioChannelInternalType aType, - bool aElementHidden) +PLDHashOperator +AudioChannelService::TelephonyChannelIsActiveEnumerator( + const uint64_t& aWindowID, + nsAutoPtr& aWinData, + void* aPtr) { - // Only normal & content channels are considered - if (aType > AUDIO_CHANNEL_INT_CONTENT_HIDDEN) { - return false; - } - - // Consider that audio from notification is with short duration - // so just fade the volume not pause it - if (mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION].IsEmpty() && - mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN].IsEmpty()) { - return false; - } - - // Since this element is on the foreground, it can be allowed to play always. - // So return true directly when there is any notification channel alive. - if (aElementHidden == false) { - return true; - } - - // If element is on the background, it is possible paused by channels higher - // then notification. - for (int i = AUDIO_CHANNEL_INT_LAST - 1; - i != AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN; --i) { - if (!mChannelCounters[i].IsEmpty()) { - return false; - } - } - - return true; -} - -bool -AudioChannelService::ContentOrNormalChannelIsActive() -{ - return !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty() || - !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].IsEmpty() || - !mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].IsEmpty(); + bool* isActive = static_cast(aPtr); + *isActive = + aWinData->mChannels[(uint32_t)AudioChannel::Telephony].mNumberOfAgents != 0 && + !aWinData->mChannels[(uint32_t)AudioChannel::Telephony].mMuted; + return *isActive ? PL_DHASH_STOP : PL_DHASH_NEXT; } bool AudioChannelService::TelephonyChannelIsActive() { - return !mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].IsEmpty() || - !mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN].IsEmpty(); + // TODO: no child process check. + + bool active = false; + mWindows.Enumerate(TelephonyChannelIsActiveEnumerator, &active); + return active; } bool AudioChannelService::ProcessContentOrNormalChannelIsActive(uint64_t aChildID) { - return mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID) || - mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Contains(aChildID) || - mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].Contains(aChildID); +/* TODO + AudioChannelChildData* data; + if (!mData.Get(aChildID, &data)) { + return false; + } + + return data->mChannels[(uint32_t)AudioChannel::Content].mNumberOfAgents != 0 || + data->mChannels[(uint32_t)AudioChannel::Normal].mNumberOfAgents != 0; +*/ + return true; } -void -AudioChannelService::SetDefaultVolumeControlChannel(int32_t aChannel, - bool aVisible) +PLDHashOperator +AudioChannelService::AnyAudioChannelIsActiveEnumerator( + const uint64_t& aWindowID, + nsAutoPtr& aWinData, + void* aPtr) { - SetDefaultVolumeControlChannelInternal(aChannel, aVisible, - CONTENT_PROCESS_ID_MAIN); -} - -void -AudioChannelService::SetDefaultVolumeControlChannelInternal(int32_t aChannel, - bool aVisible, - uint64_t aChildID) -{ - if (!XRE_IsParentProcess()) { - return; - } - - // If this child is in the background and mDefChannelChildID is set to - // others then it means other child in the foreground already set it's - // own default channel already. - if (!aVisible && mDefChannelChildID != aChildID) { - return; - } - // Workaround for the call screen app. The call screen app is running on the - // main process, that will results in wrong visible state. Because we use the - // docshell's active state as visible state, the main process is always - // active. Therefore, we will see the strange situation that the visible - // state of the call screen is always true. If the mDefChannelChildID is set - // to others then it means other child in the foreground already set it's - // own default channel already. - // Summary : - // Child process : foreground app always can set type. - // Parent process : check the mDefChannelChildID. - else if (aChildID == CONTENT_PROCESS_ID_MAIN && - mDefChannelChildID != CONTENT_PROCESS_ID_UNKNOWN) { - return; - } - - mDefChannelChildID = aVisible ? aChildID : CONTENT_PROCESS_ID_UNKNOWN; - nsAutoString channelName; - if (aChannel == -1) { - channelName.AssignASCII("unknown"); - } else { - GetAudioChannelString(static_cast(aChannel), channelName); - } - - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->NotifyObservers(nullptr, "default-volume-channel-changed", - channelName.get()); - } -} - -void -AudioChannelService::SendAudioChannelChangedNotification(uint64_t aChildID) -{ - if (!XRE_IsParentProcess()) { - return; - } - - nsRefPtr props = new nsHashPropertyBag(); - props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), aChildID); - - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->NotifyObservers(static_cast(props), - "audio-channel-process-changed", nullptr); - } - - // Calculating the most important active channel. - int32_t higher = -1; - - // Top-Down in the hierarchy for visible elements - if (!mChannelCounters[AUDIO_CHANNEL_INT_PUBLICNOTIFICATION].IsEmpty()) { - higher = static_cast(AudioChannel::Publicnotification); - } - - else if (!mChannelCounters[AUDIO_CHANNEL_INT_RINGER].IsEmpty()) { - higher = static_cast(AudioChannel::Ringer); - } - - else if (!mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].IsEmpty()) { - higher = static_cast(AudioChannel::Telephony); - } - - else if (!mChannelCounters[AUDIO_CHANNEL_INT_ALARM].IsEmpty()) { - higher = static_cast(AudioChannel::Alarm); - } - - else if (!mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION].IsEmpty()) { - higher = static_cast(AudioChannel::Notification); - } - - else if (!mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) { - higher = static_cast(AudioChannel::Content); - } - - else if (!mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].IsEmpty()) { - higher = static_cast(AudioChannel::Normal); - } - - int32_t visibleHigher = higher; - - // Top-Down in the hierarchy for non-visible elements - // And we can ignore normal channel because it can't play in the background. - int32_t index; - for (index = 0; kMozAudioChannelAttributeTable[index].tag; ++index); - - for (--index; - kMozAudioChannelAttributeTable[index].value > higher && - kMozAudioChannelAttributeTable[index].value > (int16_t)AudioChannel::Normal; - --index) { - // Each channel type will be split to fg and bg for recording the state, - // so here need to do a translation. - if (mChannelCounters[index * 2 + 1].IsEmpty()) { - continue; - } - - if (kMozAudioChannelAttributeTable[index].value == (int16_t)AudioChannel::Content) { - if (mPlayableHiddenContentChildID != CONTENT_PROCESS_ID_UNKNOWN) { - higher = kMozAudioChannelAttributeTable[index].value; - break; - } - } else { - higher = kMozAudioChannelAttributeTable[index].value; + bool* isActive = static_cast(aPtr); + for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) { + if (aWinData->mChannels[kMozAudioChannelAttributeTable[i].value].mNumberOfAgents + != 0) { + *isActive = true; break; } } - if (higher != mCurrentHigherChannel) { - mCurrentHigherChannel = higher; - - nsString channelName; - if (mCurrentHigherChannel != -1) { - GetAudioChannelString(static_cast(mCurrentHigherChannel), - channelName); - } else { - channelName.AssignLiteral("none"); - } - - if (obs) { - obs->NotifyObservers(nullptr, "audio-channel-changed", channelName.get()); - } - } - - if (visibleHigher != mCurrentVisibleHigherChannel) { - mCurrentVisibleHigherChannel = visibleHigher; - - nsString channelName; - if (mCurrentVisibleHigherChannel != -1) { - GetAudioChannelString(static_cast(mCurrentVisibleHigherChannel), - channelName); - } else { - channelName.AssignLiteral("none"); - } - - if (obs) { - obs->NotifyObservers(nullptr, "visible-audio-channel-changed", channelName.get()); - } - } -} - -PLDHashOperator -AudioChannelService::NotifyEnumerator(AudioChannelAgent* aAgent, - AudioChannelAgentData* aData, void* aUnused) -{ - MOZ_ASSERT(aAgent); - aAgent->NotifyAudioChannelStateChanged(); - return PL_DHASH_NEXT; -} - -class NotifyRunnable : public nsRunnable -{ -public: - explicit NotifyRunnable(AudioChannelService* aService) - : mService(aService) - {} - - NS_IMETHOD Run() - { - mService->Notify(); - return NS_OK; - } - -private: - nsRefPtr mService; -}; - -void -AudioChannelService::SendNotification() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (mRunnable) { - return; - } - - mRunnable = new NotifyRunnable(this); - NS_DispatchToCurrentThread(mRunnable); -} - -void -AudioChannelService::Notify() -{ - MOZ_ASSERT(NS_IsMainThread()); - mRunnable = nullptr; - - // Notify any agent for the main process. - mAgents.EnumerateRead(NotifyEnumerator, nullptr); - - // Notify for the child processes. - nsTArray children; - ContentParent::GetAll(children); - for (uint32_t i = 0; i < children.Length(); i++) { - unused << children[i]->SendAudioChannelNotify(); - } -} - -NS_IMETHODIMP -AudioChannelService::Notify(nsITimer* aTimer) -{ - UnregisterTypeInternal(AudioChannel::Telephony, mTimerElementHidden, - mTimerChildID, false); - mDeferTelChannelTimer = nullptr; - return NS_OK; + return *isActive ? PL_DHASH_STOP : PL_DHASH_NEXT; } bool AudioChannelService::AnyAudioChannelIsActive() { - for (int i = AUDIO_CHANNEL_INT_LAST - 1; - i >= AUDIO_CHANNEL_INT_NORMAL; --i) { - if (!mChannelCounters[i].IsEmpty()) { - return true; - } - } - - return false; -} - -bool -AudioChannelService::ChannelsActiveWithHigherPriorityThan( - AudioChannelInternalType aType) -{ - for (int i = AUDIO_CHANNEL_INT_LAST - 1; - i != AUDIO_CHANNEL_INT_CONTENT_HIDDEN; --i) { - if (i == aType) { - return false; - } - - if (!mChannelCounters[i].IsEmpty()) { - return true; - } - } - - return false; -} - -PLDHashOperator -AudioChannelService::WindowDestroyedEnumerator(AudioChannelAgent* aAgent, - nsAutoPtr& aData, - void* aPtr) -{ - auto* data = static_cast(aPtr); - MOZ_ASSERT(data); - - nsCOMPtr window = do_QueryInterface(aAgent->Window()); - if (window && !window->IsInnerWindow()) { - window = window->GetCurrentInnerWindow(); - } - - if (!window || window->WindowID() != data->mInnerID) { - return PL_DHASH_NEXT; - } - - AudioChannelService* service = AudioChannelService::GetAudioChannelService(); - MOZ_ASSERT(service); - - service->UnregisterType(aData->mChannel, aData->mElementHidden, - CONTENT_PROCESS_ID_MAIN, aData->mWithVideo); - data->mAgents.AppendElement(aAgent); - - return PL_DHASH_REMOVE; + // TODO: no child process check. + bool active = false; + mWindows.Enumerate(AnyAudioChannelIsActiveEnumerator, &active); + return active; } NS_IMETHODIMP -AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) +AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { if (!strcmp(aTopic, "xpcom-shutdown")) { mDisabled = true; - } - - if (!strcmp(aTopic, "ipc:content-shutdown")) { - nsCOMPtr props = do_QueryInterface(aSubject); - if (!props) { - NS_WARNING("ipc:content-shutdown message without property bag as subject"); - return NS_OK; - } - - int32_t index; - uint64_t childID = 0; - nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), - &childID); - if (NS_SUCCEEDED(rv)) { - for (int32_t type = AUDIO_CHANNEL_INT_NORMAL; - type < AUDIO_CHANNEL_INT_LAST; - ++type) { - - while ((index = mChannelCounters[type].IndexOf(childID)) != -1) { - mChannelCounters[type].RemoveElementAt(index); - } - } - - // No hidden content channel is playable if the original playable hidden - // process shuts down. - if (mPlayableHiddenContentChildID == childID) { - mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN; - } - - while ((index = mWithVideoChildIDs.IndexOf(childID)) != -1) { - mWithVideoChildIDs.RemoveElementAt(index); - } - - // We don't have to remove the agents from the mAgents hashtable because if - // that table contains only agents running on the same process. - - SendAudioChannelChangedNotification(childID); - SendNotification(); - - if (mDefChannelChildID == childID) { - SetDefaultVolumeControlChannelInternal(-1, false, childID); - mDefChannelChildID = CONTENT_PROCESS_ID_UNKNOWN; - } - } else { - NS_WARNING("ipc:content-shutdown message without childID property"); - } + mWindows.Clear(); } #ifdef MOZ_WIDGET_GONK @@ -920,10 +409,10 @@ AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic, const ch return rv; } - WindowDestroyedEnumeratorData data(innerID); - mAgents.Enumerate(WindowDestroyedEnumerator, &data); - for (uint32_t i = 0, len = data.mAgents.Length(); i < len; ++i) { - data.mAgents[i]->NotifyAudioChannelStateChanged(); + nsAutoPtr window; + mWindows.RemoveAndForget(innerID, window); + if (window) { + window->mAgents.EnumerateRead(NotifyEnumerator, nullptr); } #ifdef MOZ_WIDGET_GONK @@ -934,54 +423,27 @@ AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic, const ch #endif } - return NS_OK; -} + else if (!strcmp(aTopic, "ipc:content-shutdown")) { + nsCOMPtr props = do_QueryInterface(aSubject); + if (!props) { + NS_WARNING("ipc:content-shutdown message without property bag as subject"); + return NS_OK; + } -AudioChannelService::AudioChannelInternalType -AudioChannelService::GetInternalType(AudioChannel aChannel, - bool aElementHidden) -{ - switch (aChannel) { - case AudioChannel::Normal: - return aElementHidden - ? AUDIO_CHANNEL_INT_NORMAL_HIDDEN - : AUDIO_CHANNEL_INT_NORMAL; + uint64_t childID = 0; + nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), + &childID); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } - case AudioChannel::Content: - return aElementHidden - ? AUDIO_CHANNEL_INT_CONTENT_HIDDEN - : AUDIO_CHANNEL_INT_CONTENT; - - case AudioChannel::Notification: - return aElementHidden - ? AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN - : AUDIO_CHANNEL_INT_NOTIFICATION; - - case AudioChannel::Alarm: - return aElementHidden - ? AUDIO_CHANNEL_INT_ALARM_HIDDEN - : AUDIO_CHANNEL_INT_ALARM; - - case AudioChannel::Telephony: - return aElementHidden - ? AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN - : AUDIO_CHANNEL_INT_TELEPHONY; - - case AudioChannel::Ringer: - return aElementHidden - ? AUDIO_CHANNEL_INT_RINGER_HIDDEN - : AUDIO_CHANNEL_INT_RINGER; - - case AudioChannel::Publicnotification: - return aElementHidden - ? AUDIO_CHANNEL_INT_PUBLICNOTIFICATION_HIDDEN - : AUDIO_CHANNEL_INT_PUBLICNOTIFICATION; - - default: - break; + if (mDefChannelChildID == childID) { + SetDefaultVolumeControlChannelInternal(-1, false, childID); + mDefChannelChildID = CONTENT_PROCESS_ID_UNKNOWN; + } } - MOZ_CRASH("unexpected audio channel"); + return NS_OK; } struct RefreshAgentsVolumeData @@ -995,68 +457,24 @@ struct RefreshAgentsVolumeData }; PLDHashOperator -AudioChannelService::RefreshAgentsVolumeEnumerator(AudioChannelAgent* aAgent, - AudioChannelAgentData* aUnused, - void* aPtr) +AudioChannelService::RefreshAgentsVolumeEnumerator( + AudioChannelAgent* aAgent, + AudioChannel* aUnused, + void* aPtr) { MOZ_ASSERT(aAgent); - RefreshAgentsVolumeData* data = static_cast(aPtr); - MOZ_ASSERT(data); - - nsCOMPtr window = do_QueryInterface(aAgent->Window()); - if (window && !window->IsInnerWindow()) { - window = window->GetCurrentInnerWindow(); - } - - if (window == data->mWindow) { - data->mAgents.AppendElement(aAgent); - } - + aAgent->WindowVolumeChanged(); return PL_DHASH_NEXT; } void AudioChannelService::RefreshAgentsVolume(nsPIDOMWindow* aWindow) { - RefreshAgentsVolumeData data(aWindow); - mAgents.EnumerateRead(RefreshAgentsVolumeEnumerator, &data); - - for (uint32_t i = 0; i < data.mAgents.Length(); ++i) { - data.mAgents[i]->WindowVolumeChanged(); - } -} - -struct CountWindowData -{ - explicit CountWindowData(nsIDOMWindow* aWindow) - : mWindow(aWindow) - , mCount(0) - {} - - nsIDOMWindow* mWindow; - uint32_t mCount; -}; - -PLDHashOperator -AudioChannelService::CountWindowEnumerator(AudioChannelAgent* aAgent, - AudioChannelAgentData* aUnused, - void* aPtr) -{ - CountWindowData* data = static_cast(aPtr); - MOZ_ASSERT(aAgent); - - if (aAgent->Window() == data->mWindow) { - ++data->mCount; + AudioChannelWindow* winData = mWindows.Get(aWindow->WindowID()); + if (!winData) { + return; } - return PL_DHASH_NEXT; -} - -uint32_t -AudioChannelService::CountWindow(nsIDOMWindow* aWindow) -{ - CountWindowData data(aWindow); - mAgents.EnumerateRead(CountWindowEnumerator, &data); - return data.mCount; + winData->mAgents.EnumerateRead(RefreshAgentsVolumeEnumerator, nullptr); } /* static */ const nsAttrValue::EnumTable* @@ -1080,7 +498,7 @@ AudioChannelService::GetAudioChannel(const nsAString& aChannel) /* static */ AudioChannel AudioChannelService::GetDefaultAudioChannel() { - nsString audioChannel = Preferences::GetString("media.defaultAudioChannel"); + nsAutoString audioChannel(Preferences::GetString("media.defaultAudioChannel")); if (audioChannel.IsEmpty()) { return AudioChannel::Normal; } @@ -1114,7 +532,7 @@ AudioChannelService::GetDefaultAudioChannelString(nsAString& aString) { aString.AssignASCII("normal"); - nsString audioChannel = Preferences::GetString("media.defaultAudioChannel"); + nsAutoString audioChannel(Preferences::GetString("media.defaultAudioChannel")); if (!audioChannel.IsEmpty()) { for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) { if (audioChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].tag)) { @@ -1125,38 +543,196 @@ AudioChannelService::GetDefaultAudioChannelString(nsAString& aString) } } -void -AudioChannelService::RegisterTelephonyChild(uint64_t aChildID) +AudioChannelService::AudioChannelWindow& +AudioChannelService::GetOrCreateWindowData(nsPIDOMWindow* aWindow) { - for (uint32_t i = 0, len = mTelephonyChildren.Length(); i < len; ++i) { - if (mTelephonyChildren[i].mChildID == aChildID) { - ++mTelephonyChildren[i].mInstances; + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aWindow->IsOuterWindow()); - if (i != len - 1) { - TelephonyChild child = mTelephonyChildren[i]; - mTelephonyChildren.RemoveElementAt(i); - mTelephonyChildren.AppendElement(child); - } + AudioChannelWindow* winData = mWindows.LookupOrAdd(aWindow->WindowID()); + return *winData; +} - return; - } - } +float +AudioChannelService::GetAudioChannelVolume(nsPIDOMWindow* aWindow, + AudioChannel aAudioChannel) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aWindow->IsOuterWindow()); - mTelephonyChildren.AppendElement(TelephonyChild(aChildID)); + AudioChannelWindow& winData = GetOrCreateWindowData(aWindow); + return winData.mChannels[(uint32_t)aAudioChannel].mVolume; +} + +NS_IMETHODIMP +AudioChannelService::GetAudioChannelVolume(nsIDOMWindow* aWindow, + unsigned short aAudioChannel, + float* aVolume) +{ + nsCOMPtr window = GetTopWindow(aWindow); + *aVolume = GetAudioChannelVolume(window, (AudioChannel)aAudioChannel); + return NS_OK; } void -AudioChannelService::UnregisterTelephonyChild(uint64_t aChildID) +AudioChannelService::SetAudioChannelVolume(nsPIDOMWindow* aWindow, + AudioChannel aAudioChannel, + float aVolume) { - for (uint32_t i = 0, len = mTelephonyChildren.Length(); i < len; ++i) { - if (mTelephonyChildren[i].mChildID == aChildID) { - if (!--mTelephonyChildren[i].mInstances) { - mTelephonyChildren.RemoveElementAt(i); - } + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aWindow->IsOuterWindow()); - return; + AudioChannelWindow& winData = GetOrCreateWindowData(aWindow); + winData.mChannels[(uint32_t)aAudioChannel].mVolume = aVolume; + RefreshAgentsVolume(aWindow); +} + +NS_IMETHODIMP +AudioChannelService::SetAudioChannelVolume(nsIDOMWindow* aWindow, + unsigned short aAudioChannel, + float aVolume) +{ + nsCOMPtr window = GetTopWindow(aWindow); + SetAudioChannelVolume(window, (AudioChannel)aAudioChannel, aVolume); + return NS_OK; +} + +bool +AudioChannelService::GetAudioChannelMuted(nsPIDOMWindow* aWindow, + AudioChannel aAudioChannel) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aWindow->IsOuterWindow()); + + AudioChannelWindow& winData = GetOrCreateWindowData(aWindow); + return winData.mChannels[(uint32_t)aAudioChannel].mMuted; +} + +NS_IMETHODIMP +AudioChannelService::GetAudioChannelMuted(nsIDOMWindow* aWindow, + unsigned short aAudioChannel, + bool* aMuted) +{ + nsCOMPtr window = GetTopWindow(aWindow); + *aMuted = GetAudioChannelMuted(window, (AudioChannel)aAudioChannel); + return NS_OK; +} + +void +AudioChannelService::SetAudioChannelMuted(nsPIDOMWindow* aWindow, + AudioChannel aAudioChannel, + bool aMuted) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aWindow->IsOuterWindow()); + + AudioChannelWindow& winData = GetOrCreateWindowData(aWindow); + winData.mChannels[(uint32_t)aAudioChannel].mMuted = aMuted; + RefreshAgentsVolume(aWindow); +} + +NS_IMETHODIMP +AudioChannelService::SetAudioChannelMuted(nsIDOMWindow* aWindow, + unsigned short aAudioChannel, + bool aMuted) +{ + nsCOMPtr window = GetTopWindow(aWindow); + SetAudioChannelMuted(window, (AudioChannel)aAudioChannel, aMuted); + return NS_OK; +} + +bool +AudioChannelService::IsAudioChannelActive(nsPIDOMWindow* aWindow, + AudioChannel aAudioChannel) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aWindow->IsOuterWindow()); + + AudioChannelWindow& winData = GetOrCreateWindowData(aWindow); + return !!winData.mChannels[(uint32_t)aAudioChannel].mNumberOfAgents; +} + +NS_IMETHODIMP +AudioChannelService::IsAudioChannelActive(nsIDOMWindow* aWindow, + unsigned short aAudioChannel, + bool* aActive) +{ + nsCOMPtr window = GetTopWindow(aWindow); + *aActive = IsAudioChannelActive(window, (AudioChannel)aAudioChannel); + return NS_OK; +} +void +AudioChannelService::SetDefaultVolumeControlChannel(int32_t aChannel, + bool aVisible) +{ + SetDefaultVolumeControlChannelInternal(aChannel, aVisible, + CONTENT_PROCESS_ID_MAIN); +} + +void +AudioChannelService::SetDefaultVolumeControlChannelInternal(int32_t aChannel, + bool aVisible, + uint64_t aChildID) +{ + if (XRE_GetProcessType() != GeckoProcessType_Default) { + ContentChild* cc = ContentChild::GetSingleton(); + if (cc) { + cc->SendAudioChannelChangeDefVolChannel(aChannel, aVisible); } + + return; } - MOZ_ASSERT(false, "This should not happen."); + // If this child is in the background and mDefChannelChildID is set to + // others then it means other child in the foreground already set it's + // own default channel. + if (!aVisible && mDefChannelChildID != aChildID) { + return; + } + + // Workaround for the call screen app. The call screen app is running on the + // main process, that will results in wrong visible state. Because we use the + // docshell's active state as visible state, the main process is always + // active. Therefore, we will see the strange situation that the visible + // state of the call screen is always true. If the mDefChannelChildID is set + // to others then it means other child in the foreground already set it's + // own default channel already. + // Summary : + // Child process : foreground app always can set type. + // Parent process : check the mDefChannelChildID. + else if (aChildID == CONTENT_PROCESS_ID_MAIN && + mDefChannelChildID != CONTENT_PROCESS_ID_UNKNOWN) { + return; + } + + mDefChannelChildID = aVisible ? aChildID : CONTENT_PROCESS_ID_UNKNOWN; + nsAutoString channelName; + + if (aChannel == -1) { + channelName.AssignASCII("unknown"); + } else { + GetAudioChannelString(static_cast(aChannel), channelName); + } + + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + obs->NotifyObservers(nullptr, "default-volume-channel-changed", + channelName.get()); + } } + +/* static */ PLDHashOperator +AudioChannelService::NotifyEnumerator(AudioChannelAgent* aAgent, + AudioChannel* aAudioChannel, + void* aUnused) +{ + aAgent->WindowVolumeChanged(); + return PL_DHASH_NEXT; +} + diff --git a/dom/audiochannel/AudioChannelService.h b/dom/audiochannel/AudioChannelService.h index 8e20c92b53e..2ff391a9365 100644 --- a/dom/audiochannel/AudioChannelService.h +++ b/dom/audiochannel/AudioChannelService.h @@ -7,12 +7,11 @@ #ifndef mozilla_dom_audiochannelservice_h__ #define mozilla_dom_audiochannelservice_h__ +#include "nsIAudioChannelService.h" #include "nsAutoPtr.h" #include "nsIObserver.h" #include "nsTArray.h" -#include "nsITimer.h" -#include "AudioChannelCommon.h" #include "AudioChannelAgent.h" #include "nsAttrValue.h" #include "nsClassHashtable.h" @@ -26,27 +25,23 @@ namespace dom { #ifdef MOZ_WIDGET_GONK class SpeakerManagerService; #endif -class AudioChannelService -: public nsIObserver -, public nsITimerCallback + +#define NUMBER_OF_AUDIO_CHANNELS (uint32_t)AudioChannel::Publicnotification + 1 + +class AudioChannelService final : public nsIAudioChannelService + , public nsIObserver { public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER - NS_DECL_NSITIMERCALLBACK - - /** - * Returns the AudioChannelServce singleton or null if the process havn't create it before. - * Only to be called from main thread. - */ - static AudioChannelService* GetAudioChannelService(); + NS_DECL_NSIAUDIOCHANNELSERVICE /** * Returns the AudioChannelServce singleton. * If AudioChannelServce is not exist, create and return new one. * Only to be called from main thread. */ - static AudioChannelService* GetOrCreateAudioChannelService(); + static already_AddRefed GetOrCreate(); /** * Shutdown the singleton. @@ -57,40 +52,45 @@ public: * Any audio channel agent that starts playing should register itself to * this service, sharing the AudioChannel. */ - virtual void RegisterAudioChannelAgent(AudioChannelAgent* aAgent, - AudioChannel aChannel, - bool aWithVideo); + void RegisterAudioChannelAgent(AudioChannelAgent* aAgent, AudioChannel aChannel); /** * Any audio channel agent that stops playing should unregister itself to * this service. */ - virtual void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent); + void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent); /** - * Return the state to indicate this agent should keep playing/ - * fading volume/muted. + * Return the state to indicate this audioChannel for his window should keep + * playing/muted. */ - virtual AudioChannelState GetState(AudioChannelAgent* aAgent, - bool aElementHidden); + void GetState(nsPIDOMWindow* aWindow, uint32_t aChannel, + float* aVolume, bool* aMuted); - /** - * Return true if there is a content channel active in this process - * or one of its subprocesses. - */ - virtual bool ContentOrNormalChannelIsActive(); + /* Methods for the BrowserElementAudioChannel */ + float GetAudioChannelVolume(nsPIDOMWindow* aWindow, AudioChannel aChannel); + + void SetAudioChannelVolume(nsPIDOMWindow* aWindow, AudioChannel aChannel, + float aVolume); + + bool GetAudioChannelMuted(nsPIDOMWindow* aWindow, AudioChannel aChannel); + + void SetAudioChannelMuted(nsPIDOMWindow* aWindow, AudioChannel aChannel, + bool aMuted); + + bool IsAudioChannelActive(nsPIDOMWindow* aWindow, AudioChannel aChannel); /** * Return true if there is a telephony channel active in this process * or one of its subprocesses. */ - virtual bool TelephonyChannelIsActive(); + bool TelephonyChannelIsActive(); /** * Return true if a normal or content channel is active for the given * process ID. */ - virtual bool ProcessContentOrNormalChannelIsActive(uint64_t aChildID); + bool ProcessContentOrNormalChannelIsActive(uint64_t aChildID); /*** * AudioChannelManager calls this function to notify the default channel used @@ -125,161 +125,69 @@ public: static void GetAudioChannelString(AudioChannel aChannel, nsAString& aString); static void GetDefaultAudioChannelString(nsAString& aString); - void Notify(); + void Notify(uint64_t aWindowID); -protected: - void SendNotification(); - - /** - * Send the audio-channel-changed notification for the given process ID if - * needed. - */ - void SendAudioChannelChangedNotification(uint64_t aChildID); - - /* Register/Unregister IPC types: */ - void RegisterType(AudioChannel aChannel, uint64_t aChildID, bool aWithVideo); - void UnregisterType(AudioChannel aChannel, bool aElementHidden, - uint64_t aChildID, bool aWithVideo); - void UnregisterTypeInternal(AudioChannel aChannel, bool aElementHidden, - uint64_t aChildID, bool aWithVideo); - - AudioChannelState GetStateInternal(AudioChannel aChannel, uint64_t aChildID, - bool aElementHidden, - bool aElementWasHidden); - - /* Update the internal type value following the visibility changes */ - void UpdateChannelType(AudioChannel aChannel, uint64_t aChildID, - bool aElementHidden, bool aElementWasHidden); +private: + AudioChannelService(); + ~AudioChannelService(); /* Send the default-volume-channel-changed notification */ void SetDefaultVolumeControlChannelInternal(int32_t aChannel, bool aVisible, uint64_t aChildID); - AudioChannelState CheckTelephonyPolicy(AudioChannel aChannel, - uint64_t aChildID); - void RegisterTelephonyChild(uint64_t aChildID); - void UnregisterTelephonyChild(uint64_t aChildID); - - AudioChannelService(); - virtual ~AudioChannelService(); - - enum AudioChannelInternalType { - AUDIO_CHANNEL_INT_NORMAL = 0, - AUDIO_CHANNEL_INT_NORMAL_HIDDEN, - AUDIO_CHANNEL_INT_CONTENT, - AUDIO_CHANNEL_INT_CONTENT_HIDDEN, - AUDIO_CHANNEL_INT_NOTIFICATION, - AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN, - AUDIO_CHANNEL_INT_ALARM, - AUDIO_CHANNEL_INT_ALARM_HIDDEN, - AUDIO_CHANNEL_INT_TELEPHONY, - AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN, - AUDIO_CHANNEL_INT_RINGER, - AUDIO_CHANNEL_INT_RINGER_HIDDEN, - AUDIO_CHANNEL_INT_PUBLICNOTIFICATION, - AUDIO_CHANNEL_INT_PUBLICNOTIFICATION_HIDDEN, - AUDIO_CHANNEL_INT_LAST - }; - - bool ChannelsActiveWithHigherPriorityThan(AudioChannelInternalType aType); - - bool CheckVolumeFadedCondition(AudioChannelInternalType aType, - bool aElementHidden); - - AudioChannelInternalType GetInternalType(AudioChannel aChannel, - bool aElementHidden); - - class AudioChannelAgentData { - public: - AudioChannelAgentData(AudioChannel aChannel, - bool aElementHidden, - AudioChannelState aState, - bool aWithVideo) - : mChannel(aChannel) - , mElementHidden(aElementHidden) - , mState(aState) - , mWithVideo(aWithVideo) + struct AudioChannelConfig final + { + AudioChannelConfig() + : mVolume(1.0) + , mMuted(false) + , mNumberOfAgents(0) {} - AudioChannel mChannel; - bool mElementHidden; - AudioChannelState mState; - const bool mWithVideo; + float mVolume; + bool mMuted; + + uint32_t mNumberOfAgents; }; + struct AudioChannelWindow final + { + AudioChannelConfig mChannels[NUMBER_OF_AUDIO_CHANNELS]; + nsClassHashtable, AudioChannel> mAgents; + }; + + AudioChannelWindow& + GetOrCreateWindowData(nsPIDOMWindow* aWindow); + static PLDHashOperator - NotifyEnumerator(AudioChannelAgent* aAgent, - AudioChannelAgentData* aData, void *aUnused); + TelephonyChannelIsActiveEnumerator(const uint64_t& aWindowID, + nsAutoPtr& aWinData, + void *aPtr); + + static PLDHashOperator + AnyAudioChannelIsActiveEnumerator(const uint64_t& aWindowID, + nsAutoPtr& aWinData, + void *aPtr); static PLDHashOperator RefreshAgentsVolumeEnumerator(AudioChannelAgent* aAgent, - AudioChannelAgentData* aUnused, + AudioChannel* aUnused, void *aPtr); static PLDHashOperator - CountWindowEnumerator(AudioChannelAgent* aAgent, - AudioChannelAgentData* aUnused, - void *aPtr); + NotifyEnumerator(AudioChannelAgent* aAgent, + AudioChannel* aAudioChannel, + void* aUnused); - static PLDHashOperator - WindowDestroyedEnumerator(AudioChannelAgent* aAgent, - nsAutoPtr& aData, - void *aPtr); + nsClassHashtable mWindows; - // This returns the number of agents from this aWindow. - uint32_t CountWindow(nsIDOMWindow* aWindow); - - nsClassHashtable< nsPtrHashKey, AudioChannelAgentData > mAgents; #ifdef MOZ_WIDGET_GONK nsTArray mSpeakerManager; #endif - nsTArray mChannelCounters[AUDIO_CHANNEL_INT_LAST]; - - int32_t mCurrentHigherChannel; - int32_t mCurrentVisibleHigherChannel; - - nsTArray mWithVideoChildIDs; - - // Telephony Channel policy is "LIFO", the last app to require the resource is - // allowed to play. The others are muted. - struct TelephonyChild { - uint64_t mChildID; - uint32_t mInstances; - - explicit TelephonyChild(uint64_t aChildID) - : mChildID(aChildID) - , mInstances(1) - {} - }; - nsTArray mTelephonyChildren; - - // mPlayableHiddenContentChildID stores the ChildID of the process which can - // play content channel(s) in the background. - // A background process contained content channel(s) will become playable: - // 1. When this background process registers its content channel(s) in - // AudioChannelService and there is no foreground process with registered - // content channel(s). - // 2. When this process goes from foreground into background and there is - // no foreground process with registered content channel(s). - // A background process contained content channel(s) will become non-playable: - // 1. When there is a foreground process registering its content channel(s) - // in AudioChannelService. - // ps. Currently this condition is never satisfied because the default value - // of visibility status of each channel during registering is hidden = true. - // 2. When there is a process with registered content channel(s) goes from - // background into foreground. - // 3. When this process unregisters all hidden content channels. - // 4. When this process shuts down. - uint64_t mPlayableHiddenContentChildID; bool mDisabled; nsCOMPtr mRunnable; - nsCOMPtr mDeferTelChannelTimer; - bool mTimerElementHidden; - uint64_t mTimerChildID; - uint64_t mDefChannelChildID; // This is needed for IPC comunication between diff --git a/dom/audiochannel/AudioChannelServiceChild.cpp b/dom/audiochannel/AudioChannelServiceChild.cpp deleted file mode 100644 index ba23feb8ba7..00000000000 --- a/dom/audiochannel/AudioChannelServiceChild.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "AudioChannelServiceChild.h" - -#include "base/basictypes.h" - -#include "mozilla/Services.h" -#include "mozilla/StaticPtr.h" -#include "mozilla/unused.h" -#include "mozilla/dom/ContentChild.h" -#include "mozilla/dom/ContentParent.h" -#include "nsIObserverService.h" -#include "nsThreadUtils.h" - -#ifdef MOZ_WIDGET_GONK -#include "SpeakerManagerService.h" -#endif - -using namespace mozilla; -using namespace mozilla::dom; -using namespace mozilla::hal; - -StaticRefPtr gAudioChannelServiceChild; - -// static -AudioChannelService* -AudioChannelServiceChild::GetAudioChannelService() -{ - MOZ_ASSERT(NS_IsMainThread()); - - return gAudioChannelServiceChild; - -} - -// static -AudioChannelService* -AudioChannelServiceChild::GetOrCreateAudioChannelService() -{ - MOZ_ASSERT(NS_IsMainThread()); - - // If we already exist, exit early - if (gAudioChannelServiceChild) { - return gAudioChannelServiceChild; - } - - // Create new instance, register, return - nsRefPtr service = new AudioChannelServiceChild(); - MOZ_ASSERT(service); - - gAudioChannelServiceChild = service; - return gAudioChannelServiceChild; -} - -void -AudioChannelServiceChild::Shutdown() -{ - if (gAudioChannelServiceChild) { - gAudioChannelServiceChild = nullptr; - } -} - -AudioChannelServiceChild::AudioChannelServiceChild() -{ -} - -AudioChannelServiceChild::~AudioChannelServiceChild() -{ -} - -AudioChannelState -AudioChannelServiceChild::GetState(AudioChannelAgent* aAgent, bool aElementHidden) -{ - AudioChannelAgentData* data; - if (!mAgents.Get(aAgent, &data)) { - return AUDIO_CHANNEL_STATE_MUTED; - } - - AudioChannelState state = AUDIO_CHANNEL_STATE_MUTED; - bool oldElementHidden = data->mElementHidden; - - UpdateChannelType(data->mChannel, CONTENT_PROCESS_ID_MAIN, aElementHidden, - oldElementHidden); - - // Update visibility. - data->mElementHidden = aElementHidden; - - ContentChild* cc = ContentChild::GetSingleton(); - cc->SendAudioChannelGetState(data->mChannel, aElementHidden, oldElementHidden, - &state); - data->mState = state; - cc->SendAudioChannelChangedNotification(); - - #ifdef MOZ_WIDGET_GONK - /** Only modify the speaker status when - * (1) apps in the foreground. - * (2) apps in the backgrund and inactive. - * Notice : modify only when the visible status is stable, because there - * has lantency in passing the visibility events. - **/ - bool active = AnyAudioChannelIsActive(); - if (aElementHidden == oldElementHidden && - (!aElementHidden || (aElementHidden && !active))) { - for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) { - mSpeakerManager[i]->SetAudioChannelActive(active); - } - } - #endif - - return state; -} - -void -AudioChannelServiceChild::RegisterAudioChannelAgent(AudioChannelAgent* aAgent, - AudioChannel aChannel, - bool aWithVideo) -{ - AudioChannelService::RegisterAudioChannelAgent(aAgent, aChannel, aWithVideo); - - ContentChild::GetSingleton()->SendAudioChannelRegisterType(aChannel, aWithVideo); - - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->NotifyObservers(nullptr, "audio-channel-agent-changed", nullptr); - } -} - -void -AudioChannelServiceChild::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent) -{ - AudioChannelAgentData *pData; - if (!mAgents.Get(aAgent, &pData)) { - return; - } - - // We need to keep a copy because unregister will remove the - // AudioChannelAgentData object from the hashtable. - AudioChannelAgentData data(*pData); - - AudioChannelService::UnregisterAudioChannelAgent(aAgent); - - ContentChild::GetSingleton()->SendAudioChannelUnregisterType( - data.mChannel, data.mElementHidden, data.mWithVideo); - - nsCOMPtr obs = mozilla::services::GetObserverService(); - if (obs) { - obs->NotifyObservers(nullptr, "audio-channel-agent-changed", nullptr); - } -#ifdef MOZ_WIDGET_GONK - bool active = AnyAudioChannelIsActive(); - for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) { - mSpeakerManager[i]->SetAudioChannelActive(active); - } -#endif -} - -void -AudioChannelServiceChild::SetDefaultVolumeControlChannel(int32_t aChannel, - bool aHidden) -{ - ContentChild *cc = ContentChild::GetSingleton(); - if (cc) { - cc->SendAudioChannelChangeDefVolChannel(aChannel, aHidden); - } -} diff --git a/dom/audiochannel/AudioChannelServiceChild.h b/dom/audiochannel/AudioChannelServiceChild.h deleted file mode 100644 index 503033495c5..00000000000 --- a/dom/audiochannel/AudioChannelServiceChild.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_audiochannelservicechild_h__ -#define mozilla_dom_audiochannelservicechild_h__ - -#include "nsAutoPtr.h" -#include "nsISupports.h" - -#include "AudioChannelService.h" -#include "AudioChannelCommon.h" - -namespace mozilla { -namespace dom { - -class AudioChannelServiceChild : public AudioChannelService -{ -public: - - /** - * Returns the AudioChannelServce singleton or null if the process havn't create it before. - * Only to be called from main thread. - */ - static AudioChannelService* GetAudioChannelService(); - - /** - * Returns the AudioChannelServce singleton. - * If AudioChannelServce is not exist, create and return new one. - * Only to be called from main thread. - */ - static AudioChannelService* GetOrCreateAudioChannelService(); - - static void Shutdown(); - - virtual void RegisterAudioChannelAgent(AudioChannelAgent* aAgent, - AudioChannel aChannel, - bool aWithVideo); - virtual void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent); - - /** - * Return the state to indicate this agent should keep playing/ - * fading volume/muted. - */ - virtual AudioChannelState GetState(AudioChannelAgent* aAgent, - bool aElementHidden); - - virtual void SetDefaultVolumeControlChannel(int32_t aChannel, - bool aHidden); - -protected: - AudioChannelServiceChild(); - virtual ~AudioChannelServiceChild(); -}; - -} // namespace dom -} // namespace mozilla - -#endif - diff --git a/dom/audiochannel/moz.build b/dom/audiochannel/moz.build index 00803501bc4..cc8b71ffd0c 100644 --- a/dom/audiochannel/moz.build +++ b/dom/audiochannel/moz.build @@ -4,25 +4,21 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -TEST_DIRS += ['tests'] - XPIDL_SOURCES += [ 'nsIAudioChannelAgent.idl', + 'nsIAudioChannelService.idl', ] XPIDL_MODULE = 'dom_audiochannel' EXPORTS += [ 'AudioChannelAgent.h', - 'AudioChannelCommon.h', 'AudioChannelService.h', - 'AudioChannelServiceChild.h', ] UNIFIED_SOURCES += [ 'AudioChannelAgent.cpp', 'AudioChannelService.cpp', - 'AudioChannelServiceChild.cpp', ] FAIL_ON_WARNINGS = True diff --git a/dom/audiochannel/nsIAudioChannelAgent.idl b/dom/audiochannel/nsIAudioChannelAgent.idl index e990d3959ab..8febf962fbd 100644 --- a/dom/audiochannel/nsIAudioChannelAgent.idl +++ b/dom/audiochannel/nsIAudioChannelAgent.idl @@ -6,24 +6,13 @@ interface nsIDOMWindow; -[uuid(194b55d9-39c0-45c6-b8ef-b8049f978ea5)] +[uuid(4f537c88-3722-4946-9a09-ce559fa0591d)] interface nsIAudioChannelAgentCallback : nsISupports { - /** - * Notified when the playable status of channel is changed. - * - * @param canPlay - * Callback from agent to notify component of the playable status - * of the channel. If canPlay is muted state, component SHOULD stop - * playing media associated with this channel as soon as possible. if - * it is faded state then the volume of media should be reduced. - */ - void canPlayChanged(in long canPlay); - /** * Notified when the window volume/mute is changed */ - void windowVolumeChanged(); + void windowVolumeChanged(in float aVolume, in bool aMuted); }; /** @@ -40,7 +29,7 @@ interface nsIAudioChannelAgentCallback : nsISupports * 1. Changes to the playable status of this channel. */ -[uuid(2b0222a5-8f7b-49d2-9ab8-cd01b744b23e)] +[uuid(363ff8d3-5bd2-485a-84ac-125062cbdc19)] interface nsIAudioChannelAgent : nsISupports { const long AUDIO_AGENT_CHANNEL_NORMAL = 0; @@ -91,16 +80,6 @@ interface nsIAudioChannelAgent : nsISupports void initWithWeakCallback(in nsIDOMWindow window, in long channelType, in nsIAudioChannelAgentCallback callback); - /** - * This method is just like init(), and specify the channel is associated - * with video. - * - * @param weak - * true if weak reference should be hold. - */ - void initWithVideo(in nsIDOMWindow window, in long channelType, - in nsIAudioChannelAgentCallback callback, in boolean weak); - /** * Notify the agent that we want to start playing. * Note: Gecko component SHOULD call this function first then start to @@ -115,7 +94,7 @@ interface nsIAudioChannelAgent : nsISupports * faded state: the agent has registered with audio channel service the * component should start playback as well as reducing the volume. */ - long startPlaying(); + void startPlaying(out float volume, out bool muted); /** * Notify the agent we no longer want to play. @@ -126,17 +105,4 @@ interface nsIAudioChannelAgent : nsISupports * channel service. */ void stopPlaying(); - - /** - * Notify the agent of the visibility state of the window using this agent. - * @param visible - * True if the window associated with the agent is visible. - */ - void setVisibilityState(in boolean visible); - - /** - * Retrieve the volume from the window. - */ - readonly attribute float windowVolume; }; - diff --git a/dom/audiochannel/nsIAudioChannelService.idl b/dom/audiochannel/nsIAudioChannelService.idl new file mode 100644 index 00000000000..8cdc0379d26 --- /dev/null +++ b/dom/audiochannel/nsIAudioChannelService.idl @@ -0,0 +1,29 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIDOMWindow; + +[scriptable, builtinclass, uuid(323e5472-b8f4-4288-b1b9-53c7c54bbbe8)] +interface nsIAudioChannelService : nsISupports +{ + float getAudioChannelVolume(in nsIDOMWindow window, + in unsigned short audioChannel); + + void setAudioChannelVolume(in nsIDOMWindow window, + in unsigned short audioChannel, + in float volume); + + boolean getAudioChannelMuted(in nsIDOMWindow window, + in unsigned short audioChannel); + + void setAudioChannelMuted(in nsIDOMWindow window, + in unsigned short audioChannel, + in boolean muted); + + boolean isAudioChannelActive(in nsIDOMWindow window, + in unsigned short audioChannel); +}; diff --git a/dom/audiochannel/tests/AudioChannelChromeScript.js b/dom/audiochannel/tests/AudioChannelChromeScript.js deleted file mode 100644 index 46bd7d30791..00000000000 --- a/dom/audiochannel/tests/AudioChannelChromeScript.js +++ /dev/null @@ -1,18 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components; -const { Services } = Cu.import('resource://gre/modules/Services.jsm'); -const { SystemAppProxy } = Cu.import('resource://gre/modules/SystemAppProxy.jsm'); - -addMessageListener('init-chrome-event', function(message) { - // listen mozChromeEvent and forward to content process. - let type = message.type; - - SystemAppProxy.addEventListener('mozChromeEvent', function(event) { - let details = event.detail; - if (details.type === type) { - sendAsyncMessage('chrome-event', details); - } - }, true); -}); diff --git a/dom/audiochannel/tests/TestAudioChannelService.cpp b/dom/audiochannel/tests/TestAudioChannelService.cpp deleted file mode 100644 index 9d6a6d1a40d..00000000000 --- a/dom/audiochannel/tests/TestAudioChannelService.cpp +++ /dev/null @@ -1,669 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifdef XP_WIN -#include -#else -#include -#endif - -#include "TestHarness.h" - -#include "nsWeakReference.h" -#include "AudioChannelService.h" -#include "AudioChannelAgent.h" - -#include "nsThreadUtils.h" - -#define TEST_ENSURE_BASE(_test, _msg) \ - PR_BEGIN_MACRO \ - if (!(_test)) { \ - fail(_msg); \ - return NS_ERROR_FAILURE; \ - } else { \ - passed(_msg); \ - } \ - PR_END_MACRO - -using namespace mozilla::dom; - -void -spin_events_loop_until_false(const bool* const aCondition) -{ - nsCOMPtr thread(::do_GetCurrentThread()); - nsresult rv = NS_OK; - bool processed = true; - while (*aCondition && NS_SUCCEEDED(rv)) { - rv = thread->ProcessNextEvent(true, &processed); - } -} - -class Agent : public nsIAudioChannelAgentCallback, - public nsSupportsWeakReference -{ -protected: - virtual ~Agent() - { - if (mRegistered) { - StopPlaying(); - } - } - -public: - NS_DECL_ISUPPORTS - - explicit Agent(AudioChannel aChannel) - : mChannel(aChannel) - , mWaitCallback(false) - , mRegistered(false) - , mCanPlay(AUDIO_CHANNEL_STATE_MUTED) - { - mAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1"); - } - - nsresult Init(bool video=false) - { - nsresult rv = NS_OK; - if (video) { - rv = mAgent->InitWithVideo(nullptr, static_cast(mChannel), - this, true); - } - else { - rv = mAgent->InitWithWeakCallback(nullptr, static_cast(mChannel), - this); - } - NS_ENSURE_SUCCESS(rv, rv); - - return mAgent->SetVisibilityState(false); - } - - nsresult StartPlaying(AudioChannelState *_ret) - { - if (mRegistered) { - StopPlaying(); - } - - nsresult rv = mAgent->StartPlaying((int32_t *)_ret); - mRegistered = true; - return rv; - } - - nsresult StopPlaying() - { - mRegistered = false; - spin_events_loop_until_false(&mWaitCallback); - return mAgent->StopPlaying(); - } - - nsresult SetVisibilityState(bool visible) - { - if (mRegistered) { - mWaitCallback = true; - } - return mAgent->SetVisibilityState(visible); - } - - NS_IMETHODIMP CanPlayChanged(int32_t canPlay) override - { - mCanPlay = static_cast(canPlay); - mWaitCallback = false; - return NS_OK; - } - - NS_IMETHODIMP WindowVolumeChanged() override - { - return NS_OK; - } - - nsresult GetCanPlay(AudioChannelState *_ret, bool aWaitCallback = false) - { - if (aWaitCallback) { - mWaitCallback = true; - } - - spin_events_loop_until_false(&mWaitCallback); - *_ret = mCanPlay; - return NS_OK; - } - - nsCOMPtr mAgent; - AudioChannel mChannel; - bool mWaitCallback; - bool mRegistered; - AudioChannelState mCanPlay; -}; - -NS_IMPL_ISUPPORTS(Agent, nsIAudioChannelAgentCallback, - nsISupportsWeakReference) - -nsresult -TestDoubleStartPlaying() -{ - nsRefPtr agent = new Agent(AudioChannel::Normal); - - nsresult rv = agent->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - AudioChannelState playable; - rv = agent->mAgent->StartPlaying((int32_t *)&playable); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent->mAgent->StartPlaying((int32_t *)&playable); - TEST_ENSURE_BASE(NS_FAILED(rv), - "Test0: StartPlaying calling twice must return error"); - - return NS_OK; -} - -nsresult -TestOneNormalChannel() -{ - nsRefPtr agent = new Agent(AudioChannel::Normal); - nsresult rv = agent->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - AudioChannelState playable; - rv = agent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, - "Test1: A normal channel unvisible agent must be muted"); - - rv = agent->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test1: A normal channel visible agent must be playable"); - - return rv; -} - -nsresult -TestTwoNormalChannels() -{ - nsRefPtr agent1 = new Agent(AudioChannel::Normal); - nsresult rv = agent1->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - nsRefPtr agent2 = new Agent(AudioChannel::Normal); - rv = agent2->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - AudioChannelState playable; - rv = agent1->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, - "Test2: A normal channel unvisible agent1 must be muted"); - - rv = agent2->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, - "Test2: A normal channel unvisible agent2 must be muted"); - - rv = agent1->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent2->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent1->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test2: A normal channel visible agent1 must be playable"); - - rv = agent2->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test2: A normal channel visible agent2 must be playable"); - - return rv; -} - -nsresult -TestContentChannels() -{ - nsRefPtr agent1 = new Agent(AudioChannel::Content); - nsresult rv = agent1->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - nsRefPtr agent2 = new Agent(AudioChannel::Content); - rv = agent2->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - // All content channels in the foreground can be allowed to play - rv = agent1->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent2->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - AudioChannelState playable; - rv = agent1->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test3: A content channel visible agent1 must be playable"); - - rv = agent2->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test3: A content channel visible agent2 must be playable"); - - // Test the transition state of one content channel tried to set non-visible - // state first when app is going to background. - rv = agent1->SetVisibilityState(false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent1->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test3: A content channel unvisible agent1 must be playable from " - "foreground to background"); - - // Test all content channels set non-visible already - rv = agent2->SetVisibilityState(false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent2->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test3: A content channel unvisible agent2 must be playable from " - "foreground to background"); - - // Clear the content channels & mActiveContentChildIDs in AudioChannelService. - // If agent stop playable in the background, we will reserve it's childID in - // mActiveContentChildIDs, then it can allow to play next song. So we set agents - // to foreground first then stopping to play - rv = agent1->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - rv = agent2->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - rv = agent1->StopPlaying(); - NS_ENSURE_SUCCESS(rv, rv); - rv = agent2->StopPlaying(); - NS_ENSURE_SUCCESS(rv, rv); - - // Test that content channels can be allow to play when they starts from - // the background state - rv = agent1->SetVisibilityState(false); - NS_ENSURE_SUCCESS(rv, rv); - rv = agent2->SetVisibilityState(false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent1->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test3: A content channel unvisible agent1 must be playable " - "from background state"); - - rv = agent2->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test3: A content channel unvisible agent2 must be playable " - "from background state"); - - return rv; -} - -nsresult -TestFadedState() -{ - nsRefPtr normalAgent = new Agent(AudioChannel::Normal); - nsresult rv = normalAgent->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - nsRefPtr contentAgent = new Agent(AudioChannel::Content); - rv = contentAgent->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - nsRefPtr notificationAgent = new Agent(AudioChannel::Notification); - rv = notificationAgent->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = normalAgent->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = contentAgent->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = notificationAgent->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - AudioChannelState playable; - rv = normalAgent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test4: A normal channel visible agent must be playable"); - - rv = contentAgent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test4: A content channel visible agent must be playable"); - - rv = notificationAgent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test4: A notification channel visible agent must be playable"); - - rv = contentAgent->GetCanPlay(&playable, true); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_FADED, - "Test4: A content channel unvisible agent must be faded because of " - "notification channel is playing"); - - rv = contentAgent->SetVisibilityState(false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = contentAgent->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_FADED, - "Test4: A content channel unvisible agent must be faded because of " - "notification channel is playing"); - - rv = notificationAgent->SetVisibilityState(false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = notificationAgent->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test4: A notification channel unvisible agent must be playable from " - "foreground to background"); - - rv = notificationAgent->StopPlaying(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = contentAgent->GetCanPlay(&playable, true); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test4: A content channel unvisible agent must be playable " - "because of notification channel is stopped"); - - rv = contentAgent->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - return rv; -} - -nsresult -TestPriorities() -{ - nsRefPtr normalAgent = new Agent(AudioChannel::Normal); - nsresult rv = normalAgent->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - nsRefPtr contentAgent = new Agent(AudioChannel::Content); - rv = contentAgent->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - nsRefPtr notificationAgent = new Agent(AudioChannel::Notification); - rv = notificationAgent->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - nsRefPtr alarmAgent = new Agent(AudioChannel::Alarm); - rv = alarmAgent->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - nsRefPtr telephonyAgent = new Agent(AudioChannel::Telephony); - rv = telephonyAgent->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - nsRefPtr ringerAgent = new Agent(AudioChannel::Ringer); - rv = ringerAgent->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - nsRefPtr pNotificationAgent = - new Agent(AudioChannel::Publicnotification); - rv = pNotificationAgent->Init(); - NS_ENSURE_SUCCESS(rv, rv); - - AudioChannelState playable; - - rv = normalAgent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, - "Test5: A normal channel unvisible agent must be muted"); - - rv = contentAgent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test5: A content channel unvisible agent must be playable while " - "playing from background state"); - - rv = notificationAgent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test5: A notification channel unvisible agent must be playable"); - - rv = alarmAgent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test5: An alarm channel unvisible agent must be playable"); - - rv = notificationAgent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, - "Test5: A notification channel unvisible agent must be muted when an " - "alarm is playing"); - - rv = telephonyAgent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test5: A telephony channel unvisible agent must be playable"); - - rv = alarmAgent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, - "Test5: An alarm channel unvisible agent must be muted when a telephony " - "is playing"); - - rv = ringerAgent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test5: A ringer channel unvisible agent must be playable"); - - rv = telephonyAgent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, - "Test5: A telephony channel unvisible agent must be muted when a ringer " - "is playing"); - - rv = pNotificationAgent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test5: A pNotification channel unvisible agent must be playable"); - - rv = ringerAgent->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, - "Test5: A ringer channel unvisible agent must be muted when a public " - "notification is playing"); - - // Stop to play notification channel or normal/content will be faded. - // Which already be tested on Test 4. - rv = notificationAgent->StopPlaying(); - NS_ENSURE_SUCCESS(rv, rv); - - // Settings visible the normal channel. - rv = normalAgent->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = normalAgent->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test5: A normal channel visible agent must be playable"); - - // Set the content channel as visible . - rv = contentAgent->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - // Content must be playable because visible. - rv = contentAgent->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test5: A content channel visible agent must be playable"); - - // Set the alarm channel as visible. - rv = alarmAgent->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = alarmAgent->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test5: An alarm channel visible agent must be playable"); - - // Set the telephony channel as visible. - rv = telephonyAgent->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = telephonyAgent->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test5: A telephony channel visible agent must be playable"); - - // Set the ringer channel as visible. - rv = ringerAgent->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = ringerAgent->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test5: A ringer channel visible agent must be playable"); - - // Set the public notification channel as visible. - rv = pNotificationAgent->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = pNotificationAgent->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test5: A pNotification channel visible agent must be playable"); - - return rv; -} - -nsresult -TestOneVideoNormalChannel() -{ - nsRefPtr agent1 = new Agent(AudioChannel::Normal); - nsresult rv = agent1->Init(true); - NS_ENSURE_SUCCESS(rv, rv); - - nsRefPtr agent2 = new Agent(AudioChannel::Content); - rv = agent2->Init(false); - NS_ENSURE_SUCCESS(rv, rv); - - AudioChannelState playable; - rv = agent1->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, - "Test6: A video normal channel invisible agent1 must be muted"); - - rv = agent2->StartPlaying(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test6: A content channel invisible agent2 must be playable"); - - // one video normal channel in foreground and one content channel in background - rv = agent1->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent1->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test6: A video normal channel visible agent1 must be playable"); - - rv = agent2->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, - "Test6: A content channel invisible agent2 must be muted"); - - // both one video normal channel and one content channel in foreground - rv = agent2->SetVisibilityState(true); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent1->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test6: A video normal channel visible agent1 must be playable"); - - rv = agent2->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test6: A content channel visible agent2 must be playable"); - - // one video normal channel in background and one content channel in foreground - rv = agent1->SetVisibilityState(false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent1->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, - "Test6: A video normal channel invisible agent1 must be muted"); - - rv = agent2->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test6: A content channel visible agent2 must be playable"); - - // both one video normal channel and one content channel in background - rv = agent2->SetVisibilityState(false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = agent1->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED, - "Test6: A video normal channel invisible agent1 must be muted"); - - rv = agent2->GetCanPlay(&playable); - NS_ENSURE_SUCCESS(rv, rv); - TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_NORMAL, - "Test6: A content channel invisible agent2 must be playable"); - - return rv; -} - -int main(int argc, char** argv) -{ - ScopedXPCOM xpcom("AudioChannelService"); - if (xpcom.failed()) { - return 1; - } - - if (NS_FAILED(TestDoubleStartPlaying())) { - return 1; - } - - if (NS_FAILED(TestOneNormalChannel())) { - return 1; - } - - if (NS_FAILED(TestTwoNormalChannels())) { - return 1; - } - - if (NS_FAILED(TestContentChannels())) { - return 1; - } - - if (NS_FAILED(TestFadedState())) { - return 1; - } - - // Channel type with AudioChannel::Telephony cannot be unregistered until the - // main thread has chances to process 1500 millisecond timer. In order to - // skip ambiguous return value of ChannelsActiveWithHigherPriorityThan(), new - // test cases are added before any test case that registers the channel type - // with AudioChannel::Telephony channel. - if (NS_FAILED(TestOneVideoNormalChannel())) { - return 1; - } - - if (NS_FAILED(TestPriorities())) { - return 1; - } - - return 0; -} - diff --git a/dom/audiochannel/tests/audio.ogg b/dom/audiochannel/tests/audio.ogg deleted file mode 100644 index d7f6a0ccf47fbc30e936b47e3f4d2cf8f4a90a16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16521 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

9eH6-?Re=p+l66 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 DFy$Y2 diff --git a/dom/audiochannel/tests/file_audio.html b/dom/audiochannel/tests/file_audio.html deleted file mode 100644 index 11058a1ff11..00000000000 --- a/dom/audiochannel/tests/file_audio.html +++ /dev/null @@ -1,99 +0,0 @@ - - - - Test audio-channel-changed & visible-audio-channel-changed mozChromeEvent - - -

- - - diff --git a/dom/audiochannel/tests/file_telephonyPolicy.html b/dom/audiochannel/tests/file_telephonyPolicy.html deleted file mode 100644 index f95b7d40ff8..00000000000 --- a/dom/audiochannel/tests/file_telephonyPolicy.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Test Telephony Channel Policy - - -
- - - diff --git a/dom/audiochannel/tests/mochitest.ini b/dom/audiochannel/tests/mochitest.ini deleted file mode 100644 index 45564d6dd46..00000000000 --- a/dom/audiochannel/tests/mochitest.ini +++ /dev/null @@ -1,11 +0,0 @@ -[DEFAULT] -support-files = - audio.ogg - file_audio.html - file_telephonyPolicy.html - AudioChannelChromeScript.js - -[test_telephonyPolicy.html] -skip-if = buildapp == 'mulet' || (toolkit == 'gonk' || e10s) || os == "android" -[test_audioChannelChange.html] -skip-if = (toolkit != 'gonk') diff --git a/dom/audiochannel/tests/moz.build b/dom/audiochannel/tests/moz.build deleted file mode 100644 index b1bf003ce18..00000000000 --- a/dom/audiochannel/tests/moz.build +++ /dev/null @@ -1,16 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -GeckoCppUnitTests([ - 'TestAudioChannelService', -]) - -if CONFIG['OS_ARCH'] == 'WINNT': - DEFINES['NOMINMAX'] = True - -MOCHITEST_MANIFESTS += ['mochitest.ini'] - -FAIL_ON_WARNINGS = True diff --git a/dom/audiochannel/tests/test_audioChannelChange.html b/dom/audiochannel/tests/test_audioChannelChange.html deleted file mode 100644 index 1036018047f..00000000000 --- a/dom/audiochannel/tests/test_audioChannelChange.html +++ /dev/null @@ -1,209 +0,0 @@ - - - - - Test audio-channel-changed & visible-audio-channel-changed mozChromeEvent - - - - -
- - - diff --git a/dom/audiochannel/tests/test_telephonyPolicy.html b/dom/audiochannel/tests/test_telephonyPolicy.html deleted file mode 100644 index f0294ac3cf8..00000000000 --- a/dom/audiochannel/tests/test_telephonyPolicy.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - - Test the Telephony Channel Policy - - - - -
- - - diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 816a4cfadf0..30de73cac69 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -2055,6 +2055,7 @@ GK_ATOM(onwarning, "onwarning") GK_ATOM(onstart, "onstart") GK_ATOM(onstop, "onstop") GK_ATOM(onphoto, "onphoto") +GK_ATOM(onactivestatechanged, "onactivestatechanged") #ifdef MOZ_GAMEPAD GK_ATOM(ongamepadbuttondown, "ongamepadbuttondown") GK_ATOM(ongamepadbuttonup, "ongamepadbuttonup") diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index a41ec9ed104..50968c7881e 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -3735,39 +3735,10 @@ nsPIDOMWindow::SetAudioVolume(float aVolume) return NS_OK; } -float -nsPIDOMWindow::GetAudioGlobalVolume() -{ - float globalVolume = 1.0; - nsCOMPtr window = this; - - do { - if (window->GetAudioMuted()) { - return 0; - } - - globalVolume *= window->GetAudioVolume(); - - nsCOMPtr win; - window->GetParent(getter_AddRefs(win)); - if (window == win) { - break; - } - - window = do_QueryInterface(win); - - // If there is not parent, or we are the toplevel or the volume is - // already 0.0, we don't continue. - } while (window && window != this && globalVolume); - - return globalVolume; -} - void nsPIDOMWindow::RefreshMediaElements() { - nsRefPtr service = - AudioChannelService::GetOrCreateAudioChannelService(); + nsRefPtr service = AudioChannelService::GetOrCreate(); service->RefreshAgentsVolume(GetCurrentInnerWindow()); } diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h index fd4593d580e..3f7b1b220fd 100644 --- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -185,8 +185,6 @@ public: float GetAudioVolume() const; nsresult SetAudioVolume(float aVolume); - float GetAudioGlobalVolume(); - virtual void SetServiceWorkersTestingEnabled(bool aEnabled) { MOZ_ASSERT(IsOuterWindow()); diff --git a/dom/browser-element/BrowserElementAudioChannel.cpp b/dom/browser-element/BrowserElementAudioChannel.cpp new file mode 100644 index 00000000000..bb540ba1111 --- /dev/null +++ b/dom/browser-element/BrowserElementAudioChannel.cpp @@ -0,0 +1,489 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "BrowserElementAudioChannel.h" + +#include "mozilla/Services.h" +#include "mozilla/dom/BrowserElementAudioChannelBinding.h" +#include "mozilla/dom/DOMRequest.h" +#include "mozilla/dom/ToJSValue.h" +#include "AudioChannelService.h" +#include "nsIBrowserElementAPI.h" +#include "nsIDocShell.h" +#include "nsIDOMDOMRequest.h" +#include "nsIObserverService.h" +#include "nsISupportsPrimitives.h" +#include "nsITabParent.h" +#include "nsPIDOMWindow.h" + +namespace { + +void +AssertIsInMainProcess() +{ + MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); +} + +} // anonymous namespace + +namespace mozilla { +namespace dom { + +NS_IMPL_ADDREF_INHERITED(BrowserElementAudioChannel, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(BrowserElementAudioChannel, DOMEventTargetHelper) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BrowserElementAudioChannel) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY(nsIObserver) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_CYCLE_COLLECTION_INHERITED(BrowserElementAudioChannel, + DOMEventTargetHelper, + mFrameLoader, + mFrameWindow, + mTabParent, + mBrowserElementAPI) + +BrowserElementAudioChannel::BrowserElementAudioChannel( + nsPIDOMWindow* aWindow, + nsIFrameLoader* aFrameLoader, + nsIBrowserElementAPI* aAPI, + AudioChannel aAudioChannel) + : DOMEventTargetHelper(aWindow) + , mFrameLoader(aFrameLoader) + , mBrowserElementAPI(aAPI) + , mAudioChannel(aAudioChannel) + , mState(eStateUnknown) +{ + MOZ_ASSERT(NS_IsMainThread()); + AssertIsInMainProcess(); + MOZ_ASSERT(mFrameLoader); + + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + nsAutoString name; + AudioChannelService::GetAudioChannelString(aAudioChannel, name); + + nsAutoCString topic; + topic.Assign("audiochannel-activity-"); + topic.Append(NS_ConvertUTF16toUTF8(name)); + + obs->AddObserver(this, topic.get(), true); + } +} + +BrowserElementAudioChannel::~BrowserElementAudioChannel() +{ + MOZ_ASSERT(NS_IsMainThread()); + AssertIsInMainProcess(); + + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (obs) { + nsAutoString name; + AudioChannelService::GetAudioChannelString(mAudioChannel, name); + + nsAutoCString topic; + topic.Assign("audiochannel-activity-"); + topic.Append(NS_ConvertUTF16toUTF8(name)); + + obs->RemoveObserver(this, topic.get()); + } +} + +nsresult +BrowserElementAudioChannel::Initialize() +{ + nsCOMPtr docShell; + nsresult rv = mFrameLoader->GetDocShell(getter_AddRefs(docShell)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (docShell) { + nsCOMPtr window = docShell->GetWindow(); + if (!window) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr topWindow; + window->GetScriptableTop(getter_AddRefs(topWindow)); + + mFrameWindow = do_QueryInterface(topWindow); + mFrameWindow = mFrameWindow->GetOuterWindow(); + return NS_OK; + } + + rv = mFrameLoader->GetTabParent(getter_AddRefs(mTabParent)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(mTabParent); + return NS_OK; +} + +JSObject* +BrowserElementAudioChannel::WrapObject(JSContext *aCx, + JS::Handle aGivenProto) +{ + return BrowserElementAudioChannelBinding::Wrap(aCx, this, aGivenProto); +} + +AudioChannel +BrowserElementAudioChannel::Name() const +{ + MOZ_ASSERT(NS_IsMainThread()); + AssertIsInMainProcess(); + + return mAudioChannel; +} + +namespace { + +class BaseRunnable : public nsRunnable +{ +protected: + nsCOMPtr mParentWindow; + nsCOMPtr mFrameWindow; + nsRefPtr mRequest; + AudioChannel mAudioChannel; + + virtual void DoWork(AudioChannelService* aService, + JSContext* aCx) = 0; + +public: + BaseRunnable(nsPIDOMWindow* aParentWindow, nsPIDOMWindow* aFrameWindow, + DOMRequest* aRequest, AudioChannel aAudioChannel) + : mParentWindow(aParentWindow) + , mFrameWindow(aFrameWindow) + , mRequest(aRequest) + , mAudioChannel(aAudioChannel) + {} + + NS_IMETHODIMP Run() override + { + nsRefPtr service = AudioChannelService::GetOrCreate(); + MOZ_ASSERT(service); + + AutoJSAPI jsapi; + if (!jsapi.Init(mParentWindow)) { + mRequest->FireError(NS_ERROR_FAILURE); + return NS_OK; + } + + DoWork(service, jsapi.cx()); + return NS_OK; + } +}; + +class GetVolumeRunnable final : public BaseRunnable +{ +public: + GetVolumeRunnable(nsPIDOMWindow* aParentWindow, nsPIDOMWindow* aFrameWindow, + DOMRequest* aRequest, AudioChannel aAudioChannel) + : BaseRunnable(aParentWindow, aFrameWindow, aRequest, aAudioChannel) + {} + +protected: + virtual void DoWork(AudioChannelService* aService, JSContext* aCx) override + { + float volume = aService->GetAudioChannelVolume(mFrameWindow, mAudioChannel); + + JS::Rooted value(aCx); + if (!ToJSValue(aCx, volume, &value)) { + mRequest->FireError(NS_ERROR_FAILURE); + return; + } + + mRequest->FireSuccess(value); + } +}; + +class GetMutedRunnable final : public BaseRunnable +{ +public: + GetMutedRunnable(nsPIDOMWindow* aParentWindow, nsPIDOMWindow* aFrameWindow, + DOMRequest* aRequest, AudioChannel aAudioChannel) + : BaseRunnable(aParentWindow, aFrameWindow, aRequest, aAudioChannel) + {} + +protected: + virtual void DoWork(AudioChannelService* aService, JSContext* aCx) override + { + bool muted = aService->GetAudioChannelMuted(mFrameWindow, mAudioChannel); + + JS::Rooted value(aCx); + if (!ToJSValue(aCx, muted, &value)) { + mRequest->FireError(NS_ERROR_FAILURE); + return; + } + + mRequest->FireSuccess(value); + } +}; + +class IsActiveRunnable final : public BaseRunnable +{ + bool mActive; + bool mValueKnown; + +public: + IsActiveRunnable(nsPIDOMWindow* aParentWindow, nsPIDOMWindow* aFrameWindow, + DOMRequest* aRequest, AudioChannel aAudioChannel, + bool aActive) + : BaseRunnable(aParentWindow, aFrameWindow, aRequest, aAudioChannel) + , mActive(aActive) + , mValueKnown(true) + {} + + IsActiveRunnable(nsPIDOMWindow* aParentWindow, nsPIDOMWindow* aFrameWindow, + DOMRequest* aRequest, AudioChannel aAudioChannel) + : BaseRunnable(aParentWindow, aFrameWindow, aRequest, aAudioChannel) + , mActive(true) + , mValueKnown(false) + {} + +protected: + virtual void DoWork(AudioChannelService* aService, JSContext* aCx) override + { + if (!mValueKnown) { + mActive = aService->IsAudioChannelActive(mFrameWindow, mAudioChannel); + } + + JS::Rooted value(aCx); + if (!ToJSValue(aCx, mActive, &value)) { + mRequest->FireError(NS_ERROR_FAILURE); + return; + } + + mRequest->FireSuccess(value); + } +}; + +class FireSuccessRunnable final : public BaseRunnable +{ +public: + FireSuccessRunnable(nsPIDOMWindow* aParentWindow, nsPIDOMWindow* aFrameWindow, + DOMRequest* aRequest, AudioChannel aAudioChannel) + : BaseRunnable(aParentWindow, aFrameWindow, aRequest, aAudioChannel) + {} + +protected: + virtual void DoWork(AudioChannelService* aService, JSContext* aCx) override + { + JS::Rooted value(aCx); + mRequest->FireSuccess(value); + } +}; + +} // anonymous namespace + +already_AddRefed +BrowserElementAudioChannel::GetVolume(ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + AssertIsInMainProcess(); + + if (!mFrameWindow) { + nsCOMPtr request; + aRv = mBrowserElementAPI->GetAudioChannelVolume((uint32_t)mAudioChannel, + getter_AddRefs(request)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return request.forget().downcast(); + } + + nsRefPtr domRequest = new DOMRequest(GetOwner()); + + nsCOMPtr runnable = + new GetVolumeRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel); + NS_DispatchToMainThread(runnable); + + return domRequest.forget(); +} + +already_AddRefed +BrowserElementAudioChannel::SetVolume(float aVolume, ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + AssertIsInMainProcess(); + + if (!mFrameWindow) { + nsCOMPtr request; + aRv = mBrowserElementAPI->SetAudioChannelVolume((uint32_t)mAudioChannel, + aVolume, + getter_AddRefs(request)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return request.forget().downcast(); + } + + nsRefPtr service = AudioChannelService::GetOrCreate(); + MOZ_ASSERT(service); + + service->SetAudioChannelVolume(mFrameWindow, mAudioChannel, aVolume); + + nsRefPtr domRequest = new DOMRequest(GetOwner()); + nsCOMPtr runnable = new FireSuccessRunnable(GetOwner(), + mFrameWindow, + domRequest, + mAudioChannel); + NS_DispatchToMainThread(runnable); + + return domRequest.forget(); +} + +already_AddRefed +BrowserElementAudioChannel::GetMuted(ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + AssertIsInMainProcess(); + + if (!mFrameWindow) { + nsCOMPtr request; + aRv = mBrowserElementAPI->GetAudioChannelMuted((uint32_t)mAudioChannel, + getter_AddRefs(request)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return request.forget().downcast(); + } + + nsRefPtr domRequest = new DOMRequest(GetOwner()); + + nsCOMPtr runnable = + new GetMutedRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel); + NS_DispatchToMainThread(runnable); + + return domRequest.forget(); +} + +already_AddRefed +BrowserElementAudioChannel::SetMuted(bool aMuted, ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + AssertIsInMainProcess(); + + if (!mFrameWindow) { + nsCOMPtr request; + aRv = mBrowserElementAPI->SetAudioChannelMuted((uint32_t)mAudioChannel, + aMuted, + getter_AddRefs(request)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return request.forget().downcast(); + } + + nsRefPtr service = AudioChannelService::GetOrCreate(); + MOZ_ASSERT(service); + + service->SetAudioChannelMuted(mFrameWindow, mAudioChannel, aMuted); + + nsRefPtr domRequest = new DOMRequest(GetOwner()); + nsCOMPtr runnable = new FireSuccessRunnable(GetOwner(), + mFrameWindow, + domRequest, + mAudioChannel); + NS_DispatchToMainThread(runnable); + + return domRequest.forget(); +} + +already_AddRefed +BrowserElementAudioChannel::IsActive(ErrorResult& aRv) +{ + MOZ_ASSERT(NS_IsMainThread()); + AssertIsInMainProcess(); + + if (mState != eStateUnknown) { + nsRefPtr domRequest = new DOMRequest(GetOwner()); + + nsCOMPtr runnable = + new IsActiveRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel, + mState == eStateActive); + NS_DispatchToMainThread(runnable); + + return domRequest.forget(); + } + + if (!mFrameWindow) { + nsCOMPtr request; + aRv = mBrowserElementAPI->IsAudioChannelActive((uint32_t)mAudioChannel, + getter_AddRefs(request)); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + return request.forget().downcast(); + } + + nsRefPtr domRequest = new DOMRequest(GetOwner()); + + nsCOMPtr runnable = + new IsActiveRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel); + NS_DispatchToMainThread(runnable); + + return domRequest.forget(); +} + +NS_IMETHODIMP +BrowserElementAudioChannel::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) +{ + nsAutoString name; + AudioChannelService::GetAudioChannelString(mAudioChannel, name); + + nsAutoCString topic; + topic.Assign("audiochannel-activity-"); + topic.Append(NS_ConvertUTF16toUTF8(name)); + + if (strcmp(topic.get(), aTopic)) { + return NS_OK; + } + + // Message received from the child. + if (!mFrameWindow) { + if (mTabParent == aSubject) { + ProcessStateChanged(aData); + } + + return NS_OK; + } + + nsCOMPtr wrapper = do_QueryInterface(aSubject); + if (NS_WARN_IF(!wrapper)) { + return NS_ERROR_FAILURE; + } + + uint64_t windowID; + nsresult rv = wrapper->GetData(&windowID); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (windowID != mFrameWindow->WindowID()) { + return NS_OK; + } + + ProcessStateChanged(aData); + return NS_OK; +} + +void +BrowserElementAudioChannel::ProcessStateChanged(const char16_t* aData) +{ + nsAutoString value(aData); + mState = value.EqualsASCII("active") ? eStateActive : eStateInactive; + DispatchTrustedEvent(NS_LITERAL_STRING("activestatechanged")); +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/browser-element/BrowserElementAudioChannel.h b/dom/browser-element/BrowserElementAudioChannel.h new file mode 100644 index 00000000000..af990caa8a5 --- /dev/null +++ b/dom/browser-element/BrowserElementAudioChannel.h @@ -0,0 +1,83 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_BrowserElementAudioChannels_h +#define mozilla_dom_BrowserElementAudioChannels_h + +#include "mozilla/dom/AudioChannelBinding.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/ErrorResult.h" +#include "nsCycleCollectionParticipant.h" +#include "nsIObserver.h" +#include "nsIFrameLoader.h" +#include "nsWeakReference.h" +#include "nsWrapperCache.h" + +class nsIBrowserElementAPI; +class nsITabParent; +class nsPIDOMWindow; + +namespace mozilla { +namespace dom { + +class DOMRequest; + +class BrowserElementAudioChannel final : public DOMEventTargetHelper + , public nsSupportsWeakReference + , public nsIObserver +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIOBSERVER + + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BrowserElementAudioChannel, + DOMEventTargetHelper) + + BrowserElementAudioChannel(nsPIDOMWindow* aWindow, + nsIFrameLoader* aFrameLoader, + nsIBrowserElementAPI* aAPI, + AudioChannel aAudioChannel); + + nsresult Initialize(); + + // WebIDL methods + + virtual JSObject* WrapObject(JSContext *aCx, + JS::Handle aGivenProto) override; + + AudioChannel Name() const; + + already_AddRefed GetVolume(ErrorResult& aRv); + already_AddRefed SetVolume(float aVolume, ErrorResult& aRv); + + already_AddRefed GetMuted(ErrorResult& aRv); + already_AddRefed SetMuted(bool aMuted, ErrorResult& aRv); + + already_AddRefed IsActive(ErrorResult& aRv); + + IMPL_EVENT_HANDLER(activestatechanged); + +private: + ~BrowserElementAudioChannel(); + + void ProcessStateChanged(const char16_t* aData); + + nsCOMPtr mFrameLoader; + nsCOMPtr mBrowserElementAPI; + nsCOMPtr mTabParent; + nsCOMPtr mFrameWindow; + AudioChannel mAudioChannel; + + enum { + eStateActive, + eStateInactive, + eStateUnknown + } mState; +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_BrowserElementAudioChannels_h diff --git a/dom/browser-element/BrowserElementChildPreload.js b/dom/browser-element/BrowserElementChildPreload.js index 147a4af91e7..457f882496a 100644 --- a/dom/browser-element/BrowserElementChildPreload.js +++ b/dom/browser-element/BrowserElementChildPreload.js @@ -13,6 +13,10 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/BrowserElementPromptService.jsm"); +XPCOMUtils.defineLazyServiceGetter(this, "acs", + "@mozilla.org/audiochannel/service;1", + "nsIAudioChannelService"); + let kLongestReturnedString = 128; function debug(msg) { @@ -234,6 +238,11 @@ BrowserElementChild.prototype = { "find-all": this._recvFindAll.bind(this), "find-next": this._recvFindNext.bind(this), "clear-match": this._recvClearMatch.bind(this), + "get-audio-channel-volume": this._recvGetAudioChannelVolume, + "set-audio-channel-volume": this._recvSetAudioChannelVolume, + "get-audio-channel-muted": this._recvGetAudioChannelMuted, + "set-audio-channel-muted": this._recvSetAudioChannelMuted, + "get-is-audio-channel-active": this._recvIsAudioChannelActive } addMessageListener("browser-element-api:call", function(aMessage) { @@ -1246,6 +1255,55 @@ BrowserElementChild.prototype = { } }, + _recvGetAudioChannelVolume: function(data) { + debug("Received getAudioChannelVolume message: (" + data.json.id + ")"); + + let volume = acs.getAudioChannelVolume(content, + data.json.args.audioChannel); + sendAsyncMsg('got-audio-channel-volume', { + id: data.json.id, successRv: volume + }); + }, + + _recvSetAudioChannelVolume: function(data) { + debug("Received setAudioChannelVolume message: (" + data.json.id + ")"); + + acs.setAudioChannelVolume(content, + data.json.args.audioChannel, + data.json.args.volume); + sendAsyncMsg('got-set-audio-channel-volume', { + id: data.json.id, successRv: true + }); + }, + + _recvGetAudioChannelMuted: function(data) { + debug("Received getAudioChannelMuted message: (" + data.json.id + ")"); + + let muted = acs.getAudioChannelMuted(content, data.json.args.audioChannel); + sendAsyncMsg('got-audio-channel-muted', { + id: data.json.id, successRv: muted + }); + }, + + _recvSetAudioChannelMuted: function(data) { + debug("Received setAudioChannelMuted message: (" + data.json.id + ")"); + + acs.setAudioChannelMuted(content, data.json.args.audioChannel, + data.json.args.muted); + sendAsyncMsg('got-set-audio-channel-muted', { + id: data.json.id, successRv: true + }); + }, + + _recvIsAudioChannelActive: function(data) { + debug("Received isAudioChannelActive message: (" + data.json.id + ")"); + + let active = acs.isAudioChannelActive(content, data.json.args.audioChannel); + sendAsyncMsg('got-is-audio-channel-active', { + id: data.json.id, successRv: active + }); + }, + _initFinder: function() { if (!this._finder) { try { diff --git a/dom/browser-element/BrowserElementParent.cpp b/dom/browser-element/BrowserElementParent.cpp index 8c77ba46bc2..d1094c12198 100644 --- a/dom/browser-element/BrowserElementParent.cpp +++ b/dom/browser-element/BrowserElementParent.cpp @@ -15,6 +15,7 @@ #endif #include "BrowserElementParent.h" +#include "BrowserElementAudioChannel.h" #include "mozilla/EventDispatcher.h" #include "mozilla/dom/HTMLIFrameElement.h" #include "mozilla/dom/ToJSValue.h" diff --git a/dom/browser-element/BrowserElementParent.js b/dom/browser-element/BrowserElementParent.js index 4dfa4a9ab92..44a0c3e5a0e 100644 --- a/dom/browser-element/BrowserElementParent.js +++ b/dom/browser-element/BrowserElementParent.js @@ -206,7 +206,12 @@ BrowserElementParent.prototype = { "selectionstatechanged": this._handleSelectionStateChanged, "scrollviewchange": this._handleScrollViewChange, "caretstatechanged": this._handleCaretStateChanged, - "findchange": this._handleFindChange + "findchange": this._handleFindChange, + "got-audio-channel-volume": this._gotDOMRequestResult, + "got-set-audio-channel-volume": this._gotDOMRequestResult, + "got-audio-channel-muted": this._gotDOMRequestResult, + "got-set-audio-channel-muted": this._gotDOMRequestResult, + "got-is-audio-channel-active": this._gotDOMRequestResult }; let mmSecuritySensitiveCalls = { @@ -973,6 +978,33 @@ BrowserElementParent.prototype = { } }, + getAudioChannelVolume: function(aAudioChannel) { + return this._sendDOMRequest('get-audio-channel-volume', + {audioChannel: aAudioChannel}); + }, + + setAudioChannelVolume: function(aAudioChannel, aVolume) { + return this._sendDOMRequest('set-audio-channel-volume', + {audioChannel: aAudioChannel, + volume: aVolume}); + }, + + getAudioChannelMuted: function(aAudioChannel) { + return this._sendDOMRequest('get-audio-channel-muted', + {audioChannel: aAudioChannel}); + }, + + setAudioChannelMuted: function(aAudioChannel, aMuted) { + return this._sendDOMRequest('set-audio-channel-muted', + {audioChannel: aAudioChannel, + muted: aMuted}); + }, + + isAudioChannelActive: function(aAudioChannel) { + return this._sendDOMRequest('get-is-audio-channel-active', + {audioChannel: aAudioChannel}); + }, + /** * Called when the visibility of the window which owns this iframe changes. */ diff --git a/dom/browser-element/mochitest/browserElement_AudioChannel.js b/dom/browser-element/mochitest/browserElement_AudioChannel.js new file mode 100644 index 00000000000..f61ff70bf81 --- /dev/null +++ b/dom/browser-element/mochitest/browserElement_AudioChannel.js @@ -0,0 +1,153 @@ +/* Any copyright is dedicated to the public domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Bug 1113086 - tests for AudioChannel API into BrowserElement + +"use strict"; + +SimpleTest.waitForExplicitFinish(); +browserElementTestHelpers.setEnabledPref(true); +browserElementTestHelpers.addPermission(); + +SpecialPowers.setBoolPref("dom.testing.browserElementAudioChannel.noapp", true); +SpecialPowers.setBoolPref("media.useAudioChannelService", true); + +function noaudio() { + var iframe = document.createElement('iframe'); + iframe.setAttribute('mozbrowser', 'true'); + iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp'); + iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_empty.html'; + + function noaudio_loadend() { + ok("allowedAudioChannels" in iframe, "allowedAudioChannels exist"); + var channels = iframe.allowedAudioChannels; + is(channels.length, 1, "1 audio channel by default"); + + var ac = channels[0]; + + ok(ac instanceof BrowserElementAudioChannel, "Correct class"); + ok("getVolume" in ac, "ac.getVolume exists"); + ok("setVolume" in ac, "ac.setVolume exists"); + ok("getMuted" in ac, "ac.getMuted exists"); + ok("setMuted" in ac, "ac.setMuted exists"); + ok("isActive" in ac, "ac.isActive exists"); + + new Promise(function(r, rr) { + var req = ac.getVolume(); + ok(req instanceof DOMRequest, "This is a domRequest."); + req.onsuccess = function(e) { + is(e.target.result, 1.0, "The default volume should be 1.0"); + r(); + } + }) + + .then(function() { + return new Promise(function(r, rr) { + ac.getMuted().onsuccess = function(e) { + is(e.target.result, false, "The default muted value should be false"); + r(); + } + }); + }) + + .then(function() { + return new Promise(function(r, rr) { + ac.setVolume(0.8).onsuccess = function() { r(); } + }); + }) + + .then(function() { + return new Promise(function(r, rr) { + ac.getVolume().onsuccess = function(e) { + // the actual value is 0.800000011920929.. + ok(Math.abs(0.8 - e.target.result) < 0.01, "The new volume should be 0.8: " + e.target.result); + r(); + } + }); + }) + + .then(function() { + return new Promise(function(r, rr) { + ac.setVolume(1.0).onsuccess = function() { r(); } + }); + }) + + .then(function() { + return new Promise(function(r, rr) { + ac.setMuted(true).onsuccess = function() { r(); } + }); + }) + + .then(function() { + return new Promise(function(r, rr) { + ac.getMuted().onsuccess = function(e) { + is(e.target.result, true, "The new muted value should be true"); + r(); + } + }); + }) + + .then(function() { + return new Promise(function(r, rr) { + ac.isActive().onsuccess = function(e) { + is(e.target.result, false, "ac.isActive is false: no audio element active."); + r(); + } + }); + }) + + .then(runTests); + } + + iframe.addEventListener('mozbrowserloadend', noaudio_loadend); + document.body.appendChild(iframe); +} + +function audio() { + var iframe = document.createElement('iframe'); + iframe.setAttribute('mozbrowser', 'true'); + iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp'); + iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/iframe_file_audio.html'; + + function audio_loadend() { + ok("allowedAudioChannels" in iframe, "allowedAudioChannels exist"); + var channels = iframe.allowedAudioChannels; + is(channels.length, 1, "1 audio channel by default"); + + var ac = channels[0]; + + ok(ac instanceof BrowserElementAudioChannel, "Correct class"); + ok("getVolume" in ac, "ac.getVolume exists"); + ok("setVolume" in ac, "ac.setVolume exists"); + ok("getMuted" in ac, "ac.getMuted exists"); + ok("setMuted" in ac, "ac.setMuted exists"); + ok("isActive" in ac, "ac.isActive exists"); + + ac.onactivestatechanged = function() { + ok("activestatechanged event received."); + ac.onactivestatechanged = null; + runTests(); + } + } + + iframe.addEventListener('mozbrowserloadend', audio_loadend); + document.body.appendChild(iframe); +} + +var tests = [ noaudio, audio ]; + +function runTests() { + if (tests.length == 0) { + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); +} + + +addEventListener('load', function() { + SimpleTest.executeSoon(runTests); +}); + diff --git a/dom/browser-element/mochitest/file_audio.html b/dom/browser-element/mochitest/file_audio.html new file mode 100644 index 00000000000..38ea1c41f3d --- /dev/null +++ b/dom/browser-element/mochitest/file_audio.html @@ -0,0 +1,15 @@ + + +