Bug 836654 - Part 4: Add PowerManagerService::NewWakeLockOnBehalfOfProcess. r=cjones
authorJustin Lebar <justin.lebar@gmail.com>
Thu, 14 Feb 2013 15:41:30 -0500
changeset 131840 691f0a62b261e89137d8a5de1191e4d1578a4acc
parent 131839 6480e7d33c5a02b0ff083188b7976066b5b5dc40
child 131841 63219dc42eac96c3206613c9fdb73f165a79c26d
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones
bugs836654
milestone21.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 836654 - Part 4: Add PowerManagerService::NewWakeLockOnBehalfOfProcess. r=cjones
dom/power/PowerManagerService.cpp
dom/power/PowerManagerService.h
dom/power/WakeLock.cpp
dom/power/WakeLock.h
hal/Hal.h
--- a/dom/power/PowerManagerService.cpp
+++ b/dom/power/PowerManagerService.cpp
@@ -194,11 +194,21 @@ PowerManagerService::NewWakeLock(const n
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDOMMozWakeLock> wl(wakelock);
   wl.forget(aWakeLock);
 
   return NS_OK;
 }
 
+already_AddRefed<nsIDOMMozWakeLock>
+PowerManagerService::NewWakeLockOnBehalfOfProcess(const nsAString& aTopic,
+                                                  ContentParent* aContentParent)
+{
+  nsRefPtr<WakeLock> wakelock = new WakeLock();
+  nsresult rv = wakelock->Init(aTopic, aContentParent);
+  NS_ENSURE_SUCCESS(rv, nullptr);
+  return wakelock.forget();
+}
+
 } // power
 } // dom
 } // mozilla
--- a/dom/power/PowerManagerService.h
+++ b/dom/power/PowerManagerService.h
@@ -11,16 +11,19 @@
 #include "nsTArray.h"
 #include "nsIPowerManagerService.h"
 #include "mozilla/Observer.h"
 #include "Types.h"
 #include "mozilla/StaticPtr.h"
 
 namespace mozilla {
 namespace dom {
+
+class ContentParent;
+
 namespace power {
 
 class PowerManagerService
   : public nsIPowerManagerService
   , public WakeLockObserver
 {
 public:
   NS_DECL_ISUPPORTS
@@ -28,16 +31,32 @@ public:
 
   static already_AddRefed<PowerManagerService> GetInstance();
 
   void Init();
 
   // Implement WakeLockObserver
   void Notify(const hal::WakeLockInformation& aWakeLockInfo);
 
+  /**
+   * Acquire a wake lock on behalf of a given process (aContentParent).
+   *
+   * This method stands in contrast to nsIPowerManagerService::NewWakeLock,
+   * which acquires a wake lock on behalf of the /current/ process.
+   *
+   * NewWakeLockOnBehalfOfProcess is different from NewWakeLock in that
+   *
+   *  - The wake lock unlocks itself if the /given/ process dies, and
+   *  - The /given/ process shows up in WakeLockInfo::lockingProcesses.
+   *
+   */
+  already_AddRefed<nsIDOMMozWakeLock>
+  NewWakeLockOnBehalfOfProcess(const nsAString& aTopic,
+                               ContentParent* aContentParent);
+
 private:
 
   ~PowerManagerService();
 
   void ComputeWakeLockState(const hal::WakeLockInformation& aWakeLockInfo,
                             nsAString &aState);
 
   void SyncProfile();
--- a/dom/power/WakeLock.cpp
+++ b/dom/power/WakeLock.cpp
@@ -1,56 +1,69 @@
 /* -*- 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 "WakeLock.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/Hal.h"
 #include "mozilla/HalWakeLock.h"
 #include "nsDOMClassInfoID.h"
 #include "nsError.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)
 
+using namespace mozilla::hal;
+
 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_INTERFACE_MAP_ENTRY(nsIObserver)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   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)
+  , mContentParentID(CONTENT_PROCESS_ID_UNKNOWN)
 {
 }
 
 WakeLock::~WakeLock()
 {
   DoUnlock();
   DetachEventListener();
 }
 
 nsresult
 WakeLock::Init(const nsAString &aTopic, nsIDOMWindow *aWindow)
 {
+  // Don't Init() a WakeLock twice.
+  MOZ_ASSERT(mTopic.IsEmpty());
+
+  if (aTopic.IsEmpty()) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
   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.
@@ -62,37 +75,97 @@ WakeLock::Init(const nsAString &aTopic, 
   }
 
   AttachEventListener();
   DoLock();
 
   return NS_OK;
 }
 
+nsresult
+WakeLock::Init(const nsAString& aTopic, ContentParent* aContentParent)
+{
+  // Don't Init() a WakeLock twice.
+  MOZ_ASSERT(mTopic.IsEmpty());
+  MOZ_ASSERT(aContentParent);
+
+  if (aTopic.IsEmpty()) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  mTopic.Assign(aTopic);
+  mContentParentID = aContentParent->ChildID();
+  mHidden = false;
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  if (obs) {
+    obs->AddObserver(this, "ipc:content-shutdown", /* ownsWeak */ true);
+  }
+
+  DoLock();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WakeLock::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* data)
+{
+  // If this wake lock was acquired on behalf of another process, unlock it
+  // when that process dies.
+  //
+  // Note that we do /not/ call DoUnlock() here!  The wake lock back-end is
+  // already listening for ipc:content-shutdown messages and will clear out its
+  // tally for the process when it dies.  All we need to do here is ensure that
+  // unlock() becomes a nop.
+
+  MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
+
+  nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
+  if (!props) {
+    NS_WARNING("ipc:content-shutdown message without property bag as subject");
+    return NS_OK;
+  }
+
+  uint64_t childID = 0;
+  nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
+                                           &childID);
+  if (NS_SUCCEEDED(rv)) {
+    if (childID == mContentParentID) {
+      mLocked = false;
+    }
+  } else {
+    NS_WARNING("ipc:content-shutdown message without childID property");
+  }
+  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);
+                        mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_NO_CHANGE,
+                        mContentParentID);
   }
 }
 
 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);
+                        mHidden ? hal::WAKE_LOCK_REMOVE_ONE : hal::WAKE_LOCK_NO_CHANGE,
+                        mContentParentID);
   }
 }
 
 void
 WakeLock::AttachEventListener()
 {
   nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
   
@@ -176,17 +249,18 @@ WakeLock::HandleEvent(nsIDOMEvent *aEven
     nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(target);
     NS_ENSURE_STATE(domDoc);
     bool oldHidden = mHidden;
     domDoc->GetHidden(&mHidden);
 
     if (mLocked && oldHidden != mHidden) {
       hal::ModifyWakeLock(mTopic,
                           hal::WAKE_LOCK_NO_CHANGE,
-                          mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_REMOVE_ONE);
+                          mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_REMOVE_ONE,
+                          mContentParentID);
     }
 
     return NS_OK;
   }
 
   if (type.EqualsLiteral("pagehide")) {
     DoUnlock();
     return NS_OK;
--- a/dom/power/WakeLock.h
+++ b/dom/power/WakeLock.h
@@ -4,47 +4,67 @@
  * 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 "nsIObserver.h"
 #include "nsString.h"
 #include "nsWeakReference.h"
 
 class nsIDOMWindow;
 
 namespace mozilla {
 namespace dom {
+
+class ContentParent;
+
 namespace power {
 
 class WakeLock
   : public nsIDOMMozWakeLock
   , public nsIDOMEventListener
+  , public nsIObserver
+  , public nsSupportsWeakReference
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMMOZWAKELOCK
   NS_DECL_NSIDOMEVENTLISTENER
+  NS_DECL_NSIOBSERVER
 
   WakeLock();
   virtual ~WakeLock();
 
-  nsresult Init(const nsAString &aTopic, nsIDOMWindow *aWindow);
+  // Initialize this wake lock on behalf of the given window.  Null windows are
+  // allowed; a lock without an associated window is always considered
+  // invisible.
+  nsresult Init(const nsAString &aTopic, nsIDOMWindow* aWindow);
+
+  // Initialize this wake lock on behalf of the given process.  If the process
+  // dies, the lock is released.  A wake lock initialized via this method is
+  // always considered visible.
+  nsresult Init(const nsAString &aTopic, ContentParent* aContentParent);
 
 private:
   void     DoUnlock();
   void     DoLock();
   void     AttachEventListener();
   void     DetachEventListener();
 
   bool      mLocked;
   bool      mHidden;
+
+  // The ID of the ContentParent on behalf of whom we acquired this lock, or
+  // CONTENT_PROCESS_UNKNOWN_ID if this lock was acquired on behalf of the
+  // current process.
+  uint64_t  mContentParentID;
   nsString  mTopic;
 
   // window that this was created for.  Weak reference.
   nsWeakPtr mWindow;
 };
 
 } // namespace power
 } // namespace dom
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -3,16 +3,17 @@
 /* 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_Hal_h
 #define mozilla_Hal_h
 
 #include "mozilla/hal_sandbox/PHal.h"
+#include "mozilla/HalTypes.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/dom/ContentParent.h"
@@ -354,17 +355,17 @@ void UnregisterWakeLockObserver(WakeLock
  *
  *                      CONTENT_PROCESS_ID_UNKNOWN: The current process
  *                      CONTENT_PROCESS_ID_MAIN: The root process
  *                      X: The process with ContentChild::GetID() == X
  */
 void ModifyWakeLock(const nsAString &aTopic,
                     hal::WakeLockControl aLockAdjust,
                     hal::WakeLockControl aHiddenAdjust,
-                    uint64_t aProcessID = CONTENT_PROCESS_ID_UNKNOWN);
+                    uint64_t aProcessID = hal::CONTENT_PROCESS_ID_UNKNOWN);
 
 /**
  * Query the wake lock numbers of aTopic.
  * @param aTopic        lock topic
  * @param aWakeLockInfo wake lock numbers
  */
 void GetWakeLockInfo(const nsAString &aTopic, hal::WakeLockInformation *aWakeLockInfo);