forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			318 lines
		
	
	
	
		
			7.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			318 lines
		
	
	
	
		
			7.7 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 "StorageActivityService.h"
 | |
| 
 | |
| #include "mozilla/ipc/BackgroundUtils.h"
 | |
| #include "mozilla/StaticPtr.h"
 | |
| #include "nsIMutableArray.h"
 | |
| #include "nsSupportsPrimitives.h"
 | |
| #include "nsXPCOM.h"
 | |
| 
 | |
| // This const is used to know when origin activities should be purged because
 | |
| // too old. This value should be in sync with what the UI needs.
 | |
| #define TIME_MAX_SECS 86400 /* 24 hours */
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace dom {
 | |
| 
 | |
| static StaticRefPtr<StorageActivityService> gStorageActivityService;
 | |
| static bool gStorageActivityShutdown = false;
 | |
| 
 | |
| /* static */ void
 | |
| StorageActivityService::SendActivity(nsIPrincipal* aPrincipal)
 | |
| {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   if (!aPrincipal ||
 | |
|       BasePrincipal::Cast(aPrincipal)->Kind() != BasePrincipal::eCodebasePrincipal) {
 | |
|     // Only codebase principals.
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   RefPtr<StorageActivityService> service = GetOrCreate();
 | |
|   if (NS_WARN_IF(!service)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   service->SendActivityInternal(aPrincipal);
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| StorageActivityService::SendActivity(const mozilla::ipc::PrincipalInfo& aPrincipalInfo)
 | |
| {
 | |
|   if (aPrincipalInfo.type() !=
 | |
|       mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) {
 | |
|     // only content principal.
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   RefPtr<Runnable> r = NS_NewRunnableFunction(
 | |
|     "StorageActivityService::SendActivity",
 | |
|     [aPrincipalInfo] () {
 | |
|       MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|       nsCOMPtr<nsIPrincipal> principal =
 | |
|         mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo);
 | |
| 
 | |
|       StorageActivityService::SendActivity(principal);
 | |
|     });
 | |
| 
 | |
|   SystemGroup::Dispatch(TaskCategory::Other, r.forget());
 | |
| }
 | |
| 
 | |
| /* static */ void
 | |
| StorageActivityService::SendActivity(const nsACString& aOrigin)
 | |
| {
 | |
|   MOZ_ASSERT(XRE_IsParentProcess());
 | |
| 
 | |
|   nsCString origin;
 | |
|   origin.Assign(aOrigin);
 | |
| 
 | |
|   RefPtr<Runnable> r = NS_NewRunnableFunction(
 | |
|     "StorageActivityService::SendActivity",
 | |
|     [origin] () {
 | |
|       MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|       RefPtr<StorageActivityService> service = GetOrCreate();
 | |
|       if (NS_WARN_IF(!service)) {
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       service->SendActivityInternal(origin);
 | |
|     });
 | |
| 
 | |
|   if (NS_IsMainThread()) {
 | |
|     Unused << r->Run();
 | |
|   } else {
 | |
|     SystemGroup::Dispatch(TaskCategory::Other, r.forget());
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */ already_AddRefed<StorageActivityService>
 | |
| StorageActivityService::GetOrCreate()
 | |
| {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   if (!gStorageActivityService && !gStorageActivityShutdown) {
 | |
|     RefPtr<StorageActivityService> service = new StorageActivityService();
 | |
| 
 | |
|     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 | |
|     if (NS_WARN_IF(!obs)) {
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     nsresult rv = obs->AddObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
 | |
|     if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     gStorageActivityService = service;
 | |
|   }
 | |
| 
 | |
|   RefPtr<StorageActivityService> service = gStorageActivityService;
 | |
|   return service.forget();
 | |
| }
 | |
| 
 | |
| StorageActivityService::StorageActivityService()
 | |
| {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| }
 | |
| 
 | |
| StorageActivityService::~StorageActivityService()
 | |
| {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(!mTimer);
 | |
| }
 | |
| 
 | |
| void
 | |
| StorageActivityService::SendActivityInternal(nsIPrincipal* aPrincipal)
 | |
| {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(aPrincipal);
 | |
|   MOZ_ASSERT(BasePrincipal::Cast(aPrincipal)->Kind() == BasePrincipal::eCodebasePrincipal);
 | |
| 
 | |
|   if (!XRE_IsParentProcess()) {
 | |
|     SendActivityToParent(aPrincipal);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsAutoCString origin;
 | |
|   nsresult rv = aPrincipal->GetOrigin(origin);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   SendActivityInternal(origin);
 | |
| }
 | |
| 
 | |
| void
 | |
| StorageActivityService::SendActivityInternal(const nsACString& aOrigin)
 | |
| {
 | |
|   MOZ_ASSERT(XRE_IsParentProcess());
 | |
| 
 | |
|   mActivities.Put(aOrigin, PR_Now());
 | |
|   MaybeStartTimer();
 | |
| }
 | |
| 
 | |
| void
 | |
| StorageActivityService::SendActivityToParent(nsIPrincipal* aPrincipal)
 | |
| {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(!XRE_IsParentProcess());
 | |
| 
 | |
|   PBackgroundChild* actor = BackgroundChild::GetOrCreateForCurrentThread();
 | |
|   if (NS_WARN_IF(!actor)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   mozilla::ipc::PrincipalInfo principalInfo;
 | |
|   nsresult rv =
 | |
|     mozilla::ipc::PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   actor->SendStorageActivity(principalInfo);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| StorageActivityService::Observe(nsISupports* aSubject, const char* aTopic,
 | |
|                                 const char16_t* aData)
 | |
| {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID));
 | |
| 
 | |
|   MaybeStopTimer();
 | |
| 
 | |
|   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 | |
|   if (obs) {
 | |
|     obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
 | |
|   }
 | |
| 
 | |
|   gStorageActivityShutdown = true;
 | |
|   gStorageActivityService = nullptr;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| StorageActivityService::MaybeStartTimer()
 | |
| {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   if (!mTimer) {
 | |
|     mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
 | |
|     mTimer->InitWithCallback(this,
 | |
|                              1000 * 5 * 60 /* any 5 minutes */,
 | |
|                              nsITimer::TYPE_REPEATING_SLACK);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| StorageActivityService::MaybeStopTimer()
 | |
| {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   if (mTimer) {
 | |
|     mTimer->Cancel();
 | |
|     mTimer = nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| StorageActivityService::Notify(nsITimer* aTimer)
 | |
| {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(mTimer == aTimer);
 | |
| 
 | |
|   uint64_t now = PR_Now();
 | |
| 
 | |
|   for (auto iter = mActivities.Iter(); !iter.Done(); iter.Next()) {
 | |
|     if ((now - iter.UserData()) / PR_USEC_PER_SEC > TIME_MAX_SECS) {
 | |
|       iter.Remove();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If no activities, let's stop the timer.
 | |
|   if (mActivities.Count() == 0) {
 | |
|     MaybeStopTimer();
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| StorageActivityService::GetActiveOrigins(PRTime aFrom, PRTime aTo,
 | |
|                                          nsIArray** aRetval)
 | |
| {
 | |
|   uint64_t now = PR_Now();
 | |
|   if (((now - aFrom) / PR_USEC_PER_SEC) > TIME_MAX_SECS ||
 | |
|        aFrom >= aTo) {
 | |
|     return NS_ERROR_RANGE_ERR;
 | |
|   }
 | |
| 
 | |
|   nsresult rv = NS_OK;
 | |
|   nsCOMPtr<nsIMutableArray> devices =
 | |
|     do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   for (auto iter = mActivities.Iter(); !iter.Done(); iter.Next()) {
 | |
|     if (iter.UserData() >= aFrom && iter.UserData() <= aTo) {
 | |
|       RefPtr<BasePrincipal> principal =
 | |
|         BasePrincipal::CreateCodebasePrincipal(iter.Key());
 | |
|       MOZ_ASSERT(principal);
 | |
| 
 | |
|       rv = devices->AppendElement(principal);
 | |
|       if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|         return rv;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   devices.forget(aRetval);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| StorageActivityService::MoveOriginInTime(nsIPrincipal* aPrincipal,
 | |
|                                          PRTime aWhen)
 | |
| {
 | |
|   if (!XRE_IsParentProcess()) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   nsAutoCString origin;
 | |
|   nsresult rv = aPrincipal->GetOrigin(origin);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   mActivities.Put(origin, aWhen / PR_USEC_PER_SEC);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| StorageActivityService::TestOnlyReset()
 | |
| {
 | |
|   mActivities.Clear();
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN(StorageActivityService)
 | |
|   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStorageActivityService)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIStorageActivityService)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIObserver)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 | |
| NS_INTERFACE_MAP_END
 | |
| 
 | |
| NS_IMPL_ADDREF(StorageActivityService)
 | |
| NS_IMPL_RELEASE(StorageActivityService)
 | |
| 
 | |
| } // dom namespace
 | |
| } // mozilla namespace
 | 
