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