Bug 1113086 - AudioChannel policy in Browser API - patch 1 - BrowserElementAudioChannel, r=ehsan

This commit is contained in:
Andrea Marchesini 2015-07-10 17:38:44 +01:00 committed by Ehsan Akhgari
parent 76a19944b1
commit 80b5f477f2
74 changed files with 1773 additions and 3109 deletions

View File

@ -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

View File

@ -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<nsPIDOMWindow> pWindow = do_QueryInterface(aWindow);
if (!pWindow->IsInnerWindow()) {
pWindow = pWindow->GetCurrentInnerWindow();
}
nsCOMPtr<nsIDOMWindow> 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<AudioChannelService> service = AudioChannelService::GetOrCreate();
if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
service == nullptr || mIsRegToService) {
return NS_ERROR_FAILURE;
}
service->RegisterAudioChannelAgent(this,
static_cast<AudioChannel>(mAudioChannelType), mWithVideo);
*_retval = service->GetState(this, !mVisible);
static_cast<AudioChannel>(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<AudioChannelService> 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<nsIAudioChannelAgentCallback> 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<nsIAudioChannelAgentCallback> callback = GetCallback();
if (callback) {
AudioChannelService *service = AudioChannelService::GetOrCreateAudioChannelService();
callback->CanPlayChanged(service->GetState(this, !mVisible));
}
}
already_AddRefed<nsIAudioChannelAgentCallback>
AudioChannelAgent::GetCallback()
{
@ -193,20 +158,17 @@ AudioChannelAgent::WindowVolumeChanged()
return;
}
callback->WindowVolumeChanged();
float volume = 1.0;
bool muted = false;
nsRefPtr<AudioChannelService> 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<nsPIDOMWindow> win = do_QueryInterface(mWindow);
if (!win) {
*aVolume = 1.0f;
return NS_OK;
}
*aVolume = win->GetAudioGlobalVolume();
return NS_OK;
return mWindow ? mWindow->WindowID() : 0;
}

View File

@ -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<nsIDOMWindow> mWindow;
nsCOMPtr<nsPIDOMWindow> mWindow;
nsCOMPtr<nsIAudioChannelAgentCallback> mCallback;
nsWeakPtr mWeakCallback;
int32_t mAudioChannelType;
bool mIsRegToService;
bool mVisible;
bool mWithVideo;
};
} // namespace dom

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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<AudioChannelService> 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<nsPtrHashKey<AudioChannelAgent>, AudioChannel> mAgents;
};
AudioChannelWindow&
GetOrCreateWindowData(nsPIDOMWindow* aWindow);
static PLDHashOperator
NotifyEnumerator(AudioChannelAgent* aAgent,
AudioChannelAgentData* aData, void *aUnused);
TelephonyChannelIsActiveEnumerator(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,
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<AudioChannelAgentData>& aData,
void *aPtr);
nsClassHashtable<nsUint64HashKey, AudioChannelWindow> mWindows;
// This returns the number of agents from this aWindow.
uint32_t CountWindow(nsIDOMWindow* aWindow);
nsClassHashtable< nsPtrHashKey<AudioChannelAgent>, AudioChannelAgentData > mAgents;
#ifdef MOZ_WIDGET_GONK
nsTArray<SpeakerManagerService*> mSpeakerManager;
#endif
nsTArray<uint64_t> mChannelCounters[AUDIO_CHANNEL_INT_LAST];
int32_t mCurrentHigherChannel;
int32_t mCurrentVisibleHigherChannel;
nsTArray<uint64_t> 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<TelephonyChild> 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<nsIRunnable> mRunnable;
nsCOMPtr<nsITimer> mDeferTelChannelTimer;
bool mTimerElementHidden;
uint64_t mTimerChildID;
uint64_t mDefChannelChildID;
// This is needed for IPC comunication between

View File

@ -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<AudioChannelServiceChild> 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<AudioChannelServiceChild> 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<nsIObserverService> 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<nsIObserverService> 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);
}
}

View File

@ -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

View File

@ -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

View File

@ -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;
};

View File

@ -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);
};

View File

@ -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);
});

View File

@ -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 <windows.h>
#else
#include <unistd.h>
#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<nsIThread> 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<int32_t>(mChannel),
this, true);
}
else {
rv = mAgent->InitWithWeakCallback(nullptr, static_cast<int32_t>(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<AudioChannelState>(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<nsIAudioChannelAgent> mAgent;
AudioChannel mChannel;
bool mWaitCallback;
bool mRegistered;
AudioChannelState mCanPlay;
};
NS_IMPL_ISUPPORTS(Agent, nsIAudioChannelAgentCallback,
nsISupportsWeakReference)
nsresult
TestDoubleStartPlaying()
{
nsRefPtr<Agent> 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> 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<Agent> agent1 = new Agent(AudioChannel::Normal);
nsresult rv = agent1->Init();
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<Agent> 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<Agent> agent1 = new Agent(AudioChannel::Content);
nsresult rv = agent1->Init();
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<Agent> 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<Agent> normalAgent = new Agent(AudioChannel::Normal);
nsresult rv = normalAgent->Init();
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<Agent> contentAgent = new Agent(AudioChannel::Content);
rv = contentAgent->Init();
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<Agent> 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<Agent> normalAgent = new Agent(AudioChannel::Normal);
nsresult rv = normalAgent->Init();
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<Agent> contentAgent = new Agent(AudioChannel::Content);
rv = contentAgent->Init();
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<Agent> notificationAgent = new Agent(AudioChannel::Notification);
rv = notificationAgent->Init();
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<Agent> alarmAgent = new Agent(AudioChannel::Alarm);
rv = alarmAgent->Init();
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<Agent> telephonyAgent = new Agent(AudioChannel::Telephony);
rv = telephonyAgent->Init();
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<Agent> ringerAgent = new Agent(AudioChannel::Ringer);
rv = ringerAgent->Init();
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<Agent> 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<Agent> agent1 = new Agent(AudioChannel::Normal);
nsresult rv = agent1->Init(true);
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<Agent> 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;
}

Binary file not shown.

View File

@ -1,99 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test audio-channel-changed & visible-audio-channel-changed mozChromeEvent</title>
</head>
<body>
<div id="content"></div>
<script>
var normalAudio;
var contentAudio;
var notificationAudio;
var alarmAudio;
var telephonyAudio;
var ringerAudio;
var publicnotificationAudio;
function playWithAudioType(audio, type) {
audio.mozAudioChannelType = type;
audio.src = "test.ogg";
audio.loop = true;
audio.play();
}
function runTest() {
// normal channel.
normalAudio = new Audio();
playWithAudioType(normalAudio, 'normal');
// content channel.
contentAudio = new Audio();
playWithAudioType(contentAudio, 'content');
// notification channel.
notificationAudio = new Audio();
playWithAudioType(notificationAudio, 'notification');
// alarm channel.
alarmAudio = new Audio();
playWithAudioType(alarmAudio, 'alarm');
// telephony channel.
telephonyAudio = new Audio();
playWithAudioType(telephonyAudio, 'telephony');
// ringer channel.
ringerAudio = new Audio();
playWithAudioType(ringerAudio, 'ringer');
// publicnotification channel.
publicnotificationAudio = new Audio();
playWithAudioType(publicnotificationAudio, 'publicnotification');
window.addEventListener('hashchange', function(event) {
if (location.hash == "#pauseAudio") {
publicnotificationAudio.pause();
ringerAudio.pause();
telephonyAudio.pause();
}
if (location.hash == "#pauseAudioFollowing") {
alarmAudio.pause();
notificationAudio.pause();
contentAudio.pause();
normalAudio.pause();
}
}, false);
}
function checkBackgroundStatus() {
if (location.hash == "#fg") {
runTest();
return;
}
if (document.hidden) {
runTest();
return;
}
document.addEventListener('visibilitychange', function visibilityChange() {
if (document.hidden) {
runTest();
}
});
}
SpecialPowers.pushPermissions(
[{ "type": "audio-channel-content", "allow": 1, "context": document },
{ "type": "audio-channel-notification", "allow": 1, "context": document },
{ "type": "audio-channel-alarm", "allow": 1, "context": document },
{ "type": "audio-channel-telephony", "allow": 1, "context": document },
{ "type": "audio-channel-ringer", "allow": 1, "context": document },
{ "type": "audio-channel-publicnotification", "allow": 1, "context": document }],
checkBackgroundStatus);
</script>
</body>
</html>

View File

@ -1,18 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test Telephony Channel Policy</title>
</head>
<body>
<div id="container"></div>
<script type="application/javascript;version=1.7">
var audio = new Audio();
audio.mozAudioChannelType = 'telephony';
audio.src = "audio.ogg";
audio.play();
</script>
</body>
</html>

View File

@ -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')

View File

@ -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

View File

@ -1,209 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test audio-channel-changed & visible-audio-channel-changed mozChromeEvent</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<div id="content"></div>
<script type="application/javascript;version=1.7">
var expectedAudioTypes;
var expectedVisibleAudioTypes;
var expectedVisibleAudioType;
var index;
var visibleIndex;
var iframe1;
var normalAudio;
function playWithAudioType(audio, type) {
audio.mozAudioChannelType = type;
audio.src = "test.ogg";
audio.loop = true;
audio.play();
}
function fgBgTestListener(message) {
var type = message.type;
var channel = message.channel;
if (type == 'audio-channel-changed') {
is(channel, expectedAudioTypes[index], channel + " is received and expected " + expectedAudioTypes[index]);
index++;
}
if (type == 'visible-audio-channel-changed') {
is(channel, expectedVisibleAudioType, channel + " is received and expected " + expectedVisibleAudioType);
}
// All audio types are playing now so ask to pause them.
// This call will stop audio from highest to telephony.
if ('cmd-pause' == expectedAudioTypes[index]) {
iframe1.src = 'file_audio.html#pauseAudio';
index++;
}
// According to there is a 1.5 second delay of releasing telephony,
// we need to wait for it then continue to pause others.
if ('cmd-secondPause' == expectedAudioTypes[index]) {
iframe1.src = 'file_audio.html#pauseAudioFollowing';
index++;
}
if (index == expectedAudioTypes.length) {
document.body.removeChild(iframe1);
script.removeMessageListener('chrome-event', fgBgTestListener);
normalAudio.pause();
SimpleTest.finish();
}
}
// Channel of visible-audio-channel-changed event should be always normal.
// Audios in background should not effect visible-audio-channel-changed.
function runFgBgTest() {
expectedAudioTypes = ["normal", "content", "notification",
"alarm", "telephony", "ringer", "publicnotification", "cmd-pause",
"ringer", "telephony", "alarm", "cmd-secondPause", "notification",
"content", "normal"];
expectedVisibleAudioType = "normal";
index = 0;
script.addMessageListener('chrome-event', fgBgTestListener);
// To play a audio with normal channel in the foreground.
normalAudio = new Audio();
playWithAudioType(normalAudio, 'normal');
iframe1.src = 'file_audio.html#bg';
document.body.appendChild(iframe1);
iframe1.setVisible(false);
}
function bgTestListener(message) {
var type = message.type;
var channel = message.channel;
if (type == 'audio-channel-changed') {
is(channel, expectedAudioTypes[index], channel + " is received and expected " + expectedAudioTypes[index]);
index++;
}
if (type == 'visible-audio-channel-changed') {
is(channel, expectedVisibleAudioType, channel + " is received and expected " + expectedVisibleAudioType);
}
// All audio types are playing now so ask to pause them.
if ('cmd-pause' == expectedAudioTypes[index]) {
iframe1.src = 'file_audio.html#pauseAudio';
index++;
}
if ('cmd-secondPause' == expectedAudioTypes[index]) {
iframe1.src = 'file_audio.html#pauseAudioFollowing';
index++;
}
if (index == expectedAudioTypes.length) {
document.body.removeChild(iframe1);
script.removeMessageListener('chrome-event', bgTestListener);
runFgBgTest();
}
}
// 1. Channel of visible-audio-channel-changed event should be always none.
// 2. normal is not allowed to be played in the background.
function runBgTest() {
expectedAudioTypes = ["content", "notification",
"alarm", "telephony", "ringer", "publicnotification", "cmd-pause",
"ringer", "telephony", "alarm", "cmd-secondPause", "notification",
"content", "none"];
expectedVisibleAudioType = "none";
index = 0;
script.addMessageListener('chrome-event', bgTestListener);
iframe1.src = 'file_audio.html#bg';
document.body.appendChild(iframe1);
iframe1.setVisible(false);
}
function fgTestListener(message) {
var type = message.type;
var channel = message.channel;
if (type == 'audio-channel-changed') {
is(channel, expectedAudioTypes[index], channel + " is received and expected " + expectedAudioTypes[index]);
index++;
}
if (type == 'visible-audio-channel-changed') {
is(channel, expectedAudioTypes[visibleIndex], channel + " is received and expected " + expectedAudioTypes[visibleIndex]);
visibleIndex++;
}
// All audio types are playing now so ask to pause them.
if ('cmd-pause' == expectedAudioTypes[visibleIndex] &&
'cmd-pause' == expectedAudioTypes[index]) {
iframe1.src = 'file_audio.html#pauseAudio';
visibleIndex++;
index++;
}
if ('cmd-secondPause' == expectedAudioTypes[visibleIndex] &&
'cmd-secondPause' == expectedAudioTypes[index]) {
iframe1.src = 'file_audio.html#pauseAudioFollowing';
visibleIndex++;
index++;
}
if (index == expectedAudioTypes.length && visibleIndex == expectedAudioTypes.length) {
document.body.removeChild(iframe1);
script.removeMessageListener('chrome-event', fgTestListener);
runBgTest();
}
}
// The foreground audio will effect both of audio-channel-changed and
// visible-audio-channel-changed.
function runFgTest() {
expectedAudioTypes = ["normal", "content", "notification",
"alarm", "telephony", "ringer", "publicnotification",
"cmd-pause", "ringer", "telephony", "alarm",
"cmd-secondPause", "notification", "content",
"normal", "none"];
index = 0;
visibleIndex = 0;
script.addMessageListener('chrome-event', fgTestListener);
iframe1 = document.createElement('iframe');
iframe1.setAttribute('mozbrowser', true);
iframe1.src = 'file_audio.html#fg';
document.body.appendChild(iframe1);
}
var url = SimpleTest.getTestFileURL("AudioChannelChromeScript.js")
var script = SpecialPowers.loadChromeScript(url);
script.sendAsyncMessage("init-chrome-event", {
type: 'audio-channel-changed'
});
script.sendAsyncMessage("init-chrome-event", {
type: 'visible-audio-channel-changed'
});
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPermissions(
[{ "type": "browser", "allow": 1, "context": document },
{ "type": "embed-apps", "allow": 1, "context": document },
{ "type": "webapps-manage", "allow": 1, "context": document }], function() {
SpecialPowers.pushPrefEnv({"set": [["dom.ipc.browser_frames.oop_by_default", true],
["media.useAudioChannelService", true],
["dom.mozBrowserFramesEnabled", true]]}, runFgTest);
});
</script>
</body>
</html>

View File

@ -1,87 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test the Telephony Channel Policy</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<div id="container"></div>
<script type="application/javascript;version=1.7">
function mainApp() {
var audio = new Audio();
audio.mozAudioChannelType = 'telephony';
audio.src = "audio.ogg";
audio.loop = true;
audio.play();
audio.addEventListener('mozinterruptbegin', function() {
ok(true, "This element has been muted!");
}, false);
audio.addEventListener('mozinterruptend', function() {
ok(true, "This element has been unmuted!");
audio.pause();
runTest();
}, false);
setTimeout(runTest, 600);
}
function newApp() {
var iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', true);
// That needs to be an app.
iframe.setAttribute('mozapp', 'https://acertified.com/manifest.webapp');
iframe.src = "file_telephonyPolicy.html";
document.body.appendChild(iframe);
}
var tests = [
// Permissions
function() {
SpecialPowers.pushPermissions(
[{ "type": "browser", "allow": 1, "context": document },
{ "type": "embed-apps", "allow": 1, "context": document },
{ "type": "webapps-manage", "allow": 1, "context": document },
{ "type": "audio-channel-telephony", "allow": 1, "context": document }], runTest);
},
// Preferences
function() {
SpecialPowers.pushPrefEnv({"set": [["dom.ipc.browser_frames.oop_by_default", true],
["media.useAudioChannelAPI", true],
["media.useAudioChannelService", true],
["media.defaultAudioChannel", "telephony"],
["dom.mozBrowserFramesEnabled", true],
["network.disable.ipc.security", true]]}, runTest);
},
// Run 2 apps
mainApp,
newApp,
];
function runTest() {
if (!tests.length) {
finish();
return;
}
var test = tests.shift();
test();
}
function finish() {
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout("untriaged");
runTest();
</script>
</body>
</html>

View File

@ -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")

View File

@ -3735,39 +3735,10 @@ nsPIDOMWindow::SetAudioVolume(float aVolume)
return NS_OK;
}
float
nsPIDOMWindow::GetAudioGlobalVolume()
{
float globalVolume = 1.0;
nsCOMPtr<nsPIDOMWindow> window = this;
do {
if (window->GetAudioMuted()) {
return 0;
}
globalVolume *= window->GetAudioVolume();
nsCOMPtr<nsIDOMWindow> 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<AudioChannelService> service =
AudioChannelService::GetOrCreateAudioChannelService();
nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
service->RefreshAgentsVolume(GetCurrentInnerWindow());
}

View File

@ -185,8 +185,6 @@ public:
float GetAudioVolume() const;
nsresult SetAudioVolume(float aVolume);
float GetAudioGlobalVolume();
virtual void SetServiceWorkersTestingEnabled(bool aEnabled)
{
MOZ_ASSERT(IsOuterWindow());

View File

@ -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<nsIObserverService> 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<nsIObserverService> 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<nsIDocShell> docShell;
nsresult rv = mFrameLoader->GetDocShell(getter_AddRefs(docShell));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (docShell) {
nsCOMPtr<nsPIDOMWindow> window = docShell->GetWindow();
if (!window) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDOMWindow> 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<JSObject*> 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<nsPIDOMWindow> mParentWindow;
nsCOMPtr<nsPIDOMWindow> mFrameWindow;
nsRefPtr<DOMRequest> 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<AudioChannelService> 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<JS::Value> 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<JS::Value> 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<JS::Value> 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<JS::Value> value(aCx);
mRequest->FireSuccess(value);
}
};
} // anonymous namespace
already_AddRefed<dom::DOMRequest>
BrowserElementAudioChannel::GetVolume(ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsInMainProcess();
if (!mFrameWindow) {
nsCOMPtr<nsIDOMDOMRequest> request;
aRv = mBrowserElementAPI->GetAudioChannelVolume((uint32_t)mAudioChannel,
getter_AddRefs(request));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return request.forget().downcast<DOMRequest>();
}
nsRefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
nsCOMPtr<nsIRunnable> runnable =
new GetVolumeRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel);
NS_DispatchToMainThread(runnable);
return domRequest.forget();
}
already_AddRefed<dom::DOMRequest>
BrowserElementAudioChannel::SetVolume(float aVolume, ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsInMainProcess();
if (!mFrameWindow) {
nsCOMPtr<nsIDOMDOMRequest> request;
aRv = mBrowserElementAPI->SetAudioChannelVolume((uint32_t)mAudioChannel,
aVolume,
getter_AddRefs(request));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return request.forget().downcast<DOMRequest>();
}
nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
MOZ_ASSERT(service);
service->SetAudioChannelVolume(mFrameWindow, mAudioChannel, aVolume);
nsRefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
nsCOMPtr<nsIRunnable> runnable = new FireSuccessRunnable(GetOwner(),
mFrameWindow,
domRequest,
mAudioChannel);
NS_DispatchToMainThread(runnable);
return domRequest.forget();
}
already_AddRefed<dom::DOMRequest>
BrowserElementAudioChannel::GetMuted(ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsInMainProcess();
if (!mFrameWindow) {
nsCOMPtr<nsIDOMDOMRequest> request;
aRv = mBrowserElementAPI->GetAudioChannelMuted((uint32_t)mAudioChannel,
getter_AddRefs(request));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return request.forget().downcast<DOMRequest>();
}
nsRefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
nsCOMPtr<nsIRunnable> runnable =
new GetMutedRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel);
NS_DispatchToMainThread(runnable);
return domRequest.forget();
}
already_AddRefed<dom::DOMRequest>
BrowserElementAudioChannel::SetMuted(bool aMuted, ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsInMainProcess();
if (!mFrameWindow) {
nsCOMPtr<nsIDOMDOMRequest> request;
aRv = mBrowserElementAPI->SetAudioChannelMuted((uint32_t)mAudioChannel,
aMuted,
getter_AddRefs(request));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return request.forget().downcast<DOMRequest>();
}
nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
MOZ_ASSERT(service);
service->SetAudioChannelMuted(mFrameWindow, mAudioChannel, aMuted);
nsRefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
nsCOMPtr<nsIRunnable> runnable = new FireSuccessRunnable(GetOwner(),
mFrameWindow,
domRequest,
mAudioChannel);
NS_DispatchToMainThread(runnable);
return domRequest.forget();
}
already_AddRefed<dom::DOMRequest>
BrowserElementAudioChannel::IsActive(ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread());
AssertIsInMainProcess();
if (mState != eStateUnknown) {
nsRefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
nsCOMPtr<nsIRunnable> runnable =
new IsActiveRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel,
mState == eStateActive);
NS_DispatchToMainThread(runnable);
return domRequest.forget();
}
if (!mFrameWindow) {
nsCOMPtr<nsIDOMDOMRequest> request;
aRv = mBrowserElementAPI->IsAudioChannelActive((uint32_t)mAudioChannel,
getter_AddRefs(request));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return request.forget().downcast<DOMRequest>();
}
nsRefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
nsCOMPtr<nsIRunnable> 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<nsISupportsPRUint64> 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

View File

@ -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<JSObject*> aGivenProto) override;
AudioChannel Name() const;
already_AddRefed<dom::DOMRequest> GetVolume(ErrorResult& aRv);
already_AddRefed<dom::DOMRequest> SetVolume(float aVolume, ErrorResult& aRv);
already_AddRefed<dom::DOMRequest> GetMuted(ErrorResult& aRv);
already_AddRefed<dom::DOMRequest> SetMuted(bool aMuted, ErrorResult& aRv);
already_AddRefed<dom::DOMRequest> IsActive(ErrorResult& aRv);
IMPL_EVENT_HANDLER(activestatechanged);
private:
~BrowserElementAudioChannel();
void ProcessStateChanged(const char16_t* aData);
nsCOMPtr<nsIFrameLoader> mFrameLoader;
nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI;
nsCOMPtr<nsITabParent> mTabParent;
nsCOMPtr<nsPIDOMWindow> mFrameWindow;
AudioChannel mAudioChannel;
enum {
eStateActive,
eStateInactive,
eStateUnknown
} mState;
};
} // dom namespace
} // mozilla namespace
#endif // mozilla_dom_BrowserElementAudioChannels_h

View File

@ -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 {

View File

@ -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"

View File

@ -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.
*/

View File

@ -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);
});

View File

@ -0,0 +1,15 @@
<html>
<body>
<audio src="http://mochi.test:8888/tests/dom/browser-element/mochitest/audio.ogg" id="audio" />
<script>
var audio = document.getElementById('audio');
audio.play();
audio.onended = function() {
setTimeout(function() {
audio.play();
}, 0);
}
</script>
</body>
</html>

View File

@ -0,0 +1,5 @@
<html>
<body>
<iframe src="file_audio.html"></iframe>
</body>
</html>

View File

@ -109,3 +109,4 @@ disabled = bug 924771
[test_browserElement_oop_ExposableURI.html]
disabled = bug 924771
[test_browserElement_oop_GetContentDimensions.html]
[test_browserElement_oop_AudioChannel.html]

View File

@ -75,6 +75,7 @@ support-files =
browserElement_XFrameOptionsSameOrigin.js
browserElement_XFrameOptionsSameOrigin.js
browserElement_GetContentDimensions.js
browserElement_AudioChannel.js
file_browserElement_AlertInFrame.html
file_browserElement_AlertInFrame_Inner.html
file_browserElement_AllowEmbedAppsInNestedOOIframe.html
@ -122,6 +123,8 @@ support-files =
file_inputmethod.html
file_post_request.html
file_wyciwyg.html
file_audio.html
iframe_file_audio.html
# Note: browserElementTestHelpers.js looks at the test's filename to determine
# whether the test should be OOP. "_oop_" signals OOP, "_inproc_" signals in
@ -223,3 +226,4 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
[test_browserElement_inproc_Reload.html]
disabled = bug 774100
[test_browserElement_inproc_GetContentDimensions.html]
[test_browserElement_inproc_AudioChannel.html]

View File

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test of browser element audioChannel.</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7" src="browserElement_AudioChannel.js">
</script>
</body>
</html>

View File

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test of browser element audioChannel.</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7" src="browserElement_AudioChannel.js">
</script>
</body>
</html>

View File

@ -8,7 +8,12 @@ EXPORTS.mozilla += [
'BrowserElementParent.h',
]
EXPORTS.mozilla.dom += [
'BrowserElementAudioChannel.h',
]
SOURCES += [
'BrowserElementAudioChannel.cpp',
'BrowserElementParent.cpp',
]

View File

@ -26,7 +26,7 @@ interface nsIBrowserElementNextPaintListener : nsISupports
* Interface to the BrowserElementParent implementation. All methods
* but setFrameLoader throw when the remote process is dead.
*/
[scriptable, uuid(8ecb598c-f886-11e4-9915-778f934fbf93)]
[scriptable, uuid(daa264b2-54df-4fc7-89b7-c9d02167c5d4)]
interface nsIBrowserElementAPI : nsISupports
{
const long FIND_CASE_SENSITIVE = 0;
@ -82,5 +82,13 @@ interface nsIBrowserElementAPI : nsISupports
nsIDOMDOMRequest setInputMethodActive(in boolean isActive);
nsIDOMDOMRequest getAudioChannelVolume(in uint32_t audioChannel);
nsIDOMDOMRequest setAudioChannelVolume(in uint32_t audioChannel, in float volume);
nsIDOMDOMRequest getAudioChannelMuted(in uint32_t audioChannel);
nsIDOMDOMRequest setAudioChannelMuted(in uint32_t audioChannel, in bool muted);
nsIDOMDOMRequest isAudioChannelActive(in uint32_t audioChannel);
void setNFCFocus(in boolean isFocus);
};

View File

@ -1135,8 +1135,12 @@ nsDOMCameraControl::NotifyRecordingStatusChange(const nsString& aMsg)
// Camera app will stop recording when it falls to the background, so no callback is necessary.
mAudioChannelAgent->Init(mWindow, (int32_t)AudioChannel::Content, nullptr);
// Video recording doesn't output any sound, so it's not necessary to check canPlay.
int32_t canPlay;
mAudioChannelAgent->StartPlaying(&canPlay);
float volume = 0.0;
bool muted = true;
rv = mAudioChannelAgent->StartPlaying(&volume, &muted);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
#endif
return rv;

View File

@ -135,13 +135,6 @@ FMRadio::Init(nsPIDOMWindow *aWindow)
RegisterSwitchObserver(SWITCH_HEADPHONES, this);
}
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
NS_ENSURE_TRUE_VOID(target);
target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
/* useCapture = */ true,
/* wantsUntrusted = */ false);
// All of the codes below are for AudioChannel. We can directly return here
// if preferences doesn't enable AudioChannelService.
NS_ENSURE_TRUE_VOID(Preferences::GetBool("media.useAudioChannelService"));
@ -155,13 +148,6 @@ FMRadio::Init(nsPIDOMWindow *aWindow)
nsIAudioChannelAgent::AUDIO_AGENT_CHANNEL_CONTENT,
this);
nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
NS_ENSURE_TRUE_VOID(docshell);
bool isActive = false;
docshell->GetIsActive(&isActive);
audioChannelAgent->SetVisibilityState(isActive);
// Once all necessary resources are got successfully, we just enabled
// mAudioChannelAgent.
mAudioChannelAgent = audioChannelAgent;
@ -176,11 +162,6 @@ FMRadio::Shutdown()
UnregisterSwitchObserver(SWITCH_HEADPHONES, this);
}
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
NS_ENSURE_TRUE_VOID(target);
target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
/* useCapture = */ true);
mIsShutdown = true;
}
@ -469,61 +450,30 @@ FMRadio::DisableRDS()
return r.forget();
}
NS_IMETHODIMP
FMRadio::HandleEvent(nsIDOMEvent* aEvent)
{
nsAutoString type;
aEvent->GetType(type);
if (!type.EqualsLiteral("visibilitychange")) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE);
bool isActive = false;
docshell->GetIsActive(&isActive);
mAudioChannelAgent->SetVisibilityState(isActive);
return NS_OK;
}
void
FMRadio::EnableAudioChannelAgent()
{
NS_ENSURE_TRUE_VOID(mAudioChannelAgent);
int32_t playingState = 0;
mAudioChannelAgent->StartPlaying(&playingState);
SetCanPlay(playingState == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL);
float volume = 0.0;
bool muted = true;
mAudioChannelAgent->StartPlaying(&volume, &muted);
WindowVolumeChanged(volume, muted);
mAudioChannelAgentEnabled = true;
}
NS_IMETHODIMP
FMRadio::CanPlayChanged(int32_t aCanPlay)
FMRadio::WindowVolumeChanged(float aVolume, bool aMuted)
{
SetCanPlay(!(aCanPlay == AudioChannelState::AUDIO_CHANNEL_STATE_MUTED));
IFMRadioService::Singleton()->EnableAudio(!aMuted);
// TODO: what about the volume?
return NS_OK;
}
NS_IMETHODIMP
FMRadio::WindowVolumeChanged()
{
return NS_ERROR_NOT_IMPLEMENTED;
}
void
FMRadio::SetCanPlay(bool aCanPlay)
{
IFMRadioService::Singleton()->EnableAudio(aCanPlay);
}
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FMRadio)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(FMRadio, DOMEventTargetHelper)

View File

@ -25,7 +25,6 @@ class FMRadio final : public DOMEventTargetHelper
, public FMRadioEventObserver
, public nsSupportsWeakReference
, public nsIAudioChannelAgentCallback
, public nsIDOMEventListener
{
friend class FMRadioRequest;
@ -109,13 +108,9 @@ public:
IMPL_EVENT_HANDLER(rtchange);
IMPL_EVENT_HANDLER(newrdsgroup);
// nsIDOMEventListener
NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
private:
~FMRadio();
void SetCanPlay(bool aCanPlay);
void EnableAudioChannelAgent();
hal::SwitchState mHeadphoneState;

View File

@ -14,7 +14,6 @@
#include "nsContentUtils.h"
#include "nsJSUtils.h"
#include "AudioSampleFormat.h"
#include "AudioChannelCommon.h"
#include <algorithm>
#include "nsComponentManagerUtils.h"
#include "nsIHttpChannel.h"

View File

@ -1831,16 +1831,7 @@ void HTMLMediaElement::SetMutedInternal(uint32_t aMuted)
void HTMLMediaElement::SetVolumeInternal()
{
float effectiveVolume = mMuted ? 0.0f :
mAudioChannelFaded ? float(mVolume) * FADED_VOLUME_RATIO : float(mVolume);
if (mAudioChannelAgent) {
float volume;
nsresult rv = mAudioChannelAgent->GetWindowVolume(&volume);
if (NS_SUCCEEDED(rv)) {
effectiveVolume *= volume;
}
}
float effectiveVolume = mMuted ? 0.0f : float(mVolume * mAudioChannelVolume);
if (mDecoder) {
mDecoder->SetVolume(effectiveVolume);
@ -2092,7 +2083,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
mCORSMode(CORS_NONE),
mIsEncrypted(false),
mDownloadSuspendedByCache(false, "HTMLMediaElement::mDownloadSuspendedByCache"),
mAudioChannelFaded(false),
mAudioChannelVolume(1.0),
mPlayingThroughTheAudioChannel(false),
mDisableVideo(false),
mPlayBlockedBecauseHidden(false),
@ -4043,13 +4034,6 @@ void HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
mDecoder->NotifyOwnerActivityChanged();
}
// SetVisibilityState will update mMuted with MUTED_BY_AUDIO_CHANNEL via the
// CanPlayChanged callback.
if (UseAudioChannelService() && mPlayingThroughTheAudioChannel &&
mAudioChannelAgent) {
AutoNoJSAPI nojsapi;
mAudioChannelAgent->SetVisibilityState(!ownerDoc->Hidden());
}
bool pauseElement = !IsActive() || (mMuted & MUTED_BY_AUDIO_CHANNEL);
SuspendOrResumeElement(pauseElement, !IsActive());
@ -4466,26 +4450,24 @@ ImageContainer* HTMLMediaElement::GetImageContainer()
return container ? container->GetImageContainer() : nullptr;
}
nsresult HTMLMediaElement::UpdateChannelMuteState(AudioChannelState aCanPlay)
nsresult HTMLMediaElement::UpdateChannelMuteState(float aVolume, bool aMuted)
{
if (!UseAudioChannelService()) {
return NS_OK;
}
if ((aCanPlay == AUDIO_CHANNEL_STATE_FADED && !mAudioChannelFaded) ||
(aCanPlay != AUDIO_CHANNEL_STATE_FADED && mAudioChannelFaded)) {
mAudioChannelFaded = !mAudioChannelFaded;
if (mAudioChannelVolume != aVolume) {
mAudioChannelVolume = aVolume;
SetVolumeInternal();
}
// We have to mute this channel.
if (aCanPlay == AUDIO_CHANNEL_STATE_MUTED && !(mMuted & MUTED_BY_AUDIO_CHANNEL)) {
if (aMuted && !(mMuted & MUTED_BY_AUDIO_CHANNEL)) {
SetMutedInternal(mMuted | MUTED_BY_AUDIO_CHANNEL);
if (UseAudioChannelAPI()) {
DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptbegin"));
}
} else if (aCanPlay != AUDIO_CHANNEL_STATE_MUTED &&
(mMuted & MUTED_BY_AUDIO_CHANNEL)) {
} else if (!aMuted && (mMuted & MUTED_BY_AUDIO_CHANNEL)) {
SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_CHANNEL);
if (UseAudioChannelAPI()) {
DispatchAsyncEvent(NS_LITERAL_STRING("mozinterruptend"));
@ -4523,17 +4505,9 @@ void HTMLMediaElement::UpdateAudioChannelPlayingState()
if (!mAudioChannelAgent) {
return;
}
// Use a weak ref so the audio channel agent can't leak |this|.
if (AudioChannel::Normal == mAudioChannel && IsVideo()) {
mAudioChannelAgent->InitWithVideo(OwnerDoc()->GetWindow(),
static_cast<int32_t>(mAudioChannel),
this, true);
} else {
mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetWindow(),
static_cast<int32_t>(mAudioChannel),
this);
}
mAudioChannelAgent->SetVisibilityState(!OwnerDoc()->Hidden());
mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetWindow(),
static_cast<int32_t>(mAudioChannel),
this);
}
// This is needed to pass nsContentUtils::IsCallerChrome().
@ -4542,9 +4516,10 @@ void HTMLMediaElement::UpdateAudioChannelPlayingState()
AutoNoJSAPI nojsapi;
if (mPlayingThroughTheAudioChannel) {
int32_t canPlay;
mAudioChannelAgent->StartPlaying(&canPlay);
CanPlayChanged(canPlay);
float volume = 0.0;
bool muted = true;
mAudioChannelAgent->StartPlaying(&volume, &muted);
WindowVolumeChanged(volume, muted);
} else {
mAudioChannelAgent->StopPlaying();
mAudioChannelAgent = nullptr;
@ -4552,25 +4527,12 @@ void HTMLMediaElement::UpdateAudioChannelPlayingState()
}
}
/* void canPlayChanged (in boolean canPlay); */
NS_IMETHODIMP HTMLMediaElement::CanPlayChanged(int32_t canPlay)
NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged(float aVolume, bool aMuted)
{
static_assert(static_cast<AudioChannelState>(
nsIAudioChannelAgent::AUDIO_AGENT_STATE_NORMAL) ==
AUDIO_CHANNEL_STATE_NORMAL &&
static_cast<AudioChannelState>(
nsIAudioChannelAgent::AUDIO_AGENT_STATE_MUTED) ==
AUDIO_CHANNEL_STATE_MUTED &&
static_cast<AudioChannelState>(
nsIAudioChannelAgent::AUDIO_AGENT_STATE_FADED) ==
AUDIO_CHANNEL_STATE_FADED,
"Enum of channel state on nsIAudioChannelAgent.idl should be "
"the same with AudioChannelCommon.h");
NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
UpdateChannelMuteState(static_cast<AudioChannelState>(canPlay));
mPaused.SetCanPlay(canPlay != AUDIO_CHANNEL_STATE_MUTED);
UpdateChannelMuteState(aVolume, aMuted);
mPaused.SetCanPlay(!aMuted);
return NS_OK;
}
@ -4715,12 +4677,6 @@ HTMLMediaElement::GetTopLevelPrincipal()
}
#endif // MOZ_EME
NS_IMETHODIMP HTMLMediaElement::WindowVolumeChanged()
{
SetVolumeInternal();
return NS_OK;
}
AudioTrackList*
HTMLMediaElement::AudioTracks()
{

View File

@ -13,7 +13,6 @@
#include "nsIObserver.h"
#include "mozilla/CORSMode.h"
#include "DOMMediaStream.h"
#include "AudioChannelCommon.h"
#include "DecoderTraits.h"
#include "nsIAudioChannelAgent.h"
#include "mozilla/Attributes.h"
@ -1008,8 +1007,8 @@ protected:
// Check the permissions for audiochannel.
bool CheckAudioChannelPermissions(const nsAString& aType);
// This method does the check for muting/fading/unmuting the audio channel.
nsresult UpdateChannelMuteState(mozilla::dom::AudioChannelState aCanPlay);
// This method does the check for muting/nmuting the audio channel.
nsresult UpdateChannelMuteState(float aVolume, bool aMuted);
// Seeks to aTime seconds. aSeekType can be Exact to seek to exactly the
// seek target, or PrevSyncPoint if a quicker but less precise seek is
@ -1363,8 +1362,8 @@ protected:
// Audio Channel.
AudioChannel mAudioChannel;
// The audio channel has been faded.
bool mAudioChannelFaded;
// The audio channel volume
float mAudioChannelVolume;
// Is this media element playing?
bool mPlayingThroughTheAudioChannel;

View File

@ -9,15 +9,21 @@
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/dom/BrowserElementBinding.h"
#include "mozilla/dom/BrowserElementAudioChannel.h"
#include "mozilla/dom/DOMRequest.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/ToJSValue.h"
#include "AudioChannelService.h"
#include "mozIApplication.h"
#include "nsComponentManagerUtils.h"
#include "nsContentUtils.h"
#include "nsFrameLoader.h"
#include "nsIAppsService.h"
#include "nsIDOMDOMRequest.h"
#include "nsIDOMElement.h"
#include "nsIMozBrowserFrame.h"
#include "nsINode.h"
#include "nsIPrincipal.h"
@ -507,6 +513,141 @@ nsBrowserElement::SetInputMethodActive(bool aIsActive,
return req.forget().downcast<DOMRequest>();
}
void
nsBrowserElement::GetAllowedAudioChannels(
nsTArray<nsRefPtr<BrowserElementAudioChannel>>& aAudioChannels,
ErrorResult& aRv)
{
aAudioChannels.Clear();
NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
NS_ENSURE_TRUE_VOID(IsNotWidgetOrThrow(aRv));
// If empty, it means that this is the first call of this method.
if (mBrowserElementAudioChannels.IsEmpty()) {
nsCOMPtr<nsIFrameLoader> frameLoader = GetFrameLoader();
if (!frameLoader) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
nsCOMPtr<nsIDOMElement> frameElement;
aRv = frameLoader->GetOwnerElement(getter_AddRefs(frameElement));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
MOZ_ASSERT(frameElement);
nsCOMPtr<nsIDOMDocument> doc;
aRv = frameElement->GetOwnerDocument(getter_AddRefs(doc));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
MOZ_ASSERT(doc);
nsCOMPtr<nsIDOMWindow> win;
aRv = doc->GetDefaultView(getter_AddRefs(win));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
MOZ_ASSERT(win);
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(win);
if (!window->IsInnerWindow()) {
window = window->GetCurrentInnerWindow();
}
nsCOMPtr<nsIMozBrowserFrame> mozBrowserFrame =
do_QueryInterface(frameElement);
if (NS_WARN_IF(!mozBrowserFrame)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
nsAutoString manifestURL;
aRv = mozBrowserFrame->GetAppManifestURL(manifestURL);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
nsCOMPtr<nsIAppsService> appsService =
do_GetService("@mozilla.org/AppsService;1");
if (NS_WARN_IF(!appsService)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
nsCOMPtr<mozIApplication> app;
aRv = appsService->GetAppByManifestURL(manifestURL, getter_AddRefs(app));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
bool noapp = false;
Preferences::GetBool("dom.testing.browserElementAudioChannel.noapp", &noapp);
if (!noapp && !app) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
// Normal is always allowed.
nsTArray<nsRefPtr<BrowserElementAudioChannel>> channels;
nsRefPtr<BrowserElementAudioChannel> ac =
new BrowserElementAudioChannel(window, frameLoader, mBrowserElementAPI,
AudioChannel::Normal);
aRv = ac->Initialize();
if (NS_WARN_IF(aRv.Failed())) {
return;
}
channels.AppendElement(ac);
// app can be null in case we are in a test.
if (app) {
const nsAttrValue::EnumTable* audioChannelTable =
AudioChannelService::GetAudioChannelTable();
bool allowed;
nsAutoCString permissionName;
for (uint32_t i = 0; audioChannelTable && audioChannelTable[i].tag; ++i) {
permissionName.AssignASCII("audio-channel-");
permissionName.AppendASCII(audioChannelTable[i].tag);
aRv = app->HasPermission(permissionName.get(), &allowed);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
if (allowed) {
nsRefPtr<BrowserElementAudioChannel> ac =
new BrowserElementAudioChannel(window, frameLoader,
mBrowserElementAPI,
(AudioChannel)audioChannelTable[i].value);
aRv = ac->Initialize();
if (NS_WARN_IF(aRv.Failed())) {
return;
}
channels.AppendElement(ac);
}
}
}
mBrowserElementAudioChannels.AppendElements(channels);
}
aAudioChannels.AppendElements(mBrowserElementAudioChannels);
}
void
nsBrowserElement::SetNFCFocus(bool aIsFocus,
ErrorResult& aRv)

View File

@ -8,6 +8,7 @@
#define nsBrowserElement_h
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/BrowserElementAudioChannel.h"
#include "nsCOMPtr.h"
#include "nsIBrowserElementAPI.h"
@ -70,6 +71,10 @@ public:
already_AddRefed<dom::DOMRequest> PurgeHistory(ErrorResult& aRv);
void GetAllowedAudioChannels(
nsTArray<nsRefPtr<dom::BrowserElementAudioChannel>>& aAudioChannels,
ErrorResult& aRv);
already_AddRefed<dom::DOMRequest>
GetScreenshot(uint32_t aWidth,
uint32_t aHeight,
@ -102,6 +107,7 @@ protected:
NS_IMETHOD_(already_AddRefed<nsFrameLoader>) GetFrameLoader() = 0;
void InitBrowserElementAPI();
nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI;
nsTArray<nsRefPtr<dom::BrowserElementAudioChannel>> mBrowserElementAudioChannels;
private:
bool IsBrowserElementOrThrow(ErrorResult& aRv);

View File

@ -6,6 +6,7 @@
#include "nsGenericHTMLFrameElement.h"
#include "mozilla/dom/BrowserElementAudioChannel.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/Preferences.h"
#include "mozilla/ErrorResult.h"
@ -34,8 +35,20 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsGenericHTMLFrameElement,
nsGenericHTMLElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameLoader)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserElementAPI)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserElementAudioChannels)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGenericHTMLFrameElement,
nsGenericHTMLElement)
if (tmp->mFrameLoader) {
tmp->mFrameLoader->Destroy();
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameLoader)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserElementAPI)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserElementAudioChannels)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_ADDREF_INHERITED(nsGenericHTMLFrameElement, nsGenericHTMLElement)
NS_IMPL_RELEASE_INHERITED(nsGenericHTMLFrameElement, nsGenericHTMLElement)

View File

@ -71,8 +71,8 @@ public:
virtual int32_t TabIndexDefault() override;
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsGenericHTMLFrameElement,
nsGenericHTMLElement)
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsGenericHTMLFrameElement,
nsGenericHTMLElement)
void SwapFrameLoaders(nsXULElement& aOtherOwner, mozilla::ErrorResult& aError);

View File

@ -182,7 +182,6 @@
#include "nsContentUtils.h"
#include "nsIPrincipal.h"
#include "nsDeviceStorage.h"
#include "AudioChannelService.h"
#include "DomainPolicy.h"
#include "mozilla/dom/DataStoreService.h"
#include "mozilla/dom/telephony/PTelephonyChild.h"
@ -943,17 +942,6 @@ NS_IMETHODIMP MemoryReportRequestChild::Run()
return sent ? NS_OK : NS_ERROR_FAILURE;
}
bool
ContentChild::RecvAudioChannelNotify()
{
nsRefPtr<AudioChannelService> service =
AudioChannelService::GetAudioChannelService();
if (service) {
service->Notify();
}
return true;
}
bool
ContentChild::RecvDataStoreNotify(const uint32_t& aAppId,
const nsString& aName,

View File

@ -200,9 +200,6 @@ public:
const FileDescriptor& aGCLog,
const FileDescriptor& aCCLog) override;
virtual bool
RecvAudioChannelNotify() override;
virtual bool
RecvDataStoreNotify(const uint32_t& aAppId, const nsString& aName,
const nsString& aManifestURL) override;

View File

@ -2791,64 +2791,11 @@ ContentParent::RecvFirstIdle()
return true;
}
bool
ContentParent::RecvAudioChannelGetState(const AudioChannel& aChannel,
const bool& aElementHidden,
const bool& aElementWasHidden,
AudioChannelState* aState)
{
nsRefPtr<AudioChannelService> service =
AudioChannelService::GetOrCreateAudioChannelService();
*aState = AUDIO_CHANNEL_STATE_NORMAL;
MOZ_ASSERT(service);
*aState = service->GetStateInternal(aChannel, mChildID,
aElementHidden, aElementWasHidden);
return true;
}
bool
ContentParent::RecvAudioChannelRegisterType(const AudioChannel& aChannel,
const bool& aWithVideo)
{
nsRefPtr<AudioChannelService> service =
AudioChannelService::GetOrCreateAudioChannelService();
MOZ_ASSERT(service);
service->RegisterType(aChannel, mChildID, aWithVideo);
return true;
}
bool
ContentParent::RecvAudioChannelUnregisterType(const AudioChannel& aChannel,
const bool& aElementHidden,
const bool& aWithVideo)
{
nsRefPtr<AudioChannelService> service =
AudioChannelService::GetOrCreateAudioChannelService();
MOZ_ASSERT(service);
service->UnregisterType(aChannel, aElementHidden, mChildID, aWithVideo);
return true;
}
bool
ContentParent::RecvAudioChannelChangedNotification()
{
nsRefPtr<AudioChannelService> service =
AudioChannelService::GetOrCreateAudioChannelService();
MOZ_ASSERT(service);
service->SendAudioChannelChangedNotification(ChildID());
return true;
}
bool
ContentParent::RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
const bool& aHidden)
{
nsRefPtr<AudioChannelService> service =
AudioChannelService::GetOrCreateAudioChannelService();
nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
MOZ_ASSERT(service);
service->SetDefaultVolumeControlChannelInternal(aChannel,
aHidden, mChildID);

View File

@ -752,19 +752,6 @@ private:
virtual bool RecvFirstIdle() override;
virtual bool RecvAudioChannelGetState(const AudioChannel& aChannel,
const bool& aElementHidden,
const bool& aElementWasHidden,
AudioChannelState* aValue) override;
virtual bool RecvAudioChannelRegisterType(const AudioChannel& aChannel,
const bool& aWithVideo) override;
virtual bool RecvAudioChannelUnregisterType(const AudioChannel& aChannel,
const bool& aElementHidden,
const bool& aWithVideo) override;
virtual bool RecvAudioChannelChangedNotification() override;
virtual bool RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
const bool& aHidden) override;
virtual bool RecvGetSystemMemory(const uint64_t& getterId) override;

View File

@ -539,6 +539,10 @@ parent:
InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action,
nsCString visualData, uint32_t width, uint32_t height,
uint32_t stride, uint8_t format, int32_t dragAreaX, int32_t dragAreaY);
async AudioChannelActivityNotification(uint32_t aAudioChannel,
bool aActive);
child:
/**
* Notify the remote browser that it has been Show()n on this

View File

@ -81,8 +81,6 @@ using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
using mozilla::dom::asmjscache::OpenMode from "mozilla/dom/asmjscache/AsmJSCache.h";
using mozilla::dom::asmjscache::WriteParams from "mozilla/dom/asmjscache/AsmJSCache.h";
using mozilla::dom::AudioChannel from "mozilla/dom/AudioChannelBinding.h";
using mozilla::dom::AudioChannelState from "AudioChannelCommon.h";
using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
using mozilla::dom::quota::PersistenceType from "mozilla/dom/quota/PersistenceType.h";
using mozilla::hal::ProcessPriority from "mozilla/HalTypes.h";
@ -498,11 +496,6 @@ child:
PMemoryReportRequest(uint32_t generation, bool anonymize,
bool minimizeMemoryUsage, MaybeFileDesc DMDFile);
/**
* Notify the AudioChannelService in the child processes.
*/
async AudioChannelNotify();
async SpeakerManagerNotify();
/**
@ -870,17 +863,6 @@ parent:
// Tell the parent that the child has gone idle for the first time
async FirstIdle();
// Get Muted from the main AudioChannelService.
sync AudioChannelGetState(AudioChannel aChannel, bool aElementHidden,
bool aElementWasHidden)
returns (AudioChannelState value);
sync AudioChannelRegisterType(AudioChannel aChannel, bool aWithVideo);
sync AudioChannelUnregisterType(AudioChannel aChannel,
bool aElementHidden,
bool aWithVideo);
async AudioChannelChangedNotification();
async AudioChannelChangeDefVolChannel(int32_t aChannel, bool aHidden);
sync DataStoreGetStores(nsString aName, nsString aOwner, Principal aPrincipal)

View File

@ -1041,7 +1041,7 @@ ParticularProcessPriorityManager::ComputePriority()
return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
}
AudioChannelService* service = AudioChannelService::GetOrCreateAudioChannelService();
nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
if (service->ProcessContentOrNormalChannelIsActive(ChildID())) {
return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
}

View File

@ -890,6 +890,22 @@ TabChild::TabChild(nsIContentChild* aManager,
MOZ_ASSERT(NestedTabChildMap().find(mUniqueId) == NestedTabChildMap().end());
NestedTabChildMap()[mUniqueId] = this;
}
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
const nsAttrValue::EnumTable* table =
AudioChannelService::GetAudioChannelTable();
nsAutoCString topic;
for (uint32_t i = 0; table[i].tag; ++i) {
topic.Assign("audiochannel-activity-");
topic.Append(table[i].tag);
observerService->AddObserver(this, topic.get(), false);
}
}
}
NS_IMETHODIMP
@ -957,6 +973,27 @@ TabChild::Observe(nsISupports *aSubject,
}
}
const nsAttrValue::EnumTable* table =
AudioChannelService::GetAudioChannelTable();
nsAutoCString topic;
int16_t audioChannel = -1;
for (uint32_t i = 0; table[i].tag; ++i) {
topic.Assign("audiochannel-activity-");
topic.Append(table[i].tag);
if (topic.Equals(aTopic)) {
audioChannel = table[i].value;
break;
}
}
if (audioChannel != -1) {
nsAutoString active(aData);
unused << SendAudioChannelActivityNotification(audioChannel,
active.Equals(NS_LITERAL_STRING("active")));
}
return NS_OK;
}
@ -2817,6 +2854,17 @@ TabChild::RecvDestroy()
observerService->RemoveObserver(this, BROWSER_ZOOM_TO_RECT);
observerService->RemoveObserver(this, BEFORE_FIRST_PAINT);
const nsAttrValue::EnumTable* table =
AudioChannelService::GetAudioChannelTable();
nsAutoCString topic;
for (uint32_t i = 0; table[i].tag; ++i) {
topic.Assign("audiochannel-activity-");
topic.Append(table[i].tag);
observerService->RemoveObserver(this, topic.get());
}
// XXX what other code in ~TabChild() should we be running here?
DestroyWindow();

View File

@ -7,7 +7,6 @@
#ifndef TABMESSAGE_UTILS_H
#define TABMESSAGE_UTILS_H
#include "AudioChannelCommon.h"
#include "ipc/IPCMessageUtils.h"
#include "mozilla/dom/AudioChannelBinding.h"
#include "nsIDOMEvent.h"
@ -89,14 +88,6 @@ struct ParamTraits<mozilla::dom::AudioChannel>
}
};
template <>
struct ParamTraits<mozilla::dom::AudioChannelState>
: public ContiguousEnumSerializer<mozilla::dom::AudioChannelState,
mozilla::dom::AUDIO_CHANNEL_STATE_NORMAL,
mozilla::dom::AUDIO_CHANNEL_STATE_LAST>
{ };
template <>
struct ParamTraits<nsEventStatus>
: public ContiguousEnumSerializer<nsEventStatus,

View File

@ -8,6 +8,7 @@
#include "TabParent.h"
#include "AudioChannelService.h"
#include "AppProcessChecker.h"
#include "mozIApplication.h"
#ifdef ACCESSIBILITY
@ -2630,6 +2631,29 @@ TabParent::RecvGetRenderFrameInfo(PRenderFrameParent* aRenderFrame,
return true;
}
bool
TabParent::RecvAudioChannelActivityNotification(const uint32_t& aAudioChannel,
const bool& aActive)
{
if (aAudioChannel >= NUMBER_OF_AUDIO_CHANNELS) {
return false;
}
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
if (os) {
nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
nsAutoCString topic;
topic.Assign("audiochannel-activity-");
topic.Append(AudioChannelService::GetAudioChannelTable()[aAudioChannel].tag);
os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this),
topic.get(),
aActive ? MOZ_UTF16("active") : MOZ_UTF16("inactive"));
}
return true;
}
already_AddRefed<nsFrameLoader>
TabParent::GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy) const
{

View File

@ -461,6 +461,9 @@ protected:
const int32_t& aX, const int32_t& aY,
const int32_t& aCx, const int32_t& aCy) override;
virtual bool RecvAudioChannelActivityNotification(const uint32_t& aAudioChannel,
const bool& aActive) override;
bool InitBrowserConfiguration(const nsCString& aURI,
BrowserConfiguration& aConfiguration);

View File

@ -298,44 +298,11 @@ static bool UseAudioChannelAPI()
return Preferences::GetBool("media.useAudioChannelAPI");
}
class EventProxyHandler final : public nsIDOMEventListener
{
public:
NS_DECL_ISUPPORTS
explicit EventProxyHandler(nsIDOMEventListener* aNode)
{
MOZ_ASSERT(aNode);
mWeakNode = do_GetWeakReference(aNode);
}
// nsIDOMEventListener
NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) override
{
nsCOMPtr<nsIDOMEventListener> listener = do_QueryReferent(mWeakNode);
if (!listener) {
return NS_OK;
}
auto node = static_cast<AudioDestinationNode*>(listener.get());
return node->HandleEvent(aEvent);
}
private:
~EventProxyHandler()
{ }
nsWeakPtr mWeakNode;
};
NS_IMPL_ISUPPORTS(EventProxyHandler, nsIDOMEventListener)
NS_IMPL_CYCLE_COLLECTION_INHERITED(AudioDestinationNode, AudioNode,
mAudioChannelAgent, mEventProxyHelper,
mAudioChannelAgent,
mOfflineRenderingPromise)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioDestinationNode)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
NS_INTERFACE_MAP_END_INHERITING(AudioNode)
@ -408,13 +375,6 @@ AudioDestinationNode::DestroyAudioChannelAgent()
if (mAudioChannelAgent && !Context()->IsOffline()) {
mAudioChannelAgent->StopPlaying();
mAudioChannelAgent = nullptr;
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
NS_ENSURE_TRUE_VOID(target);
target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
mEventProxyHelper,
/* useCapture = */ true);
}
}
@ -518,65 +478,30 @@ AudioDestinationNode::StartRendering(Promise* aPromise)
}
void
AudioDestinationNode::SetCanPlay(bool aCanPlay)
AudioDestinationNode::SetCanPlay(float aVolume, bool aMuted)
{
mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, aCanPlay);
}
NS_IMETHODIMP
AudioDestinationNode::HandleEvent(nsIDOMEvent* aEvent)
{
nsAutoString type;
aEvent->GetType(type);
if (!type.EqualsLiteral("visibilitychange")) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE);
bool isActive = false;
docshell->GetIsActive(&isActive);
mAudioChannelAgent->SetVisibilityState(isActive);
return NS_OK;
}
NS_IMETHODIMP
AudioDestinationNode::CanPlayChanged(int32_t aCanPlay)
{
bool playing = aCanPlay == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL;
if (playing == mAudioChannelAgentPlaying) {
return NS_OK;
}
mAudioChannelAgentPlaying = playing;
SetCanPlay(playing);
if (UseAudioChannelAPI()) {
Context()->DispatchTrustedEvent(
playing ? NS_LITERAL_STRING("mozinterruptend")
: NS_LITERAL_STRING("mozinterruptbegin"));
}
return NS_OK;
}
NS_IMETHODIMP
AudioDestinationNode::WindowVolumeChanged()
{
MOZ_ASSERT(mAudioChannelAgent);
if (!mStream) {
return NS_OK;
return;
}
float volume;
nsresult rv = mAudioChannelAgent->GetWindowVolume(&volume);
NS_ENSURE_SUCCESS(rv, rv);
mStream->SetTrackEnabled(AudioNodeStream::AUDIO_TRACK, !aMuted);
mStream->SetAudioOutputVolume(&gWebAudioOutputKey, aVolume);
}
mStream->SetAudioOutputVolume(&gWebAudioOutputKey, volume);
NS_IMETHODIMP
AudioDestinationNode::WindowVolumeChanged(float aVolume, bool aMuted)
{
if (aMuted != mAudioChannelAgentPlaying) {
mAudioChannelAgentPlaying = aMuted;
if (UseAudioChannelAPI()) {
Context()->DispatchTrustedEvent(
!aMuted ? NS_LITERAL_STRING("mozinterruptend")
: NS_LITERAL_STRING("mozinterruptbegin"));
}
}
SetCanPlay(aVolume, aMuted);
return NS_OK;
}
@ -654,20 +579,6 @@ AudioDestinationNode::CreateAudioChannelAgent()
return;
}
if (!mEventProxyHelper) {
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
if (target) {
// We use a proxy because otherwise the event listerner would hold a
// reference of the destination node, and by extension, everything
// connected to it.
mEventProxyHelper = new EventProxyHandler(this);
target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
mEventProxyHelper,
/* useCapture = */ true,
/* wantsUntrusted = */ false);
}
}
if (mAudioChannelAgent) {
mAudioChannelAgent->StopPlaying();
}
@ -677,16 +588,9 @@ AudioDestinationNode::CreateAudioChannelAgent()
static_cast<int32_t>(mAudioChannel),
this);
nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
if (docshell) {
bool isActive = false;
docshell->GetIsActive(&isActive);
mAudioChannelAgent->SetVisibilityState(isActive);
// The AudioChannelAgent must start playing immediately in order to avoid
// race conditions with mozinterruptbegin/end events.
InputMuted(false);
}
// The AudioChannelAgent must start playing immediately in order to avoid
// race conditions with mozinterruptbegin/end events.
InputMuted(false);
}
void
@ -771,13 +675,14 @@ AudioDestinationNode::InputMuted(bool aMuted)
return;
}
int32_t state = 0;
nsresult rv = mAudioChannelAgent->StartPlaying(&state);
float volume = 0.0;
bool muted = true;
nsresult rv = mAudioChannelAgent->StartPlaying(&volume, &muted);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
CanPlayChanged(state);
WindowVolumeChanged(volume, muted);
}
} // dom namespace

View File

@ -9,18 +9,14 @@
#include "mozilla/dom/AudioChannelBinding.h"
#include "AudioNode.h"
#include "nsIDOMEventListener.h"
#include "nsIAudioChannelAgent.h"
#include "AudioChannelCommon.h"
namespace mozilla {
namespace dom {
class AudioContext;
class EventProxyHandler;
class AudioDestinationNode final : public AudioNode
, public nsIDOMEventListener
, public nsIAudioChannelAgentCallback
, public MainThreadMediaStreamListener
{
@ -61,9 +57,6 @@ public:
void OfflineShutdown();
// nsIDOMEventListener - by proxy
NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) override;
AudioChannel MozAudioChannelType() const;
void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
@ -97,7 +90,7 @@ protected:
private:
bool CheckAudioChannelPermissions(AudioChannel aValue);
void SetCanPlay(bool aCanPlay);
void SetCanPlay(float aVolume, bool aMuted);
void NotifyStableState();
void ScheduleStableStateNotification();
@ -107,7 +100,6 @@ private:
nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
nsRefPtr<EventProxyHandler> mEventProxyHelper;
nsRefPtr<Promise> mOfflineRenderingPromise;
// Audio Channel Type.

View File

@ -18,8 +18,6 @@ MOCHITEST_MANIFESTS += [
MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
EXPORTS += [
'AlignedTArray.h',
'AudioContext.h',

View File

@ -1,8 +0,0 @@
[DEFAULT]
support-files =
browser_mozAudioChannel.html
browser_mozAudioChannel_muted.html
[browser_mozAudioChannel.js]
[browser_mozAudioChannel_muted.js]
skip-if = e10s

View File

@ -1,33 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<meta charset="utf-8">
<title>Test for mozinterruptbegin/end in AudioContext</title>
<script type="application/javascript">
var ac = new AudioContext();
function createEvent(msg) {
var event = document.createEvent('CustomEvent');
event.initCustomEvent('testmozchannel', true, true, { msg: msg });
dispatchEvent(event);
}
ac.onmozinterruptbegin = function(evt) {
createEvent('mozinterruptbegin');
}
ac.addEventListener('mozinterruptend', function() {
createEvent('mozinterruptend');
}, false);
var buffer = ac.createBuffer(1, 2048, ac.sampleRate);
for (var i = 0; i < 2048; ++i) {
buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / ac.sampleRate);
}
var source = ac.createBufferSource();
source.buffer = buffer;
source.connect(ac.destination);
source.loop = true;
source.start(0);
</script>

View File

@ -1,80 +0,0 @@
/* 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/. */
function whenBrowserLoaded(aBrowser, aCallback) {
aBrowser.addEventListener("load", function onLoad(event) {
if (event.target == aBrowser.contentDocument) {
aBrowser.removeEventListener("load", onLoad, true);
executeSoon(aCallback);
}
}, true);
}
function whenBrowserUnloaded(aBrowser, aCallback) {
aBrowser.addEventListener("unload", function onUnload() {
aBrowser.removeEventListener("unload", onUnload, true);
executeSoon(aCallback);
}, true);
}
var event;
var next = function() {}
function eventListener(evt) {
info("Event has been received!");
is(evt.detail.msg, event, "AudioContext has been received the right event: " + event);
next();
}
function test() {
waitForExplicitFinish();
let testURL = "http://mochi.test:8888/browser/" +
"dom/media/webaudio/test/browser_mozAudioChannel.html";
SpecialPowers.pushPrefEnv({"set": [["media.defaultAudioChannel", "content" ],
["media.useAudioChannelAPI", true ],
["media.useAudioChannelService", true ]]},
function() {
let tab1 = gBrowser.addTab(testURL);
gBrowser.selectedTab = tab1;
whenBrowserLoaded(tab1.linkedBrowser, function() {
let doc = tab1.linkedBrowser.contentDocument;
tab1.linkedBrowser.contentWindow.addEventListener('testmozchannel', eventListener, false);
SpecialPowers.pushPrefEnv({"set": [["media.defaultAudioChannel", "telephony" ]]},
function() {
event = 'mozinterruptbegin';
next = function() {
info("Next is called.");
event = 'mozinterruptend';
next = function() {
info("Next is called again.");
tab1.linkedBrowser.contentWindow.removeEventListener('testmozchannel', eventListener);
gBrowser.removeTab(tab1);
finish();
}
info("Unloading a tab...");
whenBrowserUnloaded(tab2.linkedBrowser, function() { info("Tab unloaded."); });
executeSoon(function() {
gBrowser.removeTab(tab2);
gBrowser.selectedTab = tab1;
});
}
let tab2 = gBrowser.addTab(testURL);
gBrowser.selectedTab = tab2;
info("Loading the tab...");
whenBrowserLoaded(tab2.linkedBrowser, function() { info("Tab restored."); });
}
);
});
}
);
}

View File

@ -1,21 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<meta charset="utf-8">
<title>Test for mozinterruptbegin/end in AudioContext</title>
mozAudioChannelTest = <span id="mozAudioChannelTest">FAIL</span>
<script type="application/javascript">
var ac = new AudioContext();
ac.onmozinterruptbegin = function(evt) {
document.getElementById("mozAudioChannelTest").innerHTML = "mozinterruptbegin";
}
ac.addEventListener('mozinterruptend', function() {
document.getElementById("mozAudioChannelTest").innerHTML = "mozinterruptend";
}, false);
document.getElementById("mozAudioChannelTest").innerHTML = "READY";
</script>

View File

@ -1,72 +0,0 @@
/* 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/. */
function whenBrowserLoaded(aBrowser, aCallback) {
aBrowser.addEventListener("load", function onLoad(event) {
if (event.target == aBrowser.contentDocument) {
aBrowser.removeEventListener("load", onLoad, true);
executeSoon(aCallback);
}
}, true);
}
function whenTabRestored(aTab, aCallback) {
aTab.addEventListener("SSTabRestored", function onRestored(aEvent) {
aTab.removeEventListener("SSTabRestored", onRestored, true);
executeSoon(function executeWhenTabRestored() {
aCallback();
});
}, true);
}
function whenBrowserUnloaded(aBrowser, aCallback) {
aBrowser.addEventListener("unload", function onUnload() {
aBrowser.removeEventListener("unload", onUnload, true);
executeSoon(aCallback);
}, true);
}
function test() {
waitForExplicitFinish();
let testURL = "http://mochi.test:8888/browser/" +
"dom/media/webaudio/test/browser_mozAudioChannel_muted.html";
SpecialPowers.pushPrefEnv({"set": [["media.defaultAudioChannel", "content" ],
["media.useAudioChannelAPI", true ],
["media.useAudioChannelService", true ]]},
function() {
let tab1 = gBrowser.addTab(testURL);
gBrowser.selectedTab = tab1;
whenBrowserLoaded(tab1.linkedBrowser, function() {
let doc = tab1.linkedBrowser.contentDocument;
is(doc.getElementById("mozAudioChannelTest").textContent, "READY",
"Test is ready to run");
SpecialPowers.pushPrefEnv({"set": [["media.defaultAudioChannel", "telephony" ]]},
function() {
let tab2 = gBrowser.duplicateTab(tab1);
gBrowser.selectedTab = tab2;
whenTabRestored(tab2, function() {
is(doc.getElementById("mozAudioChannelTest").textContent, "mozinterruptbegin",
"AudioContext should be muted by the second tab.");
whenBrowserUnloaded(tab2.linkedBrowser, function() {
is(doc.getElementById("mozAudioChannelTest").textContent, "mozinterruptend",
"AudioContext should be muted by the second tab.");
gBrowser.removeTab(tab1);
finish();
});
gBrowser.removeTab(tab2);
gBrowser.selectedTab = tab1;
});
}
);
});
}
);
}

View File

@ -201,8 +201,8 @@ SpeakerManager::HandleEvent(nsIDOMEvent* aEvent)
// currently playing in the app itself, if application switch to
// the background, we switch 'speakerforced' to false.
if (!mVisible && mForcespeaker) {
AudioChannelService* audioChannelService =
AudioChannelService::GetOrCreateAudioChannelService();
nsRefPtr<AudioChannelService> audioChannelService =
AudioChannelService::GetOrCreate();
if (audioChannelService && !audioChannelService->AnyAudioChannelIsActive()) {
service->ForceSpeaker(false, mVisible);
}

View File

@ -194,18 +194,15 @@ SpeakerManagerService::SpeakerManagerService()
obs->AddObserver(this, "ipc:content-shutdown", false);
}
}
AudioChannelService* audioChannelService =
AudioChannelService::GetOrCreateAudioChannelService();
if (audioChannelService) {
audioChannelService->RegisterSpeakerManager(this);
}
nsRefPtr<AudioChannelService> audioChannelService =
AudioChannelService::GetOrCreate();
audioChannelService->RegisterSpeakerManager(this);
}
SpeakerManagerService::~SpeakerManagerService()
{
MOZ_COUNT_DTOR(SpeakerManagerService);
AudioChannelService* audioChannelService =
AudioChannelService::GetOrCreateAudioChannelService();
if (audioChannelService)
audioChannelService->UnregisterSpeakerManager(this);
nsRefPtr<AudioChannelService> audioChannelService =
AudioChannelService::GetOrCreate();
audioChannelService->UnregisterSpeakerManager(this);
}

View File

@ -96,7 +96,7 @@ SpeakerManagerServiceChild::SetAudioChannelActive(bool aIsActive)
SpeakerManagerServiceChild::SpeakerManagerServiceChild()
{
MOZ_ASSERT(NS_IsMainThread());
AudioChannelService* audioChannelService = AudioChannelService::GetOrCreateAudioChannelService();
nsRefPtr<AudioChannelService> audioChannelService = AudioChannelService::GetOrCreate();
if (audioChannelService) {
audioChannelService->RegisterSpeakerManager(this);
}
@ -105,7 +105,7 @@ SpeakerManagerServiceChild::SpeakerManagerServiceChild()
SpeakerManagerServiceChild::~SpeakerManagerServiceChild()
{
AudioChannelService* audioChannelService = AudioChannelService::GetOrCreateAudioChannelService();
nsRefPtr<AudioChannelService> audioChannelService = AudioChannelService::GetOrCreate();
if (audioChannelService) {
audioChannelService->UnregisterSpeakerManager(this);
}

View File

@ -131,7 +131,7 @@ AudioChannelManager::NotifyVolumeControlChannelChanged()
bool isActive = false;
docshell->GetIsActive(&isActive);
AudioChannelService* service = AudioChannelService::GetOrCreateAudioChannelService();
nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
if (isActive) {
service->SetDefaultVolumeControlChannel(mVolumeChannel, isActive);
} else {

View File

@ -356,7 +356,7 @@ AudioManager::HandleAudioChannelProcessChanged()
return;
}
AudioChannelService *service = AudioChannelService::GetOrCreateAudioChannelService();
nsRefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
MOZ_ASSERT(service);
bool telephonyChannelIsActive = service->TelephonyChannelIsActive();
@ -651,8 +651,13 @@ AudioManager::SetPhoneState(int32_t aState)
}
// Telephony can always play.
int32_t canPlay;
mPhoneAudioAgent->StartPlaying(&canPlay);
float volume = 0.0;
bool muted = true;
nsresult rv = mPhoneAudioAgent->StartPlaying(&volume, &muted);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
return NS_OK;

View File

@ -0,0 +1,37 @@
/* -*- 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/.
*/
[Pref="dom.mozBrowserFramesEnabled",
CheckPermissions="browser"]
interface BrowserElementAudioChannel : EventTarget {
readonly attribute AudioChannel name;
// This event is dispatched when this audiochannel is actually in used by the
// app or one of the sub-iframes.
attribute EventHandler onactivestatechanged;
[Throws]
DOMRequest getVolume();
[Throws]
DOMRequest setVolume(float aVolume);
[Throws]
DOMRequest getMuted();
[Throws]
DOMRequest setMuted(boolean aMuted);
[Throws]
DOMRequest isActive();
};
partial interface BrowserElementPrivileged {
[Constant, Cached, Throws,
Pref="dom.mozBrowserFramesEnabled",
CheckPermissions="browser"]
readonly attribute sequence<BrowserElementAudioChannel> allowedAudioChannels;
};

View File

@ -57,6 +57,7 @@ WEBIDL_FILES = [
'BoxObject.webidl',
'BroadcastChannel.webidl',
'BrowserElement.webidl',
'BrowserElementAudioChannel.webidl',
'BrowserElementDictionaries.webidl',
'Cache.webidl',
'CacheStorage.webidl',

View File

@ -631,7 +631,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(Geolocation, Init)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsGeolocationService, nsGeolocationService::GetGeolocationService)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AudioChannelService, AudioChannelService::GetOrCreateAudioChannelService)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AudioChannelService, AudioChannelService::GetOrCreate)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DataStoreService, DataStoreService::GetOrCreate)