diff options
Diffstat (limited to 'WebCore/loader/appcache/ApplicationCacheStorage.cpp')
-rw-r--r-- | WebCore/loader/appcache/ApplicationCacheStorage.cpp | 663 |
1 files changed, 0 insertions, 663 deletions
diff --git a/WebCore/loader/appcache/ApplicationCacheStorage.cpp b/WebCore/loader/appcache/ApplicationCacheStorage.cpp deleted file mode 100644 index 910d00c..0000000 --- a/WebCore/loader/appcache/ApplicationCacheStorage.cpp +++ /dev/null @@ -1,663 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "ApplicationCacheStorage.h" - -#if ENABLE(OFFLINE_WEB_APPLICATIONS) - -#include "ApplicationCache.h" -#include "ApplicationCacheGroup.h" -#include "ApplicationCacheResource.h" -#include "FileSystem.h" -#include "CString.h" -#include "KURL.h" -#include "SQLiteStatement.h" -#include "SQLiteTransaction.h" - -namespace WebCore { - -static unsigned urlHostHash(const KURL& url) -{ - unsigned hostStart = url.hostStart(); - unsigned hostEnd = url.hostEnd(); - - return AlreadyHashed::avoidDeletedValue(StringImpl::computeHash(url.string().characters() + hostStart, hostEnd - hostStart)); -} - -ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const KURL& manifestURL) -{ - openDatabase(false); - if (!m_database.isOpen()) - return 0; - - SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL AND manifestURL=?"); - if (statement.prepare() != SQLResultOk) - return 0; - - statement.bindText(1, manifestURL); - - int result = statement.step(); - if (result == SQLResultDone) - return 0; - - if (result != SQLResultRow) { - LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg()); - return 0; - } - - unsigned newestCacheStorageID = (unsigned)statement.getColumnInt64(2); - - RefPtr<ApplicationCache> cache = loadCache(newestCacheStorageID); - if (!cache) - return 0; - - ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL); - - group->setStorageID((unsigned)statement.getColumnInt64(0)); - group->setNewestCache(cache.release()); - - return group; -} - -ApplicationCacheGroup* ApplicationCacheStorage::findOrCreateCacheGroup(const KURL& manifestURL) -{ - std::pair<CacheGroupMap::iterator, bool> result = m_cachesInMemory.add(manifestURL, 0); - - if (!result.second) { - ASSERT(result.first->second); - - return result.first->second; - } - - // Look up the group in the database - ApplicationCacheGroup* group = loadCacheGroup(manifestURL); - - // If the group was not found we need to create it - if (!group) { - group = new ApplicationCacheGroup(manifestURL); - m_cacheHostSet.add(urlHostHash(manifestURL)); - } - - result.first->second = group; - - return group; -} - -void ApplicationCacheStorage::loadManifestHostHashes() -{ - static bool hasLoadedHashes = false; - - if (hasLoadedHashes) - return; - - // We set this flag to true before the database has been opened - // to avoid trying to open the database over and over if it doesn't exist. - hasLoadedHashes = true; - - openDatabase(false); - if (!m_database.isOpen()) - return; - - // Fetch the host hashes. - SQLiteStatement statement(m_database, "SELECT manifestHostHash FROM CacheGroups"); - if (statement.prepare() != SQLResultOk) - return; - - int result; - while ((result = statement.step()) == SQLResultRow) - m_cacheHostSet.add((unsigned)statement.getColumnInt64(0)); -} - -ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url) -{ - loadManifestHostHashes(); - - // Hash the host name and see if there's a manifest with the same host. - if (!m_cacheHostSet.contains(urlHostHash(url))) - return 0; - - // Check if a cache already exists in memory. - CacheGroupMap::const_iterator end = m_cachesInMemory.end(); - for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) { - ApplicationCacheGroup* group = it->second; - - if (!protocolHostAndPortAreEqual(url, group->manifestURL())) - continue; - - if (ApplicationCache* cache = group->newestCache()) { - if (cache->resourceForURL(url)) - return group; - } - } - - if (!m_database.isOpen()) - return 0; - - // Check the database. Look for all cache groups with a newest cache. - SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL"); - if (statement.prepare() != SQLResultOk) - return 0; - - int result; - while ((result = statement.step()) == SQLResultRow) { - KURL manifestURL = KURL(statement.getColumnText(1)); - - if (!protocolHostAndPortAreEqual(url, manifestURL)) - continue; - - // We found a cache group that matches. Now check if the newest cache has a resource with - // a matching URL. - unsigned newestCacheID = (unsigned)statement.getColumnInt64(2); - RefPtr<ApplicationCache> cache = loadCache(newestCacheID); - - if (!cache->resourceForURL(url)) - continue; - - ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL); - - group->setStorageID((unsigned)statement.getColumnInt64(0)); - group->setNewestCache(cache.release()); - - ASSERT(!m_cachesInMemory.contains(manifestURL)); - m_cachesInMemory.set(group->manifestURL(), group); - - return group; - } - - if (result != SQLResultDone) - LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg()); - - return 0; -} - -void ApplicationCacheStorage::cacheGroupDestroyed(ApplicationCacheGroup* group) -{ - ASSERT(m_cachesInMemory.get(group->manifestURL()) == group); - - m_cachesInMemory.remove(group->manifestURL()); - - // If the cache is half-created, we don't want it in the saved set. - if (!group->savedNewestCachePointer()) - m_cacheHostSet.remove(urlHostHash(group->manifestURL())); -} - -void ApplicationCacheStorage::setCacheDirectory(const String& cacheDirectory) -{ - ASSERT(m_cacheDirectory.isNull()); - ASSERT(!cacheDirectory.isNull()); - - m_cacheDirectory = cacheDirectory; -} - -bool ApplicationCacheStorage::executeSQLCommand(const String& sql) -{ - ASSERT(m_database.isOpen()); - - bool result = m_database.executeCommand(sql); - if (!result) - LOG_ERROR("Application Cache Storage: failed to execute statement \"%s\" error \"%s\"", - sql.utf8().data(), m_database.lastErrorMsg()); - - return result; -} - -static const int SchemaVersion = 2; - -void ApplicationCacheStorage::verifySchemaVersion() -{ - if (m_database.tableExists("SchemaVersion")) { - int version = SQLiteStatement(m_database, "SELECT version from SchemaVersion").getColumnInt(0); - - if (version == SchemaVersion) - return; - } - - m_database.clearAllTables(); - - SQLiteTransaction createSchemaVersionTable(m_database); - createSchemaVersionTable.begin(); - - executeSQLCommand("CREATE TABLE SchemaVersion (version INTEGER NOT NULL)"); - SQLiteStatement statement(m_database, "INSERT INTO SchemaVersion (version) VALUES (?)"); - if (statement.prepare() != SQLResultOk) - return; - - statement.bindInt64(1, SchemaVersion); - executeStatement(statement); - createSchemaVersionTable.commit(); -} - -void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist) -{ - if (m_database.isOpen()) - return; - - // The cache directory should never be null, but if it for some weird reason is we bail out. - if (m_cacheDirectory.isNull()) - return; - - String applicationCachePath = pathByAppendingComponent(m_cacheDirectory, "ApplicationCache.db"); - if (!createIfDoesNotExist && !fileExists(applicationCachePath)) - return; - - makeAllDirectories(m_cacheDirectory); - m_database.open(applicationCachePath); - - if (!m_database.isOpen()) - return; - - verifySchemaVersion(); - - // Create tables - executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheGroups (id INTEGER PRIMARY KEY AUTOINCREMENT, " - "manifestHostHash INTEGER NOT NULL ON CONFLICT FAIL, manifestURL TEXT UNIQUE ON CONFLICT FAIL, newestCache INTEGER)"); - executeSQLCommand("CREATE TABLE IF NOT EXISTS Caches (id INTEGER PRIMARY KEY AUTOINCREMENT, cacheGroup INTEGER)"); - executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheWhitelistURLs (url TEXT NOT NULL ON CONFLICT FAIL, cache INTEGER NOT NULL ON CONFLICT FAIL)"); - executeSQLCommand("CREATE TABLE IF NOT EXISTS FallbackURLs (namespace TEXT NOT NULL ON CONFLICT FAIL, fallbackURL TEXT NOT NULL ON CONFLICT FAIL, " - "cache INTEGER NOT NULL ON CONFLICT FAIL)"); - executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheEntries (cache INTEGER NOT NULL ON CONFLICT FAIL, type INTEGER, resource INTEGER NOT NULL)"); - executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResources (id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT NOT NULL ON CONFLICT FAIL, " - "statusCode INTEGER NOT NULL, responseURL TEXT NOT NULL, mimeType TEXT, textEncodingName TEXT, headers TEXT, data INTEGER NOT NULL ON CONFLICT FAIL)"); - executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResourceData (id INTEGER PRIMARY KEY AUTOINCREMENT, data BLOB)"); - - // When a cache is deleted, all its entries and its whitelist should be deleted. - executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheDeleted AFTER DELETE ON Caches" - " FOR EACH ROW BEGIN" - " DELETE FROM CacheEntries WHERE cache = OLD.id;" - " DELETE FROM CacheWhitelistURLs WHERE cache = OLD.id;" - " DELETE FROM FallbackURLs WHERE cache = OLD.id;" - " END"); - - // When a cache resource is deleted, its data blob should also be deleted. - executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheResourceDeleted AFTER DELETE ON CacheResources" - " FOR EACH ROW BEGIN" - " DELETE FROM CacheResourceData WHERE id = OLD.data;" - " END"); -} - -bool ApplicationCacheStorage::executeStatement(SQLiteStatement& statement) -{ - bool result = statement.executeCommand(); - if (!result) - LOG_ERROR("Application Cache Storage: failed to execute statement \"%s\" error \"%s\"", - statement.query().utf8().data(), m_database.lastErrorMsg()); - - return result; -} - -bool ApplicationCacheStorage::store(ApplicationCacheGroup* group) -{ - ASSERT(group->storageID() == 0); - - SQLiteStatement statement(m_database, "INSERT INTO CacheGroups (manifestHostHash, manifestURL) VALUES (?, ?)"); - if (statement.prepare() != SQLResultOk) - return false; - - statement.bindInt64(1, urlHostHash(group->manifestURL())); - statement.bindText(2, group->manifestURL()); - - if (!executeStatement(statement)) - return false; - - group->setStorageID((unsigned)m_database.lastInsertRowID()); - return true; -} - -bool ApplicationCacheStorage::store(ApplicationCache* cache) -{ - ASSERT(cache->storageID() == 0); - ASSERT(cache->group()->storageID() != 0); - - SQLiteStatement statement(m_database, "INSERT INTO Caches (cacheGroup) VALUES (?)"); - if (statement.prepare() != SQLResultOk) - return false; - - statement.bindInt64(1, cache->group()->storageID()); - - if (!executeStatement(statement)) - return false; - - unsigned cacheStorageID = (unsigned)m_database.lastInsertRowID(); - - // Store all resources - { - ApplicationCache::ResourceMap::const_iterator end = cache->end(); - for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) { - if (!store(it->second.get(), cacheStorageID)) - return false; - } - } - - // Store the online whitelist - const HashSet<String>& onlineWhitelist = cache->onlineWhitelist(); - { - HashSet<String>::const_iterator end = onlineWhitelist.end(); - for (HashSet<String>::const_iterator it = onlineWhitelist.begin(); it != end; ++it) { - SQLiteStatement statement(m_database, "INSERT INTO CacheWhitelistURLs (url, cache) VALUES (?, ?)"); - statement.prepare(); - - statement.bindText(1, *it); - statement.bindInt64(2, cacheStorageID); - - if (!executeStatement(statement)) - return false; - } - } - - cache->setStorageID(cacheStorageID); - return true; -} - -bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned cacheStorageID) -{ - ASSERT(cacheStorageID); - ASSERT(!resource->storageID()); - - openDatabase(true); - - // First, insert the data - SQLiteStatement dataStatement(m_database, "INSERT INTO CacheResourceData (data) VALUES (?)"); - if (dataStatement.prepare() != SQLResultOk) - return false; - - if (resource->data()->size()) - dataStatement.bindBlob(1, resource->data()->data(), resource->data()->size()); - - if (!dataStatement.executeCommand()) - return false; - - unsigned dataId = (unsigned)m_database.lastInsertRowID(); - - // Then, insert the resource - - // Serialize the headers - Vector<UChar> stringBuilder; - - HTTPHeaderMap::const_iterator end = resource->response().httpHeaderFields().end(); - for (HTTPHeaderMap::const_iterator it = resource->response().httpHeaderFields().begin(); it!= end; ++it) { - stringBuilder.append(it->first.characters(), it->first.length()); - stringBuilder.append((UChar)':'); - stringBuilder.append(it->second.characters(), it->second.length()); - stringBuilder.append((UChar)'\n'); - } - - String headers = String::adopt(stringBuilder); - - SQLiteStatement resourceStatement(m_database, "INSERT INTO CacheResources (url, statusCode, responseURL, headers, data, mimeType, textEncodingName) VALUES (?, ?, ?, ?, ?, ?, ?)"); - if (resourceStatement.prepare() != SQLResultOk) - return false; - - resourceStatement.bindText(1, resource->url()); - resourceStatement.bindInt64(2, resource->response().httpStatusCode()); - resourceStatement.bindText(3, resource->response().url()); - resourceStatement.bindText(4, headers); - resourceStatement.bindInt64(5, dataId); - resourceStatement.bindText(6, resource->response().mimeType()); - resourceStatement.bindText(7, resource->response().textEncodingName()); - - if (!executeStatement(resourceStatement)) - return false; - - unsigned resourceId = (unsigned)m_database.lastInsertRowID(); - - // Finally, insert the cache entry - SQLiteStatement entryStatement(m_database, "INSERT INTO CacheEntries (cache, type, resource) VALUES (?, ?, ?)"); - if (entryStatement.prepare() != SQLResultOk) - return false; - - entryStatement.bindInt64(1, cacheStorageID); - entryStatement.bindInt64(2, resource->type()); - entryStatement.bindInt64(3, resourceId); - - if (!executeStatement(entryStatement)) - return false; - - resource->setStorageID(resourceId); - return true; -} - -void ApplicationCacheStorage::store(ApplicationCacheResource* resource, ApplicationCache* cache) -{ - ASSERT(cache->storageID()); - - openDatabase(true); - - SQLiteTransaction storeResourceTransaction(m_database); - storeResourceTransaction.begin(); - - if (!store(resource, cache->storageID())) - return; - - storeResourceTransaction.commit(); -} - -bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group) -{ - openDatabase(true); - - SQLiteTransaction storeCacheTransaction(m_database); - - storeCacheTransaction.begin(); - - if (!group->storageID()) { - // Store the group - if (!store(group)) - return false; - } - - ASSERT(group->newestCache()); - ASSERT(!group->newestCache()->storageID()); - - // Store the newest cache - if (!store(group->newestCache())) - return false; - - // Update the newest cache in the group. - - SQLiteStatement statement(m_database, "UPDATE CacheGroups SET newestCache=? WHERE id=?"); - if (statement.prepare() != SQLResultOk) - return false; - - statement.bindInt64(1, group->newestCache()->storageID()); - statement.bindInt64(2, group->storageID()); - - if (!executeStatement(statement)) - return false; - - storeCacheTransaction.commit(); - return true; -} - -static inline void parseHeader(const UChar* header, size_t headerLength, ResourceResponse& response) -{ - int pos = find(header, headerLength, ':'); - ASSERT(pos != -1); - - String headerName = String(header, pos); - String headerValue = String(header + pos + 1, headerLength - pos - 1); - - response.setHTTPHeaderField(headerName, headerValue); -} - -static inline void parseHeaders(const String& headers, ResourceResponse& response) -{ - int startPos = 0; - int endPos; - while ((endPos = headers.find('\n', startPos)) != -1) { - ASSERT(startPos != endPos); - - parseHeader(headers.characters() + startPos, endPos - startPos, response); - - startPos = endPos + 1; - } - - if (startPos != static_cast<int>(headers.length())) - parseHeader(headers.characters(), headers.length(), response); -} - -PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storageID) -{ - SQLiteStatement cacheStatement(m_database, - "SELECT url, type, mimeType, textEncodingName, headers, CacheResourceData.data FROM CacheEntries INNER JOIN CacheResources ON CacheEntries.resource=CacheResources.id " - "INNER JOIN CacheResourceData ON CacheResourceData.id=CacheResources.data WHERE CacheEntries.cache=?"); - if (cacheStatement.prepare() != SQLResultOk) { - LOG_ERROR("Could not prepare cache statement, error \"%s\"", m_database.lastErrorMsg()); - return 0; - } - - cacheStatement.bindInt64(1, storageID); - - RefPtr<ApplicationCache> cache = ApplicationCache::create(); - - int result; - while ((result = cacheStatement.step()) == SQLResultRow) { - KURL url(cacheStatement.getColumnText(0)); - - unsigned type = (unsigned)cacheStatement.getColumnInt64(1); - - Vector<char> blob; - cacheStatement.getColumnBlobAsVector(5, blob); - - RefPtr<SharedBuffer> data = SharedBuffer::adoptVector(blob); - - String mimeType = cacheStatement.getColumnText(2); - String textEncodingName = cacheStatement.getColumnText(3); - - ResourceResponse response(url, mimeType, data->size(), textEncodingName, ""); - - String headers = cacheStatement.getColumnText(4); - parseHeaders(headers, response); - - RefPtr<ApplicationCacheResource> resource = ApplicationCacheResource::create(url, response, type, data.release()); - - if (type & ApplicationCacheResource::Manifest) - cache->setManifestResource(resource.release()); - else - cache->addResource(resource.release()); - } - - if (result != SQLResultDone) - LOG_ERROR("Could not load cache resources, error \"%s\"", m_database.lastErrorMsg()); - - // Load the online whitelist - SQLiteStatement whitelistStatement(m_database, "SELECT url FROM CacheWhitelistURLs WHERE cache=?"); - if (whitelistStatement.prepare() != SQLResultOk) - return 0; - whitelistStatement.bindInt64(1, storageID); - - HashSet<String> whitelist; - while ((result = whitelistStatement.step()) == SQLResultRow) - whitelist.add(whitelistStatement.getColumnText(0)); - - if (result != SQLResultDone) - LOG_ERROR("Could not load cache online whitelist, error \"%s\"", m_database.lastErrorMsg()); - - cache->setOnlineWhitelist(whitelist); - - cache->setStorageID(storageID); - - return cache.release(); -} - -void ApplicationCacheStorage::remove(ApplicationCache* cache) -{ - if (!cache->storageID()) - return; - - openDatabase(false); - if (!m_database.isOpen()) - return; - - SQLiteStatement statement(m_database, "DELETE FROM Caches WHERE id=?"); - if (statement.prepare() != SQLResultOk) - return; - - statement.bindInt64(1, cache->storageID()); - executeStatement(statement); -} - -void ApplicationCacheStorage::empty() -{ - openDatabase(false); - - if (!m_database.isOpen()) - return; - - // Clear cache groups, caches and cache resources. - executeSQLCommand("DELETE FROM CacheGroups"); - executeSQLCommand("DELETE FROM Caches"); - executeSQLCommand("DELETE FROM CacheResources"); - - // Clear the storage IDs for the caches in memory. - // The caches will still work, but cached resources will not be saved to disk - // until a cache update process has been initiated. - CacheGroupMap::const_iterator end = m_cachesInMemory.end(); - for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) - it->second->clearStorageID(); -} - -bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, ApplicationCache* cache) -{ - // Create a new cache. - RefPtr<ApplicationCache> cacheCopy = ApplicationCache::create(); - - // Set the online whitelist - cacheCopy->setOnlineWhitelist(cache->onlineWhitelist()); - - // Traverse the cache and add copies of all resources. - ApplicationCache::ResourceMap::const_iterator end = cache->end(); - for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) { - ApplicationCacheResource* resource = it->second.get(); - - RefPtr<ApplicationCacheResource> resourceCopy = ApplicationCacheResource::create(resource->url(), resource->response(), resource->type(), resource->data()); - - cacheCopy->addResource(resourceCopy.release()); - } - - // Now create a new cache group. - OwnPtr<ApplicationCacheGroup> groupCopy(new ApplicationCacheGroup(cache->group()->manifestURL(), true)); - - groupCopy->setNewestCache(cacheCopy); - - ApplicationCacheStorage copyStorage; - copyStorage.setCacheDirectory(cacheDirectory); - - // Empty the cache in case something was there before. - copyStorage.empty(); - - return copyStorage.storeNewestCache(groupCopy.get()); -} - -ApplicationCacheStorage& cacheStorage() -{ - static ApplicationCacheStorage storage; - - return storage; -} - -} // namespace WebCore - -#endif // ENABLE(OFFLINE_WEB_APPLICATIONS) |