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 "LSDatabase.h"
10 #include "ActorsChild.h"
12 #include "LSSnapshot.h"
18 #include "MainThreadUtils.h"
19 #include "mozilla/MacroForEach.h"
20 #include "mozilla/RefPtr.h"
21 #include "mozilla/Services.h"
22 #include "mozilla/StaticPtr.h"
23 #include "mozilla/dom/PBackgroundLSDatabase.h"
24 #include "nsBaseHashtable.h"
26 #include "nsTHashMap.h"
29 #include "nsHashKeys.h"
30 #include "nsIObserver.h"
31 #include "nsIObserverService.h"
36 namespace mozilla::dom
{
40 #define XPCOM_SHUTDOWN_OBSERVER_TOPIC "xpcom-shutdown"
42 using LSDatabaseHashtable
= nsTHashMap
<nsCStringHashKey
, LSDatabase
*>;
44 StaticAutoPtr
<LSDatabaseHashtable
> gLSDatabases
;
48 StaticRefPtr
<LSDatabase::Observer
> LSDatabase::sObserver
;
50 class LSDatabase::Observer final
: public nsIObserver
{
54 Observer() : mInvalidated(false) { MOZ_ASSERT(NS_IsMainThread()); }
56 void Invalidate() { mInvalidated
= true; }
59 ~Observer() { MOZ_ASSERT(NS_IsMainThread()); }
65 LSDatabase::LSDatabase(const nsACString
& aOrigin
)
69 mAllowedToClose(false),
70 mRequestedAllowToClose(false) {
71 AssertIsOnOwningThread();
74 gLSDatabases
= new LSDatabaseHashtable();
76 MOZ_ASSERT(!sObserver
);
78 sObserver
= new Observer();
80 nsCOMPtr
<nsIObserverService
> obsSvc
=
81 mozilla::services::GetObserverService();
85 obsSvc
->AddObserver(sObserver
, XPCOM_SHUTDOWN_OBSERVER_TOPIC
, false));
88 MOZ_ASSERT(!gLSDatabases
->Contains(mOrigin
));
89 gLSDatabases
->InsertOrUpdate(mOrigin
, this);
92 LSDatabase::~LSDatabase() {
93 AssertIsOnOwningThread();
94 MOZ_ASSERT(!mSnapshot
);
96 if (!mAllowedToClose
) {
101 mActor
->SendDeleteMeInternal();
102 MOZ_ASSERT(!mActor
, "SendDeleteMeInternal should have cleared!");
107 LSDatabase
* LSDatabase::Get(const nsACString
& aOrigin
) {
108 return gLSDatabases
? gLSDatabases
->Get(aOrigin
) : nullptr;
111 void LSDatabase::SetActor(LSDatabaseChild
* aActor
) {
112 AssertIsOnOwningThread();
119 void LSDatabase::RequestAllowToClose() {
120 AssertIsOnOwningThread();
122 if (mRequestedAllowToClose
) {
126 mRequestedAllowToClose
= true;
129 mSnapshot
->MarkDirty();
135 void LSDatabase::NoteFinishedSnapshot(LSSnapshot
* aSnapshot
) {
136 AssertIsOnOwningThread();
137 MOZ_ASSERT(aSnapshot
== mSnapshot
);
141 if (mRequestedAllowToClose
) {
146 // All these methods assert `!mAllowedToClose` because they shoudn't be called
147 // if the database is being closed. Callers should first check the state by
148 // calling `IsAlloweToClose` and eventually obtain a new database.
150 nsresult
LSDatabase::GetLength(LSObject
* aObject
, uint32_t* aResult
) {
151 AssertIsOnOwningThread();
154 MOZ_ASSERT(!mAllowedToClose
);
156 nsresult rv
= EnsureSnapshot(aObject
, VoidString());
157 if (NS_WARN_IF(NS_FAILED(rv
))) {
161 rv
= mSnapshot
->GetLength(aResult
);
162 if (NS_WARN_IF(NS_FAILED(rv
))) {
169 nsresult
LSDatabase::GetKey(LSObject
* aObject
, uint32_t aIndex
,
170 nsAString
& aResult
) {
171 AssertIsOnOwningThread();
174 MOZ_ASSERT(!mAllowedToClose
);
176 nsresult rv
= EnsureSnapshot(aObject
, VoidString());
177 if (NS_WARN_IF(NS_FAILED(rv
))) {
181 rv
= mSnapshot
->GetKey(aIndex
, aResult
);
182 if (NS_WARN_IF(NS_FAILED(rv
))) {
189 nsresult
LSDatabase::GetItem(LSObject
* aObject
, const nsAString
& aKey
,
190 nsAString
& aResult
) {
191 AssertIsOnOwningThread();
194 MOZ_ASSERT(!mAllowedToClose
);
196 nsresult rv
= EnsureSnapshot(aObject
, aKey
);
197 if (NS_WARN_IF(NS_FAILED(rv
))) {
201 rv
= mSnapshot
->GetItem(aKey
, aResult
);
202 if (NS_WARN_IF(NS_FAILED(rv
))) {
209 nsresult
LSDatabase::GetKeys(LSObject
* aObject
, nsTArray
<nsString
>& aKeys
) {
210 AssertIsOnOwningThread();
213 MOZ_ASSERT(!mAllowedToClose
);
215 nsresult rv
= EnsureSnapshot(aObject
, VoidString());
216 if (NS_WARN_IF(NS_FAILED(rv
))) {
220 rv
= mSnapshot
->GetKeys(aKeys
);
221 if (NS_WARN_IF(NS_FAILED(rv
))) {
228 nsresult
LSDatabase::SetItem(LSObject
* aObject
, const nsAString
& aKey
,
229 const nsAString
& aValue
,
230 LSNotifyInfo
& aNotifyInfo
) {
231 AssertIsOnOwningThread();
234 MOZ_ASSERT(!mAllowedToClose
);
236 nsresult rv
= EnsureSnapshot(aObject
, aKey
);
237 if (NS_WARN_IF(NS_FAILED(rv
))) {
241 rv
= mSnapshot
->SetItem(aKey
, aValue
, aNotifyInfo
);
242 if (NS_WARN_IF(NS_FAILED(rv
))) {
249 nsresult
LSDatabase::RemoveItem(LSObject
* aObject
, const nsAString
& aKey
,
250 LSNotifyInfo
& aNotifyInfo
) {
251 AssertIsOnOwningThread();
254 MOZ_ASSERT(!mAllowedToClose
);
256 nsresult rv
= EnsureSnapshot(aObject
, aKey
);
257 if (NS_WARN_IF(NS_FAILED(rv
))) {
261 rv
= mSnapshot
->RemoveItem(aKey
, aNotifyInfo
);
262 if (NS_WARN_IF(NS_FAILED(rv
))) {
269 nsresult
LSDatabase::Clear(LSObject
* aObject
, LSNotifyInfo
& aNotifyInfo
) {
270 AssertIsOnOwningThread();
273 MOZ_ASSERT(!mAllowedToClose
);
275 nsresult rv
= EnsureSnapshot(aObject
, VoidString());
276 if (NS_WARN_IF(NS_FAILED(rv
))) {
280 rv
= mSnapshot
->Clear(aNotifyInfo
);
281 if (NS_WARN_IF(NS_FAILED(rv
))) {
288 nsresult
LSDatabase::BeginExplicitSnapshot(LSObject
* aObject
) {
289 AssertIsOnOwningThread();
292 MOZ_ASSERT(!mAllowedToClose
);
293 MOZ_ASSERT(!mSnapshot
);
295 nsresult rv
= EnsureSnapshot(aObject
, VoidString(), /* aExplicit */ true);
296 if (NS_WARN_IF(NS_FAILED(rv
))) {
303 nsresult
LSDatabase::CheckpointExplicitSnapshot() {
304 AssertIsOnOwningThread();
306 MOZ_ASSERT(!mAllowedToClose
);
307 MOZ_ASSERT(mSnapshot
);
308 MOZ_ASSERT(mSnapshot
->Explicit());
310 nsresult rv
= mSnapshot
->ExplicitCheckpoint();
311 if (NS_WARN_IF(NS_FAILED(rv
))) {
318 nsresult
LSDatabase::EndExplicitSnapshot() {
319 AssertIsOnOwningThread();
321 MOZ_ASSERT(!mAllowedToClose
);
322 MOZ_ASSERT(mSnapshot
);
323 MOZ_ASSERT(mSnapshot
->Explicit());
325 nsresult rv
= mSnapshot
->ExplicitEnd();
326 if (NS_WARN_IF(NS_FAILED(rv
))) {
333 bool LSDatabase::HasSnapshot() const {
334 AssertIsOnOwningThread();
336 MOZ_ASSERT(!mAllowedToClose
);
341 int64_t LSDatabase::GetSnapshotUsage() const {
342 AssertIsOnOwningThread();
344 MOZ_ASSERT(!mAllowedToClose
);
345 MOZ_ASSERT(mSnapshot
);
347 return mSnapshot
->GetUsage();
350 nsresult
LSDatabase::EnsureSnapshot(LSObject
* aObject
, const nsAString
& aKey
,
354 MOZ_ASSERT_IF(mSnapshot
, !aExplicit
);
355 MOZ_ASSERT(!mAllowedToClose
);
361 RefPtr
<LSSnapshot
> snapshot
= new LSSnapshot(this);
363 LSSnapshotChild
* actor
= new LSSnapshotChild(snapshot
);
365 LSSnapshotInitInfo initInfo
;
366 bool ok
= mActor
->SendPBackgroundLSSnapshotConstructor(
367 actor
, aObject
->DocumentURI(), nsString(aKey
),
368 /* increasePeakUsage */ true,
369 /* minSize */ 0, &initInfo
);
370 if (NS_WARN_IF(!ok
)) {
371 return NS_ERROR_FAILURE
;
374 snapshot
->SetActor(actor
);
376 // This add refs snapshot.
377 nsresult rv
= snapshot
->Init(aKey
, initInfo
, aExplicit
);
378 if (NS_WARN_IF(NS_FAILED(rv
))) {
382 // This is cleared in LSSnapshot::Run() before the snapshot is destroyed.
383 mSnapshot
= snapshot
;
388 void LSDatabase::AllowToClose() {
389 AssertIsOnOwningThread();
390 MOZ_ASSERT(!mAllowedToClose
);
391 MOZ_ASSERT(!mSnapshot
);
393 mAllowedToClose
= true;
396 mActor
->SendAllowToClose();
399 MOZ_ASSERT(gLSDatabases
);
400 MOZ_ASSERT(gLSDatabases
->Get(mOrigin
));
401 gLSDatabases
->Remove(mOrigin
);
403 if (!gLSDatabases
->Count()) {
404 gLSDatabases
= nullptr;
406 MOZ_ASSERT(sObserver
);
408 nsCOMPtr
<nsIObserverService
> obsSvc
=
409 mozilla::services::GetObserverService();
413 obsSvc
->RemoveObserver(sObserver
, XPCOM_SHUTDOWN_OBSERVER_TOPIC
));
415 // We also need to invalidate the observer because AllowToClose can be
416 // triggered by an indirectly related observer, so the observer service
417 // may still keep our observer alive and call Observe on it. This is
418 // possible because observer service snapshots the observer list for given
419 // subject before looping over the list.
420 sObserver
->Invalidate();
426 NS_IMPL_ISUPPORTS(LSDatabase::Observer
, nsIObserver
)
429 LSDatabase::Observer::Observe(nsISupports
* aSubject
, const char* aTopic
,
430 const char16_t
* aData
) {
431 MOZ_ASSERT(NS_IsMainThread());
432 MOZ_ASSERT(!strcmp(aTopic
, XPCOM_SHUTDOWN_OBSERVER_TOPIC
));
438 MOZ_ASSERT(gLSDatabases
);
440 for (const RefPtr
<LSDatabase
>& database
:
441 ToTArray
<nsTArray
<RefPtr
<LSDatabase
>>>(gLSDatabases
->Values())) {
442 database
->RequestAllowToClose();
448 } // namespace mozilla::dom