LCOV - code coverage report
Current view: top level - dom/storage - StorageObserver.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 50 167 29.9 %
Date: 2017-07-14 16:53:18 Functions: 8 10 80.0 %
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 "StorageObserver.h"
       8             : 
       9             : #include "LocalStorageCache.h"
      10             : #include "StorageDBThread.h"
      11             : #include "StorageUtils.h"
      12             : 
      13             : #include "mozilla/BasePrincipal.h"
      14             : #include "nsIObserverService.h"
      15             : #include "nsIURI.h"
      16             : #include "nsIURL.h"
      17             : #include "nsIScriptSecurityManager.h"
      18             : #include "nsIPermission.h"
      19             : #include "nsIIDNService.h"
      20             : #include "nsICookiePermission.h"
      21             : 
      22             : #include "nsPrintfCString.h"
      23             : #include "nsXULAppAPI.h"
      24             : #include "nsEscape.h"
      25             : #include "nsNetCID.h"
      26             : #include "mozilla/Preferences.h"
      27             : #include "mozilla/Services.h"
      28             : #include "nsServiceManagerUtils.h"
      29             : 
      30             : namespace mozilla {
      31             : namespace dom {
      32             : 
      33             : using namespace StorageUtils;
      34             : 
      35             : static const char kStartupTopic[] = "sessionstore-windows-restored";
      36             : static const uint32_t kStartupDelay = 0;
      37             : 
      38             : const char kTestingPref[] = "dom.storage.testing";
      39             : 
      40         133 : NS_IMPL_ISUPPORTS(StorageObserver,
      41             :                   nsIObserver,
      42             :                   nsISupportsWeakReference)
      43             : 
      44             : StorageObserver* StorageObserver::sSelf = nullptr;
      45             : 
      46             : // static
      47             : nsresult
      48           3 : StorageObserver::Init()
      49             : {
      50           3 :   if (sSelf) {
      51           0 :     return NS_OK;
      52             :   }
      53             : 
      54           6 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
      55           3 :   if (!obs) {
      56           0 :     return NS_ERROR_UNEXPECTED;
      57             :   }
      58             : 
      59           3 :   sSelf = new StorageObserver();
      60           3 :   NS_ADDREF(sSelf);
      61             : 
      62             :   // Chrome clear operations.
      63           3 :   obs->AddObserver(sSelf, kStartupTopic, true);
      64           3 :   obs->AddObserver(sSelf, "cookie-changed", true);
      65           3 :   obs->AddObserver(sSelf, "perm-changed", true);
      66           3 :   obs->AddObserver(sSelf, "browser:purge-domain-data", true);
      67           3 :   obs->AddObserver(sSelf, "last-pb-context-exited", true);
      68           3 :   obs->AddObserver(sSelf, "clear-origin-attributes-data", true);
      69             : 
      70             :   // Shutdown
      71           3 :   obs->AddObserver(sSelf, "profile-after-change", true);
      72           3 :   obs->AddObserver(sSelf, "profile-before-change", true);
      73           3 :   obs->AddObserver(sSelf, "xpcom-shutdown", true);
      74             : 
      75             :   // Observe low device storage notifications.
      76           3 :   obs->AddObserver(sSelf, "disk-space-watcher", true);
      77             : 
      78             :   // Testing
      79             : #ifdef DOM_STORAGE_TESTS
      80           3 :   Preferences::RegisterCallbackAndCall(TestingPrefChanged, kTestingPref);
      81             : #endif
      82             : 
      83           3 :   return NS_OK;
      84             : }
      85             : 
      86             : // static
      87             : nsresult
      88           0 : StorageObserver::Shutdown()
      89             : {
      90           0 :   if (!sSelf) {
      91           0 :     return NS_ERROR_NOT_INITIALIZED;
      92             :   }
      93             : 
      94           0 :   NS_RELEASE(sSelf);
      95           0 :   return NS_OK;
      96             : }
      97             : 
      98             : // static
      99             : void
     100           3 : StorageObserver::TestingPrefChanged(const char* aPrefName, void* aClosure)
     101             : {
     102           6 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     103           3 :   if (!obs) {
     104           0 :     return;
     105             :   }
     106             : 
     107           3 :   if (Preferences::GetBool(kTestingPref)) {
     108           0 :     obs->AddObserver(sSelf, "domstorage-test-flush-force", true);
     109           0 :     if (XRE_IsParentProcess()) {
     110             :       // Only to forward to child process.
     111           0 :       obs->AddObserver(sSelf, "domstorage-test-flushed", true);
     112             :     }
     113           0 :     obs->AddObserver(sSelf, "domstorage-test-reload", true);
     114             :   } else {
     115           3 :     obs->RemoveObserver(sSelf, "domstorage-test-flush-force");
     116           3 :     if (XRE_IsParentProcess()) {
     117             :       // Only to forward to child process.
     118           1 :       obs->RemoveObserver(sSelf, "domstorage-test-flushed");
     119             :     }
     120           3 :     obs->RemoveObserver(sSelf, "domstorage-test-reload");
     121             :   }
     122             : }
     123             : 
     124             : void
     125           4 : StorageObserver::AddSink(StorageObserverSink* aObs)
     126             : {
     127           4 :   mSinks.AppendElement(aObs);
     128           4 : }
     129             : 
     130             : void
     131           0 : StorageObserver::RemoveSink(StorageObserverSink* aObs)
     132             : {
     133           0 :   mSinks.RemoveElement(aObs);
     134           0 : }
     135             : 
     136             : void
     137           1 : StorageObserver::Notify(const char* aTopic,
     138             :                         const nsAString& aOriginAttributesPattern,
     139             :                         const nsACString& aOriginScope)
     140             : {
     141           1 :   for (uint32_t i = 0; i < mSinks.Length(); ++i) {
     142           0 :     StorageObserverSink* sink = mSinks[i];
     143           0 :     sink->Observe(aTopic, aOriginAttributesPattern, aOriginScope);
     144             :   }
     145           1 : }
     146             : 
     147             : NS_IMETHODIMP
     148           5 : StorageObserver::Observe(nsISupports* aSubject,
     149             :                          const char* aTopic,
     150             :                          const char16_t* aData)
     151             : {
     152             :   nsresult rv;
     153             : 
     154             :   // Start the thread that opens the database.
     155           5 :   if (!strcmp(aTopic, kStartupTopic)) {
     156           0 :     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     157           0 :     obs->RemoveObserver(this, kStartupTopic);
     158             : 
     159           0 :     mDBThreadStartDelayTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
     160           0 :     if (!mDBThreadStartDelayTimer) {
     161           0 :       return NS_ERROR_UNEXPECTED;
     162             :     }
     163             : 
     164           0 :     mDBThreadStartDelayTimer->Init(this, nsITimer::TYPE_ONE_SHOT, kStartupDelay);
     165             : 
     166           0 :     return NS_OK;
     167             :   }
     168             : 
     169             :   // Timer callback used to start the database a short timer after startup
     170           5 :   if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
     171           0 :     nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject);
     172           0 :     if (!timer) {
     173           0 :       return NS_ERROR_UNEXPECTED;
     174             :     }
     175             : 
     176           0 :     if (timer == mDBThreadStartDelayTimer) {
     177           0 :       mDBThreadStartDelayTimer = nullptr;
     178             : 
     179           0 :       StorageDBBridge* db = LocalStorageCache::StartDatabase();
     180           0 :       NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
     181             :     }
     182             : 
     183           0 :     return NS_OK;
     184             :   }
     185             : 
     186             :   // Clear everything, caches + database
     187           5 :   if (!strcmp(aTopic, "cookie-changed")) {
     188           0 :     if (!NS_LITERAL_STRING("cleared").Equals(aData)) {
     189           0 :       return NS_OK;
     190             :     }
     191             : 
     192           0 :     StorageDBBridge* db = LocalStorageCache::StartDatabase();
     193           0 :     NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
     194             : 
     195           0 :     db->AsyncClearAll();
     196             : 
     197           0 :     Notify("cookie-cleared");
     198             : 
     199           0 :     return NS_OK;
     200             :   }
     201             : 
     202             :   // Clear from caches everything that has been stored
     203             :   // while in session-only mode
     204           5 :   if (!strcmp(aTopic, "perm-changed")) {
     205             :     // Check for cookie permission change
     206           8 :     nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
     207           4 :     if (!perm) {
     208           0 :       return NS_OK;
     209             :     }
     210             : 
     211           8 :     nsAutoCString type;
     212           4 :     perm->GetType(type);
     213           4 :     if (type != NS_LITERAL_CSTRING("cookie")) {
     214           4 :       return NS_OK;
     215             :     }
     216             : 
     217           0 :     uint32_t cap = 0;
     218           0 :     perm->GetCapability(&cap);
     219           0 :     if (!(cap & nsICookiePermission::ACCESS_SESSION) ||
     220           0 :         !NS_LITERAL_STRING("deleted").Equals(nsDependentString(aData))) {
     221           0 :       return NS_OK;
     222             :     }
     223             : 
     224           0 :     nsCOMPtr<nsIPrincipal> principal;
     225           0 :     perm->GetPrincipal(getter_AddRefs(principal));
     226           0 :     if (!principal) {
     227           0 :       return NS_OK;
     228             :     }
     229             : 
     230           0 :     nsAutoCString originSuffix;
     231           0 :     BasePrincipal::Cast(principal)->OriginAttributesRef().CreateSuffix(originSuffix);
     232             : 
     233           0 :     nsCOMPtr<nsIURI> origin;
     234           0 :     principal->GetURI(getter_AddRefs(origin));
     235           0 :     if (!origin) {
     236           0 :       return NS_OK;
     237             :     }
     238             : 
     239           0 :     nsAutoCString host;
     240           0 :     origin->GetHost(host);
     241           0 :     if (host.IsEmpty()) {
     242           0 :       return NS_OK;
     243             :     }
     244             : 
     245           0 :     nsAutoCString originScope;
     246           0 :     rv = CreateReversedDomain(host, originScope);
     247           0 :     NS_ENSURE_SUCCESS(rv, rv);
     248             : 
     249           0 :     Notify("session-only-cleared", NS_ConvertUTF8toUTF16(originSuffix),
     250           0 :            originScope);
     251             : 
     252           0 :     return NS_OK;
     253             :   }
     254             : 
     255             :   // Clear everything (including so and pb data) from caches and database
     256             :   // for the gived domain and subdomains.
     257           1 :   if (!strcmp(aTopic, "browser:purge-domain-data")) {
     258             :     // Convert the domain name to the ACE format
     259           0 :     nsAutoCString aceDomain;
     260           0 :     nsCOMPtr<nsIIDNService> converter = do_GetService(NS_IDNSERVICE_CONTRACTID);
     261           0 :     if (converter) {
     262           0 :       rv = converter->ConvertUTF8toACE(NS_ConvertUTF16toUTF8(aData), aceDomain);
     263           0 :       NS_ENSURE_SUCCESS(rv, rv);
     264             :     } else {
     265             :       // In case the IDN service is not available, this is the best we can come
     266             :       // up with!
     267           0 :       rv = NS_EscapeURL(NS_ConvertUTF16toUTF8(aData),
     268             :                         esc_OnlyNonASCII | esc_AlwaysCopy,
     269             :                         aceDomain,
     270           0 :                         fallible);
     271           0 :       NS_ENSURE_SUCCESS(rv, rv);
     272             :     }
     273             : 
     274           0 :     nsAutoCString originScope;
     275           0 :     rv = CreateReversedDomain(aceDomain, originScope);
     276           0 :     NS_ENSURE_SUCCESS(rv, rv);
     277             : 
     278           0 :     StorageDBBridge* db = LocalStorageCache::StartDatabase();
     279           0 :     NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
     280             : 
     281           0 :     db->AsyncClearMatchingOrigin(originScope);
     282             : 
     283           0 :     Notify("domain-data-cleared", EmptyString(), originScope);
     284             : 
     285           0 :     return NS_OK;
     286             :   }
     287             : 
     288             :   // Clear all private-browsing caches
     289           1 :   if (!strcmp(aTopic, "last-pb-context-exited")) {
     290           0 :     Notify("private-browsing-data-cleared");
     291             : 
     292           0 :     return NS_OK;
     293             :   }
     294             : 
     295             :   // Clear data of the origins whose prefixes will match the suffix.
     296           1 :   if (!strcmp(aTopic, "clear-origin-attributes-data")) {
     297           0 :     OriginAttributesPattern pattern;
     298           0 :     if (!pattern.Init(nsDependentString(aData))) {
     299           0 :       NS_ERROR("Cannot parse origin attributes pattern");
     300           0 :       return NS_ERROR_FAILURE;
     301             :     }
     302             : 
     303           0 :     StorageDBBridge* db = LocalStorageCache::StartDatabase();
     304           0 :     NS_ENSURE_TRUE(db, NS_ERROR_FAILURE);
     305             : 
     306           0 :     db->AsyncClearMatchingOriginAttributes(pattern);
     307             : 
     308           0 :     Notify("origin-attr-pattern-cleared", nsDependentString(aData));
     309             : 
     310           0 :     return NS_OK;
     311             :   }
     312             : 
     313           1 :   if (!strcmp(aTopic, "profile-after-change")) {
     314           2 :     Notify("profile-change");
     315             : 
     316           1 :     return NS_OK;
     317             :   }
     318             : 
     319           0 :   if (!strcmp(aTopic, "profile-before-change") ||
     320           0 :       !strcmp(aTopic, "xpcom-shutdown")) {
     321           0 :     rv = LocalStorageCache::StopDatabase();
     322           0 :     if (NS_FAILED(rv)) {
     323           0 :       NS_WARNING("Error while stopping Storage DB background thread");
     324             :     }
     325             : 
     326           0 :     return NS_OK;
     327             :   }
     328             : 
     329           0 :   if (!strcmp(aTopic, "disk-space-watcher")) {
     330           0 :     if (NS_LITERAL_STRING("full").Equals(aData)) {
     331           0 :       Notify("low-disk-space");
     332           0 :     } else if (NS_LITERAL_STRING("free").Equals(aData)) {
     333           0 :       Notify("no-low-disk-space");
     334             :     }
     335             : 
     336           0 :     return NS_OK;
     337             :   }
     338             : 
     339             : #ifdef DOM_STORAGE_TESTS
     340           0 :   if (!strcmp(aTopic, "domstorage-test-flush-force")) {
     341           0 :     StorageDBBridge* db = LocalStorageCache::GetDatabase();
     342           0 :     if (db) {
     343           0 :       db->AsyncFlush();
     344             :     }
     345             : 
     346           0 :     return NS_OK;
     347             :   }
     348             : 
     349           0 :   if (!strcmp(aTopic, "domstorage-test-flushed")) {
     350             :     // Only used to propagate to IPC children
     351           0 :     Notify("test-flushed");
     352             : 
     353           0 :     return NS_OK;
     354             :   }
     355             : 
     356           0 :   if (!strcmp(aTopic, "domstorage-test-reload")) {
     357           0 :     Notify("test-reload");
     358             : 
     359           0 :     return NS_OK;
     360             :   }
     361             : #endif
     362             : 
     363           0 :   NS_ERROR("Unexpected topic");
     364           0 :   return NS_ERROR_UNEXPECTED;
     365             : }
     366             : 
     367             : } // namespace dom
     368             : } // namespace mozilla

Generated by: LCOV version 1.13