Source code

Revision control

Other Tools

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/. */
4
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"
11
12
using namespace mozilla::dom;
13
14
NS_IMPL_CYCLE_COLLECTION_CLASS(AudioChannelAgent)
15
16
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioChannelAgent)
17
tmp->Shutdown();
18
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
19
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
20
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
21
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
26
27
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioChannelAgent)
28
NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgent)
29
NS_INTERFACE_MAP_ENTRY(nsISupports)
30
NS_INTERFACE_MAP_END
31
32
NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioChannelAgent)
33
NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioChannelAgent)
34
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();
40
}
41
42
AudioChannelAgent::~AudioChannelAgent() { Shutdown(); }
43
44
void AudioChannelAgent::Shutdown() {
45
if (mIsRegToService) {
46
NotifyStoppedPlaying();
47
}
48
}
49
50
NS_IMETHODIMP
51
AudioChannelAgent::Init(mozIDOMWindow* aWindow,
52
nsIAudioChannelAgentCallback* aCallback) {
53
return InitInternal(nsPIDOMWindowInner::From(aWindow), aCallback,
54
/* useWeakRef = */ false);
55
}
56
57
NS_IMETHODIMP
58
AudioChannelAgent::InitWithWeakCallback(
59
mozIDOMWindow* aWindow, nsIAudioChannelAgentCallback* aCallback) {
60
return InitInternal(nsPIDOMWindowInner::From(aWindow), aCallback,
61
/* useWeakRef = */ true);
62
}
63
64
nsresult AudioChannelAgent::FindCorrectWindow(nsPIDOMWindowInner* aWindow) {
65
mWindow = aWindow->GetInProcessScriptableTop();
66
if (NS_WARN_IF(!mWindow)) {
67
return NS_OK;
68
}
69
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) {
79
return NS_OK;
80
}
81
82
nsCOMPtr<nsPIDOMWindowInner> parent = outerParent->GetCurrentInnerWindow();
83
if (!parent) {
84
return NS_OK;
85
}
86
87
nsCOMPtr<Document> doc = parent->GetExtantDoc();
88
if (!doc) {
89
return NS_OK;
90
}
91
92
if (nsContentUtils::IsChromeDoc(doc)) {
93
return NS_OK;
94
}
95
96
return FindCorrectWindow(parent);
97
}
98
99
nsresult AudioChannelAgent::InitInternal(
100
nsPIDOMWindowInner* aWindow, nsIAudioChannelAgentCallback* aCallback,
101
bool aUseWeakRef) {
102
if (NS_WARN_IF(!aWindow)) {
103
return NS_ERROR_FAILURE;
104
}
105
106
mInnerWindowID = aWindow->WindowID();
107
108
nsresult rv = FindCorrectWindow(aWindow);
109
if (NS_WARN_IF(NS_FAILED(rv))) {
110
return rv;
111
}
112
113
if (aUseWeakRef) {
114
mWeakCallback = do_GetWeakReference(aCallback);
115
} else {
116
mCallback = aCallback;
117
}
118
119
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
120
("AudioChannelAgent, InitInternal, this = %p, "
121
"owner = %p, hasCallback = %d\n",
122
this, mWindow.get(), (!!mCallback || !!mWeakCallback)));
123
124
return NS_OK;
125
}
126
127
void AudioChannelAgent::PullInitialUpdate() {
128
RefPtr<AudioChannelService> service = AudioChannelService::Get();
129
MOZ_ASSERT(service);
130
MOZ_ASSERT(mIsRegToService);
131
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);
142
}
143
144
NS_IMETHODIMP
145
AudioChannelAgent::NotifyStartedPlaying(uint8_t aAudible) {
146
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
147
if (service == nullptr || mIsRegToService) {
148
return NS_ERROR_FAILURE;
149
}
150
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));
156
157
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
158
("AudioChannelAgent, NotifyStartedPlaying, this = %p, audible = %s\n",
159
this,
160
AudibleStateToStr(
161
static_cast<AudioChannelService::AudibleState>(aAudible))));
162
163
mIsRegToService = true;
164
return NS_OK;
165
}
166
167
NS_IMETHODIMP
168
AudioChannelAgent::NotifyStoppedPlaying() {
169
if (!mIsRegToService) {
170
return NS_ERROR_FAILURE;
171
}
172
173
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
174
("AudioChannelAgent, NotifyStoppedPlaying, this = %p\n", this));
175
176
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
177
if (service) {
178
service->UnregisterAudioChannelAgent(this);
179
}
180
181
mIsRegToService = false;
182
return NS_OK;
183
}
184
185
NS_IMETHODIMP
186
AudioChannelAgent::NotifyStartedAudible(uint8_t aAudible, uint32_t aReason) {
187
MOZ_LOG(
188
AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
189
("AudioChannelAgent, NotifyStartedAudible, this = %p, "
190
"audible = %s, reason = %s\n",
191
this,
192
AudibleStateToStr(
193
static_cast<AudioChannelService::AudibleState>(aAudible)),
194
AudibleChangedReasonToStr(
195
static_cast<AudioChannelService::AudibleChangedReasons>(aReason))));
196
197
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
198
if (NS_WARN_IF(!service)) {
199
return NS_ERROR_FAILURE;
200
}
201
202
service->AudioAudibleChanged(
203
this, static_cast<AudioChannelService::AudibleState>(aAudible),
204
static_cast<AudioChannelService::AudibleChangedReasons>(aReason));
205
return NS_OK;
206
}
207
208
already_AddRefed<nsIAudioChannelAgentCallback>
209
AudioChannelAgent::GetCallback() {
210
nsCOMPtr<nsIAudioChannelAgentCallback> callback = mCallback;
211
if (!callback) {
212
callback = do_QueryReferent(mWeakCallback);
213
}
214
return callback.forget();
215
}
216
217
void AudioChannelAgent::WindowVolumeChanged(float aVolume, bool aMuted) {
218
nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
219
if (!callback) {
220
return;
221
}
222
223
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
224
("AudioChannelAgent, WindowVolumeChanged, this = %p, mute = %s, "
225
"volume = %f\n",
226
this, aMuted ? "true" : "false", aVolume));
227
callback->WindowVolumeChanged(aVolume, aMuted);
228
}
229
230
void AudioChannelAgent::WindowSuspendChanged(nsSuspendedTypes aSuspend) {
231
nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
232
if (!callback) {
233
return;
234
}
235
236
if (!IsDisposableSuspend(aSuspend)) {
237
aSuspend = GetMediaConfig().mSuspend;
238
}
239
240
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
241
("AudioChannelAgent, WindowSuspendChanged, this = %p, "
242
"suspended = %s\n",
243
this, SuspendTypeToStr(aSuspend)));
244
245
callback->WindowSuspendChanged(aSuspend);
246
}
247
248
AudioPlaybackConfig AudioChannelAgent::GetMediaConfig() const {
249
RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
250
AudioPlaybackConfig config(1.0, false, nsISuspendedTypes::NONE_SUSPENDED);
251
if (service) {
252
config = service->GetMediaConfig(mWindow);
253
}
254
return config;
255
}
256
257
bool AudioChannelAgent::IsDisposableSuspend(nsSuspendedTypes aSuspend) const {
258
return (aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
259
aSuspend == nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE);
260
}
261
262
uint64_t AudioChannelAgent::WindowID() const {
263
return mWindow ? mWindow->WindowID() : 0;
264
}
265
266
uint64_t AudioChannelAgent::InnerWindowID() const { return mInnerWindowID; }
267
268
void AudioChannelAgent::WindowAudioCaptureChanged(uint64_t aInnerWindowID,
269
bool aCapture) {
270
if (aInnerWindowID != mInnerWindowID) {
271
return;
272
}
273
274
nsCOMPtr<nsIAudioChannelAgentCallback> callback = GetCallback();
275
if (!callback) {
276
return;
277
}
278
279
MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
280
("AudioChannelAgent, WindowAudioCaptureChanged, this = %p, "
281
"capture = %d\n",
282
this, aCapture));
283
284
callback->WindowAudioCaptureChanged(aCapture);
285
}
286
287
bool AudioChannelAgent::IsWindowAudioCapturingEnabled() const {
288
return GetMediaConfig().mCapturedAudio;
289
}
290
291
bool AudioChannelAgent::IsPlayingStarted() const { return mIsRegToService; }
292
293
bool AudioChannelAgent::ShouldBlockMedia() const {
294
return mWindow
295
? mWindow->GetMediaSuspend() == nsISuspendedTypes::SUSPENDED_BLOCK
296
: false;
297
}