1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "SpeakerManager.h"
8 
9 #include "mozilla/Services.h"
10 
11 #include "mozilla/dom/Event.h"
12 
13 #include "AudioChannelService.h"
14 #include "nsIDocShell.h"
15 #include "nsIDOMClassInfo.h"
16 #include "nsIDOMEventListener.h"
17 #include "nsIInterfaceRequestorUtils.h"
18 #include "nsIPermissionManager.h"
19 #include "SpeakerManagerService.h"
20 
21 namespace mozilla {
22 namespace dom {
23 
NS_IMPL_QUERY_INTERFACE_INHERITED(SpeakerManager,DOMEventTargetHelper,nsIDOMEventListener)24 NS_IMPL_QUERY_INTERFACE_INHERITED(SpeakerManager, DOMEventTargetHelper,
25                                   nsIDOMEventListener)
26 NS_IMPL_ADDREF_INHERITED(SpeakerManager, DOMEventTargetHelper)
27 NS_IMPL_RELEASE_INHERITED(SpeakerManager, DOMEventTargetHelper)
28 
29 SpeakerManager::SpeakerManager()
30   : mForcespeaker(false)
31   , mVisible(false)
32 {
33   SpeakerManagerService *service =
34     SpeakerManagerService::GetOrCreateSpeakerManagerService();
35   MOZ_ASSERT(service);
36   service->RegisterSpeakerManager(this);
37 }
38 
~SpeakerManager()39 SpeakerManager::~SpeakerManager()
40 {
41   SpeakerManagerService *service = SpeakerManagerService::GetOrCreateSpeakerManagerService();
42   MOZ_ASSERT(service);
43 
44   service->UnRegisterSpeakerManager(this);
45   nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner());
46   NS_ENSURE_TRUE_VOID(target);
47 
48   target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
49                                     this,
50                                     /* useCapture = */ true);
51 }
52 
53 bool
Speakerforced()54 SpeakerManager::Speakerforced()
55 {
56   // If a background app calls forcespeaker=true that doesn't change anything.
57   // 'speakerforced' remains false everywhere.
58   if (mForcespeaker && !mVisible) {
59     return false;
60   }
61   SpeakerManagerService *service = SpeakerManagerService::GetOrCreateSpeakerManagerService();
62   MOZ_ASSERT(service);
63   return service->GetSpeakerStatus();
64 
65 }
66 
67 bool
Forcespeaker()68 SpeakerManager::Forcespeaker()
69 {
70   return mForcespeaker;
71 }
72 
73 void
SetForcespeaker(bool aEnable)74 SpeakerManager::SetForcespeaker(bool aEnable)
75 {
76   SpeakerManagerService *service = SpeakerManagerService::GetOrCreateSpeakerManagerService();
77   MOZ_ASSERT(service);
78 
79   service->ForceSpeaker(aEnable, mVisible);
80   mForcespeaker = aEnable;
81 }
82 
83 void
DispatchSimpleEvent(const nsAString & aStr)84 SpeakerManager::DispatchSimpleEvent(const nsAString& aStr)
85 {
86   MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
87   nsresult rv = CheckInnerWindowCorrectness();
88   if (NS_FAILED(rv)) {
89     return;
90   }
91 
92   RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
93   event->InitEvent(aStr, false, false);
94   event->SetTrusted(true);
95 
96   rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr);
97   if (NS_FAILED(rv)) {
98     NS_ERROR("Failed to dispatch the event!!!");
99     return;
100   }
101 }
102 
103 void
Init(nsPIDOMWindowInner * aWindow)104 SpeakerManager::Init(nsPIDOMWindowInner* aWindow)
105 {
106   BindToOwner(aWindow);
107 
108   nsCOMPtr<nsIDocShell> docshell = GetOwner()->GetDocShell();
109   NS_ENSURE_TRUE_VOID(docshell);
110   docshell->GetIsActive(&mVisible);
111 
112   nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
113   NS_ENSURE_TRUE_VOID(target);
114 
115   target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
116                                  this,
117                                  /* useCapture = */ true,
118                                  /* wantsUntrusted = */ false);
119 }
120 
121 nsPIDOMWindowInner*
GetParentObject() const122 SpeakerManager::GetParentObject() const
123 {
124   return GetOwner();
125 }
126 
127 /* static */ already_AddRefed<SpeakerManager>
Constructor(const GlobalObject & aGlobal,ErrorResult & aRv)128 SpeakerManager::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
129 {
130   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aGlobal.GetAsSupports());
131   if (!sgo) {
132     aRv.Throw(NS_ERROR_FAILURE);
133     return nullptr;
134   }
135 
136   nsCOMPtr<nsPIDOMWindowInner> ownerWindow = do_QueryInterface(aGlobal.GetAsSupports());
137   if (!ownerWindow) {
138     aRv.Throw(NS_ERROR_FAILURE);
139     return nullptr;
140   }
141 
142   nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
143   NS_ENSURE_TRUE(permMgr, nullptr);
144 
145   uint32_t permission = nsIPermissionManager::DENY_ACTION;
146   nsresult rv =
147     permMgr->TestPermissionFromWindow(ownerWindow, "speaker-control",
148                                       &permission);
149   NS_ENSURE_SUCCESS(rv, nullptr);
150 
151   if (permission != nsIPermissionManager::ALLOW_ACTION) {
152     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
153     return nullptr;
154   }
155 
156   RefPtr<SpeakerManager> object = new SpeakerManager();
157   object->Init(ownerWindow);
158   return object.forget();
159 }
160 
161 JSObject*
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)162 SpeakerManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
163 {
164   return MozSpeakerManagerBinding::Wrap(aCx, this, aGivenProto);
165 }
166 
167 NS_IMETHODIMP
HandleEvent(nsIDOMEvent * aEvent)168 SpeakerManager::HandleEvent(nsIDOMEvent* aEvent)
169 {
170   nsAutoString type;
171   aEvent->GetType(type);
172 
173   if (!type.EqualsLiteral("visibilitychange")) {
174     return NS_ERROR_FAILURE;
175   }
176 
177   nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
178   NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE);
179   docshell->GetIsActive(&mVisible);
180 
181   // If an app that has called forcespeaker=true is switched
182   // from the background to the foreground 'speakerforced'
183   // switches to true in all apps. I.e. the app doesn't have to
184   // call forcespeaker=true again when it comes into foreground.
185   SpeakerManagerService *service =
186     SpeakerManagerService::GetOrCreateSpeakerManagerService();
187   MOZ_ASSERT(service);
188 
189   if (mVisible && mForcespeaker) {
190     service->ForceSpeaker(mForcespeaker, mVisible);
191   }
192   // If an application that has called forcespeaker=true, but no audio is
193   // currently playing in the app itself, if application switch to
194   // the background, we switch 'speakerforced' to false.
195   if (!mVisible && mForcespeaker) {
196     RefPtr<AudioChannelService> audioChannelService =
197       AudioChannelService::GetOrCreate();
198     if (audioChannelService && !audioChannelService->AnyAudioChannelIsActive()) {
199       service->ForceSpeaker(false, mVisible);
200     }
201   }
202   return NS_OK;
203 }
204 
205 void
SetAudioChannelActive(bool isActive)206 SpeakerManager::SetAudioChannelActive(bool isActive)
207 {
208   if (mForcespeaker) {
209     SpeakerManagerService *service =
210       SpeakerManagerService::GetOrCreateSpeakerManagerService();
211     MOZ_ASSERT(service);
212     service->ForceSpeaker(isActive, mVisible);
213   }
214 }
215 
216 } // namespace dom
217 } // namespace mozilla
218