Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / localstorage / LSDatabase.cpp
blob2dccdaad26639c6167553d1b07f58039a299ecff
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"
9 // Local includes
10 #include "ActorsChild.h"
11 #include "LSObject.h"
12 #include "LSSnapshot.h"
14 // Global includes
15 #include <cstring>
16 #include <new>
17 #include <utility>
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"
25 #include "nsCOMPtr.h"
26 #include "nsTHashMap.h"
27 #include "nsDebug.h"
28 #include "nsError.h"
29 #include "nsHashKeys.h"
30 #include "nsIObserver.h"
31 #include "nsIObserverService.h"
32 #include "nsString.h"
33 #include "nsTArray.h"
34 #include "nscore.h"
36 namespace mozilla::dom {
38 namespace {
40 #define XPCOM_SHUTDOWN_OBSERVER_TOPIC "xpcom-shutdown"
42 using LSDatabaseHashtable = nsTHashMap<nsCStringHashKey, LSDatabase*>;
44 StaticAutoPtr<LSDatabaseHashtable> gLSDatabases;
46 } // namespace
48 StaticRefPtr<LSDatabase::Observer> LSDatabase::sObserver;
50 class LSDatabase::Observer final : public nsIObserver {
51 bool mInvalidated;
53 public:
54 Observer() : mInvalidated(false) { MOZ_ASSERT(NS_IsMainThread()); }
56 void Invalidate() { mInvalidated = true; }
58 private:
59 ~Observer() { MOZ_ASSERT(NS_IsMainThread()); }
61 NS_DECL_ISUPPORTS
62 NS_DECL_NSIOBSERVER
65 LSDatabase::LSDatabase(const nsACString& aOrigin)
66 : mActor(nullptr),
67 mSnapshot(nullptr),
68 mOrigin(aOrigin),
69 mAllowedToClose(false),
70 mRequestedAllowToClose(false) {
71 AssertIsOnOwningThread();
73 if (!gLSDatabases) {
74 gLSDatabases = new LSDatabaseHashtable();
76 MOZ_ASSERT(!sObserver);
78 sObserver = new Observer();
80 nsCOMPtr<nsIObserverService> obsSvc =
81 mozilla::services::GetObserverService();
82 MOZ_ASSERT(obsSvc);
84 MOZ_ALWAYS_SUCCEEDS(
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) {
97 AllowToClose();
100 if (mActor) {
101 mActor->SendDeleteMeInternal();
102 MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!");
106 // static
107 LSDatabase* LSDatabase::Get(const nsACString& aOrigin) {
108 return gLSDatabases ? gLSDatabases->Get(aOrigin) : nullptr;
111 void LSDatabase::SetActor(LSDatabaseChild* aActor) {
112 AssertIsOnOwningThread();
113 MOZ_ASSERT(aActor);
114 MOZ_ASSERT(!mActor);
116 mActor = aActor;
119 void LSDatabase::RequestAllowToClose() {
120 AssertIsOnOwningThread();
122 if (mRequestedAllowToClose) {
123 return;
126 mRequestedAllowToClose = true;
128 if (mSnapshot) {
129 mSnapshot->MarkDirty();
130 } else {
131 AllowToClose();
135 void LSDatabase::NoteFinishedSnapshot(LSSnapshot* aSnapshot) {
136 AssertIsOnOwningThread();
137 MOZ_ASSERT(aSnapshot == mSnapshot);
139 mSnapshot = nullptr;
141 if (mRequestedAllowToClose) {
142 AllowToClose();
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();
152 MOZ_ASSERT(aObject);
153 MOZ_ASSERT(mActor);
154 MOZ_ASSERT(!mAllowedToClose);
156 nsresult rv = EnsureSnapshot(aObject, VoidString());
157 if (NS_WARN_IF(NS_FAILED(rv))) {
158 return rv;
161 rv = mSnapshot->GetLength(aResult);
162 if (NS_WARN_IF(NS_FAILED(rv))) {
163 return rv;
166 return NS_OK;
169 nsresult LSDatabase::GetKey(LSObject* aObject, uint32_t aIndex,
170 nsAString& aResult) {
171 AssertIsOnOwningThread();
172 MOZ_ASSERT(aObject);
173 MOZ_ASSERT(mActor);
174 MOZ_ASSERT(!mAllowedToClose);
176 nsresult rv = EnsureSnapshot(aObject, VoidString());
177 if (NS_WARN_IF(NS_FAILED(rv))) {
178 return rv;
181 rv = mSnapshot->GetKey(aIndex, aResult);
182 if (NS_WARN_IF(NS_FAILED(rv))) {
183 return rv;
186 return NS_OK;
189 nsresult LSDatabase::GetItem(LSObject* aObject, const nsAString& aKey,
190 nsAString& aResult) {
191 AssertIsOnOwningThread();
192 MOZ_ASSERT(aObject);
193 MOZ_ASSERT(mActor);
194 MOZ_ASSERT(!mAllowedToClose);
196 nsresult rv = EnsureSnapshot(aObject, aKey);
197 if (NS_WARN_IF(NS_FAILED(rv))) {
198 return rv;
201 rv = mSnapshot->GetItem(aKey, aResult);
202 if (NS_WARN_IF(NS_FAILED(rv))) {
203 return rv;
206 return NS_OK;
209 nsresult LSDatabase::GetKeys(LSObject* aObject, nsTArray<nsString>& aKeys) {
210 AssertIsOnOwningThread();
211 MOZ_ASSERT(aObject);
212 MOZ_ASSERT(mActor);
213 MOZ_ASSERT(!mAllowedToClose);
215 nsresult rv = EnsureSnapshot(aObject, VoidString());
216 if (NS_WARN_IF(NS_FAILED(rv))) {
217 return rv;
220 rv = mSnapshot->GetKeys(aKeys);
221 if (NS_WARN_IF(NS_FAILED(rv))) {
222 return rv;
225 return NS_OK;
228 nsresult LSDatabase::SetItem(LSObject* aObject, const nsAString& aKey,
229 const nsAString& aValue,
230 LSNotifyInfo& aNotifyInfo) {
231 AssertIsOnOwningThread();
232 MOZ_ASSERT(aObject);
233 MOZ_ASSERT(mActor);
234 MOZ_ASSERT(!mAllowedToClose);
236 nsresult rv = EnsureSnapshot(aObject, aKey);
237 if (NS_WARN_IF(NS_FAILED(rv))) {
238 return rv;
241 rv = mSnapshot->SetItem(aKey, aValue, aNotifyInfo);
242 if (NS_WARN_IF(NS_FAILED(rv))) {
243 return rv;
246 return NS_OK;
249 nsresult LSDatabase::RemoveItem(LSObject* aObject, const nsAString& aKey,
250 LSNotifyInfo& aNotifyInfo) {
251 AssertIsOnOwningThread();
252 MOZ_ASSERT(aObject);
253 MOZ_ASSERT(mActor);
254 MOZ_ASSERT(!mAllowedToClose);
256 nsresult rv = EnsureSnapshot(aObject, aKey);
257 if (NS_WARN_IF(NS_FAILED(rv))) {
258 return rv;
261 rv = mSnapshot->RemoveItem(aKey, aNotifyInfo);
262 if (NS_WARN_IF(NS_FAILED(rv))) {
263 return rv;
266 return NS_OK;
269 nsresult LSDatabase::Clear(LSObject* aObject, LSNotifyInfo& aNotifyInfo) {
270 AssertIsOnOwningThread();
271 MOZ_ASSERT(aObject);
272 MOZ_ASSERT(mActor);
273 MOZ_ASSERT(!mAllowedToClose);
275 nsresult rv = EnsureSnapshot(aObject, VoidString());
276 if (NS_WARN_IF(NS_FAILED(rv))) {
277 return rv;
280 rv = mSnapshot->Clear(aNotifyInfo);
281 if (NS_WARN_IF(NS_FAILED(rv))) {
282 return rv;
285 return NS_OK;
288 nsresult LSDatabase::BeginExplicitSnapshot(LSObject* aObject) {
289 AssertIsOnOwningThread();
290 MOZ_ASSERT(aObject);
291 MOZ_ASSERT(mActor);
292 MOZ_ASSERT(!mAllowedToClose);
293 MOZ_ASSERT(!mSnapshot);
295 nsresult rv = EnsureSnapshot(aObject, VoidString(), /* aExplicit */ true);
296 if (NS_WARN_IF(NS_FAILED(rv))) {
297 return rv;
300 return NS_OK;
303 nsresult LSDatabase::CheckpointExplicitSnapshot() {
304 AssertIsOnOwningThread();
305 MOZ_ASSERT(mActor);
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))) {
312 return rv;
315 return NS_OK;
318 nsresult LSDatabase::EndExplicitSnapshot() {
319 AssertIsOnOwningThread();
320 MOZ_ASSERT(mActor);
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))) {
327 return rv;
330 return NS_OK;
333 bool LSDatabase::HasSnapshot() const {
334 AssertIsOnOwningThread();
335 MOZ_ASSERT(mActor);
336 MOZ_ASSERT(!mAllowedToClose);
338 return !!mSnapshot;
341 int64_t LSDatabase::GetSnapshotUsage() const {
342 AssertIsOnOwningThread();
343 MOZ_ASSERT(mActor);
344 MOZ_ASSERT(!mAllowedToClose);
345 MOZ_ASSERT(mSnapshot);
347 return mSnapshot->GetUsage();
350 nsresult LSDatabase::EnsureSnapshot(LSObject* aObject, const nsAString& aKey,
351 bool aExplicit) {
352 MOZ_ASSERT(aObject);
353 MOZ_ASSERT(mActor);
354 MOZ_ASSERT_IF(mSnapshot, !aExplicit);
355 MOZ_ASSERT(!mAllowedToClose);
357 if (mSnapshot) {
358 return NS_OK;
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))) {
379 return rv;
382 // This is cleared in LSSnapshot::Run() before the snapshot is destroyed.
383 mSnapshot = snapshot;
385 return NS_OK;
388 void LSDatabase::AllowToClose() {
389 AssertIsOnOwningThread();
390 MOZ_ASSERT(!mAllowedToClose);
391 MOZ_ASSERT(!mSnapshot);
393 mAllowedToClose = true;
395 if (mActor) {
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();
410 MOZ_ASSERT(obsSvc);
412 MOZ_ALWAYS_SUCCEEDS(
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();
422 sObserver = nullptr;
426 NS_IMPL_ISUPPORTS(LSDatabase::Observer, nsIObserver)
428 NS_IMETHODIMP
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));
434 if (mInvalidated) {
435 return NS_OK;
438 MOZ_ASSERT(gLSDatabases);
440 for (const RefPtr<LSDatabase>& database :
441 ToTArray<nsTArray<RefPtr<LSDatabase>>>(gLSDatabases->Values())) {
442 database->RequestAllowToClose();
445 return NS_OK;
448 } // namespace mozilla::dom