|
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 |