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