LCOV - code coverage report
Current view: top level - netwerk/cache - nsDiskCacheDeviceSQL.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1332 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 157 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim:set ts=2 sw=2 sts=2 et cin: */
       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 <inttypes.h>
       8             : 
       9             : #include "mozilla/ArrayUtils.h"
      10             : #include "mozilla/Attributes.h"
      11             : #include "mozilla/Sprintf.h"
      12             : #include "mozilla/ThreadLocal.h"
      13             : 
      14             : #include "nsCache.h"
      15             : #include "nsDiskCache.h"
      16             : #include "nsDiskCacheDeviceSQL.h"
      17             : #include "nsCacheService.h"
      18             : #include "nsApplicationCache.h"
      19             : 
      20             : #include "nsNetCID.h"
      21             : #include "nsNetUtil.h"
      22             : #include "nsIURI.h"
      23             : #include "nsAutoPtr.h"
      24             : #include "nsEscape.h"
      25             : #include "nsIPrefBranch.h"
      26             : #include "nsIPrefService.h"
      27             : #include "nsString.h"
      28             : #include "nsPrintfCString.h"
      29             : #include "nsCRT.h"
      30             : #include "nsArrayUtils.h"
      31             : #include "nsIArray.h"
      32             : #include "nsIVariant.h"
      33             : #include "nsILoadContextInfo.h"
      34             : #include "nsThreadUtils.h"
      35             : #include "nsISerializable.h"
      36             : #include "nsIInputStream.h"
      37             : #include "nsIOutputStream.h"
      38             : #include "nsSerializationHelper.h"
      39             : 
      40             : #include "mozIStorageService.h"
      41             : #include "mozIStorageStatement.h"
      42             : #include "mozIStorageFunction.h"
      43             : #include "mozStorageHelper.h"
      44             : 
      45             : #include "nsICacheVisitor.h"
      46             : #include "nsISeekableStream.h"
      47             : 
      48             : #include "mozilla/Telemetry.h"
      49             : 
      50             : #include "sqlite3.h"
      51             : #include "mozilla/storage.h"
      52             : #include "nsVariant.h"
      53             : #include "mozilla/BasePrincipal.h"
      54             : 
      55             : using namespace mozilla;
      56             : using namespace mozilla::storage;
      57             : using mozilla::OriginAttributes;
      58             : 
      59             : static const char OFFLINE_CACHE_DEVICE_ID[] = { "offline" };
      60             : 
      61             : #define LOG(args) CACHE_LOG_DEBUG(args)
      62             : 
      63             : static uint32_t gNextTemporaryClientID = 0;
      64             : 
      65             : /*****************************************************************************
      66             :  * helpers
      67             :  */
      68             : 
      69             : static nsresult
      70           0 : EnsureDir(nsIFile *dir)
      71             : {
      72             :   bool exists;
      73           0 :   nsresult rv = dir->Exists(&exists);
      74           0 :   if (NS_SUCCEEDED(rv) && !exists)
      75           0 :     rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700);
      76           0 :   return rv;
      77             : }
      78             : 
      79             : static bool
      80           0 : DecomposeCacheEntryKey(const nsCString *fullKey,
      81             :                        const char **cid,
      82             :                        const char **key,
      83             :                        nsCString &buf)
      84             : {
      85           0 :   buf = *fullKey;
      86             : 
      87           0 :   int32_t colon = buf.FindChar(':');
      88           0 :   if (colon == kNotFound)
      89             :   {
      90           0 :     NS_ERROR("Invalid key");
      91           0 :     return false;
      92             :   }
      93           0 :   buf.SetCharAt('\0', colon);
      94             : 
      95           0 :   *cid = buf.get();
      96           0 :   *key = buf.get() + colon + 1;
      97             : 
      98           0 :   return true;
      99             : }
     100             : 
     101             : class AutoResetStatement
     102             : {
     103             :   public:
     104           0 :     explicit AutoResetStatement(mozIStorageStatement *s)
     105           0 :       : mStatement(s) {}
     106           0 :     ~AutoResetStatement() { mStatement->Reset(); }
     107           0 :     mozIStorageStatement *operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN { return mStatement; }
     108             :   private:
     109             :     mozIStorageStatement *mStatement;
     110             : };
     111             : 
     112             : class EvictionObserver
     113             : {
     114             :   public:
     115           0 :   EvictionObserver(mozIStorageConnection *db,
     116             :                    nsOfflineCacheEvictionFunction *evictionFunction)
     117           0 :     : mDB(db), mEvictionFunction(evictionFunction)
     118             :     {
     119           0 :       mEvictionFunction->Init();
     120           0 :       mDB->ExecuteSimpleSQL(
     121           0 :           NS_LITERAL_CSTRING("CREATE TEMP TRIGGER cache_on_delete BEFORE DELETE"
     122             :                              " ON moz_cache FOR EACH ROW BEGIN SELECT"
     123             :                              " cache_eviction_observer("
     124             :                              "  OLD.ClientID, OLD.key, OLD.generation);"
     125           0 :                              " END;"));
     126           0 :     }
     127             : 
     128           0 :     ~EvictionObserver()
     129           0 :     {
     130           0 :       mDB->ExecuteSimpleSQL(
     131           0 :         NS_LITERAL_CSTRING("DROP TRIGGER cache_on_delete;"));
     132           0 :       mEvictionFunction->Reset();
     133           0 :     }
     134             : 
     135           0 :     void Apply() { return mEvictionFunction->Apply(); }
     136             : 
     137             :   private:
     138             :     mozIStorageConnection *mDB;
     139             :     RefPtr<nsOfflineCacheEvictionFunction> mEvictionFunction;
     140             : };
     141             : 
     142             : #define DCACHE_HASH_MAX  INT64_MAX
     143             : #define DCACHE_HASH_BITS 64
     144             : 
     145             : /**
     146             :  *  nsOfflineCache::Hash(const char * key)
     147             :  *
     148             :  *  This algorithm of this method implies nsOfflineCacheRecords will be stored
     149             :  *  in a certain order on disk.  If the algorithm changes, existing cache
     150             :  *  map files may become invalid, and therefore the kCurrentVersion needs
     151             :  *  to be revised.
     152             :  */
     153             : static uint64_t
     154           0 : DCacheHash(const char * key)
     155             : {
     156             :   // initval 0x7416f295 was chosen randomly
     157           0 :   return (uint64_t(nsDiskCache::Hash(key, 0)) << 32) | nsDiskCache::Hash(key, 0x7416f295);
     158             : }
     159             : 
     160             : /******************************************************************************
     161             :  * nsOfflineCacheEvictionFunction
     162             :  */
     163             : 
     164           0 : NS_IMPL_ISUPPORTS(nsOfflineCacheEvictionFunction, mozIStorageFunction)
     165             : 
     166             : // helper function for directly exposing the same data file binding
     167             : // path algorithm used in nsOfflineCacheBinding::Create
     168             : static nsresult
     169           0 : GetCacheDataFile(nsIFile *cacheDir, const char *key,
     170             :                  int generation, nsCOMPtr<nsIFile> &file)
     171             : {
     172           0 :   cacheDir->Clone(getter_AddRefs(file));
     173           0 :   if (!file)
     174           0 :     return NS_ERROR_OUT_OF_MEMORY;
     175             : 
     176           0 :   uint64_t hash = DCacheHash(key);
     177             : 
     178           0 :   uint32_t dir1 = (uint32_t) (hash & 0x0F);
     179           0 :   uint32_t dir2 = (uint32_t)((hash & 0xF0) >> 4);
     180             : 
     181           0 :   hash >>= 8;
     182             : 
     183           0 :   file->AppendNative(nsPrintfCString("%X", dir1));
     184           0 :   file->AppendNative(nsPrintfCString("%X", dir2));
     185             : 
     186             :   char leaf[64];
     187           0 :   SprintfLiteral(leaf, "%014" PRIX64 "-%X", hash, generation);
     188           0 :   return file->AppendNative(nsDependentCString(leaf));
     189             : }
     190             : 
     191             : namespace appcachedetail {
     192             : 
     193             : typedef nsCOMArray<nsIFile> FileArray;
     194             : static MOZ_THREAD_LOCAL(FileArray*) tlsEvictionItems;
     195             : 
     196             : } // appcachedetail
     197             : 
     198             : NS_IMETHODIMP
     199           0 : nsOfflineCacheEvictionFunction::OnFunctionCall(mozIStorageValueArray *values, nsIVariant **_retval)
     200             : {
     201           0 :   LOG(("nsOfflineCacheEvictionFunction::OnFunctionCall\n"));
     202             : 
     203           0 :   *_retval = nullptr;
     204             : 
     205             :   uint32_t numEntries;
     206           0 :   nsresult rv = values->GetNumEntries(&numEntries);
     207           0 :   NS_ENSURE_SUCCESS(rv, rv);
     208           0 :   NS_ASSERTION(numEntries == 3, "unexpected number of arguments");
     209             : 
     210             :   uint32_t valueLen;
     211           0 :   const char *clientID = values->AsSharedUTF8String(0, &valueLen);
     212           0 :   const char *key = values->AsSharedUTF8String(1, &valueLen);
     213           0 :   nsAutoCString fullKey(clientID);
     214           0 :   fullKey.Append(':');
     215           0 :   fullKey.Append(key);
     216           0 :   int generation  = values->AsInt32(2);
     217             : 
     218             :   // If the key is currently locked, refuse to delete this row.
     219           0 :   if (mDevice->IsLocked(fullKey)) {
     220           0 :     NS_ADDREF(*_retval = new IntegerVariant(SQLITE_IGNORE));
     221           0 :     return NS_OK;
     222             :   }
     223             : 
     224           0 :   nsCOMPtr<nsIFile> file;
     225           0 :   rv = GetCacheDataFile(mDevice->CacheDirectory(), key,
     226           0 :                         generation, file);
     227           0 :   if (NS_FAILED(rv))
     228             :   {
     229           0 :     LOG(("GetCacheDataFile [key=%s generation=%d] failed [rv=%" PRIx32 "]!\n",
     230             :          key, generation, static_cast<uint32_t>(rv)));
     231           0 :     return rv;
     232             :   }
     233             : 
     234           0 :   appcachedetail::FileArray* items = appcachedetail::tlsEvictionItems.get();
     235           0 :   MOZ_ASSERT(items);
     236           0 :   if (items) {
     237           0 :     items->AppendObject(file);
     238             :   }
     239             : 
     240           0 :   return NS_OK;
     241             : }
     242             : 
     243           0 : nsOfflineCacheEvictionFunction::nsOfflineCacheEvictionFunction(nsOfflineCacheDevice * device)
     244           0 :   : mDevice(device)
     245             : {
     246           0 :   mTLSInited = appcachedetail::tlsEvictionItems.init();
     247           0 : }
     248             : 
     249           0 : void nsOfflineCacheEvictionFunction::Init()
     250             : {
     251           0 :   if (mTLSInited) {
     252           0 :     appcachedetail::tlsEvictionItems.set(new appcachedetail::FileArray());
     253             :   }
     254           0 : }
     255             : 
     256           0 : void nsOfflineCacheEvictionFunction::Reset()
     257             : {
     258           0 :   if (!mTLSInited) {
     259           0 :     return;
     260             :   }
     261             : 
     262           0 :   appcachedetail::FileArray* items = appcachedetail::tlsEvictionItems.get();
     263           0 :   if (!items) {
     264           0 :     return;
     265             :   }
     266             : 
     267           0 :   appcachedetail::tlsEvictionItems.set(nullptr);
     268           0 :   delete items;
     269             : }
     270             : 
     271             : void
     272           0 : nsOfflineCacheEvictionFunction::Apply()
     273             : {
     274           0 :   LOG(("nsOfflineCacheEvictionFunction::Apply\n"));
     275             : 
     276           0 :   if (!mTLSInited) {
     277           0 :     return;
     278             :   }
     279             : 
     280           0 :   appcachedetail::FileArray* pitems = appcachedetail::tlsEvictionItems.get();
     281           0 :   if (!pitems) {
     282           0 :     return;
     283             :   }
     284             : 
     285           0 :   appcachedetail::FileArray items;
     286           0 :   items.SwapElements(*pitems);
     287             : 
     288           0 :   for (int32_t i = 0; i < items.Count(); i++) {
     289           0 :     if (MOZ_LOG_TEST(gCacheLog, LogLevel::Debug)) {
     290           0 :       nsAutoCString path;
     291           0 :       items[i]->GetNativePath(path);
     292           0 :       LOG(("  removing %s\n", path.get()));
     293             :     }
     294             : 
     295           0 :     items[i]->Remove(false);
     296             :   }
     297             : }
     298             : 
     299           0 : class nsOfflineCacheDiscardCache : public Runnable
     300             : {
     301             : public:
     302           0 :   nsOfflineCacheDiscardCache(nsOfflineCacheDevice* device,
     303             :                              nsCString& group,
     304             :                              nsCString& clientID)
     305           0 :     : mozilla::Runnable("nsOfflineCacheDiscardCache")
     306             :     , mDevice(device)
     307             :     , mGroup(group)
     308           0 :     , mClientID(clientID)
     309             :   {
     310           0 :   }
     311             : 
     312           0 :   NS_IMETHOD Run() override
     313             :   {
     314           0 :     if (mDevice->IsActiveCache(mGroup, mClientID))
     315             :     {
     316           0 :       mDevice->DeactivateGroup(mGroup);
     317             :     }
     318             : 
     319           0 :     return mDevice->EvictEntries(mClientID.get());
     320             :   }
     321             : 
     322             : private:
     323             :   RefPtr<nsOfflineCacheDevice> mDevice;
     324             :   nsCString mGroup;
     325             :   nsCString mClientID;
     326             : };
     327             : 
     328             : /******************************************************************************
     329             :  * nsOfflineCacheDeviceInfo
     330             :  */
     331             : 
     332             : class nsOfflineCacheDeviceInfo final : public nsICacheDeviceInfo
     333             : {
     334             : public:
     335             :   NS_DECL_ISUPPORTS
     336             :   NS_DECL_NSICACHEDEVICEINFO
     337             : 
     338           0 :   explicit nsOfflineCacheDeviceInfo(nsOfflineCacheDevice* device)
     339           0 :     : mDevice(device)
     340           0 :   {}
     341             : 
     342             : private:
     343           0 :   ~nsOfflineCacheDeviceInfo() {}
     344             : 
     345             :   nsOfflineCacheDevice* mDevice;
     346             : };
     347             : 
     348           0 : NS_IMPL_ISUPPORTS(nsOfflineCacheDeviceInfo, nsICacheDeviceInfo)
     349             : 
     350             : NS_IMETHODIMP
     351           0 : nsOfflineCacheDeviceInfo::GetDescription(char **aDescription)
     352             : {
     353           0 :   *aDescription = NS_strdup("Offline cache device");
     354           0 :   return *aDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
     355             : }
     356             : 
     357             : NS_IMETHODIMP
     358           0 : nsOfflineCacheDeviceInfo::GetUsageReport(char ** usageReport)
     359             : {
     360           0 :   nsAutoCString buffer;
     361             :   buffer.AssignLiteral("  <tr>\n"
     362             :                        "    <th>Cache Directory:</th>\n"
     363           0 :                        "    <td>");
     364           0 :   nsIFile *cacheDir = mDevice->CacheDirectory();
     365           0 :   if (!cacheDir)
     366           0 :     return NS_OK;
     367             : 
     368           0 :   nsAutoString path;
     369           0 :   nsresult rv = cacheDir->GetPath(path);
     370           0 :   if (NS_SUCCEEDED(rv))
     371           0 :     AppendUTF16toUTF8(path, buffer);
     372             :   else
     373           0 :     buffer.AppendLiteral("directory unavailable");
     374             : 
     375             :   buffer.AppendLiteral("</td>\n"
     376           0 :                        "  </tr>\n");
     377             : 
     378           0 :   *usageReport = ToNewCString(buffer);
     379           0 :   if (!*usageReport)
     380           0 :     return NS_ERROR_OUT_OF_MEMORY;
     381             : 
     382           0 :   return NS_OK;
     383             : }
     384             : 
     385             : NS_IMETHODIMP
     386           0 : nsOfflineCacheDeviceInfo::GetEntryCount(uint32_t *aEntryCount)
     387             : {
     388           0 :   *aEntryCount = mDevice->EntryCount();
     389           0 :   return NS_OK;
     390             : }
     391             : 
     392             : NS_IMETHODIMP
     393           0 : nsOfflineCacheDeviceInfo::GetTotalSize(uint32_t *aTotalSize)
     394             : {
     395           0 :   *aTotalSize = mDevice->CacheSize();
     396           0 :   return NS_OK;
     397             : }
     398             : 
     399             : NS_IMETHODIMP
     400           0 : nsOfflineCacheDeviceInfo::GetMaximumSize(uint32_t *aMaximumSize)
     401             : {
     402           0 :   *aMaximumSize = mDevice->CacheCapacity();
     403           0 :   return NS_OK;
     404             : }
     405             : 
     406             : /******************************************************************************
     407             :  * nsOfflineCacheBinding
     408             :  */
     409             : 
     410           0 : class nsOfflineCacheBinding final : public nsISupports
     411             : {
     412           0 :   ~nsOfflineCacheBinding() {}
     413             : 
     414             : public:
     415             :   NS_DECL_THREADSAFE_ISUPPORTS
     416             : 
     417             :   static nsOfflineCacheBinding *
     418             :       Create(nsIFile *cacheDir, const nsCString *key, int generation);
     419             : 
     420             :   enum { FLAG_NEW_ENTRY = 1 };
     421             : 
     422             :   nsCOMPtr<nsIFile> mDataFile;
     423             :   int               mGeneration;
     424             :   int               mFlags;
     425             : 
     426           0 :   bool IsNewEntry() { return mFlags & FLAG_NEW_ENTRY; }
     427           0 :   void MarkNewEntry() { mFlags |= FLAG_NEW_ENTRY; }
     428             :   void ClearNewEntry() { mFlags &= ~FLAG_NEW_ENTRY; }
     429             : };
     430             : 
     431           0 : NS_IMPL_ISUPPORTS0(nsOfflineCacheBinding)
     432             : 
     433             : nsOfflineCacheBinding *
     434           0 : nsOfflineCacheBinding::Create(nsIFile *cacheDir,
     435             :                               const nsCString *fullKey,
     436             :                               int generation)
     437             : {
     438           0 :   nsCOMPtr<nsIFile> file;
     439           0 :   cacheDir->Clone(getter_AddRefs(file));
     440           0 :   if (!file)
     441           0 :     return nullptr;
     442             : 
     443           0 :   nsAutoCString keyBuf;
     444             :   const char *cid, *key;
     445           0 :   if (!DecomposeCacheEntryKey(fullKey, &cid, &key, keyBuf))
     446           0 :     return nullptr;
     447             : 
     448           0 :   uint64_t hash = DCacheHash(key);
     449             : 
     450           0 :   uint32_t dir1 = (uint32_t) (hash & 0x0F);
     451           0 :   uint32_t dir2 = (uint32_t)((hash & 0xF0) >> 4);
     452             : 
     453           0 :   hash >>= 8;
     454             : 
     455             :   // XXX we might want to create these directories up-front
     456             : 
     457           0 :   file->AppendNative(nsPrintfCString("%X", dir1));
     458           0 :   Unused << file->Create(nsIFile::DIRECTORY_TYPE, 00700);
     459             : 
     460           0 :   file->AppendNative(nsPrintfCString("%X", dir2));
     461           0 :   Unused << file->Create(nsIFile::DIRECTORY_TYPE, 00700);
     462             : 
     463             :   nsresult rv;
     464             :   char leaf[64];
     465             : 
     466           0 :   if (generation == -1)
     467             :   {
     468           0 :     file->AppendNative(NS_LITERAL_CSTRING("placeholder"));
     469             : 
     470           0 :     for (generation = 0; ; ++generation)
     471             :     {
     472           0 :       SprintfLiteral(leaf, "%014" PRIX64 "-%X", hash, generation);
     473             : 
     474           0 :       rv = file->SetNativeLeafName(nsDependentCString(leaf));
     475           0 :       if (NS_FAILED(rv))
     476           0 :         return nullptr;
     477           0 :       rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
     478           0 :       if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)
     479           0 :         return nullptr;
     480           0 :       if (NS_SUCCEEDED(rv))
     481           0 :         break;
     482             :     }
     483             :   }
     484             :   else
     485             :   {
     486           0 :     SprintfLiteral(leaf, "%014" PRIX64 "-%X", hash, generation);
     487           0 :     rv = file->AppendNative(nsDependentCString(leaf));
     488           0 :     if (NS_FAILED(rv))
     489           0 :       return nullptr;
     490             :   }
     491             : 
     492           0 :   nsOfflineCacheBinding *binding = new nsOfflineCacheBinding;
     493           0 :   if (!binding)
     494           0 :     return nullptr;
     495             : 
     496           0 :   binding->mDataFile.swap(file);
     497           0 :   binding->mGeneration = generation;
     498           0 :   binding->mFlags = 0;
     499           0 :   return binding;
     500             : }
     501             : 
     502             : /******************************************************************************
     503             :  * nsOfflineCacheRecord
     504             :  */
     505             : 
     506             : struct nsOfflineCacheRecord
     507             : {
     508             :   const char    *clientID;
     509             :   const char    *key;
     510             :   const uint8_t *metaData;
     511             :   uint32_t       metaDataLen;
     512             :   int32_t        generation;
     513             :   int32_t        dataSize;
     514             :   int32_t        fetchCount;
     515             :   int64_t        lastFetched;
     516             :   int64_t        lastModified;
     517             :   int64_t        expirationTime;
     518             : };
     519             : 
     520             : static nsCacheEntry *
     521           0 : CreateCacheEntry(nsOfflineCacheDevice *device,
     522             :                  const nsCString *fullKey,
     523             :                  const nsOfflineCacheRecord &rec)
     524             : {
     525             :   nsCacheEntry *entry;
     526             : 
     527           0 :   if (device->IsLocked(*fullKey)) {
     528           0 :       return nullptr;
     529             :   }
     530             : 
     531           0 :   nsresult rv = nsCacheEntry::Create(fullKey->get(), // XXX enable sharing
     532             :                                      nsICache::STREAM_BASED,
     533             :                                      nsICache::STORE_OFFLINE,
     534           0 :                                      device, &entry);
     535           0 :   if (NS_FAILED(rv))
     536           0 :     return nullptr;
     537             : 
     538           0 :   entry->SetFetchCount((uint32_t) rec.fetchCount);
     539           0 :   entry->SetLastFetched(SecondsFromPRTime(rec.lastFetched));
     540           0 :   entry->SetLastModified(SecondsFromPRTime(rec.lastModified));
     541           0 :   entry->SetExpirationTime(SecondsFromPRTime(rec.expirationTime));
     542           0 :   entry->SetDataSize((uint32_t) rec.dataSize);
     543             : 
     544           0 :   entry->UnflattenMetaData((const char *) rec.metaData, rec.metaDataLen);
     545             : 
     546             :   // Restore security info, if present
     547           0 :   const char* info = entry->GetMetaDataElement("security-info");
     548           0 :   if (info) {
     549           0 :     nsCOMPtr<nsISupports> infoObj;
     550           0 :     rv = NS_DeserializeObject(nsDependentCString(info),
     551           0 :                               getter_AddRefs(infoObj));
     552           0 :     if (NS_FAILED(rv)) {
     553           0 :       delete entry;
     554           0 :       return nullptr;
     555             :     }
     556           0 :     entry->SetSecurityInfo(infoObj);
     557             :   }
     558             : 
     559             :   // create a binding object for this entry
     560             :   nsOfflineCacheBinding *binding =
     561           0 :       nsOfflineCacheBinding::Create(device->CacheDirectory(),
     562             :                                     fullKey,
     563           0 :                                     rec.generation);
     564           0 :   if (!binding)
     565             :   {
     566           0 :     delete entry;
     567           0 :     return nullptr;
     568             :   }
     569           0 :   entry->SetData(binding);
     570             : 
     571           0 :   return entry;
     572             : }
     573             : 
     574             : 
     575             : /******************************************************************************
     576             :  * nsOfflineCacheEntryInfo
     577             :  */
     578             : 
     579           0 : class nsOfflineCacheEntryInfo final : public nsICacheEntryInfo
     580             : {
     581           0 :   ~nsOfflineCacheEntryInfo() {}
     582             : 
     583             : public:
     584             :   NS_DECL_ISUPPORTS
     585             :   NS_DECL_NSICACHEENTRYINFO
     586             : 
     587             :   nsOfflineCacheRecord *mRec;
     588             : };
     589             : 
     590           0 : NS_IMPL_ISUPPORTS(nsOfflineCacheEntryInfo, nsICacheEntryInfo)
     591             : 
     592             : NS_IMETHODIMP
     593           0 : nsOfflineCacheEntryInfo::GetClientID(char **result)
     594             : {
     595           0 :   *result = NS_strdup(mRec->clientID);
     596           0 :   return *result ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
     597             : }
     598             : 
     599             : NS_IMETHODIMP
     600           0 : nsOfflineCacheEntryInfo::GetDeviceID(char ** deviceID)
     601             : {
     602           0 :   *deviceID = NS_strdup(OFFLINE_CACHE_DEVICE_ID);
     603           0 :   return *deviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
     604             : }
     605             : 
     606             : NS_IMETHODIMP
     607           0 : nsOfflineCacheEntryInfo::GetKey(nsACString &clientKey)
     608             : {
     609           0 :   clientKey.Assign(mRec->key);
     610           0 :   return NS_OK;
     611             : }
     612             : 
     613             : NS_IMETHODIMP
     614           0 : nsOfflineCacheEntryInfo::GetFetchCount(int32_t *aFetchCount)
     615             : {
     616           0 :   *aFetchCount = mRec->fetchCount;
     617           0 :   return NS_OK;
     618             : }
     619             : 
     620             : NS_IMETHODIMP
     621           0 : nsOfflineCacheEntryInfo::GetLastFetched(uint32_t *aLastFetched)
     622             : {
     623           0 :   *aLastFetched = SecondsFromPRTime(mRec->lastFetched);
     624           0 :   return NS_OK;
     625             : }
     626             : 
     627             : NS_IMETHODIMP
     628           0 : nsOfflineCacheEntryInfo::GetLastModified(uint32_t *aLastModified)
     629             : {
     630           0 :   *aLastModified = SecondsFromPRTime(mRec->lastModified);
     631           0 :   return NS_OK;
     632             : }
     633             : 
     634             : NS_IMETHODIMP
     635           0 : nsOfflineCacheEntryInfo::GetExpirationTime(uint32_t *aExpirationTime)
     636             : {
     637           0 :   *aExpirationTime = SecondsFromPRTime(mRec->expirationTime);
     638           0 :   return NS_OK;
     639             : }
     640             : 
     641             : NS_IMETHODIMP
     642           0 : nsOfflineCacheEntryInfo::IsStreamBased(bool *aStreamBased)
     643             : {
     644           0 :   *aStreamBased = true;
     645           0 :   return NS_OK;
     646             : }
     647             : 
     648             : NS_IMETHODIMP
     649           0 : nsOfflineCacheEntryInfo::GetDataSize(uint32_t *aDataSize)
     650             : {
     651           0 :   *aDataSize = mRec->dataSize;
     652           0 :   return NS_OK;
     653             : }
     654             : 
     655             : 
     656             : /******************************************************************************
     657             :  * nsApplicationCacheNamespace
     658             :  */
     659             : 
     660           0 : NS_IMPL_ISUPPORTS(nsApplicationCacheNamespace, nsIApplicationCacheNamespace)
     661             : 
     662             : NS_IMETHODIMP
     663           0 : nsApplicationCacheNamespace::Init(uint32_t itemType,
     664             :                                   const nsACString &namespaceSpec,
     665             :                                   const nsACString &data)
     666             : {
     667           0 :   mItemType = itemType;
     668           0 :   mNamespaceSpec = namespaceSpec;
     669           0 :   mData = data;
     670           0 :   return NS_OK;
     671             : }
     672             : 
     673             : NS_IMETHODIMP
     674           0 : nsApplicationCacheNamespace::GetItemType(uint32_t *out)
     675             : {
     676           0 :   *out = mItemType;
     677           0 :   return NS_OK;
     678             : }
     679             : 
     680             : NS_IMETHODIMP
     681           0 : nsApplicationCacheNamespace::GetNamespaceSpec(nsACString &out)
     682             : {
     683           0 :   out = mNamespaceSpec;
     684           0 :   return NS_OK;
     685             : }
     686             : 
     687             : NS_IMETHODIMP
     688           0 : nsApplicationCacheNamespace::GetData(nsACString &out)
     689             : {
     690           0 :   out = mData;
     691           0 :   return NS_OK;
     692             : }
     693             : 
     694             : /******************************************************************************
     695             :  * nsApplicationCache
     696             :  */
     697             : 
     698           0 : NS_IMPL_ISUPPORTS(nsApplicationCache,
     699             :                   nsIApplicationCache,
     700             :                   nsISupportsWeakReference)
     701             : 
     702           0 : nsApplicationCache::nsApplicationCache()
     703             :   : mDevice(nullptr)
     704           0 :   , mValid(true)
     705             : {
     706           0 : }
     707             : 
     708           0 : nsApplicationCache::nsApplicationCache(nsOfflineCacheDevice *device,
     709             :                                        const nsACString &group,
     710           0 :                                        const nsACString &clientID)
     711             :   : mDevice(device)
     712             :   , mGroup(group)
     713             :   , mClientID(clientID)
     714           0 :   , mValid(true)
     715             : {
     716           0 : }
     717             : 
     718           0 : nsApplicationCache::~nsApplicationCache()
     719             : {
     720           0 :   if (!mDevice)
     721           0 :     return;
     722             : 
     723             :   {
     724           0 :     MutexAutoLock lock(mDevice->mLock);
     725           0 :     mDevice->mCaches.Remove(mClientID);
     726             :   }
     727             : 
     728             :   // If this isn't an active cache anymore, it can be destroyed.
     729           0 :   if (mValid && !mDevice->IsActiveCache(mGroup, mClientID))
     730           0 :     Discard();
     731           0 : }
     732             : 
     733             : void
     734           0 : nsApplicationCache::MarkInvalid()
     735             : {
     736           0 :   mValid = false;
     737           0 : }
     738             : 
     739             : NS_IMETHODIMP
     740           0 : nsApplicationCache::InitAsHandle(const nsACString &groupId,
     741             :                                  const nsACString &clientId)
     742             : {
     743           0 :   NS_ENSURE_FALSE(mDevice, NS_ERROR_ALREADY_INITIALIZED);
     744           0 :   NS_ENSURE_TRUE(mGroup.IsEmpty(), NS_ERROR_ALREADY_INITIALIZED);
     745             : 
     746           0 :   mGroup = groupId;
     747           0 :   mClientID = clientId;
     748           0 :   return NS_OK;
     749             : }
     750             : 
     751             : NS_IMETHODIMP
     752           0 : nsApplicationCache::GetManifestURI(nsIURI **out)
     753             : {
     754           0 :   nsCOMPtr<nsIURI> uri;
     755           0 :   nsresult rv = NS_NewURI(getter_AddRefs(uri), mGroup);
     756           0 :   NS_ENSURE_SUCCESS(rv, rv);
     757             : 
     758           0 :   rv = uri->CloneIgnoringRef(out);
     759           0 :   NS_ENSURE_SUCCESS(rv, rv);
     760             : 
     761           0 :   return NS_OK;
     762             : }
     763             : 
     764             : NS_IMETHODIMP
     765           0 : nsApplicationCache::GetGroupID(nsACString &out)
     766             : {
     767           0 :   out = mGroup;
     768           0 :   return NS_OK;
     769             : }
     770             : 
     771             : NS_IMETHODIMP
     772           0 : nsApplicationCache::GetClientID(nsACString &out)
     773             : {
     774           0 :   out = mClientID;
     775           0 :   return NS_OK;
     776             : }
     777             : 
     778             : NS_IMETHODIMP
     779           0 : nsApplicationCache::GetProfileDirectory(nsIFile **out)
     780             : {
     781           0 :   if (mDevice->BaseDirectory())
     782           0 :       NS_ADDREF(*out = mDevice->BaseDirectory());
     783             :   else
     784           0 :       *out = nullptr;
     785             : 
     786           0 :   return NS_OK;
     787             : }
     788             : 
     789             : NS_IMETHODIMP
     790           0 : nsApplicationCache::GetActive(bool *out)
     791             : {
     792           0 :   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
     793             : 
     794           0 :   *out = mDevice->IsActiveCache(mGroup, mClientID);
     795           0 :   return NS_OK;
     796             : }
     797             : 
     798             : NS_IMETHODIMP
     799           0 : nsApplicationCache::Activate()
     800             : {
     801           0 :   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
     802           0 :   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
     803             : 
     804           0 :   mDevice->ActivateCache(mGroup, mClientID);
     805             : 
     806           0 :   if (mDevice->AutoShutdown(this))
     807           0 :     mDevice = nullptr;
     808             : 
     809           0 :   return NS_OK;
     810             : }
     811             : 
     812             : NS_IMETHODIMP
     813           0 : nsApplicationCache::Discard()
     814             : {
     815           0 :   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
     816           0 :   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
     817             : 
     818           0 :   mValid = false;
     819             : 
     820             :   nsCOMPtr<nsIRunnable> ev =
     821           0 :     new nsOfflineCacheDiscardCache(mDevice, mGroup, mClientID);
     822           0 :   nsresult rv = nsCacheService::DispatchToCacheIOThread(ev);
     823           0 :   return rv;
     824             : }
     825             : 
     826             : NS_IMETHODIMP
     827           0 : nsApplicationCache::MarkEntry(const nsACString &key,
     828             :                               uint32_t typeBits)
     829             : {
     830           0 :   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
     831           0 :   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
     832             : 
     833           0 :   return mDevice->MarkEntry(mClientID, key, typeBits);
     834             : }
     835             : 
     836             : 
     837             : NS_IMETHODIMP
     838           0 : nsApplicationCache::UnmarkEntry(const nsACString &key,
     839             :                                 uint32_t typeBits)
     840             : {
     841           0 :   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
     842           0 :   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
     843             : 
     844           0 :   return mDevice->UnmarkEntry(mClientID, key, typeBits);
     845             : }
     846             : 
     847             : NS_IMETHODIMP
     848           0 : nsApplicationCache::GetTypes(const nsACString &key,
     849             :                              uint32_t *typeBits)
     850             : {
     851           0 :   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
     852           0 :   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
     853             : 
     854           0 :   return mDevice->GetTypes(mClientID, key, typeBits);
     855             : }
     856             : 
     857             : NS_IMETHODIMP
     858           0 : nsApplicationCache::GatherEntries(uint32_t typeBits,
     859             :                                   uint32_t * count,
     860             :                                   char *** keys)
     861             : {
     862           0 :   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
     863           0 :   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
     864             : 
     865           0 :   return mDevice->GatherEntries(mClientID, typeBits, count, keys);
     866             : }
     867             : 
     868             : NS_IMETHODIMP
     869           0 : nsApplicationCache::AddNamespaces(nsIArray *namespaces)
     870             : {
     871           0 :   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
     872           0 :   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
     873             : 
     874           0 :   if (!namespaces)
     875           0 :     return NS_OK;
     876             : 
     877           0 :   mozStorageTransaction transaction(mDevice->mDB, false);
     878             : 
     879             :   uint32_t length;
     880           0 :   nsresult rv = namespaces->GetLength(&length);
     881           0 :   NS_ENSURE_SUCCESS(rv, rv);
     882             : 
     883           0 :   for (uint32_t i = 0; i < length; i++) {
     884             :     nsCOMPtr<nsIApplicationCacheNamespace> ns =
     885           0 :       do_QueryElementAt(namespaces, i);
     886           0 :     if (ns) {
     887           0 :       rv = mDevice->AddNamespace(mClientID, ns);
     888           0 :       NS_ENSURE_SUCCESS(rv, rv);
     889             :     }
     890             :   }
     891             : 
     892           0 :   rv = transaction.Commit();
     893           0 :   NS_ENSURE_SUCCESS(rv, rv);
     894             : 
     895           0 :   return NS_OK;
     896             : }
     897             : 
     898             : NS_IMETHODIMP
     899           0 : nsApplicationCache::GetMatchingNamespace(const nsACString &key,
     900             :                                          nsIApplicationCacheNamespace **out)
     901             : 
     902             : {
     903           0 :   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
     904           0 :   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
     905             : 
     906           0 :   return mDevice->GetMatchingNamespace(mClientID, key, out);
     907             : }
     908             : 
     909             : NS_IMETHODIMP
     910           0 : nsApplicationCache::GetUsage(uint32_t *usage)
     911             : {
     912           0 :   NS_ENSURE_TRUE(mValid, NS_ERROR_NOT_AVAILABLE);
     913           0 :   NS_ENSURE_TRUE(mDevice, NS_ERROR_NOT_AVAILABLE);
     914             : 
     915           0 :   return mDevice->GetUsage(mClientID, usage);
     916             : }
     917             : 
     918             : /******************************************************************************
     919             :  * nsCloseDBEvent
     920             :  *****************************************************************************/
     921             : 
     922             : class nsCloseDBEvent : public Runnable {
     923             : public:
     924           0 :   explicit nsCloseDBEvent(mozIStorageConnection* aDB)
     925           0 :     : mozilla::Runnable("nsCloseDBEvent")
     926             :   {
     927           0 :     mDB = aDB;
     928           0 :   }
     929             : 
     930           0 :   NS_IMETHOD Run() override
     931             :   {
     932           0 :     mDB->Close();
     933           0 :     return NS_OK;
     934             :   }
     935             : 
     936             : protected:
     937           0 :   virtual ~nsCloseDBEvent() {}
     938             : 
     939             : private:
     940             :   nsCOMPtr<mozIStorageConnection> mDB;
     941             : };
     942             : 
     943             : 
     944             : 
     945             : /******************************************************************************
     946             :  * nsOfflineCacheDevice
     947             :  */
     948             : 
     949           0 : NS_IMPL_ISUPPORTS0(nsOfflineCacheDevice)
     950             : 
     951           0 : nsOfflineCacheDevice::nsOfflineCacheDevice()
     952             :   : mDB(nullptr)
     953             :   , mCacheCapacity(0)
     954             :   , mDeltaCounter(0)
     955             :   , mAutoShutdown(false)
     956             :   , mLock("nsOfflineCacheDevice.lock")
     957             :   , mActiveCaches(4)
     958           0 :   , mLockedEntries(32)
     959             : {
     960           0 : }
     961             : 
     962           0 : nsOfflineCacheDevice::~nsOfflineCacheDevice()
     963           0 : {}
     964             : 
     965             : /* static */
     966             : bool
     967           0 : nsOfflineCacheDevice::GetStrictFileOriginPolicy()
     968             : {
     969           0 :     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     970             : 
     971             :     bool retval;
     972           0 :     if (prefs && NS_SUCCEEDED(prefs->GetBoolPref("security.fileuri.strict_origin_policy", &retval)))
     973           0 :         return retval;
     974             : 
     975             :     // As default value use true (be more strict)
     976           0 :     return true;
     977             : }
     978             : 
     979             : uint32_t
     980           0 : nsOfflineCacheDevice::CacheSize()
     981             : {
     982           0 :   NS_ENSURE_TRUE(Initialized(), 0);
     983             : 
     984           0 :   AutoResetStatement statement(mStatement_CacheSize);
     985             : 
     986             :   bool hasRows;
     987           0 :   nsresult rv = statement->ExecuteStep(&hasRows);
     988           0 :   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasRows, 0);
     989             : 
     990           0 :   return (uint32_t) statement->AsInt32(0);
     991             : }
     992             : 
     993             : uint32_t
     994           0 : nsOfflineCacheDevice::EntryCount()
     995             : {
     996           0 :   NS_ENSURE_TRUE(Initialized(), 0);
     997             : 
     998           0 :   AutoResetStatement statement(mStatement_EntryCount);
     999             : 
    1000             :   bool hasRows;
    1001           0 :   nsresult rv = statement->ExecuteStep(&hasRows);
    1002           0 :   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasRows, 0);
    1003             : 
    1004           0 :   return (uint32_t) statement->AsInt32(0);
    1005             : }
    1006             : 
    1007             : nsresult
    1008           0 : nsOfflineCacheDevice::UpdateEntry(nsCacheEntry *entry)
    1009             : {
    1010           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    1011             : 
    1012             :   // Decompose the key into "ClientID" and "Key"
    1013           0 :   nsAutoCString keyBuf;
    1014             :   const char *cid, *key;
    1015             : 
    1016           0 :   if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf))
    1017           0 :     return NS_ERROR_UNEXPECTED;
    1018             : 
    1019             :   // Store security info, if it is serializable
    1020           0 :   nsCOMPtr<nsISupports> infoObj = entry->SecurityInfo();
    1021           0 :   nsCOMPtr<nsISerializable> serializable = do_QueryInterface(infoObj);
    1022           0 :   if (infoObj && !serializable)
    1023           0 :     return NS_ERROR_UNEXPECTED;
    1024             : 
    1025           0 :   if (serializable) {
    1026           0 :     nsCString info;
    1027           0 :     nsresult rv = NS_SerializeToString(serializable, info);
    1028           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1029             : 
    1030           0 :     rv = entry->SetMetaDataElement("security-info", info.get());
    1031           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1032             :   }
    1033             : 
    1034           0 :   nsCString metaDataBuf;
    1035           0 :   uint32_t mdSize = entry->MetaDataSize();
    1036           0 :   if (!metaDataBuf.SetLength(mdSize, fallible))
    1037           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1038           0 :   char *md = metaDataBuf.BeginWriting();
    1039           0 :   entry->FlattenMetaData(md, mdSize);
    1040             : 
    1041             :   nsOfflineCacheRecord rec;
    1042           0 :   rec.metaData = (const uint8_t *) md;
    1043           0 :   rec.metaDataLen = mdSize;
    1044           0 :   rec.dataSize = entry->DataSize();
    1045           0 :   rec.fetchCount = entry->FetchCount();
    1046           0 :   rec.lastFetched = PRTimeFromSeconds(entry->LastFetched());
    1047           0 :   rec.lastModified = PRTimeFromSeconds(entry->LastModified());
    1048           0 :   rec.expirationTime = PRTimeFromSeconds(entry->ExpirationTime());
    1049             : 
    1050           0 :   AutoResetStatement statement(mStatement_UpdateEntry);
    1051             : 
    1052             :   nsresult rv;
    1053           0 :   rv = statement->BindBlobByIndex(0, rec.metaData, rec.metaDataLen);
    1054           0 :   nsresult tmp = statement->BindInt32ByIndex(1, rec.dataSize);
    1055           0 :   if (NS_FAILED(tmp)) {
    1056           0 :     rv = tmp;
    1057             :   }
    1058           0 :   tmp = statement->BindInt32ByIndex(2, rec.fetchCount);
    1059           0 :   if (NS_FAILED(tmp)) {
    1060           0 :     rv = tmp;
    1061             :   }
    1062           0 :   tmp = statement->BindInt64ByIndex(3, rec.lastFetched);
    1063           0 :   if (NS_FAILED(tmp)) {
    1064           0 :     rv = tmp;
    1065             :   }
    1066           0 :   tmp = statement->BindInt64ByIndex(4, rec.lastModified);
    1067           0 :   if (NS_FAILED(tmp)) {
    1068           0 :     rv = tmp;
    1069             :   }
    1070           0 :   tmp = statement->BindInt64ByIndex(5, rec.expirationTime);
    1071           0 :   if (NS_FAILED(tmp)) {
    1072           0 :     rv = tmp;
    1073             :   }
    1074           0 :   tmp = statement->BindUTF8StringByIndex(6, nsDependentCString(cid));
    1075           0 :   if (NS_FAILED(tmp)) {
    1076           0 :     rv = tmp;
    1077             :   }
    1078           0 :   tmp = statement->BindUTF8StringByIndex(7, nsDependentCString(key));
    1079           0 :   if (NS_FAILED(tmp)) {
    1080           0 :     rv = tmp;
    1081             :   }
    1082           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1083             : 
    1084             :   bool hasRows;
    1085           0 :   rv = statement->ExecuteStep(&hasRows);
    1086           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1087             : 
    1088           0 :   NS_ASSERTION(!hasRows, "UPDATE should not result in output");
    1089           0 :   return rv;
    1090             : }
    1091             : 
    1092             : nsresult
    1093           0 : nsOfflineCacheDevice::UpdateEntrySize(nsCacheEntry *entry, uint32_t newSize)
    1094             : {
    1095           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    1096             : 
    1097             :   // Decompose the key into "ClientID" and "Key"
    1098           0 :   nsAutoCString keyBuf;
    1099             :   const char *cid, *key;
    1100           0 :   if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf))
    1101           0 :     return NS_ERROR_UNEXPECTED;
    1102             : 
    1103           0 :   AutoResetStatement statement(mStatement_UpdateEntrySize);
    1104             : 
    1105           0 :   nsresult rv = statement->BindInt32ByIndex(0, newSize);
    1106           0 :   nsresult tmp = statement->BindUTF8StringByIndex(1, nsDependentCString(cid));
    1107           0 :   if (NS_FAILED(tmp)) {
    1108           0 :     rv = tmp;
    1109             :   }
    1110           0 :   tmp = statement->BindUTF8StringByIndex(2, nsDependentCString(key));
    1111           0 :   if (NS_FAILED(tmp)) {
    1112           0 :     rv = tmp;
    1113             :   }
    1114           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1115             : 
    1116             :   bool hasRows;
    1117           0 :   rv = statement->ExecuteStep(&hasRows);
    1118           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1119             : 
    1120           0 :   NS_ASSERTION(!hasRows, "UPDATE should not result in output");
    1121           0 :   return rv;
    1122             : }
    1123             : 
    1124             : nsresult
    1125           0 : nsOfflineCacheDevice::DeleteEntry(nsCacheEntry *entry, bool deleteData)
    1126             : {
    1127           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    1128             : 
    1129           0 :   if (deleteData)
    1130             :   {
    1131           0 :     nsresult rv = DeleteData(entry);
    1132           0 :     if (NS_FAILED(rv))
    1133           0 :       return rv;
    1134             :   }
    1135             : 
    1136             :   // Decompose the key into "ClientID" and "Key"
    1137           0 :   nsAutoCString keyBuf;
    1138             :   const char *cid, *key;
    1139           0 :   if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf))
    1140           0 :     return NS_ERROR_UNEXPECTED;
    1141             : 
    1142           0 :   AutoResetStatement statement(mStatement_DeleteEntry);
    1143             : 
    1144           0 :   nsresult rv = statement->BindUTF8StringByIndex(0, nsDependentCString(cid));
    1145           0 :   nsresult rv2 = statement->BindUTF8StringByIndex(1, nsDependentCString(key));
    1146           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1147           0 :   NS_ENSURE_SUCCESS(rv2, rv2);
    1148             : 
    1149             :   bool hasRows;
    1150           0 :   rv = statement->ExecuteStep(&hasRows);
    1151           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1152             : 
    1153           0 :   NS_ASSERTION(!hasRows, "DELETE should not result in output");
    1154           0 :   return rv;
    1155             : }
    1156             : 
    1157             : nsresult
    1158           0 : nsOfflineCacheDevice::DeleteData(nsCacheEntry *entry)
    1159             : {
    1160           0 :   nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data();
    1161           0 :   NS_ENSURE_STATE(binding);
    1162             : 
    1163           0 :   return binding->mDataFile->Remove(false);
    1164             : }
    1165             : 
    1166             : /**
    1167             :  * nsCacheDevice implementation
    1168             :  */
    1169             : 
    1170             : // This struct is local to nsOfflineCacheDevice::Init, but ISO C++98 doesn't
    1171             : // allow a template (mozilla::ArrayLength) to be instantiated based on a local
    1172             : // type.  Boo-urns!
    1173             : struct StatementSql {
    1174             :     nsCOMPtr<mozIStorageStatement> &statement;
    1175             :     const char *sql;
    1176           0 :     StatementSql (nsCOMPtr<mozIStorageStatement> &aStatement, const char *aSql):
    1177           0 :       statement (aStatement), sql (aSql) {}
    1178             : };
    1179             : 
    1180             : nsresult
    1181           0 : nsOfflineCacheDevice::Init()
    1182             : {
    1183           0 :   MOZ_ASSERT(false, "Need to be initialized with sqlite");
    1184             :   return NS_ERROR_NOT_IMPLEMENTED;
    1185             : }
    1186             : 
    1187             : nsresult
    1188           0 : nsOfflineCacheDevice::InitWithSqlite(mozIStorageService * ss)
    1189             : {
    1190           0 :   NS_ENSURE_TRUE(!mDB, NS_ERROR_ALREADY_INITIALIZED);
    1191             : 
    1192             :   // SetCacheParentDirectory must have been called
    1193           0 :   NS_ENSURE_TRUE(mCacheDirectory, NS_ERROR_UNEXPECTED);
    1194             : 
    1195             :   // make sure the cache directory exists
    1196           0 :   nsresult rv = EnsureDir(mCacheDirectory);
    1197           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1198             : 
    1199             :   // build path to index file
    1200           0 :   nsCOMPtr<nsIFile> indexFile;
    1201           0 :   rv = mCacheDirectory->Clone(getter_AddRefs(indexFile));
    1202           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1203           0 :   rv = indexFile->AppendNative(NS_LITERAL_CSTRING("index.sqlite"));
    1204           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1205             : 
    1206           0 :   MOZ_ASSERT(ss, "nsOfflineCacheDevice::InitWithSqlite called before nsCacheService::Init() ?");
    1207           0 :   NS_ENSURE_TRUE(ss, NS_ERROR_UNEXPECTED);
    1208             : 
    1209           0 :   rv = ss->OpenDatabase(indexFile, getter_AddRefs(mDB));
    1210           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1211             : 
    1212           0 :   mInitEventTarget = GetCurrentThreadEventTarget();
    1213             : 
    1214           0 :   mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous = OFF;"));
    1215             : 
    1216             :   // XXX ... other initialization steps
    1217             : 
    1218             :   // XXX in the future we may wish to verify the schema for moz_cache
    1219             :   //     perhaps using "PRAGMA table_info" ?
    1220             : 
    1221             :   // build the table
    1222             :   //
    1223             :   //  "Generation" is the data file generation number.
    1224             :   //
    1225           0 :   rv = mDB->ExecuteSimpleSQL(
    1226           0 :       NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_cache (\n"
    1227             :                          "  ClientID        TEXT,\n"
    1228             :                          "  Key             TEXT,\n"
    1229             :                          "  MetaData        BLOB,\n"
    1230             :                          "  Generation      INTEGER,\n"
    1231             :                          "  DataSize        INTEGER,\n"
    1232             :                          "  FetchCount      INTEGER,\n"
    1233             :                          "  LastFetched     INTEGER,\n"
    1234             :                          "  LastModified    INTEGER,\n"
    1235             :                          "  ExpirationTime  INTEGER,\n"
    1236             :                          "  ItemType        INTEGER DEFAULT 0\n"
    1237           0 :                          ");\n"));
    1238           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1239             : 
    1240             :   // Databases from 1.9.0 don't have the ItemType column.  Add the column
    1241             :   // here, but don't worry about failures (the column probably already exists)
    1242           0 :   mDB->ExecuteSimpleSQL(
    1243           0 :     NS_LITERAL_CSTRING("ALTER TABLE moz_cache ADD ItemType INTEGER DEFAULT 0"));
    1244             : 
    1245             :   // Create the table for storing cache groups.  All actions on
    1246             :   // moz_cache_groups use the GroupID, so use it as the primary key.
    1247           0 :   rv = mDB->ExecuteSimpleSQL(
    1248           0 :       NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_cache_groups (\n"
    1249             :                          " GroupID TEXT PRIMARY KEY,\n"
    1250             :                          " ActiveClientID TEXT\n"
    1251           0 :                          ");\n"));
    1252           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1253             : 
    1254           0 :   mDB->ExecuteSimpleSQL(
    1255           0 :     NS_LITERAL_CSTRING("ALTER TABLE moz_cache_groups "
    1256           0 :                        "ADD ActivateTimeStamp INTEGER DEFAULT 0"));
    1257             : 
    1258             :   // ClientID: clientID joining moz_cache and moz_cache_namespaces
    1259             :   // tables.
    1260             :   // Data: Data associated with this namespace (e.g. a fallback URI
    1261             :   // for fallback entries).
    1262             :   // ItemType: the type of namespace.
    1263           0 :   rv = mDB->ExecuteSimpleSQL(
    1264           0 :       NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS"
    1265             :                          " moz_cache_namespaces (\n"
    1266             :                          " ClientID TEXT,\n"
    1267             :                          " NameSpace TEXT,\n"
    1268             :                          " Data TEXT,\n"
    1269             :                          " ItemType INTEGER\n"
    1270           0 :                           ");\n"));
    1271           0 :    NS_ENSURE_SUCCESS(rv, rv);
    1272             : 
    1273             :   // Databases from 1.9.0 have a moz_cache_index that should be dropped
    1274           0 :   rv = mDB->ExecuteSimpleSQL(
    1275           0 :       NS_LITERAL_CSTRING("DROP INDEX IF EXISTS moz_cache_index"));
    1276           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1277             : 
    1278             :   // Key/ClientID pairs should be unique in the database.  All queries
    1279             :   // against moz_cache use the Key (which is also the most unique), so
    1280             :   // use it as the primary key for this index.
    1281           0 :   rv = mDB->ExecuteSimpleSQL(
    1282           0 :       NS_LITERAL_CSTRING("CREATE UNIQUE INDEX IF NOT EXISTS "
    1283             :                          " moz_cache_key_clientid_index"
    1284           0 :                          " ON moz_cache (Key, ClientID);"));
    1285           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1286             : 
    1287             :   // Used for ClientID lookups and to keep ClientID/NameSpace pairs unique.
    1288           0 :   rv = mDB->ExecuteSimpleSQL(
    1289           0 :       NS_LITERAL_CSTRING("CREATE UNIQUE INDEX IF NOT EXISTS"
    1290             :                          " moz_cache_namespaces_clientid_index"
    1291           0 :                          " ON moz_cache_namespaces (ClientID, NameSpace);"));
    1292           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1293             : 
    1294             :   // Used for namespace lookups.
    1295           0 :   rv = mDB->ExecuteSimpleSQL(
    1296           0 :       NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS"
    1297             :                          " moz_cache_namespaces_namespace_index"
    1298           0 :                          " ON moz_cache_namespaces (NameSpace);"));
    1299           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1300             : 
    1301             : 
    1302           0 :   mEvictionFunction = new nsOfflineCacheEvictionFunction(this);
    1303           0 :   if (!mEvictionFunction) return NS_ERROR_OUT_OF_MEMORY;
    1304             : 
    1305           0 :   rv = mDB->CreateFunction(NS_LITERAL_CSTRING("cache_eviction_observer"), 3, mEvictionFunction);
    1306           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1307             : 
    1308             :   // create all (most) of our statements up front
    1309             :   StatementSql prepared[] = {
    1310             :     StatementSql ( mStatement_CacheSize,         "SELECT Sum(DataSize) from moz_cache;" ),
    1311             :     StatementSql ( mStatement_ApplicationCacheSize, "SELECT Sum(DataSize) from moz_cache WHERE ClientID = ?;" ),
    1312             :     StatementSql ( mStatement_EntryCount,        "SELECT count(*) from moz_cache;" ),
    1313             :     StatementSql ( mStatement_UpdateEntry,       "UPDATE moz_cache SET MetaData = ?, DataSize = ?, FetchCount = ?, LastFetched = ?, LastModified = ?, ExpirationTime = ? WHERE ClientID = ? AND Key = ?;" ),
    1314             :     StatementSql ( mStatement_UpdateEntrySize,   "UPDATE moz_cache SET DataSize = ? WHERE ClientID = ? AND Key = ?;" ),
    1315             :     StatementSql ( mStatement_DeleteEntry,       "DELETE FROM moz_cache WHERE ClientID = ? AND Key = ?;" ),
    1316             :     StatementSql ( mStatement_FindEntry,         "SELECT MetaData, Generation, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime, ItemType FROM moz_cache WHERE ClientID = ? AND Key = ?;" ),
    1317             :     StatementSql ( mStatement_BindEntry,         "INSERT INTO moz_cache (ClientID, Key, MetaData, Generation, DataSize, FetchCount, LastFetched, LastModified, ExpirationTime) VALUES(?,?,?,?,?,?,?,?,?);" ),
    1318             : 
    1319             :     StatementSql ( mStatement_MarkEntry,         "UPDATE moz_cache SET ItemType = (ItemType | ?) WHERE ClientID = ? AND Key = ?;" ),
    1320             :     StatementSql ( mStatement_UnmarkEntry,       "UPDATE moz_cache SET ItemType = (ItemType & ~?) WHERE ClientID = ? AND Key = ?;" ),
    1321             :     StatementSql ( mStatement_GetTypes,          "SELECT ItemType FROM moz_cache WHERE ClientID = ? AND Key = ?;"),
    1322             :     StatementSql ( mStatement_CleanupUnmarked,   "DELETE FROM moz_cache WHERE ClientID = ? AND Key = ? AND ItemType = 0;" ),
    1323             :     StatementSql ( mStatement_GatherEntries,     "SELECT Key FROM moz_cache WHERE ClientID = ? AND (ItemType & ?) > 0;" ),
    1324             : 
    1325             :     StatementSql ( mStatement_ActivateClient,    "INSERT OR REPLACE INTO moz_cache_groups (GroupID, ActiveClientID, ActivateTimeStamp) VALUES (?, ?, ?);" ),
    1326             :     StatementSql ( mStatement_DeactivateGroup,   "DELETE FROM moz_cache_groups WHERE GroupID = ?;" ),
    1327             :     StatementSql ( mStatement_FindClient,        "SELECT ClientID, ItemType FROM moz_cache WHERE Key = ? ORDER BY LastFetched DESC, LastModified DESC;" ),
    1328             : 
    1329             :     // Search for namespaces that match the URI.  Use the <= operator
    1330             :     // to ensure that we use the index on moz_cache_namespaces.
    1331             :     StatementSql ( mStatement_FindClientByNamespace, "SELECT ns.ClientID, ns.ItemType FROM"
    1332             :                                                      "  moz_cache_namespaces AS ns JOIN moz_cache_groups AS groups"
    1333             :                                                      "  ON ns.ClientID = groups.ActiveClientID"
    1334             :                                                      " WHERE ns.NameSpace <= ?1 AND ?1 GLOB ns.NameSpace || '*'"
    1335             :                                                      " ORDER BY ns.NameSpace DESC, groups.ActivateTimeStamp DESC;"),
    1336             :     StatementSql ( mStatement_FindNamespaceEntry,    "SELECT NameSpace, Data, ItemType FROM moz_cache_namespaces"
    1337             :                                                      " WHERE ClientID = ?1"
    1338             :                                                      " AND NameSpace <= ?2 AND ?2 GLOB NameSpace || '*'"
    1339             :                                                      " ORDER BY NameSpace DESC;"),
    1340             :     StatementSql ( mStatement_InsertNamespaceEntry,  "INSERT INTO moz_cache_namespaces (ClientID, NameSpace, Data, ItemType) VALUES(?, ?, ?, ?);"),
    1341             :     StatementSql ( mStatement_EnumerateApps,         "SELECT GroupID, ActiveClientID FROM moz_cache_groups WHERE GroupID LIKE ?1;"),
    1342             :     StatementSql ( mStatement_EnumerateGroups,       "SELECT GroupID, ActiveClientID FROM moz_cache_groups;"),
    1343             :     StatementSql ( mStatement_EnumerateGroupsTimeOrder, "SELECT GroupID, ActiveClientID FROM moz_cache_groups ORDER BY ActivateTimeStamp;")
    1344           0 :   };
    1345           0 :   for (uint32_t i = 0; NS_SUCCEEDED(rv) && i < ArrayLength(prepared); ++i)
    1346             :   {
    1347           0 :     LOG(("Creating statement: %s\n", prepared[i].sql));
    1348             : 
    1349           0 :     rv = mDB->CreateStatement(nsDependentCString(prepared[i].sql),
    1350           0 :                               getter_AddRefs(prepared[i].statement));
    1351           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1352             :   }
    1353             : 
    1354           0 :   rv = InitActiveCaches();
    1355           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1356             : 
    1357           0 :   return NS_OK;
    1358             : }
    1359             : 
    1360             : namespace {
    1361             : 
    1362             : nsresult
    1363           0 : GetGroupForCache(const nsACString& clientID, nsCString& group)
    1364             : {
    1365           0 :   group.Assign(clientID);
    1366           0 :   group.Truncate(group.FindChar('|'));
    1367           0 :   NS_UnescapeURL(group);
    1368             : 
    1369           0 :   return NS_OK;
    1370             : }
    1371             : 
    1372             : } // namespace
    1373             : 
    1374             : // static
    1375             : nsresult
    1376           0 : nsOfflineCacheDevice::BuildApplicationCacheGroupID(nsIURI *aManifestURL,
    1377             :                                                    nsACString const &aOriginSuffix,
    1378             :                                                    nsACString &_result)
    1379             : {
    1380           0 :   nsCOMPtr<nsIURI> newURI;
    1381           0 :   nsresult rv = aManifestURL->CloneIgnoringRef(getter_AddRefs(newURI));
    1382           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1383             : 
    1384           0 :   nsAutoCString manifestSpec;
    1385           0 :   rv = newURI->GetAsciiSpec(manifestSpec);
    1386           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1387             : 
    1388           0 :   _result.Assign(manifestSpec);
    1389           0 :   _result.Append('#');
    1390           0 :   _result.Append(aOriginSuffix);
    1391             : 
    1392           0 :   return NS_OK;
    1393             : }
    1394             : 
    1395             : nsresult
    1396           0 : nsOfflineCacheDevice::InitActiveCaches()
    1397             : {
    1398           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    1399             : 
    1400           0 :   MutexAutoLock lock(mLock);
    1401             : 
    1402           0 :   AutoResetStatement statement(mStatement_EnumerateGroups);
    1403             : 
    1404             :   bool hasRows;
    1405           0 :   nsresult rv = statement->ExecuteStep(&hasRows);
    1406           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1407             : 
    1408           0 :   while (hasRows)
    1409             :   {
    1410           0 :     nsAutoCString group;
    1411           0 :     statement->GetUTF8String(0, group);
    1412           0 :     nsCString clientID;
    1413           0 :     statement->GetUTF8String(1, clientID);
    1414             : 
    1415           0 :     mActiveCaches.PutEntry(clientID);
    1416           0 :     mActiveCachesByGroup.Put(group, new nsCString(clientID));
    1417             : 
    1418           0 :     rv = statement->ExecuteStep(&hasRows);
    1419           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1420             :   }
    1421             : 
    1422           0 :   return NS_OK;
    1423             : }
    1424             : 
    1425             : nsresult
    1426           0 : nsOfflineCacheDevice::Shutdown()
    1427             : {
    1428           0 :   NS_ENSURE_TRUE(mDB, NS_ERROR_NOT_INITIALIZED);
    1429             : 
    1430             :   {
    1431           0 :     MutexAutoLock lock(mLock);
    1432           0 :     for (auto iter = mCaches.Iter(); !iter.Done(); iter.Next()) {
    1433           0 :       nsCOMPtr<nsIApplicationCache> obj = do_QueryReferent(iter.UserData());
    1434           0 :       if (obj) {
    1435           0 :         auto appCache = static_cast<nsApplicationCache*>(obj.get());
    1436           0 :         appCache->MarkInvalid();
    1437             :       }
    1438             :     }
    1439             :   }
    1440             : 
    1441             :   {
    1442           0 :   EvictionObserver evictionObserver(mDB, mEvictionFunction);
    1443             : 
    1444             :   // Delete all rows whose clientID is not an active clientID.
    1445           0 :   nsresult rv = mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1446             :     "DELETE FROM moz_cache WHERE rowid IN"
    1447             :     "  (SELECT moz_cache.rowid FROM"
    1448             :     "    moz_cache LEFT OUTER JOIN moz_cache_groups ON"
    1449             :     "      (moz_cache.ClientID = moz_cache_groups.ActiveClientID)"
    1450           0 :     "   WHERE moz_cache_groups.GroupID ISNULL)"));
    1451             : 
    1452           0 :   if (NS_FAILED(rv))
    1453           0 :     NS_WARNING("Failed to clean up unused application caches.");
    1454             :   else
    1455           0 :     evictionObserver.Apply();
    1456             : 
    1457             :   // Delete all namespaces whose clientID is not an active clientID.
    1458           0 :   rv = mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1459             :     "DELETE FROM moz_cache_namespaces WHERE rowid IN"
    1460             :     "  (SELECT moz_cache_namespaces.rowid FROM"
    1461             :     "    moz_cache_namespaces LEFT OUTER JOIN moz_cache_groups ON"
    1462             :     "      (moz_cache_namespaces.ClientID = moz_cache_groups.ActiveClientID)"
    1463           0 :     "   WHERE moz_cache_groups.GroupID ISNULL)"));
    1464             : 
    1465           0 :   if (NS_FAILED(rv))
    1466           0 :     NS_WARNING("Failed to clean up namespaces.");
    1467             : 
    1468           0 :   mEvictionFunction = nullptr;
    1469             : 
    1470           0 :   mStatement_CacheSize = nullptr;
    1471           0 :   mStatement_ApplicationCacheSize = nullptr;
    1472           0 :   mStatement_EntryCount = nullptr;
    1473           0 :   mStatement_UpdateEntry = nullptr;
    1474           0 :   mStatement_UpdateEntrySize = nullptr;
    1475           0 :   mStatement_DeleteEntry = nullptr;
    1476           0 :   mStatement_FindEntry = nullptr;
    1477           0 :   mStatement_BindEntry = nullptr;
    1478           0 :   mStatement_ClearDomain = nullptr;
    1479           0 :   mStatement_MarkEntry = nullptr;
    1480           0 :   mStatement_UnmarkEntry = nullptr;
    1481           0 :   mStatement_GetTypes = nullptr;
    1482           0 :   mStatement_FindNamespaceEntry = nullptr;
    1483           0 :   mStatement_InsertNamespaceEntry = nullptr;
    1484           0 :   mStatement_CleanupUnmarked = nullptr;
    1485           0 :   mStatement_GatherEntries = nullptr;
    1486           0 :   mStatement_ActivateClient = nullptr;
    1487           0 :   mStatement_DeactivateGroup = nullptr;
    1488           0 :   mStatement_FindClient = nullptr;
    1489           0 :   mStatement_FindClientByNamespace = nullptr;
    1490           0 :   mStatement_EnumerateApps = nullptr;
    1491           0 :   mStatement_EnumerateGroups = nullptr;
    1492           0 :   mStatement_EnumerateGroupsTimeOrder = nullptr;
    1493             :   }
    1494             : 
    1495             :   // Close Database on the correct thread
    1496           0 :   bool isOnCurrentThread = true;
    1497           0 :   if (mInitEventTarget)
    1498           0 :     isOnCurrentThread = mInitEventTarget->IsOnCurrentThread();
    1499             : 
    1500           0 :   if (!isOnCurrentThread) {
    1501           0 :     nsCOMPtr<nsIRunnable> ev = new nsCloseDBEvent(mDB);
    1502             : 
    1503           0 :     if (ev) {
    1504           0 :       mInitEventTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
    1505             :     }
    1506             :   }
    1507             :   else {
    1508           0 :     mDB->Close();
    1509             :   }
    1510             : 
    1511           0 :   mDB = nullptr;
    1512           0 :   mInitEventTarget = nullptr;
    1513             : 
    1514           0 :   return NS_OK;
    1515             : }
    1516             : 
    1517             : const char *
    1518           0 : nsOfflineCacheDevice::GetDeviceID()
    1519             : {
    1520           0 :   return OFFLINE_CACHE_DEVICE_ID;
    1521             : }
    1522             : 
    1523             : nsCacheEntry *
    1524           0 : nsOfflineCacheDevice::FindEntry(nsCString *fullKey, bool *collision)
    1525             : {
    1526           0 :   NS_ENSURE_TRUE(Initialized(), nullptr);
    1527             : 
    1528           0 :   mozilla::Telemetry::AutoTimer<mozilla::Telemetry::CACHE_OFFLINE_SEARCH_2> timer;
    1529           0 :   LOG(("nsOfflineCacheDevice::FindEntry [key=%s]\n", fullKey->get()));
    1530             : 
    1531             :   // SELECT * FROM moz_cache WHERE key = ?
    1532             : 
    1533             :   // Decompose the key into "ClientID" and "Key"
    1534           0 :   nsAutoCString keyBuf;
    1535             :   const char *cid, *key;
    1536           0 :   if (!DecomposeCacheEntryKey(fullKey, &cid, &key, keyBuf))
    1537           0 :     return nullptr;
    1538             : 
    1539           0 :   AutoResetStatement statement(mStatement_FindEntry);
    1540             : 
    1541           0 :   nsresult rv = statement->BindUTF8StringByIndex(0, nsDependentCString(cid));
    1542           0 :   nsresult rv2 = statement->BindUTF8StringByIndex(1, nsDependentCString(key));
    1543           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
    1544           0 :   NS_ENSURE_SUCCESS(rv2, nullptr);
    1545             : 
    1546             :   bool hasRows;
    1547           0 :   rv = statement->ExecuteStep(&hasRows);
    1548           0 :   if (NS_FAILED(rv) || !hasRows)
    1549           0 :     return nullptr; // entry not found
    1550             : 
    1551             :   nsOfflineCacheRecord rec;
    1552           0 :   statement->GetSharedBlob(0, &rec.metaDataLen,
    1553           0 :                            (const uint8_t **) &rec.metaData);
    1554           0 :   rec.generation     = statement->AsInt32(1);
    1555           0 :   rec.dataSize       = statement->AsInt32(2);
    1556           0 :   rec.fetchCount     = statement->AsInt32(3);
    1557           0 :   rec.lastFetched    = statement->AsInt64(4);
    1558           0 :   rec.lastModified   = statement->AsInt64(5);
    1559           0 :   rec.expirationTime = statement->AsInt64(6);
    1560             : 
    1561           0 :   LOG(("entry: [%u %d %d %d %" PRId64 " %" PRId64 " %" PRId64 "]\n",
    1562             :         rec.metaDataLen,
    1563             :         rec.generation,
    1564             :         rec.dataSize,
    1565             :         rec.fetchCount,
    1566             :         rec.lastFetched,
    1567             :         rec.lastModified,
    1568             :         rec.expirationTime));
    1569             : 
    1570           0 :   nsCacheEntry *entry = CreateCacheEntry(this, fullKey, rec);
    1571             : 
    1572           0 :   if (entry)
    1573             :   {
    1574             :     // make sure that the data file exists
    1575           0 :     nsOfflineCacheBinding *binding = (nsOfflineCacheBinding*)entry->Data();
    1576             :     bool isFile;
    1577           0 :     rv = binding->mDataFile->IsFile(&isFile);
    1578           0 :     if (NS_FAILED(rv) || !isFile)
    1579             :     {
    1580           0 :       DeleteEntry(entry, false);
    1581           0 :       delete entry;
    1582           0 :       return nullptr;
    1583             :     }
    1584             : 
    1585             :     // lock the entry
    1586           0 :     Lock(*fullKey);
    1587             :   }
    1588             : 
    1589           0 :   return entry;
    1590             : }
    1591             : 
    1592             : nsresult
    1593           0 : nsOfflineCacheDevice::DeactivateEntry(nsCacheEntry *entry)
    1594             : {
    1595           0 :   LOG(("nsOfflineCacheDevice::DeactivateEntry [key=%s]\n",
    1596             :        entry->Key()->get()));
    1597             : 
    1598             :   // This method is called to inform us that the nsCacheEntry object is going
    1599             :   // away.  We should persist anything that needs to be persisted, or if the
    1600             :   // entry is doomed, we can go ahead and clear its storage.
    1601             : 
    1602           0 :   if (entry->IsDoomed())
    1603             :   {
    1604             :     // remove corresponding row and file if they exist
    1605             : 
    1606             :     // the row should have been removed in DoomEntry... we could assert that
    1607             :     // that happened.  otherwise, all we have to do here is delete the file
    1608             :     // on disk.
    1609           0 :     DeleteData(entry);
    1610             :   }
    1611           0 :   else if (((nsOfflineCacheBinding *)entry->Data())->IsNewEntry())
    1612             :   {
    1613             :     // UPDATE the database row
    1614             : 
    1615             :     // Only new entries are updated, since offline cache is updated in
    1616             :     // transactions.  New entries are those who is returned from
    1617             :     // BindEntry().
    1618             : 
    1619           0 :     LOG(("nsOfflineCacheDevice::DeactivateEntry updating new entry\n"));
    1620           0 :     UpdateEntry(entry);
    1621             :   } else {
    1622           0 :     LOG(("nsOfflineCacheDevice::DeactivateEntry "
    1623             :          "skipping update since entry is not dirty\n"));
    1624             :   }
    1625             : 
    1626             :   // Unlock the entry
    1627           0 :   Unlock(*entry->Key());
    1628             : 
    1629           0 :   delete entry;
    1630             : 
    1631           0 :   return NS_OK;
    1632             : }
    1633             : 
    1634             : nsresult
    1635           0 : nsOfflineCacheDevice::BindEntry(nsCacheEntry *entry)
    1636             : {
    1637           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    1638             : 
    1639           0 :   LOG(("nsOfflineCacheDevice::BindEntry [key=%s]\n", entry->Key()->get()));
    1640             : 
    1641           0 :   NS_ENSURE_STATE(!entry->Data());
    1642             : 
    1643             :   // This method is called to inform us that we have a new entry.  The entry
    1644             :   // may collide with an existing entry in our DB, but if that happens we can
    1645             :   // assume that the entry is not being used.
    1646             : 
    1647             :   // INSERT the database row
    1648             : 
    1649             :   // XXX Assumption: if the row already exists, then FindEntry would have
    1650             :   // returned it.  if that entry was doomed, then DoomEntry would have removed
    1651             :   // it from the table.  so, we should always have to insert at this point.
    1652             : 
    1653             :   // Decompose the key into "ClientID" and "Key"
    1654           0 :   nsAutoCString keyBuf;
    1655             :   const char *cid, *key;
    1656           0 :   if (!DecomposeCacheEntryKey(entry->Key(), &cid, &key, keyBuf))
    1657           0 :     return NS_ERROR_UNEXPECTED;
    1658             : 
    1659             :   // create binding, pick best generation number
    1660             :   RefPtr<nsOfflineCacheBinding> binding =
    1661           0 :       nsOfflineCacheBinding::Create(mCacheDirectory, entry->Key(), -1);
    1662           0 :   if (!binding)
    1663           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1664           0 :   binding->MarkNewEntry();
    1665             : 
    1666             :   nsOfflineCacheRecord rec;
    1667           0 :   rec.clientID = cid;
    1668           0 :   rec.key = key;
    1669           0 :   rec.metaData = nullptr; // don't write any metadata now.
    1670           0 :   rec.metaDataLen = 0;
    1671           0 :   rec.generation = binding->mGeneration;
    1672           0 :   rec.dataSize = 0;
    1673           0 :   rec.fetchCount = entry->FetchCount();
    1674           0 :   rec.lastFetched = PRTimeFromSeconds(entry->LastFetched());
    1675           0 :   rec.lastModified = PRTimeFromSeconds(entry->LastModified());
    1676           0 :   rec.expirationTime = PRTimeFromSeconds(entry->ExpirationTime());
    1677             : 
    1678           0 :   AutoResetStatement statement(mStatement_BindEntry);
    1679             : 
    1680           0 :   nsresult rv = statement->BindUTF8StringByIndex(0, nsDependentCString(rec.clientID));
    1681           0 :   nsresult tmp = statement->BindUTF8StringByIndex(1, nsDependentCString(rec.key));
    1682           0 :   if (NS_FAILED(tmp)) {
    1683           0 :     rv = tmp;
    1684             :   }
    1685           0 :   tmp = statement->BindBlobByIndex(2, rec.metaData, rec.metaDataLen);
    1686           0 :   if (NS_FAILED(tmp)) {
    1687           0 :     rv = tmp;
    1688             :   }
    1689           0 :   tmp = statement->BindInt32ByIndex(3, rec.generation);
    1690           0 :   if (NS_FAILED(tmp)) {
    1691           0 :     rv = tmp;
    1692             :   }
    1693           0 :   tmp = statement->BindInt32ByIndex(4, rec.dataSize);
    1694           0 :   if (NS_FAILED(tmp)) {
    1695           0 :     rv = tmp;
    1696             :   }
    1697           0 :   tmp = statement->BindInt32ByIndex(5, rec.fetchCount);
    1698           0 :   if (NS_FAILED(tmp)) {
    1699           0 :     rv = tmp;
    1700             :   }
    1701           0 :   tmp = statement->BindInt64ByIndex(6, rec.lastFetched);
    1702           0 :   if (NS_FAILED(tmp)) {
    1703           0 :     rv = tmp;
    1704             :   }
    1705           0 :   tmp = statement->BindInt64ByIndex(7, rec.lastModified);
    1706           0 :   if (NS_FAILED(tmp)) {
    1707           0 :     rv = tmp;
    1708             :   }
    1709           0 :   tmp = statement->BindInt64ByIndex(8, rec.expirationTime);
    1710           0 :   if (NS_FAILED(tmp)) {
    1711           0 :     rv = tmp;
    1712             :   }
    1713           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1714             : 
    1715             :   bool hasRows;
    1716           0 :   rv = statement->ExecuteStep(&hasRows);
    1717           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1718           0 :   NS_ASSERTION(!hasRows, "INSERT should not result in output");
    1719             : 
    1720           0 :   entry->SetData(binding);
    1721             : 
    1722             :   // lock the entry
    1723           0 :   Lock(*entry->Key());
    1724             : 
    1725           0 :   return NS_OK;
    1726             : }
    1727             : 
    1728             : void
    1729           0 : nsOfflineCacheDevice::DoomEntry(nsCacheEntry *entry)
    1730             : {
    1731           0 :   LOG(("nsOfflineCacheDevice::DoomEntry [key=%s]\n", entry->Key()->get()));
    1732             : 
    1733             :   // This method is called to inform us that we should mark the entry to be
    1734             :   // deleted when it is no longer in use.
    1735             : 
    1736             :   // We can go ahead and delete the corresponding row in our table,
    1737             :   // but we must not delete the file on disk until we are deactivated.
    1738             :   // In another word, the file should be deleted if the entry had been
    1739             :   // deactivated.
    1740             : 
    1741           0 :   DeleteEntry(entry, !entry->IsActive());
    1742           0 : }
    1743             : 
    1744             : nsresult
    1745           0 : nsOfflineCacheDevice::OpenInputStreamForEntry(nsCacheEntry      *entry,
    1746             :                                               nsCacheAccessMode  mode,
    1747             :                                               uint32_t           offset,
    1748             :                                               nsIInputStream   **result)
    1749             : {
    1750           0 :   LOG(("nsOfflineCacheDevice::OpenInputStreamForEntry [key=%s]\n",
    1751             :        entry->Key()->get()));
    1752             : 
    1753           0 :   *result = nullptr;
    1754             : 
    1755           0 :   NS_ENSURE_TRUE(!offset || (offset < entry->DataSize()), NS_ERROR_INVALID_ARG);
    1756             : 
    1757             :   // return an input stream to the entry's data file.  the stream
    1758             :   // may be read on a background thread.
    1759             : 
    1760           0 :   nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data();
    1761           0 :   NS_ENSURE_STATE(binding);
    1762             : 
    1763           0 :   nsCOMPtr<nsIInputStream> in;
    1764           0 :   NS_NewLocalFileInputStream(getter_AddRefs(in), binding->mDataFile, PR_RDONLY);
    1765           0 :   if (!in)
    1766           0 :     return NS_ERROR_UNEXPECTED;
    1767             : 
    1768             :   // respect |offset| param
    1769           0 :   if (offset != 0)
    1770             :   {
    1771           0 :     nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(in);
    1772           0 :     NS_ENSURE_TRUE(seekable, NS_ERROR_UNEXPECTED);
    1773             : 
    1774           0 :     seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
    1775             :   }
    1776             : 
    1777           0 :   in.swap(*result);
    1778           0 :   return NS_OK;
    1779             : }
    1780             : 
    1781             : nsresult
    1782           0 : nsOfflineCacheDevice::OpenOutputStreamForEntry(nsCacheEntry       *entry,
    1783             :                                                nsCacheAccessMode   mode,
    1784             :                                                uint32_t            offset,
    1785             :                                                nsIOutputStream   **result)
    1786             : {
    1787           0 :   LOG(("nsOfflineCacheDevice::OpenOutputStreamForEntry [key=%s]\n",
    1788             :        entry->Key()->get()));
    1789             : 
    1790           0 :   *result = nullptr;
    1791             : 
    1792           0 :   NS_ENSURE_TRUE(offset <= entry->DataSize(), NS_ERROR_INVALID_ARG);
    1793             : 
    1794             :   // return an output stream to the entry's data file.  we can assume
    1795             :   // that the output stream will only be used on the main thread.
    1796             : 
    1797           0 :   nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data();
    1798           0 :   NS_ENSURE_STATE(binding);
    1799             : 
    1800           0 :   nsCOMPtr<nsIOutputStream> out;
    1801           0 :   NS_NewLocalFileOutputStream(getter_AddRefs(out), binding->mDataFile,
    1802             :                               PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
    1803           0 :                               00600);
    1804           0 :   if (!out)
    1805           0 :     return NS_ERROR_UNEXPECTED;
    1806             : 
    1807             :   // respect |offset| param
    1808           0 :   nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(out);
    1809           0 :   NS_ENSURE_TRUE(seekable, NS_ERROR_UNEXPECTED);
    1810           0 :   if (offset != 0)
    1811           0 :     seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
    1812             : 
    1813             :   // truncate the file at the given offset
    1814           0 :   seekable->SetEOF();
    1815             : 
    1816           0 :   nsCOMPtr<nsIOutputStream> bufferedOut;
    1817             :   nsresult rv =
    1818           0 :     NS_NewBufferedOutputStream(getter_AddRefs(bufferedOut), out, 16 * 1024);
    1819           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1820             : 
    1821           0 :   bufferedOut.swap(*result);
    1822           0 :   return NS_OK;
    1823             : }
    1824             : 
    1825             : nsresult
    1826           0 : nsOfflineCacheDevice::GetFileForEntry(nsCacheEntry *entry, nsIFile **result)
    1827             : {
    1828           0 :   LOG(("nsOfflineCacheDevice::GetFileForEntry [key=%s]\n",
    1829             :        entry->Key()->get()));
    1830             : 
    1831           0 :   nsOfflineCacheBinding *binding = (nsOfflineCacheBinding *) entry->Data();
    1832           0 :   NS_ENSURE_STATE(binding);
    1833             : 
    1834           0 :   NS_IF_ADDREF(*result = binding->mDataFile);
    1835           0 :   return NS_OK;
    1836             : }
    1837             : 
    1838             : nsresult
    1839           0 : nsOfflineCacheDevice::OnDataSizeChange(nsCacheEntry *entry, int32_t deltaSize)
    1840             : {
    1841           0 :   LOG(("nsOfflineCacheDevice::OnDataSizeChange [key=%s delta=%d]\n",
    1842             :       entry->Key()->get(), deltaSize));
    1843             : 
    1844           0 :   const int32_t DELTA_THRESHOLD = 1<<14; // 16k
    1845             : 
    1846             :   // called to notify us of an impending change in the total size of the
    1847             :   // specified entry.
    1848             : 
    1849           0 :   uint32_t oldSize = entry->DataSize();
    1850           0 :   NS_ASSERTION(deltaSize >= 0 || int32_t(oldSize) + deltaSize >= 0, "oops");
    1851           0 :   uint32_t newSize = int32_t(oldSize) + deltaSize;
    1852           0 :   UpdateEntrySize(entry, newSize);
    1853             : 
    1854           0 :   mDeltaCounter += deltaSize; // this may go negative
    1855             : 
    1856           0 :   if (mDeltaCounter >= DELTA_THRESHOLD)
    1857             :   {
    1858           0 :     if (CacheSize() > mCacheCapacity) {
    1859             :       // the entry will overrun the cache capacity, doom the entry
    1860             :       // and abort
    1861             : #ifdef DEBUG
    1862             :       nsresult rv =
    1863             : #endif
    1864           0 :         nsCacheService::DoomEntry(entry);
    1865           0 :       NS_ASSERTION(NS_SUCCEEDED(rv), "DoomEntry() failed.");
    1866           0 :       return NS_ERROR_ABORT;
    1867             :     }
    1868             : 
    1869           0 :     mDeltaCounter = 0; // reset counter
    1870             :   }
    1871             : 
    1872           0 :   return NS_OK;
    1873             : }
    1874             : 
    1875             : nsresult
    1876           0 : nsOfflineCacheDevice::Visit(nsICacheVisitor *visitor)
    1877             : {
    1878           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    1879             : 
    1880             :   // called to enumerate the offline cache.
    1881             : 
    1882             :   nsCOMPtr<nsICacheDeviceInfo> deviceInfo =
    1883           0 :       new nsOfflineCacheDeviceInfo(this);
    1884             : 
    1885             :   bool keepGoing;
    1886           0 :   nsresult rv = visitor->VisitDevice(OFFLINE_CACHE_DEVICE_ID, deviceInfo,
    1887           0 :                                      &keepGoing);
    1888           0 :   if (NS_FAILED(rv))
    1889           0 :     return rv;
    1890             : 
    1891           0 :   if (!keepGoing)
    1892           0 :     return NS_OK;
    1893             : 
    1894             :   // SELECT * from moz_cache;
    1895             : 
    1896             :   nsOfflineCacheRecord rec;
    1897           0 :   RefPtr<nsOfflineCacheEntryInfo> info = new nsOfflineCacheEntryInfo;
    1898           0 :   if (!info)
    1899           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1900           0 :   info->mRec = &rec;
    1901             : 
    1902             :   // XXX may want to list columns explicitly
    1903           0 :   nsCOMPtr<mozIStorageStatement> statement;
    1904           0 :   rv = mDB->CreateStatement(
    1905           0 :       NS_LITERAL_CSTRING("SELECT * FROM moz_cache;"),
    1906           0 :       getter_AddRefs(statement));
    1907           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1908             : 
    1909             :   bool hasRows;
    1910             :   for (;;)
    1911             :   {
    1912           0 :     rv = statement->ExecuteStep(&hasRows);
    1913           0 :     if (NS_FAILED(rv) || !hasRows)
    1914           0 :       break;
    1915             : 
    1916           0 :     statement->GetSharedUTF8String(0, nullptr, &rec.clientID);
    1917           0 :     statement->GetSharedUTF8String(1, nullptr, &rec.key);
    1918           0 :     statement->GetSharedBlob(2, &rec.metaDataLen,
    1919           0 :                              (const uint8_t **) &rec.metaData);
    1920           0 :     rec.generation     = statement->AsInt32(3);
    1921           0 :     rec.dataSize       = statement->AsInt32(4);
    1922           0 :     rec.fetchCount     = statement->AsInt32(5);
    1923           0 :     rec.lastFetched    = statement->AsInt64(6);
    1924           0 :     rec.lastModified   = statement->AsInt64(7);
    1925           0 :     rec.expirationTime = statement->AsInt64(8);
    1926             : 
    1927             :     bool keepGoing;
    1928           0 :     rv = visitor->VisitEntry(OFFLINE_CACHE_DEVICE_ID, info, &keepGoing);
    1929           0 :     if (NS_FAILED(rv) || !keepGoing)
    1930           0 :       break;
    1931           0 :   }
    1932             : 
    1933           0 :   info->mRec = nullptr;
    1934           0 :   return NS_OK;
    1935             : }
    1936             : 
    1937             : nsresult
    1938           0 : nsOfflineCacheDevice::EvictEntries(const char *clientID)
    1939             : {
    1940           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    1941             : 
    1942           0 :   LOG(("nsOfflineCacheDevice::EvictEntries [cid=%s]\n",
    1943             :        clientID ? clientID : ""));
    1944             : 
    1945             :   // called to evict all entries matching the given clientID.
    1946             : 
    1947             :   // need trigger to fire user defined function after a row is deleted
    1948             :   // so we can delete the corresponding data file.
    1949           0 :   EvictionObserver evictionObserver(mDB, mEvictionFunction);
    1950             : 
    1951           0 :   nsCOMPtr<mozIStorageStatement> statement;
    1952             :   nsresult rv;
    1953           0 :   if (clientID)
    1954             :   {
    1955           0 :     rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache WHERE ClientID=?;"),
    1956           0 :                               getter_AddRefs(statement));
    1957           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1958             : 
    1959           0 :     rv = statement->BindUTF8StringByIndex(0, nsDependentCString(clientID));
    1960           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1961             : 
    1962           0 :     rv = statement->Execute();
    1963           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1964             : 
    1965           0 :     rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache_groups WHERE ActiveClientID=?;"),
    1966           0 :                               getter_AddRefs(statement));
    1967           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1968             : 
    1969           0 :     rv = statement->BindUTF8StringByIndex(0, nsDependentCString(clientID));
    1970           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1971             : 
    1972           0 :     rv = statement->Execute();
    1973           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1974             : 
    1975             :     // TODO - Should update internal hashtables.
    1976             :     // Low priority, since this API is not widely used.
    1977             :   }
    1978             :   else
    1979             :   {
    1980           0 :     rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache;"),
    1981           0 :                               getter_AddRefs(statement));
    1982           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1983             : 
    1984           0 :     rv = statement->Execute();
    1985           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1986             : 
    1987           0 :     rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache_groups;"),
    1988           0 :                               getter_AddRefs(statement));
    1989           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1990             : 
    1991           0 :     rv = statement->Execute();
    1992           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1993             : 
    1994           0 :     MutexAutoLock lock(mLock);
    1995           0 :     mCaches.Clear();
    1996           0 :     mActiveCaches.Clear();
    1997           0 :     mActiveCachesByGroup.Clear();
    1998             :   }
    1999             : 
    2000           0 :   evictionObserver.Apply();
    2001             : 
    2002           0 :   statement = nullptr;
    2003             :   // Also evict any namespaces associated with this clientID.
    2004           0 :   if (clientID)
    2005             :   {
    2006           0 :     rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache_namespaces WHERE ClientID=?"),
    2007           0 :                               getter_AddRefs(statement));
    2008           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2009             : 
    2010           0 :     rv = statement->BindUTF8StringByIndex(0, nsDependentCString(clientID));
    2011           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2012             :   }
    2013             :   else
    2014             :   {
    2015           0 :     rv = mDB->CreateStatement(NS_LITERAL_CSTRING("DELETE FROM moz_cache_namespaces;"),
    2016           0 :                               getter_AddRefs(statement));
    2017           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2018             :   }
    2019             : 
    2020           0 :   rv = statement->Execute();
    2021           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2022             : 
    2023           0 :   return NS_OK;
    2024             : }
    2025             : 
    2026             : nsresult
    2027           0 : nsOfflineCacheDevice::MarkEntry(const nsCString &clientID,
    2028             :                                 const nsACString &key,
    2029             :                                 uint32_t typeBits)
    2030             : {
    2031           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    2032             : 
    2033           0 :   LOG(("nsOfflineCacheDevice::MarkEntry [cid=%s, key=%s, typeBits=%d]\n",
    2034             :        clientID.get(), PromiseFlatCString(key).get(), typeBits));
    2035             : 
    2036           0 :   AutoResetStatement statement(mStatement_MarkEntry);
    2037           0 :   nsresult rv = statement->BindInt32ByIndex(0, typeBits);
    2038           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2039           0 :   rv = statement->BindUTF8StringByIndex(1, clientID);
    2040           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2041           0 :   rv = statement->BindUTF8StringByIndex(2, key);
    2042           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2043             : 
    2044           0 :   rv = statement->Execute();
    2045           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2046             : 
    2047           0 :   return NS_OK;
    2048             : }
    2049             : 
    2050             : nsresult
    2051           0 : nsOfflineCacheDevice::UnmarkEntry(const nsCString &clientID,
    2052             :                                   const nsACString &key,
    2053             :                                   uint32_t typeBits)
    2054             : {
    2055           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    2056             : 
    2057           0 :   LOG(("nsOfflineCacheDevice::UnmarkEntry [cid=%s, key=%s, typeBits=%d]\n",
    2058             :        clientID.get(), PromiseFlatCString(key).get(), typeBits));
    2059             : 
    2060           0 :   AutoResetStatement statement(mStatement_UnmarkEntry);
    2061           0 :   nsresult rv = statement->BindInt32ByIndex(0, typeBits);
    2062           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2063           0 :   rv = statement->BindUTF8StringByIndex(1, clientID);
    2064           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2065           0 :   rv = statement->BindUTF8StringByIndex(2, key);
    2066           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2067             : 
    2068           0 :   rv = statement->Execute();
    2069           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2070             : 
    2071             :   // Remove the entry if it is now empty.
    2072             : 
    2073           0 :   EvictionObserver evictionObserver(mDB, mEvictionFunction);
    2074             : 
    2075           0 :   AutoResetStatement cleanupStatement(mStatement_CleanupUnmarked);
    2076           0 :   rv = cleanupStatement->BindUTF8StringByIndex(0, clientID);
    2077           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2078           0 :   rv = cleanupStatement->BindUTF8StringByIndex(1, key);
    2079           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2080             : 
    2081           0 :   rv = cleanupStatement->Execute();
    2082           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2083             : 
    2084           0 :   evictionObserver.Apply();
    2085             : 
    2086           0 :   return NS_OK;
    2087             : }
    2088             : 
    2089             : nsresult
    2090           0 : nsOfflineCacheDevice::GetMatchingNamespace(const nsCString &clientID,
    2091             :                                            const nsACString &key,
    2092             :                                            nsIApplicationCacheNamespace **out)
    2093             : {
    2094           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    2095             : 
    2096           0 :   LOG(("nsOfflineCacheDevice::GetMatchingNamespace [cid=%s, key=%s]\n",
    2097             :        clientID.get(), PromiseFlatCString(key).get()));
    2098             : 
    2099             :   nsresult rv;
    2100             : 
    2101           0 :   AutoResetStatement statement(mStatement_FindNamespaceEntry);
    2102             : 
    2103           0 :   rv = statement->BindUTF8StringByIndex(0, clientID);
    2104           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2105           0 :   rv = statement->BindUTF8StringByIndex(1, key);
    2106           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2107             : 
    2108             :   bool hasRows;
    2109           0 :   rv = statement->ExecuteStep(&hasRows);
    2110           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2111             : 
    2112           0 :   *out = nullptr;
    2113             : 
    2114           0 :   bool found = false;
    2115           0 :   nsCString nsSpec;
    2116           0 :   int32_t nsType = 0;
    2117           0 :   nsCString nsData;
    2118             : 
    2119           0 :   while (hasRows)
    2120             :   {
    2121             :     int32_t itemType;
    2122           0 :     rv = statement->GetInt32(2, &itemType);
    2123           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2124             : 
    2125           0 :     if (!found || itemType > nsType)
    2126             :     {
    2127           0 :       nsType = itemType;
    2128             : 
    2129           0 :       rv = statement->GetUTF8String(0, nsSpec);
    2130           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2131             : 
    2132           0 :       rv = statement->GetUTF8String(1, nsData);
    2133           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2134             : 
    2135           0 :       found = true;
    2136             :     }
    2137             : 
    2138           0 :     rv = statement->ExecuteStep(&hasRows);
    2139           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2140             :   }
    2141             : 
    2142           0 :   if (found) {
    2143             :     nsCOMPtr<nsIApplicationCacheNamespace> ns =
    2144           0 :       new nsApplicationCacheNamespace();
    2145           0 :     if (!ns)
    2146           0 :       return NS_ERROR_OUT_OF_MEMORY;
    2147           0 :     rv = ns->Init(nsType, nsSpec, nsData);
    2148           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2149             : 
    2150           0 :     ns.swap(*out);
    2151             :   }
    2152             : 
    2153           0 :   return NS_OK;
    2154             : }
    2155             : 
    2156             : nsresult
    2157           0 : nsOfflineCacheDevice::CacheOpportunistically(const nsCString &clientID,
    2158             :                                              const nsACString &key)
    2159             : {
    2160             :   // XXX: We should also be propagating this cache entry to other matching
    2161             :   // caches.  See bug 444807.
    2162             : 
    2163           0 :   return MarkEntry(clientID, key, nsIApplicationCache::ITEM_OPPORTUNISTIC);
    2164             : }
    2165             : 
    2166             : nsresult
    2167           0 : nsOfflineCacheDevice::GetTypes(const nsCString &clientID,
    2168             :                                const nsACString &key,
    2169             :                                uint32_t *typeBits)
    2170             : {
    2171           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    2172             : 
    2173           0 :   LOG(("nsOfflineCacheDevice::GetTypes [cid=%s, key=%s]\n",
    2174             :        clientID.get(), PromiseFlatCString(key).get()));
    2175             : 
    2176           0 :   AutoResetStatement statement(mStatement_GetTypes);
    2177           0 :   nsresult rv = statement->BindUTF8StringByIndex(0, clientID);
    2178           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2179           0 :   rv = statement->BindUTF8StringByIndex(1, key);
    2180           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2181             : 
    2182             :   bool hasRows;
    2183           0 :   rv = statement->ExecuteStep(&hasRows);
    2184           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2185             : 
    2186           0 :   if (!hasRows)
    2187           0 :     return NS_ERROR_CACHE_KEY_NOT_FOUND;
    2188             : 
    2189           0 :   *typeBits = statement->AsInt32(0);
    2190             : 
    2191           0 :   return NS_OK;
    2192             : }
    2193             : 
    2194             : nsresult
    2195           0 : nsOfflineCacheDevice::GatherEntries(const nsCString &clientID,
    2196             :                                     uint32_t typeBits,
    2197             :                                     uint32_t *count,
    2198             :                                     char ***keys)
    2199             : {
    2200           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    2201             : 
    2202           0 :   LOG(("nsOfflineCacheDevice::GatherEntries [cid=%s, typeBits=%X]\n",
    2203             :        clientID.get(), typeBits));
    2204             : 
    2205           0 :   AutoResetStatement statement(mStatement_GatherEntries);
    2206           0 :   nsresult rv = statement->BindUTF8StringByIndex(0, clientID);
    2207           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2208             : 
    2209           0 :   rv = statement->BindInt32ByIndex(1, typeBits);
    2210           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2211             : 
    2212           0 :   return RunSimpleQuery(mStatement_GatherEntries, 0, count, keys);
    2213             : }
    2214             : 
    2215             : nsresult
    2216           0 : nsOfflineCacheDevice::AddNamespace(const nsCString &clientID,
    2217             :                                    nsIApplicationCacheNamespace *ns)
    2218             : {
    2219           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    2220             : 
    2221           0 :   nsCString namespaceSpec;
    2222           0 :   nsresult rv = ns->GetNamespaceSpec(namespaceSpec);
    2223           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2224             : 
    2225           0 :   nsCString data;
    2226           0 :   rv = ns->GetData(data);
    2227           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2228             : 
    2229             :   uint32_t itemType;
    2230           0 :   rv = ns->GetItemType(&itemType);
    2231           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2232             : 
    2233           0 :   LOG(("nsOfflineCacheDevice::AddNamespace [cid=%s, ns=%s, data=%s, type=%d]",
    2234             :        clientID.get(), namespaceSpec.get(), data.get(), itemType));
    2235             : 
    2236           0 :   AutoResetStatement statement(mStatement_InsertNamespaceEntry);
    2237             : 
    2238           0 :   rv = statement->BindUTF8StringByIndex(0, clientID);
    2239           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2240             : 
    2241           0 :   rv = statement->BindUTF8StringByIndex(1, namespaceSpec);
    2242           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2243             : 
    2244           0 :   rv = statement->BindUTF8StringByIndex(2, data);
    2245           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2246             : 
    2247           0 :   rv = statement->BindInt32ByIndex(3, itemType);
    2248           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2249             : 
    2250           0 :   rv = statement->Execute();
    2251           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2252             : 
    2253           0 :   return NS_OK;
    2254             : }
    2255             : 
    2256             : nsresult
    2257           0 : nsOfflineCacheDevice::GetUsage(const nsACString &clientID,
    2258             :                                uint32_t *usage)
    2259             : {
    2260           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    2261             : 
    2262           0 :   LOG(("nsOfflineCacheDevice::GetUsage [cid=%s]\n",
    2263             :        PromiseFlatCString(clientID).get()));
    2264             : 
    2265           0 :   *usage = 0;
    2266             : 
    2267           0 :   AutoResetStatement statement(mStatement_ApplicationCacheSize);
    2268             : 
    2269           0 :   nsresult rv = statement->BindUTF8StringByIndex(0, clientID);
    2270           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2271             : 
    2272             :   bool hasRows;
    2273           0 :   rv = statement->ExecuteStep(&hasRows);
    2274           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2275             : 
    2276           0 :   if (!hasRows)
    2277           0 :     return NS_OK;
    2278             : 
    2279           0 :   *usage = static_cast<uint32_t>(statement->AsInt32(0));
    2280             : 
    2281           0 :   return NS_OK;
    2282             : }
    2283             : 
    2284             : nsresult
    2285           0 : nsOfflineCacheDevice::GetGroups(uint32_t *count,
    2286             :                                  char ***keys)
    2287             : {
    2288           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    2289             : 
    2290           0 :   LOG(("nsOfflineCacheDevice::GetGroups"));
    2291             : 
    2292           0 :   return RunSimpleQuery(mStatement_EnumerateGroups, 0, count, keys);
    2293             : }
    2294             : 
    2295             : nsresult
    2296           0 : nsOfflineCacheDevice::GetGroupsTimeOrdered(uint32_t *count,
    2297             :                                            char ***keys)
    2298             : {
    2299           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    2300             : 
    2301           0 :   LOG(("nsOfflineCacheDevice::GetGroupsTimeOrder"));
    2302             : 
    2303           0 :   return RunSimpleQuery(mStatement_EnumerateGroupsTimeOrder, 0, count, keys);
    2304             : }
    2305             : 
    2306             : bool
    2307           0 : nsOfflineCacheDevice::IsLocked(const nsACString &key)
    2308             : {
    2309           0 :   MutexAutoLock lock(mLock);
    2310           0 :   return mLockedEntries.GetEntry(key);
    2311             : }
    2312             : 
    2313             : void
    2314           0 : nsOfflineCacheDevice::Lock(const nsACString &key)
    2315             : {
    2316           0 :   MutexAutoLock lock(mLock);
    2317           0 :   mLockedEntries.PutEntry(key);
    2318           0 : }
    2319             : 
    2320             : void
    2321           0 : nsOfflineCacheDevice::Unlock(const nsACString &key)
    2322             : {
    2323           0 :   MutexAutoLock lock(mLock);
    2324           0 :   mLockedEntries.RemoveEntry(key);
    2325           0 : }
    2326             : 
    2327             : nsresult
    2328           0 : nsOfflineCacheDevice::RunSimpleQuery(mozIStorageStatement * statement,
    2329             :                                      uint32_t resultIndex,
    2330             :                                      uint32_t * count,
    2331             :                                      char *** values)
    2332             : {
    2333             :   bool hasRows;
    2334           0 :   nsresult rv = statement->ExecuteStep(&hasRows);
    2335           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2336             : 
    2337           0 :   nsTArray<nsCString> valArray;
    2338           0 :   while (hasRows)
    2339             :   {
    2340             :     uint32_t length;
    2341             :     valArray.AppendElement(
    2342           0 :       nsDependentCString(statement->AsSharedUTF8String(resultIndex, &length)));
    2343             : 
    2344           0 :     rv = statement->ExecuteStep(&hasRows);
    2345           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2346             :   }
    2347             : 
    2348           0 :   *count = valArray.Length();
    2349           0 :   char **ret = static_cast<char **>(moz_xmalloc(*count * sizeof(char*)));
    2350           0 :   if (!ret) return NS_ERROR_OUT_OF_MEMORY;
    2351             : 
    2352           0 :   for (uint32_t i = 0; i <  *count; i++) {
    2353           0 :     ret[i] = NS_strdup(valArray[i].get());
    2354           0 :     if (!ret[i]) {
    2355           0 :       NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, ret);
    2356           0 :       return NS_ERROR_OUT_OF_MEMORY;
    2357             :     }
    2358             :   }
    2359             : 
    2360           0 :   *values = ret;
    2361             : 
    2362           0 :   return NS_OK;
    2363             : }
    2364             : 
    2365             : nsresult
    2366           0 : nsOfflineCacheDevice::CreateApplicationCache(const nsACString &group,
    2367             :                                              nsIApplicationCache **out)
    2368             : {
    2369           0 :   *out = nullptr;
    2370             : 
    2371           0 :   nsCString clientID;
    2372             :   // Some characters are special in the clientID.  Escape the groupID
    2373             :   // before putting it in to the client key.
    2374           0 :   if (!NS_Escape(nsCString(group), clientID, url_Path)) {
    2375           0 :     return NS_ERROR_OUT_OF_MEMORY;
    2376             :   }
    2377             : 
    2378           0 :   PRTime now = PR_Now();
    2379             : 
    2380             :   // Include the timestamp to guarantee uniqueness across runs, and
    2381             :   // the gNextTemporaryClientID for uniqueness within a second.
    2382           0 :   clientID.Append(nsPrintfCString("|%016" PRId64 "|%d",
    2383             :                                   now / PR_USEC_PER_SEC,
    2384           0 :                                   gNextTemporaryClientID++));
    2385             : 
    2386             :   nsCOMPtr<nsIApplicationCache> cache = new nsApplicationCache(this,
    2387             :                                                                group,
    2388           0 :                                                                clientID);
    2389           0 :   if (!cache)
    2390           0 :     return NS_ERROR_OUT_OF_MEMORY;
    2391             : 
    2392           0 :   nsCOMPtr<nsIWeakReference> weak = do_GetWeakReference(cache);
    2393           0 :   if (!weak)
    2394           0 :     return NS_ERROR_OUT_OF_MEMORY;
    2395             : 
    2396           0 :   MutexAutoLock lock(mLock);
    2397           0 :   mCaches.Put(clientID, weak);
    2398             : 
    2399           0 :   cache.swap(*out);
    2400             : 
    2401           0 :   return NS_OK;
    2402             : }
    2403             : 
    2404             : nsresult
    2405           0 : nsOfflineCacheDevice::GetApplicationCache(const nsACString &clientID,
    2406             :                                           nsIApplicationCache **out)
    2407             : {
    2408           0 :   MutexAutoLock lock(mLock);
    2409           0 :   return GetApplicationCache_Unlocked(clientID, out);
    2410             : }
    2411             : 
    2412             : nsresult
    2413           0 : nsOfflineCacheDevice::GetApplicationCache_Unlocked(const nsACString &clientID,
    2414             :                                                    nsIApplicationCache **out)
    2415             : {
    2416           0 :   *out = nullptr;
    2417             : 
    2418           0 :   nsCOMPtr<nsIApplicationCache> cache;
    2419             : 
    2420           0 :   nsWeakPtr weak;
    2421           0 :   if (mCaches.Get(clientID, getter_AddRefs(weak)))
    2422           0 :     cache = do_QueryReferent(weak);
    2423             : 
    2424           0 :   if (!cache)
    2425             :   {
    2426           0 :     nsCString group;
    2427           0 :     nsresult rv = GetGroupForCache(clientID, group);
    2428           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2429             : 
    2430           0 :     if (group.IsEmpty()) {
    2431           0 :       return NS_OK;
    2432             :     }
    2433             : 
    2434           0 :     cache = new nsApplicationCache(this, group, clientID);
    2435           0 :     weak = do_GetWeakReference(cache);
    2436           0 :     if (!weak)
    2437           0 :       return NS_ERROR_OUT_OF_MEMORY;
    2438             : 
    2439           0 :     mCaches.Put(clientID, weak);
    2440             :   }
    2441             : 
    2442           0 :   cache.swap(*out);
    2443             : 
    2444           0 :   return NS_OK;
    2445             : }
    2446             : 
    2447             : nsresult
    2448           0 : nsOfflineCacheDevice::GetActiveCache(const nsACString &group,
    2449             :                                      nsIApplicationCache **out)
    2450             : {
    2451           0 :   *out = nullptr;
    2452             : 
    2453           0 :   MutexAutoLock lock(mLock);
    2454             : 
    2455             :   nsCString *clientID;
    2456           0 :   if (mActiveCachesByGroup.Get(group, &clientID))
    2457           0 :     return GetApplicationCache_Unlocked(*clientID, out);
    2458             : 
    2459           0 :   return NS_OK;
    2460             : }
    2461             : 
    2462             : nsresult
    2463           0 : nsOfflineCacheDevice::DeactivateGroup(const nsACString &group)
    2464             : {
    2465           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    2466             : 
    2467           0 :   nsCString *active = nullptr;
    2468             : 
    2469           0 :   AutoResetStatement statement(mStatement_DeactivateGroup);
    2470           0 :   nsresult rv = statement->BindUTF8StringByIndex(0, group);
    2471           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2472             : 
    2473           0 :   rv = statement->Execute();
    2474           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2475             : 
    2476           0 :   MutexAutoLock lock(mLock);
    2477             : 
    2478           0 :   if (mActiveCachesByGroup.Get(group, &active))
    2479             :   {
    2480           0 :     mActiveCaches.RemoveEntry(*active);
    2481           0 :     mActiveCachesByGroup.Remove(group);
    2482           0 :     active = nullptr;
    2483             :   }
    2484             : 
    2485           0 :   return NS_OK;
    2486             : }
    2487             : 
    2488             : nsresult
    2489           0 : nsOfflineCacheDevice::Evict(nsILoadContextInfo *aInfo)
    2490             : {
    2491           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    2492             : 
    2493           0 :   NS_ENSURE_ARG(aInfo);
    2494             : 
    2495             :   nsresult rv;
    2496             : 
    2497           0 :   mozilla::OriginAttributes const *oa = aInfo->OriginAttributesPtr();
    2498             : 
    2499           0 :   if (oa->mInIsolatedMozBrowser == false) {
    2500           0 :     nsCOMPtr<nsICacheService> serv = do_GetService(kCacheServiceCID, &rv);
    2501           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2502             : 
    2503           0 :     return nsCacheService::GlobalInstance()->EvictEntriesInternal(nsICache::STORE_OFFLINE);
    2504             :   }
    2505             : 
    2506           0 :   nsAutoCString jaridsuffix;
    2507           0 :   jaridsuffix.Append('%');
    2508             : 
    2509           0 :   nsAutoCString suffix;
    2510           0 :   oa->CreateSuffix(suffix);
    2511           0 :   jaridsuffix.Append('#');
    2512           0 :   jaridsuffix.Append(suffix);
    2513             : 
    2514           0 :   AutoResetStatement statement(mStatement_EnumerateApps);
    2515           0 :   rv = statement->BindUTF8StringByIndex(0, jaridsuffix);
    2516           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2517             : 
    2518             :   bool hasRows;
    2519           0 :   rv = statement->ExecuteStep(&hasRows);
    2520           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2521             : 
    2522           0 :   while (hasRows) {
    2523           0 :     nsAutoCString group;
    2524           0 :     rv = statement->GetUTF8String(0, group);
    2525           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2526             : 
    2527           0 :     nsCString clientID;
    2528           0 :     rv = statement->GetUTF8String(1, clientID);
    2529           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2530             : 
    2531             :     nsCOMPtr<nsIRunnable> ev =
    2532           0 :       new nsOfflineCacheDiscardCache(this, group, clientID);
    2533             : 
    2534           0 :     rv = nsCacheService::DispatchToCacheIOThread(ev);
    2535           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2536             : 
    2537           0 :     rv = statement->ExecuteStep(&hasRows);
    2538           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2539             :   }
    2540             : 
    2541           0 :   return NS_OK;
    2542             : }
    2543             : 
    2544             : namespace { // anon
    2545             : 
    2546             : class OriginMatch final : public mozIStorageFunction
    2547             : {
    2548           0 :   ~OriginMatch() {}
    2549             :   mozilla::OriginAttributesPattern const mPattern;
    2550             : 
    2551             :   NS_DECL_ISUPPORTS
    2552             :   NS_DECL_MOZISTORAGEFUNCTION
    2553           0 :   explicit OriginMatch(mozilla::OriginAttributesPattern const &aPattern)
    2554           0 :     : mPattern(aPattern) {}
    2555             : };
    2556             : 
    2557           0 : NS_IMPL_ISUPPORTS(OriginMatch, mozIStorageFunction)
    2558             : 
    2559             : NS_IMETHODIMP
    2560           0 : OriginMatch::OnFunctionCall(mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult)
    2561             : {
    2562             :   nsresult rv;
    2563             : 
    2564           0 :   nsAutoCString groupId;
    2565           0 :   rv = aFunctionArguments->GetUTF8String(0, groupId);
    2566           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2567             : 
    2568           0 :   int32_t hash = groupId.Find(NS_LITERAL_CSTRING("#"));
    2569           0 :   if (hash == kNotFound) {
    2570             :     // Just ignore...
    2571           0 :     return NS_OK;
    2572             :   }
    2573             : 
    2574           0 :   ++hash;
    2575             : 
    2576           0 :   nsDependentCSubstring suffix(groupId.BeginReading() + hash, groupId.Length() - hash);
    2577             : 
    2578           0 :   mozilla::OriginAttributes oa;
    2579           0 :   bool ok = oa.PopulateFromSuffix(suffix);
    2580           0 :   NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
    2581             : 
    2582           0 :   bool match = mPattern.Matches(oa);
    2583             : 
    2584           0 :   RefPtr<nsVariant> outVar(new nsVariant());
    2585           0 :   rv = outVar->SetAsUint32(match ? 1 : 0);
    2586           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2587             : 
    2588           0 :   outVar.forget(aResult);
    2589           0 :   return NS_OK;
    2590             : }
    2591             : 
    2592             : } // anon
    2593             : 
    2594             : nsresult
    2595           0 : nsOfflineCacheDevice::Evict(mozilla::OriginAttributesPattern const &aPattern)
    2596             : {
    2597           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    2598             : 
    2599             :   nsresult rv;
    2600             : 
    2601           0 :   nsCOMPtr<mozIStorageFunction> function1(new OriginMatch(aPattern));
    2602           0 :   rv = mDB->CreateFunction(NS_LITERAL_CSTRING("ORIGIN_MATCH"), 1, function1);
    2603           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2604             : 
    2605             :   class AutoRemoveFunc {
    2606             :   public:
    2607             :     mozIStorageConnection* mDB;
    2608           0 :     explicit AutoRemoveFunc(mozIStorageConnection* aDB) : mDB(aDB) {}
    2609           0 :     ~AutoRemoveFunc() {
    2610           0 :       mDB->RemoveFunction(NS_LITERAL_CSTRING("ORIGIN_MATCH"));
    2611           0 :     }
    2612             :   };
    2613           0 :   AutoRemoveFunc autoRemove(mDB);
    2614             : 
    2615           0 :   nsCOMPtr<mozIStorageStatement> statement;
    2616           0 :   rv = mDB->CreateStatement(
    2617           0 :     NS_LITERAL_CSTRING("SELECT GroupID, ActiveClientID FROM moz_cache_groups WHERE ORIGIN_MATCH(GroupID);"),
    2618           0 :     getter_AddRefs(statement));
    2619           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2620             : 
    2621           0 :   AutoResetStatement statementScope(statement);
    2622             : 
    2623             :   bool hasRows;
    2624           0 :   rv = statement->ExecuteStep(&hasRows);
    2625           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2626             : 
    2627           0 :   while (hasRows) {
    2628           0 :     nsAutoCString group;
    2629           0 :     rv = statement->GetUTF8String(0, group);
    2630           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2631             : 
    2632           0 :     nsCString clientID;
    2633           0 :     rv = statement->GetUTF8String(1, clientID);
    2634           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2635             : 
    2636             :     nsCOMPtr<nsIRunnable> ev =
    2637           0 :       new nsOfflineCacheDiscardCache(this, group, clientID);
    2638             : 
    2639           0 :     rv = nsCacheService::DispatchToCacheIOThread(ev);
    2640           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2641             : 
    2642           0 :     rv = statement->ExecuteStep(&hasRows);
    2643           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2644             :   }
    2645             : 
    2646           0 :   return NS_OK;
    2647             : }
    2648             : 
    2649             : bool
    2650           0 : nsOfflineCacheDevice::CanUseCache(nsIURI *keyURI,
    2651             :                                   const nsACString &clientID,
    2652             :                                   nsILoadContextInfo *loadContextInfo)
    2653             : {
    2654             :   {
    2655           0 :     MutexAutoLock lock(mLock);
    2656           0 :     if (!mActiveCaches.Contains(clientID))
    2657           0 :       return false;
    2658             :   }
    2659             : 
    2660           0 :   nsAutoCString groupID;
    2661           0 :   nsresult rv = GetGroupForCache(clientID, groupID);
    2662           0 :   NS_ENSURE_SUCCESS(rv, false);
    2663             : 
    2664           0 :   nsCOMPtr<nsIURI> groupURI;
    2665           0 :   rv = NS_NewURI(getter_AddRefs(groupURI), groupID);
    2666           0 :   if (NS_FAILED(rv)) {
    2667           0 :     return false;
    2668             :   }
    2669             : 
    2670             :   // When we are choosing an initial cache to load the top
    2671             :   // level document from, the URL of that document must have
    2672             :   // the same origin as the manifest, according to the spec.
    2673             :   // The following check is here because explicit, fallback
    2674             :   // and dynamic entries might have origin different from the
    2675             :   // manifest origin.
    2676           0 :   if (!NS_SecurityCompareURIs(keyURI, groupURI,
    2677           0 :                               GetStrictFileOriginPolicy())) {
    2678           0 :     return false;
    2679             :   }
    2680             : 
    2681             :   // Check the groupID we found is equal to groupID based
    2682             :   // on the load context demanding load from app cache.
    2683             :   // This is check of extended origin.
    2684             : 
    2685           0 :   nsAutoCString originSuffix;
    2686           0 :   loadContextInfo->OriginAttributesPtr()->CreateSuffix(originSuffix);
    2687             : 
    2688           0 :   nsAutoCString demandedGroupID;
    2689           0 :   rv = BuildApplicationCacheGroupID(groupURI, originSuffix, demandedGroupID);
    2690           0 :   NS_ENSURE_SUCCESS(rv, false);
    2691             : 
    2692           0 :   if (groupID != demandedGroupID) {
    2693           0 :     return false;
    2694             :   }
    2695             : 
    2696           0 :   return true;
    2697             : }
    2698             : 
    2699             : 
    2700             : nsresult
    2701           0 : nsOfflineCacheDevice::ChooseApplicationCache(const nsACString &key,
    2702             :                                              nsILoadContextInfo *loadContextInfo,
    2703             :                                              nsIApplicationCache **out)
    2704             : {
    2705           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    2706             : 
    2707           0 :   NS_ENSURE_ARG(loadContextInfo);
    2708             : 
    2709             :   nsresult rv;
    2710             : 
    2711           0 :   *out = nullptr;
    2712             : 
    2713           0 :   nsCOMPtr<nsIURI> keyURI;
    2714           0 :   rv = NS_NewURI(getter_AddRefs(keyURI), key);
    2715           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2716             : 
    2717             :   // First try to find a matching cache entry.
    2718           0 :   AutoResetStatement statement(mStatement_FindClient);
    2719           0 :   rv = statement->BindUTF8StringByIndex(0, key);
    2720           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2721             : 
    2722             :   bool hasRows;
    2723           0 :   rv = statement->ExecuteStep(&hasRows);
    2724           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2725             : 
    2726           0 :   while (hasRows) {
    2727             :     int32_t itemType;
    2728           0 :     rv = statement->GetInt32(1, &itemType);
    2729           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2730             : 
    2731           0 :     if (!(itemType & nsIApplicationCache::ITEM_FOREIGN)) {
    2732           0 :       nsAutoCString clientID;
    2733           0 :       rv = statement->GetUTF8String(0, clientID);
    2734           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2735             : 
    2736           0 :       if (CanUseCache(keyURI, clientID, loadContextInfo)) {
    2737           0 :         return GetApplicationCache(clientID, out);
    2738             :       }
    2739             :     }
    2740             : 
    2741           0 :     rv = statement->ExecuteStep(&hasRows);
    2742           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2743             :   }
    2744             : 
    2745             :   // OK, we didn't find an exact match.  Search for a client with a
    2746             :   // matching namespace.
    2747             : 
    2748           0 :   AutoResetStatement nsstatement(mStatement_FindClientByNamespace);
    2749             : 
    2750           0 :   rv = nsstatement->BindUTF8StringByIndex(0, key);
    2751           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2752             : 
    2753           0 :   rv = nsstatement->ExecuteStep(&hasRows);
    2754           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2755             : 
    2756           0 :   while (hasRows)
    2757             :   {
    2758             :     int32_t itemType;
    2759           0 :     rv = nsstatement->GetInt32(1, &itemType);
    2760           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2761             : 
    2762             :     // Don't associate with a cache based solely on a whitelist entry
    2763           0 :     if (!(itemType & nsIApplicationCacheNamespace::NAMESPACE_BYPASS)) {
    2764           0 :       nsAutoCString clientID;
    2765           0 :       rv = nsstatement->GetUTF8String(0, clientID);
    2766           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2767             : 
    2768           0 :       if (CanUseCache(keyURI, clientID, loadContextInfo)) {
    2769           0 :         return GetApplicationCache(clientID, out);
    2770             :       }
    2771             :     }
    2772             : 
    2773           0 :     rv = nsstatement->ExecuteStep(&hasRows);
    2774           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2775             :   }
    2776             : 
    2777           0 :   return NS_OK;
    2778             : }
    2779             : 
    2780             : nsresult
    2781           0 : nsOfflineCacheDevice::CacheOpportunistically(nsIApplicationCache* cache,
    2782             :                                              const nsACString &key)
    2783             : {
    2784           0 :   NS_ENSURE_ARG_POINTER(cache);
    2785             : 
    2786             :   nsresult rv;
    2787             : 
    2788           0 :   nsAutoCString clientID;
    2789           0 :   rv = cache->GetClientID(clientID);
    2790           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2791             : 
    2792           0 :   return CacheOpportunistically(clientID, key);
    2793             : }
    2794             : 
    2795             : nsresult
    2796           0 : nsOfflineCacheDevice::ActivateCache(const nsACString& group,
    2797             :                                     const nsACString& clientID)
    2798             : {
    2799           0 :   NS_ENSURE_TRUE(Initialized(), NS_ERROR_NOT_INITIALIZED);
    2800             : 
    2801           0 :   AutoResetStatement statement(mStatement_ActivateClient);
    2802           0 :   nsresult rv = statement->BindUTF8StringByIndex(0, group);
    2803           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2804           0 :   rv = statement->BindUTF8StringByIndex(1, clientID);
    2805           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2806           0 :   rv = statement->BindInt32ByIndex(2, SecondsFromPRTime(PR_Now()));
    2807           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2808             : 
    2809           0 :   rv = statement->Execute();
    2810           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2811             : 
    2812           0 :   MutexAutoLock lock(mLock);
    2813             : 
    2814             :   nsCString *active;
    2815           0 :   if (mActiveCachesByGroup.Get(group, &active))
    2816             :   {
    2817           0 :     mActiveCaches.RemoveEntry(*active);
    2818           0 :     mActiveCachesByGroup.Remove(group);
    2819           0 :     active = nullptr;
    2820             :   }
    2821             : 
    2822           0 :   if (!clientID.IsEmpty())
    2823             :   {
    2824           0 :     mActiveCaches.PutEntry(clientID);
    2825           0 :     mActiveCachesByGroup.Put(group, new nsCString(clientID));
    2826             :   }
    2827             : 
    2828           0 :   return NS_OK;
    2829             : }
    2830             : 
    2831             : bool
    2832           0 : nsOfflineCacheDevice::IsActiveCache(const nsACString& group,
    2833             :                                     const nsACString& clientID)
    2834             : {
    2835           0 :   nsCString *active = nullptr;
    2836           0 :   MutexAutoLock lock(mLock);
    2837           0 :   return mActiveCachesByGroup.Get(group, &active) && *active == clientID;
    2838             : }
    2839             : 
    2840             : /**
    2841             :  * Preference accessors
    2842             :  */
    2843             : 
    2844             : void
    2845           0 : nsOfflineCacheDevice::SetCacheParentDirectory(nsIFile *parentDir)
    2846             : {
    2847           0 :   if (Initialized())
    2848             :   {
    2849           0 :     NS_ERROR("cannot switch cache directory once initialized");
    2850           0 :     return;
    2851             :   }
    2852             : 
    2853           0 :   if (!parentDir)
    2854             :   {
    2855           0 :     mCacheDirectory = nullptr;
    2856           0 :     return;
    2857             :   }
    2858             : 
    2859             :   // ensure parent directory exists
    2860           0 :   nsresult rv = EnsureDir(parentDir);
    2861           0 :   if (NS_FAILED(rv))
    2862             :   {
    2863           0 :     NS_WARNING("unable to create parent directory");
    2864           0 :     return;
    2865             :   }
    2866             : 
    2867           0 :   mBaseDirectory = parentDir;
    2868             : 
    2869             :   // cache dir may not exist, but that's ok
    2870           0 :   nsCOMPtr<nsIFile> dir;
    2871           0 :   rv = parentDir->Clone(getter_AddRefs(dir));
    2872           0 :   if (NS_FAILED(rv))
    2873           0 :     return;
    2874           0 :   rv = dir->AppendNative(NS_LITERAL_CSTRING("OfflineCache"));
    2875           0 :   if (NS_FAILED(rv))
    2876           0 :     return;
    2877             : 
    2878           0 :   mCacheDirectory = do_QueryInterface(dir);
    2879             : }
    2880             : 
    2881             : void
    2882           0 : nsOfflineCacheDevice::SetCapacity(uint32_t capacity)
    2883             : {
    2884           0 :   mCacheCapacity = capacity * 1024;
    2885           0 : }
    2886             : 
    2887             : bool
    2888           0 : nsOfflineCacheDevice::AutoShutdown(nsIApplicationCache * aAppCache)
    2889             : {
    2890           0 :   if (!mAutoShutdown)
    2891           0 :     return false;
    2892             : 
    2893           0 :   mAutoShutdown = false;
    2894             : 
    2895           0 :   Shutdown();
    2896             : 
    2897           0 :   nsCOMPtr<nsICacheService> serv = do_GetService(kCacheServiceCID);
    2898           0 :   RefPtr<nsCacheService> cacheService = nsCacheService::GlobalInstance();
    2899           0 :   cacheService->RemoveCustomOfflineDevice(this);
    2900             : 
    2901           0 :   nsAutoCString clientID;
    2902           0 :   aAppCache->GetClientID(clientID);
    2903             : 
    2904           0 :   MutexAutoLock lock(mLock);
    2905           0 :   mCaches.Remove(clientID);
    2906             : 
    2907           0 :   return true;
    2908             : }

Generated by: LCOV version 1.13