1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/fmradio/FMRadio.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,414 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "mozilla/dom/FMRadio.h" 1.11 +#include "nsContentUtils.h" 1.12 +#include "mozilla/Hal.h" 1.13 +#include "mozilla/HalTypes.h" 1.14 +#include "mozilla/Preferences.h" 1.15 +#include "mozilla/dom/FMRadioBinding.h" 1.16 +#include "mozilla/dom/ContentChild.h" 1.17 +#include "mozilla/dom/PFMRadioChild.h" 1.18 +#include "mozilla/dom/FMRadioService.h" 1.19 +#include "DOMRequest.h" 1.20 +#include "nsDOMClassInfo.h" 1.21 +#include "nsIDocShell.h" 1.22 +#include "nsIInterfaceRequestorUtils.h" 1.23 +#include "nsIAudioManager.h" 1.24 + 1.25 +#undef LOG 1.26 +#define LOG(args...) FM_LOG("FMRadio", args) 1.27 + 1.28 +// The pref indicates if the device has an internal antenna. 1.29 +// If the pref is true, the antanna will be always available. 1.30 +#define DOM_FM_ANTENNA_INTERNAL_PREF "dom.fmradio.antenna.internal" 1.31 + 1.32 +using namespace mozilla::hal; 1.33 +using mozilla::Preferences; 1.34 + 1.35 +BEGIN_FMRADIO_NAMESPACE 1.36 + 1.37 +class FMRadioRequest MOZ_FINAL : public FMRadioReplyRunnable 1.38 + , public DOMRequest 1.39 +{ 1.40 +public: 1.41 + NS_DECL_ISUPPORTS_INHERITED 1.42 + 1.43 + FMRadioRequest(nsPIDOMWindow* aWindow, FMRadio* aFMRadio) 1.44 + : DOMRequest(aWindow) 1.45 + , mType(FMRadioRequestArgs::T__None) 1.46 + { 1.47 + // |FMRadio| inherits from |nsIDOMEventTarget| and |nsISupportsWeakReference| 1.48 + // which both inherits from nsISupports, so |nsISupports| is an ambiguous 1.49 + // base of |FMRadio|, we have to cast |aFMRadio| to one of the base classes. 1.50 + mFMRadio = do_GetWeakReference(static_cast<nsIDOMEventTarget*>(aFMRadio)); 1.51 + } 1.52 + 1.53 + FMRadioRequest(nsPIDOMWindow* aWindow, FMRadio* aFMRadio, 1.54 + FMRadioRequestArgs::Type aType) 1.55 + : DOMRequest(aWindow) 1.56 + { 1.57 + MOZ_ASSERT(aType >= FMRadioRequestArgs::T__None && 1.58 + aType <= FMRadioRequestArgs::T__Last, 1.59 + "Wrong FMRadioRequestArgs in FMRadioRequest"); 1.60 + 1.61 + mFMRadio = do_GetWeakReference(static_cast<nsIDOMEventTarget*>(aFMRadio)); 1.62 + mType = aType; 1.63 + } 1.64 + 1.65 + ~FMRadioRequest() { } 1.66 + 1.67 + NS_IMETHODIMP 1.68 + Run() 1.69 + { 1.70 + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); 1.71 + 1.72 + nsCOMPtr<nsIDOMEventTarget> target = do_QueryReferent(mFMRadio); 1.73 + if (!target) { 1.74 + return NS_OK; 1.75 + } 1.76 + 1.77 + FMRadio* fmRadio = static_cast<FMRadio*>( 1.78 + static_cast<nsIDOMEventTarget*>(target)); 1.79 + 1.80 + if (fmRadio->mIsShutdown) { 1.81 + return NS_OK; 1.82 + } 1.83 + 1.84 + switch (mResponseType.type()) { 1.85 + case FMRadioResponseType::TErrorResponse: 1.86 + FireError(mResponseType.get_ErrorResponse().error()); 1.87 + break; 1.88 + case FMRadioResponseType::TSuccessResponse: 1.89 + if (mType == FMRadioRequestArgs::TEnableRequestArgs) { 1.90 + fmRadio->EnableAudioChannelAgent(); 1.91 + } 1.92 + 1.93 + FireSuccess(JS::UndefinedHandleValue); 1.94 + break; 1.95 + default: 1.96 + MOZ_CRASH(); 1.97 + } 1.98 + 1.99 + return NS_OK; 1.100 + } 1.101 + 1.102 +private: 1.103 + FMRadioRequestArgs::Type mType; 1.104 + nsWeakPtr mFMRadio; 1.105 +}; 1.106 + 1.107 +NS_IMPL_ISUPPORTS_INHERITED0(FMRadioRequest, DOMRequest) 1.108 + 1.109 +FMRadio::FMRadio() 1.110 + : mHeadphoneState(SWITCH_STATE_OFF) 1.111 + , mAudioChannelAgentEnabled(false) 1.112 + , mHasInternalAntenna(false) 1.113 + , mIsShutdown(false) 1.114 +{ 1.115 + LOG("FMRadio is initialized."); 1.116 + 1.117 + SetIsDOMBinding(); 1.118 +} 1.119 + 1.120 +FMRadio::~FMRadio() 1.121 +{ 1.122 +} 1.123 + 1.124 +void 1.125 +FMRadio::Init(nsPIDOMWindow *aWindow) 1.126 +{ 1.127 + BindToOwner(aWindow); 1.128 + 1.129 + IFMRadioService::Singleton()->AddObserver(this); 1.130 + 1.131 + mHasInternalAntenna = Preferences::GetBool(DOM_FM_ANTENNA_INTERNAL_PREF, 1.132 + /* default = */ false); 1.133 + if (mHasInternalAntenna) { 1.134 + LOG("We have an internal antenna."); 1.135 + } else { 1.136 + mHeadphoneState = GetCurrentSwitchState(SWITCH_HEADPHONES); 1.137 + RegisterSwitchObserver(SWITCH_HEADPHONES, this); 1.138 + } 1.139 + 1.140 + nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner()); 1.141 + NS_ENSURE_TRUE_VOID(target); 1.142 + target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this, 1.143 + /* useCapture = */ true, 1.144 + /* wantsUntrusted = */ false); 1.145 + 1.146 + 1.147 + // All of the codes below are for AudioChannel. We can directly return here 1.148 + // if preferences doesn't enable AudioChannelService. 1.149 + NS_ENSURE_TRUE_VOID(Preferences::GetBool("media.useAudioChannelService")); 1.150 + 1.151 + nsCOMPtr<nsIAudioChannelAgent> audioChannelAgent = 1.152 + do_CreateInstance("@mozilla.org/audiochannelagent;1"); 1.153 + NS_ENSURE_TRUE_VOID(audioChannelAgent); 1.154 + 1.155 + audioChannelAgent->InitWithWeakCallback( 1.156 + GetOwner(), 1.157 + nsIAudioChannelAgent::AUDIO_AGENT_CHANNEL_CONTENT, 1.158 + this); 1.159 + 1.160 + nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner()); 1.161 + NS_ENSURE_TRUE_VOID(docshell); 1.162 + 1.163 + bool isActive = false; 1.164 + docshell->GetIsActive(&isActive); 1.165 + audioChannelAgent->SetVisibilityState(isActive); 1.166 + 1.167 + // Once all necessary resources are got successfully, we just enabled 1.168 + // mAudioChannelAgent. 1.169 + mAudioChannelAgent = audioChannelAgent; 1.170 +} 1.171 + 1.172 +void 1.173 +FMRadio::Shutdown() 1.174 +{ 1.175 + IFMRadioService::Singleton()->RemoveObserver(this); 1.176 + 1.177 + if (!mHasInternalAntenna) { 1.178 + UnregisterSwitchObserver(SWITCH_HEADPHONES, this); 1.179 + } 1.180 + 1.181 + nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner()); 1.182 + NS_ENSURE_TRUE_VOID(target); 1.183 + target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this, 1.184 + /* useCapture = */ true); 1.185 + 1.186 + mIsShutdown = true; 1.187 +} 1.188 + 1.189 +JSObject* 1.190 +FMRadio::WrapObject(JSContext* aCx) 1.191 +{ 1.192 + return FMRadioBinding::Wrap(aCx, this); 1.193 +} 1.194 + 1.195 +void 1.196 +FMRadio::Notify(const SwitchEvent& aEvent) 1.197 +{ 1.198 + MOZ_ASSERT(!mHasInternalAntenna); 1.199 + 1.200 + if (mHeadphoneState != aEvent.status()) { 1.201 + mHeadphoneState = aEvent.status(); 1.202 + 1.203 + DispatchTrustedEvent(NS_LITERAL_STRING("antennaavailablechange")); 1.204 + } 1.205 +} 1.206 + 1.207 +void 1.208 +FMRadio::Notify(const FMRadioEventType& aType) 1.209 +{ 1.210 + switch (aType) { 1.211 + case FrequencyChanged: 1.212 + DispatchTrustedEvent(NS_LITERAL_STRING("frequencychange")); 1.213 + break; 1.214 + case EnabledChanged: 1.215 + if (Enabled()) { 1.216 + DispatchTrustedEvent(NS_LITERAL_STRING("enabled")); 1.217 + } else { 1.218 + if (mAudioChannelAgentEnabled) { 1.219 + mAudioChannelAgent->StopPlaying(); 1.220 + mAudioChannelAgentEnabled = false; 1.221 + } 1.222 + 1.223 + DispatchTrustedEvent(NS_LITERAL_STRING("disabled")); 1.224 + } 1.225 + break; 1.226 + default: 1.227 + MOZ_CRASH(); 1.228 + } 1.229 +} 1.230 + 1.231 +/* static */ 1.232 +bool 1.233 +FMRadio::Enabled() 1.234 +{ 1.235 + return IFMRadioService::Singleton()->IsEnabled(); 1.236 +} 1.237 + 1.238 +bool 1.239 +FMRadio::AntennaAvailable() const 1.240 +{ 1.241 + return mHasInternalAntenna ? true : (mHeadphoneState != SWITCH_STATE_OFF) && 1.242 + (mHeadphoneState != SWITCH_STATE_UNKNOWN); 1.243 +} 1.244 + 1.245 +Nullable<double> 1.246 +FMRadio::GetFrequency() const 1.247 +{ 1.248 + return Enabled() ? 1.249 + Nullable<double>(IFMRadioService::Singleton()->GetFrequency()) : 1.250 + Nullable<double>(); 1.251 +} 1.252 + 1.253 +double 1.254 +FMRadio::FrequencyUpperBound() const 1.255 +{ 1.256 + return IFMRadioService::Singleton()->GetFrequencyUpperBound(); 1.257 +} 1.258 + 1.259 +double 1.260 +FMRadio::FrequencyLowerBound() const 1.261 +{ 1.262 + return IFMRadioService::Singleton()->GetFrequencyLowerBound(); 1.263 +} 1.264 + 1.265 +double 1.266 +FMRadio::ChannelWidth() const 1.267 +{ 1.268 + return IFMRadioService::Singleton()->GetChannelWidth(); 1.269 +} 1.270 + 1.271 +already_AddRefed<DOMRequest> 1.272 +FMRadio::Enable(double aFrequency) 1.273 +{ 1.274 + nsCOMPtr<nsPIDOMWindow> win = GetOwner(); 1.275 + if (!win) { 1.276 + return nullptr; 1.277 + } 1.278 + 1.279 + nsRefPtr<FMRadioRequest> r = 1.280 + new FMRadioRequest(win, this, FMRadioRequestArgs::TEnableRequestArgs); 1.281 + IFMRadioService::Singleton()->Enable(aFrequency, r); 1.282 + 1.283 + return r.forget(); 1.284 +} 1.285 + 1.286 +already_AddRefed<DOMRequest> 1.287 +FMRadio::Disable() 1.288 +{ 1.289 + nsCOMPtr<nsPIDOMWindow> win = GetOwner(); 1.290 + if (!win) { 1.291 + return nullptr; 1.292 + } 1.293 + 1.294 + nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this); 1.295 + IFMRadioService::Singleton()->Disable(r); 1.296 + 1.297 + return r.forget(); 1.298 +} 1.299 + 1.300 +already_AddRefed<DOMRequest> 1.301 +FMRadio::SetFrequency(double aFrequency) 1.302 +{ 1.303 + nsCOMPtr<nsPIDOMWindow> win = GetOwner(); 1.304 + if (!win) { 1.305 + return nullptr; 1.306 + } 1.307 + 1.308 + nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this); 1.309 + IFMRadioService::Singleton()->SetFrequency(aFrequency, r); 1.310 + 1.311 + return r.forget(); 1.312 +} 1.313 + 1.314 +already_AddRefed<DOMRequest> 1.315 +FMRadio::SeekUp() 1.316 +{ 1.317 + nsCOMPtr<nsPIDOMWindow> win = GetOwner(); 1.318 + if (!win) { 1.319 + return nullptr; 1.320 + } 1.321 + 1.322 + nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this); 1.323 + IFMRadioService::Singleton()->Seek(FM_RADIO_SEEK_DIRECTION_UP, r); 1.324 + 1.325 + return r.forget(); 1.326 +} 1.327 + 1.328 +already_AddRefed<DOMRequest> 1.329 +FMRadio::SeekDown() 1.330 +{ 1.331 + nsCOMPtr<nsPIDOMWindow> win = GetOwner(); 1.332 + if (!win) { 1.333 + return nullptr; 1.334 + } 1.335 + 1.336 + nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this); 1.337 + IFMRadioService::Singleton()->Seek(FM_RADIO_SEEK_DIRECTION_DOWN, r); 1.338 + 1.339 + return r.forget(); 1.340 +} 1.341 + 1.342 +already_AddRefed<DOMRequest> 1.343 +FMRadio::CancelSeek() 1.344 +{ 1.345 + nsCOMPtr<nsPIDOMWindow> win = GetOwner(); 1.346 + if (!win) { 1.347 + return nullptr; 1.348 + } 1.349 + 1.350 + nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this); 1.351 + IFMRadioService::Singleton()->CancelSeek(r); 1.352 + 1.353 + return r.forget(); 1.354 +} 1.355 + 1.356 +NS_IMETHODIMP 1.357 +FMRadio::HandleEvent(nsIDOMEvent* aEvent) 1.358 +{ 1.359 + nsAutoString type; 1.360 + aEvent->GetType(type); 1.361 + 1.362 + if (!type.EqualsLiteral("visibilitychange")) { 1.363 + return NS_ERROR_FAILURE; 1.364 + } 1.365 + 1.366 + nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner()); 1.367 + NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE); 1.368 + 1.369 + bool isActive = false; 1.370 + docshell->GetIsActive(&isActive); 1.371 + 1.372 + mAudioChannelAgent->SetVisibilityState(isActive); 1.373 + return NS_OK; 1.374 +} 1.375 + 1.376 +void 1.377 +FMRadio::EnableAudioChannelAgent() 1.378 +{ 1.379 + NS_ENSURE_TRUE_VOID(mAudioChannelAgent); 1.380 + 1.381 + int32_t playingState = 0; 1.382 + mAudioChannelAgent->StartPlaying(&playingState); 1.383 + SetCanPlay(playingState == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL); 1.384 + 1.385 + mAudioChannelAgentEnabled = true; 1.386 +} 1.387 + 1.388 +NS_IMETHODIMP 1.389 +FMRadio::CanPlayChanged(int32_t aCanPlay) 1.390 +{ 1.391 + SetCanPlay(aCanPlay == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL); 1.392 + return NS_OK; 1.393 +} 1.394 + 1.395 +NS_IMETHODIMP 1.396 +FMRadio::WindowVolumeChanged() 1.397 +{ 1.398 + return NS_ERROR_NOT_IMPLEMENTED; 1.399 +} 1.400 + 1.401 +void 1.402 +FMRadio::SetCanPlay(bool aCanPlay) 1.403 +{ 1.404 + IFMRadioService::Singleton()->EnableAudio(aCanPlay); 1.405 +} 1.406 + 1.407 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FMRadio) 1.408 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.409 + NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback) 1.410 + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) 1.411 +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 1.412 + 1.413 +NS_IMPL_ADDREF_INHERITED(FMRadio, DOMEventTargetHelper) 1.414 +NS_IMPL_RELEASE_INHERITED(FMRadio, DOMEventTargetHelper) 1.415 + 1.416 +END_FMRADIO_NAMESPACE 1.417 +