forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			282 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			282 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* 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 "Hal.h"
 | |
| #include "mozilla/HalWakeLock.h"
 | |
| #include "mozilla/Services.h"
 | |
| #include "mozilla/StaticPtr.h"
 | |
| #include "mozilla/dom/ContentParent.h"
 | |
| #include "nsAutoPtr.h"
 | |
| #include "nsClassHashtable.h"
 | |
| #include "nsDataHashtable.h"
 | |
| #include "nsHashKeys.h"
 | |
| #include "nsIPropertyBag2.h"
 | |
| #include "nsIObserverService.h"
 | |
| 
 | |
| using namespace mozilla;
 | |
| using namespace mozilla::hal;
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| struct LockCount {
 | |
|   LockCount()
 | |
|     : numLocks(0)
 | |
|     , numHidden(0)
 | |
|   {}
 | |
|   uint32_t numLocks;
 | |
|   uint32_t numHidden;
 | |
|   nsTArray<uint64_t> processes;
 | |
| };
 | |
| 
 | |
| typedef nsDataHashtable<nsUint64HashKey, LockCount> ProcessLockTable;
 | |
| typedef nsClassHashtable<nsStringHashKey, ProcessLockTable> LockTable;
 | |
| 
 | |
| int sActiveListeners = 0;
 | |
| StaticAutoPtr<LockTable> sLockTable;
 | |
| bool sInitialized = false;
 | |
| bool sIsShuttingDown = false;
 | |
| 
 | |
| WakeLockInformation
 | |
| WakeLockInfoFromLockCount(const nsAString& aTopic, const LockCount& aLockCount)
 | |
| {
 | |
|   // TODO: Once we abandon b2g18, we can switch this to use the
 | |
|   // WakeLockInformation constructor, which is better because it doesn't let us
 | |
|   // forget to assign a param.  For now we have to do it this way, because
 | |
|   // b2g18 doesn't have the nsTArray <--> InfallibleTArray conversion (bug
 | |
|   // 819791).
 | |
| 
 | |
|   WakeLockInformation info;
 | |
|   info.topic() = aTopic;
 | |
|   info.numLocks() = aLockCount.numLocks;
 | |
|   info.numHidden() = aLockCount.numHidden;
 | |
|   info.lockingProcesses().AppendElements(aLockCount.processes);
 | |
|   return info;
 | |
| }
 | |
| 
 | |
| static void
 | |
| CountWakeLocks(ProcessLockTable* aTable, LockCount* aTotalCount)
 | |
| {
 | |
|   for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
 | |
|     const uint64_t& key = iter.Key();
 | |
|     LockCount count = iter.UserData();
 | |
| 
 | |
|     aTotalCount->numLocks += count.numLocks;
 | |
|     aTotalCount->numHidden += count.numHidden;
 | |
| 
 | |
|     // This is linear in the number of processes, but that should be small.
 | |
|     if (!aTotalCount->processes.Contains(key)) {
 | |
|       aTotalCount->processes.AppendElement(key);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| class ClearHashtableOnShutdown final : public nsIObserver {
 | |
|   ~ClearHashtableOnShutdown() {}
 | |
| public:
 | |
|   NS_DECL_ISUPPORTS
 | |
|   NS_DECL_NSIOBSERVER
 | |
| };
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(ClearHashtableOnShutdown, nsIObserver)
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| ClearHashtableOnShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
 | |
| {
 | |
|   MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
 | |
| 
 | |
|   sIsShuttingDown = true;
 | |
|   sLockTable = nullptr;
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| class CleanupOnContentShutdown final : public nsIObserver {
 | |
|   ~CleanupOnContentShutdown() {}
 | |
| public:
 | |
|   NS_DECL_ISUPPORTS
 | |
|   NS_DECL_NSIOBSERVER
 | |
| };
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(CleanupOnContentShutdown, nsIObserver)
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| CleanupOnContentShutdown::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* data)
 | |
| {
 | |
|   MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
 | |
| 
 | |
|   if (sIsShuttingDown) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   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)) {
 | |
|     for (auto iter = sLockTable->Iter(); !iter.Done(); iter.Next()) {
 | |
|       nsAutoPtr<ProcessLockTable>& table = iter.Data();
 | |
| 
 | |
|       if (table->Get(childID, nullptr)) {
 | |
|         table->Remove(childID);
 | |
| 
 | |
|         LockCount totalCount;
 | |
|         CountWakeLocks(table, &totalCount);
 | |
| 
 | |
|         if (sActiveListeners) {
 | |
|           NotifyWakeLockChange(WakeLockInfoFromLockCount(iter.Key(),
 | |
|                                                          totalCount));
 | |
|         }
 | |
| 
 | |
|         if (totalCount.numLocks == 0) {
 | |
|           iter.Remove();
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     NS_WARNING("ipc:content-shutdown message without childID property");
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| Init()
 | |
| {
 | |
|   sLockTable = new LockTable();
 | |
|   sInitialized = true;
 | |
| 
 | |
|   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 | |
|   if (obs) {
 | |
|     obs->AddObserver(new ClearHashtableOnShutdown(), "xpcom-shutdown", false);
 | |
|     obs->AddObserver(new CleanupOnContentShutdown(), "ipc:content-shutdown", false);
 | |
|   }
 | |
| }
 | |
| 
 | |
| } // namespace
 | |
| 
 | |
| 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;
 | |
|   }
 | |
| }
 | |
| 
 | |
| } // namespace hal
 | |
| 
 | |
| namespace hal_impl {
 | |
| 
 | |
| void
 | |
| EnableWakeLockNotifications()
 | |
| {
 | |
|   sActiveListeners++;
 | |
| }
 | |
| 
 | |
| void
 | |
| DisableWakeLockNotifications()
 | |
| {
 | |
|   sActiveListeners--;
 | |
| }
 | |
| 
 | |
| void
 | |
| ModifyWakeLock(const nsAString& aTopic,
 | |
|                hal::WakeLockControl aLockAdjust,
 | |
|                hal::WakeLockControl aHiddenAdjust,
 | |
|                uint64_t aProcessID)
 | |
| {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(aProcessID != CONTENT_PROCESS_ID_UNKNOWN);
 | |
| 
 | |
|   if (sIsShuttingDown) {
 | |
|     return;
 | |
|   }
 | |
|   if (!sInitialized) {
 | |
|     Init();
 | |
|   }
 | |
| 
 | |
|   ProcessLockTable* table = sLockTable->Get(aTopic);
 | |
|   LockCount processCount;
 | |
|   LockCount totalCount;
 | |
|   if (!table) {
 | |
|     table = new ProcessLockTable();
 | |
|     sLockTable->Put(aTopic, table);
 | |
|   } else {
 | |
|     table->Get(aProcessID, &processCount);
 | |
|     CountWakeLocks(table, &totalCount);
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
 | |
|   MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0);
 | |
|   MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0);
 | |
|   MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden);
 | |
|   MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0);
 | |
|   MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
 | |
| 
 | |
|   WakeLockState oldState = ComputeWakeLockState(totalCount.numLocks, totalCount.numHidden);
 | |
|   bool processWasLocked = processCount.numLocks > 0;
 | |
| 
 | |
|   processCount.numLocks += aLockAdjust;
 | |
|   processCount.numHidden += aHiddenAdjust;
 | |
| 
 | |
|   totalCount.numLocks += aLockAdjust;
 | |
|   totalCount.numHidden += aHiddenAdjust;
 | |
| 
 | |
|   if (processCount.numLocks) {
 | |
|     table->Put(aProcessID, processCount);
 | |
|   } else {
 | |
|     table->Remove(aProcessID);
 | |
|   }
 | |
|   if (!totalCount.numLocks) {
 | |
|     sLockTable->Remove(aTopic);
 | |
|   }
 | |
| 
 | |
|   if (sActiveListeners &&
 | |
|       (oldState != ComputeWakeLockState(totalCount.numLocks,
 | |
|                                         totalCount.numHidden) ||
 | |
|        processWasLocked != (processCount.numLocks > 0))) {
 | |
| 
 | |
|     WakeLockInformation info;
 | |
|     hal::GetWakeLockInfo(aTopic, &info);
 | |
|     NotifyWakeLockChange(info);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| GetWakeLockInfo(const nsAString& aTopic, WakeLockInformation* aWakeLockInfo)
 | |
| {
 | |
|   if (sIsShuttingDown) {
 | |
|     NS_WARNING("You don't want to get wake lock information during xpcom-shutdown!");
 | |
|     *aWakeLockInfo = WakeLockInformation();
 | |
|     return;
 | |
|   }
 | |
|   if (!sInitialized) {
 | |
|     Init();
 | |
|   }
 | |
| 
 | |
|   ProcessLockTable* table = sLockTable->Get(aTopic);
 | |
|   if (!table) {
 | |
|     *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
 | |
|     return;
 | |
|   }
 | |
|   LockCount totalCount;
 | |
|   CountWakeLocks(table, &totalCount);
 | |
|   *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
 | |
| }
 | |
| 
 | |
| } // namespace hal_impl
 | |
| } // namespace mozilla
 | 
