1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "AudioChannelAgent.h"
6 #include "AudioChannelService.h"
7 #include "mozilla/Preferences.h"
8 #include "nsContentUtils.h"
9 #include "mozilla/dom/Document.h"
10 #include "nsPIDOMWindow.h"
12 using namespace mozilla::dom
;
14 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioChannelAgent
)
16 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioChannelAgent
)
18 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow
)
19 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback
)
20 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
22 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AudioChannelAgent
)
23 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow
)
24 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback
)
25 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
27 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioChannelAgent
)
28 NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgent
)
29 NS_INTERFACE_MAP_ENTRY(nsISupports
)
32 NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioChannelAgent
)
33 NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioChannelAgent
)
35 AudioChannelAgent::AudioChannelAgent()
36 : mInnerWindowID(0), mIsRegToService(false) {
37 // Init service in the begining, it can help us to know whether there is any
38 // created media component via AudioChannelService::IsServiceStarted().
39 RefPtr
<AudioChannelService
> service
= AudioChannelService::GetOrCreate();
42 AudioChannelAgent::~AudioChannelAgent() { Shutdown(); }
44 void AudioChannelAgent::Shutdown() {
45 if (mIsRegToService
) {
46 NotifyStoppedPlaying();
51 AudioChannelAgent::Init(mozIDOMWindow
* aWindow
,
52 nsIAudioChannelAgentCallback
* aCallback
) {
53 return InitInternal(nsPIDOMWindowInner::From(aWindow
), aCallback
,
54 /* useWeakRef = */ false);
58 AudioChannelAgent::InitWithWeakCallback(
59 mozIDOMWindow
* aWindow
, nsIAudioChannelAgentCallback
* aCallback
) {
60 return InitInternal(nsPIDOMWindowInner::From(aWindow
), aCallback
,
61 /* useWeakRef = */ true);
64 nsresult
AudioChannelAgent::FindCorrectWindow(nsPIDOMWindowInner
* aWindow
) {
65 mWindow
= aWindow
->GetInProcessScriptableTop();
66 if (NS_WARN_IF(!mWindow
)) {
70 // From here we do an hack for nested iframes.
71 // The system app doesn't have access to the nested iframe objects so it
72 // cannot control the volume of the agents running in nested apps. What we do
73 // here is to assign those Agents to the top scriptable window of the parent
74 // iframe (what is controlled by the system app).
75 // For doing this we go recursively back into the chain of windows until we
76 // find apps that are not the system one.
77 nsCOMPtr
<nsPIDOMWindowOuter
> outerParent
= mWindow
->GetInProcessParent();
78 if (!outerParent
|| outerParent
== mWindow
) {
82 nsCOMPtr
<nsPIDOMWindowInner
> parent
= outerParent
->GetCurrentInnerWindow();
87 nsCOMPtr
<Document
> doc
= parent
->GetExtantDoc();
92 if (nsContentUtils::IsChromeDoc(doc
)) {
96 return FindCorrectWindow(parent
);
99 nsresult
AudioChannelAgent::InitInternal(
100 nsPIDOMWindowInner
* aWindow
, nsIAudioChannelAgentCallback
* aCallback
,
102 if (NS_WARN_IF(!aWindow
)) {
103 return NS_ERROR_FAILURE
;
106 mInnerWindowID
= aWindow
->WindowID();
108 nsresult rv
= FindCorrectWindow(aWindow
);
109 if (NS_WARN_IF(NS_FAILED(rv
))) {
114 mWeakCallback
= do_GetWeakReference(aCallback
);
116 mCallback
= aCallback
;
119 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
120 ("AudioChannelAgent, InitInternal, this = %p, "
121 "owner = %p, hasCallback = %d\n",
122 this, mWindow
.get(), (!!mCallback
|| !!mWeakCallback
)));
127 void AudioChannelAgent::PullInitialUpdate() {
128 RefPtr
<AudioChannelService
> service
= AudioChannelService::Get();
130 MOZ_ASSERT(mIsRegToService
);
132 AudioPlaybackConfig config
= service
->GetMediaConfig(mWindow
);
133 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
134 ("AudioChannelAgent, PullInitialUpdate, this=%p, "
135 "mute=%s, volume=%f, suspend=%s, audioCapturing=%s\n",
136 this, config
.mMuted
? "true" : "false", config
.mVolume
,
137 SuspendTypeToStr(config
.mSuspend
),
138 config
.mCapturedAudio
? "true" : "false"));
139 WindowVolumeChanged(config
.mVolume
, config
.mMuted
);
140 WindowSuspendChanged(config
.mSuspend
);
141 WindowAudioCaptureChanged(InnerWindowID(), config
.mCapturedAudio
);
145 AudioChannelAgent::NotifyStartedPlaying(uint8_t aAudible
) {
146 RefPtr
<AudioChannelService
> service
= AudioChannelService::GetOrCreate();
147 if (service
== nullptr || mIsRegToService
) {
148 return NS_ERROR_FAILURE
;
151 MOZ_ASSERT(AudioChannelService::AudibleState::eNotAudible
== 0 &&
152 AudioChannelService::AudibleState::eMaybeAudible
== 1 &&
153 AudioChannelService::AudibleState::eAudible
== 2);
154 service
->RegisterAudioChannelAgent(
155 this, static_cast<AudioChannelService::AudibleState
>(aAudible
));
157 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
158 ("AudioChannelAgent, NotifyStartedPlaying, this = %p, audible = %s\n",
161 static_cast<AudioChannelService::AudibleState
>(aAudible
))));
163 mIsRegToService
= true;
168 AudioChannelAgent::NotifyStoppedPlaying() {
169 if (!mIsRegToService
) {
170 return NS_ERROR_FAILURE
;
173 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
174 ("AudioChannelAgent, NotifyStoppedPlaying, this = %p\n", this));
176 RefPtr
<AudioChannelService
> service
= AudioChannelService::GetOrCreate();
178 service
->UnregisterAudioChannelAgent(this);
181 mIsRegToService
= false;
186 AudioChannelAgent::NotifyStartedAudible(uint8_t aAudible
, uint32_t aReason
) {
188 AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
189 ("AudioChannelAgent, NotifyStartedAudible, this = %p, "
190 "audible = %s, reason = %s\n",
193 static_cast<AudioChannelService::AudibleState
>(aAudible
)),
194 AudibleChangedReasonToStr(
195 static_cast<AudioChannelService::AudibleChangedReasons
>(aReason
))));
197 RefPtr
<AudioChannelService
> service
= AudioChannelService::GetOrCreate();
198 if (NS_WARN_IF(!service
)) {
199 return NS_ERROR_FAILURE
;
202 service
->AudioAudibleChanged(
203 this, static_cast<AudioChannelService::AudibleState
>(aAudible
),
204 static_cast<AudioChannelService::AudibleChangedReasons
>(aReason
));
208 already_AddRefed
<nsIAudioChannelAgentCallback
>
209 AudioChannelAgent::GetCallback() {
210 nsCOMPtr
<nsIAudioChannelAgentCallback
> callback
= mCallback
;
212 callback
= do_QueryReferent(mWeakCallback
);
214 return callback
.forget();
217 void AudioChannelAgent::WindowVolumeChanged(float aVolume
, bool aMuted
) {
218 nsCOMPtr
<nsIAudioChannelAgentCallback
> callback
= GetCallback();
223 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
224 ("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %s, "
226 this, aMuted
? "true" : "false", aVolume
));
227 callback
->WindowVolumeChanged(aVolume
, aMuted
);
230 void AudioChannelAgent::WindowSuspendChanged(nsSuspendedTypes aSuspend
) {
231 nsCOMPtr
<nsIAudioChannelAgentCallback
> callback
= GetCallback();
236 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
237 ("AudioChannelAgent, WindowSuspendChanged, this = %p, "
239 this, SuspendTypeToStr(aSuspend
)));
240 callback
->WindowSuspendChanged(aSuspend
);
243 AudioPlaybackConfig
AudioChannelAgent::GetMediaConfig() const {
244 RefPtr
<AudioChannelService
> service
= AudioChannelService::GetOrCreate();
245 AudioPlaybackConfig
config(1.0, false, nsISuspendedTypes::NONE_SUSPENDED
);
247 config
= service
->GetMediaConfig(mWindow
);
252 uint64_t AudioChannelAgent::WindowID() const {
253 return mWindow
? mWindow
->WindowID() : 0;
256 uint64_t AudioChannelAgent::InnerWindowID() const { return mInnerWindowID
; }
258 void AudioChannelAgent::WindowAudioCaptureChanged(uint64_t aInnerWindowID
,
260 if (aInnerWindowID
!= mInnerWindowID
) {
264 nsCOMPtr
<nsIAudioChannelAgentCallback
> callback
= GetCallback();
269 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
270 ("AudioChannelAgent, WindowAudioCaptureChanged, this = %p, "
274 callback
->WindowAudioCaptureChanged(aCapture
);
277 bool AudioChannelAgent::IsWindowAudioCapturingEnabled() const {
278 return GetMediaConfig().mCapturedAudio
;
281 bool AudioChannelAgent::IsPlayingStarted() const { return mIsRegToService
; }