tor-browser

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

LSDatabase.cpp (10346B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "LSDatabase.h"
      8 
      9 // Local includes
     10 #include "ActorsChild.h"
     11 #include "LSObject.h"
     12 #include "LSSnapshot.h"
     13 
     14 // Global includes
     15 #include <cstring>
     16 #include <new>
     17 
     18 #include "MainThreadUtils.h"
     19 #include "mozilla/RefPtr.h"
     20 #include "mozilla/Services.h"
     21 #include "mozilla/StaticPtr.h"
     22 #include "mozilla/dom/PBackgroundLSDatabase.h"
     23 #include "nsBaseHashtable.h"
     24 #include "nsCOMPtr.h"
     25 #include "nsDebug.h"
     26 #include "nsError.h"
     27 #include "nsHashKeys.h"
     28 #include "nsIObserver.h"
     29 #include "nsIObserverService.h"
     30 #include "nsString.h"
     31 #include "nsTArray.h"
     32 #include "nsTHashMap.h"
     33 #include "nscore.h"
     34 
     35 namespace mozilla::dom {
     36 
     37 namespace {
     38 
     39 #define XPCOM_SHUTDOWN_OBSERVER_TOPIC "xpcom-shutdown"
     40 
     41 using LSDatabaseHashtable = nsTHashMap<nsCStringHashKey, LSDatabase*>;
     42 
     43 StaticAutoPtr<LSDatabaseHashtable> gLSDatabases;
     44 
     45 }  // namespace
     46 
     47 StaticRefPtr<LSDatabase::Observer> LSDatabase::sObserver;
     48 
     49 class LSDatabase::Observer final : public nsIObserver {
     50  bool mInvalidated;
     51 
     52 public:
     53  Observer() : mInvalidated(false) { MOZ_ASSERT(NS_IsMainThread()); }
     54 
     55  void Invalidate() { mInvalidated = true; }
     56 
     57 private:
     58  ~Observer() { MOZ_ASSERT(NS_IsMainThread()); }
     59 
     60  NS_DECL_ISUPPORTS
     61  NS_DECL_NSIOBSERVER
     62 };
     63 
     64 LSDatabase::LSDatabase(const nsACString& aOrigin)
     65    : mActor(nullptr),
     66      mSnapshot(nullptr),
     67      mOrigin(aOrigin),
     68      mAllowedToClose(false),
     69      mRequestedAllowToClose(false) {
     70  AssertIsOnOwningThread();
     71 
     72  if (!gLSDatabases) {
     73    gLSDatabases = new LSDatabaseHashtable();
     74 
     75    MOZ_ASSERT(!sObserver);
     76 
     77    sObserver = new Observer();
     78 
     79    nsCOMPtr<nsIObserverService> obsSvc =
     80        mozilla::services::GetObserverService();
     81    MOZ_ASSERT(obsSvc);
     82 
     83    MOZ_ALWAYS_SUCCEEDS(
     84        obsSvc->AddObserver(sObserver, XPCOM_SHUTDOWN_OBSERVER_TOPIC, false));
     85  }
     86 
     87  MOZ_ASSERT(!gLSDatabases->Contains(mOrigin));
     88  gLSDatabases->InsertOrUpdate(mOrigin, this);
     89 }
     90 
     91 LSDatabase::~LSDatabase() {
     92  AssertIsOnOwningThread();
     93  MOZ_ASSERT(!mSnapshot);
     94 
     95  if (!mAllowedToClose) {
     96    AllowToClose();
     97  }
     98 
     99  if (mActor) {
    100    mActor->Shutdown();
    101    MOZ_ASSERT(!mActor, "Shutdown should have cleared!");
    102  }
    103 }
    104 
    105 // static
    106 LSDatabase* LSDatabase::Get(const nsACString& aOrigin) {
    107  return gLSDatabases ? gLSDatabases->Get(aOrigin) : nullptr;
    108 }
    109 
    110 void LSDatabase::SetActor(LSDatabaseChild* aActor) {
    111  AssertIsOnOwningThread();
    112  MOZ_ASSERT(aActor);
    113  MOZ_ASSERT(!mActor);
    114 
    115  mActor = aActor;
    116 }
    117 
    118 void LSDatabase::RequestAllowToClose() {
    119  AssertIsOnOwningThread();
    120 
    121  if (mRequestedAllowToClose) {
    122    return;
    123  }
    124 
    125  mRequestedAllowToClose = true;
    126 
    127  if (mSnapshot) {
    128    mSnapshot->MarkDirty();
    129  } else {
    130    AllowToClose();
    131  }
    132 }
    133 
    134 void LSDatabase::NoteFinishedSnapshot(LSSnapshot* aSnapshot) {
    135  AssertIsOnOwningThread();
    136  MOZ_ASSERT(aSnapshot == mSnapshot);
    137 
    138  mSnapshot = nullptr;
    139 
    140  if (mRequestedAllowToClose) {
    141    AllowToClose();
    142  }
    143 }
    144 
    145 // All these methods assert `!mAllowedToClose` because they shoudn't be called
    146 // if the database is being closed. Callers should first check the state by
    147 // calling `IsAlloweToClose` and eventually obtain a new database.
    148 
    149 nsresult LSDatabase::GetLength(LSObject* aObject, uint32_t* aResult) {
    150  AssertIsOnOwningThread();
    151  MOZ_ASSERT(aObject);
    152  MOZ_ASSERT(mActor);
    153  MOZ_ASSERT(!mAllowedToClose);
    154 
    155  nsresult rv = EnsureSnapshot(aObject, VoidString());
    156  if (NS_WARN_IF(NS_FAILED(rv))) {
    157    return rv;
    158  }
    159 
    160  rv = mSnapshot->GetLength(aResult);
    161  if (NS_WARN_IF(NS_FAILED(rv))) {
    162    return rv;
    163  }
    164 
    165  return NS_OK;
    166 }
    167 
    168 nsresult LSDatabase::GetKey(LSObject* aObject, uint32_t aIndex,
    169                            nsAString& aResult) {
    170  AssertIsOnOwningThread();
    171  MOZ_ASSERT(aObject);
    172  MOZ_ASSERT(mActor);
    173  MOZ_ASSERT(!mAllowedToClose);
    174 
    175  nsresult rv = EnsureSnapshot(aObject, VoidString());
    176  if (NS_WARN_IF(NS_FAILED(rv))) {
    177    return rv;
    178  }
    179 
    180  rv = mSnapshot->GetKey(aIndex, aResult);
    181  if (NS_WARN_IF(NS_FAILED(rv))) {
    182    return rv;
    183  }
    184 
    185  return NS_OK;
    186 }
    187 
    188 nsresult LSDatabase::GetItem(LSObject* aObject, const nsAString& aKey,
    189                             nsAString& aResult) {
    190  AssertIsOnOwningThread();
    191  MOZ_ASSERT(aObject);
    192  MOZ_ASSERT(mActor);
    193  MOZ_ASSERT(!mAllowedToClose);
    194 
    195  nsresult rv = EnsureSnapshot(aObject, aKey);
    196  if (NS_WARN_IF(NS_FAILED(rv))) {
    197    return rv;
    198  }
    199 
    200  rv = mSnapshot->GetItem(aKey, aResult);
    201  if (NS_WARN_IF(NS_FAILED(rv))) {
    202    return rv;
    203  }
    204 
    205  return NS_OK;
    206 }
    207 
    208 nsresult LSDatabase::GetKeys(LSObject* aObject, nsTArray<nsString>& aKeys) {
    209  AssertIsOnOwningThread();
    210  MOZ_ASSERT(aObject);
    211  MOZ_ASSERT(mActor);
    212  MOZ_ASSERT(!mAllowedToClose);
    213 
    214  nsresult rv = EnsureSnapshot(aObject, VoidString());
    215  if (NS_WARN_IF(NS_FAILED(rv))) {
    216    return rv;
    217  }
    218 
    219  rv = mSnapshot->GetKeys(aKeys);
    220  if (NS_WARN_IF(NS_FAILED(rv))) {
    221    return rv;
    222  }
    223 
    224  return NS_OK;
    225 }
    226 
    227 nsresult LSDatabase::SetItem(LSObject* aObject, const nsAString& aKey,
    228                             const nsAString& aValue,
    229                             LSNotifyInfo& aNotifyInfo) {
    230  AssertIsOnOwningThread();
    231  MOZ_ASSERT(aObject);
    232  MOZ_ASSERT(mActor);
    233  MOZ_ASSERT(!mAllowedToClose);
    234 
    235  nsresult rv = EnsureSnapshot(aObject, aKey);
    236  if (NS_WARN_IF(NS_FAILED(rv))) {
    237    return rv;
    238  }
    239 
    240  rv = mSnapshot->SetItem(aKey, aValue, aNotifyInfo);
    241  if (NS_WARN_IF(NS_FAILED(rv))) {
    242    return rv;
    243  }
    244 
    245  return NS_OK;
    246 }
    247 
    248 nsresult LSDatabase::RemoveItem(LSObject* aObject, const nsAString& aKey,
    249                                LSNotifyInfo& aNotifyInfo) {
    250  AssertIsOnOwningThread();
    251  MOZ_ASSERT(aObject);
    252  MOZ_ASSERT(mActor);
    253  MOZ_ASSERT(!mAllowedToClose);
    254 
    255  nsresult rv = EnsureSnapshot(aObject, aKey);
    256  if (NS_WARN_IF(NS_FAILED(rv))) {
    257    return rv;
    258  }
    259 
    260  rv = mSnapshot->RemoveItem(aKey, aNotifyInfo);
    261  if (NS_WARN_IF(NS_FAILED(rv))) {
    262    return rv;
    263  }
    264 
    265  return NS_OK;
    266 }
    267 
    268 nsresult LSDatabase::Clear(LSObject* aObject, LSNotifyInfo& aNotifyInfo) {
    269  AssertIsOnOwningThread();
    270  MOZ_ASSERT(aObject);
    271  MOZ_ASSERT(mActor);
    272  MOZ_ASSERT(!mAllowedToClose);
    273 
    274  nsresult rv = EnsureSnapshot(aObject, VoidString());
    275  if (NS_WARN_IF(NS_FAILED(rv))) {
    276    return rv;
    277  }
    278 
    279  rv = mSnapshot->Clear(aNotifyInfo);
    280  if (NS_WARN_IF(NS_FAILED(rv))) {
    281    return rv;
    282  }
    283 
    284  return NS_OK;
    285 }
    286 
    287 nsresult LSDatabase::BeginExplicitSnapshot(LSObject* aObject) {
    288  AssertIsOnOwningThread();
    289  MOZ_ASSERT(aObject);
    290  MOZ_ASSERT(mActor);
    291  MOZ_ASSERT(!mAllowedToClose);
    292  MOZ_ASSERT(!mSnapshot);
    293 
    294  nsresult rv = EnsureSnapshot(aObject, VoidString(), /* aExplicit */ true);
    295  if (NS_WARN_IF(NS_FAILED(rv))) {
    296    return rv;
    297  }
    298 
    299  return NS_OK;
    300 }
    301 
    302 nsresult LSDatabase::CheckpointExplicitSnapshot() {
    303  AssertIsOnOwningThread();
    304  MOZ_ASSERT(mActor);
    305  MOZ_ASSERT(!mAllowedToClose);
    306  MOZ_ASSERT(mSnapshot);
    307  MOZ_ASSERT(mSnapshot->Explicit());
    308 
    309  nsresult rv = mSnapshot->ExplicitCheckpoint();
    310  if (NS_WARN_IF(NS_FAILED(rv))) {
    311    return rv;
    312  }
    313 
    314  return NS_OK;
    315 }
    316 
    317 nsresult LSDatabase::EndExplicitSnapshot() {
    318  AssertIsOnOwningThread();
    319  MOZ_ASSERT(mActor);
    320  MOZ_ASSERT(!mAllowedToClose);
    321  MOZ_ASSERT(mSnapshot);
    322  MOZ_ASSERT(mSnapshot->Explicit());
    323 
    324  nsresult rv = mSnapshot->ExplicitEnd();
    325  if (NS_WARN_IF(NS_FAILED(rv))) {
    326    return rv;
    327  }
    328 
    329  return NS_OK;
    330 }
    331 
    332 bool LSDatabase::HasSnapshot() const {
    333  AssertIsOnOwningThread();
    334  MOZ_ASSERT(mActor);
    335  MOZ_ASSERT(!mAllowedToClose);
    336 
    337  return !!mSnapshot;
    338 }
    339 
    340 int64_t LSDatabase::GetSnapshotUsage() const {
    341  AssertIsOnOwningThread();
    342  MOZ_ASSERT(mActor);
    343  MOZ_ASSERT(!mAllowedToClose);
    344  MOZ_ASSERT(mSnapshot);
    345 
    346  return mSnapshot->GetUsage();
    347 }
    348 
    349 nsresult LSDatabase::EnsureSnapshot(LSObject* aObject, const nsAString& aKey,
    350                                    bool aExplicit) {
    351  MOZ_ASSERT(aObject);
    352  MOZ_ASSERT(mActor);
    353  MOZ_ASSERT_IF(mSnapshot, !aExplicit);
    354  MOZ_ASSERT(!mAllowedToClose);
    355 
    356  if (mSnapshot) {
    357    return NS_OK;
    358  }
    359 
    360  RefPtr<LSSnapshot> snapshot = new LSSnapshot(this);
    361 
    362  LSSnapshotChild* actor = new LSSnapshotChild(snapshot);
    363 
    364  LSSnapshotInitInfo initInfo;
    365  bool ok = mActor->SendPBackgroundLSSnapshotConstructor(
    366      actor, aObject->DocumentURI(), nsString(aKey),
    367      /* increasePeakUsage */ true,
    368      /* minSize */ 0, &initInfo);
    369  if (NS_WARN_IF(!ok)) {
    370    return NS_ERROR_FAILURE;
    371  }
    372 
    373  snapshot->SetActor(actor);
    374 
    375  // This add refs snapshot.
    376  nsresult rv = snapshot->Init(aKey, initInfo, aExplicit);
    377  if (NS_WARN_IF(NS_FAILED(rv))) {
    378    return rv;
    379  }
    380 
    381  // This is cleared in LSSnapshot::Run() before the snapshot is destroyed.
    382  mSnapshot = snapshot;
    383 
    384  return NS_OK;
    385 }
    386 
    387 void LSDatabase::AllowToClose() {
    388  AssertIsOnOwningThread();
    389  MOZ_ASSERT(!mAllowedToClose);
    390  MOZ_ASSERT(!mSnapshot);
    391 
    392  mAllowedToClose = true;
    393 
    394  if (mActor) {
    395    mActor->SendAllowToClose();
    396  }
    397 
    398  MOZ_ASSERT(gLSDatabases);
    399  MOZ_ASSERT(gLSDatabases->Get(mOrigin));
    400  gLSDatabases->Remove(mOrigin);
    401 
    402  if (!gLSDatabases->Count()) {
    403    gLSDatabases = nullptr;
    404 
    405    MOZ_ASSERT(sObserver);
    406 
    407    nsCOMPtr<nsIObserverService> obsSvc =
    408        mozilla::services::GetObserverService();
    409    MOZ_ASSERT(obsSvc);
    410 
    411    MOZ_ALWAYS_SUCCEEDS(
    412        obsSvc->RemoveObserver(sObserver, XPCOM_SHUTDOWN_OBSERVER_TOPIC));
    413 
    414    // We also need to invalidate the observer because AllowToClose can be
    415    // triggered by an indirectly related observer, so the observer service
    416    // may still keep our observer alive and call Observe on it. This is
    417    // possible because observer service snapshots the observer list for given
    418    // subject before looping over the list.
    419    sObserver->Invalidate();
    420 
    421    sObserver = nullptr;
    422  }
    423 }
    424 
    425 NS_IMPL_ISUPPORTS(LSDatabase::Observer, nsIObserver)
    426 
    427 NS_IMETHODIMP
    428 LSDatabase::Observer::Observe(nsISupports* aSubject, const char* aTopic,
    429                              const char16_t* aData) {
    430  MOZ_ASSERT(NS_IsMainThread());
    431  MOZ_ASSERT(!strcmp(aTopic, XPCOM_SHUTDOWN_OBSERVER_TOPIC));
    432 
    433  if (mInvalidated) {
    434    return NS_OK;
    435  }
    436 
    437  MOZ_ASSERT(gLSDatabases);
    438 
    439  for (const RefPtr<LSDatabase>& database :
    440       ToTArray<nsTArray<RefPtr<LSDatabase>>>(gLSDatabases->Values())) {
    441    database->RequestAllowToClose();
    442  }
    443 
    444  return NS_OK;
    445 }
    446 
    447 }  // namespace mozilla::dom