1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et ft=cpp : */
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/. */
11 #include "HalSandbox.h"
12 #include "HalWakeLockInternal.h"
13 #include "mozilla/dom/Document.h"
14 #include "nsIDocShell.h"
15 #include "nsIBrowserChild.h"
16 #include "nsIWebNavigation.h"
17 #include "nsThreadUtils.h"
18 #include "nsXULAppAPI.h"
19 #include "nsPIDOMWindow.h"
20 #include "nsJSUtils.h"
21 #include "mozilla/ClearOnShutdown.h"
22 #include "mozilla/Observer.h"
23 #include "mozilla/dom/ContentChild.h"
24 #include "WindowIdentifier.h"
28 # define getpid _getpid
31 using namespace mozilla::services
;
32 using namespace mozilla::dom
;
34 #define PROXY_IF_SANDBOXED(_call) \
37 if (!hal_sandbox::HalChildDestroyed()) { \
45 #define RETURN_PROXY_IF_SANDBOXED(_call, defValue) \
48 if (hal_sandbox::HalChildDestroyed()) { \
51 return hal_sandbox::_call; \
53 return hal_impl::_call; \
60 static bool sInitialized
= false;
62 mozilla::LogModule
* GetHalLog() {
63 static mozilla::LazyLogModule
sHalLog("hal");
69 void AssertMainThread() { MOZ_ASSERT(NS_IsMainThread()); }
71 bool InSandbox() { return GeckoProcessType_Content
== XRE_GetProcessType(); }
73 bool WindowIsActive(nsPIDOMWindowInner
* aWindow
) {
74 dom::Document
* document
= aWindow
->GetDoc();
75 NS_ENSURE_TRUE(document
, false);
76 return !document
->Hidden();
79 StaticAutoPtr
<WindowIdentifier::IDArrayType
> gLastIDToVibrate
;
81 static void RecordLastIDToVibrate(const WindowIdentifier
& aId
) {
83 *gLastIDToVibrate
= aId
.AsArray();
87 static bool MayCancelVibration(const WindowIdentifier
& aId
) {
88 // Although only active windows may start vibrations, a window may
89 // cancel its own vibration even if it's no longer active.
91 // After a window is marked as inactive, it sends a CancelVibrate
92 // request. We want this request to cancel a playing vibration
93 // started by that window, so we certainly don't want to reject the
94 // cancellation request because the window is now inactive.
96 // But it could be the case that, after this window became inactive,
97 // some other window came along and started a vibration. We don't
98 // want this window's cancellation request to cancel that window's
99 // actively-playing vibration!
101 // To solve this problem, we keep track of the id of the last window
102 // to start a vibration, and only accepts cancellation requests from
103 // the same window. All other cancellation requests are ignored.
105 return InSandbox() || (*gLastIDToVibrate
== aId
.AsArray());
110 void Vibrate(const nsTArray
<uint32_t>& pattern
, nsPIDOMWindowInner
* window
) {
111 Vibrate(pattern
, WindowIdentifier(window
));
114 void Vibrate(const nsTArray
<uint32_t>& pattern
, const WindowIdentifier
& id
) {
117 // Only active windows may start vibrations. If |id| hasn't gone
118 // through the IPC layer -- that is, if our caller is the outside
119 // world, not hal_proxy -- check whether the window is active. If
120 // |id| has gone through IPC, don't check the window's visibility;
121 // only the window corresponding to the bottommost process has its
122 // visibility state set correctly.
123 if (!id
.HasTraveledThroughIPC() && !WindowIsActive(id
.GetWindow())) {
124 HAL_LOG("Vibrate: Window is inactive, dropping vibrate.");
128 RecordLastIDToVibrate(id
);
130 // Don't forward our ID if we are not in the sandbox, because hal_impl
131 // doesn't need it, and we don't want it to be tempted to read it. The
132 // empty identifier will assert if it's used.
133 PROXY_IF_SANDBOXED(Vibrate(pattern
, InSandbox() ? id
: WindowIdentifier()));
136 void CancelVibrate(nsPIDOMWindowInner
* window
) {
137 CancelVibrate(WindowIdentifier(window
));
140 void CancelVibrate(const WindowIdentifier
& id
) {
143 if (MayCancelVibration(id
)) {
144 // Don't forward our ID if we are not in the sandbox, because hal_impl
145 // doesn't need it, and we don't want it to be tempted to read it. The
146 // empty identifier will assert if it's used.
147 PROXY_IF_SANDBOXED(CancelVibrate(InSandbox() ? id
: WindowIdentifier()));
151 template <class InfoType
>
152 class ObserversManager
{
154 void AddObserver(Observer
<InfoType
>* aObserver
) {
155 mObservers
.AddObserver(aObserver
);
157 if (mObservers
.Length() == 1) {
158 EnableNotifications();
162 void RemoveObserver(Observer
<InfoType
>* aObserver
) {
163 bool removed
= mObservers
.RemoveObserver(aObserver
);
168 if (mObservers
.Length() == 0) {
169 DisableNotifications();
170 OnNotificationsDisabled();
174 void BroadcastInformation(const InfoType
& aInfo
) {
175 mObservers
.Broadcast(aInfo
);
179 ~ObserversManager() { MOZ_ASSERT(mObservers
.Length() == 0); }
181 virtual void EnableNotifications() = 0;
182 virtual void DisableNotifications() = 0;
183 virtual void OnNotificationsDisabled() {}
186 mozilla::ObserverList
<InfoType
> mObservers
;
189 template <class InfoType
>
190 class CachingObserversManager
: public ObserversManager
<InfoType
> {
192 InfoType
GetCurrentInformation() {
193 if (mHasValidCache
) {
197 GetCurrentInformationInternal(&mInfo
);
198 mHasValidCache
= true;
202 void CacheInformation(const InfoType
& aInfo
) {
203 mHasValidCache
= true;
207 void BroadcastCachedInformation() { this->BroadcastInformation(mInfo
); }
210 virtual void GetCurrentInformationInternal(InfoType
*) = 0;
212 void OnNotificationsDisabled() override
{ mHasValidCache
= false; }
219 class BatteryObserversManager final
220 : public CachingObserversManager
<BatteryInformation
> {
222 void EnableNotifications() override
{
223 PROXY_IF_SANDBOXED(EnableBatteryNotifications());
226 void DisableNotifications() override
{
227 PROXY_IF_SANDBOXED(DisableBatteryNotifications());
230 void GetCurrentInformationInternal(BatteryInformation
* aInfo
) override
{
231 PROXY_IF_SANDBOXED(GetCurrentBatteryInformation(aInfo
));
235 class NetworkObserversManager final
236 : public CachingObserversManager
<NetworkInformation
> {
238 void EnableNotifications() override
{
239 PROXY_IF_SANDBOXED(EnableNetworkNotifications());
242 void DisableNotifications() override
{
243 PROXY_IF_SANDBOXED(DisableNetworkNotifications());
246 void GetCurrentInformationInternal(NetworkInformation
* aInfo
) override
{
247 PROXY_IF_SANDBOXED(GetCurrentNetworkInformation(aInfo
));
251 class WakeLockObserversManager final
252 : public ObserversManager
<WakeLockInformation
> {
254 void EnableNotifications() override
{
255 PROXY_IF_SANDBOXED(EnableWakeLockNotifications());
258 void DisableNotifications() override
{
259 PROXY_IF_SANDBOXED(DisableWakeLockNotifications());
263 class ScreenConfigurationObserversManager final
264 : public CachingObserversManager
<ScreenConfiguration
> {
266 void EnableNotifications() override
{
267 PROXY_IF_SANDBOXED(EnableScreenConfigurationNotifications());
270 void DisableNotifications() override
{
271 PROXY_IF_SANDBOXED(DisableScreenConfigurationNotifications());
274 void GetCurrentInformationInternal(ScreenConfiguration
* aInfo
) override
{
275 PROXY_IF_SANDBOXED(GetCurrentScreenConfiguration(aInfo
));
279 typedef mozilla::ObserverList
<SensorData
> SensorObserverList
;
280 StaticAutoPtr
<SensorObserverList
> sSensorObservers
[NUM_SENSOR_TYPE
];
282 static SensorObserverList
* GetSensorObservers(SensorType sensor_type
) {
284 MOZ_ASSERT(sensor_type
< NUM_SENSOR_TYPE
);
286 if (!sSensorObservers
[sensor_type
]) {
287 sSensorObservers
[sensor_type
] = new SensorObserverList();
290 return sSensorObservers
[sensor_type
];
293 #define MOZ_IMPL_HAL_OBSERVER(name_) \
294 StaticAutoPtr<name_##ObserversManager> s##name_##Observers; \
296 static name_##ObserversManager* name_##Observers() { \
297 AssertMainThread(); \
299 if (!s##name_##Observers) { \
300 MOZ_ASSERT(sInitialized); \
301 s##name_##Observers = new name_##ObserversManager(); \
304 return s##name_##Observers; \
307 void Register##name_##Observer(name_##Observer* aObserver) { \
308 AssertMainThread(); \
309 name_##Observers()->AddObserver(aObserver); \
312 void Unregister##name_##Observer(name_##Observer* aObserver) { \
313 AssertMainThread(); \
314 name_##Observers()->RemoveObserver(aObserver); \
317 MOZ_IMPL_HAL_OBSERVER(Battery
)
319 void GetCurrentBatteryInformation(BatteryInformation
* aInfo
) {
320 *aInfo
= BatteryObservers()->GetCurrentInformation();
323 void NotifyBatteryChange(const BatteryInformation
& aInfo
) {
324 BatteryObservers()->CacheInformation(aInfo
);
325 BatteryObservers()->BroadcastCachedInformation();
328 void EnableSensorNotifications(SensorType aSensor
) {
330 PROXY_IF_SANDBOXED(EnableSensorNotifications(aSensor
));
333 void DisableSensorNotifications(SensorType aSensor
) {
335 PROXY_IF_SANDBOXED(DisableSensorNotifications(aSensor
));
338 void RegisterSensorObserver(SensorType aSensor
, ISensorObserver
* aObserver
) {
339 SensorObserverList
* observers
= GetSensorObservers(aSensor
);
341 observers
->AddObserver(aObserver
);
342 if (observers
->Length() == 1) {
343 EnableSensorNotifications(aSensor
);
347 void UnregisterSensorObserver(SensorType aSensor
, ISensorObserver
* aObserver
) {
348 SensorObserverList
* observers
= GetSensorObservers(aSensor
);
349 if (!observers
->RemoveObserver(aObserver
) || observers
->Length() > 0) {
352 DisableSensorNotifications(aSensor
);
355 void NotifySensorChange(const SensorData
& aSensorData
) {
356 SensorObserverList
* observers
= GetSensorObservers(aSensorData
.sensor());
358 observers
->Broadcast(aSensorData
);
361 MOZ_IMPL_HAL_OBSERVER(Network
)
363 void GetCurrentNetworkInformation(NetworkInformation
* aInfo
) {
364 *aInfo
= NetworkObservers()->GetCurrentInformation();
367 void NotifyNetworkChange(const NetworkInformation
& aInfo
) {
368 NetworkObservers()->CacheInformation(aInfo
);
369 NetworkObservers()->BroadcastCachedInformation();
372 MOZ_IMPL_HAL_OBSERVER(WakeLock
)
374 void ModifyWakeLock(const nsAString
& aTopic
, WakeLockControl aLockAdjust
,
375 WakeLockControl aHiddenAdjust
,
376 uint64_t aProcessID
/* = CONTENT_PROCESS_ID_UNKNOWN */) {
379 if (aProcessID
== CONTENT_PROCESS_ID_UNKNOWN
) {
380 aProcessID
= InSandbox() ? ContentChild::GetSingleton()->GetID()
381 : CONTENT_PROCESS_ID_MAIN
;
385 ModifyWakeLock(aTopic
, aLockAdjust
, aHiddenAdjust
, aProcessID
));
388 void GetWakeLockInfo(const nsAString
& aTopic
,
389 WakeLockInformation
* aWakeLockInfo
) {
391 PROXY_IF_SANDBOXED(GetWakeLockInfo(aTopic
, aWakeLockInfo
));
394 void NotifyWakeLockChange(const WakeLockInformation
& aInfo
) {
396 WakeLockObservers()->BroadcastInformation(aInfo
);
399 MOZ_IMPL_HAL_OBSERVER(ScreenConfiguration
)
401 void GetCurrentScreenConfiguration(ScreenConfiguration
* aScreenConfiguration
) {
402 *aScreenConfiguration
=
403 ScreenConfigurationObservers()->GetCurrentInformation();
406 void NotifyScreenConfigurationChange(
407 const ScreenConfiguration
& aScreenConfiguration
) {
408 ScreenConfigurationObservers()->CacheInformation(aScreenConfiguration
);
409 ScreenConfigurationObservers()->BroadcastCachedInformation();
412 bool LockScreenOrientation(const ScreenOrientation
& aOrientation
) {
414 RETURN_PROXY_IF_SANDBOXED(LockScreenOrientation(aOrientation
), false);
417 void UnlockScreenOrientation() {
419 PROXY_IF_SANDBOXED(UnlockScreenOrientation());
422 bool SetProcessPrioritySupported() {
423 RETURN_PROXY_IF_SANDBOXED(SetProcessPrioritySupported(), false);
426 void SetProcessPriority(int aPid
, ProcessPriority aPriority
) {
427 // n.b. The sandboxed implementation crashes; SetProcessPriority works only
428 // from the main process.
429 PROXY_IF_SANDBOXED(SetProcessPriority(aPid
, aPriority
));
433 const char* ProcessPriorityToString(ProcessPriority aPriority
) {
435 case PROCESS_PRIORITY_MASTER
:
437 case PROCESS_PRIORITY_PREALLOC
:
439 case PROCESS_PRIORITY_FOREGROUND_HIGH
:
440 return "FOREGROUND_HIGH";
441 case PROCESS_PRIORITY_FOREGROUND
:
443 case PROCESS_PRIORITY_FOREGROUND_KEYBOARD
:
444 return "FOREGROUND_KEYBOARD";
445 case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE
:
446 return "BACKGROUND_PERCEIVABLE";
447 case PROCESS_PRIORITY_BACKGROUND
:
449 case PROCESS_PRIORITY_UNKNOWN
:
458 MOZ_ASSERT(!sInitialized
);
461 gLastIDToVibrate
= new WindowIdentifier::IDArrayType();
470 MOZ_ASSERT(sInitialized
);
472 gLastIDToVibrate
= nullptr;
474 sBatteryObservers
= nullptr;
475 sNetworkObservers
= nullptr;
476 sWakeLockObservers
= nullptr;
477 sScreenConfigurationObservers
= nullptr;
479 for (auto& sensorObserver
: sSensorObservers
) {
480 sensorObserver
= nullptr;
483 sInitialized
= false;
487 } // namespace mozilla