dom/fmradio/FMRadio.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:79ac40301fe0
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 "mozilla/dom/FMRadio.h"
8 #include "nsContentUtils.h"
9 #include "mozilla/Hal.h"
10 #include "mozilla/HalTypes.h"
11 #include "mozilla/Preferences.h"
12 #include "mozilla/dom/FMRadioBinding.h"
13 #include "mozilla/dom/ContentChild.h"
14 #include "mozilla/dom/PFMRadioChild.h"
15 #include "mozilla/dom/FMRadioService.h"
16 #include "DOMRequest.h"
17 #include "nsDOMClassInfo.h"
18 #include "nsIDocShell.h"
19 #include "nsIInterfaceRequestorUtils.h"
20 #include "nsIAudioManager.h"
21
22 #undef LOG
23 #define LOG(args...) FM_LOG("FMRadio", args)
24
25 // The pref indicates if the device has an internal antenna.
26 // If the pref is true, the antanna will be always available.
27 #define DOM_FM_ANTENNA_INTERNAL_PREF "dom.fmradio.antenna.internal"
28
29 using namespace mozilla::hal;
30 using mozilla::Preferences;
31
32 BEGIN_FMRADIO_NAMESPACE
33
34 class FMRadioRequest MOZ_FINAL : public FMRadioReplyRunnable
35 , public DOMRequest
36 {
37 public:
38 NS_DECL_ISUPPORTS_INHERITED
39
40 FMRadioRequest(nsPIDOMWindow* aWindow, FMRadio* aFMRadio)
41 : DOMRequest(aWindow)
42 , mType(FMRadioRequestArgs::T__None)
43 {
44 // |FMRadio| inherits from |nsIDOMEventTarget| and |nsISupportsWeakReference|
45 // which both inherits from nsISupports, so |nsISupports| is an ambiguous
46 // base of |FMRadio|, we have to cast |aFMRadio| to one of the base classes.
47 mFMRadio = do_GetWeakReference(static_cast<nsIDOMEventTarget*>(aFMRadio));
48 }
49
50 FMRadioRequest(nsPIDOMWindow* aWindow, FMRadio* aFMRadio,
51 FMRadioRequestArgs::Type aType)
52 : DOMRequest(aWindow)
53 {
54 MOZ_ASSERT(aType >= FMRadioRequestArgs::T__None &&
55 aType <= FMRadioRequestArgs::T__Last,
56 "Wrong FMRadioRequestArgs in FMRadioRequest");
57
58 mFMRadio = do_GetWeakReference(static_cast<nsIDOMEventTarget*>(aFMRadio));
59 mType = aType;
60 }
61
62 ~FMRadioRequest() { }
63
64 NS_IMETHODIMP
65 Run()
66 {
67 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
68
69 nsCOMPtr<nsIDOMEventTarget> target = do_QueryReferent(mFMRadio);
70 if (!target) {
71 return NS_OK;
72 }
73
74 FMRadio* fmRadio = static_cast<FMRadio*>(
75 static_cast<nsIDOMEventTarget*>(target));
76
77 if (fmRadio->mIsShutdown) {
78 return NS_OK;
79 }
80
81 switch (mResponseType.type()) {
82 case FMRadioResponseType::TErrorResponse:
83 FireError(mResponseType.get_ErrorResponse().error());
84 break;
85 case FMRadioResponseType::TSuccessResponse:
86 if (mType == FMRadioRequestArgs::TEnableRequestArgs) {
87 fmRadio->EnableAudioChannelAgent();
88 }
89
90 FireSuccess(JS::UndefinedHandleValue);
91 break;
92 default:
93 MOZ_CRASH();
94 }
95
96 return NS_OK;
97 }
98
99 private:
100 FMRadioRequestArgs::Type mType;
101 nsWeakPtr mFMRadio;
102 };
103
104 NS_IMPL_ISUPPORTS_INHERITED0(FMRadioRequest, DOMRequest)
105
106 FMRadio::FMRadio()
107 : mHeadphoneState(SWITCH_STATE_OFF)
108 , mAudioChannelAgentEnabled(false)
109 , mHasInternalAntenna(false)
110 , mIsShutdown(false)
111 {
112 LOG("FMRadio is initialized.");
113
114 SetIsDOMBinding();
115 }
116
117 FMRadio::~FMRadio()
118 {
119 }
120
121 void
122 FMRadio::Init(nsPIDOMWindow *aWindow)
123 {
124 BindToOwner(aWindow);
125
126 IFMRadioService::Singleton()->AddObserver(this);
127
128 mHasInternalAntenna = Preferences::GetBool(DOM_FM_ANTENNA_INTERNAL_PREF,
129 /* default = */ false);
130 if (mHasInternalAntenna) {
131 LOG("We have an internal antenna.");
132 } else {
133 mHeadphoneState = GetCurrentSwitchState(SWITCH_HEADPHONES);
134 RegisterSwitchObserver(SWITCH_HEADPHONES, this);
135 }
136
137 nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
138 NS_ENSURE_TRUE_VOID(target);
139 target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
140 /* useCapture = */ true,
141 /* wantsUntrusted = */ false);
142
143
144 // All of the codes below are for AudioChannel. We can directly return here
145 // if preferences doesn't enable AudioChannelService.
146 NS_ENSURE_TRUE_VOID(Preferences::GetBool("media.useAudioChannelService"));
147
148 nsCOMPtr<nsIAudioChannelAgent> audioChannelAgent =
149 do_CreateInstance("@mozilla.org/audiochannelagent;1");
150 NS_ENSURE_TRUE_VOID(audioChannelAgent);
151
152 audioChannelAgent->InitWithWeakCallback(
153 GetOwner(),
154 nsIAudioChannelAgent::AUDIO_AGENT_CHANNEL_CONTENT,
155 this);
156
157 nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
158 NS_ENSURE_TRUE_VOID(docshell);
159
160 bool isActive = false;
161 docshell->GetIsActive(&isActive);
162 audioChannelAgent->SetVisibilityState(isActive);
163
164 // Once all necessary resources are got successfully, we just enabled
165 // mAudioChannelAgent.
166 mAudioChannelAgent = audioChannelAgent;
167 }
168
169 void
170 FMRadio::Shutdown()
171 {
172 IFMRadioService::Singleton()->RemoveObserver(this);
173
174 if (!mHasInternalAntenna) {
175 UnregisterSwitchObserver(SWITCH_HEADPHONES, this);
176 }
177
178 nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
179 NS_ENSURE_TRUE_VOID(target);
180 target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this,
181 /* useCapture = */ true);
182
183 mIsShutdown = true;
184 }
185
186 JSObject*
187 FMRadio::WrapObject(JSContext* aCx)
188 {
189 return FMRadioBinding::Wrap(aCx, this);
190 }
191
192 void
193 FMRadio::Notify(const SwitchEvent& aEvent)
194 {
195 MOZ_ASSERT(!mHasInternalAntenna);
196
197 if (mHeadphoneState != aEvent.status()) {
198 mHeadphoneState = aEvent.status();
199
200 DispatchTrustedEvent(NS_LITERAL_STRING("antennaavailablechange"));
201 }
202 }
203
204 void
205 FMRadio::Notify(const FMRadioEventType& aType)
206 {
207 switch (aType) {
208 case FrequencyChanged:
209 DispatchTrustedEvent(NS_LITERAL_STRING("frequencychange"));
210 break;
211 case EnabledChanged:
212 if (Enabled()) {
213 DispatchTrustedEvent(NS_LITERAL_STRING("enabled"));
214 } else {
215 if (mAudioChannelAgentEnabled) {
216 mAudioChannelAgent->StopPlaying();
217 mAudioChannelAgentEnabled = false;
218 }
219
220 DispatchTrustedEvent(NS_LITERAL_STRING("disabled"));
221 }
222 break;
223 default:
224 MOZ_CRASH();
225 }
226 }
227
228 /* static */
229 bool
230 FMRadio::Enabled()
231 {
232 return IFMRadioService::Singleton()->IsEnabled();
233 }
234
235 bool
236 FMRadio::AntennaAvailable() const
237 {
238 return mHasInternalAntenna ? true : (mHeadphoneState != SWITCH_STATE_OFF) &&
239 (mHeadphoneState != SWITCH_STATE_UNKNOWN);
240 }
241
242 Nullable<double>
243 FMRadio::GetFrequency() const
244 {
245 return Enabled() ?
246 Nullable<double>(IFMRadioService::Singleton()->GetFrequency()) :
247 Nullable<double>();
248 }
249
250 double
251 FMRadio::FrequencyUpperBound() const
252 {
253 return IFMRadioService::Singleton()->GetFrequencyUpperBound();
254 }
255
256 double
257 FMRadio::FrequencyLowerBound() const
258 {
259 return IFMRadioService::Singleton()->GetFrequencyLowerBound();
260 }
261
262 double
263 FMRadio::ChannelWidth() const
264 {
265 return IFMRadioService::Singleton()->GetChannelWidth();
266 }
267
268 already_AddRefed<DOMRequest>
269 FMRadio::Enable(double aFrequency)
270 {
271 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
272 if (!win) {
273 return nullptr;
274 }
275
276 nsRefPtr<FMRadioRequest> r =
277 new FMRadioRequest(win, this, FMRadioRequestArgs::TEnableRequestArgs);
278 IFMRadioService::Singleton()->Enable(aFrequency, r);
279
280 return r.forget();
281 }
282
283 already_AddRefed<DOMRequest>
284 FMRadio::Disable()
285 {
286 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
287 if (!win) {
288 return nullptr;
289 }
290
291 nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
292 IFMRadioService::Singleton()->Disable(r);
293
294 return r.forget();
295 }
296
297 already_AddRefed<DOMRequest>
298 FMRadio::SetFrequency(double aFrequency)
299 {
300 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
301 if (!win) {
302 return nullptr;
303 }
304
305 nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
306 IFMRadioService::Singleton()->SetFrequency(aFrequency, r);
307
308 return r.forget();
309 }
310
311 already_AddRefed<DOMRequest>
312 FMRadio::SeekUp()
313 {
314 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
315 if (!win) {
316 return nullptr;
317 }
318
319 nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
320 IFMRadioService::Singleton()->Seek(FM_RADIO_SEEK_DIRECTION_UP, r);
321
322 return r.forget();
323 }
324
325 already_AddRefed<DOMRequest>
326 FMRadio::SeekDown()
327 {
328 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
329 if (!win) {
330 return nullptr;
331 }
332
333 nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
334 IFMRadioService::Singleton()->Seek(FM_RADIO_SEEK_DIRECTION_DOWN, r);
335
336 return r.forget();
337 }
338
339 already_AddRefed<DOMRequest>
340 FMRadio::CancelSeek()
341 {
342 nsCOMPtr<nsPIDOMWindow> win = GetOwner();
343 if (!win) {
344 return nullptr;
345 }
346
347 nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
348 IFMRadioService::Singleton()->CancelSeek(r);
349
350 return r.forget();
351 }
352
353 NS_IMETHODIMP
354 FMRadio::HandleEvent(nsIDOMEvent* aEvent)
355 {
356 nsAutoString type;
357 aEvent->GetType(type);
358
359 if (!type.EqualsLiteral("visibilitychange")) {
360 return NS_ERROR_FAILURE;
361 }
362
363 nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
364 NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE);
365
366 bool isActive = false;
367 docshell->GetIsActive(&isActive);
368
369 mAudioChannelAgent->SetVisibilityState(isActive);
370 return NS_OK;
371 }
372
373 void
374 FMRadio::EnableAudioChannelAgent()
375 {
376 NS_ENSURE_TRUE_VOID(mAudioChannelAgent);
377
378 int32_t playingState = 0;
379 mAudioChannelAgent->StartPlaying(&playingState);
380 SetCanPlay(playingState == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL);
381
382 mAudioChannelAgentEnabled = true;
383 }
384
385 NS_IMETHODIMP
386 FMRadio::CanPlayChanged(int32_t aCanPlay)
387 {
388 SetCanPlay(aCanPlay == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL);
389 return NS_OK;
390 }
391
392 NS_IMETHODIMP
393 FMRadio::WindowVolumeChanged()
394 {
395 return NS_ERROR_NOT_IMPLEMENTED;
396 }
397
398 void
399 FMRadio::SetCanPlay(bool aCanPlay)
400 {
401 IFMRadioService::Singleton()->EnableAudio(aCanPlay);
402 }
403
404 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FMRadio)
405 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
406 NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
407 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
408 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
409
410 NS_IMPL_ADDREF_INHERITED(FMRadio, DOMEventTargetHelper)
411 NS_IMPL_RELEASE_INHERITED(FMRadio, DOMEventTargetHelper)
412
413 END_FMRADIO_NAMESPACE
414

mercurial