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/. */
10 #include "ActorsChild.h"
11 #include "LSDatabase.h"
12 #include "LSObserver.h"
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"
40 #include "nsContentUtils.h"
43 #include "nsIEventTarget.h"
44 #include "nsIPrincipal.h"
45 #include "nsIRunnable.h"
46 #include "nsIScriptObjectPrincipal.h"
47 #include "nsISerialEventTarget.h"
49 #include "nsPIDOMWindow.h"
52 #include "nsTStringRepr.h"
54 #include "nsThreadUtils.h"
55 #include "nsXULAppAPI.h"
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
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
{
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
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
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
106 class RequestHelper final
: public Runnable
, public LSRequestChildCallback
{
109 * The RequestHelper has been created and dispatched to the
110 * RemoteLazyInputStream Thread.
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.
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.
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.
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
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
;
148 LSRequestResponse mResponse
MOZ_GUARDED_BY(mMonitor
);
149 nsresult mResultCode
MOZ_GUARDED_BY(mMonitor
);
150 State mState
MOZ_GUARDED_BY(mMonitor
);
153 RequestHelper(LSObject
* aObject
, const LSRequestParams
& aParams
)
154 : Runnable("dom::RequestHelper"),
156 mOwningEventTarget(GetCurrentSerialEventTarget()),
159 mMonitor("dom::RequestHelper::mMonitor"),
161 mState(State::Initial
) {}
163 bool IsOnOwningThread() const {
164 MOZ_ASSERT(mOwningEventTarget
);
167 return NS_SUCCEEDED(mOwningEventTarget
->IsOnCurrentThread(¤t
)) &&
171 void AssertIsOnOwningThread() const {
172 MOZ_ASSERT(NS_IsMainThread());
173 MOZ_ASSERT(IsOnOwningThread());
176 nsresult
StartAndReturnResponse(LSRequestResponse
& aResponse
);
179 ~RequestHelper() = default;
181 NS_DECL_ISUPPORTS_INHERITED
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());
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();
226 nsresult
LSObject::CreateForWindow(nsPIDOMWindowInner
* aWindow
,
227 Storage
** aStorage
) {
228 MOZ_ASSERT(NS_IsMainThread());
230 MOZ_ASSERT(aStorage
);
231 MOZ_ASSERT(NextGenLocalStorageEnabled());
232 MOZ_ASSERT(StorageAllowedForWindow(aWindow
) != StorageAccess::eDeny
);
234 nsCOMPtr
<nsIScriptObjectPrincipal
> sop
= do_QueryInterface(aWindow
);
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
254 nsCString originAttrSuffix
;
256 nsresult rv
= storagePrincipal
->GetStorageOriginKey(originKey
);
257 storagePrincipal
->OriginAttributesRef().CreateSuffix(originAttrSuffix
);
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
))) {
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
))) {
277 MOZ_ASSERT(storagePrincipalInfo
->type() ==
278 PrincipalInfo::TContentPrincipalInfo
);
281 !quota::QuotaManager::IsPrincipalInfoValid(*storagePrincipalInfo
))) {
282 return NS_ERROR_FAILURE
;
287 const auto& principalMetadata
,
288 quota::QuotaManager::GetInfoFromPrincipal(storagePrincipal
.get()));
290 MOZ_ASSERT(originAttrSuffix
== principalMetadata
.mSuffix
);
292 const auto& origin
= principalMetadata
.mOrigin
;
296 quota::QuotaManager::GetOriginFromPrincipal(storagePrincipal
.get()));
299 uint32_t privateBrowsingId
;
300 rv
= storagePrincipal
->GetPrivateBrowsingId(&privateBrowsingId
);
301 if (NS_WARN_IF(NS_FAILED(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
))) {
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
);
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
);
348 nsCString originAttrSuffix
;
350 nsresult rv
= aStoragePrincipal
->GetStorageOriginKey(originKey
);
351 aStoragePrincipal
->OriginAttributesRef().CreateSuffix(originAttrSuffix
);
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
))) {
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
))) {
372 storagePrincipalInfo
->type() == PrincipalInfo::TContentPrincipalInfo
||
373 storagePrincipalInfo
->type() == PrincipalInfo::TSystemPrincipalInfo
);
376 !quota::QuotaManager::IsPrincipalInfoValid(*storagePrincipalInfo
))) {
377 return NS_ERROR_FAILURE
;
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
));
393 MOZ_ASSERT(originAttrSuffix
== principalMetadata
.mSuffix
);
395 const auto& origin
= principalMetadata
.mOrigin
;
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
));
409 Maybe
<nsID
> clientId
;
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
);
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
)) {
445 LSRequestChild
* actor
= new LSRequestChild();
447 if (!backgroundActor
->SendPBackgroundLSRequestConstructor(actor
, aParams
)) {
451 // Must set callback after calling SendPBackgroundLSRequestConstructor since
452 // it can be called synchronously when SendPBackgroundLSRequestConstructor
454 actor
->SetCallback(aCallback
);
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
) {
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.
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
);
513 nsresult rv
= EnsureDatabase();
514 if (NS_WARN_IF(NS_FAILED(rv
))) {
520 rv
= mDatabase
->GetLength(this, &result
);
521 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
);
538 nsresult rv
= EnsureDatabase();
539 if (NS_WARN_IF(NS_FAILED(rv
))) {
545 rv
= mDatabase
->GetKey(this, aIndex
, result
);
546 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
);
563 nsresult rv
= EnsureDatabase();
564 if (NS_WARN_IF(NS_FAILED(rv
))) {
570 rv
= mDatabase
->GetItem(this, aKey
, result
);
571 if (NS_WARN_IF(NS_FAILED(rv
))) {
579 void LSObject::GetSupportedNames(nsTArray
<nsString
>& aNames
) {
580 AssertIsOnOwningThread();
582 if (!CanUseStorage(*nsContentUtils::SubjectPrincipal())) {
583 // Return just an empty array.
588 nsresult rv
= EnsureDatabase();
589 if (NS_WARN_IF(NS_FAILED(rv
))) {
593 rv
= mDatabase
->GetKeys(this, aNames
);
594 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
);
608 nsresult rv
= EnsureDatabase();
609 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
))) {
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
);
639 nsresult rv
= EnsureDatabase();
640 if (NS_WARN_IF(NS_FAILED(rv
))) {
646 rv
= mDatabase
->RemoveItem(this, aKey
, info
);
647 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
);
665 nsresult rv
= EnsureDatabase();
666 if (NS_WARN_IF(NS_FAILED(rv
))) {
672 rv
= mDatabase
->Clear(this, info
);
673 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
);
691 nsresult rv
= EnsureDatabase();
692 if (NS_WARN_IF(NS_FAILED(rv
))) {
698 void LSObject::Close(nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aError
) {
699 AssertIsOnOwningThread();
701 if (!CanUseStorage(aSubjectPrincipal
)) {
702 aError
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
709 void LSObject::BeginExplicitSnapshot(nsIPrincipal
& aSubjectPrincipal
,
710 ErrorResult
& aError
) {
711 AssertIsOnOwningThread();
713 if (!CanUseStorage(aSubjectPrincipal
)) {
714 aError
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
718 if (mInExplicitSnapshot
) {
719 aError
.Throw(NS_ERROR_ALREADY_INITIALIZED
);
723 nsresult rv
= EnsureDatabase();
724 if (NS_WARN_IF(NS_FAILED(rv
))) {
729 rv
= mDatabase
->BeginExplicitSnapshot(this);
730 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
);
747 if (!mInExplicitSnapshot
) {
748 aError
.Throw(NS_ERROR_NOT_INITIALIZED
);
752 AssertExplicitSnapshotInvariants(*this);
754 nsresult rv
= mDatabase
->CheckpointExplicitSnapshot();
755 if (NS_WARN_IF(NS_FAILED(rv
))) {
761 void LSObject::EndExplicitSnapshot(nsIPrincipal
& aSubjectPrincipal
,
762 ErrorResult
& aError
) {
763 AssertIsOnOwningThread();
765 if (!CanUseStorage(aSubjectPrincipal
)) {
766 aError
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
770 if (!mInExplicitSnapshot
) {
771 aError
.Throw(NS_ERROR_NOT_INITIALIZED
);
775 AssertExplicitSnapshotInvariants(*this);
777 nsresult rv
= mDatabase
->EndExplicitSnapshot();
778 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
);
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()) {
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
);
814 if (!mDatabase
|| mDatabase
->IsAllowedToClose()) {
815 aError
.Throw(NS_ERROR_NOT_AVAILABLE
);
819 if (!mDatabase
->HasSnapshot()) {
820 aError
.Throw(NS_ERROR_NOT_AVAILABLE
);
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();
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
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
))) {
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
;
879 nsresult
LSObject::EnsureDatabase() {
880 AssertIsOnOwningThread();
882 if (mDatabase
&& !mDatabase
->IsAllowedToClose()) {
886 mDatabase
= LSDatabase::Get(mOrigin
);
889 MOZ_ASSERT(!mDatabase
->IsAllowedToClose());
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
))) {
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
);
948 void LSObject::DropDatabase() {
949 AssertIsOnOwningThread();
954 nsresult
LSObject::EnsureObserver() {
955 AssertIsOnOwningThread();
961 mObserver
= LSObserver::Get(mOrigin
);
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
))) {
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
);
1004 backgroundActor
->SendPBackgroundLSObserverConstructor(actor
, observerId
));
1006 observer
->SetActor(actor
);
1008 mObserver
= std::move(observer
);
1013 void LSObject::DropObserver() {
1014 AssertIsOnOwningThread();
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();
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
))) {
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
) {
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
;
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
));
1076 case State::Canceling
:
1077 // We've cancelled the request, so just need to wait indefinitely for
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
),
1092 // The operation is complete, clear our reference to the LSObject.
1095 if (NS_WARN_IF(NS_FAILED(mResultCode
))) {
1099 aResponse
= std::move(mResponse
);
1103 NS_IMPL_ISUPPORTS_INHERITED0(RequestHelper
, Runnable
)
1106 RequestHelper::Run() {
1107 AssertIsOnDOMFileThread();
1109 MonitorAutoLock
lock(mMonitor
);
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
1122 mResultCode
= NS_ERROR_FAILURE
;
1123 mState
= State::Complete
;
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();
1141 case State::Complete
: {
1142 // The operation was cancelled before we ran, do nothing.
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
);
1160 mResponse
= aResponse
;
1162 mState
= State::Complete
;
1167 } // namespace mozilla::dom