dom/src/storage/DOMStorageManager.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/src/storage/DOMStorageManager.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,719 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "DOMStorageManager.h"
    1.10 +#include "DOMStorage.h"
    1.11 +#include "DOMStorageDBThread.h"
    1.12 +
    1.13 +#include "nsIScriptSecurityManager.h"
    1.14 +#include "nsIEffectiveTLDService.h"
    1.15 +
    1.16 +#include "nsNetUtil.h"
    1.17 +#include "nsPrintfCString.h"
    1.18 +#include "nsXULAppAPI.h"
    1.19 +#include "nsThreadUtils.h"
    1.20 +#include "nsIObserverService.h"
    1.21 +#include "mozIThirdPartyUtil.h"
    1.22 +#include "mozilla/Services.h"
    1.23 +#include "mozilla/Preferences.h"
    1.24 +
    1.25 +// Only allow relatively small amounts of data since performance of
    1.26 +// the synchronous IO is very bad.
    1.27 +// We are enforcing simple per-origin quota only.
    1.28 +#define DEFAULT_QUOTA_LIMIT (5 * 1024)
    1.29 +
    1.30 +namespace mozilla {
    1.31 +namespace dom {
    1.32 +
    1.33 +namespace { // anon
    1.34 +
    1.35 +int32_t gQuotaLimit = DEFAULT_QUOTA_LIMIT;
    1.36 +
    1.37 +} // anon
    1.38 +
    1.39 +DOMLocalStorageManager*
    1.40 +DOMLocalStorageManager::sSelf = nullptr;
    1.41 +
    1.42 +// static
    1.43 +uint32_t
    1.44 +DOMStorageManager::GetQuota()
    1.45 +{
    1.46 +  static bool preferencesInitialized = false;
    1.47 +  if (!preferencesInitialized) {
    1.48 +    mozilla::Preferences::AddIntVarCache(&gQuotaLimit, "dom.storage.default_quota",
    1.49 +                                         DEFAULT_QUOTA_LIMIT);
    1.50 +    preferencesInitialized = true;
    1.51 +  }
    1.52 +
    1.53 +  return gQuotaLimit * 1024; // pref is in kBs
    1.54 +}
    1.55 +
    1.56 +void
    1.57 +ReverseString(const nsCSubstring& aSource, nsCSubstring& aResult)
    1.58 +{
    1.59 +  nsACString::const_iterator sourceBegin, sourceEnd;
    1.60 +  aSource.BeginReading(sourceBegin);
    1.61 +  aSource.EndReading(sourceEnd);
    1.62 +
    1.63 +  aResult.SetLength(aSource.Length());
    1.64 +  nsACString::iterator destEnd;
    1.65 +  aResult.EndWriting(destEnd);
    1.66 +
    1.67 +  while (sourceBegin != sourceEnd) {
    1.68 +    *(--destEnd) = *sourceBegin;
    1.69 +    ++sourceBegin;
    1.70 +  }
    1.71 +}
    1.72 +
    1.73 +nsresult
    1.74 +CreateReversedDomain(const nsACString& aAsciiDomain,
    1.75 +                     nsACString& aKey)
    1.76 +{
    1.77 +  if (aAsciiDomain.IsEmpty()) {
    1.78 +    return NS_ERROR_NOT_AVAILABLE;
    1.79 +  }
    1.80 +
    1.81 +  ReverseString(aAsciiDomain, aKey);
    1.82 +
    1.83 +  aKey.AppendLiteral(".");
    1.84 +  return NS_OK;
    1.85 +}
    1.86 +
    1.87 +bool
    1.88 +PrincipalsEqual(nsIPrincipal* aObjectPrincipal, nsIPrincipal* aSubjectPrincipal)
    1.89 +{
    1.90 +  if (!aSubjectPrincipal) {
    1.91 +    return true;
    1.92 +  }
    1.93 +
    1.94 +  if (!aObjectPrincipal) {
    1.95 +    return false;
    1.96 +  }
    1.97 +
    1.98 +  return aSubjectPrincipal->Equals(aObjectPrincipal);
    1.99 +}
   1.100 +
   1.101 +NS_IMPL_ISUPPORTS(DOMStorageManager,
   1.102 +                  nsIDOMStorageManager)
   1.103 +
   1.104 +DOMStorageManager::DOMStorageManager(nsPIDOMStorage::StorageType aType)
   1.105 +  : mCaches(10)
   1.106 +  , mType(aType)
   1.107 +  , mLowDiskSpace(false)
   1.108 +{
   1.109 +  DOMStorageObserver* observer = DOMStorageObserver::Self();
   1.110 +  NS_ASSERTION(observer, "No DOMStorageObserver, cannot observe private data delete notifications!");
   1.111 +
   1.112 +  if (observer) {
   1.113 +    observer->AddSink(this);
   1.114 +  }
   1.115 +}
   1.116 +
   1.117 +DOMStorageManager::~DOMStorageManager()
   1.118 +{
   1.119 +  DOMStorageObserver* observer = DOMStorageObserver::Self();
   1.120 +  if (observer) {
   1.121 +    observer->RemoveSink(this);
   1.122 +  }
   1.123 +}
   1.124 +
   1.125 +namespace { // anon
   1.126 +
   1.127 +nsresult
   1.128 +AppendFirstPartyToKey(nsIURI* aFirstPartyIsolationURI, nsACString& aKey)
   1.129 +{
   1.130 +  if (aFirstPartyIsolationURI) {
   1.131 +    nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
   1.132 +                            do_GetService(THIRDPARTYUTIL_CONTRACTID);
   1.133 +    if (!thirdPartyUtil)
   1.134 +      return NS_ERROR_FAILURE;
   1.135 +
   1.136 +    nsAutoCString firstPartyHost;
   1.137 +    nsresult rv = thirdPartyUtil->GetFirstPartyHostForIsolation(aFirstPartyIsolationURI,
   1.138 +                                                                firstPartyHost);
   1.139 +    NS_ENSURE_SUCCESS(rv, rv);
   1.140 +
   1.141 +    aKey.AppendLiteral("&");
   1.142 +    aKey.Append(firstPartyHost);
   1.143 +  }
   1.144 +
   1.145 +  return NS_OK;
   1.146 +}
   1.147 +
   1.148 +nsresult
   1.149 +CreateScopeKey(nsIURI* aFirstPartyIsolationURI, nsIPrincipal* aPrincipal,
   1.150 +               nsACString& aKey)
   1.151 +{
   1.152 +  nsCOMPtr<nsIURI> uri;
   1.153 +  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
   1.154 +  NS_ENSURE_SUCCESS(rv, rv);
   1.155 +  if (!uri) {
   1.156 +    return NS_ERROR_UNEXPECTED;
   1.157 +  }
   1.158 +
   1.159 +  nsAutoCString domainScope;
   1.160 +  rv = uri->GetAsciiHost(domainScope);
   1.161 +  NS_ENSURE_SUCCESS(rv, rv);
   1.162 +
   1.163 +  if (domainScope.IsEmpty()) {
   1.164 +    // For the file:/// protocol use the exact directory as domain.
   1.165 +    bool isScheme = false;
   1.166 +    if (NS_SUCCEEDED(uri->SchemeIs("file", &isScheme)) && isScheme) {
   1.167 +      nsCOMPtr<nsIURL> url = do_QueryInterface(uri, &rv);
   1.168 +      NS_ENSURE_SUCCESS(rv, rv);
   1.169 +      rv = url->GetDirectory(domainScope);
   1.170 +      NS_ENSURE_SUCCESS(rv, rv);
   1.171 +    }
   1.172 +  }
   1.173 +
   1.174 +  nsAutoCString key;
   1.175 +
   1.176 +  rv = CreateReversedDomain(domainScope, key);
   1.177 +  if (NS_FAILED(rv)) {
   1.178 +    return rv;
   1.179 +  }
   1.180 +
   1.181 +  nsAutoCString scheme;
   1.182 +  rv = uri->GetScheme(scheme);
   1.183 +  NS_ENSURE_SUCCESS(rv, rv);
   1.184 +
   1.185 +  key.Append(NS_LITERAL_CSTRING(":") + scheme);
   1.186 +
   1.187 +  int32_t port = NS_GetRealPort(uri);
   1.188 +  if (port != -1) {
   1.189 +    key.Append(nsPrintfCString(":%d", port));
   1.190 +  }
   1.191 +
   1.192 +  bool unknownAppId;
   1.193 +  rv = aPrincipal->GetUnknownAppId(&unknownAppId);
   1.194 +  NS_ENSURE_SUCCESS(rv, rv);
   1.195 +
   1.196 +  if (!unknownAppId) {
   1.197 +    uint32_t appId;
   1.198 +    rv = aPrincipal->GetAppId(&appId);
   1.199 +    NS_ENSURE_SUCCESS(rv, rv);
   1.200 +
   1.201 +    bool isInBrowserElement;
   1.202 +    rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
   1.203 +    NS_ENSURE_SUCCESS(rv, rv);
   1.204 +
   1.205 +    if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
   1.206 +      aKey.Assign(key);
   1.207 +    } else {
   1.208 +      aKey.Truncate();
   1.209 +      aKey.AppendInt(appId);
   1.210 +      aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
   1.211 +                  NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
   1.212 +                  NS_LITERAL_CSTRING(":") + key);
   1.213 +    }
   1.214 +  }
   1.215 +
   1.216 +  // Isolate scope keys to the URL bar domain by appending &firstPartyHost
   1.217 +  // if available.
   1.218 +  return AppendFirstPartyToKey(aFirstPartyIsolationURI, aKey);
   1.219 +}
   1.220 +
   1.221 +nsresult
   1.222 +CreateQuotaDBKey(nsIURI* aFirstPartyIsolationURI, nsIPrincipal* aPrincipal,
   1.223 +                 nsACString& aKey)
   1.224 +{
   1.225 +  nsresult rv;
   1.226 +
   1.227 +  nsAutoCString subdomainsDBKey;
   1.228 +  nsCOMPtr<nsIEffectiveTLDService> eTLDService(do_GetService(
   1.229 +    NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv));
   1.230 +  NS_ENSURE_SUCCESS(rv, rv);
   1.231 +
   1.232 +  nsCOMPtr<nsIURI> uri;
   1.233 +  rv = aPrincipal->GetURI(getter_AddRefs(uri));
   1.234 +  NS_ENSURE_SUCCESS(rv, rv);
   1.235 +  NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
   1.236 +
   1.237 +  nsAutoCString eTLDplusOne;
   1.238 +  rv = eTLDService->GetBaseDomain(uri, 0, eTLDplusOne);
   1.239 +  if (NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS == rv) {
   1.240 +    // XXX bug 357323 - what to do for localhost/file exactly?
   1.241 +    rv = uri->GetAsciiHost(eTLDplusOne);
   1.242 +  }
   1.243 +  NS_ENSURE_SUCCESS(rv, rv);
   1.244 +
   1.245 +  CreateReversedDomain(eTLDplusOne, subdomainsDBKey);
   1.246 +
   1.247 +  bool unknownAppId;
   1.248 +  rv = aPrincipal->GetUnknownAppId(&unknownAppId);
   1.249 +  NS_ENSURE_SUCCESS(rv, rv);
   1.250 +
   1.251 +  if (!unknownAppId) {
   1.252 +    uint32_t appId;
   1.253 +    rv = aPrincipal->GetAppId(&appId);
   1.254 +    NS_ENSURE_SUCCESS(rv, rv);
   1.255 +
   1.256 +    bool isInBrowserElement;
   1.257 +    rv = aPrincipal->GetIsInBrowserElement(&isInBrowserElement);
   1.258 +    NS_ENSURE_SUCCESS(rv, rv);
   1.259 +
   1.260 +    if (appId == nsIScriptSecurityManager::NO_APP_ID && !isInBrowserElement) {
   1.261 +      aKey.Assign(subdomainsDBKey);
   1.262 +    } else {
   1.263 +      aKey.Truncate();
   1.264 +      aKey.AppendInt(appId);
   1.265 +      aKey.Append(NS_LITERAL_CSTRING(":") + (isInBrowserElement ?
   1.266 +                  NS_LITERAL_CSTRING("t") : NS_LITERAL_CSTRING("f")) +
   1.267 +                  NS_LITERAL_CSTRING(":") + subdomainsDBKey);
   1.268 +    }
   1.269 +  }
   1.270 +
   1.271 +  // Isolate scope keys to the URL bar domain by appending &firstPartyHost
   1.272 +  // if available.
   1.273 +  return AppendFirstPartyToKey(aFirstPartyIsolationURI, aKey);
   1.274 +}
   1.275 +
   1.276 +} // anon
   1.277 +
   1.278 +DOMStorageCache*
   1.279 +DOMStorageManager::GetCache(const nsACString& aScope) const
   1.280 +{
   1.281 +  DOMStorageCacheHashKey* entry = mCaches.GetEntry(aScope);
   1.282 +  if (!entry) {
   1.283 +    return nullptr;
   1.284 +  }
   1.285 +
   1.286 +  return entry->cache();
   1.287 +}
   1.288 +
   1.289 +already_AddRefed<DOMStorageUsage>
   1.290 +DOMStorageManager::GetScopeUsage(const nsACString& aScope)
   1.291 +{
   1.292 +  nsRefPtr<DOMStorageUsage> usage;
   1.293 +  if (mUsages.Get(aScope, &usage)) {
   1.294 +    return usage.forget();
   1.295 +  }
   1.296 +
   1.297 +  usage = new DOMStorageUsage(aScope);
   1.298 +
   1.299 +  if (mType == LocalStorage) {
   1.300 +    DOMStorageDBBridge* db = DOMStorageCache::StartDatabase();
   1.301 +    if (db) {
   1.302 +      db->AsyncGetUsage(usage);
   1.303 +    }
   1.304 +  }
   1.305 +
   1.306 +  mUsages.Put(aScope, usage);
   1.307 +
   1.308 +  return usage.forget();
   1.309 +}
   1.310 +
   1.311 +already_AddRefed<DOMStorageCache>
   1.312 +DOMStorageManager::PutCache(const nsACString& aScope,
   1.313 +                            nsIURI* aFirstPartyIsolationURI,
   1.314 +                            nsIPrincipal* aPrincipal)
   1.315 +{
   1.316 +  DOMStorageCacheHashKey* entry = mCaches.PutEntry(aScope);
   1.317 +  nsRefPtr<DOMStorageCache> cache = entry->cache();
   1.318 +
   1.319 +  nsAutoCString quotaScope;
   1.320 +  CreateQuotaDBKey(aFirstPartyIsolationURI, aPrincipal, quotaScope);
   1.321 +
   1.322 +  // To avoid ever persisting session storage to disk, initialize LocalStorage
   1.323 +  // like SessionStorage.
   1.324 +  switch (mType) {
   1.325 +  case SessionStorage:
   1.326 +  case LocalStorage:
   1.327 +    // Lifetime handled by the manager, don't persist
   1.328 +    entry->HardRef();
   1.329 +    cache->Init(this, false, aFirstPartyIsolationURI, aPrincipal, quotaScope);
   1.330 +    break;
   1.331 +
   1.332 +  default:
   1.333 +    MOZ_ASSERT(false);
   1.334 +  }
   1.335 +
   1.336 +  return cache.forget();
   1.337 +}
   1.338 +
   1.339 +void
   1.340 +DOMStorageManager::DropCache(DOMStorageCache* aCache)
   1.341 +{
   1.342 +  if (!NS_IsMainThread()) {
   1.343 +    NS_WARNING("DOMStorageManager::DropCache called on a non-main thread, shutting down?");
   1.344 +  }
   1.345 +
   1.346 +  mCaches.RemoveEntry(aCache->Scope());
   1.347 +}
   1.348 +
   1.349 +nsresult
   1.350 +DOMStorageManager::GetStorageInternal(bool aCreate,
   1.351 +                                      nsIURI* aFirstPartyIsolationURI,
   1.352 +                                      nsIPrincipal* aPrincipal,
   1.353 +                                      const nsAString& aDocumentURI,
   1.354 +                                      bool aPrivate,
   1.355 +                                      nsIDOMStorage** aRetval)
   1.356 +{
   1.357 +  nsresult rv;
   1.358 +
   1.359 +  nsAutoCString scope;
   1.360 +  rv = CreateScopeKey(aFirstPartyIsolationURI, aPrincipal, scope);
   1.361 +  if (NS_FAILED(rv)) {
   1.362 +    return NS_ERROR_NOT_AVAILABLE;
   1.363 +  }
   1.364 +
   1.365 +  nsRefPtr<DOMStorageCache> cache = GetCache(scope);
   1.366 +
   1.367 +  // Get or create a cache for the given scope
   1.368 +  if (!cache) {
   1.369 +    if (!aCreate) {
   1.370 +      *aRetval = nullptr;
   1.371 +      return NS_OK;
   1.372 +    }
   1.373 +
   1.374 +    if (!aRetval) {
   1.375 +      // This is demand to just preload the cache, if the scope has
   1.376 +      // no data stored, bypass creation and preload of the cache.
   1.377 +      DOMStorageDBBridge* db = DOMStorageCache::GetDatabase();
   1.378 +      if (db) {
   1.379 +        if (!db->ShouldPreloadScope(scope)) {
   1.380 +          return NS_OK;
   1.381 +        }
   1.382 +      } else {
   1.383 +        if (scope.Equals(NS_LITERAL_CSTRING("knalb.:about"))) {
   1.384 +          return NS_OK;
   1.385 +        }
   1.386 +      }
   1.387 +    }
   1.388 +
   1.389 +    // There is always a single instance of a cache per scope
   1.390 +    // in a single instance of a DOM storage manager.
   1.391 +    cache = PutCache(scope, aFirstPartyIsolationURI, aPrincipal);
   1.392 +  } else if (mType == SessionStorage) {
   1.393 +    if (!cache->CheckPrincipal(aPrincipal)) {
   1.394 +      return NS_ERROR_DOM_SECURITY_ERR;
   1.395 +    }
   1.396 +  }
   1.397 +
   1.398 +  if (aRetval) {
   1.399 +    *aRetval = new DOMStorage(this, cache, aDocumentURI, aPrincipal, aPrivate);
   1.400 +    NS_ADDREF(*aRetval);
   1.401 +  }
   1.402 +
   1.403 +  return NS_OK;
   1.404 +}
   1.405 +
   1.406 +NS_IMETHODIMP
   1.407 +DOMStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal)
   1.408 +{
   1.409 +  return GetStorageInternal(true, nullptr, aPrincipal, EmptyString(),
   1.410 +                            false, nullptr);
   1.411 +}
   1.412 +
   1.413 +NS_IMETHODIMP
   1.414 +DOMStorageManager::PrecacheStorageForFirstParty(nsIURI* aFirstPartyIsolationURI,
   1.415 +                                                nsIPrincipal* aPrincipal)
   1.416 +{
   1.417 +  return GetStorageInternal(true, aFirstPartyIsolationURI, aPrincipal, EmptyString(),
   1.418 +                            false, nullptr);
   1.419 +}
   1.420 +
   1.421 +NS_IMETHODIMP
   1.422 +DOMStorageManager::CreateStorage(nsIPrincipal* aPrincipal,
   1.423 +                                 const nsAString& aDocumentURI,
   1.424 +                                 bool aPrivate,
   1.425 +                                 nsIDOMStorage** aRetval)
   1.426 +{
   1.427 +  return GetStorageInternal(true, nullptr, aPrincipal, aDocumentURI,
   1.428 +                            aPrivate, aRetval);
   1.429 +}
   1.430 +
   1.431 +NS_IMETHODIMP
   1.432 +DOMStorageManager::CreateStorageForFirstParty(nsIURI* aFirstPartyIsolationURI,
   1.433 +                                              nsIPrincipal* aPrincipal,
   1.434 +                                              const nsAString& aDocumentURI,
   1.435 +                                              bool aPrivate,
   1.436 +                                              nsIDOMStorage** aRetval)
   1.437 +{
   1.438 +  return GetStorageInternal(true, aFirstPartyIsolationURI, aPrincipal, aDocumentURI,
   1.439 +                            aPrivate, aRetval);
   1.440 +}
   1.441 +
   1.442 +NS_IMETHODIMP
   1.443 +DOMStorageManager::GetStorage(nsIPrincipal* aPrincipal,
   1.444 +                              bool aPrivate,
   1.445 +                              nsIDOMStorage** aRetval)
   1.446 +{
   1.447 +  return GetStorageInternal(false, nullptr, aPrincipal, EmptyString(),
   1.448 +                            aPrivate, aRetval);
   1.449 +}
   1.450 +
   1.451 +NS_IMETHODIMP
   1.452 +DOMStorageManager::GetStorageForFirstParty(nsIURI* aFirstPartyIsolationURI,
   1.453 +                                           nsIPrincipal* aPrincipal,
   1.454 +                                           bool aPrivate,
   1.455 +                                           nsIDOMStorage** aRetval)
   1.456 +{
   1.457 +  return GetStorageInternal(false, aFirstPartyIsolationURI, aPrincipal,
   1.458 +                            EmptyString(), aPrivate, aRetval);
   1.459 +}
   1.460 +
   1.461 +NS_IMETHODIMP
   1.462 +DOMStorageManager::CloneStorage(nsIDOMStorage* aStorage)
   1.463 +{
   1.464 +  if (mType != SessionStorage) {
   1.465 +    // Cloning is supported only for sessionStorage
   1.466 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.467 +  }
   1.468 +
   1.469 +  nsCOMPtr<nsPIDOMStorage> pstorage = do_QueryInterface(aStorage);
   1.470 +  if (!pstorage) {
   1.471 +    return NS_ERROR_UNEXPECTED;
   1.472 +  }
   1.473 +
   1.474 +  const DOMStorageCache* origCache = pstorage->GetCache();
   1.475 +
   1.476 +  DOMStorageCache* existingCache = GetCache(origCache->Scope());
   1.477 +  if (existingCache) {
   1.478 +    // Do not replace an existing sessionStorage.
   1.479 +    return NS_ERROR_NOT_AVAILABLE;
   1.480 +  }
   1.481 +
   1.482 +  // Since this manager is sessionStorage manager, PutCache hard references
   1.483 +  // the cache in our hashtable.
   1.484 +  nsRefPtr<DOMStorageCache> newCache = PutCache(origCache->Scope(),
   1.485 +                                                origCache->FirstPartyIsolationURI(),
   1.486 +                                                origCache->Principal());
   1.487 +
   1.488 +  newCache->CloneFrom(origCache);
   1.489 +  return NS_OK;
   1.490 +}
   1.491 +
   1.492 +NS_IMETHODIMP
   1.493 +DOMStorageManager::CheckStorage(nsIPrincipal* aPrincipal,
   1.494 +                                nsIDOMStorage* aStorage,
   1.495 +                                bool* aRetval)
   1.496 +{
   1.497 +  return CheckStorageForFirstParty(nullptr, aPrincipal, aStorage, aRetval);
   1.498 +}
   1.499 +
   1.500 +NS_IMETHODIMP
   1.501 +DOMStorageManager::CheckStorageForFirstParty(nsIURI* aFirstPartyIsolationURI,
   1.502 +                                             nsIPrincipal* aPrincipal,
   1.503 +                                             nsIDOMStorage* aStorage,
   1.504 +                                             bool* aRetval)
   1.505 +{
   1.506 +  nsCOMPtr<nsPIDOMStorage> pstorage = do_QueryInterface(aStorage);
   1.507 +  if (!pstorage) {
   1.508 +    return NS_ERROR_UNEXPECTED;
   1.509 +  }
   1.510 +
   1.511 +  *aRetval = false;
   1.512 +
   1.513 +  if (!aPrincipal) {
   1.514 +    return NS_ERROR_NOT_AVAILABLE;
   1.515 +  }
   1.516 +
   1.517 +  nsAutoCString scope;
   1.518 +  nsresult rv = CreateScopeKey(aFirstPartyIsolationURI, aPrincipal, scope);
   1.519 +  if (NS_FAILED(rv)) {
   1.520 +    return rv;
   1.521 +  }
   1.522 +
   1.523 +  DOMStorageCache* cache = GetCache(scope);
   1.524 +  if (cache != pstorage->GetCache()) {
   1.525 +    return NS_OK;
   1.526 +  }
   1.527 +
   1.528 +  if (!pstorage->PrincipalEquals(aPrincipal)) {
   1.529 +    return NS_OK;
   1.530 +  }
   1.531 +
   1.532 +  *aRetval = true;
   1.533 +  return NS_OK;
   1.534 +}
   1.535 +
   1.536 +// Obsolete nsIDOMStorageManager methods
   1.537 +
   1.538 +NS_IMETHODIMP
   1.539 +DOMStorageManager::GetLocalStorageForPrincipal(nsIPrincipal* aPrincipal,
   1.540 +                                               const nsAString& aDocumentURI,
   1.541 +                                               bool aPrivate,
   1.542 +                                               nsIDOMStorage** aRetval)
   1.543 +{
   1.544 +  if (mType != LocalStorage) {
   1.545 +    return NS_ERROR_UNEXPECTED;
   1.546 +  }
   1.547 +
   1.548 +  return CreateStorage(aPrincipal, aDocumentURI, aPrivate, aRetval);
   1.549 +}
   1.550 +
   1.551 +namespace { // anon
   1.552 +
   1.553 +class ClearCacheEnumeratorData
   1.554 +{
   1.555 +public:
   1.556 +  ClearCacheEnumeratorData(uint32_t aFlags)
   1.557 +    : mUnloadFlags(aFlags)
   1.558 +  {}
   1.559 +
   1.560 +  uint32_t mUnloadFlags;
   1.561 +  nsCString mKeyPrefix;
   1.562 +};
   1.563 +
   1.564 +} // anon
   1.565 +
   1.566 +PLDHashOperator
   1.567 +DOMStorageManager::ClearCacheEnumerator(DOMStorageCacheHashKey* aEntry, void* aClosure)
   1.568 +{
   1.569 +  DOMStorageCache* cache = aEntry->cache();
   1.570 +  nsCString& key = const_cast<nsCString&>(cache->Scope());
   1.571 +
   1.572 +  ClearCacheEnumeratorData* data = static_cast<ClearCacheEnumeratorData*>(aClosure);
   1.573 +
   1.574 +  if (data->mKeyPrefix.IsEmpty() || StringBeginsWith(key, data->mKeyPrefix)) {
   1.575 +    cache->UnloadItems(data->mUnloadFlags);
   1.576 +  }
   1.577 +
   1.578 +  return PL_DHASH_NEXT;
   1.579 +}
   1.580 +
   1.581 +nsresult
   1.582 +DOMStorageManager::Observe(const char* aTopic, const nsACString& aScopePrefix)
   1.583 +{
   1.584 +  // Clear everything, caches + database
   1.585 +  if (!strcmp(aTopic, "cookie-cleared")) {
   1.586 +    ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
   1.587 +    mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
   1.588 +
   1.589 +    return NS_OK;
   1.590 +  }
   1.591 +
   1.592 +  // Clear from caches everything that has been stored
   1.593 +  // while in session-only mode
   1.594 +  if (!strcmp(aTopic, "session-only-cleared")) {
   1.595 +    ClearCacheEnumeratorData data(DOMStorageCache::kUnloadSession);
   1.596 +    data.mKeyPrefix = aScopePrefix;
   1.597 +    mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
   1.598 +
   1.599 +    return NS_OK;
   1.600 +  }
   1.601 +
   1.602 +  // Clear everything (including so and pb data) from caches and database
   1.603 +  // for the gived domain and subdomains.
   1.604 +  if (!strcmp(aTopic, "domain-data-cleared")) {
   1.605 +    ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
   1.606 +    data.mKeyPrefix = aScopePrefix;
   1.607 +    mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
   1.608 +
   1.609 +    return NS_OK;
   1.610 +  }
   1.611 +
   1.612 +  // Clear all private-browsing caches
   1.613 +  if (!strcmp(aTopic, "private-browsing-data-cleared")) {
   1.614 +    ClearCacheEnumeratorData data(DOMStorageCache::kUnloadPrivate);
   1.615 +    mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
   1.616 +
   1.617 +    return NS_OK;
   1.618 +  }
   1.619 +
   1.620 +  // Clear localStorage data beloging to an app.
   1.621 +  if (!strcmp(aTopic, "app-data-cleared")) {
   1.622 +
   1.623 +    // sessionStorage is expected to stay
   1.624 +    if (mType == SessionStorage) {
   1.625 +      return NS_OK;
   1.626 +    }
   1.627 +
   1.628 +    ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
   1.629 +    data.mKeyPrefix = aScopePrefix;
   1.630 +    mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
   1.631 +
   1.632 +    return NS_OK;
   1.633 +  }
   1.634 +
   1.635 +  if (!strcmp(aTopic, "profile-change")) {
   1.636 +    // For case caches are still referenced - clear them completely
   1.637 +    ClearCacheEnumeratorData data(DOMStorageCache::kUnloadComplete);
   1.638 +    mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
   1.639 +
   1.640 +    mCaches.Clear();
   1.641 +    return NS_OK;
   1.642 +  }
   1.643 +
   1.644 +  if (!strcmp(aTopic, "low-disk-space")) {
   1.645 +    if (mType == LocalStorage) {
   1.646 +      mLowDiskSpace = true;
   1.647 +    }
   1.648 +
   1.649 +    return NS_OK;
   1.650 +  }
   1.651 +
   1.652 +  if (!strcmp(aTopic, "no-low-disk-space")) {
   1.653 +    if (mType == LocalStorage) {
   1.654 +      mLowDiskSpace = false;
   1.655 +    }
   1.656 +
   1.657 +    return NS_OK;
   1.658 +  }
   1.659 +
   1.660 +#ifdef DOM_STORAGE_TESTS
   1.661 +  if (!strcmp(aTopic, "test-reload")) {
   1.662 +    if (mType != LocalStorage) {
   1.663 +      return NS_OK;
   1.664 +    }
   1.665 +
   1.666 +    // This immediately completely reloads all caches from the database.
   1.667 +    ClearCacheEnumeratorData data(DOMStorageCache::kTestReload);
   1.668 +    mCaches.EnumerateEntries(ClearCacheEnumerator, &data);
   1.669 +    return NS_OK;
   1.670 +  }
   1.671 +
   1.672 +  if (!strcmp(aTopic, "test-flushed")) {
   1.673 +    if (XRE_GetProcessType() != GeckoProcessType_Default) {
   1.674 +      nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   1.675 +      if (obs) {
   1.676 +        obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr);
   1.677 +      }
   1.678 +    }
   1.679 +
   1.680 +    return NS_OK;
   1.681 +  }
   1.682 +#endif
   1.683 +
   1.684 +  NS_ERROR("Unexpected topic");
   1.685 +  return NS_ERROR_UNEXPECTED;
   1.686 +}
   1.687 +
   1.688 +// DOMLocalStorageManager
   1.689 +
   1.690 +DOMLocalStorageManager::DOMLocalStorageManager()
   1.691 +  : DOMStorageManager(LocalStorage)
   1.692 +{
   1.693 +  NS_ASSERTION(!sSelf, "Somebody is trying to do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\"");
   1.694 +  sSelf = this;
   1.695 +
   1.696 +  if (XRE_GetProcessType() != GeckoProcessType_Default) {
   1.697 +    // Do this only on the child process.  The thread IPC bridge
   1.698 +    // is also used to communicate chrome observer notifications.
   1.699 +    // Note: must be called after we set sSelf
   1.700 +    DOMStorageCache::StartDatabase();
   1.701 +  }
   1.702 +}
   1.703 +
   1.704 +DOMLocalStorageManager::~DOMLocalStorageManager()
   1.705 +{
   1.706 +  sSelf = nullptr;
   1.707 +}
   1.708 +
   1.709 +// DOMSessionStorageManager
   1.710 +
   1.711 +DOMSessionStorageManager::DOMSessionStorageManager()
   1.712 +  : DOMStorageManager(SessionStorage)
   1.713 +{
   1.714 +  if (XRE_GetProcessType() != GeckoProcessType_Default) {
   1.715 +    // Do this only on the child process.  The thread IPC bridge
   1.716 +    // is also used to communicate chrome observer notifications.
   1.717 +    DOMStorageCache::StartDatabase();
   1.718 +  }
   1.719 +}
   1.720 +
   1.721 +} // ::dom
   1.722 +} // ::mozilla

mercurial