Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "AudioChannelAgent.h"
8 : #include "AudioChannelService.h"
9 : #include "mozilla/Preferences.h"
10 : #include "nsContentUtils.h"
11 : #include "nsIDocument.h"
12 : #include "nsIDOMWindow.h"
13 : #include "nsPIDOMWindow.h"
14 : #include "nsIURI.h"
15 :
16 : using namespace mozilla::dom;
17 :
18 : NS_IMPL_CYCLE_COLLECTION_CLASS(AudioChannelAgent)
19 :
20 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioChannelAgent)
21 0 : tmp->Shutdown();
22 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
23 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
24 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
25 :
26 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AudioChannelAgent)
27 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
28 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
29 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
30 :
31 1 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioChannelAgent)
32 0 : NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgent)
33 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
34 0 : NS_INTERFACE_MAP_END
35 :
36 1 : NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioChannelAgent)
37 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioChannelAgent)
38 :
39 1 : AudioChannelAgent::AudioChannelAgent()
40 : : mAudioChannelType(AUDIO_AGENT_CHANNEL_ERROR)
41 : , mInnerWindowID(0)
42 1 : , mIsRegToService(false)
43 : {
44 : // Init service in the begining, it can help us to know whether there is any
45 : // created media component via AudioChannelService::IsServiceStarted().
46 1 : RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
47 1 : }
48 :
49 0 : AudioChannelAgent::~AudioChannelAgent()
50 : {
51 0 : Shutdown();
52 0 : }
53 :
54 : void
55 0 : AudioChannelAgent::Shutdown()
56 : {
57 0 : if (mIsRegToService) {
58 0 : NotifyStoppedPlaying();
59 : }
60 0 : }
61 :
62 0 : NS_IMETHODIMP AudioChannelAgent::GetAudioChannelType(int32_t *aAudioChannelType)
63 : {
64 0 : *aAudioChannelType = mAudioChannelType;
65 0 : return NS_OK;
66 : }
67 :
68 : NS_IMETHODIMP
69 1 : AudioChannelAgent::Init(mozIDOMWindow* aWindow, int32_t aChannelType,
70 : nsIAudioChannelAgentCallback *aCallback)
71 : {
72 1 : return InitInternal(nsPIDOMWindowInner::From(aWindow), aChannelType,
73 1 : aCallback, /* useWeakRef = */ false);
74 : }
75 :
76 : NS_IMETHODIMP
77 0 : AudioChannelAgent::InitWithWeakCallback(mozIDOMWindow* aWindow,
78 : int32_t aChannelType,
79 : nsIAudioChannelAgentCallback *aCallback)
80 : {
81 0 : return InitInternal(nsPIDOMWindowInner::From(aWindow), aChannelType,
82 0 : aCallback, /* useWeakRef = */ true);
83 : }
84 :
85 : nsresult
86 1 : AudioChannelAgent::FindCorrectWindow(nsPIDOMWindowInner* aWindow)
87 : {
88 1 : MOZ_ASSERT(aWindow->IsInnerWindow());
89 :
90 1 : mWindow = aWindow->GetScriptableTop();
91 1 : if (NS_WARN_IF(!mWindow)) {
92 0 : return NS_OK;
93 : }
94 :
95 : // From here we do an hack for nested iframes.
96 : // The system app doesn't have access to the nested iframe objects so it
97 : // cannot control the volume of the agents running in nested apps. What we do
98 : // here is to assign those Agents to the top scriptable window of the parent
99 : // iframe (what is controlled by the system app).
100 : // For doing this we go recursively back into the chain of windows until we
101 : // find apps that are not the system one.
102 2 : nsCOMPtr<nsPIDOMWindowOuter> outerParent = mWindow->GetParent();
103 1 : if (!outerParent || outerParent == mWindow) {
104 1 : return NS_OK;
105 : }
106 :
107 0 : nsCOMPtr<nsPIDOMWindowInner> parent = outerParent->GetCurrentInnerWindow();
108 0 : if (!parent) {
109 0 : return NS_OK;
110 : }
111 :
112 0 : nsCOMPtr<nsIDocument> doc = parent->GetExtantDoc();
113 0 : if (!doc) {
114 0 : return NS_OK;
115 : }
116 :
117 0 : if (nsContentUtils::IsChromeDoc(doc)) {
118 0 : return NS_OK;
119 : }
120 :
121 : nsAdoptingCString systemAppUrl =
122 0 : mozilla::Preferences::GetCString("b2g.system_startup_url");
123 0 : if (!systemAppUrl) {
124 0 : return NS_OK;
125 : }
126 :
127 0 : nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
128 0 : nsCOMPtr<nsIURI> uri;
129 0 : principal->GetURI(getter_AddRefs(uri));
130 :
131 0 : if (uri) {
132 0 : nsAutoCString spec;
133 0 : uri->GetSpec(spec);
134 :
135 0 : if (spec.Equals(systemAppUrl)) {
136 0 : return NS_OK;
137 : }
138 : }
139 :
140 0 : return FindCorrectWindow(parent);
141 : }
142 :
143 : nsresult
144 1 : AudioChannelAgent::InitInternal(nsPIDOMWindowInner* aWindow,
145 : int32_t aChannelType,
146 : nsIAudioChannelAgentCallback *aCallback,
147 : bool aUseWeakRef)
148 : {
149 : // We syncd the enum of channel type between nsIAudioChannelAgent.idl and
150 : // AudioChannelBinding.h the same.
151 : MOZ_ASSERT(int(AUDIO_AGENT_CHANNEL_NORMAL) == int(AudioChannel::Normal) &&
152 : int(AUDIO_AGENT_CHANNEL_CONTENT) == int(AudioChannel::Content) &&
153 : int(AUDIO_AGENT_CHANNEL_NOTIFICATION) == int(AudioChannel::Notification) &&
154 : int(AUDIO_AGENT_CHANNEL_ALARM) == int(AudioChannel::Alarm) &&
155 : int(AUDIO_AGENT_CHANNEL_TELEPHONY) == int(AudioChannel::Telephony) &&
156 : int(AUDIO_AGENT_CHANNEL_RINGER) == int(AudioChannel::Ringer) &&
157 : int(AUDIO_AGENT_CHANNEL_SYSTEM) == int(AudioChannel::System) &&
158 : int(AUDIO_AGENT_CHANNEL_PUBLICNOTIFICATION) == int(AudioChannel::Publicnotification),
159 : "Enum of channel on nsIAudioChannelAgent.idl should be the same with AudioChannelBinding.h");
160 :
161 1 : if (mAudioChannelType != AUDIO_AGENT_CHANNEL_ERROR ||
162 1 : aChannelType > AUDIO_AGENT_CHANNEL_SYSTEM ||
163 : aChannelType < AUDIO_AGENT_CHANNEL_NORMAL) {
164 0 : return NS_ERROR_FAILURE;
165 : }
166 :
167 1 : if (NS_WARN_IF(!aWindow)) {
168 0 : return NS_ERROR_FAILURE;
169 : }
170 :
171 1 : MOZ_ASSERT(aWindow->IsInnerWindow());
172 1 : mInnerWindowID = aWindow->WindowID();
173 :
174 1 : nsresult rv = FindCorrectWindow(aWindow);
175 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
176 0 : return rv;
177 : }
178 :
179 1 : mAudioChannelType = aChannelType;
180 :
181 1 : if (aUseWeakRef) {
182 0 : mWeakCallback = do_GetWeakReference(aCallback);
183 : } else {
184 1 : mCallback = aCallback;
185 : }
186 :
187 1 : MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
188 : ("AudioChannelAgent, InitInternal, this = %p, type = %d, "
189 : "owner = %p, hasCallback = %d\n", this, mAudioChannelType,
190 : mWindow.get(), (!!mCallback || !!mWeakCallback)));
191 :
192 1 : return NS_OK;
193 : }
194 :
195 : NS_IMETHODIMP
196 0 : AudioChannelAgent::NotifyStartedPlaying(AudioPlaybackConfig* aConfig,
197 : uint8_t aAudible)
198 : {
199 0 : if (NS_WARN_IF(!aConfig)) {
200 0 : return NS_ERROR_FAILURE;
201 : }
202 :
203 0 : RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
204 0 : if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
205 0 : service == nullptr || mIsRegToService) {
206 0 : return NS_ERROR_FAILURE;
207 : }
208 :
209 : MOZ_ASSERT(AudioChannelService::AudibleState::eNotAudible == 0 &&
210 : AudioChannelService::AudibleState::eMaybeAudible == 1 &&
211 : AudioChannelService::AudibleState::eAudible == 2);
212 0 : service->RegisterAudioChannelAgent(this,
213 0 : static_cast<AudioChannelService::AudibleState>(aAudible));
214 :
215 : AudioPlaybackConfig config = service->GetMediaConfig(mWindow,
216 0 : mAudioChannelType);
217 :
218 0 : MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
219 : ("AudioChannelAgent, NotifyStartedPlaying, this = %p, "
220 : "audible = %s, mute = %s, volume = %f, suspend = %s\n", this,
221 : AudibleStateToStr(static_cast<AudioChannelService::AudibleState>(aAudible)),
222 : config.mMuted ? "true" : "false", config.mVolume,
223 : SuspendTypeToStr(config.mSuspend)));
224 :
225 0 : aConfig->SetConfig(config.mVolume, config.mMuted, config.mSuspend);
226 0 : mIsRegToService = true;
227 0 : return NS_OK;
228 : }
229 :
230 : NS_IMETHODIMP
231 0 : AudioChannelAgent::NotifyStoppedPlaying()
232 : {
233 0 : if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
234 0 : !mIsRegToService) {
235 0 : return NS_ERROR_FAILURE;
236 : }
237 :
238 0 : MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
239 : ("AudioChannelAgent, NotifyStoppedPlaying, this = %p\n", this));
240 :
241 0 : RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
242 0 : if (service) {
243 0 : service->UnregisterAudioChannelAgent(this);
244 : }
245 :
246 0 : mIsRegToService = false;
247 0 : return NS_OK;
248 : }
249 :
250 : NS_IMETHODIMP
251 0 : AudioChannelAgent::NotifyStartedAudible(uint8_t aAudible, uint32_t aReason)
252 : {
253 0 : MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
254 : ("AudioChannelAgent, NotifyStartedAudible, this = %p, "
255 : "audible = %s, reason = %s\n", this,
256 : AudibleStateToStr(static_cast<AudioChannelService::AudibleState>(aAudible)),
257 : AudibleChangedReasonToStr(static_cast<AudioChannelService::AudibleChangedReasons>(aReason))));
258 :
259 0 : RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
260 0 : if (NS_WARN_IF(!service)) {
261 0 : return NS_ERROR_FAILURE;
262 : }
263 :
264 0 : service->AudioAudibleChanged(
265 : this,
266 : static_cast<AudioChannelService::AudibleState>(aAudible),
267 0 : static_cast<AudioChannelService::AudibleChangedReasons>(aReason));
268 0 : return NS_OK;
269 : }
270 :
271 : already_AddRefed<nsIAudioChannelAgentCallback>
272 0 : AudioChannelAgent::GetCallback()
273 : {
274 0 : nsCOMPtr<nsIAudioChannelAgentCallback> callback = mCallback;
275 0 : if (!callback) {
276 0 : callback = do_QueryReferent(mWeakCallback);
277 : }
278 0 : return callback.forget();
279 : }
280 :
281 : void
282 0 : AudioChannelAgent::WindowVolumeChanged()
283 : {
284 0 : nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
285 0 : if (!callback) {
286 0 : return;
287 : }
288 :
289 0 : AudioPlaybackConfig config = GetMediaConfig();
290 0 : MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
291 : ("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %s, "
292 : "volume = %f\n",
293 : this, config.mMuted ? "true" : "false", config.mVolume));
294 :
295 0 : callback->WindowVolumeChanged(config.mVolume, config.mMuted);
296 : }
297 :
298 : void
299 0 : AudioChannelAgent::WindowSuspendChanged(nsSuspendedTypes aSuspend)
300 : {
301 0 : nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
302 0 : if (!callback) {
303 0 : return;
304 : }
305 :
306 0 : if (!IsDisposableSuspend(aSuspend)) {
307 0 : aSuspend = GetMediaConfig().mSuspend;
308 : }
309 :
310 0 : MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
311 : ("AudioChannelAgent, WindowSuspendChanged, this = %p, "
312 : "suspended = %s\n", this, SuspendTypeToStr(aSuspend)));
313 :
314 0 : callback->WindowSuspendChanged(aSuspend);
315 : }
316 :
317 : AudioPlaybackConfig
318 0 : AudioChannelAgent::GetMediaConfig()
319 : {
320 0 : RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
321 0 : AudioPlaybackConfig config(1.0, false, nsISuspendedTypes::NONE_SUSPENDED);
322 0 : if (service) {
323 0 : config = service->GetMediaConfig(mWindow, mAudioChannelType);
324 : }
325 0 : return config;
326 : }
327 :
328 : bool
329 0 : AudioChannelAgent::IsDisposableSuspend(nsSuspendedTypes aSuspend) const
330 : {
331 0 : return (aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
332 0 : aSuspend == nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE);
333 : }
334 :
335 : uint64_t
336 0 : AudioChannelAgent::WindowID() const
337 : {
338 0 : return mWindow ? mWindow->WindowID() : 0;
339 : }
340 :
341 : uint64_t
342 0 : AudioChannelAgent::InnerWindowID() const
343 : {
344 0 : return mInnerWindowID;
345 : }
346 :
347 : void
348 0 : AudioChannelAgent::WindowAudioCaptureChanged(uint64_t aInnerWindowID,
349 : bool aCapture)
350 : {
351 0 : if (aInnerWindowID != mInnerWindowID) {
352 0 : return;
353 : }
354 :
355 0 : nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
356 0 : if (!callback) {
357 0 : return;
358 : }
359 :
360 0 : MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
361 : ("AudioChannelAgent, WindowAudioCaptureChanged, this = %p, "
362 : "capture = %d\n", this, aCapture));
363 :
364 0 : callback->WindowAudioCaptureChanged(aCapture);
365 : }
366 :
367 : bool
368 0 : AudioChannelAgent::IsPlayingStarted() const
369 : {
370 0 : return mIsRegToService;
371 : }
372 :
373 : bool
374 0 : AudioChannelAgent::ShouldBlockMedia() const
375 : {
376 0 : return mWindow ?
377 0 : mWindow->GetMediaSuspend() == nsISuspendedTypes::SUSPENDED_BLOCK : false;
378 : }
|