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/. */
6
7 #include "LSDatabase.h"
8
9 namespace mozilla {
10 namespace dom {
11
12 using namespace mozilla::services;
13
14 namespace {
15
16 #define XPCOM_SHUTDOWN_OBSERVER_TOPIC "xpcom-shutdown"
17
18 typedef nsDataHashtable<nsCStringHashKey, LSDatabase*> LSDatabaseHashtable;
19
20 StaticAutoPtr<LSDatabaseHashtable> gLSDatabases;
21
22 } // namespace
23
24 StaticRefPtr<LSDatabase::Observer> LSDatabase::sObserver;
25
26 class LSDatabase::Observer final : public nsIObserver {
27 bool mInvalidated;
28
29 public:
Observer()30 Observer() : mInvalidated(false) { MOZ_ASSERT(NS_IsMainThread()); }
31
Invalidate()32 void Invalidate() { mInvalidated = true; }
33
34 private:
~Observer()35 ~Observer() { MOZ_ASSERT(NS_IsMainThread()); }
36
37 NS_DECL_ISUPPORTS
38 NS_DECL_NSIOBSERVER
39 };
40
LSDatabase(const nsACString & aOrigin)41 LSDatabase::LSDatabase(const nsACString& aOrigin)
42 : mActor(nullptr),
43 mSnapshot(nullptr),
44 mOrigin(aOrigin),
45 mAllowedToClose(false),
46 mRequestedAllowToClose(false) {
47 AssertIsOnOwningThread();
48
49 if (!gLSDatabases) {
50 gLSDatabases = new LSDatabaseHashtable();
51
52 MOZ_ASSERT(!sObserver);
53
54 sObserver = new Observer();
55
56 nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
57 MOZ_ASSERT(obsSvc);
58
59 MOZ_ALWAYS_SUCCEEDS(
60 obsSvc->AddObserver(sObserver, XPCOM_SHUTDOWN_OBSERVER_TOPIC, false));
61 }
62
63 MOZ_ASSERT(!gLSDatabases->Get(mOrigin));
64 gLSDatabases->Put(mOrigin, this);
65 }
66
~LSDatabase()67 LSDatabase::~LSDatabase() {
68 AssertIsOnOwningThread();
69 MOZ_ASSERT(!mSnapshot);
70
71 if (!mAllowedToClose) {
72 AllowToClose();
73 }
74
75 if (mActor) {
76 mActor->SendDeleteMeInternal();
77 MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!");
78 }
79 }
80
81 // static
Get(const nsACString & aOrigin)82 LSDatabase* LSDatabase::Get(const nsACString& aOrigin) {
83 return gLSDatabases ? gLSDatabases->Get(aOrigin) : nullptr;
84 }
85
SetActor(LSDatabaseChild * aActor)86 void LSDatabase::SetActor(LSDatabaseChild* aActor) {
87 AssertIsOnOwningThread();
88 MOZ_ASSERT(aActor);
89 MOZ_ASSERT(!mActor);
90
91 mActor = aActor;
92 }
93
RequestAllowToClose()94 void LSDatabase::RequestAllowToClose() {
95 AssertIsOnOwningThread();
96
97 if (mRequestedAllowToClose) {
98 return;
99 }
100
101 mRequestedAllowToClose = true;
102
103 if (mSnapshot) {
104 mSnapshot->MarkDirty();
105 } else {
106 AllowToClose();
107 }
108 }
109
NoteFinishedSnapshot(LSSnapshot * aSnapshot)110 void LSDatabase::NoteFinishedSnapshot(LSSnapshot* aSnapshot) {
111 AssertIsOnOwningThread();
112 MOZ_ASSERT(aSnapshot == mSnapshot);
113
114 mSnapshot = nullptr;
115
116 if (mRequestedAllowToClose) {
117 AllowToClose();
118 }
119 }
120
GetLength(LSObject * aObject,uint32_t * aResult)121 nsresult LSDatabase::GetLength(LSObject* aObject, uint32_t* aResult) {
122 AssertIsOnOwningThread();
123 MOZ_ASSERT(aObject);
124 MOZ_ASSERT(mActor);
125 MOZ_ASSERT(!mAllowedToClose);
126
127 nsresult rv = EnsureSnapshot(aObject, VoidString());
128 if (NS_WARN_IF(NS_FAILED(rv))) {
129 return rv;
130 }
131
132 rv = mSnapshot->GetLength(aResult);
133 if (NS_WARN_IF(NS_FAILED(rv))) {
134 return rv;
135 }
136
137 return NS_OK;
138 }
139
GetKey(LSObject * aObject,uint32_t aIndex,nsAString & aResult)140 nsresult LSDatabase::GetKey(LSObject* aObject, uint32_t aIndex,
141 nsAString& aResult) {
142 AssertIsOnOwningThread();
143 MOZ_ASSERT(aObject);
144 MOZ_ASSERT(mActor);
145 MOZ_ASSERT(!mAllowedToClose);
146
147 nsresult rv = EnsureSnapshot(aObject, VoidString());
148 if (NS_WARN_IF(NS_FAILED(rv))) {
149 return rv;
150 }
151
152 rv = mSnapshot->GetKey(aIndex, aResult);
153 if (NS_WARN_IF(NS_FAILED(rv))) {
154 return rv;
155 }
156
157 return NS_OK;
158 }
159
GetItem(LSObject * aObject,const nsAString & aKey,nsAString & aResult)160 nsresult LSDatabase::GetItem(LSObject* aObject, const nsAString& aKey,
161 nsAString& aResult) {
162 AssertIsOnOwningThread();
163 MOZ_ASSERT(aObject);
164 MOZ_ASSERT(mActor);
165 MOZ_ASSERT(!mAllowedToClose);
166
167 nsresult rv = EnsureSnapshot(aObject, aKey);
168 if (NS_WARN_IF(NS_FAILED(rv))) {
169 return rv;
170 }
171
172 rv = mSnapshot->GetItem(aKey, aResult);
173 if (NS_WARN_IF(NS_FAILED(rv))) {
174 return rv;
175 }
176
177 return NS_OK;
178 }
179
GetKeys(LSObject * aObject,nsTArray<nsString> & aKeys)180 nsresult LSDatabase::GetKeys(LSObject* aObject, nsTArray<nsString>& aKeys) {
181 AssertIsOnOwningThread();
182 MOZ_ASSERT(aObject);
183 MOZ_ASSERT(mActor);
184 MOZ_ASSERT(!mAllowedToClose);
185
186 nsresult rv = EnsureSnapshot(aObject, VoidString());
187 if (NS_WARN_IF(NS_FAILED(rv))) {
188 return rv;
189 }
190
191 rv = mSnapshot->GetKeys(aKeys);
192 if (NS_WARN_IF(NS_FAILED(rv))) {
193 return rv;
194 }
195
196 return NS_OK;
197 }
198
SetItem(LSObject * aObject,const nsAString & aKey,const nsAString & aValue,LSNotifyInfo & aNotifyInfo)199 nsresult LSDatabase::SetItem(LSObject* aObject, const nsAString& aKey,
200 const nsAString& aValue,
201 LSNotifyInfo& aNotifyInfo) {
202 AssertIsOnOwningThread();
203 MOZ_ASSERT(aObject);
204 MOZ_ASSERT(mActor);
205 MOZ_ASSERT(!mAllowedToClose);
206
207 nsresult rv = EnsureSnapshot(aObject, aKey);
208 if (NS_WARN_IF(NS_FAILED(rv))) {
209 return rv;
210 }
211
212 rv = mSnapshot->SetItem(aKey, aValue, aNotifyInfo);
213 if (NS_WARN_IF(NS_FAILED(rv))) {
214 return rv;
215 }
216
217 return NS_OK;
218 }
219
RemoveItem(LSObject * aObject,const nsAString & aKey,LSNotifyInfo & aNotifyInfo)220 nsresult LSDatabase::RemoveItem(LSObject* aObject, const nsAString& aKey,
221 LSNotifyInfo& aNotifyInfo) {
222 AssertIsOnOwningThread();
223 MOZ_ASSERT(aObject);
224 MOZ_ASSERT(mActor);
225 MOZ_ASSERT(!mAllowedToClose);
226
227 nsresult rv = EnsureSnapshot(aObject, aKey);
228 if (NS_WARN_IF(NS_FAILED(rv))) {
229 return rv;
230 }
231
232 rv = mSnapshot->RemoveItem(aKey, aNotifyInfo);
233 if (NS_WARN_IF(NS_FAILED(rv))) {
234 return rv;
235 }
236
237 return NS_OK;
238 }
239
Clear(LSObject * aObject,LSNotifyInfo & aNotifyInfo)240 nsresult LSDatabase::Clear(LSObject* aObject, LSNotifyInfo& aNotifyInfo) {
241 AssertIsOnOwningThread();
242 MOZ_ASSERT(aObject);
243 MOZ_ASSERT(mActor);
244 MOZ_ASSERT(!mAllowedToClose);
245
246 nsresult rv = EnsureSnapshot(aObject, VoidString());
247 if (NS_WARN_IF(NS_FAILED(rv))) {
248 return rv;
249 }
250
251 rv = mSnapshot->Clear(aNotifyInfo);
252 if (NS_WARN_IF(NS_FAILED(rv))) {
253 return rv;
254 }
255
256 return NS_OK;
257 }
258
BeginExplicitSnapshot(LSObject * aObject)259 nsresult LSDatabase::BeginExplicitSnapshot(LSObject* aObject) {
260 AssertIsOnOwningThread();
261 MOZ_ASSERT(aObject);
262 MOZ_ASSERT(mActor);
263 MOZ_ASSERT(!mAllowedToClose);
264
265 if (mSnapshot) {
266 return NS_ERROR_ALREADY_INITIALIZED;
267 }
268
269 nsresult rv = EnsureSnapshot(aObject, VoidString(), /* aExplicit */ true);
270 if (NS_WARN_IF(NS_FAILED(rv))) {
271 return rv;
272 }
273
274 return NS_OK;
275 }
276
EndExplicitSnapshot(LSObject * aObject)277 nsresult LSDatabase::EndExplicitSnapshot(LSObject* aObject) {
278 AssertIsOnOwningThread();
279 MOZ_ASSERT(aObject);
280 MOZ_ASSERT(mActor);
281 MOZ_ASSERT(!mAllowedToClose);
282
283 if (!mSnapshot) {
284 return NS_ERROR_NOT_INITIALIZED;
285 }
286
287 MOZ_ASSERT(mSnapshot->Explicit());
288
289 nsresult rv = mSnapshot->End();
290 if (NS_WARN_IF(NS_FAILED(rv))) {
291 return rv;
292 }
293
294 return NS_OK;
295 }
296
EnsureSnapshot(LSObject * aObject,const nsAString & aKey,bool aExplicit)297 nsresult LSDatabase::EnsureSnapshot(LSObject* aObject, const nsAString& aKey,
298 bool aExplicit) {
299 MOZ_ASSERT(aObject);
300 MOZ_ASSERT(mActor);
301 MOZ_ASSERT_IF(mSnapshot, !aExplicit);
302 MOZ_ASSERT(!mAllowedToClose);
303
304 if (mSnapshot) {
305 return NS_OK;
306 }
307
308 RefPtr<LSSnapshot> snapshot = new LSSnapshot(this);
309
310 LSSnapshotChild* actor = new LSSnapshotChild(snapshot);
311
312 LSSnapshotInitInfo initInfo;
313 bool ok = mActor->SendPBackgroundLSSnapshotConstructor(
314 actor, aObject->DocumentURI(), nsString(aKey),
315 /* increasePeakUsage */ true,
316 /* requestedSize */ 131072,
317 /* minSize */ 4096, &initInfo);
318 if (NS_WARN_IF(!ok)) {
319 return NS_ERROR_FAILURE;
320 }
321
322 snapshot->SetActor(actor);
323
324 // This add refs snapshot.
325 nsresult rv = snapshot->Init(aKey, initInfo, aExplicit);
326 if (NS_WARN_IF(NS_FAILED(rv))) {
327 return rv;
328 }
329
330 // This is cleared in LSSnapshot::Run() before the snapshot is destroyed.
331 mSnapshot = snapshot;
332
333 return NS_OK;
334 }
335
AllowToClose()336 void LSDatabase::AllowToClose() {
337 AssertIsOnOwningThread();
338 MOZ_ASSERT(!mAllowedToClose);
339 MOZ_ASSERT(!mSnapshot);
340
341 mAllowedToClose = true;
342
343 if (mActor) {
344 mActor->SendAllowToClose();
345 }
346
347 MOZ_ASSERT(gLSDatabases);
348 MOZ_ASSERT(gLSDatabases->Get(mOrigin));
349 gLSDatabases->Remove(mOrigin);
350
351 if (!gLSDatabases->Count()) {
352 gLSDatabases = nullptr;
353
354 MOZ_ASSERT(sObserver);
355
356 nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
357 MOZ_ASSERT(obsSvc);
358
359 MOZ_ALWAYS_SUCCEEDS(
360 obsSvc->RemoveObserver(sObserver, XPCOM_SHUTDOWN_OBSERVER_TOPIC));
361
362 // We also need to invalidate the observer because AllowToClose can be
363 // triggered by an indirectly related observer, so the observer service
364 // may still keep our observer alive and call Observe on it. This is
365 // possible because observer service snapshots the observer list for given
366 // subject before looping over the list.
367 sObserver->Invalidate();
368
369 sObserver = nullptr;
370 }
371 }
372
NS_IMPL_ISUPPORTS(LSDatabase::Observer,nsIObserver)373 NS_IMPL_ISUPPORTS(LSDatabase::Observer, nsIObserver)
374
375 NS_IMETHODIMP
376 LSDatabase::Observer::Observe(nsISupports* aSubject, const char* aTopic,
377 const char16_t* aData) {
378 MOZ_ASSERT(NS_IsMainThread());
379 MOZ_ASSERT(!strcmp(aTopic, XPCOM_SHUTDOWN_OBSERVER_TOPIC));
380
381 if (mInvalidated) {
382 return NS_OK;
383 }
384
385 MOZ_ASSERT(gLSDatabases);
386
387 nsTArray<RefPtr<LSDatabase>> databases;
388
389 for (auto iter = gLSDatabases->ConstIter(); !iter.Done(); iter.Next()) {
390 LSDatabase* database = iter.Data();
391 MOZ_ASSERT(database);
392
393 databases.AppendElement(database);
394 }
395
396 for (RefPtr<LSDatabase>& database : databases) {
397 database->RequestAllowToClose();
398 }
399
400 return NS_OK;
401 }
402
403 } // namespace dom
404 } // namespace mozilla
405