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"
7 #include "AudioChannelService.h"
8 #include "mozilla/Preferences.h"
9 #include "mozilla/dom/Document.h"
10 #include "nsContentUtils.h"
11 #include "nsPIDOMWindow.h"
13 using namespace mozilla::dom
;
15 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioChannelAgent
)
17 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioChannelAgent
)
19 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow
)
20 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback
)
21 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
23 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AudioChannelAgent
)
24 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow
)
25 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback
)
26 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
28 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioChannelAgent
)
29 NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgent
)
30 NS_INTERFACE_MAP_ENTRY(nsISupports
)
33 NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioChannelAgent
)
34 NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioChannelAgent
)
36 AudioChannelAgent::AudioChannelAgent()
37 : mInnerWindowID(0), mIsRegToService(false) {
38 // Init service in the begining, it can help us to know whether there is any
39 // created media component via AudioChannelService::IsServiceStarted().
40 RefPtr
<AudioChannelService
> service
= AudioChannelService::GetOrCreate();
43 AudioChannelAgent::~AudioChannelAgent() { Shutdown(); }
45 void AudioChannelAgent::Shutdown() {
46 if (mIsRegToService
) {
47 NotifyStoppedPlaying();
52 AudioChannelAgent::Init(mozIDOMWindow
* aWindow
,
53 nsIAudioChannelAgentCallback
* aCallback
) {
54 return InitInternal(nsPIDOMWindowInner::From(aWindow
), aCallback
,
55 /* useWeakRef = */ false);
59 AudioChannelAgent::InitWithWeakCallback(
60 mozIDOMWindow
* aWindow
, nsIAudioChannelAgentCallback
* aCallback
) {
61 return InitInternal(nsPIDOMWindowInner::From(aWindow
), aCallback
,
62 /* useWeakRef = */ true);
65 nsresult
AudioChannelAgent::FindCorrectWindow(nsPIDOMWindowInner
* aWindow
) {
66 mWindow
= aWindow
->GetInProcessScriptableTop();
67 if (NS_WARN_IF(!mWindow
)) {
71 // From here we do an hack for nested iframes.
72 // The system app doesn't have access to the nested iframe objects so it
73 // cannot control the volume of the agents running in nested apps. What we do
74 // here is to assign those Agents to the top scriptable window of the parent
75 // iframe (what is controlled by the system app).
76 // For doing this we go recursively back into the chain of windows until we
77 // find apps that are not the system one.
78 nsCOMPtr
<nsPIDOMWindowOuter
> outerParent
= mWindow
->GetInProcessParent();
79 if (!outerParent
|| outerParent
== mWindow
) {
83 nsCOMPtr
<nsPIDOMWindowInner
> parent
= outerParent
->GetCurrentInnerWindow();
88 nsCOMPtr
<Document
> doc
= parent
->GetExtantDoc();
93 if (nsContentUtils::IsChromeDoc(doc
)) {
97 return FindCorrectWindow(parent
);
100 nsresult
AudioChannelAgent::InitInternal(
101 nsPIDOMWindowInner
* aWindow
, nsIAudioChannelAgentCallback
* aCallback
,
103 if (NS_WARN_IF(!aWindow
)) {
104 return NS_ERROR_FAILURE
;
107 mInnerWindowID
= aWindow
->WindowID();
109 nsresult rv
= FindCorrectWindow(aWindow
);
110 if (NS_WARN_IF(NS_FAILED(rv
))) {
115 mWeakCallback
= do_GetWeakReference(aCallback
);
117 mCallback
= aCallback
;
120 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
121 ("AudioChannelAgent, InitInternal, this = %p, "
122 "owner = %p, hasCallback = %d\n",
123 this, mWindow
.get(), (!!mCallback
|| !!mWeakCallback
)));
128 void AudioChannelAgent::PullInitialUpdate() {
129 RefPtr
<AudioChannelService
> service
= AudioChannelService::Get();
131 MOZ_ASSERT(mIsRegToService
);
133 AudioPlaybackConfig config
= service
->GetMediaConfig(mWindow
);
134 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
135 ("AudioChannelAgent, PullInitialUpdate, this=%p, "
136 "mute=%s, volume=%f, suspend=%s, audioCapturing=%s\n",
137 this, config
.mMuted
? "true" : "false", config
.mVolume
,
138 SuspendTypeToStr(config
.mSuspend
),
139 config
.mCapturedAudio
? "true" : "false"));
140 WindowVolumeChanged(config
.mVolume
, config
.mMuted
);
141 WindowSuspendChanged(config
.mSuspend
);
142 WindowAudioCaptureChanged(InnerWindowID(), config
.mCapturedAudio
);
146 AudioChannelAgent::NotifyStartedPlaying(uint8_t aAudible
) {
147 RefPtr
<AudioChannelService
> service
= AudioChannelService::GetOrCreate();
148 if (service
== nullptr || mIsRegToService
) {
149 return NS_ERROR_FAILURE
;
152 MOZ_ASSERT(AudioChannelService::AudibleState::eNotAudible
== 0 &&
153 AudioChannelService::AudibleState::eMaybeAudible
== 1 &&
154 AudioChannelService::AudibleState::eAudible
== 2);
155 service
->RegisterAudioChannelAgent(
156 this, static_cast<AudioChannelService::AudibleState
>(aAudible
));
158 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
159 ("AudioChannelAgent, NotifyStartedPlaying, this = %p, audible = %s\n",
161 AudioChannelService::EnumValueToString(
162 static_cast<AudioChannelService::AudibleState
>(aAudible
))));
164 mIsRegToService
= true;
169 AudioChannelAgent::NotifyStoppedPlaying() {
170 if (!mIsRegToService
) {
171 return NS_ERROR_FAILURE
;
174 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
175 ("AudioChannelAgent, NotifyStoppedPlaying, this = %p\n", this));
177 RefPtr
<AudioChannelService
> service
= AudioChannelService::GetOrCreate();
179 service
->UnregisterAudioChannelAgent(this);
182 mIsRegToService
= false;
187 AudioChannelAgent::NotifyStartedAudible(uint8_t aAudible
, uint32_t aReason
) {
189 AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
190 ("AudioChannelAgent, NotifyStartedAudible, this = %p, "
191 "audible = %s, reason = %s\n",
193 AudioChannelService::EnumValueToString(
194 static_cast<AudioChannelService::AudibleState
>(aAudible
)),
195 AudioChannelService::EnumValueToString(
196 static_cast<AudioChannelService::AudibleChangedReasons
>(aReason
))));
198 RefPtr
<AudioChannelService
> service
= AudioChannelService::GetOrCreate();
199 if (NS_WARN_IF(!service
)) {
200 return NS_ERROR_FAILURE
;
203 service
->AudioAudibleChanged(
204 this, static_cast<AudioChannelService::AudibleState
>(aAudible
),
205 static_cast<AudioChannelService::AudibleChangedReasons
>(aReason
));
209 already_AddRefed
<nsIAudioChannelAgentCallback
>
210 AudioChannelAgent::GetCallback() {
211 nsCOMPtr
<nsIAudioChannelAgentCallback
> callback
= mCallback
;
213 callback
= do_QueryReferent(mWeakCallback
);
215 return callback
.forget();
218 void AudioChannelAgent::WindowVolumeChanged(float aVolume
, bool aMuted
) {
219 nsCOMPtr
<nsIAudioChannelAgentCallback
> callback
= GetCallback();
224 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
225 ("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %s, "
227 this, aMuted
? "true" : "false", aVolume
));
228 callback
->WindowVolumeChanged(aVolume
, aMuted
);
231 void AudioChannelAgent::WindowSuspendChanged(nsSuspendedTypes aSuspend
) {
232 nsCOMPtr
<nsIAudioChannelAgentCallback
> callback
= GetCallback();
237 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
238 ("AudioChannelAgent, WindowSuspendChanged, this = %p, "
240 this, SuspendTypeToStr(aSuspend
)));
241 callback
->WindowSuspendChanged(aSuspend
);
244 AudioPlaybackConfig
AudioChannelAgent::GetMediaConfig() const {
245 RefPtr
<AudioChannelService
> service
= AudioChannelService::GetOrCreate();
246 AudioPlaybackConfig
config(1.0, false, nsISuspendedTypes::NONE_SUSPENDED
);
248 config
= service
->GetMediaConfig(mWindow
);
253 uint64_t AudioChannelAgent::WindowID() const {
254 return mWindow
? mWindow
->WindowID() : 0;
257 uint64_t AudioChannelAgent::InnerWindowID() const { return mInnerWindowID
; }
259 void AudioChannelAgent::WindowAudioCaptureChanged(uint64_t aInnerWindowID
,
261 if (aInnerWindowID
!= mInnerWindowID
) {
265 nsCOMPtr
<nsIAudioChannelAgentCallback
> callback
= GetCallback();
270 MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug
,
271 ("AudioChannelAgent, WindowAudioCaptureChanged, this = %p, "
275 callback
->WindowAudioCaptureChanged(aCapture
);
278 bool AudioChannelAgent::IsWindowAudioCapturingEnabled() const {
279 return GetMediaConfig().mCapturedAudio
;
282 bool AudioChannelAgent::IsPlayingStarted() const { return mIsRegToService
; }