Bug 1708422: part 14) Extend documentation of `mozInlineSpellChecker::SpellCheckerTim...
[gecko.git] / dom / localstorage / LSDatabase.cpp
blob2bf08b12927e10b03b41fd4580f9d5b65252023a
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 typedef nsTHashMap<nsCStringHashKey, LSDatabase*> LSDatabaseHashtable;
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 nsresult LSDatabase::GetLength(LSObject* aObject, uint32_t* aResult) {
147 AssertIsOnOwningThread();
148 MOZ_ASSERT(aObject);
149 MOZ_ASSERT(mActor);
150 MOZ_ASSERT(!mAllowedToClose);
152 nsresult rv = EnsureSnapshot(aObject, VoidString());
153 if (NS_WARN_IF(NS_FAILED(rv))) {
154 return rv;
157 rv = mSnapshot->GetLength(aResult);
158 if (NS_WARN_IF(NS_FAILED(rv))) {
159 return rv;
162 return NS_OK;
165 nsresult LSDatabase::GetKey(LSObject* aObject, uint32_t aIndex,
166 nsAString& aResult) {
167 AssertIsOnOwningThread();
168 MOZ_ASSERT(aObject);
169 MOZ_ASSERT(mActor);
170 MOZ_ASSERT(!mAllowedToClose);
172 nsresult rv = EnsureSnapshot(aObject, VoidString());
173 if (NS_WARN_IF(NS_FAILED(rv))) {
174 return rv;
177 rv = mSnapshot->GetKey(aIndex, aResult);
178 if (NS_WARN_IF(NS_FAILED(rv))) {
179 return rv;
182 return NS_OK;
185 nsresult LSDatabase::GetItem(LSObject* aObject, const nsAString& aKey,
186 nsAString& aResult) {
187 AssertIsOnOwningThread();
188 MOZ_ASSERT(aObject);
189 MOZ_ASSERT(mActor);
190 MOZ_ASSERT(!mAllowedToClose);
192 nsresult rv = EnsureSnapshot(aObject, aKey);
193 if (NS_WARN_IF(NS_FAILED(rv))) {
194 return rv;
197 rv = mSnapshot->GetItem(aKey, aResult);
198 if (NS_WARN_IF(NS_FAILED(rv))) {
199 return rv;
202 return NS_OK;
205 nsresult LSDatabase::GetKeys(LSObject* aObject, nsTArray<nsString>& aKeys) {
206 AssertIsOnOwningThread();
207 MOZ_ASSERT(aObject);
208 MOZ_ASSERT(mActor);
209 MOZ_ASSERT(!mAllowedToClose);
211 nsresult rv = EnsureSnapshot(aObject, VoidString());
212 if (NS_WARN_IF(NS_FAILED(rv))) {
213 return rv;
216 rv = mSnapshot->GetKeys(aKeys);
217 if (NS_WARN_IF(NS_FAILED(rv))) {
218 return rv;
221 return NS_OK;
224 nsresult LSDatabase::SetItem(LSObject* aObject, const nsAString& aKey,
225 const nsAString& aValue,
226 LSNotifyInfo& aNotifyInfo) {
227 AssertIsOnOwningThread();
228 MOZ_ASSERT(aObject);
229 MOZ_ASSERT(mActor);
230 MOZ_ASSERT(!mAllowedToClose);
232 nsresult rv = EnsureSnapshot(aObject, aKey);
233 if (NS_WARN_IF(NS_FAILED(rv))) {
234 return rv;
237 rv = mSnapshot->SetItem(aKey, aValue, aNotifyInfo);
238 if (NS_WARN_IF(NS_FAILED(rv))) {
239 return rv;
242 return NS_OK;
245 nsresult LSDatabase::RemoveItem(LSObject* aObject, const nsAString& aKey,
246 LSNotifyInfo& aNotifyInfo) {
247 AssertIsOnOwningThread();
248 MOZ_ASSERT(aObject);
249 MOZ_ASSERT(mActor);
250 MOZ_ASSERT(!mAllowedToClose);
252 nsresult rv = EnsureSnapshot(aObject, aKey);
253 if (NS_WARN_IF(NS_FAILED(rv))) {
254 return rv;
257 rv = mSnapshot->RemoveItem(aKey, aNotifyInfo);
258 if (NS_WARN_IF(NS_FAILED(rv))) {
259 return rv;
262 return NS_OK;
265 nsresult LSDatabase::Clear(LSObject* aObject, LSNotifyInfo& aNotifyInfo) {
266 AssertIsOnOwningThread();
267 MOZ_ASSERT(aObject);
268 MOZ_ASSERT(mActor);
269 MOZ_ASSERT(!mAllowedToClose);
271 nsresult rv = EnsureSnapshot(aObject, VoidString());
272 if (NS_WARN_IF(NS_FAILED(rv))) {
273 return rv;
276 rv = mSnapshot->Clear(aNotifyInfo);
277 if (NS_WARN_IF(NS_FAILED(rv))) {
278 return rv;
281 return NS_OK;
284 nsresult LSDatabase::BeginExplicitSnapshot(LSObject* aObject) {
285 AssertIsOnOwningThread();
286 MOZ_ASSERT(aObject);
287 MOZ_ASSERT(mActor);
288 MOZ_ASSERT(!mAllowedToClose);
290 if (mSnapshot) {
291 return NS_ERROR_ALREADY_INITIALIZED;
294 nsresult rv = EnsureSnapshot(aObject, VoidString(), /* aExplicit */ true);
295 if (NS_WARN_IF(NS_FAILED(rv))) {
296 return rv;
299 return NS_OK;
302 nsresult LSDatabase::EndExplicitSnapshot(LSObject* aObject) {
303 AssertIsOnOwningThread();
304 MOZ_ASSERT(aObject);
305 MOZ_ASSERT(mActor);
306 MOZ_ASSERT(!mAllowedToClose);
308 if (!mSnapshot) {
309 return NS_ERROR_NOT_INITIALIZED;
312 MOZ_ASSERT(mSnapshot->Explicit());
314 nsresult rv = mSnapshot->End();
315 if (NS_WARN_IF(NS_FAILED(rv))) {
316 return rv;
319 return NS_OK;
322 nsresult LSDatabase::EnsureSnapshot(LSObject* aObject, const nsAString& aKey,
323 bool aExplicit) {
324 MOZ_ASSERT(aObject);
325 MOZ_ASSERT(mActor);
326 MOZ_ASSERT_IF(mSnapshot, !aExplicit);
327 MOZ_ASSERT(!mAllowedToClose);
329 if (mSnapshot) {
330 return NS_OK;
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))) {
352 return rv;
355 // This is cleared in LSSnapshot::Run() before the snapshot is destroyed.
356 mSnapshot = snapshot;
358 return NS_OK;
361 void LSDatabase::AllowToClose() {
362 AssertIsOnOwningThread();
363 MOZ_ASSERT(!mAllowedToClose);
364 MOZ_ASSERT(!mSnapshot);
366 mAllowedToClose = true;
368 if (mActor) {
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();
383 MOZ_ASSERT(obsSvc);
385 MOZ_ALWAYS_SUCCEEDS(
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();
395 sObserver = nullptr;
399 NS_IMPL_ISUPPORTS(LSDatabase::Observer, nsIObserver)
401 NS_IMETHODIMP
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));
407 if (mInvalidated) {
408 return NS_OK;
411 MOZ_ASSERT(gLSDatabases);
413 for (const RefPtr<LSDatabase>& database :
414 ToTArray<nsTArray<RefPtr<LSDatabase>>>(gLSDatabases->Values())) {
415 database->RequestAllowToClose();
418 return NS_OK;
421 } // namespace mozilla::dom