Bug 874353 - Remove CPU wake lock control from ContentParent
☠☠ backed out by d48107587dcb ☠ ☠
authorSean Lin <selin@mozilla.com>
Tue, 24 Jun 2014 10:51:48 +0800
changeset 216360 3ab7869b4c47485b0aaba620c5cb6da6d26cfd85
parent 216359 7991e97af1ddfc54460f9a896ca31984d7948d64
child 216361 d48107587dcbdc570a19cbf9e45b4777c0721fe9
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs874353
milestone33.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 874353 - Remove CPU wake lock control from ContentParent
dom/browser-element/mochitest/priority/test_ExpectingSystemMessage.html
dom/browser-element/mochitest/priority/test_ExpectingSystemMessage2.html
dom/browser-element/mochitest/priority/test_HighPriorityDowngrade.html
dom/browser-element/mochitest/priority/test_HighPriorityDowngrade2.html
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/ProcessPriorityManager.cpp
dom/ipc/ProcessPriorityManager.h
dom/messages/SystemMessageInternal.js
dom/messages/SystemMessageManager.js
--- a/dom/browser-element/mochitest/priority/test_ExpectingSystemMessage.html
+++ b/dom/browser-element/mochitest/priority/test_ExpectingSystemMessage.html
@@ -38,18 +38,18 @@ function runTest() {
     // process times out.
     return expectPriorityChange(childID, 'FOREGROUND');
   }).then(SimpleTest.finish);
 
   document.body.appendChild(iframe);
 }
 
 addEventListener('testready', function() {
-  // Cause the CPU wake lock taken on behalf of this new process to time out
-  // after 1s.
+  // Cause the grace period of priority privilege for this new process to time
+  // out after 1s.
   SpecialPowers.pushPrefEnv(
     {set: [["dom.ipc.systemMessageCPULockTimeoutSec", 1]]},
     runTest);
 });
 
 </script>
 </body>
 </html>
--- a/dom/browser-element/mochitest/priority/test_ExpectingSystemMessage2.html
+++ b/dom/browser-element/mochitest/priority/test_ExpectingSystemMessage2.html
@@ -52,19 +52,19 @@ function runTest() {
     iframe.setVisible(false);
     return p;
   }).then(SimpleTest.finish);
 
   document.body.appendChild(iframe);
 }
 
 addEventListener('testready', function() {
-  // We don't want this wake lock to time out during the test; if it did, then
-  // we might see BACKGROUND priority instead of BACKGROUND_PERCEIVABLE.  So
-  // set the timeout to a large value.
+  // We don't want the grace period of priority privilege to time out during the
+  // test; should it really happen, we would see BACKGROUND priority instead of
+  // BACKGROUND_PERCEIVABLE.  So set the timeout to a large value.
   SpecialPowers.pushPrefEnv(
     {set: [["dom.ipc.systemMessageCPULockTimeoutSec", 99999]]},
     runTest);
 });
 
 </script>
 </body>
 </html>
--- a/dom/browser-element/mochitest/priority/test_HighPriorityDowngrade.html
+++ b/dom/browser-element/mochitest/priority/test_HighPriorityDowngrade.html
@@ -64,18 +64,18 @@ function runTest() {
   }).then(SimpleTest.finish);
 
   document.body.appendChild(iframe);
 }
 
 addEventListener('testready', function() {
   SpecialPowers.pushPrefEnv(
     {set: [
-      /* Cause the CPU wake lock taken on behalf of the high-priority process
-       * to time out after 1s. */
+      /* Cause the grace period of priority privilege for the high-priority
+       * process to time out after 1s. */
        ["dom.ipc.systemMessageCPULockTimeoutSec", 1],
        ["dom.wakelock.enabled", true]
     ]},
     runTest);
 });
 
 </script>
 </body>
--- a/dom/browser-element/mochitest/priority/test_HighPriorityDowngrade2.html
+++ b/dom/browser-element/mochitest/priority/test_HighPriorityDowngrade2.html
@@ -60,18 +60,18 @@ function runTest() {
     document.body.removeChild(highPriorityIframe);
     return p;
   }).then(SimpleTest.finish);
 
   document.body.appendChild(iframe);
 }
 
 addEventListener('testready', function() {
-  // Cause the CPU wake lock taken on behalf of the high-priority process never
-  // to time out during this test.
+  // Cause the grace period of priority privilege for the high-priority process
+  // never to time out during this test.
   SpecialPowers.pushPrefEnv(
     {set: [["dom.ipc.systemMessageCPULockTimeoutSec", 1000]]},
     runTest);
 });
 
 </script>
 </body>
 </html>
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -36,17 +36,16 @@
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PFileDescriptorSetParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
 #include "mozilla/dom/PMemoryReportRequestParent.h"
-#include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/bluetooth/PBluetoothParent.h"
 #include "mozilla/dom/PFMRadioParent.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
 #include "mozilla/dom/GeolocationBinding.h"
 #include "mozilla/dom/FileDescriptorSetParent.h"
 #include "mozilla/dom/telephony/TelephonyParent.h"
@@ -79,17 +78,16 @@
 #include "nsDOMFile.h"
 #include "nsFrameMessageManager.h"
 #include "nsHashPropertyBag.h"
 #include "nsIAlertsService.h"
 #include "nsIAppsService.h"
 #include "nsIClipboard.h"
 #include "nsICycleCollectorListener.h"
 #include "nsIDOMGeoGeolocation.h"
-#include "mozilla/dom/WakeLock.h"
 #include "nsIDOMWindow.h"
 #include "nsIExternalProtocolService.h"
 #include "nsIGfxInfo.h"
 #include "nsIIdleService.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIMemoryInfoDumper.h"
 #include "nsIMemoryReporter.h"
 #include "nsIMozBrowserFrame.h"
@@ -179,17 +177,16 @@ using namespace mozilla::system;
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 static const char* sClipboardTextFlavors[] = { kUnicodeMime };
 
 using base::ChildPrivileges;
 using base::KillProcess;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::indexedDB;
-using namespace mozilla::dom::power;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::telephony;
 using namespace mozilla::hal;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::net;
 using namespace mozilla::jsipc;
 using namespace mozilla::widget;
@@ -833,16 +830,105 @@ ContentParent::AnswerBridgeToChildProces
         return PContentBridge::Bridge(this, cp);
     } else {
         // You can't bridge to a process you didn't open!
         KillHard();
         return false;
     }
 }
 
+namespace {
+
+class SystemMessageHandledListener MOZ_FINAL
+    : public nsITimerCallback
+    , public LinkedListElement<SystemMessageHandledListener>
+{
+public:
+    NS_DECL_ISUPPORTS
+
+    SystemMessageHandledListener() {}
+
+    static void OnSystemMessageHandled()
+    {
+        if (!sListeners) {
+            return;
+        }
+
+        SystemMessageHandledListener* listener = sListeners->popFirst();
+        if (!listener) {
+            return;
+        }
+
+        // Careful: ShutDown() may delete |this|.
+        listener->ShutDown();
+    }
+
+    void Init(ContentParent* aContentParent)
+    {
+        MOZ_ASSERT(!mContentParent);
+        MOZ_ASSERT(!mTimer);
+
+        // mTimer keeps a strong reference to |this|.  When this object's
+        // destructor runs, it will remove itself from the LinkedList.
+
+        if (!sListeners) {
+            sListeners = new LinkedList<SystemMessageHandledListener>();
+            ClearOnShutdown(&sListeners);
+        }
+        sListeners->insertBack(this);
+
+        mContentParent = aContentParent;
+
+        mTimer = do_CreateInstance("@mozilla.org/timer;1");
+
+        uint32_t timeoutSec =
+            Preferences::GetInt("dom.ipc.systemMessageCPULockTimeoutSec", 30);
+        mTimer->InitWithCallback(this, timeoutSec * 1000,
+                                 nsITimer::TYPE_ONE_SHOT);
+    }
+
+    NS_IMETHOD Notify(nsITimer* aTimer)
+    {
+        // Careful: ShutDown() may delete |this|.
+        ShutDown();
+        return NS_OK;
+    }
+
+private:
+    ~SystemMessageHandledListener() {}
+
+    static StaticAutoPtr<LinkedList<SystemMessageHandledListener> > sListeners;
+
+    void ShutDown()
+    {
+        ProcessPriorityManager::ResetProcessPriority(mContentParent, false);
+
+        nsRefPtr<SystemMessageHandledListener> kungFuDeathGrip = this;
+
+        if (mContentParent) {
+            mContentParent = nullptr;
+        }
+        if (mTimer) {
+            mTimer->Cancel();
+            mTimer = nullptr;
+        }
+    }
+
+    nsRefPtr<ContentParent> mContentParent;
+    nsCOMPtr<nsITimer> mTimer;
+};
+
+StaticAutoPtr<LinkedList<SystemMessageHandledListener> >
+    SystemMessageHandledListener::sListeners;
+
+NS_IMPL_ISUPPORTS(SystemMessageHandledListener,
+                  nsITimerCallback)
+
+} // anonymous namespace
+
 /*static*/ TabParent*
 ContentParent::CreateBrowserOrApp(const TabContext& aContext,
                                   Element* aFrameElement,
                                   ContentParent* aOpenerContentParent)
 {
     if (!sCanLaunchSubprocesses) {
         return nullptr;
     }
@@ -1037,17 +1123,31 @@ ContentParent::CreateBrowserOrApp(const 
                                                    aFrameElement,
                                                    aOpenerContentParent);
         }
 
         // Otherwise just give up.
         return nullptr;
     }
 
-    p->MaybeTakeCPUWakeLock(aFrameElement);
+    // Request a higher priority above BACKGROUND if the child process is
+    // "critical" and probably has system messages coming soon. (A CPU wake lock
+    // may already be controlled by the B2G process in SystemMessageInternal.js
+    // for message handling.) This privilege is revoked once the message is
+    // delivered, or the grace period is up, whichever comes first.
+    nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(aFrameElement);
+    if (browserFrame && browserFrame->GetIsExpectingSystemMessage()) {
+      ProcessPriorityManager::ResetProcessPriority(p, true);
+
+      // This object's Init() function keeps it alive.
+      nsRefPtr<SystemMessageHandledListener> listener =
+          new SystemMessageHandledListener();
+      listener->Init(p);
+    }
+
 
     return static_cast<TabParent*>(browser);
 }
 
 void
 ContentParent::GetAll(nsTArray<ContentParent*>& aArray)
 {
     aArray.Clear();
@@ -1101,127 +1201,16 @@ ContentParent::Init()
         unused << SendActivateA11y();
     }
 #endif
 
     DebugOnly<FileUpdateDispatcher*> observer = FileUpdateDispatcher::GetSingleton();
     NS_ASSERTION(observer, "FileUpdateDispatcher is null");
 }
 
-namespace {
-
-class SystemMessageHandledListener MOZ_FINAL
-    : public nsITimerCallback
-    , public LinkedListElement<SystemMessageHandledListener>
-{
-public:
-    NS_DECL_ISUPPORTS
-
-    SystemMessageHandledListener() {}
-
-    static void OnSystemMessageHandled()
-    {
-        if (!sListeners) {
-            return;
-        }
-
-        SystemMessageHandledListener* listener = sListeners->popFirst();
-        if (!listener) {
-            return;
-        }
-
-        // Careful: ShutDown() may delete |this|.
-        listener->ShutDown();
-    }
-
-    void Init(WakeLock* aWakeLock)
-    {
-        MOZ_ASSERT(!mWakeLock);
-        MOZ_ASSERT(!mTimer);
-
-        // mTimer keeps a strong reference to |this|.  When this object's
-        // destructor runs, it will remove itself from the LinkedList.
-
-        if (!sListeners) {
-            sListeners = new LinkedList<SystemMessageHandledListener>();
-            ClearOnShutdown(&sListeners);
-        }
-        sListeners->insertBack(this);
-
-        mWakeLock = aWakeLock;
-
-        mTimer = do_CreateInstance("@mozilla.org/timer;1");
-
-        uint32_t timeoutSec =
-            Preferences::GetInt("dom.ipc.systemMessageCPULockTimeoutSec", 30);
-        mTimer->InitWithCallback(this, timeoutSec * 1000,
-                                 nsITimer::TYPE_ONE_SHOT);
-    }
-
-    NS_IMETHOD Notify(nsITimer* aTimer)
-    {
-        // Careful: ShutDown() may delete |this|.
-        ShutDown();
-        return NS_OK;
-    }
-
-private:
-    ~SystemMessageHandledListener() {}
-
-    static StaticAutoPtr<LinkedList<SystemMessageHandledListener> > sListeners;
-
-    void ShutDown()
-    {
-        nsRefPtr<SystemMessageHandledListener> kungFuDeathGrip = this;
-
-        ErrorResult rv;
-        mWakeLock->Unlock(rv);
-
-        if (mTimer) {
-            mTimer->Cancel();
-            mTimer = nullptr;
-        }
-    }
-
-    nsRefPtr<WakeLock> mWakeLock;
-    nsCOMPtr<nsITimer> mTimer;
-};
-
-StaticAutoPtr<LinkedList<SystemMessageHandledListener> >
-    SystemMessageHandledListener::sListeners;
-
-NS_IMPL_ISUPPORTS(SystemMessageHandledListener,
-                  nsITimerCallback)
-
-} // anonymous namespace
-
-void
-ContentParent::MaybeTakeCPUWakeLock(Element* aFrameElement)
-{
-    // Take the CPU wake lock on behalf of this processs if it's expecting a
-    // system message.  We'll release the CPU lock once the message is
-    // delivered, or after some period of time, which ever comes first.
-
-    nsCOMPtr<nsIMozBrowserFrame> browserFrame =
-        do_QueryInterface(aFrameElement);
-    if (!browserFrame ||
-        !browserFrame->GetIsExpectingSystemMessage()) {
-        return;
-    }
-
-    nsRefPtr<PowerManagerService> pms = PowerManagerService::GetInstance();
-    nsRefPtr<WakeLock> lock =
-        pms->NewWakeLockOnBehalfOfProcess(NS_LITERAL_STRING("cpu"), this);
-
-    // This object's Init() function keeps it alive.
-    nsRefPtr<SystemMessageHandledListener> listener =
-        new SystemMessageHandledListener();
-    listener->Init(lock);
-}
-
 bool
 ContentParent::SetPriorityAndCheckIsAlive(ProcessPriority aPriority)
 {
     ProcessPriorityManager::SetProcessPriority(this, aPriority);
 
     // Now that we've set this process's priority, check whether the process is
     // still alive.  Hopefully we've set the priority to FOREGROUND*, so the
     // process won't unexpectedly crash after this point!
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -328,22 +328,16 @@ private:
     void InitInternal(ProcessPriority aPriority,
                       bool aSetupOffMainThreadCompositing,
                       bool aSendRegisteredChrome);
 
     virtual ~ContentParent();
 
     void Init();
 
-    // If the frame element indicates that the child process is "critical" and
-    // has a pending system message, this function acquires the CPU wake lock on
-    // behalf of the child.  We'll release the lock when the system message is
-    // handled or after a timeout, whichever comes first.
-    void MaybeTakeCPUWakeLock(Element* aFrameElement);
-
     // Set the child process's priority and then check whether the child is
     // still alive.  Returns true if the process is still alive, and false
     // otherwise.  If you pass a FOREGROUND* priority here, it's (hopefully)
     // unlikely that the process will be killed after this point.
     bool SetPriorityAndCheckIsAlive(hal::ProcessPriority aPriority);
 
     // Transform a pre-allocated app process into a "real" app
     // process, for the specified manifest URL.
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -127,16 +127,22 @@ public:
   /**
    * This function implements ProcessPriorityManager::SetProcessPriority.
    */
   void SetProcessPriority(ContentParent* aContentParent,
                           ProcessPriority aPriority,
                           uint32_t aBackgroundLRU = 0);
 
   /**
+   * This function implements ProcessPriorityManager::ResetProcessPriority.
+   */
+  void ResetProcessPriority(ContentParent* aContentParent,
+                            bool aHandleSystemMessage);
+
+  /**
    * If a magic testing-only pref is set, notify the observer service on the
    * given topic with the given data.  This is used for testing
    */
   void FireTestOnlyObserverNotification(const char* aTopic,
                                         const nsACString& aData = EmptyCString());
 
   /**
    * Does some process, other than the one handled by aParticularManager, have
@@ -282,31 +288,34 @@ public:
                       uint32_t aBackgroundLRU = 0);
 
   void SetPriorityNow(ProcessPriority aPriority,
                       ProcessCPUPriority aCPUPriority,
                       uint32_t aBackgroundLRU = 0);
 
   void ShutDown();
 
+  void SetHandlesSystemMessage(bool aHandlesSystemMessage);
+
 private:
   void FireTestOnlyObserverNotification(
     const char* aTopic,
     const nsACString& aData = EmptyCString());
 
   void FireTestOnlyObserverNotification(
     const char* aTopic,
     const char* aData = nullptr);
 
   ContentParent* mContentParent;
   uint64_t mChildID;
   ProcessPriority mPriority;
   ProcessCPUPriority mCPUPriority;
   bool mHoldsCPUWakeLock;
   bool mHoldsHighPriorityWakeLock;
+  bool mHandlesSystemMessage;
 
   /**
    * Used to implement NameWithComma().
    */
   nsAutoCString mNameWithComma;
 
   nsCOMPtr<nsITimer> mResetPriorityTimer;
 };
@@ -492,16 +501,27 @@ ProcessPriorityManagerImpl::SetProcessPr
 {
   MOZ_ASSERT(aContentParent);
   nsRefPtr<ParticularProcessPriorityManager> pppm =
     GetParticularProcessPriorityManager(aContentParent);
   pppm->SetPriorityNow(aPriority, aBackgroundLRU);
 }
 
 void
+ProcessPriorityManagerImpl::ResetProcessPriority(ContentParent* aContentParent,
+                                                 bool aHandleSystemMessage)
+{
+  MOZ_ASSERT(aContentParent);
+  nsRefPtr<ParticularProcessPriorityManager> pppm =
+    GetParticularProcessPriorityManager(aContentParent);
+  pppm->SetHandlesSystemMessage(aHandleSystemMessage);
+  pppm->ResetPriority();
+}
+
+void
 ProcessPriorityManagerImpl::ObserveContentParentCreated(
   nsISupports* aContentParent)
 {
   // Do nothing; it's sufficient to get the PPPM.  But assign to nsRefPtr so we
   // don't leak the already_AddRefed object.
   nsCOMPtr<nsIContentParent> cp = do_QueryInterface(aContentParent);
   nsRefPtr<ParticularProcessPriorityManager> pppm =
     GetParticularProcessPriorityManager(cp->AsContentParent());
@@ -642,16 +662,17 @@ NS_IMPL_ISUPPORTS(ParticularProcessPrior
 ParticularProcessPriorityManager::ParticularProcessPriorityManager(
   ContentParent* aContentParent)
   : mContentParent(aContentParent)
   , mChildID(aContentParent->ChildID())
   , mPriority(PROCESS_PRIORITY_UNKNOWN)
   , mCPUPriority(PROCESS_CPU_PRIORITY_NORMAL)
   , mHoldsCPUWakeLock(false)
   , mHoldsHighPriorityWakeLock(false)
+  , mHandlesSystemMessage(false)
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   LOGP("Creating ParticularProcessPriorityManager.");
 }
 
 void
 ParticularProcessPriorityManager::Init()
 {
@@ -964,17 +985,17 @@ ProcessPriority
 ParticularProcessPriorityManager::CurrentPriority()
 {
   return mPriority;
 }
 
 ProcessPriority
 ParticularProcessPriorityManager::ComputePriority()
 {
-  if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
+  if ((mHandlesSystemMessage || mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
       HasAppType("critical")) {
     return PROCESS_PRIORITY_FOREGROUND_HIGH;
   }
 
   bool isVisible = false;
   const InfallibleTArray<PBrowserParent*>& browsers =
     mContentParent->ManagedPBrowserParent();
   for (uint32_t i = 0; i < browsers.Length(); i++) {
@@ -985,17 +1006,17 @@ ParticularProcessPriorityManager::Comput
   }
 
   if (isVisible) {
     return HasAppType("inputmethod") ?
       PROCESS_PRIORITY_FOREGROUND_KEYBOARD :
       PROCESS_PRIORITY_FOREGROUND;
   }
 
-  if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
+  if ((mHandlesSystemMessage || mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
       IsExpectingSystemMessage()) {
     return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
   }
 
   AudioChannelService* service = AudioChannelService::GetAudioChannelService();
   if (service->ProcessContentOrNormalChannelIsActive(ChildID())) {
     return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
   }
@@ -1133,16 +1154,22 @@ ParticularProcessPriorityManager::ShutDo
   if (mPriority == PROCESS_PRIORITY_BACKGROUND && !IsPreallocated()) {
     ProcessPriorityManager::RemoveFromBackgroundLRUPool(mContentParent);
   }
 
   mContentParent = nullptr;
 }
 
 void
+ParticularProcessPriorityManager::SetHandlesSystemMessage(bool aHandlesSystemMessage)
+{
+  mHandlesSystemMessage = aHandlesSystemMessage;
+}
+
+void
 ProcessPriorityManagerImpl::FireTestOnlyObserverNotification(
   const char* aTopic,
   const nsACString& aData /* = EmptyCString() */)
 {
   if (!Preferences::GetBool("dom.ipc.processPriorityManager.testMode")) {
     return;
   }
 
@@ -1469,16 +1496,29 @@ ProcessPriorityManager::SetProcessPriori
   ProcessPriorityManagerImpl* singleton =
     ProcessPriorityManagerImpl::GetSingleton();
   if (singleton) {
     singleton->SetProcessPriority(aContentParent, aPriority);
   }
 }
 
 /* static */ void
+ProcessPriorityManager::ResetProcessPriority(ContentParent* aContentParent,
+                                             bool aHandleSystemMessage)
+{
+  MOZ_ASSERT(aContentParent);
+
+  ProcessPriorityManagerImpl* singleton =
+    ProcessPriorityManagerImpl::GetSingleton();
+  if (singleton) {
+    singleton->ResetProcessPriority(aContentParent, aHandleSystemMessage);
+  }
+}
+
+/* static */ void
 ProcessPriorityManager::RemoveFromBackgroundLRUPool(
     ContentParent* aContentParent)
 {
   MOZ_ASSERT(aContentParent);
 
   BackgroundProcessLRUPool* singleton =
     BackgroundProcessLRUPool::Singleton();
   if (singleton) {
--- a/dom/ipc/ProcessPriorityManager.h
+++ b/dom/ipc/ProcessPriorityManager.h
@@ -55,16 +55,30 @@ public:
    *
    * Eventually whatever priority you set here can and probably will be
    * overwritten by the process priority manager.
    */
   static void SetProcessPriority(dom::ContentParent* aContentParent,
                                  hal::ProcessPriority aPriority);
 
   /**
+   * Reset the process priority of a given ContentParent's process in
+   * consideration of system message handling.
+   *
+   * Note that because this method takes a ContentParent*, you can only set the
+   * priority of your subprocesses.  In fact, because we don't support nested
+   * content processes (bug 761935), you can only call this method from the
+   * main process.
+   *
+   * The process priority manager will determine a new appropriate priority.
+   */
+  static void ResetProcessPriority(dom::ContentParent* aContentParent,
+                                   bool aHandleSystemMessage);
+
+  /**
    * Returns true iff this process's priority is FOREGROUND*.
    *
    * Note that because process priorities are set in the main process, it's
    * possible for this method to return a stale value.  So be careful about
    * what you use this for.
    */
   static bool CurrentProcessIsForeground();
 
--- a/dom/messages/SystemMessageInternal.js
+++ b/dom/messages/SystemMessageInternal.js
@@ -34,16 +34,26 @@ let kMaxPendingMessages;
 try {
   kMaxPendingMessages =
     Services.prefs.getIntPref("dom.messages.maxPendingMessages");
 } catch(e) {
   // getIntPref throws when the pref is not set.
   kMaxPendingMessages = 5;
 }
 
+//Limit the duration to hold the CPU wake lock.
+let kCpuWakeLockTimeoutSec;
+try {
+  kCpuWakeLockTimeoutSec =
+    Services.prefs.getIntPref("dom.ipc.systemMessageCPULockTimeoutSec");
+} catch (e) {
+  // getIntPref throws when the pref is not set.
+  kCpuWakeLockTimeoutSec = 30;
+}
+
 const kMessages =["SystemMessageManager:GetPendingMessages",
                   "SystemMessageManager:HasPendingMessages",
                   "SystemMessageManager:Register",
                   "SystemMessageManager:Unregister",
                   "SystemMessageManager:Message:Return:OK",
                   "SystemMessageManager:AskReadyToRegister",
                   "SystemMessageManager:HandleMessagesDone",
                   "child-process-shutdown"]
@@ -144,17 +154,17 @@ SystemMessageInternal.prototype = {
     // Set a watchdog to avoid locking the CPU wake lock too long,
     // because it'd exhaust the battery quickly which is very bad.
     // This could probably happen if the app failed to launch or
     // handle the system messages due to any unexpected reasons.
     cpuWakeLock.timer.initWithCallback(function timerCb() {
       debug("Releasing the CPU wake lock because the system messages " +
             "were not handled by its registered page before time out.");
       this._cancelCpuWakeLock(aPageKey);
-    }.bind(this), 30000, Ci.nsITimer.TYPE_ONE_SHOT);
+    }.bind(this), kCpuWakeLockTimeoutSec * 1000, Ci.nsITimer.TYPE_ONE_SHOT);
   },
 
   _releaseCpuWakeLock: function _releaseCpuWakeLock(aPageKey, aHandledCount) {
     let cpuWakeLock = this._cpuWakeLocks[aPageKey];
     if (cpuWakeLock) {
       cpuWakeLock.lockCount -= aHandledCount;
       if (cpuWakeLock.lockCount <= 0) {
         debug("Unlocking the CPU wake lock now that the system messages " +
--- a/dom/messages/SystemMessageManager.js
+++ b/dom/messages/SystemMessageManager.js
@@ -103,20 +103,18 @@ SystemMessageManager.prototype = {
                             handledCount: 1 });
 
     aDispatcher.isHandling = false;
 
     if (aDispatcher.messages.length > 0) {
       this._dispatchMessage(aType, aDispatcher, aDispatcher.messages.shift());
     } else {
       // No more messages that need to be handled, we can notify the
-      // ContentChild to release the CPU wake lock grabbed by the ContentParent
-      // (i.e. NewWakeLockOnBehalfOfProcess()) and reset the process's priority.
-      //
-      // TODO: Bug 874353 - Remove SystemMessageHandledListener in ContentParent
+      // ContentChild to propogate the event, so that the ContentParent can
+      // reset the process's priority.
       Services.obs.notifyObservers(/* aSubject */ null,
                                    "handle-system-messages-done",
                                    /* aData */ null);
     }
   },
 
   mozSetMessageHandler: function(aType, aHandler) {
     debug("set message handler for [" + aType + "] " + aHandler);
@@ -244,21 +242,18 @@ SystemMessageManager.prototype = {
       // messages.length|), so the parent can release the CPU wake lock it took
       // on our behalf.
       cpmm.sendAsyncMessage("SystemMessageManager:HandleMessagesDone",
                             { type: msg.type,
                               manifestURL: this._manifestURL,
                               pageURL: this._pageURL,
                               handledCount: messages.length });
 
-      // We also need to notify the ContentChild to release the CPU wake lock
-      // grabbed by the ContentParent (i.e. NewWakeLockOnBehalfOfProcess()) and
-      // reset the process's priority.
-      //
-      // TODO: Bug 874353 - Remove SystemMessageHandledListener in ContentParent
+      // We also need to notify the ContentChild to propogate the event, so that
+      // the ContentParent can reset the process's priority.
       Services.obs.notifyObservers(/* aSubject */ null,
                                    "handle-system-messages-done",
                                    /* aData */ null);
     }
   },
 
   // nsIDOMGlobalPropertyInitializer implementation.
   init: function(aWindow) {