hal/HalWakeLock.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     4  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/Hal.h"
     7 #include "mozilla/HalWakeLock.h"
     8 #include "mozilla/Services.h"
     9 #include "mozilla/StaticPtr.h"
    10 #include "mozilla/dom/ContentParent.h"
    11 #include "nsClassHashtable.h"
    12 #include "nsDataHashtable.h"
    13 #include "nsHashKeys.h"
    14 #include "nsIPropertyBag2.h"
    15 #include "nsIObserverService.h"
    17 using namespace mozilla;
    18 using namespace mozilla::hal;
    20 namespace {
    22 struct LockCount {
    23   LockCount()
    24     : numLocks(0)
    25     , numHidden(0)
    26   {}
    27   uint32_t numLocks;
    28   uint32_t numHidden;
    29   nsTArray<uint64_t> processes;
    30 };
    32 typedef nsDataHashtable<nsUint64HashKey, LockCount> ProcessLockTable;
    33 typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
    35 int sActiveListeners = 0;
    36 StaticAutoPtr<LockTable> sLockTable;
    37 bool sInitialized = false;
    38 bool sIsShuttingDown = false;
    40 WakeLockInformation
    41 WakeLockInfoFromLockCount(const nsAString& aTopic, const LockCount& aLockCount)
    42 {
    43   // TODO: Once we abandon b2g18, we can switch this to use the
    44   // WakeLockInformation constructor, which is better because it doesn't let us
    45   // forget to assign a param.  For now we have to do it this way, because
    46   // b2g18 doesn't have the nsTArray <--> InfallibleTArray conversion (bug
    47   // 819791).
    49   WakeLockInformation info;
    50   info.topic() = aTopic;
    51   info.numLocks() = aLockCount.numLocks;
    52   info.numHidden() = aLockCount.numHidden;
    53   info.lockingProcesses().AppendElements(aLockCount.processes);
    54   return info;
    55 }
    57 PLDHashOperator
    58 CountWakeLocks(const uint64_t& aKey, LockCount aCount, void* aUserArg)
    59 {
    60   MOZ_ASSERT(aUserArg);
    62   LockCount* totalCount = static_cast<LockCount*>(aUserArg);
    63   totalCount->numLocks += aCount.numLocks;
    64   totalCount->numHidden += aCount.numHidden;
    66   // This is linear in the number of processes, but that should be small.
    67   if (!totalCount->processes.Contains(aKey)) {
    68     totalCount->processes.AppendElement(aKey);
    69   }
    71   return PL_DHASH_NEXT;
    72 }
    74 static PLDHashOperator
    75 RemoveChildFromList(const nsAString& aKey, nsAutoPtr<ProcessLockTable>& aTable,
    76                     void* aUserArg)
    77 {
    78   MOZ_ASSERT(aUserArg);
    80   PLDHashOperator op = PL_DHASH_NEXT;
    81   uint64_t childID = *static_cast<uint64_t*>(aUserArg);
    82   if (aTable->Get(childID, nullptr)) {
    83     aTable->Remove(childID);
    85     LockCount totalCount;
    86     aTable->EnumerateRead(CountWakeLocks, &totalCount);
    87     if (!totalCount.numLocks) {
    88       op = PL_DHASH_REMOVE;
    89     }
    91     if (sActiveListeners) {
    92       NotifyWakeLockChange(WakeLockInfoFromLockCount(aKey, totalCount));
    93     }
    94   }
    96   return op;
    97 }
    99 class ClearHashtableOnShutdown MOZ_FINAL : public nsIObserver {
   100 public:
   101   NS_DECL_ISUPPORTS
   102   NS_DECL_NSIOBSERVER
   103 };
   105 NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown, nsIObserver)
   107 NS_IMETHODIMP
   108 ClearHashtableOnShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
   109 {
   110   MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
   112   sIsShuttingDown = true;
   113   sLockTable = nullptr;
   115   return NS_OK;
   116 }
   118 class CleanupOnContentShutdown MOZ_FINAL : public nsIObserver {
   119 public:
   120   NS_DECL_ISUPPORTS
   121   NS_DECL_NSIOBSERVER
   122 };
   124 NS_IMPL_ISUPPORTS(CleanupOnContentShutdown, nsIObserver)
   126 NS_IMETHODIMP
   127 CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
   128 {
   129   MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
   131   if (sIsShuttingDown) {
   132     return NS_OK;
   133   }
   135   nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
   136   if (!props) {
   137     NS_WARNING("ipc:content-shutdown message without property bag as subject");
   138     return NS_OK;
   139   }
   141   uint64_t childID = 0;
   142   nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
   143                                            &childID);
   144   if (NS_SUCCEEDED(rv)) {
   145     sLockTable->Enumerate(RemoveChildFromList, &childID);
   146   } else {
   147     NS_WARNING("ipc:content-shutdown message without childID property");
   148   }
   149   return NS_OK;
   150 }
   152 void
   153 Init()
   154 {
   155   sLockTable = new LockTable();
   156   sInitialized = true;
   158   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   159   if (obs) {
   160     obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
   161     obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown", false);
   162   }
   163 }
   165 } // anonymous namespace
   167 namespace mozilla {
   169 namespace hal {
   171 WakeLockState
   172 ComputeWakeLockState(int aNumLocks, int aNumHidden)
   173 {
   174   if (aNumLocks == 0) {
   175     return WAKE_LOCK_STATE_UNLOCKED;
   176   } else if (aNumLocks == aNumHidden) {
   177     return WAKE_LOCK_STATE_HIDDEN;
   178   } else {
   179     return WAKE_LOCK_STATE_VISIBLE;
   180   }
   181 }
   183 } // namespace hal
   185 namespace hal_impl {
   187 void
   188 EnableWakeLockNotifications()
   189 {
   190   sActiveListeners++;
   191 }
   193 void
   194 DisableWakeLockNotifications()
   195 {
   196   sActiveListeners--;
   197 }
   199 void
   200 ModifyWakeLock(const nsAString& aTopic,
   201                hal::WakeLockControl aLockAdjust,
   202                hal::WakeLockControl aHiddenAdjust,
   203                uint64_t aProcessID)
   204 {
   205   MOZ_ASSERT(NS_IsMainThread());
   206   MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN);
   208   if (sIsShuttingDown) {
   209     return;
   210   }
   211   if (!sInitialized) {
   212     Init();
   213   }
   215   ProcessLockTable* table = sLockTable->Get(aTopic);
   216   LockCount processCount;
   217   LockCount totalCount;
   218   if (!table) {
   219     table = new ProcessLockTable();
   220     sLockTable->Put(aTopic, table);
   221   } else {
   222     table->Get(aProcessID, &processCount);
   223     table->EnumerateRead(CountWakeLocks, &totalCount);
   224   }
   226   MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
   227   MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0);
   228   MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0);
   229   MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden);
   230   MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0);
   231   MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
   233   WakeLockState oldState = ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
   234   bool processWasLocked = processCount.numLocks > 0;
   236   processCount.numLocks += aLockAdjust;
   237   processCount.numHidden += aHiddenAdjust;
   239   totalCount.numLocks += aLockAdjust;
   240   totalCount.numHidden += aHiddenAdjust;
   242   if (processCount.numLocks) {
   243     table->Put(aProcessID, processCount);
   244   } else {
   245     table->Remove(aProcessID);
   246   }
   247   if (!totalCount.numLocks) {
   248     sLockTable->Remove(aTopic);
   249   }
   251   if (sActiveListeners &&
   252       (oldState != ComputeWakeLockState(totalCount.numLocks,
   253                                         totalCount.numHidden) ||
   254        processWasLocked != (processCount.numLocks > 0))) {
   256     WakeLockInformation info;
   257     hal::GetWakeLockInfo(aTopic, &info);
   258     NotifyWakeLockChange(info);
   259   }
   260 }
   262 void
   263 GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
   264 {
   265   if (sIsShuttingDown) {
   266     NS_WARNING("You don't want to get wake lock information during xpcom-shutdown!");
   267     *aWakeLockInfo = WakeLockInformation();
   268     return;
   269   }
   270   if (!sInitialized) {
   271     Init();
   272   }
   274   ProcessLockTable* table = sLockTable->Get(aTopic);
   275   if (!table) {
   276     *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
   277     return;
   278   }
   279   LockCount totalCount;
   280   table->EnumerateRead(CountWakeLocks, &totalCount);
   281   *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
   282 }
   284 } // hal_impl
   285 } // mozilla

mercurial