LCOV - code coverage report
Current view: top level - dom/storage - LocalStorageManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 87 196 44.4 %
Date: 2017-07-14 16:53:18 Functions: 12 23 52.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=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
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "LocalStorageManager.h"
       8             : #include "LocalStorage.h"
       9             : #include "StorageDBThread.h"
      10             : #include "StorageUtils.h"
      11             : 
      12             : #include "nsIScriptSecurityManager.h"
      13             : #include "nsIEffectiveTLDService.h"
      14             : 
      15             : #include "nsNetUtil.h"
      16             : #include "nsNetCID.h"
      17             : #include "nsIURL.h"
      18             : #include "nsPrintfCString.h"
      19             : #include "nsXULAppAPI.h"
      20             : #include "nsThreadUtils.h"
      21             : #include "nsIObserverService.h"
      22             : #include "mozilla/Services.h"
      23             : #include "mozilla/Preferences.h"
      24             : 
      25             : // Only allow relatively small amounts of data since performance of
      26             : // the synchronous IO is very bad.
      27             : // We are enforcing simple per-origin quota only.
      28             : #define DEFAULT_QUOTA_LIMIT (5 * 1024)
      29             : 
      30             : namespace mozilla {
      31             : namespace dom {
      32             : 
      33             : using namespace StorageUtils;
      34             : 
      35             : namespace {
      36             : 
      37             : int32_t gQuotaLimit = DEFAULT_QUOTA_LIMIT;
      38             : 
      39             : } // namespace
      40             : 
      41             : LocalStorageManager* LocalStorageManager::sSelf = nullptr;
      42             : 
      43             : // static
      44             : uint32_t
      45           0 : LocalStorageManager::GetQuota()
      46             : {
      47             :   static bool preferencesInitialized = false;
      48           0 :   if (!preferencesInitialized) {
      49             :     mozilla::Preferences::AddIntVarCache(&gQuotaLimit,
      50             :                                          "dom.storage.default_quota",
      51           0 :                                          DEFAULT_QUOTA_LIMIT);
      52           0 :     preferencesInitialized = true;
      53             :   }
      54             : 
      55           0 :   return gQuotaLimit * 1024; // pref is in kBs
      56             : }
      57             : 
      58          24 : NS_IMPL_ISUPPORTS(LocalStorageManager,
      59             :                   nsIDOMStorageManager)
      60             : 
      61           2 : LocalStorageManager::LocalStorageManager()
      62             :   : mCaches(8)
      63           2 :   , mLowDiskSpace(false)
      64             : {
      65           2 :   StorageObserver* observer = StorageObserver::Self();
      66           2 :   NS_ASSERTION(observer, "No StorageObserver, cannot observe private data delete notifications!");
      67             : 
      68           2 :   if (observer) {
      69           2 :     observer->AddSink(this);
      70             :   }
      71             : 
      72           2 :   NS_ASSERTION(!sSelf, "Somebody is trying to do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\"");
      73           2 :   sSelf = this;
      74             : 
      75           2 :   if (!XRE_IsParentProcess()) {
      76             :     // Do this only on the child process.  The thread IPC bridge
      77             :     // is also used to communicate chrome observer notifications.
      78             :     // Note: must be called after we set sSelf
      79           1 :     LocalStorageCache::StartDatabase();
      80             :   }
      81           2 : }
      82             : 
      83           0 : LocalStorageManager::~LocalStorageManager()
      84             : {
      85           0 :   StorageObserver* observer = StorageObserver::Self();
      86           0 :   if (observer) {
      87           0 :     observer->RemoveSink(this);
      88             :   }
      89             : 
      90           0 :   sSelf = nullptr;
      91           0 : }
      92             : 
      93             : namespace {
      94             : 
      95             : nsresult
      96           1 : CreateQuotaDBKey(nsIPrincipal* aPrincipal,
      97             :                  nsACString& aKey)
      98             : {
      99             :   nsresult rv;
     100             : 
     101           2 :   nsCOMPtr<nsIEffectiveTLDService> eTLDService(do_GetService(
     102           2 :     NS_EFFECTIVETLDSERVICE_CONTRACTID, &rv));
     103           1 :   NS_ENSURE_SUCCESS(rv, rv);
     104             : 
     105           2 :   nsCOMPtr<nsIURI> uri;
     106           1 :   rv = aPrincipal->GetURI(getter_AddRefs(uri));
     107           1 :   NS_ENSURE_SUCCESS(rv, rv);
     108           1 :   NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
     109             : 
     110           2 :   nsAutoCString eTLDplusOne;
     111           1 :   rv = eTLDService->GetBaseDomain(uri, 0, eTLDplusOne);
     112           1 :   if (NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS == rv) {
     113             :     // XXX bug 357323 - what to do for localhost/file exactly?
     114           1 :     rv = uri->GetAsciiHost(eTLDplusOne);
     115             :   }
     116           1 :   NS_ENSURE_SUCCESS(rv, rv);
     117             : 
     118           1 :   aKey.Truncate();
     119           1 :   aPrincipal->OriginAttributesRef().CreateSuffix(aKey);
     120             : 
     121           2 :   nsAutoCString subdomainsDBKey;
     122           1 :   CreateReversedDomain(eTLDplusOne, subdomainsDBKey);
     123             : 
     124           1 :   aKey.Append(':');
     125           1 :   aKey.Append(subdomainsDBKey);
     126             : 
     127           1 :   return NS_OK;
     128             : }
     129             : 
     130             : } // namespace
     131             : 
     132             : // static
     133             : nsCString
     134           1 : LocalStorageManager::CreateOrigin(const nsACString& aOriginSuffix,
     135             :                                   const nsACString& aOriginNoSuffix)
     136             : {
     137             :   // Note: some hard-coded sqlite statements are dependent on the format this
     138             :   // method returns.  Changing this without updating those sqlite statements
     139             :   // will cause malfunction.
     140             : 
     141           2 :   nsAutoCString scope;
     142           1 :   scope.Append(aOriginSuffix);
     143           1 :   scope.Append(':');
     144           1 :   scope.Append(aOriginNoSuffix);
     145           2 :   return scope;
     146             : }
     147             : 
     148             : LocalStorageCache*
     149           3 : LocalStorageManager::GetCache(const nsACString& aOriginSuffix,
     150             :                               const nsACString& aOriginNoSuffix)
     151             : {
     152           3 :   CacheOriginHashtable* table = mCaches.LookupOrAdd(aOriginSuffix);
     153           3 :   LocalStorageCacheHashKey* entry = table->GetEntry(aOriginNoSuffix);
     154           3 :   if (!entry) {
     155           1 :     return nullptr;
     156             :   }
     157             : 
     158           2 :   return entry->cache();
     159             : }
     160             : 
     161             : already_AddRefed<StorageUsage>
     162           2 : LocalStorageManager::GetOriginUsage(const nsACString& aOriginNoSuffix)
     163             : {
     164           4 :   RefPtr<StorageUsage> usage;
     165           2 :   if (mUsages.Get(aOriginNoSuffix, &usage)) {
     166           1 :     return usage.forget();
     167             :   }
     168             : 
     169           1 :   usage = new StorageUsage(aOriginNoSuffix);
     170             : 
     171           1 :   StorageDBBridge* db = LocalStorageCache::StartDatabase();
     172           1 :   if (db) {
     173           1 :     db->AsyncGetUsage(usage);
     174             :   }
     175             : 
     176           1 :   mUsages.Put(aOriginNoSuffix, usage);
     177             : 
     178           1 :   return usage.forget();
     179             : }
     180             : 
     181             : already_AddRefed<LocalStorageCache>
     182           1 : LocalStorageManager::PutCache(const nsACString& aOriginSuffix,
     183             :                               const nsACString& aOriginNoSuffix,
     184             :                               nsIPrincipal* aPrincipal)
     185             : {
     186           1 :   CacheOriginHashtable* table = mCaches.LookupOrAdd(aOriginSuffix);
     187           1 :   LocalStorageCacheHashKey* entry = table->PutEntry(aOriginNoSuffix);
     188           2 :   RefPtr<LocalStorageCache> cache = entry->cache();
     189             : 
     190           2 :   nsAutoCString quotaOrigin;
     191           1 :   CreateQuotaDBKey(aPrincipal, quotaOrigin);
     192             : 
     193             :   // Lifetime handled by the cache, do persist
     194           1 :   cache->Init(this, true, aPrincipal, quotaOrigin);
     195           2 :   return cache.forget();
     196             : }
     197             : 
     198             : void
     199           0 : LocalStorageManager::DropCache(LocalStorageCache* aCache)
     200             : {
     201           0 :   if (!NS_IsMainThread()) {
     202           0 :     NS_WARNING("StorageManager::DropCache called on a non-main thread, shutting down?");
     203             :   }
     204             : 
     205           0 :   CacheOriginHashtable* table = mCaches.LookupOrAdd(aCache->OriginSuffix());
     206           0 :   table->RemoveEntry(aCache->OriginNoSuffix());
     207           0 : }
     208             : 
     209             : nsresult
     210           3 : LocalStorageManager::GetStorageInternal(CreateMode aCreateMode,
     211             :                                         mozIDOMWindow* aWindow,
     212             :                                         nsIPrincipal* aPrincipal,
     213             :                                         const nsAString& aDocumentURI,
     214             :                                         bool aPrivate,
     215             :                                         nsIDOMStorage** aRetval)
     216             : {
     217           6 :   nsAutoCString originAttrSuffix;
     218           6 :   nsAutoCString originKey;
     219             : 
     220           3 :   nsresult rv = GenerateOriginKey(aPrincipal, originAttrSuffix, originKey);
     221           3 :   if (NS_FAILED(rv)) {
     222           2 :     return NS_ERROR_NOT_AVAILABLE;
     223             :   }
     224             : 
     225           2 :   RefPtr<LocalStorageCache> cache = GetCache(originAttrSuffix, originKey);
     226             : 
     227             :   // Get or create a cache for the given scope
     228           1 :   if (!cache) {
     229           1 :     if (aCreateMode == CreateMode::UseIfExistsNeverCreate) {
     230           0 :       *aRetval = nullptr;
     231           0 :       return NS_OK;
     232             :     }
     233             : 
     234           1 :     if (aCreateMode == CreateMode::CreateIfShouldPreload) {
     235             :       // This is a demand to just preload the cache, if the scope has
     236             :       // no data stored, bypass creation and preload of the cache.
     237           1 :       StorageDBBridge* db = LocalStorageCache::GetDatabase();
     238           1 :       if (db) {
     239           1 :         if (!db->ShouldPreloadOrigin(LocalStorageManager::CreateOrigin(originAttrSuffix, originKey))) {
     240           0 :           return NS_OK;
     241             :         }
     242             :       } else {
     243           0 :         if (originKey.EqualsLiteral("knalb.:about")) {
     244           0 :           return NS_OK;
     245             :         }
     246             :       }
     247             :     }
     248             : 
     249             :     // There is always a single instance of a cache per scope
     250             :     // in a single instance of a DOM storage manager.
     251           1 :     cache = PutCache(originAttrSuffix, originKey, aPrincipal);
     252             :   }
     253             : 
     254           1 :   if (aRetval) {
     255           2 :     nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
     256             : 
     257             :     nsCOMPtr<nsIDOMStorage> storage = new LocalStorage(
     258           3 :       inner, this, cache, aDocumentURI, aPrincipal, aPrivate);
     259           1 :     storage.forget(aRetval);
     260             :   }
     261             : 
     262           1 :   return NS_OK;
     263             : }
     264             : 
     265             : NS_IMETHODIMP
     266           3 : LocalStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal,
     267             :                                      nsIDOMStorage** aRetval)
     268             : {
     269           3 :   return GetStorageInternal(CreateMode::CreateIfShouldPreload, nullptr,
     270           6 :                             aPrincipal, EmptyString(), false, aRetval);
     271             : }
     272             : 
     273             : NS_IMETHODIMP
     274           0 : LocalStorageManager::CreateStorage(mozIDOMWindow* aWindow,
     275             :                                    nsIPrincipal* aPrincipal,
     276             :                                    const nsAString& aDocumentURI,
     277             :                                    bool aPrivate,
     278             :                                    nsIDOMStorage** aRetval)
     279             : {
     280           0 :   return GetStorageInternal(CreateMode::CreateAlways, aWindow, aPrincipal,
     281           0 :                             aDocumentURI, aPrivate, aRetval);
     282             : }
     283             : 
     284             : NS_IMETHODIMP
     285           0 : LocalStorageManager::GetStorage(mozIDOMWindow* aWindow,
     286             :                                 nsIPrincipal* aPrincipal,
     287             :                                 bool aPrivate,
     288             :                                 nsIDOMStorage** aRetval)
     289             : {
     290           0 :   return GetStorageInternal(CreateMode::UseIfExistsNeverCreate, aWindow,
     291           0 :                             aPrincipal, EmptyString(), aPrivate, aRetval);
     292             : }
     293             : 
     294             : NS_IMETHODIMP
     295           0 : LocalStorageManager::CloneStorage(nsIDOMStorage* aStorage)
     296             : {
     297             :   // Cloning is supported only for sessionStorage
     298           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     299             : }
     300             : 
     301             : NS_IMETHODIMP
     302           0 : LocalStorageManager::CheckStorage(nsIPrincipal* aPrincipal,
     303             :                                   nsIDOMStorage* aStorage,
     304             :                                   bool* aRetval)
     305             : {
     306             :   nsresult rv;
     307             : 
     308           0 :   RefPtr<LocalStorage> storage = static_cast<LocalStorage*>(aStorage);
     309           0 :   if (!storage) {
     310           0 :     return NS_ERROR_UNEXPECTED;
     311             :   }
     312             : 
     313           0 :   *aRetval = false;
     314             : 
     315           0 :   if (!aPrincipal) {
     316           0 :     return NS_ERROR_NOT_AVAILABLE;
     317             :   }
     318             : 
     319           0 :   nsAutoCString suffix;
     320           0 :   nsAutoCString origin;
     321           0 :   rv = GenerateOriginKey(aPrincipal, suffix, origin);
     322           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     323           0 :     return rv;
     324             :   }
     325             : 
     326           0 :   LocalStorageCache* cache = GetCache(suffix, origin);
     327           0 :   if (cache != storage->GetCache()) {
     328           0 :     return NS_OK;
     329             :   }
     330             : 
     331           0 :   if (!storage->PrincipalEquals(aPrincipal)) {
     332           0 :     return NS_OK;
     333             :   }
     334             : 
     335           0 :   *aRetval = true;
     336           0 :   return NS_OK;
     337             : }
     338             : 
     339             : // Obsolete nsIDOMStorageManager methods
     340             : 
     341             : NS_IMETHODIMP
     342           0 : LocalStorageManager::GetLocalStorageForPrincipal(nsIPrincipal* aPrincipal,
     343             :                                                  const nsAString& aDocumentURI,
     344             :                                                  bool aPrivate,
     345             :                                                  nsIDOMStorage** aRetval)
     346             : {
     347           0 :   return CreateStorage(nullptr, aPrincipal, aDocumentURI, aPrivate, aRetval);
     348             : }
     349             : 
     350             : void
     351           0 : LocalStorageManager::ClearCaches(uint32_t aUnloadFlags,
     352             :                                  const OriginAttributesPattern& aPattern,
     353             :                                  const nsACString& aOriginScope)
     354             : {
     355           0 :   for (auto iter1 = mCaches.Iter(); !iter1.Done(); iter1.Next()) {
     356           0 :     OriginAttributes oa;
     357           0 :     DebugOnly<bool> rv = oa.PopulateFromSuffix(iter1.Key());
     358           0 :     MOZ_ASSERT(rv);
     359           0 :     if (!aPattern.Matches(oa)) {
     360             :       // This table doesn't match the given origin attributes pattern
     361           0 :       continue;
     362             :     }
     363             : 
     364           0 :     CacheOriginHashtable* table = iter1.Data();
     365             : 
     366           0 :     for (auto iter2 = table->Iter(); !iter2.Done(); iter2.Next()) {
     367           0 :       LocalStorageCache* cache = iter2.Get()->cache();
     368             : 
     369           0 :       if (aOriginScope.IsEmpty() ||
     370           0 :           StringBeginsWith(cache->OriginNoSuffix(), aOriginScope)) {
     371           0 :         cache->UnloadItems(aUnloadFlags);
     372             :       }
     373             :     }
     374             :   }
     375           0 : }
     376             : 
     377             : nsresult
     378           0 : LocalStorageManager::Observe(const char* aTopic,
     379             :                              const nsAString& aOriginAttributesPattern,
     380             :                              const nsACString& aOriginScope)
     381             : {
     382           0 :   OriginAttributesPattern pattern;
     383           0 :   if (!pattern.Init(aOriginAttributesPattern)) {
     384           0 :     NS_ERROR("Cannot parse origin attributes pattern");
     385           0 :     return NS_ERROR_FAILURE;
     386             :   }
     387             : 
     388             :   // Clear everything, caches + database
     389           0 :   if (!strcmp(aTopic, "cookie-cleared")) {
     390           0 :     ClearCaches(LocalStorageCache::kUnloadComplete, pattern, EmptyCString());
     391           0 :     return NS_OK;
     392             :   }
     393             : 
     394             :   // Clear from caches everything that has been stored
     395             :   // while in session-only mode
     396           0 :   if (!strcmp(aTopic, "session-only-cleared")) {
     397           0 :     ClearCaches(LocalStorageCache::kUnloadSession, pattern, aOriginScope);
     398           0 :     return NS_OK;
     399             :   }
     400             : 
     401             :   // Clear everything (including so and pb data) from caches and database
     402             :   // for the gived domain and subdomains.
     403           0 :   if (!strcmp(aTopic, "domain-data-cleared")) {
     404           0 :     ClearCaches(LocalStorageCache::kUnloadComplete, pattern, aOriginScope);
     405           0 :     return NS_OK;
     406             :   }
     407             : 
     408             :   // Clear all private-browsing caches
     409           0 :   if (!strcmp(aTopic, "private-browsing-data-cleared")) {
     410           0 :     ClearCaches(LocalStorageCache::kUnloadPrivate, pattern, EmptyCString());
     411           0 :     return NS_OK;
     412             :   }
     413             : 
     414             :   // Clear localStorage data beloging to an origin pattern
     415           0 :   if (!strcmp(aTopic, "origin-attr-pattern-cleared")) {
     416           0 :     ClearCaches(LocalStorageCache::kUnloadComplete, pattern, EmptyCString());
     417           0 :     return NS_OK;
     418             :   }
     419             : 
     420           0 :   if (!strcmp(aTopic, "profile-change")) {
     421             :     // For case caches are still referenced - clear them completely
     422           0 :     ClearCaches(LocalStorageCache::kUnloadComplete, pattern, EmptyCString());
     423           0 :     mCaches.Clear();
     424           0 :     return NS_OK;
     425             :   }
     426             : 
     427           0 :   if (!strcmp(aTopic, "low-disk-space")) {
     428           0 :     mLowDiskSpace = true;
     429           0 :     return NS_OK;
     430             :   }
     431             : 
     432           0 :   if (!strcmp(aTopic, "no-low-disk-space")) {
     433           0 :     mLowDiskSpace = false;
     434           0 :     return NS_OK;
     435             :   }
     436             : 
     437             : #ifdef DOM_STORAGE_TESTS
     438           0 :   if (!strcmp(aTopic, "test-reload")) {
     439             :     // This immediately completely reloads all caches from the database.
     440           0 :     ClearCaches(LocalStorageCache::kTestReload, pattern, EmptyCString());
     441           0 :     return NS_OK;
     442             :   }
     443             : 
     444           0 :   if (!strcmp(aTopic, "test-flushed")) {
     445           0 :     if (!XRE_IsParentProcess()) {
     446           0 :       nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     447           0 :       if (obs) {
     448           0 :         obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr);
     449             :       }
     450             :     }
     451             : 
     452           0 :     return NS_OK;
     453             :   }
     454             : #endif
     455             : 
     456           0 :   NS_ERROR("Unexpected topic");
     457           0 :   return NS_ERROR_UNEXPECTED;
     458             : }
     459             : 
     460             : LocalStorageManager*
     461           1 : LocalStorageManager::Ensure()
     462             : {
     463           1 :   if (sSelf) {
     464           1 :     return sSelf;
     465             :   }
     466             : 
     467             :   // Cause sSelf to be populated.
     468             :   nsCOMPtr<nsIDOMStorageManager> initializer =
     469           0 :     do_GetService("@mozilla.org/dom/localStorage-manager;1");
     470           0 :   MOZ_ASSERT(sSelf, "Didn't initialize?");
     471             : 
     472           0 :   return sSelf;
     473             : }
     474             : 
     475             : } // namespace dom
     476             : } // namespace mozilla

Generated by: LCOV version 1.13