michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/dom/FMRadio.h" michael@0: #include "nsContentUtils.h" michael@0: #include "mozilla/Hal.h" michael@0: #include "mozilla/HalTypes.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/dom/FMRadioBinding.h" michael@0: #include "mozilla/dom/ContentChild.h" michael@0: #include "mozilla/dom/PFMRadioChild.h" michael@0: #include "mozilla/dom/FMRadioService.h" michael@0: #include "DOMRequest.h" michael@0: #include "nsDOMClassInfo.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIAudioManager.h" michael@0: michael@0: #undef LOG michael@0: #define LOG(args...) FM_LOG("FMRadio", args) michael@0: michael@0: // The pref indicates if the device has an internal antenna. michael@0: // If the pref is true, the antanna will be always available. michael@0: #define DOM_FM_ANTENNA_INTERNAL_PREF "dom.fmradio.antenna.internal" michael@0: michael@0: using namespace mozilla::hal; michael@0: using mozilla::Preferences; michael@0: michael@0: BEGIN_FMRADIO_NAMESPACE michael@0: michael@0: class FMRadioRequest MOZ_FINAL : public FMRadioReplyRunnable michael@0: , public DOMRequest michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: michael@0: FMRadioRequest(nsPIDOMWindow* aWindow, FMRadio* aFMRadio) michael@0: : DOMRequest(aWindow) michael@0: , mType(FMRadioRequestArgs::T__None) michael@0: { michael@0: // |FMRadio| inherits from |nsIDOMEventTarget| and |nsISupportsWeakReference| michael@0: // which both inherits from nsISupports, so |nsISupports| is an ambiguous michael@0: // base of |FMRadio|, we have to cast |aFMRadio| to one of the base classes. michael@0: mFMRadio = do_GetWeakReference(static_cast(aFMRadio)); michael@0: } michael@0: michael@0: FMRadioRequest(nsPIDOMWindow* aWindow, FMRadio* aFMRadio, michael@0: FMRadioRequestArgs::Type aType) michael@0: : DOMRequest(aWindow) michael@0: { michael@0: MOZ_ASSERT(aType >= FMRadioRequestArgs::T__None && michael@0: aType <= FMRadioRequestArgs::T__Last, michael@0: "Wrong FMRadioRequestArgs in FMRadioRequest"); michael@0: michael@0: mFMRadio = do_GetWeakReference(static_cast(aFMRadio)); michael@0: mType = aType; michael@0: } michael@0: michael@0: ~FMRadioRequest() { } michael@0: michael@0: NS_IMETHODIMP michael@0: Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: nsCOMPtr target = do_QueryReferent(mFMRadio); michael@0: if (!target) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: FMRadio* fmRadio = static_cast( michael@0: static_cast(target)); michael@0: michael@0: if (fmRadio->mIsShutdown) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: switch (mResponseType.type()) { michael@0: case FMRadioResponseType::TErrorResponse: michael@0: FireError(mResponseType.get_ErrorResponse().error()); michael@0: break; michael@0: case FMRadioResponseType::TSuccessResponse: michael@0: if (mType == FMRadioRequestArgs::TEnableRequestArgs) { michael@0: fmRadio->EnableAudioChannelAgent(); michael@0: } michael@0: michael@0: FireSuccess(JS::UndefinedHandleValue); michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: FMRadioRequestArgs::Type mType; michael@0: nsWeakPtr mFMRadio; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED0(FMRadioRequest, DOMRequest) michael@0: michael@0: FMRadio::FMRadio() michael@0: : mHeadphoneState(SWITCH_STATE_OFF) michael@0: , mAudioChannelAgentEnabled(false) michael@0: , mHasInternalAntenna(false) michael@0: , mIsShutdown(false) michael@0: { michael@0: LOG("FMRadio is initialized."); michael@0: michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: FMRadio::~FMRadio() michael@0: { michael@0: } michael@0: michael@0: void michael@0: FMRadio::Init(nsPIDOMWindow *aWindow) michael@0: { michael@0: BindToOwner(aWindow); michael@0: michael@0: IFMRadioService::Singleton()->AddObserver(this); michael@0: michael@0: mHasInternalAntenna = Preferences::GetBool(DOM_FM_ANTENNA_INTERNAL_PREF, michael@0: /* default = */ false); michael@0: if (mHasInternalAntenna) { michael@0: LOG("We have an internal antenna."); michael@0: } else { michael@0: mHeadphoneState = GetCurrentSwitchState(SWITCH_HEADPHONES); michael@0: RegisterSwitchObserver(SWITCH_HEADPHONES, this); michael@0: } michael@0: michael@0: nsCOMPtr target = do_QueryInterface(GetOwner()); michael@0: NS_ENSURE_TRUE_VOID(target); michael@0: target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this, michael@0: /* useCapture = */ true, michael@0: /* wantsUntrusted = */ false); michael@0: michael@0: michael@0: // All of the codes below are for AudioChannel. We can directly return here michael@0: // if preferences doesn't enable AudioChannelService. michael@0: NS_ENSURE_TRUE_VOID(Preferences::GetBool("media.useAudioChannelService")); michael@0: michael@0: nsCOMPtr audioChannelAgent = michael@0: do_CreateInstance("@mozilla.org/audiochannelagent;1"); michael@0: NS_ENSURE_TRUE_VOID(audioChannelAgent); michael@0: michael@0: audioChannelAgent->InitWithWeakCallback( michael@0: GetOwner(), michael@0: nsIAudioChannelAgent::AUDIO_AGENT_CHANNEL_CONTENT, michael@0: this); michael@0: michael@0: nsCOMPtr docshell = do_GetInterface(GetOwner()); michael@0: NS_ENSURE_TRUE_VOID(docshell); michael@0: michael@0: bool isActive = false; michael@0: docshell->GetIsActive(&isActive); michael@0: audioChannelAgent->SetVisibilityState(isActive); michael@0: michael@0: // Once all necessary resources are got successfully, we just enabled michael@0: // mAudioChannelAgent. michael@0: mAudioChannelAgent = audioChannelAgent; michael@0: } michael@0: michael@0: void michael@0: FMRadio::Shutdown() michael@0: { michael@0: IFMRadioService::Singleton()->RemoveObserver(this); michael@0: michael@0: if (!mHasInternalAntenna) { michael@0: UnregisterSwitchObserver(SWITCH_HEADPHONES, this); michael@0: } michael@0: michael@0: nsCOMPtr target = do_QueryInterface(GetOwner()); michael@0: NS_ENSURE_TRUE_VOID(target); michael@0: target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"), this, michael@0: /* useCapture = */ true); michael@0: michael@0: mIsShutdown = true; michael@0: } michael@0: michael@0: JSObject* michael@0: FMRadio::WrapObject(JSContext* aCx) michael@0: { michael@0: return FMRadioBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: void michael@0: FMRadio::Notify(const SwitchEvent& aEvent) michael@0: { michael@0: MOZ_ASSERT(!mHasInternalAntenna); michael@0: michael@0: if (mHeadphoneState != aEvent.status()) { michael@0: mHeadphoneState = aEvent.status(); michael@0: michael@0: DispatchTrustedEvent(NS_LITERAL_STRING("antennaavailablechange")); michael@0: } michael@0: } michael@0: michael@0: void michael@0: FMRadio::Notify(const FMRadioEventType& aType) michael@0: { michael@0: switch (aType) { michael@0: case FrequencyChanged: michael@0: DispatchTrustedEvent(NS_LITERAL_STRING("frequencychange")); michael@0: break; michael@0: case EnabledChanged: michael@0: if (Enabled()) { michael@0: DispatchTrustedEvent(NS_LITERAL_STRING("enabled")); michael@0: } else { michael@0: if (mAudioChannelAgentEnabled) { michael@0: mAudioChannelAgent->StopPlaying(); michael@0: mAudioChannelAgentEnabled = false; michael@0: } michael@0: michael@0: DispatchTrustedEvent(NS_LITERAL_STRING("disabled")); michael@0: } michael@0: break; michael@0: default: michael@0: MOZ_CRASH(); michael@0: } michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: FMRadio::Enabled() michael@0: { michael@0: return IFMRadioService::Singleton()->IsEnabled(); michael@0: } michael@0: michael@0: bool michael@0: FMRadio::AntennaAvailable() const michael@0: { michael@0: return mHasInternalAntenna ? true : (mHeadphoneState != SWITCH_STATE_OFF) && michael@0: (mHeadphoneState != SWITCH_STATE_UNKNOWN); michael@0: } michael@0: michael@0: Nullable michael@0: FMRadio::GetFrequency() const michael@0: { michael@0: return Enabled() ? michael@0: Nullable(IFMRadioService::Singleton()->GetFrequency()) : michael@0: Nullable(); michael@0: } michael@0: michael@0: double michael@0: FMRadio::FrequencyUpperBound() const michael@0: { michael@0: return IFMRadioService::Singleton()->GetFrequencyUpperBound(); michael@0: } michael@0: michael@0: double michael@0: FMRadio::FrequencyLowerBound() const michael@0: { michael@0: return IFMRadioService::Singleton()->GetFrequencyLowerBound(); michael@0: } michael@0: michael@0: double michael@0: FMRadio::ChannelWidth() const michael@0: { michael@0: return IFMRadioService::Singleton()->GetChannelWidth(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: FMRadio::Enable(double aFrequency) michael@0: { michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr r = michael@0: new FMRadioRequest(win, this, FMRadioRequestArgs::TEnableRequestArgs); michael@0: IFMRadioService::Singleton()->Enable(aFrequency, r); michael@0: michael@0: return r.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: FMRadio::Disable() michael@0: { michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr r = new FMRadioRequest(win, this); michael@0: IFMRadioService::Singleton()->Disable(r); michael@0: michael@0: return r.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: FMRadio::SetFrequency(double aFrequency) michael@0: { michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr r = new FMRadioRequest(win, this); michael@0: IFMRadioService::Singleton()->SetFrequency(aFrequency, r); michael@0: michael@0: return r.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: FMRadio::SeekUp() michael@0: { michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr r = new FMRadioRequest(win, this); michael@0: IFMRadioService::Singleton()->Seek(FM_RADIO_SEEK_DIRECTION_UP, r); michael@0: michael@0: return r.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: FMRadio::SeekDown() michael@0: { michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr r = new FMRadioRequest(win, this); michael@0: IFMRadioService::Singleton()->Seek(FM_RADIO_SEEK_DIRECTION_DOWN, r); michael@0: michael@0: return r.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: FMRadio::CancelSeek() michael@0: { michael@0: nsCOMPtr win = GetOwner(); michael@0: if (!win) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr r = new FMRadioRequest(win, this); michael@0: IFMRadioService::Singleton()->CancelSeek(r); michael@0: michael@0: return r.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FMRadio::HandleEvent(nsIDOMEvent* aEvent) michael@0: { michael@0: nsAutoString type; michael@0: aEvent->GetType(type); michael@0: michael@0: if (!type.EqualsLiteral("visibilitychange")) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsCOMPtr docshell = do_GetInterface(GetOwner()); michael@0: NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE); michael@0: michael@0: bool isActive = false; michael@0: docshell->GetIsActive(&isActive); michael@0: michael@0: mAudioChannelAgent->SetVisibilityState(isActive); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: FMRadio::EnableAudioChannelAgent() michael@0: { michael@0: NS_ENSURE_TRUE_VOID(mAudioChannelAgent); michael@0: michael@0: int32_t playingState = 0; michael@0: mAudioChannelAgent->StartPlaying(&playingState); michael@0: SetCanPlay(playingState == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL); michael@0: michael@0: mAudioChannelAgentEnabled = true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FMRadio::CanPlayChanged(int32_t aCanPlay) michael@0: { michael@0: SetCanPlay(aCanPlay == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FMRadio::WindowVolumeChanged() michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: void michael@0: FMRadio::SetCanPlay(bool aCanPlay) michael@0: { michael@0: IFMRadioService::Singleton()->EnableAudio(aCanPlay); michael@0: } michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FMRadio) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) michael@0: NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) michael@0: NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(FMRadio, DOMEventTargetHelper) michael@0: NS_IMPL_RELEASE_INHERITED(FMRadio, DOMEventTargetHelper) michael@0: michael@0: END_FMRADIO_NAMESPACE michael@0: