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