tor-browser

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

LSObject.cpp (34905B)


      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 "LSObject.h"
      8 
      9 // Local includes
     10 #include "ActorsChild.h"
     11 #include "LSDatabase.h"
     12 #include "LSObserver.h"
     13 
     14 // Global includes
     15 #include <utility>
     16 
     17 #include "MainThreadUtils.h"
     18 #include "mozilla/AppShutdown.h"
     19 #include "mozilla/BasePrincipal.h"
     20 #include "mozilla/ErrorResult.h"
     21 #include "mozilla/Monitor.h"
     22 #include "mozilla/OriginAttributes.h"
     23 #include "mozilla/Preferences.h"
     24 #include "mozilla/RemoteLazyInputStreamThread.h"
     25 #include "mozilla/ScopeExit.h"
     26 #include "mozilla/SpinEventLoopUntil.h"
     27 #include "mozilla/StaticMutex.h"
     28 #include "mozilla/StorageAccess.h"
     29 #include "mozilla/dom/ClientInfo.h"
     30 #include "mozilla/dom/Document.h"
     31 #include "mozilla/dom/LocalStorageCommon.h"
     32 #include "mozilla/dom/PBackgroundLSRequest.h"
     33 #include "mozilla/dom/PBackgroundLSSharedTypes.h"
     34 #include "mozilla/dom/quota/PrincipalUtils.h"
     35 #include "mozilla/glean/DomLocalstorageMetrics.h"
     36 #include "mozilla/ipc/BackgroundChild.h"
     37 #include "mozilla/ipc/BackgroundUtils.h"
     38 #include "mozilla/ipc/PBackgroundChild.h"
     39 #include "nsCOMPtr.h"
     40 #include "nsContentUtils.h"
     41 #include "nsDebug.h"
     42 #include "nsError.h"
     43 #include "nsIEventTarget.h"
     44 #include "nsIPrincipal.h"
     45 #include "nsIRunnable.h"
     46 #include "nsIScriptObjectPrincipal.h"
     47 #include "nsISerialEventTarget.h"
     48 #include "nsITimer.h"
     49 #include "nsPIDOMWindow.h"
     50 #include "nsString.h"
     51 #include "nsTArray.h"
     52 #include "nsTStringRepr.h"
     53 #include "nsThread.h"
     54 #include "nsThreadUtils.h"
     55 #include "nsXULAppAPI.h"
     56 #include "nscore.h"
     57 
     58 /**
     59 * Automatically cancel and abort synchronous LocalStorage requests (for example
     60 * datastore preparation) if they take this long.  We've chosen a value that is
     61 * long enough that it is unlikely for the problem to be falsely triggered by
     62 * slow system I/O.  We've also chosen a value long enough so that automated
     63 * tests should time out and fail if LocalStorage hangs.  Also, this value is
     64 * long enough so that testers can notice the (content process) hang; we want to
     65 * know about the hangs, not hide them.  On the other hand this value is less
     66 * than 60 seconds which is used by nsTerminator to crash a hung main process.
     67 */
     68 #define FAILSAFE_CANCEL_SYNC_OP_MS 50000
     69 
     70 /**
     71 * Interval with which to wake up while waiting for the sync op to complete to
     72 * check ExpectingShutdown().
     73 */
     74 #define SYNC_OP_WAKE_INTERVAL_MS 500
     75 
     76 namespace mozilla::dom {
     77 
     78 namespace {
     79 
     80 class RequestHelper;
     81 
     82 /**
     83 * Main-thread helper that implements the blocking logic required by
     84 * LocalStorage's synchronous semantics.  StartAndReturnResponse blocks on a
     85 * monitor until a result is received. See StartAndReturnResponse() for info on
     86 * this choice.
     87 *
     88 * The normal life-cycle of this method looks like:
     89 * - Main Thread: LSObject::DoRequestSynchronously creates a RequestHelper and
     90 *   invokes StartAndReturnResponse().  It Dispatches the RequestHelper to the
     91 *   RemoteLazyInputStream thread, and waits on mMonitor.
     92 * - RemoteLazyInputStream Thread: RequestHelper::Run is called, invoking
     93 *   Start() which invokes LSObject::StartRequest, which gets-or-creates the
     94 *   PBackground actor if necessary, sends LSRequest constructor which is
     95 *   provided with a callback reference to the RequestHelper. State advances to
     96 *   ResponsePending.
     97 * - RemoteLazyInputStreamThread: LSRequestChild::Recv__delete__ is received,
     98 *   which invokes RequestHelepr::OnResponse, advancing the state to Complete
     99 *   and notifying mMonitor.
    100 * - Main Thread: The main thread wakes up after waiting on the monitor,
    101 *   returning the received response.
    102 *
    103 * See LocalStorageCommon.h for high-level context and method comments for
    104 * low-level details.
    105 */
    106 class RequestHelper final : public Runnable, public LSRequestChildCallback {
    107  enum class State {
    108    /**
    109     * The RequestHelper has been created and dispatched to the
    110     * RemoteLazyInputStream Thread.
    111     */
    112    Initial,
    113    /**
    114     * Start() has been invoked on the RemoteLazyInputStream Thread and
    115     * LSObject::StartRequest has been invoked from there, sending an IPC
    116     * message to PBackground to service the request.  We stay in this state
    117     * until a response is received or a timeout occurs.
    118     */
    119    ResponsePending,
    120    /**
    121     * The request timed out, or failed in some fashion, and needs to be
    122     * cancelled. A runnable has been dispatched to the DOM File thread to
    123     * notify the parent actor, and the main thread will continue to block until
    124     * we receive a reponse.
    125     */
    126    Canceling,
    127    /**
    128     * The request is complete, either successfully or due to being cancelled.
    129     * The main thread can stop waiting and immediately return to the caller of
    130     * StartAndReturnResponse.
    131     */
    132    Complete
    133  };
    134 
    135  // The object we are issuing a request on behalf of.  Present because of the
    136  // need to invoke LSObject::StartRequest off the main thread.  Dropped on
    137  // return to the main-thread in Finish().
    138  RefPtr<LSObject> mObject;
    139  // The thread the RequestHelper was created on.  This should be the main
    140  // thread.
    141  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
    142  // The IPC actor handling the request with standard IPC allocation rules.
    143  // Our reference is nulled in OnResponse which corresponds to the actor's
    144  // __destroy__ method.
    145  LSRequestChild* mActor;
    146  const LSRequestParams mParams;
    147  Monitor mMonitor;
    148  LSRequestResponse mResponse MOZ_GUARDED_BY(mMonitor);
    149  nsresult mResultCode MOZ_GUARDED_BY(mMonitor);
    150  State mState MOZ_GUARDED_BY(mMonitor);
    151 
    152 public:
    153  RequestHelper(LSObject* aObject, const LSRequestParams& aParams)
    154      : Runnable("dom::RequestHelper"),
    155        mObject(aObject),
    156        mOwningEventTarget(GetCurrentSerialEventTarget()),
    157        mActor(nullptr),
    158        mParams(aParams),
    159        mMonitor("dom::RequestHelper::mMonitor"),
    160        mResultCode(NS_OK),
    161        mState(State::Initial) {}
    162 
    163  bool IsOnOwningThread() const {
    164    MOZ_ASSERT(mOwningEventTarget);
    165 
    166    bool current;
    167    return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
    168           current;
    169  }
    170 
    171  void AssertIsOnOwningThread() const {
    172    MOZ_ASSERT(NS_IsMainThread());
    173    MOZ_ASSERT(IsOnOwningThread());
    174  }
    175 
    176  nsresult StartAndReturnResponse(LSRequestResponse& aResponse);
    177 
    178 private:
    179  ~RequestHelper() = default;
    180 
    181  NS_DECL_ISUPPORTS_INHERITED
    182 
    183  NS_DECL_NSIRUNNABLE
    184 
    185  // LSRequestChildCallback
    186  void OnResponse(LSRequestResponse&& aResponse) override;
    187 };
    188 
    189 void AssertExplicitSnapshotInvariants(const LSObject& aObject) {
    190  // Can be only called if the mInExplicitSnapshot flag is true.
    191  // An explicit snapshot must have been created.
    192  MOZ_ASSERT(aObject.InExplicitSnapshot());
    193 
    194  // If an explicit snapshot has been created then mDatabase must be not null.
    195  // DropDatabase could be called in the meatime, but that must be preceded by
    196  // Disconnect which sets mInExplicitSnapshot to false. EnsureDatabase could
    197  // be called in the meantime too, but that can't set mDatabase to null or to
    198  // a new value. See the comment below.
    199  MOZ_ASSERT(aObject.DatabaseStrongRef());
    200 
    201  // Existence of a snapshot prevents the database from allowing to close. See
    202  // LSDatabase::RequestAllowToClose and LSDatabase::NoteFinishedSnapshot.
    203  // If the database is not allowed to close then mDatabase could not have been
    204  // nulled out or set to a new value. See EnsureDatabase.
    205  MOZ_ASSERT(!aObject.DatabaseStrongRef()->IsAllowedToClose());
    206 }
    207 
    208 }  // namespace
    209 
    210 LSObject::LSObject(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
    211                   nsIPrincipal* aStoragePrincipal)
    212    : Storage(aWindow, aPrincipal, aStoragePrincipal),
    213      mPrivateBrowsingId(0),
    214      mInExplicitSnapshot(false) {
    215  AssertIsOnOwningThread();
    216  MOZ_ASSERT(NextGenLocalStorageEnabled());
    217 }
    218 
    219 LSObject::~LSObject() {
    220  AssertIsOnOwningThread();
    221 
    222  DropObserver();
    223 }
    224 
    225 // static
    226 nsresult LSObject::CreateForWindow(nsPIDOMWindowInner* aWindow,
    227                                   Storage** aStorage) {
    228  MOZ_ASSERT(NS_IsMainThread());
    229  MOZ_ASSERT(aWindow);
    230  MOZ_ASSERT(aStorage);
    231  MOZ_ASSERT(NextGenLocalStorageEnabled());
    232  MOZ_ASSERT(StorageAllowedForWindow(aWindow) != StorageAccess::eDeny);
    233 
    234  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
    235  MOZ_ASSERT(sop);
    236 
    237  nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
    238  if (NS_WARN_IF(!principal)) {
    239    return NS_ERROR_FAILURE;
    240  }
    241 
    242  nsCOMPtr<nsIPrincipal> storagePrincipal = sop->GetEffectiveStoragePrincipal();
    243  if (NS_WARN_IF(!storagePrincipal)) {
    244    return NS_ERROR_FAILURE;
    245  }
    246 
    247  if (principal->IsSystemPrincipal()) {
    248    return NS_ERROR_NOT_AVAILABLE;
    249  }
    250 
    251  // localStorage is not available on some pages on purpose, for example
    252  // about:home. Match the old implementation by using GenerateOriginKey
    253  // for the check.
    254  nsCString originAttrSuffix;
    255  nsCString originKey;
    256  nsresult rv = storagePrincipal->GetStorageOriginKey(originKey);
    257  storagePrincipal->OriginAttributesRef().CreateSuffix(originAttrSuffix);
    258 
    259  if (NS_FAILED(rv)) {
    260    return NS_ERROR_NOT_AVAILABLE;
    261  }
    262 
    263  auto principalInfo = MakeUnique<PrincipalInfo>();
    264  rv = PrincipalToPrincipalInfo(principal, principalInfo.get());
    265  if (NS_WARN_IF(NS_FAILED(rv))) {
    266    return rv;
    267  }
    268 
    269  MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo);
    270 
    271  auto storagePrincipalInfo = MakeUnique<PrincipalInfo>();
    272  rv = PrincipalToPrincipalInfo(storagePrincipal, storagePrincipalInfo.get());
    273  if (NS_WARN_IF(NS_FAILED(rv))) {
    274    return rv;
    275  }
    276 
    277  MOZ_ASSERT(storagePrincipalInfo->type() ==
    278             PrincipalInfo::TContentPrincipalInfo);
    279 
    280  if (NS_WARN_IF(!quota::IsPrincipalInfoValid(*storagePrincipalInfo))) {
    281    return NS_ERROR_FAILURE;
    282  }
    283 
    284 #ifdef DEBUG
    285  QM_TRY_INSPECT(const auto& principalMetadata,
    286                 quota::GetInfoFromPrincipal(storagePrincipal.get()));
    287 
    288  MOZ_ASSERT(originAttrSuffix == principalMetadata.mSuffix);
    289 
    290  const auto& origin = principalMetadata.mOrigin;
    291 #else
    292  QM_TRY_INSPECT(const auto& origin,
    293                 quota::GetOriginFromPrincipal(storagePrincipal.get()));
    294 #endif
    295 
    296  uint32_t privateBrowsingId;
    297  rv = storagePrincipal->GetPrivateBrowsingId(&privateBrowsingId);
    298  if (NS_WARN_IF(NS_FAILED(rv))) {
    299    return rv;
    300  }
    301 
    302  Maybe<ClientInfo> clientInfo = aWindow->GetClientInfo();
    303  if (clientInfo.isNothing()) {
    304    return NS_ERROR_FAILURE;
    305  }
    306 
    307  Maybe<nsID> clientId = Some(clientInfo.ref().Id());
    308 
    309  Maybe<PrincipalInfo> clientPrincipalInfo =
    310      Some(clientInfo.ref().PrincipalInfo());
    311 
    312  nsString documentURI;
    313  if (nsCOMPtr<Document> doc = aWindow->GetExtantDoc()) {
    314    rv = doc->GetDocumentURI(documentURI);
    315    if (NS_WARN_IF(NS_FAILED(rv))) {
    316      return rv;
    317    }
    318  }
    319 
    320  RefPtr<LSObject> object = new LSObject(aWindow, principal, storagePrincipal);
    321  object->mPrincipalInfo = std::move(principalInfo);
    322  object->mStoragePrincipalInfo = std::move(storagePrincipalInfo);
    323  object->mPrivateBrowsingId = privateBrowsingId;
    324  object->mClientId = clientId;
    325  object->mClientPrincipalInfo = clientPrincipalInfo;
    326  object->mOrigin = origin;
    327  object->mOriginKey = originKey;
    328  object->mDocumentURI = documentURI;
    329 
    330  object.forget(aStorage);
    331  return NS_OK;
    332 }
    333 
    334 // static
    335 nsresult LSObject::CreateForPrincipal(nsPIDOMWindowInner* aWindow,
    336                                      nsIPrincipal* aPrincipal,
    337                                      nsIPrincipal* aStoragePrincipal,
    338                                      const nsAString& aDocumentURI,
    339                                      bool aPrivate, LSObject** aObject) {
    340  MOZ_ASSERT(NS_IsMainThread());
    341  MOZ_ASSERT(aPrincipal);
    342  MOZ_ASSERT(aStoragePrincipal);
    343  MOZ_ASSERT(aObject);
    344 
    345  nsCString originAttrSuffix;
    346  nsCString originKey;
    347  nsresult rv = aStoragePrincipal->GetStorageOriginKey(originKey);
    348  aStoragePrincipal->OriginAttributesRef().CreateSuffix(originAttrSuffix);
    349  if (NS_FAILED(rv)) {
    350    return NS_ERROR_NOT_AVAILABLE;
    351  }
    352 
    353  auto principalInfo = MakeUnique<PrincipalInfo>();
    354  rv = PrincipalToPrincipalInfo(aPrincipal, principalInfo.get());
    355  if (NS_WARN_IF(NS_FAILED(rv))) {
    356    return rv;
    357  }
    358 
    359  MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo ||
    360             principalInfo->type() == PrincipalInfo::TSystemPrincipalInfo);
    361 
    362  auto storagePrincipalInfo = MakeUnique<PrincipalInfo>();
    363  rv = PrincipalToPrincipalInfo(aStoragePrincipal, storagePrincipalInfo.get());
    364  if (NS_WARN_IF(NS_FAILED(rv))) {
    365    return rv;
    366  }
    367 
    368  MOZ_ASSERT(
    369      storagePrincipalInfo->type() == PrincipalInfo::TContentPrincipalInfo ||
    370      storagePrincipalInfo->type() == PrincipalInfo::TSystemPrincipalInfo);
    371 
    372  if (NS_WARN_IF(!quota::IsPrincipalInfoValid(*storagePrincipalInfo))) {
    373    return NS_ERROR_FAILURE;
    374  }
    375 
    376 #ifdef DEBUG
    377  QM_TRY_INSPECT(
    378      const auto& principalMetadata,
    379      ([&storagePrincipalInfo,
    380        &aPrincipal]() -> Result<quota::PrincipalMetadata, nsresult> {
    381        if (storagePrincipalInfo->type() ==
    382            PrincipalInfo::TSystemPrincipalInfo) {
    383          return quota::GetInfoForChrome();
    384        }
    385 
    386        QM_TRY_RETURN(quota::GetInfoFromPrincipal(aPrincipal));
    387      }()));
    388 
    389  MOZ_ASSERT(originAttrSuffix == principalMetadata.mSuffix);
    390 
    391  const auto& origin = principalMetadata.mOrigin;
    392 #else
    393  QM_TRY_INSPECT(const auto& origin,
    394                 ([&storagePrincipalInfo,
    395                   &aPrincipal]() -> Result<nsAutoCString, nsresult> {
    396                   if (storagePrincipalInfo->type() ==
    397                       PrincipalInfo::TSystemPrincipalInfo) {
    398                     return nsAutoCString{quota::GetOriginForChrome()};
    399                   }
    400 
    401                   QM_TRY_RETURN(quota::GetOriginFromPrincipal(aPrincipal));
    402                 }()));
    403 #endif
    404 
    405  Maybe<nsID> clientId;
    406  if (aWindow) {
    407    Maybe<ClientInfo> clientInfo = aWindow->GetClientInfo();
    408    if (clientInfo.isNothing()) {
    409      return NS_ERROR_FAILURE;
    410    }
    411 
    412    clientId = Some(clientInfo.ref().Id());
    413  } else if (Preferences::GetBool("dom.storage.client_validation")) {
    414    return NS_ERROR_FAILURE;
    415  }
    416 
    417  RefPtr<LSObject> object =
    418      new LSObject(aWindow, aPrincipal, aStoragePrincipal);
    419  object->mPrincipalInfo = std::move(principalInfo);
    420  object->mStoragePrincipalInfo = std::move(storagePrincipalInfo);
    421  object->mPrivateBrowsingId = aPrivate ? 1 : 0;
    422  object->mClientId = clientId;
    423  object->mOrigin = origin;
    424  object->mOriginKey = originKey;
    425  object->mDocumentURI = aDocumentURI;
    426 
    427  object.forget(aObject);
    428  return NS_OK;
    429 }  // namespace dom
    430 
    431 LSRequestChild* LSObject::StartRequest(const LSRequestParams& aParams,
    432                                       LSRequestChildCallback* aCallback) {
    433  AssertIsOnDOMFileThread();
    434 
    435  mozilla::ipc::PBackgroundChild* backgroundActor =
    436      mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
    437  if (NS_WARN_IF(!backgroundActor)) {
    438    return nullptr;
    439  }
    440 
    441  LSRequestChild* actor = new LSRequestChild();
    442 
    443  if (!backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams)) {
    444    return nullptr;
    445  }
    446 
    447  // Must set callback after calling SendPBackgroundLSRequestConstructor since
    448  // it can be called synchronously when SendPBackgroundLSRequestConstructor
    449  // fails.
    450  actor->SetCallback(aCallback);
    451 
    452  return actor;
    453 }
    454 
    455 Storage::StorageType LSObject::Type() const {
    456  AssertIsOnOwningThread();
    457 
    458  return eLocalStorage;
    459 }
    460 
    461 bool LSObject::IsForkOf(const Storage* aStorage) const {
    462  AssertIsOnOwningThread();
    463  MOZ_ASSERT(aStorage);
    464 
    465  if (aStorage->Type() != eLocalStorage) {
    466    return false;
    467  }
    468 
    469  return static_cast<const LSObject*>(aStorage)->mOrigin == mOrigin;
    470 }
    471 
    472 int64_t LSObject::GetOriginQuotaUsage() const {
    473  AssertIsOnOwningThread();
    474 
    475  // It's not necessary to return an actual value here.  This method is
    476  // implemented only because the SessionStore currently needs it to cap the
    477  // amount of data it persists to disk (via nsIDOMWindowUtils.getStorageUsage).
    478  // Any callers that want to know about storage usage should be asking
    479  // QuotaManager directly.
    480  //
    481  // Note: This may change as LocalStorage is repurposed to be the new
    482  // SessionStorage backend.
    483  return 0;
    484 }
    485 
    486 void LSObject::Disconnect() {
    487  // Explicit snapshots which were not ended in JS, must be ended here while
    488  // IPC is still available. We can't do that in DropDatabase because actors
    489  // may have been destroyed already at that point.
    490  if (mInExplicitSnapshot) {
    491    AssertExplicitSnapshotInvariants(*this);
    492 
    493    nsresult rv = mDatabase->EndExplicitSnapshot();
    494    (void)NS_WARN_IF(NS_FAILED(rv));
    495 
    496    mInExplicitSnapshot = false;
    497  }
    498 }
    499 
    500 uint32_t LSObject::GetLength(nsIPrincipal& aSubjectPrincipal,
    501                             ErrorResult& aError) {
    502  AssertIsOnOwningThread();
    503 
    504  if (!CanUseStorage(aSubjectPrincipal)) {
    505    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    506    return 0;
    507  }
    508 
    509  nsresult rv = EnsureDatabase();
    510  if (NS_WARN_IF(NS_FAILED(rv))) {
    511    aError.Throw(rv);
    512    return 0;
    513  }
    514 
    515  uint32_t result;
    516  rv = mDatabase->GetLength(this, &result);
    517  if (NS_WARN_IF(NS_FAILED(rv))) {
    518    aError.Throw(rv);
    519    return 0;
    520  }
    521 
    522  return result;
    523 }
    524 
    525 void LSObject::Key(uint32_t aIndex, nsAString& aResult,
    526                   nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) {
    527  AssertIsOnOwningThread();
    528 
    529  if (!CanUseStorage(aSubjectPrincipal)) {
    530    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    531    return;
    532  }
    533 
    534  nsresult rv = EnsureDatabase();
    535  if (NS_WARN_IF(NS_FAILED(rv))) {
    536    aError.Throw(rv);
    537    return;
    538  }
    539 
    540  nsString result;
    541  rv = mDatabase->GetKey(this, aIndex, result);
    542  if (NS_WARN_IF(NS_FAILED(rv))) {
    543    aError.Throw(rv);
    544    return;
    545  }
    546 
    547  aResult = result;
    548 }
    549 
    550 void LSObject::GetItem(const nsAString& aKey, nsAString& aResult,
    551                       nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) {
    552  AssertIsOnOwningThread();
    553 
    554  if (!CanUseStorage(aSubjectPrincipal)) {
    555    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    556    return;
    557  }
    558 
    559  nsresult rv = EnsureDatabase();
    560  if (NS_WARN_IF(NS_FAILED(rv))) {
    561    aError.Throw(rv);
    562    return;
    563  }
    564 
    565  nsString result;
    566  rv = mDatabase->GetItem(this, aKey, result);
    567  if (NS_WARN_IF(NS_FAILED(rv))) {
    568    aError.Throw(rv);
    569    return;
    570  }
    571 
    572  aResult = result;
    573 }
    574 
    575 void LSObject::GetSupportedNames(nsTArray<nsString>& aNames) {
    576  AssertIsOnOwningThread();
    577 
    578  if (!CanUseStorage(*nsContentUtils::SubjectPrincipal())) {
    579    // Return just an empty array.
    580    aNames.Clear();
    581    return;
    582  }
    583 
    584  nsresult rv = EnsureDatabase();
    585  if (NS_WARN_IF(NS_FAILED(rv))) {
    586    return;
    587  }
    588 
    589  rv = mDatabase->GetKeys(this, aNames);
    590  if (NS_WARN_IF(NS_FAILED(rv))) {
    591    return;
    592  }
    593 }
    594 
    595 void LSObject::SetItem(const nsAString& aKey, const nsAString& aValue,
    596                       nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) {
    597  AssertIsOnOwningThread();
    598 
    599  if (!CanUseStorage(aSubjectPrincipal)) {
    600    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    601    return;
    602  }
    603 
    604  nsresult rv = EnsureDatabase();
    605  if (NS_WARN_IF(NS_FAILED(rv))) {
    606    aError.Throw(rv);
    607    return;
    608  }
    609 
    610  LSNotifyInfo info;
    611  rv = mDatabase->SetItem(this, aKey, aValue, info);
    612  if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
    613    rv = NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
    614  }
    615  if (NS_WARN_IF(NS_FAILED(rv))) {
    616    aError.Throw(rv);
    617    return;
    618  }
    619 
    620  if (info.changed()) {
    621    OnChange(aKey, info.oldValue(), aValue);
    622  }
    623 }
    624 
    625 void LSObject::RemoveItem(const nsAString& aKey,
    626                          nsIPrincipal& aSubjectPrincipal,
    627                          ErrorResult& aError) {
    628  AssertIsOnOwningThread();
    629 
    630  if (!CanUseStorage(aSubjectPrincipal)) {
    631    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    632    return;
    633  }
    634 
    635  nsresult rv = EnsureDatabase();
    636  if (NS_WARN_IF(NS_FAILED(rv))) {
    637    aError.Throw(rv);
    638    return;
    639  }
    640 
    641  LSNotifyInfo info;
    642  rv = mDatabase->RemoveItem(this, aKey, info);
    643  if (NS_WARN_IF(NS_FAILED(rv))) {
    644    aError.Throw(rv);
    645    return;
    646  }
    647 
    648  if (info.changed()) {
    649    OnChange(aKey, info.oldValue(), VoidString());
    650  }
    651 }
    652 
    653 void LSObject::Clear(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) {
    654  AssertIsOnOwningThread();
    655 
    656  if (!CanUseStorage(aSubjectPrincipal)) {
    657    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    658    return;
    659  }
    660 
    661  nsresult rv = EnsureDatabase();
    662  if (NS_WARN_IF(NS_FAILED(rv))) {
    663    aError.Throw(rv);
    664    return;
    665  }
    666 
    667  LSNotifyInfo info;
    668  rv = mDatabase->Clear(this, info);
    669  if (NS_WARN_IF(NS_FAILED(rv))) {
    670    aError.Throw(rv);
    671    return;
    672  }
    673 
    674  if (info.changed()) {
    675    OnChange(VoidString(), VoidString(), VoidString());
    676  }
    677 }
    678 
    679 void LSObject::Open(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) {
    680  AssertIsOnOwningThread();
    681 
    682  if (!CanUseStorage(aSubjectPrincipal)) {
    683    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    684    return;
    685  }
    686 
    687  nsresult rv = EnsureDatabase();
    688  if (NS_WARN_IF(NS_FAILED(rv))) {
    689    aError.Throw(rv);
    690    return;
    691  }
    692 }
    693 
    694 void LSObject::Close(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) {
    695  AssertIsOnOwningThread();
    696 
    697  if (!CanUseStorage(aSubjectPrincipal)) {
    698    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    699    return;
    700  }
    701 
    702  DropDatabase();
    703 }
    704 
    705 void LSObject::BeginExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
    706                                     ErrorResult& aError) {
    707  AssertIsOnOwningThread();
    708 
    709  if (!CanUseStorage(aSubjectPrincipal)) {
    710    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    711    return;
    712  }
    713 
    714  if (mInExplicitSnapshot) {
    715    aError.Throw(NS_ERROR_ALREADY_INITIALIZED);
    716    return;
    717  }
    718 
    719  nsresult rv = EnsureDatabase();
    720  if (NS_WARN_IF(NS_FAILED(rv))) {
    721    aError.Throw(rv);
    722    return;
    723  }
    724 
    725  rv = mDatabase->BeginExplicitSnapshot(this);
    726  if (NS_WARN_IF(NS_FAILED(rv))) {
    727    aError.Throw(rv);
    728    return;
    729  }
    730 
    731  mInExplicitSnapshot = true;
    732 }
    733 
    734 void LSObject::CheckpointExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
    735                                          ErrorResult& aError) {
    736  AssertIsOnOwningThread();
    737 
    738  if (!CanUseStorage(aSubjectPrincipal)) {
    739    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    740    return;
    741  }
    742 
    743  if (!mInExplicitSnapshot) {
    744    aError.Throw(NS_ERROR_NOT_INITIALIZED);
    745    return;
    746  }
    747 
    748  AssertExplicitSnapshotInvariants(*this);
    749 
    750  nsresult rv = mDatabase->CheckpointExplicitSnapshot();
    751  if (NS_WARN_IF(NS_FAILED(rv))) {
    752    aError.Throw(rv);
    753    return;
    754  }
    755 }
    756 
    757 void LSObject::EndExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
    758                                   ErrorResult& aError) {
    759  AssertIsOnOwningThread();
    760 
    761  if (!CanUseStorage(aSubjectPrincipal)) {
    762    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    763    return;
    764  }
    765 
    766  if (!mInExplicitSnapshot) {
    767    aError.Throw(NS_ERROR_NOT_INITIALIZED);
    768    return;
    769  }
    770 
    771  AssertExplicitSnapshotInvariants(*this);
    772 
    773  nsresult rv = mDatabase->EndExplicitSnapshot();
    774  if (NS_WARN_IF(NS_FAILED(rv))) {
    775    aError.Throw(rv);
    776    return;
    777  }
    778 
    779  mInExplicitSnapshot = false;
    780 }
    781 
    782 bool LSObject::GetHasSnapshot(nsIPrincipal& aSubjectPrincipal,
    783                              ErrorResult& aError) {
    784  AssertIsOnOwningThread();
    785 
    786  if (!CanUseStorage(aSubjectPrincipal)) {
    787    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    788    return false;
    789  }
    790 
    791  // We can't call `HasSnapshot` on the database if it's being closed, but we
    792  // know that a database which is being closed can't have a snapshot, so we
    793  // return false in that case directly here.
    794  if (!mDatabase || mDatabase->IsAllowedToClose()) {
    795    return false;
    796  }
    797 
    798  return mDatabase->HasSnapshot();
    799 }
    800 
    801 int64_t LSObject::GetSnapshotUsage(nsIPrincipal& aSubjectPrincipal,
    802                                   ErrorResult& aError) {
    803  AssertIsOnOwningThread();
    804 
    805  if (!CanUseStorage(aSubjectPrincipal)) {
    806    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
    807    return 0;
    808  }
    809 
    810  if (!mDatabase || mDatabase->IsAllowedToClose()) {
    811    aError.Throw(NS_ERROR_NOT_AVAILABLE);
    812    return 0;
    813  }
    814 
    815  if (!mDatabase->HasSnapshot()) {
    816    aError.Throw(NS_ERROR_NOT_AVAILABLE);
    817    return 0;
    818  }
    819 
    820  return mDatabase->GetSnapshotUsage();
    821 }
    822 
    823 NS_IMPL_ADDREF_INHERITED(LSObject, Storage)
    824 NS_IMPL_RELEASE_INHERITED(LSObject, Storage)
    825 
    826 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LSObject)
    827 NS_INTERFACE_MAP_END_INHERITING(Storage)
    828 
    829 NS_IMPL_CYCLE_COLLECTION_CLASS(LSObject)
    830 
    831 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(LSObject, Storage)
    832  tmp->AssertIsOnOwningThread();
    833 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    834 
    835 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(LSObject, Storage)
    836  tmp->AssertIsOnOwningThread();
    837  tmp->DropDatabase();
    838 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    839 
    840 nsresult LSObject::DoRequestSynchronously(const LSRequestParams& aParams,
    841                                          LSRequestResponse& aResponse) {
    842  // We don't need this yet, but once the request successfully finishes, it's
    843  // too late to initialize PBackground child on the owning thread, because
    844  // it can fail and parent would keep an extra strong ref to the datastore or
    845  // observer.
    846  mozilla::ipc::PBackgroundChild* backgroundActor =
    847      mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
    848  if (NS_WARN_IF(!backgroundActor)) {
    849    return NS_ERROR_FAILURE;
    850  }
    851 
    852  RefPtr<RequestHelper> helper = new RequestHelper(this, aParams);
    853 
    854  // This will start and finish the request on the RemoteLazyInputStream thread.
    855  // The owning thread is synchronously blocked while the request is
    856  // asynchronously processed on the RemoteLazyInputStream thread.
    857  nsresult rv = helper->StartAndReturnResponse(aResponse);
    858  if (NS_WARN_IF(NS_FAILED(rv))) {
    859    return rv;
    860  }
    861 
    862  if (aResponse.type() == LSRequestResponse::Tnsresult) {
    863    nsresult errorCode = aResponse.get_nsresult();
    864 
    865    if (errorCode == NS_ERROR_FILE_NO_DEVICE_SPACE) {
    866      errorCode = NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
    867    }
    868 
    869    return errorCode;
    870  }
    871 
    872  return NS_OK;
    873 }
    874 
    875 nsresult LSObject::EnsureDatabase() {
    876  AssertIsOnOwningThread();
    877 
    878  if (mDatabase && !mDatabase->IsAllowedToClose()) {
    879    return NS_OK;
    880  }
    881 
    882  mDatabase = LSDatabase::Get(mOrigin);
    883 
    884  if (mDatabase) {
    885    MOZ_ASSERT(!mDatabase->IsAllowedToClose());
    886    return NS_OK;
    887  }
    888 
    889  auto timerId = glean::localstorage_database::new_object_setup_time.Start();
    890 
    891  auto autoCancelTimer = MakeScopeExit([&timerId] {
    892    glean::localstorage_database::new_object_setup_time.Cancel(
    893        std::move(timerId));
    894  });
    895 
    896  // We don't need this yet, but once the request successfully finishes, it's
    897  // too late to initialize PBackground child on the owning thread, because
    898  // it can fail and parent would keep an extra strong ref to the datastore.
    899  mozilla::ipc::PBackgroundChild* backgroundActor =
    900      mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
    901  if (NS_WARN_IF(!backgroundActor)) {
    902    return NS_ERROR_FAILURE;
    903  }
    904 
    905  LSRequestCommonParams commonParams;
    906  commonParams.principalInfo() = *mPrincipalInfo;
    907  commonParams.storagePrincipalInfo() = *mStoragePrincipalInfo;
    908  commonParams.originKey() = mOriginKey;
    909 
    910  LSRequestPrepareDatastoreParams params;
    911  params.commonParams() = commonParams;
    912  params.clientId() = mClientId;
    913  params.clientPrincipalInfo() = mClientPrincipalInfo;
    914 
    915  LSRequestResponse response;
    916 
    917  nsresult rv = DoRequestSynchronously(params, response);
    918  if (NS_WARN_IF(NS_FAILED(rv))) {
    919    return rv;
    920  }
    921 
    922  MOZ_ASSERT(response.type() ==
    923             LSRequestResponse::TLSRequestPrepareDatastoreResponse);
    924 
    925  LSRequestPrepareDatastoreResponse prepareDatastoreResponse =
    926      std::move(response.get_LSRequestPrepareDatastoreResponse());
    927 
    928  auto childEndpoint =
    929      std::move(prepareDatastoreResponse.databaseChildEndpoint());
    930 
    931  // The datastore is now ready on the parent side (prepared by the asynchronous
    932  // request on the RemoteLazyInputStream thread).
    933  // Let's create a direct connection to the datastore (through a database
    934  // actor) from the owning thread.
    935  // Note that we now can't error out, otherwise parent will keep an extra
    936  // strong reference to the datastore.
    937 
    938  RefPtr<LSDatabase> database = new LSDatabase(mOrigin);
    939 
    940  RefPtr<LSDatabaseChild> actor = new LSDatabaseChild(database);
    941 
    942  MOZ_ALWAYS_TRUE(childEndpoint.Bind(actor));
    943 
    944  database->SetActor(actor);
    945 
    946  if (prepareDatastoreResponse.invalidated()) {
    947    database->RequestAllowToClose();
    948    return NS_ERROR_ABORT;
    949  }
    950 
    951  autoCancelTimer.release();
    952 
    953  glean::localstorage_database::new_object_setup_time.StopAndAccumulate(
    954      std::move(timerId));
    955 
    956  mDatabase = std::move(database);
    957 
    958  return NS_OK;
    959 }
    960 
    961 void LSObject::DropDatabase() {
    962  AssertIsOnOwningThread();
    963 
    964  mDatabase = nullptr;
    965 }
    966 
    967 nsresult LSObject::EnsureObserver() {
    968  AssertIsOnOwningThread();
    969 
    970  if (mObserver) {
    971    return NS_OK;
    972  }
    973 
    974  mObserver = LSObserver::Get(mOrigin);
    975 
    976  if (mObserver) {
    977    return NS_OK;
    978  }
    979 
    980  LSRequestPrepareObserverParams params;
    981  params.principalInfo() = *mPrincipalInfo;
    982  params.storagePrincipalInfo() = *mStoragePrincipalInfo;
    983  params.clientId() = mClientId;
    984  params.clientPrincipalInfo() = mClientPrincipalInfo;
    985 
    986  LSRequestResponse response;
    987 
    988  nsresult rv = DoRequestSynchronously(params, response);
    989  if (NS_WARN_IF(NS_FAILED(rv))) {
    990    return rv;
    991  }
    992 
    993  MOZ_ASSERT(response.type() ==
    994             LSRequestResponse::TLSRequestPrepareObserverResponse);
    995 
    996  const LSRequestPrepareObserverResponse& prepareObserverResponse =
    997      response.get_LSRequestPrepareObserverResponse();
    998 
    999  uint64_t observerId = prepareObserverResponse.observerId();
   1000 
   1001  // The obsserver is now ready on the parent side (prepared by the asynchronous
   1002  // request on the RemoteLazyInputStream thread).
   1003  // Let's create a direct connection to the observer (through an observer
   1004  // actor) from the owning thread.
   1005  // Note that we now can't error out, otherwise parent will keep an extra
   1006  // strong reference to the observer.
   1007 
   1008  mozilla::ipc::PBackgroundChild* backgroundActor =
   1009      mozilla::ipc::BackgroundChild::GetForCurrentThread();
   1010  MOZ_ASSERT(backgroundActor);
   1011 
   1012  RefPtr<LSObserver> observer = new LSObserver(mOrigin);
   1013 
   1014  LSObserverChild* actor = new LSObserverChild(observer);
   1015 
   1016  MOZ_ALWAYS_TRUE(
   1017      backgroundActor->SendPBackgroundLSObserverConstructor(actor, observerId));
   1018 
   1019  observer->SetActor(actor);
   1020 
   1021  mObserver = std::move(observer);
   1022 
   1023  return NS_OK;
   1024 }
   1025 
   1026 void LSObject::DropObserver() {
   1027  AssertIsOnOwningThread();
   1028 
   1029  if (mObserver) {
   1030    mObserver = nullptr;
   1031  }
   1032 }
   1033 
   1034 void LSObject::OnChange(const nsAString& aKey, const nsAString& aOldValue,
   1035                        const nsAString& aNewValue) {
   1036  AssertIsOnOwningThread();
   1037 
   1038  NotifyChange(/* aStorage */ this, StoragePrincipal(), aKey, aOldValue,
   1039               aNewValue, /* aStorageType */ kLocalStorageType, mDocumentURI,
   1040               /* aIsPrivate */ !!mPrivateBrowsingId,
   1041               /* aImmediateDispatch */ false);
   1042 }
   1043 
   1044 void LSObject::LastRelease() {
   1045  AssertIsOnOwningThread();
   1046 
   1047  DropDatabase();
   1048 }
   1049 
   1050 nsresult RequestHelper::StartAndReturnResponse(LSRequestResponse& aResponse) {
   1051  AssertIsOnOwningThread();
   1052 
   1053  nsCOMPtr<nsIEventTarget> domFileThread =
   1054      RemoteLazyInputStreamThread::GetOrCreate();
   1055  if (NS_WARN_IF(!domFileThread)) {
   1056    return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
   1057  }
   1058 
   1059  nsresult rv = domFileThread->Dispatch(this, NS_DISPATCH_NORMAL);
   1060  if (NS_WARN_IF(NS_FAILED(rv))) {
   1061    return rv;
   1062  }
   1063 
   1064  TimeStamp deadline = TimeStamp::Now() + TimeDuration::FromMilliseconds(
   1065                                              FAILSAFE_CANCEL_SYNC_OP_MS);
   1066 
   1067  MonitorAutoLock lock(mMonitor);
   1068  while (mState != State::Complete) {
   1069    TimeStamp now = TimeStamp::Now();
   1070    // If we are expecting shutdown or have passed our deadline, immediately
   1071    // dispatch ourselves to the DOM File thread to cancel the operation.  We
   1072    // don't abort until the cancellation has gone through, as otherwise we
   1073    // could race with the DOM File thread.
   1074    if (AppShutdown::IsShutdownImpending() || now >= deadline) {
   1075      switch (mState) {
   1076        case State::Initial:
   1077          // The DOM File thread never even woke before ExpectingShutdown() or a
   1078          // timeout - skip even creating the actor and just report an error.
   1079          mResultCode = NS_ERROR_FAILURE;
   1080          mState = State::Complete;
   1081          continue;
   1082        case State::ResponsePending:
   1083          // The DOM File thread is currently waiting for a reply, switch to a
   1084          // canceling state, and notify it to cancel by dispatching a runnable.
   1085          mState = State::Canceling;
   1086          MOZ_ALWAYS_SUCCEEDS(
   1087              domFileThread->Dispatch(this, NS_DISPATCH_NORMAL));
   1088          [[fallthrough]];
   1089        case State::Canceling:
   1090          // We've cancelled the request, so just need to wait indefinitely for
   1091          // it to complete.
   1092          lock.Wait();
   1093          continue;
   1094        default:
   1095          MOZ_ASSERT_UNREACHABLE("unexpected state");
   1096      }
   1097    }
   1098 
   1099    // Wait until either we reach out deadline or for SYNC_OP_WAIT_INTERVAL_MS.
   1100    lock.Wait(TimeDuration::Min(
   1101        TimeDuration::FromMilliseconds(SYNC_OP_WAKE_INTERVAL_MS),
   1102        deadline - now));
   1103  }
   1104 
   1105  // The operation is complete, clear our reference to the LSObject.
   1106  mObject = nullptr;
   1107 
   1108  if (NS_WARN_IF(NS_FAILED(mResultCode))) {
   1109    return mResultCode;
   1110  }
   1111 
   1112  aResponse = std::move(mResponse);
   1113  return NS_OK;
   1114 }
   1115 
   1116 NS_IMPL_ISUPPORTS_INHERITED0(RequestHelper, Runnable)
   1117 
   1118 NS_IMETHODIMP
   1119 RequestHelper::Run() {
   1120  AssertIsOnDOMFileThread();
   1121 
   1122  MonitorAutoLock lock(mMonitor);
   1123 
   1124  switch (mState) {
   1125    case State::Initial: {
   1126      mState = State::ResponsePending;
   1127      {
   1128        MonitorAutoUnlock unlock(mMonitor);
   1129        mActor = mObject->StartRequest(mParams, this);
   1130      }
   1131      if (NS_WARN_IF(!mActor) && mState != State::Complete) {
   1132        // If we fail to even create the actor, instantly fail and notify our
   1133        // caller of the error. Otherwise we'll notify from OnResponse as called
   1134        // by the actor.
   1135        mResultCode = NS_ERROR_FAILURE;
   1136        mState = State::Complete;
   1137        lock.Notify();
   1138      }
   1139      return NS_OK;
   1140    }
   1141 
   1142    case State::Canceling: {
   1143      // StartRequest() could fail or OnResponse was already called, so we need
   1144      // to check if actor is not null. The actor can also be in the final
   1145      // (finishing) state, in that case we are not allowed to send the cancel
   1146      // message and it wouldn't make any sense because the request is about to
   1147      // be destroyed anyway.
   1148      if (mActor && !mActor->Finishing()) {
   1149        if (mActor->SendCancel()) {
   1150          glean::localstorage_request::send_cancel_counter.Add();
   1151        }
   1152      }
   1153 
   1154      return NS_OK;
   1155    }
   1156 
   1157    case State::Complete: {
   1158      // The operation was cancelled before we ran, do nothing.
   1159      return NS_OK;
   1160    }
   1161 
   1162    default:
   1163      MOZ_CRASH("Bad state!");
   1164  }
   1165 }
   1166 
   1167 void RequestHelper::OnResponse(LSRequestResponse&& aResponse) {
   1168  AssertIsOnDOMFileThread();
   1169 
   1170  MonitorAutoLock lock(mMonitor);
   1171 
   1172  MOZ_ASSERT(mState == State::ResponsePending || mState == State::Canceling);
   1173 
   1174  mActor = nullptr;
   1175 
   1176  mResponse = std::move(aResponse);
   1177 
   1178  mState = State::Complete;
   1179 
   1180  lock.Notify();
   1181 }
   1182 
   1183 }  // namespace mozilla::dom