dom/src/storage/DOMStorageCache.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "DOMStorageCache.h"
michael@0 7
michael@0 8 #include "DOMStorage.h"
michael@0 9 #include "DOMStorageDBThread.h"
michael@0 10 #include "DOMStorageIPC.h"
michael@0 11 #include "DOMStorageManager.h"
michael@0 12
michael@0 13 #include "nsDOMString.h"
michael@0 14 #include "nsXULAppAPI.h"
michael@0 15 #include "mozilla/unused.h"
michael@0 16 #include "nsProxyRelease.h"
michael@0 17 #include "nsThreadUtils.h"
michael@0 18
michael@0 19 namespace mozilla {
michael@0 20 namespace dom {
michael@0 21
michael@0 22 #define DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS 20000
michael@0 23
michael@0 24 // static
michael@0 25 DOMStorageDBBridge* DOMStorageCache::sDatabase = nullptr;
michael@0 26 bool DOMStorageCache::sDatabaseDown = false;
michael@0 27
michael@0 28 namespace { // anon
michael@0 29
michael@0 30 const uint32_t kDefaultSet = 0;
michael@0 31 const uint32_t kPrivateSet = 1;
michael@0 32 const uint32_t kSessionSet = 2;
michael@0 33
michael@0 34 inline uint32_t
michael@0 35 GetDataSetIndex(bool aPrivate, bool aSessionOnly)
michael@0 36 {
michael@0 37 if (aPrivate) {
michael@0 38 return kPrivateSet;
michael@0 39 }
michael@0 40
michael@0 41 if (aSessionOnly) {
michael@0 42 return kSessionSet;
michael@0 43 }
michael@0 44
michael@0 45 return kDefaultSet;
michael@0 46 }
michael@0 47
michael@0 48 inline uint32_t
michael@0 49 GetDataSetIndex(const DOMStorage* aStorage)
michael@0 50 {
michael@0 51 return GetDataSetIndex(aStorage->IsPrivate(), aStorage->IsSessionOnly());
michael@0 52 }
michael@0 53
michael@0 54 } // anon
michael@0 55
michael@0 56 // DOMStorageCacheBridge
michael@0 57
michael@0 58 NS_IMPL_ADDREF(DOMStorageCacheBridge)
michael@0 59
michael@0 60 // Since there is no consumer of return value of Release, we can turn this
michael@0 61 // method to void to make implementation of asynchronous DOMStorageCache::Release
michael@0 62 // much simpler.
michael@0 63 NS_IMETHODIMP_(void) DOMStorageCacheBridge::Release(void)
michael@0 64 {
michael@0 65 MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
michael@0 66 nsrefcnt count = --mRefCnt;
michael@0 67 NS_LOG_RELEASE(this, count, "DOMStorageCacheBridge");
michael@0 68 if (0 == count) {
michael@0 69 mRefCnt = 1; /* stabilize */
michael@0 70 /* enable this to find non-threadsafe destructors: */
michael@0 71 /* NS_ASSERT_OWNINGTHREAD(_class); */
michael@0 72 delete (this);
michael@0 73 }
michael@0 74 }
michael@0 75
michael@0 76 // DOMStorageCache
michael@0 77
michael@0 78 DOMStorageCache::DOMStorageCache(const nsACString* aScope)
michael@0 79 : mScope(*aScope)
michael@0 80 , mMonitor("DOMStorageCache")
michael@0 81 , mLoaded(false)
michael@0 82 , mLoadResult(NS_OK)
michael@0 83 , mInitialized(false)
michael@0 84 , mPersistent(false)
michael@0 85 , mSessionOnlyDataSetActive(false)
michael@0 86 , mPreloadTelemetryRecorded(false)
michael@0 87 {
michael@0 88 MOZ_COUNT_CTOR(DOMStorageCache);
michael@0 89 }
michael@0 90
michael@0 91 DOMStorageCache::~DOMStorageCache()
michael@0 92 {
michael@0 93 if (mManager) {
michael@0 94 mManager->DropCache(this);
michael@0 95 }
michael@0 96
michael@0 97 MOZ_COUNT_DTOR(DOMStorageCache);
michael@0 98 }
michael@0 99
michael@0 100 NS_IMETHODIMP_(void)
michael@0 101 DOMStorageCache::Release(void)
michael@0 102 {
michael@0 103 // We must actually release on the main thread since the cache removes it
michael@0 104 // self from the manager's hash table. And we don't want to lock access to
michael@0 105 // that hash table.
michael@0 106 if (NS_IsMainThread()) {
michael@0 107 DOMStorageCacheBridge::Release();
michael@0 108 return;
michael@0 109 }
michael@0 110
michael@0 111 nsRefPtr<nsRunnableMethod<DOMStorageCacheBridge, void, false> > event =
michael@0 112 NS_NewNonOwningRunnableMethod(static_cast<DOMStorageCacheBridge*>(this),
michael@0 113 &DOMStorageCacheBridge::Release);
michael@0 114
michael@0 115 nsresult rv = NS_DispatchToMainThread(event);
michael@0 116 if (NS_FAILED(rv)) {
michael@0 117 NS_WARNING("DOMStorageCache::Release() on a non-main thread");
michael@0 118 DOMStorageCacheBridge::Release();
michael@0 119 }
michael@0 120 }
michael@0 121
michael@0 122 void
michael@0 123 DOMStorageCache::Init(DOMStorageManager* aManager,
michael@0 124 bool aPersistent,
michael@0 125 nsIURI* aFirstPartyIsolationURI,
michael@0 126 nsIPrincipal* aPrincipal,
michael@0 127 const nsACString& aQuotaScope)
michael@0 128 {
michael@0 129 if (mInitialized) {
michael@0 130 return;
michael@0 131 }
michael@0 132
michael@0 133 mInitialized = true;
michael@0 134 mFirstPartyIsolationURI = aFirstPartyIsolationURI;
michael@0 135 mPrincipal = aPrincipal;
michael@0 136 mPersistent = aPersistent;
michael@0 137 mQuotaScope = aQuotaScope.IsEmpty() ? mScope : aQuotaScope;
michael@0 138
michael@0 139 if (mPersistent) {
michael@0 140 mManager = aManager;
michael@0 141 Preload();
michael@0 142 }
michael@0 143
michael@0 144 mUsage = aManager->GetScopeUsage(mQuotaScope);
michael@0 145 }
michael@0 146
michael@0 147 inline bool
michael@0 148 DOMStorageCache::Persist(const DOMStorage* aStorage) const
michael@0 149 {
michael@0 150 return mPersistent &&
michael@0 151 !aStorage->IsSessionOnly() &&
michael@0 152 !aStorage->IsPrivate();
michael@0 153 }
michael@0 154
michael@0 155 namespace { // anon
michael@0 156
michael@0 157 PLDHashOperator
michael@0 158 CloneSetData(const nsAString& aKey, const nsString aValue, void* aArg)
michael@0 159 {
michael@0 160 DOMStorageCache::Data* target = static_cast<DOMStorageCache::Data*>(aArg);
michael@0 161 target->mKeys.Put(aKey, aValue);
michael@0 162
michael@0 163 return PL_DHASH_NEXT;
michael@0 164 }
michael@0 165
michael@0 166 } // anon
michael@0 167
michael@0 168 DOMStorageCache::Data&
michael@0 169 DOMStorageCache::DataSet(const DOMStorage* aStorage)
michael@0 170 {
michael@0 171 uint32_t index = GetDataSetIndex(aStorage);
michael@0 172
michael@0 173 if (index == kSessionSet && !mSessionOnlyDataSetActive) {
michael@0 174 // Session only data set is demanded but not filled with
michael@0 175 // current data set, copy to session only set now.
michael@0 176
michael@0 177 WaitForPreload(Telemetry::LOCALDOMSTORAGE_SESSIONONLY_PRELOAD_BLOCKING_MS);
michael@0 178
michael@0 179 Data& defaultSet = mData[kDefaultSet];
michael@0 180 Data& sessionSet = mData[kSessionSet];
michael@0 181
michael@0 182 defaultSet.mKeys.EnumerateRead(CloneSetData, &sessionSet);
michael@0 183
michael@0 184 mSessionOnlyDataSetActive = true;
michael@0 185
michael@0 186 // This updates sessionSet.mOriginQuotaUsage and also updates global usage
michael@0 187 // for all session only data
michael@0 188 ProcessUsageDelta(kSessionSet, defaultSet.mOriginQuotaUsage);
michael@0 189 }
michael@0 190
michael@0 191 return mData[index];
michael@0 192 }
michael@0 193
michael@0 194 bool
michael@0 195 DOMStorageCache::ProcessUsageDelta(const DOMStorage* aStorage, int64_t aDelta)
michael@0 196 {
michael@0 197 return ProcessUsageDelta(GetDataSetIndex(aStorage), aDelta);
michael@0 198 }
michael@0 199
michael@0 200 bool
michael@0 201 DOMStorageCache::ProcessUsageDelta(uint32_t aGetDataSetIndex, const int64_t aDelta)
michael@0 202 {
michael@0 203 // Check if we are in a low disk space situation
michael@0 204 if (aDelta > 0 && mManager && mManager->IsLowDiskSpace()) {
michael@0 205 return false;
michael@0 206 }
michael@0 207
michael@0 208 // Check limit per this origin
michael@0 209 Data& data = mData[aGetDataSetIndex];
michael@0 210 uint64_t newOriginUsage = data.mOriginQuotaUsage + aDelta;
michael@0 211 if (aDelta > 0 && newOriginUsage > DOMStorageManager::GetQuota()) {
michael@0 212 return false;
michael@0 213 }
michael@0 214
michael@0 215 // Now check eTLD+1 limit
michael@0 216 if (mUsage && !mUsage->CheckAndSetETLD1UsageDelta(aGetDataSetIndex, aDelta)) {
michael@0 217 return false;
michael@0 218 }
michael@0 219
michael@0 220 // Update size in our data set
michael@0 221 data.mOriginQuotaUsage = newOriginUsage;
michael@0 222 return true;
michael@0 223 }
michael@0 224
michael@0 225 void
michael@0 226 DOMStorageCache::Preload()
michael@0 227 {
michael@0 228 if (mLoaded || !mPersistent) {
michael@0 229 return;
michael@0 230 }
michael@0 231
michael@0 232 if (!StartDatabase()) {
michael@0 233 mLoaded = true;
michael@0 234 mLoadResult = NS_ERROR_FAILURE;
michael@0 235 return;
michael@0 236 }
michael@0 237
michael@0 238 sDatabase->AsyncPreload(this);
michael@0 239 }
michael@0 240
michael@0 241 namespace { // anon
michael@0 242
michael@0 243 // This class is passed to timer as a tick observer. It refers the cache
michael@0 244 // and keeps it alive for a time.
michael@0 245 class DOMStorageCacheHolder : public nsITimerCallback
michael@0 246 {
michael@0 247 NS_DECL_ISUPPORTS
michael@0 248
michael@0 249 NS_IMETHODIMP
michael@0 250 Notify(nsITimer* aTimer)
michael@0 251 {
michael@0 252 mCache = nullptr;
michael@0 253 return NS_OK;
michael@0 254 }
michael@0 255
michael@0 256 virtual ~DOMStorageCacheHolder() {}
michael@0 257
michael@0 258 nsRefPtr<DOMStorageCache> mCache;
michael@0 259
michael@0 260 public:
michael@0 261 DOMStorageCacheHolder(DOMStorageCache* aCache) : mCache(aCache) {}
michael@0 262 };
michael@0 263
michael@0 264 NS_IMPL_ISUPPORTS(DOMStorageCacheHolder, nsITimerCallback)
michael@0 265
michael@0 266 } // anon
michael@0 267
michael@0 268 void
michael@0 269 DOMStorageCache::KeepAlive()
michael@0 270 {
michael@0 271 // Missing reference back to the manager means the cache is not responsible
michael@0 272 // for its lifetime. Used for keeping sessionStorage live forever.
michael@0 273 if (!mManager) {
michael@0 274 return;
michael@0 275 }
michael@0 276
michael@0 277 if (!NS_IsMainThread()) {
michael@0 278 // Timer and the holder must be initialized on the main thread.
michael@0 279 nsRefPtr<nsRunnableMethod<DOMStorageCache> > event =
michael@0 280 NS_NewRunnableMethod(this, &DOMStorageCache::KeepAlive);
michael@0 281
michael@0 282 NS_DispatchToMainThread(event);
michael@0 283 return;
michael@0 284 }
michael@0 285
michael@0 286 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1");
michael@0 287 if (!timer) {
michael@0 288 return;
michael@0 289 }
michael@0 290
michael@0 291 nsRefPtr<DOMStorageCacheHolder> holder = new DOMStorageCacheHolder(this);
michael@0 292 timer->InitWithCallback(holder, DOM_STORAGE_CACHE_KEEP_ALIVE_TIME_MS,
michael@0 293 nsITimer::TYPE_ONE_SHOT);
michael@0 294
michael@0 295 mKeepAliveTimer.swap(timer);
michael@0 296 }
michael@0 297
michael@0 298 namespace { // anon
michael@0 299
michael@0 300 // The AutoTimer provided by telemetry headers is only using static,
michael@0 301 // i.e. compile time known ID, but here we know the ID only at run time.
michael@0 302 // Hence a new class.
michael@0 303 class TelemetryAutoTimer
michael@0 304 {
michael@0 305 public:
michael@0 306 TelemetryAutoTimer(Telemetry::ID aId)
michael@0 307 : id(aId), start(TimeStamp::Now()) {}
michael@0 308 ~TelemetryAutoTimer()
michael@0 309 { Telemetry::AccumulateDelta_impl<Telemetry::Millisecond>::compute(id, start); }
michael@0 310 private:
michael@0 311 Telemetry::ID id;
michael@0 312 const TimeStamp start;
michael@0 313 };
michael@0 314
michael@0 315 } // anon
michael@0 316
michael@0 317 void
michael@0 318 DOMStorageCache::WaitForPreload(Telemetry::ID aTelemetryID)
michael@0 319 {
michael@0 320 if (!mPersistent) {
michael@0 321 return;
michael@0 322 }
michael@0 323
michael@0 324 bool loaded = mLoaded;
michael@0 325
michael@0 326 // Telemetry of rates of pending preloads
michael@0 327 if (!mPreloadTelemetryRecorded) {
michael@0 328 mPreloadTelemetryRecorded = true;
michael@0 329 Telemetry::Accumulate(
michael@0 330 Telemetry::LOCALDOMSTORAGE_PRELOAD_PENDING_ON_FIRST_ACCESS,
michael@0 331 !loaded);
michael@0 332 }
michael@0 333
michael@0 334 if (loaded) {
michael@0 335 return;
michael@0 336 }
michael@0 337
michael@0 338 // Measure which operation blocks and for how long
michael@0 339 TelemetryAutoTimer timer(aTelemetryID);
michael@0 340
michael@0 341 // If preload already started (i.e. we got some first data, but not all)
michael@0 342 // SyncPreload will just wait for it to finish rather then synchronously
michael@0 343 // read from the database. It seems to me more optimal.
michael@0 344
michael@0 345 // TODO place for A/B testing (force main thread load vs. let preload finish)
michael@0 346
michael@0 347 // No need to check sDatabase for being non-null since preload is either
michael@0 348 // done before we've shut the DB down or when the DB could not start,
michael@0 349 // preload has not even be started.
michael@0 350 sDatabase->SyncPreload(this);
michael@0 351 }
michael@0 352
michael@0 353 nsresult
michael@0 354 DOMStorageCache::GetLength(const DOMStorage* aStorage, uint32_t* aRetval)
michael@0 355 {
michael@0 356 Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETLENGTH_MS> autoTimer;
michael@0 357
michael@0 358 if (Persist(aStorage)) {
michael@0 359 WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETLENGTH_BLOCKING_MS);
michael@0 360 if (NS_FAILED(mLoadResult)) {
michael@0 361 return mLoadResult;
michael@0 362 }
michael@0 363 }
michael@0 364
michael@0 365 *aRetval = DataSet(aStorage).mKeys.Count();
michael@0 366 return NS_OK;
michael@0 367 }
michael@0 368
michael@0 369 namespace { // anon
michael@0 370
michael@0 371 class IndexFinderData
michael@0 372 {
michael@0 373 public:
michael@0 374 IndexFinderData(uint32_t aIndex, nsAString& aRetval)
michael@0 375 : mIndex(aIndex), mKey(aRetval)
michael@0 376 {
michael@0 377 mKey.SetIsVoid(true);
michael@0 378 }
michael@0 379
michael@0 380 uint32_t mIndex;
michael@0 381 nsAString& mKey;
michael@0 382 };
michael@0 383
michael@0 384 PLDHashOperator
michael@0 385 FindKeyOrder(const nsAString& aKey, const nsString aValue, void* aArg)
michael@0 386 {
michael@0 387 IndexFinderData* data = static_cast<IndexFinderData*>(aArg);
michael@0 388
michael@0 389 if (data->mIndex--) {
michael@0 390 return PL_DHASH_NEXT;
michael@0 391 }
michael@0 392
michael@0 393 data->mKey = aKey;
michael@0 394 return PL_DHASH_STOP;
michael@0 395 }
michael@0 396
michael@0 397 } // anon
michael@0 398
michael@0 399 nsresult
michael@0 400 DOMStorageCache::GetKey(const DOMStorage* aStorage, uint32_t aIndex, nsAString& aRetval)
michael@0 401 {
michael@0 402 // XXX: This does a linear search for the key at index, which would
michael@0 403 // suck if there's a large numer of indexes. Do we care? If so,
michael@0 404 // maybe we need to have a lazily populated key array here or
michael@0 405 // something?
michael@0 406 Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETKEY_MS> autoTimer;
michael@0 407
michael@0 408 if (Persist(aStorage)) {
michael@0 409 WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETKEY_BLOCKING_MS);
michael@0 410 if (NS_FAILED(mLoadResult)) {
michael@0 411 return mLoadResult;
michael@0 412 }
michael@0 413 }
michael@0 414
michael@0 415 IndexFinderData data(aIndex, aRetval);
michael@0 416 DataSet(aStorage).mKeys.EnumerateRead(FindKeyOrder, &data);
michael@0 417 return NS_OK;
michael@0 418 }
michael@0 419
michael@0 420 namespace { // anon
michael@0 421
michael@0 422 static PLDHashOperator
michael@0 423 KeysArrayBuilder(const nsAString& aKey, const nsString aValue, void* aArg)
michael@0 424 {
michael@0 425 nsTArray<nsString>* keys = static_cast<nsTArray<nsString>* >(aArg);
michael@0 426
michael@0 427 keys->AppendElement(aKey);
michael@0 428 return PL_DHASH_NEXT;
michael@0 429 }
michael@0 430
michael@0 431 } // anon
michael@0 432
michael@0 433 nsTArray<nsString>*
michael@0 434 DOMStorageCache::GetKeys(const DOMStorage* aStorage)
michael@0 435 {
michael@0 436 Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETALLKEYS_MS> autoTimer;
michael@0 437
michael@0 438 if (Persist(aStorage)) {
michael@0 439 WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETALLKEYS_BLOCKING_MS);
michael@0 440 }
michael@0 441
michael@0 442 nsTArray<nsString>* result = new nsTArray<nsString>();
michael@0 443 if (NS_SUCCEEDED(mLoadResult)) {
michael@0 444 DataSet(aStorage).mKeys.EnumerateRead(KeysArrayBuilder, result);
michael@0 445 }
michael@0 446
michael@0 447 return result;
michael@0 448 }
michael@0 449
michael@0 450 nsresult
michael@0 451 DOMStorageCache::GetItem(const DOMStorage* aStorage, const nsAString& aKey,
michael@0 452 nsAString& aRetval)
michael@0 453 {
michael@0 454 Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_GETVALUE_MS> autoTimer;
michael@0 455
michael@0 456 if (Persist(aStorage)) {
michael@0 457 WaitForPreload(Telemetry::LOCALDOMSTORAGE_GETVALUE_BLOCKING_MS);
michael@0 458 if (NS_FAILED(mLoadResult)) {
michael@0 459 return mLoadResult;
michael@0 460 }
michael@0 461 }
michael@0 462
michael@0 463 // not using AutoString since we don't want to copy buffer to result
michael@0 464 nsString value;
michael@0 465 if (!DataSet(aStorage).mKeys.Get(aKey, &value)) {
michael@0 466 SetDOMStringToNull(value);
michael@0 467 }
michael@0 468
michael@0 469 aRetval = value;
michael@0 470
michael@0 471 return NS_OK;
michael@0 472 }
michael@0 473
michael@0 474 nsresult
michael@0 475 DOMStorageCache::SetItem(const DOMStorage* aStorage, const nsAString& aKey,
michael@0 476 const nsString& aValue, nsString& aOld)
michael@0 477 {
michael@0 478 Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_SETVALUE_MS> autoTimer;
michael@0 479
michael@0 480 if (Persist(aStorage)) {
michael@0 481 WaitForPreload(Telemetry::LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS);
michael@0 482 if (NS_FAILED(mLoadResult)) {
michael@0 483 return mLoadResult;
michael@0 484 }
michael@0 485 }
michael@0 486
michael@0 487 Data& data = DataSet(aStorage);
michael@0 488 if (!data.mKeys.Get(aKey, &aOld)) {
michael@0 489 SetDOMStringToNull(aOld);
michael@0 490 }
michael@0 491
michael@0 492 // Check the quota first
michael@0 493 const int64_t delta = static_cast<int64_t>(aValue.Length()) -
michael@0 494 static_cast<int64_t>(aOld.Length());
michael@0 495 if (!ProcessUsageDelta(aStorage, delta)) {
michael@0 496 return NS_ERROR_DOM_QUOTA_REACHED;
michael@0 497 }
michael@0 498
michael@0 499 if (aValue == aOld && DOMStringIsNull(aValue) == DOMStringIsNull(aOld)) {
michael@0 500 return NS_SUCCESS_DOM_NO_OPERATION;
michael@0 501 }
michael@0 502
michael@0 503 data.mKeys.Put(aKey, aValue);
michael@0 504
michael@0 505 if (Persist(aStorage)) {
michael@0 506 if (!sDatabase) {
michael@0 507 NS_ERROR("Writing to localStorage after the database has been shut down"
michael@0 508 ", data lose!");
michael@0 509 return NS_ERROR_NOT_INITIALIZED;
michael@0 510 }
michael@0 511
michael@0 512 if (DOMStringIsNull(aOld)) {
michael@0 513 return sDatabase->AsyncAddItem(this, aKey, aValue);
michael@0 514 }
michael@0 515
michael@0 516 return sDatabase->AsyncUpdateItem(this, aKey, aValue);
michael@0 517 }
michael@0 518
michael@0 519 return NS_OK;
michael@0 520 }
michael@0 521
michael@0 522 nsresult
michael@0 523 DOMStorageCache::RemoveItem(const DOMStorage* aStorage, const nsAString& aKey,
michael@0 524 nsString& aOld)
michael@0 525 {
michael@0 526 Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_REMOVEKEY_MS> autoTimer;
michael@0 527
michael@0 528 if (Persist(aStorage)) {
michael@0 529 WaitForPreload(Telemetry::LOCALDOMSTORAGE_REMOVEKEY_BLOCKING_MS);
michael@0 530 if (NS_FAILED(mLoadResult)) {
michael@0 531 return mLoadResult;
michael@0 532 }
michael@0 533 }
michael@0 534
michael@0 535 Data& data = DataSet(aStorage);
michael@0 536 if (!data.mKeys.Get(aKey, &aOld)) {
michael@0 537 SetDOMStringToNull(aOld);
michael@0 538 return NS_SUCCESS_DOM_NO_OPERATION;
michael@0 539 }
michael@0 540
michael@0 541 // Recalculate the cached data size
michael@0 542 const int64_t delta = -(static_cast<int64_t>(aOld.Length()));
michael@0 543 unused << ProcessUsageDelta(aStorage, delta);
michael@0 544 data.mKeys.Remove(aKey);
michael@0 545
michael@0 546 if (Persist(aStorage)) {
michael@0 547 if (!sDatabase) {
michael@0 548 NS_ERROR("Writing to localStorage after the database has been shut down"
michael@0 549 ", data lose!");
michael@0 550 return NS_ERROR_NOT_INITIALIZED;
michael@0 551 }
michael@0 552
michael@0 553 return sDatabase->AsyncRemoveItem(this, aKey);
michael@0 554 }
michael@0 555
michael@0 556 return NS_OK;
michael@0 557 }
michael@0 558
michael@0 559 nsresult
michael@0 560 DOMStorageCache::Clear(const DOMStorage* aStorage)
michael@0 561 {
michael@0 562 Telemetry::AutoTimer<Telemetry::LOCALDOMSTORAGE_CLEAR_MS> autoTimer;
michael@0 563
michael@0 564 bool refresh = false;
michael@0 565 if (Persist(aStorage)) {
michael@0 566 // We need to preload all data (know the size) before we can proceeed
michael@0 567 // to correctly decrease cached usage number.
michael@0 568 // XXX as in case of unload, this is not technically needed now, but
michael@0 569 // after super-scope quota introduction we have to do this. Get telemetry
michael@0 570 // right now.
michael@0 571 WaitForPreload(Telemetry::LOCALDOMSTORAGE_CLEAR_BLOCKING_MS);
michael@0 572 if (NS_FAILED(mLoadResult)) {
michael@0 573 // When we failed to load data from the database, force delete of the
michael@0 574 // scope data and make use of the storage possible again.
michael@0 575 refresh = true;
michael@0 576 mLoadResult = NS_OK;
michael@0 577 }
michael@0 578 }
michael@0 579
michael@0 580 Data& data = DataSet(aStorage);
michael@0 581 bool hadData = !!data.mKeys.Count();
michael@0 582
michael@0 583 if (hadData) {
michael@0 584 unused << ProcessUsageDelta(aStorage, -data.mOriginQuotaUsage);
michael@0 585 data.mKeys.Clear();
michael@0 586 }
michael@0 587
michael@0 588 if (Persist(aStorage) && (refresh || hadData)) {
michael@0 589 if (!sDatabase) {
michael@0 590 NS_ERROR("Writing to localStorage after the database has been shut down"
michael@0 591 ", data lose!");
michael@0 592 return NS_ERROR_NOT_INITIALIZED;
michael@0 593 }
michael@0 594
michael@0 595 return sDatabase->AsyncClear(this);
michael@0 596 }
michael@0 597
michael@0 598 return hadData ? NS_OK : NS_SUCCESS_DOM_NO_OPERATION;
michael@0 599 }
michael@0 600
michael@0 601 void
michael@0 602 DOMStorageCache::CloneFrom(const DOMStorageCache* aThat)
michael@0 603 {
michael@0 604 mLoaded = aThat->mLoaded;
michael@0 605 mInitialized = aThat->mInitialized;
michael@0 606 mPersistent = aThat->mPersistent;
michael@0 607 mSessionOnlyDataSetActive = aThat->mSessionOnlyDataSetActive;
michael@0 608
michael@0 609 for (uint32_t i = 0; i < kDataSetCount; ++i) {
michael@0 610 aThat->mData[i].mKeys.EnumerateRead(CloneSetData, &mData[i]);
michael@0 611 ProcessUsageDelta(i, aThat->mData[i].mOriginQuotaUsage);
michael@0 612 }
michael@0 613 }
michael@0 614
michael@0 615 // Defined in DOMStorageManager.cpp
michael@0 616 extern bool
michael@0 617 PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal);
michael@0 618
michael@0 619 bool
michael@0 620 DOMStorageCache::CheckPrincipal(nsIPrincipal* aPrincipal) const
michael@0 621 {
michael@0 622 return PrincipalsEqual(mPrincipal, aPrincipal);
michael@0 623 }
michael@0 624
michael@0 625 void
michael@0 626 DOMStorageCache::UnloadItems(uint32_t aUnloadFlags)
michael@0 627 {
michael@0 628 if (aUnloadFlags & kUnloadDefault) {
michael@0 629 // Must wait for preload to pass correct usage to ProcessUsageDelta
michael@0 630 // XXX this is not technically needed right now since there is just
michael@0 631 // per-origin isolated quota handling, but when we introduce super-
michael@0 632 // -scope quotas, we have to do this. Better to start getting
michael@0 633 // telemetry right now.
michael@0 634 WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
michael@0 635
michael@0 636 mData[kDefaultSet].mKeys.Clear();
michael@0 637 ProcessUsageDelta(kDefaultSet, -mData[kDefaultSet].mOriginQuotaUsage);
michael@0 638 }
michael@0 639
michael@0 640 if (aUnloadFlags & kUnloadPrivate) {
michael@0 641 mData[kPrivateSet].mKeys.Clear();
michael@0 642 ProcessUsageDelta(kPrivateSet, -mData[kPrivateSet].mOriginQuotaUsage);
michael@0 643 }
michael@0 644
michael@0 645 if (aUnloadFlags & kUnloadSession) {
michael@0 646 mData[kSessionSet].mKeys.Clear();
michael@0 647 ProcessUsageDelta(kSessionSet, -mData[kSessionSet].mOriginQuotaUsage);
michael@0 648 mSessionOnlyDataSetActive = false;
michael@0 649 }
michael@0 650
michael@0 651 #ifdef DOM_STORAGE_TESTS
michael@0 652 if (aUnloadFlags & kTestReload) {
michael@0 653 WaitForPreload(Telemetry::LOCALDOMSTORAGE_UNLOAD_BLOCKING_MS);
michael@0 654
michael@0 655 mData[kDefaultSet].mKeys.Clear();
michael@0 656 mLoaded = false; // This is only used in testing code
michael@0 657 Preload();
michael@0 658 }
michael@0 659 #endif
michael@0 660 }
michael@0 661
michael@0 662 // DOMStorageCacheBridge
michael@0 663
michael@0 664 uint32_t
michael@0 665 DOMStorageCache::LoadedCount()
michael@0 666 {
michael@0 667 MonitorAutoLock monitor(mMonitor);
michael@0 668 Data& data = mData[kDefaultSet];
michael@0 669 return data.mKeys.Count();
michael@0 670 }
michael@0 671
michael@0 672 bool
michael@0 673 DOMStorageCache::LoadItem(const nsAString& aKey, const nsString& aValue)
michael@0 674 {
michael@0 675 MonitorAutoLock monitor(mMonitor);
michael@0 676 if (mLoaded) {
michael@0 677 return false;
michael@0 678 }
michael@0 679
michael@0 680 Data& data = mData[kDefaultSet];
michael@0 681 if (data.mKeys.Get(aKey, nullptr)) {
michael@0 682 return true; // don't stop, just don't override
michael@0 683 }
michael@0 684
michael@0 685 data.mKeys.Put(aKey, aValue);
michael@0 686 data.mOriginQuotaUsage += aKey.Length() + aValue.Length();
michael@0 687 return true;
michael@0 688 }
michael@0 689
michael@0 690 void
michael@0 691 DOMStorageCache::LoadDone(nsresult aRv)
michael@0 692 {
michael@0 693 // Keep the preloaded cache alive for a time
michael@0 694 KeepAlive();
michael@0 695
michael@0 696 MonitorAutoLock monitor(mMonitor);
michael@0 697 mLoadResult = aRv;
michael@0 698 mLoaded = true;
michael@0 699 monitor.Notify();
michael@0 700 }
michael@0 701
michael@0 702 void
michael@0 703 DOMStorageCache::LoadWait()
michael@0 704 {
michael@0 705 MonitorAutoLock monitor(mMonitor);
michael@0 706 while (!mLoaded) {
michael@0 707 monitor.Wait();
michael@0 708 }
michael@0 709 }
michael@0 710
michael@0 711 // DOMStorageUsage
michael@0 712
michael@0 713 DOMStorageUsage::DOMStorageUsage(const nsACString& aScope)
michael@0 714 : mScope(aScope)
michael@0 715 {
michael@0 716 mUsage[kDefaultSet] = mUsage[kPrivateSet] = mUsage[kSessionSet] = 0LL;
michael@0 717 }
michael@0 718
michael@0 719 namespace { // anon
michael@0 720
michael@0 721 class LoadUsageRunnable : public nsRunnable
michael@0 722 {
michael@0 723 public:
michael@0 724 LoadUsageRunnable(int64_t* aUsage, const int64_t aDelta)
michael@0 725 : mTarget(aUsage)
michael@0 726 , mDelta(aDelta)
michael@0 727 {}
michael@0 728
michael@0 729 private:
michael@0 730 int64_t* mTarget;
michael@0 731 int64_t mDelta;
michael@0 732
michael@0 733 NS_IMETHOD Run() { *mTarget = mDelta; return NS_OK; }
michael@0 734 };
michael@0 735
michael@0 736 } // anon
michael@0 737
michael@0 738 void
michael@0 739 DOMStorageUsage::LoadUsage(const int64_t aUsage)
michael@0 740 {
michael@0 741 // Using kDefaultSet index since it is the index for the persitent data
michael@0 742 // stored in the database we have just loaded usage for.
michael@0 743 if (!NS_IsMainThread()) {
michael@0 744 // In single process scenario we get this call from the DB thread
michael@0 745 nsRefPtr<LoadUsageRunnable> r =
michael@0 746 new LoadUsageRunnable(mUsage + kDefaultSet, aUsage);
michael@0 747 NS_DispatchToMainThread(r);
michael@0 748 } else {
michael@0 749 // On a child process we get this on the main thread already
michael@0 750 mUsage[kDefaultSet] += aUsage;
michael@0 751 }
michael@0 752 }
michael@0 753
michael@0 754 bool
michael@0 755 DOMStorageUsage::CheckAndSetETLD1UsageDelta(uint32_t aDataSetIndex, const int64_t aDelta)
michael@0 756 {
michael@0 757 MOZ_ASSERT(NS_IsMainThread());
michael@0 758
michael@0 759 int64_t newUsage = mUsage[aDataSetIndex] + aDelta;
michael@0 760 if (aDelta > 0 && newUsage > DOMStorageManager::GetQuota()) {
michael@0 761 return false;
michael@0 762 }
michael@0 763
michael@0 764 mUsage[aDataSetIndex] = newUsage;
michael@0 765 return true;
michael@0 766 }
michael@0 767
michael@0 768
michael@0 769 // static
michael@0 770 DOMStorageDBBridge*
michael@0 771 DOMStorageCache::StartDatabase()
michael@0 772 {
michael@0 773 if (sDatabase || sDatabaseDown) {
michael@0 774 // When sDatabaseDown is at true, sDatabase is null.
michael@0 775 // Checking sDatabaseDown flag here prevents reinitialization of
michael@0 776 // the database after shutdown.
michael@0 777 return sDatabase;
michael@0 778 }
michael@0 779
michael@0 780 if (XRE_GetProcessType() == GeckoProcessType_Default) {
michael@0 781 nsAutoPtr<DOMStorageDBThread> db(new DOMStorageDBThread());
michael@0 782
michael@0 783 nsresult rv = db->Init();
michael@0 784 if (NS_FAILED(rv)) {
michael@0 785 return nullptr;
michael@0 786 }
michael@0 787
michael@0 788 sDatabase = db.forget();
michael@0 789 } else {
michael@0 790 nsRefPtr<DOMStorageDBChild> db = new DOMStorageDBChild(
michael@0 791 DOMLocalStorageManager::Self());
michael@0 792
michael@0 793 nsresult rv = db->Init();
michael@0 794 if (NS_FAILED(rv)) {
michael@0 795 return nullptr;
michael@0 796 }
michael@0 797
michael@0 798 db.forget(&sDatabase);
michael@0 799 }
michael@0 800
michael@0 801 return sDatabase;
michael@0 802 }
michael@0 803
michael@0 804 // static
michael@0 805 DOMStorageDBBridge*
michael@0 806 DOMStorageCache::GetDatabase()
michael@0 807 {
michael@0 808 return sDatabase;
michael@0 809 }
michael@0 810
michael@0 811 // static
michael@0 812 nsresult
michael@0 813 DOMStorageCache::StopDatabase()
michael@0 814 {
michael@0 815 if (!sDatabase) {
michael@0 816 return NS_OK;
michael@0 817 }
michael@0 818
michael@0 819 sDatabaseDown = true;
michael@0 820
michael@0 821 nsresult rv = sDatabase->Shutdown();
michael@0 822 if (XRE_GetProcessType() == GeckoProcessType_Default) {
michael@0 823 delete sDatabase;
michael@0 824 } else {
michael@0 825 DOMStorageDBChild* child = static_cast<DOMStorageDBChild*>(sDatabase);
michael@0 826 NS_RELEASE(child);
michael@0 827 }
michael@0 828
michael@0 829 sDatabase = nullptr;
michael@0 830 return rv;
michael@0 831 }
michael@0 832
michael@0 833 } // ::dom
michael@0 834 } // ::mozilla

mercurial