Bug 697132 - Implement wakelock interfaces. r=jlebar
authorKan-Ru Chen <kanru@kanru.info>
Wed, 07 Mar 2012 12:03:25 +0100
changeset 91318 72bc6f12d1cc5237b7347f2ed737e929def0ad07
parent 91317 031fd6560e841f2ec22714f2319883c55a1597bb
child 91319 d68420c97e0e8edb8bca539ce706b300451f119a
push idunknown
push userunknown
push dateunknown
reviewersjlebar
bugs697132
milestone13.0a1
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;
 }