LCOV - code coverage report
Current view: top level - security/manager/ssl - DataStorage.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 169 502 33.7 %
Date: 2017-07-14 16:53:18 Functions: 27 58 46.6 %
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 "DataStorage.h"
       8             : 
       9             : #include "mozilla/Assertions.h"
      10             : #include "mozilla/ClearOnShutdown.h"
      11             : #include "mozilla/dom/PContent.h"
      12             : #include "mozilla/dom/ContentChild.h"
      13             : #include "mozilla/dom/ContentParent.h"
      14             : #include "mozilla/Preferences.h"
      15             : #include "mozilla/Services.h"
      16             : #include "mozilla/Telemetry.h"
      17             : #include "mozilla/Unused.h"
      18             : #include "nsAppDirectoryServiceDefs.h"
      19             : #include "nsDirectoryServiceUtils.h"
      20             : #include "nsIMemoryReporter.h"
      21             : #include "nsIObserverService.h"
      22             : #include "nsITimer.h"
      23             : #include "nsNetUtil.h"
      24             : #include "nsPrintfCString.h"
      25             : #include "nsStreamUtils.h"
      26             : #include "nsThreadUtils.h"
      27             : 
      28             : // NB: Read DataStorage.h first.
      29             : 
      30             : // The default time between data changing and a write, in milliseconds.
      31             : static const uint32_t sDataStorageDefaultTimerDelay = 5u * 60u * 1000u;
      32             : // The maximum score an entry can have (prevents overflow)
      33             : static const uint32_t sMaxScore = UINT32_MAX;
      34             : // The maximum number of entries per type of data (limits resource use)
      35             : static const uint32_t sMaxDataEntries = 1024;
      36             : static const int64_t sOneDayInMicroseconds = int64_t(24 * 60 * 60) *
      37             :                                              PR_USEC_PER_SEC;
      38             : 
      39             : namespace mozilla {
      40             : 
      41           3 : class DataStorageMemoryReporter final : public nsIMemoryReporter
      42             : {
      43           0 :   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
      44             :   ~DataStorageMemoryReporter() = default;
      45             : 
      46             : public:
      47             :   NS_DECL_ISUPPORTS
      48             : 
      49           0 :   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
      50             :                             nsISupports* aData, bool aAnonymize) final
      51             :   {
      52           0 :     nsTArray<nsString> fileNames;
      53           0 :     DataStorage::GetAllFileNames(fileNames);
      54           0 :     for (const auto& file: fileNames) {
      55           0 :       RefPtr<DataStorage> ds = DataStorage::GetFromRawFileName(file);
      56           0 :       size_t amount = ds->SizeOfIncludingThis(MallocSizeOf);
      57             :       nsPrintfCString path("explicit/data-storage/%s",
      58           0 :                            NS_ConvertUTF16toUTF8(file).get());
      59           0 :       Unused << aHandleReport->Callback(EmptyCString(), path, KIND_HEAP,
      60             :         UNITS_BYTES, amount,
      61           0 :         NS_LITERAL_CSTRING("Memory used by PSM data storage cache."),
      62           0 :         aData);
      63             :     }
      64           0 :     return NS_OK;
      65             :   }
      66             : };
      67             : 
      68          39 : NS_IMPL_ISUPPORTS(DataStorageMemoryReporter, nsIMemoryReporter)
      69             : 
      70         137 : NS_IMPL_ISUPPORTS(DataStorage, nsIObserver)
      71             : 
      72           3 : StaticAutoPtr<DataStorage::DataStorages> DataStorage::sDataStorages;
      73             : 
      74           9 : DataStorage::DataStorage(const nsString& aFilename)
      75             :   : mMutex("DataStorage::mMutex")
      76             :   , mPendingWrite(false)
      77             :   , mShuttingDown(false)
      78             :   , mInitCalled(false)
      79             :   , mReadyMonitor("DataStorage::mReadyMonitor")
      80             :   , mReady(false)
      81           9 :   , mFilename(aFilename)
      82             : {
      83           9 : }
      84             : 
      85           0 : DataStorage::~DataStorage()
      86             : {
      87           0 : }
      88             : 
      89             : // static
      90             : already_AddRefed<DataStorage>
      91           5 : DataStorage::Get(DataStorageClass aFilename)
      92             : {
      93           5 :   switch (aFilename) {
      94             : #define DATA_STORAGE(_)         \
      95             :     case DataStorageClass::_:   \
      96             :       return GetFromRawFileName(NS_LITERAL_STRING(#_ ".txt"));
      97             : #include "mozilla/DataStorageList.h"
      98             : #undef DATA_STORAGE
      99             :     default:
     100           0 :       MOZ_ASSERT_UNREACHABLE("Invalid DataStorage type passed?");
     101             :       return nullptr;
     102             :   }
     103             : }
     104             : 
     105             : // static
     106             : already_AddRefed<DataStorage>
     107          14 : DataStorage::GetFromRawFileName(const nsString& aFilename)
     108             : {
     109          14 :   MOZ_ASSERT(NS_IsMainThread());
     110          14 :   if (!sDataStorages) {
     111           3 :     sDataStorages = new DataStorages();
     112           3 :     ClearOnShutdown(&sDataStorages);
     113             :   }
     114          28 :   RefPtr<DataStorage> storage;
     115          14 :   if (!sDataStorages->Get(aFilename, getter_AddRefs(storage))) {
     116           9 :     storage = new DataStorage(aFilename);
     117           9 :     sDataStorages->Put(aFilename, storage);
     118             :   }
     119          28 :   return storage.forget();
     120             : }
     121             : 
     122             : // static
     123             : already_AddRefed<DataStorage>
     124           0 : DataStorage::GetIfExists(DataStorageClass aFilename)
     125             : {
     126           0 :   MOZ_ASSERT(NS_IsMainThread());
     127           0 :   if (!sDataStorages) {
     128           0 :     sDataStorages = new DataStorages();
     129             :   }
     130           0 :   nsString name;
     131           0 :   switch (aFilename) {
     132             : #define DATA_STORAGE(_)              \
     133             :     case DataStorageClass::_:        \
     134             :       name.AssignLiteral(#_ ".txt"); \
     135             :       break;
     136             : #include "mozilla/DataStorageList.h"
     137             : #undef DATA_STORAGE
     138             :     default:
     139           0 :       MOZ_ASSERT_UNREACHABLE("Invalid DataStorages type passed?");
     140             :   }
     141           0 :   RefPtr<DataStorage> storage;
     142           0 :   if (!name.IsEmpty()) {
     143           0 :     sDataStorages->Get(name, getter_AddRefs(storage));
     144             :   }
     145           0 :   return storage.forget();
     146             : }
     147             : 
     148             : // static
     149             : void
     150           2 : DataStorage::GetAllFileNames(nsTArray<nsString>& aItems)
     151             : {
     152           2 :   MOZ_ASSERT(NS_IsMainThread());
     153           2 :   if (!sDataStorages) {
     154           1 :     return;
     155             :   }
     156             : #define DATA_STORAGE(_)     \
     157             :   aItems.AppendElement(NS_LITERAL_STRING(#_ ".txt"));
     158             : #include "mozilla/DataStorageList.h"
     159             : #undef DATA_STORAGE
     160             : }
     161             : 
     162             : // static
     163             : void
     164           2 : DataStorage::GetAllChildProcessData(
     165             :   nsTArray<mozilla::dom::DataStorageEntry>& aEntries)
     166             : {
     167           4 :   nsTArray<nsString> storageFiles;
     168           2 :   GetAllFileNames(storageFiles);
     169           5 :   for (auto& file : storageFiles) {
     170           6 :     dom::DataStorageEntry entry;
     171           3 :     entry.filename() = file;
     172           6 :     RefPtr<DataStorage> storage = DataStorage::GetFromRawFileName(file);
     173           3 :     if (!storage->mInitCalled) {
     174             :       // Perhaps no consumer has initialized the DataStorage object yet,
     175             :       // so do that now!
     176           0 :       bool dataWillPersist = false;
     177           0 :       nsresult rv = storage->Init(dataWillPersist);
     178           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
     179           0 :         return;
     180             :       }
     181             :     }
     182           3 :     storage->GetAll(&entry.items());
     183           3 :     aEntries.AppendElement(Move(entry));
     184             :   }
     185             : }
     186             : 
     187             : // static
     188             : void
     189           2 : DataStorage::SetCachedStorageEntries(
     190             :   const InfallibleTArray<mozilla::dom::DataStorageEntry>& aEntries)
     191             : {
     192           2 :   MOZ_ASSERT(XRE_IsContentProcess());
     193             : 
     194             :   // Make sure to initialize all DataStorage classes.
     195             :   // For each one, we look through the list of our entries and if we find
     196             :   // a matching DataStorage object, we initialize it.
     197             :   //
     198             :   // Note that this is an O(n^2) operation, but the n here is very small
     199             :   // (currently 3).  There is a comment in the DataStorageList.h header
     200             :   // about updating the algorithm here to something more fancy if the list
     201             :   // of DataStorage items grows some day.
     202           4 :   nsTArray<dom::DataStorageEntry> entries;
     203             : #define DATA_STORAGE(_)                              \
     204             :   {                                                  \
     205             :     dom::DataStorageEntry entry;                     \
     206             :     entry.filename() = NS_LITERAL_STRING(#_ ".txt"); \
     207             :     for (auto& e : aEntries) {                       \
     208             :       if (entry.filename().Equals(e.filename())) {   \
     209             :         entry.items() = Move(e.items());             \
     210             :         break;                                       \
     211             :       }                                              \
     212             :     }                                                \
     213             :     entries.AppendElement(Move(entry));              \
     214             :   }
     215             : #include "mozilla/DataStorageList.h"
     216             : #undef DATA_STORAGE
     217             : 
     218           8 :   for (auto& entry : entries) {
     219             :     RefPtr<DataStorage> storage =
     220          12 :       DataStorage::GetFromRawFileName(entry.filename());
     221           6 :     bool dataWillPersist = false;
     222           6 :     storage->Init(dataWillPersist, &entry.items());
     223             :   }
     224           2 : }
     225             : 
     226             : size_t
     227           0 : DataStorage::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     228             : {
     229             :   size_t sizeOfExcludingThis =
     230           0 :     mPersistentDataTable.ShallowSizeOfExcludingThis(aMallocSizeOf) +
     231           0 :     mTemporaryDataTable.ShallowSizeOfExcludingThis(aMallocSizeOf) +
     232           0 :     mPrivateDataTable.ShallowSizeOfExcludingThis(aMallocSizeOf) +
     233           0 :     mFilename.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
     234           0 :   return aMallocSizeOf(this) + sizeOfExcludingThis;
     235             : }
     236             : 
     237             : nsresult
     238          11 : DataStorage::Init(bool& aDataWillPersist,
     239             :                   const InfallibleTArray<mozilla::dom::DataStorageItem>* aItems)
     240             : {
     241             :   // Don't access the observer service or preferences off the main thread.
     242          11 :   if (!NS_IsMainThread()) {
     243           0 :     MOZ_ASSERT_UNREACHABLE("DataStorage::Init called off main thread");
     244             :     return NS_ERROR_NOT_SAME_THREAD;
     245             :   }
     246             : 
     247          22 :   MutexAutoLock lock(mMutex);
     248             : 
     249             :   // Ignore attempts to initialize several times.
     250          11 :   if (mInitCalled) {
     251           2 :     return NS_OK;
     252             :   }
     253             : 
     254           9 :   mInitCalled = true;
     255             : 
     256             :   static bool memoryReporterRegistered = false;
     257           9 :   if (!memoryReporterRegistered) {
     258             :     nsresult rv =
     259           3 :       RegisterStrongMemoryReporter(new DataStorageMemoryReporter());
     260           3 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     261           0 :       return rv;
     262             :     }
     263           3 :     memoryReporterRegistered = true;
     264             :   }
     265             : 
     266             :   nsresult rv;
     267           9 :   if (XRE_IsParentProcess()) {
     268           3 :     MOZ_ASSERT(!aItems);
     269             : 
     270           3 :     rv = NS_NewNamedThread("DataStorage", getter_AddRefs(mWorkerThread));
     271           3 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     272           0 :       return rv;
     273             :     }
     274             : 
     275           3 :     rv = AsyncReadData(aDataWillPersist, lock);
     276           3 :     if (NS_FAILED(rv)) {
     277           0 :       return rv;
     278             :     }
     279             :   } else {
     280             :     // In the child process, we use the data passed to us by the parent process
     281             :     // to initialize.
     282           6 :     MOZ_ASSERT(XRE_IsContentProcess());
     283           6 :     MOZ_ASSERT(aItems);
     284             : 
     285           6 :     aDataWillPersist = false;
     286           6 :     for (auto& item : *aItems) {
     287           0 :       Entry entry;
     288           0 :       entry.mValue = item.value();
     289           0 :       rv = PutInternal(item.key(), entry, item.type(), lock);
     290           0 :       if (NS_FAILED(rv)) {
     291           0 :         return rv;
     292             :       }
     293             :     }
     294           6 :     mReady = true;
     295           6 :     NotifyObservers("data-storage-ready");
     296             :   }
     297             : 
     298          18 :   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     299           9 :   if (NS_WARN_IF(!os)) {
     300           0 :     return NS_ERROR_FAILURE;
     301             :   }
     302             :   // Clear private data as appropriate.
     303           9 :   os->AddObserver(this, "last-pb-context-exited", false);
     304             :   // Observe shutdown; save data and prevent any further writes.
     305             :   // In the parent process, we need to write to the profile directory, so
     306             :   // we should listen for profile-before-change so that we can safely
     307             :   // write to the profile.  In the content process however we don't have
     308             :   // access to the profile directory and profile notifications are not
     309             :   // dispatched, so we need to clean up on xpcom-shutdown.
     310           9 :   if (XRE_IsParentProcess()) {
     311           3 :     os->AddObserver(this, "profile-before-change", false);
     312             :   }
     313             :   // In the Parent process, this is a backstop for xpcshell and other cases
     314             :   // where profile-before-change might not get sent.
     315           9 :   os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
     316             : 
     317             :   // For test purposes, we can set the write timer to be very fast.
     318           9 :   mTimerDelay = Preferences::GetInt("test.datastorage.write_timer_ms",
     319             :                                     sDataStorageDefaultTimerDelay);
     320           9 :   rv = Preferences::AddStrongObserver(this, "test.datastorage.write_timer_ms");
     321           9 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     322           0 :     return rv;
     323             :   }
     324             : 
     325           9 :   return NS_OK;
     326             : }
     327             : 
     328             : class DataStorage::Reader : public Runnable
     329             : {
     330             : public:
     331           3 :   explicit Reader(DataStorage* aDataStorage)
     332           3 :     : Runnable("DataStorage::Reader")
     333           3 :     , mDataStorage(aDataStorage)
     334             :   {
     335           3 :   }
     336             :   ~Reader();
     337             : 
     338             : private:
     339             :   NS_DECL_NSIRUNNABLE
     340             : 
     341             :   static nsresult ParseLine(nsDependentCSubstring& aLine, nsCString& aKeyOut,
     342             :                             Entry& aEntryOut);
     343             : 
     344             :   RefPtr<DataStorage> mDataStorage;
     345             : };
     346             : 
     347           9 : DataStorage::Reader::~Reader()
     348             : {
     349             :   // Notify that calls to Get can proceed.
     350             :   {
     351           6 :     MonitorAutoLock readyLock(mDataStorage->mReadyMonitor);
     352           3 :     mDataStorage->mReady = true;
     353           3 :     nsresult rv = mDataStorage->mReadyMonitor.NotifyAll();
     354           3 :     Unused << NS_WARN_IF(NS_FAILED(rv));
     355             :   }
     356             : 
     357             :   // This is for tests.
     358             :   nsCOMPtr<nsIRunnable> job =
     359           6 :     NewRunnableMethod<const char*>("DataStorage::NotifyObservers",
     360             :                                    mDataStorage,
     361             :                                    &DataStorage::NotifyObservers,
     362           6 :                                    "data-storage-ready");
     363           3 :   nsresult rv = NS_DispatchToMainThread(job, NS_DISPATCH_NORMAL);
     364           3 :   Unused << NS_WARN_IF(NS_FAILED(rv));
     365           9 : }
     366             : 
     367             : NS_IMETHODIMP
     368           3 : DataStorage::Reader::Run()
     369             : {
     370             :   nsresult rv;
     371             :   // Concurrent operations on nsIFile objects are not guaranteed to be safe,
     372             :   // so we clone the file while holding the lock and then release the lock.
     373             :   // At that point, we can safely operate on the clone.
     374           6 :   nsCOMPtr<nsIFile> file;
     375             :   {
     376           6 :     MutexAutoLock lock(mDataStorage->mMutex);
     377             :     // If we don't have a profile, bail.
     378           3 :     if (!mDataStorage->mBackingFile) {
     379           0 :       return NS_OK;
     380             :     }
     381           3 :     rv = mDataStorage->mBackingFile->Clone(getter_AddRefs(file));
     382           3 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     383           0 :       return rv;
     384             :     }
     385             :   }
     386           6 :   nsCOMPtr<nsIInputStream> fileInputStream;
     387           3 :   rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), file);
     388             :   // If we failed for some reason other than the file doesn't exist, bail.
     389           3 :   if (NS_WARN_IF(NS_FAILED(rv) &&
     390             :                  rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&  // on Unix
     391             :                  rv != NS_ERROR_FILE_NOT_FOUND)) {             // on Windows
     392           0 :     return rv;
     393             :   }
     394             : 
     395             :   // If there is a file with data in it, read it. If there isn't,
     396             :   // we'll essentially fall through to notifying that we're good to go.
     397           6 :   nsCString data;
     398           3 :   if (fileInputStream) {
     399             :     // Limit to 2MB of data, but only store sMaxDataEntries entries.
     400           3 :     rv = NS_ConsumeStream(fileInputStream, 1u << 21, data);
     401           3 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     402           0 :       return rv;
     403             :     }
     404             :   }
     405             : 
     406             :   // Atomically parse the data and insert the entries read.
     407             :   // Don't clear existing entries - they may have been inserted between when
     408             :   // this read was kicked-off and when it was run.
     409             :   {
     410           6 :     MutexAutoLock lock(mDataStorage->mMutex);
     411             :     // The backing file consists of a list of
     412             :     //   <key>\t<score>\t<last accessed time>\t<value>\n
     413             :     // The final \n is not optional; if it is not present the line is assumed
     414             :     // to be corrupt.
     415           3 :     int32_t currentIndex = 0;
     416           3 :     int32_t newlineIndex = 0;
     417             :     do {
     418           3 :       newlineIndex = data.FindChar('\n', currentIndex);
     419             :       // If there are no more newlines or the data table has too many
     420             :       // entries, we are done.
     421           3 :       if (newlineIndex < 0 ||
     422           0 :           mDataStorage->mPersistentDataTable.Count() >= sMaxDataEntries) {
     423           3 :         break;
     424             :       }
     425             : 
     426             :       nsDependentCSubstring line(data, currentIndex,
     427           0 :                                  newlineIndex - currentIndex);
     428           0 :       currentIndex = newlineIndex + 1;
     429           0 :       nsCString key;
     430           0 :       Entry entry;
     431           0 :       nsresult parseRV = ParseLine(line, key, entry);
     432           0 :       if (NS_SUCCEEDED(parseRV)) {
     433             :         // It could be the case that a newer entry was added before
     434             :         // we got around to reading the file. Don't overwrite new entries.
     435           0 :         Entry newerEntry;
     436           0 :         bool present = mDataStorage->mPersistentDataTable.Get(key, &newerEntry);
     437           0 :         if (!present) {
     438           0 :           mDataStorage->mPersistentDataTable.Put(key, entry);
     439             :         }
     440           0 :       }
     441             :     } while (true);
     442             : 
     443           3 :     Telemetry::Accumulate(Telemetry::DATA_STORAGE_ENTRIES,
     444           6 :                           mDataStorage->mPersistentDataTable.Count());
     445             :   }
     446             : 
     447           3 :   return NS_OK;
     448             : }
     449             : 
     450             : // The key must be a non-empty string containing no instances of '\t' or '\n',
     451             : // and must have a length no more than 256.
     452             : // The value must not contain '\n' and must have a length no more than 1024.
     453             : // The length limits are to prevent unbounded memory and disk usage.
     454             : /* static */
     455             : nsresult
     456           0 : DataStorage::ValidateKeyAndValue(const nsCString& aKey, const nsCString& aValue)
     457             : {
     458           0 :   if (aKey.IsEmpty()) {
     459           0 :     return NS_ERROR_INVALID_ARG;
     460             :   }
     461           0 :   if (aKey.Length() > 256) {
     462           0 :     return NS_ERROR_INVALID_ARG;
     463             :   }
     464           0 :   int32_t delimiterIndex = aKey.FindChar('\t', 0);
     465           0 :   if (delimiterIndex >= 0) {
     466           0 :     return NS_ERROR_INVALID_ARG;
     467             :   }
     468           0 :   delimiterIndex = aKey.FindChar('\n', 0);
     469           0 :   if (delimiterIndex >= 0) {
     470           0 :     return NS_ERROR_INVALID_ARG;
     471             :   }
     472           0 :   delimiterIndex = aValue.FindChar('\n', 0);
     473           0 :   if (delimiterIndex >= 0) {
     474           0 :     return NS_ERROR_INVALID_ARG;
     475             :   }
     476           0 :   if (aValue.Length() > 1024) {
     477           0 :     return NS_ERROR_INVALID_ARG;
     478             :   }
     479             : 
     480           0 :   return NS_OK;
     481             : }
     482             : 
     483             : // Each line is: <key>\t<score>\t<last accessed time>\t<value>
     484             : // Where <score> is a uint32_t as a string, <last accessed time> is a
     485             : // int32_t as a string, and the rest are strings.
     486             : // <value> can contain anything but a newline.
     487             : // Returns a successful status if the line can be decoded into a key and entry.
     488             : // Otherwise, an error status is returned and the values assigned to the
     489             : // output parameters are in an undefined state.
     490             : /* static */
     491             : nsresult
     492           0 : DataStorage::Reader::ParseLine(nsDependentCSubstring& aLine, nsCString& aKeyOut,
     493             :                                Entry& aEntryOut)
     494             : {
     495             :   // First find the indices to each part of the line.
     496             :   int32_t scoreIndex;
     497           0 :   scoreIndex = aLine.FindChar('\t', 0) + 1;
     498           0 :   if (scoreIndex <= 0) {
     499           0 :     return NS_ERROR_UNEXPECTED;
     500             :   }
     501           0 :   int32_t accessedIndex = aLine.FindChar('\t', scoreIndex) + 1;
     502           0 :   if (accessedIndex <= 0) {
     503           0 :     return NS_ERROR_UNEXPECTED;
     504             :   }
     505           0 :   int32_t valueIndex = aLine.FindChar('\t', accessedIndex) + 1;
     506           0 :   if (valueIndex <= 0) {
     507           0 :     return NS_ERROR_UNEXPECTED;
     508             :   }
     509             : 
     510             :   // Now make substrings based on where each part is.
     511           0 :   nsDependentCSubstring keyPart(aLine, 0, scoreIndex - 1);
     512             :   nsDependentCSubstring scorePart(aLine, scoreIndex,
     513           0 :                                   accessedIndex - scoreIndex - 1);
     514             :   nsDependentCSubstring accessedPart(aLine, accessedIndex,
     515           0 :                                      valueIndex - accessedIndex - 1);
     516           0 :   nsDependentCSubstring valuePart(aLine, valueIndex);
     517             : 
     518             :   nsresult rv;
     519           0 :   rv = DataStorage::ValidateKeyAndValue(nsCString(keyPart),
     520           0 :                                         nsCString(valuePart));
     521           0 :   if (NS_FAILED(rv)) {
     522           0 :     return NS_ERROR_UNEXPECTED;
     523             :   }
     524             : 
     525             :   // Now attempt to decode the score part as a uint32_t.
     526             :   // XXX nsDependentCSubstring doesn't support ToInteger
     527           0 :   int32_t integer = nsCString(scorePart).ToInteger(&rv);
     528           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     529           0 :     return rv;
     530             :   }
     531           0 :   if (integer < 0) {
     532           0 :     return NS_ERROR_UNEXPECTED;
     533             :   }
     534           0 :   aEntryOut.mScore = (uint32_t)integer;
     535             : 
     536           0 :   integer = nsCString(accessedPart).ToInteger(&rv);
     537           0 :   if (NS_FAILED(rv)) {
     538           0 :     return rv;
     539             :   }
     540           0 :   if (integer < 0) {
     541           0 :     return NS_ERROR_UNEXPECTED;
     542             :   }
     543           0 :   aEntryOut.mLastAccessed = integer;
     544             : 
     545             :   // Now set the key and value.
     546           0 :   aKeyOut.Assign(keyPart);
     547           0 :   aEntryOut.mValue.Assign(valuePart);
     548             : 
     549           0 :   return NS_OK;
     550             : }
     551             : 
     552             : nsresult
     553           3 : DataStorage::AsyncReadData(bool& aHaveProfileDir,
     554             :                            const MutexAutoLock& /*aProofOfLock*/)
     555             : {
     556           3 :   MOZ_ASSERT(XRE_IsParentProcess());
     557           3 :   aHaveProfileDir = false;
     558             :   // Allocate a Reader so that even if it isn't dispatched,
     559             :   // the data-storage-ready notification will be fired and Get
     560             :   // will be able to proceed (this happens in its destructor).
     561           6 :   RefPtr<Reader> job(new Reader(this));
     562             :   nsresult rv;
     563             :   // If we don't have a profile directory, this will fail.
     564             :   // That's okay - it just means there is no persistent state.
     565           3 :   rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
     566           6 :                               getter_AddRefs(mBackingFile));
     567           3 :   if (NS_FAILED(rv)) {
     568           0 :     mBackingFile = nullptr;
     569           0 :     return NS_OK;
     570             :   }
     571             : 
     572           3 :   rv = mBackingFile->Append(mFilename);
     573           3 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     574           0 :     return rv;
     575             :   }
     576             : 
     577           3 :   rv = mWorkerThread->Dispatch(job, NS_DISPATCH_NORMAL);
     578           3 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     579           0 :     return rv;
     580             :   }
     581             : 
     582           3 :   aHaveProfileDir = true;
     583           3 :   return NS_OK;
     584             : }
     585             : 
     586             : void
     587          25 : DataStorage::WaitForReady()
     588             : {
     589          25 :   MOZ_DIAGNOSTIC_ASSERT(mInitCalled, "Waiting before Init() has been called?");
     590             : 
     591          50 :   MonitorAutoLock readyLock(mReadyMonitor);
     592          29 :   while (!mReady) {
     593           2 :     nsresult rv = readyLock.Wait();
     594           2 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     595           0 :       break;
     596             :     }
     597             :   }
     598          25 :   MOZ_ASSERT(mReady);
     599          25 : }
     600             : 
     601             : nsCString
     602          22 : DataStorage::Get(const nsCString& aKey, DataStorageType aType)
     603             : {
     604          22 :   WaitForReady();
     605          44 :   MutexAutoLock lock(mMutex);
     606             : 
     607          44 :   Entry entry;
     608          22 :   bool foundValue = GetInternal(aKey, &entry, aType, lock);
     609          22 :   if (!foundValue) {
     610          22 :     return EmptyCString();
     611             :   }
     612             : 
     613             :   // If we're here, we found a value. Maybe update its score.
     614           0 :   if (entry.UpdateScore()) {
     615           0 :     PutInternal(aKey, entry, aType, lock);
     616             :   }
     617             : 
     618           0 :   return entry.mValue;
     619             : }
     620             : 
     621             : bool
     622          22 : DataStorage::GetInternal(const nsCString& aKey, Entry* aEntry,
     623             :                          DataStorageType aType,
     624             :                          const MutexAutoLock& aProofOfLock)
     625             : {
     626          22 :   DataStorageTable& table = GetTableForType(aType, aProofOfLock);
     627          22 :   bool foundValue = table.Get(aKey, aEntry);
     628          22 :   return foundValue;
     629             : }
     630             : 
     631             : DataStorage::DataStorageTable&
     632          31 : DataStorage::GetTableForType(DataStorageType aType,
     633             :                              const MutexAutoLock& /*aProofOfLock*/)
     634             : {
     635          31 :   switch (aType) {
     636             :     case DataStorage_Persistent:
     637          25 :       return mPersistentDataTable;
     638             :     case DataStorage_Temporary:
     639           3 :       return mTemporaryDataTable;
     640             :     case DataStorage_Private:
     641           3 :       return mPrivateDataTable;
     642             :   }
     643             : 
     644           0 :   MOZ_CRASH("given bad DataStorage storage type");
     645             : }
     646             : 
     647             : void
     648           9 : DataStorage::ReadAllFromTable(DataStorageType aType,
     649             :                               InfallibleTArray<dom::DataStorageItem>* aItems,
     650             :                               const MutexAutoLock& aProofOfLock)
     651             : {
     652          18 :   for (auto iter = GetTableForType(aType, aProofOfLock).Iter();
     653           9 :        !iter.Done(); iter.Next()) {
     654           0 :     DataStorageItem* item = aItems->AppendElement();
     655           0 :     item->key() = iter.Key();
     656           0 :     item->value() = iter.Data().mValue;
     657           0 :     item->type() = aType;
     658             :   }
     659           9 : }
     660             : 
     661             : void
     662           3 : DataStorage::GetAll(InfallibleTArray<dom::DataStorageItem>* aItems)
     663             : {
     664           3 :   WaitForReady();
     665           6 :   MutexAutoLock lock(mMutex);
     666             : 
     667           9 :   aItems->SetCapacity(mPersistentDataTable.Count() +
     668           6 :                       mTemporaryDataTable.Count() +
     669           6 :                       mPrivateDataTable.Count());
     670           3 :   ReadAllFromTable(DataStorage_Persistent, aItems, lock);
     671           3 :   ReadAllFromTable(DataStorage_Temporary, aItems, lock);
     672           3 :   ReadAllFromTable(DataStorage_Private, aItems, lock);
     673           3 : }
     674             : 
     675             : // Limit the number of entries per table. This is to prevent unbounded
     676             : // resource use. The eviction strategy is as follows:
     677             : // - An entry's score is incremented once for every day it is accessed.
     678             : // - Evict an entry with score no more than any other entry in the table
     679             : //   (this is the same as saying evict the entry with the lowest score,
     680             : //    except for when there are multiple entries with the lowest score,
     681             : //    in which case one of them is evicted - which one is not specified).
     682             : void
     683           0 : DataStorage::MaybeEvictOneEntry(DataStorageType aType,
     684             :                                 const MutexAutoLock& aProofOfLock)
     685             : {
     686           0 :   DataStorageTable& table = GetTableForType(aType, aProofOfLock);
     687           0 :   if (table.Count() >= sMaxDataEntries) {
     688           0 :     KeyAndEntry toEvict;
     689             :     // If all entries have score sMaxScore, this won't actually remove
     690             :     // anything. This will never happen, however, because having that high
     691             :     // a score either means someone tampered with the backing file or every
     692             :     // entry has been accessed once a day for ~4 billion days.
     693             :     // The worst that will happen is there will be 1025 entries in the
     694             :     // persistent data table, with the 1025th entry being replaced every time
     695             :     // data with a new key is inserted into the table. This is bad but
     696             :     // ultimately not that concerning, considering that if an attacker can
     697             :     // modify data in the profile, they can cause much worse harm.
     698           0 :     toEvict.mEntry.mScore = sMaxScore;
     699             : 
     700           0 :     for (auto iter = table.Iter(); !iter.Done(); iter.Next()) {
     701           0 :       Entry entry = iter.UserData();
     702           0 :       if (entry.mScore < toEvict.mEntry.mScore) {
     703           0 :         toEvict.mKey = iter.Key();
     704           0 :         toEvict.mEntry = entry;
     705             :       }
     706             :     }
     707             : 
     708           0 :     table.Remove(toEvict.mKey);
     709             :   }
     710           0 : }
     711             : 
     712             : template <class Functor>
     713             : static
     714             : void
     715           0 : RunOnAllContentParents(Functor func)
     716             : {
     717           0 :   if (!XRE_IsParentProcess()) {
     718           0 :     return;
     719             :   }
     720             :   using dom::ContentParent;
     721           0 :   nsTArray<ContentParent*> parents;
     722           0 :   ContentParent::GetAll(parents);
     723           0 :   for (auto& parent: parents) {
     724           0 :     func(parent);
     725             :   }
     726             : }
     727             : 
     728             : nsresult
     729           0 : DataStorage::Put(const nsCString& aKey, const nsCString& aValue,
     730             :                  DataStorageType aType)
     731             : {
     732           0 :   WaitForReady();
     733           0 :   MutexAutoLock lock(mMutex);
     734             : 
     735             :   nsresult rv;
     736           0 :   rv = ValidateKeyAndValue(aKey, aValue);
     737           0 :   if (NS_FAILED(rv)) {
     738           0 :     return rv;
     739             :   }
     740             : 
     741           0 :   Entry entry;
     742           0 :   bool exists = GetInternal(aKey, &entry, aType, lock);
     743           0 :   if (exists) {
     744           0 :     entry.UpdateScore();
     745             :   } else {
     746           0 :     MaybeEvictOneEntry(aType, lock);
     747             :   }
     748           0 :   entry.mValue = aValue;
     749           0 :   rv = PutInternal(aKey, entry, aType, lock);
     750           0 :   if (NS_FAILED(rv)) {
     751           0 :     return rv;
     752             :   }
     753             : 
     754           0 :   RunOnAllContentParents([&](dom::ContentParent* aParent) {
     755           0 :     DataStorageItem item;
     756           0 :     item.key() = aKey;
     757           0 :     item.value() = aValue;
     758           0 :     item.type() = aType;
     759           0 :     Unused << aParent->SendDataStoragePut(mFilename, item);
     760           0 :   });
     761             : 
     762           0 :   return NS_OK;
     763             : }
     764             : 
     765             : nsresult
     766           0 : DataStorage::PutInternal(const nsCString& aKey, Entry& aEntry,
     767             :                          DataStorageType aType,
     768             :                          const MutexAutoLock& aProofOfLock)
     769             : {
     770           0 :   DataStorageTable& table = GetTableForType(aType, aProofOfLock);
     771           0 :   table.Put(aKey, aEntry);
     772             : 
     773           0 :   if (aType == DataStorage_Persistent && !mPendingWrite) {
     774           0 :     return AsyncSetTimer(aProofOfLock);
     775             :   }
     776             : 
     777           0 :   return NS_OK;
     778             : }
     779             : 
     780             : void
     781           0 : DataStorage::Remove(const nsCString& aKey, DataStorageType aType)
     782             : {
     783           0 :   WaitForReady();
     784           0 :   MutexAutoLock lock(mMutex);
     785             : 
     786           0 :   DataStorageTable& table = GetTableForType(aType, lock);
     787           0 :   table.Remove(aKey);
     788             : 
     789           0 :   if (aType == DataStorage_Persistent && !mPendingWrite) {
     790           0 :     Unused << AsyncSetTimer(lock);
     791             :   }
     792             : 
     793           0 :   RunOnAllContentParents([&](dom::ContentParent* aParent) {
     794           0 :     Unused << aParent->SendDataStorageRemove(mFilename, aKey, aType);
     795           0 :   });
     796           0 : }
     797             : 
     798           0 : class DataStorage::Writer : public Runnable
     799             : {
     800             : public:
     801           0 :   Writer(nsCString& aData, DataStorage* aDataStorage)
     802           0 :     : Runnable("DataStorage::Writer")
     803             :     , mData(aData)
     804           0 :     , mDataStorage(aDataStorage)
     805             :   {
     806           0 :   }
     807             : 
     808             : private:
     809             :   NS_DECL_NSIRUNNABLE
     810             : 
     811             :   nsCString mData;
     812             :   RefPtr<DataStorage> mDataStorage;
     813             : };
     814             : 
     815             : NS_IMETHODIMP
     816           0 : DataStorage::Writer::Run()
     817             : {
     818             :   nsresult rv;
     819             :   // Concurrent operations on nsIFile objects are not guaranteed to be safe,
     820             :   // so we clone the file while holding the lock and then release the lock.
     821             :   // At that point, we can safely operate on the clone.
     822           0 :   nsCOMPtr<nsIFile> file;
     823             :   {
     824           0 :     MutexAutoLock lock(mDataStorage->mMutex);
     825             :     // If we don't have a profile, bail.
     826           0 :     if (!mDataStorage->mBackingFile) {
     827           0 :       return NS_OK;
     828             :     }
     829           0 :     rv = mDataStorage->mBackingFile->Clone(getter_AddRefs(file));
     830           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     831           0 :       return rv;
     832             :     }
     833             :   }
     834             : 
     835           0 :   nsCOMPtr<nsIOutputStream> outputStream;
     836           0 :   rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file,
     837           0 :                                    PR_CREATE_FILE | PR_TRUNCATE | PR_WRONLY);
     838           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     839           0 :     return rv;
     840             :   }
     841             : 
     842           0 :   const char* ptr = mData.get();
     843           0 :   int32_t remaining = mData.Length();
     844           0 :   uint32_t written = 0;
     845           0 :   while (remaining > 0) {
     846           0 :     rv = outputStream->Write(ptr, remaining, &written);
     847           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     848           0 :       return rv;
     849             :     }
     850           0 :     remaining -= written;
     851           0 :     ptr += written;
     852             :   }
     853             : 
     854             :   // Observed by tests.
     855             :   nsCOMPtr<nsIRunnable> job =
     856           0 :     NewRunnableMethod<const char*>("DataStorage::NotifyObservers",
     857             :                                    mDataStorage,
     858             :                                    &DataStorage::NotifyObservers,
     859           0 :                                    "data-storage-written");
     860           0 :   rv = NS_DispatchToMainThread(job, NS_DISPATCH_NORMAL);
     861           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     862           0 :     return rv;
     863             :   }
     864             : 
     865           0 :   return NS_OK;
     866             : }
     867             : 
     868             : nsresult
     869           0 : DataStorage::AsyncWriteData(const MutexAutoLock& /*aProofOfLock*/)
     870             : {
     871           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     872             : 
     873           0 :   if (mShuttingDown || !mBackingFile) {
     874           0 :     return NS_OK;
     875             :   }
     876             : 
     877           0 :   nsCString output;
     878           0 :   for (auto iter = mPersistentDataTable.Iter(); !iter.Done(); iter.Next()) {
     879           0 :     Entry entry = iter.UserData();
     880           0 :     output.Append(iter.Key());
     881           0 :     output.Append('\t');
     882           0 :     output.AppendInt(entry.mScore);
     883           0 :     output.Append('\t');
     884           0 :     output.AppendInt(entry.mLastAccessed);
     885           0 :     output.Append('\t');
     886           0 :     output.Append(entry.mValue);
     887           0 :     output.Append('\n');
     888             :   }
     889             : 
     890           0 :   RefPtr<Writer> job(new Writer(output, this));
     891           0 :   nsresult rv = mWorkerThread->Dispatch(job, NS_DISPATCH_NORMAL);
     892           0 :   mPendingWrite = false;
     893           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     894           0 :     return rv;
     895             :   }
     896             : 
     897           0 :   return NS_OK;
     898             : }
     899             : 
     900             : nsresult
     901           0 : DataStorage::Clear()
     902             : {
     903           0 :   WaitForReady();
     904           0 :   MutexAutoLock lock(mMutex);
     905           0 :   mPersistentDataTable.Clear();
     906           0 :   mTemporaryDataTable.Clear();
     907           0 :   mPrivateDataTable.Clear();
     908             : 
     909           0 :   if (XRE_IsParentProcess()) {
     910             :     // Asynchronously clear the file. This is similar to the permission manager
     911             :     // in that it doesn't wait to synchronously remove the data from its backing
     912             :     // storage either.
     913           0 :     nsresult rv = AsyncWriteData(lock);
     914           0 :     if (NS_FAILED(rv)) {
     915           0 :       return rv;
     916             :     }
     917             :   }
     918             : 
     919           0 :   RunOnAllContentParents([&](dom::ContentParent* aParent) {
     920           0 :     Unused << aParent->SendDataStorageClear(mFilename);
     921           0 :   });
     922             : 
     923           0 :   return NS_OK;
     924             : }
     925             : 
     926             : /* static */
     927             : void
     928           0 : DataStorage::TimerCallback(nsITimer* aTimer, void* aClosure)
     929             : {
     930           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     931             : 
     932           0 :   RefPtr<DataStorage> aDataStorage = (DataStorage*)aClosure;
     933           0 :   MutexAutoLock lock(aDataStorage->mMutex);
     934           0 :   Unused << aDataStorage->AsyncWriteData(lock);
     935           0 : }
     936             : 
     937             : // We only initialize the timer on the worker thread because it's not safe
     938             : // to mix what threads are operating on the timer.
     939             : nsresult
     940           0 : DataStorage::AsyncSetTimer(const MutexAutoLock& /*aProofOfLock*/)
     941             : {
     942           0 :   if (mShuttingDown || !XRE_IsParentProcess()) {
     943           0 :     return NS_OK;
     944             :   }
     945             : 
     946           0 :   mPendingWrite = true;
     947             :   nsCOMPtr<nsIRunnable> job =
     948           0 :     NewRunnableMethod("DataStorage::SetTimer", this, &DataStorage::SetTimer);
     949           0 :   nsresult rv = mWorkerThread->Dispatch(job, NS_DISPATCH_NORMAL);
     950           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     951           0 :     return rv;
     952             :   }
     953           0 :   return NS_OK;
     954             : }
     955             : 
     956             : void
     957           0 : DataStorage::SetTimer()
     958             : {
     959           0 :   MOZ_ASSERT(!NS_IsMainThread());
     960           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     961             : 
     962           0 :   MutexAutoLock lock(mMutex);
     963             : 
     964             :   nsresult rv;
     965           0 :   if (!mTimer) {
     966           0 :     mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
     967           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     968           0 :       return;
     969             :     }
     970             :   }
     971             : 
     972           0 :   rv = mTimer->InitWithNamedFuncCallback(TimerCallback,
     973             :                                          this,
     974             :                                          mTimerDelay,
     975             :                                          nsITimer::TYPE_ONE_SHOT,
     976           0 :                                          "DataStorage::SetTimer");
     977           0 :   Unused << NS_WARN_IF(NS_FAILED(rv));
     978             : }
     979             : 
     980             : void
     981           9 : DataStorage::NotifyObservers(const char* aTopic)
     982             : {
     983             :   // Don't access the observer service off the main thread.
     984           9 :   if (!NS_IsMainThread()) {
     985           0 :     MOZ_ASSERT_UNREACHABLE("DataStorage::NotifyObservers called off main thread");
     986             :     return;
     987             :   }
     988             : 
     989          18 :   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     990           9 :   if (os) {
     991           9 :     os->NotifyObservers(nullptr, aTopic, mFilename.get());
     992             :   }
     993           9 : }
     994             : 
     995             : nsresult
     996           0 : DataStorage::DispatchShutdownTimer(const MutexAutoLock& /*aProofOfLock*/)
     997             : {
     998           0 :   MOZ_ASSERT(XRE_IsParentProcess());
     999             : 
    1000           0 :   nsCOMPtr<nsIRunnable> job = NewRunnableMethod(
    1001           0 :     "DataStorage::ShutdownTimer", this, &DataStorage::ShutdownTimer);
    1002           0 :   nsresult rv = mWorkerThread->Dispatch(job, NS_DISPATCH_NORMAL);
    1003           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1004           0 :     return rv;
    1005             :   }
    1006           0 :   return NS_OK;
    1007             : }
    1008             : 
    1009             : void
    1010           0 : DataStorage::ShutdownTimer()
    1011             : {
    1012           0 :   MOZ_ASSERT(XRE_IsParentProcess());
    1013           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1014           0 :   MutexAutoLock lock(mMutex);
    1015           0 :   nsresult rv = mTimer->Cancel();
    1016           0 :   Unused << NS_WARN_IF(NS_FAILED(rv));
    1017           0 :   mTimer = nullptr;
    1018           0 : }
    1019             : 
    1020             : //------------------------------------------------------------
    1021             : // DataStorage::nsIObserver
    1022             : //------------------------------------------------------------
    1023             : 
    1024             : NS_IMETHODIMP
    1025           0 : DataStorage::Observe(nsISupports* /*aSubject*/, const char* aTopic,
    1026             :                      const char16_t* /*aData*/)
    1027             : {
    1028             :   // Don't access preferences off the main thread.
    1029           0 :   if (!NS_IsMainThread()) {
    1030           0 :     MOZ_ASSERT_UNREACHABLE("DataStorage::Observe called off main thread");
    1031             :     return NS_ERROR_NOT_SAME_THREAD;
    1032             :   }
    1033             : 
    1034             :   nsresult rv;
    1035           0 :   if (strcmp(aTopic, "last-pb-context-exited") == 0) {
    1036           0 :     MutexAutoLock lock(mMutex);
    1037           0 :     mPrivateDataTable.Clear();
    1038           0 :   } else if (strcmp(aTopic, "profile-before-change") == 0 ||
    1039           0 :              (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0 &&
    1040           0 :               XRE_IsParentProcess())) {
    1041           0 :     MOZ_ASSERT(XRE_IsParentProcess());
    1042             :     // per bug 1271402, this should be safe to run multiple times
    1043             :     {
    1044           0 :       MutexAutoLock lock(mMutex);
    1045           0 :       rv = AsyncWriteData(lock);
    1046           0 :       mShuttingDown = true;
    1047           0 :       Unused << NS_WARN_IF(NS_FAILED(rv));
    1048           0 :       if (mTimer) {
    1049           0 :         rv = DispatchShutdownTimer(lock);
    1050           0 :         Unused << NS_WARN_IF(NS_FAILED(rv));
    1051             :       }
    1052             :     }
    1053             :     // Run the thread to completion and prevent any further events
    1054             :     // being scheduled to it. The thread may need the lock, so we can't
    1055             :     // hold it here.
    1056           0 :     rv = mWorkerThread->Shutdown();
    1057           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1058           0 :       return rv;
    1059             :     }
    1060             : 
    1061           0 :     sDataStorages->Clear();
    1062           0 :   } else if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
    1063           0 :     MOZ_ASSERT(!XRE_IsParentProcess());
    1064           0 :     sDataStorages->Clear();
    1065           0 :   } else if (strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
    1066           0 :     MutexAutoLock lock(mMutex);
    1067           0 :     mTimerDelay = Preferences::GetInt("test.datastorage.write_timer_ms",
    1068             :                                       sDataStorageDefaultTimerDelay);
    1069             :   }
    1070             : 
    1071           0 :   return NS_OK;
    1072             : }
    1073             : 
    1074          22 : DataStorage::Entry::Entry()
    1075             :   : mScore(0)
    1076          22 :   , mLastAccessed((int32_t)(PR_Now() / sOneDayInMicroseconds))
    1077             : {
    1078          22 : }
    1079             : 
    1080             : // Updates this entry's score. Returns true if the score has actually changed.
    1081             : // If it's been less than a day since this entry has been accessed, the score
    1082             : // does not change. Otherwise, the score increases by 1.
    1083             : // The default score is 0. The maximum score is the maximum value that can
    1084             : // be represented by an unsigned 32 bit integer.
    1085             : // This is to handle evictions from our tables, which in turn is to prevent
    1086             : // unbounded resource use.
    1087             : bool
    1088           0 : DataStorage::Entry::UpdateScore()
    1089             : {
    1090             : 
    1091           0 :   int32_t nowInDays = (int32_t)(PR_Now() / sOneDayInMicroseconds);
    1092           0 :   int32_t daysSinceAccessed = (nowInDays - mLastAccessed);
    1093             : 
    1094             :   // Update the last accessed time.
    1095           0 :   mLastAccessed = nowInDays;
    1096             : 
    1097             :   // If it's been less than a day since we've been accessed,
    1098             :   // the score isn't updated.
    1099           0 :   if (daysSinceAccessed < 1) {
    1100           0 :     return false;
    1101             :   }
    1102             : 
    1103             :   // Otherwise, increment the score (but don't overflow).
    1104           0 :   if (mScore < sMaxScore) {
    1105           0 :     mScore++;
    1106             :   }
    1107           0 :   return true;
    1108             : }
    1109             : 
    1110             : } // namespace mozilla

Generated by: LCOV version 1.13