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

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/dom/cache/DBSchema.h"
       8             : 
       9             : #include "ipc/IPCMessageUtils.h"
      10             : #include "mozilla/BasePrincipal.h"
      11             : #include "mozilla/dom/HeadersBinding.h"
      12             : #include "mozilla/dom/InternalHeaders.h"
      13             : #include "mozilla/dom/RequestBinding.h"
      14             : #include "mozilla/dom/ResponseBinding.h"
      15             : #include "mozilla/dom/cache/CacheTypes.h"
      16             : #include "mozilla/dom/cache/SavedTypes.h"
      17             : #include "mozilla/dom/cache/Types.h"
      18             : #include "mozilla/dom/cache/TypeUtils.h"
      19             : #include "mozIStorageConnection.h"
      20             : #include "mozIStorageStatement.h"
      21             : #include "mozStorageHelper.h"
      22             : #include "nsCOMPtr.h"
      23             : #include "nsCRT.h"
      24             : #include "nsHttp.h"
      25             : #include "nsIContentPolicy.h"
      26             : #include "nsICryptoHash.h"
      27             : #include "nsNetCID.h"
      28             : #include "nsPrintfCString.h"
      29             : #include "nsTArray.h"
      30             : 
      31             : namespace mozilla {
      32             : namespace dom {
      33             : namespace cache {
      34             : namespace db {
      35             : const int32_t kFirstShippedSchemaVersion = 15;
      36             : namespace {
      37             : // Update this whenever the DB schema is changed.
      38             : const int32_t kLatestSchemaVersion = 25;
      39             : // ---------
      40             : // The following constants define the SQL schema.  These are defined in the
      41             : // same order the SQL should be executed in CreateOrMigrateSchema().  They are
      42             : // broken out as constants for convenient use in validation and migration.
      43             : // ---------
      44             : // The caches table is the single source of truth about what Cache
      45             : // objects exist for the origin.  The contents of the Cache are stored
      46             : // in the entries table that references back to caches.
      47             : //
      48             : // The caches table is also referenced from storage.  Rows in storage
      49             : // represent named Cache objects.  There are cases, however, where
      50             : // a Cache can still exist, but not be in a named Storage.  For example,
      51             : // when content is still using the Cache after CacheStorage::Delete()
      52             : // has been run.
      53             : //
      54             : // For now, the caches table mainly exists for data integrity with
      55             : // foreign keys, but could be expanded to contain additional cache object
      56             : // information.
      57             : //
      58             : // AUTOINCREMENT is necessary to prevent CacheId values from being reused.
      59             : const char* const kTableCaches =
      60             :   "CREATE TABLE caches ("
      61             :     "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT "
      62             :   ")";
      63             : 
      64             : // Security blobs are quite large and duplicated for every Response from
      65             : // the same https origin.  This table is used to de-duplicate this data.
      66             : const char* const kTableSecurityInfo =
      67             :   "CREATE TABLE security_info ("
      68             :     "id INTEGER NOT NULL PRIMARY KEY, "
      69             :     "hash BLOB NOT NULL, "  // first 8-bytes of the sha1 hash of data column
      70             :     "data BLOB NOT NULL, "  // full security info data, usually a few KB
      71             :     "refcount INTEGER NOT NULL"
      72             :   ")";
      73             : 
      74             : // Index the smaller hash value instead of the large security data blob.
      75             : const char* const kIndexSecurityInfoHash =
      76             :   "CREATE INDEX security_info_hash_index ON security_info (hash)";
      77             : 
      78             : const char* const kTableEntries =
      79             :   "CREATE TABLE entries ("
      80             :     "id INTEGER NOT NULL PRIMARY KEY, "
      81             :     "request_method TEXT NOT NULL, "
      82             :     "request_url_no_query TEXT NOT NULL, "
      83             :     "request_url_no_query_hash BLOB NOT NULL, " // first 8-bytes of sha1 hash
      84             :     "request_url_query TEXT NOT NULL, "
      85             :     "request_url_query_hash BLOB NOT NULL, "    // first 8-bytes of sha1 hash
      86             :     "request_referrer TEXT NOT NULL, "
      87             :     "request_headers_guard INTEGER NOT NULL, "
      88             :     "request_mode INTEGER NOT NULL, "
      89             :     "request_credentials INTEGER NOT NULL, "
      90             :     "request_contentpolicytype INTEGER NOT NULL, "
      91             :     "request_cache INTEGER NOT NULL, "
      92             :     "request_body_id TEXT NULL, "
      93             :     "response_type INTEGER NOT NULL, "
      94             :     "response_status INTEGER NOT NULL, "
      95             :     "response_status_text TEXT NOT NULL, "
      96             :     "response_headers_guard INTEGER NOT NULL, "
      97             :     "response_body_id TEXT NULL, "
      98             :     "response_security_info_id INTEGER NULL REFERENCES security_info(id), "
      99             :     "response_principal_info TEXT NOT NULL, "
     100             :     "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE, "
     101             :     "request_redirect INTEGER NOT NULL, "
     102             :     "request_referrer_policy INTEGER NOT NULL, "
     103             :     "request_integrity TEXT NOT NULL, "
     104             :     "request_url_fragment TEXT NOT NULL"
     105             :     // New columns must be added at the end of table to migrate and
     106             :     // validate properly.
     107             :   ")";
     108             : // Create an index to support the QueryCache() matching algorithm.  This
     109             : // needs to quickly find entries in a given Cache that match the request
     110             : // URL.  The url query is separated in order to support the ignoreSearch
     111             : // option.  Finally, we index hashes of the URL values instead of the
     112             : // actual strings to avoid excessive disk bloat.  The index will duplicate
     113             : // the contents of the columsn in the index.  The hash index will prune
     114             : // the vast majority of values from the query result so that normal
     115             : // scanning only has to be done on a few values to find an exact URL match.
     116             : const char* const kIndexEntriesRequest =
     117             :   "CREATE INDEX entries_request_match_index "
     118             :             "ON entries (cache_id, request_url_no_query_hash, "
     119             :                         "request_url_query_hash)";
     120             : 
     121             : const char* const kTableRequestHeaders =
     122             :   "CREATE TABLE request_headers ("
     123             :     "name TEXT NOT NULL, "
     124             :     "value TEXT NOT NULL, "
     125             :     "entry_id INTEGER NOT NULL REFERENCES entries(id) ON DELETE CASCADE"
     126             :   ")";
     127             : 
     128             : const char* const kTableResponseHeaders =
     129             :   "CREATE TABLE response_headers ("
     130             :     "name TEXT NOT NULL, "
     131             :     "value TEXT NOT NULL, "
     132             :     "entry_id INTEGER NOT NULL REFERENCES entries(id) ON DELETE CASCADE"
     133             :   ")";
     134             : 
     135             : // We need an index on response_headers, but not on request_headers,
     136             : // because we quickly need to determine if a VARY header is present.
     137             : const char* const kIndexResponseHeadersName =
     138             :   "CREATE INDEX response_headers_name_index "
     139             :             "ON response_headers (name)";
     140             : 
     141             : const char* const kTableResponseUrlList =
     142             :   "CREATE TABLE response_url_list ("
     143             :     "url TEXT NOT NULL, "
     144             :     "entry_id INTEGER NOT NULL REFERENCES entries(id) ON DELETE CASCADE"
     145             :   ")";
     146             : 
     147             : // NOTE: key allows NULL below since that is how "" is represented
     148             : //       in a BLOB column.  We use BLOB to avoid encoding issues
     149             : //       with storing DOMStrings.
     150             : const char* const kTableStorage =
     151             :   "CREATE TABLE storage ("
     152             :     "namespace INTEGER NOT NULL, "
     153             :     "key BLOB NULL, "
     154             :     "cache_id INTEGER NOT NULL REFERENCES caches(id), "
     155             :     "PRIMARY KEY(namespace, key) "
     156             :   ")";
     157             : 
     158             : // ---------
     159             : // End schema definition
     160             : // ---------
     161             : 
     162             : const int32_t kMaxEntriesPerStatement = 255;
     163             : 
     164             : const uint32_t kPageSize = 4 * 1024;
     165             : 
     166             : // Grow the database in chunks to reduce fragmentation
     167             : const uint32_t kGrowthSize = 32 * 1024;
     168             : const uint32_t kGrowthPages = kGrowthSize / kPageSize;
     169             : static_assert(kGrowthSize % kPageSize == 0,
     170             :               "Growth size must be multiple of page size");
     171             : 
     172             : // Only release free pages when we have more than this limit
     173             : const int32_t kMaxFreePages = kGrowthPages;
     174             : 
     175             : // Limit WAL journal to a reasonable size
     176             : const uint32_t kWalAutoCheckpointSize = 512 * 1024;
     177             : const uint32_t kWalAutoCheckpointPages = kWalAutoCheckpointSize / kPageSize;
     178             : static_assert(kWalAutoCheckpointSize % kPageSize == 0,
     179             :               "WAL checkpoint size must be multiple of page size");
     180             : 
     181             : } // namespace
     182             : 
     183             : // If any of the static_asserts below fail, it means that you have changed
     184             : // the corresponding WebIDL enum in a way that may be incompatible with the
     185             : // existing data stored in the DOM Cache.  You would need to update the Cache
     186             : // database schema accordingly and adjust the failing static_assert.
     187             : static_assert(int(HeadersGuardEnum::None) == 0 &&
     188             :               int(HeadersGuardEnum::Request) == 1 &&
     189             :               int(HeadersGuardEnum::Request_no_cors) == 2 &&
     190             :               int(HeadersGuardEnum::Response) == 3 &&
     191             :               int(HeadersGuardEnum::Immutable) == 4 &&
     192             :               int(HeadersGuardEnum::EndGuard_) == 5,
     193             :               "HeadersGuardEnum values are as expected");
     194             : static_assert(int(ReferrerPolicy::_empty) == 0 &&
     195             :               int(ReferrerPolicy::No_referrer) == 1 &&
     196             :               int(ReferrerPolicy::No_referrer_when_downgrade) == 2 &&
     197             :               int(ReferrerPolicy::Origin) == 3 &&
     198             :               int(ReferrerPolicy::Origin_when_cross_origin) == 4 &&
     199             :               int(ReferrerPolicy::Unsafe_url) == 5 &&
     200             :               int(ReferrerPolicy::Same_origin) == 6 &&
     201             :               int(ReferrerPolicy::Strict_origin) == 7 &&
     202             :               int(ReferrerPolicy::Strict_origin_when_cross_origin) == 8 &&
     203             :               int(ReferrerPolicy::EndGuard_) == 9,
     204             :               "ReferrerPolicy values are as expected");
     205             : static_assert(int(RequestMode::Same_origin) == 0 &&
     206             :               int(RequestMode::No_cors) == 1 &&
     207             :               int(RequestMode::Cors) == 2 &&
     208             :               int(RequestMode::Navigate) == 3 &&
     209             :               int(RequestMode::EndGuard_) == 4,
     210             :               "RequestMode values are as expected");
     211             : static_assert(int(RequestCredentials::Omit) == 0 &&
     212             :               int(RequestCredentials::Same_origin) == 1 &&
     213             :               int(RequestCredentials::Include) == 2 &&
     214             :               int(RequestCredentials::EndGuard_) == 3,
     215             :               "RequestCredentials values are as expected");
     216             : static_assert(int(RequestCache::Default) == 0 &&
     217             :               int(RequestCache::No_store) == 1 &&
     218             :               int(RequestCache::Reload) == 2 &&
     219             :               int(RequestCache::No_cache) == 3 &&
     220             :               int(RequestCache::Force_cache) == 4 &&
     221             :               int(RequestCache::Only_if_cached) == 5 &&
     222             :               int(RequestCache::EndGuard_) == 6,
     223             :               "RequestCache values are as expected");
     224             : static_assert(int(RequestRedirect::Follow) == 0 &&
     225             :               int(RequestRedirect::Error) == 1 &&
     226             :               int(RequestRedirect::Manual) == 2 &&
     227             :               int(RequestRedirect::EndGuard_) == 3,
     228             :               "RequestRedirect values are as expected");
     229             : static_assert(int(ResponseType::Basic) == 0 &&
     230             :               int(ResponseType::Cors) == 1 &&
     231             :               int(ResponseType::Default) == 2 &&
     232             :               int(ResponseType::Error) == 3 &&
     233             :               int(ResponseType::Opaque) == 4 &&
     234             :               int(ResponseType::Opaqueredirect) == 5 &&
     235             :               int(ResponseType::EndGuard_) == 6,
     236             :               "ResponseType values are as expected");
     237             : 
     238             : // If the static_asserts below fails, it means that you have changed the
     239             : // Namespace enum in a way that may be incompatible with the existing data
     240             : // stored in the DOM Cache.  You would need to update the Cache database schema
     241             : // accordingly and adjust the failing static_assert.
     242             : static_assert(DEFAULT_NAMESPACE == 0 &&
     243             :               CHROME_ONLY_NAMESPACE == 1 &&
     244             :               NUMBER_OF_NAMESPACES == 2,
     245             :               "Namespace values are as expected");
     246             : 
     247             : // If the static_asserts below fails, it means that you have changed the
     248             : // nsContentPolicy enum in a way that may be incompatible with the existing data
     249             : // stored in the DOM Cache.  You would need to update the Cache database schema
     250             : // accordingly and adjust the failing static_assert.
     251             : static_assert(nsIContentPolicy::TYPE_INVALID == 0 &&
     252             :               nsIContentPolicy::TYPE_OTHER == 1 &&
     253             :               nsIContentPolicy::TYPE_SCRIPT == 2 &&
     254             :               nsIContentPolicy::TYPE_IMAGE == 3 &&
     255             :               nsIContentPolicy::TYPE_STYLESHEET == 4 &&
     256             :               nsIContentPolicy::TYPE_OBJECT == 5 &&
     257             :               nsIContentPolicy::TYPE_DOCUMENT == 6 &&
     258             :               nsIContentPolicy::TYPE_SUBDOCUMENT == 7 &&
     259             :               nsIContentPolicy::TYPE_REFRESH == 8 &&
     260             :               nsIContentPolicy::TYPE_XBL == 9 &&
     261             :               nsIContentPolicy::TYPE_PING == 10 &&
     262             :               nsIContentPolicy::TYPE_XMLHTTPREQUEST == 11 &&
     263             :               nsIContentPolicy::TYPE_DATAREQUEST == 11 &&
     264             :               nsIContentPolicy::TYPE_OBJECT_SUBREQUEST == 12 &&
     265             :               nsIContentPolicy::TYPE_DTD == 13 &&
     266             :               nsIContentPolicy::TYPE_FONT == 14 &&
     267             :               nsIContentPolicy::TYPE_MEDIA == 15 &&
     268             :               nsIContentPolicy::TYPE_WEBSOCKET == 16 &&
     269             :               nsIContentPolicy::TYPE_CSP_REPORT == 17 &&
     270             :               nsIContentPolicy::TYPE_XSLT == 18 &&
     271             :               nsIContentPolicy::TYPE_BEACON == 19 &&
     272             :               nsIContentPolicy::TYPE_FETCH == 20 &&
     273             :               nsIContentPolicy::TYPE_IMAGESET == 21 &&
     274             :               nsIContentPolicy::TYPE_WEB_MANIFEST == 22 &&
     275             :               nsIContentPolicy::TYPE_INTERNAL_SCRIPT == 23 &&
     276             :               nsIContentPolicy::TYPE_INTERNAL_WORKER == 24 &&
     277             :               nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER == 25 &&
     278             :               nsIContentPolicy::TYPE_INTERNAL_EMBED == 26 &&
     279             :               nsIContentPolicy::TYPE_INTERNAL_OBJECT == 27 &&
     280             :               nsIContentPolicy::TYPE_INTERNAL_FRAME == 28 &&
     281             :               nsIContentPolicy::TYPE_INTERNAL_IFRAME == 29 &&
     282             :               nsIContentPolicy::TYPE_INTERNAL_AUDIO == 30 &&
     283             :               nsIContentPolicy::TYPE_INTERNAL_VIDEO == 31 &&
     284             :               nsIContentPolicy::TYPE_INTERNAL_TRACK == 32 &&
     285             :               nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST == 33 &&
     286             :               nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE == 34 &&
     287             :               nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER == 35 &&
     288             :               nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD == 36 &&
     289             :               nsIContentPolicy::TYPE_INTERNAL_IMAGE == 37 &&
     290             :               nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD == 38 &&
     291             :               nsIContentPolicy::TYPE_INTERNAL_STYLESHEET == 39 &&
     292             :               nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD == 40 &&
     293             :               nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON == 41 &&
     294             :               nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS == 42,
     295             :               "nsContentPolicyType values are as expected");
     296             : 
     297             : namespace {
     298             : 
     299             : typedef int32_t EntryId;
     300             : 
     301             : struct IdCount
     302             : {
     303           0 :   explicit IdCount(int32_t aId) : mId(aId), mCount(1) { }
     304             :   int32_t mId;
     305             :   int32_t mCount;
     306             : };
     307             : 
     308             : static nsresult QueryAll(mozIStorageConnection* aConn, CacheId aCacheId,
     309             :                          nsTArray<EntryId>& aEntryIdListOut);
     310             : static nsresult QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
     311             :                            const CacheRequest& aRequest,
     312             :                            const CacheQueryParams& aParams,
     313             :                            nsTArray<EntryId>& aEntryIdListOut,
     314             :                            uint32_t aMaxResults = UINT32_MAX);
     315             : static nsresult MatchByVaryHeader(mozIStorageConnection* aConn,
     316             :                                   const CacheRequest& aRequest,
     317             :                                   EntryId entryId, bool* aSuccessOut);
     318             : static nsresult DeleteEntries(mozIStorageConnection* aConn,
     319             :                               const nsTArray<EntryId>& aEntryIdList,
     320             :                               nsTArray<nsID>& aDeletedBodyIdListOut,
     321             :                               nsTArray<IdCount>& aDeletedSecurityIdListOut,
     322             :                               uint32_t aPos=0, int32_t aLen=-1);
     323             : static nsresult InsertSecurityInfo(mozIStorageConnection* aConn,
     324             :                                    nsICryptoHash* aCrypto,
     325             :                                    const nsACString& aData, int32_t *aIdOut);
     326             : static nsresult DeleteSecurityInfo(mozIStorageConnection* aConn, int32_t aId,
     327             :                                    int32_t aCount);
     328             : static nsresult DeleteSecurityInfoList(mozIStorageConnection* aConn,
     329             :                                        const nsTArray<IdCount>& aDeletedStorageIdList);
     330             : static nsresult InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
     331             :                             const CacheRequest& aRequest,
     332             :                             const nsID* aRequestBodyId,
     333             :                             const CacheResponse& aResponse,
     334             :                             const nsID* aResponseBodyId);
     335             : static nsresult ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
     336             :                              SavedResponse* aSavedResponseOut);
     337             : static nsresult ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
     338             :                             SavedRequest* aSavedRequestOut);
     339             : 
     340             : static void AppendListParamsToQuery(nsACString& aQuery,
     341             :                                     const nsTArray<EntryId>& aEntryIdList,
     342             :                                     uint32_t aPos, int32_t aLen);
     343             : static nsresult BindListParamsToQuery(mozIStorageStatement* aState,
     344             :                                       const nsTArray<EntryId>& aEntryIdList,
     345             :                                       uint32_t aPos, int32_t aLen);
     346             : static nsresult BindId(mozIStorageStatement* aState, const nsACString& aName,
     347             :                        const nsID* aId);
     348             : static nsresult ExtractId(mozIStorageStatement* aState, uint32_t aPos,
     349             :                           nsID* aIdOut);
     350             : static nsresult CreateAndBindKeyStatement(mozIStorageConnection* aConn,
     351             :                                           const char* aQueryFormat,
     352             :                                           const nsAString& aKey,
     353             :                                           mozIStorageStatement** aStateOut);
     354             : static nsresult HashCString(nsICryptoHash* aCrypto, const nsACString& aIn,
     355             :                             nsACString& aOut);
     356             : nsresult Validate(mozIStorageConnection* aConn);
     357             : nsresult Migrate(mozIStorageConnection* aConn);
     358             : } // namespace
     359             : 
     360             : class MOZ_RAII AutoDisableForeignKeyChecking
     361             : {
     362             : public:
     363           0 :   explicit AutoDisableForeignKeyChecking(mozIStorageConnection* aConn)
     364           0 :     : mConn(aConn)
     365           0 :     , mForeignKeyCheckingDisabled(false)
     366             :   {
     367           0 :     nsCOMPtr<mozIStorageStatement> state;
     368           0 :     nsresult rv = mConn->CreateStatement(NS_LITERAL_CSTRING(
     369             :       "PRAGMA foreign_keys;"
     370           0 :     ), getter_AddRefs(state));
     371           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return; }
     372             : 
     373           0 :     bool hasMoreData = false;
     374           0 :     rv = state->ExecuteStep(&hasMoreData);
     375           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return; }
     376             : 
     377             :     int32_t mode;
     378           0 :     rv = state->GetInt32(0, &mode);
     379           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return; }
     380             : 
     381           0 :     if (mode) {
     382           0 :       nsresult rv = mConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     383             :         "PRAGMA foreign_keys = OFF;"
     384           0 :       ));
     385           0 :       if (NS_WARN_IF(NS_FAILED(rv))) { return; }
     386           0 :       mForeignKeyCheckingDisabled = true;
     387             :     }
     388             :   }
     389             : 
     390           0 :   ~AutoDisableForeignKeyChecking()
     391           0 :   {
     392           0 :     if (mForeignKeyCheckingDisabled) {
     393           0 :       nsresult rv = mConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     394             :         "PRAGMA foreign_keys = ON;"
     395           0 :       ));
     396           0 :       if (NS_WARN_IF(NS_FAILED(rv))) { return; }
     397             :     }
     398           0 :   }
     399             : 
     400             : private:
     401             :   nsCOMPtr<mozIStorageConnection> mConn;
     402             :   bool mForeignKeyCheckingDisabled;
     403             : };
     404             : 
     405             : nsresult
     406           0 : CreateOrMigrateSchema(mozIStorageConnection* aConn)
     407             : {
     408           0 :   MOZ_ASSERT(!NS_IsMainThread());
     409           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
     410             : 
     411             :   int32_t schemaVersion;
     412           0 :   nsresult rv = aConn->GetSchemaVersion(&schemaVersion);
     413           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     414             : 
     415           0 :   if (schemaVersion == kLatestSchemaVersion) {
     416             :     // We already have the correct schema version.  Validate it matches
     417             :     // our expected schema and then proceed.
     418           0 :     rv = Validate(aConn);
     419           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     420             : 
     421           0 :     return rv;
     422             :   }
     423             : 
     424             :   // Turn off checking foreign keys before starting a transaction, and restore
     425             :   // it once we're done.
     426           0 :   AutoDisableForeignKeyChecking restoreForeignKeyChecking(aConn);
     427             :   mozStorageTransaction trans(aConn, false,
     428           0 :                               mozIStorageConnection::TRANSACTION_IMMEDIATE);
     429           0 :   bool needVacuum = false;
     430             : 
     431           0 :   if (schemaVersion) {
     432             :     // A schema exists, but its not the current version.  Attempt to
     433             :     // migrate it to our new schema.
     434           0 :     rv = Migrate(aConn);
     435           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     436             : 
     437             :     // Migrations happen infrequently and reflect a chance in DB structure.
     438             :     // This is a good time to rebuild the database.  It also helps catch
     439             :     // if a new migration is incorrect by fast failing on the corruption.
     440           0 :     needVacuum = true;
     441             :   } else {
     442             :     // There is no schema installed.  Create the database from scratch.
     443           0 :     rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableCaches));
     444           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     445             : 
     446           0 :     rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableSecurityInfo));
     447           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     448             : 
     449           0 :     rv = aConn->ExecuteSimpleSQL(nsDependentCString(kIndexSecurityInfoHash));
     450           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     451             : 
     452           0 :     rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableEntries));
     453           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     454             : 
     455           0 :     rv = aConn->ExecuteSimpleSQL(nsDependentCString(kIndexEntriesRequest));
     456           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     457             : 
     458           0 :     rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableRequestHeaders));
     459           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     460             : 
     461           0 :     rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableResponseHeaders));
     462           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     463             : 
     464           0 :     rv = aConn->ExecuteSimpleSQL(nsDependentCString(kIndexResponseHeadersName));
     465           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     466             : 
     467           0 :     rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableResponseUrlList));
     468           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     469             : 
     470           0 :     rv = aConn->ExecuteSimpleSQL(nsDependentCString(kTableStorage));
     471           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     472             : 
     473           0 :     rv = aConn->SetSchemaVersion(kLatestSchemaVersion);
     474           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     475             : 
     476           0 :     rv = aConn->GetSchemaVersion(&schemaVersion);
     477           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     478             :   }
     479             : 
     480           0 :   rv = Validate(aConn);
     481           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     482             : 
     483           0 :   rv = trans.Commit();
     484           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     485             : 
     486           0 :   if (needVacuum) {
     487             :     // Unfortunately, this must be performed outside of the transaction.
     488           0 :     aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM"));
     489           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     490             :   }
     491             : 
     492           0 :   return rv;
     493             : }
     494             : 
     495             : nsresult
     496           0 : InitializeConnection(mozIStorageConnection* aConn)
     497             : {
     498           0 :   MOZ_ASSERT(!NS_IsMainThread());
     499           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
     500             : 
     501             :   // This function needs to perform per-connection initialization tasks that
     502             :   // need to happen regardless of the schema.
     503             : 
     504             :   nsPrintfCString pragmas(
     505             :     // Use a smaller page size to improve perf/footprint; default is too large
     506             :     "PRAGMA page_size = %u; "
     507             :     // Enable auto_vacuum; this must happen after page_size and before WAL
     508             :     "PRAGMA auto_vacuum = INCREMENTAL; "
     509             :     "PRAGMA foreign_keys = ON; ",
     510             :     kPageSize
     511           0 :   );
     512             : 
     513             :   // Note, the default encoding of UTF-8 is preferred.  mozStorage does all
     514             :   // the work necessary to convert UTF-16 nsString values for us.  We don't
     515             :   // need ordering and the binary equality operations are correct.  So, do
     516             :   // NOT set PRAGMA encoding to UTF-16.
     517             : 
     518           0 :   nsresult rv = aConn->ExecuteSimpleSQL(pragmas);
     519           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     520             : 
     521             :   // Limit fragmentation by growing the database by many pages at once.
     522           0 :   rv = aConn->SetGrowthIncrement(kGrowthSize, EmptyCString());
     523           0 :   if (rv == NS_ERROR_FILE_TOO_BIG) {
     524           0 :     NS_WARNING("Not enough disk space to set sqlite growth increment.");
     525           0 :     rv = NS_OK;
     526             :   }
     527           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     528             : 
     529             :   // Enable WAL journaling.  This must be performed in a separate transaction
     530             :   // after changing the page_size and enabling auto_vacuum.
     531             :   nsPrintfCString wal(
     532             :     // WAL journal can grow to given number of *pages*
     533             :     "PRAGMA wal_autocheckpoint = %u; "
     534             :     // Always truncate the journal back to given number of *bytes*
     535             :     "PRAGMA journal_size_limit = %u; "
     536             :     // WAL must be enabled at the end to allow page size to be changed, etc.
     537             :     "PRAGMA journal_mode = WAL; ",
     538             :     kWalAutoCheckpointPages,
     539             :     kWalAutoCheckpointSize
     540           0 :   );
     541             : 
     542           0 :   rv = aConn->ExecuteSimpleSQL(wal);
     543           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     544             : 
     545             :   // Verify that we successfully set the vacuum mode to incremental.  It
     546             :   // is very easy to put the database in a state where the auto_vacuum
     547             :   // pragma above fails silently.
     548             : #ifdef DEBUG
     549           0 :   nsCOMPtr<mozIStorageStatement> state;
     550           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     551             :     "PRAGMA auto_vacuum;"
     552           0 :   ), getter_AddRefs(state));
     553           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     554             : 
     555           0 :   bool hasMoreData = false;
     556           0 :   rv = state->ExecuteStep(&hasMoreData);
     557           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     558             : 
     559             :   int32_t mode;
     560           0 :   rv = state->GetInt32(0, &mode);
     561           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     562             : 
     563             :   // integer value 2 is incremental mode
     564           0 :   if (NS_WARN_IF(mode != 2)) { return NS_ERROR_UNEXPECTED; }
     565             : #endif
     566             : 
     567           0 :   return NS_OK;
     568             : }
     569             : 
     570             : nsresult
     571           0 : CreateCacheId(mozIStorageConnection* aConn, CacheId* aCacheIdOut)
     572             : {
     573           0 :   MOZ_ASSERT(!NS_IsMainThread());
     574           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
     575           0 :   MOZ_DIAGNOSTIC_ASSERT(aCacheIdOut);
     576             : 
     577           0 :   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     578             :     "INSERT INTO caches DEFAULT VALUES;"
     579           0 :   ));
     580           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     581             : 
     582           0 :   nsCOMPtr<mozIStorageStatement> state;
     583           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     584             :     "SELECT last_insert_rowid()"
     585           0 :   ), getter_AddRefs(state));
     586           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     587             : 
     588           0 :   bool hasMoreData = false;
     589           0 :   rv = state->ExecuteStep(&hasMoreData);
     590           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     591           0 :   if (NS_WARN_IF(!hasMoreData)) { return NS_ERROR_UNEXPECTED; }
     592             : 
     593           0 :   rv = state->GetInt64(0, aCacheIdOut);
     594           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     595             : 
     596           0 :   return rv;
     597             : }
     598             : 
     599             : nsresult
     600           0 : DeleteCacheId(mozIStorageConnection* aConn, CacheId aCacheId,
     601             :               nsTArray<nsID>& aDeletedBodyIdListOut)
     602             : {
     603           0 :   MOZ_ASSERT(!NS_IsMainThread());
     604           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
     605             : 
     606             :   // Delete the bodies explicitly as we need to read out the body IDs
     607             :   // anyway.  These body IDs must be deleted one-by-one as content may
     608             :   // still be referencing them invidivually.
     609           0 :   AutoTArray<EntryId, 256> matches;
     610           0 :   nsresult rv = QueryAll(aConn, aCacheId, matches);
     611           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     612             : 
     613           0 :   AutoTArray<IdCount, 16> deletedSecurityIdList;
     614             :   rv = DeleteEntries(aConn, matches, aDeletedBodyIdListOut,
     615           0 :                      deletedSecurityIdList);
     616           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     617             : 
     618           0 :   rv = DeleteSecurityInfoList(aConn, deletedSecurityIdList);
     619           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     620             : 
     621             :   // Delete the remainder of the cache using cascade semantics.
     622           0 :   nsCOMPtr<mozIStorageStatement> state;
     623           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     624             :     "DELETE FROM caches WHERE id=:id;"
     625           0 :   ), getter_AddRefs(state));
     626           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     627             : 
     628           0 :   rv = state->BindInt64ByName(NS_LITERAL_CSTRING("id"), aCacheId);
     629           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     630             : 
     631           0 :   rv = state->Execute();
     632           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     633             : 
     634           0 :   return rv;
     635             : }
     636             : 
     637             : nsresult
     638           0 : IsCacheOrphaned(mozIStorageConnection* aConn, CacheId aCacheId,
     639             :                 bool* aOrphanedOut)
     640             : {
     641           0 :   MOZ_ASSERT(!NS_IsMainThread());
     642           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
     643           0 :   MOZ_DIAGNOSTIC_ASSERT(aOrphanedOut);
     644             : 
     645             :   // err on the side of not deleting user data
     646           0 :   *aOrphanedOut = false;
     647             : 
     648           0 :   nsCOMPtr<mozIStorageStatement> state;
     649           0 :   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     650             :     "SELECT COUNT(*) FROM storage WHERE cache_id=:cache_id;"
     651           0 :   ), getter_AddRefs(state));
     652           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     653             : 
     654           0 :   rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
     655           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     656             : 
     657           0 :   bool hasMoreData = false;
     658           0 :   rv = state->ExecuteStep(&hasMoreData);
     659           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     660           0 :   MOZ_DIAGNOSTIC_ASSERT(hasMoreData);
     661             : 
     662             :   int32_t refCount;
     663           0 :   rv = state->GetInt32(0, &refCount);
     664           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     665             : 
     666           0 :   *aOrphanedOut = refCount == 0;
     667             : 
     668           0 :   return rv;
     669             : }
     670             : 
     671             : nsresult
     672           0 : FindOrphanedCacheIds(mozIStorageConnection* aConn,
     673             :                      nsTArray<CacheId>& aOrphanedListOut)
     674             : {
     675           0 :   nsCOMPtr<mozIStorageStatement> state;
     676           0 :   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     677             :     "SELECT id FROM caches "
     678             :     "WHERE id NOT IN (SELECT cache_id from storage);"
     679           0 :   ), getter_AddRefs(state));
     680           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     681             : 
     682           0 :   bool hasMoreData = false;
     683           0 :   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
     684           0 :     CacheId cacheId = INVALID_CACHE_ID;
     685           0 :     rv = state->GetInt64(0, &cacheId);
     686           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     687           0 :     aOrphanedListOut.AppendElement(cacheId);
     688             :   }
     689             : 
     690           0 :   return rv;
     691             : }
     692             : 
     693             : nsresult
     694           0 : GetKnownBodyIds(mozIStorageConnection* aConn, nsTArray<nsID>& aBodyIdListOut)
     695             : {
     696           0 :   MOZ_ASSERT(!NS_IsMainThread());
     697           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
     698             : 
     699           0 :   nsCOMPtr<mozIStorageStatement> state;
     700           0 :   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     701             :     "SELECT request_body_id, response_body_id FROM entries;"
     702           0 :   ), getter_AddRefs(state));
     703           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     704             : 
     705           0 :   bool hasMoreData = false;
     706           0 :   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
     707             :     // extract 0 to 2 nsID structs per row
     708           0 :     for (uint32_t i = 0; i < 2; ++i) {
     709           0 :       bool isNull = false;
     710             : 
     711           0 :       rv = state->GetIsNull(i, &isNull);
     712           0 :       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     713             : 
     714           0 :       if (!isNull) {
     715             :         nsID id;
     716           0 :         rv = ExtractId(state, i, &id);
     717           0 :         if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     718             : 
     719           0 :         aBodyIdListOut.AppendElement(id);
     720             :       }
     721             :     }
     722             :   }
     723             : 
     724           0 :   return rv;
     725             : }
     726             : 
     727             : nsresult
     728           0 : CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
     729             :            const CacheRequest& aRequest,
     730             :            const CacheQueryParams& aParams,
     731             :            bool* aFoundResponseOut,
     732             :            SavedResponse* aSavedResponseOut)
     733             : {
     734           0 :   MOZ_ASSERT(!NS_IsMainThread());
     735           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
     736           0 :   MOZ_DIAGNOSTIC_ASSERT(aFoundResponseOut);
     737           0 :   MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut);
     738             : 
     739           0 :   *aFoundResponseOut = false;
     740             : 
     741           0 :   AutoTArray<EntryId, 1> matches;
     742           0 :   nsresult rv = QueryCache(aConn, aCacheId, aRequest, aParams, matches, 1);
     743           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     744             : 
     745           0 :   if (matches.IsEmpty()) {
     746           0 :     return rv;
     747             :   }
     748             : 
     749           0 :   rv = ReadResponse(aConn, matches[0], aSavedResponseOut);
     750           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     751             : 
     752           0 :   aSavedResponseOut->mCacheId = aCacheId;
     753           0 :   *aFoundResponseOut = true;
     754             : 
     755           0 :   return rv;
     756             : }
     757             : 
     758             : nsresult
     759           0 : CacheMatchAll(mozIStorageConnection* aConn, CacheId aCacheId,
     760             :               const CacheRequestOrVoid& aRequestOrVoid,
     761             :               const CacheQueryParams& aParams,
     762             :               nsTArray<SavedResponse>& aSavedResponsesOut)
     763             : {
     764           0 :   MOZ_ASSERT(!NS_IsMainThread());
     765           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
     766             :   nsresult rv;
     767             : 
     768           0 :   AutoTArray<EntryId, 256> matches;
     769           0 :   if (aRequestOrVoid.type() == CacheRequestOrVoid::Tvoid_t) {
     770           0 :     rv = QueryAll(aConn, aCacheId, matches);
     771           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     772             :   } else {
     773           0 :     rv = QueryCache(aConn, aCacheId, aRequestOrVoid, aParams, matches);
     774           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     775             :   }
     776             : 
     777             :   // TODO: replace this with a bulk load using SQL IN clause (bug 1110458)
     778           0 :   for (uint32_t i = 0; i < matches.Length(); ++i) {
     779           0 :     SavedResponse savedResponse;
     780           0 :     rv = ReadResponse(aConn, matches[i], &savedResponse);
     781           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     782           0 :     savedResponse.mCacheId = aCacheId;
     783           0 :     aSavedResponsesOut.AppendElement(savedResponse);
     784             :   }
     785             : 
     786           0 :   return rv;
     787             : }
     788             : 
     789             : nsresult
     790           0 : CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
     791             :          const CacheRequest& aRequest,
     792             :          const nsID* aRequestBodyId,
     793             :          const CacheResponse& aResponse,
     794             :          const nsID* aResponseBodyId,
     795             :          nsTArray<nsID>& aDeletedBodyIdListOut)
     796             : {
     797           0 :   MOZ_ASSERT(!NS_IsMainThread());
     798           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
     799             : 
     800             :   CacheQueryParams params(false, false, false, false,
     801           0 :                            NS_LITERAL_STRING(""));
     802           0 :   AutoTArray<EntryId, 256> matches;
     803           0 :   nsresult rv = QueryCache(aConn, aCacheId, aRequest, params, matches);
     804           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     805             : 
     806           0 :   AutoTArray<IdCount, 16> deletedSecurityIdList;
     807             :   rv = DeleteEntries(aConn, matches, aDeletedBodyIdListOut,
     808           0 :                      deletedSecurityIdList);
     809           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     810             : 
     811             :   rv = InsertEntry(aConn, aCacheId, aRequest, aRequestBodyId, aResponse,
     812           0 :                    aResponseBodyId);
     813           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     814             : 
     815             :   // Delete the security values after doing the insert to avoid churning
     816             :   // the security table when its not necessary.
     817           0 :   rv = DeleteSecurityInfoList(aConn, deletedSecurityIdList);
     818           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     819             : 
     820           0 :   return rv;
     821             : }
     822             : 
     823             : nsresult
     824           0 : CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
     825             :             const CacheRequest& aRequest,
     826             :             const CacheQueryParams& aParams,
     827             :             nsTArray<nsID>& aDeletedBodyIdListOut, bool* aSuccessOut)
     828             : {
     829           0 :   MOZ_ASSERT(!NS_IsMainThread());
     830           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
     831           0 :   MOZ_DIAGNOSTIC_ASSERT(aSuccessOut);
     832             : 
     833           0 :   *aSuccessOut = false;
     834             : 
     835           0 :   AutoTArray<EntryId, 256> matches;
     836           0 :   nsresult rv = QueryCache(aConn, aCacheId, aRequest, aParams, matches);
     837           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     838             : 
     839           0 :   if (matches.IsEmpty()) {
     840           0 :     return rv;
     841             :   }
     842             : 
     843           0 :   AutoTArray<IdCount, 16> deletedSecurityIdList;
     844             :   rv = DeleteEntries(aConn, matches, aDeletedBodyIdListOut,
     845           0 :                      deletedSecurityIdList);
     846           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     847             : 
     848           0 :   rv = DeleteSecurityInfoList(aConn, deletedSecurityIdList);
     849           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     850             : 
     851           0 :   *aSuccessOut = true;
     852             : 
     853           0 :   return rv;
     854             : }
     855             : 
     856             : nsresult
     857           0 : CacheKeys(mozIStorageConnection* aConn, CacheId aCacheId,
     858             :           const CacheRequestOrVoid& aRequestOrVoid,
     859             :           const CacheQueryParams& aParams,
     860             :           nsTArray<SavedRequest>& aSavedRequestsOut)
     861             : {
     862           0 :   MOZ_ASSERT(!NS_IsMainThread());
     863           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
     864             :   nsresult rv;
     865             : 
     866           0 :   AutoTArray<EntryId, 256> matches;
     867           0 :   if (aRequestOrVoid.type() == CacheRequestOrVoid::Tvoid_t) {
     868           0 :     rv = QueryAll(aConn, aCacheId, matches);
     869           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     870             :   } else {
     871           0 :     rv = QueryCache(aConn, aCacheId, aRequestOrVoid, aParams, matches);
     872           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     873             :   }
     874             : 
     875             :   // TODO: replace this with a bulk load using SQL IN clause (bug 1110458)
     876           0 :   for (uint32_t i = 0; i < matches.Length(); ++i) {
     877           0 :     SavedRequest savedRequest;
     878           0 :     rv = ReadRequest(aConn, matches[i], &savedRequest);
     879           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     880           0 :     savedRequest.mCacheId = aCacheId;
     881           0 :     aSavedRequestsOut.AppendElement(savedRequest);
     882             :   }
     883             : 
     884           0 :   return rv;
     885             : }
     886             : 
     887             : nsresult
     888           0 : StorageMatch(mozIStorageConnection* aConn,
     889             :              Namespace aNamespace,
     890             :              const CacheRequest& aRequest,
     891             :              const CacheQueryParams& aParams,
     892             :              bool* aFoundResponseOut,
     893             :              SavedResponse* aSavedResponseOut)
     894             : {
     895           0 :   MOZ_ASSERT(!NS_IsMainThread());
     896           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
     897           0 :   MOZ_DIAGNOSTIC_ASSERT(aFoundResponseOut);
     898           0 :   MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut);
     899             : 
     900           0 :   *aFoundResponseOut = false;
     901             : 
     902             :   nsresult rv;
     903             : 
     904             :   // If we are given a cache to check, then simply find its cache ID
     905             :   // and perform the match.
     906           0 :   if (!aParams.cacheName().EqualsLiteral("")) {
     907           0 :     bool foundCache = false;
     908             :     // no invalid CacheId, init to least likely real value
     909           0 :     CacheId cacheId = INVALID_CACHE_ID;
     910           0 :     rv = StorageGetCacheId(aConn, aNamespace, aParams.cacheName(), &foundCache,
     911           0 :                            &cacheId);
     912           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     913           0 :     if (!foundCache) { return NS_OK; }
     914             : 
     915           0 :     rv = CacheMatch(aConn, cacheId, aRequest, aParams, aFoundResponseOut,
     916           0 :                     aSavedResponseOut);
     917           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     918             : 
     919           0 :     return rv;
     920             :   }
     921             : 
     922             :   // Otherwise we need to get a list of all the cache IDs in this namespace.
     923             : 
     924           0 :   nsCOMPtr<mozIStorageStatement> state;
     925           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     926             :     "SELECT cache_id FROM storage WHERE namespace=:namespace ORDER BY rowid;"
     927           0 :   ), getter_AddRefs(state));
     928           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     929             : 
     930           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("namespace"), aNamespace);
     931           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     932             : 
     933           0 :   AutoTArray<CacheId, 32> cacheIdList;
     934             : 
     935           0 :   bool hasMoreData = false;
     936           0 :   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
     937           0 :     CacheId cacheId = INVALID_CACHE_ID;
     938           0 :     rv = state->GetInt64(0, &cacheId);
     939           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     940           0 :     cacheIdList.AppendElement(cacheId);
     941             :   }
     942             : 
     943             :   // Now try to find a match in each cache in order
     944           0 :   for (uint32_t i = 0; i < cacheIdList.Length(); ++i) {
     945           0 :     rv = CacheMatch(aConn, cacheIdList[i], aRequest, aParams, aFoundResponseOut,
     946           0 :                     aSavedResponseOut);
     947           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     948             : 
     949           0 :     if (*aFoundResponseOut) {
     950           0 :       aSavedResponseOut->mCacheId = cacheIdList[i];
     951           0 :       return rv;
     952             :     }
     953             :   }
     954             : 
     955           0 :   return NS_OK;
     956             : }
     957             : 
     958             : nsresult
     959           0 : StorageGetCacheId(mozIStorageConnection* aConn, Namespace aNamespace,
     960             :                   const nsAString& aKey, bool* aFoundCacheOut,
     961             :                   CacheId* aCacheIdOut)
     962             : {
     963           0 :   MOZ_ASSERT(!NS_IsMainThread());
     964           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
     965           0 :   MOZ_DIAGNOSTIC_ASSERT(aFoundCacheOut);
     966           0 :   MOZ_DIAGNOSTIC_ASSERT(aCacheIdOut);
     967             : 
     968           0 :   *aFoundCacheOut = false;
     969             : 
     970             :   // How we constrain the key column depends on the value of our key.  Use
     971             :   // a format string for the query and let CreateAndBindKeyStatement() fill
     972             :   // it in for us.
     973             :   const char* query = "SELECT cache_id FROM storage "
     974             :                       "WHERE namespace=:namespace AND %s "
     975           0 :                       "ORDER BY rowid;";
     976             : 
     977           0 :   nsCOMPtr<mozIStorageStatement> state;
     978           0 :   nsresult rv = CreateAndBindKeyStatement(aConn, query, aKey,
     979           0 :                                           getter_AddRefs(state));
     980           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     981             : 
     982           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("namespace"), aNamespace);
     983           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     984             : 
     985           0 :   bool hasMoreData = false;
     986           0 :   rv = state->ExecuteStep(&hasMoreData);
     987           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     988             : 
     989           0 :   if (!hasMoreData) {
     990           0 :     return rv;
     991             :   }
     992             : 
     993           0 :   rv = state->GetInt64(0, aCacheIdOut);
     994           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     995             : 
     996           0 :   *aFoundCacheOut = true;
     997           0 :   return rv;
     998             : }
     999             : 
    1000             : nsresult
    1001           0 : StoragePutCache(mozIStorageConnection* aConn, Namespace aNamespace,
    1002             :                 const nsAString& aKey, CacheId aCacheId)
    1003             : {
    1004           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1005           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    1006             : 
    1007           0 :   nsCOMPtr<mozIStorageStatement> state;
    1008           0 :   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1009             :     "INSERT INTO storage (namespace, key, cache_id) "
    1010             :                  "VALUES (:namespace, :key, :cache_id);"
    1011           0 :   ), getter_AddRefs(state));
    1012           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1013             : 
    1014           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("namespace"), aNamespace);
    1015           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1016             : 
    1017           0 :   rv = state->BindStringAsBlobByName(NS_LITERAL_CSTRING("key"), aKey);
    1018           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1019             : 
    1020           0 :   rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
    1021           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1022             : 
    1023           0 :   rv = state->Execute();
    1024           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1025             : 
    1026           0 :   return rv;
    1027             : }
    1028             : 
    1029             : nsresult
    1030           0 : StorageForgetCache(mozIStorageConnection* aConn, Namespace aNamespace,
    1031             :                    const nsAString& aKey)
    1032             : {
    1033           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1034           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    1035             : 
    1036             :   // How we constrain the key column depends on the value of our key.  Use
    1037             :   // a format string for the query and let CreateAndBindKeyStatement() fill
    1038             :   // it in for us.
    1039           0 :   const char *query = "DELETE FROM storage WHERE namespace=:namespace AND %s;";
    1040             : 
    1041           0 :   nsCOMPtr<mozIStorageStatement> state;
    1042           0 :   nsresult rv = CreateAndBindKeyStatement(aConn, query, aKey,
    1043           0 :                                           getter_AddRefs(state));
    1044           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1045             : 
    1046           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("namespace"), aNamespace);
    1047           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1048             : 
    1049           0 :   rv = state->Execute();
    1050           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1051             : 
    1052           0 :   return rv;
    1053             : }
    1054             : 
    1055             : nsresult
    1056           0 : StorageGetKeys(mozIStorageConnection* aConn, Namespace aNamespace,
    1057             :                nsTArray<nsString>& aKeysOut)
    1058             : {
    1059           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1060           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    1061             : 
    1062           0 :   nsCOMPtr<mozIStorageStatement> state;
    1063           0 :   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1064             :     "SELECT key FROM storage WHERE namespace=:namespace ORDER BY rowid;"
    1065           0 :   ), getter_AddRefs(state));
    1066           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1067             : 
    1068           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("namespace"), aNamespace);
    1069           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1070             : 
    1071           0 :   bool hasMoreData = false;
    1072           0 :   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
    1073           0 :     nsAutoString key;
    1074           0 :     rv = state->GetBlobAsString(0, key);
    1075           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1076             : 
    1077           0 :     aKeysOut.AppendElement(key);
    1078             :   }
    1079             : 
    1080           0 :   return rv;
    1081             : }
    1082             : 
    1083             : namespace {
    1084             : 
    1085             : nsresult
    1086           0 : QueryAll(mozIStorageConnection* aConn, CacheId aCacheId,
    1087             :          nsTArray<EntryId>& aEntryIdListOut)
    1088             : {
    1089           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1090           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    1091             : 
    1092           0 :   nsCOMPtr<mozIStorageStatement> state;
    1093           0 :   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1094             :     "SELECT id FROM entries WHERE cache_id=:cache_id ORDER BY id;"
    1095           0 :   ), getter_AddRefs(state));
    1096           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1097             : 
    1098           0 :   rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
    1099           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1100             : 
    1101           0 :   bool hasMoreData = false;
    1102           0 :   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
    1103           0 :     EntryId entryId = INT32_MAX;
    1104           0 :     rv = state->GetInt32(0, &entryId);
    1105           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1106           0 :     aEntryIdListOut.AppendElement(entryId);
    1107             :   }
    1108             : 
    1109           0 :   return rv;
    1110             : }
    1111             : 
    1112             : nsresult
    1113           0 : QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
    1114             :            const CacheRequest& aRequest,
    1115             :            const CacheQueryParams& aParams,
    1116             :            nsTArray<EntryId>& aEntryIdListOut,
    1117             :            uint32_t aMaxResults)
    1118             : {
    1119           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1120           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    1121           0 :   MOZ_DIAGNOSTIC_ASSERT(aMaxResults > 0);
    1122             : 
    1123           0 :   if (!aParams.ignoreMethod() &&
    1124           0 :       !aRequest.method().LowerCaseEqualsLiteral("get"))
    1125             :   {
    1126           0 :     return NS_OK;
    1127             :   }
    1128             : 
    1129             :   nsAutoCString query(
    1130             :     "SELECT id, COUNT(response_headers.name) AS vary_count "
    1131             :     "FROM entries "
    1132             :     "LEFT OUTER JOIN response_headers ON entries.id=response_headers.entry_id "
    1133             :                                     "AND response_headers.name='vary' "
    1134             :     "WHERE entries.cache_id=:cache_id "
    1135             :       "AND entries.request_url_no_query_hash=:url_no_query_hash "
    1136           0 :   );
    1137             : 
    1138           0 :   if (!aParams.ignoreSearch()) {
    1139           0 :     query.AppendLiteral("AND entries.request_url_query_hash=:url_query_hash ");
    1140             :   }
    1141             : 
    1142           0 :   query.AppendLiteral("AND entries.request_url_no_query=:url_no_query ");
    1143             : 
    1144           0 :   if (!aParams.ignoreSearch()) {
    1145           0 :     query.AppendLiteral("AND entries.request_url_query=:url_query ");
    1146             :   }
    1147             : 
    1148           0 :   query.AppendLiteral("GROUP BY entries.id ORDER BY entries.id;");
    1149             : 
    1150           0 :   nsCOMPtr<mozIStorageStatement> state;
    1151           0 :   nsresult rv = aConn->CreateStatement(query, getter_AddRefs(state));
    1152           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1153             : 
    1154           0 :   rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
    1155           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1156             : 
    1157             :   nsCOMPtr<nsICryptoHash> crypto =
    1158           0 :     do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
    1159           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1160             : 
    1161           0 :   nsAutoCString urlWithoutQueryHash;
    1162           0 :   rv = HashCString(crypto, aRequest.urlWithoutQuery(), urlWithoutQueryHash);
    1163           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1164             : 
    1165           0 :   rv = state->BindUTF8StringAsBlobByName(NS_LITERAL_CSTRING("url_no_query_hash"),
    1166           0 :                                          urlWithoutQueryHash);
    1167           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1168             : 
    1169           0 :   if (!aParams.ignoreSearch()) {
    1170           0 :     nsAutoCString urlQueryHash;
    1171           0 :     rv = HashCString(crypto, aRequest.urlQuery(), urlQueryHash);
    1172           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1173             : 
    1174           0 :     rv = state->BindUTF8StringAsBlobByName(NS_LITERAL_CSTRING("url_query_hash"),
    1175           0 :                                            urlQueryHash);
    1176           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1177             :   }
    1178             : 
    1179           0 :   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("url_no_query"),
    1180           0 :                                    aRequest.urlWithoutQuery());
    1181           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1182             : 
    1183           0 :   if (!aParams.ignoreSearch()) {
    1184           0 :     rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("url_query"),
    1185           0 :                                      aRequest.urlQuery());
    1186           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1187             :   }
    1188             : 
    1189           0 :   bool hasMoreData = false;
    1190           0 :   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
    1191             :     // no invalid EntryId, init to least likely real value
    1192           0 :     EntryId entryId = INT32_MAX;
    1193           0 :     rv = state->GetInt32(0, &entryId);
    1194           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1195             : 
    1196             :     int32_t varyCount;
    1197           0 :     rv = state->GetInt32(1, &varyCount);
    1198           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1199             : 
    1200           0 :     if (!aParams.ignoreVary() && varyCount > 0) {
    1201           0 :       bool matchedByVary = false;
    1202           0 :       rv = MatchByVaryHeader(aConn, aRequest, entryId, &matchedByVary);
    1203           0 :       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1204           0 :       if (!matchedByVary) {
    1205           0 :         continue;
    1206             :       }
    1207             :     }
    1208             : 
    1209           0 :     aEntryIdListOut.AppendElement(entryId);
    1210             : 
    1211           0 :     if (aEntryIdListOut.Length() == aMaxResults) {
    1212           0 :       return NS_OK;
    1213             :     }
    1214             :   }
    1215             : 
    1216           0 :   return rv;
    1217             : }
    1218             : 
    1219             : nsresult
    1220           0 : MatchByVaryHeader(mozIStorageConnection* aConn,
    1221             :                   const CacheRequest& aRequest,
    1222             :                   EntryId entryId, bool* aSuccessOut)
    1223             : {
    1224           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1225           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    1226             : 
    1227           0 :   *aSuccessOut = false;
    1228             : 
    1229           0 :   nsCOMPtr<mozIStorageStatement> state;
    1230           0 :   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1231             :     "SELECT value FROM response_headers "
    1232             :     "WHERE name='vary' AND entry_id=:entry_id;"
    1233           0 :   ), getter_AddRefs(state));
    1234           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1235             : 
    1236           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), entryId);
    1237           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1238             : 
    1239           0 :   AutoTArray<nsCString, 8> varyValues;
    1240             : 
    1241           0 :   bool hasMoreData = false;
    1242           0 :   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
    1243           0 :     nsAutoCString value;
    1244           0 :     rv = state->GetUTF8String(0, value);
    1245           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1246           0 :     varyValues.AppendElement(value);
    1247             :   }
    1248           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1249             : 
    1250             :   // Should not have called this function if this was not the case
    1251           0 :   MOZ_DIAGNOSTIC_ASSERT(!varyValues.IsEmpty());
    1252             : 
    1253           0 :   state->Reset();
    1254           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1255             :     "SELECT name, value FROM request_headers "
    1256             :     "WHERE entry_id=:entry_id;"
    1257           0 :   ), getter_AddRefs(state));
    1258           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1259             : 
    1260           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), entryId);
    1261           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1262             : 
    1263             :   RefPtr<InternalHeaders> cachedHeaders =
    1264           0 :     new InternalHeaders(HeadersGuardEnum::None);
    1265             : 
    1266           0 :   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
    1267           0 :     nsAutoCString name;
    1268           0 :     nsAutoCString value;
    1269           0 :     rv = state->GetUTF8String(0, name);
    1270           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1271           0 :     rv = state->GetUTF8String(1, value);
    1272           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1273             : 
    1274           0 :     ErrorResult errorResult;
    1275             : 
    1276           0 :     cachedHeaders->Append(name, value, errorResult);
    1277           0 :     if (errorResult.Failed()) { return errorResult.StealNSResult(); }
    1278             :   }
    1279           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1280             : 
    1281             :   RefPtr<InternalHeaders> queryHeaders =
    1282           0 :     TypeUtils::ToInternalHeaders(aRequest.headers());
    1283             : 
    1284             :   // Assume the vary headers match until we find a conflict
    1285           0 :   bool varyHeadersMatch = true;
    1286             : 
    1287           0 :   for (uint32_t i = 0; i < varyValues.Length(); ++i) {
    1288             :     // Extract the header names inside the Vary header value.
    1289           0 :     nsAutoCString varyValue(varyValues[i]);
    1290           0 :     char* rawBuffer = varyValue.BeginWriting();
    1291           0 :     char* token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer);
    1292           0 :     bool bailOut = false;
    1293           0 :     for (; token;
    1294           0 :          token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer)) {
    1295           0 :       nsDependentCString header(token);
    1296           0 :       MOZ_DIAGNOSTIC_ASSERT(!header.EqualsLiteral("*"),
    1297             :                             "We should have already caught this in "
    1298             :                             "TypeUtils::ToPCacheResponseWithoutBody()");
    1299             : 
    1300           0 :       ErrorResult errorResult;
    1301           0 :       nsAutoCString queryValue;
    1302           0 :       queryHeaders->Get(header, queryValue, errorResult);
    1303           0 :       if (errorResult.Failed()) {
    1304           0 :         errorResult.SuppressException();
    1305           0 :         MOZ_DIAGNOSTIC_ASSERT(queryValue.IsEmpty());
    1306             :       }
    1307             : 
    1308           0 :       nsAutoCString cachedValue;
    1309           0 :       cachedHeaders->Get(header, cachedValue, errorResult);
    1310           0 :       if (errorResult.Failed()) {
    1311           0 :         errorResult.SuppressException();
    1312           0 :         MOZ_DIAGNOSTIC_ASSERT(cachedValue.IsEmpty());
    1313             :       }
    1314             : 
    1315           0 :       if (queryValue != cachedValue) {
    1316           0 :         varyHeadersMatch = false;
    1317           0 :         bailOut = true;
    1318           0 :         break;
    1319             :       }
    1320             :     }
    1321             : 
    1322           0 :     if (bailOut) {
    1323           0 :       break;
    1324             :     }
    1325             :   }
    1326             : 
    1327           0 :   *aSuccessOut = varyHeadersMatch;
    1328           0 :   return rv;
    1329             : }
    1330             : 
    1331             : nsresult
    1332           0 : DeleteEntries(mozIStorageConnection* aConn,
    1333             :               const nsTArray<EntryId>& aEntryIdList,
    1334             :               nsTArray<nsID>& aDeletedBodyIdListOut,
    1335             :               nsTArray<IdCount>& aDeletedSecurityIdListOut,
    1336             :               uint32_t aPos, int32_t aLen)
    1337             : {
    1338           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1339           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    1340             : 
    1341           0 :   if (aEntryIdList.IsEmpty()) {
    1342           0 :     return NS_OK;
    1343             :   }
    1344             : 
    1345           0 :   MOZ_DIAGNOSTIC_ASSERT(aPos < aEntryIdList.Length());
    1346             : 
    1347           0 :   if (aLen < 0) {
    1348           0 :     aLen = aEntryIdList.Length() - aPos;
    1349             :   }
    1350             : 
    1351             :   // Sqlite limits the number of entries allowed for an IN clause,
    1352             :   // so split up larger operations.
    1353           0 :   if (aLen > kMaxEntriesPerStatement) {
    1354           0 :     uint32_t curPos = aPos;
    1355           0 :     int32_t remaining = aLen;
    1356           0 :     while (remaining > 0) {
    1357           0 :       int32_t max = kMaxEntriesPerStatement;
    1358           0 :       int32_t curLen = std::min(max, remaining);
    1359             :       nsresult rv = DeleteEntries(aConn, aEntryIdList, aDeletedBodyIdListOut,
    1360           0 :                                   aDeletedSecurityIdListOut, curPos, curLen);
    1361           0 :       if (NS_FAILED(rv)) { return rv; }
    1362             : 
    1363           0 :       curPos += curLen;
    1364           0 :       remaining -= curLen;
    1365             :     }
    1366           0 :     return NS_OK;
    1367             :   }
    1368             : 
    1369           0 :   nsCOMPtr<mozIStorageStatement> state;
    1370             :   nsAutoCString query(
    1371             :     "SELECT request_body_id, response_body_id, response_security_info_id "
    1372             :     "FROM entries WHERE id IN ("
    1373           0 :   );
    1374           0 :   AppendListParamsToQuery(query, aEntryIdList, aPos, aLen);
    1375           0 :   query.AppendLiteral(")");
    1376             : 
    1377           0 :   nsresult rv = aConn->CreateStatement(query, getter_AddRefs(state));
    1378           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1379             : 
    1380           0 :   rv = BindListParamsToQuery(state, aEntryIdList, aPos, aLen);
    1381           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1382             : 
    1383           0 :   bool hasMoreData = false;
    1384           0 :   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
    1385             :     // extract 0 to 2 nsID structs per row
    1386           0 :     for (uint32_t i = 0; i < 2; ++i) {
    1387           0 :       bool isNull = false;
    1388             : 
    1389           0 :       rv = state->GetIsNull(i, &isNull);
    1390           0 :       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1391             : 
    1392           0 :       if (!isNull) {
    1393             :         nsID id;
    1394           0 :         rv = ExtractId(state, i, &id);
    1395           0 :         if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1396           0 :         aDeletedBodyIdListOut.AppendElement(id);
    1397             :       }
    1398             :     }
    1399             : 
    1400             :     // and then a possible third entry for the security id
    1401           0 :     bool isNull = false;
    1402           0 :     rv = state->GetIsNull(2, &isNull);
    1403           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1404             : 
    1405           0 :     if (!isNull) {
    1406           0 :       int32_t securityId = -1;
    1407           0 :       rv = state->GetInt32(2, &securityId);
    1408           0 :       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1409             : 
    1410             :       // First try to increment the count for this ID if we're already
    1411             :       // seen it
    1412           0 :       bool found = false;
    1413           0 :       for (uint32_t i = 0; i < aDeletedSecurityIdListOut.Length(); ++i) {
    1414           0 :         if (aDeletedSecurityIdListOut[i].mId == securityId) {
    1415           0 :           found = true;
    1416           0 :           aDeletedSecurityIdListOut[i].mCount += 1;
    1417           0 :           break;
    1418             :         }
    1419             :       }
    1420             : 
    1421             :       // Otherwise add a new entry for this ID with a count of 1
    1422           0 :       if (!found) {
    1423           0 :         aDeletedSecurityIdListOut.AppendElement(IdCount(securityId));
    1424             :       }
    1425             :     }
    1426             :   }
    1427             : 
    1428             :   // Dependent records removed via ON DELETE CASCADE
    1429             : 
    1430           0 :   query = NS_LITERAL_CSTRING(
    1431             :     "DELETE FROM entries WHERE id IN ("
    1432           0 :   );
    1433           0 :   AppendListParamsToQuery(query, aEntryIdList, aPos, aLen);
    1434           0 :   query.AppendLiteral(")");
    1435             : 
    1436           0 :   rv = aConn->CreateStatement(query, getter_AddRefs(state));
    1437           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1438             : 
    1439           0 :   rv = BindListParamsToQuery(state, aEntryIdList, aPos, aLen);
    1440           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1441             : 
    1442           0 :   rv = state->Execute();
    1443           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1444             : 
    1445           0 :   return rv;
    1446             : }
    1447             : 
    1448             : nsresult
    1449           0 : InsertSecurityInfo(mozIStorageConnection* aConn, nsICryptoHash* aCrypto,
    1450             :                    const nsACString& aData, int32_t *aIdOut)
    1451             : {
    1452           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    1453           0 :   MOZ_DIAGNOSTIC_ASSERT(aCrypto);
    1454           0 :   MOZ_DIAGNOSTIC_ASSERT(aIdOut);
    1455           0 :   MOZ_DIAGNOSTIC_ASSERT(!aData.IsEmpty());
    1456             : 
    1457             :   // We want to use an index to find existing security blobs, but indexing
    1458             :   // the full blob would be quite expensive.  Instead, we index a small
    1459             :   // hash value.  Calculate this hash as the first 8 bytes of the SHA1 of
    1460             :   // the full data.
    1461           0 :   nsAutoCString hash;
    1462           0 :   nsresult rv = HashCString(aCrypto, aData, hash);
    1463           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1464             : 
    1465             :   // Next, search for an existing entry for this blob by comparing the hash
    1466             :   // value first and then the full data.  SQLite is smart enough to use
    1467             :   // the index on the hash to search the table before doing the expensive
    1468             :   // comparison of the large data column.  (This was verified with EXPLAIN.)
    1469           0 :   nsCOMPtr<mozIStorageStatement> state;
    1470           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1471             :     // Note that hash and data are blobs, but we can use = here since the
    1472             :     // columns are NOT NULL.
    1473             :     "SELECT id, refcount FROM security_info WHERE hash=:hash AND data=:data;"
    1474           0 :   ), getter_AddRefs(state));
    1475           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1476             : 
    1477           0 :   rv = state->BindUTF8StringAsBlobByName(NS_LITERAL_CSTRING("hash"), hash);
    1478           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1479             : 
    1480           0 :   rv = state->BindUTF8StringAsBlobByName(NS_LITERAL_CSTRING("data"), aData);
    1481           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1482             : 
    1483           0 :   bool hasMoreData = false;
    1484           0 :   rv = state->ExecuteStep(&hasMoreData);
    1485           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1486             : 
    1487             :   // This security info blob is already in the database
    1488           0 :   if (hasMoreData) {
    1489             :     // get the existing security blob id to return
    1490           0 :     rv = state->GetInt32(0, aIdOut);
    1491           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1492             : 
    1493           0 :     int32_t refcount = -1;
    1494           0 :     rv = state->GetInt32(1, &refcount);
    1495           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1496             : 
    1497             :     // But first, update the refcount in the database.
    1498           0 :     refcount += 1;
    1499             : 
    1500           0 :     rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1501             :       "UPDATE security_info SET refcount=:refcount WHERE id=:id;"
    1502           0 :     ), getter_AddRefs(state));
    1503           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1504             : 
    1505           0 :     rv = state->BindInt32ByName(NS_LITERAL_CSTRING("refcount"), refcount);
    1506           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1507             : 
    1508           0 :     rv = state->BindInt32ByName(NS_LITERAL_CSTRING("id"), *aIdOut);
    1509           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1510             : 
    1511           0 :     rv = state->Execute();
    1512           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1513             : 
    1514           0 :     return NS_OK;
    1515             :   }
    1516             : 
    1517             :   // This is a new security info blob.  Create a new row in the security table
    1518             :   // with an initial refcount of 1.
    1519           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1520             :     "INSERT INTO security_info (hash, data, refcount) VALUES (:hash, :data, 1);"
    1521           0 :   ), getter_AddRefs(state));
    1522           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1523             : 
    1524           0 :   rv = state->BindUTF8StringAsBlobByName(NS_LITERAL_CSTRING("hash"), hash);
    1525           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1526             : 
    1527           0 :   rv = state->BindUTF8StringAsBlobByName(NS_LITERAL_CSTRING("data"), aData);
    1528           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1529             : 
    1530           0 :   rv = state->Execute();
    1531           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1532             : 
    1533           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1534             :     "SELECT last_insert_rowid()"
    1535           0 :   ), getter_AddRefs(state));
    1536           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1537             : 
    1538           0 :   hasMoreData = false;
    1539           0 :   rv = state->ExecuteStep(&hasMoreData);
    1540           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1541             : 
    1542           0 :   rv = state->GetInt32(0, aIdOut);
    1543           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1544             : 
    1545           0 :   return NS_OK;
    1546             : }
    1547             : 
    1548             : nsresult
    1549           0 : DeleteSecurityInfo(mozIStorageConnection* aConn, int32_t aId, int32_t aCount)
    1550             : {
    1551             :   // First, we need to determine the current refcount for this security blob.
    1552           0 :   nsCOMPtr<mozIStorageStatement> state;
    1553           0 :   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1554             :     "SELECT refcount FROM security_info WHERE id=:id;"
    1555           0 :   ), getter_AddRefs(state));
    1556           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1557             : 
    1558           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("id"), aId);
    1559           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1560             : 
    1561           0 :   bool hasMoreData = false;
    1562           0 :   rv = state->ExecuteStep(&hasMoreData);
    1563           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1564             : 
    1565           0 :   int32_t refcount = -1;
    1566           0 :   rv = state->GetInt32(0, &refcount);
    1567           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1568             : 
    1569           0 :   MOZ_DIAGNOSTIC_ASSERT(refcount >= aCount);
    1570             : 
    1571             :   // Next, calculate the new refcount
    1572           0 :   int32_t newCount = refcount - aCount;
    1573             : 
    1574             :   // If the last reference to this security blob was removed we can
    1575             :   // just remove the entire row.
    1576           0 :   if (newCount == 0) {
    1577           0 :     rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1578             :       "DELETE FROM security_info WHERE id=:id;"
    1579           0 :     ), getter_AddRefs(state));
    1580           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1581             : 
    1582           0 :     rv = state->BindInt32ByName(NS_LITERAL_CSTRING("id"), aId);
    1583           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1584             : 
    1585           0 :     rv = state->Execute();
    1586           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1587             : 
    1588           0 :     return NS_OK;
    1589             :   }
    1590             : 
    1591             :   // Otherwise update the refcount in the table to reflect the reduced
    1592             :   // number of references to the security blob.
    1593           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1594             :     "UPDATE security_info SET refcount=:refcount WHERE id=:id;"
    1595           0 :   ), getter_AddRefs(state));
    1596           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1597             : 
    1598           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("refcount"), newCount);
    1599           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1600             : 
    1601           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("id"), aId);
    1602           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1603             : 
    1604           0 :   rv = state->Execute();
    1605           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1606             : 
    1607           0 :   return NS_OK;
    1608             : }
    1609             : 
    1610             : nsresult
    1611           0 : DeleteSecurityInfoList(mozIStorageConnection* aConn,
    1612             :                         const nsTArray<IdCount>& aDeletedStorageIdList)
    1613             : {
    1614           0 :   for (uint32_t i = 0; i < aDeletedStorageIdList.Length(); ++i) {
    1615           0 :     nsresult rv = DeleteSecurityInfo(aConn, aDeletedStorageIdList[i].mId,
    1616           0 :                                      aDeletedStorageIdList[i].mCount);
    1617           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1618             :   }
    1619             : 
    1620           0 :   return NS_OK;
    1621             : }
    1622             : 
    1623             : nsresult
    1624           0 : InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
    1625             :             const CacheRequest& aRequest,
    1626             :             const nsID* aRequestBodyId,
    1627             :             const CacheResponse& aResponse,
    1628             :             const nsID* aResponseBodyId)
    1629             : {
    1630           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1631           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    1632             : 
    1633           0 :   nsresult rv = NS_OK;
    1634             : 
    1635             :   nsCOMPtr<nsICryptoHash> crypto =
    1636           0 :     do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
    1637           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1638             : 
    1639           0 :   int32_t securityId = -1;
    1640           0 :   if (!aResponse.channelInfo().securityInfo().IsEmpty()) {
    1641           0 :     rv = InsertSecurityInfo(aConn, crypto,
    1642           0 :                             aResponse.channelInfo().securityInfo(),
    1643             :                             &securityId);
    1644           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1645             :   }
    1646             : 
    1647           0 :   nsCOMPtr<mozIStorageStatement> state;
    1648           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1649             :     "INSERT INTO entries ("
    1650             :       "request_method, "
    1651             :       "request_url_no_query, "
    1652             :       "request_url_no_query_hash, "
    1653             :       "request_url_query, "
    1654             :       "request_url_query_hash, "
    1655             :       "request_url_fragment, "
    1656             :       "request_referrer, "
    1657             :       "request_referrer_policy, "
    1658             :       "request_headers_guard, "
    1659             :       "request_mode, "
    1660             :       "request_credentials, "
    1661             :       "request_contentpolicytype, "
    1662             :       "request_cache, "
    1663             :       "request_redirect, "
    1664             :       "request_integrity, "
    1665             :       "request_body_id, "
    1666             :       "response_type, "
    1667             :       "response_status, "
    1668             :       "response_status_text, "
    1669             :       "response_headers_guard, "
    1670             :       "response_body_id, "
    1671             :       "response_security_info_id, "
    1672             :       "response_principal_info, "
    1673             :       "cache_id "
    1674             :     ") VALUES ("
    1675             :       ":request_method, "
    1676             :       ":request_url_no_query, "
    1677             :       ":request_url_no_query_hash, "
    1678             :       ":request_url_query, "
    1679             :       ":request_url_query_hash, "
    1680             :       ":request_url_fragment, "
    1681             :       ":request_referrer, "
    1682             :       ":request_referrer_policy, "
    1683             :       ":request_headers_guard, "
    1684             :       ":request_mode, "
    1685             :       ":request_credentials, "
    1686             :       ":request_contentpolicytype, "
    1687             :       ":request_cache, "
    1688             :       ":request_redirect, "
    1689             :       ":request_integrity, "
    1690             :       ":request_body_id, "
    1691             :       ":response_type, "
    1692             :       ":response_status, "
    1693             :       ":response_status_text, "
    1694             :       ":response_headers_guard, "
    1695             :       ":response_body_id, "
    1696             :       ":response_security_info_id, "
    1697             :       ":response_principal_info, "
    1698             :       ":cache_id "
    1699             :     ");"
    1700           0 :   ), getter_AddRefs(state));
    1701           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1702             : 
    1703           0 :   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_method"),
    1704           0 :                                    aRequest.method());
    1705           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1706             : 
    1707           0 :   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_url_no_query"),
    1708           0 :                                    aRequest.urlWithoutQuery());
    1709           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1710             : 
    1711           0 :   nsAutoCString urlWithoutQueryHash;
    1712           0 :   rv = HashCString(crypto, aRequest.urlWithoutQuery(), urlWithoutQueryHash);
    1713           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1714             : 
    1715           0 :   rv = state->BindUTF8StringAsBlobByName(
    1716           0 :     NS_LITERAL_CSTRING("request_url_no_query_hash"), urlWithoutQueryHash);
    1717           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1718             : 
    1719           0 :   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_url_query"),
    1720           0 :                                    aRequest.urlQuery());
    1721           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1722             : 
    1723           0 :   nsAutoCString urlQueryHash;
    1724           0 :   rv = HashCString(crypto, aRequest.urlQuery(), urlQueryHash);
    1725           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1726           0 :   rv = state->BindUTF8StringAsBlobByName(
    1727           0 :     NS_LITERAL_CSTRING("request_url_query_hash"), urlQueryHash);
    1728           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1729           0 :   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("request_url_fragment"),
    1730           0 :                                    aRequest.urlFragment());
    1731           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1732             : 
    1733           0 :   rv = state->BindStringByName(NS_LITERAL_CSTRING("request_referrer"),
    1734           0 :                                aRequest.referrer());
    1735           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1736           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_referrer_policy"),
    1737           0 :                               static_cast<int32_t>(aRequest.referrerPolicy()));
    1738           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1739           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_headers_guard"),
    1740           0 :     static_cast<int32_t>(aRequest.headersGuard()));
    1741           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1742             : 
    1743           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_mode"),
    1744           0 :                               static_cast<int32_t>(aRequest.mode()));
    1745           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1746             : 
    1747           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_credentials"),
    1748           0 :     static_cast<int32_t>(aRequest.credentials()));
    1749           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1750             : 
    1751           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_contentpolicytype"),
    1752           0 :     static_cast<int32_t>(aRequest.contentPolicyType()));
    1753           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1754             : 
    1755           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_cache"),
    1756           0 :     static_cast<int32_t>(aRequest.requestCache()));
    1757           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1758             : 
    1759           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("request_redirect"),
    1760           0 :     static_cast<int32_t>(aRequest.requestRedirect()));
    1761             : 
    1762           0 :   rv = state->BindStringByName(NS_LITERAL_CSTRING("request_integrity"),
    1763           0 :                                aRequest.integrity());
    1764             : 
    1765           0 :   rv = BindId(state, NS_LITERAL_CSTRING("request_body_id"), aRequestBodyId);
    1766           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1767             : 
    1768           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("response_type"),
    1769           0 :                               static_cast<int32_t>(aResponse.type()));
    1770           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1771             : 
    1772           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("response_status"),
    1773           0 :                               aResponse.status());
    1774           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1775             : 
    1776           0 :   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("response_status_text"),
    1777           0 :                                    aResponse.statusText());
    1778           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1779             : 
    1780           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("response_headers_guard"),
    1781           0 :     static_cast<int32_t>(aResponse.headersGuard()));
    1782           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1783             : 
    1784           0 :   rv = BindId(state, NS_LITERAL_CSTRING("response_body_id"), aResponseBodyId);
    1785           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1786             : 
    1787           0 :   if (aResponse.channelInfo().securityInfo().IsEmpty()) {
    1788           0 :     rv = state->BindNullByName(NS_LITERAL_CSTRING("response_security_info_id"));
    1789             :   } else {
    1790           0 :     rv = state->BindInt32ByName(NS_LITERAL_CSTRING("response_security_info_id"),
    1791           0 :                                 securityId);
    1792             :   }
    1793           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1794             : 
    1795           0 :   nsAutoCString serializedInfo;
    1796             :   // We only allow content serviceworkers right now.
    1797           0 :   if (aResponse.principalInfo().type() == mozilla::ipc::OptionalPrincipalInfo::TPrincipalInfo) {
    1798             :     const mozilla::ipc::PrincipalInfo& principalInfo =
    1799           0 :       aResponse.principalInfo().get_PrincipalInfo();
    1800           0 :     MOZ_DIAGNOSTIC_ASSERT(principalInfo.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
    1801             :     const mozilla::ipc::ContentPrincipalInfo& cInfo =
    1802           0 :       principalInfo.get_ContentPrincipalInfo();
    1803             : 
    1804           0 :     serializedInfo.Append(cInfo.spec());
    1805             : 
    1806           0 :     nsAutoCString suffix;
    1807           0 :     cInfo.attrs().CreateSuffix(suffix);
    1808           0 :     serializedInfo.Append(suffix);
    1809             :   }
    1810             : 
    1811           0 :   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("response_principal_info"),
    1812           0 :                                    serializedInfo);
    1813           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1814             : 
    1815           0 :   rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
    1816           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1817             : 
    1818           0 :   rv = state->Execute();
    1819           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1820             : 
    1821           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1822             :     "SELECT last_insert_rowid()"
    1823           0 :   ), getter_AddRefs(state));
    1824           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1825             : 
    1826           0 :   bool hasMoreData = false;
    1827           0 :   rv = state->ExecuteStep(&hasMoreData);
    1828           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1829             : 
    1830             :   int32_t entryId;
    1831           0 :   rv = state->GetInt32(0, &entryId);
    1832           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1833             : 
    1834           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1835             :     "INSERT INTO request_headers ("
    1836             :       "name, "
    1837             :       "value, "
    1838             :       "entry_id "
    1839             :     ") VALUES (:name, :value, :entry_id)"
    1840           0 :   ), getter_AddRefs(state));
    1841           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1842             : 
    1843           0 :   const nsTArray<HeadersEntry>& requestHeaders = aRequest.headers();
    1844           0 :   for (uint32_t i = 0; i < requestHeaders.Length(); ++i) {
    1845           0 :     rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("name"),
    1846           0 :                                      requestHeaders[i].name());
    1847           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1848             : 
    1849           0 :     rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("value"),
    1850           0 :                                      requestHeaders[i].value());
    1851           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1852             : 
    1853           0 :     rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), entryId);
    1854           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1855             : 
    1856           0 :     rv = state->Execute();
    1857           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1858             :   }
    1859             : 
    1860           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1861             :     "INSERT INTO response_headers ("
    1862             :       "name, "
    1863             :       "value, "
    1864             :       "entry_id "
    1865             :     ") VALUES (:name, :value, :entry_id)"
    1866           0 :   ), getter_AddRefs(state));
    1867           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1868             : 
    1869           0 :   const nsTArray<HeadersEntry>& responseHeaders = aResponse.headers();
    1870           0 :   for (uint32_t i = 0; i < responseHeaders.Length(); ++i) {
    1871           0 :     rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("name"),
    1872           0 :                                      responseHeaders[i].name());
    1873           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1874             : 
    1875           0 :     rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("value"),
    1876           0 :                                      responseHeaders[i].value());
    1877           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1878             : 
    1879           0 :     rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), entryId);
    1880           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1881             : 
    1882           0 :     rv = state->Execute();
    1883           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1884             :   }
    1885             : 
    1886           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1887             :     "INSERT INTO response_url_list ("
    1888             :       "url, "
    1889             :       "entry_id "
    1890             :     ") VALUES (:url, :entry_id)"
    1891           0 :   ), getter_AddRefs(state));
    1892           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1893             : 
    1894           0 :   const nsTArray<nsCString>& responseUrlList = aResponse.urlList();
    1895           0 :   for (uint32_t i = 0; i < responseUrlList.Length(); ++i) {
    1896           0 :     rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("url"),
    1897           0 :                                      responseUrlList[i]);
    1898           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1899             : 
    1900           0 :     rv = state->BindInt64ByName(NS_LITERAL_CSTRING("entry_id"), entryId);
    1901           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1902             : 
    1903           0 :     rv = state->Execute();
    1904           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1905             :   }
    1906             : 
    1907           0 :   return rv;
    1908             : }
    1909             : 
    1910             : nsresult
    1911           0 : ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
    1912             :              SavedResponse* aSavedResponseOut)
    1913             : {
    1914           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1915           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    1916           0 :   MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut);
    1917             : 
    1918           0 :   nsCOMPtr<mozIStorageStatement> state;
    1919           0 :   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1920             :     "SELECT "
    1921             :       "entries.response_type, "
    1922             :       "entries.response_status, "
    1923             :       "entries.response_status_text, "
    1924             :       "entries.response_headers_guard, "
    1925             :       "entries.response_body_id, "
    1926             :       "entries.response_principal_info, "
    1927             :       "security_info.data "
    1928             :     "FROM entries "
    1929             :     "LEFT OUTER JOIN security_info "
    1930             :     "ON entries.response_security_info_id=security_info.id "
    1931             :     "WHERE entries.id=:id;"
    1932           0 :   ), getter_AddRefs(state));
    1933           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1934             : 
    1935           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("id"), aEntryId);
    1936           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1937             : 
    1938           0 :   bool hasMoreData = false;
    1939           0 :   rv = state->ExecuteStep(&hasMoreData);
    1940           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1941             : 
    1942             :   int32_t type;
    1943           0 :   rv = state->GetInt32(0, &type);
    1944           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1945           0 :   aSavedResponseOut->mValue.type() = static_cast<ResponseType>(type);
    1946             : 
    1947             :   int32_t status;
    1948           0 :   rv = state->GetInt32(1, &status);
    1949           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1950           0 :   aSavedResponseOut->mValue.status() = status;
    1951             : 
    1952           0 :   rv = state->GetUTF8String(2, aSavedResponseOut->mValue.statusText());
    1953           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1954             : 
    1955             :   int32_t guard;
    1956           0 :   rv = state->GetInt32(3, &guard);
    1957           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1958           0 :   aSavedResponseOut->mValue.headersGuard() =
    1959           0 :     static_cast<HeadersGuardEnum>(guard);
    1960             : 
    1961           0 :   bool nullBody = false;
    1962           0 :   rv = state->GetIsNull(4, &nullBody);
    1963           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1964           0 :   aSavedResponseOut->mHasBodyId = !nullBody;
    1965             : 
    1966           0 :   if (aSavedResponseOut->mHasBodyId) {
    1967           0 :     rv = ExtractId(state, 4, &aSavedResponseOut->mBodyId);
    1968           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1969             :   }
    1970             : 
    1971           0 :   nsAutoCString serializedInfo;
    1972           0 :   rv = state->GetUTF8String(5, serializedInfo);
    1973           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1974             : 
    1975           0 :   aSavedResponseOut->mValue.principalInfo() = void_t();
    1976           0 :   if (!serializedInfo.IsEmpty()) {
    1977           0 :     nsAutoCString specNoSuffix;
    1978           0 :     OriginAttributes attrs;
    1979           0 :     if (!attrs.PopulateFromOrigin(serializedInfo, specNoSuffix)) {
    1980           0 :       NS_WARNING("Something went wrong parsing a serialized principal!");
    1981           0 :       return NS_ERROR_FAILURE;
    1982             :     }
    1983             : 
    1984           0 :     aSavedResponseOut->mValue.principalInfo() =
    1985           0 :       mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), specNoSuffix);
    1986             :   }
    1987             : 
    1988           0 :   rv = state->GetBlobAsUTF8String(6, aSavedResponseOut->mValue.channelInfo().securityInfo());
    1989           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1990             : 
    1991           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    1992             :     "SELECT "
    1993             :       "name, "
    1994             :       "value "
    1995             :     "FROM response_headers "
    1996             :     "WHERE entry_id=:entry_id;"
    1997           0 :   ), getter_AddRefs(state));
    1998           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    1999             : 
    2000           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), aEntryId);
    2001           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2002             : 
    2003           0 :   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
    2004           0 :     HeadersEntry header;
    2005             : 
    2006           0 :     rv = state->GetUTF8String(0, header.name());
    2007           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2008             : 
    2009           0 :     rv = state->GetUTF8String(1, header.value());
    2010           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2011             : 
    2012           0 :     aSavedResponseOut->mValue.headers().AppendElement(header);
    2013             :   }
    2014             : 
    2015           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    2016             :     "SELECT "
    2017             :       "url "
    2018             :     "FROM response_url_list "
    2019             :     "WHERE entry_id=:entry_id;"
    2020           0 :   ), getter_AddRefs(state));
    2021           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2022             : 
    2023           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), aEntryId);
    2024           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2025             : 
    2026           0 :   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
    2027           0 :     nsCString url;
    2028             : 
    2029           0 :     rv = state->GetUTF8String(0, url);
    2030           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2031             : 
    2032           0 :     aSavedResponseOut->mValue.urlList().AppendElement(url);
    2033             :   }
    2034             : 
    2035           0 :   return rv;
    2036             : }
    2037             : 
    2038             : nsresult
    2039           0 : ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
    2040             :             SavedRequest* aSavedRequestOut)
    2041             : {
    2042           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2043           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    2044           0 :   MOZ_DIAGNOSTIC_ASSERT(aSavedRequestOut);
    2045           0 :   nsCOMPtr<mozIStorageStatement> state;
    2046           0 :   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    2047             :     "SELECT "
    2048             :       "request_method, "
    2049             :       "request_url_no_query, "
    2050             :       "request_url_query, "
    2051             :       "request_url_fragment, "
    2052             :       "request_referrer, "
    2053             :       "request_referrer_policy, "
    2054             :       "request_headers_guard, "
    2055             :       "request_mode, "
    2056             :       "request_credentials, "
    2057             :       "request_contentpolicytype, "
    2058             :       "request_cache, "
    2059             :       "request_redirect, "
    2060             :       "request_integrity, "
    2061             :       "request_body_id "
    2062             :     "FROM entries "
    2063             :     "WHERE id=:id;"
    2064           0 :   ), getter_AddRefs(state));
    2065           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2066             : 
    2067           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("id"), aEntryId);
    2068           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2069             : 
    2070           0 :   bool hasMoreData = false;
    2071           0 :   rv = state->ExecuteStep(&hasMoreData);
    2072           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2073             : 
    2074           0 :   rv = state->GetUTF8String(0, aSavedRequestOut->mValue.method());
    2075           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2076           0 :   rv = state->GetUTF8String(1, aSavedRequestOut->mValue.urlWithoutQuery());
    2077           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2078           0 :   rv = state->GetUTF8String(2, aSavedRequestOut->mValue.urlQuery());
    2079           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2080           0 :   rv = state->GetUTF8String(3, aSavedRequestOut->mValue.urlFragment());
    2081           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2082           0 :   rv = state->GetString(4, aSavedRequestOut->mValue.referrer());
    2083           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2084             : 
    2085             :   int32_t referrerPolicy;
    2086           0 :   rv = state->GetInt32(5, &referrerPolicy);
    2087           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2088           0 :   aSavedRequestOut->mValue.referrerPolicy() =
    2089           0 :     static_cast<ReferrerPolicy>(referrerPolicy);
    2090             :   int32_t guard;
    2091           0 :   rv = state->GetInt32(6, &guard);
    2092           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2093           0 :   aSavedRequestOut->mValue.headersGuard() =
    2094           0 :     static_cast<HeadersGuardEnum>(guard);
    2095             :   int32_t mode;
    2096           0 :   rv = state->GetInt32(7, &mode);
    2097           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2098           0 :   aSavedRequestOut->mValue.mode() = static_cast<RequestMode>(mode);
    2099             :   int32_t credentials;
    2100           0 :   rv = state->GetInt32(8, &credentials);
    2101           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2102           0 :   aSavedRequestOut->mValue.credentials() =
    2103           0 :     static_cast<RequestCredentials>(credentials);
    2104             :   int32_t requestContentPolicyType;
    2105           0 :   rv = state->GetInt32(9, &requestContentPolicyType);
    2106           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2107           0 :   aSavedRequestOut->mValue.contentPolicyType() =
    2108           0 :     static_cast<nsContentPolicyType>(requestContentPolicyType);
    2109             :   int32_t requestCache;
    2110           0 :   rv = state->GetInt32(10, &requestCache);
    2111           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2112           0 :   aSavedRequestOut->mValue.requestCache() =
    2113           0 :     static_cast<RequestCache>(requestCache);
    2114             :   int32_t requestRedirect;
    2115           0 :   rv = state->GetInt32(11, &requestRedirect);
    2116           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2117           0 :   aSavedRequestOut->mValue.requestRedirect() =
    2118           0 :     static_cast<RequestRedirect>(requestRedirect);
    2119           0 :   rv = state->GetString(12, aSavedRequestOut->mValue.integrity());
    2120           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2121           0 :   bool nullBody = false;
    2122           0 :   rv = state->GetIsNull(13, &nullBody);
    2123           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2124           0 :   aSavedRequestOut->mHasBodyId = !nullBody;
    2125           0 :   if (aSavedRequestOut->mHasBodyId) {
    2126           0 :     rv = ExtractId(state, 13, &aSavedRequestOut->mBodyId);
    2127           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2128             :   }
    2129           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    2130             :     "SELECT "
    2131             :       "name, "
    2132             :       "value "
    2133             :     "FROM request_headers "
    2134             :     "WHERE entry_id=:entry_id;"
    2135           0 :   ), getter_AddRefs(state));
    2136           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2137             : 
    2138           0 :   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("entry_id"), aEntryId);
    2139           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2140             : 
    2141           0 :   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
    2142           0 :     HeadersEntry header;
    2143             : 
    2144           0 :     rv = state->GetUTF8String(0, header.name());
    2145           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2146             : 
    2147           0 :     rv = state->GetUTF8String(1, header.value());
    2148           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2149             : 
    2150           0 :     aSavedRequestOut->mValue.headers().AppendElement(header);
    2151             :   }
    2152             : 
    2153           0 :   return rv;
    2154             : }
    2155             : 
    2156             : void
    2157           0 : AppendListParamsToQuery(nsACString& aQuery,
    2158             :                         const nsTArray<EntryId>& aEntryIdList,
    2159             :                         uint32_t aPos, int32_t aLen)
    2160             : {
    2161           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2162           0 :   MOZ_DIAGNOSTIC_ASSERT((aPos + aLen) <= aEntryIdList.Length());
    2163           0 :   for (int32_t i = aPos; i < aLen; ++i) {
    2164           0 :     if (i == 0) {
    2165           0 :       aQuery.AppendLiteral("?");
    2166             :     } else {
    2167           0 :       aQuery.AppendLiteral(",?");
    2168             :     }
    2169             :   }
    2170           0 : }
    2171             : 
    2172             : nsresult
    2173           0 : BindListParamsToQuery(mozIStorageStatement* aState,
    2174             :                       const nsTArray<EntryId>& aEntryIdList,
    2175             :                       uint32_t aPos, int32_t aLen)
    2176             : {
    2177           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2178           0 :   MOZ_DIAGNOSTIC_ASSERT((aPos + aLen) <= aEntryIdList.Length());
    2179           0 :   for (int32_t i = aPos; i < aLen; ++i) {
    2180           0 :     nsresult rv = aState->BindInt32ByIndex(i, aEntryIdList[i]);
    2181           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2182             :   }
    2183           0 :   return NS_OK;
    2184             : }
    2185             : 
    2186             : nsresult
    2187           0 : BindId(mozIStorageStatement* aState, const nsACString& aName, const nsID* aId)
    2188             : {
    2189           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2190           0 :   MOZ_DIAGNOSTIC_ASSERT(aState);
    2191             :   nsresult rv;
    2192             : 
    2193           0 :   if (!aId) {
    2194           0 :     rv = aState->BindNullByName(aName);
    2195           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2196           0 :     return rv;
    2197             :   }
    2198             : 
    2199             :   char idBuf[NSID_LENGTH];
    2200           0 :   aId->ToProvidedString(idBuf);
    2201           0 :   rv = aState->BindUTF8StringByName(aName, nsDependentCString(idBuf));
    2202           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2203             : 
    2204           0 :   return rv;
    2205             : }
    2206             : 
    2207             : nsresult
    2208           0 : ExtractId(mozIStorageStatement* aState, uint32_t aPos, nsID* aIdOut)
    2209             : {
    2210           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2211           0 :   MOZ_DIAGNOSTIC_ASSERT(aState);
    2212           0 :   MOZ_DIAGNOSTIC_ASSERT(aIdOut);
    2213             : 
    2214           0 :   nsAutoCString idString;
    2215           0 :   nsresult rv = aState->GetUTF8String(aPos, idString);
    2216           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2217             : 
    2218           0 :   bool success = aIdOut->Parse(idString.get());
    2219           0 :   if (NS_WARN_IF(!success)) { return NS_ERROR_UNEXPECTED; }
    2220             : 
    2221           0 :   return rv;
    2222             : }
    2223             : 
    2224             : nsresult
    2225           0 : CreateAndBindKeyStatement(mozIStorageConnection* aConn,
    2226             :                           const char* aQueryFormat,
    2227             :                           const nsAString& aKey,
    2228             :                           mozIStorageStatement** aStateOut)
    2229             : {
    2230           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    2231           0 :   MOZ_DIAGNOSTIC_ASSERT(aQueryFormat);
    2232           0 :   MOZ_DIAGNOSTIC_ASSERT(aStateOut);
    2233             : 
    2234             :   // The key is stored as a blob to avoid encoding issues.  An empty string
    2235             :   // is mapped to NULL for blobs.  Normally we would just write the query
    2236             :   // as "key IS :key" to do the proper NULL checking, but that prevents
    2237             :   // sqlite from using the key index.  Therefore use "IS NULL" explicitly
    2238             :   // if the key is empty, otherwise use "=:key" so that sqlite uses the
    2239             :   // index.
    2240           0 :   const char* constraint = nullptr;
    2241           0 :   if (aKey.IsEmpty()) {
    2242           0 :     constraint = "key IS NULL";
    2243             :   } else {
    2244           0 :     constraint = "key=:key";
    2245             :   }
    2246             : 
    2247           0 :   nsPrintfCString query(aQueryFormat, constraint);
    2248             : 
    2249           0 :   nsCOMPtr<mozIStorageStatement> state;
    2250           0 :   nsresult rv = aConn->CreateStatement(query, getter_AddRefs(state));
    2251           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2252             : 
    2253           0 :   if (!aKey.IsEmpty()) {
    2254           0 :     rv = state->BindStringAsBlobByName(NS_LITERAL_CSTRING("key"), aKey);
    2255           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2256             :   }
    2257             : 
    2258           0 :   state.forget(aStateOut);
    2259             : 
    2260           0 :   return rv;
    2261             : }
    2262             : 
    2263             : nsresult
    2264           0 : HashCString(nsICryptoHash* aCrypto, const nsACString& aIn, nsACString& aOut)
    2265             : {
    2266           0 :   MOZ_DIAGNOSTIC_ASSERT(aCrypto);
    2267             : 
    2268           0 :   nsresult rv = aCrypto->Init(nsICryptoHash::SHA1);
    2269           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2270             : 
    2271           0 :   rv = aCrypto->Update(reinterpret_cast<const uint8_t*>(aIn.BeginReading()),
    2272           0 :                        aIn.Length());
    2273           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2274             : 
    2275           0 :   nsAutoCString fullHash;
    2276           0 :   rv = aCrypto->Finish(false /* based64 result */, fullHash);
    2277           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2278             : 
    2279           0 :   aOut = Substring(fullHash, 0, 8);
    2280           0 :   return rv;
    2281             : }
    2282             : 
    2283             : } // namespace
    2284             : 
    2285             : nsresult
    2286           0 : IncrementalVacuum(mozIStorageConnection* aConn)
    2287             : {
    2288             :   // Determine how much free space is in the database.
    2289           0 :   nsCOMPtr<mozIStorageStatement> state;
    2290           0 :   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    2291             :     "PRAGMA freelist_count;"
    2292           0 :   ), getter_AddRefs(state));
    2293           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2294             : 
    2295           0 :   bool hasMoreData = false;
    2296           0 :   rv = state->ExecuteStep(&hasMoreData);
    2297           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2298             : 
    2299           0 :   int32_t freePages = 0;
    2300           0 :   rv = state->GetInt32(0, &freePages);
    2301           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2302             : 
    2303             :   // We have a relatively small page size, so we want to be careful to avoid
    2304             :   // fragmentation.  We already use a growth incremental which will cause
    2305             :   // sqlite to allocate and release multiple pages at the same time.  We can
    2306             :   // further reduce fragmentation by making our allocated chunks a bit
    2307             :   // "sticky".  This is done by creating some hysteresis where we allocate
    2308             :   // pages/chunks as soon as we need them, but we only release pages/chunks
    2309             :   // when we have a large amount of free space.  This helps with the case
    2310             :   // where a page is adding and remove resources causing it to dip back and
    2311             :   // forth across a chunk boundary.
    2312             :   //
    2313             :   // So only proceed with releasing pages if we have more than our constant
    2314             :   // threshold.
    2315           0 :   if (freePages <= kMaxFreePages) {
    2316           0 :     return NS_OK;
    2317             :   }
    2318             : 
    2319             :   // Release the excess pages back to the sqlite VFS.  This may also release
    2320             :   // chunks of multiple pages back to the OS.
    2321           0 :   int32_t pagesToRelease = freePages - kMaxFreePages;
    2322             : 
    2323           0 :   rv = aConn->ExecuteSimpleSQL(nsPrintfCString(
    2324             :     "PRAGMA incremental_vacuum(%d);", pagesToRelease
    2325           0 :   ));
    2326           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2327             : 
    2328             :   // Verify that our incremental vacuum actually did something
    2329             : #ifdef DEBUG
    2330           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    2331             :     "PRAGMA freelist_count;"
    2332           0 :   ), getter_AddRefs(state));
    2333           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2334             : 
    2335           0 :   hasMoreData = false;
    2336           0 :   rv = state->ExecuteStep(&hasMoreData);
    2337           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2338             : 
    2339           0 :   freePages = 0;
    2340           0 :   rv = state->GetInt32(0, &freePages);
    2341           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2342             : 
    2343           0 :   MOZ_ASSERT(freePages <= kMaxFreePages);
    2344             : #endif
    2345             : 
    2346           0 :   return NS_OK;
    2347             : }
    2348             : 
    2349             : namespace {
    2350             : 
    2351             : #ifdef DEBUG
    2352           0 : struct Expect
    2353             : {
    2354             :   // Expect exact SQL
    2355           0 :   Expect(const char* aName, const char* aType, const char* aSql)
    2356           0 :     : mName(aName)
    2357             :     , mType(aType)
    2358             :     , mSql(aSql)
    2359           0 :     , mIgnoreSql(false)
    2360           0 :   { }
    2361             : 
    2362             :   // Ignore SQL
    2363           0 :   Expect(const char* aName, const char* aType)
    2364           0 :     : mName(aName)
    2365             :     , mType(aType)
    2366           0 :     , mIgnoreSql(true)
    2367           0 :   { }
    2368             : 
    2369             :   const nsCString mName;
    2370             :   const nsCString mType;
    2371             :   const nsCString mSql;
    2372             :   const bool mIgnoreSql;
    2373             : };
    2374             : #endif
    2375             : 
    2376             : nsresult
    2377           0 : Validate(mozIStorageConnection* aConn)
    2378             : {
    2379             :   int32_t schemaVersion;
    2380           0 :   nsresult rv = aConn->GetSchemaVersion(&schemaVersion);
    2381           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2382             : 
    2383           0 :   if (NS_WARN_IF(schemaVersion != kLatestSchemaVersion)) {
    2384           0 :     return NS_ERROR_FAILURE;
    2385             :   }
    2386             : 
    2387             : #ifdef DEBUG
    2388             :   // This is the schema we expect the database at the latest version to
    2389             :   // contain.  Update this list if you add a new table or index.
    2390             :   Expect expect[] = {
    2391             :     Expect("caches", "table", kTableCaches),
    2392             :     Expect("sqlite_sequence", "table"), // auto-gen by sqlite
    2393             :     Expect("security_info", "table", kTableSecurityInfo),
    2394             :     Expect("security_info_hash_index", "index", kIndexSecurityInfoHash),
    2395             :     Expect("entries", "table", kTableEntries),
    2396             :     Expect("entries_request_match_index", "index", kIndexEntriesRequest),
    2397             :     Expect("request_headers", "table", kTableRequestHeaders),
    2398             :     Expect("response_headers", "table", kTableResponseHeaders),
    2399             :     Expect("response_headers_name_index", "index", kIndexResponseHeadersName),
    2400             :     Expect("response_url_list", "table", kTableResponseUrlList),
    2401             :     Expect("storage", "table", kTableStorage),
    2402             :     Expect("sqlite_autoindex_storage_1", "index"), // auto-gen by sqlite
    2403           0 :   };
    2404           0 :   const uint32_t expectLength = sizeof(expect) / sizeof(Expect);
    2405             : 
    2406             :   // Read the schema from the sqlite_master table and compare.
    2407           0 :   nsCOMPtr<mozIStorageStatement> state;
    2408           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    2409             :     "SELECT name, type, sql FROM sqlite_master;"
    2410           0 :   ), getter_AddRefs(state));
    2411           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2412             : 
    2413           0 :   bool hasMoreData = false;
    2414           0 :   while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
    2415           0 :     nsAutoCString name;
    2416           0 :     rv = state->GetUTF8String(0, name);
    2417           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2418             : 
    2419           0 :     nsAutoCString type;
    2420           0 :     rv = state->GetUTF8String(1, type);
    2421           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2422             : 
    2423           0 :     nsAutoCString sql;
    2424           0 :     rv = state->GetUTF8String(2, sql);
    2425           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2426             : 
    2427           0 :     bool foundMatch = false;
    2428           0 :     for (uint32_t i = 0; i < expectLength; ++i) {
    2429           0 :       if (name == expect[i].mName) {
    2430           0 :         if (type != expect[i].mType) {
    2431           0 :           NS_WARNING(nsPrintfCString("Unexpected type for Cache schema entry %s",
    2432           0 :                      name.get()).get());
    2433           0 :           return NS_ERROR_FAILURE;
    2434             :         }
    2435             : 
    2436           0 :         if (!expect[i].mIgnoreSql && sql != expect[i].mSql) {
    2437           0 :           NS_WARNING(nsPrintfCString("Unexpected SQL for Cache schema entry %s",
    2438           0 :                      name.get()).get());
    2439           0 :           return NS_ERROR_FAILURE;
    2440             :         }
    2441             : 
    2442           0 :         foundMatch = true;
    2443           0 :         break;
    2444             :       }
    2445             :     }
    2446             : 
    2447           0 :     if (NS_WARN_IF(!foundMatch)) {
    2448           0 :       NS_WARNING(nsPrintfCString("Unexpected schema entry %s in Cache database",
    2449           0 :                  name.get()).get());
    2450           0 :       return NS_ERROR_FAILURE;
    2451             :     }
    2452             :   }
    2453             : #endif
    2454             : 
    2455           0 :   return rv;
    2456             : }
    2457             : 
    2458             : // -----
    2459             : // Schema migration code
    2460             : // -----
    2461             : 
    2462             : typedef nsresult (*MigrationFunc)(mozIStorageConnection*, bool&);
    2463             : struct Migration
    2464             : {
    2465             :   constexpr Migration(int32_t aFromVersion, MigrationFunc aFunc)
    2466             :     : mFromVersion(aFromVersion)
    2467             :     , mFunc(aFunc)
    2468             :   { }
    2469             :   int32_t mFromVersion;
    2470             :   MigrationFunc mFunc;
    2471             : };
    2472             : 
    2473             : // Declare migration functions here.  Each function should upgrade
    2474             : // the version by a single increment.  Don't skip versions.
    2475             : nsresult MigrateFrom15To16(mozIStorageConnection* aConn, bool& aRewriteSchema);
    2476             : nsresult MigrateFrom16To17(mozIStorageConnection* aConn, bool& aRewriteSchema);
    2477             : nsresult MigrateFrom17To18(mozIStorageConnection* aConn, bool& aRewriteSchema);
    2478             : nsresult MigrateFrom18To19(mozIStorageConnection* aConn, bool& aRewriteSchema);
    2479             : nsresult MigrateFrom19To20(mozIStorageConnection* aConn, bool& aRewriteSchema);
    2480             : nsresult MigrateFrom20To21(mozIStorageConnection* aConn, bool& aRewriteSchema);
    2481             : nsresult MigrateFrom21To22(mozIStorageConnection* aConn, bool& aRewriteSchema);
    2482             : nsresult MigrateFrom22To23(mozIStorageConnection* aConn, bool& aRewriteSchema);
    2483             : nsresult MigrateFrom23To24(mozIStorageConnection* aConn, bool& aRewriteSchema);
    2484             : nsresult MigrateFrom24To25(mozIStorageConnection* aConn, bool& aRewriteSchema);
    2485             : // Configure migration functions to run for the given starting version.
    2486             : Migration sMigrationList[] = {
    2487             :   Migration(15, MigrateFrom15To16),
    2488             :   Migration(16, MigrateFrom16To17),
    2489             :   Migration(17, MigrateFrom17To18),
    2490             :   Migration(18, MigrateFrom18To19),
    2491             :   Migration(19, MigrateFrom19To20),
    2492             :   Migration(20, MigrateFrom20To21),
    2493             :   Migration(21, MigrateFrom21To22),
    2494             :   Migration(22, MigrateFrom22To23),
    2495             :   Migration(23, MigrateFrom23To24),
    2496             :   Migration(24, MigrateFrom24To25),
    2497             : };
    2498             : uint32_t sMigrationListLength = sizeof(sMigrationList) / sizeof(Migration);
    2499             : nsresult
    2500           0 : RewriteEntriesSchema(mozIStorageConnection* aConn)
    2501             : {
    2502           0 :   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2503             :     "PRAGMA writable_schema = ON"
    2504           0 :   ));
    2505           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2506             : 
    2507           0 :   nsCOMPtr<mozIStorageStatement> state;
    2508           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    2509             :     "UPDATE sqlite_master SET sql=:sql WHERE name='entries'"
    2510           0 :   ), getter_AddRefs(state));
    2511           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2512             : 
    2513           0 :   rv = state->BindUTF8StringByName(NS_LITERAL_CSTRING("sql"),
    2514           0 :                                    nsDependentCString(kTableEntries));
    2515           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2516             : 
    2517           0 :   rv = state->Execute();
    2518           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2519             : 
    2520           0 :   rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2521             :     "PRAGMA writable_schema = OFF"
    2522           0 :   ));
    2523           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2524             : 
    2525           0 :   return rv;
    2526             : }
    2527             : 
    2528             : nsresult
    2529           0 : Migrate(mozIStorageConnection* aConn)
    2530             : {
    2531           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2532           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    2533             : 
    2534           0 :   int32_t currentVersion = 0;
    2535           0 :   nsresult rv = aConn->GetSchemaVersion(&currentVersion);
    2536           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2537             : 
    2538           0 :   bool rewriteSchema = false;
    2539             : 
    2540           0 :   while (currentVersion < kLatestSchemaVersion) {
    2541             :     // Wiping old databases is handled in DBAction because it requires
    2542             :     // making a whole new mozIStorageConnection.  Make sure we don't
    2543             :     // accidentally get here for one of those old databases.
    2544           0 :     MOZ_DIAGNOSTIC_ASSERT(currentVersion >= kFirstShippedSchemaVersion);
    2545             : 
    2546           0 :     for (uint32_t i = 0; i < sMigrationListLength; ++i) {
    2547           0 :       if (sMigrationList[i].mFromVersion == currentVersion) {
    2548           0 :         bool shouldRewrite = false;
    2549           0 :         rv = sMigrationList[i].mFunc(aConn, shouldRewrite);
    2550           0 :         if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2551           0 :         if (shouldRewrite) {
    2552           0 :           rewriteSchema = true;
    2553             :         }
    2554           0 :         break;
    2555             :       }
    2556             :     }
    2557             : 
    2558             : #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    2559           0 :     int32_t lastVersion = currentVersion;
    2560             : #endif
    2561           0 :     rv = aConn->GetSchemaVersion(&currentVersion);
    2562           0 :     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2563           0 :     MOZ_DIAGNOSTIC_ASSERT(currentVersion > lastVersion);
    2564             :   }
    2565             : 
    2566             :   // Don't release assert this since people do sometimes share profiles
    2567             :   // across schema versions.  Our check in Validate() will catch it.
    2568           0 :   MOZ_ASSERT(currentVersion == kLatestSchemaVersion);
    2569             : 
    2570           0 :   if (rewriteSchema) {
    2571             :     // Now overwrite the master SQL for the entries table to remove the column
    2572             :     // default value.  This is also necessary for our Validate() method to
    2573             :     // pass on this database.
    2574           0 :     rv = RewriteEntriesSchema(aConn);
    2575             :   }
    2576             : 
    2577           0 :   return rv;
    2578             : }
    2579             : 
    2580           0 : nsresult MigrateFrom15To16(mozIStorageConnection* aConn, bool& aRewriteSchema)
    2581             : {
    2582           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2583           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    2584             : 
    2585             :   // Add the request_redirect column with a default value of "follow".  Note,
    2586             :   // we only use a default value here because its required by ALTER TABLE and
    2587             :   // we need to apply the default "follow" to existing records in the table.
    2588             :   // We don't actually want to keep the default in the schema for future
    2589             :   // INSERTs.
    2590           0 :   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2591             :     "ALTER TABLE entries "
    2592             :     "ADD COLUMN request_redirect INTEGER NOT NULL DEFAULT 0"
    2593           0 :   ));
    2594           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2595             : 
    2596           0 :   rv = aConn->SetSchemaVersion(16);
    2597           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2598             : 
    2599           0 :   aRewriteSchema = true;
    2600             : 
    2601           0 :   return rv;
    2602             : }
    2603             : 
    2604             : nsresult
    2605           0 : MigrateFrom16To17(mozIStorageConnection* aConn, bool& aRewriteSchema)
    2606             : {
    2607           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2608           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    2609             : 
    2610             :   // This migration path removes the response_redirected and
    2611             :   // response_redirected_url columns from the entries table.  sqlite doesn't
    2612             :   // support removing a column from a table using ALTER TABLE, so we need to
    2613             :   // create a new table without those columns, fill it up with the existing
    2614             :   // data, and then drop the original table and rename the new one to the old
    2615             :   // one.
    2616             : 
    2617             :   // Create a new_entries table with the new fields as of version 17.
    2618           0 :   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2619             :     "CREATE TABLE new_entries ("
    2620             :       "id INTEGER NOT NULL PRIMARY KEY, "
    2621             :       "request_method TEXT NOT NULL, "
    2622             :       "request_url_no_query TEXT NOT NULL, "
    2623             :       "request_url_no_query_hash BLOB NOT NULL, "
    2624             :       "request_url_query TEXT NOT NULL, "
    2625             :       "request_url_query_hash BLOB NOT NULL, "
    2626             :       "request_referrer TEXT NOT NULL, "
    2627             :       "request_headers_guard INTEGER NOT NULL, "
    2628             :       "request_mode INTEGER NOT NULL, "
    2629             :       "request_credentials INTEGER NOT NULL, "
    2630             :       "request_contentpolicytype INTEGER NOT NULL, "
    2631             :       "request_cache INTEGER NOT NULL, "
    2632             :       "request_body_id TEXT NULL, "
    2633             :       "response_type INTEGER NOT NULL, "
    2634             :       "response_url TEXT NOT NULL, "
    2635             :       "response_status INTEGER NOT NULL, "
    2636             :       "response_status_text TEXT NOT NULL, "
    2637             :       "response_headers_guard INTEGER NOT NULL, "
    2638             :       "response_body_id TEXT NULL, "
    2639             :       "response_security_info_id INTEGER NULL REFERENCES security_info(id), "
    2640             :       "response_principal_info TEXT NOT NULL, "
    2641             :       "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE, "
    2642             :       "request_redirect INTEGER NOT NULL"
    2643             :     ")"
    2644           0 :   ));
    2645           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2646             : 
    2647             :   // Copy all of the data to the newly created table.
    2648           0 :   rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2649             :     "INSERT INTO new_entries ("
    2650             :       "id, "
    2651             :       "request_method, "
    2652             :       "request_url_no_query, "
    2653             :       "request_url_no_query_hash, "
    2654             :       "request_url_query, "
    2655             :       "request_url_query_hash, "
    2656             :       "request_referrer, "
    2657             :       "request_headers_guard, "
    2658             :       "request_mode, "
    2659             :       "request_credentials, "
    2660             :       "request_contentpolicytype, "
    2661             :       "request_cache, "
    2662             :       "request_redirect, "
    2663             :       "request_body_id, "
    2664             :       "response_type, "
    2665             :       "response_url, "
    2666             :       "response_status, "
    2667             :       "response_status_text, "
    2668             :       "response_headers_guard, "
    2669             :       "response_body_id, "
    2670             :       "response_security_info_id, "
    2671             :       "response_principal_info, "
    2672             :       "cache_id "
    2673             :     ") SELECT "
    2674             :       "id, "
    2675             :       "request_method, "
    2676             :       "request_url_no_query, "
    2677             :       "request_url_no_query_hash, "
    2678             :       "request_url_query, "
    2679             :       "request_url_query_hash, "
    2680             :       "request_referrer, "
    2681             :       "request_headers_guard, "
    2682             :       "request_mode, "
    2683             :       "request_credentials, "
    2684             :       "request_contentpolicytype, "
    2685             :       "request_cache, "
    2686             :       "request_redirect, "
    2687             :       "request_body_id, "
    2688             :       "response_type, "
    2689             :       "response_url, "
    2690             :       "response_status, "
    2691             :       "response_status_text, "
    2692             :       "response_headers_guard, "
    2693             :       "response_body_id, "
    2694             :       "response_security_info_id, "
    2695             :       "response_principal_info, "
    2696             :       "cache_id "
    2697             :     "FROM entries;"
    2698           0 :   ));
    2699           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2700             : 
    2701             :   // Remove the old table.
    2702           0 :   rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2703             :     "DROP TABLE entries;"
    2704           0 :   ));
    2705           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2706             : 
    2707             :   // Rename new_entries to entries.
    2708           0 :   rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2709             :     "ALTER TABLE new_entries RENAME to entries;"
    2710           0 :   ));
    2711           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2712             : 
    2713             :   // Now, recreate our indices.
    2714           0 :   rv = aConn->ExecuteSimpleSQL(nsDependentCString(kIndexEntriesRequest));
    2715           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2716             : 
    2717             :   // Revalidate the foreign key constraints, and ensure that there are no
    2718             :   // violations.
    2719           0 :   nsCOMPtr<mozIStorageStatement> state;
    2720           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    2721             :     "PRAGMA foreign_key_check;"
    2722           0 :   ), getter_AddRefs(state));
    2723           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2724             : 
    2725           0 :   bool hasMoreData = false;
    2726           0 :   rv = state->ExecuteStep(&hasMoreData);
    2727           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2728           0 :   if (NS_WARN_IF(hasMoreData)) { return NS_ERROR_FAILURE; }
    2729             : 
    2730           0 :   rv = aConn->SetSchemaVersion(17);
    2731           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2732             : 
    2733           0 :   return rv;
    2734             : }
    2735             : 
    2736             : nsresult
    2737           0 : MigrateFrom17To18(mozIStorageConnection* aConn, bool& aRewriteSchema)
    2738             : {
    2739           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2740           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    2741             : 
    2742             :   // This migration is needed in order to remove "only-if-cached" RequestCache
    2743             :   // values from the database.  This enum value was removed from the spec in
    2744             :   // https://github.com/whatwg/fetch/issues/39 but we unfortunately happily
    2745             :   // accepted this value in the Request constructor.
    2746             :   //
    2747             :   // There is no good value to upgrade this to, so we just stick to "default".
    2748             : 
    2749             :   static_assert(int(RequestCache::Default) == 0,
    2750             :                 "This is where the 0 below comes from!");
    2751           0 :   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2752             :     "UPDATE entries SET request_cache = 0 "
    2753             :       "WHERE request_cache = 5;"
    2754           0 :   ));
    2755           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2756             : 
    2757           0 :   rv = aConn->SetSchemaVersion(18);
    2758           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2759             : 
    2760           0 :   return rv;
    2761             : }
    2762             : 
    2763             : nsresult
    2764           0 : MigrateFrom18To19(mozIStorageConnection* aConn, bool& aRewriteSchema)
    2765             : {
    2766           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2767           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    2768             : 
    2769             :   // This migration is needed in order to update the RequestMode values for
    2770             :   // Request objects corresponding to a navigation content policy type to
    2771             :   // "navigate".
    2772             : 
    2773             :   static_assert(int(nsIContentPolicy::TYPE_DOCUMENT) == 6 &&
    2774             :                 int(nsIContentPolicy::TYPE_SUBDOCUMENT) == 7 &&
    2775             :                 int(nsIContentPolicy::TYPE_INTERNAL_FRAME) == 28 &&
    2776             :                 int(nsIContentPolicy::TYPE_INTERNAL_IFRAME) == 29 &&
    2777             :                 int(nsIContentPolicy::TYPE_REFRESH) == 8 &&
    2778             :                 int(RequestMode::Navigate) == 3,
    2779             :                 "This is where the numbers below come from!");
    2780           0 :   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2781             :     "UPDATE entries SET request_mode = 3 "
    2782             :       "WHERE request_contentpolicytype IN (6, 7, 28, 29, 8);"
    2783           0 :   ));
    2784           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2785             : 
    2786           0 :   rv = aConn->SetSchemaVersion(19);
    2787           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2788             : 
    2789           0 :   return rv;
    2790             : }
    2791             : 
    2792           0 : nsresult MigrateFrom19To20(mozIStorageConnection* aConn, bool& aRewriteSchema)
    2793             : {
    2794           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2795           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    2796             : 
    2797             :   // Add the request_referrer_policy column with a default value of
    2798             :   // "no-referrer-when-downgrade".  Note, we only use a default value here
    2799             :   // because its required by ALTER TABLE and we need to apply the default
    2800             :   // "no-referrer-when-downgrade" to existing records in the table. We don't
    2801             :   // actually want to keep the default in the schema for future INSERTs.
    2802           0 :   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2803             :     "ALTER TABLE entries "
    2804             :     "ADD COLUMN request_referrer_policy INTEGER NOT NULL DEFAULT 2"
    2805           0 :   ));
    2806           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2807             : 
    2808           0 :   rv = aConn->SetSchemaVersion(20);
    2809           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2810             : 
    2811           0 :   aRewriteSchema = true;
    2812             : 
    2813           0 :   return rv;
    2814             : }
    2815             : 
    2816           0 : nsresult MigrateFrom20To21(mozIStorageConnection* aConn, bool& aRewriteSchema)
    2817             : {
    2818           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2819           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    2820             : 
    2821             :   // This migration creates response_url_list table to store response_url and
    2822             :   // removes the response_url column from the entries table.
    2823             :   // sqlite doesn't support removing a column from a table using ALTER TABLE,
    2824             :   // so we need to create a new table without those columns, fill it up with the
    2825             :   // existing data, and then drop the original table and rename the new one to
    2826             :   // the old one.
    2827             : 
    2828             :   // Create a new_entries table with the new fields as of version 21.
    2829           0 :   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2830             :     "CREATE TABLE new_entries ("
    2831             :       "id INTEGER NOT NULL PRIMARY KEY, "
    2832             :       "request_method TEXT NOT NULL, "
    2833             :       "request_url_no_query TEXT NOT NULL, "
    2834             :       "request_url_no_query_hash BLOB NOT NULL, "
    2835             :       "request_url_query TEXT NOT NULL, "
    2836             :       "request_url_query_hash BLOB NOT NULL, "
    2837             :       "request_referrer TEXT NOT NULL, "
    2838             :       "request_headers_guard INTEGER NOT NULL, "
    2839             :       "request_mode INTEGER NOT NULL, "
    2840             :       "request_credentials INTEGER NOT NULL, "
    2841             :       "request_contentpolicytype INTEGER NOT NULL, "
    2842             :       "request_cache INTEGER NOT NULL, "
    2843             :       "request_body_id TEXT NULL, "
    2844             :       "response_type INTEGER NOT NULL, "
    2845             :       "response_status INTEGER NOT NULL, "
    2846             :       "response_status_text TEXT NOT NULL, "
    2847             :       "response_headers_guard INTEGER NOT NULL, "
    2848             :       "response_body_id TEXT NULL, "
    2849             :       "response_security_info_id INTEGER NULL REFERENCES security_info(id), "
    2850             :       "response_principal_info TEXT NOT NULL, "
    2851             :       "cache_id INTEGER NOT NULL REFERENCES caches(id) ON DELETE CASCADE, "
    2852             :       "request_redirect INTEGER NOT NULL, "
    2853             :       "request_referrer_policy INTEGER NOT NULL"
    2854             :     ")"
    2855           0 :   ));
    2856           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2857             : 
    2858             :   // Create a response_url_list table with the new fields as of version 21.
    2859           0 :   rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2860             :     "CREATE TABLE response_url_list ("
    2861             :       "url TEXT NOT NULL, "
    2862             :       "entry_id INTEGER NOT NULL REFERENCES entries(id) ON DELETE CASCADE"
    2863             :     ")"
    2864           0 :   ));
    2865           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2866             : 
    2867             :   // Copy all of the data to the newly created entries table.
    2868           0 :   rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2869             :     "INSERT INTO new_entries ("
    2870             :       "id, "
    2871             :       "request_method, "
    2872             :       "request_url_no_query, "
    2873             :       "request_url_no_query_hash, "
    2874             :       "request_url_query, "
    2875             :       "request_url_query_hash, "
    2876             :       "request_referrer, "
    2877             :       "request_headers_guard, "
    2878             :       "request_mode, "
    2879             :       "request_credentials, "
    2880             :       "request_contentpolicytype, "
    2881             :       "request_cache, "
    2882             :       "request_redirect, "
    2883             :       "request_referrer_policy, "
    2884             :       "request_body_id, "
    2885             :       "response_type, "
    2886             :       "response_status, "
    2887             :       "response_status_text, "
    2888             :       "response_headers_guard, "
    2889             :       "response_body_id, "
    2890             :       "response_security_info_id, "
    2891             :       "response_principal_info, "
    2892             :       "cache_id "
    2893             :     ") SELECT "
    2894             :       "id, "
    2895             :       "request_method, "
    2896             :       "request_url_no_query, "
    2897             :       "request_url_no_query_hash, "
    2898             :       "request_url_query, "
    2899             :       "request_url_query_hash, "
    2900             :       "request_referrer, "
    2901             :       "request_headers_guard, "
    2902             :       "request_mode, "
    2903             :       "request_credentials, "
    2904             :       "request_contentpolicytype, "
    2905             :       "request_cache, "
    2906             :       "request_redirect, "
    2907             :       "request_referrer_policy, "
    2908             :       "request_body_id, "
    2909             :       "response_type, "
    2910             :       "response_status, "
    2911             :       "response_status_text, "
    2912             :       "response_headers_guard, "
    2913             :       "response_body_id, "
    2914             :       "response_security_info_id, "
    2915             :       "response_principal_info, "
    2916             :       "cache_id "
    2917             :     "FROM entries;"
    2918           0 :   ));
    2919           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2920             : 
    2921             :   // Copy reponse_url to the newly created response_url_list table.
    2922           0 :   rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2923             :     "INSERT INTO response_url_list ("
    2924             :       "url, "
    2925             :       "entry_id "
    2926             :     ") SELECT "
    2927             :       "response_url, "
    2928             :       "id "
    2929             :     "FROM entries;"
    2930           0 :   ));
    2931           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2932             : 
    2933             :   // Remove the old table.
    2934           0 :   rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2935             :     "DROP TABLE entries;"
    2936           0 :   ));
    2937           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2938             : 
    2939             :   // Rename new_entries to entries.
    2940           0 :   rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2941             :     "ALTER TABLE new_entries RENAME to entries;"
    2942           0 :   ));
    2943           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2944             : 
    2945             :   // Now, recreate our indices.
    2946           0 :   rv = aConn->ExecuteSimpleSQL(nsDependentCString(kIndexEntriesRequest));
    2947           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2948             : 
    2949             :   // Revalidate the foreign key constraints, and ensure that there are no
    2950             :   // violations.
    2951           0 :   nsCOMPtr<mozIStorageStatement> state;
    2952           0 :   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
    2953             :     "PRAGMA foreign_key_check;"
    2954           0 :   ), getter_AddRefs(state));
    2955           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2956             : 
    2957           0 :   bool hasMoreData = false;
    2958           0 :   rv = state->ExecuteStep(&hasMoreData);
    2959           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2960           0 :   if (NS_WARN_IF(hasMoreData)) { return NS_ERROR_FAILURE; }
    2961             : 
    2962           0 :   rv = aConn->SetSchemaVersion(21);
    2963           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2964             : 
    2965           0 :   aRewriteSchema = true;
    2966             : 
    2967           0 :   return rv;
    2968             : }
    2969             : 
    2970           0 : nsresult MigrateFrom21To22(mozIStorageConnection* aConn, bool& aRewriteSchema)
    2971             : {
    2972           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2973           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    2974             : 
    2975             :   // Add the request_integrity column.
    2976           0 :   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2977             :     "ALTER TABLE entries "
    2978             :     "ADD COLUMN request_integrity TEXT NULL"
    2979           0 :   ));
    2980           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2981             : 
    2982           0 :   rv = aConn->SetSchemaVersion(22);
    2983           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2984             : 
    2985           0 :   aRewriteSchema = true;
    2986             : 
    2987           0 :   return rv;
    2988             : }
    2989             : 
    2990           0 : nsresult MigrateFrom22To23(mozIStorageConnection* aConn, bool& aRewriteSchema)
    2991             : {
    2992           0 :   MOZ_ASSERT(!NS_IsMainThread());
    2993           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    2994             : 
    2995             :   // The only change between 22 and 23 was a different snappy compression
    2996             :   // format, but it's backwards-compatible.
    2997           0 :   nsresult rv = aConn->SetSchemaVersion(23);
    2998           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    2999           0 :   return rv;
    3000             : }
    3001             : 
    3002           0 : nsresult MigrateFrom23To24(mozIStorageConnection* aConn, bool& aRewriteSchema)
    3003             : {
    3004           0 :   MOZ_ASSERT(!NS_IsMainThread());
    3005           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    3006             : 
    3007             :   // Add the request_url_fragment column.
    3008           0 :   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    3009             :     "ALTER TABLE entries "
    3010             :     "ADD COLUMN request_url_fragment TEXT NOT NULL DEFAULT ''"
    3011           0 :   ));
    3012           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    3013             : 
    3014           0 :   rv = aConn->SetSchemaVersion(24);
    3015           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    3016             : 
    3017           0 :   aRewriteSchema = true;
    3018             : 
    3019           0 :   return rv;
    3020             : }
    3021             : 
    3022           0 : nsresult MigrateFrom24To25(mozIStorageConnection* aConn, bool& aRewriteSchema)
    3023             : {
    3024           0 :   MOZ_ASSERT(!NS_IsMainThread());
    3025           0 :   MOZ_DIAGNOSTIC_ASSERT(aConn);
    3026             : 
    3027             :   // The only change between 24 and 25 was a new nsIContentPolicy type.
    3028           0 :   nsresult rv = aConn->SetSchemaVersion(25);
    3029           0 :   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
    3030           0 :   return rv;
    3031             : }
    3032             : 
    3033             : } // anonymous namespace
    3034             : } // namespace db
    3035             : } // namespace cache
    3036             : } // namespace dom
    3037             : } // namespace mozilla

Generated by: LCOV version 1.13