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 typedef nsTHashMap
<nsCStringHashKey
, LSDatabase
*> LSDatabaseHashtable
;
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 nsresult
LSDatabase::GetLength(LSObject
* aObject
, uint32_t* aResult
) {
147 AssertIsOnOwningThread();
150 MOZ_ASSERT(!mAllowedToClose
);
152 nsresult rv
= EnsureSnapshot(aObject
, VoidString());
153 if (NS_WARN_IF(NS_FAILED(rv
))) {
157 rv
= mSnapshot
->GetLength(aResult
);
158 if (NS_WARN_IF(NS_FAILED(rv
))) {
165 nsresult
LSDatabase::GetKey(LSObject
* aObject
, uint32_t aIndex
,
166 nsAString
& aResult
) {
167 AssertIsOnOwningThread();
170 MOZ_ASSERT(!mAllowedToClose
);
172 nsresult rv
= EnsureSnapshot(aObject
, VoidString());
173 if (NS_WARN_IF(NS_FAILED(rv
))) {
177 rv
= mSnapshot
->GetKey(aIndex
, aResult
);
178 if (NS_WARN_IF(NS_FAILED(rv
))) {
185 nsresult
LSDatabase::GetItem(LSObject
* aObject
, const nsAString
& aKey
,
186 nsAString
& aResult
) {
187 AssertIsOnOwningThread();
190 MOZ_ASSERT(!mAllowedToClose
);
192 nsresult rv
= EnsureSnapshot(aObject
, aKey
);
193 if (NS_WARN_IF(NS_FAILED(rv
))) {
197 rv
= mSnapshot
->GetItem(aKey
, aResult
);
198 if (NS_WARN_IF(NS_FAILED(rv
))) {
205 nsresult
LSDatabase::GetKeys(LSObject
* aObject
, nsTArray
<nsString
>& aKeys
) {
206 AssertIsOnOwningThread();
209 MOZ_ASSERT(!mAllowedToClose
);
211 nsresult rv
= EnsureSnapshot(aObject
, VoidString());
212 if (NS_WARN_IF(NS_FAILED(rv
))) {
216 rv
= mSnapshot
->GetKeys(aKeys
);
217 if (NS_WARN_IF(NS_FAILED(rv
))) {
224 nsresult
LSDatabase::SetItem(LSObject
* aObject
, const nsAString
& aKey
,
225 const nsAString
& aValue
,
226 LSNotifyInfo
& aNotifyInfo
) {
227 AssertIsOnOwningThread();
230 MOZ_ASSERT(!mAllowedToClose
);
232 nsresult rv
= EnsureSnapshot(aObject
, aKey
);
233 if (NS_WARN_IF(NS_FAILED(rv
))) {
237 rv
= mSnapshot
->SetItem(aKey
, aValue
, aNotifyInfo
);
238 if (NS_WARN_IF(NS_FAILED(rv
))) {
245 nsresult
LSDatabase::RemoveItem(LSObject
* aObject
, const nsAString
& aKey
,
246 LSNotifyInfo
& aNotifyInfo
) {
247 AssertIsOnOwningThread();
250 MOZ_ASSERT(!mAllowedToClose
);
252 nsresult rv
= EnsureSnapshot(aObject
, aKey
);
253 if (NS_WARN_IF(NS_FAILED(rv
))) {
257 rv
= mSnapshot
->RemoveItem(aKey
, aNotifyInfo
);
258 if (NS_WARN_IF(NS_FAILED(rv
))) {
265 nsresult
LSDatabase::Clear(LSObject
* aObject
, LSNotifyInfo
& aNotifyInfo
) {
266 AssertIsOnOwningThread();
269 MOZ_ASSERT(!mAllowedToClose
);
271 nsresult rv
= EnsureSnapshot(aObject
, VoidString());
272 if (NS_WARN_IF(NS_FAILED(rv
))) {
276 rv
= mSnapshot
->Clear(aNotifyInfo
);
277 if (NS_WARN_IF(NS_FAILED(rv
))) {
284 nsresult
LSDatabase::BeginExplicitSnapshot(LSObject
* aObject
) {
285 AssertIsOnOwningThread();
288 MOZ_ASSERT(!mAllowedToClose
);
291 return NS_ERROR_ALREADY_INITIALIZED
;
294 nsresult rv
= EnsureSnapshot(aObject
, VoidString(), /* aExplicit */ true);
295 if (NS_WARN_IF(NS_FAILED(rv
))) {
302 nsresult
LSDatabase::EndExplicitSnapshot(LSObject
* aObject
) {
303 AssertIsOnOwningThread();
306 MOZ_ASSERT(!mAllowedToClose
);
309 return NS_ERROR_NOT_INITIALIZED
;
312 MOZ_ASSERT(mSnapshot
->Explicit());
314 nsresult rv
= mSnapshot
->End();
315 if (NS_WARN_IF(NS_FAILED(rv
))) {
322 nsresult
LSDatabase::EnsureSnapshot(LSObject
* aObject
, const nsAString
& aKey
,
326 MOZ_ASSERT_IF(mSnapshot
, !aExplicit
);
327 MOZ_ASSERT(!mAllowedToClose
);
333 RefPtr
<LSSnapshot
> snapshot
= new LSSnapshot(this);
335 LSSnapshotChild
* actor
= new LSSnapshotChild(snapshot
);
337 LSSnapshotInitInfo initInfo
;
338 bool ok
= mActor
->SendPBackgroundLSSnapshotConstructor(
339 actor
, aObject
->DocumentURI(), nsString(aKey
),
340 /* increasePeakUsage */ true,
341 /* requestedSize */ 131072,
342 /* minSize */ 4096, &initInfo
);
343 if (NS_WARN_IF(!ok
)) {
344 return NS_ERROR_FAILURE
;
347 snapshot
->SetActor(actor
);
349 // This add refs snapshot.
350 nsresult rv
= snapshot
->Init(aKey
, initInfo
, aExplicit
);
351 if (NS_WARN_IF(NS_FAILED(rv
))) {
355 // This is cleared in LSSnapshot::Run() before the snapshot is destroyed.
356 mSnapshot
= snapshot
;
361 void LSDatabase::AllowToClose() {
362 AssertIsOnOwningThread();
363 MOZ_ASSERT(!mAllowedToClose
);
364 MOZ_ASSERT(!mSnapshot
);
366 mAllowedToClose
= true;
369 mActor
->SendAllowToClose();
372 MOZ_ASSERT(gLSDatabases
);
373 MOZ_ASSERT(gLSDatabases
->Get(mOrigin
));
374 gLSDatabases
->Remove(mOrigin
);
376 if (!gLSDatabases
->Count()) {
377 gLSDatabases
= nullptr;
379 MOZ_ASSERT(sObserver
);
381 nsCOMPtr
<nsIObserverService
> obsSvc
=
382 mozilla::services::GetObserverService();
386 obsSvc
->RemoveObserver(sObserver
, XPCOM_SHUTDOWN_OBSERVER_TOPIC
));
388 // We also need to invalidate the observer because AllowToClose can be
389 // triggered by an indirectly related observer, so the observer service
390 // may still keep our observer alive and call Observe on it. This is
391 // possible because observer service snapshots the observer list for given
392 // subject before looping over the list.
393 sObserver
->Invalidate();
399 NS_IMPL_ISUPPORTS(LSDatabase::Observer
, nsIObserver
)
402 LSDatabase::Observer::Observe(nsISupports
* aSubject
, const char* aTopic
,
403 const char16_t
* aData
) {
404 MOZ_ASSERT(NS_IsMainThread());
405 MOZ_ASSERT(!strcmp(aTopic
, XPCOM_SHUTDOWN_OBSERVER_TOPIC
));
411 MOZ_ASSERT(gLSDatabases
);
413 for (const RefPtr
<LSDatabase
>& database
:
414 ToTArray
<nsTArray
<RefPtr
<LSDatabase
>>>(gLSDatabases
->Values())) {
415 database
->RequestAllowToClose();
421 } // namespace mozilla::dom