Move URL from WebCore to WTF
[WebKit-https.git] / Source / WebCore / loader / appcache / ApplicationCacheStorage.cpp
1 /*
2  * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "ApplicationCacheStorage.h"
28
29 #include "ApplicationCache.h"
30 #include "ApplicationCacheGroup.h"
31 #include "ApplicationCacheHost.h"
32 #include "ApplicationCacheResource.h"
33 #include "FileSystem.h"
34 #include "SQLiteDatabaseTracker.h"
35 #include "SQLiteStatement.h"
36 #include "SQLiteTransaction.h"
37 #include "SecurityOrigin.h"
38 #include "SecurityOriginData.h"
39 #include <wtf/StdLibExtras.h>
40 #include <wtf/URL.h>
41 #include <wtf/UUID.h>
42 #include <wtf/text/CString.h>
43 #include <wtf/text/StringBuilder.h>
44
45 namespace WebCore {
46
47 template <class T>
48 class StorageIDJournal {
49 public:  
50     ~StorageIDJournal()
51     {
52         for (auto& record : m_records)
53             record.restore();
54     }
55
56     void add(T* resource, unsigned storageID)
57     {
58         m_records.append(Record(resource, storageID));
59     }
60
61     void commit()
62     {
63         m_records.clear();
64     }
65
66 private:
67     class Record {
68     public:
69         Record() : m_resource(nullptr), m_storageID(0) { }
70         Record(T* resource, unsigned storageID) : m_resource(resource), m_storageID(storageID) { }
71
72         void restore()
73         {
74             m_resource->setStorageID(m_storageID);
75         }
76
77     private:
78         T* m_resource;
79         unsigned m_storageID;
80     };
81
82     Vector<Record> m_records;
83 };
84
85 static unsigned urlHostHash(const URL& url)
86 {
87     StringView host = url.host();
88     if (host.is8Bit())
89         return AlreadyHashed::avoidDeletedValue(StringHasher::computeHashAndMaskTop8Bits(host.characters8(), host.length()));
90     return AlreadyHashed::avoidDeletedValue(StringHasher::computeHashAndMaskTop8Bits(host.characters16(), host.length()));
91 }
92
93 ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const URL& manifestURL)
94 {
95     SQLiteTransactionInProgressAutoCounter transactionCounter;
96
97     openDatabase(false);
98     if (!m_database.isOpen())
99         return nullptr;
100
101     SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL AND manifestURL=?");
102     if (statement.prepare() != SQLITE_OK)
103         return nullptr;
104     
105     statement.bindText(1, manifestURL);
106    
107     int result = statement.step();
108     if (result == SQLITE_DONE)
109         return nullptr;
110     
111     if (result != SQLITE_ROW) {
112         LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg());
113         return nullptr;
114     }
115     
116     unsigned newestCacheStorageID = static_cast<unsigned>(statement.getColumnInt64(2));
117
118     auto cache = loadCache(newestCacheStorageID);
119     if (!cache)
120         return nullptr;
121         
122     auto& group = *new ApplicationCacheGroup(*this, manifestURL);
123     group.setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
124     group.setNewestCache(cache.releaseNonNull());
125     return &group;
126 }    
127
128 ApplicationCacheGroup* ApplicationCacheStorage::findOrCreateCacheGroup(const URL& manifestURL)
129 {
130     ASSERT(!manifestURL.hasFragmentIdentifier());
131
132     auto result = m_cachesInMemory.add(manifestURL, nullptr);
133     if (!result.isNewEntry) {
134         ASSERT(result.iterator->value);
135         return result.iterator->value;
136     }
137
138     // Look up the group in the database
139     auto* group = loadCacheGroup(manifestURL);
140     
141     // If the group was not found we need to create it
142     if (!group) {
143         group = new ApplicationCacheGroup(*this, manifestURL);
144         m_cacheHostSet.add(urlHostHash(manifestURL));
145     }
146
147     result.iterator->value = group;
148     return group;
149 }
150
151 ApplicationCacheGroup* ApplicationCacheStorage::findInMemoryCacheGroup(const URL& manifestURL) const
152 {
153     return m_cachesInMemory.get(manifestURL);
154 }
155
156 void ApplicationCacheStorage::loadManifestHostHashes()
157 {
158     static bool hasLoadedHashes = false;
159     
160     if (hasLoadedHashes)
161         return;
162     
163     // We set this flag to true before the database has been opened
164     // to avoid trying to open the database over and over if it doesn't exist.
165     hasLoadedHashes = true;
166     
167     SQLiteTransactionInProgressAutoCounter transactionCounter;
168
169     openDatabase(false);
170     if (!m_database.isOpen())
171         return;
172
173     // Fetch the host hashes.
174     SQLiteStatement statement(m_database, "SELECT manifestHostHash FROM CacheGroups");    
175     if (statement.prepare() != SQLITE_OK)
176         return;
177     
178     while (statement.step() == SQLITE_ROW)
179         m_cacheHostSet.add(static_cast<unsigned>(statement.getColumnInt64(0)));
180 }    
181
182 ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const URL& url)
183 {
184     ASSERT(!url.hasFragmentIdentifier());
185     
186     loadManifestHostHashes();
187     
188     // Hash the host name and see if there's a manifest with the same host.
189     if (!m_cacheHostSet.contains(urlHostHash(url)))
190         return nullptr;
191
192     // Check if a cache already exists in memory.
193     for (const auto& group : m_cachesInMemory.values()) {
194         ASSERT(!group->isObsolete());
195
196         if (!protocolHostAndPortAreEqual(url, group->manifestURL()))
197             continue;
198         
199         if (ApplicationCache* cache = group->newestCache()) {
200             ApplicationCacheResource* resource = cache->resourceForURL(url);
201             if (!resource)
202                 continue;
203             if (resource->type() & ApplicationCacheResource::Foreign)
204                 continue;
205             return group;
206         }
207     }
208     
209     if (!m_database.isOpen())
210         return nullptr;
211         
212     SQLiteTransactionInProgressAutoCounter transactionCounter;
213
214     // Check the database. Look for all cache groups with a newest cache.
215     SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL");
216     if (statement.prepare() != SQLITE_OK)
217         return nullptr;
218     
219     int result;
220     while ((result = statement.step()) == SQLITE_ROW) {
221         URL manifestURL = URL({ }, statement.getColumnText(1));
222
223         if (m_cachesInMemory.contains(manifestURL))
224             continue;
225
226         if (!protocolHostAndPortAreEqual(url, manifestURL))
227             continue;
228
229         // We found a cache group that matches. Now check if the newest cache has a resource with
230         // a matching URL.
231         unsigned newestCacheID = static_cast<unsigned>(statement.getColumnInt64(2));
232         auto cache = loadCache(newestCacheID);
233         if (!cache)
234             continue;
235
236         auto* resource = cache->resourceForURL(url);
237         if (!resource)
238             continue;
239         if (resource->type() & ApplicationCacheResource::Foreign)
240             continue;
241
242         auto& group = *new ApplicationCacheGroup(*this, manifestURL);
243         group.setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
244         group.setNewestCache(cache.releaseNonNull());
245         m_cachesInMemory.set(group.manifestURL(), &group);
246
247         return &group;
248     }
249
250     if (result != SQLITE_DONE)
251         LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg());
252     
253     return nullptr;
254 }
255
256 ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const URL& url)
257 {
258     SQLiteTransactionInProgressAutoCounter transactionCounter;
259
260     ASSERT(!url.hasFragmentIdentifier());
261
262     // Check if an appropriate cache already exists in memory.
263     for (auto* group : m_cachesInMemory.values()) {
264         ASSERT(!group->isObsolete());
265
266         if (ApplicationCache* cache = group->newestCache()) {
267             URL fallbackURL;
268             if (cache->isURLInOnlineWhitelist(url))
269                 continue;
270             if (!cache->urlMatchesFallbackNamespace(url, &fallbackURL))
271                 continue;
272             if (cache->resourceForURL(fallbackURL)->type() & ApplicationCacheResource::Foreign)
273                 continue;
274             return group;
275         }
276     }
277     
278     if (!m_database.isOpen())
279         return nullptr;
280         
281     // Check the database. Look for all cache groups with a newest cache.
282     SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL");
283     if (statement.prepare() != SQLITE_OK)
284         return nullptr;
285     
286     int result;
287     while ((result = statement.step()) == SQLITE_ROW) {
288         URL manifestURL = URL({ }, statement.getColumnText(1));
289
290         if (m_cachesInMemory.contains(manifestURL))
291             continue;
292
293         // Fallback namespaces always have the same origin as manifest URL, so we can avoid loading caches that cannot match.
294         if (!protocolHostAndPortAreEqual(url, manifestURL))
295             continue;
296
297         // We found a cache group that matches. Now check if the newest cache has a resource with
298         // a matching fallback namespace.
299         unsigned newestCacheID = static_cast<unsigned>(statement.getColumnInt64(2));
300         auto cache = loadCache(newestCacheID);
301
302         URL fallbackURL;
303         if (cache->isURLInOnlineWhitelist(url))
304             continue;
305         if (!cache->urlMatchesFallbackNamespace(url, &fallbackURL))
306             continue;
307         if (cache->resourceForURL(fallbackURL)->type() & ApplicationCacheResource::Foreign)
308             continue;
309
310         auto& group = *new ApplicationCacheGroup(*this, manifestURL);
311         group.setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
312         group.setNewestCache(cache.releaseNonNull());
313
314         m_cachesInMemory.set(group.manifestURL(), &group);
315
316         return &group;
317     }
318
319     if (result != SQLITE_DONE)
320         LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg());
321     
322     return nullptr;
323 }
324
325 void ApplicationCacheStorage::cacheGroupDestroyed(ApplicationCacheGroup& group)
326 {
327     if (group.isObsolete()) {
328         ASSERT(!group.storageID());
329         ASSERT(m_cachesInMemory.get(group.manifestURL()) != &group);
330         return;
331     }
332
333     ASSERT(m_cachesInMemory.get(group.manifestURL()) == &group);
334
335     m_cachesInMemory.remove(group.manifestURL());
336     
337     // If the cache group is half-created, we don't want it in the saved set (as it is not stored in database).
338     if (!group.storageID())
339         m_cacheHostSet.remove(urlHostHash(group.manifestURL()));
340 }
341
342 void ApplicationCacheStorage::cacheGroupMadeObsolete(ApplicationCacheGroup& group)
343 {
344     ASSERT(m_cachesInMemory.get(group.manifestURL()) == &group);
345     ASSERT(m_cacheHostSet.contains(urlHostHash(group.manifestURL())));
346
347     if (auto* newestCache = group.newestCache())
348         remove(newestCache);
349
350     m_cachesInMemory.remove(group.manifestURL());
351     m_cacheHostSet.remove(urlHostHash(group.manifestURL()));
352 }
353
354 void ApplicationCacheStorage::setMaximumSize(int64_t size)
355 {
356     m_maximumSize = size;
357 }
358
359 int64_t ApplicationCacheStorage::maximumSize() const
360 {
361     return m_maximumSize;
362 }
363
364 bool ApplicationCacheStorage::isMaximumSizeReached() const
365 {
366     return m_isMaximumSizeReached;
367 }
368
369 int64_t ApplicationCacheStorage::spaceNeeded(int64_t cacheToSave)
370 {
371     int64_t spaceNeeded = 0;
372     long long fileSize = 0;
373     if (!FileSystem::getFileSize(m_cacheFile, fileSize))
374         return 0;
375
376     int64_t currentSize = fileSize + flatFileAreaSize();
377
378     // Determine the amount of free space we have available.
379     int64_t totalAvailableSize = 0;
380     if (m_maximumSize < currentSize) {
381         // The max size is smaller than the actual size of the app cache file.
382         // This can happen if the client previously imposed a larger max size
383         // value and the app cache file has already grown beyond the current
384         // max size value.
385         // The amount of free space is just the amount of free space inside
386         // the database file. Note that this is always 0 if SQLite is compiled
387         // with AUTO_VACUUM = 1.
388         totalAvailableSize = m_database.freeSpaceSize();
389     } else {
390         // The max size is the same or larger than the current size.
391         // The amount of free space available is the amount of free space
392         // inside the database file plus the amount we can grow until we hit
393         // the max size.
394         totalAvailableSize = (m_maximumSize - currentSize) + m_database.freeSpaceSize();
395     }
396
397     // The space needed to be freed in order to accommodate the failed cache is
398     // the size of the failed cache minus any already available free space.
399     spaceNeeded = cacheToSave - totalAvailableSize;
400     // The space needed value must be positive (or else the total already
401     // available free space would be larger than the size of the failed cache and
402     // saving of the cache should have never failed).
403     ASSERT(spaceNeeded);
404     return spaceNeeded;
405 }
406
407 void ApplicationCacheStorage::setDefaultOriginQuota(int64_t quota)
408 {
409     m_defaultOriginQuota = quota;
410 }
411
412 bool ApplicationCacheStorage::calculateQuotaForOrigin(const SecurityOrigin& origin, int64_t& quota)
413 {
414     SQLiteTransactionInProgressAutoCounter transactionCounter;
415
416     // If an Origin record doesn't exist, then the COUNT will be 0 and quota will be 0.
417     // Using the count to determine if a record existed or not is a safe way to determine
418     // if a quota of 0 is real, from the record, or from null.
419     SQLiteStatement statement(m_database, "SELECT COUNT(quota), quota FROM Origins WHERE origin=?");
420     if (statement.prepare() != SQLITE_OK)
421         return false;
422
423     statement.bindText(1, origin.data().databaseIdentifier());
424     int result = statement.step();
425
426     // Return the quota, or if it was null the default.
427     if (result == SQLITE_ROW) {
428         bool wasNoRecord = statement.getColumnInt64(0) == 0;
429         quota = wasNoRecord ? m_defaultOriginQuota : statement.getColumnInt64(1);
430         return true;
431     }
432
433     LOG_ERROR("Could not get the quota of an origin, error \"%s\"", m_database.lastErrorMsg());
434     return false;
435 }
436
437 bool ApplicationCacheStorage::calculateUsageForOrigin(const SecurityOrigin* origin, int64_t& usage)
438 {
439     SQLiteTransactionInProgressAutoCounter transactionCounter;
440
441     // If an Origins record doesn't exist, then the SUM will be null,
442     // which will become 0, as expected, when converting to a number.
443     SQLiteStatement statement(m_database, "SELECT SUM(Caches.size)"
444                                           "  FROM CacheGroups"
445                                           " INNER JOIN Origins ON CacheGroups.origin = Origins.origin"
446                                           " INNER JOIN Caches ON CacheGroups.id = Caches.cacheGroup"
447                                           " WHERE Origins.origin=?");
448     if (statement.prepare() != SQLITE_OK)
449         return false;
450
451     statement.bindText(1, origin->data().databaseIdentifier());
452     int result = statement.step();
453
454     if (result == SQLITE_ROW) {
455         usage = statement.getColumnInt64(0);
456         return true;
457     }
458
459     LOG_ERROR("Could not get the quota of an origin, error \"%s\"", m_database.lastErrorMsg());
460     return false;
461 }
462
463 bool ApplicationCacheStorage::calculateRemainingSizeForOriginExcludingCache(const SecurityOrigin& origin, ApplicationCache* cache, int64_t& remainingSize)
464 {
465     SQLiteTransactionInProgressAutoCounter transactionCounter;
466
467     openDatabase(false);
468     if (!m_database.isOpen())
469         return false;
470
471     // Remaining size = total origin quota - size of all caches with origin excluding the provided cache.
472     // Keep track of the number of caches so we can tell if the result was a calculation or not.
473     const char* query;
474     int64_t excludingCacheIdentifier = cache ? cache->storageID() : 0;
475     if (excludingCacheIdentifier != 0) {
476         query = "SELECT COUNT(Caches.size), Origins.quota - SUM(Caches.size)"
477                 "  FROM CacheGroups"
478                 " INNER JOIN Origins ON CacheGroups.origin = Origins.origin"
479                 " INNER JOIN Caches ON CacheGroups.id = Caches.cacheGroup"
480                 " WHERE Origins.origin=?"
481                 "   AND Caches.id!=?";
482     } else {
483         query = "SELECT COUNT(Caches.size), Origins.quota - SUM(Caches.size)"
484                 "  FROM CacheGroups"
485                 " INNER JOIN Origins ON CacheGroups.origin = Origins.origin"
486                 " INNER JOIN Caches ON CacheGroups.id = Caches.cacheGroup"
487                 " WHERE Origins.origin=?";
488     }
489
490     SQLiteStatement statement(m_database, query);
491     if (statement.prepare() != SQLITE_OK)
492         return false;
493
494     statement.bindText(1, origin.data().databaseIdentifier());
495     if (excludingCacheIdentifier != 0)
496         statement.bindInt64(2, excludingCacheIdentifier);
497     int result = statement.step();
498
499     // If the count was 0 that then we have to query the origin table directly
500     // for its quota. Otherwise we can use the calculated value.
501     if (result == SQLITE_ROW) {
502         int64_t numberOfCaches = statement.getColumnInt64(0);
503         if (numberOfCaches == 0)
504             calculateQuotaForOrigin(origin, remainingSize);
505         else
506             remainingSize = statement.getColumnInt64(1);
507         return true;
508     }
509
510     LOG_ERROR("Could not get the remaining size of an origin's quota, error \"%s\"", m_database.lastErrorMsg());
511     return false;
512 }
513
514 bool ApplicationCacheStorage::storeUpdatedQuotaForOrigin(const SecurityOrigin* origin, int64_t quota)
515 {
516     SQLiteTransactionInProgressAutoCounter transactionCounter;
517
518     openDatabase(true);
519     if (!m_database.isOpen())
520         return false;
521
522     if (!ensureOriginRecord(origin))
523         return false;
524
525     SQLiteStatement updateStatement(m_database, "UPDATE Origins SET quota=? WHERE origin=?");
526     if (updateStatement.prepare() != SQLITE_OK)
527         return false;
528
529     updateStatement.bindInt64(1, quota);
530     updateStatement.bindText(2, origin->data().databaseIdentifier());
531
532     return executeStatement(updateStatement);
533 }
534
535 bool ApplicationCacheStorage::executeSQLCommand(const String& sql)
536 {
537     ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
538     ASSERT(m_database.isOpen());
539     
540     bool result = m_database.executeCommand(sql);
541     if (!result)
542         LOG_ERROR("Application Cache Storage: failed to execute statement \"%s\" error \"%s\"", 
543                   sql.utf8().data(), m_database.lastErrorMsg());
544
545     return result;
546 }
547
548 // Update the schemaVersion when the schema of any the Application Cache
549 // SQLite tables changes. This allows the database to be rebuilt when
550 // a new, incompatible change has been introduced to the database schema.
551 static const int schemaVersion = 7;
552     
553 void ApplicationCacheStorage::verifySchemaVersion()
554 {
555     ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
556
557     int version = SQLiteStatement(m_database, "PRAGMA user_version").getColumnInt(0);
558     if (version == schemaVersion)
559         return;
560
561     // Version will be 0 if we just created an empty file. Trying to delete tables would cause errors, because they don't exist yet.
562     if (version)
563         deleteTables();
564
565     // Update user version.
566     SQLiteTransaction setDatabaseVersion(m_database);
567     setDatabaseVersion.begin();
568
569     char userVersionSQL[32];
570     int unusedNumBytes = snprintf(userVersionSQL, sizeof(userVersionSQL), "PRAGMA user_version=%d", schemaVersion);
571     ASSERT_UNUSED(unusedNumBytes, static_cast<int>(sizeof(userVersionSQL)) >= unusedNumBytes);
572
573     SQLiteStatement statement(m_database, userVersionSQL);
574     if (statement.prepare() != SQLITE_OK)
575         return;
576     
577     executeStatement(statement);
578     setDatabaseVersion.commit();
579 }
580     
581 void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist)
582 {
583     SQLiteTransactionInProgressAutoCounter transactionCounter;
584
585     if (m_database.isOpen())
586         return;
587
588     // The cache directory should never be null, but if it for some weird reason is we bail out.
589     if (m_cacheDirectory.isNull())
590         return;
591
592     m_cacheFile = FileSystem::pathByAppendingComponent(m_cacheDirectory, "ApplicationCache.db");
593     if (!createIfDoesNotExist && !FileSystem::fileExists(m_cacheFile))
594         return;
595
596     FileSystem::makeAllDirectories(m_cacheDirectory);
597     m_database.open(m_cacheFile);
598     
599     if (!m_database.isOpen())
600         return;
601     
602     verifySchemaVersion();
603     
604     // Create tables
605     executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheGroups (id INTEGER PRIMARY KEY AUTOINCREMENT, "
606                       "manifestHostHash INTEGER NOT NULL ON CONFLICT FAIL, manifestURL TEXT UNIQUE ON CONFLICT FAIL, newestCache INTEGER, origin TEXT)");
607     executeSQLCommand("CREATE TABLE IF NOT EXISTS Caches (id INTEGER PRIMARY KEY AUTOINCREMENT, cacheGroup INTEGER, size INTEGER)");
608     executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheWhitelistURLs (url TEXT NOT NULL ON CONFLICT FAIL, cache INTEGER NOT NULL ON CONFLICT FAIL)");
609     executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheAllowsAllNetworkRequests (wildcard INTEGER NOT NULL ON CONFLICT FAIL, cache INTEGER NOT NULL ON CONFLICT FAIL)");
610     executeSQLCommand("CREATE TABLE IF NOT EXISTS FallbackURLs (namespace TEXT NOT NULL ON CONFLICT FAIL, fallbackURL TEXT NOT NULL ON CONFLICT FAIL, "
611                       "cache INTEGER NOT NULL ON CONFLICT FAIL)");
612     executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheEntries (cache INTEGER NOT NULL ON CONFLICT FAIL, type INTEGER, resource INTEGER NOT NULL)");
613     executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResources (id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT NOT NULL ON CONFLICT FAIL, "
614                       "statusCode INTEGER NOT NULL, responseURL TEXT NOT NULL, mimeType TEXT, textEncodingName TEXT, headers TEXT, data INTEGER NOT NULL ON CONFLICT FAIL)");
615     executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResourceData (id INTEGER PRIMARY KEY AUTOINCREMENT, data BLOB, path TEXT)");
616     executeSQLCommand("CREATE TABLE IF NOT EXISTS DeletedCacheResources (id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT)");
617     executeSQLCommand("CREATE TABLE IF NOT EXISTS Origins (origin TEXT UNIQUE ON CONFLICT IGNORE, quota INTEGER NOT NULL ON CONFLICT FAIL)");
618
619     // When a cache is deleted, all its entries and its whitelist should be deleted.
620     executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheDeleted AFTER DELETE ON Caches"
621                       " FOR EACH ROW BEGIN"
622                       "  DELETE FROM CacheEntries WHERE cache = OLD.id;"
623                       "  DELETE FROM CacheWhitelistURLs WHERE cache = OLD.id;"
624                       "  DELETE FROM CacheAllowsAllNetworkRequests WHERE cache = OLD.id;"
625                       "  DELETE FROM FallbackURLs WHERE cache = OLD.id;"
626                       " END");
627
628     // When a cache entry is deleted, its resource should also be deleted.
629     executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheEntryDeleted AFTER DELETE ON CacheEntries"
630                       " FOR EACH ROW BEGIN"
631                       "  DELETE FROM CacheResources WHERE id = OLD.resource;"
632                       " END");
633
634     // When a cache resource is deleted, its data blob should also be deleted.
635     executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheResourceDeleted AFTER DELETE ON CacheResources"
636                       " FOR EACH ROW BEGIN"
637                       "  DELETE FROM CacheResourceData WHERE id = OLD.data;"
638                       " END");
639     
640     // When a cache resource is deleted, if it contains a non-empty path, that path should
641     // be added to the DeletedCacheResources table so the flat file at that path can
642     // be deleted at a later time.
643     executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheResourceDataDeleted AFTER DELETE ON CacheResourceData"
644                       " FOR EACH ROW"
645                       " WHEN OLD.path NOT NULL BEGIN"
646                       "  INSERT INTO DeletedCacheResources (path) values (OLD.path);"
647                       " END");
648 }
649
650 bool ApplicationCacheStorage::executeStatement(SQLiteStatement& statement)
651 {
652     ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
653     bool result = statement.executeCommand();
654     if (!result)
655         LOG_ERROR("Application Cache Storage: failed to execute statement \"%s\" error \"%s\"", 
656                   statement.query().utf8().data(), m_database.lastErrorMsg());
657     
658     return result;
659 }    
660
661 bool ApplicationCacheStorage::store(ApplicationCacheGroup* group, GroupStorageIDJournal* journal)
662 {
663     ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
664     ASSERT(group->storageID() == 0);
665     ASSERT(journal);
666
667     // For some reason, an app cache may be partially written to disk. In particular, there may be
668     // a cache group with an identical manifest URL and associated cache entries. We want to remove
669     // this cache group and its associated cache entries so that we can create it again (below) as
670     // a way to repair it.
671     deleteCacheGroupRecord(group->manifestURL());
672
673     SQLiteStatement statement(m_database, "INSERT INTO CacheGroups (manifestHostHash, manifestURL, origin) VALUES (?, ?, ?)");
674     if (statement.prepare() != SQLITE_OK)
675         return false;
676
677     statement.bindInt64(1, urlHostHash(group->manifestURL()));
678     statement.bindText(2, group->manifestURL());
679     statement.bindText(3, group->origin().data().databaseIdentifier());
680
681     if (!executeStatement(statement))
682         return false;
683
684     unsigned groupStorageID = static_cast<unsigned>(m_database.lastInsertRowID());
685
686     if (!ensureOriginRecord(&group->origin()))
687         return false;
688
689     group->setStorageID(groupStorageID);
690     journal->add(group, 0);
691     return true;
692 }    
693
694 bool ApplicationCacheStorage::store(ApplicationCache* cache, ResourceStorageIDJournal* storageIDJournal)
695 {
696     ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
697     ASSERT(cache->storageID() == 0);
698     ASSERT(cache->group()->storageID() != 0);
699     ASSERT(storageIDJournal);
700     
701     SQLiteStatement statement(m_database, "INSERT INTO Caches (cacheGroup, size) VALUES (?, ?)");
702     if (statement.prepare() != SQLITE_OK)
703         return false;
704
705     statement.bindInt64(1, cache->group()->storageID());
706     statement.bindInt64(2, cache->estimatedSizeInStorage());
707
708     if (!executeStatement(statement))
709         return false;
710     
711     unsigned cacheStorageID = static_cast<unsigned>(m_database.lastInsertRowID());
712
713     // Store all resources
714     for (auto& resource : cache->resources().values()) {
715         unsigned oldStorageID = resource->storageID();
716         if (!store(resource.get(), cacheStorageID))
717             return false;
718
719         // Storing the resource succeeded. Log its old storageID in case
720         // it needs to be restored later.
721         storageIDJournal->add(resource.get(), oldStorageID);
722     }
723     
724     // Store the online whitelist
725     const Vector<URL>& onlineWhitelist = cache->onlineWhitelist();
726     {
727         for (auto& whitelistURL : onlineWhitelist) {
728             SQLiteStatement statement(m_database, "INSERT INTO CacheWhitelistURLs (url, cache) VALUES (?, ?)");
729             statement.prepare();
730
731             statement.bindText(1, whitelistURL);
732             statement.bindInt64(2, cacheStorageID);
733
734             if (!executeStatement(statement))
735                 return false;
736         }
737     }
738
739     // Store online whitelist wildcard flag.
740     {
741         SQLiteStatement statement(m_database, "INSERT INTO CacheAllowsAllNetworkRequests (wildcard, cache) VALUES (?, ?)");
742         statement.prepare();
743
744         statement.bindInt64(1, cache->allowsAllNetworkRequests());
745         statement.bindInt64(2, cacheStorageID);
746
747         if (!executeStatement(statement))
748             return false;
749     }
750     
751     // Store fallback URLs.
752     const FallbackURLVector& fallbackURLs = cache->fallbackURLs();
753     {
754         for (auto& fallbackURL : fallbackURLs) {
755             SQLiteStatement statement(m_database, "INSERT INTO FallbackURLs (namespace, fallbackURL, cache) VALUES (?, ?, ?)");
756             statement.prepare();
757
758             statement.bindText(1, fallbackURL.first);
759             statement.bindText(2, fallbackURL.second);
760             statement.bindInt64(3, cacheStorageID);
761
762             if (!executeStatement(statement))
763                 return false;
764         }
765     }
766
767     cache->setStorageID(cacheStorageID);
768     return true;
769 }
770
771 bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned cacheStorageID)
772 {
773     ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
774     ASSERT(cacheStorageID);
775     ASSERT(!resource->storageID());
776     
777     openDatabase(true);
778
779     // openDatabase(true) could still fail, for example when cacheStorage is full or no longer available.
780     if (!m_database.isOpen())
781         return false;
782
783     // First, insert the data
784     SQLiteStatement dataStatement(m_database, "INSERT INTO CacheResourceData (data, path) VALUES (?, ?)");
785     if (dataStatement.prepare() != SQLITE_OK)
786         return false;
787     
788
789     String fullPath;
790     if (!resource->path().isEmpty())
791         dataStatement.bindText(2, FileSystem::pathGetFileName(resource->path()));
792     else if (shouldStoreResourceAsFlatFile(resource)) {
793         // First, check to see if creating the flat file would violate the maximum total quota. We don't need
794         // to check the per-origin quota here, as it was already checked in storeNewestCache().
795         if (m_database.totalSize() + flatFileAreaSize() + static_cast<int64_t>(resource->data().size()) > m_maximumSize) {
796             m_isMaximumSizeReached = true;
797             return false;
798         }
799         
800         String flatFileDirectory = FileSystem::pathByAppendingComponent(m_cacheDirectory, m_flatFileSubdirectoryName);
801         FileSystem::makeAllDirectories(flatFileDirectory);
802
803         String extension;
804         
805         String fileName = resource->response().suggestedFilename();
806         size_t dotIndex = fileName.reverseFind('.');
807         if (dotIndex != notFound && dotIndex < (fileName.length() - 1))
808             extension = fileName.substring(dotIndex);
809
810         String path;
811         if (!writeDataToUniqueFileInDirectory(resource->data(), flatFileDirectory, path, extension))
812             return false;
813         
814         fullPath = FileSystem::pathByAppendingComponent(flatFileDirectory, path);
815         resource->setPath(fullPath);
816         dataStatement.bindText(2, path);
817     } else {
818         if (resource->data().size())
819             dataStatement.bindBlob(1, resource->data().data(), resource->data().size());
820     }
821     
822     if (!dataStatement.executeCommand()) {
823         // Clean up the file which we may have written to:
824         if (!fullPath.isEmpty())
825             FileSystem::deleteFile(fullPath);
826
827         return false;
828     }
829
830     unsigned dataId = static_cast<unsigned>(m_database.lastInsertRowID());
831
832     // Then, insert the resource
833     
834     // Serialize the headers
835     StringBuilder stringBuilder;
836     
837     for (const auto& header : resource->response().httpHeaderFields()) {
838         stringBuilder.append(header.key);
839         stringBuilder.append(':');
840         stringBuilder.append(header.value);
841         stringBuilder.append('\n');
842     }
843     
844     String headers = stringBuilder.toString();
845     
846     SQLiteStatement resourceStatement(m_database, "INSERT INTO CacheResources (url, statusCode, responseURL, headers, data, mimeType, textEncodingName) VALUES (?, ?, ?, ?, ?, ?, ?)");
847     if (resourceStatement.prepare() != SQLITE_OK)
848         return false;
849     
850     // The same ApplicationCacheResource are used in ApplicationCacheResource::size()
851     // to calculate the approximate size of an ApplicationCacheResource object. If
852     // you change the code below, please also change ApplicationCacheResource::size().
853     resourceStatement.bindText(1, resource->url());
854     resourceStatement.bindInt64(2, resource->response().httpStatusCode());
855     resourceStatement.bindText(3, resource->response().url());
856     resourceStatement.bindText(4, headers);
857     resourceStatement.bindInt64(5, dataId);
858     resourceStatement.bindText(6, resource->response().mimeType());
859     resourceStatement.bindText(7, resource->response().textEncodingName());
860
861     if (!executeStatement(resourceStatement))
862         return false;
863
864     unsigned resourceId = static_cast<unsigned>(m_database.lastInsertRowID());
865     
866     // Finally, insert the cache entry
867     SQLiteStatement entryStatement(m_database, "INSERT INTO CacheEntries (cache, type, resource) VALUES (?, ?, ?)");
868     if (entryStatement.prepare() != SQLITE_OK)
869         return false;
870     
871     entryStatement.bindInt64(1, cacheStorageID);
872     entryStatement.bindInt64(2, resource->type());
873     entryStatement.bindInt64(3, resourceId);
874     
875     if (!executeStatement(entryStatement))
876         return false;
877     
878     // Did we successfully write the resource data to a file? If so,
879     // release the resource's data and free up a potentially large amount
880     // of memory:
881     if (!fullPath.isEmpty())
882         resource->data().clear();
883
884     resource->setStorageID(resourceId);
885     return true;
886 }
887
888 bool ApplicationCacheStorage::storeUpdatedType(ApplicationCacheResource* resource, ApplicationCache* cache)
889 {
890     SQLiteTransactionInProgressAutoCounter transactionCounter;
891
892     ASSERT_UNUSED(cache, cache->storageID());
893     ASSERT(resource->storageID());
894
895     // First, insert the data
896     SQLiteStatement entryStatement(m_database, "UPDATE CacheEntries SET type=? WHERE resource=?");
897     if (entryStatement.prepare() != SQLITE_OK)
898         return false;
899
900     entryStatement.bindInt64(1, resource->type());
901     entryStatement.bindInt64(2, resource->storageID());
902
903     return executeStatement(entryStatement);
904 }
905
906 bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, ApplicationCache* cache)
907 {
908     SQLiteTransactionInProgressAutoCounter transactionCounter;
909
910     ASSERT(cache->storageID());
911     
912     openDatabase(true);
913
914     if (!m_database.isOpen())
915         return false;
916  
917     m_isMaximumSizeReached = false;
918     m_database.setMaximumSize(m_maximumSize - flatFileAreaSize());
919
920     SQLiteTransaction storeResourceTransaction(m_database);
921     storeResourceTransaction.begin();
922     
923     if (!store(resource, cache->storageID())) {
924         checkForMaxSizeReached();
925         return false;
926     }
927
928     // A resource was added to the cache. Update the total data size for the cache.
929     SQLiteStatement sizeUpdateStatement(m_database, "UPDATE Caches SET size=size+? WHERE id=?");
930     if (sizeUpdateStatement.prepare() != SQLITE_OK)
931         return false;
932
933     sizeUpdateStatement.bindInt64(1, resource->estimatedSizeInStorage());
934     sizeUpdateStatement.bindInt64(2, cache->storageID());
935
936     if (!executeStatement(sizeUpdateStatement))
937         return false;
938     
939     storeResourceTransaction.commit();
940     return true;
941 }
942
943 bool ApplicationCacheStorage::ensureOriginRecord(const SecurityOrigin* origin)
944 {
945     ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
946     SQLiteStatement insertOriginStatement(m_database, "INSERT INTO Origins (origin, quota) VALUES (?, ?)");
947     if (insertOriginStatement.prepare() != SQLITE_OK)
948         return false;
949
950     insertOriginStatement.bindText(1, origin->data().databaseIdentifier());
951     insertOriginStatement.bindInt64(2, m_defaultOriginQuota);
952     if (!executeStatement(insertOriginStatement))
953         return false;
954
955     return true;
956 }
957
958 bool ApplicationCacheStorage::checkOriginQuota(ApplicationCacheGroup* group, ApplicationCache* oldCache, ApplicationCache* newCache, int64_t& totalSpaceNeeded)
959 {
960     // Check if the oldCache with the newCache would reach the per-origin quota.
961     int64_t remainingSpaceInOrigin;
962     auto& origin = group->origin();
963     if (calculateRemainingSizeForOriginExcludingCache(origin, oldCache, remainingSpaceInOrigin)) {
964         if (remainingSpaceInOrigin < newCache->estimatedSizeInStorage()) {
965             int64_t quota;
966             if (calculateQuotaForOrigin(origin, quota)) {
967                 totalSpaceNeeded = quota - remainingSpaceInOrigin + newCache->estimatedSizeInStorage();
968                 return false;
969             }
970
971             ASSERT_NOT_REACHED();
972             totalSpaceNeeded = 0;
973             return false;
974         }
975     }
976
977     return true;
978 }
979
980 bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup& group, ApplicationCache* oldCache, FailureReason& failureReason)
981 {
982     openDatabase(true);
983
984     if (!m_database.isOpen())
985         return false;
986
987     m_isMaximumSizeReached = false;
988     m_database.setMaximumSize(m_maximumSize - flatFileAreaSize());
989
990     SQLiteTransaction storeCacheTransaction(m_database);
991     
992     storeCacheTransaction.begin();
993
994     // Check if this would reach the per-origin quota.
995     int64_t totalSpaceNeededIgnored;
996     if (!checkOriginQuota(&group, oldCache, group.newestCache(), totalSpaceNeededIgnored)) {
997         failureReason = OriginQuotaReached;
998         return false;
999     }
1000
1001     GroupStorageIDJournal groupStorageIDJournal;
1002     if (!group.storageID()) {
1003         // Store the group
1004         if (!store(&group, &groupStorageIDJournal)) {
1005             checkForMaxSizeReached();
1006             failureReason = isMaximumSizeReached() ? TotalQuotaReached : DiskOrOperationFailure;
1007             return false;
1008         }
1009     }
1010     
1011     ASSERT(group.newestCache());
1012     ASSERT(!group.isObsolete());
1013     ASSERT(!group.newestCache()->storageID());
1014     
1015     // Log the storageID changes to the in-memory resource objects. The journal
1016     // object will roll them back automatically in case a database operation
1017     // fails and this method returns early.
1018     ResourceStorageIDJournal resourceStorageIDJournal;
1019
1020     // Store the newest cache
1021     if (!store(group.newestCache(), &resourceStorageIDJournal)) {
1022         checkForMaxSizeReached();
1023         failureReason = isMaximumSizeReached() ? TotalQuotaReached : DiskOrOperationFailure;
1024         return false;
1025     }
1026     
1027     // Update the newest cache in the group.
1028     
1029     SQLiteStatement statement(m_database, "UPDATE CacheGroups SET newestCache=? WHERE id=?");
1030     if (statement.prepare() != SQLITE_OK) {
1031         failureReason = DiskOrOperationFailure;
1032         return false;
1033     }
1034     
1035     statement.bindInt64(1, group.newestCache()->storageID());
1036     statement.bindInt64(2, group.storageID());
1037     
1038     if (!executeStatement(statement)) {
1039         failureReason = DiskOrOperationFailure;
1040         return false;
1041     }
1042     
1043     groupStorageIDJournal.commit();
1044     resourceStorageIDJournal.commit();
1045     storeCacheTransaction.commit();
1046     return true;
1047 }
1048
1049 bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup& group)
1050 {
1051     // Ignore the reason for failing, just attempt the store.
1052     FailureReason ignoredFailureReason;
1053     return storeNewestCache(group, nullptr, ignoredFailureReason);
1054 }
1055
1056 template<typename CharacterType>
1057 static inline void parseHeader(const CharacterType* header, unsigned headerLength, ResourceResponse& response)
1058 {
1059     ASSERT(find(header, headerLength, ':') != notFound);
1060     unsigned colonPosition = find(header, headerLength, ':');
1061
1062     // Save memory by putting the header names into atomic strings so each is stored only once,
1063     // even though the setHTTPHeaderField function does not require an atomic string.
1064     AtomicString headerName { header, colonPosition };
1065     String headerValue { header + colonPosition + 1, headerLength - colonPosition - 1 };
1066
1067     response.setHTTPHeaderField(headerName, headerValue);
1068 }
1069
1070 static inline void parseHeaders(const String& headers, ResourceResponse& response)
1071 {
1072     unsigned startPos = 0;
1073     size_t endPos;
1074     while ((endPos = headers.find('\n', startPos)) != notFound) {
1075         ASSERT(startPos != endPos);
1076
1077         if (headers.is8Bit())
1078             parseHeader(headers.characters8() + startPos, endPos - startPos, response);
1079         else
1080             parseHeader(headers.characters16() + startPos, endPos - startPos, response);
1081         
1082         startPos = endPos + 1;
1083     }
1084     
1085     if (startPos != headers.length()) {
1086         if (headers.is8Bit())
1087             parseHeader(headers.characters8(), headers.length(), response);
1088         else
1089             parseHeader(headers.characters16(), headers.length(), response);
1090     }
1091 }
1092     
1093 RefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storageID)
1094 {
1095     ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
1096     SQLiteStatement cacheStatement(m_database,
1097                                    "SELECT url, statusCode, type, mimeType, textEncodingName, headers, CacheResourceData.data, CacheResourceData.path FROM CacheEntries INNER JOIN CacheResources ON CacheEntries.resource=CacheResources.id "
1098                                    "INNER JOIN CacheResourceData ON CacheResourceData.id=CacheResources.data WHERE CacheEntries.cache=?");
1099     if (cacheStatement.prepare() != SQLITE_OK) {
1100         LOG_ERROR("Could not prepare cache statement, error \"%s\"", m_database.lastErrorMsg());
1101         return nullptr;
1102     }
1103     
1104     cacheStatement.bindInt64(1, storageID);
1105
1106     auto cache = ApplicationCache::create();
1107
1108     String flatFileDirectory = FileSystem::pathByAppendingComponent(m_cacheDirectory, m_flatFileSubdirectoryName);
1109
1110     int result;
1111     while ((result = cacheStatement.step()) == SQLITE_ROW) {
1112         URL url({ }, cacheStatement.getColumnText(0));
1113         
1114         int httpStatusCode = cacheStatement.getColumnInt(1);
1115
1116         unsigned type = static_cast<unsigned>(cacheStatement.getColumnInt64(2));
1117
1118         Vector<char> blob;
1119         cacheStatement.getColumnBlobAsVector(6, blob);
1120         
1121         auto data = SharedBuffer::create(WTFMove(blob));
1122         
1123         String path = cacheStatement.getColumnText(7);
1124         long long size = 0;
1125         if (path.isEmpty())
1126             size = data->size();
1127         else {
1128             path = FileSystem::pathByAppendingComponent(flatFileDirectory, path);
1129             FileSystem::getFileSize(path, size);
1130         }
1131         
1132         String mimeType = cacheStatement.getColumnText(3);
1133         String textEncodingName = cacheStatement.getColumnText(4);
1134         
1135         ResourceResponse response(url, mimeType, size, textEncodingName);
1136         response.setHTTPStatusCode(httpStatusCode);
1137
1138         String headers = cacheStatement.getColumnText(5);
1139         parseHeaders(headers, response);
1140         
1141         auto resource = ApplicationCacheResource::create(url, response, type, WTFMove(data), path);
1142
1143         if (type & ApplicationCacheResource::Manifest)
1144             cache->setManifestResource(WTFMove(resource));
1145         else
1146             cache->addResource(WTFMove(resource));
1147     }
1148
1149     if (result != SQLITE_DONE)
1150         LOG_ERROR("Could not load cache resources, error \"%s\"", m_database.lastErrorMsg());
1151
1152     if (!cache->manifestResource()) {
1153         LOG_ERROR("Could not load application cache because there was no manifest resource");
1154         return nullptr;
1155     }
1156
1157     // Load the online whitelist
1158     SQLiteStatement whitelistStatement(m_database, "SELECT url FROM CacheWhitelistURLs WHERE cache=?");
1159     if (whitelistStatement.prepare() != SQLITE_OK)
1160         return nullptr;
1161     whitelistStatement.bindInt64(1, storageID);
1162     
1163     Vector<URL> whitelist;
1164     while ((result = whitelistStatement.step()) == SQLITE_ROW) 
1165         whitelist.append(URL({ }, whitelistStatement.getColumnText(0)));
1166
1167     if (result != SQLITE_DONE)
1168         LOG_ERROR("Could not load cache online whitelist, error \"%s\"", m_database.lastErrorMsg());
1169
1170     cache->setOnlineWhitelist(whitelist);
1171
1172     // Load online whitelist wildcard flag.
1173     SQLiteStatement whitelistWildcardStatement(m_database, "SELECT wildcard FROM CacheAllowsAllNetworkRequests WHERE cache=?");
1174     if (whitelistWildcardStatement.prepare() != SQLITE_OK)
1175         return nullptr;
1176     whitelistWildcardStatement.bindInt64(1, storageID);
1177     
1178     result = whitelistWildcardStatement.step();
1179     if (result != SQLITE_ROW)
1180         LOG_ERROR("Could not load cache online whitelist wildcard flag, error \"%s\"", m_database.lastErrorMsg());
1181
1182     cache->setAllowsAllNetworkRequests(whitelistWildcardStatement.getColumnInt64(0));
1183
1184     if (whitelistWildcardStatement.step() != SQLITE_DONE)
1185         LOG_ERROR("Too many rows for online whitelist wildcard flag");
1186
1187     // Load fallback URLs.
1188     SQLiteStatement fallbackStatement(m_database, "SELECT namespace, fallbackURL FROM FallbackURLs WHERE cache=?");
1189     if (fallbackStatement.prepare() != SQLITE_OK)
1190         return nullptr;
1191     fallbackStatement.bindInt64(1, storageID);
1192     
1193     FallbackURLVector fallbackURLs;
1194     while ((result = fallbackStatement.step()) == SQLITE_ROW) 
1195         fallbackURLs.append(std::make_pair(URL({ }, fallbackStatement.getColumnText(0)), URL({ }, fallbackStatement.getColumnText(1))));
1196
1197     if (result != SQLITE_DONE)
1198         LOG_ERROR("Could not load fallback URLs, error \"%s\"", m_database.lastErrorMsg());
1199
1200     cache->setFallbackURLs(fallbackURLs);
1201     
1202     cache->setStorageID(storageID);
1203
1204     return WTFMove(cache);
1205 }    
1206     
1207 void ApplicationCacheStorage::remove(ApplicationCache* cache)
1208 {
1209     SQLiteTransactionInProgressAutoCounter transactionCounter;
1210
1211     if (!cache->storageID())
1212         return;
1213     
1214     openDatabase(false);
1215     if (!m_database.isOpen())
1216         return;
1217
1218     ASSERT(cache->group());
1219     ASSERT(cache->group()->storageID());
1220
1221     // All associated data will be deleted by database triggers.
1222     SQLiteStatement statement(m_database, "DELETE FROM Caches WHERE id=?");
1223     if (statement.prepare() != SQLITE_OK)
1224         return;
1225     
1226     statement.bindInt64(1, cache->storageID());
1227     executeStatement(statement);
1228
1229     cache->clearStorageID();
1230
1231     if (cache->group()->newestCache() == cache) {
1232         // Currently, there are no triggers on the cache group, which is why the cache had to be removed separately above.
1233         SQLiteStatement groupStatement(m_database, "DELETE FROM CacheGroups WHERE id=?");
1234         if (groupStatement.prepare() != SQLITE_OK)
1235             return;
1236         
1237         groupStatement.bindInt64(1, cache->group()->storageID());
1238         executeStatement(groupStatement);
1239
1240         cache->group()->clearStorageID();
1241     }
1242     
1243     checkForDeletedResources();
1244 }    
1245
1246 void ApplicationCacheStorage::empty()
1247 {
1248     SQLiteTransactionInProgressAutoCounter transactionCounter;
1249
1250     openDatabase(false);
1251     
1252     if (!m_database.isOpen())
1253         return;
1254     
1255     // Clear cache groups, caches, cache resources, and origins.
1256     executeSQLCommand("DELETE FROM CacheGroups");
1257     executeSQLCommand("DELETE FROM Caches");
1258     executeSQLCommand("DELETE FROM Origins");
1259     
1260     // Clear the storage IDs for the caches in memory.
1261     // The caches will still work, but cached resources will not be saved to disk 
1262     // until a cache update process has been initiated.
1263     for (auto* group : m_cachesInMemory.values())
1264         group->clearStorageID();
1265
1266     checkForDeletedResources();
1267 }
1268     
1269 void ApplicationCacheStorage::deleteTables()
1270 {
1271     empty();
1272     m_database.clearAllTables();
1273 }
1274     
1275 bool ApplicationCacheStorage::shouldStoreResourceAsFlatFile(ApplicationCacheResource* resource)
1276 {
1277     auto& type = resource->response().mimeType();
1278     return startsWithLettersIgnoringASCIICase(type, "audio/") || startsWithLettersIgnoringASCIICase(type, "video/");
1279 }
1280     
1281 bool ApplicationCacheStorage::writeDataToUniqueFileInDirectory(SharedBuffer& data, const String& directory, String& path, const String& fileExtension)
1282 {
1283     String fullPath;
1284     
1285     do {
1286         path = FileSystem::encodeForFileName(createCanonicalUUIDString()) + fileExtension;
1287         // Guard against the above function being called on a platform which does not implement
1288         // createCanonicalUUIDString().
1289         ASSERT(!path.isEmpty());
1290         if (path.isEmpty())
1291             return false;
1292         
1293         fullPath = FileSystem::pathByAppendingComponent(directory, path);
1294     } while (FileSystem::directoryName(fullPath) != directory || FileSystem::fileExists(fullPath));
1295     
1296     FileSystem::PlatformFileHandle handle = FileSystem::openFile(fullPath, FileSystem::FileOpenMode::Write);
1297     if (!handle)
1298         return false;
1299     
1300     int64_t writtenBytes = FileSystem::writeToFile(handle, data.data(), data.size());
1301     FileSystem::closeFile(handle);
1302     
1303     if (writtenBytes != static_cast<int64_t>(data.size())) {
1304         FileSystem::deleteFile(fullPath);
1305         return false;
1306     }
1307     
1308     return true;
1309 }
1310
1311 std::optional<Vector<URL>> ApplicationCacheStorage::manifestURLs()
1312 {
1313     SQLiteTransactionInProgressAutoCounter transactionCounter;
1314
1315     openDatabase(false);
1316     if (!m_database.isOpen())
1317         return std::nullopt;
1318
1319     SQLiteStatement selectURLs(m_database, "SELECT manifestURL FROM CacheGroups");
1320
1321     if (selectURLs.prepare() != SQLITE_OK)
1322         return std::nullopt;
1323
1324     Vector<URL> urls;
1325     while (selectURLs.step() == SQLITE_ROW)
1326         urls.append(URL({ }, selectURLs.getColumnText(0)));
1327
1328     return WTFMove(urls);
1329 }
1330
1331 bool ApplicationCacheStorage::deleteCacheGroupRecord(const String& manifestURL)
1332 {
1333     ASSERT(SQLiteDatabaseTracker::hasTransactionInProgress());
1334     SQLiteStatement idStatement(m_database, "SELECT id FROM CacheGroups WHERE manifestURL=?");
1335     if (idStatement.prepare() != SQLITE_OK)
1336         return false;
1337
1338     idStatement.bindText(1, manifestURL);
1339
1340     int result = idStatement.step();
1341     if (result != SQLITE_ROW)
1342         return false;
1343
1344     int64_t groupId = idStatement.getColumnInt64(0);
1345
1346     SQLiteStatement cacheStatement(m_database, "DELETE FROM Caches WHERE cacheGroup=?");
1347     if (cacheStatement.prepare() != SQLITE_OK)
1348         return false;
1349
1350     SQLiteStatement groupStatement(m_database, "DELETE FROM CacheGroups WHERE id=?");
1351     if (groupStatement.prepare() != SQLITE_OK)
1352         return false;
1353
1354     cacheStatement.bindInt64(1, groupId);
1355     executeStatement(cacheStatement);
1356     groupStatement.bindInt64(1, groupId);
1357     executeStatement(groupStatement);
1358     return true;
1359 }
1360
1361 bool ApplicationCacheStorage::deleteCacheGroup(const String& manifestURL)
1362 {
1363     SQLiteTransactionInProgressAutoCounter transactionCounter;
1364
1365     SQLiteTransaction deleteTransaction(m_database);
1366
1367     // Check to see if the group is in memory.
1368     if (auto* group = m_cachesInMemory.get(manifestURL))
1369         cacheGroupMadeObsolete(*group);
1370     else {
1371         // The cache group is not in memory, so remove it from the disk.
1372         openDatabase(false);
1373         if (!m_database.isOpen())
1374             return false;
1375         if (!deleteCacheGroupRecord(manifestURL)) {
1376             LOG_ERROR("Could not delete cache group record, error \"%s\"", m_database.lastErrorMsg());
1377             return false;
1378         }
1379     }
1380
1381     deleteTransaction.commit();
1382
1383     checkForDeletedResources();
1384
1385     return true;
1386 }
1387
1388 void ApplicationCacheStorage::vacuumDatabaseFile()
1389 {
1390     SQLiteTransactionInProgressAutoCounter transactionCounter;
1391
1392     openDatabase(false);
1393     if (!m_database.isOpen())
1394         return;
1395
1396     m_database.runVacuumCommand();
1397 }
1398
1399 void ApplicationCacheStorage::checkForMaxSizeReached()
1400 {
1401     if (m_database.lastError() == SQLITE_FULL)
1402         m_isMaximumSizeReached = true;
1403 }
1404     
1405 void ApplicationCacheStorage::checkForDeletedResources()
1406 {
1407     openDatabase(false);
1408     if (!m_database.isOpen())
1409         return;
1410
1411     // Select only the paths in DeletedCacheResources that do not also appear in CacheResourceData:
1412     SQLiteStatement selectPaths(m_database, "SELECT DeletedCacheResources.path "
1413         "FROM DeletedCacheResources "
1414         "LEFT JOIN CacheResourceData "
1415         "ON DeletedCacheResources.path = CacheResourceData.path "
1416         "WHERE (SELECT DeletedCacheResources.path == CacheResourceData.path) IS NULL");
1417     
1418     if (selectPaths.prepare() != SQLITE_OK)
1419         return;
1420     
1421     if (selectPaths.step() != SQLITE_ROW)
1422         return;
1423     
1424     do {
1425         String path = selectPaths.getColumnText(0);
1426         if (path.isEmpty())
1427             continue;
1428         
1429         String flatFileDirectory = FileSystem::pathByAppendingComponent(m_cacheDirectory, m_flatFileSubdirectoryName);
1430         String fullPath = FileSystem::pathByAppendingComponent(flatFileDirectory, path);
1431         
1432         // Don't exit the flatFileDirectory! This should only happen if the "path" entry contains a directory 
1433         // component, but protect against it regardless.
1434         if (FileSystem::directoryName(fullPath) != flatFileDirectory)
1435             continue;
1436         
1437         FileSystem::deleteFile(fullPath);
1438     } while (selectPaths.step() == SQLITE_ROW);
1439     
1440     executeSQLCommand("DELETE FROM DeletedCacheResources");
1441 }
1442     
1443 long long ApplicationCacheStorage::flatFileAreaSize()
1444 {
1445     openDatabase(false);
1446     if (!m_database.isOpen())
1447         return 0;
1448     
1449     SQLiteStatement selectPaths(m_database, "SELECT path FROM CacheResourceData WHERE path NOT NULL");
1450
1451     if (selectPaths.prepare() != SQLITE_OK) {
1452         LOG_ERROR("Could not load flat file cache resource data, error \"%s\"", m_database.lastErrorMsg());
1453         return 0;
1454     }
1455
1456     long long totalSize = 0;
1457     String flatFileDirectory = FileSystem::pathByAppendingComponent(m_cacheDirectory, m_flatFileSubdirectoryName);
1458     while (selectPaths.step() == SQLITE_ROW) {
1459         String path = selectPaths.getColumnText(0);
1460         String fullPath = FileSystem::pathByAppendingComponent(flatFileDirectory, path);
1461         long long pathSize = 0;
1462         if (!FileSystem::getFileSize(fullPath, pathSize))
1463             continue;
1464         totalSize += pathSize;
1465     }
1466     
1467     return totalSize;
1468 }
1469
1470 Vector<Ref<SecurityOrigin>> ApplicationCacheStorage::originsWithCache()
1471 {
1472     auto urls = manifestURLs();
1473     if (!urls)
1474         return { };
1475
1476     // Multiple manifest URLs might share the same SecurityOrigin, so we might be creating extra, wasted origins here.
1477     // The current schema doesn't allow for a more efficient way of building this list.
1478     Vector<Ref<SecurityOrigin>> origins;
1479     origins.reserveInitialCapacity(urls->size());
1480     for (auto& url : *urls)
1481         origins.uncheckedAppend(SecurityOrigin::create(url));
1482     return origins;
1483 }
1484
1485 void ApplicationCacheStorage::deleteAllEntries()
1486 {
1487     empty();
1488     vacuumDatabaseFile();
1489 }
1490
1491 void ApplicationCacheStorage::deleteAllCaches()
1492 {
1493     auto origins = originsWithCache();
1494     for (auto& origin : origins)
1495         deleteCacheForOrigin(origin);
1496
1497     vacuumDatabaseFile();
1498 }
1499
1500 void ApplicationCacheStorage::deleteCacheForOrigin(const SecurityOrigin& securityOrigin)
1501 {
1502     auto urls = manifestURLs();
1503     if (!urls) {
1504         LOG_ERROR("Failed to retrieve ApplicationCache manifest URLs");
1505         return;
1506     }
1507
1508     URL originURL(URL(), securityOrigin.toString());
1509
1510     for (const auto& url : *urls) {
1511         if (!protocolHostAndPortAreEqual(url, originURL))
1512             continue;
1513
1514         if (auto* group = findInMemoryCacheGroup(url))
1515             group->makeObsolete();
1516         else
1517             deleteCacheGroup(url);
1518     }
1519 }
1520
1521 int64_t ApplicationCacheStorage::diskUsageForOrigin(const SecurityOrigin& securityOrigin)
1522 {
1523     int64_t usage = 0;
1524     calculateUsageForOrigin(&securityOrigin, usage);
1525     return usage;
1526 }
1527
1528 ApplicationCacheStorage::ApplicationCacheStorage(const String& cacheDirectory, const String& flatFileSubdirectoryName)
1529     : m_cacheDirectory(cacheDirectory)
1530     , m_flatFileSubdirectoryName(flatFileSubdirectoryName)
1531 {
1532 }
1533
1534 } // namespace WebCore