summaryrefslogtreecommitdiff
path: root/src/3rdparty/webkit/WebCore/loader/appcache
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 10:34:13 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 10:34:13 +0100
commit67ad0519fd165acee4a4d2a94fa502e9e4847bd0 (patch)
tree1dbf50b3dff8d5ca7e9344733968c72704eb15ff /src/3rdparty/webkit/WebCore/loader/appcache
downloadqt4-tools-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.gz
Long live Qt!
Diffstat (limited to 'src/3rdparty/webkit/WebCore/loader/appcache')
-rw-r--r--src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.cpp210
-rw-r--r--src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.h113
-rw-r--r--src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheGroup.cpp719
-rw-r--r--src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheGroup.h154
-rw-r--r--src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.cpp69
-rw-r--r--src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.h73
-rw-r--r--src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.cpp796
-rw-r--r--src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.h100
-rw-r--r--src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.cpp292
-rw-r--r--src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.h141
-rw-r--r--src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.idl74
-rw-r--r--src/3rdparty/webkit/WebCore/loader/appcache/ManifestParser.cpp187
-rw-r--r--src/3rdparty/webkit/WebCore/loader/appcache/ManifestParser.h49
13 files changed, 2977 insertions, 0 deletions
diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.cpp b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.cpp
new file mode 100644
index 0000000000..d7387995ba
--- /dev/null
+++ b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.cpp
@@ -0,0 +1,210 @@
+/*
+ * 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 "ApplicationCache.h"
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "ApplicationCacheGroup.h"
+#include "ApplicationCacheResource.h"
+#include "ApplicationCacheStorage.h"
+#include "ResourceRequest.h"
+#include <stdio.h>
+
+namespace WebCore {
+
+ApplicationCache::ApplicationCache()
+ : m_group(0)
+ , m_manifest(0)
+ , m_storageID(0)
+{
+}
+
+ApplicationCache::~ApplicationCache()
+{
+ if (m_group && !m_group->isCopy())
+ m_group->cacheDestroyed(this);
+}
+
+void ApplicationCache::setGroup(ApplicationCacheGroup* group)
+{
+ ASSERT(!m_group);
+ m_group = group;
+}
+
+void ApplicationCache::setManifestResource(PassRefPtr<ApplicationCacheResource> manifest)
+{
+ ASSERT(manifest);
+ ASSERT(!m_manifest);
+ ASSERT(manifest->type() & ApplicationCacheResource::Manifest);
+
+ m_manifest = manifest.get();
+
+ addResource(manifest);
+}
+
+void ApplicationCache::addResource(PassRefPtr<ApplicationCacheResource> resource)
+{
+ ASSERT(resource);
+
+ const String& url = resource->url();
+
+ ASSERT(!m_resources.contains(url));
+
+ if (m_storageID) {
+ ASSERT(!resource->storageID());
+
+ // Add the resource to the storage.
+ cacheStorage().store(resource.get(), this);
+ }
+
+ m_resources.set(url, resource);
+}
+
+unsigned ApplicationCache::removeResource(const String& url)
+{
+ HashMap<String, RefPtr<ApplicationCacheResource> >::iterator it = m_resources.find(url);
+ if (it == m_resources.end())
+ return 0;
+
+ // The resource exists, get its type so we can return it.
+ unsigned type = it->second->type();
+
+ m_resources.remove(it);
+
+ return type;
+}
+
+ApplicationCacheResource* ApplicationCache::resourceForURL(const String& url)
+{
+ return m_resources.get(url).get();
+}
+
+bool ApplicationCache::requestIsHTTPOrHTTPSGet(const ResourceRequest& request)
+{
+ if (!request.url().protocolIs("http") && !request.url().protocolIs("https"))
+ return false;
+
+ if (!equalIgnoringCase(request.httpMethod(), "GET"))
+ return false;
+
+ return true;
+}
+
+ApplicationCacheResource* ApplicationCache::resourceForRequest(const ResourceRequest& request)
+{
+ // We only care about HTTP/HTTPS GET requests.
+ if (!requestIsHTTPOrHTTPSGet(request))
+ return false;
+
+ return resourceForURL(request.url());
+}
+
+unsigned ApplicationCache::numDynamicEntries() const
+{
+ // FIXME: Implement
+ return 0;
+}
+
+String ApplicationCache::dynamicEntry(unsigned) const
+{
+ // FIXME: Implement
+ return String();
+}
+
+bool ApplicationCache::addDynamicEntry(const String& url)
+{
+ if (!equalIgnoringCase(m_group->manifestURL().protocol(), KURL(url).protocol()))
+ return false;
+
+ // FIXME: Implement
+ return true;
+}
+
+void ApplicationCache::removeDynamicEntry(const String&)
+{
+ // FIXME: Implement
+}
+
+void ApplicationCache::setOnlineWhitelist(const Vector<KURL>& onlineWhitelist)
+{
+ ASSERT(m_onlineWhitelist.isEmpty());
+ m_onlineWhitelist = onlineWhitelist;
+}
+
+bool ApplicationCache::isURLInOnlineWhitelist(const KURL& url)
+{
+ size_t whitelistSize = m_onlineWhitelist.size();
+ for (size_t i = 0; i < whitelistSize; ++i) {
+ if (protocolHostAndPortAreEqual(url, m_onlineWhitelist[i]) && url.string().startsWith(m_onlineWhitelist[i].string()))
+ return true;
+ }
+ return false;
+}
+
+void ApplicationCache::setFallbackURLs(const FallbackURLVector& fallbackURLs)
+{
+ ASSERT(m_fallbackURLs.isEmpty());
+ m_fallbackURLs = fallbackURLs;
+}
+
+bool ApplicationCache::urlMatchesFallbackNamespace(const KURL& url, KURL* fallbackURL)
+{
+ size_t fallbackCount = m_fallbackURLs.size();
+ for (size_t i = 0; i < fallbackCount; ++i) {
+ if (protocolHostAndPortAreEqual(url, m_fallbackURLs[i].first) && url.string().startsWith(m_fallbackURLs[i].first.string())) {
+ if (fallbackURL)
+ *fallbackURL = m_fallbackURLs[i].second;
+ return true;
+ }
+ }
+ return false;
+}
+
+void ApplicationCache::clearStorageID()
+{
+ m_storageID = 0;
+
+ ResourceMap::const_iterator end = m_resources.end();
+ for (ResourceMap::const_iterator it = m_resources.begin(); it != end; ++it)
+ it->second->clearStorageID();
+}
+
+#ifndef NDEBUG
+void ApplicationCache::dump()
+{
+ HashMap<String, RefPtr<ApplicationCacheResource> >::const_iterator end = m_resources.end();
+
+ for (HashMap<String, RefPtr<ApplicationCacheResource> >::const_iterator it = m_resources.begin(); it != end; ++it) {
+ printf("%s ", it->first.ascii().data());
+ ApplicationCacheResource::dumpType(it->second->type());
+ }
+}
+#endif
+
+}
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.h b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.h
new file mode 100644
index 0000000000..9bfa16aa08
--- /dev/null
+++ b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.h
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+#ifndef ApplicationCache_h
+#define ApplicationCache_h
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "PlatformString.h"
+#include "StringHash.h"
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class ApplicationCacheGroup;
+class ApplicationCacheResource;
+class DocumentLoader;
+class KURL;
+class ResourceRequest;
+
+typedef Vector<std::pair<KURL, KURL> > FallbackURLVector;
+
+class ApplicationCache : public RefCounted<ApplicationCache> {
+public:
+ static PassRefPtr<ApplicationCache> create() { return adoptRef(new ApplicationCache); }
+ ~ApplicationCache();
+
+ void addResource(PassRefPtr<ApplicationCacheResource> resource);
+ unsigned removeResource(const String& url);
+
+ void setManifestResource(PassRefPtr<ApplicationCacheResource> manifest);
+ ApplicationCacheResource* manifestResource() const { return m_manifest; }
+
+ void setGroup(ApplicationCacheGroup*);
+ ApplicationCacheGroup* group() const { return m_group; }
+
+ ApplicationCacheResource* resourceForRequest(const ResourceRequest&);
+ ApplicationCacheResource* resourceForURL(const String& url);
+
+ unsigned numDynamicEntries() const;
+ String dynamicEntry(unsigned index) const;
+
+ bool addDynamicEntry(const String& url);
+ void removeDynamicEntry(const String& url);
+
+ void setOnlineWhitelist(const Vector<KURL>& onlineWhitelist);
+ const Vector<KURL>& onlineWhitelist() const { return m_onlineWhitelist; }
+ bool isURLInOnlineWhitelist(const KURL&); // There is an entry in online whitelist that has the same origin as the resource's URL and that is a prefix match for the resource's URL.
+
+ void setFallbackURLs(const FallbackURLVector&);
+ const FallbackURLVector& fallbackURLs() const { return m_fallbackURLs; }
+ bool urlMatchesFallbackNamespace(const KURL&, KURL* fallbackURL = 0);
+
+#ifndef NDEBUG
+ void dump();
+#endif
+
+ typedef HashMap<String, RefPtr<ApplicationCacheResource> > ResourceMap;
+ ResourceMap::const_iterator begin() const { return m_resources.begin(); }
+ ResourceMap::const_iterator end() const { return m_resources.end(); }
+
+ void setStorageID(unsigned storageID) { m_storageID = storageID; }
+ unsigned storageID() const { return m_storageID; }
+ void clearStorageID();
+
+ static bool requestIsHTTPOrHTTPSGet(const ResourceRequest&);
+
+private:
+ ApplicationCache();
+
+ ApplicationCacheGroup* m_group;
+ ResourceMap m_resources;
+ ApplicationCacheResource* m_manifest;
+
+ Vector<KURL> m_onlineWhitelist;
+ FallbackURLVector m_fallbackURLs;
+
+ // While an update is in progress, changes in dynamic entries are queued for later execution.
+ Vector<std::pair<KURL, bool> > m_pendingDynamicEntryActions;
+
+ unsigned m_storageID;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#endif // ApplicationCache_h
diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheGroup.cpp b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheGroup.cpp
new file mode 100644
index 0000000000..b53787feb7
--- /dev/null
+++ b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheGroup.cpp
@@ -0,0 +1,719 @@
+/*
+ * 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 "ApplicationCacheGroup.h"
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "ApplicationCache.h"
+#include "ApplicationCacheResource.h"
+#include "ApplicationCacheStorage.h"
+#include "DocumentLoader.h"
+#include "DOMApplicationCache.h"
+#include "DOMWindow.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "MainResourceLoader.h"
+#include "ManifestParser.h"
+#include "Page.h"
+#include "Settings.h"
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+ApplicationCacheGroup::ApplicationCacheGroup(const KURL& manifestURL, bool isCopy)
+ : m_manifestURL(manifestURL)
+ , m_status(Idle)
+ , m_savedNewestCachePointer(0)
+ , m_frame(0)
+ , m_storageID(0)
+ , m_isCopy(isCopy)
+{
+}
+
+ApplicationCacheGroup::~ApplicationCacheGroup()
+{
+ if (m_isCopy) {
+ ASSERT(m_newestCache);
+ ASSERT(m_caches.size() == 1);
+ ASSERT(m_caches.contains(m_newestCache.get()));
+ ASSERT(!m_cacheBeingUpdated);
+ ASSERT(m_associatedDocumentLoaders.isEmpty());
+ ASSERT(m_cacheCandidates.isEmpty());
+ ASSERT(m_newestCache->group() == this);
+
+ return;
+ }
+
+ ASSERT(!m_newestCache);
+ ASSERT(m_caches.isEmpty());
+
+ stopLoading();
+
+ cacheStorage().cacheGroupDestroyed(this);
+}
+
+ApplicationCache* ApplicationCacheGroup::cacheForMainRequest(const ResourceRequest& request, DocumentLoader*)
+{
+ if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
+ return 0;
+
+ if (ApplicationCacheGroup* group = cacheStorage().cacheGroupForURL(request.url())) {
+ ASSERT(group->newestCache());
+
+ return group->newestCache();
+ }
+
+ return 0;
+}
+
+ApplicationCache* ApplicationCacheGroup::fallbackCacheForMainRequest(const ResourceRequest& request, DocumentLoader*)
+{
+ if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
+ return 0;
+
+ if (ApplicationCacheGroup* group = cacheStorage().fallbackCacheGroupForURL(request.url())) {
+ ASSERT(group->newestCache());
+
+ return group->newestCache();
+ }
+
+ return 0;
+}
+
+void ApplicationCacheGroup::selectCache(Frame* frame, const KURL& manifestURL)
+{
+ ASSERT(frame && frame->page());
+
+ if (!frame->settings()->offlineWebApplicationCacheEnabled())
+ return;
+
+ DocumentLoader* documentLoader = frame->loader()->documentLoader();
+ ASSERT(!documentLoader->applicationCache());
+
+ if (manifestURL.isNull()) {
+ selectCacheWithoutManifestURL(frame);
+ return;
+ }
+
+ ApplicationCache* mainResourceCache = documentLoader->mainResourceApplicationCache();
+
+ if (mainResourceCache) {
+ if (manifestURL == mainResourceCache->group()->m_manifestURL) {
+ mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache);
+ mainResourceCache->group()->update(frame);
+ } else {
+ // The main resource was loaded from cache, so the cache must have an entry for it. Mark it as foreign.
+ ApplicationCacheResource* resource = mainResourceCache->resourceForURL(documentLoader->url());
+ bool inStorage = resource->storageID();
+ resource->addType(ApplicationCacheResource::Foreign);
+ if (inStorage)
+ cacheStorage().storeUpdatedType(resource, mainResourceCache);
+
+ // Restart the current navigation from the top of the navigation algorithm, undoing any changes that were made
+ // as part of the initial load.
+ // The navigation will not result in the same resource being loaded, because "foreign" entries are never picked during navigation.
+ frame->loader()->scheduleLocationChange(documentLoader->url(), frame->loader()->referrer(), true);
+ }
+
+ return;
+ }
+
+ // The resource was loaded from the network, check if it is a HTTP/HTTPS GET.
+ const ResourceRequest& request = frame->loader()->activeDocumentLoader()->request();
+
+ if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) {
+ selectCacheWithoutManifestURL(frame);
+ return;
+ }
+
+ // Check that the resource URL has the same scheme/host/port as the manifest URL.
+ if (!protocolHostAndPortAreEqual(manifestURL, request.url())) {
+ selectCacheWithoutManifestURL(frame);
+ return;
+ }
+
+ ApplicationCacheGroup* group = cacheStorage().findOrCreateCacheGroup(manifestURL);
+
+ if (ApplicationCache* cache = group->newestCache()) {
+ ASSERT(cache->manifestResource());
+
+ group->associateDocumentLoaderWithCache(frame->loader()->documentLoader(), cache);
+
+ if (!frame->loader()->documentLoader()->isLoadingMainResource())
+ group->finishedLoadingMainResource(frame->loader()->documentLoader());
+
+ group->update(frame);
+ } else {
+ bool isUpdating = group->m_cacheBeingUpdated;
+
+ if (!isUpdating)
+ group->m_cacheBeingUpdated = ApplicationCache::create();
+ documentLoader->setCandidateApplicationCacheGroup(group);
+ group->m_cacheCandidates.add(documentLoader);
+
+ const KURL& url = frame->loader()->documentLoader()->originalURL();
+
+ unsigned type = 0;
+
+ // If the resource has already been downloaded, remove it so that it will be replaced with the implicit resource
+ if (isUpdating)
+ type = group->m_cacheBeingUpdated->removeResource(url);
+
+ // Add the main resource URL as an implicit entry.
+ group->addEntry(url, type | ApplicationCacheResource::Implicit);
+
+ if (!frame->loader()->documentLoader()->isLoadingMainResource())
+ group->finishedLoadingMainResource(frame->loader()->documentLoader());
+
+ if (!isUpdating)
+ group->update(frame);
+ }
+}
+
+void ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame* frame)
+{
+ if (!frame->settings()->offlineWebApplicationCacheEnabled())
+ return;
+
+ DocumentLoader* documentLoader = frame->loader()->documentLoader();
+ ASSERT(!documentLoader->applicationCache());
+
+ ApplicationCache* mainResourceCache = documentLoader->mainResourceApplicationCache();
+
+ if (mainResourceCache) {
+ mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache);
+ mainResourceCache->group()->update(frame);
+ }
+}
+
+void ApplicationCacheGroup::finishedLoadingMainResource(DocumentLoader* loader)
+{
+ const KURL& url = loader->originalURL();
+
+ if (ApplicationCache* cache = loader->applicationCache()) {
+ RefPtr<ApplicationCacheResource> resource = ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Implicit, loader->mainResourceData());
+ cache->addResource(resource.release());
+
+ if (!m_cacheBeingUpdated)
+ return;
+ }
+
+ ASSERT(m_pendingEntries.contains(url));
+
+ EntryMap::iterator it = m_pendingEntries.find(url);
+ ASSERT(it->second & ApplicationCacheResource::Implicit);
+
+ RefPtr<ApplicationCacheResource> resource = ApplicationCacheResource::create(url, loader->response(), it->second, loader->mainResourceData());
+
+ ASSERT(m_cacheBeingUpdated);
+ m_cacheBeingUpdated->addResource(resource.release());
+
+ m_pendingEntries.remove(it);
+
+ checkIfLoadIsComplete();
+}
+
+void ApplicationCacheGroup::failedLoadingMainResource(DocumentLoader* loader)
+{
+ ASSERT(m_cacheCandidates.contains(loader) || m_associatedDocumentLoaders.contains(loader));
+
+ // Note that cacheUpdateFailed() can cause the cache group to be deleted.
+ cacheUpdateFailed();
+}
+
+void ApplicationCacheGroup::stopLoading()
+{
+ if (m_manifestHandle) {
+ ASSERT(!m_currentHandle);
+
+ m_manifestHandle->setClient(0);
+ m_manifestHandle->cancel();
+ m_manifestHandle = 0;
+ }
+
+ if (m_currentHandle) {
+ ASSERT(!m_manifestHandle);
+ ASSERT(m_cacheBeingUpdated);
+
+ m_currentHandle->setClient(0);
+ m_currentHandle->cancel();
+ m_currentHandle = 0;
+ }
+
+ m_cacheBeingUpdated = 0;
+}
+
+void ApplicationCacheGroup::documentLoaderDestroyed(DocumentLoader* loader)
+{
+ HashSet<DocumentLoader*>::iterator it = m_associatedDocumentLoaders.find(loader);
+
+ if (it != m_associatedDocumentLoaders.end()) {
+ ASSERT(!m_cacheCandidates.contains(loader));
+
+ m_associatedDocumentLoaders.remove(it);
+ } else {
+ ASSERT(m_cacheCandidates.contains(loader));
+ m_cacheCandidates.remove(loader);
+ }
+
+ if (!m_associatedDocumentLoaders.isEmpty() || !m_cacheCandidates.isEmpty())
+ return;
+
+ // We should only have the newest cache remaining, or there is an initial cache attempt in progress.
+ ASSERT(m_caches.size() == 1 || m_cacheBeingUpdated);
+
+ // If a cache update is in progress, stop it.
+ if (m_caches.size() == 1) {
+ ASSERT(m_caches.contains(m_newestCache.get()));
+
+ // Release our reference to the newest cache.
+ m_savedNewestCachePointer = m_newestCache.get();
+
+ // This could cause us to be deleted.
+ m_newestCache = 0;
+
+ return;
+ }
+
+ // There is an initial cache attempt in progress
+ ASSERT(m_cacheBeingUpdated);
+ ASSERT(m_caches.size() == 0);
+
+ // Delete ourselves, causing the cache attempt to be stopped.
+ delete this;
+}
+
+void ApplicationCacheGroup::cacheDestroyed(ApplicationCache* cache)
+{
+ ASSERT(m_caches.contains(cache));
+
+ m_caches.remove(cache);
+
+ if (cache != m_savedNewestCachePointer)
+ cacheStorage().remove(cache);
+
+ if (m_caches.isEmpty())
+ delete this;
+}
+
+void ApplicationCacheGroup::setNewestCache(PassRefPtr<ApplicationCache> newestCache)
+{
+ ASSERT(!m_newestCache);
+ ASSERT(!m_caches.contains(newestCache.get()));
+ ASSERT(!newestCache->group());
+
+ m_newestCache = newestCache;
+ m_caches.add(m_newestCache.get());
+ m_newestCache->setGroup(this);
+}
+
+void ApplicationCacheGroup::update(Frame* frame)
+{
+ if (m_status == Checking || m_status == Downloading)
+ return;
+
+ ASSERT(!m_frame);
+ m_frame = frame;
+
+ m_status = Checking;
+
+ callListenersOnAssociatedDocuments(&DOMApplicationCache::callCheckingListener);
+
+ ASSERT(!m_manifestHandle);
+ ASSERT(!m_manifestResource);
+
+ // FIXME: Handle defer loading
+
+ ResourceRequest request(m_manifestURL);
+ m_frame->loader()->applyUserAgent(request);
+
+ m_manifestHandle = ResourceHandle::create(request, this, m_frame, false, true, false);
+}
+
+void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response)
+{
+ if (handle == m_manifestHandle) {
+ didReceiveManifestResponse(response);
+ return;
+ }
+
+ ASSERT(handle == m_currentHandle);
+
+ int statusCode = response.httpStatusCode() / 100;
+ if (statusCode == 4 || statusCode == 5) {
+ // Note that cacheUpdateFailed() can cause the cache group to be deleted.
+ cacheUpdateFailed();
+ return;
+ }
+
+ const KURL& url = handle->request().url();
+
+ ASSERT(!m_currentResource);
+ ASSERT(m_pendingEntries.contains(url));
+
+ unsigned type = m_pendingEntries.get(url);
+
+ // If this is an initial cache attempt, we should not get implicit resources delivered here.
+ if (!m_newestCache)
+ ASSERT(!(type & ApplicationCacheResource::Implicit));
+
+ m_currentResource = ApplicationCacheResource::create(url, response, type);
+}
+
+void ApplicationCacheGroup::didReceiveData(ResourceHandle* handle, const char* data, int length, int)
+{
+ if (handle == m_manifestHandle) {
+ didReceiveManifestData(data, length);
+ return;
+ }
+
+ ASSERT(handle == m_currentHandle);
+
+ ASSERT(m_currentResource);
+ m_currentResource->data()->append(data, length);
+}
+
+void ApplicationCacheGroup::didFinishLoading(ResourceHandle* handle)
+{
+ if (handle == m_manifestHandle) {
+ didFinishLoadingManifest();
+ return;
+ }
+
+ ASSERT(m_currentHandle == handle);
+ ASSERT(m_pendingEntries.contains(handle->request().url()));
+
+ m_pendingEntries.remove(handle->request().url());
+
+ ASSERT(m_cacheBeingUpdated);
+
+ m_cacheBeingUpdated->addResource(m_currentResource.release());
+ m_currentHandle = 0;
+
+ // Load the next file.
+ if (!m_pendingEntries.isEmpty()) {
+ startLoadingEntry();
+ return;
+ }
+
+ checkIfLoadIsComplete();
+}
+
+void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError&)
+{
+ if (handle == m_manifestHandle) {
+ didFailToLoadManifest();
+ return;
+ }
+
+ // Note that cacheUpdateFailed() can cause the cache group to be deleted.
+ cacheUpdateFailed();
+}
+
+void ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& response)
+{
+ int statusCode = response.httpStatusCode() / 100;
+
+ if (statusCode == 4 || statusCode == 5 ||
+ !equalIgnoringCase(response.mimeType(), "text/cache-manifest")) {
+ didFailToLoadManifest();
+ return;
+ }
+
+ ASSERT(!m_manifestResource);
+ ASSERT(m_manifestHandle);
+ m_manifestResource = ApplicationCacheResource::create(m_manifestHandle->request().url(), response,
+ ApplicationCacheResource::Manifest);
+}
+
+void ApplicationCacheGroup::didReceiveManifestData(const char* data, int length)
+{
+ ASSERT(m_manifestResource);
+ m_manifestResource->data()->append(data, length);
+}
+
+void ApplicationCacheGroup::didFinishLoadingManifest()
+{
+ if (!m_manifestResource) {
+ didFailToLoadManifest();
+ return;
+ }
+
+ bool isUpgradeAttempt = m_newestCache;
+
+ m_manifestHandle = 0;
+
+ // Check if the manifest is byte-for-byte identical.
+ if (isUpgradeAttempt) {
+ ApplicationCacheResource* newestManifest = m_newestCache->manifestResource();
+ ASSERT(newestManifest);
+
+ if (newestManifest->data()->size() == m_manifestResource->data()->size() &&
+ !memcmp(newestManifest->data()->data(), m_manifestResource->data()->data(), newestManifest->data()->size())) {
+
+ callListenersOnAssociatedDocuments(&DOMApplicationCache::callNoUpdateListener);
+
+ m_status = Idle;
+ m_frame = 0;
+ m_manifestResource = 0;
+ return;
+ }
+ }
+
+ Manifest manifest;
+ if (!parseManifest(m_manifestURL, m_manifestResource->data()->data(), m_manifestResource->data()->size(), manifest)) {
+ didFailToLoadManifest();
+ return;
+ }
+
+ // We have the manifest, now download the resources.
+ m_status = Downloading;
+
+ callListenersOnAssociatedDocuments(&DOMApplicationCache::callDownloadingListener);
+
+#ifndef NDEBUG
+ // We should only have implicit entries.
+ {
+ EntryMap::const_iterator end = m_pendingEntries.end();
+ for (EntryMap::const_iterator it = m_pendingEntries.begin(); it != end; ++it)
+ ASSERT(it->second & ApplicationCacheResource::Implicit);
+ }
+#endif
+
+ if (isUpgradeAttempt) {
+ ASSERT(!m_cacheBeingUpdated);
+
+ m_cacheBeingUpdated = ApplicationCache::create();
+
+ ApplicationCache::ResourceMap::const_iterator end = m_newestCache->end();
+ for (ApplicationCache::ResourceMap::const_iterator it = m_newestCache->begin(); it != end; ++it) {
+ unsigned type = it->second->type();
+ if (type & (ApplicationCacheResource::Implicit | ApplicationCacheResource::Dynamic))
+ addEntry(it->first, type);
+ }
+ }
+
+ HashSet<String>::const_iterator end = manifest.explicitURLs.end();
+ for (HashSet<String>::const_iterator it = manifest.explicitURLs.begin(); it != end; ++it)
+ addEntry(*it, ApplicationCacheResource::Explicit);
+
+ size_t fallbackCount = manifest.fallbackURLs.size();
+ for (size_t i = 0; i < fallbackCount; ++i)
+ addEntry(manifest.fallbackURLs[i].second, ApplicationCacheResource::Fallback);
+
+ m_cacheBeingUpdated->setOnlineWhitelist(manifest.onlineWhitelistedURLs);
+ m_cacheBeingUpdated->setFallbackURLs(manifest.fallbackURLs);
+
+ startLoadingEntry();
+}
+
+void ApplicationCacheGroup::cacheUpdateFailed()
+{
+ stopLoading();
+
+ callListenersOnAssociatedDocuments(&DOMApplicationCache::callErrorListener);
+
+ m_pendingEntries.clear();
+ m_manifestResource = 0;
+
+ while (!m_cacheCandidates.isEmpty()) {
+ HashSet<DocumentLoader*>::iterator it = m_cacheCandidates.begin();
+
+ ASSERT((*it)->candidateApplicationCacheGroup() == this);
+ (*it)->setCandidateApplicationCacheGroup(0);
+ m_cacheCandidates.remove(it);
+ }
+
+ m_status = Idle;
+ m_frame = 0;
+
+ // If there are no associated caches, delete ourselves
+ if (m_associatedDocumentLoaders.isEmpty())
+ delete this;
+}
+
+
+void ApplicationCacheGroup::didFailToLoadManifest()
+{
+ // Note that cacheUpdateFailed() can cause the cache group to be deleted.
+ cacheUpdateFailed();
+}
+
+void ApplicationCacheGroup::checkIfLoadIsComplete()
+{
+ ASSERT(m_cacheBeingUpdated);
+
+ if (m_manifestHandle)
+ return;
+
+ if (!m_pendingEntries.isEmpty())
+ return;
+
+ // We're done
+ bool isUpgradeAttempt = m_newestCache;
+
+ m_cacheBeingUpdated->setManifestResource(m_manifestResource.release());
+
+ Vector<RefPtr<DocumentLoader> > documentLoaders;
+
+ if (isUpgradeAttempt) {
+ ASSERT(m_cacheCandidates.isEmpty());
+
+ copyToVector(m_associatedDocumentLoaders, documentLoaders);
+ } else {
+ while (!m_cacheCandidates.isEmpty()) {
+ HashSet<DocumentLoader*>::iterator it = m_cacheCandidates.begin();
+
+ DocumentLoader* loader = *it;
+ ASSERT(!loader->applicationCache());
+ ASSERT(loader->candidateApplicationCacheGroup() == this);
+
+ associateDocumentLoaderWithCache(loader, m_cacheBeingUpdated.get());
+
+ documentLoaders.append(loader);
+
+ m_cacheCandidates.remove(it);
+ }
+ }
+
+ setNewestCache(m_cacheBeingUpdated.release());
+
+ // Store the cache
+ cacheStorage().storeNewestCache(this);
+
+ callListeners(isUpgradeAttempt ? &DOMApplicationCache::callUpdateReadyListener : &DOMApplicationCache::callCachedListener,
+ documentLoaders);
+
+ m_status = Idle;
+ m_frame = 0;
+}
+
+void ApplicationCacheGroup::startLoadingEntry()
+{
+ ASSERT(m_cacheBeingUpdated);
+
+ if (m_pendingEntries.isEmpty()) {
+ checkIfLoadIsComplete();
+ return;
+ }
+
+ EntryMap::const_iterator it = m_pendingEntries.begin();
+
+ // If this is an initial cache attempt, we do not want to fetch any implicit entries,
+ // since those are fed to us by the normal loader machinery.
+ if (!m_newestCache) {
+ // Get the first URL in the entry table that is not implicit
+ EntryMap::const_iterator end = m_pendingEntries.end();
+
+ while (it->second & ApplicationCacheResource::Implicit) {
+ ++it;
+
+ if (it == end)
+ return;
+ }
+ }
+
+ callListenersOnAssociatedDocuments(&DOMApplicationCache::callProgressListener);
+
+ // FIXME: If this is an upgrade attempt, the newest cache should be used as an HTTP cache.
+
+ ASSERT(!m_currentHandle);
+
+ ResourceRequest request(it->first);
+ m_frame->loader()->applyUserAgent(request);
+
+ m_currentHandle = ResourceHandle::create(request, this, m_frame, false, true, false);
+}
+
+void ApplicationCacheGroup::addEntry(const String& url, unsigned type)
+{
+ ASSERT(m_cacheBeingUpdated);
+
+ // Don't add the URL if we already have an implicit resource in the cache
+ if (ApplicationCacheResource* resource = m_cacheBeingUpdated->resourceForURL(url)) {
+ ASSERT(resource->type() & ApplicationCacheResource::Implicit);
+
+ resource->addType(type);
+ return;
+ }
+
+ // Don't add the URL if it's the same as the manifest URL.
+ if (m_manifestResource && m_manifestResource->url() == url) {
+ m_manifestResource->addType(type);
+ return;
+ }
+
+ pair<EntryMap::iterator, bool> result = m_pendingEntries.add(url, type);
+
+ if (!result.second)
+ result.first->second |= type;
+}
+
+void ApplicationCacheGroup::associateDocumentLoaderWithCache(DocumentLoader* loader, ApplicationCache* cache)
+{
+ loader->setApplicationCache(cache);
+
+ ASSERT(!m_associatedDocumentLoaders.contains(loader));
+ m_associatedDocumentLoaders.add(loader);
+}
+
+void ApplicationCacheGroup::callListenersOnAssociatedDocuments(ListenerFunction listenerFunction)
+{
+ Vector<RefPtr<DocumentLoader> > loaders;
+ copyToVector(m_associatedDocumentLoaders, loaders);
+
+ callListeners(listenerFunction, loaders);
+}
+
+void ApplicationCacheGroup::callListeners(ListenerFunction listenerFunction, const Vector<RefPtr<DocumentLoader> >& loaders)
+{
+ for (unsigned i = 0; i < loaders.size(); i++) {
+ Frame* frame = loaders[i]->frame();
+ if (!frame)
+ continue;
+
+ ASSERT(frame->loader()->documentLoader() == loaders[i]);
+ DOMWindow* window = frame->domWindow();
+
+ if (DOMApplicationCache* domCache = window->optionalApplicationCache())
+ (domCache->*listenerFunction)();
+ }
+}
+
+void ApplicationCacheGroup::clearStorageID()
+{
+ m_storageID = 0;
+
+ HashSet<ApplicationCache*>::const_iterator end = m_caches.end();
+ for (HashSet<ApplicationCache*>::const_iterator it = m_caches.begin(); it != end; ++it)
+ (*it)->clearStorageID();
+}
+
+
+}
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheGroup.h b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheGroup.h
new file mode 100644
index 0000000000..8928e7547a
--- /dev/null
+++ b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheGroup.h
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#ifndef ApplicationCacheGroup_h
+#define ApplicationCacheGroup_h
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include <wtf/Noncopyable.h>
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+
+#include "KURL.h"
+#include "PlatformString.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleClient.h"
+#include "SharedBuffer.h"
+
+namespace WebCore {
+
+class ApplicationCache;
+class ApplicationCacheResource;
+class DOMApplicationCache;
+class Document;
+class DocumentLoader;
+class Frame;
+
+class ApplicationCacheGroup : Noncopyable, ResourceHandleClient {
+public:
+ ApplicationCacheGroup(const KURL& manifestURL, bool isCopy = false);
+ ~ApplicationCacheGroup();
+
+ enum Status { Idle, Checking, Downloading };
+
+ static ApplicationCache* cacheForMainRequest(const ResourceRequest&, DocumentLoader*);
+ static ApplicationCache* fallbackCacheForMainRequest(const ResourceRequest&, DocumentLoader*);
+
+ static void selectCache(Frame*, const KURL& manifestURL);
+ static void selectCacheWithoutManifestURL(Frame*);
+
+ const KURL& manifestURL() const { return m_manifestURL; }
+ Status status() const { return m_status; }
+
+ void setStorageID(unsigned storageID) { m_storageID = storageID; }
+ unsigned storageID() const { return m_storageID; }
+ void clearStorageID();
+
+ void update(Frame*);
+ void cacheDestroyed(ApplicationCache*);
+
+ ApplicationCache* newestCache() const { return m_newestCache.get(); }
+ ApplicationCache* savedNewestCachePointer() const { return m_savedNewestCachePointer; }
+
+ void finishedLoadingMainResource(DocumentLoader*);
+ void failedLoadingMainResource(DocumentLoader*);
+ void documentLoaderDestroyed(DocumentLoader*);
+
+ void setNewestCache(PassRefPtr<ApplicationCache> newestCache);
+
+ bool isCopy() const { return m_isCopy; }
+private:
+ typedef void (DOMApplicationCache::*ListenerFunction)();
+ void callListenersOnAssociatedDocuments(ListenerFunction);
+ void callListeners(ListenerFunction, const Vector<RefPtr<DocumentLoader> >& loaders);
+
+ virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
+ virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived);
+ virtual void didFinishLoading(ResourceHandle*);
+ virtual void didFail(ResourceHandle*, const ResourceError&);
+
+ void didReceiveManifestResponse(const ResourceResponse&);
+ void didReceiveManifestData(const char*, int);
+ void didFinishLoadingManifest();
+ void didFailToLoadManifest();
+
+ void startLoadingEntry();
+ void checkIfLoadIsComplete();
+ void cacheUpdateFailed();
+
+ void addEntry(const String&, unsigned type);
+
+ void associateDocumentLoaderWithCache(DocumentLoader*, ApplicationCache*);
+
+ void stopLoading();
+
+ KURL m_manifestURL;
+ Status m_status;
+
+ // This is the newest cache in the group.
+ RefPtr<ApplicationCache> m_newestCache;
+
+ // During tear-down we save the pointer to the newest cache to prevent reference cycles.
+ ApplicationCache* m_savedNewestCachePointer;
+
+ // The caches in this cache group.
+ HashSet<ApplicationCache*> m_caches;
+
+ // The cache being updated (if any). Note that cache updating does not immediately create a new
+ // ApplicationCache object, so this may be null even when status is not Idle.
+ RefPtr<ApplicationCache> m_cacheBeingUpdated;
+
+ // When a cache group does not yet have a complete cache, this contains the document loaders
+ // that should be associated with the cache once it has been downloaded.
+ HashSet<DocumentLoader*> m_cacheCandidates;
+
+ // These are all the document loaders that are associated with a cache in this group.
+ HashSet<DocumentLoader*> m_associatedDocumentLoaders;
+
+ // The URLs and types of pending cache entries.
+ typedef HashMap<String, unsigned> EntryMap;
+ EntryMap m_pendingEntries;
+
+ // Frame used for fetching resources when updating
+ Frame* m_frame;
+
+ unsigned m_storageID;
+
+ // Whether this cache group is a copy that's only used for transferring the cache to another file.
+ bool m_isCopy;
+
+ RefPtr<ResourceHandle> m_currentHandle;
+ RefPtr<ApplicationCacheResource> m_currentResource;
+
+ RefPtr<ApplicationCacheResource> m_manifestResource;
+ RefPtr<ResourceHandle> m_manifestHandle;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#endif // ApplicationCacheGroup_h
diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.cpp b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.cpp
new file mode 100644
index 0000000000..d78cf7f4c5
--- /dev/null
+++ b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 "ApplicationCacheResource.h"
+#include <stdio.h>
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+namespace WebCore {
+
+ApplicationCacheResource::ApplicationCacheResource(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> data)
+ : SubstituteResource(url, response, data)
+ , m_type(type)
+ , m_storageID(0)
+{
+}
+
+void ApplicationCacheResource::addType(unsigned type)
+{
+ // Caller should take care of storing the new type in database.
+ m_type |= type;
+}
+
+#ifndef NDEBUG
+void ApplicationCacheResource::dumpType(unsigned type)
+{
+ if (type & Implicit)
+ printf("implicit ");
+ if (type & Manifest)
+ printf("manifest ");
+ if (type & Explicit)
+ printf("explicit ");
+ if (type & Foreign)
+ printf("foreign ");
+ if (type & Fallback)
+ printf("fallback ");
+ if (type & Dynamic)
+ printf("dynamic ");
+
+ printf("\n");
+}
+#endif
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.h b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.h
new file mode 100644
index 0000000000..96f5a0e9a0
--- /dev/null
+++ b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef ApplicationCacheResource_h
+#define ApplicationCacheResource_h
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "SubstituteResource.h"
+
+namespace WebCore {
+
+class ApplicationCacheResource : public SubstituteResource {
+public:
+ enum Type {
+ Implicit = 1 << 0,
+ Manifest = 1 << 1,
+ Explicit = 1 << 2,
+ Foreign = 1 << 3,
+ Fallback = 1 << 4,
+ Dynamic = 1 << 5
+ };
+
+ static PassRefPtr<ApplicationCacheResource> create(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> buffer = SharedBuffer::create())
+ {
+ return adoptRef(new ApplicationCacheResource(url, response, type, buffer));
+ }
+
+ unsigned type() const { return m_type; }
+ void addType(unsigned type);
+
+ void setStorageID(unsigned storageID) { m_storageID = storageID; }
+ unsigned storageID() const { return m_storageID; }
+ void clearStorageID() { m_storageID = 0; }
+
+#ifndef NDEBUG
+ static void dumpType(unsigned type);
+#endif
+
+private:
+ ApplicationCacheResource(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> buffer);
+
+ unsigned m_type;
+ unsigned m_storageID;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#endif // ApplicationCacheResource_h
diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.cpp b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.cpp
new file mode 100644
index 0000000000..97a6d5a1c6
--- /dev/null
+++ b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.cpp
@@ -0,0 +1,796 @@
+/*
+ * 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 "CString.h"
+#include "FileSystem.h"
+#include "KURL.h"
+#include "SQLiteStatement.h"
+#include "SQLiteTransaction.h"
+#include <wtf/StdLibExtras.h>
+#include <wtf/StringExtras.h>
+
+using namespace std;
+
+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 = static_cast<unsigned>(statement.getColumnInt64(2));
+
+ RefPtr<ApplicationCache> cache = loadCache(newestCacheStorageID);
+ if (!cache)
+ return 0;
+
+ ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL);
+
+ group->setStorageID(static_cast<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(static_cast<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()) {
+ ApplicationCacheResource* resource = cache->resourceForURL(url);
+ if (!resource)
+ continue;
+ if (resource->type() & ApplicationCacheResource::Foreign)
+ continue;
+ 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 (m_cachesInMemory.contains(manifestURL))
+ continue;
+
+ 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 = static_cast<unsigned>(statement.getColumnInt64(2));
+ RefPtr<ApplicationCache> cache = loadCache(newestCacheID);
+
+ ApplicationCacheResource* resource = cache->resourceForURL(url);
+ if (!resource)
+ continue;
+ if (resource->type() & ApplicationCacheResource::Foreign)
+ continue;
+
+ ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL);
+
+ group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
+ group->setNewestCache(cache.release());
+
+ 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;
+}
+
+ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const KURL& url)
+{
+ // Check if an appropriate 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 (ApplicationCache* cache = group->newestCache()) {
+ KURL fallbackURL;
+ if (!cache->urlMatchesFallbackNamespace(url, &fallbackURL))
+ continue;
+ if (cache->resourceForURL(fallbackURL)->type() & ApplicationCacheResource::Foreign)
+ continue;
+ 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 (m_cachesInMemory.contains(manifestURL))
+ continue;
+
+ // Fallback namespaces always have the same origin as manifest URL, so we can avoid loading caches that cannot match.
+ if (!protocolHostAndPortAreEqual(url, manifestURL))
+ continue;
+
+ // We found a cache group that matches. Now check if the newest cache has a resource with
+ // a matching fallback namespace.
+ unsigned newestCacheID = static_cast<unsigned>(statement.getColumnInt64(2));
+ RefPtr<ApplicationCache> cache = loadCache(newestCacheID);
+
+ KURL fallbackURL;
+ if (!cache->urlMatchesFallbackNamespace(url, &fallbackURL))
+ continue;
+ if (cache->resourceForURL(fallbackURL)->type() & ApplicationCacheResource::Foreign)
+ continue;
+
+ ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL);
+
+ group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0)));
+ group->setNewestCache(cache.release());
+
+ 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;
+}
+
+const String& ApplicationCacheStorage::cacheDirectory() const
+{
+ return m_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 = 3;
+
+void ApplicationCacheStorage::verifySchemaVersion()
+{
+ int version = SQLiteStatement(m_database, "PRAGMA user_version").getColumnInt(0);
+ if (version == schemaVersion)
+ return;
+
+ m_database.clearAllTables();
+
+ // Update user version.
+ SQLiteTransaction setDatabaseVersion(m_database);
+ setDatabaseVersion.begin();
+
+ char userVersionSQL[32];
+ int numBytes = snprintf(userVersionSQL, sizeof(userVersionSQL), "PRAGMA user_version=%d", schemaVersion);
+ ASSERT_UNUSED(numBytes, static_cast<int>(sizeof(userVersionSQL)) >= numBytes);
+
+ SQLiteStatement statement(m_database, userVersionSQL);
+ if (statement.prepare() != SQLResultOk)
+ return;
+
+ executeStatement(statement);
+ setDatabaseVersion.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(static_cast<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 = static_cast<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 Vector<KURL>& onlineWhitelist = cache->onlineWhitelist();
+ {
+ size_t whitelistSize = onlineWhitelist.size();
+ for (size_t i = 0; i < whitelistSize; ++i) {
+ SQLiteStatement statement(m_database, "INSERT INTO CacheWhitelistURLs (url, cache) VALUES (?, ?)");
+ statement.prepare();
+
+ statement.bindText(1, onlineWhitelist[i]);
+ statement.bindInt64(2, cacheStorageID);
+
+ if (!executeStatement(statement))
+ return false;
+ }
+ }
+
+ // Store fallback URLs.
+ const FallbackURLVector& fallbackURLs = cache->fallbackURLs();
+ {
+ size_t fallbackCount = fallbackURLs.size();
+ for (size_t i = 0; i < fallbackCount; ++i) {
+ SQLiteStatement statement(m_database, "INSERT INTO FallbackURLs (namespace, fallbackURL, cache) VALUES (?, ?, ?)");
+ statement.prepare();
+
+ statement.bindText(1, fallbackURLs[i].first);
+ statement.bindText(2, fallbackURLs[i].second);
+ statement.bindInt64(3, 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 = static_cast<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 = static_cast<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;
+}
+
+bool ApplicationCacheStorage::storeUpdatedType(ApplicationCacheResource* resource, ApplicationCache* cache)
+{
+ ASSERT_UNUSED(cache, cache->storageID());
+ ASSERT(resource->storageID());
+
+ // FIXME: If the resource gained a Dynamic bit, it should be re-inserted at the end for correct order.
+ ASSERT(!(resource->type() & ApplicationCacheResource::Dynamic));
+
+ // First, insert the data
+ SQLiteStatement entryStatement(m_database, "UPDATE CacheEntries SET type=? WHERE resource=?");
+ if (entryStatement.prepare() != SQLResultOk)
+ return false;
+
+ entryStatement.bindInt64(1, resource->type());
+ entryStatement.bindInt64(2, resource->storageID());
+
+ return executeStatement(entryStatement);
+}
+
+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);
+
+ AtomicString headerName = AtomicString(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 = static_cast<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);
+
+ Vector<KURL> whitelist;
+ while ((result = whitelistStatement.step()) == SQLResultRow)
+ whitelist.append(whitelistStatement.getColumnText(0));
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Could not load cache online whitelist, error \"%s\"", m_database.lastErrorMsg());
+
+ cache->setOnlineWhitelist(whitelist);
+
+ // Load fallback URLs.
+ SQLiteStatement fallbackStatement(m_database, "SELECT namespace, fallbackURL FROM FallbackURLs WHERE cache=?");
+ if (fallbackStatement.prepare() != SQLResultOk)
+ return 0;
+ fallbackStatement.bindInt64(1, storageID);
+
+ FallbackURLVector fallbackURLs;
+ while ((result = fallbackStatement.step()) == SQLResultRow)
+ fallbackURLs.append(make_pair(fallbackStatement.getColumnText(0), fallbackStatement.getColumnText(1)));
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Could not load fallback URLs, error \"%s\"", m_database.lastErrorMsg());
+
+ cache->setFallbackURLs(fallbackURLs);
+
+ 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();
+
+ cacheCopy->setOnlineWhitelist(cache->onlineWhitelist());
+ cacheCopy->setFallbackURLs(cache->fallbackURLs());
+
+ // 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()
+{
+ DEFINE_STATIC_LOCAL(ApplicationCacheStorage, storage, ());
+
+ return storage;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.h b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.h
new file mode 100644
index 0000000000..cf64dd44be
--- /dev/null
+++ b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.h
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+#ifndef ApplicationCacheStorage_h
+#define ApplicationCacheStorage_h
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "PlatformString.h"
+#include "SQLiteDatabase.h"
+#include "StringHash.h"
+
+#include <wtf/HashCountedSet.h>
+
+namespace WebCore {
+
+class ApplicationCache;
+class ApplicationCacheGroup;
+class ApplicationCacheResource;
+class KURL;
+
+class ApplicationCacheStorage {
+public:
+ void setCacheDirectory(const String&);
+ const String& cacheDirectory() const;
+
+ ApplicationCacheGroup* cacheGroupForURL(const KURL&); // Cache to load a main resource from.
+ ApplicationCacheGroup* fallbackCacheGroupForURL(const KURL&); // Cache that has a fallback entry to load a main resource from if normal loading fails.
+
+ ApplicationCacheGroup* findOrCreateCacheGroup(const KURL& manifestURL);
+ void cacheGroupDestroyed(ApplicationCacheGroup*);
+
+ bool storeNewestCache(ApplicationCacheGroup*);
+ void store(ApplicationCacheResource*, ApplicationCache*);
+ bool storeUpdatedType(ApplicationCacheResource*, ApplicationCache*);
+
+ void remove(ApplicationCache*);
+
+ void empty();
+
+ static bool storeCopyOfCache(const String& cacheDirectory, ApplicationCache*);
+
+private:
+ PassRefPtr<ApplicationCache> loadCache(unsigned storageID);
+ ApplicationCacheGroup* loadCacheGroup(const KURL& manifestURL);
+
+ bool store(ApplicationCacheGroup*);
+ bool store(ApplicationCache*);
+ bool store(ApplicationCacheResource*, unsigned cacheStorageID);
+
+ void loadManifestHostHashes();
+
+ void verifySchemaVersion();
+
+ void openDatabase(bool createIfDoesNotExist);
+
+ bool executeStatement(SQLiteStatement&);
+ bool executeSQLCommand(const String&);
+
+ String m_cacheDirectory;
+
+ SQLiteDatabase m_database;
+
+ // In order to quickly determine if a given resource exists in an application cache,
+ // we keep a hash set of the hosts of the manifest URLs of all cache groups.
+ HashCountedSet<unsigned, AlreadyHashed> m_cacheHostSet;
+
+ typedef HashMap<String, ApplicationCacheGroup*> CacheGroupMap;
+ CacheGroupMap m_cachesInMemory;
+};
+
+ApplicationCacheStorage& cacheStorage();
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#endif // ApplicationCacheStorage_h
diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.cpp b/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.cpp
new file mode 100644
index 0000000000..a07442b277
--- /dev/null
+++ b/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.cpp
@@ -0,0 +1,292 @@
+/*
+ * 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 "DOMApplicationCache.h"
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "ApplicationCache.h"
+#include "ApplicationCacheGroup.h"
+#include "ApplicationCacheResource.h"
+#include "DocumentLoader.h"
+#include "Event.h"
+#include "EventException.h"
+#include "EventListener.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "StaticStringList.h"
+
+namespace WebCore {
+
+DOMApplicationCache::DOMApplicationCache(Frame* frame)
+ : m_frame(frame)
+{
+}
+
+void DOMApplicationCache::disconnectFrame()
+{
+ m_frame = 0;
+}
+
+ApplicationCache* DOMApplicationCache::associatedCache() const
+{
+ if (!m_frame)
+ return 0;
+
+ return m_frame->loader()->documentLoader()->applicationCache();
+}
+
+unsigned short DOMApplicationCache::status() const
+{
+ ApplicationCache* cache = associatedCache();
+ if (!cache)
+ return UNCACHED;
+
+ switch (cache->group()->status()) {
+ case ApplicationCacheGroup::Checking:
+ return CHECKING;
+ case ApplicationCacheGroup::Downloading:
+ return DOWNLOADING;
+ case ApplicationCacheGroup::Idle: {
+ if (cache != cache->group()->newestCache())
+ return UPDATEREADY;
+
+ return IDLE;
+ }
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ return 0;
+}
+
+void DOMApplicationCache::update(ExceptionCode& ec)
+{
+ ApplicationCache* cache = associatedCache();
+ if (!cache) {
+ ec = INVALID_STATE_ERR;
+ return;
+ }
+
+ cache->group()->update(m_frame);
+}
+
+bool DOMApplicationCache::swapCache()
+{
+ if (!m_frame)
+ return false;
+
+ ApplicationCache* cache = m_frame->loader()->documentLoader()->applicationCache();
+ if (!cache)
+ return false;
+
+ // Check if we already have the newest cache
+ ApplicationCache* newestCache = cache->group()->newestCache();
+ if (cache == newestCache)
+ return false;
+
+ ASSERT(cache->group() == newestCache->group());
+ m_frame->loader()->documentLoader()->setApplicationCache(newestCache);
+
+ return true;
+}
+
+void DOMApplicationCache::swapCache(ExceptionCode& ec)
+{
+ if (!swapCache())
+ ec = INVALID_STATE_ERR;
+}
+
+PassRefPtr<DOMStringList> DOMApplicationCache::items()
+{
+ Vector<String> result;
+ if (ApplicationCache* cache = associatedCache()) {
+ unsigned numEntries = cache->numDynamicEntries();
+ result.reserveCapacity(numEntries);
+ for (unsigned i = 0; i < numEntries; ++i)
+ result.append(cache->dynamicEntry(i));
+ }
+ return StaticStringList::adopt(result);
+}
+
+bool DOMApplicationCache::hasItem(const KURL& url, ExceptionCode& ec)
+{
+ ApplicationCache* cache = associatedCache();
+ if (!cache) {
+ ec = INVALID_STATE_ERR;
+ return false;
+ }
+
+ if (!url.isValid()) {
+ ec = SYNTAX_ERR;
+ return false;
+ }
+
+ ApplicationCacheResource* resource = cache->resourceForURL(url.string());
+ return resource && (resource->type() & ApplicationCacheResource::Dynamic);
+}
+
+void DOMApplicationCache::add(const KURL& url, ExceptionCode& ec)
+{
+ ApplicationCache* cache = associatedCache();
+ if (!cache) {
+ ec = INVALID_STATE_ERR;
+ return;
+ }
+
+ if (!url.isValid()) {
+ ec = SYNTAX_ERR;
+ return;
+ }
+
+ if (!cache->addDynamicEntry(url)) {
+ // This should use the (currently not specified) security exceptions in HTML5 4.3.4
+ ec = SECURITY_ERR;
+ }
+}
+
+void DOMApplicationCache::remove(const KURL& url, ExceptionCode& ec)
+{
+ ApplicationCache* cache = associatedCache();
+ if (!cache) {
+ ec = INVALID_STATE_ERR;
+ return;
+ }
+
+ cache->removeDynamicEntry(url);
+}
+
+ScriptExecutionContext* DOMApplicationCache::scriptExecutionContext() const
+{
+ return m_frame->document();
+}
+
+void DOMApplicationCache::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool)
+{
+ EventListenersMap::iterator iter = m_eventListeners.find(eventType);
+ if (iter == m_eventListeners.end()) {
+ ListenerVector listeners;
+ listeners.append(eventListener);
+ m_eventListeners.add(eventType, listeners);
+ } else {
+ ListenerVector& listeners = iter->second;
+ for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
+ if (*listenerIter == eventListener)
+ return;
+ }
+
+ listeners.append(eventListener);
+ m_eventListeners.add(eventType, listeners);
+ }
+}
+
+void DOMApplicationCache::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool)
+{
+ EventListenersMap::iterator iter = m_eventListeners.find(eventType);
+ if (iter == m_eventListeners.end())
+ return;
+
+ ListenerVector& listeners = iter->second;
+ for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) {
+ if (*listenerIter == eventListener) {
+ listeners.remove(listenerIter - listeners.begin());
+ return;
+ }
+ }
+}
+
+bool DOMApplicationCache::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec)
+{
+ if (!event || event->type().isEmpty()) {
+ ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR;
+ return true;
+ }
+
+ ListenerVector listenersCopy = m_eventListeners.get(event->type());
+ for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) {
+ event->setTarget(this);
+ event->setCurrentTarget(this);
+ listenerIter->get()->handleEvent(event.get(), false);
+ }
+
+ return !event->defaultPrevented();
+}
+
+void DOMApplicationCache::callListener(const AtomicString& eventType, EventListener* listener)
+{
+ ASSERT(m_frame);
+
+ RefPtr<Event> event = Event::create(eventType, false, false);
+ if (listener) {
+ event->setTarget(this);
+ event->setCurrentTarget(this);
+ listener->handleEvent(event.get(), false);
+ }
+
+ ExceptionCode ec = 0;
+ dispatchEvent(event.release(), ec);
+ ASSERT(!ec);
+}
+
+void DOMApplicationCache::callCheckingListener()
+{
+ callListener(eventNames().checkingEvent, m_onCheckingListener.get());
+}
+
+void DOMApplicationCache::callErrorListener()
+{
+ callListener(eventNames().errorEvent, m_onErrorListener.get());
+}
+
+void DOMApplicationCache::callNoUpdateListener()
+{
+ callListener(eventNames().noupdateEvent, m_onNoUpdateListener.get());
+}
+
+void DOMApplicationCache::callDownloadingListener()
+{
+ callListener(eventNames().downloadingEvent, m_onDownloadingListener.get());
+}
+
+void DOMApplicationCache::callProgressListener()
+{
+ callListener(eventNames().progressEvent, m_onProgressListener.get());
+}
+
+void DOMApplicationCache::callUpdateReadyListener()
+{
+ callListener(eventNames().updatereadyEvent, m_onUpdateReadyListener.get());
+}
+
+void DOMApplicationCache::callCachedListener()
+{
+ callListener(eventNames().cachedEvent, m_onCachedListener.get());
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.h b/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.h
new file mode 100644
index 0000000000..9371cd79fc
--- /dev/null
+++ b/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.h
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+#ifndef DOMApplicationCache_h
+#define DOMApplicationCache_h
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "AtomicStringHash.h"
+#include "EventTarget.h"
+#include "EventListener.h"
+#include <wtf/HashMap.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class ApplicationCache;
+class AtomicStringImpl;
+class DOMStringList;
+class Frame;
+class KURL;
+class String;
+
+class DOMApplicationCache : public RefCounted<DOMApplicationCache>, public EventTarget {
+public:
+ static PassRefPtr<DOMApplicationCache> create(Frame* frame) { return adoptRef(new DOMApplicationCache(frame)); }
+ void disconnectFrame();
+
+ enum Status {
+ UNCACHED = 0,
+ IDLE = 1,
+ CHECKING = 2,
+ DOWNLOADING = 3,
+ UPDATEREADY = 4,
+ };
+
+ unsigned short status() const;
+
+ void update(ExceptionCode&);
+ void swapCache(ExceptionCode&);
+
+ PassRefPtr<DOMStringList> items();
+ bool hasItem(const KURL&, ExceptionCode&);
+ void add(const KURL&, ExceptionCode&);
+ void remove(const KURL&, ExceptionCode&);
+
+ virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture);
+ virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture);
+ virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&);
+
+ typedef Vector<RefPtr<EventListener> > ListenerVector;
+ typedef HashMap<AtomicString, ListenerVector> EventListenersMap;
+ EventListenersMap& eventListeners() { return m_eventListeners; }
+
+ using RefCounted<DOMApplicationCache>::ref;
+ using RefCounted<DOMApplicationCache>::deref;
+
+ void setOnchecking(PassRefPtr<EventListener> eventListener) { m_onCheckingListener = eventListener; }
+ EventListener* onchecking() const { return m_onCheckingListener.get(); }
+
+ void setOnerror(PassRefPtr<EventListener> eventListener) { m_onErrorListener = eventListener; }
+ EventListener* onerror() const { return m_onErrorListener.get(); }
+
+ void setOnnoupdate(PassRefPtr<EventListener> eventListener) { m_onNoUpdateListener = eventListener; }
+ EventListener* onnoupdate() const { return m_onNoUpdateListener.get(); }
+
+ void setOndownloading(PassRefPtr<EventListener> eventListener) { m_onDownloadingListener = eventListener; }
+ EventListener* ondownloading() const { return m_onDownloadingListener.get(); }
+
+ void setOnprogress(PassRefPtr<EventListener> eventListener) { m_onProgressListener = eventListener; }
+ EventListener* onprogress() const { return m_onProgressListener.get(); }
+
+ void setOnupdateready(PassRefPtr<EventListener> eventListener) { m_onUpdateReadyListener = eventListener; }
+ EventListener* onupdateready() const { return m_onUpdateReadyListener.get(); }
+
+ void setOncached(PassRefPtr<EventListener> eventListener) { m_onCachedListener = eventListener; }
+ EventListener* oncached() const { return m_onCachedListener.get(); }
+
+ virtual ScriptExecutionContext* scriptExecutionContext() const;
+ DOMApplicationCache* toDOMApplicationCache() { return this; }
+
+ void callCheckingListener();
+ void callErrorListener();
+ void callNoUpdateListener();
+ void callDownloadingListener();
+ void callProgressListener();
+ void callUpdateReadyListener();
+ void callCachedListener();
+
+private:
+ DOMApplicationCache(Frame*);
+ void callListener(const AtomicString& eventType, EventListener*);
+
+ virtual void refEventTarget() { ref(); }
+ virtual void derefEventTarget() { deref(); }
+
+ ApplicationCache* associatedCache() const;
+ bool swapCache();
+
+ RefPtr<EventListener> m_onCheckingListener;
+ RefPtr<EventListener> m_onErrorListener;
+ RefPtr<EventListener> m_onNoUpdateListener;
+ RefPtr<EventListener> m_onDownloadingListener;
+ RefPtr<EventListener> m_onProgressListener;
+ RefPtr<EventListener> m_onUpdateReadyListener;
+ RefPtr<EventListener> m_onCachedListener;
+
+ EventListenersMap m_eventListeners;
+
+ Frame* m_frame;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#endif // DOMApplicationCache_h
diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.idl b/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.idl
new file mode 100644
index 0000000000..f38cd74b47
--- /dev/null
+++ b/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.idl
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+module offline {
+
+ interface [
+ Conditional=OFFLINE_WEB_APPLICATIONS,
+ CustomMarkFunction
+ ] DOMApplicationCache {
+ // update status
+ const unsigned short UNCACHED = 0;
+ const unsigned short IDLE = 1;
+ const unsigned short CHECKING = 2;
+ const unsigned short DOWNLOADING = 3;
+ const unsigned short UPDATEREADY = 4;
+ readonly attribute unsigned short status;
+
+ void update()
+ raises(DOMException);
+ void swapCache()
+ raises(DOMException);
+
+ // dynamic entries
+ readonly attribute DOMStringList items;
+ [Custom] boolean hasItem(in DOMString url)
+ raises(DOMException);
+ [Custom] void add(in DOMString uri)
+ raises(DOMException);
+ [Custom] void remove(in DOMString uri)
+ raises(DOMException);
+
+ // events
+ attribute EventListener onchecking;
+ attribute EventListener onerror;
+ attribute EventListener onnoupdate;
+ attribute EventListener ondownloading;
+ attribute EventListener onprogress;
+ attribute EventListener onupdateready;
+ attribute EventListener oncached;
+
+ // EventTarget interface
+ [Custom] void addEventListener(in DOMString type,
+ in EventListener listener,
+ in boolean useCapture);
+ [Custom] void removeEventListener(in DOMString type,
+ in EventListener listener,
+ in boolean useCapture);
+ boolean dispatchEvent(in Event evt)
+ raises(EventException);
+ };
+
+}
diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ManifestParser.cpp b/src/3rdparty/webkit/WebCore/loader/appcache/ManifestParser.cpp
new file mode 100644
index 0000000000..f1b60fcb57
--- /dev/null
+++ b/src/3rdparty/webkit/WebCore/loader/appcache/ManifestParser.cpp
@@ -0,0 +1,187 @@
+/*
+ * 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 "ManifestParser.h"
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "CharacterNames.h"
+#include "KURL.h"
+#include "TextEncoding.h"
+
+using namespace std;
+
+namespace WebCore {
+
+enum Mode { Explicit, Fallback, OnlineWhitelist, Unknown };
+
+bool parseManifest(const KURL& manifestURL, const char* data, int length, Manifest& manifest)
+{
+ ASSERT(manifest.explicitURLs.isEmpty());
+ ASSERT(manifest.onlineWhitelistedURLs.isEmpty());
+ ASSERT(manifest.fallbackURLs.isEmpty());
+
+ Mode mode = Explicit;
+ String s = UTF8Encoding().decode(data, length);
+
+ if (s.isEmpty())
+ return false;
+
+ // Replace nulls with U+FFFD REPLACEMENT CHARACTER
+ s.replace(0, replacementCharacter);
+
+ // Look for the magic signature
+ if (!s.startsWith("CACHE MANIFEST")) {
+ // The magic signature was not found.
+ return false;
+ }
+
+ const UChar* end = s.characters() + s.length();
+ const UChar* p = s.characters() + 14; // "CACHE MANIFEST" is 14 characters.
+
+ while (p < end) {
+ // Skip whitespace
+ if (*p == ' ' || *p == '\t') {
+ p++;
+ } else
+ break;
+ }
+
+ if (p < end && *p != '\n' && *p != '\r') {
+ // The magic signature was invalid
+ return false;
+ }
+
+ while (1) {
+ // Skip whitespace
+ while (p < end && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t'))
+ p++;
+
+ if (p == end)
+ break;
+
+ const UChar* lineStart = p;
+
+ // Find the end of the line
+ while (p < end && *p != '\r' && *p != '\n')
+ p++;
+
+ // Check if we have a comment
+ if (*lineStart == '#')
+ continue;
+
+ // Get rid of trailing whitespace
+ const UChar* tmp = p - 1;
+ while (tmp > lineStart && (*tmp == ' ' || *tmp == '\t'))
+ tmp--;
+
+ String line(lineStart, tmp - lineStart + 1);
+
+ if (line == "CACHE:")
+ mode = Explicit;
+ else if (line == "FALLBACK:")
+ mode = Fallback;
+ else if (line == "NETWORK:")
+ mode = OnlineWhitelist;
+ else if (line.endsWith(":"))
+ mode = Unknown;
+ else if (mode == Unknown)
+ continue;
+ else if (mode == Explicit || mode == OnlineWhitelist) {
+ const UChar* p = line.characters();
+ const UChar* lineEnd = p + line.length();
+
+ // Look for whitespace separating the URL from subsequent ignored tokens.
+ while (p < lineEnd && *p != '\t' && *p != ' ')
+ p++;
+
+ KURL url(manifestURL, String(line.characters(), p - line.characters()));
+
+ if (!url.isValid())
+ continue;
+
+ if (url.hasRef())
+ url.setRef(String());
+
+ if (!equalIgnoringCase(url.protocol(), manifestURL.protocol()))
+ continue;
+
+ if (mode == Explicit)
+ manifest.explicitURLs.add(url.string());
+ else
+ manifest.onlineWhitelistedURLs.append(url);
+
+ } else if (mode == Fallback) {
+ const UChar* p = line.characters();
+ const UChar* lineEnd = p + line.length();
+
+ // Look for whitespace separating the two URLs
+ while (p < lineEnd && *p != '\t' && *p != ' ')
+ p++;
+
+ if (p == lineEnd) {
+ // There was no whitespace separating the URLs.
+ continue;
+ }
+
+ KURL namespaceURL(manifestURL, String(line.characters(), p - line.characters()));
+ if (!namespaceURL.isValid())
+ continue;
+ if (namespaceURL.hasRef())
+ namespaceURL.setRef(String());
+
+ if (!protocolHostAndPortAreEqual(manifestURL, namespaceURL))
+ continue;
+
+ // Skip whitespace separating fallback namespace from URL.
+ while (p < lineEnd && (*p == '\t' || *p == ' '))
+ p++;
+
+ // Look for whitespace separating the URL from subsequent ignored tokens.
+ const UChar* fallbackStart = p;
+ while (p < lineEnd && *p != '\t' && *p != ' ')
+ p++;
+
+ KURL fallbackURL(manifestURL, String(fallbackStart, p - fallbackStart));
+ if (!fallbackURL.isValid())
+ continue;
+ if (fallbackURL.hasRef())
+ fallbackURL.setRef(String());
+
+ if (!protocolHostAndPortAreEqual(manifestURL, fallbackURL))
+ continue;
+
+ manifest.fallbackURLs.append(make_pair(namespaceURL, fallbackURL));
+ } else
+ ASSERT_NOT_REACHED();
+ }
+
+ return true;
+}
+
+}
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ManifestParser.h b/src/3rdparty/webkit/WebCore/loader/appcache/ManifestParser.h
new file mode 100644
index 0000000000..c03cf2728c
--- /dev/null
+++ b/src/3rdparty/webkit/WebCore/loader/appcache/ManifestParser.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef ManifestParser_h
+#define ManifestParser_h
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "ApplicationCache.h"
+
+namespace WebCore {
+
+ class KURL;
+
+ struct Manifest {
+ Vector<KURL> onlineWhitelistedURLs;
+ HashSet<String> explicitURLs;
+ FallbackURLVector fallbackURLs;
+ };
+
+ bool parseManifest(const KURL& manifestURL, const char* data, int length, Manifest&);
+
+}
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#endif // ManifestParser_h