tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

StorageActivityService.cpp (8557B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "StorageActivityService.h"
      8 
      9 #include "mozilla/BasePrincipal.h"
     10 #include "mozilla/SchedulerGroup.h"
     11 #include "mozilla/Services.h"
     12 #include "mozilla/StaticPtr.h"
     13 #include "mozilla/ipc/BackgroundChild.h"
     14 #include "mozilla/ipc/BackgroundUtils.h"
     15 #include "mozilla/ipc/PBackgroundChild.h"
     16 #include "mozilla/ipc/PBackgroundSharedTypes.h"
     17 #include "nsCOMPtr.h"
     18 #include "nsComponentManagerUtils.h"
     19 #include "nsIMutableArray.h"
     20 #include "nsIObserverService.h"
     21 #include "nsIPrincipal.h"
     22 #include "nsIUserIdleService.h"
     23 #include "nsSupportsPrimitives.h"
     24 #include "nsXPCOM.h"
     25 
     26 // This const is used to know when origin activities should be purged because
     27 // too old. This value should be in sync with what the UI needs.
     28 #define TIME_MAX_SECS 86400 /* 24 hours */
     29 
     30 namespace mozilla::dom {
     31 
     32 static StaticRefPtr<StorageActivityService> gStorageActivityService;
     33 static bool gStorageActivityShutdown = false;
     34 
     35 /* static */
     36 void StorageActivityService::SendActivity(nsIPrincipal* aPrincipal) {
     37  MOZ_ASSERT(NS_IsMainThread());
     38 
     39  if (!aPrincipal || BasePrincipal::Cast(aPrincipal)->Kind() !=
     40                         BasePrincipal::eContentPrincipal) {
     41    // Only content principals.
     42    return;
     43  }
     44 
     45  RefPtr<StorageActivityService> service = GetOrCreate();
     46  if (NS_WARN_IF(!service)) {
     47    return;
     48  }
     49 
     50  service->SendActivityInternal(aPrincipal);
     51 }
     52 
     53 /* static */
     54 void StorageActivityService::SendActivity(
     55    const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
     56  if (aPrincipalInfo.type() !=
     57      mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) {
     58    // only content principal.
     59    return;
     60  }
     61 
     62  RefPtr<Runnable> r = NS_NewRunnableFunction(
     63      "StorageActivityService::SendActivity", [aPrincipalInfo]() {
     64        MOZ_ASSERT(NS_IsMainThread());
     65 
     66        auto principalOrErr =
     67            mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo);
     68 
     69        if (principalOrErr.isOk()) {
     70          nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
     71          StorageActivityService::SendActivity(principal);
     72        } else {
     73          NS_WARNING(
     74              "Could not obtain principal from "
     75              "mozilla::ipc::PrincipalInfoToPrincipal");
     76        }
     77      });
     78 
     79  SchedulerGroup::Dispatch(r.forget());
     80 }
     81 
     82 /* static */
     83 void StorageActivityService::SendActivity(const nsACString& aOrigin) {
     84  MOZ_ASSERT(XRE_IsParentProcess());
     85 
     86  nsCString origin;
     87  origin.Assign(aOrigin);
     88 
     89  RefPtr<Runnable> r = NS_NewRunnableFunction(
     90      "StorageActivityService::SendActivity", [origin]() {
     91        MOZ_ASSERT(NS_IsMainThread());
     92 
     93        RefPtr<StorageActivityService> service = GetOrCreate();
     94        if (NS_WARN_IF(!service)) {
     95          return;
     96        }
     97 
     98        service->SendActivityInternal(origin);
     99      });
    100 
    101  if (NS_IsMainThread()) {
    102    (void)r->Run();
    103  } else {
    104    NS_DispatchToMainThread(r.forget());
    105  }
    106 }
    107 
    108 /* static */
    109 already_AddRefed<StorageActivityService> StorageActivityService::GetOrCreate() {
    110  MOZ_ASSERT(NS_IsMainThread());
    111 
    112  if (!gStorageActivityService && !gStorageActivityShutdown) {
    113    RefPtr<StorageActivityService> service = new StorageActivityService();
    114 
    115    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    116    if (NS_WARN_IF(!obs)) {
    117      return nullptr;
    118    }
    119 
    120    nsresult rv =
    121        obs->AddObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
    122    if (NS_WARN_IF(NS_FAILED(rv))) {
    123      return nullptr;
    124    }
    125 
    126    gStorageActivityService = service;
    127  }
    128 
    129  RefPtr<StorageActivityService> service = gStorageActivityService;
    130  return service.forget();
    131 }
    132 
    133 StorageActivityService::StorageActivityService() {
    134  MOZ_ASSERT(NS_IsMainThread());
    135 }
    136 
    137 StorageActivityService::~StorageActivityService() {
    138  MOZ_ASSERT(NS_IsMainThread());
    139 }
    140 
    141 void StorageActivityService::SendActivityInternal(nsIPrincipal* aPrincipal) {
    142  MOZ_ASSERT(NS_IsMainThread());
    143  MOZ_ASSERT(aPrincipal);
    144  MOZ_ASSERT(BasePrincipal::Cast(aPrincipal)->Kind() ==
    145             BasePrincipal::eContentPrincipal);
    146 
    147  if (!XRE_IsParentProcess()) {
    148    SendActivityToParent(aPrincipal);
    149    return;
    150  }
    151 
    152  nsAutoCString origin;
    153  nsresult rv = aPrincipal->GetOrigin(origin);
    154  if (NS_WARN_IF(NS_FAILED(rv))) {
    155    return;
    156  }
    157 
    158  SendActivityInternal(origin);
    159 }
    160 
    161 void StorageActivityService::SendActivityInternal(const nsACString& aOrigin) {
    162  MOZ_ASSERT(XRE_IsParentProcess());
    163 
    164  bool shouldAddObserver = mActivities.Count() == 0;
    165  mActivities.InsertOrUpdate(aOrigin, PR_Now());
    166 
    167  if (shouldAddObserver) {
    168    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    169    if (NS_WARN_IF(!obs)) {
    170      return;
    171    }
    172 
    173    obs->AddObserver(this, OBSERVER_TOPIC_IDLE_DAILY, true);
    174  }
    175 }
    176 
    177 void StorageActivityService::SendActivityToParent(nsIPrincipal* aPrincipal) {
    178  MOZ_ASSERT(NS_IsMainThread());
    179  MOZ_ASSERT(!XRE_IsParentProcess());
    180 
    181  ::mozilla::ipc::PBackgroundChild* actor =
    182      ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
    183  if (NS_WARN_IF(!actor)) {
    184    return;
    185  }
    186 
    187  mozilla::ipc::PrincipalInfo principalInfo;
    188  nsresult rv =
    189      mozilla::ipc::PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
    190  if (NS_WARN_IF(NS_FAILED(rv))) {
    191    return;
    192  }
    193 
    194  actor->SendStorageActivity(principalInfo);
    195 }
    196 
    197 NS_IMETHODIMP
    198 StorageActivityService::Observe(nsISupports* aSubject, const char* aTopic,
    199                                const char16_t* aData) {
    200  MOZ_ASSERT(NS_IsMainThread());
    201 
    202  if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE_DAILY)) {
    203    CleanUp();
    204    return NS_OK;
    205  }
    206 
    207  MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID));
    208 
    209  gStorageActivityShutdown = true;
    210  gStorageActivityService = nullptr;
    211  return NS_OK;
    212 }
    213 
    214 void StorageActivityService::CleanUp() {
    215  MOZ_ASSERT(NS_IsMainThread());
    216 
    217  uint64_t now = PR_Now();
    218 
    219  for (auto iter = mActivities.Iter(); !iter.Done(); iter.Next()) {
    220    if ((now - iter.UserData()) / PR_USEC_PER_SEC > TIME_MAX_SECS) {
    221      iter.Remove();
    222    }
    223  }
    224 
    225  // If no activities, remove the observer.
    226  if (mActivities.Count() == 0) {
    227    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    228    if (obs) {
    229      obs->RemoveObserver(this, OBSERVER_TOPIC_IDLE_DAILY);
    230    }
    231  }
    232 }
    233 
    234 NS_IMETHODIMP
    235 StorageActivityService::GetActiveOrigins(PRTime aFrom, PRTime aTo,
    236                                         nsIArray** aRetval) {
    237  uint64_t now = PR_Now();
    238  if (((now - aFrom) / PR_USEC_PER_SEC) > TIME_MAX_SECS || aFrom >= aTo) {
    239    return NS_ERROR_INVALID_ARG;
    240  }
    241 
    242  // Remove expired entries first.
    243  CleanUp();
    244 
    245  nsresult rv = NS_OK;
    246  nsCOMPtr<nsIMutableArray> devices =
    247      do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
    248  if (NS_WARN_IF(NS_FAILED(rv))) {
    249    return rv;
    250  }
    251 
    252  for (const auto& activityEntry : mActivities) {
    253    if (activityEntry.GetData() >= aFrom && activityEntry.GetData() <= aTo) {
    254      RefPtr<BasePrincipal> principal =
    255          BasePrincipal::CreateContentPrincipal(activityEntry.GetKey());
    256      MOZ_ASSERT(principal);
    257 
    258      rv = devices->AppendElement(principal);
    259      if (NS_WARN_IF(NS_FAILED(rv))) {
    260        return rv;
    261      }
    262    }
    263  }
    264 
    265  devices.forget(aRetval);
    266  return NS_OK;
    267 }
    268 
    269 NS_IMETHODIMP
    270 StorageActivityService::MoveOriginInTime(nsIPrincipal* aPrincipal,
    271                                         PRTime aWhen) {
    272  if (!XRE_IsParentProcess()) {
    273    return NS_ERROR_FAILURE;
    274  }
    275 
    276  nsAutoCString origin;
    277  nsresult rv = aPrincipal->GetOrigin(origin);
    278  if (NS_WARN_IF(NS_FAILED(rv))) {
    279    return rv;
    280  }
    281 
    282  mActivities.InsertOrUpdate(origin, aWhen / PR_USEC_PER_SEC);
    283  return NS_OK;
    284 }
    285 
    286 NS_IMETHODIMP
    287 StorageActivityService::TestOnlyReset() {
    288  const bool shouldRemoveObserver = mActivities.Count() > 0;
    289  mActivities.Clear();
    290  if (shouldRemoveObserver) {
    291    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    292    if (obs) {
    293      obs->RemoveObserver(this, OBSERVER_TOPIC_IDLE_DAILY);
    294    }
    295  }
    296  return NS_OK;
    297 }
    298 
    299 NS_INTERFACE_MAP_BEGIN(StorageActivityService)
    300  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStorageActivityService)
    301  NS_INTERFACE_MAP_ENTRY(nsIStorageActivityService)
    302  NS_INTERFACE_MAP_ENTRY(nsIObserver)
    303  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    304 NS_INTERFACE_MAP_END
    305 
    306 NS_IMPL_ADDREF(StorageActivityService)
    307 NS_IMPL_RELEASE(StorageActivityService)
    308 
    309 }  // namespace mozilla::dom