--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -65,16 +65,18 @@
#include "nsIJSContextStack.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentUtils.h"
#include "nsUnicharUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "BatteryManager.h"
#include "PowerManager.h"
+#include "nsIDOMWakeLock.h"
+#include "nsIPowerManagerService.h"
#include "SmsManager.h"
#include "nsISmsService.h"
#include "mozilla/Hal.h"
#include "nsIWebNavigation.h"
#include "mozilla/ClearOnShutdown.h"
#include "Connection.h"
#ifdef MOZ_B2G_RIL
@@ -167,17 +169,20 @@ Navigator::Invalidate()
mNotification = nsnull;
}
if (mBatteryManager) {
mBatteryManager->Shutdown();
mBatteryManager = nsnull;
}
- mPowerManager = nsnull;
+ if (mPowerManager) {
+ mPowerManager->Shutdown();
+ mPowerManager = nsnull;
+ }
if (mSmsManager) {
mSmsManager->Shutdown();
mSmsManager = nsnull;
}
#ifdef MOZ_B2G_RIL
if (mTelephony) {
@@ -953,25 +958,48 @@ Navigator::GetMozBattery(nsIDOMMozBatter
NS_ADDREF(*aBattery = mBatteryManager);
return NS_OK;
}
NS_IMETHODIMP
Navigator::GetMozPower(nsIDOMMozPowerManager** aPower)
{
+ *aPower = nsnull;
+
if (!mPowerManager) {
+ nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
+ NS_ENSURE_TRUE(win, NS_OK);
+
mPowerManager = new power::PowerManager();
+ mPowerManager->Init(win);
}
- NS_ADDREF(*aPower = mPowerManager);
+ nsCOMPtr<nsIDOMMozPowerManager> power =
+ do_QueryInterface(NS_ISUPPORTS_CAST(nsIDOMMozPowerManager*, mPowerManager));
+ power.forget(aPower);
return NS_OK;
}
+NS_IMETHODIMP
+Navigator::RequestWakeLock(const nsAString &aTopic, nsIDOMMozWakeLock **aWakeLock)
+{
+ *aWakeLock = nsnull;
+
+ nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
+ NS_ENSURE_TRUE(win, NS_OK);
+
+ nsCOMPtr<nsIPowerManagerService> pmService =
+ do_GetService(POWERMANAGERSERVICE_CONTRACTID);
+ NS_ENSURE_TRUE(pmService, NS_OK);
+
+ return pmService->NewWakeLock(aTopic, win, aWakeLock);
+}
+
//*****************************************************************************
// Navigator::nsIDOMNavigatorSms
//*****************************************************************************
bool
Navigator::IsSmsAllowed() const
{
static const bool defaultSmsPermission = false;
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -502,16 +502,17 @@ using mozilla::dom::indexedDB::IDBWrappe
#include "nsIDOMCustomEvent.h"
#include "nsWrapperCacheInlines.h"
#include "dombindings.h"
#include "nsIDOMBatteryManager.h"
#include "BatteryManager.h"
#include "nsIDOMPowerManager.h"
+#include "nsIDOMWakeLock.h"
#include "nsIDOMSmsManager.h"
#include "nsIDOMSmsMessage.h"
#include "nsIDOMSmsEvent.h"
#include "nsIDOMSmsRequest.h"
#include "nsIDOMSmsFilter.h"
#include "nsIDOMSmsCursor.h"
#include "nsIPrivateDOMEvent.h"
#include "nsIDOMConnection.h"
@@ -1431,16 +1432,19 @@ static nsDOMClassInfoData sClassInfoData
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(MozBatteryManager, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(MozPowerManager, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
+ NS_DEFINE_CLASSINFO_DATA(MozWakeLock, nsDOMGenericSH,
+ DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
NS_DEFINE_CLASSINFO_DATA(MozSmsManager, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(MozSmsMessage, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(MozSmsEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -4026,16 +4030,20 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozBatteryManager)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(MozPowerManager, nsIDOMMozPowerManager)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozPowerManager)
DOM_CLASSINFO_MAP_END
+ DOM_CLASSINFO_MAP_BEGIN(MozWakeLock, nsIDOMMozWakeLock)
+ DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozWakeLock)
+ DOM_CLASSINFO_MAP_END
+
DOM_CLASSINFO_MAP_BEGIN(MozSmsManager, nsIDOMMozSmsManager)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsManager)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(MozSmsMessage, nsIDOMMozSmsMessage)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsMessage)
DOM_CLASSINFO_MAP_END
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -424,16 +424,17 @@ DOMCI_CLASS(GeoGeolocation)
DOMCI_CLASS(GeoPosition)
DOMCI_CLASS(GeoPositionCoords)
DOMCI_CLASS(GeoPositionAddress)
DOMCI_CLASS(GeoPositionError)
DOMCI_CLASS(MozBatteryManager)
DOMCI_CLASS(MozPowerManager)
+DOMCI_CLASS(MozWakeLock)
DOMCI_CLASS(MozSmsManager)
DOMCI_CLASS(MozSmsMessage)
DOMCI_CLASS(MozSmsEvent)
DOMCI_CLASS(MozSmsRequest)
DOMCI_CLASS(MozSmsFilter)
DOMCI_CLASS(MozSmsCursor)
--- a/dom/interfaces/base/domstubs.idl
+++ b/dom/interfaces/base/domstubs.idl
@@ -126,8 +126,9 @@ interface nsIDOMCrypto;
interface nsIDOMPkcs11;
// Used font face (for inspector)
interface nsIDOMFontFace;
interface nsIDOMFontFaceList;
// Power
interface nsIDOMMozPowerManager;
+interface nsIDOMMozWakeLock;
--- a/dom/interfaces/base/nsIDOMNavigator.idl
+++ b/dom/interfaces/base/nsIDOMNavigator.idl
@@ -34,17 +34,17 @@
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "domstubs.idl"
-[scriptable, uuid(b1f4b1fa-49c2-4375-9ce8-bf97ecf6b428)]
+[scriptable, uuid(e610c037-db58-4cd7-8ed3-0d7f1422b4d3)]
interface nsIDOMNavigator : nsISupports
{
readonly attribute DOMString appCodeName;
readonly attribute DOMString appName;
readonly attribute DOMString appVersion;
readonly attribute DOMString language;
readonly attribute nsIDOMMimeTypeArray mimeTypes;
readonly attribute DOMString platform;
@@ -105,9 +105,39 @@ interface nsIDOMNavigator : nsISupports
* pass an even number of elements (that is, if your list ends with b_n
* instead of a_n), the final element doesn't specify anything meaningful.
*
* We may throw NS_ERROR_DOM_NOT_SUPPORTED_ERR if the vibration pattern is
* too long, or if any of its elements is too large.
*/
[implicit_jscontext]
void mozVibrate(in jsval aPattern);
+
+ /**
+ * Request a wake lock for a resource.
+ *
+ * A page holds a wake lock to request that a resource not be turned
+ * off (or otherwise made unavailable).
+ *
+ * The topic is the name of a resource that might be made unavailable for
+ * various reasons. For example, on a mobile device the power manager might
+ * decide to turn off the screen after a period of idle time to save power.
+ *
+ * The resource manager checks the lock state of a topic before turning off
+ * the associated resource. For example, a page could hold a lock on the
+ * "screen" topic to prevent the screensaver from appearing or the screen
+ * from turning off.
+ *
+ * The resource manager defines what each topic means and sets policy. For
+ * example, the resource manager might decide to ignore 'screen' wake locks
+ * held by pages which are not visible.
+ *
+ * One topic can be locked multiple times; it is considered released only when
+ * all locks on the topic have been released.
+ *
+ * The returned nsIDOMMozWakeLock object is a token of the lock. You can
+ * unlock the lock via the object's |unlock| method. The lock is released
+ * automatically when its associated window is unloaded.
+ *
+ * @param aTopic resource name
+ */
+ nsIDOMMozWakeLock requestWakeLock(in DOMString aTopic);
};
--- a/dom/power/Makefile.in
+++ b/dom/power/Makefile.in
@@ -47,25 +47,29 @@ LIBXUL_LIBRARY = 1
FORCE_STATIC_LIB = 1
include $(topsrcdir)/dom/dom-config.mk
EXPORTS_NAMESPACES = mozilla/dom/power
EXPORTS_mozilla/dom/power = \
PowerManagerService.h \
+ Types.h \
$(NULL)
CPPSRCS = \
PowerManager.cpp \
PowerManagerService.cpp \
+ WakeLock.cpp \
$(NULL)
XPIDLSRCS = \
nsIDOMPowerManager.idl \
+ nsIDOMWakeLock.idl \
+ nsIDOMWakeLockListener.idl \
nsIPowerManagerService.idl \
$(NULL)
ifdef ENABLE_TESTS
DIRS += test
endif
include $(topsrcdir)/config/config.mk
--- a/dom/power/PowerManager.cpp
+++ b/dom/power/PowerManager.cpp
@@ -31,59 +31,166 @@
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "PowerManager.h"
+#include "WakeLock.h"
#include "nsContentUtils.h"
#include "nsDOMClassInfoID.h"
+#include "nsIDOMWakeLockListener.h"
#include "nsIPowerManagerService.h"
+#include "nsIPrincipal.h"
+#include "nsPIDOMWindow.h"
#include "nsServiceManagerUtils.h"
DOMCI_DATA(MozPowerManager, mozilla::dom::power::PowerManager)
namespace mozilla {
namespace dom {
namespace power {
NS_INTERFACE_MAP_BEGIN(PowerManager)
NS_INTERFACE_MAP_ENTRY(nsIDOMMozPowerManager)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozPowerManager)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMMozWakeLockListener)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozPowerManager)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(PowerManager)
NS_IMPL_RELEASE(PowerManager)
+nsresult
+PowerManager::Init(nsIDOMWindow *aWindow)
+{
+ mWindow = do_GetWeakReference(aWindow);
+
+ nsCOMPtr<nsIPowerManagerService> pmService =
+ do_GetService(POWERMANAGERSERVICE_CONTRACTID);
+ NS_ENSURE_STATE(pmService);
+
+ // Add ourself to the global notification list.
+ pmService->AddWakeLockListener(this);
+ return NS_OK;
+}
+
+nsresult
+PowerManager::Shutdown()
+{
+ nsCOMPtr<nsIPowerManagerService> pmService =
+ do_GetService(POWERMANAGERSERVICE_CONTRACTID);
+ NS_ENSURE_STATE(pmService);
+
+ // Remove ourself from the global notification list.
+ pmService->RemoveWakeLockListener(this);
+ return NS_OK;
+}
+
+nsresult
+PowerManager::CheckPermission()
+{
+ nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
+ NS_ENSURE_STATE(win);
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(win->GetExtantDocument());
+ NS_ENSURE_STATE(doc);
+
+ nsCOMPtr<nsIURI> uri;
+ doc->NodePrincipal()->GetURI(getter_AddRefs(uri));
+
+ if (!nsContentUtils::URIIsChromeOrInPref(uri, "dom.power.whitelist")) {
+ return NS_ERROR_DOM_SECURITY_ERR;
+ }
+
+ return NS_OK;
+}
+
NS_IMETHODIMP
PowerManager::Reboot()
{
- NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR);
+ nsresult rv = CheckPermission();
+ NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPowerManagerService> pmService =
do_GetService(POWERMANAGERSERVICE_CONTRACTID);
- NS_ENSURE_TRUE(pmService, NS_OK);
+ NS_ENSURE_STATE(pmService);
pmService->Reboot();
return NS_OK;
}
NS_IMETHODIMP
PowerManager::PowerOff()
{
- NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR);
+ nsresult rv = CheckPermission();
+ NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPowerManagerService> pmService =
do_GetService(POWERMANAGERSERVICE_CONTRACTID);
- NS_ENSURE_TRUE(pmService, NS_OK);
+ NS_ENSURE_STATE(pmService);
pmService->PowerOff();
return NS_OK;
}
+NS_IMETHODIMP
+PowerManager::AddWakeLockListener(nsIDOMMozWakeLockListener *aListener)
+{
+ nsresult rv = CheckPermission();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // already added? bail out.
+ if (mListeners.Contains(aListener))
+ return NS_OK;
+
+ mListeners.AppendElement(aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PowerManager::RemoveWakeLockListener(nsIDOMMozWakeLockListener *aListener)
+{
+ nsresult rv = CheckPermission();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mListeners.RemoveElement(aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PowerManager::GetWakeLockState(const nsAString &aTopic, nsAString &aState)
+{
+ nsresult rv = CheckPermission();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPowerManagerService> pmService =
+ do_GetService(POWERMANAGERSERVICE_CONTRACTID);
+ NS_ENSURE_STATE(pmService);
+
+ return pmService->GetWakeLockState(aTopic, aState);
+}
+
+NS_IMETHODIMP
+PowerManager::Callback(const nsAString &aTopic, const nsAString &aState)
+{
+ /**
+ * We maintain a local listener list instead of using the global
+ * list so that when the window is destroyed we don't have to
+ * cleanup the mess.
+ * Copy the listeners list before we walk through the callbacks
+ * because the callbacks may install new listeners. We expect no
+ * more than one listener per window, so it shouldn't be too long.
+ */
+ nsAutoTArray<nsCOMPtr<nsIDOMMozWakeLockListener>, 2> listeners(mListeners);
+ for (PRUint32 i = 0; i < listeners.Length(); ++i) {
+ listeners[i]->Callback(aTopic, aState);
+ }
+
+ return NS_OK;
+}
+
} // power
} // dom
} // mozilla
--- a/dom/power/PowerManager.h
+++ b/dom/power/PowerManager.h
@@ -32,30 +32,46 @@
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef mozilla_dom_power_PowerManager_h
#define mozilla_dom_power_PowerManager_h
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
#include "nsIDOMPowerManager.h"
+#include "nsIDOMWakeLockListener.h"
+#include "nsIDOMWindow.h"
+#include "nsWeakReference.h"
namespace mozilla {
namespace dom {
namespace power {
class PowerManager
: public nsIDOMMozPowerManager
+ , public nsIDOMMozWakeLockListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMMOZPOWERMANAGER
+ NS_DECL_NSIDOMMOZWAKELOCKLISTENER
PowerManager() {};
virtual ~PowerManager() {};
+
+ nsresult Init(nsIDOMWindow *aWindow);
+ nsresult Shutdown();
+
+private:
+ nsresult CheckPermission();
+
+ nsWeakPtr mWindow;
+ nsTArray<nsCOMPtr<nsIDOMMozWakeLockListener> > mListeners;
};
} // namespace power
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_power_PowerManager_h
--- a/dom/power/PowerManagerService.cpp
+++ b/dom/power/PowerManagerService.cpp
@@ -31,43 +31,145 @@
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "mozilla/Hal.h"
+#include "mozilla/HalWakeLock.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "nsIDOMWakeLockListener.h"
+#include "nsIDOMWindow.h"
#include "PowerManagerService.h"
+#include "WakeLock.h"
namespace mozilla {
namespace dom {
namespace power {
NS_IMPL_ISUPPORTS1(PowerManagerService, nsIPowerManagerService)
+/* static */ nsRefPtr<PowerManagerService> PowerManagerService::sSingleton;
+
/* static */ already_AddRefed<nsIPowerManagerService>
PowerManagerService::GetInstance()
{
- nsCOMPtr<nsIPowerManagerService> pmService;
+ if (!sSingleton) {
+ sSingleton = new PowerManagerService();
+ sSingleton->Init();
+ ClearOnShutdown(&sSingleton);
+ }
+
+ nsCOMPtr<nsIPowerManagerService> service(do_QueryInterface(sSingleton));
+ return service.forget();
+}
+
+void
+PowerManagerService::Init()
+{
+ hal::RegisterWakeLockObserver(this);
+}
+
+PowerManagerService::~PowerManagerService()
+{
+ hal::UnregisterWakeLockObserver(this);
+}
- pmService = new PowerManagerService();
+void
+PowerManagerService::ComputeWakeLockState(const hal::WakeLockInformation& aWakeLockInfo,
+ nsAString &aState)
+{
+ hal::WakeLockState state = hal::ComputeWakeLockState(aWakeLockInfo.numLocks(),
+ aWakeLockInfo.numHidden());
+ switch (state) {
+ case hal::WAKE_LOCK_STATE_UNLOCKED:
+ aState.AssignLiteral("unlocked");
+ break;
+ case hal::WAKE_LOCK_STATE_HIDDEN:
+ aState.AssignLiteral("locked-background");
+ break;
+ case hal::WAKE_LOCK_STATE_VISIBLE:
+ aState.AssignLiteral("locked-foreground");
+ break;
+ }
+}
- return pmService.forget();
+void
+PowerManagerService::Notify(const hal::WakeLockInformation& aWakeLockInfo)
+{
+ nsAutoString state;
+ ComputeWakeLockState(aWakeLockInfo, state);
+
+ /**
+ * Copy the listeners list before we walk through the callbacks
+ * because the callbacks may install new listeners. We expect no
+ * more than one listener per window, so it shouldn't be too long.
+ */
+ nsAutoTArray<nsCOMPtr<nsIDOMMozWakeLockListener>, 2> listeners(mWakeLockListeners);
+
+ for (PRUint32 i = 0; i < listeners.Length(); ++i) {
+ listeners[i]->Callback(aWakeLockInfo.topic(), state);
+ }
}
NS_IMETHODIMP
PowerManagerService::Reboot()
{
hal::Reboot();
return NS_OK;
}
NS_IMETHODIMP
PowerManagerService::PowerOff()
{
hal::PowerOff();
return NS_OK;
}
+NS_IMETHODIMP
+PowerManagerService::AddWakeLockListener(nsIDOMMozWakeLockListener *aListener)
+{
+ if (mWakeLockListeners.Contains(aListener))
+ return NS_OK;
+
+ mWakeLockListeners.AppendElement(aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PowerManagerService::RemoveWakeLockListener(nsIDOMMozWakeLockListener *aListener)
+{
+ mWakeLockListeners.RemoveElement(aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PowerManagerService::GetWakeLockState(const nsAString &aTopic, nsAString &aState)
+{
+ hal::WakeLockInformation info;
+ hal::GetWakeLockInfo(aTopic, &info);
+
+ ComputeWakeLockState(info, aState);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PowerManagerService::NewWakeLock(const nsAString &aTopic,
+ nsIDOMWindow *aWindow,
+ nsIDOMMozWakeLock **aWakeLock)
+{
+ nsRefPtr<WakeLock> wakelock = new WakeLock();
+ nsresult rv = wakelock->Init(aTopic, aWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDOMMozWakeLock> wl =
+ do_QueryInterface(NS_ISUPPORTS_CAST(nsIDOMMozWakeLock*, wakelock));
+ wl.forget(aWakeLock);
+
+ return NS_OK;
+}
+
} // power
} // dom
} // mozilla
--- a/dom/power/PowerManagerService.h
+++ b/dom/power/PowerManagerService.h
@@ -32,30 +32,52 @@
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef mozilla_dom_power_PowerManagerService_h
#define mozilla_dom_power_PowerManagerService_h
+#include "nsCOMPtr.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsTArray.h"
#include "nsIPowerManagerService.h"
-#include "nsCOMPtr.h" // for already_AddRefed
+#include "mozilla/Observer.h"
+#include "Types.h"
namespace mozilla {
namespace dom {
namespace power {
class PowerManagerService
: public nsIPowerManagerService
+ , public WakeLockObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPOWERMANAGERSERVICE
static already_AddRefed<nsIPowerManagerService> GetInstance();
+
+ void Init();
+
+ // Implement WakeLockObserver
+ void Notify(const hal::WakeLockInformation& aWakeLockInfo);
+
+private:
+
+ ~PowerManagerService();
+
+ void ComputeWakeLockState(const hal::WakeLockInformation& aWakeLockInfo,
+ nsAString &aState);
+
+ static nsRefPtr<PowerManagerService> sSingleton;
+
+ nsTArray<nsCOMPtr<nsIDOMMozWakeLockListener> > mWakeLockListeners;
};
} // namespace power
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_power_PowerManagerService_h
new file mode 100644
--- /dev/null
+++ b/dom/power/Types.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+#ifndef mozilla_dom_power_Types_h
+#define mozilla_dom_power_Types_h
+
+namespace mozilla {
+namespace hal {
+class WakeLockInformation;
+} // namespace hal
+
+template <class T>
+class Observer;
+
+typedef Observer<hal::WakeLockInformation> WakeLockObserver;
+
+} // namespace mozilla
+
+#endif // mozilla_dom_power_Types_h
+
new file mode 100644
--- /dev/null
+++ b/dom/power/WakeLock.cpp
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "mozilla/Hal.h"
+#include "mozilla/HalWakeLock.h"
+#include "nsDOMClassInfoID.h"
+#include "nsDOMError.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMEventTarget.h"
+#include "nsPIDOMWindow.h"
+#include "PowerManager.h"
+#include "WakeLock.h"
+
+DOMCI_DATA(MozWakeLock, mozilla::dom::power::WakeLock)
+
+namespace mozilla {
+namespace dom {
+namespace power {
+
+NS_INTERFACE_MAP_BEGIN(WakeLock)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMMozWakeLock)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozWakeLock)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
+ NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozWakeLock)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(WakeLock)
+NS_IMPL_RELEASE(WakeLock)
+
+WakeLock::WakeLock()
+ : mLocked(false)
+ , mHidden(true)
+{
+}
+
+WakeLock::~WakeLock()
+{
+ DoUnlock();
+ DetachEventListener();
+}
+
+nsresult
+WakeLock::Init(const nsAString &aTopic, nsIDOMWindow *aWindow)
+{
+ mTopic.Assign(aTopic);
+
+ mWindow = do_GetWeakReference(aWindow);
+ nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
+
+ /**
+ * Null windows are allowed. A wake lock without associated window
+ * is always considered invisible.
+ */
+ if (window) {
+ nsCOMPtr<nsIDOMDocument> domDoc = window->GetExtantDocument();
+ NS_ENSURE_STATE(domDoc);
+ domDoc->GetMozHidden(&mHidden);
+ }
+
+ AttachEventListener();
+ DoLock();
+
+ return NS_OK;
+}
+
+void
+WakeLock::DoLock()
+{
+ if (!mLocked) {
+ // Change the flag immediately to prevent recursive reentering
+ mLocked = true;
+ hal::ModifyWakeLock(mTopic,
+ hal::WAKE_LOCK_ADD_ONE,
+ mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_NO_CHANGE);
+ }
+}
+
+void
+WakeLock::DoUnlock()
+{
+ if (mLocked) {
+ // Change the flag immediately to prevent recursive reentering
+ mLocked = false;
+ hal::ModifyWakeLock(mTopic,
+ hal::WAKE_LOCK_REMOVE_ONE,
+ mHidden ? hal::WAKE_LOCK_REMOVE_ONE : hal::WAKE_LOCK_NO_CHANGE);
+ }
+}
+
+void
+WakeLock::AttachEventListener()
+{
+ nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
+
+ if (window) {
+ nsCOMPtr<nsIDOMDocument> domDoc = window->GetExtantDocument();
+ if (domDoc) {
+ nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(domDoc);
+ target->AddSystemEventListener(NS_LITERAL_STRING("mozvisibilitychange"),
+ this,
+ /* useCapture = */ true,
+ /* wantsUntrusted = */ false);
+
+ target = do_QueryInterface(window);
+ target->AddSystemEventListener(NS_LITERAL_STRING("pagehide"),
+ this,
+ /* useCapture = */ true,
+ /* wantsUntrusted = */ false);
+ target->AddSystemEventListener(NS_LITERAL_STRING("pageshow"),
+ this,
+ /* useCapture = */ true,
+ /* wantsUntrusted = */ false);
+ }
+ }
+}
+
+void
+WakeLock::DetachEventListener()
+{
+ nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
+
+ if (window) {
+ nsCOMPtr<nsIDOMDocument> domDoc = window->GetExtantDocument();
+ if (domDoc) {
+ nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(domDoc);
+ target->RemoveSystemEventListener(NS_LITERAL_STRING("mozvisibilitychange"),
+ this,
+ /* useCapture = */ true);
+ target = do_QueryInterface(window);
+ target->RemoveSystemEventListener(NS_LITERAL_STRING("pagehide"),
+ this,
+ /* useCapture = */ true);
+ target->RemoveSystemEventListener(NS_LITERAL_STRING("pageshow"),
+ this,
+ /* useCapture = */ true);
+ }
+ }
+}
+
+NS_IMETHODIMP
+WakeLock::Unlock()
+{
+ /*
+ * We throw NS_ERROR_DOM_INVALID_STATE_ERR on double unlock.
+ */
+ if (!mLocked) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ DoUnlock();
+ DetachEventListener();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WakeLock::GetTopic(nsAString &aTopic)
+{
+ aTopic.Assign(mTopic);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WakeLock::HandleEvent(nsIDOMEvent *aEvent)
+{
+ nsAutoString type;
+ aEvent->GetType(type);
+
+ if (type.EqualsLiteral("mozvisibilitychange")) {
+ nsCOMPtr<nsIDOMEventTarget> target;
+ aEvent->GetTarget(getter_AddRefs(target));
+ nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(target);
+ NS_ENSURE_STATE(domDoc);
+ domDoc->GetMozHidden(&mHidden);
+
+ if (mLocked) {
+ hal::ModifyWakeLock(mTopic,
+ hal::WAKE_LOCK_NO_CHANGE,
+ mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_REMOVE_ONE);
+ }
+
+ return NS_OK;
+ }
+
+ if (type.EqualsLiteral("pagehide")) {
+ DoUnlock();
+ return NS_OK;
+ }
+
+ if (type.EqualsLiteral("pageshow")) {
+ DoLock();
+ return NS_OK;
+ }
+
+ return NS_OK;
+}
+
+} // power
+} // dom
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/dom/power/WakeLock.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_dom_power_WakeLock_h
+#define mozilla_dom_power_WakeLock_h
+
+#include "nsCOMPtr.h"
+#include "nsIDOMWakeLock.h"
+#include "nsIDOMEventListener.h"
+#include "nsString.h"
+#include "nsWeakReference.h"
+
+class nsIDOMWindow;
+
+namespace mozilla {
+namespace dom {
+namespace power {
+
+class WakeLock
+ : public nsIDOMMozWakeLock
+ , public nsIDOMEventListener
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMMOZWAKELOCK
+ NS_DECL_NSIDOMEVENTLISTENER
+
+ WakeLock();
+ virtual ~WakeLock();
+
+ nsresult Init(const nsAString &aTopic, nsIDOMWindow *aWindow);
+
+private:
+ void DoUnlock();
+ void DoLock();
+ void AttachEventListener();
+ void DetachEventListener();
+
+ bool mLocked;
+ bool mHidden;
+ nsString mTopic;
+
+ // window that this was created for. Weak reference.
+ nsWeakPtr mWindow;
+};
+
+} // namespace power
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_power_WakeLock_h
--- a/dom/power/nsIDOMPowerManager.idl
+++ b/dom/power/nsIDOMPowerManager.idl
@@ -32,14 +32,45 @@
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
-[scriptable, uuid(6ec16abc-2fe8-4ab3-99b0-0f08405be81b)]
+interface nsIDOMMozWakeLockListener;
+
+/**
+ * This interface implements navigator.mozPower
+ */
+[scriptable, uuid(abf4b2b1-139d-4eff-998d-8f24616910ae)]
interface nsIDOMMozPowerManager : nsISupports
{
- void powerOff();
- void reboot();
+ void powerOff();
+ void reboot();
+
+ /**
+ * The listeners are notified when a resource changes its lock state to:
+ * - unlocked
+ * - locked but not visible
+ * - locked and visible
+ */
+ void addWakeLockListener(in nsIDOMMozWakeLockListener aListener);
+ void removeWakeLockListener(in nsIDOMMozWakeLockListener aListener);
+
+ /**
+ * Query the wake lock state of the topic.
+ *
+ * Possible states are:
+ *
+ * - "unlocked" - nobody holds the wake lock.
+ *
+ * - "locked-foreground" - at least one window holds the wake lock,
+ * and it is visible.
+ *
+ * - "locked-background" - at least one window holds the wake lock,
+ * but all of them are hidden.
+ *
+ * @param aTopic The resource name related to the wake lock.
+ */
+ DOMString getWakeLockState(in DOMString aTopic);
};
new file mode 100644
--- /dev/null
+++ b/dom/power/nsIDOMWakeLock.idl
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.idl"
+
+[scriptable, uuid(2e61eed1-5983-4562-8f26-fd361ab4a00d)]
+interface nsIDOMMozWakeLock : nsISupports
+{
+ readonly attribute DOMString topic;
+
+ /**
+ * Release the wake lock.
+ *
+ * @throw NS_ERROR_DOM_INVALID_STATE_ERR if already unlocked.
+ */
+ void unlock();
+};
new file mode 100644
--- /dev/null
+++ b/dom/power/nsIDOMWakeLockListener.idl
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.idl"
+
+[scriptable, function, uuid(4e258af8-cffb-47bc-b16d-e8241243426e)]
+interface nsIDOMMozWakeLockListener : nsISupports
+{
+ /**
+ * The callback will be called when a lock topic changes its lock
+ * state.
+ *
+ * Possible states are:
+ *
+ * - "unlocked" - nobody holds the wake lock.
+ *
+ * - "locked-foreground" - at least one window holds the wake lock,
+ * and it is visible.
+ *
+ * - "locked-background" - at least one window holds the wake lock,
+ * but all of them are hidden.
+ *
+ * @param aTopic The resource name related to the wake lock.
+ * @param aState The wake lock state
+ */
+ void callback(in DOMString aTopic, in DOMString aState);
+};
--- a/dom/power/nsIPowerManagerService.idl
+++ b/dom/power/nsIPowerManagerService.idl
@@ -37,14 +37,31 @@
#include "nsISupports.idl"
%{C++
#define NS_POWERMANAGERSERVICE_CID { 0x18c2e238, 0x3a0a, 0x4153, {0x89, 0xfc, 0x16, 0x6b, 0x3b, 0x14, 0x65, 0xa1 } }
#define POWERMANAGERSERVICE_CONTRACTID "@mozilla.org/power/powermanagerservice;1"
%}
-[scriptable, builtinclass, uuid(38919539-4641-4f0b-9f11-6b6294a9386f)]
+interface nsIDOMMozWakeLock;
+interface nsIDOMMozWakeLockListener;
+interface nsIDOMWindow;
+
+/**
+ * For use with non-content code.
+ */
+[scriptable, builtinclass, uuid(235ca1a1-d0c8-41f3-9b4a-dbaa4437d69c)]
interface nsIPowerManagerService : nsISupports
{
- void powerOff();
- void reboot();
+ void powerOff();
+ void reboot();
+ void addWakeLockListener(in nsIDOMMozWakeLockListener aListener);
+ void removeWakeLockListener(in nsIDOMMozWakeLockListener aListener);
+ DOMString getWakeLockState(in DOMString aTopic);
+
+ /**
+ * Return a wake lock object of aTopic associated with aWindow.
+ * A wake lock without associated window, e.g. used in chrome, is
+ * always considered invisible.
+ */
+ nsIDOMMozWakeLock newWakeLock(in DOMString aTopic, [optional] in nsIDOMWindow aWindow);
};
--- a/dom/power/test/Makefile.in
+++ b/dom/power/test/Makefile.in
@@ -47,13 +47,20 @@ DIRS = \
$(NULL)
include $(topsrcdir)/config/rules.mk
_TEST_FILES = \
test_power_basics.html \
$(NULL)
+_BROWSER_TEST_FILES = \
+ browser_bug697132.js \
+ $(NULL)
+
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+libs:: $(_BROWSER_TEST_FILES)
+ $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+
#libs:: $(_CHROME_TEST_FILES)
# $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/power/test/browser_bug697132.js
@@ -0,0 +1,235 @@
+"use strict";
+
+waitForExplicitFinish();
+
+let kPrefNode = "dom.power.whitelist";
+let kPageSource1 = "data:text/html,1";
+let kPageSource2 = "data:text/html,2";
+
+let gOldPref;
+let gWin, gWin1, gWin2;
+let gTab, gTab1, gTab2;
+let gLock, gLock1, gLock2;
+let gCurStepIndex = -1;
+let gSteps = [
+ function basicWakeLock() {
+ gTab = gBrowser.addTab(kPageSource1);
+ gWin = gBrowser.getBrowserForTab(gTab).contentWindow;
+ let browser = gBrowser.getBrowserForTab(gTab);
+
+ browser.addEventListener("load", function onLoad(e) {
+ browser.removeEventListener("load", onLoad, true);
+ let nav = gWin.navigator;
+ let power = nav.mozPower;
+ gLock = nav.requestWakeLock("test");
+
+ ok(gLock != null,
+ "navigator.requestWakeLock should return a wake lock");
+ is(gLock.topic, "test",
+ "wake lock should remember the locked topic");
+ isnot(power.getWakeLockState("test"), "unlocked",
+ "topic is locked");
+
+ gLock.unlock();
+
+ is(gLock.topic, "test",
+ "wake lock should remember the locked topic even after unlock");
+ is(power.getWakeLockState("test"), "unlocked",
+ "topic is unlocked");
+
+ try {
+ gLock.unlock();
+ ok(false, "Should have thrown an error.");
+ } catch (e) {
+ is(e.code, DOMException.INVALID_STATE_ERR, "double unlock should throw InvalidStateError");
+ }
+
+ gBrowser.removeTab(gTab);
+
+ executeSoon(runNextStep);
+ }, true);
+ },
+ function multiWakeLock() {
+ gTab = gBrowser.addTab(kPageSource1);
+ gWin = gBrowser.getBrowserForTab(gTab).contentWindow;
+ let browser = gBrowser.getBrowserForTab(gTab);
+
+ browser.addEventListener("load", function onLoad(e) {
+ browser.removeEventListener("load", onLoad, true);
+ let nav = gWin.navigator;
+ let power = nav.mozPower;
+ let count = 0;
+ power.addWakeLockListener(function onWakeLockEvent(topic, state) {
+ is(topic, "test", "gLock topic is test");
+ ok(state == "unlocked" ||
+ state == "locked-foreground" ||
+ state == "locked-background",
+ "wake lock should be either locked or unlocked");
+ count++;
+ if (state == "locked-foreground" ||
+ state == "locked-background") {
+ is(count, 1,
+ "wake lock should be locked and the listener should only fire once");
+ }
+ if (state == "unlocked") {
+ is(count, 2,
+ "wake lock should be unlocked and the listener should only fire once");
+
+ ok(power.getWakeLockState("test") == "unlocked",
+ "topic is unlocked");
+ power.removeWakeLockListener(onWakeLockEvent);
+ gBrowser.removeTab(gTab);
+ executeSoon(runNextStep);
+ }
+ });
+
+ gLock1 = nav.requestWakeLock("test");
+ isnot(power.getWakeLockState("test"), "unlocked",
+ "topic is locked");
+
+ gLock2 = nav.requestWakeLock("test");
+ isnot(power.getWakeLockState("test"), "unlocked",
+ "topic is locked");
+
+ gLock1.unlock();
+ isnot(power.getWakeLockState("test"), "unlocked",
+ "topic is locked");
+
+ gLock2.unlock();
+ }, true);
+ },
+ function crossTabWakeLock1() {
+ gTab1 = gBrowser.addTab(kPageSource1);
+ gWin1 = gBrowser.getBrowserForTab(gTab1).contentWindow;
+ gTab2 = gBrowser.addTab(kPageSource1);
+ gWin2 = gBrowser.getBrowserForTab(gTab2).contentWindow;
+
+ gBrowser.selectedTab = gTab1;
+ let browser = gBrowser.getBrowserForTab(gTab2);
+
+ browser.addEventListener("load", function onLoad(e) {
+ browser.removeEventListener("load", onLoad, true);
+ gLock2 = gWin2.navigator.requestWakeLock("test");
+ is(gWin2.document.mozHidden, true,
+ "window is background")
+ is(gWin2.navigator.mozPower.getWakeLockState("test"), "locked-background",
+ "wake lock is background");
+ let doc2 = gWin2.document;
+ doc2.addEventListener("mozvisibilitychange", function onVisibilityChange(e) {
+ if (!doc2.mozHidden) {
+ doc2.removeEventListener("mozvisibilitychange", onVisibilityChange);
+ executeSoon(runNextStep);
+ }
+ });
+ gBrowser.selectedTab = gTab2;
+ }, true);
+ },
+ function crossTabWakeLock2() {
+ is(gWin2.document.mozHidden, false,
+ "window is foreground")
+ is(gWin2.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+ "wake lock is foreground");
+ gWin2.addEventListener("pagehide", function onPageHide(e) {
+ gWin2.removeEventListener("pagehide", onPageHide, true);
+ executeSoon(runNextStep);
+ }, true);
+ gWin2.addEventListener("pageshow", function onPageShow(e) {
+ gWin2.removeEventListener("pageshow", onPageShow, true);
+ executeSoon(runNextStep);
+ }, true);
+ gWin2.location = kPageSource2;
+ },
+ function crossTabWakeLock3() {
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "unlocked",
+ "wake lock should auto-unlock when page is unloaded");
+ gWin2.back();
+ // runNextStep called in onPageShow
+ },
+ function crossTabWakeLock4() {
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+ "wake lock should auto-reacquire when page is available again");
+ gBrowser.selectedTab = gTab1;
+ executeSoon(runNextStep);
+ },
+ function crossTabWakeLock5() {
+ // Test again in background tab
+ is(gWin2.document.mozHidden, true,
+ "window is background")
+ is(gWin2.navigator.mozPower.getWakeLockState("test"), "locked-background",
+ "wake lock is background");
+ gWin2.addEventListener("pagehide", function onPageHide(e) {
+ gWin2.removeEventListener("pagehide", onPageHide, true);
+ executeSoon(runNextStep);
+ }, true);
+ gWin2.addEventListener("pageshow", function onPageShow(e) {
+ gWin2.removeEventListener("pageshow", onPageShow, true);
+ executeSoon(runNextStep);
+ }, true);
+ gWin2.location = kPageSource2;
+ },
+ function crossTabWakeLock6() {
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "unlocked",
+ "wake lock should auto-unlock when page is unloaded");
+ gWin2.back();
+ // runNextStep called in onPageShow
+ },
+ function crossTabWakeLock7() {
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-background",
+ "wake lock should auto-reacquire when page is available again");
+ gLock2.unlock();
+ gBrowser.selectedTab = gTab2;
+ executeSoon(runNextStep);
+ },
+ function crossTabWakeLock8() {
+ is(gWin1.document.mozHidden, true,
+ "gWin1 is background");
+ is(gWin2.document.mozHidden, false,
+ "gWin2 is foreground");
+
+ gLock1 = gWin1.navigator.requestWakeLock("test");
+ gLock2 = gWin2.navigator.requestWakeLock("test");
+
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+ "topic is locked-foreground when one page is foreground and one is background");
+
+ gLock2.unlock();
+
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-background",
+ "topic is locked-background when all locks are background");
+
+ gLock2 = gWin2.navigator.requestWakeLock("test");
+
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+ "topic is locked-foreground when one page is foreground and one is background");
+
+ gLock1.unlock();
+
+ is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+ "topic is locked-foreground");
+
+ gBrowser.removeTab(gTab1);
+ gBrowser.removeTab(gTab2);
+ executeSoon(runNextStep);
+ },
+];
+
+function runNextStep() {
+ gCurStepIndex++;
+ if (gCurStepIndex < gSteps.length) {
+ gSteps[gCurStepIndex]();
+ } else {
+ Services.prefs.setCharPref(kPrefNode, gOldPref);
+ finish();
+ }
+}
+
+function test() {
+ try {
+ gOldPref = Services.prefs.getCharPref(kPrefNode);
+ } catch (e) {
+ gOldPref = "";
+ }
+ // data url inherits its parent's principal, which is |about:| here.
+ Services.prefs.setCharPref(kPrefNode, "about:");
+ runNextStep();
+}
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -179,55 +179,73 @@ public:
void RemoveObserver(Observer<InfoType>* aObserver) {
MOZ_ASSERT(mObservers);
mObservers->RemoveObserver(aObserver);
if (mObservers->Length() == 0) {
DisableNotifications();
+ OnNotificationsDisabled();
+
delete mObservers;
mObservers = 0;
-
- mHasValidCache = false;
}
}
+ void BroadcastInformation(const InfoType& aInfo) {
+ MOZ_ASSERT(mObservers);
+ mObservers->Broadcast(aInfo);
+ }
+
+protected:
+ 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;
}
mHasValidCache = true;
GetCurrentInformationInternal(&mInfo);
return mInfo;
}
void CacheInformation(const InfoType& aInfo) {
mHasValidCache = true;
mInfo = aInfo;
}
void BroadcastCachedInformation() {
- MOZ_ASSERT(mObservers);
- mObservers->Broadcast(mInfo);
+ BroadcastInformation(mInfo);
}
protected:
- virtual void EnableNotifications() = 0;
- virtual void DisableNotifications() = 0;
virtual void GetCurrentInformationInternal(InfoType*) = 0;
+ virtual void OnNotificationsDisabled() {
+ mHasValidCache = false;
+ }
+
private:
- mozilla::ObserverList<InfoType>* mObservers;
InfoType mInfo;
bool mHasValidCache;
};
-class BatteryObserversManager : public ObserversManager<BatteryInformation>
+class BatteryObserversManager : public CachingObserversManager<BatteryInformation>
{
protected:
void EnableNotifications() {
PROXY_IF_SANDBOXED(EnableBatteryNotifications());
}
void DisableNotifications() {
PROXY_IF_SANDBOXED(DisableBatteryNotifications());
@@ -235,17 +253,17 @@ protected:
void GetCurrentInformationInternal(BatteryInformation* aInfo) {
PROXY_IF_SANDBOXED(GetCurrentBatteryInformation(aInfo));
}
};
static BatteryObserversManager sBatteryObservers;
-class NetworkObserversManager : public ObserversManager<NetworkInformation>
+class NetworkObserversManager : public CachingObserversManager<NetworkInformation>
{
protected:
void EnableNotifications() {
PROXY_IF_SANDBOXED(EnableNetworkNotifications());
}
void DisableNotifications() {
PROXY_IF_SANDBOXED(DisableNetworkNotifications());
@@ -253,16 +271,30 @@ protected:
void GetCurrentInformationInternal(NetworkInformation* aInfo) {
PROXY_IF_SANDBOXED(GetCurrentNetworkInformation(aInfo));
}
};
static NetworkObserversManager sNetworkObservers;
+class WakeLockObserversManager : public ObserversManager<WakeLockInformation>
+{
+protected:
+ void EnableNotifications() {
+ PROXY_IF_SANDBOXED(EnableWakeLockNotifications());
+ }
+
+ void DisableNotifications() {
+ PROXY_IF_SANDBOXED(DisableWakeLockNotifications());
+ }
+};
+
+static WakeLockObserversManager sWakeLockObservers;
+
void
RegisterBatteryObserver(BatteryObserver* aObserver)
{
AssertMainThread();
sBatteryObservers.AddObserver(aObserver);
}
void
@@ -430,10 +462,47 @@ void Reboot()
}
void PowerOff()
{
AssertMainThread();
PROXY_IF_SANDBOXED(PowerOff());
}
+void
+RegisterWakeLockObserver(WakeLockObserver* aObserver)
+{
+ AssertMainThread();
+ sWakeLockObservers.AddObserver(aObserver);
+}
+
+void
+UnregisterWakeLockObserver(WakeLockObserver* aObserver)
+{
+ AssertMainThread();
+ sWakeLockObservers.RemoveObserver(aObserver);
+}
+
+void
+ModifyWakeLock(const nsAString &aTopic,
+ hal::WakeLockControl aLockAdjust,
+ hal::WakeLockControl aHiddenAdjust)
+{
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(ModifyWakeLock(aTopic, aLockAdjust, aHiddenAdjust));
+}
+
+void
+GetWakeLockInfo(const nsAString &aTopic, WakeLockInformation *aWakeLockInfo)
+{
+ AssertMainThread();
+ PROXY_IF_SANDBOXED(GetWakeLockInfo(aTopic, aWakeLockInfo));
+}
+
+void
+NotifyWakeLockChange(const WakeLockInformation& aInfo)
+{
+ AssertMainThread();
+ sWakeLockObservers.BroadcastInformation(aInfo);
+}
+
} // namespace hal
} // namespace mozilla
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -9,16 +9,17 @@
#include "mozilla/hal_sandbox/PHal.h"
#include "base/basictypes.h"
#include "mozilla/Types.h"
#include "nsTArray.h"
#include "prlog.h"
#include "mozilla/dom/battery/Types.h"
#include "mozilla/dom/network/Types.h"
+#include "mozilla/dom/power/Types.h"
#include "mozilla/hal_sandbox/PHal.h"
/*
* Hal.h contains the public Hal API.
*
* By default, this file defines its functions in the hal namespace, but if
* MOZ_HAL_NAMESPACE is defined, we'll define our functions in that namespace.
*
@@ -235,16 +236,65 @@ void SetTimezone(const nsCString& aTimez
*/
void Reboot();
/**
* Power off the device.
*/
void PowerOff();
+/**
+ * Enable wake lock notifications from the backend.
+ *
+ * This method is only used by WakeLockObserversManager.
+ */
+void EnableWakeLockNotifications();
+
+/**
+ * Disable wake lock notifications from the backend.
+ *
+ * This method is only used by WakeLockObserversManager.
+ */
+void DisableWakeLockNotifications();
+
+/**
+ * Inform the wake lock backend there is a new wake lock observer.
+ * @param aWakeLockObserver The observer that should be added.
+ */
+void RegisterWakeLockObserver(WakeLockObserver* aObserver);
+
+/**
+ * Inform the wake lock backend a wake lock observer unregistered.
+ * @param aWakeLockObserver The observer that should be removed.
+ */
+void UnregisterWakeLockObserver(WakeLockObserver* aObserver);
+
+/**
+ * Adjust the internal wake lock counts.
+ * @param aTopic lock topic
+ * @param aLockAdjust to increase or decrease active locks
+ * @param aHiddenAdjust to increase or decrease hidden locks
+ */
+void ModifyWakeLock(const nsAString &aTopic,
+ hal::WakeLockControl aLockAdjust,
+ hal::WakeLockControl aHiddenAdjust);
+
+/**
+ * Query the wake lock numbers of aTopic.
+ * @param aTopic lock topic
+ * @param aWakeLockInfo wake lock numbers
+ */
+void GetWakeLockInfo(const nsAString &aTopic, hal::WakeLockInformation *aWakeLockInfo);
+
+/**
+ * Notify of a change in the wake lock state.
+ * @param aWakeLockInfo The new wake lock information.
+ */
+void NotifyWakeLockChange(const hal::WakeLockInformation& aWakeLockInfo);
+
} // namespace MOZ_HAL_NAMESPACE
} // namespace mozilla
#ifdef MOZ_DEFINED_HAL_NAMESPACE
# undef MOZ_DEFINED_HAL_NAMESPACE
# undef MOZ_HAL_NAMESPACE
#endif
--- a/hal/HalTypes.h
+++ b/hal/HalTypes.h
@@ -31,19 +31,35 @@ enum LightMode {
eHalLightMode_User = 0, // brightness is managed by user setting
eHalLightMode_Sensor = 1 // brightness is managed by a light sensor
};
enum FlashMode {
eHalLightFlash_None = 0,
eHalLightFlash_Timed = 1, // timed flashing. Use flashOnMS and flashOffMS for timing
eHalLightFlash_Hardware = 2 // hardware assisted flashing
};
+
} // namespace hal
} // namespace mozilla
+namespace mozilla {
+namespace hal {
+
+/**
+ * Used by ModifyWakeLock
+ */
+enum WakeLockControl {
+ WAKE_LOCK_REMOVE_ONE = -1,
+ WAKE_LOCK_NO_CHANGE = 0,
+ WAKE_LOCK_ADD_ONE = 1,
+};
+
+}
+}
+
namespace IPC {
/**
* Light type serializer.
*/
template <>
struct ParamTraits<mozilla::hal::LightType>
: public EnumSerializer<mozilla::hal::LightType,
@@ -66,11 +82,21 @@ struct ParamTraits<mozilla::hal::LightMo
*/
template <>
struct ParamTraits<mozilla::hal::FlashMode>
: public EnumSerializer<mozilla::hal::FlashMode,
mozilla::hal::eHalLightFlash_None,
mozilla::hal::eHalLightFlash_Hardware>
{};
+/**
+ * WakeLockControl serializer.
+ */
+template <>
+struct ParamTraits<mozilla::hal::WakeLockControl>
+ : public EnumSerializer<mozilla::hal::WakeLockControl,
+ mozilla::hal::WAKE_LOCK_REMOVE_ONE,
+ mozilla::hal::WAKE_LOCK_ADD_ONE>
+{};
+
} // namespace IPC
#endif // mozilla_hal_Types_h
new file mode 100644
--- /dev/null
+++ b/hal/HalWakeLock.cpp
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "mozilla/Hal.h"
+#include "mozilla/HalWakeLock.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+
+using namespace mozilla::hal;
+
+namespace mozilla {
+namespace hal {
+
+WakeLockState
+ComputeWakeLockState(int aNumLocks, int aNumHidden)
+{
+ if (aNumLocks == 0) {
+ return WAKE_LOCK_STATE_UNLOCKED;
+ } else if (aNumLocks == aNumHidden) {
+ return WAKE_LOCK_STATE_HIDDEN;
+ } else {
+ return WAKE_LOCK_STATE_VISIBLE;
+ }
+}
+
+} // hal
+} // mozilla
+
+namespace mozilla {
+namespace hal_impl {
+
+namespace {
+struct LockCount {
+ PRUint32 numLocks;
+ PRUint32 numHidden;
+};
+}
+
+static int sActiveChildren = 0;
+static nsAutoPtr<nsDataHashtable<nsStringHashKey, LockCount> > sLockTable;
+static bool sInitialized = false;
+
+static void
+Init()
+{
+ sLockTable = new nsDataHashtable<nsStringHashKey, LockCount>();
+ sLockTable->Init();
+ ClearOnShutdown(&sLockTable);
+ sInitialized = true;
+}
+
+void
+EnableWakeLockNotifications()
+{
+ sActiveChildren++;
+}
+
+void
+DisableWakeLockNotifications()
+{
+ sActiveChildren--;
+}
+
+void
+ModifyWakeLock(const nsAString &aTopic,
+ hal::WakeLockControl aLockAdjust,
+ hal::WakeLockControl aHiddenAdjust)
+{
+ if (!sInitialized) {
+ Init();
+ }
+
+ LockCount count;
+ count.numLocks = 0;
+ count.numHidden = 0;
+ sLockTable->Get(aTopic, &count);
+ MOZ_ASSERT(count.numLocks >= count.numHidden);
+ MOZ_ASSERT(aLockAdjust >= 0 || count.numLocks > 0);
+ MOZ_ASSERT(aHiddenAdjust >= 0 || count.numHidden > 0);
+
+ WakeLockState oldState = ComputeWakeLockState(count.numLocks, count.numHidden);
+
+ count.numLocks += aLockAdjust;
+ count.numHidden += aHiddenAdjust;
+ MOZ_ASSERT(count.numLocks >= count.numHidden);
+
+ if (count.numLocks) {
+ sLockTable->Put(aTopic, count);
+ } else {
+ sLockTable->Remove(aTopic);
+ }
+
+ WakeLockState newState = ComputeWakeLockState(count.numLocks, count.numHidden);
+
+ if (sActiveChildren && oldState != newState) {
+ WakeLockInformation info;
+ info.numLocks() = count.numLocks;
+ info.numHidden() = count.numHidden;
+ info.topic() = aTopic;
+ NotifyWakeLockChange(info);
+ }
+}
+
+void
+GetWakeLockInfo(const nsAString &aTopic, WakeLockInformation *aWakeLockInfo)
+{
+ if (!sInitialized) {
+ Init();
+ }
+
+ LockCount count;
+ count.numLocks = 0;
+ count.numHidden = 0;
+ sLockTable->Get(aTopic, &count);
+
+ aWakeLockInfo->numLocks() = count.numLocks;
+ aWakeLockInfo->numHidden() = count.numHidden;
+ aWakeLockInfo->topic() = aTopic;
+}
+
+} // hal_impl
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/hal/HalWakeLock.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef __HAL_WAKELOCK_H_
+#define __HAL_WAKELOCK_H_
+
+namespace mozilla {
+namespace hal {
+
+enum WakeLockState {
+ WAKE_LOCK_STATE_UNLOCKED,
+ WAKE_LOCK_STATE_HIDDEN,
+ WAKE_LOCK_STATE_VISIBLE
+};
+
+/**
+ * Return the wake lock state according to the numbers.
+ */
+WakeLockState ComputeWakeLockState(int aNumLocks, int aNumHidden);
+
+} // hal
+} // mozilla
+
+#endif /* __HAL_WAKELOCK_H_ */
--- a/hal/Makefile.in
+++ b/hal/Makefile.in
@@ -58,22 +58,24 @@ EXPORT_LIBRARY = 1
EXPORTS_NAMESPACES = mozilla
EXPORTS_mozilla = \
Hal.h \
HalImpl.h \
HalSandbox.h \
HalSensor.h \
HalTypes.h \
+ HalWakeLock.h \
$(NULL)
CPPSRCS = \
Hal.cpp \
SandboxHal.cpp \
WindowIdentifier.cpp \
+ HalWakeLock.cpp \
$(NULL)
ifeq (android,$(MOZ_WIDGET_TOOLKIT))
CPPSRCS += \
AndroidHal.cpp \
AndroidSensor.cpp \
$(NULL)
else ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
--- a/hal/sandbox/PHal.ipdl
+++ b/hal/sandbox/PHal.ipdl
@@ -43,16 +43,17 @@ include "nspr/prtime.h";
include "mozilla/HalSensor.h";
include "mozilla/HalTypes.h";
using PRTime;
using mozilla::hal::FlashMode;
using mozilla::hal::LightType;
using mozilla::hal::LightMode;
using mozilla::hal::SensorType;
+using mozilla::hal::WakeLockControl;
namespace mozilla {
namespace hal {
struct BatteryInformation {
double level;
bool charging;
double remainingTime;
@@ -76,24 +77,33 @@ namespace hal {
namespace hal {
struct NetworkInformation {
double bandwidth;
bool canBeMetered;
};
}
+namespace hal {
+ struct WakeLockInformation {
+ uint32_t numLocks;
+ uint32_t numHidden;
+ nsString topic;
+ };
+}
+
namespace hal_sandbox {
sync protocol PHal {
manager PContent;
child:
NotifyBatteryChange(BatteryInformation aBatteryInfo);
NotifyNetworkChange(NetworkInformation aNetworkInfo);
+ NotifyWakeLockChange(WakeLockInformation aWakeLockInfo);
parent:
Vibrate(uint32[] pattern, uint64[] id, PBrowser browser);
CancelVibrate(uint64[] id, PBrowser browser);
EnableBatteryNotifications();
DisableBatteryNotifications();
sync GetCurrentBatteryInformation()
@@ -116,16 +126,22 @@ parent:
sync SetLight(LightType light, LightConfiguration aConfig)
returns (bool status);
sync GetLight(LightType light)
returns (LightConfiguration aConfig, bool status);
Reboot();
PowerOff();
+ ModifyWakeLock(nsString aTopic, WakeLockControl aLockAdjust, WakeLockControl aHiddenAdjust);
+ EnableWakeLockNotifications();
+ DisableWakeLockNotifications();
+ sync GetWakeLockInfo(nsString aTopic)
+ returns (WakeLockInformation aWakeLockInfo);
+
child:
NotifySensorChange(SensorData aSensorData);
parent:
EnableSensorNotifications(SensorType aSensor);
DisableSensorNotifications(SensorType aSensor);
__delete__();
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -164,20 +164,45 @@ EnableSensorNotifications(SensorType aSe
Hal()->SendEnableSensorNotifications(aSensor);
}
void
DisableSensorNotifications(SensorType aSensor) {
Hal()->SendDisableSensorNotifications(aSensor);
}
+void
+EnableWakeLockNotifications()
+{
+ Hal()->SendEnableWakeLockNotifications();
+}
+
+void
+DisableWakeLockNotifications()
+{
+ Hal()->SendDisableWakeLockNotifications();
+}
+
+void
+ModifyWakeLock(const nsAString &aTopic, WakeLockControl aLockAdjust, WakeLockControl aHiddenAdjust)
+{
+ Hal()->SendModifyWakeLock(nsString(aTopic), aLockAdjust, aHiddenAdjust);
+}
+
+void
+GetWakeLockInfo(const nsAString &aTopic, WakeLockInformation *aWakeLockInfo)
+{
+ Hal()->SendGetWakeLockInfo(nsString(aTopic), aWakeLockInfo);
+}
+
class HalParent : public PHalParent
, public BatteryObserver
, public NetworkObserver
, public ISensorObserver
+ , public WakeLockObserver
{
public:
NS_OVERRIDE virtual bool
RecvVibrate(const InfallibleTArray<unsigned int>& pattern,
const InfallibleTArray<uint64> &id,
PBrowserParent *browserParent)
{
// Check whether browserParent is active. We should have already
@@ -338,16 +363,51 @@ public:
RecvDisableSensorNotifications(const SensorType &aSensor) {
hal::UnregisterSensorObserver(aSensor, this);
return true;
}
void Notify(const SensorData& aSensorData) {
unused << SendNotifySensorChange(aSensorData);
}
+
+ NS_OVERRIDE virtual bool
+ RecvModifyWakeLock(const nsString &aTopic,
+ const WakeLockControl &aLockAdjust,
+ const WakeLockControl &aHiddenAdjust)
+ {
+ hal::ModifyWakeLock(aTopic, aLockAdjust, aHiddenAdjust);
+ return true;
+ }
+
+ NS_OVERRIDE virtual bool
+ RecvEnableWakeLockNotifications()
+ {
+ hal::RegisterWakeLockObserver(this);
+ return true;
+ }
+
+ NS_OVERRIDE virtual bool
+ RecvDisableWakeLockNotifications()
+ {
+ hal::UnregisterWakeLockObserver(this);
+ return true;
+ }
+
+ NS_OVERRIDE virtual bool
+ RecvGetWakeLockInfo(const nsString &aTopic, WakeLockInformation *aWakeLockInfo)
+ {
+ hal::GetWakeLockInfo(aTopic, aWakeLockInfo);
+ return true;
+ }
+
+ void Notify(const WakeLockInformation& aWakeLockInfo)
+ {
+ unused << SendNotifyWakeLockChange(aWakeLockInfo);
+ }
};
class HalChild : public PHalChild {
public:
NS_OVERRIDE virtual bool
RecvNotifyBatteryChange(const BatteryInformation& aBatteryInfo) {
hal::NotifyBatteryChange(aBatteryInfo);
return true;
@@ -356,16 +416,22 @@ public:
NS_OVERRIDE virtual bool
RecvNotifySensorChange(const hal::SensorData &aSensorData);
NS_OVERRIDE virtual bool
RecvNotifyNetworkChange(const NetworkInformation& aNetworkInfo) {
hal::NotifyNetworkChange(aNetworkInfo);
return true;
}
+
+ NS_OVERRIDE virtual bool
+ RecvNotifyWakeLockChange(const WakeLockInformation& aWakeLockInfo) {
+ hal::NotifyWakeLockChange(aWakeLockInfo);
+ return true;
+ }
};
bool
HalChild::RecvNotifySensorChange(const hal::SensorData &aSensorData) {
hal::NotifySensorChange(aSensorData);
return true;
}