Bug 1113086 - AudioChannel policy in Browser API - patch 5 - nsTObserverArray instead hashtables, r=ehsan, r=alwu

This commit is contained in:
Andrea Marchesini 2015-07-10 17:38:51 +01:00
Родитель 7fe6a31b4a
Коммит 2ba53b5aab
6 изменённых файлов: 208 добавлений и 174 удалений

Просмотреть файл

@ -91,6 +91,10 @@ AudioChannelAgent::InitInternal(nsIDOMWindow* aWindow, int32_t aChannelType,
MOZ_ASSERT(topWindow);
mWindow = do_QueryInterface(topWindow);
if (!mWindow) {
return NS_ERROR_FAILURE;
}
mWindow = mWindow->GetOuterWindow();
}

Просмотреть файл

@ -64,5 +64,5 @@ private:
} // namespace dom
} // namespace mozilla
#endif
#endif

Просмотреть файл

@ -190,12 +190,14 @@ AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
}
uint64_t windowID = aAgent->WindowID();
AudioChannelWindow* winData = mWindows.LookupOrAdd(windowID);
AudioChannelWindow* winData = GetWindowData(windowID);
if (!winData) {
winData = new AudioChannelWindow(windowID);
mWindows.AppendElement(winData);
}
MOZ_ASSERT(!winData->mAgents.Get(aAgent));
AudioChannel* audioChannel = new AudioChannel(aChannel);
winData->mAgents.Put(aAgent, audioChannel);
MOZ_ASSERT(!winData->mAgents.Contains(aAgent));
winData->mAgents.AppendElement(aAgent);
++winData->mChannels[(uint32_t)aChannel].mNumberOfAgents;
@ -205,7 +207,7 @@ AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
}
// If this is the first agent for this window, we must notify the observers.
if (winData->mAgents.Count() == 1) {
if (winData->mAgents.Length() == 1) {
nsCOMPtr<nsIObserverService> observerService =
services::GetObserverService();
if (observerService) {
@ -225,22 +227,25 @@ AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
return;
}
uint64_t windowID = aAgent->WindowID();
AudioChannelWindow* winData = nullptr;
if (!mWindows.Get(windowID, &winData)) {
AudioChannelWindow* winData = GetWindowData(aAgent->WindowID());
if (!winData) {
return;
}
nsAutoPtr<AudioChannel> audioChannel;
winData->mAgents.RemoveAndForget(aAgent, audioChannel);
if (audioChannel) {
MOZ_ASSERT(winData->mChannels[(uint32_t)*audioChannel].mNumberOfAgents > 0);
if (winData->mAgents.Contains(aAgent)) {
int32_t channel = aAgent->AudioChannelType();
uint64_t windowID = aAgent->WindowID();
--winData->mChannels[(uint32_t)*audioChannel].mNumberOfAgents;
// aAgent can be freed after this call.
winData->mAgents.RemoveElement(aAgent);
MOZ_ASSERT(winData->mChannels[channel].mNumberOfAgents > 0);
--winData->mChannels[channel].mNumberOfAgents;
// The last one, we must inform the BrowserElementAudioChannel.
if (winData->mChannels[(uint32_t)*audioChannel].mNumberOfAgents == 0) {
NotifyChannelActive(aAgent->WindowID(), *audioChannel, false);
if (winData->mChannels[channel].mNumberOfAgents == 0) {
NotifyChannelActive(windowID, static_cast<AudioChannel>(channel), false);
}
}
@ -252,7 +257,7 @@ AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent)
#endif
// If this is the last agent for this window, we must notify the observers.
if (winData->mAgents.Count() == 0) {
if (winData->mAgents.IsEmpty()) {
nsCOMPtr<nsIObserverService> observerService =
services::GetObserverService();
if (observerService) {
@ -286,7 +291,8 @@ AudioChannelService::GetState(nsPIDOMWindow* aWindow, uint32_t aAudioChannel,
// 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)) {
winData = GetWindowData(window->WindowID());
if (winData) {
*aVolume *= winData->mChannels[aAudioChannel].mVolume;
*aMuted = *aMuted || winData->mChannels[aAudioChannel].mMuted;
}
@ -306,55 +312,30 @@ AudioChannelService::GetState(nsPIDOMWindow* aWindow, uint32_t aAudioChannel,
} while (window && window != aWindow);
}
PLDHashOperator
AudioChannelService::TelephonyChannelIsActiveEnumerator(
const uint64_t& aWindowID,
nsAutoPtr<AudioChannelWindow>& aWinData,
void* aPtr)
{
bool* isActive = static_cast<bool*>(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;
}
PLDHashOperator
AudioChannelService::TelephonyChannelIsActiveInChildrenEnumerator(
const uint64_t& aChildID,
nsAutoPtr<AudioChannelChildStatus>& aData,
void* aPtr)
{
bool* isActive = static_cast<bool*>(aPtr);
*isActive = aData->mActiveTelephonyChannel;
return *isActive ? PL_DHASH_STOP : PL_DHASH_NEXT;
}
bool
AudioChannelService::TelephonyChannelIsActive()
{
bool active = false;
mWindows.Enumerate(TelephonyChannelIsActiveEnumerator, &active);
if (!active && IsParentProcess()) {
mPlayingChildren.Enumerate(TelephonyChannelIsActiveInChildrenEnumerator,
&active);
nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator iter(mWindows);
while (iter.HasMore()) {
AudioChannelWindow* next = iter.GetNext();
if (next->mChannels[(uint32_t)AudioChannel::Telephony].mNumberOfAgents != 0 &&
!next->mChannels[(uint32_t)AudioChannel::Telephony].mMuted) {
return true;
}
}
return active;
}
if (IsParentProcess()) {
nsTObserverArray<nsAutoPtr<AudioChannelChildStatus>>::ForwardIterator
iter(mPlayingChildren);
while (iter.HasMore()) {
AudioChannelChildStatus* child = iter.GetNext();
if (child->mActiveTelephonyChannel) {
return true;
}
}
}
PLDHashOperator
AudioChannelService::ContentOrNormalChannelIsActiveEnumerator(
const uint64_t& aWindowID,
nsAutoPtr<AudioChannelWindow>& aWinData,
void* aPtr)
{
bool* isActive = static_cast<bool*>(aPtr);
*isActive =
aWinData->mChannels[(uint32_t)AudioChannel::Content].mNumberOfAgents > 0 ||
aWinData->mChannels[(uint32_t)AudioChannel::Normal].mNumberOfAgents > 0;
return *isActive ? PL_DHASH_STOP : PL_DHASH_NEXT;
return false;
}
bool
@ -363,51 +344,76 @@ AudioChannelService::ContentOrNormalChannelIsActive()
// This method is meant to be used just by the child to send status update.
MOZ_ASSERT(!IsParentProcess());
bool active = false;
mWindows.Enumerate(ContentOrNormalChannelIsActiveEnumerator, &active);
return active;
nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator iter(mWindows);
while (iter.HasMore()) {
AudioChannelWindow* next = iter.GetNext();
if (next->mChannels[(uint32_t)AudioChannel::Content].mNumberOfAgents > 0 ||
next->mChannels[(uint32_t)AudioChannel::Normal].mNumberOfAgents > 0) {
return true;
}
}
return false;
}
AudioChannelService::AudioChannelChildStatus*
AudioChannelService::GetChildStatus(uint64_t aChildID) const
{
nsTObserverArray<nsAutoPtr<AudioChannelChildStatus>>::ForwardIterator
iter(mPlayingChildren);
while (iter.HasMore()) {
AudioChannelChildStatus* child = iter.GetNext();
if (child->mChildID == aChildID) {
return child;
}
}
return nullptr;
}
void
AudioChannelService::RemoveChildStatus(uint64_t aChildID)
{
nsTObserverArray<nsAutoPtr<AudioChannelChildStatus>>::ForwardIterator
iter(mPlayingChildren);
while (iter.HasMore()) {
nsAutoPtr<AudioChannelChildStatus>& child = iter.GetNext();
if (child->mChildID == aChildID) {
mPlayingChildren.RemoveElement(child);
break;
}
}
}
bool
AudioChannelService::ProcessContentOrNormalChannelIsActive(uint64_t aChildID)
{
AudioChannelChildStatus* status = mPlayingChildren.Get(aChildID);
if (!status) {
AudioChannelChildStatus* child = GetChildStatus(aChildID);
if (!child) {
return false;
}
return status->mActiveContentOrNormalChannel;
}
PLDHashOperator
AudioChannelService::AnyAudioChannelIsActiveEnumerator(
const uint64_t& aWindowID,
nsAutoPtr<AudioChannelWindow>& aWinData,
void* aPtr)
{
bool* isActive = static_cast<bool*>(aPtr);
for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) {
if (aWinData->mChannels[kMozAudioChannelAttributeTable[i].value].mNumberOfAgents
!= 0) {
*isActive = true;
break;
}
}
return *isActive ? PL_DHASH_STOP : PL_DHASH_NEXT;
return child->mActiveContentOrNormalChannel;
}
bool
AudioChannelService::AnyAudioChannelIsActive()
{
bool active = false;
mWindows.Enumerate(AnyAudioChannelIsActiveEnumerator, &active);
if (!active && IsParentProcess()) {
active = !!mPlayingChildren.Count();
nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator iter(mWindows);
while (iter.HasMore()) {
AudioChannelWindow* next = iter.GetNext();
for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) {
if (next->mChannels[kMozAudioChannelAttributeTable[i].value].mNumberOfAgents
!= 0) {
return true;
}
}
}
return active;
if (IsParentProcess()) {
return !mPlayingChildren.IsEmpty();
}
return false;
}
NS_IMETHODIMP
@ -467,10 +473,27 @@ AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic,
return rv;
}
nsAutoPtr<AudioChannelWindow> window;
mWindows.RemoveAndForget(innerID, window);
if (window) {
window->mAgents.EnumerateRead(NotifyEnumerator, nullptr);
nsAutoPtr<AudioChannelWindow> winData;
{
nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator
iter(mWindows);
while (iter.HasMore()) {
nsAutoPtr<AudioChannelWindow>& next = iter.GetNext();
if (next->mWindowID == innerID) {
uint32_t pos = mWindows.IndexOf(next);
winData = next.forget();
mWindows.RemoveElementAt(pos);
break;
}
}
}
if (winData) {
nsTObserverArray<AudioChannelAgent*>::ForwardIterator
iter(winData->mAgents);
while (iter.HasMore()) {
iter.GetNext()->WindowVolumeChanged();
}
}
#ifdef MOZ_WIDGET_GONK
@ -500,7 +523,7 @@ AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic,
mDefChannelChildID = CONTENT_PROCESS_ID_UNKNOWN;
}
mPlayingChildren.Remove(childID);
RemoveChildStatus(childID);
}
return NS_OK;
@ -516,25 +539,21 @@ struct RefreshAgentsVolumeData
nsTArray<nsRefPtr<AudioChannelAgent>> mAgents;
};
PLDHashOperator
AudioChannelService::RefreshAgentsVolumeEnumerator(
AudioChannelAgent* aAgent,
AudioChannel* aUnused,
void* aPtr)
{
MOZ_ASSERT(aAgent);
aAgent->WindowVolumeChanged();
return PL_DHASH_NEXT;
}
void
AudioChannelService::RefreshAgentsVolume(nsPIDOMWindow* aWindow)
{
AudioChannelWindow* winData = mWindows.Get(aWindow->WindowID());
MOZ_ASSERT(aWindow);
AudioChannelWindow* winData = GetWindowData(aWindow->WindowID());
if (!winData) {
return;
}
winData->mAgents.EnumerateRead(RefreshAgentsVolumeEnumerator, nullptr);
nsTObserverArray<AudioChannelAgent*>::ForwardIterator
iter(winData->mAgents);
while (iter.HasMore()) {
iter.GetNext()->WindowVolumeChanged();
}
}
/* static */ const nsAttrValue::EnumTable*
@ -603,15 +622,35 @@ AudioChannelService::GetDefaultAudioChannelString(nsAString& aString)
}
}
AudioChannelService::AudioChannelWindow&
AudioChannelService::AudioChannelWindow*
AudioChannelService::GetOrCreateWindowData(nsPIDOMWindow* aWindow)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aWindow->IsOuterWindow());
AudioChannelWindow* winData = mWindows.LookupOrAdd(aWindow->WindowID());
return *winData;
AudioChannelWindow* winData = GetWindowData(aWindow->WindowID());
if (!winData) {
winData = new AudioChannelWindow(aWindow->WindowID());
mWindows.AppendElement(winData);
}
return winData;
}
AudioChannelService::AudioChannelWindow*
AudioChannelService::GetWindowData(uint64_t aWindowID) const
{
nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator
iter(mWindows);
while (iter.HasMore()) {
AudioChannelWindow* next = iter.GetNext();
if (next->mWindowID == aWindowID) {
return next;
}
}
return nullptr;
}
float
@ -622,8 +661,8 @@ AudioChannelService::GetAudioChannelVolume(nsPIDOMWindow* aWindow,
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aWindow->IsOuterWindow());
AudioChannelWindow& winData = GetOrCreateWindowData(aWindow);
return winData.mChannels[(uint32_t)aAudioChannel].mVolume;
AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
return winData->mChannels[(uint32_t)aAudioChannel].mVolume;
}
NS_IMETHODIMP
@ -645,8 +684,8 @@ AudioChannelService::SetAudioChannelVolume(nsPIDOMWindow* aWindow,
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aWindow->IsOuterWindow());
AudioChannelWindow& winData = GetOrCreateWindowData(aWindow);
winData.mChannels[(uint32_t)aAudioChannel].mVolume = aVolume;
AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
winData->mChannels[(uint32_t)aAudioChannel].mVolume = aVolume;
RefreshAgentsVolume(aWindow);
}
@ -668,8 +707,8 @@ AudioChannelService::GetAudioChannelMuted(nsPIDOMWindow* aWindow,
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aWindow->IsOuterWindow());
AudioChannelWindow& winData = GetOrCreateWindowData(aWindow);
return winData.mChannels[(uint32_t)aAudioChannel].mMuted;
AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
return winData->mChannels[(uint32_t)aAudioChannel].mMuted;
}
NS_IMETHODIMP
@ -691,8 +730,8 @@ AudioChannelService::SetAudioChannelMuted(nsPIDOMWindow* aWindow,
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aWindow->IsOuterWindow());
AudioChannelWindow& winData = GetOrCreateWindowData(aWindow);
winData.mChannels[(uint32_t)aAudioChannel].mMuted = aMuted;
AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
winData->mChannels[(uint32_t)aAudioChannel].mMuted = aMuted;
RefreshAgentsVolume(aWindow);
}
@ -714,8 +753,8 @@ AudioChannelService::IsAudioChannelActive(nsPIDOMWindow* aWindow,
MOZ_ASSERT(aWindow);
MOZ_ASSERT(aWindow->IsOuterWindow());
AudioChannelWindow& winData = GetOrCreateWindowData(aWindow);
return !!winData.mChannels[(uint32_t)aAudioChannel].mNumberOfAgents;
AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
return !!winData->mChannels[(uint32_t)aAudioChannel].mNumberOfAgents;
}
NS_IMETHODIMP
@ -822,11 +861,16 @@ AudioChannelService::ChildStatusReceived(uint64_t aChildID,
bool aAnyChannel)
{
if (!aAnyChannel) {
mPlayingChildren.Remove(aChildID);
RemoveChildStatus(aChildID);
return;
}
AudioChannelChildStatus* data = mPlayingChildren.LookupOrAdd(aChildID);
AudioChannelChildStatus* data = GetChildStatus(aChildID);
if (!data) {
data = new AudioChannelChildStatus(aChildID);
mPlayingChildren.AppendElement(data);
}
data->mActiveTelephonyChannel = aTelephonyChannel;
data->mActiveContentOrNormalChannel = aContentOrNormalChannel;
}
@ -836,13 +880,3 @@ AudioChannelService::IsAudioChannelMutedByDefault()
{
return sAudioChannelMutedByDefault;
}
/* static */ PLDHashOperator
AudioChannelService::NotifyEnumerator(AudioChannelAgent* aAgent,
AudioChannel* aAudioChannel,
void* aUnused)
{
aAgent->WindowVolumeChanged();
return PL_DHASH_NEXT;
}

Просмотреть файл

@ -10,11 +10,11 @@
#include "nsIAudioChannelService.h"
#include "nsAutoPtr.h"
#include "nsIObserver.h"
#include "nsTObserverArray.h"
#include "nsTArray.h"
#include "AudioChannelAgent.h"
#include "nsAttrValue.h"
#include "nsClassHashtable.h"
#include "mozilla/dom/AudioChannelBinding.h"
class nsIRunnable;
@ -160,58 +160,45 @@ private:
struct AudioChannelWindow final
{
explicit AudioChannelWindow(uint64_t aWindowID)
: mWindowID(aWindowID)
{}
uint64_t mWindowID;
AudioChannelConfig mChannels[NUMBER_OF_AUDIO_CHANNELS];
nsClassHashtable<nsPtrHashKey<AudioChannelAgent>, AudioChannel> mAgents;
// Raw pointer because the AudioChannelAgent must unregister itself.
nsTObserverArray<AudioChannelAgent*> mAgents;
};
AudioChannelWindow&
AudioChannelWindow*
GetOrCreateWindowData(nsPIDOMWindow* aWindow);
static PLDHashOperator
TelephonyChannelIsActiveEnumerator(const uint64_t& aWindowID,
nsAutoPtr<AudioChannelWindow>& aWinData,
void *aPtr);
AudioChannelWindow*
GetWindowData(uint64_t aWindowID) const;
static PLDHashOperator
ContentOrNormalChannelIsActiveEnumerator(
const uint64_t& aWindowID,
nsAutoPtr<AudioChannelWindow>& aWinData,
void *aPtr);
static PLDHashOperator
AnyAudioChannelIsActiveEnumerator(const uint64_t& aWindowID,
nsAutoPtr<AudioChannelWindow>& aWinData,
void *aPtr);
static PLDHashOperator
RefreshAgentsVolumeEnumerator(AudioChannelAgent* aAgent,
AudioChannel* aUnused,
void *aPtr);
static PLDHashOperator
NotifyEnumerator(AudioChannelAgent* aAgent,
AudioChannel* aAudioChannel,
void* aUnused);
nsClassHashtable<nsUint64HashKey, AudioChannelWindow> mWindows;
struct AudioChannelChildStatus final {
AudioChannelChildStatus()
: mActiveTelephonyChannel(false)
struct AudioChannelChildStatus final
{
explicit AudioChannelChildStatus(uint64_t aChildID)
: mChildID(aChildID)
, mActiveTelephonyChannel(false)
, mActiveContentOrNormalChannel(false)
{}
uint64_t mChildID;
bool mActiveTelephonyChannel;
bool mActiveContentOrNormalChannel;
};
static PLDHashOperator
TelephonyChannelIsActiveInChildrenEnumerator(
const uint64_t& aChildID,
nsAutoPtr<AudioChannelChildStatus>& aData,
void *aPtr);
AudioChannelChildStatus*
GetChildStatus(uint64_t aChildID) const;
nsClassHashtable<nsUint64HashKey, AudioChannelChildStatus> mPlayingChildren;
void
RemoveChildStatus(uint64_t aChildID);
nsTObserverArray<nsAutoPtr<AudioChannelWindow>> mWindows;
nsTObserverArray<nsAutoPtr<AudioChannelChildStatus>> mPlayingChildren;
#ifdef MOZ_WIDGET_GONK
nsTArray<SpeakerManagerService*> mSpeakerManager;

Просмотреть файл

@ -29,7 +29,7 @@ interface nsIAudioChannelAgentCallback : nsISupports
* 1. Changes to the playable status of this channel.
*/
[uuid(363ff8d3-5bd2-485a-84ac-125062cbdc19)]
[uuid(11138a74-ee64-4a91-9457-f60a5f4527c1)]
interface nsIAudioChannelAgent : nsISupports
{
const long AUDIO_AGENT_CHANNEL_NORMAL = 0;
@ -51,6 +51,13 @@ interface nsIAudioChannelAgent : nsISupports
*/
readonly attribute long audioChannelType;
%{C++
inline int32_t AudioChannelType() {
int32_t channel;
return NS_SUCCEEDED(GetAudioChannelType(&channel)) ? channel : AUDIO_AGENT_CHANNEL_ERROR;
}
%}
/**
* Initialize the agent with a channel type.
* Note: This function should only be called once.

Просмотреть файл

@ -220,6 +220,8 @@ var interfaceNamesInGlobalScope =
permission: ["bluetooth"]},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "BoxObject", xbl: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "BrowserElementAudioChannel", b2g: true, permission: ["browser"] },
// IMPORTANT: Do not change this list without review from a DOM peer!
"BroadcastChannel",
// IMPORTANT: Do not change this list without review from a DOM peer!