Bug 697132 - Implement wakelock interfaces. r=jlebar
authorKan-Ru Chen <kanru@kanru.info>
Wed, 07 Mar 2012 12:03:25 +0100
changeset 88439 72bc6f12d1cc5237b7347f2ed737e929def0ad07
parent 88438 031fd6560e841f2ec22714f2319883c55a1597bb
child 88440 d68420c97e0e8edb8bca539ce706b300451f119a
push id22199
push userbmo@edmorley.co.uk
push dateThu, 08 Mar 2012 13:15:53 +0000
treeherdermozilla-central@09a8a48476e5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjlebar
bugs697132
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 697132 - Implement wakelock interfaces. r=jlebar
dom/base/Navigator.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/interfaces/base/domstubs.idl
dom/interfaces/base/nsIDOMNavigator.idl
dom/power/Makefile.in
dom/power/PowerManager.cpp
dom/power/PowerManager.h
dom/power/PowerManagerService.cpp
dom/power/PowerManagerService.h
dom/power/Types.h
dom/power/WakeLock.cpp
dom/power/WakeLock.h
dom/power/nsIDOMPowerManager.idl
dom/power/nsIDOMWakeLock.idl
dom/power/nsIDOMWakeLockListener.idl
dom/power/nsIPowerManagerService.idl
dom/power/test/Makefile.in
dom/power/test/browser_bug697132.js
hal/Hal.cpp
hal/Hal.h
hal/HalTypes.h
hal/HalWakeLock.cpp
hal/HalWakeLock.h
hal/Makefile.in
hal/sandbox/PHal.ipdl
hal/sandbox/SandboxHal.cpp
--- 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;
 }