no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / localstorage / LSObject.cpp
blob196b401e216391b6146dc4df24d5644fe50522c4
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/. */
7 #include "LSObject.h"
9 // Local includes
10 #include "ActorsChild.h"
11 #include "LSDatabase.h"
12 #include "LSObserver.h"
14 // Global includes
15 #include <utility>
16 #include "MainThreadUtils.h"
17 #include "mozilla/BasePrincipal.h"
18 #include "mozilla/ErrorResult.h"
19 #include "mozilla/MacroForEach.h"
20 #include "mozilla/Monitor.h"
21 #include "mozilla/OriginAttributes.h"
22 #include "mozilla/Preferences.h"
23 #include "mozilla/RemoteLazyInputStreamThread.h"
24 #include "mozilla/ScopeExit.h"
25 #include "mozilla/SpinEventLoopUntil.h"
26 #include "mozilla/StaticMutex.h"
27 #include "mozilla/StorageAccess.h"
28 #include "mozilla/Unused.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/QuotaManager.h"
35 #include "mozilla/ipc/BackgroundChild.h"
36 #include "mozilla/ipc/BackgroundUtils.h"
37 #include "mozilla/ipc/PBackgroundChild.h"
38 #include "mozilla/ipc/ProcessChild.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"
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.
68 #define FAILSAFE_CANCEL_SYNC_OP_MS 50000
70 /**
71 * Interval with which to wake up while waiting for the sync op to complete to
72 * check ExpectingShutdown().
74 #define SYNC_OP_WAKE_INTERVAL_MS 500
76 namespace mozilla::dom {
78 namespace {
80 class RequestHelper;
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.
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.
103 * See LocalStorageCommon.h for high-level context and method comments for
104 * low-level details.
106 class RequestHelper final : public Runnable, public LSRequestChildCallback {
107 enum class State {
109 * The RequestHelper has been created and dispatched to the
110 * RemoteLazyInputStream Thread.
112 Initial,
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.
119 ResponsePending,
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.
126 Canceling,
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.
132 Complete
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);
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) {}
163 bool IsOnOwningThread() const {
164 MOZ_ASSERT(mOwningEventTarget);
166 bool current;
167 return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
168 current;
171 void AssertIsOnOwningThread() const {
172 MOZ_ASSERT(NS_IsMainThread());
173 MOZ_ASSERT(IsOnOwningThread());
176 nsresult StartAndReturnResponse(LSRequestResponse& aResponse);
178 private:
179 ~RequestHelper() = default;
181 NS_DECL_ISUPPORTS_INHERITED
183 NS_DECL_NSIRUNNABLE
185 // LSRequestChildCallback
186 void OnResponse(const LSRequestResponse& aResponse) override;
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());
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());
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());
208 } // namespace
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());
219 LSObject::~LSObject() {
220 AssertIsOnOwningThread();
222 DropObserver();
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);
234 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
235 MOZ_ASSERT(sop);
237 nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
238 if (NS_WARN_IF(!principal)) {
239 return NS_ERROR_FAILURE;
242 nsCOMPtr<nsIPrincipal> storagePrincipal = sop->GetEffectiveStoragePrincipal();
243 if (NS_WARN_IF(!storagePrincipal)) {
244 return NS_ERROR_FAILURE;
247 if (principal->IsSystemPrincipal()) {
248 return NS_ERROR_NOT_AVAILABLE;
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);
259 if (NS_FAILED(rv)) {
260 return NS_ERROR_NOT_AVAILABLE;
263 auto principalInfo = MakeUnique<PrincipalInfo>();
264 rv = PrincipalToPrincipalInfo(principal, principalInfo.get());
265 if (NS_WARN_IF(NS_FAILED(rv))) {
266 return rv;
269 MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo);
271 auto storagePrincipalInfo = MakeUnique<PrincipalInfo>();
272 rv = PrincipalToPrincipalInfo(storagePrincipal, storagePrincipalInfo.get());
273 if (NS_WARN_IF(NS_FAILED(rv))) {
274 return rv;
277 MOZ_ASSERT(storagePrincipalInfo->type() ==
278 PrincipalInfo::TContentPrincipalInfo);
280 if (NS_WARN_IF(
281 !quota::QuotaManager::IsPrincipalInfoValid(*storagePrincipalInfo))) {
282 return NS_ERROR_FAILURE;
285 #ifdef DEBUG
286 QM_TRY_INSPECT(
287 const auto& principalMetadata,
288 quota::QuotaManager::GetInfoFromPrincipal(storagePrincipal.get()));
290 MOZ_ASSERT(originAttrSuffix == principalMetadata.mSuffix);
292 const auto& origin = principalMetadata.mOrigin;
293 #else
294 QM_TRY_INSPECT(
295 const auto& origin,
296 quota::QuotaManager::GetOriginFromPrincipal(storagePrincipal.get()));
297 #endif
299 uint32_t privateBrowsingId;
300 rv = storagePrincipal->GetPrivateBrowsingId(&privateBrowsingId);
301 if (NS_WARN_IF(NS_FAILED(rv))) {
302 return rv;
305 Maybe<ClientInfo> clientInfo = aWindow->GetClientInfo();
306 if (clientInfo.isNothing()) {
307 return NS_ERROR_FAILURE;
310 Maybe<nsID> clientId = Some(clientInfo.ref().Id());
312 Maybe<PrincipalInfo> clientPrincipalInfo =
313 Some(clientInfo.ref().PrincipalInfo());
315 nsString documentURI;
316 if (nsCOMPtr<Document> doc = aWindow->GetExtantDoc()) {
317 rv = doc->GetDocumentURI(documentURI);
318 if (NS_WARN_IF(NS_FAILED(rv))) {
319 return rv;
323 RefPtr<LSObject> object = new LSObject(aWindow, principal, storagePrincipal);
324 object->mPrincipalInfo = std::move(principalInfo);
325 object->mStoragePrincipalInfo = std::move(storagePrincipalInfo);
326 object->mPrivateBrowsingId = privateBrowsingId;
327 object->mClientId = clientId;
328 object->mClientPrincipalInfo = clientPrincipalInfo;
329 object->mOrigin = origin;
330 object->mOriginKey = originKey;
331 object->mDocumentURI = documentURI;
333 object.forget(aStorage);
334 return NS_OK;
337 // static
338 nsresult LSObject::CreateForPrincipal(nsPIDOMWindowInner* aWindow,
339 nsIPrincipal* aPrincipal,
340 nsIPrincipal* aStoragePrincipal,
341 const nsAString& aDocumentURI,
342 bool aPrivate, LSObject** aObject) {
343 MOZ_ASSERT(NS_IsMainThread());
344 MOZ_ASSERT(aPrincipal);
345 MOZ_ASSERT(aStoragePrincipal);
346 MOZ_ASSERT(aObject);
348 nsCString originAttrSuffix;
349 nsCString originKey;
350 nsresult rv = aStoragePrincipal->GetStorageOriginKey(originKey);
351 aStoragePrincipal->OriginAttributesRef().CreateSuffix(originAttrSuffix);
352 if (NS_FAILED(rv)) {
353 return NS_ERROR_NOT_AVAILABLE;
356 auto principalInfo = MakeUnique<PrincipalInfo>();
357 rv = PrincipalToPrincipalInfo(aPrincipal, principalInfo.get());
358 if (NS_WARN_IF(NS_FAILED(rv))) {
359 return rv;
362 MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo ||
363 principalInfo->type() == PrincipalInfo::TSystemPrincipalInfo);
365 auto storagePrincipalInfo = MakeUnique<PrincipalInfo>();
366 rv = PrincipalToPrincipalInfo(aStoragePrincipal, storagePrincipalInfo.get());
367 if (NS_WARN_IF(NS_FAILED(rv))) {
368 return rv;
371 MOZ_ASSERT(
372 storagePrincipalInfo->type() == PrincipalInfo::TContentPrincipalInfo ||
373 storagePrincipalInfo->type() == PrincipalInfo::TSystemPrincipalInfo);
375 if (NS_WARN_IF(
376 !quota::QuotaManager::IsPrincipalInfoValid(*storagePrincipalInfo))) {
377 return NS_ERROR_FAILURE;
380 #ifdef DEBUG
381 QM_TRY_INSPECT(
382 const auto& principalMetadata,
383 ([&storagePrincipalInfo,
384 &aPrincipal]() -> Result<quota::PrincipalMetadata, nsresult> {
385 if (storagePrincipalInfo->type() ==
386 PrincipalInfo::TSystemPrincipalInfo) {
387 return quota::QuotaManager::GetInfoForChrome();
390 QM_TRY_RETURN(quota::QuotaManager::GetInfoFromPrincipal(aPrincipal));
391 }()));
393 MOZ_ASSERT(originAttrSuffix == principalMetadata.mSuffix);
395 const auto& origin = principalMetadata.mOrigin;
396 #else
397 QM_TRY_INSPECT(
398 const auto& origin, ([&storagePrincipalInfo,
399 &aPrincipal]() -> Result<nsAutoCString, nsresult> {
400 if (storagePrincipalInfo->type() ==
401 PrincipalInfo::TSystemPrincipalInfo) {
402 return nsAutoCString{quota::QuotaManager::GetOriginForChrome()};
405 QM_TRY_RETURN(quota::QuotaManager::GetOriginFromPrincipal(aPrincipal));
406 }()));
407 #endif
409 Maybe<nsID> clientId;
410 if (aWindow) {
411 Maybe<ClientInfo> clientInfo = aWindow->GetClientInfo();
412 if (clientInfo.isNothing()) {
413 return NS_ERROR_FAILURE;
416 clientId = Some(clientInfo.ref().Id());
417 } else if (Preferences::GetBool("dom.storage.client_validation")) {
418 return NS_ERROR_FAILURE;
421 RefPtr<LSObject> object =
422 new LSObject(aWindow, aPrincipal, aStoragePrincipal);
423 object->mPrincipalInfo = std::move(principalInfo);
424 object->mStoragePrincipalInfo = std::move(storagePrincipalInfo);
425 object->mPrivateBrowsingId = aPrivate ? 1 : 0;
426 object->mClientId = clientId;
427 object->mOrigin = origin;
428 object->mOriginKey = originKey;
429 object->mDocumentURI = aDocumentURI;
431 object.forget(aObject);
432 return NS_OK;
433 } // namespace dom
435 LSRequestChild* LSObject::StartRequest(const LSRequestParams& aParams,
436 LSRequestChildCallback* aCallback) {
437 AssertIsOnDOMFileThread();
439 mozilla::ipc::PBackgroundChild* backgroundActor =
440 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
441 if (NS_WARN_IF(!backgroundActor)) {
442 return nullptr;
445 LSRequestChild* actor = new LSRequestChild();
447 if (!backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams)) {
448 return nullptr;
451 // Must set callback after calling SendPBackgroundLSRequestConstructor since
452 // it can be called synchronously when SendPBackgroundLSRequestConstructor
453 // fails.
454 actor->SetCallback(aCallback);
456 return actor;
459 Storage::StorageType LSObject::Type() const {
460 AssertIsOnOwningThread();
462 return eLocalStorage;
465 bool LSObject::IsForkOf(const Storage* aStorage) const {
466 AssertIsOnOwningThread();
467 MOZ_ASSERT(aStorage);
469 if (aStorage->Type() != eLocalStorage) {
470 return false;
473 return static_cast<const LSObject*>(aStorage)->mOrigin == mOrigin;
476 int64_t LSObject::GetOriginQuotaUsage() const {
477 AssertIsOnOwningThread();
479 // It's not necessary to return an actual value here. This method is
480 // implemented only because the SessionStore currently needs it to cap the
481 // amount of data it persists to disk (via nsIDOMWindowUtils.getStorageUsage).
482 // Any callers that want to know about storage usage should be asking
483 // QuotaManager directly.
485 // Note: This may change as LocalStorage is repurposed to be the new
486 // SessionStorage backend.
487 return 0;
490 void LSObject::Disconnect() {
491 // Explicit snapshots which were not ended in JS, must be ended here while
492 // IPC is still available. We can't do that in DropDatabase because actors
493 // may have been destroyed already at that point.
494 if (mInExplicitSnapshot) {
495 AssertExplicitSnapshotInvariants(*this);
497 nsresult rv = mDatabase->EndExplicitSnapshot();
498 Unused << NS_WARN_IF(NS_FAILED(rv));
500 mInExplicitSnapshot = false;
504 uint32_t LSObject::GetLength(nsIPrincipal& aSubjectPrincipal,
505 ErrorResult& aError) {
506 AssertIsOnOwningThread();
508 if (!CanUseStorage(aSubjectPrincipal)) {
509 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
510 return 0;
513 nsresult rv = EnsureDatabase();
514 if (NS_WARN_IF(NS_FAILED(rv))) {
515 aError.Throw(rv);
516 return 0;
519 uint32_t result;
520 rv = mDatabase->GetLength(this, &result);
521 if (NS_WARN_IF(NS_FAILED(rv))) {
522 aError.Throw(rv);
523 return 0;
526 return result;
529 void LSObject::Key(uint32_t aIndex, nsAString& aResult,
530 nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) {
531 AssertIsOnOwningThread();
533 if (!CanUseStorage(aSubjectPrincipal)) {
534 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
535 return;
538 nsresult rv = EnsureDatabase();
539 if (NS_WARN_IF(NS_FAILED(rv))) {
540 aError.Throw(rv);
541 return;
544 nsString result;
545 rv = mDatabase->GetKey(this, aIndex, result);
546 if (NS_WARN_IF(NS_FAILED(rv))) {
547 aError.Throw(rv);
548 return;
551 aResult = result;
554 void LSObject::GetItem(const nsAString& aKey, nsAString& aResult,
555 nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) {
556 AssertIsOnOwningThread();
558 if (!CanUseStorage(aSubjectPrincipal)) {
559 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
560 return;
563 nsresult rv = EnsureDatabase();
564 if (NS_WARN_IF(NS_FAILED(rv))) {
565 aError.Throw(rv);
566 return;
569 nsString result;
570 rv = mDatabase->GetItem(this, aKey, result);
571 if (NS_WARN_IF(NS_FAILED(rv))) {
572 aError.Throw(rv);
573 return;
576 aResult = result;
579 void LSObject::GetSupportedNames(nsTArray<nsString>& aNames) {
580 AssertIsOnOwningThread();
582 if (!CanUseStorage(*nsContentUtils::SubjectPrincipal())) {
583 // Return just an empty array.
584 aNames.Clear();
585 return;
588 nsresult rv = EnsureDatabase();
589 if (NS_WARN_IF(NS_FAILED(rv))) {
590 return;
593 rv = mDatabase->GetKeys(this, aNames);
594 if (NS_WARN_IF(NS_FAILED(rv))) {
595 return;
599 void LSObject::SetItem(const nsAString& aKey, const nsAString& aValue,
600 nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) {
601 AssertIsOnOwningThread();
603 if (!CanUseStorage(aSubjectPrincipal)) {
604 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
605 return;
608 nsresult rv = EnsureDatabase();
609 if (NS_WARN_IF(NS_FAILED(rv))) {
610 aError.Throw(rv);
611 return;
614 LSNotifyInfo info;
615 rv = mDatabase->SetItem(this, aKey, aValue, info);
616 if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
617 rv = NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
619 if (NS_WARN_IF(NS_FAILED(rv))) {
620 aError.Throw(rv);
621 return;
624 if (info.changed()) {
625 OnChange(aKey, info.oldValue(), aValue);
629 void LSObject::RemoveItem(const nsAString& aKey,
630 nsIPrincipal& aSubjectPrincipal,
631 ErrorResult& aError) {
632 AssertIsOnOwningThread();
634 if (!CanUseStorage(aSubjectPrincipal)) {
635 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
636 return;
639 nsresult rv = EnsureDatabase();
640 if (NS_WARN_IF(NS_FAILED(rv))) {
641 aError.Throw(rv);
642 return;
645 LSNotifyInfo info;
646 rv = mDatabase->RemoveItem(this, aKey, info);
647 if (NS_WARN_IF(NS_FAILED(rv))) {
648 aError.Throw(rv);
649 return;
652 if (info.changed()) {
653 OnChange(aKey, info.oldValue(), VoidString());
657 void LSObject::Clear(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) {
658 AssertIsOnOwningThread();
660 if (!CanUseStorage(aSubjectPrincipal)) {
661 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
662 return;
665 nsresult rv = EnsureDatabase();
666 if (NS_WARN_IF(NS_FAILED(rv))) {
667 aError.Throw(rv);
668 return;
671 LSNotifyInfo info;
672 rv = mDatabase->Clear(this, info);
673 if (NS_WARN_IF(NS_FAILED(rv))) {
674 aError.Throw(rv);
675 return;
678 if (info.changed()) {
679 OnChange(VoidString(), VoidString(), VoidString());
683 void LSObject::Open(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) {
684 AssertIsOnOwningThread();
686 if (!CanUseStorage(aSubjectPrincipal)) {
687 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
688 return;
691 nsresult rv = EnsureDatabase();
692 if (NS_WARN_IF(NS_FAILED(rv))) {
693 aError.Throw(rv);
694 return;
698 void LSObject::Close(nsIPrincipal& aSubjectPrincipal, ErrorResult& aError) {
699 AssertIsOnOwningThread();
701 if (!CanUseStorage(aSubjectPrincipal)) {
702 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
703 return;
706 DropDatabase();
709 void LSObject::BeginExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
710 ErrorResult& aError) {
711 AssertIsOnOwningThread();
713 if (!CanUseStorage(aSubjectPrincipal)) {
714 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
715 return;
718 if (mInExplicitSnapshot) {
719 aError.Throw(NS_ERROR_ALREADY_INITIALIZED);
720 return;
723 nsresult rv = EnsureDatabase();
724 if (NS_WARN_IF(NS_FAILED(rv))) {
725 aError.Throw(rv);
726 return;
729 rv = mDatabase->BeginExplicitSnapshot(this);
730 if (NS_WARN_IF(NS_FAILED(rv))) {
731 aError.Throw(rv);
732 return;
735 mInExplicitSnapshot = true;
738 void LSObject::CheckpointExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
739 ErrorResult& aError) {
740 AssertIsOnOwningThread();
742 if (!CanUseStorage(aSubjectPrincipal)) {
743 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
744 return;
747 if (!mInExplicitSnapshot) {
748 aError.Throw(NS_ERROR_NOT_INITIALIZED);
749 return;
752 AssertExplicitSnapshotInvariants(*this);
754 nsresult rv = mDatabase->CheckpointExplicitSnapshot();
755 if (NS_WARN_IF(NS_FAILED(rv))) {
756 aError.Throw(rv);
757 return;
761 void LSObject::EndExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
762 ErrorResult& aError) {
763 AssertIsOnOwningThread();
765 if (!CanUseStorage(aSubjectPrincipal)) {
766 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
767 return;
770 if (!mInExplicitSnapshot) {
771 aError.Throw(NS_ERROR_NOT_INITIALIZED);
772 return;
775 AssertExplicitSnapshotInvariants(*this);
777 nsresult rv = mDatabase->EndExplicitSnapshot();
778 if (NS_WARN_IF(NS_FAILED(rv))) {
779 aError.Throw(rv);
780 return;
783 mInExplicitSnapshot = false;
786 bool LSObject::GetHasSnapshot(nsIPrincipal& aSubjectPrincipal,
787 ErrorResult& aError) {
788 AssertIsOnOwningThread();
790 if (!CanUseStorage(aSubjectPrincipal)) {
791 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
792 return false;
795 // We can't call `HasSnapshot` on the database if it's being closed, but we
796 // know that a database which is being closed can't have a snapshot, so we
797 // return false in that case directly here.
798 if (!mDatabase || mDatabase->IsAllowedToClose()) {
799 return false;
802 return mDatabase->HasSnapshot();
805 int64_t LSObject::GetSnapshotUsage(nsIPrincipal& aSubjectPrincipal,
806 ErrorResult& aError) {
807 AssertIsOnOwningThread();
809 if (!CanUseStorage(aSubjectPrincipal)) {
810 aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
811 return 0;
814 if (!mDatabase || mDatabase->IsAllowedToClose()) {
815 aError.Throw(NS_ERROR_NOT_AVAILABLE);
816 return 0;
819 if (!mDatabase->HasSnapshot()) {
820 aError.Throw(NS_ERROR_NOT_AVAILABLE);
821 return 0;
824 return mDatabase->GetSnapshotUsage();
827 NS_IMPL_ADDREF_INHERITED(LSObject, Storage)
828 NS_IMPL_RELEASE_INHERITED(LSObject, Storage)
830 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LSObject)
831 NS_INTERFACE_MAP_END_INHERITING(Storage)
833 NS_IMPL_CYCLE_COLLECTION_CLASS(LSObject)
835 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(LSObject, Storage)
836 tmp->AssertIsOnOwningThread();
837 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
839 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(LSObject, Storage)
840 tmp->AssertIsOnOwningThread();
841 tmp->DropDatabase();
842 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
844 nsresult LSObject::DoRequestSynchronously(const LSRequestParams& aParams,
845 LSRequestResponse& aResponse) {
846 // We don't need this yet, but once the request successfully finishes, it's
847 // too late to initialize PBackground child on the owning thread, because
848 // it can fail and parent would keep an extra strong ref to the datastore or
849 // observer.
850 mozilla::ipc::PBackgroundChild* backgroundActor =
851 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
852 if (NS_WARN_IF(!backgroundActor)) {
853 return NS_ERROR_FAILURE;
856 RefPtr<RequestHelper> helper = new RequestHelper(this, aParams);
858 // This will start and finish the request on the RemoteLazyInputStream thread.
859 // The owning thread is synchronously blocked while the request is
860 // asynchronously processed on the RemoteLazyInputStream thread.
861 nsresult rv = helper->StartAndReturnResponse(aResponse);
862 if (NS_WARN_IF(NS_FAILED(rv))) {
863 return rv;
866 if (aResponse.type() == LSRequestResponse::Tnsresult) {
867 nsresult errorCode = aResponse.get_nsresult();
869 if (errorCode == NS_ERROR_FILE_NO_DEVICE_SPACE) {
870 errorCode = NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
873 return errorCode;
876 return NS_OK;
879 nsresult LSObject::EnsureDatabase() {
880 AssertIsOnOwningThread();
882 if (mDatabase && !mDatabase->IsAllowedToClose()) {
883 return NS_OK;
886 mDatabase = LSDatabase::Get(mOrigin);
888 if (mDatabase) {
889 MOZ_ASSERT(!mDatabase->IsAllowedToClose());
890 return NS_OK;
893 // We don't need this yet, but once the request successfully finishes, it's
894 // too late to initialize PBackground child on the owning thread, because
895 // it can fail and parent would keep an extra strong ref to the datastore.
896 mozilla::ipc::PBackgroundChild* backgroundActor =
897 mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
898 if (NS_WARN_IF(!backgroundActor)) {
899 return NS_ERROR_FAILURE;
902 LSRequestCommonParams commonParams;
903 commonParams.principalInfo() = *mPrincipalInfo;
904 commonParams.storagePrincipalInfo() = *mStoragePrincipalInfo;
905 commonParams.originKey() = mOriginKey;
907 LSRequestPrepareDatastoreParams params;
908 params.commonParams() = commonParams;
909 params.clientId() = mClientId;
910 params.clientPrincipalInfo() = mClientPrincipalInfo;
912 LSRequestResponse response;
914 nsresult rv = DoRequestSynchronously(params, response);
915 if (NS_WARN_IF(NS_FAILED(rv))) {
916 return rv;
919 MOZ_ASSERT(response.type() ==
920 LSRequestResponse::TLSRequestPrepareDatastoreResponse);
922 const LSRequestPrepareDatastoreResponse& prepareDatastoreResponse =
923 response.get_LSRequestPrepareDatastoreResponse();
925 uint64_t datastoreId = prepareDatastoreResponse.datastoreId();
927 // The datastore is now ready on the parent side (prepared by the asynchronous
928 // request on the RemoteLazyInputStream thread).
929 // Let's create a direct connection to the datastore (through a database
930 // actor) from the owning thread.
931 // Note that we now can't error out, otherwise parent will keep an extra
932 // strong reference to the datastore.
934 RefPtr<LSDatabase> database = new LSDatabase(mOrigin);
936 LSDatabaseChild* actor = new LSDatabaseChild(database);
938 MOZ_ALWAYS_TRUE(backgroundActor->SendPBackgroundLSDatabaseConstructor(
939 actor, *mStoragePrincipalInfo, mPrivateBrowsingId, datastoreId));
941 database->SetActor(actor);
943 mDatabase = std::move(database);
945 return NS_OK;
948 void LSObject::DropDatabase() {
949 AssertIsOnOwningThread();
951 mDatabase = nullptr;
954 nsresult LSObject::EnsureObserver() {
955 AssertIsOnOwningThread();
957 if (mObserver) {
958 return NS_OK;
961 mObserver = LSObserver::Get(mOrigin);
963 if (mObserver) {
964 return NS_OK;
967 LSRequestPrepareObserverParams params;
968 params.principalInfo() = *mPrincipalInfo;
969 params.storagePrincipalInfo() = *mStoragePrincipalInfo;
970 params.clientId() = mClientId;
971 params.clientPrincipalInfo() = mClientPrincipalInfo;
973 LSRequestResponse response;
975 nsresult rv = DoRequestSynchronously(params, response);
976 if (NS_WARN_IF(NS_FAILED(rv))) {
977 return rv;
980 MOZ_ASSERT(response.type() ==
981 LSRequestResponse::TLSRequestPrepareObserverResponse);
983 const LSRequestPrepareObserverResponse& prepareObserverResponse =
984 response.get_LSRequestPrepareObserverResponse();
986 uint64_t observerId = prepareObserverResponse.observerId();
988 // The obsserver is now ready on the parent side (prepared by the asynchronous
989 // request on the RemoteLazyInputStream thread).
990 // Let's create a direct connection to the observer (through an observer
991 // actor) from the owning thread.
992 // Note that we now can't error out, otherwise parent will keep an extra
993 // strong reference to the observer.
995 mozilla::ipc::PBackgroundChild* backgroundActor =
996 mozilla::ipc::BackgroundChild::GetForCurrentThread();
997 MOZ_ASSERT(backgroundActor);
999 RefPtr<LSObserver> observer = new LSObserver(mOrigin);
1001 LSObserverChild* actor = new LSObserverChild(observer);
1003 MOZ_ALWAYS_TRUE(
1004 backgroundActor->SendPBackgroundLSObserverConstructor(actor, observerId));
1006 observer->SetActor(actor);
1008 mObserver = std::move(observer);
1010 return NS_OK;
1013 void LSObject::DropObserver() {
1014 AssertIsOnOwningThread();
1016 if (mObserver) {
1017 mObserver = nullptr;
1021 void LSObject::OnChange(const nsAString& aKey, const nsAString& aOldValue,
1022 const nsAString& aNewValue) {
1023 AssertIsOnOwningThread();
1025 NotifyChange(/* aStorage */ this, StoragePrincipal(), aKey, aOldValue,
1026 aNewValue, /* aStorageType */ kLocalStorageType, mDocumentURI,
1027 /* aIsPrivate */ !!mPrivateBrowsingId,
1028 /* aImmediateDispatch */ false);
1031 void LSObject::LastRelease() {
1032 AssertIsOnOwningThread();
1034 DropDatabase();
1037 nsresult RequestHelper::StartAndReturnResponse(LSRequestResponse& aResponse) {
1038 AssertIsOnOwningThread();
1040 nsCOMPtr<nsIEventTarget> domFileThread =
1041 RemoteLazyInputStreamThread::GetOrCreate();
1042 if (NS_WARN_IF(!domFileThread)) {
1043 return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
1046 nsresult rv = domFileThread->Dispatch(this, NS_DISPATCH_NORMAL);
1047 if (NS_WARN_IF(NS_FAILED(rv))) {
1048 return rv;
1051 TimeStamp deadline = TimeStamp::Now() + TimeDuration::FromMilliseconds(
1052 FAILSAFE_CANCEL_SYNC_OP_MS);
1054 MonitorAutoLock lock(mMonitor);
1055 while (mState != State::Complete) {
1056 TimeStamp now = TimeStamp::Now();
1057 // If we are expecting shutdown or have passed our deadline, immediately
1058 // dispatch ourselves to the DOM File thread to cancel the operation. We
1059 // don't abort until the cancellation has gone through, as otherwise we
1060 // could race with the DOM File thread.
1061 if (mozilla::ipc::ProcessChild::ExpectingShutdown() || now >= deadline) {
1062 switch (mState) {
1063 case State::Initial:
1064 // The DOM File thread never even woke before ExpectingShutdown() or a
1065 // timeout - skip even creating the actor and just report an error.
1066 mResultCode = NS_ERROR_FAILURE;
1067 mState = State::Complete;
1068 continue;
1069 case State::ResponsePending:
1070 // The DOM File thread is currently waiting for a reply, switch to a
1071 // canceling state, and notify it to cancel by dispatching a runnable.
1072 mState = State::Canceling;
1073 MOZ_ALWAYS_SUCCEEDS(
1074 domFileThread->Dispatch(this, NS_DISPATCH_NORMAL));
1075 [[fallthrough]];
1076 case State::Canceling:
1077 // We've cancelled the request, so just need to wait indefinitely for
1078 // it to complete.
1079 lock.Wait();
1080 continue;
1081 default:
1082 MOZ_ASSERT_UNREACHABLE("unexpected state");
1086 // Wait until either we reach out deadline or for SYNC_OP_WAIT_INTERVAL_MS.
1087 lock.Wait(TimeDuration::Min(
1088 TimeDuration::FromMilliseconds(SYNC_OP_WAKE_INTERVAL_MS),
1089 deadline - now));
1092 // The operation is complete, clear our reference to the LSObject.
1093 mObject = nullptr;
1095 if (NS_WARN_IF(NS_FAILED(mResultCode))) {
1096 return mResultCode;
1099 aResponse = std::move(mResponse);
1100 return NS_OK;
1103 NS_IMPL_ISUPPORTS_INHERITED0(RequestHelper, Runnable)
1105 NS_IMETHODIMP
1106 RequestHelper::Run() {
1107 AssertIsOnDOMFileThread();
1109 MonitorAutoLock lock(mMonitor);
1111 switch (mState) {
1112 case State::Initial: {
1113 mState = State::ResponsePending;
1115 MonitorAutoUnlock unlock(mMonitor);
1116 mActor = mObject->StartRequest(mParams, this);
1118 if (NS_WARN_IF(!mActor) && mState != State::Complete) {
1119 // If we fail to even create the actor, instantly fail and notify our
1120 // caller of the error. Otherwise we'll notify from OnResponse as called
1121 // by the actor.
1122 mResultCode = NS_ERROR_FAILURE;
1123 mState = State::Complete;
1124 lock.Notify();
1126 return NS_OK;
1129 case State::Canceling: {
1130 // StartRequest() could fail or OnResponse was already called, so we need
1131 // to check if actor is not null. The actor can also be in the final
1132 // (finishing) state, in that case we are not allowed to send the cancel
1133 // message and it wouldn't make any sense because the request is about to
1134 // be destroyed anyway.
1135 if (mActor && !mActor->Finishing()) {
1136 mActor->SendCancel();
1138 return NS_OK;
1141 case State::Complete: {
1142 // The operation was cancelled before we ran, do nothing.
1143 return NS_OK;
1146 default:
1147 MOZ_CRASH("Bad state!");
1151 void RequestHelper::OnResponse(const LSRequestResponse& aResponse) {
1152 AssertIsOnDOMFileThread();
1154 MonitorAutoLock lock(mMonitor);
1156 MOZ_ASSERT(mState == State::ResponsePending || mState == State::Canceling);
1158 mActor = nullptr;
1160 mResponse = aResponse;
1162 mState = State::Complete;
1164 lock.Notify();
1167 } // namespace mozilla::dom