dom/src/storage/DOMStorageManager.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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 "DOMStorageManager.h"
michael@0 7 #include "DOMStorage.h"
michael@0 8 #include "DOMStorageDBThread.h"
michael@0 9
michael@0 10 #include "nsIScriptSecurityManager.h"
michael@0 11 #include "nsIEffectiveTLDService.h"
michael@0 12
michael@0 13 #include "nsNetUtil.h"
michael@0 14 #include "nsPrintfCString.h"
michael@0 15 #include "nsXULAppAPI.h"
michael@0 16 #include "nsThreadUtils.h"
michael@0 17 #include "nsIObserverService.h"
michael@0 18 #include "mozIThirdPartyUtil.h"
michael@0 19 #include "mozilla/Services.h"
michael@0 20 #include "mozilla/Preferences.h"
michael@0 21
michael@0 22 // Only allow relatively small amounts of data since performance of
michael@0 23 // the synchronous IO is very bad.
michael@0 24 // We are enforcing simple per-origin quota only.
michael@0 25 #define DEFAULT_QUOTA_LIMIT (5 * 1024)
michael@0 26
michael@0 27 namespace mozilla {
michael@0 28 namespace dom {
michael@0 29
michael@0 30 namespace { // anon
michael@0 31
michael@0 32 int32_t gQuotaLimit = DEFAULT_QUOTA_LIMIT;
michael@0 33
michael@0 34 } // anon
michael@0 35
michael@0 36 DOMLocalStorageManager*
michael@0 37 DOMLocalStorageManager::sSelf = nullptr;
michael@0 38
michael@0 39 // static
michael@0 40 uint32_t
michael@0 41 DOMStorageManager::GetQuota()
michael@0 42 {
michael@0 43 static bool preferencesInitialized = false;
michael@0 44 if (!preferencesInitialized) {
michael@0 45 mozilla::Preferences::AddIntVarCache(&gQuotaLimit, "dom.storage.default_quota",
michael@0 46 DEFAULT_QUOTA_LIMIT);
michael@0 47 preferencesInitialized = true;
michael@0 48 }
michael@0 49
michael@0 50 return gQuotaLimit * 1024; // pref is in kBs
michael@0 51 }
michael@0 52
michael@0 53 void
michael@0 54 ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult)
michael@0 55 {
michael@0 56 nsACString::const_iterator sourceBegin, sourceEnd;
michael@0 57 aSource.BeginReading(sourceBegin);
michael@0 58 aSource.EndReading(sourceEnd);
michael@0 59
michael@0 60 aResult.SetLength(aSource.Length());
michael@0 61 nsACString::iterator destEnd;
michael@0 62 aResult.EndWriting(destEnd);
michael@0 63
michael@0 64 while (sourceBegin != sourceEnd) {
michael@0 65 *(--destEnd) = *sourceBegin;
michael@0 66 ++sourceBegin;
michael@0 67 }
michael@0 68 }
michael@0 69
michael@0 70 nsresult
michael@0 71 CreateReversedDomain(const nsACString& aAsciiDomain,
michael@0 72 nsACString& aKey)
michael@0 73 {
michael@0 74 if (aAsciiDomain.IsEmpty()) {
michael@0 75 return NS_ERROR_NOT_AVAILABLE;
michael@0 76 }
michael@0 77
michael@0 78 ReverseString(aAsciiDomain, aKey);
michael@0 79
michael@0 80 aKey.AppendLiteral(".");
michael@0 81 return NS_OK;
michael@0 82 }
michael@0 83
michael@0 84 bool
michael@0 85 PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal)
michael@0 86 {
michael@0 87 if (!aSubjectPrincipal) {
michael@0 88 return true;
michael@0 89 }
michael@0 90
michael@0 91 if (!aObjectPrincipal) {
michael@0 92 return false;
michael@0 93 }
michael@0 94
michael@0 95 return aSubjectPrincipal->Equals(aObjectPrincipal);
michael@0 96 }
michael@0 97
michael@0 98 NS_IMPL_ISUPPORTS(DOMStorageManager,
michael@0 99 nsIDOMStorageManager)
michael@0 100
michael@0 101 DOMStorageManager::DOMStorageManager(nsPIDOMStorage::StorageType aType)
michael@0 102 : mCaches(10)
michael@0 103 , mType(aType)
michael@0 104 , mLowDiskSpace(false)
michael@0 105 {
michael@0 106 DOMStorageObserver* observer = DOMStorageObserver::Self();
michael@0 107 NS_ASSERTION(observer, "No DOMStorageObserver, cannot observe private data delete notifications!");
michael@0 108
michael@0 109 if (observer) {
michael@0 110 observer->AddSink(this);
michael@0 111 }
michael@0 112 }
michael@0 113
michael@0 114 DOMStorageManager::~DOMStorageManager()
michael@0 115 {
michael@0 116 DOMStorageObserver* observer = DOMStorageObserver::Self();
michael@0 117 if (observer) {
michael@0 118 observer->RemoveSink(this);
michael@0 119 }
michael@0 120 }
michael@0 121
michael@0 122 namespace { // anon
michael@0 123
michael@0 124 nsresult
michael@0 125 AppendFirstPartyToKey(nsIURI* aFirstPartyIsolationURI, nsACString& aKey)
michael@0 126 {
michael@0 127 if (aFirstPartyIsolationURI) {
michael@0 128 nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
michael@0 129 do_GetService(THIRDPARTYUTIL_CONTRACTID);
michael@0 130 if (!thirdPartyUtil)
michael@0 131 return NS_ERROR_FAILURE;
michael@0 132
michael@0 133 nsAutoCString firstPartyHost;
michael@0 134 nsresult rv = thirdPartyUtil->GetFirstPartyHostForIsolation(aFirstPartyIsolationURI,
michael@0 135 firstPartyHost);
michael@0 136 NS_ENSURE_SUCCESS(rv, rv);
michael@0 137
michael@0 138 aKey.AppendLiteral("&");
michael@0 139 aKey.Append(firstPartyHost);
michael@0 140 }
michael@0 141
michael@0 142 return NS_OK;
michael@0 143 }
michael@0 144
michael@0 145 nsresult
michael@0 146 CreateScopeKey(nsIURI* aFirstPartyIsolationURI, nsIPrincipal* aPrincipal,
michael@0 147 nsACString& aKey)
michael@0 148 {
michael@0 149 nsCOMPtr<nsIURI> uri;
michael@0 150 nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
michael@0 151 NS_ENSURE_SUCCESS(rv, rv);
michael@0 152 if (!uri) {
michael@0 153 return NS_ERROR_UNEXPECTED;
michael@0 154 }
michael@0 155
michael@0 156 nsAutoCString domainScope;
michael@0 157 rv = uri->GetAsciiHost(domainScope);
michael@0 158 NS_ENSURE_SUCCESS(rv, rv);
michael@0 159
michael@0 160 if (domainScope.IsEmpty()) {
michael@0 161 // For the file:/// protocol use the exact directory as domain.
michael@0 162 bool isScheme = false;
michael@0 163 if (NS_SUCCEEDED(uri->SchemeIs("file", &isScheme)) && isScheme) {
michael@0 164 nsCOMPtr<nsIURL> url = do_QueryInterface(uri, &rv);
michael@0 165 NS_ENSURE_SUCCESS(rv, rv);
michael@0 166 rv = url->GetDirectory(domainScope);
michael@0 167 NS_ENSURE_SUCCESS(rv, rv);
michael@0 168 }
michael@0 169 }
michael@0 170
michael@0 171 nsAutoCString key;
michael@0 172
michael@0 173 rv = CreateReversedDomain(domainScope, key);
michael@0 174 if (NS_FAILED(rv)) {
michael@0 175 return rv;
michael@0 176 }
michael@0 177
michael@0 178 nsAutoCString scheme;
michael@0 179 rv = uri->GetScheme(scheme);
michael@0 180 NS_ENSURE_SUCCESS(rv, rv);
michael@0 181
michael@0 182 key.Append(NS_LITERAL_CSTRING(":") + scheme);
michael@0 183
michael@0 184 int32_t port = NS_GetRealPort(uri);
michael@0 185 if (port != -1) {
michael@0 186 key.Append(nsPrintfCString(":%d", port));
michael@0 187 }
michael@0 188
michael@0 189 bool unknownAppId;
michael@0 190 rv = aPrincipal->GetUnknownAppId(&unknownAppId);
michael@0 191 NS_ENSURE_SUCCESS(rv, rv);
michael@0 192
michael@0 193 if (!unknownAppId) {
michael@0 194 uint32_t appId;
michael@0 195 rv = aPrincipal->GetAppId(&appId);
michael@0 196 NS_ENSURE_SUCCESS(rv, rv);
michael@0 197
michael@0 198 bool isInBrowserElement;
michael@0 199 rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
michael@0 200 NS_ENSURE_SUCCESS(rv, rv);
michael@0 201
michael@0 202 if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
michael@0 203 aKey.Assign(key);
michael@0 204 } else {
michael@0 205 aKey.Truncate();
michael@0 206 aKey.AppendInt(appId);
michael@0 207 aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
michael@0 208 NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
michael@0 209 NS_LITERAL_CSTRING(":") + key);
michael@0 210 }
michael@0 211 }
michael@0 212
michael@0 213 // Isolate scope keys to the URL bar domain by appending &firstPartyHost
michael@0 214 // if available.
michael@0 215 return AppendFirstPartyToKey(aFirstPartyIsolationURI, aKey);
michael@0 216 }
michael@0 217
michael@0 218 nsresult
michael@0 219 CreateQuotaDBKey(nsIURI* aFirstPartyIsolationURI, nsIPrincipal* aPrincipal,
michael@0 220 nsACString& aKey)
michael@0 221 {
michael@0 222 nsresult rv;
michael@0 223
michael@0 224 nsAutoCString subdomainsDBKey;
michael@0 225 nsCOMPtr<nsIEffectiveTLDService> eTLDService(do_GetService(
michael@0 226 NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv));
michael@0 227 NS_ENSURE_SUCCESS(rv, rv);
michael@0 228
michael@0 229 nsCOMPtr<nsIURI> uri;
michael@0 230 rv = aPrincipal->GetURI(getter_AddRefs(uri));
michael@0 231 NS_ENSURE_SUCCESS(rv, rv);
michael@0 232 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
michael@0 233
michael@0 234 nsAutoCString eTLDplusOne;
michael@0 235 rv = eTLDService->GetBaseDomain(uri, 0, eTLDplusOne);
michael@0 236 if (NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS == rv) {
michael@0 237 // XXX bug 357323 - what to do for localhost/file exactly?
michael@0 238 rv = uri->GetAsciiHost(eTLDplusOne);
michael@0 239 }
michael@0 240 NS_ENSURE_SUCCESS(rv, rv);
michael@0 241
michael@0 242 CreateReversedDomain(eTLDplusOne, subdomainsDBKey);
michael@0 243
michael@0 244 bool unknownAppId;
michael@0 245 rv = aPrincipal->GetUnknownAppId(&unknownAppId);
michael@0 246 NS_ENSURE_SUCCESS(rv, rv);
michael@0 247
michael@0 248 if (!unknownAppId) {
michael@0 249 uint32_t appId;
michael@0 250 rv = aPrincipal->GetAppId(&appId);
michael@0 251 NS_ENSURE_SUCCESS(rv, rv);
michael@0 252
michael@0 253 bool isInBrowserElement;
michael@0 254 rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
michael@0 255 NS_ENSURE_SUCCESS(rv, rv);
michael@0 256
michael@0 257 if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
michael@0 258 aKey.Assign(subdomainsDBKey);
michael@0 259 } else {
michael@0 260 aKey.Truncate();
michael@0 261 aKey.AppendInt(appId);
michael@0 262 aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
michael@0 263 NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
michael@0 264 NS_LITERAL_CSTRING(":") + subdomainsDBKey);
michael@0 265 }
michael@0 266 }
michael@0 267
michael@0 268 // Isolate scope keys to the URL bar domain by appending &firstPartyHost
michael@0 269 // if available.
michael@0 270 return AppendFirstPartyToKey(aFirstPartyIsolationURI, aKey);
michael@0 271 }
michael@0 272
michael@0 273 } // anon
michael@0 274
michael@0 275 DOMStorageCache*
michael@0 276 DOMStorageManager::GetCache(const nsACString& aScope) const
michael@0 277 {
michael@0 278 DOMStorageCacheHashKey* entry = mCaches.GetEntry(aScope);
michael@0 279 if (!entry) {
michael@0 280 return nullptr;
michael@0 281 }
michael@0 282
michael@0 283 return entry->cache();
michael@0 284 }
michael@0 285
michael@0 286 already_AddRefed<DOMStorageUsage>
michael@0 287 DOMStorageManager::GetScopeUsage(const nsACString& aScope)
michael@0 288 {
michael@0 289 nsRefPtr<DOMStorageUsage> usage;
michael@0 290 if (mUsages.Get(aScope, &usage)) {
michael@0 291 return usage.forget();
michael@0 292 }
michael@0 293
michael@0 294 usage = new DOMStorageUsage(aScope);
michael@0 295
michael@0 296 if (mType == LocalStorage) {
michael@0 297 DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
michael@0 298 if (db) {
michael@0 299 db->AsyncGetUsage(usage);
michael@0 300 }
michael@0 301 }
michael@0 302
michael@0 303 mUsages.Put(aScope, usage);
michael@0 304
michael@0 305 return usage.forget();
michael@0 306 }
michael@0 307
michael@0 308 already_AddRefed<DOMStorageCache>
michael@0 309 DOMStorageManager::PutCache(const nsACString& aScope,
michael@0 310 nsIURI* aFirstPartyIsolationURI,
michael@0 311 nsIPrincipal* aPrincipal)
michael@0 312 {
michael@0 313 DOMStorageCacheHashKey* entry = mCaches.PutEntry(aScope);
michael@0 314 nsRefPtr<DOMStorageCache> cache = entry->cache();
michael@0 315
michael@0 316 nsAutoCString quotaScope;
michael@0 317 CreateQuotaDBKey(aFirstPartyIsolationURI, aPrincipal, quotaScope);
michael@0 318
michael@0 319 // To avoid ever persisting session storage to disk, initialize LocalStorage
michael@0 320 // like SessionStorage.
michael@0 321 switch (mType) {
michael@0 322 case SessionStorage:
michael@0 323 case LocalStorage:
michael@0 324 // Lifetime handled by the manager, don't persist
michael@0 325 entry->HardRef();
michael@0 326 cache->Init(this, false, aFirstPartyIsolationURI, aPrincipal, quotaScope);
michael@0 327 break;
michael@0 328
michael@0 329 default:
michael@0 330 MOZ_ASSERT(false);
michael@0 331 }
michael@0 332
michael@0 333 return cache.forget();
michael@0 334 }
michael@0 335
michael@0 336 void
michael@0 337 DOMStorageManager::DropCache(DOMStorageCache* aCache)
michael@0 338 {
michael@0 339 if (!NS_IsMainThread()) {
michael@0 340 NS_WARNING("DOMStorageManager::DropCache called on a non-main thread, shutting down?");
michael@0 341 }
michael@0 342
michael@0 343 mCaches.RemoveEntry(aCache->Scope());
michael@0 344 }
michael@0 345
michael@0 346 nsresult
michael@0 347 DOMStorageManager::GetStorageInternal(bool aCreate,
michael@0 348 nsIURI* aFirstPartyIsolationURI,
michael@0 349 nsIPrincipal* aPrincipal,
michael@0 350 const nsAString& aDocumentURI,
michael@0 351 bool aPrivate,
michael@0 352 nsIDOMStorage** aRetval)
michael@0 353 {
michael@0 354 nsresult rv;
michael@0 355
michael@0 356 nsAutoCString scope;
michael@0 357 rv = CreateScopeKey(aFirstPartyIsolationURI, aPrincipal, scope);
michael@0 358 if (NS_FAILED(rv)) {
michael@0 359 return NS_ERROR_NOT_AVAILABLE;
michael@0 360 }
michael@0 361
michael@0 362 nsRefPtr<DOMStorageCache> cache = GetCache(scope);
michael@0 363
michael@0 364 // Get or create a cache for the given scope
michael@0 365 if (!cache) {
michael@0 366 if (!aCreate) {
michael@0 367 *aRetval = nullptr;
michael@0 368 return NS_OK;
michael@0 369 }
michael@0 370
michael@0 371 if (!aRetval) {
michael@0 372 // This is demand to just preload the cache, if the scope has
michael@0 373 // no data stored, bypass creation and preload of the cache.
michael@0 374 DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
michael@0 375 if (db) {
michael@0 376 if (!db->ShouldPreloadScope(scope)) {
michael@0 377 return NS_OK;
michael@0 378 }
michael@0 379 } else {
michael@0 380 if (scope.Equals(NS_LITERAL_CSTRING("knalb.:about"))) {
michael@0 381 return NS_OK;
michael@0 382 }
michael@0 383 }
michael@0 384 }
michael@0 385
michael@0 386 // There is always a single instance of a cache per scope
michael@0 387 // in a single instance of a DOM storage manager.
michael@0 388 cache = PutCache(scope, aFirstPartyIsolationURI, aPrincipal);
michael@0 389 } else if (mType == SessionStorage) {
michael@0 390 if (!cache->CheckPrincipal(aPrincipal)) {
michael@0 391 return NS_ERROR_DOM_SECURITY_ERR;
michael@0 392 }
michael@0 393 }
michael@0 394
michael@0 395 if (aRetval) {
michael@0 396 *aRetval = new DOMStorage(this, cache, aDocumentURI, aPrincipal, aPrivate);
michael@0 397 NS_ADDREF(*aRetval);
michael@0 398 }
michael@0 399
michael@0 400 return NS_OK;
michael@0 401 }
michael@0 402
michael@0 403 NS_IMETHODIMP
michael@0 404 DOMStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal)
michael@0 405 {
michael@0 406 return GetStorageInternal(true, nullptr, aPrincipal, EmptyString(),
michael@0 407 false, nullptr);
michael@0 408 }
michael@0 409
michael@0 410 NS_IMETHODIMP
michael@0 411 DOMStorageManager::PrecacheStorageForFirstParty(nsIURI* aFirstPartyIsolationURI,
michael@0 412 nsIPrincipal* aPrincipal)
michael@0 413 {
michael@0 414 return GetStorageInternal(true, aFirstPartyIsolationURI, aPrincipal, EmptyString(),
michael@0 415 false, nullptr);
michael@0 416 }
michael@0 417
michael@0 418 NS_IMETHODIMP
michael@0 419 DOMStorageManager::CreateStorage(nsIPrincipal* aPrincipal,
michael@0 420 const nsAString& aDocumentURI,
michael@0 421 bool aPrivate,
michael@0 422 nsIDOMStorage** aRetval)
michael@0 423 {
michael@0 424 return GetStorageInternal(true, nullptr, aPrincipal, aDocumentURI,
michael@0 425 aPrivate, aRetval);
michael@0 426 }
michael@0 427
michael@0 428 NS_IMETHODIMP
michael@0 429 DOMStorageManager::CreateStorageForFirstParty(nsIURI* aFirstPartyIsolationURI,
michael@0 430 nsIPrincipal* aPrincipal,
michael@0 431 const nsAString& aDocumentURI,
michael@0 432 bool aPrivate,
michael@0 433 nsIDOMStorage** aRetval)
michael@0 434 {
michael@0 435 return GetStorageInternal(true, aFirstPartyIsolationURI, aPrincipal, aDocumentURI,
michael@0 436 aPrivate, aRetval);
michael@0 437 }
michael@0 438
michael@0 439 NS_IMETHODIMP
michael@0 440 DOMStorageManager::GetStorage(nsIPrincipal* aPrincipal,
michael@0 441 bool aPrivate,
michael@0 442 nsIDOMStorage** aRetval)
michael@0 443 {
michael@0 444 return GetStorageInternal(false, nullptr, aPrincipal, EmptyString(),
michael@0 445 aPrivate, aRetval);
michael@0 446 }
michael@0 447
michael@0 448 NS_IMETHODIMP
michael@0 449 DOMStorageManager::GetStorageForFirstParty(nsIURI* aFirstPartyIsolationURI,
michael@0 450 nsIPrincipal* aPrincipal,
michael@0 451 bool aPrivate,
michael@0 452 nsIDOMStorage** aRetval)
michael@0 453 {
michael@0 454 return GetStorageInternal(false, aFirstPartyIsolationURI, aPrincipal,
michael@0 455 EmptyString(), aPrivate, aRetval);
michael@0 456 }
michael@0 457
michael@0 458 NS_IMETHODIMP
michael@0 459 DOMStorageManager::CloneStorage(nsIDOMStorage* aStorage)
michael@0 460 {
michael@0 461 if (mType != SessionStorage) {
michael@0 462 // Cloning is supported only for sessionStorage
michael@0 463 return NS_ERROR_NOT_IMPLEMENTED;
michael@0 464 }
michael@0 465
michael@0 466 nsCOMPtr<nsPIDOMStorage> pstorage = do_QueryInterface(aStorage);
michael@0 467 if (!pstorage) {
michael@0 468 return NS_ERROR_UNEXPECTED;
michael@0 469 }
michael@0 470
michael@0 471 const DOMStorageCache* origCache = pstorage->GetCache();
michael@0 472
michael@0 473 DOMStorageCache* existingCache = GetCache(origCache->Scope());
michael@0 474 if (existingCache) {
michael@0 475 // Do not replace an existing sessionStorage.
michael@0 476 return NS_ERROR_NOT_AVAILABLE;
michael@0 477 }
michael@0 478
michael@0 479 // Since this manager is sessionStorage manager, PutCache hard references
michael@0 480 // the cache in our hashtable.
michael@0 481 nsRefPtr<DOMStorageCache> newCache = PutCache(origCache->Scope(),
michael@0 482 origCache->FirstPartyIsolationURI(),
michael@0 483 origCache->Principal());
michael@0 484
michael@0 485 newCache->CloneFrom(origCache);
michael@0 486 return NS_OK;
michael@0 487 }
michael@0 488
michael@0 489 NS_IMETHODIMP
michael@0 490 DOMStorageManager::CheckStorage(nsIPrincipal* aPrincipal,
michael@0 491 nsIDOMStorage* aStorage,
michael@0 492 bool* aRetval)
michael@0 493 {
michael@0 494 return CheckStorageForFirstParty(nullptr, aPrincipal, aStorage, aRetval);
michael@0 495 }
michael@0 496
michael@0 497 NS_IMETHODIMP
michael@0 498 DOMStorageManager::CheckStorageForFirstParty(nsIURI* aFirstPartyIsolationURI,
michael@0 499 nsIPrincipal* aPrincipal,
michael@0 500 nsIDOMStorage* aStorage,
michael@0 501 bool* aRetval)
michael@0 502 {
michael@0 503 nsCOMPtr<nsPIDOMStorage> pstorage = do_QueryInterface(aStorage);
michael@0 504 if (!pstorage) {
michael@0 505 return NS_ERROR_UNEXPECTED;
michael@0 506 }
michael@0 507
michael@0 508 *aRetval = false;
michael@0 509
michael@0 510 if (!aPrincipal) {
michael@0 511 return NS_ERROR_NOT_AVAILABLE;
michael@0 512 }
michael@0 513
michael@0 514 nsAutoCString scope;
michael@0 515 nsresult rv = CreateScopeKey(aFirstPartyIsolationURI, aPrincipal, scope);
michael@0 516 if (NS_FAILED(rv)) {
michael@0 517 return rv;
michael@0 518 }
michael@0 519
michael@0 520 DOMStorageCache* cache = GetCache(scope);
michael@0 521 if (cache != pstorage->GetCache()) {
michael@0 522 return NS_OK;
michael@0 523 }
michael@0 524
michael@0 525 if (!pstorage->PrincipalEquals(aPrincipal)) {
michael@0 526 return NS_OK;
michael@0 527 }
michael@0 528
michael@0 529 *aRetval = true;
michael@0 530 return NS_OK;
michael@0 531 }
michael@0 532
michael@0 533 // Obsolete nsIDOMStorageManager methods
michael@0 534
michael@0 535 NS_IMETHODIMP
michael@0 536 DOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal* aPrincipal,
michael@0 537 const nsAString& aDocumentURI,
michael@0 538 bool aPrivate,
michael@0 539 nsIDOMStorage** aRetval)
michael@0 540 {
michael@0 541 if (mType != LocalStorage) {
michael@0 542 return NS_ERROR_UNEXPECTED;
michael@0 543 }
michael@0 544
michael@0 545 return CreateStorage(aPrincipal, aDocumentURI, aPrivate, aRetval);
michael@0 546 }
michael@0 547
michael@0 548 namespace { // anon
michael@0 549
michael@0 550 class ClearCacheEnumeratorData
michael@0 551 {
michael@0 552 public:
michael@0 553 ClearCacheEnumeratorData(uint32_t aFlags)
michael@0 554 : mUnloadFlags(aFlags)
michael@0 555 {}
michael@0 556
michael@0 557 uint32_t mUnloadFlags;
michael@0 558 nsCString mKeyPrefix;
michael@0 559 };
michael@0 560
michael@0 561 } // anon
michael@0 562
michael@0 563 PLDHashOperator
michael@0 564 DOMStorageManager::ClearCacheEnumerator(DOMStorageCacheHashKey* aEntry, void* aClosure)
michael@0 565 {
michael@0 566 DOMStorageCache* cache = aEntry->cache();
michael@0 567 nsCString& key = const_cast<nsCString&>(cache->Scope());
michael@0 568
michael@0 569 ClearCacheEnumeratorData* data = static_cast<ClearCacheEnumeratorData*>(aClosure);
michael@0 570
michael@0 571 if (data->mKeyPrefix.IsEmpty() || StringBeginsWith(key, data->mKeyPrefix)) {
michael@0 572 cache->UnloadItems(data->mUnloadFlags);
michael@0 573 }
michael@0 574
michael@0 575 return PL_DHASH_NEXT;
michael@0 576 }
michael@0 577
michael@0 578 nsresult
michael@0 579 DOMStorageManager::Observe(const char* aTopic, const nsACString& aScopePrefix)
michael@0 580 {
michael@0 581 // Clear everything, caches + database
michael@0 582 if (!strcmp(aTopic, "cookie-cleared")) {
michael@0 583 ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
michael@0 584 mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
michael@0 585
michael@0 586 return NS_OK;
michael@0 587 }
michael@0 588
michael@0 589 // Clear from caches everything that has been stored
michael@0 590 // while in session-only mode
michael@0 591 if (!strcmp(aTopic, "session-only-cleared")) {
michael@0 592 ClearCacheEnumeratorData data(DOMStorageCache::kUnloadSession);
michael@0 593 data.mKeyPrefix = aScopePrefix;
michael@0 594 mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
michael@0 595
michael@0 596 return NS_OK;
michael@0 597 }
michael@0 598
michael@0 599 // Clear everything (including so and pb data) from caches and database
michael@0 600 // for the gived domain and subdomains.
michael@0 601 if (!strcmp(aTopic, "domain-data-cleared")) {
michael@0 602 ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
michael@0 603 data.mKeyPrefix = aScopePrefix;
michael@0 604 mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
michael@0 605
michael@0 606 return NS_OK;
michael@0 607 }
michael@0 608
michael@0 609 // Clear all private-browsing caches
michael@0 610 if (!strcmp(aTopic, "private-browsing-data-cleared")) {
michael@0 611 ClearCacheEnumeratorData data(DOMStorageCache::kUnloadPrivate);
michael@0 612 mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
michael@0 613
michael@0 614 return NS_OK;
michael@0 615 }
michael@0 616
michael@0 617 // Clear localStorage data beloging to an app.
michael@0 618 if (!strcmp(aTopic, "app-data-cleared")) {
michael@0 619
michael@0 620 // sessionStorage is expected to stay
michael@0 621 if (mType == SessionStorage) {
michael@0 622 return NS_OK;
michael@0 623 }
michael@0 624
michael@0 625 ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
michael@0 626 data.mKeyPrefix = aScopePrefix;
michael@0 627 mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
michael@0 628
michael@0 629 return NS_OK;
michael@0 630 }
michael@0 631
michael@0 632 if (!strcmp(aTopic, "profile-change")) {
michael@0 633 // For case caches are still referenced - clear them completely
michael@0 634 ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
michael@0 635 mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
michael@0 636
michael@0 637 mCaches.Clear();
michael@0 638 return NS_OK;
michael@0 639 }
michael@0 640
michael@0 641 if (!strcmp(aTopic, "low-disk-space")) {
michael@0 642 if (mType == LocalStorage) {
michael@0 643 mLowDiskSpace = true;
michael@0 644 }
michael@0 645
michael@0 646 return NS_OK;
michael@0 647 }
michael@0 648
michael@0 649 if (!strcmp(aTopic, "no-low-disk-space")) {
michael@0 650 if (mType == LocalStorage) {
michael@0 651 mLowDiskSpace = false;
michael@0 652 }
michael@0 653
michael@0 654 return NS_OK;
michael@0 655 }
michael@0 656
michael@0 657 #ifdef DOM_STORAGE_TESTS
michael@0 658 if (!strcmp(aTopic, "test-reload")) {
michael@0 659 if (mType != LocalStorage) {
michael@0 660 return NS_OK;
michael@0 661 }
michael@0 662
michael@0 663 // This immediately completely reloads all caches from the database.
michael@0 664 ClearCacheEnumeratorData data(DOMStorageCache::kTestReload);
michael@0 665 mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
michael@0 666 return NS_OK;
michael@0 667 }
michael@0 668
michael@0 669 if (!strcmp(aTopic, "test-flushed")) {
michael@0 670 if (XRE_GetProcessType() != GeckoProcessType_Default) {
michael@0 671 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
michael@0 672 if (obs) {
michael@0 673 obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr);
michael@0 674 }
michael@0 675 }
michael@0 676
michael@0 677 return NS_OK;
michael@0 678 }
michael@0 679 #endif
michael@0 680
michael@0 681 NS_ERROR("Unexpected topic");
michael@0 682 return NS_ERROR_UNEXPECTED;
michael@0 683 }
michael@0 684
michael@0 685 // DOMLocalStorageManager
michael@0 686
michael@0 687 DOMLocalStorageManager::DOMLocalStorageManager()
michael@0 688 : DOMStorageManager(LocalStorage)
michael@0 689 {
michael@0 690 NS_ASSERTION(!sSelf, "Somebody is trying to do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\"");
michael@0 691 sSelf = this;
michael@0 692
michael@0 693 if (XRE_GetProcessType() != GeckoProcessType_Default) {
michael@0 694 // Do this only on the child process. The thread IPC bridge
michael@0 695 // is also used to communicate chrome observer notifications.
michael@0 696 // Note: must be called after we set sSelf
michael@0 697 DOMStorageCache::StartDatabase();
michael@0 698 }
michael@0 699 }
michael@0 700
michael@0 701 DOMLocalStorageManager::~DOMLocalStorageManager()
michael@0 702 {
michael@0 703 sSelf = nullptr;
michael@0 704 }
michael@0 705
michael@0 706 // DOMSessionStorageManager
michael@0 707
michael@0 708 DOMSessionStorageManager::DOMSessionStorageManager()
michael@0 709 : DOMStorageManager(SessionStorage)
michael@0 710 {
michael@0 711 if (XRE_GetProcessType() != GeckoProcessType_Default) {
michael@0 712 // Do this only on the child process. The thread IPC bridge
michael@0 713 // is also used to communicate chrome observer notifications.
michael@0 714 DOMStorageCache::StartDatabase();
michael@0 715 }
michael@0 716 }
michael@0 717
michael@0 718 } // ::dom
michael@0 719 } // ::mozilla

mercurial