Source code

Revision control

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et ft=cpp : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Hal.h"
#include "HalImpl.h"
#include "HalLog.h"
#include "HalSandbox.h"
#include "HalWakeLockInternal.h"
#include "mozilla/dom/Document.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "nsPIDOMWindow.h"
#include "nsJSUtils.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Observer.h"
#include "mozilla/dom/ContentChild.h"
#include "WindowIdentifier.h"
#ifdef XP_WIN
# include <process.h>
# define getpid _getpid
#endif
using namespace mozilla::services;
using namespace mozilla::dom;
#define PROXY_IF_SANDBOXED(_call) \
do { \
if (InSandbox()) { \
if (!hal_sandbox::HalChildDestroyed()) { \
hal_sandbox::_call; \
} \
} else { \
hal_impl::_call; \
} \
} while (0)
#define RETURN_PROXY_IF_SANDBOXED(_call, defValue) \
do { \
if (InSandbox()) { \
if (hal_sandbox::HalChildDestroyed()) { \
return defValue; \
} \
return hal_sandbox::_call; \
} else { \
return hal_impl::_call; \
} \
} while (0)
namespace mozilla::hal {
static bool sInitialized = false;
mozilla::LogModule* GetHalLog() {
static mozilla::LazyLogModule sHalLog("hal");
return sHalLog;
}
namespace {
void AssertMainThread() { MOZ_ASSERT(NS_IsMainThread()); }
bool InSandbox() { return GeckoProcessType_Content == XRE_GetProcessType(); }
bool WindowIsActive(nsPIDOMWindowInner* aWindow) {
dom::Document* document = aWindow->GetDoc();
NS_ENSURE_TRUE(document, false);
return !document->Hidden();
}
StaticAutoPtr<WindowIdentifier::IDArrayType> gLastIDToVibrate;
static void RecordLastIDToVibrate(const WindowIdentifier& aId) {
if (!InSandbox()) {
*gLastIDToVibrate = aId.AsArray().Clone();
}
}
static bool MayCancelVibration(const WindowIdentifier& aId) {
// Although only active windows may start vibrations, a window may
// cancel its own vibration even if it's no longer active.
//
// After a window is marked as inactive, it sends a CancelVibrate
// request. We want this request to cancel a playing vibration
// started by that window, so we certainly don't want to reject the
// cancellation request because the window is now inactive.
//
// But it could be the case that, after this window became inactive,
// some other window came along and started a vibration. We don't
// want this window's cancellation request to cancel that window's
// actively-playing vibration!
//
// To solve this problem, we keep track of the id of the last window
// to start a vibration, and only accepts cancellation requests from
// the same window. All other cancellation requests are ignored.
return InSandbox() || (*gLastIDToVibrate == aId.AsArray());
}
} // namespace
void Vibrate(const nsTArray<uint32_t>& pattern, nsPIDOMWindowInner* window) {
Vibrate(pattern, WindowIdentifier(window));
}
void Vibrate(const nsTArray<uint32_t>& pattern, WindowIdentifier&& id) {
AssertMainThread();
// Only active windows may start vibrations. If |id| hasn't gone
// through the IPC layer -- that is, if our caller is the outside
// world, not hal_proxy -- check whether the window is active. If
// |id| has gone through IPC, don't check the window's visibility;
// only the window corresponding to the bottommost process has its
// visibility state set correctly.
if (!id.HasTraveledThroughIPC() && !WindowIsActive(id.GetWindow())) {
HAL_LOG("Vibrate: Window is inactive, dropping vibrate.");
return;
}
RecordLastIDToVibrate(id);
// Don't forward our ID if we are not in the sandbox, because hal_impl
// doesn't need it, and we don't want it to be tempted to read it. The
// empty identifier will assert if it's used.
PROXY_IF_SANDBOXED(
Vibrate(pattern, InSandbox() ? std::move(id) : WindowIdentifier()));
}
void CancelVibrate(nsPIDOMWindowInner* window) {
CancelVibrate(WindowIdentifier(window));
}
void CancelVibrate(WindowIdentifier&& id) {
AssertMainThread();
if (MayCancelVibration(id)) {
// Don't forward our ID if we are not in the sandbox, because hal_impl
// doesn't need it, and we don't want it to be tempted to read it. The
// empty identifier will assert if it's used.
PROXY_IF_SANDBOXED(
CancelVibrate(InSandbox() ? std::move(id) : WindowIdentifier()));
}
}
template <class InfoType>
class ObserversManager {
public:
void AddObserver(Observer<InfoType>* aObserver) {
mObservers.AddObserver(aObserver);
if (mObservers.Length() == 1) {
EnableNotifications();
}
}
void RemoveObserver(Observer<InfoType>* aObserver) {
bool removed = mObservers.RemoveObserver(aObserver);
if (!removed) {
return;
}
if (mObservers.Length() == 0) {
DisableNotifications();
OnNotificationsDisabled();
}
}
void BroadcastInformation(const InfoType& aInfo) {
mObservers.Broadcast(aInfo);
}
protected:
~ObserversManager() { MOZ_ASSERT(mObservers.Length() == 0); }
virtual void EnableNotifications() = 0;
virtual void DisableNotifications() = 0;
virtual void OnNotificationsDisabled() {}
private:
mozilla::ObserverList<InfoType> mObservers;
};
template <class InfoType>
class CachingObserversManager : public ObserversManager<InfoType> {
public:
InfoType GetCurrentInformation() {
if (mHasValidCache) {
return mInfo;
}
GetCurrentInformationInternal(&mInfo);
mHasValidCache = true;
return mInfo;
}
void CacheInformation(const InfoType& aInfo) {
mHasValidCache = true;
mInfo = aInfo;
}
void BroadcastCachedInformation() { this->BroadcastInformation(mInfo); }
protected:
virtual void GetCurrentInformationInternal(InfoType*) = 0;
void OnNotificationsDisabled() override { mHasValidCache = false; }
private:
InfoType mInfo;
bool mHasValidCache;
};
class BatteryObserversManager final
: public CachingObserversManager<BatteryInformation> {
protected:
void EnableNotifications() override {
PROXY_IF_SANDBOXED(EnableBatteryNotifications());
}
void DisableNotifications() override {
PROXY_IF_SANDBOXED(DisableBatteryNotifications());
}
void GetCurrentInformationInternal(BatteryInformation* aInfo) override {
PROXY_IF_SANDBOXED(GetCurrentBatteryInformation(aInfo));
}
};
class NetworkObserversManager final
: public CachingObserversManager<NetworkInformation> {
protected:
void EnableNotifications() override {
PROXY_IF_SANDBOXED(EnableNetworkNotifications());
}
void DisableNotifications() override {
PROXY_IF_SANDBOXED(DisableNetworkNotifications());
}
void GetCurrentInformationInternal(NetworkInformation* aInfo) override {
PROXY_IF_SANDBOXED(GetCurrentNetworkInformation(aInfo));
}
};
class WakeLockObserversManager final
: public ObserversManager<WakeLockInformation> {
protected:
void EnableNotifications() override {
PROXY_IF_SANDBOXED(EnableWakeLockNotifications());
}
void DisableNotifications() override {
PROXY_IF_SANDBOXED(DisableWakeLockNotifications());
}
};
class ScreenConfigurationObserversManager final
: public CachingObserversManager<ScreenConfiguration> {
protected:
void EnableNotifications() override {
PROXY_IF_SANDBOXED(EnableScreenConfigurationNotifications());
}
void DisableNotifications() override {
PROXY_IF_SANDBOXED(DisableScreenConfigurationNotifications());
}
void GetCurrentInformationInternal(ScreenConfiguration* aInfo) override {
PROXY_IF_SANDBOXED(GetCurrentScreenConfiguration(aInfo));
}
};
typedef mozilla::ObserverList<SensorData> SensorObserverList;
StaticAutoPtr<SensorObserverList> sSensorObservers[NUM_SENSOR_TYPE];
static SensorObserverList* GetSensorObservers(SensorType sensor_type) {
AssertMainThread();
MOZ_ASSERT(sensor_type < NUM_SENSOR_TYPE);
if (!sSensorObservers[sensor_type]) {
sSensorObservers[sensor_type] = new SensorObserverList();
}
return sSensorObservers[sensor_type];
}
#define MOZ_IMPL_HAL_OBSERVER(name_) \
StaticAutoPtr<name_##ObserversManager> s##name_##Observers; \
\
static name_##ObserversManager* name_##Observers() { \
AssertMainThread(); \
\
if (!s##name_##Observers) { \
MOZ_ASSERT(sInitialized); \
s##name_##Observers = new name_##ObserversManager(); \
} \
\
return s##name_##Observers; \
} \
\
void Register##name_##Observer(name_##Observer* aObserver) { \
AssertMainThread(); \
name_##Observers()->AddObserver(aObserver); \
} \
\
void Unregister##name_##Observer(name_##Observer* aObserver) { \
AssertMainThread(); \
name_##Observers()->RemoveObserver(aObserver); \
}
MOZ_IMPL_HAL_OBSERVER(Battery)
void GetCurrentBatteryInformation(BatteryInformation* aInfo) {
*aInfo = BatteryObservers()->GetCurrentInformation();
}
void NotifyBatteryChange(const BatteryInformation& aInfo) {
BatteryObservers()->CacheInformation(aInfo);
BatteryObservers()->BroadcastCachedInformation();
}
void EnableSensorNotifications(SensorType aSensor) {
AssertMainThread();
PROXY_IF_SANDBOXED(EnableSensorNotifications(aSensor));
}
void DisableSensorNotifications(SensorType aSensor) {
AssertMainThread();
PROXY_IF_SANDBOXED(DisableSensorNotifications(aSensor));
}
void RegisterSensorObserver(SensorType aSensor, ISensorObserver* aObserver) {
SensorObserverList* observers = GetSensorObservers(aSensor);
observers->AddObserver(aObserver);
if (observers->Length() == 1) {
EnableSensorNotifications(aSensor);
}
}
void UnregisterSensorObserver(SensorType aSensor, ISensorObserver* aObserver) {
SensorObserverList* observers = GetSensorObservers(aSensor);
if (!observers->RemoveObserver(aObserver) || observers->Length() > 0) {
return;
}
DisableSensorNotifications(aSensor);
}
void NotifySensorChange(const SensorData& aSensorData) {
SensorObserverList* observers = GetSensorObservers(aSensorData.sensor());
observers->Broadcast(aSensorData);
}
MOZ_IMPL_HAL_OBSERVER(Network)
void GetCurrentNetworkInformation(NetworkInformation* aInfo) {
*aInfo = NetworkObservers()->GetCurrentInformation();
}
void NotifyNetworkChange(const NetworkInformation& aInfo) {
NetworkObservers()->CacheInformation(aInfo);
NetworkObservers()->BroadcastCachedInformation();
}
MOZ_IMPL_HAL_OBSERVER(WakeLock)
void ModifyWakeLock(const nsAString& aTopic, WakeLockControl aLockAdjust,
WakeLockControl aHiddenAdjust,
uint64_t aProcessID /* = CONTENT_PROCESS_ID_UNKNOWN */) {
AssertMainThread();
if (aProcessID == CONTENT_PROCESS_ID_UNKNOWN) {
aProcessID = InSandbox() ? ContentChild::GetSingleton()->GetID()
: CONTENT_PROCESS_ID_MAIN;
}
PROXY_IF_SANDBOXED(
ModifyWakeLock(aTopic, aLockAdjust, aHiddenAdjust, aProcessID));
}
void GetWakeLockInfo(const nsAString& aTopic,
WakeLockInformation* aWakeLockInfo) {
AssertMainThread();
PROXY_IF_SANDBOXED(GetWakeLockInfo(aTopic, aWakeLockInfo));
}
void NotifyWakeLockChange(const WakeLockInformation& aInfo) {
AssertMainThread();
WakeLockObservers()->BroadcastInformation(aInfo);
}
MOZ_IMPL_HAL_OBSERVER(ScreenConfiguration)
void GetCurrentScreenConfiguration(ScreenConfiguration* aScreenConfiguration) {
*aScreenConfiguration =
ScreenConfigurationObservers()->GetCurrentInformation();
}
void NotifyScreenConfigurationChange(
const ScreenConfiguration& aScreenConfiguration) {
ScreenConfigurationObservers()->CacheInformation(aScreenConfiguration);
ScreenConfigurationObservers()->BroadcastCachedInformation();
}
bool LockScreenOrientation(const ScreenOrientation& aOrientation) {
AssertMainThread();
RETURN_PROXY_IF_SANDBOXED(LockScreenOrientation(aOrientation), false);
}
void UnlockScreenOrientation() {
AssertMainThread();
PROXY_IF_SANDBOXED(UnlockScreenOrientation());
}
void SetProcessPriority(int aPid, ProcessPriority aPriority) {
// n.b. The sandboxed implementation crashes; SetProcessPriority works only
// from the main process.
PROXY_IF_SANDBOXED(SetProcessPriority(aPid, aPriority));
}
// From HalTypes.h.
const char* ProcessPriorityToString(ProcessPriority aPriority) {
switch (aPriority) {
case PROCESS_PRIORITY_PARENT_PROCESS:
return "PARENT_PROCESS";
case PROCESS_PRIORITY_PREALLOC:
return "PREALLOC";
case PROCESS_PRIORITY_FOREGROUND_HIGH:
return "FOREGROUND_HIGH";
case PROCESS_PRIORITY_FOREGROUND:
return "FOREGROUND";
case PROCESS_PRIORITY_FOREGROUND_KEYBOARD:
return "FOREGROUND_KEYBOARD";
case PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE:
return "BACKGROUND_PERCEIVABLE";
case PROCESS_PRIORITY_BACKGROUND:
return "BACKGROUND";
case PROCESS_PRIORITY_UNKNOWN:
return "UNKNOWN";
default:
MOZ_ASSERT(false);
return "???";
}
}
void Init() {
MOZ_ASSERT(!sInitialized);
if (!InSandbox()) {
gLastIDToVibrate = new WindowIdentifier::IDArrayType();
}
WakeLockInit();
sInitialized = true;
}
void Shutdown() {
MOZ_ASSERT(sInitialized);
gLastIDToVibrate = nullptr;
sBatteryObservers = nullptr;
sNetworkObservers = nullptr;
sWakeLockObservers = nullptr;
sScreenConfigurationObservers = nullptr;
for (auto& sensorObserver : sSensorObservers) {
sensorObserver = nullptr;
}
sInitialized = false;
}
} // namespace mozilla::hal