Coverage Report

Created: 2018-09-25 14:53

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