summaryrefslogtreecommitdiffstats
path: root/WebCore/loader
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:15 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:15 -0800
commit1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353 (patch)
tree4457a7306ea5acb43fe05bfe0973b1f7faf97ba2 /WebCore/loader
parent9364f22aed35e1a1e9d07c121510f80be3ab0502 (diff)
downloadexternal_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.zip
external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.gz
external_webkit-1cbdecfa9fc428ac2d8aca0fa91c9580b3d57353.tar.bz2
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'WebCore/loader')
-rw-r--r--WebCore/loader/Cache.cpp229
-rw-r--r--WebCore/loader/Cache.h27
-rw-r--r--WebCore/loader/CachedCSSStyleSheet.cpp30
-rw-r--r--WebCore/loader/CachedCSSStyleSheet.h9
-rw-r--r--WebCore/loader/CachedFont.cpp34
-rw-r--r--WebCore/loader/CachedFont.h13
-rw-r--r--WebCore/loader/CachedImage.cpp133
-rw-r--r--WebCore/loader/CachedImage.h24
-rw-r--r--WebCore/loader/CachedResource.cpp148
-rw-r--r--WebCore/loader/CachedResource.h72
-rw-r--r--WebCore/loader/CachedResourceClient.h3
-rw-r--r--WebCore/loader/CachedResourceHandle.cpp (renamed from WebCore/loader/android/DocumentLoaderAndroid.cpp)34
-rw-r--r--WebCore/loader/CachedResourceHandle.h92
-rw-r--r--WebCore/loader/CachedScript.cpp11
-rw-r--r--WebCore/loader/CachedScript.h4
-rw-r--r--WebCore/loader/CachedXBLDocument.cpp9
-rw-r--r--WebCore/loader/CachedXBLDocument.h4
-rw-r--r--WebCore/loader/CachedXSLStyleSheet.cpp14
-rw-r--r--WebCore/loader/CachedXSLStyleSheet.h4
-rw-r--r--WebCore/loader/DocLoader.cpp197
-rw-r--r--WebCore/loader/DocLoader.h37
-rw-r--r--WebCore/loader/DocumentLoader.cpp397
-rw-r--r--WebCore/loader/DocumentLoader.h142
-rw-r--r--WebCore/loader/EmptyClients.h418
-rw-r--r--WebCore/loader/FTPDirectoryDocument.cpp29
-rw-r--r--WebCore/loader/FTPDirectoryDocument.h12
-rw-r--r--WebCore/loader/FTPDirectoryParser.cpp14
-rw-r--r--WebCore/loader/FormState.cpp2
-rw-r--r--WebCore/loader/FrameLoader.cpp1232
-rw-r--r--WebCore/loader/FrameLoader.h130
-rw-r--r--WebCore/loader/FrameLoaderClient.h38
-rw-r--r--WebCore/loader/ImageDocument.cpp55
-rw-r--r--WebCore/loader/ImageDocument.h16
-rw-r--r--WebCore/loader/ImageLoader.cpp137
-rw-r--r--WebCore/loader/ImageLoader.h73
-rw-r--r--WebCore/loader/MainResourceLoader.cpp78
-rw-r--r--WebCore/loader/MainResourceLoader.h12
-rw-r--r--WebCore/loader/MediaDocument.cpp168
-rw-r--r--WebCore/loader/MediaDocument.h (renamed from WebCore/loader/mac/ImageDocumentMac.h)33
-rw-r--r--WebCore/loader/NetscapePlugInStreamLoader.cpp17
-rw-r--r--WebCore/loader/NetscapePlugInStreamLoader.h10
-rw-r--r--WebCore/loader/PluginDocument.cpp42
-rw-r--r--WebCore/loader/PluginDocument.h20
-rw-r--r--WebCore/loader/ResourceLoader.cpp69
-rw-r--r--WebCore/loader/ResourceLoader.h29
-rw-r--r--WebCore/loader/SubresourceLoader.cpp41
-rw-r--r--WebCore/loader/SubresourceLoader.h9
-rw-r--r--WebCore/loader/SubresourceLoaderClient.h7
-rw-r--r--WebCore/loader/SubstituteData.h1
-rw-r--r--WebCore/loader/SubstituteResource.h64
-rw-r--r--WebCore/loader/TextDocument.cpp43
-rw-r--r--WebCore/loader/TextDocument.h45
-rw-r--r--WebCore/loader/TextResourceDecoder.cpp181
-rw-r--r--WebCore/loader/TextResourceDecoder.h17
-rw-r--r--WebCore/loader/UserStyleSheetLoader.cpp (renamed from WebCore/loader/mac/UserStyleSheetLoader.cpp)8
-rw-r--r--WebCore/loader/UserStyleSheetLoader.h (renamed from WebCore/loader/mac/UserStyleSheetLoader.h)5
-rw-r--r--WebCore/loader/appcache/ApplicationCache.cpp192
-rw-r--r--WebCore/loader/appcache/ApplicationCache.h103
-rw-r--r--WebCore/loader/appcache/ApplicationCacheGroup.cpp715
-rw-r--r--WebCore/loader/appcache/ApplicationCacheGroup.h152
-rw-r--r--WebCore/loader/appcache/ApplicationCacheResource.cpp71
-rw-r--r--WebCore/loader/appcache/ApplicationCacheResource.h74
-rw-r--r--WebCore/loader/appcache/ApplicationCacheStorage.cpp663
-rw-r--r--WebCore/loader/appcache/ApplicationCacheStorage.h96
-rw-r--r--WebCore/loader/appcache/DOMApplicationCache.cpp286
-rw-r--r--WebCore/loader/appcache/DOMApplicationCache.h140
-rw-r--r--WebCore/loader/appcache/DOMApplicationCache.idl74
-rw-r--r--WebCore/loader/appcache/ManifestParser.cpp166
-rw-r--r--WebCore/loader/appcache/ManifestParser.h (renamed from WebCore/loader/mac/ImageDocumentMac.mm)49
-rw-r--r--WebCore/loader/archive/Archive.h62
-rw-r--r--WebCore/loader/archive/ArchiveFactory.cpp89
-rw-r--r--WebCore/loader/archive/ArchiveFactory.h50
-rw-r--r--WebCore/loader/archive/ArchiveResource.cpp77
-rw-r--r--WebCore/loader/archive/ArchiveResource.h65
-rw-r--r--WebCore/loader/archive/ArchiveResourceCollection.cpp89
-rw-r--r--WebCore/loader/archive/ArchiveResourceCollection.h59
-rw-r--r--WebCore/loader/archive/cf/LegacyWebArchive.cpp562
-rw-r--r--WebCore/loader/archive/cf/LegacyWebArchive.h67
-rw-r--r--WebCore/loader/archive/cf/LegacyWebArchiveMac.mm74
-rw-r--r--WebCore/loader/icon/IconDatabase.cpp120
-rw-r--r--WebCore/loader/icon/IconDatabase.h13
-rw-r--r--WebCore/loader/icon/IconDatabaseNone.cpp2
-rw-r--r--WebCore/loader/icon/IconFetcher.cpp236
-rw-r--r--WebCore/loader/icon/IconFetcher.h78
-rw-r--r--WebCore/loader/icon/IconLoader.h7
-rw-r--r--WebCore/loader/icon/IconRecord.cpp6
-rw-r--r--WebCore/loader/icon/IconRecord.h10
-rw-r--r--WebCore/loader/loader.cpp450
-rw-r--r--WebCore/loader/loader.h63
-rw-r--r--WebCore/loader/mac/DocumentLoaderMac.cpp79
-rw-r--r--WebCore/loader/mac/LoaderNSURLExtras.h11
-rw-r--r--WebCore/loader/mac/LoaderNSURLExtras.m258
-rw-r--r--WebCore/loader/mac/LoaderNSURLExtras.mm102
93 files changed, 8363 insertions, 1769 deletions
diff --git a/WebCore/loader/Cache.cpp b/WebCore/loader/Cache.cpp
index bb239fc..7d30e5f 100644
--- a/WebCore/loader/Cache.cpp
+++ b/WebCore/loader/Cache.cpp
@@ -30,11 +30,16 @@
#include "CachedXSLStyleSheet.h"
#include "DocLoader.h"
#include "Document.h"
+#if USE(LOW_BANDWIDTH_DISPLAY)
#include "Frame.h"
+#endif
#include "FrameLoader.h"
+#include "FrameView.h"
#include "Image.h"
#include "ResourceHandle.h"
#include "SystemTime.h"
+#include <stdio.h>
+
using namespace std;
@@ -43,6 +48,7 @@ namespace WebCore {
static const int cDefaultCacheCapacity = 8192 * 1024;
static const double cMinDelayBeforeLiveDecodedPrune = 1; // Seconds.
static const float cTargetPrunePercentage = .95f; // Percentage of capacity toward which we prune, to avoid immediately pruning again.
+static const double cDefaultDecodedDataDeletionInterval = 0;
Cache* cache()
{
@@ -51,35 +57,36 @@ Cache* cache()
}
Cache::Cache()
-: m_disabled(false)
-, m_pruneEnabled(true)
-, m_capacity(cDefaultCacheCapacity)
-, m_minDeadCapacity(0)
-, m_maxDeadCapacity(cDefaultCacheCapacity)
-, m_liveSize(0)
-, m_deadSize(0)
+ : m_disabled(false)
+ , m_pruneEnabled(true)
+ , m_inPruneDeadResources(false)
+ , m_capacity(cDefaultCacheCapacity)
+ , m_minDeadCapacity(0)
+ , m_maxDeadCapacity(cDefaultCacheCapacity)
+ , m_deadDecodedDataDeletionInterval(cDefaultDecodedDataDeletionInterval)
+ , m_liveSize(0)
+ , m_deadSize(0)
{
}
-static CachedResource* createResource(CachedResource::Type type, DocLoader* docLoader, const KURL& url, const String* charset, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true)
+static CachedResource* createResource(CachedResource::Type type, const KURL& url, const String& charset)
{
switch (type) {
case CachedResource::ImageResource:
- // User agent images need to null check the docloader. No other resources need to.
- return new CachedImage(docLoader, url.string(), true /* for cache */);
+ return new CachedImage(url.string());
case CachedResource::CSSStyleSheet:
- return new CachedCSSStyleSheet(docLoader, url.string(), *charset, skipCanLoadCheck, sendResourceLoadCallbacks);
+ return new CachedCSSStyleSheet(url.string(), charset);
case CachedResource::Script:
- return new CachedScript(docLoader, url.string(), *charset);
+ return new CachedScript(url.string(), charset);
case CachedResource::FontResource:
- return new CachedFont(docLoader, url.string());
+ return new CachedFont(url.string());
#if ENABLE(XSLT)
case CachedResource::XSLStyleSheet:
- return new CachedXSLStyleSheet(docLoader, url.string());
+ return new CachedXSLStyleSheet(url.string());
#endif
#if ENABLE(XBL)
case CachedResource::XBLStyleSheet:
- return new CachedXBLDocument(docLoader, url.string());
+ return new CachedXBLDocument(url.string());
#endif
default:
break;
@@ -88,11 +95,7 @@ static CachedResource* createResource(CachedResource::Type type, DocLoader* docL
return 0;
}
-#ifdef ANDROID_PRELOAD_CHANGES
-CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Type type, const KURL& url, const String* charset, bool skipCanLoadCheck, bool sendResourceLoadCallbacks, bool isPreload)
-#else
-CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Type type, const KURL& url, const String* charset, bool skipCanLoadCheck, bool sendResourceLoadCallbacks)
-#endif
+CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Type type, const KURL& url, const String& charset, bool isPreload)
{
// FIXME: Do we really need to special-case an empty URL?
// Would it be better to just go on with the cache code and let it fail later?
@@ -103,43 +106,35 @@ CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Typ
CachedResource* resource = m_resources.get(url.string());
if (resource) {
-#ifdef ANDROID_PRELOAD_CHANGES
if (isPreload && !resource->isPreloaded())
return 0;
-#endif
- if (!skipCanLoadCheck && FrameLoader::restrictAccessToLocal() && !FrameLoader::canLoad(*resource, docLoader->doc())) {
+ if (FrameLoader::restrictAccessToLocal() && !FrameLoader::canLoad(url, String(), docLoader->doc())) {
Document* doc = docLoader->doc();
-#ifdef ANDROID_PRELOAD_CHANGES
if(doc && !isPreload)
-#else
- if(doc)
-#endif
- FrameLoader::reportLocalLoadFailed(doc->page(), resource->url());
+ FrameLoader::reportLocalLoadFailed(doc->frame(), resource->url());
return 0;
}
} else {
- if (!skipCanLoadCheck && FrameLoader::restrictAccessToLocal() && !FrameLoader::canLoad(url, docLoader->doc())) {
+ if (FrameLoader::restrictAccessToLocal() && !FrameLoader::canLoad(url, String(), docLoader->doc())) {
Document* doc = docLoader->doc();
-#ifdef ANDROID_PRELOAD_CHANGES
if(doc && !isPreload)
-#else
- if(doc)
-#endif
- FrameLoader::reportLocalLoadFailed(doc->page(), url.string());
-
+ FrameLoader::reportLocalLoadFailed(doc->frame(), url.string());
return 0;
}
// The resource does not exist. Create it.
- resource = createResource(type, docLoader, url, charset, skipCanLoadCheck, sendResourceLoadCallbacks);
+ resource = createResource(type, url, charset);
ASSERT(resource);
- ASSERT(resource->inCache());
- if (!disabled()) {
+
+ // Pretend the resource is in the cache, to prevent it from being deleted during the load() call.
+ // FIXME: CachedResource should just use normal refcounting instead.
+ resource->setInCache(true);
+
+ resource->load(docLoader);
+
+ if (!disabled())
m_resources.set(url.string(), resource); // The size will be added in later once the resource is loaded and calls back to us with the new size.
-
- // This will move the resource to the front of its LRU list and increase its access count.
- resourceAccessed(resource);
- } else {
+ else {
// Kick the resource out of the cache, because the cache is disabled.
resource->setInCache(false);
resource->setDocLoader(docLoader);
@@ -163,8 +158,93 @@ CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Typ
return 0;
#endif
+ if (!disabled()) {
+ // This will move the resource to the front of its LRU list and increase its access count.
+ resourceAccessed(resource);
+ }
+
return resource;
}
+
+CachedCSSStyleSheet* Cache::requestUserCSSStyleSheet(DocLoader* docLoader, const String& url, const String& charset)
+{
+ CachedCSSStyleSheet* userSheet;
+ if (CachedResource* existing = m_resources.get(url)) {
+ if (existing->type() != CachedResource::CSSStyleSheet)
+ return 0;
+ userSheet = static_cast<CachedCSSStyleSheet*>(existing);
+ } else {
+ userSheet = new CachedCSSStyleSheet(url, charset);
+
+ // Pretend the resource is in the cache, to prevent it from being deleted during the load() call.
+ // FIXME: CachedResource should just use normal refcounting instead.
+ userSheet->setInCache(true);
+ // Don't load incrementally, skip load checks, don't send resource load callbacks.
+ userSheet->load(docLoader, false, true, false);
+ if (!disabled())
+ m_resources.set(url, userSheet);
+ else
+ userSheet->setInCache(false);
+ }
+
+ if (!disabled()) {
+ // This will move the resource to the front of its LRU list and increase its access count.
+ resourceAccessed(userSheet);
+ }
+
+ return userSheet;
+}
+
+void Cache::revalidateResource(CachedResource* resource, DocLoader* docLoader)
+{
+ ASSERT(resource);
+ ASSERT(!disabled());
+ if (resource->resourceToRevalidate())
+ return;
+ if (!resource->canUseCacheValidator()) {
+ evict(resource);
+ return;
+ }
+ const String& url = resource->url();
+ CachedResource* newResource = createResource(resource->type(), KURL(url), resource->encoding());
+ newResource->setResourceToRevalidate(resource);
+ evict(resource);
+ m_resources.set(url, newResource);
+ newResource->setInCache(true);
+ resourceAccessed(newResource);
+ newResource->load(docLoader);
+}
+
+void Cache::revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse& response)
+{
+ CachedResource* resource = revalidatingResource->resourceToRevalidate();
+ ASSERT(resource);
+ ASSERT(!resource->inCache());
+ ASSERT(resource->isLoaded());
+
+ evict(revalidatingResource);
+
+ ASSERT(!m_resources.get(resource->url()));
+ m_resources.set(resource->url(), resource);
+ resource->setInCache(true);
+ resource->setExpirationDate(response.expirationDate());
+ insertInLRUList(resource);
+ int delta = resource->size();
+ if (resource->decodedSize() && resource->hasClients())
+ insertInLiveDecodedResourcesList(resource);
+ if (delta)
+ adjustSize(resource->hasClients(), delta);
+
+ revalidatingResource->switchClientsToRevalidatedResource();
+ // this deletes the revalidating resource
+ revalidatingResource->clearResourceToRevalidate();
+}
+
+void Cache::revalidationFailed(CachedResource* revalidatingResource)
+{
+ ASSERT(revalidatingResource->resourceToRevalidate());
+ revalidatingResource->clearResourceToRevalidate();
+}
CachedResource* Cache::resourceForURL(const String& url)
{
@@ -192,11 +272,11 @@ void Cache::pruneLiveResources()
return;
unsigned capacity = liveCapacity();
- if (m_liveSize <= capacity)
+ if (capacity && m_liveSize <= capacity)
return;
unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again.
- double currentTime = Frame::currentPaintTimeStamp();
+ double currentTime = FrameView::currentPaintTimeStamp();
if (!currentTime) // In case prune is called directly, outside of a Frame paint.
currentTime = WebCore::currentTime();
@@ -205,7 +285,7 @@ void Cache::pruneLiveResources()
CachedResource* current = m_liveDecodedResources.m_tail;
while (current) {
CachedResource* prev = current->m_prevInLiveResourcesList;
- ASSERT(current->referenced());
+ ASSERT(current->hasClients());
if (current->isLoaded() && current->decodedSize()) {
// Check to see if the remaining resources are too new to prune.
double elapsedTime = currentTime - current->m_lastDecodedAccessTime;
@@ -217,7 +297,7 @@ void Cache::pruneLiveResources()
// list in m_allResources.
current->destroyDecodedData();
- if (m_liveSize <= targetSize)
+ if (targetSize && m_liveSize <= targetSize)
return;
}
current = prev;
@@ -230,12 +310,13 @@ void Cache::pruneDeadResources()
return;
unsigned capacity = deadCapacity();
- if (m_deadSize <= capacity)
+ if (capacity && m_deadSize <= capacity)
return;
unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again.
int size = m_allResources.size();
bool canShrinkLRULists = true;
+ m_inPruneDeadResources = true;
for (int i = size - 1; i >= 0; i--) {
// Remove from the tail, since this is the least frequently accessed of the objects.
CachedResource* current = m_allResources[i].m_tail;
@@ -243,18 +324,16 @@ void Cache::pruneDeadResources()
// First flush all the decoded data in this queue.
while (current) {
CachedResource* prev = current->m_prevInAllResourcesList;
-#ifdef ANDROID_PRELOAD_CHANGES
- if (!current->referenced() && !current->isPreloaded() && current->isLoaded() && current->decodedSize()) {
-#else
- if (!current->referenced() && current->isLoaded() && current->decodedSize()) {
-#endif
+ if (!current->hasClients() && !current->isPreloaded() && current->isLoaded() && current->decodedSize()) {
// Destroy our decoded data. This will remove us from
// m_liveDecodedResources, and possibly move us to a differnt
// LRU list in m_allResources.
current->destroyDecodedData();
- if (m_deadSize <= targetSize)
+ if (targetSize && m_deadSize <= targetSize) {
+ m_inPruneDeadResources = false;
return;
+ }
}
current = prev;
}
@@ -263,18 +342,21 @@ void Cache::pruneDeadResources()
current = m_allResources[i].m_tail;
while (current) {
CachedResource* prev = current->m_prevInAllResourcesList;
-#ifdef ANDROID_PRELOAD_CHANGES
- if (!current->referenced() && !current->isPreloaded()) {
-#else
- if (!current->referenced()) {
-#endif
- remove(current);
+ if (!current->hasClients() && !current->isPreloaded()) {
+ evict(current);
+ // If evict() caused pruneDeadResources() to be re-entered, bail out. This can happen when removing an
+ // SVG CachedImage that has subresources.
+ if (!m_inPruneDeadResources)
+ return;
- if (m_deadSize <= targetSize)
+ if (targetSize && m_deadSize <= targetSize) {
+ m_inPruneDeadResources = false;
return;
+ }
}
current = prev;
}
+
// Shrink the vector back down so we don't waste time inspecting
// empty LRU lists on future prunes.
if (m_allResources[i].m_head)
@@ -282,6 +364,7 @@ void Cache::pruneDeadResources()
else if (canShrinkLRULists)
m_allResources.resize(i);
}
+ m_inPruneDeadResources = false;
}
void Cache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes)
@@ -294,7 +377,7 @@ void Cache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned
prune();
}
-void Cache::remove(CachedResource* resource)
+void Cache::evict(CachedResource* resource)
{
// The resource may have already been removed by someone other than our caller,
// who needed a fresh copy for a reload. See <http://bugs.webkit.org/show_bug.cgi?id=12479#c6>.
@@ -316,8 +399,9 @@ void Cache::remove(CachedResource* resource)
// Subtract from our size totals.
int delta = -static_cast<int>(resource->size());
if (delta)
- adjustSize(resource->referenced(), delta);
- }
+ adjustSize(resource->hasClients(), delta);
+ } else
+ ASSERT(m_resources.get(resource->url()) != resource);
if (resource->canDelete())
delete resource;
@@ -415,6 +499,7 @@ void Cache::insertInLRUList(CachedResource* resource)
// Make sure we aren't in some list already.
ASSERT(!resource->m_nextInAllResourcesList && !resource->m_prevInAllResourcesList);
ASSERT(resource->inCache());
+ ASSERT(resource->accessCount() > 0);
LRUList* list = lruListFor(resource);
@@ -556,42 +641,42 @@ Cache::Statistics Cache::getStatistics()
case CachedResource::ImageResource:
stats.images.count++;
stats.images.size += o->size();
- stats.images.liveSize += o->referenced() ? o->size() : 0;
+ stats.images.liveSize += o->hasClients() ? o->size() : 0;
stats.images.decodedSize += o->decodedSize();
break;
case CachedResource::CSSStyleSheet:
stats.cssStyleSheets.count++;
stats.cssStyleSheets.size += o->size();
- stats.cssStyleSheets.liveSize += o->referenced() ? o->size() : 0;
+ stats.cssStyleSheets.liveSize += o->hasClients() ? o->size() : 0;
stats.cssStyleSheets.decodedSize += o->decodedSize();
break;
case CachedResource::Script:
stats.scripts.count++;
stats.scripts.size += o->size();
- stats.scripts.liveSize += o->referenced() ? o->size() : 0;
+ stats.scripts.liveSize += o->hasClients() ? o->size() : 0;
stats.scripts.decodedSize += o->decodedSize();
break;
#if ENABLE(XSLT)
case CachedResource::XSLStyleSheet:
stats.xslStyleSheets.count++;
stats.xslStyleSheets.size += o->size();
- stats.xslStyleSheets.liveSize += o->referenced() ? o->size() : 0;
+ stats.xslStyleSheets.liveSize += o->hasClients() ? o->size() : 0;
stats.xslStyleSheets.decodedSize += o->decodedSize();
break;
#endif
case CachedResource::FontResource:
stats.fonts.count++;
stats.fonts.size += o->size();
- stats.fonts.liveSize += o->referenced() ? o->size() : 0;
+ stats.fonts.liveSize += o->hasClients() ? o->size() : 0;
stats.fonts.decodedSize += o->decodedSize();
break;
#if ENABLE(XBL)
case CachedResource::XBL:
stats.xblDocs.count++;
stats.xblDocs.size += o->size();
- stats.xblDocs.liveSize += o->referenced() ? o->size() : 0;
+ stats.xblDocs.liveSize += o->hasClients() ? o->size() : 0;
stats.xblDocs.decodedSize += o->decodedSize();
break;
#endif
@@ -613,7 +698,7 @@ void Cache::setDisabled(bool disabled)
CachedResourceMap::iterator i = m_resources.begin();
if (i == m_resources.end())
break;
- remove(i->second);
+ evict(i->second);
}
}
@@ -628,8 +713,8 @@ void Cache::dumpLRULists(bool includeLive) const
CachedResource* current = m_allResources[i].m_tail;
while (current) {
CachedResource* prev = current->m_prevInAllResourcesList;
- if (includeLive || !current->referenced())
- printf("(%.1fK, %.1fK, %uA, %dR); ", current->decodedSize() / 1024.0f, current->encodedSize() / 1024.0f, current->accessCount(), current->referenced());
+ if (includeLive || !current->hasClients())
+ printf("(%.1fK, %.1fK, %uA, %dR); ", current->decodedSize() / 1024.0f, current->encodedSize() / 1024.0f, current->accessCount(), current->hasClients());
current = prev;
}
}
diff --git a/WebCore/loader/Cache.h b/WebCore/loader/Cache.h
index 8256708..ec0ea0e 100644
--- a/WebCore/loader/Cache.h
+++ b/WebCore/loader/Cache.h
@@ -3,7 +3,7 @@
Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
- Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -39,6 +39,7 @@
namespace WebCore {
+class CachedCSSStyleSheet;
class CachedResource;
class DocLoader;
class KURL;
@@ -94,11 +95,14 @@ public:
// Request resources from the cache. A load will be initiated and a cache object created if the object is not
// found in the cache.
-#ifdef ANDROID_PRELOAD_CHANGES
- CachedResource* requestResource(DocLoader*, CachedResource::Type, const KURL& url, const String* charset = 0, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true, bool isPreload = false);
-#else
- CachedResource* requestResource(DocLoader*, CachedResource::Type, const KURL& url, const String* charset = 0, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true);
-#endif
+ CachedResource* requestResource(DocLoader*, CachedResource::Type, const KURL& url, const String& charset, bool isPreload = false);
+
+ CachedCSSStyleSheet* requestUserCSSStyleSheet(DocLoader*, const String& url, const String& charset);
+
+ void revalidateResource(CachedResource*, DocLoader*);
+ void revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse&);
+ void revalidationFailed(CachedResource* revalidatingResource);
+
// Sets the cache's memory capacities, in bytes. These will hold only approximately,
// since the decoded cost of resources like scripts and stylesheets is not known.
// - minDeadBytes: The maximum number of bytes that dead resources should consume when the cache is under pressure.
@@ -114,15 +118,18 @@ public:
void setPruneEnabled(bool enabled) { m_pruneEnabled = enabled; }
void prune()
{
- if (m_liveSize + m_deadSize <= m_capacity && m_deadSize <= m_maxDeadCapacity) // Fast path.
+ if (m_liveSize + m_deadSize <= m_capacity && m_maxDeadCapacity && m_deadSize <= m_maxDeadCapacity) // Fast path.
return;
pruneDeadResources(); // Prune dead first, in case it was "borrowing" capacity from live.
pruneLiveResources();
}
+ void setDeadDecodedDataDeletionInterval(double interval) { m_deadDecodedDataDeletionInterval = interval; }
+ double deadDecodedDataDeletionInterval() const { return m_deadDecodedDataDeletionInterval; }
+
// Remove an existing cache entry from both the resource map and from the LRU list.
- void remove(CachedResource*);
+ void remove(CachedResource* resource) { evict(resource); }
void addDocLoader(DocLoader*);
void removeDocLoader(DocLoader*);
@@ -167,16 +174,20 @@ private:
void pruneDeadResources(); // Flush decoded and encoded data from resources not referenced by Web pages.
void pruneLiveResources(); // Flush decoded data from resources still referenced by Web pages.
+ void evict(CachedResource*);
+
// Member variables.
HashSet<DocLoader*> m_docLoaders;
Loader m_loader;
bool m_disabled; // Whether or not the cache is enabled.
bool m_pruneEnabled;
+ bool m_inPruneDeadResources;
unsigned m_capacity;
unsigned m_minDeadCapacity;
unsigned m_maxDeadCapacity;
+ double m_deadDecodedDataDeletionInterval;
unsigned m_liveSize; // The number of bytes currently consumed by "live" resources in the cache.
unsigned m_deadSize; // The number of bytes currently consumed by "dead" resources in the cache.
diff --git a/WebCore/loader/CachedCSSStyleSheet.cpp b/WebCore/loader/CachedCSSStyleSheet.cpp
index 649a16d..ace938b 100644
--- a/WebCore/loader/CachedCSSStyleSheet.cpp
+++ b/WebCore/loader/CachedCSSStyleSheet.cpp
@@ -27,7 +27,6 @@
#include "config.h"
#include "CachedCSSStyleSheet.h"
-#include "Cache.h"
#include "CachedResourceClient.h"
#include "CachedResourceClientWalker.h"
#include "TextResourceDecoder.h"
@@ -36,27 +35,25 @@
namespace WebCore {
-CachedCSSStyleSheet::CachedCSSStyleSheet(DocLoader* dl, const String& url, const String& charset, bool skipCanLoadCheck, bool sendResourceLoadCallbacks)
- : CachedResource(url, CSSStyleSheet, true, sendResourceLoadCallbacks)
- , m_decoder(new TextResourceDecoder("text/css", charset))
+CachedCSSStyleSheet::CachedCSSStyleSheet(const String& url, const String& charset)
+ : CachedResource(url, CSSStyleSheet)
+ , m_decoder(TextResourceDecoder::create("text/css", charset))
{
// Prefer text/css but accept any type (dell.com serves a stylesheet
// as text/html; see <http://bugs.webkit.org/show_bug.cgi?id=11451>).
setAccept("text/css,*/*;q=0.1");
- cache()->loader()->load(dl, this, false, skipCanLoadCheck, sendResourceLoadCallbacks);
- m_loading = true;
}
CachedCSSStyleSheet::~CachedCSSStyleSheet()
{
}
-void CachedCSSStyleSheet::ref(CachedResourceClient *c)
+void CachedCSSStyleSheet::addClient(CachedResourceClient *c)
{
- CachedResource::ref(c);
+ CachedResource::addClient(c);
if (!m_loading)
- c->setCSSStyleSheet(m_url, m_decoder->encoding().name(), errorOccurred() ? "" : m_sheet);
+ c->setCSSStyleSheet(m_url, m_decoder->encoding().name(), this);
}
void CachedCSSStyleSheet::setEncoding(const String& chs)
@@ -95,7 +92,7 @@ void CachedCSSStyleSheet::checkNotify()
CachedResourceClientWalker w(m_clients);
while (CachedResourceClient *c = w.next())
- c->setCSSStyleSheet(m_response.url().string(), m_decoder->encoding().name(), m_sheet);
+ c->setCSSStyleSheet(m_response.url().string(), m_decoder->encoding().name(), this);
#if USE(LOW_BANDWIDTH_DISPLAY)
// if checkNotify() is called from error(), client's setCSSStyleSheet(...)
@@ -114,4 +111,17 @@ void CachedCSSStyleSheet::error()
checkNotify();
}
+bool CachedCSSStyleSheet::canUseSheet(bool enforceMIMEType) const
+{
+ if (errorOccurred())
+ return false;
+
+ if (!enforceMIMEType)
+ return true;
+
+ // This check exactly matches Firefox.
+ String mimeType = response().mimeType();
+ return mimeType.isEmpty() || equalIgnoringCase(mimeType, "text/css") || equalIgnoringCase(mimeType, "application/x-unknown-content-type");
+}
+
}
diff --git a/WebCore/loader/CachedCSSStyleSheet.h b/WebCore/loader/CachedCSSStyleSheet.h
index 50dddc2..b9129ba 100644
--- a/WebCore/loader/CachedCSSStyleSheet.h
+++ b/WebCore/loader/CachedCSSStyleSheet.h
@@ -39,12 +39,12 @@ namespace WebCore {
class CachedCSSStyleSheet : public CachedResource {
public:
- CachedCSSStyleSheet(DocLoader*, const String& URL, const String& charset, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true);
+ CachedCSSStyleSheet(const String& URL, const String& charset);
virtual ~CachedCSSStyleSheet();
- const String& sheet() const { return m_sheet; }
+ const String sheetText(bool enforceMIMEType = true) const { return canUseSheet(enforceMIMEType) ? m_sheet : ""; }
- virtual void ref(CachedResourceClient*);
+ virtual void addClient(CachedResourceClient*);
virtual void setEncoding(const String&);
virtual String encoding() const;
@@ -54,6 +54,9 @@ namespace WebCore {
virtual bool schedule() const { return true; }
void checkNotify();
+
+ private:
+ bool canUseSheet(bool enforceMIMEType) const;
protected:
String m_sheet;
diff --git a/WebCore/loader/CachedFont.cpp b/WebCore/loader/CachedFont.cpp
index 169adfa..20479ef 100644
--- a/WebCore/loader/CachedFont.cpp
+++ b/WebCore/loader/CachedFont.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 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
@@ -48,16 +48,14 @@
namespace WebCore {
-CachedFont::CachedFont(DocLoader* dl, const String &url)
+CachedFont::CachedFont(const String &url)
: CachedResource(url, FontResource)
, m_fontData(0)
+ , m_loadInitiated(false)
#if ENABLE(SVG_FONTS)
, m_isSVGFont(false)
#endif
{
- // Don't load the file yet. Wait for an access before triggering the load.
- m_loading = true;
- m_loadInitiated = false;
}
CachedFont::~CachedFont()
@@ -67,9 +65,15 @@ CachedFont::~CachedFont()
#endif
}
-void CachedFont::ref(CachedResourceClient *c)
+void CachedFont::load(DocLoader* docLoader)
+{
+ // Don't load the file yet. Wait for an access before triggering the load.
+ m_loading = true;
+}
+
+void CachedFont::addClient(CachedResourceClient* c)
{
- CachedResource::ref(c);
+ CachedResource::addClient(c);
if (!m_loading)
c->fontLoaded(this);
@@ -109,7 +113,7 @@ bool CachedFont::ensureCustomFontData()
return m_fontData;
}
-FontPlatformData CachedFont::platformDataFromCustomData(float size, bool bold, bool italic)
+FontPlatformData CachedFont::platformDataFromCustomData(float size, bool bold, bool italic, FontRenderingMode renderingMode)
{
#if ENABLE(SVG_FONTS)
if (m_externalSVGDocument)
@@ -117,7 +121,7 @@ FontPlatformData CachedFont::platformDataFromCustomData(float size, bool bold, b
#endif
#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(SGL)
ASSERT(m_fontData);
- return m_fontData->fontPlatformData(static_cast<int>(size), bold, italic);
+ return m_fontData->fontPlatformData(static_cast<int>(size), bold, italic, renderingMode);
#else
return FontPlatformData();
#endif
@@ -128,11 +132,15 @@ bool CachedFont::ensureSVGFontData()
{
ASSERT(m_isSVGFont);
if (!m_externalSVGDocument && !m_errorOccurred && !m_loading && m_data) {
- m_externalSVGDocument = new SVGDocument(DOMImplementation::instance(), 0);
+ m_externalSVGDocument = SVGDocument::create(0);
m_externalSVGDocument->open();
- TextResourceDecoder decoder("application/xml");
- m_externalSVGDocument->write(decoder.decode(m_data->data(), m_data->size()));
+ RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("application/xml");
+ m_externalSVGDocument->write(decoder->decode(m_data->data(), m_data->size()));
+ if (decoder->sawError()) {
+ m_externalSVGDocument.clear();
+ return 0;
+ }
m_externalSVGDocument->finishParsing();
m_externalSVGDocument->close();
@@ -164,7 +172,7 @@ SVGFontElement* CachedFont::getSVGFontById(const String& fontName) const
}
#endif
-void CachedFont::allReferencesRemoved()
+void CachedFont::allClientsRemoved()
{
#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK)
if (m_fontData) {
diff --git a/WebCore/loader/CachedFont.h b/WebCore/loader/CachedFont.h
index c2b06f7..fd19cdb 100644
--- a/WebCore/loader/CachedFont.h
+++ b/WebCore/loader/CachedFont.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 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
@@ -27,6 +27,7 @@
#define CachedFont_h
#include "CachedResource.h"
+#include "FontRenderingMode.h"
#include <wtf/Vector.h>
#if ENABLE(SVG_FONTS)
@@ -44,14 +45,16 @@ class SVGFontElement;
class CachedFont : public CachedResource {
public:
- CachedFont(DocLoader*, const String& url);
+ CachedFont(const String& url);
virtual ~CachedFont();
+
+ virtual void load(DocLoader* docLoader);
- virtual void ref(CachedResourceClient*);
+ virtual void addClient(CachedResourceClient*);
virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived);
virtual void error();
- virtual void allReferencesRemoved();
+ virtual void allClientsRemoved();
virtual bool schedule() const { return true; }
@@ -60,7 +63,7 @@ public:
void beginLoadIfNeeded(DocLoader* dl);
bool ensureCustomFontData();
- FontPlatformData platformDataFromCustomData(float size, bool bold, bool italic);
+ FontPlatformData platformDataFromCustomData(float size, bool bold, bool italic, FontRenderingMode = NormalRenderingMode);
#if ENABLE(SVG_FONTS)
bool isSVGFont() const { return m_isSVGFont; }
diff --git a/WebCore/loader/CachedImage.cpp b/WebCore/loader/CachedImage.cpp
index f591d41..b915b17 100644
--- a/WebCore/loader/CachedImage.cpp
+++ b/WebCore/loader/CachedImage.cpp
@@ -30,7 +30,9 @@
#include "CachedResourceClientWalker.h"
#include "DocLoader.h"
#include "Frame.h"
+#include "FrameView.h"
#include "Request.h"
+#include "Settings.h"
#include "SystemTime.h"
#include <wtf/Vector.h>
@@ -46,66 +48,75 @@ using std::max;
namespace WebCore {
-CachedImage::CachedImage(DocLoader* docLoader, const String& url, bool forCache)
- : CachedResource(url, ImageResource, forCache)
+CachedImage::CachedImage(const String& url)
+ : CachedResource(url, ImageResource)
+ , m_image(0)
+ , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
{
- m_image = 0;
m_status = Unknown;
- if (!docLoader || docLoader->autoLoadImages()) {
-#ifdef ANDROID_BLOCK_NETWORK_IMAGE
- if (docLoader && docLoader->shouldBlockNetworkImage(url)) {
- m_loading = false;
- return;
- }
-#endif
- m_loading = true;
- cache()->loader()->load(docLoader, this, true);
- } else
- m_loading = false;
}
CachedImage::CachedImage(Image* image)
- : CachedResource(String(), ImageResource, false /* not for cache */)
+ : CachedResource(String(), ImageResource)
+ , m_image(image)
+ , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired)
{
- m_image = image;
m_status = Cached;
m_loading = false;
}
CachedImage::~CachedImage()
{
- delete m_image;
}
-void CachedImage::ref(CachedResourceClient* c)
+void CachedImage::decodedDataDeletionTimerFired(Timer<CachedImage>*)
+{
+ ASSERT(!hasClients());
+ destroyDecodedData();
+}
+
+void CachedImage::load(DocLoader* docLoader)
+{
+ if (!docLoader || docLoader->autoLoadImages())
+ CachedResource::load(docLoader, true, false, true);
+ else
+ m_loading = false;
+}
+
+void CachedImage::addClient(CachedResourceClient* c)
{
- CachedResource::ref(c);
+ CachedResource::addClient(c);
+
+ if (m_decodedDataDeletionTimer.isActive())
+ m_decodedDataDeletionTimer.stop();
- if (!imageRect().isEmpty())
+ if (m_image && !m_image->rect().isEmpty())
c->imageChanged(this);
if (!m_loading)
c->notifyFinished(this);
}
-void CachedImage::allReferencesRemoved()
+void CachedImage::allClientsRemoved()
{
if (m_image && !m_errorOccurred)
m_image->resetAnimation();
+ if (double interval = cache()->deadDecodedDataDeletionInterval())
+ m_decodedDataDeletionTimer.startOneShot(interval);
}
static Image* brokenImage()
{
- static Image* brokenImage;
+ static RefPtr<Image> brokenImage;
if (!brokenImage)
brokenImage = Image::loadPlatformResource("missingImage");
- return brokenImage;
+ return brokenImage.get();
}
static Image* nullImage()
{
- static BitmapImage nullImage;
- return &nullImage;
+ static RefPtr<BitmapImage> nullImage = BitmapImage::create();
+ return nullImage.get();
}
Image* CachedImage::image() const
@@ -114,7 +125,7 @@ Image* CachedImage::image() const
return brokenImage();
if (m_image)
- return m_image;
+ return m_image.get();
return nullImage();
}
@@ -149,27 +160,62 @@ bool CachedImage::imageHasRelativeHeight() const
return false;
}
-IntSize CachedImage::imageSize() const
+IntSize CachedImage::imageSize(float multiplier) const
{
- return (m_image ? m_image->size() : IntSize());
+ if (!m_image)
+ return IntSize();
+ if (multiplier == 1.0f)
+ return m_image->size();
+
+ // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
+ bool hasWidth = m_image->size().width() > 0;
+ bool hasHeight = m_image->size().height() > 0;
+ int width = m_image->size().width() * (m_image->hasRelativeWidth() ? 1.0f : multiplier);
+ int height = m_image->size().height() * (m_image->hasRelativeHeight() ? 1.0f : multiplier);
+ if (hasWidth)
+ width = max(1, width);
+ if (hasHeight)
+ height = max(1, height);
+ return IntSize(width, height);
}
-IntRect CachedImage::imageRect() const
+IntRect CachedImage::imageRect(float multiplier) const
{
- return (m_image ? m_image->rect() : IntRect());
+ if (!m_image)
+ return IntRect();
+ if (multiplier == 1.0f || (!m_image->hasRelativeWidth() && !m_image->hasRelativeHeight()))
+ return m_image->rect();
+
+ float widthMultiplier = (m_image->hasRelativeWidth() ? 1.0f : multiplier);
+ float heightMultiplier = (m_image->hasRelativeHeight() ? 1.0f : multiplier);
+
+ // Don't let images that have a width/height >= 1 shrink below 1 when zoomed.
+ bool hasWidth = m_image->rect().width() > 0;
+ bool hasHeight = m_image->rect().height() > 0;
+
+ int width = static_cast<int>(m_image->rect().width() * widthMultiplier);
+ int height = static_cast<int>(m_image->rect().height() * heightMultiplier);
+ if (hasWidth)
+ width = max(1, width);
+ if (hasHeight)
+ height = max(1, height);
+
+ int x = static_cast<int>(m_image->rect().x() * widthMultiplier);
+ int y = static_cast<int>(m_image->rect().y() * heightMultiplier);
+
+ return IntRect(x, y, width, height);
}
void CachedImage::notifyObservers()
{
CachedResourceClientWalker w(m_clients);
- while (CachedResourceClient *c = w.next())
+ while (CachedResourceClient* c = w.next())
c->imageChanged(this);
}
void CachedImage::clear()
{
destroyDecodedData();
- delete m_image;
m_image = 0;
setEncodedSize(0);
}
@@ -181,22 +227,31 @@ inline void CachedImage::createImage()
return;
#if PLATFORM(CG)
if (m_response.mimeType() == "application/pdf") {
- m_image = new PDFDocumentImage;
+ m_image = PDFDocumentImage::create();
return;
}
#endif
#if ENABLE(SVG_AS_IMAGE)
if (m_response.mimeType() == "image/svg+xml") {
- m_image = new SVGImage(this);
+ m_image = SVGImage::create(this);
return;
}
#endif
- m_image = new BitmapImage(this);
+ m_image = BitmapImage::create(this);
#if PLATFORM(SGL)
m_image->setURL(url());
#endif
}
+size_t CachedImage::maximumDecodedImageSize()
+{
+ Frame* frame = m_request ? m_request->docLoader()->frame() : 0;
+ if (!frame)
+ return 0;
+ Settings* settings = frame->settings();
+ return settings ? settings->maximumDecodedImageSize() : 0;
+}
+
void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived)
{
m_data = data;
@@ -215,8 +270,10 @@ void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived)
// network causes observers to repaint, which will force that chunk
// to decode.
if (sizeAvailable || allDataReceived) {
- if (m_image->isNull()) {
- // FIXME: I'm not convinced this case can even be hit.
+ size_t maxDecodedImageSize = maximumDecodedImageSize();
+ IntSize s = imageSize(1.0f);
+ size_t estimatedDecodedImageSize = s.width() * s.height() * 4; // no overflow check
+ if (m_image->isNull() || (maxDecodedImageSize > 0 && estimatedDecodedImageSize > maxDecodedImageSize)) {
error();
if (inCache())
cache()->remove(this);
@@ -273,7 +330,7 @@ void CachedImage::didDraw(const Image* image)
if (image != m_image)
return;
- double timeStamp = Frame::currentPaintTimeStamp();
+ double timeStamp = FrameView::currentPaintTimeStamp();
if (!timeStamp) // If didDraw is called outside of a Frame paint.
timeStamp = currentTime();
diff --git a/WebCore/loader/CachedImage.h b/WebCore/loader/CachedImage.h
index 71e558c..f24e2fb 100644
--- a/WebCore/loader/CachedImage.h
+++ b/WebCore/loader/CachedImage.h
@@ -25,26 +25,29 @@
#include "CachedResource.h"
#include "ImageObserver.h"
+#include "Image.h"
#include "IntRect.h"
+#include "Timer.h"
#include <wtf/Vector.h>
namespace WebCore {
class DocLoader;
class Cache;
-class Image;
class CachedImage : public CachedResource, public ImageObserver {
friend class Cache;
public:
- CachedImage(DocLoader*, const String& url, bool forCache);
+ CachedImage(const String& url);
CachedImage(Image*);
virtual ~CachedImage();
+
+ virtual void load(DocLoader* docLoader);
Image* image() const;
- bool canRender() const { return !errorOccurred() && imageSize().width() > 0 && imageSize().height() > 0; }
+ bool canRender(float multiplier) const { return !errorOccurred() && !imageSize(multiplier).isEmpty(); }
// These are only used for SVGImage right now
void setImageContainerSize(const IntSize&);
@@ -52,12 +55,14 @@ public:
bool imageHasRelativeWidth() const;
bool imageHasRelativeHeight() const;
- IntSize imageSize() const; // returns the size of the complete image
- IntRect imageRect() const; // The size of the image.
+ // Both of these methods take a zoom multiplier that can be used to increase the natural size of the image by the
+ // zoom.
+ IntSize imageSize(float multiplier) const; // returns the size of the complete image.
+ IntRect imageRect(float multiplier) const; // The size of the currently decoded portion of the image.
- virtual void ref(CachedResourceClient*);
+ virtual void addClient(CachedResourceClient*);
- virtual void allReferencesRemoved();
+ virtual void allClientsRemoved();
virtual void destroyDecodedData();
virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived);
@@ -83,9 +88,12 @@ public:
private:
void createImage();
+ size_t maximumDecodedImageSize();
void notifyObservers();
+ void decodedDataDeletionTimerFired(Timer<CachedImage>*);
- Image* m_image;
+ RefPtr<Image> m_image;
+ Timer<CachedImage> m_decodedDataDeletionTimer;
};
}
diff --git a/WebCore/loader/CachedResource.cpp b/WebCore/loader/CachedResource.cpp
index 6c78f4b..6d0af9b 100644
--- a/WebCore/loader/CachedResource.cpp
+++ b/WebCore/loader/CachedResource.cpp
@@ -25,28 +25,43 @@
#include "CachedResource.h"
#include "Cache.h"
+#include "CachedResourceHandle.h"
#include "DocLoader.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "KURL.h"
#include "Request.h"
#include "SystemTime.h"
+#include <wtf/RefCountedLeakCounter.h>
#include <wtf/Vector.h>
+using namespace WTF;
+
namespace WebCore {
-CachedResource::CachedResource(const String& url, Type type, bool forCache, bool sendResourceLoadCallbacks)
+#ifndef NDEBUG
+static RefCountedLeakCounter cachedResourceLeakCounter("CachedResource");
+#endif
+
+CachedResource::CachedResource(const String& url, Type type)
: m_url(url)
, m_lastDecodedAccessTime(0)
- , m_sendResourceLoadCallbacks(sendResourceLoadCallbacks)
-#ifdef ANDROID_PRELOAD_CHANGES
+ , m_sendResourceLoadCallbacks(true)
, m_preloadCount(0)
, m_preloadResult(PreloadNotReferenced)
, m_requestedFromNetworkingLayer(false)
-#endif
- , m_inCache(forCache)
+ , m_inCache(false)
+ , m_loading(false)
, m_docLoader(0)
+ , m_handleCount(0)
+ , m_resourceToRevalidate(0)
+ , m_isBeingRevalidated(false)
+ , m_expirationDate(0)
{
+#ifndef NDEBUG
+ cachedResourceLeakCounter.increment();
+#endif
+
m_type = type;
m_status = Pending;
m_encodedSize = 0;
@@ -67,20 +82,31 @@ CachedResource::CachedResource(const String& url, Type type, bool forCache, bool
m_lruIndex = 0;
#endif
m_errorOccurred = false;
- m_shouldTreatAsLocal = FrameLoader::shouldTreatURLAsLocal(m_url);
}
CachedResource::~CachedResource()
{
ASSERT(!inCache());
ASSERT(!m_deleted);
+ ASSERT(url().isNull() || cache()->resourceForURL(url()) != this);
#ifndef NDEBUG
m_deleted = true;
+ cachedResourceLeakCounter.decrement();
#endif
-
+
+ if (m_resourceToRevalidate)
+ m_resourceToRevalidate->m_isBeingRevalidated = false;
+
if (m_docLoader)
m_docLoader->removeCachedResource(this);
}
+
+void CachedResource::load(DocLoader* docLoader, bool incremental, bool skipCanLoadCheck, bool sendResourceLoadCallbacks)
+{
+ m_sendResourceLoadCallbacks = sendResourceLoadCallbacks;
+ cache()->loader()->load(docLoader, this, incremental, skipCanLoadCheck, sendResourceLoadCallbacks);
+ m_loading = true;
+}
void CachedResource::finish()
{
@@ -89,12 +115,18 @@ void CachedResource::finish()
bool CachedResource::isExpired() const
{
- if (!m_response.expirationDate())
+ if (!m_expirationDate)
return false;
time_t now = time(0);
- return (difftime(now, m_response.expirationDate()) >= 0);
+ return difftime(now, m_expirationDate) >= 0;
}
-
+
+void CachedResource::setResponse(const ResourceResponse& response)
+{
+ m_response = response;
+ m_expirationDate = response.expirationDate();
+}
+
void CachedResource::setRequest(Request* request)
{
if (request && !m_request)
@@ -104,9 +136,8 @@ void CachedResource::setRequest(Request* request)
delete this;
}
-void CachedResource::ref(CachedResourceClient *c)
+void CachedResource::addClient(CachedResourceClient *c)
{
-#ifdef ANDROID_PRELOAD_CHANGES
if (m_preloadResult == PreloadNotReferenced) {
if (isLoaded())
m_preloadResult = PreloadReferencedWhileComplete;
@@ -115,26 +146,31 @@ void CachedResource::ref(CachedResourceClient *c)
else
m_preloadResult = PreloadReferenced;
}
-#endif
- if (!referenced() && inCache())
+ if (!hasClients() && inCache())
cache()->addToLiveResourcesSize(this);
m_clients.add(c);
}
-void CachedResource::deref(CachedResourceClient *c)
+void CachedResource::removeClient(CachedResourceClient *c)
{
ASSERT(m_clients.contains(c));
m_clients.remove(c);
if (canDelete() && !inCache())
delete this;
- else if (!referenced() && inCache()) {
+ else if (!hasClients() && inCache()) {
cache()->removeFromLiveResourcesSize(this);
cache()->removeFromLiveDecodedResourcesList(this);
- allReferencesRemoved();
+ allClientsRemoved();
cache()->prune();
}
}
+void CachedResource::deleteIfPossible()
+{
+ if (canDelete() && !inCache())
+ delete this;
+}
+
void CachedResource::setDecodedSize(unsigned size)
{
if (size == m_decodedSize)
@@ -155,13 +191,13 @@ void CachedResource::setDecodedSize(unsigned size)
cache()->insertInLRUList(this);
// Insert into or remove from the live decoded list if necessary.
- if (m_decodedSize && !m_inLiveDecodedResourcesList && referenced())
+ if (m_decodedSize && !m_inLiveDecodedResourcesList && hasClients())
cache()->insertInLiveDecodedResourcesList(this);
else if (!m_decodedSize && m_inLiveDecodedResourcesList)
cache()->removeFromLiveDecodedResourcesList(this);
// Update the cache's size totals.
- cache()->adjustSize(referenced(), delta);
+ cache()->adjustSize(hasClients(), delta);
}
}
@@ -188,7 +224,7 @@ void CachedResource::setEncodedSize(unsigned size)
cache()->insertInLRUList(this);
// Update the cache's size totals.
- cache()->adjustSize(referenced(), delta);
+ cache()->adjustSize(hasClients(), delta);
}
}
@@ -204,5 +240,77 @@ void CachedResource::didAccessDecodedData(double timeStamp)
cache()->prune();
}
}
+
+void CachedResource::setResourceToRevalidate(CachedResource* resource)
+{
+ ASSERT(resource);
+ ASSERT(!m_resourceToRevalidate);
+ ASSERT(resource != this);
+ ASSERT(!resource->m_isBeingRevalidated);
+ ASSERT(m_handlesToRevalidate.isEmpty());
+ ASSERT(resource->type() == type());
+ resource->m_isBeingRevalidated = true;
+ m_resourceToRevalidate = resource;
+}
+
+void CachedResource::clearResourceToRevalidate()
+{
+ ASSERT(m_resourceToRevalidate);
+ ASSERT(m_resourceToRevalidate->m_isBeingRevalidated);
+ m_resourceToRevalidate->m_isBeingRevalidated = false;
+ m_resourceToRevalidate->deleteIfPossible();
+ m_handlesToRevalidate.clear();
+ m_resourceToRevalidate = 0;
+ deleteIfPossible();
+}
+
+void CachedResource::switchClientsToRevalidatedResource()
+{
+ ASSERT(m_resourceToRevalidate);
+ ASSERT(!inCache());
+
+ HashSet<CachedResourceHandleBase*>::iterator end = m_handlesToRevalidate.end();
+ for (HashSet<CachedResourceHandleBase*>::iterator it = m_handlesToRevalidate.begin(); it != end; ++it) {
+ CachedResourceHandleBase* handle = *it;
+ handle->m_resource = m_resourceToRevalidate;
+ m_resourceToRevalidate->registerHandle(handle);
+ --m_handleCount;
+ }
+ ASSERT(!m_handleCount);
+ m_handlesToRevalidate.clear();
+
+ Vector<CachedResourceClient*> clientsToMove;
+ HashCountedSet<CachedResourceClient*>::iterator end2 = m_clients.end();
+ for (HashCountedSet<CachedResourceClient*>::iterator it = m_clients.begin(); it != end2; ++it) {
+ CachedResourceClient* client = it->first;
+ unsigned count = it->second;
+ while (count) {
+ clientsToMove.append(client);
+ --count;
+ }
+ }
+ // Equivalent of calling removeClient() for all clients
+ m_clients.clear();
+
+ unsigned moveCount = clientsToMove.size();
+ for (unsigned n = 0; n < moveCount; ++n)
+ m_resourceToRevalidate->addClient(clientsToMove[n]);
+}
+
+bool CachedResource::canUseCacheValidator() const
+{
+ return !m_loading && (!m_response.httpHeaderField("Last-Modified").isEmpty() || !m_response.httpHeaderField("ETag").isEmpty());
+}
+
+bool CachedResource::mustRevalidate(CachePolicy cachePolicy) const
+{
+ if (m_loading)
+ return false;
+ String cacheControl = m_response.httpHeaderField("Cache-Control");
+ // FIXME: It would be better to tokenize the field.
+ if (cachePolicy == CachePolicyCache)
+ return !cacheControl.isEmpty() && (cacheControl.contains("no-cache", false) || (isExpired() && cacheControl.contains("must-revalidate", false)));
+ return isExpired() || cacheControl.contains("no-cache", false);
+}
}
diff --git a/WebCore/loader/CachedResource.h b/WebCore/loader/CachedResource.h
index bbfeac1..c56a889 100644
--- a/WebCore/loader/CachedResource.h
+++ b/WebCore/loader/CachedResource.h
@@ -23,10 +23,12 @@
#ifndef CachedResource_h
#define CachedResource_h
+#include "CachePolicy.h"
#include "PlatformString.h"
#include "ResourceResponse.h"
#include "SharedBuffer.h"
#include <wtf/HashCountedSet.h>
+#include <wtf/HashSet.h>
#include <wtf/Vector.h>
#include <time.h>
@@ -34,6 +36,7 @@ namespace WebCore {
class Cache;
class CachedResourceClient;
+class CachedResourceHandleBase;
class DocLoader;
class Request;
@@ -65,8 +68,11 @@ public:
Cached // regular case
};
- CachedResource(const String& url, Type, bool forCache = true, bool sendResourceLoadCallbacks = false);
+ CachedResource(const String& url, Type);
virtual ~CachedResource();
+
+ virtual void load(DocLoader* docLoader) { load(docLoader, false, false, true); }
+ void load(DocLoader*, bool incremental, bool skipCanLoadCheck, bool sendResourceLoadCallbacks);
virtual void setEncoding(const String&) { }
virtual String encoding() const { return String(); }
@@ -76,11 +82,11 @@ public:
const String &url() const { return m_url; }
Type type() const { return m_type; }
- virtual void ref(CachedResourceClient*);
- void deref(CachedResourceClient*);
- bool referenced() const { return !m_clients.isEmpty(); }
+ virtual void addClient(CachedResourceClient*);
+ void removeClient(CachedResourceClient*);
+ bool hasClients() const { return !m_clients.isEmpty(); }
+ void deleteIfPossible();
-#ifdef ANDROID_PRELOAD_CHANGES
enum PreloadResult {
PreloadNotReferenced,
PreloadReferenced,
@@ -89,9 +95,8 @@ public:
};
PreloadResult preloadResult() const { return m_preloadResult; }
void setRequestedFromNetworkingLayer() { m_requestedFromNetworkingLayer = true; }
-#endif
-
- virtual void allReferencesRemoved() {};
+
+ virtual void allClientsRemoved() { };
unsigned count() const { return m_clients.size(); }
@@ -126,14 +131,10 @@ public:
SharedBuffer* data() const { return m_data.get(); }
- void setResponse(const ResourceResponse& response) { m_response = response; }
+ void setResponse(const ResourceResponse&);
const ResourceResponse& response() const { return m_response; }
-#ifdef ANDROID_PRELOAD_CHANGES
- bool canDelete() const { return !referenced() && !m_request && !m_preloadCount; }
-#else
- bool canDelete() const { return !referenced() && !m_request; }
-#endif
+ bool canDelete() const { return !hasClients() && !m_request && !m_preloadCount && !m_handleCount && !m_resourceToRevalidate && !m_isBeingRevalidated; }
bool isExpired() const;
@@ -145,24 +146,29 @@ public:
void setAccept(const String& accept) { m_accept = accept; }
bool errorOccurred() const { return m_errorOccurred; }
- bool treatAsLocal() const { return m_shouldTreatAsLocal; }
bool sendResourceLoadCallbacks() const { return m_sendResourceLoadCallbacks; }
virtual void destroyDecodedData() {};
void setDocLoader(DocLoader* docLoader) { m_docLoader = docLoader; }
-
-#ifdef ANDROID_PRELOAD_CHANGES
+
bool isPreloaded() const { return m_preloadCount; }
void increasePreloadCount() { ++m_preloadCount; }
void decreasePreloadCount() { ASSERT(m_preloadCount); --m_preloadCount; }
-#endif
-
- protected:
+
+ void registerHandle(CachedResourceHandleBase* h) { ++m_handleCount; if (m_resourceToRevalidate) m_handlesToRevalidate.add(h); }
+ void unregisterHandle(CachedResourceHandleBase* h) { --m_handleCount; if (m_resourceToRevalidate) m_handlesToRevalidate.remove(h); if (!m_handleCount) deleteIfPossible(); }
+
+ bool canUseCacheValidator() const;
+ bool mustRevalidate(CachePolicy) const;
+ bool isCacheValidator() const { return m_resourceToRevalidate; }
+ CachedResource* resourceToRevalidate() const { return m_resourceToRevalidate; }
+
+protected:
void setEncodedSize(unsigned);
void setDecodedSize(unsigned);
void didAccessDecodedData(double timeStamp);
-
+
HashCountedSet<CachedResourceClient*> m_clients;
String m_url;
@@ -178,6 +184,12 @@ public:
bool m_errorOccurred;
private:
+ // These are called by the friendly Cache only
+ void setResourceToRevalidate(CachedResource*);
+ void switchClientsToRevalidatedResource();
+ void clearResourceToRevalidate();
+ void setExpirationDate(time_t expirationDate) { m_expirationDate = expirationDate; }
+
unsigned m_encodedSize;
unsigned m_decodedSize;
unsigned m_accessCount;
@@ -185,12 +197,10 @@ private:
double m_lastDecodedAccessTime; // Used as a "thrash guard" in the cache
bool m_sendResourceLoadCallbacks;
-
-#ifdef ANDROID_PRELOAD_CHANGES
+
unsigned m_preloadCount;
PreloadResult m_preloadResult;
bool m_requestedFromNetworkingLayer;
-#endif
protected:
bool m_inCache;
@@ -208,9 +218,19 @@ private:
CachedResource* m_nextInLiveResourcesList;
CachedResource* m_prevInLiveResourcesList;
- bool m_shouldTreatAsLocal;
-
DocLoader* m_docLoader; // only non-0 for resources that are not in the cache
+
+ unsigned m_handleCount;
+ // If this field is non-null we are using the resource as a proxy for checking whether an existing resource is still up to date
+ // using HTTP If-Modified-Since/If-None-Match headers. If the response is 304 all clients of this resource are moved
+ // to to be clients of m_resourceToRevalidate and the resource is deleted. If not, the field is zeroed and this
+ // resources becomes normal resource load.
+ CachedResource* m_resourceToRevalidate;
+ bool m_isBeingRevalidated;
+ // These handles will need to be updated to point to the m_resourceToRevalidate in case we get 304 response.
+ HashSet<CachedResourceHandleBase*> m_handlesToRevalidate;
+
+ time_t m_expirationDate;
};
}
diff --git a/WebCore/loader/CachedResourceClient.h b/WebCore/loader/CachedResourceClient.h
index 48631e3..1d8d45e 100644
--- a/WebCore/loader/CachedResourceClient.h
+++ b/WebCore/loader/CachedResourceClient.h
@@ -35,6 +35,7 @@ namespace XBL {
namespace WebCore {
+ class CachedCSSStyleSheet;
class CachedFont;
class CachedResource;
class CachedImage;
@@ -64,7 +65,7 @@ namespace WebCore {
// e.g., in the b/f cache or in a background tab).
virtual bool willRenderImage(CachedImage*) { return false; }
- virtual void setCSSStyleSheet(const String& /*URL*/, const String& /*charset*/, const String& /*sheet*/) { }
+ virtual void setCSSStyleSheet(const String& /*URL*/, const String& /*charset*/, const CachedCSSStyleSheet*) { }
virtual void setXSLStyleSheet(const String& /*URL*/, const String& /*sheet*/) { }
virtual void fontLoaded(CachedFont*) {};
diff --git a/WebCore/loader/android/DocumentLoaderAndroid.cpp b/WebCore/loader/CachedResourceHandle.cpp
index f402495..871292c 100644
--- a/WebCore/loader/android/DocumentLoaderAndroid.cpp
+++ b/WebCore/loader/CachedResourceHandle.cpp
@@ -1,8 +1,5 @@
/*
- * Copyright (C) 2006 Zack Rusin <zack@kde.org>
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
- *
- * All rights reserved.
+ * 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
@@ -13,38 +10,33 @@
* 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 COMPUTER, INC. ``AS IS'' AND ANY
+ * 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 COMPUTER, INC. OR
+ * 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.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
-#include "DocumentLoader.h"
-
-#include "KURL.h"
-#include <stdio.h>
-
-#define LOG_TAG "WebCore"
-#undef LOG
-#include <utils/Log.h>
+#include "CachedResourceHandle.h"
namespace WebCore {
-#if 0 // FIXME: no longer defined
-bool DocumentLoader::getResponseModifiedHeader(String& modified) const
+void CachedResourceHandleBase::setResource(CachedResource* resource)
{
- LOGV("--- unimpl %s\n", __PRETTY_FUNCTION__);
- return false;
+ if (resource == m_resource)
+ return;
+ if (m_resource)
+ m_resource->unregisterHandle(this);
+ m_resource = resource;
+ if (m_resource)
+ m_resource->registerHandle(this);
}
-#endif
}
-
diff --git a/WebCore/loader/CachedResourceHandle.h b/WebCore/loader/CachedResourceHandle.h
new file mode 100644
index 0000000..13c03c7
--- /dev/null
+++ b/WebCore/loader/CachedResourceHandle.h
@@ -0,0 +1,92 @@
+/*
+ * 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 CachedResourceHandle_h
+#define CachedResourceHandle_h
+
+#include "CachedResource.h"
+
+namespace WebCore {
+
+ class CachedResourceHandleBase {
+ public:
+ ~CachedResourceHandleBase() { if (m_resource) m_resource->unregisterHandle(this); }
+ CachedResource* get() const { return m_resource; }
+
+ bool operator!() const { return !m_resource; }
+
+ // This conversion operator allows implicit conversion to bool but not to other integer types.
+ typedef CachedResource* CachedResourceHandleBase::*UnspecifiedBoolType;
+ operator UnspecifiedBoolType() const { return m_resource ? &CachedResourceHandleBase::m_resource : 0; }
+
+ protected:
+ CachedResourceHandleBase() : m_resource(0) {}
+ CachedResourceHandleBase(CachedResource* res) { m_resource = res; if (m_resource) m_resource->registerHandle(this); }
+ CachedResourceHandleBase(const CachedResourceHandleBase& o) : m_resource(o.m_resource) { if (m_resource) m_resource->registerHandle(this); }
+
+ void setResource(CachedResource*);
+
+ private:
+ CachedResourceHandleBase& operator=(const CachedResourceHandleBase&) { return *this; }
+
+ friend class CachedResource;
+
+ CachedResource* m_resource;
+ };
+
+ template <class R> class CachedResourceHandle : public CachedResourceHandleBase {
+ public:
+ CachedResourceHandle() { }
+ CachedResourceHandle(R* res) : CachedResourceHandleBase(res) { }
+ CachedResourceHandle(const CachedResourceHandle<R>& o) : CachedResourceHandleBase(o) { }
+
+ R* get() const { return reinterpret_cast<R*>(CachedResourceHandleBase::get()); }
+ R* operator->() const { return get(); }
+
+ CachedResourceHandle& operator=(R* res) { setResource(res); return *this; }
+ CachedResourceHandle& operator=(const CachedResourceHandle& o) { setResource(o.get()); return *this; }
+ bool operator==(const CachedResourceHandleBase& o) const { return get() == o.get(); }
+ bool operator!=(const CachedResourceHandleBase& o) const { return get() != o.get(); }
+ };
+
+ template <class R, class RR> bool operator==(const CachedResourceHandle<R>& h, const RR* res)
+ {
+ return h.get() == res;
+ }
+ template <class R, class RR> bool operator==(const RR* res, const CachedResourceHandle<R>& h)
+ {
+ return h.get() == res;
+ }
+ template <class R, class RR> bool operator!=(const CachedResourceHandle<R>& h, const RR* res)
+ {
+ return h.get() != res;
+ }
+ template <class R, class RR> bool operator!=(const RR* res, const CachedResourceHandle<R>& h)
+ {
+ return h.get() != res;
+ }
+}
+
+#endif
diff --git a/WebCore/loader/CachedScript.cpp b/WebCore/loader/CachedScript.cpp
index f8cde0e..9666eea 100644
--- a/WebCore/loader/CachedScript.cpp
+++ b/WebCore/loader/CachedScript.cpp
@@ -29,15 +29,13 @@
#include "config.h"
#include "CachedScript.h"
-#include "Cache.h"
#include "CachedResourceClient.h"
#include "CachedResourceClientWalker.h"
-#include "loader.h"
#include <wtf/Vector.h>
namespace WebCore {
-CachedScript::CachedScript(DocLoader* dl, const String& url, const String& charset)
+CachedScript::CachedScript(const String& url, const String& charset)
: CachedResource(url, Script)
, m_encoding(charset)
{
@@ -45,9 +43,6 @@ CachedScript::CachedScript(DocLoader* dl, const String& url, const String& chars
// But some websites think their scripts are <some wrong mimetype here>
// and refuse to serve them if we only accept application/x-javascript.
setAccept("*/*");
- // load the file
- cache()->loader()->load(dl, this, false);
- m_loading = true;
if (!m_encoding.isValid())
m_encoding = Latin1Encoding();
}
@@ -56,9 +51,9 @@ CachedScript::~CachedScript()
{
}
-void CachedScript::ref(CachedResourceClient* c)
+void CachedScript::addClient(CachedResourceClient* c)
{
- CachedResource::ref(c);
+ CachedResource::addClient(c);
if (!m_loading)
c->notifyFinished(this);
}
diff --git a/WebCore/loader/CachedScript.h b/WebCore/loader/CachedScript.h
index 06e0376..4580cfe 100644
--- a/WebCore/loader/CachedScript.h
+++ b/WebCore/loader/CachedScript.h
@@ -37,12 +37,12 @@ namespace WebCore {
class CachedScript : public CachedResource {
public:
- CachedScript(DocLoader*, const String& url, const String& charset);
+ CachedScript(const String& url, const String& charset);
virtual ~CachedScript();
const String& script() const { return m_script; }
- virtual void ref(CachedResourceClient*);
+ virtual void addClient(CachedResourceClient*);
virtual void setEncoding(const String&);
virtual String encoding() const;
diff --git a/WebCore/loader/CachedXBLDocument.cpp b/WebCore/loader/CachedXBLDocument.cpp
index da603a9..1ef3bae 100644
--- a/WebCore/loader/CachedXBLDocument.cpp
+++ b/WebCore/loader/CachedXBLDocument.cpp
@@ -32,23 +32,18 @@
#include "CachedXBLDocument.h"
-#include "Cache.h"
#include "CachedResourceClientWalker.h"
#include "TextResourceDecoder.h"
-#include "loader.h"
#include <wtf/Vector.h>
namespace WebCore {
-CachedXBLDocument::CachedXBLDocument(DocLoader* dl, const String &url)
+CachedXBLDocument::CachedXBLDocument(const String &url)
: CachedResource(url, XBL), m_document(0)
{
// It's XML we want.
setAccept("text/xml, application/xml, application/xhtml+xml, text/xsl, application/rss+xml, application/atom+xml");
-
- // Load the file
- Cache::loader()->load(dl, this, false);
- m_loading = true;
+
m_decoder = new TextResourceDecoder("application/xml");
}
diff --git a/WebCore/loader/CachedXBLDocument.h b/WebCore/loader/CachedXBLDocument.h
index 692f20b..a66a0cb 100644
--- a/WebCore/loader/CachedXBLDocument.h
+++ b/WebCore/loader/CachedXBLDocument.h
@@ -41,12 +41,12 @@ namespace WebCore {
#if ENABLE(XBL)
class CachedXBLDocument : public CachedResource {
public:
- CachedXBLDocument(DocLoader*, const String& url);
+ CachedXBLDocument(const String& url);
virtual ~CachedXBLDocument();
XBL::XBLDocument* document() const { return m_document; }
- virtual void ref(CachedResourceClient*);
+ virtual void addClient(CachedResourceClient*);
virtual void setEncoding(const String&);
virtual String encoding() const;
diff --git a/WebCore/loader/CachedXSLStyleSheet.cpp b/WebCore/loader/CachedXSLStyleSheet.cpp
index 0836fb5..009b2af 100644
--- a/WebCore/loader/CachedXSLStyleSheet.cpp
+++ b/WebCore/loader/CachedXSLStyleSheet.cpp
@@ -29,33 +29,27 @@
#include "config.h"
#include "CachedXSLStyleSheet.h"
-#include "Cache.h"
#include "CachedResourceClient.h"
#include "CachedResourceClientWalker.h"
#include "TextResourceDecoder.h"
-#include "loader.h"
#include <wtf/Vector.h>
namespace WebCore {
#if ENABLE(XSLT)
-CachedXSLStyleSheet::CachedXSLStyleSheet(DocLoader* dl, const String &url)
+CachedXSLStyleSheet::CachedXSLStyleSheet(const String &url)
: CachedResource(url, XSLStyleSheet)
- , m_decoder(new TextResourceDecoder("text/xsl"))
+ , m_decoder(TextResourceDecoder::create("text/xsl"))
{
// It's XML we want.
// FIXME: This should accept more general xml formats */*+xml, image/svg+xml for example.
setAccept("text/xml, application/xml, application/xhtml+xml, text/xsl, application/rss+xml, application/atom+xml");
-
- // load the file
- cache()->loader()->load(dl, this, false);
- m_loading = true;
}
-void CachedXSLStyleSheet::ref(CachedResourceClient *c)
+void CachedXSLStyleSheet::addClient(CachedResourceClient *c)
{
- CachedResource::ref(c);
+ CachedResource::addClient(c);
if (!m_loading)
c->setXSLStyleSheet(m_url, m_sheet);
diff --git a/WebCore/loader/CachedXSLStyleSheet.h b/WebCore/loader/CachedXSLStyleSheet.h
index cddf840..c5326fa 100644
--- a/WebCore/loader/CachedXSLStyleSheet.h
+++ b/WebCore/loader/CachedXSLStyleSheet.h
@@ -39,11 +39,11 @@ namespace WebCore {
#if ENABLE(XSLT)
class CachedXSLStyleSheet : public CachedResource {
public:
- CachedXSLStyleSheet(DocLoader*, const String& url);
+ CachedXSLStyleSheet(const String& url);
const String& sheet() const { return m_sheet; }
- virtual void ref(CachedResourceClient*);
+ virtual void addClient(CachedResourceClient*);
virtual void setEncoding(const String&);
virtual String encoding() const;
diff --git a/WebCore/loader/DocLoader.cpp b/WebCore/loader/DocLoader.cpp
index e2a2517..4dbc6a0 100644
--- a/WebCore/loader/DocLoader.cpp
+++ b/WebCore/loader/DocLoader.cpp
@@ -1,10 +1,8 @@
/*
- This file is part of the KDE libraries
-
Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
- Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
+ Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -34,19 +32,23 @@
#include "CachedImage.h"
#include "CachedScript.h"
#include "CachedXSLStyleSheet.h"
+#include "Console.h"
+#include "CString.h"
#include "Document.h"
+#include "DOMWindow.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "loader.h"
+#include "SecurityOrigin.h"
+#include "Settings.h"
#define PRELOAD_DEBUG 0
namespace WebCore {
-DocLoader::DocLoader(Frame *frame, Document* doc)
+DocLoader::DocLoader(Document* doc)
: m_cache(cache())
, m_cachePolicy(CachePolicyVerify)
- , m_frame(frame)
, m_doc(doc)
, m_requestCount(0)
#ifdef ANDROID_BLOCK_NETWORK_IMAGE
@@ -61,51 +63,49 @@ DocLoader::DocLoader(Frame *frame, Document* doc)
DocLoader::~DocLoader()
{
-#ifdef ANDROID_PRELOAD_CHANGES
clearPreloads();
-#endif
HashMap<String, CachedResource*>::iterator end = m_docResources.end();
for (HashMap<String, CachedResource*>::iterator it = m_docResources.begin(); it != end; ++it)
it->second->setDocLoader(0);
m_cache->removeDocLoader(this);
}
+Frame* DocLoader::frame() const
+{
+ return m_doc->frame();
+}
+
void DocLoader::checkForReload(const KURL& fullURL)
{
if (m_allowStaleResources)
- return; //Don't reload resources while pasting
- if (m_cachePolicy == CachePolicyVerify) {
+ return; // Don't reload resources while pasting
+
+ if (fullURL.isEmpty())
+ return;
+
+ if (m_cachePolicy == CachePolicyVerify || m_cachePolicy == CachePolicyCache) {
if (!m_reloadedURLs.contains(fullURL.string())) {
CachedResource* existing = cache()->resourceForURL(fullURL.string());
-#ifdef ANDROID_PRELOAD_CHANGES
- if (existing && existing->isExpired() && !existing->isPreloaded()) {
-#else
- if (existing && existing->isExpired()) {
-#endif
- cache()->remove(existing);
- m_reloadedURLs.add(fullURL.string());
+ if (existing && !existing->isPreloaded() && existing->mustRevalidate(m_cachePolicy)) {
+ cache()->revalidateResource(existing, this);
+ m_reloadedURLs.add(fullURL.string());
}
}
} else if ((m_cachePolicy == CachePolicyReload) || (m_cachePolicy == CachePolicyRefresh)) {
if (!m_reloadedURLs.contains(fullURL.string())) {
- CachedResource* existing = cache()->resourceForURL(fullURL.string());
-#ifdef ANDROID_PRELOAD_CHANGES
- if (existing && !existing->isPreloaded()) {
-#else
- if (existing)
-#endif
- cache()->remove(existing);
- m_reloadedURLs.add(fullURL.string());
-#ifdef ANDROID_PRELOAD_CHANGES
- }
-#endif
+ CachedResource* existing = cache()->resourceForURL(fullURL.string());
+ if (existing && !existing->isPreloaded()) {
+ // FIXME: Use revalidateResource() to implement HTTP 1.1 "Specific end-to-end revalidation" for regular reloading
+ cache()->remove(existing);
+ m_reloadedURLs.add(fullURL.string());
+ }
}
}
}
CachedImage* DocLoader::requestImage(const String& url)
{
- CachedImage* resource = static_cast<CachedImage*>(requestResource(CachedResource::ImageResource, url));
+ CachedImage* resource = static_cast<CachedImage*>(requestResource(CachedResource::ImageResource, url, String()));
if (autoLoadImages() && resource && resource->stillNeedsLoad()) {
#ifdef ANDROID_BLOCK_NETWORK_IMAGE
if (shouldBlockNetworkImage(url)) {
@@ -120,57 +120,78 @@ CachedImage* DocLoader::requestImage(const String& url)
CachedFont* DocLoader::requestFont(const String& url)
{
- return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, url));
+ return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, url, String()));
}
-CachedCSSStyleSheet* DocLoader::requestCSSStyleSheet(const String& url, const String& charset, bool isUserStyleSheet)
+CachedCSSStyleSheet* DocLoader::requestCSSStyleSheet(const String& url, const String& charset)
{
- // FIXME: Passing true for "skipCanLoadCheck" here in the isUserStyleSheet case won't have any effect
- // if this resource is already in the cache. It's theoretically possible that what's in the cache already
- // is a load that failed because of the canLoad check. Probably not an issue in practice.
- CachedCSSStyleSheet *sheet = static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, url, &charset, isUserStyleSheet, !isUserStyleSheet));
-
- // A user style sheet can outlive its DocLoader so don't store any pointers to it
- if (sheet && isUserStyleSheet) {
- sheet->setDocLoader(0);
- m_docResources.remove(sheet->url());
- }
-
- return sheet;
+ return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, url, charset));
}
CachedCSSStyleSheet* DocLoader::requestUserCSSStyleSheet(const String& url, const String& charset)
{
- return requestCSSStyleSheet(url, charset, true);
+ return cache()->requestUserCSSStyleSheet(this, url, charset);
}
CachedScript* DocLoader::requestScript(const String& url, const String& charset)
{
- return static_cast<CachedScript*>(requestResource(CachedResource::Script, url, &charset));
+ return static_cast<CachedScript*>(requestResource(CachedResource::Script, url, charset));
}
#if ENABLE(XSLT)
CachedXSLStyleSheet* DocLoader::requestXSLStyleSheet(const String& url)
{
- return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, url));
+ return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, url, String()));
}
#endif
#if ENABLE(XBL)
CachedXBLDocument* DocLoader::requestXBLDocument(const String& url)
{
- return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XBL, url));
+ return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XBL, url, String()));
}
#endif
-#ifdef ANDROID_PRELOAD_CHANGES
-CachedResource* DocLoader::requestResource(CachedResource::Type type, const String& url, const String* charset, bool skipCanLoadCheck, bool sendResourceLoadCallbacks, bool isPreload)
-#else
-CachedResource* DocLoader::requestResource(CachedResource::Type type, const String& url, const String* charset, bool skipCanLoadCheck, bool sendResourceLoadCallbacks)
+bool DocLoader::canRequest(CachedResource::Type type, const KURL& url)
+{
+ // Some types of resources can be loaded only from the same origin. Other
+ // types of resources, like Images, Scripts, and CSS, can be loaded from
+ // any URL.
+ switch (type) {
+ case CachedResource::ImageResource:
+ case CachedResource::CSSStyleSheet:
+ case CachedResource::Script:
+ case CachedResource::FontResource:
+ // These types of resources can be loaded from any origin.
+ // FIXME: Are we sure about CachedResource::FontResource?
+ break;
+#if ENABLE(XSLT)
+ case CachedResource::XSLStyleSheet:
+#endif
+#if ENABLE(XBL)
+ case CachedResource::XBL:
+#endif
+#if ENABLE(XSLT) || ENABLE(XBL)
+ if (!m_doc->securityOrigin()->canRequest(url)) {
+ printAccessDeniedMessage(url);
+ return false;
+ }
+ break;
#endif
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ return true;
+}
+
+CachedResource* DocLoader::requestResource(CachedResource::Type type, const String& url, const String& charset, bool isPreload)
{
- KURL fullURL = m_doc->completeURL(url.deprecatedString());
-
+ KURL fullURL = m_doc->completeURL(url);
+
+ if (!canRequest(type, fullURL))
+ return 0;
+
if (cache()->disabled()) {
HashMap<String, CachedResource*>::iterator it = m_docResources.find(fullURL.string());
@@ -179,23 +200,48 @@ CachedResource* DocLoader::requestResource(CachedResource::Type type, const Stri
m_docResources.remove(it);
}
}
-
- if (m_frame && m_frame->loader()->isReloading())
+
+ if (frame() && frame()->loader()->isReloading())
setCachePolicy(CachePolicyReload);
checkForReload(fullURL);
-#ifdef ANDROID_PRELOAD_CHANGES
- CachedResource* resource = cache()->requestResource(this, type, fullURL, charset, skipCanLoadCheck, sendResourceLoadCallbacks, isPreload);
-#else
- CachedResource* resource = cache()->requestResource(this, type, fullURL, charset, skipCanLoadCheck, sendResourceLoadCallbacks);
-#endif
+ CachedResource* resource = cache()->requestResource(this, type, fullURL, charset, isPreload);
if (resource) {
+ // Check final URL of resource to catch redirects.
+ // See <https://bugs.webkit.org/show_bug.cgi?id=21963>.
+ if (!canRequest(type, KURL(resource->url())))
+ return 0;
+
m_docResources.set(resource->url(), resource);
checkCacheObjectStatus(resource);
}
return resource;
}
+void DocLoader::printAccessDeniedMessage(const KURL& url) const
+{
+ if (url.isNull())
+ return;
+
+ if (!frame())
+ return;
+
+ Settings* settings = frame()->settings();
+ if (!settings || settings->privateBrowsingEnabled())
+ return;
+
+ String message = m_doc->url().isNull() ?
+ String::format("Unsafe attempt to load URL %s.",
+ url.string().utf8().data()) :
+ String::format("Unsafe attempt to load URL %s from frame with URL %s. "
+ "Domains, protocols and ports must match.\n",
+ url.string().utf8().data(),
+ m_doc->url().string().utf8().data());
+
+ // FIXME: provide a real line number and source URL.
+ frame()->domWindow()->console()->addMessage(OtherMessageSource, ErrorMessageLevel, message, 1, String());
+}
+
void DocLoader::setAutoLoadImages(bool enable)
{
if (enable == m_autoLoadImages)
@@ -228,7 +274,7 @@ bool DocLoader::shouldBlockNetworkImage(const String& url) const
if (!m_blockNetworkImage)
return false;
- KURL kurl(url.deprecatedString());
+ KURL kurl(url);
if (kurl.protocolIs("http") || kurl.protocolIs("https"))
return true;
@@ -270,14 +316,14 @@ void DocLoader::removeCachedResource(CachedResource* resource) const
void DocLoader::setLoadInProgress(bool load)
{
m_loadInProgress = load;
- if (!load && m_frame)
- m_frame->loader()->loadDone();
+ if (!load && frame())
+ frame()->loader()->loadDone();
}
void DocLoader::checkCacheObjectStatus(CachedResource* resource)
{
// Return from the function for objects that we didn't load from the cache or if we don't have a frame.
- if (!resource || !m_frame)
+ if (!resource || !frame())
return;
switch (resource->status()) {
@@ -291,7 +337,7 @@ void DocLoader::checkCacheObjectStatus(CachedResource* resource)
}
// FIXME: If the WebKit client changes or cancels the request, WebCore does not respect this and continues the load.
- m_frame->loader()->loadedResourceFromMemoryCache(resource);
+ frame()->loader()->loadedResourceFromMemoryCache(resource);
}
void DocLoader::incrementRequestCount()
@@ -311,38 +357,39 @@ int DocLoader::requestCount()
return m_requestCount + 1;
return m_requestCount;
}
-
-#ifdef ANDROID_PRELOAD_CHANGES
-void DocLoader::preload(CachedResource::Type type, const String& url, const String& charset, bool inBody)
+
+void DocLoader::preload(CachedResource::Type type, const String& url, const String& charset, bool referencedFromBody)
{
- if ((inBody || type == CachedResource::ImageResource) && (!m_doc->body() || !m_doc->body()->renderer())) {
- // Don't preload images or body resources before we have the first rendering.
+ bool hasRendering = m_doc->body() && m_doc->body()->renderer();
+ if (!hasRendering && (referencedFromBody || type == CachedResource::ImageResource)) {
+ // Don't preload images or body resources before we have something to draw. This prevents
+ // preloads from body delaying first display when bandwidth is limited.
PendingPreload pendingPreload = { type, url, charset };
m_pendingPreloads.append(pendingPreload);
return;
}
requestPreload(type, url, charset);
}
-
+
void DocLoader::checkForPendingPreloads()
{
unsigned count = m_pendingPreloads.size();
if (!count || !m_doc->body() || !m_doc->body()->renderer())
return;
- for (unsigned n = 0; n < count; ++n) {
- PendingPreload& preload = m_pendingPreloads[n];
+ for (unsigned i = 0; i < count; ++i) {
+ PendingPreload& preload = m_pendingPreloads[i];
requestPreload(preload.m_type, preload.m_url, preload.m_charset);
}
m_pendingPreloads.clear();
}
-
+
void DocLoader::requestPreload(CachedResource::Type type, const String& url, const String& charset)
{
String encoding;
if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet)
encoding = charset.isEmpty() ? m_doc->frame()->loader()->encoding() : charset;
- CachedResource* resource = requestResource(type, url, &encoding, false, true, true);
+ CachedResource* resource = requestResource(type, url, encoding, true);
if (!resource || m_preloads.contains(resource))
return;
resource->increasePreloadCount();
@@ -417,5 +464,5 @@ void DocLoader::printPreloadStats()
printf("IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
}
#endif
-#endif // ANDROID_PRELOAD_CHANGES
+
}
diff --git a/WebCore/loader/DocLoader.h b/WebCore/loader/DocLoader.h
index 31fb5f8..407d85a 100644
--- a/WebCore/loader/DocLoader.h
+++ b/WebCore/loader/DocLoader.h
@@ -30,9 +30,7 @@
#include "StringHash.h"
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
-#ifdef ANDROID_PRELOAD_CHANGES
#include <wtf/ListHashSet.h>
-#endif
namespace WebCore {
@@ -43,21 +41,21 @@ class CachedScript;
class CachedXSLStyleSheet;
class Document;
class Frame;
-class HTMLImageLoader;
+class ImageLoader;
class KURL;
// The DocLoader manages the loading of scripts/images/stylesheets for a single document.
class DocLoader
{
friend class Cache;
-friend class HTMLImageLoader;
+friend class ImageLoader;
public:
- DocLoader(Frame*, Document*);
+ DocLoader(Document*);
~DocLoader();
CachedImage* requestImage(const String& url);
- CachedCSSStyleSheet* requestCSSStyleSheet(const String& url, const String& charset, bool isUserStyleSheet = false);
+ CachedCSSStyleSheet* requestCSSStyleSheet(const String& url, const String& charset);
CachedCSSStyleSheet* requestUserCSSStyleSheet(const String& url, const String& charset);
CachedScript* requestScript(const String& url, const String& charset);
CachedFont* requestFont(const String& url);
@@ -69,6 +67,9 @@ public:
CachedXBLDocument* requestXBLDocument(const String &url);
#endif
+ // Logs an access denied message to the console for the specified URL.
+ void printAccessDeniedMessage(const KURL& url) const;
+
CachedResource* cachedResource(const String& url) const { return m_docResources.get(url); }
const HashMap<String, CachedResource*>& allCachedResources() const { return m_docResources; }
@@ -84,7 +85,7 @@ public:
CachePolicy cachePolicy() const { return m_cachePolicy; }
void setCachePolicy(CachePolicy);
- Frame* frame() const { return m_frame; }
+ Frame* frame() const; // Can be NULL
Document* doc() const { return m_doc; }
void removeCachedResource(CachedResource*) const;
@@ -101,33 +102,28 @@ public:
void incrementRequestCount();
void decrementRequestCount();
int requestCount();
-
-#ifdef ANDROID_PRELOAD_CHANGES
+
void clearPreloads();
- void preload(CachedResource::Type type, const String& url, const String& charset, bool inBody);
+ void preload(CachedResource::Type, const String& url, const String& charset, bool referencedFromBody);
void checkForPendingPreloads();
void printPreloadStats();
-#endif
+
private:
-#ifdef ANDROID_PRELOAD_CHANGES
- CachedResource* requestResource(CachedResource::Type, const String& url, const String* charset = 0, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true, bool isPreload = false);
- void requestPreload(CachedResource::Type type, const String& url, const String& charset);
-#else
- CachedResource* requestResource(CachedResource::Type, const String& url, const String* charset = 0, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true);
-#endif
+ CachedResource* requestResource(CachedResource::Type, const String& url, const String& charset, bool isPreload = false);
+ void requestPreload(CachedResource::Type, const String& url, const String& charset);
+
void checkForReload(const KURL&);
void checkCacheObjectStatus(CachedResource*);
+ bool canRequest(CachedResource::Type, const KURL&);
Cache* m_cache;
HashSet<String> m_reloadedURLs;
mutable HashMap<String, CachedResource*> m_docResources;
CachePolicy m_cachePolicy;
- Frame* m_frame;
- Document *m_doc;
+ Document* m_doc;
int m_requestCount;
-#ifdef ANDROID_PRELOAD_CHANGES
ListHashSet<CachedResource*> m_preloads;
struct PendingPreload {
CachedResource::Type m_type;
@@ -135,7 +131,6 @@ private:
String m_charset;
};
Vector<PendingPreload> m_pendingPreloads;
-#endif
//29 bits left
#ifdef ANDROID_BLOCK_NETWORK_IMAGE
diff --git a/WebCore/loader/DocumentLoader.cpp b/WebCore/loader/DocumentLoader.cpp
index b90ead6..d9d99ea 100644
--- a/WebCore/loader/DocumentLoader.cpp
+++ b/WebCore/loader/DocumentLoader.cpp
@@ -29,21 +29,34 @@
#include "config.h"
#include "DocumentLoader.h"
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+#include "ApplicationCache.h"
+#include "ApplicationCacheGroup.h"
+#include "ApplicationCacheResource.h"
+#endif
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+#include "ArchiveFactory.h"
+#include "ArchiveResourceCollection.h"
+#else
+#include "SubstituteResource.h"
+#endif
#include "CachedPage.h"
-#ifdef ANDROID_PRELOAD_CHANGES
#include "DocLoader.h"
-#endif
#include "Document.h"
#include "Event.h"
#include "Frame.h"
#include "FrameLoader.h"
+#include "FrameTree.h"
#include "HistoryItem.h"
#include "Logging.h"
#include "MainResourceLoader.h"
+#include "Page.h"
#include "PlatformString.h"
+#include "Settings.h"
#include "SharedBuffer.h"
#include "StringBuffer.h"
#include "XMLTokenizer.h"
+
#include <wtf/Assertions.h>
#include <wtf/unicode/Unicode.h>
@@ -140,6 +153,10 @@ DocumentLoader::DocumentLoader(const ResourceRequest& req, const SubstituteData&
, m_isClientRedirect(false)
, m_loadingFromCachedPage(false)
, m_stopRecordingResponses(false)
+ , m_substituteResourceDeliveryTimer(this, &DocumentLoader::substituteResourceDeliveryTimerFired)
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ , m_candidateApplicationCacheGroup(0)
+#endif
{
}
@@ -153,6 +170,13 @@ FrameLoader* DocumentLoader::frameLoader() const
DocumentLoader::~DocumentLoader()
{
ASSERT(!m_frame || frameLoader()->activeDocumentLoader() != this || !frameLoader()->isLoading());
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ if (m_applicationCache)
+ m_applicationCache->group()->documentLoaderDestroyed(this);
+ else if (m_candidateApplicationCacheGroup)
+ m_candidateApplicationCacheGroup->documentLoaderDestroyed(this);
+#endif
}
PassRefPtr<SharedBuffer> DocumentLoader::mainResourceData() const
@@ -184,16 +208,6 @@ ResourceRequest& DocumentLoader::request()
return m_request;
}
-const ResourceRequest& DocumentLoader::initialRequest() const
-{
- return m_originalRequest;
-}
-
-ResourceRequest& DocumentLoader::actualRequest()
-{
- return m_request;
-}
-
const KURL& DocumentLoader::url() const
{
return request().url();
@@ -243,6 +257,15 @@ void DocumentLoader::clearErrors()
void DocumentLoader::mainReceivedError(const ResourceError& error, bool isComplete)
{
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ ApplicationCacheGroup* group = m_candidateApplicationCacheGroup;
+ if (!group && m_applicationCache && !mainResourceApplicationCache())
+ group = m_applicationCache->group();
+
+ if (group)
+ group->failedLoadingMainResource(this);
+#endif
+
if (!frameLoader())
return;
setMainDocumentError(error);
@@ -325,26 +348,6 @@ void DocumentLoader::finishedLoading()
}
}
-void DocumentLoader::setCommitted(bool f)
-{
- m_committed = f;
-}
-
-bool DocumentLoader::isCommitted() const
-{
- return m_committed;
-}
-
-void DocumentLoader::setLoading(bool f)
-{
- m_loading = f;
-}
-
-bool DocumentLoader::isLoading() const
-{
- return m_loading;
-}
-
void DocumentLoader::commitLoad(const char* data, int length)
{
// Both unloading the old page and parsing the new page may execute JavaScript which destroys the datasource
@@ -395,8 +398,9 @@ void DocumentLoader::setupForReplaceByMIMEType(const String& newMIMEType)
stopLoadingSubresources();
stopLoadingPlugIns();
-
- frameLoader()->finalSetupForReplace(this);
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ clearArchiveResources();
+#endif
}
void DocumentLoader::updateLoading()
@@ -437,22 +441,15 @@ void DocumentLoader::prepareForLoadStart()
frameLoader()->prepareForLoadStart();
}
-void DocumentLoader::setIsClientRedirect(bool flag)
-{
- m_isClientRedirect = flag;
-}
-
-bool DocumentLoader::isClientRedirect() const
-{
- return m_isClientRedirect;
-}
-
void DocumentLoader::setPrimaryLoadComplete(bool flag)
{
m_primaryLoadComplete = flag;
if (flag) {
if (m_mainResourceLoader) {
m_mainResourceData = m_mainResourceLoader->resourceData();
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ m_mainResourceApplicationCache = m_mainResourceLoader->applicationCache();
+#endif
m_mainResourceLoader = 0;
}
updateLoading();
@@ -469,10 +466,8 @@ bool DocumentLoader::isLoadingInAPISense() const
if (!m_subresourceLoaders.isEmpty())
return true;
if (Document* doc = m_frame->document()) {
-#ifdef ANDROID_PRELOAD_CHANGES
if (doc->docLoader()->requestCount())
return true;
-#endif
if (Tokenizer* tok = doc->tokenizer())
if (tok->processingData())
return true;
@@ -481,55 +476,200 @@ bool DocumentLoader::isLoadingInAPISense() const
return frameLoader()->subframeIsLoading();
}
-void DocumentLoader::addResponse(const ResourceResponse& r)
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+void DocumentLoader::addAllArchiveResources(Archive* archive)
{
- if (!m_stopRecordingResponses)
- m_responses.append(r);
+ if (!m_archiveResourceCollection)
+ m_archiveResourceCollection.set(new ArchiveResourceCollection);
+
+ ASSERT(archive);
+ if (!archive)
+ return;
+
+ m_archiveResourceCollection->addAllResources(archive);
}
-void DocumentLoader::stopRecordingResponses()
+// FIXME: Adding a resource directly to a DocumentLoader/ArchiveResourceCollection seems like bad design, but is API some apps rely on.
+// Can we change the design in a manner that will let us deprecate that API without reducing functionality of those apps?
+void DocumentLoader::addArchiveResource(PassRefPtr<ArchiveResource> resource)
{
- m_stopRecordingResponses = true;
+ if (!m_archiveResourceCollection)
+ m_archiveResourceCollection.set(new ArchiveResourceCollection);
+
+ ASSERT(resource);
+ if (!resource)
+ return;
+
+ m_archiveResourceCollection->addResource(resource);
+}
+
+ArchiveResource* DocumentLoader::archiveResourceForURL(const KURL& url) const
+{
+ if (!m_archiveResourceCollection)
+ return 0;
+
+ ArchiveResource* resource = m_archiveResourceCollection->archiveResourceForURL(url);
+
+ return resource && !resource->shouldIgnoreWhenUnarchiving() ? resource : 0;
+}
+
+PassRefPtr<Archive> DocumentLoader::popArchiveForSubframe(const String& frameName)
+{
+ return m_archiveResourceCollection ? m_archiveResourceCollection->popSubframeArchive(frameName) : 0;
+}
+
+void DocumentLoader::clearArchiveResources()
+{
+ m_archiveResourceCollection.clear();
+ m_substituteResourceDeliveryTimer.stop();
+}
+
+void DocumentLoader::setParsedArchiveData(PassRefPtr<SharedBuffer> data)
+{
+ m_parsedArchiveData = data;
}
-String DocumentLoader::title() const
+SharedBuffer* DocumentLoader::parsedArchiveData() const
{
- return m_pageTitle;
+ return m_parsedArchiveData.get();
}
-void DocumentLoader::setLastCheckedRequest(const ResourceRequest& req)
+PassRefPtr<ArchiveResource> DocumentLoader::mainResource() const
+{
+ const ResourceResponse& r = response();
+ RefPtr<SharedBuffer> mainResourceBuffer = mainResourceData();
+ if (!mainResourceBuffer)
+ mainResourceBuffer = SharedBuffer::create();
+
+ return ArchiveResource::create(mainResourceBuffer, r.url(), r.mimeType(), r.textEncodingName(), frame()->tree()->name());
+}
+
+PassRefPtr<ArchiveResource> DocumentLoader::subresource(const KURL& url) const
+{
+ if (!isCommitted())
+ return 0;
+
+ Document* doc = m_frame->document();
+ if (!doc)
+ return archiveResourceForURL(url);
+
+ CachedResource* resource = doc->docLoader()->cachedResource(url);
+ if (!resource || resource->preloadResult() == CachedResource::PreloadReferenced)
+ return archiveResourceForURL(url);
+
+ return ArchiveResource::create(resource->data(), url, resource->response());
+}
+
+void DocumentLoader::getSubresources(Vector<PassRefPtr<ArchiveResource> >& subresources) const
+{
+ if (!isCommitted())
+ return;
+
+ Document* document = m_frame->document();
+ if (!document)
+ return;
+
+ const HashMap<String, CachedResource*>& allResources = document->docLoader()->allCachedResources();
+ HashMap<String, CachedResource*>::const_iterator end = allResources.end();
+ for (HashMap<String, CachedResource*>::const_iterator it = allResources.begin(); it != end; ++it) {
+ RefPtr<ArchiveResource> subresource = this->subresource(KURL(it->second->url()));
+ if (subresource)
+ subresources.append(subresource.release());
+ }
+
+ return;
+}
+#endif
+
+void DocumentLoader::deliverSubstituteResourcesAfterDelay()
{
- m_lastCheckedRequest = req;
+ if (m_pendingSubstituteResources.isEmpty())
+ return;
+ ASSERT(m_frame && m_frame->page());
+ if (m_frame->page()->defersLoading())
+ return;
+ if (!m_substituteResourceDeliveryTimer.isActive())
+ m_substituteResourceDeliveryTimer.startOneShot(0);
}
-const ResourceRequest& DocumentLoader::lastCheckedRequest() const
+void DocumentLoader::substituteResourceDeliveryTimerFired(Timer<DocumentLoader>*)
{
- return m_lastCheckedRequest;
+ if (m_pendingSubstituteResources.isEmpty())
+ return;
+ ASSERT(m_frame && m_frame->page());
+ if (m_frame->page()->defersLoading())
+ return;
+
+ SubstituteResourceMap copy;
+ copy.swap(m_pendingSubstituteResources);
+
+ SubstituteResourceMap::const_iterator end = copy.end();
+ for (SubstituteResourceMap::const_iterator it = copy.begin(); it != end; ++it) {
+ RefPtr<ResourceLoader> loader = it->first;
+ SubstituteResource* resource = it->second.get();
+
+ if (resource) {
+ SharedBuffer* data = resource->data();
+
+ loader->didReceiveResponse(resource->response());
+ loader->didReceiveData(data->data(), data->size(), data->size(), true);
+ loader->didFinishLoading();
+ } else {
+ // A null resource means that we should fail the load.
+ // FIXME: Maybe we should use another error here - something like "not in cache".
+ loader->didFail(loader->cannotShowURLError());
+ }
+ }
}
-const NavigationAction& DocumentLoader::triggeringAction() const
+#ifndef NDEBUG
+bool DocumentLoader::isSubstituteLoadPending(ResourceLoader* loader) const
{
- return m_triggeringAction;
+ return m_pendingSubstituteResources.contains(loader);
}
+#endif
-void DocumentLoader::setTriggeringAction(const NavigationAction& action)
+void DocumentLoader::cancelPendingSubstituteLoad(ResourceLoader* loader)
{
- m_triggeringAction = action;
+ if (m_pendingSubstituteResources.isEmpty())
+ return;
+ m_pendingSubstituteResources.remove(loader);
+ if (m_pendingSubstituteResources.isEmpty())
+ m_substituteResourceDeliveryTimer.stop();
}
-const ResponseVector& DocumentLoader::responses() const
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+bool DocumentLoader::scheduleArchiveLoad(ResourceLoader* loader, const ResourceRequest& request, const KURL& originalURL)
{
- return m_responses;
+ ArchiveResource* resource = 0;
+
+ if (request.url() == originalURL)
+ resource = archiveResourceForURL(originalURL);
+
+ if (!resource) {
+ // WebArchiveDebugMode means we fail loads instead of trying to fetch them from the network if they're not in the archive.
+ bool shouldFailLoad = m_frame->settings()->webArchiveDebugModeEnabled() && ArchiveFactory::isArchiveMimeType(responseMIMEType());
+
+ if (!shouldFailLoad)
+ return false;
+ }
+
+ m_pendingSubstituteResources.set(loader, resource);
+ deliverSubstituteResourcesAfterDelay();
+
+ return true;
}
+#endif
-void DocumentLoader::setOverrideEncoding(const String& enc)
+void DocumentLoader::addResponse(const ResourceResponse& r)
{
- m_overrideEncoding = enc;
+ if (!m_stopRecordingResponses)
+ m_responses.append(r);
}
-String DocumentLoader::overrideEncoding() const
+void DocumentLoader::stopRecordingResponses()
{
- return m_overrideEncoding;
+ m_stopRecordingResponses = true;
}
void DocumentLoader::setTitle(const String& title)
@@ -566,52 +706,22 @@ void DocumentLoader::loadFromCachedPage(PassRefPtr<CachedPage> cachedPage)
frameLoader()->commitProvisionalLoad(cachedPage);
}
-const ResourceResponse& DocumentLoader::response() const
-{
- return m_response;
-}
-
-void DocumentLoader::setLoadingFromCachedPage(bool loading)
-{
- m_loadingFromCachedPage = loading;
-}
-
-bool DocumentLoader::isLoadingFromCachedPage() const
-{
- return m_loadingFromCachedPage;
-}
-
-void DocumentLoader::setResponse(const ResourceResponse& response)
-{
- m_response = response;
-}
-
-bool DocumentLoader::isStopping() const
-{
- return m_isStopping;
-}
-
-const ResourceError& DocumentLoader::mainDocumentError() const
-{
- return m_mainDocumentError;
-}
-
-KURL DocumentLoader::originalURL() const
+const KURL& DocumentLoader::originalURL() const
{
return m_originalRequestCopy.url();
}
-KURL DocumentLoader::requestURL() const
+const KURL& DocumentLoader::requestURL() const
{
return request().url();
}
-KURL DocumentLoader::responseURL() const
+const KURL& DocumentLoader::responseURL() const
{
return m_response.url();
}
-String DocumentLoader::responseMIMEType() const
+const String& DocumentLoader::responseMIMEType() const
{
return m_response.mimeType();
}
@@ -627,6 +737,8 @@ void DocumentLoader::setDefersLoading(bool defers)
m_mainResourceLoader->setDefersLoading(defers);
setAllDefersLoading(m_subresourceLoaders, defers);
setAllDefersLoading(m_plugInStreamLoaders, defers);
+ if (!defers)
+ deliverSubstituteResourcesAfterDelay();
}
void DocumentLoader::stopLoadingPlugIns()
@@ -726,4 +838,87 @@ void DocumentLoader::iconLoadDecisionAvailable()
m_frame->loader()->iconLoadDecisionAvailable();
}
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+void DocumentLoader::setCandidateApplicationCacheGroup(ApplicationCacheGroup* group)
+{
+ ASSERT(!m_applicationCache);
+ m_candidateApplicationCacheGroup = group;
+}
+
+void DocumentLoader::setApplicationCache(PassRefPtr<ApplicationCache> applicationCache)
+{
+ if (m_candidateApplicationCacheGroup) {
+ ASSERT(!m_applicationCache);
+ m_candidateApplicationCacheGroup = 0;
+ }
+
+ m_applicationCache = applicationCache;
+}
+
+ApplicationCache* DocumentLoader::topLevelApplicationCache() const
+{
+ if (!m_frame)
+ return 0;
+
+ if (m_applicationCache)
+ return m_applicationCache.get();
+
+ if (Page* page = m_frame->page())
+ return page->mainFrame()->loader()->documentLoader()->applicationCache();
+
+ return 0;
+}
+
+ApplicationCache* DocumentLoader::mainResourceApplicationCache() const
+{
+ if (m_mainResourceApplicationCache)
+ return m_mainResourceApplicationCache.get();
+ if (m_mainResourceLoader)
+ return m_mainResourceLoader->applicationCache();
+ return 0;
+}
+
+bool DocumentLoader::shouldLoadResourceFromApplicationCache(const ResourceRequest& request, ApplicationCacheResource*& resource)
+{
+ ApplicationCache* cache = topLevelApplicationCache();
+ if (!cache)
+ return false;
+
+ // If the resource is not a HTTP/HTTPS GET, then abort
+ if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
+ return false;
+
+ if (cache->isURLInOnlineWhitelist(request.url()))
+ return false;
+
+ resource = cache->resourceForURL(request.url());
+
+ // Don't load foreign resources.
+ if (resource && (resource->type() & ApplicationCacheResource::Foreign))
+ resource = 0;
+
+ return true;
+}
+
+bool DocumentLoader::scheduleApplicationCacheLoad(ResourceLoader* loader, const ResourceRequest& request, const KURL& originalURL)
+{
+ if (!frameLoader()->frame()->settings() || !frameLoader()->frame()->settings()->offlineWebApplicationCacheEnabled())
+ return false;
+
+ if (request.url() != originalURL)
+ return false;
+
+ ApplicationCacheResource* resource;
+ if (!shouldLoadResourceFromApplicationCache(request, resource))
+ // FIXME: Handle opportunistic caching namespaces
+ return false;
+
+ m_pendingSubstituteResources.set(loader, resource);
+ deliverSubstituteResourcesAfterDelay();
+
+ return true;
+}
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
}
diff --git a/WebCore/loader/DocumentLoader.h b/WebCore/loader/DocumentLoader.h
index 15303fe..aef4f49 100644
--- a/WebCore/loader/DocumentLoader.h
+++ b/WebCore/loader/DocumentLoader.h
@@ -43,6 +43,16 @@
namespace WebCore {
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ class ApplicationCache;
+ class ApplicationCacheGroup;
+ class ApplicationCacheResource;
+#endif
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ class Archive;
+ class ArchiveResource;
+ class ArchiveResourceCollection;
+#endif
class CachedPage;
class Frame;
class FrameLoader;
@@ -50,15 +60,20 @@ namespace WebCore {
class KURL;
class MainResourceLoader;
class ResourceLoader;
+ class SchedulePair;
class SharedBuffer;
class SubstituteData;
+ class SubstituteResource;
typedef HashSet<RefPtr<ResourceLoader> > ResourceLoaderSet;
typedef Vector<ResourceResponse> ResponseVector;
class DocumentLoader : public RefCounted<DocumentLoader> {
public:
- DocumentLoader(const ResourceRequest&, const SubstituteData&);
+ static PassRefPtr<DocumentLoader> create(const ResourceRequest& request, const SubstituteData& data)
+ {
+ return adoptRef(new DocumentLoader(request, data));
+ }
virtual ~DocumentLoader();
void setFrame(Frame*);
@@ -77,59 +92,86 @@ namespace WebCore {
const ResourceRequest& request() const;
ResourceRequest& request();
void setRequest(const ResourceRequest&);
- const ResourceRequest& actualRequest() const;
- ResourceRequest& actualRequest();
- const ResourceRequest& initialRequest() const;
const SubstituteData& substituteData() const { return m_substituteData; }
const KURL& url() const;
const KURL& unreachableURL() const;
- KURL originalURL() const;
- KURL requestURL() const;
- KURL responseURL() const;
- String responseMIMEType() const;
+ const KURL& originalURL() const;
+ const KURL& requestURL() const;
+ const KURL& responseURL() const;
+ const String& responseMIMEType() const;
void replaceRequestURLForAnchorScroll(const KURL&);
- bool isStopping() const;
+ bool isStopping() const { return m_isStopping; }
void stopLoading();
- void setCommitted(bool);
- bool isCommitted() const;
- bool isLoading() const;
- void setLoading(bool);
+ void setCommitted(bool committed) { m_committed = committed; }
+ bool isCommitted() const { return m_committed; }
+ bool isLoading() const { return m_loading; }
+ void setLoading(bool loading) { m_loading = loading; }
void updateLoading();
void receivedData(const char*, int);
void setupForReplaceByMIMEType(const String& newMIMEType);
void finishedLoading();
- const ResourceResponse& response() const;
- const ResourceError& mainDocumentError() const;
+ const ResourceResponse& response() const { return m_response; }
+ const ResourceError& mainDocumentError() const { return m_mainDocumentError; }
void mainReceivedError(const ResourceError&, bool isComplete);
- void setResponse(const ResourceResponse& response);
+ void setResponse(const ResourceResponse& response) { m_response = response; }
void prepareForLoadStart();
- bool isClientRedirect() const;
- void setIsClientRedirect(bool);
+ bool isClientRedirect() const { return m_isClientRedirect; }
+ void setIsClientRedirect(bool isClientRedirect) { m_isClientRedirect = isClientRedirect; }
bool isLoadingInAPISense() const;
void setPrimaryLoadComplete(bool);
void setTitle(const String&);
- String overrideEncoding() const;
+ const String& overrideEncoding() const { return m_overrideEncoding; }
+
+#if PLATFORM(MAC)
+ void schedule(SchedulePair*);
+ void unschedule(SchedulePair*);
+#endif
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ void addAllArchiveResources(Archive*);
+ void addArchiveResource(PassRefPtr<ArchiveResource>);
+
+ // Return an ArchiveResource for the URL, either creating from live data or
+ // pulling from the ArchiveResourceCollection
+ PassRefPtr<ArchiveResource> subresource(const KURL&) const;
+ // Return the ArchiveResource for the URL only when loading an Archive
+ ArchiveResource* archiveResourceForURL(const KURL&) const;
+
+ PassRefPtr<Archive> popArchiveForSubframe(const String& frameName);
+ void clearArchiveResources();
+ void setParsedArchiveData(PassRefPtr<SharedBuffer>);
+ SharedBuffer* parsedArchiveData() const;
+
+ PassRefPtr<ArchiveResource> mainResource() const;
+ void getSubresources(Vector<PassRefPtr<ArchiveResource> >&) const;
+
+ bool scheduleArchiveLoad(ResourceLoader*, const ResourceRequest&, const KURL&);
+#endif
+#ifndef NDEBUG
+ bool isSubstituteLoadPending(ResourceLoader*) const;
+#endif
+ void cancelPendingSubstituteLoad(ResourceLoader*);
+
void addResponse(const ResourceResponse&);
- const ResponseVector& responses() const;
+ const ResponseVector& responses() const { return m_responses; }
- const NavigationAction& triggeringAction() const;
- void setTriggeringAction(const NavigationAction&);
- void setOverrideEncoding(const String&);
- void setLastCheckedRequest(const ResourceRequest& request);
- const ResourceRequest& lastCheckedRequest() const;
+ const NavigationAction& triggeringAction() const { return m_triggeringAction; }
+ void setTriggeringAction(const NavigationAction& action) { m_triggeringAction = action; }
+ void setOverrideEncoding(const String& encoding) { m_overrideEncoding = encoding; }
+ void setLastCheckedRequest(const ResourceRequest& request) { m_lastCheckedRequest = request; }
+ const ResourceRequest& lastCheckedRequest() { return m_lastCheckedRequest; }
void stopRecordingResponses();
- String title() const;
+ const String& title() const { return m_pageTitle; }
KURL urlForHistory() const;
void loadFromCachedPage(PassRefPtr<CachedPage>);
- void setLoadingFromCachedPage(bool);
- bool isLoadingFromCachedPage() const;
+ void setLoadingFromCachedPage(bool loading) { m_loadingFromCachedPage = loading; }
+ bool isLoadingFromCachedPage() const { return m_loadingFromCachedPage; }
void setDefersLoading(bool);
@@ -153,8 +195,26 @@ namespace WebCore {
void subresourceLoaderFinishedLoadingOnePart(ResourceLoader*);
+ void setDeferMainResourceDataLoad(bool defer) { m_deferMainResourceDataLoad = defer; }
bool deferMainResourceDataLoad() const { return m_deferMainResourceDataLoad; }
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ bool scheduleApplicationCacheLoad(ResourceLoader*, const ResourceRequest&, const KURL& originalURL);
+ bool shouldLoadResourceFromApplicationCache(const ResourceRequest&, ApplicationCacheResource*&);
+
+ void setCandidateApplicationCacheGroup(ApplicationCacheGroup* group);
+ ApplicationCacheGroup* candidateApplicationCacheGroup() const { return m_candidateApplicationCacheGroup; }
+
+ void setApplicationCache(PassRefPtr<ApplicationCache> applicationCache);
+ ApplicationCache* applicationCache() const { return m_applicationCache.get(); }
+ ApplicationCache* topLevelApplicationCache() const;
+
+ ApplicationCache* mainResourceApplicationCache() const;
+#endif
+
protected:
+ DocumentLoader(const ResourceRequest&, const SubstituteData&);
+
bool m_deferMainResourceDataLoad;
private:
@@ -165,6 +225,9 @@ namespace WebCore {
void commitLoad(const char*, int);
bool doesProgressiveLoad(const String& MIMEType) const;
+ void deliverSubstituteResourcesAfterDelay();
+ void substituteResourceDeliveryTimerFired(Timer<DocumentLoader>*);
+
Frame* m_frame;
RefPtr<MainResourceLoader> m_mainResourceLoader;
@@ -191,8 +254,6 @@ namespace WebCore {
// headers, cookie information, canonicalization and redirects.
ResourceRequest m_request;
- mutable ResourceRequest m_externalRequest;
-
ResourceResponse m_response;
ResourceError m_mainDocumentError;
@@ -222,6 +283,27 @@ namespace WebCore {
// page cache.
ResponseVector m_responses;
bool m_stopRecordingResponses;
+
+ typedef HashMap<RefPtr<ResourceLoader>, RefPtr<SubstituteResource> > SubstituteResourceMap;
+ SubstituteResourceMap m_pendingSubstituteResources;
+ Timer<DocumentLoader> m_substituteResourceDeliveryTimer;
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ OwnPtr<ArchiveResourceCollection> m_archiveResourceCollection;
+ RefPtr<SharedBuffer> m_parsedArchiveData;
+#endif
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ // The application cache that the document loader is associated with (if any).
+ RefPtr<ApplicationCache> m_applicationCache;
+
+ // Before an application cache has finished loading, this will be the candidate application
+ // group that the document loader is associated with.
+ ApplicationCacheGroup* m_candidateApplicationCacheGroup;
+
+ // Once the main resource has finished loading, this is the application cache it was loaded from (if any).
+ RefPtr<ApplicationCache> m_mainResourceApplicationCache;
+#endif
};
}
diff --git a/WebCore/loader/EmptyClients.h b/WebCore/loader/EmptyClients.h
new file mode 100644
index 0000000..8cab747
--- /dev/null
+++ b/WebCore/loader/EmptyClients.h
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2006 Eric Seidel (eric@webkit.org)
+ *
+ * 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 COMPUTER, 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 COMPUTER, 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 EmptyClients_h
+#define EmptyClients_h
+
+#include "ChromeClient.h"
+#include "ContextMenuClient.h"
+#include "DragClient.h"
+#include "DocumentLoader.h"
+#include "EditCommand.h"
+#include "EditorClient.h"
+#include "FocusDirection.h"
+#include "FloatRect.h"
+#include "FrameLoaderClient.h"
+#include "InspectorClient.h"
+#include "ResourceError.h"
+#include "SharedBuffer.h"
+
+/*
+ This file holds empty Client stubs for use by WebCore.
+ Viewless element needs to create a dummy Page->Frame->FrameView tree for use in parsing or executing JavaScript.
+ This tree depends heavily on Clients (usually provided by WebKit classes).
+
+ This file was first created for SVGImage as it had no way to access the current Page (nor should it,
+ since Images are not tied to a page).
+ See http://bugs.webkit.org/show_bug.cgi?id=5971 for the original discussion about this file.
+
+ Ideally, whenever you change a Client class, you should add a stub here.
+ Brittle, yes. Unfortunate, yes. Hopefully temporary.
+*/
+
+namespace WebCore {
+
+class EmptyChromeClient : public ChromeClient {
+public:
+ virtual ~EmptyChromeClient() { }
+ virtual void chromeDestroyed() { }
+
+ virtual void setWindowRect(const FloatRect&) { }
+ virtual FloatRect windowRect() { return FloatRect(); }
+
+ virtual FloatRect pageRect() { return FloatRect(); }
+
+ virtual float scaleFactor() { return 1.f; }
+
+ virtual void focus() { }
+ virtual void unfocus() { }
+
+ virtual bool canTakeFocus(FocusDirection) { return false; }
+ virtual void takeFocus(FocusDirection) { }
+
+ virtual Page* createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures&) { return 0; }
+ virtual void show() { }
+
+ virtual bool canRunModal() { return false; }
+ virtual void runModal() { }
+
+ virtual void setToolbarsVisible(bool) { }
+ virtual bool toolbarsVisible() { return false; }
+
+ virtual void setStatusbarVisible(bool) { }
+ virtual bool statusbarVisible() { return false; }
+
+ virtual void setScrollbarsVisible(bool) { }
+ virtual bool scrollbarsVisible() { return false; }
+
+ virtual void setMenubarVisible(bool) { }
+ virtual bool menubarVisible() { return false; }
+
+ virtual void setResizable(bool) { }
+
+ virtual void addMessageToConsole(const String& message, unsigned int lineNumber, const String& sourceID) { }
+
+ virtual bool canRunBeforeUnloadConfirmPanel() { return false; }
+ virtual bool runBeforeUnloadConfirmPanel(const String& message, Frame* frame) { return true; }
+
+ virtual void closeWindowSoon() { }
+
+ virtual void runJavaScriptAlert(Frame*, const String&) { }
+ virtual bool runJavaScriptConfirm(Frame*, const String&) { return false; }
+ virtual bool runJavaScriptPrompt(Frame*, const String& message, const String& defaultValue, String& result) { return false; }
+ virtual bool shouldInterruptJavaScript() { return false; }
+
+ virtual void setStatusbarText(const String&) { }
+
+ virtual bool tabsToLinks() const { return false; }
+
+ virtual IntRect windowResizerRect() const { return IntRect(); }
+ virtual void addToDirtyRegion(const IntRect&) { }
+ virtual void scrollBackingStore(int dx, int dy, const IntRect& scrollViewRect, const IntRect& clipRect) { }
+ virtual void updateBackingStore() { }
+
+ virtual void repaint(const IntRect&, bool contentChanged, bool immediate = false, bool repaintContentOnly = false) { }
+ virtual void scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect) { }
+ virtual IntPoint screenToWindow(const IntPoint& p) const { return p; }
+ virtual IntRect windowToScreen(const IntRect& r) const { return r; }
+ virtual PlatformWidget platformWindow() const { return 0; }
+
+ virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned modifierFlags) { }
+
+ virtual void setToolTip(const String&) { }
+
+ virtual void print(Frame*) { }
+
+ virtual void exceededDatabaseQuota(Frame*, const String&) { }
+
+ virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>) { }
+};
+
+class EmptyFrameLoaderClient : public FrameLoaderClient {
+public:
+ virtual ~EmptyFrameLoaderClient() { }
+ virtual void frameLoaderDestroyed() { }
+
+ virtual bool hasWebView() const { return true; } // mainly for assertions
+
+ virtual void makeRepresentation(DocumentLoader*) { }
+ virtual void forceLayout() { }
+ virtual void forceLayoutForNonHTML() { }
+
+ virtual void updateHistoryForCommit() { }
+
+ virtual void updateHistoryForBackForwardNavigation() { }
+ virtual void updateHistoryForReload() { }
+ virtual void updateHistoryForStandardLoad() { }
+ virtual void updateHistoryForInternalLoad() { }
+
+ virtual void updateHistoryAfterClientRedirect() { }
+
+ virtual void setCopiesOnScroll() { }
+
+ virtual void detachedFromParent2() { }
+ virtual void detachedFromParent3() { }
+
+ virtual void download(ResourceHandle*, const ResourceRequest&, const ResourceRequest&, const ResourceResponse&) { }
+
+ virtual void assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&) { }
+ virtual void dispatchWillSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse) { }
+ virtual void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) { }
+ virtual void dispatchDidCancelAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) { }
+ virtual void dispatchDidReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&) { }
+ virtual void dispatchDidReceiveContentLength(DocumentLoader*, unsigned long identifier, int lengthReceived) { }
+ virtual void dispatchDidFinishLoading(DocumentLoader*, unsigned long identifier) { }
+ virtual void dispatchDidFailLoading(DocumentLoader*, unsigned long identifier, const ResourceError&) { }
+ virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int length) { return false; }
+
+ virtual void dispatchDidHandleOnloadEvents() { }
+ virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() { }
+ virtual void dispatchDidCancelClientRedirect() { }
+ virtual void dispatchWillPerformClientRedirect(const KURL&, double interval, double fireDate) { }
+ virtual void dispatchDidChangeLocationWithinPage() { }
+ virtual void dispatchWillClose() { }
+ virtual void dispatchDidReceiveIcon() { }
+ virtual void dispatchDidStartProvisionalLoad() { }
+ virtual void dispatchDidReceiveTitle(const String& title) { }
+ virtual void dispatchDidCommitLoad() { }
+ virtual void dispatchDidFailProvisionalLoad(const ResourceError&) { }
+ virtual void dispatchDidFailLoad(const ResourceError&) { }
+ virtual void dispatchDidFinishDocumentLoad() { }
+ virtual void dispatchDidFinishLoad() { }
+ virtual void dispatchDidFirstLayout() { }
+
+ virtual Frame* dispatchCreatePage() { return 0; }
+ virtual void dispatchShow() { }
+
+ virtual void dispatchDecidePolicyForMIMEType(FramePolicyFunction, const String& MIMEType, const ResourceRequest&) { }
+ virtual void dispatchDecidePolicyForNewWindowAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName) { }
+ virtual void dispatchDecidePolicyForNavigationAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>) { }
+ virtual void cancelPolicyCheck() { }
+
+ virtual void dispatchUnableToImplementPolicy(const ResourceError&) { }
+
+ virtual void dispatchWillSubmitForm(FramePolicyFunction, PassRefPtr<FormState>) { }
+
+ virtual void dispatchDidLoadMainResource(DocumentLoader*) { }
+ virtual void revertToProvisionalState(DocumentLoader*) { }
+ virtual void setMainDocumentError(DocumentLoader*, const ResourceError&) { }
+
+ virtual void willChangeEstimatedProgress() { }
+ virtual void didChangeEstimatedProgress() { }
+ virtual void postProgressStartedNotification() { }
+ virtual void postProgressEstimateChangedNotification() { }
+ virtual void postProgressFinishedNotification() { }
+
+ virtual void setMainFrameDocumentReady(bool) { }
+
+ virtual void startDownload(const ResourceRequest&) { }
+
+ virtual void willChangeTitle(DocumentLoader*) { }
+ virtual void didChangeTitle(DocumentLoader*) { }
+
+ virtual void committedLoad(DocumentLoader*, const char*, int) { }
+ virtual void finishedLoading(DocumentLoader*) { }
+
+ virtual ResourceError cancelledError(const ResourceRequest&) { return ResourceError(); }
+ virtual ResourceError blockedError(const ResourceRequest&) { return ResourceError(); }
+ virtual ResourceError cannotShowURLError(const ResourceRequest&) { return ResourceError(); }
+ virtual ResourceError interruptForPolicyChangeError(const ResourceRequest&) { return ResourceError(); }
+
+ virtual ResourceError cannotShowMIMETypeError(const ResourceResponse&) { return ResourceError(); }
+ virtual ResourceError fileDoesNotExistError(const ResourceResponse&) { return ResourceError(); }
+ virtual ResourceError pluginWillHandleLoadError(const ResourceResponse&) { return ResourceError(); }
+
+ virtual bool shouldFallBack(const ResourceError&) { return false; }
+
+ virtual bool canHandleRequest(const ResourceRequest&) const { return false; }
+ virtual bool canShowMIMEType(const String& MIMEType) const { return false; }
+ virtual bool representationExistsForURLScheme(const String& URLScheme) const { return false; }
+ virtual String generatedMIMETypeForURLScheme(const String& URLScheme) const { return ""; }
+
+ virtual void frameLoadCompleted() { }
+ virtual void restoreViewState() { }
+ virtual void provisionalLoadStarted() { }
+ virtual bool shouldTreatURLAsSameAsCurrent(const KURL&) const { return false; }
+ virtual void addHistoryItemForFragmentScroll() { }
+ virtual void didFinishLoad() { }
+ virtual void prepareForDataSourceReplacement() { }
+
+ virtual PassRefPtr<DocumentLoader> createDocumentLoader(const ResourceRequest& request, const SubstituteData& substituteData) { return DocumentLoader::create(request, substituteData); }
+ virtual void setTitle(const String& title, const KURL&) { }
+
+ virtual String userAgent(const KURL&) { return ""; }
+
+ virtual void savePlatformDataToCachedPage(CachedPage*) { }
+ virtual void transitionToCommittedFromCachedPage(CachedPage*) { }
+ virtual void transitionToCommittedForNewPage() { }
+
+ virtual void updateGlobalHistory(const KURL&) { }
+ virtual bool shouldGoToHistoryItem(HistoryItem*) const { return false; }
+ virtual void saveViewStateToItem(HistoryItem*) { }
+ virtual bool canCachePage() const { return false; }
+
+ virtual PassRefPtr<Frame> createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement,
+ const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight) { return 0; }
+ virtual Widget* createPlugin(const IntSize&,Element*, const KURL&, const Vector<String>&, const Vector<String>&, const String&, bool) { return 0; }
+ virtual Widget* createJavaAppletWidget(const IntSize&, Element*, const KURL&, const Vector<String>&, const Vector<String>&) { return 0; }
+
+ virtual ObjectContentType objectContentType(const KURL& url, const String& mimeType) { return ObjectContentType(); }
+ virtual String overrideMediaType() const { return String(); }
+
+ virtual void redirectDataToPlugin(Widget*) {}
+ virtual void windowObjectCleared() {}
+ virtual void didPerformFirstNavigation() const {}
+
+ virtual void registerForIconNotification(bool listen) {}
+
+#if PLATFORM(MAC)
+ virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long identifier, NSCachedURLResponse* response) const { return response; }
+#endif
+
+};
+
+class EmptyEditorClient : public EditorClient {
+public:
+ virtual ~EmptyEditorClient() { }
+ virtual void pageDestroyed() { }
+
+ virtual bool shouldDeleteRange(Range*) { return false; }
+ virtual bool shouldShowDeleteInterface(HTMLElement*) { return false; }
+ virtual bool smartInsertDeleteEnabled() { return false; }
+ virtual bool isContinuousSpellCheckingEnabled() { return false; }
+ virtual void toggleContinuousSpellChecking() { }
+ virtual bool isGrammarCheckingEnabled() { return false; }
+ virtual void toggleGrammarChecking() { }
+ virtual int spellCheckerDocumentTag() { return -1; }
+
+ virtual bool selectWordBeforeMenuEvent() { return false; }
+ virtual bool isEditable() { return false; }
+
+ virtual bool shouldBeginEditing(Range*) { return false; }
+ virtual bool shouldEndEditing(Range*) { return false; }
+ virtual bool shouldInsertNode(Node*, Range*, EditorInsertAction) { return false; }
+ // virtual bool shouldInsertNode(Node*, Range* replacingRange, WebViewInsertAction) { return false; }
+ virtual bool shouldInsertText(const String&, Range*, EditorInsertAction) { return false; }
+ virtual bool shouldChangeSelectedRange(Range* fromRange, Range* toRange, EAffinity, bool stillSelecting) { return false; }
+
+ virtual bool shouldApplyStyle(CSSStyleDeclaration*, Range*) { return false; }
+ virtual bool shouldMoveRangeAfterDelete(Range*, Range*) { return false; }
+ // virtual bool shouldChangeTypingStyle(CSSStyleDeclaration* fromStyle, CSSStyleDeclaration* toStyle) { return false; }
+ // virtual bool doCommandBySelector(SEL selector) { return false; }
+ //
+ virtual void didBeginEditing() { }
+ virtual void respondToChangedContents() { }
+ virtual void respondToChangedSelection() { }
+ virtual void didEndEditing() { }
+ virtual void didWriteSelectionToPasteboard() { }
+ virtual void didSetSelectionTypesForPasteboard() { }
+ // virtual void webViewDidChangeTypingStyle:(NSNotification *)notification { }
+ // virtual void webViewDidChangeSelection:(NSNotification *)notification { }
+ // virtual NSUndoManager* undoManagerForWebView:(WebView *)webView { return 0; }
+
+ virtual void registerCommandForUndo(PassRefPtr<EditCommand>) { }
+ virtual void registerCommandForRedo(PassRefPtr<EditCommand>) { }
+ virtual void clearUndoRedoOperations() { }
+
+ virtual bool canUndo() const { return false; }
+ virtual bool canRedo() const { return false; }
+
+ virtual void undo() { }
+ virtual void redo() { }
+
+ virtual void handleKeyboardEvent(KeyboardEvent*) { }
+ virtual void handleInputMethodKeydown(KeyboardEvent*) { }
+
+ virtual void textFieldDidBeginEditing(Element*) { }
+ virtual void textFieldDidEndEditing(Element*) { }
+ virtual void textDidChangeInTextField(Element*) { }
+ virtual bool doTextFieldCommandFromEvent(Element*, KeyboardEvent*) { return false; }
+ virtual void textWillBeDeletedInTextField(Element*) { }
+ virtual void textDidChangeInTextArea(Element*) { }
+
+#if PLATFORM(MAC)
+ virtual void markedTextAbandoned(Frame*) { }
+
+ virtual NSString* userVisibleString(NSURL*) { return 0; }
+#ifdef BUILDING_ON_TIGER
+ virtual NSArray* pasteboardTypesForSelection(Frame*) { return 0; }
+#endif
+#endif
+ virtual void ignoreWordInSpellDocument(const String&) { }
+ virtual void learnWord(const String&) { }
+ virtual void checkSpellingOfString(const UChar*, int length, int* misspellingLocation, int* misspellingLength) { }
+ virtual void checkGrammarOfString(const UChar*, int length, Vector<GrammarDetail>&, int* badGrammarLocation, int* badGrammarLength) { }
+ virtual void updateSpellingUIWithGrammarString(const String&, const GrammarDetail&) { }
+ virtual void updateSpellingUIWithMisspelledWord(const String&) { }
+ virtual void showSpellingUI(bool show) { }
+ virtual bool spellingUIIsShowing() { return false; }
+ virtual void getGuessesForWord(const String&, Vector<String>& guesses) { }
+ virtual void setInputMethodState(bool enabled) { }
+
+
+};
+
+class EmptyContextMenuClient : public ContextMenuClient {
+public:
+ virtual ~EmptyContextMenuClient() { }
+ virtual void contextMenuDestroyed() { }
+
+ virtual PlatformMenuDescription getCustomMenuFromDefaultItems(ContextMenu*) { return 0; }
+ virtual void contextMenuItemSelected(ContextMenuItem*, const ContextMenu*) { }
+
+ virtual void downloadURL(const KURL& url) { }
+ virtual void copyImageToClipboard(const HitTestResult&) { }
+ virtual void searchWithGoogle(const Frame*) { }
+ virtual void lookUpInDictionary(Frame*) { }
+ virtual void speak(const String&) { }
+ virtual void stopSpeaking() { }
+
+#if PLATFORM(MAC)
+ virtual void searchWithSpotlight() { }
+#endif
+};
+
+class EmptyDragClient : public DragClient {
+public:
+ virtual ~EmptyDragClient() {}
+ virtual void willPerformDragDestinationAction(DragDestinationAction, DragData*) { }
+ virtual void willPerformDragSourceAction(DragSourceAction, const IntPoint&, Clipboard*) { }
+ virtual DragDestinationAction actionMaskForDrag(DragData*) { return DragDestinationActionNone; }
+ virtual DragSourceAction dragSourceActionMaskForPoint(const IntPoint&) { return DragSourceActionNone; }
+ virtual void startDrag(DragImageRef, const IntPoint&, const IntPoint&, Clipboard*, Frame*, bool) { }
+ virtual DragImageRef createDragImageForLink(KURL&, const String& label, Frame*) { return 0; }
+ virtual void dragControllerDestroyed() { }
+};
+
+class EmptyInspectorClient : public InspectorClient {
+public:
+ virtual ~EmptyInspectorClient() { }
+
+ virtual void inspectorDestroyed() { }
+
+ virtual Page* createPage() { return 0; };
+
+ virtual String localizedStringsURL() { return String(); }
+
+ virtual void showWindow() { }
+ virtual void closeWindow() { }
+
+ virtual void attachWindow() { }
+ virtual void detachWindow() { }
+
+ virtual void setAttachedWindowHeight(unsigned) { }
+
+ virtual void highlight(Node*) { }
+ virtual void hideHighlight() { }
+ virtual void inspectedURLChanged(const String& newURL) { }
+
+ virtual void populateSetting(const String& key, InspectorController::Setting&) { }
+ virtual void storeSetting(const String& key, const InspectorController::Setting&) { }
+ virtual void removeSetting(const String& key) { }
+};
+
+}
+
+#endif // EmptyClients_h
diff --git a/WebCore/loader/FTPDirectoryDocument.cpp b/WebCore/loader/FTPDirectoryDocument.cpp
index beab88c..0a7e91e 100644
--- a/WebCore/loader/FTPDirectoryDocument.cpp
+++ b/WebCore/loader/FTPDirectoryDocument.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 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
@@ -28,12 +28,9 @@
#include "CharacterNames.h"
#include "CString.h"
-#include "Element.h"
#include "HTMLNames.h"
#include "HTMLTableElement.h"
-#include "HTMLTableSectionElement.h"
#include "HTMLTokenizer.h"
-#include "KURL.h"
#include "LocalizedStrings.h"
#include "Logging.h"
#include "FTPDirectoryParser.h"
@@ -41,15 +38,12 @@
#include "Settings.h"
#include "SharedBuffer.h"
#include "Text.h"
-#include "XMLTokenizer.h"
-
-// On Win, the threadsafe *_r functions need to be gotten from pthreads.
-#if COMPILER(MSVC) && USE(PTHREADS)
-#include <pthread.h>
-#endif
#if PLATFORM(QT)
#include <QDateTime>
+// On Windows, use the threadsafe *_r functions provided by pthread.
+#elif PLATFORM(WIN_OS) && (USE(PTHREADS) || HAVE(PTHREAD_H))
+#include <pthread.h>
#endif
using namespace std;
@@ -60,7 +54,7 @@ using namespace HTMLNames;
class FTPDirectoryTokenizer : public HTMLTokenizer {
public:
- FTPDirectoryTokenizer(HTMLDocument* doc);
+ FTPDirectoryTokenizer(HTMLDocument*);
virtual bool write(const SegmentedString&, bool appendData);
virtual void finish();
@@ -149,7 +143,7 @@ PassRefPtr<Element> FTPDirectoryTokenizer::createTDForFilename(const String& fil
{
ExceptionCode ec;
- String fullURL = m_doc->baseURL();
+ String fullURL = m_doc->baseURL().string();
if (fullURL[fullURL.length() - 1] == '/')
fullURL.append(filename);
else
@@ -239,6 +233,8 @@ static struct tm *localTimeQt(const time_t *const timep, struct tm *result)
}
#define localtime_r(x, y) localTimeQt(x, y)
+#elif PLATFORM(WIN_OS) && !defined(localtime_r)
+#define localtime_r(x, y) localtime_s((y), (x))
#endif
static String processFileDateString(const FTPTime& fileTime)
@@ -307,10 +303,7 @@ void FTPDirectoryTokenizer::parseAndAppendOneLine(const String& inputLine)
{
ListResult result;
- DeprecatedString depString = inputLine.deprecatedString();
- const char* line = depString.ascii();
-
- FTPEntryType typeResult = parseOneFTPLine(line, m_listState, result);
+ FTPEntryType typeResult = parseOneFTPLine(inputLine.latin1().data(), m_listState, result);
// FTPMiscEntry is a comment or usage statistic which we don't care about, and junk is invalid data - bail in these 2 cases
if (typeResult == FTPMiscEntry || typeResult == FTPJunkEntry)
@@ -482,8 +475,8 @@ void FTPDirectoryTokenizer::finish()
HTMLTokenizer::finish();
}
-FTPDirectoryDocument::FTPDirectoryDocument(DOMImplementation* implementation, Frame* frame)
- : HTMLDocument(implementation, frame)
+FTPDirectoryDocument::FTPDirectoryDocument(Frame* frame)
+ : HTMLDocument(frame)
{
#ifndef NDEBUG
LogFTP.state = WTFLogChannelOn;
diff --git a/WebCore/loader/FTPDirectoryDocument.h b/WebCore/loader/FTPDirectoryDocument.h
index bea228d..ecc6f73 100644
--- a/WebCore/loader/FTPDirectoryDocument.h
+++ b/WebCore/loader/FTPDirectoryDocument.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 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
@@ -33,9 +33,13 @@ class DOMImplementation;
class FTPDirectoryDocument : public HTMLDocument {
public:
- FTPDirectoryDocument(DOMImplementation*, Frame*);
-
- virtual bool isImageDocument() const { return false; }
+ static PassRefPtr<FTPDirectoryDocument> create(Frame* frame)
+ {
+ return new FTPDirectoryDocument(frame);
+ }
+
+private:
+ FTPDirectoryDocument(Frame*);
virtual Tokenizer* createTokenizer();
};
diff --git a/WebCore/loader/FTPDirectoryParser.cpp b/WebCore/loader/FTPDirectoryParser.cpp
index fdda0b6..8c76e97 100644
--- a/WebCore/loader/FTPDirectoryParser.cpp
+++ b/WebCore/loader/FTPDirectoryParser.cpp
@@ -24,19 +24,18 @@
#if ENABLE(FTPDIR)
#include "FTPDirectoryParser.h"
-// On Win, the threadsafe *_r functions need to be gotten from pthreads.
-#if COMPILER(MSVC) && USE(PTHREADS)
+#if PLATFORM(QT)
+#include <QDateTime>
+// On Windows, use the threadsafe *_r functions provided by pthread.
+#elif PLATFORM(WIN_OS) && (USE(PTHREADS) || HAVE(PTHREAD_H))
#include <pthread.h>
#endif
#include <wtf/ASCIICType.h>
+#include <stdio.h>
using namespace WTF;
-#if PLATFORM(QT)
-#include <QDateTime>
-#endif
-
namespace WebCore {
#if PLATFORM(QT) && defined(Q_WS_WIN32)
// Defined in FTPDirectoryDocument.cpp.
@@ -50,9 +49,10 @@ static struct tm *gmtimeQt(const time_t *const timep, struct tm *result)
}
#define gmtime_r(x, y) gmtimeQt(x, y)
+#elif PLATFORM(WIN_OS) && !defined(gmtime_r)
+#define gmtime_r(x, y) gmtime_s((y), (x))
#endif
-
FTPEntryType parseOneFTPLine(const char* line, ListState& state, ListResult& result)
{
result.clear();
diff --git a/WebCore/loader/FormState.cpp b/WebCore/loader/FormState.cpp
index f5ea1e9..c55b8ac 100644
--- a/WebCore/loader/FormState.cpp
+++ b/WebCore/loader/FormState.cpp
@@ -36,7 +36,7 @@ namespace WebCore {
PassRefPtr<FormState> FormState::create(PassRefPtr<HTMLFormElement> form, const HashMap<String, String>& values, PassRefPtr<Frame> sourceFrame)
{
- return new FormState(form, values, sourceFrame);
+ return adoptRef(new FormState(form, values, sourceFrame));
}
FormState::FormState(PassRefPtr<HTMLFormElement> form, const HashMap<String, String>& values, PassRefPtr<Frame> sourceFrame)
diff --git a/WebCore/loader/FrameLoader.cpp b/WebCore/loader/FrameLoader.cpp
index b0b4f90..2fdb8f5 100644
--- a/WebCore/loader/FrameLoader.cpp
+++ b/WebCore/loader/FrameLoader.cpp
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
- * Copyright (C) 2007 Trolltech ASA
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,15 +30,19 @@
#include "config.h"
#include "FrameLoader.h"
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+#include "Archive.h"
+#include "ArchiveFactory.h"
+#endif
#include "CString.h"
#include "Cache.h"
#include "CachedPage.h"
#include "Chrome.h"
#include "DOMImplementation.h"
+#include "DOMWindow.h"
#include "DocLoader.h"
#include "Document.h"
#include "DocumentLoader.h"
-#include "EditCommand.h"
#include "Editor.h"
#include "EditorClient.h"
#include "Element.h"
@@ -66,10 +70,12 @@
#include "MainResourceLoader.h"
#include "Page.h"
#include "PageCache.h"
-#include "PluginInfoStore.h"
+#include "PageGroup.h"
+#include "PluginData.h"
#include "ProgressTracker.h"
#include "RenderPart.h"
#include "RenderWidget.h"
+#include "RenderView.h"
#include "ResourceHandle.h"
#include "ResourceRequest.h"
#include "SecurityOrigin.h"
@@ -80,11 +86,15 @@
#include "WindowFeatures.h"
#include "XMLHttpRequest.h"
#include "XMLTokenizer.h"
-#include "kjs_binding.h"
-#include "kjs_proxy.h"
-#include "kjs_window.h"
-#include <kjs/JSLock.h>
-#include <kjs/object.h>
+#include "JSDOMBinding.h"
+#include "ScriptController.h"
+#include <runtime/JSLock.h>
+#include <runtime/JSObject.h>
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+#include "ApplicationCache.h"
+#include "ApplicationCacheResource.h"
+#endif
#if ENABLE(SVG)
#include "SVGDocument.h"
@@ -96,9 +106,7 @@
#include "SVGViewSpec.h"
#endif
-using KJS::UString;
-using KJS::JSLock;
-using KJS::JSValue;
+using namespace JSC;
namespace WebCore {
@@ -106,7 +114,6 @@ namespace WebCore {
using namespace SVGNames;
#endif
using namespace HTMLNames;
-using namespace EventNames;
#if USE(LOW_BANDWIDTH_DISPLAY)
const unsigned int cMaxPendingSourceLengthInLowBandwidthDisplay = 128 * 1024;
@@ -183,13 +190,13 @@ struct ScheduledRedirection {
};
static double storedTimeOfLastCompletedLoad;
-static bool m_restrictAccessToLocal = true;
+static FrameLoader::LocalLoadPolicy localLoadPolicy = FrameLoader::AllowLocalLoadsForLocalOnly;
static bool getString(JSValue* result, String& string)
{
if (!result)
return false;
- JSLock lock;
+ JSLock lock(false);
UString ustring;
if (!result->getString(ustring))
return false;
@@ -306,30 +313,34 @@ void FrameLoader::setDefersLoading(bool defers)
m_provisionalDocumentLoader->setDefersLoading(defers);
if (m_policyDocumentLoader)
m_policyDocumentLoader->setDefersLoading(defers);
- m_client->setDefersLoading(defers);
}
-Frame* FrameLoader::createWindow(const FrameLoadRequest& request, const WindowFeatures& features, bool& created)
+Frame* FrameLoader::createWindow(FrameLoader* frameLoaderForFrameLookup, const FrameLoadRequest& request, const WindowFeatures& features, bool& created)
{
ASSERT(!features.dialog || request.frameName().isEmpty());
- if (!request.frameName().isEmpty() && request.frameName() != "_blank")
- if (Frame* frame = findFrameForNavigation(request.frameName())) {
+ if (!request.frameName().isEmpty() && request.frameName() != "_blank") {
+ Frame* frame = frameLoaderForFrameLookup->frame()->tree()->find(request.frameName());
+ if (frame && shouldAllowNavigation(frame)) {
if (!request.resourceRequest().url().isEmpty())
- frame->loader()->load(request, false, true, 0, 0, HashMap<String, String>());
+ frame->loader()->loadFrameRequestWithFormAndValues(request, false, 0, 0, HashMap<String, String>());
if (Page* page = frame->page())
page->chrome()->focus();
created = false;
return frame;
}
-
+ }
+
// FIXME: Setting the referrer should be the caller's responsibility.
FrameLoadRequest requestWithReferrer = request;
requestWithReferrer.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
-
- Page* page = m_frame->page();
- if (page)
- page = page->chrome()->createWindow(m_frame, requestWithReferrer, features);
+ addHTTPOriginIfNeeded(requestWithReferrer.resourceRequest(), outgoingOrigin());
+
+ Page* oldPage = m_frame->page();
+ if (!oldPage)
+ return 0;
+
+ Page* page = oldPage->chrome()->createWindow(m_frame, requestWithReferrer, features);
if (!page)
return 0;
@@ -375,11 +386,17 @@ void FrameLoader::changeLocation(const String& url, const String& referrer, bool
changeLocation(completeURL(url), referrer, lockHistory, userGesture);
}
+
void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool userGesture)
{
+ RefPtr<Frame> protect(m_frame);
+
ResourceRequestCachePolicy policy = (m_cachePolicy == CachePolicyReload) || (m_cachePolicy == CachePolicyRefresh)
? ReloadIgnoringCacheData : UseProtocolCachePolicy;
ResourceRequest request(url, referrer, policy);
+#ifdef ANDROID_USER_GESTURE
+ request.setUserGesture(userGesture);
+#endif
if (executeIfJavaScriptURL(request.url(), userGesture))
return;
@@ -387,6 +404,16 @@ void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool l
urlSelected(request, "_self", 0, lockHistory, userGesture);
}
+void FrameLoader::urlSelected(const FrameLoadRequest& request, Event* event, bool lockHistory)
+{
+ FrameLoadRequest copy = request;
+ if (copy.resourceRequest().httpReferrer().isEmpty())
+ copy.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
+ addHTTPOriginIfNeeded(copy.resourceRequest(), outgoingOrigin());
+
+ loadFrameRequestWithFormAndValues(copy, lockHistory, event, 0, HashMap<String, String>());
+}
+
void FrameLoader::urlSelected(const ResourceRequest& request, const String& _target, Event* triggeringEvent, bool lockHistory, bool userGesture)
{
if (executeIfJavaScriptURL(request.url(), userGesture, false))
@@ -397,11 +424,11 @@ void FrameLoader::urlSelected(const ResourceRequest& request, const String& _tar
target = m_frame->document()->baseTarget();
FrameLoadRequest frameRequest(request, target);
+#ifdef ANDROID_USER_GESTURE
+ frameRequest.setWasUserGesture(userGesture);
+#endif
- if (frameRequest.resourceRequest().httpReferrer().isEmpty())
- frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
-
- urlSelected(frameRequest, triggeringEvent, lockHistory, userGesture);
+ urlSelected(frameRequest, triggeringEvent, lockHistory);
}
bool FrameLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName)
@@ -417,13 +444,9 @@ bool FrameLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String
// Support for <frame src="javascript:string">
KURL scriptURL;
KURL url;
-#ifdef ANDROID_JAVASCRIPT_SECURITY
if (protocolIs(urlString, "javascript")) {
-#else
- if (urlString.startsWith("javascript:", false)) {
-#endif
- scriptURL = urlString.deprecatedString();
- url = "about:blank";
+ scriptURL = KURL(urlString);
+ url = blankURL();
} else
url = completeURL(urlString);
@@ -455,7 +478,7 @@ Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL
}
if (!canLoad(url, referrer)) {
- FrameLoader::reportLocalLoadFailed(m_frame->page(), url.string());
+ FrameLoader::reportLocalLoadFailed(m_frame, url.string());
return 0;
}
@@ -469,9 +492,11 @@ Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL
}
frame->loader()->m_isComplete = false;
-
- if (ownerElement->renderer() && frame->view())
- static_cast<RenderWidget*>(ownerElement->renderer())->setWidget(frame->view());
+
+ RenderObject* renderer = ownerElement->renderer();
+ FrameView* view = frame->view();
+ if (renderer && renderer->isWidget() && view)
+ static_cast<RenderWidget*>(renderer)->setWidget(view);
checkCallImplicitClose();
@@ -481,7 +506,7 @@ Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL
// FIXME: In this case the Frame will have finished loading before
// it's being added to the child list. It would be a good idea to
// create the child first, then invoke the loader separately.
- if (url.isEmpty() || url == "about:blank") {
+ if (url.isEmpty() || url == blankURL()) {
frame->loader()->completed();
frame->loader()->checkCompleted();
}
@@ -504,18 +529,16 @@ void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<F
{
ASSERT(formData);
+ if (!m_frame->page())
+ return;
+
KURL u = completeURL(url.isNull() ? "" : url);
// FIXME: Do we really need to special-case an empty URL?
// Would it be better to just go on with the form submisson and let the I/O fail?
if (u.isEmpty())
return;
-#ifdef ANDROID_JAVASCRIPT_SECURITY
if (u.protocolIs("javascript")) {
-#else
- DeprecatedString urlString = u.deprecatedString();
- if (urlString.startsWith("javascript:", false)) {
-#endif
m_isExecutingJavaScriptFormAction = true;
executeIfJavaScriptURL(u, false, false);
m_isExecutingJavaScriptFormAction = false;
@@ -530,7 +553,12 @@ void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<F
return;
}
+ formData->generateFiles(m_frame->page()->chrome()->client());
+
FrameLoadRequest frameRequest;
+#ifdef ANDROID_USER_GESTURE
+ frameRequest.setWasUserGesture(userGestureHint());
+#endif
if (!m_outgoingReferrer.isEmpty())
frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
@@ -545,11 +573,11 @@ void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<F
String query = u.query();
if (!query.isEmpty())
query.append('&');
- u.setQuery((query + body).deprecatedString());
+ u.setQuery(query + body);
}
if (strcmp(action, "GET") == 0) {
- u.setQuery(formData->flattenToString().deprecatedString());
+ u.setQuery(formData->flattenToString());
} else {
if (!isMailtoForm)
frameRequest.resourceRequest().setHTTPBody(formData.get());
@@ -563,6 +591,7 @@ void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<F
}
frameRequest.resourceRequest().setURL(u);
+ addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin());
submitForm(frameRequest, event);
}
@@ -578,10 +607,14 @@ void FrameLoader::stopLoading(bool sendUnload)
Node* currentFocusedNode = m_frame->document()->focusedNode();
if (currentFocusedNode)
currentFocusedNode->aboutToUnload();
- m_frame->document()->dispatchWindowEvent(unloadEvent, false, false);
+ m_frame->document()->dispatchWindowEvent(eventNames().unloadEvent, false, false);
if (m_frame->document())
m_frame->document()->updateRendering();
m_wasUnloadEventEmitted = true;
+ if (m_frame->eventHandler()->pendingFrameUnloadEventCount())
+ m_frame->eventHandler()->clearPendingFrameUnloadEventCount();
+ if (m_frame->eventHandler()->pendingFrameBeforeUnloadEventCount())
+ m_frame->eventHandler()->clearPendingFrameBeforeUnloadEventCount();
}
}
if (m_frame->document() && !m_frame->document()->inPageCache())
@@ -603,10 +636,9 @@ void FrameLoader::stopLoading(bool sendUnload)
if (Document* doc = m_frame->document()) {
if (DocLoader* docLoader = doc->docLoader())
cache()->loader()->cancelRequests(docLoader);
+
+ doc->stopActiveDOMObjects();
- XMLHttpRequest::cancelRequests(doc);
-
-// ANDROID_FIX ENABLE(DATABASE) missing from WebKit sources
#if ENABLE(DATABASE)
doc->stopDatabases();
#endif
@@ -668,16 +700,16 @@ KURL FrameLoader::iconURL()
{
// If this isn't a top level frame, return nothing
if (m_frame->tree() && m_frame->tree()->parent())
- return "";
-
+ return KURL();
+
// If we have an iconURL from a Link element, return that
if (m_frame->document() && !m_frame->document()->iconURL().isEmpty())
- return m_frame->document()->iconURL().deprecatedString();
-
+ return KURL(m_frame->document()->iconURL());
+
// Don't return a favicon iconURL unless we're http or https
- if (m_URL.protocol() != "http" && m_URL.protocol() != "https")
- return "";
-
+ if (!m_URL.protocolIs("http") && !m_URL.protocolIs("https"))
+ return KURL();
+
KURL url;
url.setProtocol(m_URL.protocol());
url.setHost(m_URL.host());
@@ -695,7 +727,7 @@ bool FrameLoader::didOpenURL(const KURL& url)
return false;
cancelRedirection();
- m_frame->editor()->setLastEditCommand(0);
+ m_frame->editor()->clearLastEditCommand();
closeURL();
m_isComplete = false;
@@ -706,7 +738,7 @@ bool FrameLoader::didOpenURL(const KURL& url)
m_frame->setJSDefaultStatusBarText(String());
m_URL = url;
- if (m_URL.protocol().startsWith("http") && !m_URL.host().isEmpty() && m_URL.path().isEmpty())
+ if ((m_URL.protocolIs("http") || m_URL.protocolIs("https")) && !m_URL.host().isEmpty() && m_URL.path().isEmpty())
m_URL.setPath("/");
m_workingURL = m_URL;
@@ -728,20 +760,16 @@ void FrameLoader::didExplicitOpen()
// Cancelling redirection here works for all cases because document.open
// implicitly precedes document.write.
cancelRedirection();
- if (m_frame->document()->url() != "about:blank")
+ if (m_frame->document()->url() != blankURL())
m_URL = m_frame->document()->url();
}
bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool replaceDocument)
{
-#ifdef ANDROID_JAVASCRIPT_SECURITY
if (!url.protocolIs("javascript"))
-#else
- if (!url.deprecatedString().startsWith("javascript:", false))
-#endif
return false;
- String script = KURL::decode_string(url.deprecatedString().mid(strlen("javascript:")));
+ String script = decodeURLEscapeSequences(url.string().substring(strlen("javascript:")));
JSValue* result = executeScript(script, userGesture);
String scriptResult;
@@ -766,18 +794,18 @@ bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool
JSValue* FrameLoader::executeScript(const String& script, bool forceUserGesture)
{
- return executeScript(forceUserGesture ? String() : m_URL.string(), 0, script);
+ return executeScript(forceUserGesture ? String() : m_URL.string(), 1, script);
}
JSValue* FrameLoader::executeScript(const String& url, int baseLine, const String& script)
{
- if (!m_frame->scriptProxy()->isEnabled())
- return 0;
+ if (!m_frame->script()->isEnabled() || m_frame->script()->isPaused())
+ return noValue();
bool wasRunningScript = m_isRunningScript;
m_isRunningScript = true;
- JSValue* result = m_frame->scriptProxy()->evaluate(url, baseLine, script);
+ JSValue* result = m_frame->script()->evaluate(url, baseLine, script);
if (!wasRunningScript) {
m_isRunningScript = false;
@@ -822,27 +850,27 @@ void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects)
// Do this after detaching the document so that the unload event works.
if (clearWindowProperties) {
- m_frame->clearScriptProxy();
m_frame->clearDOMWindow();
+ m_frame->script()->clearWindowShell();
}
- m_frame->selectionController()->clear();
+ m_frame->selection()->clear();
m_frame->eventHandler()->clear();
if (m_frame->view())
m_frame->view()->clear();
-
+
m_frame->setSelectionGranularity(CharacterGranularity);
- // Do not drop the document before the script proxy and view are cleared, as some destructors
- // might still try to access the document.
+ // Do not drop the document before the ScriptController and view are cleared
+ // as some destructors might still try to access the document.
m_frame->setDocument(0);
m_decoder = 0;
m_containsPlugIns = false;
-
+
if (clearScriptObjects)
- m_frame->clearScriptObjects();
-
+ m_frame->script()->clearScriptObjects();
+
m_redirectionTimer.stop();
m_scheduledRedirection.clear();
@@ -881,7 +909,7 @@ void FrameLoader::receivedFirstData()
if (url.isEmpty())
url = m_URL.string();
else
- url = m_frame->document()->completeURL(url);
+ url = m_frame->document()->completeURL(url).string();
scheduleHTTPRedirection(delay, url);
}
@@ -919,28 +947,24 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin)
m_isDisplayingInitialEmptyDocument = m_creatingInitialEmptyDocument;
KURL ref(url);
- ref.setUser(DeprecatedString());
- ref.setPass(DeprecatedString());
- ref.setRef(DeprecatedString());
+ ref.setUser(String());
+ ref.setPass(String());
+ ref.setRef(String());
m_outgoingReferrer = ref.string();
m_URL = url;
- KURL baseurl;
- if (!m_URL.isEmpty())
- baseurl = m_URL;
-
- RefPtr<Document> document = DOMImplementation::instance()->createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode());
+ RefPtr<Document> document = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode());
m_frame->setDocument(document);
- document->setURL(m_URL.deprecatedString());
- // We prefer m_baseURL over m_URL because m_URL changes when we are
- // about to load a new page.
- document->setBaseURL(baseurl.deprecatedString());
+ document->setURL(m_URL);
if (m_decoder)
document->setDecoder(m_decoder.get());
if (forcedSecurityOrigin)
document->setSecurityOrigin(forcedSecurityOrigin.get());
+ m_frame->domWindow()->setURL(document->url());
+ m_frame->domWindow()->setSecurityOrigin(document->securityOrigin());
+
updatePolicyBaseURL();
Settings* settings = document->settings();
@@ -949,6 +973,12 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin)
document->docLoader()->setBlockNetworkImage(settings && settings->blockNetworkImage());
#endif
+ if (m_documentLoader) {
+ String dnsPrefetchControl = m_documentLoader->response().httpHeaderField("X-DNS-Prefetch-Control");
+ if (!dnsPrefetchControl.isEmpty())
+ document->parseDNSPrefetchControlHeader(dnsPrefetchControl);
+ }
+
#if FRAME_LOADS_USER_STYLESHEET
KURL userStyleSheet = settings ? settings->userStyleSheetLocation() : KURL();
if (!userStyleSheet.isEmpty())
@@ -960,7 +990,7 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin)
document->implicitOpen();
if (m_frame->view())
- m_frame->view()->resizeContents(0, 0);
+ m_frame->view()->setContentsSize(IntSize());
#if USE(LOW_BANDWIDTH_DISPLAY)
// Low bandwidth display is a first pass display without external resources
@@ -992,12 +1022,16 @@ void FrameLoader::write(const char* str, int len, bool flush)
if (!m_decoder) {
Settings* settings = m_frame->settings();
- m_decoder = new TextResourceDecoder(m_responseMIMEType, settings ? settings->defaultTextEncodingName() : String());
- if (!m_encoding.isNull())
+ m_decoder = TextResourceDecoder::create(m_responseMIMEType, settings ? settings->defaultTextEncodingName() : String());
+ if (m_encoding.isEmpty()) {
+ Frame* parentFrame = m_frame->tree()->parent();
+ if (parentFrame && parentFrame->document()->securityOrigin()->canAccess(m_frame->document()->securityOrigin()))
+ m_decoder->setEncoding(parentFrame->document()->inputEncoding(), TextResourceDecoder::DefaultEncoding);
+ } else {
m_decoder->setEncoding(m_encoding,
m_encodingWasChosenByUser ? TextResourceDecoder::UserChosenEncoding : TextResourceDecoder::EncodingFromHTTPHeader);
- if (m_frame->document())
- m_frame->document()->setDecoder(m_decoder.get());
+ }
+ m_frame->document()->setDecoder(m_decoder.get());
}
String decoded = m_decoder->decode(str, len);
@@ -1015,7 +1049,6 @@ void FrameLoader::write(const char* str, int len, bool flush)
if (!m_receivedData) {
m_receivedData = true;
- m_frame->document()->determineParseMode(decoded);
if (m_decoder->encoding().usesVisualOrdering())
m_frame->document()->setVisuallyOrdered();
m_frame->document()->recalcStyle(Node::Force);
@@ -1049,7 +1082,7 @@ void FrameLoader::end()
void FrameLoader::endIfNotLoadingMainResource()
{
- if (m_isLoadingMainResource)
+ if (m_isLoadingMainResource || !m_frame->page())
return;
// http://bugs.webkit.org/show_bug.cgi?id=10854
@@ -1073,9 +1106,6 @@ void FrameLoader::endIfNotLoadingMainResource()
// become true. An example is when a subframe is a pure text doc, and that subframe is the
// last one to complete.
checkCompleted();
-
- if (m_documentLoader && !m_documentLoader->isLoadingFromCachedPage())
- startIconLoader();
}
void FrameLoader::iconLoadDecisionAvailable()
@@ -1144,14 +1174,19 @@ void FrameLoader::startIconLoader()
m_iconLoader->startLoading();
}
+void FrameLoader::setLocalLoadPolicy(LocalLoadPolicy policy)
+{
+ localLoadPolicy = policy;
+}
+
bool FrameLoader::restrictAccessToLocal()
{
- return m_restrictAccessToLocal;
+ return localLoadPolicy != FrameLoader::AllowLocalLoadsForAll;
}
-void FrameLoader::setRestrictAccessToLocal(bool access)
+bool FrameLoader::allowSubstituteDataAccessToLocal()
{
- m_restrictAccessToLocal = access;
+ return localLoadPolicy != FrameLoader::AllowLocalLoadsForLocalOnly;
}
static HashSet<String, CaseFoldingHash>& localSchemes()
@@ -1215,21 +1250,19 @@ void FrameLoader::restoreDocumentState()
void FrameLoader::gotoAnchor()
{
// If our URL has no ref, then we have no place we need to jump to.
- // OTOH if css target was set previously, we want to set it to 0, recalc
+ // OTOH If CSS target was set previously, we want to set it to 0, recalc
// and possibly repaint because :target pseudo class may have been
- // set(See bug 11321)
- if (!m_URL.hasRef() &&
- !(m_frame->document() && m_frame->document()->getCSSTarget()))
+ // set (see bug 11321).
+ if (!m_URL.hasRef() && !(m_frame->document() && m_frame->document()->getCSSTarget()))
return;
- DeprecatedString ref = m_URL.encodedHtmlRef();
- if (!gotoAnchor(ref)) {
- // Can't use htmlRef() here because it doesn't know which encoding to use to decode.
- // Decoding here has to match encoding in completeURL, which means it has to use the
- // page's encoding rather than UTF-8.
- if (m_decoder)
- gotoAnchor(KURL::decode_string(ref, m_decoder->encoding()));
- }
+ String ref = m_URL.ref();
+ if (gotoAnchor(ref))
+ return;
+
+ // Try again after decoding the ref, based on the document's encoding.
+ if (m_decoder)
+ gotoAnchor(decodeURLEscapeSequences(ref, m_decoder->encoding()));
}
void FrameLoader::finishedParsing()
@@ -1239,7 +1272,8 @@ void FrameLoader::finishedParsing()
// This can be called from the Frame's destructor, in which case we shouldn't protect ourselves
// because doing so will cause us to re-enter the destructor when protector goes out of scope.
- RefPtr<Frame> protector = m_frame->refCount() > 0 ? m_frame : 0;
+ // Null-checking the FrameView indicates whether or not we're in the destructor.
+ RefPtr<Frame> protector = m_frame->view() ? m_frame : 0;
checkCompleted();
@@ -1316,8 +1350,9 @@ void FrameLoader::scheduleCheckCompleted()
void FrameLoader::checkLoadCompleteTimerFired(Timer<FrameLoader>*)
{
- if (m_frame->page())
- checkLoadComplete();
+ if (!m_frame->page())
+ return;
+ checkLoadComplete();
}
void FrameLoader::scheduleCheckLoadComplete()
@@ -1356,7 +1391,7 @@ String FrameLoader::baseTarget() const
KURL FrameLoader::completeURL(const String& url)
{
ASSERT(m_frame->document());
- return m_frame->document()->completeURL(url).deprecatedString();
+ return m_frame->document()->completeURL(url);
}
void FrameLoader::scheduleHTTPRedirection(double delay, const String& url)
@@ -1364,17 +1399,33 @@ void FrameLoader::scheduleHTTPRedirection(double delay, const String& url)
if (delay < 0 || delay > INT_MAX / 1000)
return;
+ if (!m_frame->page())
+ return;
+
// We want a new history item if the refresh timeout is > 1 second.
if (!m_scheduledRedirection || delay <= m_scheduledRedirection->delay)
+#ifdef ANDROID_USER_GESTURE
+ {
+ bool wasUserGesture = false;
+ DocumentLoader* docLoader = activeDocumentLoader();
+ if (docLoader)
+ wasUserGesture = docLoader->request().userGesture();
+ scheduleRedirection(new ScheduledRedirection(delay, url, delay <= 1, wasUserGesture));
+ }
+#else
scheduleRedirection(new ScheduledRedirection(delay, url, delay <= 1, false));
+#endif
}
void FrameLoader::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool wasUserGesture)
-{
+{
+ if (!m_frame->page())
+ return;
+
// If the URL we're going to navigate to is the same as the current one, except for the
// fragment part, we don't need to schedule the location change.
- KURL u(url.deprecatedString());
- if (u.hasRef() && equalIgnoringRef(m_URL, u)) {
+ KURL parsedURL(url);
+ if (parsedURL.hasRef() && equalIgnoringRef(m_URL, parsedURL)) {
changeLocation(url, referrer, lockHistory, wasUserGesture);
return;
}
@@ -1399,6 +1450,9 @@ void FrameLoader::scheduleLocationChange(const String& url, const String& referr
void FrameLoader::scheduleRefresh(bool wasUserGesture)
{
+ if (!m_frame->page())
+ return;
+
// Handle a location change of a page with no document as a special case.
// This may happen when a frame requests a refresh of another frame.
bool duringLoad = !m_frame->document();
@@ -1431,8 +1485,10 @@ bool FrameLoader::isLocationChange(const ScheduledRedirection& redirection)
void FrameLoader::scheduleHistoryNavigation(int steps)
{
- // navigation will always be allowed in the 0 steps case, which is OK because
- // that's supposed to force a reload.
+ if (!m_frame->page())
+ return;
+
+ // navigation will always be allowed in the 0 steps case, which is OK because that's supposed to force a reload.
if (!canGoBackOrForward(steps)) {
cancelRedirection();
return;
@@ -1485,6 +1541,8 @@ void FrameLoader::goBackOrForward(int distance)
void FrameLoader::redirectionTimerFired(Timer<FrameLoader>*)
{
+ ASSERT(m_frame->page());
+
OwnPtr<ScheduledRedirection> redirection(m_scheduledRedirection.release());
switch (redirection->type) {
@@ -1505,9 +1563,87 @@ void FrameLoader::redirectionTimerFired(Timer<FrameLoader>*)
goBackOrForward(redirection->historySteps);
return;
}
+
ASSERT_NOT_REACHED();
}
+/*
+ In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree.
+ The item that was the target of the user's navigation is designated as the "targetItem".
+ When this method is called with doClip=YES we're able to create the whole tree except for the target's children,
+ which will be loaded in the future. That part of the tree will be filled out as the child loads are committed.
+*/
+void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, Frame* childFrame)
+{
+ ASSERT(childFrame);
+ HistoryItem* parentItem = currentHistoryItem();
+ FrameLoadType loadType = this->loadType();
+ FrameLoadType childLoadType = FrameLoadTypeRedirectWithLockedHistory;
+
+ KURL workingURL = url;
+
+ // If we're moving in the backforward list, we might want to replace the content
+ // of this child frame with whatever was there at that point.
+ // Reload will maintain the frame contents, LoadSame will not.
+ if (parentItem && parentItem->children().size() &&
+ (isBackForwardLoadType(loadType) || loadType == FrameLoadTypeReloadAllowingStaleData))
+ {
+ HistoryItem* childItem = parentItem->childItemWithName(childFrame->tree()->name());
+ if (childItem) {
+ // Use the original URL to ensure we get all the side-effects, such as
+ // onLoad handlers, of any redirects that happened. An example of where
+ // this is needed is Radar 3213556.
+ workingURL = KURL(childItem->originalURLString());
+ // These behaviors implied by these loadTypes should apply to the child frames
+ childLoadType = loadType;
+
+ if (isBackForwardLoadType(loadType)) {
+ // For back/forward, remember this item so we can traverse any child items as child frames load
+ childFrame->loader()->setProvisionalHistoryItem(childItem);
+ } else {
+ // For reload, just reinstall the current item, since a new child frame was created but we won't be creating a new BF item
+ childFrame->loader()->setCurrentHistoryItem(childItem);
+ }
+ }
+ }
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ RefPtr<Archive> subframeArchive = activeDocumentLoader()->popArchiveForSubframe(childFrame->tree()->name());
+
+ if (subframeArchive)
+ childFrame->loader()->loadArchive(subframeArchive.release());
+ else
+#endif
+#ifdef ANDROID_USER_GESTURE
+ childFrame->loader()->loadURL(workingURL, referer, String(), childLoadType, 0, 0, false);
+#else
+ childFrame->loader()->loadURL(workingURL, referer, String(), childLoadType, 0, 0);
+#endif
+}
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+void FrameLoader::loadArchive(PassRefPtr<Archive> prpArchive)
+{
+ RefPtr<Archive> archive = prpArchive;
+
+ ArchiveResource* mainResource = archive->mainResource();
+ ASSERT(mainResource);
+ if (!mainResource)
+ return;
+
+ SubstituteData substituteData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), KURL());
+
+ ResourceRequest request(mainResource->url());
+#if PLATFORM(MAC)
+ request.applyWebArchiveHackForMail();
+#endif
+
+ RefPtr<DocumentLoader> documentLoader = m_client->createDocumentLoader(request, substituteData);
+ documentLoader->addAllArchiveResources(archive.get());
+ load(documentLoader.get());
+}
+#endif
+
String FrameLoader::encoding() const
{
if (m_encodingWasChosenByUser && !m_encoding.isEmpty())
@@ -1530,7 +1666,7 @@ bool FrameLoader::gotoAnchor(const String& name)
m_frame->document()->setGotoAnchorNeededAfterStylesheetsLoad(false);
Node* anchorNode = m_frame->document()->getElementById(AtomicString(name));
- if (!anchorNode)
+ if (!anchorNode && !name.isEmpty())
anchorNode = m_frame->document()->anchors()->namedItem(name, !m_frame->document()->inCompatMode());
#if ENABLE(SVG)
@@ -1566,8 +1702,8 @@ bool FrameLoader::gotoAnchor(const String& name)
// really mess things up if an anchor scroll comes at a bad moment.
if (m_frame->document()) {
m_frame->document()->updateRendering();
- // Only do a layout if changes have occurred that make it necessary.
- if (m_frame->view() && m_frame->document()->renderer() && m_frame->document()->renderer()->needsLayout())
+ // Only do a layout if changes have occurred that make it necessary.
+ if (m_frame->view() && m_frame->contentRenderer() && m_frame->contentRenderer()->needsLayout())
m_frame->view()->layout();
}
@@ -1582,7 +1718,7 @@ bool FrameLoader::gotoAnchor(const String& name)
rect = anchorNode->getRect();
}
if (renderer)
- renderer->enclosingLayer()->scrollRectToVisible(rect, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways);
+ renderer->enclosingLayer()->scrollRectToVisible(rect, true, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways);
return true;
}
@@ -1625,8 +1761,8 @@ bool FrameLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool
{
// Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that
// can handle TIFF (which QuickTime can also handle) they probably intended to override QT.
- if ((mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) {
- String pluginName = PluginInfoStore::pluginNameForMIMEType(mimeType);
+ if (m_frame->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) {
+ String pluginName = m_frame->page()->pluginData()->pluginNameForMimeType(mimeType);
if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false))
return true;
}
@@ -1648,8 +1784,8 @@ bool FrameLoader::loadPlugin(RenderPart* renderer, const KURL& url, const String
if (renderer->node() && renderer->node()->isElementNode())
pluginElement = static_cast<Element*>(renderer->node());
- if (!canLoad(url, frame()->document())) {
- FrameLoader::reportLocalLoadFailed(m_frame->page(), url.string());
+ if (!canLoad(url, String(), frame()->document())) {
+ FrameLoader::reportLocalLoadFailed(m_frame, url.string());
return false;
}
@@ -1671,9 +1807,13 @@ void FrameLoader::clearRecordedFormValues()
m_formValuesAboutToBeSubmitted.clear();
}
-void FrameLoader::recordFormValue(const String& name, const String& value, PassRefPtr<HTMLFormElement> element)
+void FrameLoader::setFormAboutToBeSubmitted(PassRefPtr<HTMLFormElement> element)
{
m_formAboutToBeSubmitted = element;
+}
+
+void FrameLoader::recordFormValue(const String& name, const String& value)
+{
m_formValuesAboutToBeSubmitted.set(name, value);
}
@@ -1688,6 +1828,14 @@ String FrameLoader::outgoingReferrer() const
return m_outgoingReferrer;
}
+String FrameLoader::outgoingOrigin() const
+{
+ if (m_frame->document())
+ return m_frame->document()->securityOrigin()->toString();
+
+ return SecurityOrigin::createEmpty()->toString();
+}
+
Frame* FrameLoader::opener()
{
return m_opener;
@@ -1701,8 +1849,10 @@ void FrameLoader::setOpener(Frame* opener)
opener->loader()->m_openedFrames.add(m_frame);
m_opener = opener;
- if (m_frame->document())
- m_frame->document()->initSecurityOrigin();
+ if (m_frame->document()) {
+ m_frame->document()->initSecurityContext();
+ m_frame->domWindow()->setSecurityOrigin(m_frame->document()->securityOrigin());
+ }
}
bool FrameLoader::openedByDOM() const
@@ -1754,8 +1904,8 @@ bool FrameLoader::userGestureHint()
while (rootFrame->tree()->parent())
rootFrame = rootFrame->tree()->parent();
- if (rootFrame->scriptProxy()->isEnabled())
- return rootFrame->scriptProxy()->processingUserGesture();
+ if (rootFrame->script()->isEnabled())
+ return rootFrame->script()->processingUserGesture();
return true; // If JavaScript is disabled, a user gesture must have initiated the navigation
}
@@ -1808,13 +1958,13 @@ bool FrameLoader::canCachePage()
// they would need to be destroyed and then recreated, and there is no way that we can recreate
// the right NPObjects. See <rdar://problem/5197041> for more information.
&& !m_containsPlugIns
- && !m_URL.protocol().startsWith("https")
+ && !m_URL.protocolIs("https")
&& m_frame->document()
- && !m_frame->document()->applets()->length()
- && !m_frame->document()->hasWindowEventListener(unloadEvent)
+ && !m_frame->document()->hasWindowEventListener(eventNames().unloadEvent)
#if ENABLE(DATABASE)
&& !m_frame->document()->hasOpenDatabases()
#endif
+ && !m_frame->document()->usingGeolocation()
&& m_frame->page()
&& m_frame->page()->backForwardList()->enabled()
&& m_frame->page()->backForwardList()->capacity() > 0
@@ -1825,7 +1975,14 @@ bool FrameLoader::canCachePage()
&& loadType != FrameLoadTypeReloadAllowingStaleData
&& loadType != FrameLoadTypeSame
&& !m_documentLoader->isLoadingInAPISense()
- && !m_documentLoader->isStopping();
+ && !m_documentLoader->isStopping()
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ // FIXME: We should investigating caching pages that have an associated
+ // application cache. <rdar://problem/5917899> tracks that work.
+ && !m_documentLoader->applicationCache()
+ && !m_documentLoader->candidateApplicationCacheGroup()
+#endif
+ ;
}
void FrameLoader::updatePolicyBaseURL()
@@ -1833,24 +1990,27 @@ void FrameLoader::updatePolicyBaseURL()
if (m_frame->tree()->parent() && m_frame->tree()->parent()->document())
setPolicyBaseURL(m_frame->tree()->parent()->document()->policyBaseURL());
else
- setPolicyBaseURL(m_URL.string());
+ setPolicyBaseURL(m_URL);
}
-void FrameLoader::setPolicyBaseURL(const String& s)
+void FrameLoader::setPolicyBaseURL(const KURL& url)
{
if (m_frame->document())
- m_frame->document()->setPolicyBaseURL(s);
+ m_frame->document()->setPolicyBaseURL(url);
for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
- child->loader()->setPolicyBaseURL(s);
+ child->loader()->setPolicyBaseURL(url);
}
-// This does the same kind of work that FrameLoader::openURL does, except it relies on the fact
+// This does the same kind of work that didOpenURL does, except it relies on the fact
// that a higher level already checked that the URLs match and the scrolling is the right thing to do.
void FrameLoader::scrollToAnchor(const KURL& url)
{
m_URL = url;
- started();
+ updateHistoryForAnchorScroll();
+ // If we were in the autoscroll/panScroll mode we want to stop it before following the link to the anchor
+ m_frame->eventHandler()->stopAutoscrollTimer();
+ started();
gotoAnchor();
// It's important to model this as a load that starts and immediately finishes.
@@ -1866,6 +2026,8 @@ bool FrameLoader::isComplete() const
void FrameLoader::scheduleRedirection(ScheduledRedirection* redirection)
{
+ ASSERT(m_frame->page());
+
stopRedirectionTimer();
m_scheduledRedirection.set(redirection);
if (!m_isComplete && redirection->type != ScheduledRedirection::redirection)
@@ -1876,6 +2038,7 @@ void FrameLoader::scheduleRedirection(ScheduledRedirection* redirection)
void FrameLoader::startRedirectionTimer()
{
+ ASSERT(m_frame->page());
ASSERT(m_scheduledRedirection);
m_redirectionTimer.stop();
@@ -1885,7 +2048,7 @@ void FrameLoader::startRedirectionTimer()
case ScheduledRedirection::redirection:
case ScheduledRedirection::locationChange:
case ScheduledRedirection::locationChangeDuringLoad:
- clientRedirected(m_scheduledRedirection->url.deprecatedString(),
+ clientRedirected(KURL(m_scheduledRedirection->url),
m_scheduledRedirection->delay,
currentTime() + m_redirectionTimer.nextFireInterval(),
m_scheduledRedirection->lockHistory,
@@ -1961,19 +2124,9 @@ void FrameLoader::setupForReplaceByMIMEType(const String& newMIMEType)
activeDocumentLoader()->setupForReplaceByMIMEType(newMIMEType);
}
-void FrameLoader::finalSetupForReplace(DocumentLoader* loader)
-{
- m_client->clearUnarchivingState(loader);
-}
-
-void FrameLoader::load(const KURL& url, Event* event)
-{
- load(ResourceRequest(url), false, true, event, 0, HashMap<String, String>());
-}
-
-void FrameLoader::load(const FrameLoadRequest& request, bool lockHistory, bool userGesture, Event* event,
- HTMLFormElement* submitForm, const HashMap<String, String>& formValues)
+void FrameLoader::loadFrameRequestWithFormState(const FrameLoadRequest& request, bool lockHistory, Event* event, PassRefPtr<FormState> prpFormState)
{
+ RefPtr<FormState> formState = prpFormState;
KURL url = request.resourceRequest().url();
String referrer;
@@ -1984,9 +2137,9 @@ void FrameLoader::load(const FrameLoadRequest& request, bool lockHistory, bool u
referrer = m_outgoingReferrer;
ASSERT(frame()->document());
- if (url.deprecatedString().startsWith("file:", false)) {
- if (!canLoad(url, frame()->document()) && !canLoad(url, referrer)) {
- FrameLoader::reportLocalLoadFailed(m_frame->page(), url.string());
+ if (url.protocolIs("file")) {
+ if (!canLoad(url, String(), frame()->document()) && !canLoad(url, referrer)) {
+ FrameLoader::reportLocalLoadFailed(m_frame, url.string());
return;
}
}
@@ -1995,7 +2148,7 @@ void FrameLoader::load(const FrameLoadRequest& request, bool lockHistory, bool u
referrer = String();
Frame* targetFrame = findFrameForNavigation(request.frameName());
-
+
if (request.resourceRequest().httpMethod() != "POST") {
FrameLoadType loadType;
if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData)
@@ -2005,24 +2158,18 @@ void FrameLoader::load(const FrameLoadRequest& request, bool lockHistory, bool u
else
loadType = FrameLoadTypeStandard;
- RefPtr<FormState> formState;
- if (submitForm && !formValues.isEmpty())
- formState = FormState::create(submitForm, formValues, m_frame);
-
#ifdef ANDROID_USER_GESTURE
- load(request.resourceRequest().url(), referrer, loadType,
- request.frameName(), event, formState.release(), userGesture);
+ loadURL(request.resourceRequest().url(), referrer, request.frameName(), loadType,
+ event, formState.release(), request.wasUserGesture());
#else
- load(request.resourceRequest().url(), referrer, loadType,
- request.frameName(), event, formState.release());
+ loadURL(request.resourceRequest().url(), referrer, request.frameName(), loadType,
+ event, formState.release());
#endif
} else
#ifdef ANDROID_USER_GESTURE
- post(request.resourceRequest().url(), referrer, request.frameName(),
- request.resourceRequest().httpBody(), request.resourceRequest().httpContentType(), event, submitForm, formValues, userGesture);
+ loadPostRequest(request.resourceRequest(), referrer, request.frameName(), event, formState.release(), request.wasUserGesture());
#else
- post(request.resourceRequest().url(), referrer, request.frameName(),
- request.resourceRequest().httpBody(), request.resourceRequest().httpContentType(), event, submitForm, formValues);
+ loadPostRequest(request.resourceRequest(), referrer, request.frameName(), event, formState.release());
#endif
if (targetFrame && targetFrame != m_frame)
@@ -2030,22 +2177,36 @@ void FrameLoader::load(const FrameLoadRequest& request, bool lockHistory, bool u
page->chrome()->focus();
}
+void FrameLoader::loadFrameRequestWithFormAndValues(const FrameLoadRequest& request, bool lockHistory, Event* event,
+ HTMLFormElement* submitForm, const HashMap<String, String>& formValues)
+{
+ RefPtr<FormState> formState;
+ if (submitForm)
+ formState = FormState::create(submitForm, formValues, m_frame);
+
+ loadFrameRequestWithFormState(request, lockHistory, event, formState.release());
+}
+
#ifdef ANDROID_USER_GESTURE
-void FrameLoader::load(const KURL& newURL, const String& referrer, FrameLoadType newLoadType,
- const String& frameName, Event* event, PassRefPtr<FormState> formState, bool userGesture)
+void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, FrameLoadType newLoadType,
+ Event* event, PassRefPtr<FormState> prpFormState, bool userGesture)
#else
-void FrameLoader::load(const KURL& newURL, const String& referrer, FrameLoadType newLoadType,
- const String& frameName, Event* event, PassRefPtr<FormState> formState)
+void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, FrameLoadType newLoadType,
+ Event* event, PassRefPtr<FormState> prpFormState)
#endif
{
+ RefPtr<FormState> formState = prpFormState;
bool isFormSubmission = formState;
ResourceRequest request(newURL);
#ifdef ANDROID_USER_GESTURE
request.setUserGesture(userGesture);
#endif
- if (!referrer.isEmpty())
+ if (!referrer.isEmpty()) {
request.setHTTPReferrer(referrer);
+ RefPtr<SecurityOrigin> referrerOrigin = SecurityOrigin::createFromString(referrer);
+ addHTTPOriginIfNeeded(request, referrerOrigin->toString());
+ }
addExtraFieldsToRequest(request, true, event || isFormSubmission);
if (newLoadType == FrameLoadTypeReload)
request.setCachePolicy(ReloadIgnoringCacheData);
@@ -2057,9 +2218,9 @@ void FrameLoader::load(const KURL& newURL, const String& referrer, FrameLoadType
if (!frameName.isEmpty()) {
if (Frame* targetFrame = findFrameForNavigation(frameName))
#ifdef ANDROID_USER_GESTURE
- targetFrame->loader()->load(newURL, referrer, newLoadType, String(), event, formState, userGesture);
+ targetFrame->loader()->loadURL(newURL, referrer, String(), newLoadType, event, formState, userGesture);
#else
- targetFrame->loader()->load(newURL, referrer, newLoadType, String(), event, formState);
+ targetFrame->loader()->loadURL(newURL, referrer, String(), newLoadType, event, formState);
#endif
else
checkNewWindowPolicy(action, request, formState, frameName);
@@ -2073,22 +2234,7 @@ void FrameLoader::load(const KURL& newURL, const String& referrer, FrameLoadType
// Make sure to do scroll to anchor processing even if the URL is
// exactly the same so pages with '#' links and DHTML side effects
// work properly.
- if (!isFormSubmission
- && newLoadType != FrameLoadTypeReload
- && newLoadType != FrameLoadTypeSame
- && !shouldReload(newURL, url())
- // We don't want to just scroll if a link from within a
- // frameset is trying to reload the frameset into _top.
- && !m_frame->isFrameSet()) {
-
- // Just do anchor navigation within the existing content.
-
- // We don't do this if we are submitting a form, explicitly reloading,
- // currently displaying a frameset, or if the new URL does not have a fragment.
- // These rules are based on what KHTML was doing in KHTMLPart::openURL.
-
- // FIXME: What about load types other than Standard and Reload?
-
+ if (shouldScrollToAnchor(isFormSubmission, newLoadType, newURL)) {
oldDocumentLoader->setTriggeringAction(action);
stopPolicyCheck();
checkNavigationPolicy(request, oldDocumentLoader.get(), formState,
@@ -2096,7 +2242,7 @@ void FrameLoader::load(const KURL& newURL, const String& referrer, FrameLoadType
} else {
// must grab this now, since this load may stop the previous load and clear this flag
bool isRedirect = m_quickRedirectComing;
- load(request, action, newLoadType, formState);
+ loadWithNavigationAction(request, action, newLoadType, formState);
if (isRedirect) {
m_quickRedirectComing = false;
if (m_provisionalDocumentLoader)
@@ -2144,7 +2290,7 @@ void FrameLoader::load(const ResourceRequest& request, const String& frameName)
checkNewWindowPolicy(NavigationAction(request.url(), NavigationTypeOther), request, 0, frameName);
}
-void FrameLoader::load(const ResourceRequest& request, const NavigationAction& action, FrameLoadType type, PassRefPtr<FormState> formState)
+void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, FrameLoadType type, PassRefPtr<FormState> formState)
{
RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData());
@@ -2152,7 +2298,7 @@ void FrameLoader::load(const ResourceRequest& request, const NavigationAction& a
if (m_documentLoader)
loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
- load(loader.get(), type, formState);
+ loadWithDocumentLoader(loader.get(), type, formState);
}
void FrameLoader::load(DocumentLoader* newDocumentLoader)
@@ -2171,73 +2317,84 @@ void FrameLoader::load(DocumentLoader* newDocumentLoader)
newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding());
// When we loading alternate content for an unreachable URL that we're
- // visiting in the b/f list, we treat it as a reload so the b/f list
+ // visiting in the history list, we treat it as a reload so the history list
// is appropriately maintained.
+ //
+ // FIXME: This seems like a dangerous overloading of the meaning of "FrameLoadTypeReload" ...
+ // shouldn't a more explicit type of reload be defined, that means roughly
+ // "load without affecting history" ?
if (shouldReloadToHandleUnreachableURL(newDocumentLoader)) {
ASSERT(type == FrameLoadTypeStandard);
type = FrameLoadTypeReload;
}
- load(newDocumentLoader, type, 0);
+ loadWithDocumentLoader(newDocumentLoader, type, 0);
}
-void FrameLoader::load(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> formState)
+void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState)
{
ASSERT(m_client->hasWebView());
// Unfortunately the view must be non-nil, this is ultimately due
// to parser requiring a FrameView. We should fix this dependency.
- ASSERT(m_client->hasFrameView());
+ ASSERT(m_frame->view());
m_policyLoadType = type;
+ RefPtr<FormState> formState = prpFormState;
+ bool isFormSubmission = formState;
- if (Frame* parent = m_frame->tree()->parent())
- loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding());
+ const KURL& newURL = loader->request().url();
- stopPolicyCheck();
- setPolicyDocumentLoader(loader);
+ if (shouldScrollToAnchor(isFormSubmission, m_policyLoadType, newURL)) {
+ RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader;
+ NavigationAction action(newURL, m_policyLoadType, isFormSubmission);
- checkNavigationPolicy(loader->request(), loader, formState,
- callContinueLoadAfterNavigationPolicy, this);
-}
+ oldDocumentLoader->setTriggeringAction(action);
+ stopPolicyCheck();
+ checkNavigationPolicy(loader->request(), oldDocumentLoader.get(), formState,
+ callContinueFragmentScrollAfterNavigationPolicy, this);
+ } else {
+ if (Frame* parent = m_frame->tree()->parent())
+ loader->setOverrideEncoding(parent->loader()->documentLoader()->overrideEncoding());
-// FIXME: It would be nice if we could collapse these into one or two functions.
-bool FrameLoader::canLoad(const KURL& url, const String& referrer)
-{
- if (!shouldTreatURLAsLocal(url.string()))
- return true;
+ stopPolicyCheck();
+ setPolicyDocumentLoader(loader);
- return shouldTreatURLAsLocal(referrer);
+ checkNavigationPolicy(loader->request(), loader, formState,
+ callContinueLoadAfterNavigationPolicy, this);
+ }
}
-bool FrameLoader::canLoad(const KURL& url, const Document* doc)
+bool FrameLoader::canLoad(const KURL& url, const String& referrer, const Document* doc)
{
+ // We can always load any URL that isn't considered local (e.g. http URLs)
if (!shouldTreatURLAsLocal(url.string()))
return true;
- return doc && doc->isAllowedToLoadLocalResources();
-}
-
-bool FrameLoader::canLoad(const CachedResource& resource, const Document* doc)
-{
- if (!resource.treatAsLocal())
- return true;
-
- return doc && doc->isAllowedToLoadLocalResources();
+ // If we were provided a document, we let its local file policy dictate the result,
+ // otherwise we allow local loads only if the supplied referrer is also local.
+ if (doc)
+ return doc->securityOrigin()->canLoadLocalResources();
+ else if (!referrer.isEmpty())
+ return shouldTreatURLAsLocal(referrer);
+ else
+ return false;
}
-void FrameLoader::reportLocalLoadFailed(const Page* page, const String& url)
+void FrameLoader::reportLocalLoadFailed(Frame* frame, const String& url)
{
ASSERT(!url.isEmpty());
- if (page)
- page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, "Not allowed to load local resource: " + url, 0, String());
+ if (!frame)
+ return;
+
+ frame->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, "Not allowed to load local resource: " + url, 0, String());
}
bool FrameLoader::shouldHideReferrer(const KURL& url, const String& referrer)
{
- bool referrerIsSecureURL = referrer.startsWith("https:", false);
- bool referrerIsWebURL = referrerIsSecureURL || referrer.startsWith("http:", false);
+ bool referrerIsSecureURL = protocolIs(referrer, "https");
+ bool referrerIsWebURL = referrerIsSecureURL || protocolIs(referrer, "http");
if (!referrerIsWebURL)
return true;
@@ -2245,14 +2402,14 @@ bool FrameLoader::shouldHideReferrer(const KURL& url, const String& referrer)
if (!referrerIsSecureURL)
return false;
- bool URLIsSecureURL = url.deprecatedString().startsWith("https:", false);
+ bool URLIsSecureURL = url.protocolIs("https");
return !URLIsSecureURL;
}
const ResourceRequest& FrameLoader::initialRequest() const
{
- return activeDocumentLoader()->initialRequest();
+ return activeDocumentLoader()->originalRequest();
}
void FrameLoader::receivedData(const char* data, int length)
@@ -2260,11 +2417,6 @@ void FrameLoader::receivedData(const char* data, int length)
activeDocumentLoader()->receivedData(data, length);
}
-bool FrameLoader::willUseArchive(ResourceLoader* loader, const ResourceRequest& request, const KURL& originalURL) const
-{
- return m_client->willUseArchive(loader, request, originalURL);
-}
-
void FrameLoader::handleUnimplementablePolicy(const ResourceError& error)
{
m_delegateIsHandlingUnimplementablePolicy = true;
@@ -2352,7 +2504,7 @@ void FrameLoader::reloadAllowingStaleData(const String& encoding)
loader->setOverrideEncoding(encoding);
- load(loader.get(), FrameLoadTypeReloadAllowingStaleData, 0);
+ loadWithDocumentLoader(loader.get(), FrameLoadTypeReloadAllowingStaleData, 0);
}
void FrameLoader::reload()
@@ -2372,6 +2524,8 @@ void FrameLoader::reload()
if (!unreachableURL.isEmpty())
initialRequest = ResourceRequest(unreachableURL);
+ // Create a new document loader for the reload, this will become m_documentLoader eventually,
+ // but first it has to be the "policy" document loader, and then the "provisional" document loader.
RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(initialRequest, SubstituteData());
ResourceRequest& request = loader->request();
@@ -2385,51 +2539,73 @@ void FrameLoader::reload()
loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
- load(loader.get(), FrameLoadTypeReload, 0);
+ loadWithDocumentLoader(loader.get(), FrameLoadTypeReload, 0);
+}
+
+static bool canAccessAncestor(const SecurityOrigin* activeSecurityOrigin, Frame* targetFrame)
+{
+ // targetFrame can be NULL when we're trying to navigate a top-level frame
+ // that has a NULL opener.
+ if (!targetFrame)
+ return false;
+
+ for (Frame* ancestorFrame = targetFrame; ancestorFrame; ancestorFrame = ancestorFrame->tree()->parent()) {
+ Document* ancestorDocument = ancestorFrame->document();
+ if (!ancestorDocument)
+ return true;
+
+ const SecurityOrigin* ancestorSecurityOrigin = ancestorDocument->securityOrigin();
+ if (activeSecurityOrigin->canAccess(ancestorSecurityOrigin))
+ return true;
+ }
+
+ return false;
}
bool FrameLoader::shouldAllowNavigation(Frame* targetFrame) const
{
// The navigation change is safe if the active frame is:
- // - in the same security origin as the target or one of the target's ancestors
+ // - in the same security origin as the target or one of the target's
+ // ancestors.
+ //
// Or the target frame is:
- // - a top-level frame in the frame hierarchy
+ // - a top-level frame in the frame hierarchy and the active frame can
+ // navigate the target frame's opener per above.
if (!targetFrame)
return true;
+ // Performance optimization.
if (m_frame == targetFrame)
return true;
- if (!targetFrame->tree()->parent())
+ // Let a frame navigate the top-level window that contains it. This is
+ // important to allow because it lets a site "frame-bust" (escape from a
+ // frame created by another web site).
+ if (targetFrame == m_frame->tree()->top())
return true;
Document* activeDocument = m_frame->document();
ASSERT(activeDocument);
const SecurityOrigin* activeSecurityOrigin = activeDocument->securityOrigin();
- for (Frame* ancestorFrame = targetFrame; ancestorFrame; ancestorFrame = ancestorFrame->tree()->parent()) {
- Document* ancestorDocument = ancestorFrame->document();
- if (!ancestorDocument)
- return true;
- SecurityOrigin::Reason reason;
- const SecurityOrigin* ancestorSecurityOrigin = ancestorDocument->securityOrigin();
- if (activeSecurityOrigin->canAccess(ancestorSecurityOrigin, reason))
- return true;
- }
+ // For top-level windows, check the opener.
+ if (!targetFrame->tree()->parent() && canAccessAncestor(activeSecurityOrigin, targetFrame->loader()->opener()))
+ return true;
+
+ // In general, check the frame's ancestors.
+ if (canAccessAncestor(activeSecurityOrigin, targetFrame))
+ return true;
- if (!targetFrame->settings()->privateBrowsingEnabled()) {
+ Settings* settings = targetFrame->settings();
+ if (settings && !settings->privateBrowsingEnabled()) {
Document* targetDocument = targetFrame->document();
// FIXME: this error message should contain more specifics of why the navigation change is not allowed.
String message = String::format("Unsafe JavaScript attempt to initiate a navigation change for frame with URL %s from frame with URL %s.\n",
- targetDocument->url().utf8().data(), activeDocument->url().utf8().data());
-
- if (KJS::Interpreter::shouldPrintExceptions())
- printf("%s", message.utf8().data());
+ targetDocument->url().string().utf8().data(), activeDocument->url().string().utf8().data());
// FIXME: should we print to the console of the activeFrame as well?
- if (Page* page = targetFrame->page())
- page->chrome()->addMessageToConsole(JSMessageSource, ErrorMessageLevel, message, 1, String());
+ targetFrame->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, message, 1, String());
}
return false;
@@ -2456,8 +2632,13 @@ void FrameLoader::stopAllLoaders()
m_provisionalDocumentLoader->stopLoading();
if (m_documentLoader)
m_documentLoader->stopLoading();
+
setProvisionalDocumentLoader(0);
- m_client->clearArchivedResources();
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ if (m_documentLoader)
+ m_documentLoader->clearArchiveResources();
+#endif
m_inStopAllLoaders = false;
}
@@ -2472,11 +2653,6 @@ void FrameLoader::stopForUserCancel(bool deferCheckLoadComplete)
checkLoadComplete();
}
-void FrameLoader::cancelPendingArchiveLoad(ResourceLoader* loader)
-{
- m_client->cancelPendingArchiveLoad(loader);
-}
-
DocumentLoader* FrameLoader::activeDocumentLoader() const
{
if (m_state == FrameStateProvisional)
@@ -2533,8 +2709,13 @@ void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader)
m_policyDocumentLoader = loader;
}
+
+DocumentLoader* FrameLoader::policyDocumentLoader() const
+{
+ return m_policyDocumentLoader.get();
+}
-DocumentLoader* FrameLoader::provisionalDocumentLoader()
+DocumentLoader* FrameLoader::provisionalDocumentLoader() const
{
return m_provisionalDocumentLoader.get();
}
@@ -2596,7 +2777,16 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage)
// We are doing this here because we know for sure that a new page is about to be loaded.
if (canCachePage() && m_client->canCachePage() && !m_currentHistoryItem->isInPageCache())
cachePageForHistoryItem(m_currentHistoryItem.get());
-
+ else if (m_frame->page() && m_frame == m_frame->page()->mainFrame()) {
+ // If the main frame installs a timeout late enough (for example in its onunload handler)
+ // it could sometimes fire when transitioning to a non-HTML document representation (such as the Mac bookmarks view).
+ // To avoid this, we clear all timeouts if the page is not to be cached in the back forward list.
+ // Cached pages have their timers paused so they are fine.
+ ScriptController* proxy = m_frame->script();
+ if (proxy->haveWindowShell())
+ proxy->windowShell()->window()->clearAllTimeouts();
+ }
+
if (m_loadType != FrameLoadTypeReplace)
closeOldDataSources();
@@ -2622,7 +2812,7 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage)
if (url.isEmpty())
url = pdl->responseURL();
if (url.isEmpty())
- url = "about:blank";
+ url = blankURL();
didOpenURL(url);
}
@@ -2701,7 +2891,7 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage)
// This code was originally added for a Leopard performance imporvement. We decided to
// ifdef it to fix correctness issues on Tiger documented in <rdar://problem/5441823>.
if (m_frame->view())
- m_frame->view()->suppressScrollbars(true);
+ m_frame->view()->setScrollbarsSuppressed(true);
#endif
m_client->transitionToCommittedForNewPage();
break;
@@ -2720,7 +2910,7 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage)
m_responseMIMEType = dl->responseMIMEType();
// Tell the client we've committed this URL.
- ASSERT(m_client->hasFrameView());
+ ASSERT(m_frame->view());
if (m_creatingInitialEmptyDocument)
return;
@@ -2769,7 +2959,7 @@ bool FrameLoader::shouldReload(const KURL& currentURL, const KURL& destinationUR
// This function implements the rule: "Don't reload if navigating by fragment within
// the same URL, but do reload if going to a new URL or to the same URL with no
// fragment identifier at all."
- if (!currentURL.hasRef() && !destinationURL.hasRef())
+ if (!destinationURL.hasRef())
return true;
return !equalIgnoringRef(currentURL, destinationURL);
}
@@ -2804,14 +2994,14 @@ void FrameLoader::open(CachedPage& cachedPage)
m_didCallImplicitClose = true;
// Delete old status bar messages (if it _was_ activated on last URL).
- if (m_frame->scriptProxy()->isEnabled()) {
+ if (m_frame->script()->isEnabled()) {
m_frame->setJSStatusBarText(String());
m_frame->setJSDefaultStatusBarText(String());
}
KURL url = cachedPage.url();
- if (url.protocol().startsWith("http") && !url.host().isEmpty() && url.path().isEmpty())
+ if ((url.protocolIs("http") || url.protocolIs("https")) && !url.host().isEmpty() && url.path().isEmpty())
url.setPath("/");
m_URL = url;
@@ -2836,6 +3026,9 @@ void FrameLoader::open(CachedPage& cachedPage)
m_frame->setView(view);
m_frame->setDocument(document);
+ m_frame->domWindow()->setURL(document->url());
+ m_frame->domWindow()->setSecurityOrigin(document->securityOrigin());
+
m_decoder = document->decoder();
updatePolicyBaseURL();
@@ -2864,11 +3057,6 @@ void FrameLoader::finishedLoading()
checkLoadComplete();
}
-bool FrameLoader::isArchiveLoadPending(ResourceLoader* loader) const
-{
- return m_client->isArchiveLoadPending(loader);
-}
-
bool FrameLoader::isHostedByObjectElement() const
{
HTMLFrameOwnerElement* owner = m_frame->ownerElement();
@@ -2909,10 +3097,42 @@ void FrameLoader::didReceiveServerRedirectForProvisionalLoadForFrame()
void FrameLoader::finishedLoadingDocument(DocumentLoader* loader)
{
-#if PLATFORM(WIN) || defined(ANDROID)
- if (!m_creatingInitialEmptyDocument)
+ // FIXME: Platforms shouldn't differ here!
+#if PLATFORM(WIN) || PLATFORM(CHROMIUM) || defined(ANDROID)
+ if (m_creatingInitialEmptyDocument)
+ return;
#endif
+
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ // If loading a webarchive, run through webarchive machinery
+ const String& responseMIMEType = loader->responseMIMEType();
+
+ // FIXME: Mac's FrameLoaderClient::finishedLoading() method does work that is required even with Archive loads
+ // so we still need to call it. Other platforms should only call finishLoading for non-archive loads
+ // That work should be factored out so this #ifdef can be removed
+#if PLATFORM(MAC)
+ m_client->finishedLoading(loader);
+ if (!ArchiveFactory::isArchiveMimeType(responseMIMEType))
+ return;
+#else
+ if (!ArchiveFactory::isArchiveMimeType(responseMIMEType)) {
m_client->finishedLoading(loader);
+ return;
+ }
+#endif
+
+ RefPtr<Archive> archive(ArchiveFactory::create(loader->mainResourceData().get(), responseMIMEType));
+ if (!archive)
+ return;
+
+ loader->addAllArchiveResources(archive.get());
+
+ ArchiveResource* mainResource = archive->mainResource();
+ loader->setParsedArchiveData(mainResource->data());
+ continueLoadWithData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), mainResource->url());
+#else
+ m_client->finishedLoading(loader);
+#endif
}
bool FrameLoader::isReplacing() const
@@ -3050,7 +3270,7 @@ void FrameLoader::checkLoadCompleteForThisFrame()
#ifdef ANDROID_INSTRUMENT
if (!m_frame->tree()->parent()) {
- m_frame->reportTimeCounter(m_URL.deprecatedString(),
+ m_frame->reportTimeCounter(m_URL,
static_cast<int>((currentTime() - sCurrentTime) * 1000),
(get_thread_msec() - sCurrentThreadTime));
}
@@ -3080,7 +3300,13 @@ void FrameLoader::continueLoadAfterWillSubmitForm(PolicyAction)
if (!m_provisionalDocumentLoader)
return;
+ // DocumentLoader calls back to our prepareForLoadStart
m_provisionalDocumentLoader->prepareForLoadStart();
+
+ // The load might be cancelled inside of prepareForLoadStart(), nulling out the m_provisionalDocumentLoader,
+ // so we need to null check it again.
+ if (!m_provisionalDocumentLoader)
+ return;
DocumentLoader* activeDocLoader = activeDocumentLoader();
if (activeDocLoader && activeDocLoader->isLoadingMainResource())
@@ -3206,20 +3432,11 @@ void FrameLoader::submitForm(const FrameLoadRequest& request, Event* event)
}
// FIXME: We should probably call userGestureHint() to tell whether this form submission was the result of a user gesture.
- load(request, false, true, event, m_formAboutToBeSubmitted.get(), m_formValuesAboutToBeSubmitted);
+ loadFrameRequestWithFormAndValues(request, false, event, m_formAboutToBeSubmitted.get(), m_formValuesAboutToBeSubmitted);
clearRecordedFormValues();
}
-void FrameLoader::urlSelected(const FrameLoadRequest& request, Event* event, bool lockHistory, bool userGesture)
-{
- FrameLoadRequest copy = request;
- if (copy.resourceRequest().httpReferrer().isEmpty())
- copy.resourceRequest().setHTTPReferrer(m_outgoingReferrer);
-
- load(copy, lockHistory, userGesture, event, 0, HashMap<String, String>());
-}
-
String FrameLoader::userAgent(const KURL& url) const
{
return m_client->userAgent(url);
@@ -3276,10 +3493,6 @@ void FrameLoader::detachFromParent()
m_frame->setView(0);
m_frame->pageDestroyed();
}
-#if PLATFORM(MAC)
- [m_frame->bridge() close];
-#endif
- m_client->detachedFromParent4();
}
void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, bool mainResource, bool alwaysFromRequest)
@@ -3300,80 +3513,95 @@ void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, bool mainRes
}
if (mainResource)
- request.setHTTPAccept("text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");
+ request.setHTTPAccept("application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");
+
+ // Make sure we send the Origin header.
+ addHTTPOriginIfNeeded(request, String());
+}
+
+void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, String origin)
+{
+ if (!request.httpOrigin().isEmpty())
+ return; // Request already has an Origin header.
+
+ // Don't send an Origin header for GET or HEAD to avoid privacy issues.
+ // For example, if an intranet page has a hyperlink to an external web
+ // site, we don't want to include the Origin of the request because it
+ // will leak the internal host name. Similar privacy concerns have lead
+ // to the widespread suppression of the Referer header at the network
+ // layer.
+ if (request.httpMethod() == "GET" || request.httpMethod() == "HEAD")
+ return;
+
+ // For non-GET and non-HEAD methods, always send an Origin header so the
+ // server knows we support this feature.
+
+ if (origin.isEmpty()) {
+ // If we don't know what origin header to attach, we attach the value
+ // for an empty origin.
+ origin = SecurityOrigin::createEmpty()->toString();
+ }
+
+ request.setHTTPOrigin(origin);
}
void FrameLoader::committedLoad(DocumentLoader* loader, const char* data, int length)
{
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ if (ArchiveFactory::isArchiveMimeType(loader->response().mimeType()))
+ return;
+#endif
m_client->committedLoad(loader, data, length);
}
#ifdef ANDROID_USER_GESTURE
-void FrameLoader::post(const KURL& url, const String& referrer, const String& frameName, PassRefPtr<FormData> formData,
- const String& contentType, Event* event, HTMLFormElement* form, const HashMap<String, String>& formValues, bool userGesture)
+void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName,
+ Event* event, PassRefPtr<FormState> prpFormState, bool userGesture)
#else
-void FrameLoader::post(const KURL& url, const String& referrer, const String& frameName, PassRefPtr<FormData> formData,
- const String& contentType, Event* event, HTMLFormElement* form, const HashMap<String, String>& formValues)
+void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName,
+ Event* event, PassRefPtr<FormState> prpFormState)
#endif
{
+ RefPtr<FormState> formState = prpFormState;
+
// When posting, use the NSURLRequestReloadIgnoringCacheData load flag.
// This prevents a potential bug which may cause a page with a form that uses itself
// as an action to be returned from the cache without submitting.
// FIXME: Where's the code that implements what the comment above says?
- ResourceRequest request(url);
+ // Previously when this method was reached, the original FrameLoadRequest had been deconstructed to build a
+ // bunch of parameters that would come in here and then be built back up to a ResourceRequest. In case
+ // any caller depends on the immutability of the original ResourceRequest, I'm rebuilding a ResourceRequest
+ // from scratch as it did all along.
+ const KURL& url = inRequest.url();
+ RefPtr<FormData> formData = inRequest.httpBody();
+ const String& contentType = inRequest.httpContentType();
+ String origin = inRequest.httpOrigin();
+
+ ResourceRequest workingResourceRequest(url);
#ifdef ANDROID_USER_GESTURE
- request.setUserGesture(userGesture);
+ workingResourceRequest.setUserGesture(userGesture);
#endif
- addExtraFieldsToRequest(request, true, true);
if (!referrer.isEmpty())
- request.setHTTPReferrer(referrer);
- request.setHTTPMethod("POST");
- request.setHTTPBody(formData);
- request.setHTTPContentType(contentType);
+ workingResourceRequest.setHTTPReferrer(referrer);
+ workingResourceRequest.setHTTPOrigin(origin);
+ workingResourceRequest.setHTTPMethod("POST");
+ workingResourceRequest.setHTTPBody(formData);
+ workingResourceRequest.setHTTPContentType(contentType);
+ addExtraFieldsToRequest(workingResourceRequest, true, true);
NavigationAction action(url, FrameLoadTypeStandard, true, event);
- RefPtr<FormState> formState;
- if (form && !formValues.isEmpty())
- formState = FormState::create(form, formValues, m_frame);
-
if (!frameName.isEmpty()) {
if (Frame* targetFrame = findFrameForNavigation(frameName))
- targetFrame->loader()->load(request, action, FrameLoadTypeStandard, formState.release());
+ targetFrame->loader()->loadWithNavigationAction(workingResourceRequest, action, FrameLoadTypeStandard, formState.release());
else
- checkNewWindowPolicy(action, request, formState.release(), frameName);
+ checkNewWindowPolicy(action, workingResourceRequest, formState.release(), frameName);
} else
- load(request, action, FrameLoadTypeStandard, formState.release());
-}
-
-#ifdef ANDROID_FIX
-// Fix android bug http://b/issue?id=1131683 and
-// webkit bug https://bugs.webkit.org/show_bug.cgi?id=18674
-// when doing history navigation for subframe, it needs a way to do post.
-// The following code is similar to loadItem(HistoryItem* item, FrameLoadType loadType)
-// when there is form data.
-void FrameLoader::postFromHistory(const KURL& url, PassRefPtr<FormData> formData,
- const String& contentType, const String& referrer, FrameLoadType loadType)
-{
- ASSERT(isBackForwardLoadType(loadType) ||
- loadType == FrameLoadTypeReload ||
- loadType == FrameLoadTypeReloadAllowingStaleData);
-
- ResourceRequest request(url);
- addExtraFieldsToRequest(request, true, true);
-
- request.setHTTPMethod("POST");
- request.setHTTPBody(formData);
- request.setHTTPContentType(contentType);
- request.setHTTPReferrer(referrer);
-
- NavigationAction action(url, loadType, false);
- load(request, action, loadType, 0);
+ loadWithNavigationAction(workingResourceRequest, action, FrameLoadTypeStandard, formState.release());
}
-#endif
bool FrameLoader::isReloading() const
{
@@ -3386,7 +3614,7 @@ void FrameLoader::loadEmptyDocumentSynchronously()
load(request);
}
-void FrameLoader::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
+unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data)
{
// Since this is a subresource, we can load any URL (we ignore the return value).
// But we still want to know whether we should hide the referrer or not, so we call the canLoad method.
@@ -3404,6 +3632,7 @@ void FrameLoader::loadResourceSynchronously(const ResourceRequest& request, Reso
if (!referrer.isEmpty())
initialRequest.setHTTPReferrer(referrer);
+ addHTTPOriginIfNeeded(initialRequest, outgoingOrigin());
if (Page* page = m_frame->page())
initialRequest.setMainDocumentURL(page->mainFrame()->loader()->documentLoader()->request().url());
@@ -3416,10 +3645,22 @@ void FrameLoader::loadResourceSynchronously(const ResourceRequest& request, Reso
if (error.isNull()) {
ASSERT(!newRequest.isNull());
didTellClientAboutLoad(newRequest.url().string());
- ResourceHandle::loadResourceSynchronously(newRequest, error, response, data, m_frame);
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ ApplicationCacheResource* resource;
+ if (documentLoader()->shouldLoadResourceFromApplicationCache(newRequest, resource)) {
+ if (resource) {
+ response = resource->response();
+ data.append(resource->data()->data(), resource->data()->size());
+ } else
+ error = cannotShowURLError(newRequest);
+ } else
+#endif
+ ResourceHandle::loadResourceSynchronously(newRequest, error, response, data, m_frame);
}
sendRemainingDelegateMessages(identifier, response, data.size(), error);
+ return identifier;
}
void FrameLoader::assignIdentifierToInitialRequest(unsigned long identifier, const ResourceRequest& clientRequest)
@@ -3547,13 +3788,32 @@ void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequ
m_client->didFinishLoad();
}
+bool FrameLoader::shouldScrollToAnchor(bool isFormSubmission, FrameLoadType loadType, const KURL& url)
+{
+ // Should we do anchor navigation within the existing content?
+
+ // We don't do this if we are submitting a form, explicitly reloading,
+ // currently displaying a frameset, or if the URL does not have a fragment.
+ // These rules were originally based on what KHTML was doing in KHTMLPart::openURL.
+
+ // FIXME: What about load types other than Standard and Reload?
+
+ return !isFormSubmission
+ && loadType != FrameLoadTypeReload
+ && loadType != FrameLoadTypeSame
+ && !shouldReload(this->url(), url)
+ // We don't want to just scroll if a link from within a
+ // frameset is trying to reload the frameset into _top.
+ && !m_frame->isFrameSet();
+}
+
void FrameLoader::opened()
{
if (m_loadType == FrameLoadTypeStandard && m_documentLoader->isClientRedirect())
updateHistoryForClientRedirect();
if (m_documentLoader->isLoadingFromCachedPage()) {
- m_frame->document()->didRestoreFromCache();
+ m_frame->document()->documentDidBecomeActive();
// Force a layout to update view size and thereby update scrollbars.
m_client->forceLayout();
@@ -3588,7 +3848,7 @@ void FrameLoader::checkNewWindowPolicy(const NavigationAction& action, const Res
m_policyCheck.set(request, formState, frameName,
callContinueLoadAfterNewWindowPolicy, this);
m_client->dispatchDecidePolicyForNewWindowAction(&FrameLoader::continueAfterNewWindowPolicy,
- action, request, frameName);
+ action, request, formState, frameName);
}
void FrameLoader::continueAfterNewWindowPolicy(PolicyAction policy)
@@ -3639,11 +3899,11 @@ void FrameLoader::checkNavigationPolicy(const ResourceRequest& request, Document
loader->setLastCheckedRequest(request);
- m_policyCheck.set(request, formState, function, argument);
+ m_policyCheck.set(request, formState.get(), function, argument);
m_delegateIsDecidingNavigationPolicy = true;
m_client->dispatchDecidePolicyForNavigationAction(&FrameLoader::continueAfterNavigationPolicy,
- action, request);
+ action, request, formState);
m_delegateIsDecidingNavigationPolicy = false;
}
@@ -3722,6 +3982,12 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest& reque
FrameLoadType type = m_policyLoadType;
stopAllLoaders();
+
+ // <rdar://problem/6250856> - In certain circumstances on pages with multiple frames, stopAllLoaders()
+ // might detach the current FrameLoader, in which case we should bail on this newly defunct load.
+ if (!m_frame->page())
+ return;
+
setProvisionalDocumentLoader(m_policyDocumentLoader.get());
m_loadType = type;
setState(FrameStateProvisional);
@@ -3762,7 +4028,7 @@ void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& reques
mainFrame->loader()->setOpenedByDOM();
mainFrame->loader()->m_client->dispatchShow();
mainFrame->loader()->setOpener(frame.get());
- mainFrame->loader()->load(request, NavigationAction(), FrameLoadTypeStandard, formState);
+ mainFrame->loader()->loadWithNavigationAction(request, NavigationAction(), FrameLoadTypeStandard, formState);
}
void FrameLoader::sendRemainingDelegateMessages(unsigned long identifier, const ResourceResponse& response, int length, const ResourceError& error)
@@ -3793,7 +4059,7 @@ void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& i
dispatchWillSendRequest(m_documentLoader.get(), identifier, newRequest, ResourceResponse());
if (newRequest.isNull())
- error = m_client->cancelledError(request);
+ error = cancelledError(request);
else
error = ResourceError();
@@ -3937,11 +4203,15 @@ PassRefPtr<HistoryItem> FrameLoader::createHistoryItem(bool useOriginal)
// Later we may want to learn to live with nil for URL.
// See bug 3368236 and related bugs for more information.
if (url.isEmpty())
- url = KURL("about:blank");
+ url = blankURL();
if (originalURL.isEmpty())
- originalURL = KURL("about:blank");
+ originalURL = blankURL();
- RefPtr<HistoryItem> item = new HistoryItem(url, m_frame->tree()->name(), m_frame->tree()->parent() ? m_frame->tree()->parent()->tree()->name() : "", docLoader ? docLoader->title() : "");
+ Frame* parentFrame = m_frame->tree()->parent();
+ String parent = parentFrame ? parentFrame->tree()->name() : "";
+ String title = docLoader ? docLoader->title() : "";
+
+ RefPtr<HistoryItem> item = HistoryItem::create(url, m_frame->tree()->name(), parent, title);
item->setOriginalURLString(originalURL.string());
// Save form state if this is a POST
@@ -3961,21 +4231,25 @@ PassRefPtr<HistoryItem> FrameLoader::createHistoryItem(bool useOriginal)
void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip)
{
- if (Page* page = m_frame->page())
- if (!documentLoader()->urlForHistory().isEmpty()) {
- Frame* mainFrame = page->mainFrame();
- ASSERT(mainFrame);
- FrameLoader* frameLoader = mainFrame->loader();
-
- if (!frameLoader->m_didPerformFirstNavigation && page->backForwardList()->entries().size() == 1) {
- frameLoader->m_didPerformFirstNavigation = true;
- m_client->didPerformFirstNavigation();
- }
+ Page* page = m_frame->page();
+ if (!page)
+ return;
- RefPtr<HistoryItem> item = frameLoader->createHistoryItemTree(m_frame, doClip);
- LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", item.get(), documentLoader()->url().string().ascii().data());
- page->backForwardList()->addItem(item);
- }
+ if (documentLoader()->urlForHistory().isEmpty())
+ return;
+
+ Frame* mainFrame = page->mainFrame();
+ ASSERT(mainFrame);
+ FrameLoader* frameLoader = mainFrame->loader();
+
+ if (!frameLoader->m_didPerformFirstNavigation && page->backForwardList()->entries().size() == 1) {
+ frameLoader->m_didPerformFirstNavigation = true;
+ m_client->didPerformFirstNavigation();
+ }
+
+ RefPtr<HistoryItem> item = frameLoader->createHistoryItemTree(m_frame, doClip);
+ LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", item.get(), documentLoader()->url().string().ascii().data());
+ page->backForwardList()->addItem(item);
}
PassRefPtr<HistoryItem> FrameLoader::createHistoryItemTree(Frame* targetFrame, bool clipAtTarget)
@@ -3986,8 +4260,17 @@ PassRefPtr<HistoryItem> FrameLoader::createHistoryItemTree(Frame* targetFrame, b
if (!(clipAtTarget && m_frame == targetFrame)) {
// save frame state for items that aren't loading (khtml doesn't save those)
saveDocumentState();
- for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling())
- bfItem->addChildItem(child->loader()->createHistoryItemTree(targetFrame, clipAtTarget));
+ for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) {
+ FrameLoader* childLoader = child->loader();
+ bool hasChildLoaded = childLoader->frameHasLoaded();
+
+ // If the child is a frame corresponding to an <object> element that never loaded,
+ // we don't want to create a history item, because that causes fallback content
+ // to be ignored on reload.
+
+ if (!(!hasChildLoaded && childLoader->isHostedByObjectElement()))
+ bfItem->addChildItem(childLoader->createHistoryItemTree(targetFrame, clipAtTarget));
+ }
}
if (m_frame == targetFrame)
bfItem->setIsTargetItem(true);
@@ -4007,7 +4290,7 @@ void FrameLoader::saveScrollPositionAndViewStateToItem(HistoryItem* item)
if (!item || !m_frame->view())
return;
- item->setScrollPoint(IntPoint(m_frame->view()->contentsX(), m_frame->view()->contentsY()));
+ item->setScrollPoint(m_frame->view()->scrollPosition());
// FIXME: It would be great to work out a way to put this code in WebCore instead of calling through to the client.
m_client->saveViewStateToItem(item);
}
@@ -4043,10 +4326,8 @@ void FrameLoader::restoreScrollPositionAndViewState()
m_client->restoreViewState();
if (FrameView* view = m_frame->view())
- if (!view->wasScrolledByUser()) {
- const IntPoint& scrollPoint = m_currentHistoryItem->scrollPoint();
- view->setContentsPos(scrollPoint.x(), scrollPoint.y());
- }
+ if (!view->wasScrolledByUser())
+ view->setScrollPosition(m_currentHistoryItem->scrollPoint());
}
void FrameLoader::invalidateCurrentItemCachedPage()
@@ -4092,7 +4373,7 @@ void FrameLoader::saveDocumentState()
ASSERT(document);
if (document && item->isCurrentDocument(document)) {
- LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->name().domString().utf8().data(), item);
+ LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->name().string().utf8().data(), item);
item->setDocumentState(document->formElementsState());
}
}
@@ -4100,6 +4381,9 @@ void FrameLoader::saveDocumentState()
// Loads content into this frame, as specified by history item
void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType)
{
+ if (!m_frame->page())
+ return;
+
KURL itemURL = item->url();
KURL itemOriginalURL = item->originalURL();
KURL currentURL;
@@ -4113,7 +4397,7 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType)
// check for all that as an additional optimization.
// We also do not do anchor-style navigation if we're posting a form.
- if (!formData && !shouldReload(itemURL, currentURL) && urlsMatchItem(item)) {
+ if (!formData && urlsMatchItem(item)) {
// Must do this maintenance here, since we don't go through a real page reload
saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get());
@@ -4155,7 +4439,7 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType)
// WebKitBackForwardCacheExpirationIntervalKey in WebKit.
// Or we should remove WebKitBackForwardCacheExpirationIntervalKey.
if (interval <= 1800) {
- load(cachedPage->documentLoader(), loadType, 0);
+ loadWithDocumentLoader(cachedPage->documentLoader(), loadType, 0);
inPageCache = true;
} else {
LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", m_provisionalHistoryItem->url().string().ascii().data());
@@ -4166,15 +4450,18 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType)
if (!inPageCache) {
ResourceRequest request(itemURL);
- addExtraFieldsToRequest(request, true, formData);
-
// If this was a repost that failed the page cache, we might try to repost the form.
NavigationAction action;
if (formData) {
+
+ formData->generateFiles(m_frame->page()->chrome()->client());
+
request.setHTTPMethod("POST");
request.setHTTPReferrer(item->formReferrer());
request.setHTTPBody(formData);
request.setHTTPContentType(item->formContentType());
+ RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item->formReferrer());
+ addHTTPOriginIfNeeded(request, securityOrigin->toString());
// FIXME: Slight hack to test if the NSURL cache contains the page we're going to.
// We want to know this before talking to the policy delegate, since it affects whether
@@ -4215,7 +4502,8 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType)
action = NavigationAction(itemOriginalURL, loadType, false);
}
- load(request, action, loadType, 0);
+ addExtraFieldsToRequest(request, true, formData);
+ loadWithNavigationAction(request, action, loadType, 0);
}
}
}
@@ -4223,13 +4511,12 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType)
// Walk the frame tree and ensure that the URLs match the URLs in the item.
bool FrameLoader::urlsMatchItem(HistoryItem* item) const
{
- KURL currentURL = documentLoader()->url();
-
+ const KURL& currentURL = documentLoader()->url();
if (!equalIgnoringRef(currentURL, item->url()))
return false;
-
+
const HistoryItemVector& childItems = item->children();
-
+
unsigned size = childItems.size();
for (unsigned i = 0; i < size; ++i) {
Frame* childFrame = m_frame->tree()->child(childItems[i]->target());
@@ -4250,17 +4537,19 @@ void FrameLoader::goToItem(HistoryItem* targetItem, FrameLoadType type)
// <rdar://problem/3951283> can view pages from the back/forward cache that should be disallowed by Parental Controls
// Ultimately, history item navigations should go through the policy delegate. That's covered in:
// <rdar://problem/3979539> back/forward cache navigations should consult policy delegate
- if (Page* page = m_frame->page())
- if (m_client->shouldGoToHistoryItem(targetItem)) {
- BackForwardList* bfList = page->backForwardList();
- HistoryItem* currentItem = bfList->currentItem();
-
- // Set the BF cursor before commit, which lets the user quickly click back/forward again.
- // - plus, it only makes sense for the top level of the operation through the frametree,
- // as opposed to happening for some/one of the page commits that might happen soon
- bfList->goToItem(targetItem);
- recursiveGoToItem(targetItem, currentItem, type);
- }
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+ if (!m_client->shouldGoToHistoryItem(targetItem))
+ return;
+
+ // Set the BF cursor before commit, which lets the user quickly click back/forward again.
+ // - plus, it only makes sense for the top level of the operation through the frametree,
+ // as opposed to happening for some/one of the page commits that might happen soon
+ BackForwardList* bfList = page->backForwardList();
+ HistoryItem* currentItem = bfList->currentItem();
+ bfList->goToItem(targetItem);
+ recursiveGoToItem(targetItem, currentItem, type);
}
// The general idea here is to traverse the frame tree and the item tree in parallel,
@@ -4338,40 +4627,45 @@ bool FrameLoader::childFramesMatchItem(HistoryItem* item) const
return true;
}
-void FrameLoader::addHistoryForCurrentLocation()
-{
- if (!m_frame->settings()->privateBrowsingEnabled()) {
- // FIXME: <rdar://problem/4880065> - This will be a hook into the WebCore global history, and this loader/client call will be removed
- m_client->updateGlobalHistoryForStandardLoad(documentLoader()->urlForHistory());
- }
- addBackForwardItemClippedAtTarget(true);
-}
+// There are 3 things you might think of as "history", all of which are handled by these functions.
+//
+// 1) Back/forward: The m_currentHistoryItem is part of this mechanism.
+// 2) Global history: Handled by the client.
+// 3) Visited links: Handled by the PageGroup.
void FrameLoader::updateHistoryForStandardLoad()
{
LOG(History, "WebCoreHistory: Updating History for Standard Load in frame %s", documentLoader()->url().string().ascii().data());
-
- bool frameNavigationOnLoad = false;
-
- // if the navigation occured during on load and this is a subframe
- // update the current history item rather than adding a new one
- // <rdar://problem/5333496>
+
+ Settings* settings = m_frame->settings();
+ bool needPrivacy = !settings || settings->privateBrowsingEnabled();
+ const KURL& historyURL = documentLoader()->urlForHistory();
+
+ // If the navigation occured during load and this is a subframe, update the current
+ // back/forward item rather than adding a new one and don't add the new URL to global
+ // history at all. But do add it to visited links. <rdar://problem/5333496>
+ bool frameNavigationDuringLoad = false;
if (m_navigationDuringLoad) {
HTMLFrameOwnerElement* owner = m_frame->ownerElement();
- frameNavigationOnLoad = owner && !owner->createdByParser();
+ frameNavigationDuringLoad = owner && !owner->createdByParser();
+ m_navigationDuringLoad = false;
}
-
- if (!frameNavigationOnLoad && !documentLoader()->isClientRedirect()) {
- if (!documentLoader()->urlForHistory().isEmpty())
- addHistoryForCurrentLocation();
+
+ if (!frameNavigationDuringLoad && !documentLoader()->isClientRedirect()) {
+ if (!historyURL.isEmpty()) {
+ addBackForwardItemClippedAtTarget(true);
+ if (!needPrivacy)
+ m_client->updateGlobalHistory(historyURL);
+ }
} else if (documentLoader()->unreachableURL().isEmpty() && m_currentHistoryItem) {
m_currentHistoryItem->setURL(documentLoader()->url());
m_currentHistoryItem->setFormInfoFromRequest(documentLoader()->request());
}
-
- // reset navigation during on load since we no longer
- // need it past thsi point
- m_navigationDuringLoad = false;
+
+ if (!historyURL.isEmpty() && !needPrivacy) {
+ if (Page* page = m_frame->page())
+ page->group().addVisitedLink(historyURL);
+ }
}
void FrameLoader::updateHistoryForClientRedirect()
@@ -4387,6 +4681,15 @@ void FrameLoader::updateHistoryForClientRedirect()
m_currentHistoryItem->clearDocumentState();
m_currentHistoryItem->clearScrollPoint();
}
+
+ Settings* settings = m_frame->settings();
+ bool needPrivacy = !settings || settings->privateBrowsingEnabled();
+ const KURL& historyURL = documentLoader()->urlForHistory();
+
+ if (!historyURL.isEmpty() && !needPrivacy) {
+ if (Page* page = m_frame->page())
+ page->group().addVisitedLink(historyURL);
+ }
}
void FrameLoader::updateHistoryForBackForwardNavigation()
@@ -4417,10 +4720,6 @@ void FrameLoader::updateHistoryForReload()
if (documentLoader()->unreachableURL().isEmpty())
m_currentHistoryItem->setURL(documentLoader()->requestURL());
}
-
- // FIXME: <rdar://problem/4880065> - This will be a hook into the WebCore global history, and this loader/client call will be removed
- // Update the last visited time. Mostly interesting for URL autocompletion statistics.
- m_client->updateGlobalHistoryForReload(documentLoader()->originalURL());
}
void FrameLoader::updateHistoryForRedirectWithLockedHistory()
@@ -4430,17 +4729,17 @@ void FrameLoader::updateHistoryForRedirectWithLockedHistory()
LOG(History, "WebCoreHistory: Updating History for internal load in frame %s", documentLoader()->title().utf8().data());
#endif
+ Settings* settings = m_frame->settings();
+ bool needPrivacy = !settings || settings->privateBrowsingEnabled();
+ const KURL& historyURL = documentLoader()->urlForHistory();
+
if (documentLoader()->isClientRedirect()) {
- if (!m_currentHistoryItem && !m_frame->tree()->parent())
- addHistoryForCurrentLocation();
+ if (!m_currentHistoryItem && !m_frame->tree()->parent()) {
+ addBackForwardItemClippedAtTarget(true);
+ if (!needPrivacy && !historyURL.isEmpty())
+ m_client->updateGlobalHistory(historyURL);
+ }
if (m_currentHistoryItem) {
-#ifdef ANDROID_FIX
- if (m_currentHistoryItem->formData()) {
- m_currentHistoryItem->setOriginalFormInfo(m_currentHistoryItem->formData()->copy(),
- m_currentHistoryItem->formContentType(),
- m_currentHistoryItem->formReferrer());
- }
-#endif
m_currentHistoryItem->setURL(documentLoader()->url());
m_currentHistoryItem->setFormInfoFromRequest(documentLoader()->request());
}
@@ -4449,6 +4748,11 @@ void FrameLoader::updateHistoryForRedirectWithLockedHistory()
if (parentFrame && parentFrame->loader()->m_currentHistoryItem)
parentFrame->loader()->m_currentHistoryItem->addChildItem(createHistoryItem(true));
}
+
+ if (!historyURL.isEmpty() && !needPrivacy) {
+ if (Page* page = m_frame->page())
+ page->group().addVisitedLink(historyURL);
+ }
}
void FrameLoader::updateHistoryForCommit()
@@ -4471,6 +4775,22 @@ void FrameLoader::updateHistoryForCommit()
}
}
+void FrameLoader::updateHistoryForAnchorScroll()
+{
+ if (m_URL.isEmpty())
+ return;
+
+ Settings* settings = m_frame->settings();
+ if (!settings || settings->privateBrowsingEnabled())
+ return;
+
+ Page* page = m_frame->page();
+ if (!page)
+ return;
+
+ page->group().addVisitedLink(m_URL);
+}
+
// Walk the frame tree, telling all frames to save their form state into their current
// history item.
void FrameLoader::saveDocumentAndScrollState()
@@ -4534,7 +4854,9 @@ void FrameLoader::mainReceivedError(const ResourceError& error, bool isComplete)
ResourceError FrameLoader::cancelledError(const ResourceRequest& request) const
{
- return m_client->cancelledError(request);
+ ResourceError error = m_client->cancelledError(request);
+ error.setIsCancellation(true);
+ return error;
}
ResourceError FrameLoader::blockedError(const ResourceRequest& request) const
@@ -4542,6 +4864,11 @@ ResourceError FrameLoader::blockedError(const ResourceRequest& request) const
return m_client->blockedError(request);
}
+ResourceError FrameLoader::cannotShowURLError(const ResourceRequest& request) const
+{
+ return m_client->cannotShowURLError(request);
+}
+
ResourceError FrameLoader::fileDoesNotExistError(const ResourceResponse& response) const
{
return m_client->fileDoesNotExistError(response);
@@ -4659,7 +4986,7 @@ void FrameLoader::setTitle(const String& title)
KURL FrameLoader::originalRequestURL() const
{
- return activeDocumentLoader()->initialRequest().url();
+ return activeDocumentLoader()->originalRequest().url();
}
String FrameLoader::referrer() const
@@ -4669,13 +4996,17 @@ String FrameLoader::referrer() const
void FrameLoader::dispatchWindowObjectAvailable()
{
- if (!m_frame->scriptProxy()->isEnabled() || !m_frame->scriptProxy()->haveGlobalObject())
+ if (!m_frame->script()->isEnabled() || !m_frame->script()->haveWindowShell())
return;
m_client->windowObjectCleared();
- if (Page* page = m_frame->page())
+
+ if (Page* page = m_frame->page()) {
+ if (InspectorController* inspector = page->inspectorController())
+ inspector->inspectedWindowScriptObjectCleared(m_frame);
if (InspectorController* inspector = page->parentInspectorController())
inspector->windowScriptObjectAvailable();
+ }
}
Widget* FrameLoader::createJavaAppletWidget(const IntSize& size, Element* element, const HashMap<String, String>& args)
@@ -4685,17 +5016,21 @@ Widget* FrameLoader::createJavaAppletWidget(const IntSize& size, Element* elemen
Vector<String> paramValues;
HashMap<String, String>::const_iterator end = args.end();
for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) {
- if (it->first.lower() == "baseurl")
+ if (equalIgnoringCase(it->first, "baseurl"))
baseURLString = it->second;
paramNames.append(it->first);
paramValues.append(it->second);
}
if (baseURLString.isEmpty())
- baseURLString = m_frame->document()->baseURL();
+ baseURLString = m_frame->document()->baseURL().string();
KURL baseURL = completeURL(baseURLString);
- return m_client->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues);
+ Widget* widget = m_client->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues);
+ if (widget)
+ m_containsPlugIns = true;
+
+ return widget;
}
void FrameLoader::didChangeTitle(DocumentLoader* loader)
@@ -4846,7 +5181,7 @@ bool FrameLoader::addLowBandwidthDisplayRequest(CachedResource* cache)
case CachedResource::Script:
m_needToSwitchOutLowBandwidthDisplay = true;
m_externalRequestsInLowBandwidthDisplay.add(cache);
- cache->ref(this);
+ cache->addClient(this);
return true;
case CachedResource::ImageResource:
case CachedResource::FontResource:
@@ -4867,7 +5202,7 @@ void FrameLoader::removeAllLowBandwidthDisplayRequests()
{
HashSet<CachedResource*>::iterator end = m_externalRequestsInLowBandwidthDisplay.end();
for (HashSet<CachedResource*>::iterator it = m_externalRequestsInLowBandwidthDisplay.begin(); it != end; ++it)
- (*it)->deref(this);
+ (*it)->removeClient(this);
m_externalRequestsInLowBandwidthDisplay.clear();
}
@@ -4875,7 +5210,7 @@ void FrameLoader::notifyFinished(CachedResource* script)
{
HashSet<CachedResource*>::iterator it = m_externalRequestsInLowBandwidthDisplay.find(script);
if (it != m_externalRequestsInLowBandwidthDisplay.end()) {
- (*it)->deref(this);
+ (*it)->removeClient(this);
m_externalRequestsInLowBandwidthDisplay.remove(it);
switchOutLowBandwidthDisplayIfReady();
}
@@ -4900,17 +5235,15 @@ void FrameLoader::switchOutLowBandwidthDisplayIfReady()
// similar to clear(), should be refactored to share more code
oldDoc->cancelParsing();
oldDoc->detach();
- if (m_frame->scriptProxy()->isEnabled())
- m_frame->scriptProxy()->clear();
+ if (m_frame->script()->isEnabled())
+ m_frame->script()->clearWindowShell();
if (m_frame->view())
m_frame->view()->clear();
// similar to begin(), should be refactored to share more code
- RefPtr<Document> newDoc = DOMImplementation::instance()->
- createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode());
+ RefPtr<Document> newDoc = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode());
m_frame->setDocument(newDoc);
- newDoc->setURL(m_URL.deprecatedString());
- newDoc->setBaseURL(m_URL.deprecatedString());
+ newDoc->setURL(m_URL);
if (m_decoder)
newDoc->setDecoder(m_decoder.get());
restoreDocumentState();
@@ -4931,7 +5264,6 @@ void FrameLoader::switchOutLowBandwidthDisplayIfReady()
if (m_pendingSourceInLowBandwidthDisplay.length()) {
// set cachePolicy to Cache to use the loaded resource
newDoc->docLoader()->setCachePolicy(CachePolicyCache);
- newDoc->determineParseMode(m_pendingSourceInLowBandwidthDisplay);
if (m_decoder->encoding().usesVisualOrdering())
newDoc->setVisuallyOrdered();
newDoc->recalcStyle(Node::Force);
diff --git a/WebCore/loader/FrameLoader.h b/WebCore/loader/FrameLoader.h
index a8a9735..47a56af 100644
--- a/WebCore/loader/FrameLoader.h
+++ b/WebCore/loader/FrameLoader.h
@@ -47,12 +47,12 @@
#include "CachedResourceClient.h"
#endif
-namespace KJS {
- class JSValue;
-}
-
namespace WebCore {
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ class Archive;
+ class ArchiveResource;
+#endif
class AuthenticationChallenge;
class CachedPage;
class Document;
@@ -136,57 +136,62 @@ namespace WebCore {
Frame* frame() const { return m_frame; }
- // FIXME: This is not cool, people.
+ // FIXME: This is not cool, people. We should aim to consolidate these variety of loading related methods into a smaller set,
+ // and try to reuse more of the same logic by extracting common code paths.
void prepareForLoadStart();
void setupForReplace();
void setupForReplaceByMIMEType(const String& newMIMEType);
- void finalSetupForReplace(DocumentLoader*);
- void load(const KURL&, Event*);
- void load(const FrameLoadRequest&, bool lockHistory, bool userGesture,
- Event*, HTMLFormElement*, const HashMap<String, String>& formValues);
+
+ void loadWithDocumentLoader(DocumentLoader*, FrameLoadType, PassRefPtr<FormState>); // Calls continueLoadAfterNavigationPolicy
+ void load(DocumentLoader*); // Calls loadWithDocumentLoader
+
+ void loadWithNavigationAction(const ResourceRequest&, const NavigationAction&, // Calls loadWithDocumentLoader()
+ FrameLoadType, PassRefPtr<FormState>);
+
#ifdef ANDROID_USER_GESTURE
- void load(const KURL&, const String& referrer, FrameLoadType, const String& target,
- Event*, PassRefPtr<FormState>, bool userGesture);
- void post(const KURL&, const String& referrer, const String& target,
- PassRefPtr<FormData>, const String& contentType,
- Event*, HTMLFormElement*, const HashMap<String, String>& formValues, bool userGesture);
+ void loadPostRequest(const ResourceRequest& inRequest, const String& referrer, // Called by loadFrameRequestWithFormAndValues(), calls loadWithNavigationAction
+ const String& frameName, Event* event, PassRefPtr<FormState> prpFormState, bool userGesture);
+
+ void loadURL(const KURL& newURL, const String& referrer, const String& frameName, // Called by loadFrameRequestWithFormAndValues(), calls loadWithNavigationAction or else dispatches to navigation policy delegate
+ FrameLoadType, Event* event, PassRefPtr<FormState> prpFormState, bool userGesture);
#else
- void load(const KURL&, const String& referrer, FrameLoadType, const String& target,
- Event*, PassRefPtr<FormState>);
- void post(const KURL&, const String& referrer, const String& target,
- PassRefPtr<FormData>, const String& contentType,
- Event*, HTMLFormElement*, const HashMap<String, String>& formValues);
-#endif
-#ifdef ANDROID_FIX
- void postFromHistory(const KURL& url, PassRefPtr<FormData> formData,
- const String& contentType, const String& referrer, FrameLoadType loadType);
+ void loadPostRequest(const ResourceRequest& inRequest, const String& referrer, // Called by loadFrameRequestWithFormAndValues(), calls loadWithNavigationAction
+ const String& frameName, Event* event, PassRefPtr<FormState> prpFormState);
+
+ void loadURL(const KURL& newURL, const String& referrer, const String& frameName, // Called by loadFrameRequestWithFormAndValues(), calls loadWithNavigationAction or else dispatches to navigation policy delegate
+ FrameLoadType, Event* event, PassRefPtr<FormState> prpFormState);
#endif
+ void loadURLIntoChildFrame(const KURL&, const String& referer, Frame*);
- void load(const ResourceRequest&);
- void load(const ResourceRequest&, const SubstituteData&);
- void load(const ResourceRequest&, const String& frameName);
- void load(const ResourceRequest&, const NavigationAction&, FrameLoadType, PassRefPtr<FormState>);
+ void loadFrameRequestWithFormState(const FrameLoadRequest&, bool lockHistory, Event*, PassRefPtr<FormState>);
+ void loadFrameRequestWithFormAndValues(const FrameLoadRequest&, bool lockHistory, // Called by submitForm, calls loadPostRequest()
+ Event*, HTMLFormElement*, const HashMap<String, String>& formValues);
+
+ void load(const ResourceRequest&); // Called by WebFrame, calls (ResourceRequest, SubstituteData)
+ void load(const ResourceRequest&, const SubstituteData&); // Called both by WebFrame and internally, calls (DocumentLoader*)
+ void load(const ResourceRequest&, const String& frameName); // Called by WebPluginController
- void load(DocumentLoader*);
- void load(DocumentLoader*, FrameLoadType, PassRefPtr<FormState>);
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ void loadArchive(PassRefPtr<Archive> archive);
+#endif
- static bool canLoad(const KURL&, const String& referrer);
- static bool canLoad(const KURL&, const Document*);
- static bool canLoad(const CachedResource&, const Document*);
- static void reportLocalLoadFailed(const Page*, const String& url);
+ // Returns true for any non-local URL. If Document parameter is supplied, its local load policy dictates,
+ // otherwise if referrer is non-empty and represents a local file, then the local load is allowed.
+ static bool canLoad(const KURL&, const String& referrer, const Document* theDocument = 0);
+ static void reportLocalLoadFailed(Frame*, const String& url);
static bool shouldHideReferrer(const KURL& url, const String& referrer);
- Frame* createWindow(const FrameLoadRequest&, const WindowFeatures&, bool& created);
+ // Called by createWindow in JSDOMWindowBase.cpp, e.g. to fulfill a modal dialog creation
+ Frame* createWindow(FrameLoader* frameLoaderForFrameLookup, const FrameLoadRequest&, const WindowFeatures&, bool& created);
- void loadResourceSynchronously(const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data);
+ unsigned long loadResourceSynchronously(const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data);
bool canHandleRequest(const ResourceRequest&);
// Also not cool.
void stopAllLoaders();
void stopForUserCancel(bool deferCheckLoadComplete = false);
- void cancelPendingArchiveLoad(ResourceLoader*);
bool isLoadingMainResource() const { return m_isLoadingMainResource; }
bool isLoading() const;
@@ -196,11 +201,13 @@ namespace WebCore {
bool isReloading() const;
String referrer() const;
String outgoingReferrer() const;
+ String outgoingOrigin() const;
void loadEmptyDocumentSynchronously();
DocumentLoader* activeDocumentLoader() const;
DocumentLoader* documentLoader() const;
- DocumentLoader* provisionalDocumentLoader();
+ DocumentLoader* policyDocumentLoader() const;
+ DocumentLoader* provisionalDocumentLoader() const;
FrameState state() const;
static double timeOfLastCompletedLoad();
@@ -226,8 +233,8 @@ namespace WebCore {
ResourceError cancelledError(const ResourceRequest&) const;
ResourceError fileDoesNotExistError(const ResourceResponse&) const;
ResourceError blockedError(const ResourceRequest&) const;
- bool willUseArchive(ResourceLoader*, const ResourceRequest&, const KURL&) const;
- bool isArchiveLoadPending(ResourceLoader*) const;
+ ResourceError cannotShowURLError(const ResourceRequest&) const;
+
void cannotShowMIMEType(const ResourceResponse&);
ResourceError interruptionForPolicyChangeError(const ResourceRequest&);
@@ -279,6 +286,7 @@ namespace WebCore {
void detachChildren();
void addExtraFieldsToRequest(ResourceRequest&, bool isMainResource, bool alwaysFromRequest);
+ static void addHTTPOriginIfNeeded(ResourceRequest&, String origin);
FrameLoaderClient* client() const;
@@ -287,7 +295,7 @@ namespace WebCore {
void changeLocation(const String& url, const String& referrer, bool lockHistory = true, bool userGesture = false);
void changeLocation(const KURL&, const String& referrer, bool lockHistory = true, bool userGesture = false);
void urlSelected(const ResourceRequest&, const String& target, Event*, bool lockHistory, bool userGesture);
- void urlSelected(const FrameLoadRequest&, Event*, bool lockHistory, bool userGesture);
+ void urlSelected(const FrameLoadRequest&, Event*, bool lockHistory);
bool requestFrame(HTMLFrameOwnerElement*, const String& url, const AtomicString& frameName);
Frame* loadSubframe(HTMLFrameOwnerElement*, const KURL&, const String& name, const String& referrer);
@@ -334,8 +342,8 @@ namespace WebCore {
// Returns true if url is a JavaScript URL.
bool executeIfJavaScriptURL(const KURL& url, bool userGesture = false, bool replaceDocument = true);
- KJS::JSValue* executeScript(const String& url, int baseLine, const String& script);
- KJS::JSValue* executeScript(const String& script, bool forceUserGesture = false);
+ JSC::JSValue* executeScript(const String& url, int baseLine, const String& script);
+ JSC::JSValue* executeScript(const String& script, bool forceUserGesture = false);
void gotoAnchor();
bool gotoAnchor(const String& name); // returns true if the anchor was found
@@ -388,7 +396,8 @@ namespace WebCore {
void scheduleCheckLoadComplete();
void clearRecordedFormValues();
- void recordFormValue(const String& name, const String& value, PassRefPtr<HTMLFormElement>);
+ void setFormAboutToBeSubmitted(PassRefPtr<HTMLFormElement> element);
+ void recordFormValue(const String& name, const String& value);
bool isComplete() const;
@@ -425,9 +434,16 @@ namespace WebCore {
void continueLoadWithData(SharedBuffer*, const String& mimeType, const String& textEncoding, const KURL&);
- static void registerURLSchemeAsLocal(const String& scheme);
+ enum LocalLoadPolicy {
+ AllowLocalLoadsForAll, // No restriction on local loads.
+ AllowLocalLoadsForLocalAndSubstituteData,
+ AllowLocalLoadsForLocalOnly,
+ };
+ static void setLocalLoadPolicy(LocalLoadPolicy);
static bool restrictAccessToLocal();
- static void setRestrictAccessToLocal(bool);
+ static bool allowSubstituteDataAccessToLocal();
+
+ static void registerURLSchemeAsLocal(const String& scheme);
static bool shouldTreatURLAsLocal(const String&);
static bool shouldTreatSchemeAsLocal(const String&);
@@ -448,6 +464,10 @@ namespace WebCore {
bool shouldAllowNavigation(Frame* targetFrame) const;
Frame* findFrameForNavigation(const AtomicString& name);
+ void startIconLoader();
+
+ void applyUserAgent(ResourceRequest& request);
+
private:
PassRefPtr<HistoryItem> createHistoryItem(bool useOriginal);
PassRefPtr<HistoryItem> createHistoryItemTree(Frame* targetFrame, bool clipAtTarget);
@@ -461,18 +481,18 @@ namespace WebCore {
void recursiveGoToItem(HistoryItem*, HistoryItem*, FrameLoadType);
bool childFramesMatchItem(HistoryItem*) const;
- void addHistoryForCurrentLocation();
void updateHistoryForBackForwardNavigation();
void updateHistoryForReload();
void updateHistoryForStandardLoad();
void updateHistoryForRedirectWithLockedHistory();
void updateHistoryForClientRedirect();
void updateHistoryForCommit();
+ void updateHistoryForAnchorScroll();
void redirectionTimerFired(Timer<FrameLoader>*);
void checkCompletedTimerFired(Timer<FrameLoader>*);
void checkLoadCompleteTimerFired(Timer<FrameLoader>*);
-
+
void cancelRedirection(bool newLoadInProgress = false);
void started();
@@ -490,7 +510,7 @@ namespace WebCore {
void receivedFirstData();
void updatePolicyBaseURL();
- void setPolicyBaseURL(const String&);
+ void setPolicyBaseURL(const KURL&);
// Also not cool.
void stopLoadingSubframes();
@@ -520,6 +540,7 @@ namespace WebCore {
void continueLoadAfterNewWindowPolicy(const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, bool shouldContinue);
static void callContinueFragmentScrollAfterNavigationPolicy(void*, const ResourceRequest&, PassRefPtr<FormState>, bool shouldContinue);
void continueFragmentScrollAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue);
+ bool shouldScrollToAnchor(bool isFormSubmission, FrameLoadType loadType, const KURL& url);
void addHistoryItemForFragmentScroll();
void stopPolicyCheck();
@@ -544,14 +565,10 @@ namespace WebCore {
bool shouldReloadToHandleUnreachableURL(DocumentLoader*);
void handleUnimplementablePolicy(const ResourceError&);
- void applyUserAgent(ResourceRequest& request);
-
void scheduleRedirection(ScheduledRedirection*);
void startRedirectionTimer();
void stopRedirectionTimer();
- void startIconLoader();
-
#if USE(LOW_BANDWIDTH_DISPLAY)
// implementation of CachedResourceClient
virtual void notifyFinished(CachedResource*);
@@ -575,10 +592,17 @@ namespace WebCore {
FrameState m_state;
FrameLoadType m_loadType;
+ // Document loaders for the three phases of frame loading. Note that while
+ // a new request is being loaded, the old document loader may still be referenced.
+ // E.g. while a new request is in the "policy" state, the old document loader may
+ // be consulted in particular as it makes sense to imply certain settings on the new loader.
RefPtr<DocumentLoader> m_documentLoader;
RefPtr<DocumentLoader> m_provisionalDocumentLoader;
RefPtr<DocumentLoader> m_policyDocumentLoader;
+ // This identifies the type of navigation action which prompted this load. Note
+ // that WebKit conveys this value as the WebActionNavigationTypeKey value
+ // on navigation action delegate callbacks.
FrameLoadType m_policyLoadType;
PolicyCheck m_policyCheck;
@@ -651,7 +675,7 @@ namespace WebCore {
RefPtr<HistoryItem> m_provisionalHistoryItem;
bool m_didPerformFirstNavigation;
-
+
#ifndef NDEBUG
bool m_didDispatchDidCommitLoad;
#endif
diff --git a/WebCore/loader/FrameLoaderClient.h b/WebCore/loader/FrameLoaderClient.h
index d2d31ee..6e2aba9 100644
--- a/WebCore/loader/FrameLoaderClient.h
+++ b/WebCore/loader/FrameLoaderClient.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 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
@@ -25,20 +25,20 @@
* (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 FrameLoaderClient_h
#define FrameLoaderClient_h
#include "FrameLoaderTypes.h"
-#include "StringHash.h"
#include <wtf/Forward.h>
#include <wtf/Platform.h>
+#include <wtf/Vector.h>
-#if PLATFORM(MAC)
-#ifdef __OBJC__
-@class NSCachedURLResponse;
-#else
+typedef class _jobject* jobject;
+
+#if PLATFORM(MAC) && !defined(__OBJC__)
class NSCachedURLResponse;
-#endif
+class NSView;
#endif
namespace WebCore {
@@ -74,11 +74,10 @@ namespace WebCore {
class FrameLoaderClient {
public:
- virtual ~FrameLoaderClient() { }
+ virtual ~FrameLoaderClient() { }
virtual void frameLoaderDestroyed() = 0;
virtual bool hasWebView() const = 0; // mainly for assertions
- virtual bool hasFrameView() const = 0; // ditto
virtual bool hasHTMLView() const { return true; }
@@ -90,7 +89,6 @@ namespace WebCore {
virtual void detachedFromParent2() = 0;
virtual void detachedFromParent3() = 0;
- virtual void detachedFromParent4() = 0;
virtual void assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&) = 0;
@@ -123,8 +121,8 @@ namespace WebCore {
virtual void dispatchShow() = 0;
virtual void dispatchDecidePolicyForMIMEType(FramePolicyFunction, const String& MIMEType, const ResourceRequest&) = 0;
- virtual void dispatchDecidePolicyForNewWindowAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, const String& frameName) = 0;
- virtual void dispatchDecidePolicyForNavigationAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&) = 0;
+ virtual void dispatchDecidePolicyForNewWindowAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName) = 0;
+ virtual void dispatchDecidePolicyForNavigationAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>) = 0;
virtual void cancelPolicyCheck() = 0;
virtual void dispatchUnableToImplementPolicy(const ResourceError&) = 0;
@@ -134,7 +132,6 @@ namespace WebCore {
virtual void dispatchDidLoadMainResource(DocumentLoader*) = 0;
virtual void revertToProvisionalState(DocumentLoader*) = 0;
virtual void setMainDocumentError(DocumentLoader*, const ResourceError&) = 0;
- virtual void clearUnarchivingState(DocumentLoader*) = 0;
// Maybe these should go into a ProgressTrackerClient some day
virtual void willChangeEstimatedProgress() { }
@@ -152,10 +149,8 @@ namespace WebCore {
virtual void committedLoad(DocumentLoader*, const char*, int) = 0;
virtual void finishedLoading(DocumentLoader*) = 0;
- virtual void finalSetupForReplace(DocumentLoader*) = 0;
- virtual void updateGlobalHistoryForStandardLoad(const KURL&) = 0;
- virtual void updateGlobalHistoryForReload(const KURL&) = 0;
+ virtual void updateGlobalHistory(const KURL&) = 0;
virtual bool shouldGoToHistoryItem(HistoryItem*) const = 0;
#ifdef ANDROID_HISTORY_CLIENT
virtual void dispatchDidAddHistoryItem(HistoryItem*) const = 0;
@@ -170,16 +165,10 @@ namespace WebCore {
virtual ResourceError cannotShowMIMETypeError(const ResourceResponse&) = 0;
virtual ResourceError fileDoesNotExistError(const ResourceResponse&) = 0;
+ virtual ResourceError pluginWillHandleLoadError(const ResourceResponse&) = 0;
virtual bool shouldFallBack(const ResourceError&) = 0;
- virtual void setDefersLoading(bool) = 0;
-
- virtual bool willUseArchive(ResourceLoader*, const ResourceRequest&, const KURL& originalURL) const = 0;
- virtual bool isArchiveLoadPending(ResourceLoader*) const = 0;
- virtual void cancelPendingArchiveLoad(ResourceLoader*) = 0;
- virtual void clearArchivedResources() = 0;
-
virtual bool canHandleRequest(const ResourceRequest&) const = 0;
virtual bool canShowMIMEType(const String& MIMEType) const = 0;
virtual bool representationExistsForURLScheme(const String& URLScheme) const = 0;
@@ -220,6 +209,9 @@ namespace WebCore {
virtual void registerForIconNotification(bool listen = true) = 0;
#if PLATFORM(MAC)
+#if ENABLE(MAC_JAVA_BRIDGE)
+ virtual jobject javaApplet(NSView*) { return 0; }
+#endif
virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long identifier, NSCachedURLResponse*) const = 0;
#endif
};
diff --git a/WebCore/loader/ImageDocument.cpp b/WebCore/loader/ImageDocument.cpp
index f622a52..39b1db6 100644
--- a/WebCore/loader/ImageDocument.cpp
+++ b/WebCore/loader/ImageDocument.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 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
@@ -35,6 +35,7 @@
#include "FrameView.h"
#include "HTMLImageElement.h"
#include "HTMLNames.h"
+#include "LocalizedStrings.h"
#include "MouseEvent.h"
#include "Page.h"
#include "SegmentedString.h"
@@ -42,23 +43,19 @@
#include "Text.h"
#include "XMLTokenizer.h"
-#if PLATFORM(MAC)
-#include "ImageDocumentMac.h"
-#endif
-
using std::min;
namespace WebCore {
-using namespace EventNames;
using namespace HTMLNames;
class ImageEventListener : public EventListener {
public:
- ImageEventListener(ImageDocument* doc) : m_doc(doc) { }
+ static PassRefPtr<ImageEventListener> create(ImageDocument* document) { return adoptRef(new ImageEventListener(document)); }
virtual void handleEvent(Event*, bool isWindowEvent);
private:
+ ImageEventListener(ImageDocument* document) : m_doc(document) { }
ImageDocument* m_doc;
};
@@ -114,17 +111,23 @@ void ImageTokenizer::finish()
// If this is a multipart image, make a copy of the current part, since the resource data
// will be overwritten by the next part.
if (m_doc->frame()->loader()->documentLoader()->isLoadingMultipartContent())
- data = new SharedBuffer(data->data(), data->size());
+ data = data->copy();
cachedImage->data(data.release(), true);
cachedImage->finish();
cachedImage->setResponse(m_doc->frame()->loader()->documentLoader()->response());
-
- // FIXME: Need code to set the title for platforms other than Mac OS X.
-#if PLATFORM(MAC)
- finishImageLoad(m_doc, cachedImage);
-#endif
+
+ IntSize size = cachedImage->imageSize(m_doc->frame()->pageZoomFactor());
+ if (size.width()) {
+ // Compute the title, we use the filename of the resource, falling
+ // back on the hostname if there is no path.
+ String fileName = m_doc->url().lastPathComponent();
+ if (fileName.isEmpty())
+ fileName = m_doc->url().host();
+ m_doc->setTitle(imageTitle(fileName, size));
+ }
+
m_doc->imageChanged();
}
@@ -139,8 +142,8 @@ bool ImageTokenizer::isWaitingForScripts() const
// --------
-ImageDocument::ImageDocument(DOMImplementation* implementation, Frame* frame)
- : HTMLDocument(implementation, frame)
+ImageDocument::ImageDocument(Frame* frame)
+ : HTMLDocument(frame)
, m_imageElement(0)
, m_imageSizeIsKnown(false)
, m_didShrinkImage(false)
@@ -170,13 +173,13 @@ void ImageDocument::createDocumentStructure()
imageElement->setAttribute(styleAttr, "-webkit-user-select: none");
imageElement->setLoadManually(true);
- imageElement->setSrc(url());
+ imageElement->setSrc(url().string());
body->appendChild(imageElement, ec);
if (shouldShrinkToFit()) {
// Add event listeners
- RefPtr<EventListener> listener = new ImageEventListener(this);
+ RefPtr<EventListener> listener = ImageEventListener::create(this);
addWindowEventListener("resize", listener, false);
imageElement->addEventListener("click", listener.release(), false);
}
@@ -189,7 +192,7 @@ float ImageDocument::scale() const
if (!m_imageElement)
return 1.0f;
- IntSize imageSize = m_imageElement->cachedImage()->imageSize();
+ IntSize imageSize = m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor());
IntSize windowSize = IntSize(frame()->view()->width(), frame()->view()->height());
float widthScale = (float)windowSize.width() / imageSize.width();
@@ -203,7 +206,7 @@ void ImageDocument::resizeImageToFit()
if (!m_imageElement)
return;
- IntSize imageSize = m_imageElement->cachedImage()->imageSize();
+ IntSize imageSize = m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor());
float scale = this->scale();
m_imageElement->setWidth(static_cast<int>(imageSize.width() * scale));
@@ -232,7 +235,7 @@ void ImageDocument::imageClicked(int x, int y)
int scrollX = static_cast<int>(x / scale - (float)frame()->view()->width() / 2);
int scrollY = static_cast<int>(y / scale - (float)frame()->view()->height() / 2);
- frame()->view()->setContentsPos(scrollX, scrollY);
+ frame()->view()->setScrollPosition(IntPoint(scrollX, scrollY));
}
}
@@ -243,7 +246,7 @@ void ImageDocument::imageChanged()
if (m_imageSizeIsKnown)
return;
- if (m_imageElement->cachedImage()->imageSize().isEmpty())
+ if (m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()).isEmpty())
return;
m_imageSizeIsKnown = true;
@@ -259,8 +262,8 @@ void ImageDocument::restoreImageSize()
if (!m_imageElement || !m_imageSizeIsKnown)
return;
- m_imageElement->setWidth(m_imageElement->cachedImage()->imageSize().width());
- m_imageElement->setHeight(m_imageElement->cachedImage()->imageSize().height());
+ m_imageElement->setWidth(m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()).width());
+ m_imageElement->setHeight(m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()).height());
ExceptionCode ec;
if (imageFitsInWindow())
@@ -276,7 +279,7 @@ bool ImageDocument::imageFitsInWindow() const
if (!m_imageElement)
return true;
- IntSize imageSize = m_imageElement->cachedImage()->imageSize();
+ IntSize imageSize = m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor());
IntSize windowSize = IntSize(frame()->view()->width(), frame()->view()->height());
return imageSize.width() <= windowSize.width() && imageSize.height() <= windowSize.height();
@@ -335,9 +338,9 @@ bool ImageDocument::shouldShrinkToFit() const
void ImageEventListener::handleEvent(Event* event, bool isWindowEvent)
{
- if (event->type() == resizeEvent)
+ if (event->type() == eventNames().resizeEvent)
m_doc->windowSizeChanged();
- else if (event->type() == clickEvent) {
+ else if (event->type() == eventNames().clickEvent) {
MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
m_doc->imageClicked(mouseEvent->x(), mouseEvent->y());
}
diff --git a/WebCore/loader/ImageDocument.h b/WebCore/loader/ImageDocument.h
index 0cca1a7..1bc5245 100644
--- a/WebCore/loader/ImageDocument.h
+++ b/WebCore/loader/ImageDocument.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 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
@@ -33,12 +33,11 @@ class ImageDocumentElement;
class ImageDocument : public HTMLDocument {
public:
- ImageDocument(DOMImplementation*, Frame*);
+ static PassRefPtr<ImageDocument> create(Frame* frame)
+ {
+ return new ImageDocument(frame);
+ }
- virtual bool isImageDocument() const { return true; }
-
- virtual Tokenizer* createTokenizer();
-
CachedImage* cachedImage();
ImageDocumentElement* imageElement() const { return m_imageElement; }
void disconnectImageElement() { m_imageElement = 0; }
@@ -48,6 +47,11 @@ public:
void imageClicked(int x, int y);
private:
+ ImageDocument(Frame*);
+
+ virtual Tokenizer* createTokenizer();
+ virtual bool isImageDocument() const { return true; }
+
void createDocumentStructure();
void resizeImageToFit();
void restoreImageSize();
diff --git a/WebCore/loader/ImageLoader.cpp b/WebCore/loader/ImageLoader.cpp
new file mode 100644
index 0000000..da159b4
--- /dev/null
+++ b/WebCore/loader/ImageLoader.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "ImageLoader.h"
+
+#include "CSSHelper.h"
+#include "CachedImage.h"
+#include "DocLoader.h"
+#include "Document.h"
+#include "Element.h"
+#include "RenderImage.h"
+
+namespace WebCore {
+
+ImageLoader::ImageLoader(Element* elt)
+ : m_element(elt)
+ , m_image(0)
+ , m_firedLoad(true)
+ , m_imageComplete(true)
+ , m_loadManually(false)
+{
+}
+
+ImageLoader::~ImageLoader()
+{
+ if (m_image)
+ m_image->removeClient(this);
+ m_element->document()->removeImage(this);
+}
+
+void ImageLoader::setImage(CachedImage* newImage)
+{
+ CachedImage* oldImage = m_image.get();
+ if (newImage != oldImage) {
+ setLoadingImage(newImage);
+ m_firedLoad = true;
+ m_imageComplete = true;
+ if (newImage)
+ newImage->addClient(this);
+ if (oldImage)
+ oldImage->removeClient(this);
+ }
+
+ if (RenderObject* renderer = element()->renderer()) {
+ if (!renderer->isImage())
+ return;
+
+ static_cast<RenderImage*>(renderer)->resetAnimation();
+ }
+}
+
+void ImageLoader::setLoadingImage(CachedImage* loadingImage)
+{
+ m_firedLoad = false;
+ m_imageComplete = false;
+ m_image = loadingImage;
+}
+
+void ImageLoader::updateFromElement()
+{
+ // If we're not making renderers for the page, then don't load images. We don't want to slow
+ // down the raw HTML parsing case by loading images we don't intend to display.
+ Element* elem = element();
+ Document* doc = elem->document();
+ if (!doc->renderer())
+ return;
+
+ AtomicString attr = elem->getAttribute(elem->imageSourceAttributeName());
+
+ // Do not load any image if the 'src' attribute is missing or if it is
+ // an empty string referring to a local file. The latter condition is
+ // a quirk that preserves old behavior that Dashboard widgets
+ // need (<rdar://problem/5994621>).
+ CachedImage* newImage = 0;
+ if (!(attr.isNull() || attr.isEmpty() && doc->baseURI().isLocalFile())) {
+ if (m_loadManually) {
+ doc->docLoader()->setAutoLoadImages(false);
+ newImage = new CachedImage(sourceURI(attr));
+ newImage->setLoading(true);
+ newImage->setDocLoader(doc->docLoader());
+ doc->docLoader()->m_docResources.set(newImage->url(), newImage);
+ } else
+ newImage = doc->docLoader()->requestImage(sourceURI(attr));
+ }
+
+ CachedImage* oldImage = m_image.get();
+ if (newImage != oldImage) {
+ setLoadingImage(newImage);
+ if (newImage)
+ newImage->addClient(this);
+ if (oldImage)
+ oldImage->removeClient(this);
+ }
+
+ if (RenderObject* renderer = elem->renderer()) {
+ if (!renderer->isImage())
+ return;
+
+ static_cast<RenderImage*>(renderer)->resetAnimation();
+ }
+}
+
+void ImageLoader::notifyFinished(CachedResource *image)
+{
+ m_imageComplete = true;
+
+ Element* elem = element();
+ elem->document()->dispatchImageLoadEventSoon(this);
+
+ if (RenderObject* renderer = elem->renderer()) {
+ if (!renderer->isImage())
+ return;
+
+ static_cast<RenderImage*>(renderer)->setCachedImage(m_image.get());
+ }
+}
+
+}
diff --git a/WebCore/loader/ImageLoader.h b/WebCore/loader/ImageLoader.h
new file mode 100644
index 0000000..39b5cc4
--- /dev/null
+++ b/WebCore/loader/ImageLoader.h
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2004 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ImageLoader_h
+#define ImageLoader_h
+
+#include "CachedResourceClient.h"
+#include "CachedResourceHandle.h"
+
+namespace WebCore {
+
+class AtomicString;
+class Element;
+
+class ImageLoader : public CachedResourceClient {
+public:
+ ImageLoader(Element*);
+ virtual ~ImageLoader();
+
+ void updateFromElement();
+
+ virtual void dispatchLoadEvent() = 0;
+ virtual String sourceURI(const AtomicString&) const = 0;
+
+ Element* element() const { return m_element; }
+ bool imageComplete() const { return m_imageComplete; }
+
+ CachedImage* image() const { return m_image.get(); }
+ void setImage(CachedImage*);
+
+ void setLoadManually(bool loadManually) { m_loadManually = loadManually; }
+
+ // CachedResourceClient API
+ virtual void notifyFinished(CachedResource*);
+
+ bool haveFiredLoadEvent() const { return m_firedLoad; }
+protected:
+ void setLoadingImage(CachedImage*);
+
+ void setHaveFiredLoadEvent(bool firedLoad) { m_firedLoad = firedLoad; }
+
+private:
+ Element* m_element;
+ CachedResourceHandle<CachedImage> m_image;
+ bool m_firedLoad : 1;
+ bool m_imageComplete : 1;
+ bool m_loadManually : 1;
+};
+
+}
+
+#endif
diff --git a/WebCore/loader/MainResourceLoader.cpp b/WebCore/loader/MainResourceLoader.cpp
index c591c2f..079bd79 100644
--- a/WebCore/loader/MainResourceLoader.cpp
+++ b/WebCore/loader/MainResourceLoader.cpp
@@ -29,13 +29,20 @@
#include "config.h"
#include "MainResourceLoader.h"
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+#include "ApplicationCache.h"
+#include "ApplicationCacheGroup.h"
+#include "ApplicationCacheResource.h"
+#endif
#include "DocumentLoader.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameLoaderClient.h"
#include "HTMLFormElement.h"
+#include "Page.h"
#include "ResourceError.h"
#include "ResourceHandle.h"
+#include "Settings.h"
// FIXME: More that is in common with SubresourceLoader should move up into ResourceLoader.
@@ -55,7 +62,7 @@ MainResourceLoader::~MainResourceLoader()
PassRefPtr<MainResourceLoader> MainResourceLoader::create(Frame* frame)
{
- return new MainResourceLoader(frame);
+ return adoptRef(new MainResourceLoader(frame));
}
void MainResourceLoader::receivedError(const ResourceError& error)
@@ -162,10 +169,7 @@ void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const Reso
if (newRequest.cachePolicy() == UseProtocolCachePolicy && isPostOrRedirectAfterPost(newRequest, redirectResponse))
newRequest.setCachePolicy(ReloadIgnoringCacheData);
- if (!newRequest.isNull()) {
- ResourceLoader::willSendRequest(newRequest, redirectResponse);
- setRequest(newRequest);
- }
+ ResourceLoader::willSendRequest(newRequest, redirectResponse);
// Don't set this on the first request. It is set when the main load was started.
m_documentLoader->setRequest(newRequest);
@@ -205,7 +209,9 @@ void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy,
case PolicyDownload:
frameLoader()->client()->download(m_handle.get(), request(), m_handle.get()->request(), r);
- receivedError(interruptionForPolicyChangeError());
+ // It might have gone missing
+ if (frameLoader())
+ receivedError(interruptionForPolicyChangeError());
return;
case PolicyIgnore:
@@ -262,7 +268,11 @@ void MainResourceLoader::continueAfterContentPolicy(PolicyAction policy)
void MainResourceLoader::didReceiveResponse(const ResourceResponse& r)
{
+ // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
+ // See <rdar://problem/6304600> for more details.
+#if !PLATFORM(CF)
ASSERT(shouldLoadAsEmptyDocument(r.url()) || !defersLoading());
+#endif
if (m_loadingMultipartContent) {
frameLoader()->setupForReplaceByMIMEType(r.mimeType());
@@ -290,7 +300,12 @@ void MainResourceLoader::didReceiveData(const char* data, int length, long long
{
ASSERT(data);
ASSERT(length != 0);
+
+ // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
+ // See <rdar://problem/6304600> for more details.
+#if !PLATFORM(CF)
ASSERT(!defersLoading());
+#endif
// The additional processing can do anything including possibly removing the last
// reference to this object; one example of this is 3266216.
@@ -301,20 +316,41 @@ void MainResourceLoader::didReceiveData(const char* data, int length, long long
void MainResourceLoader::didFinishLoading()
{
+ // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
+ // See <rdar://problem/6304600> for more details.
+#if !PLATFORM(CF)
ASSERT(shouldLoadAsEmptyDocument(frameLoader()->activeDocumentLoader()->url()) || !defersLoading());
-
+#endif
+
// The additional processing can do anything including possibly removing the last
// reference to this object.
RefPtr<MainResourceLoader> protect(this);
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ RefPtr<DocumentLoader> dl = documentLoader();
+#endif
+
frameLoader()->finishedLoading();
ResourceLoader::didFinishLoading();
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ ApplicationCacheGroup* group = dl->candidateApplicationCacheGroup();
+ if (!group && dl->applicationCache() && !dl->mainResourceApplicationCache())
+ group = dl->applicationCache()->group();
+
+ if (group)
+ group->finishedLoadingMainResource(dl.get());
+#endif
}
void MainResourceLoader::didFail(const ResourceError& error)
{
+ // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
+ // See <rdar://problem/6304600> for more details.
+#if !PLATFORM(CF)
ASSERT(!defersLoading());
-
+#endif
+
receivedError(error);
}
@@ -385,12 +421,36 @@ bool MainResourceLoader::loadNow(ResourceRequest& r)
return false;
}
-bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& substituteData)
+bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& substituteData)
{
ASSERT(!m_handle);
m_substituteData = substituteData;
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ // Check if this request should be loaded from the application cache
+ if (!m_substituteData.isValid() && frameLoader()->frame()->settings() && frameLoader()->frame()->settings()->offlineWebApplicationCacheEnabled()) {
+ ASSERT(!m_applicationCache);
+
+ if (Page* page = frameLoader()->frame()->page()) {
+ if (frameLoader()->frame() == page->mainFrame())
+ m_applicationCache = ApplicationCacheGroup::cacheForMainRequest(r, m_documentLoader.get());
+ else
+ m_applicationCache = frameLoader()->documentLoader()->topLevelApplicationCache();
+ }
+
+ if (m_applicationCache) {
+ // Get the resource from the application cache.
+ // FIXME: If the resource does not exist, the load should fail.
+ if (ApplicationCacheResource* resource = m_applicationCache->resourceForRequest(r)) {
+ m_substituteData = SubstituteData(resource->data(),
+ resource->response().mimeType(),
+ resource->response().textEncodingName(), KURL());
+ }
+ }
+ }
+#endif
+
ResourceRequest request(r);
bool defer = defersLoading();
if (defer) {
diff --git a/WebCore/loader/MainResourceLoader.h b/WebCore/loader/MainResourceLoader.h
index 5d1e42e..6c69c1f 100644
--- a/WebCore/loader/MainResourceLoader.h
+++ b/WebCore/loader/MainResourceLoader.h
@@ -34,6 +34,9 @@
namespace WebCore {
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ class ApplicationCache;
+#endif
class FormState;
class ResourceRequest;
@@ -57,6 +60,10 @@ namespace WebCore {
bool isLoadingMultipartContent() const { return m_loadingMultipartContent; }
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ ApplicationCache* applicationCache() const { return m_applicationCache.get(); }
+#endif
+
private:
MainResourceLoader(Frame*);
@@ -85,6 +92,11 @@ namespace WebCore {
SubstituteData m_substituteData;
Timer<MainResourceLoader> m_dataLoadTimer;
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ // The application cache that the main resource was loaded from (if any).
+ RefPtr<ApplicationCache> m_applicationCache;
+#endif
+
bool m_loadingMultipartContent;
bool m_waitingForContentPolicy;
};
diff --git a/WebCore/loader/MediaDocument.cpp b/WebCore/loader/MediaDocument.cpp
new file mode 100644
index 0000000..5689457
--- /dev/null
+++ b/WebCore/loader/MediaDocument.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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"
+
+#if ENABLE(VIDEO)
+#include "MediaDocument.h"
+
+#include "DocumentLoader.h"
+#include "Element.h"
+#include "Event.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClient.h"
+#include "HTMLEmbedElement.h"
+#include "HTMLNames.h"
+#include "HTMLVideoElement.h"
+#include "MainResourceLoader.h"
+#include "Page.h"
+#include "SegmentedString.h"
+#include "Settings.h"
+#include "Text.h"
+#include "XMLTokenizer.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+class MediaTokenizer : public Tokenizer {
+public:
+ MediaTokenizer(Document* doc) : m_doc(doc), m_mediaElement(0) {}
+
+ virtual bool write(const SegmentedString&, bool appendData);
+ virtual void stopParsing();
+ virtual void finish();
+ virtual bool isWaitingForScripts() const;
+
+ virtual bool wantsRawData() const { return true; }
+ virtual bool writeRawData(const char* data, int len);
+
+ void createDocumentStructure();
+private:
+ Document* m_doc;
+ HTMLMediaElement* m_mediaElement;
+};
+
+bool MediaTokenizer::write(const SegmentedString& s, bool appendData)
+{
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
+void MediaTokenizer::createDocumentStructure()
+{
+ ExceptionCode ec;
+ RefPtr<Element> rootElement = m_doc->createElementNS(xhtmlNamespaceURI, "html", ec);
+ m_doc->appendChild(rootElement, ec);
+
+ RefPtr<Element> body = m_doc->createElementNS(xhtmlNamespaceURI, "body", ec);
+ body->setAttribute(styleAttr, "background-color: rgb(38,38,38);");
+
+ rootElement->appendChild(body, ec);
+
+ RefPtr<Element> mediaElement = m_doc->createElementNS(xhtmlNamespaceURI, "video", ec);
+
+ m_mediaElement = static_cast<HTMLVideoElement*>(mediaElement.get());
+ m_mediaElement->setAttribute(controlsAttr, "");
+ m_mediaElement->setAttribute(autoplayAttr, "");
+ m_mediaElement->setAttribute(styleAttr, "margin: auto; position: absolute; top: 0; right: 0; bottom: 0; left: 0;");
+
+ m_mediaElement->setAttribute(nameAttr, "media");
+ m_mediaElement->setSrc(m_doc->url());
+
+ body->appendChild(mediaElement, ec);
+
+ Frame* frame = m_doc->frame();
+ if (!frame)
+ return;
+
+ frame->loader()->activeDocumentLoader()->mainResourceLoader()->setShouldBufferData(false);
+}
+
+bool MediaTokenizer::writeRawData(const char* data, int len)
+{
+ ASSERT(!m_mediaElement);
+ if (m_mediaElement)
+ return false;
+
+ createDocumentStructure();
+ finish();
+ return false;
+}
+
+void MediaTokenizer::stopParsing()
+{
+ Tokenizer::stopParsing();
+}
+
+void MediaTokenizer::finish()
+{
+ if (!m_parserStopped)
+ m_doc->finishedParsing();
+}
+
+bool MediaTokenizer::isWaitingForScripts() const
+{
+ // A media document is never waiting for scripts
+ return false;
+}
+
+MediaDocument::MediaDocument(Frame* frame)
+ : HTMLDocument(frame)
+{
+ setParseMode(Compat);
+}
+
+Tokenizer* MediaDocument::createTokenizer()
+{
+ return new MediaTokenizer(this);
+}
+
+void MediaDocument::defaultEventHandler(Event* event)
+{
+ // Match the default Quicktime plugin behavior to allow
+ // clicking and double-clicking to pause and play the media.
+ EventTargetNode* targetNode = event->target()->toNode();
+ if (targetNode && targetNode->hasTagName(videoTag)) {
+ HTMLVideoElement* video = static_cast<HTMLVideoElement*>(targetNode);
+ ExceptionCode ec;
+ if (event->type() == eventNames().clickEvent) {
+ if (!video->canPlay()) {
+ video->pause(ec);
+ event->setDefaultHandled();
+ }
+ } else if (event->type() == eventNames().dblclickEvent) {
+ if (video->canPlay()) {
+ video->play(ec);
+ event->setDefaultHandled();
+ }
+ }
+ }
+}
+
+}
+#endif
diff --git a/WebCore/loader/mac/ImageDocumentMac.h b/WebCore/loader/MediaDocument.h
index 3c7c509..e5167e4 100644
--- a/WebCore/loader/mac/ImageDocumentMac.h
+++ b/WebCore/loader/MediaDocument.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * 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
@@ -10,28 +10,45 @@
* 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 COMPUTER, INC. ``AS IS'' AND ANY
+ * 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 COMPUTER, INC. OR
+ * 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 ImageDocumentMac_h
-#define ImageDocumentMac_h
+#ifndef MediaDocument_h
+#define MediaDocument_h
+
+#if ENABLE(VIDEO)
+
+#include "HTMLDocument.h"
namespace WebCore {
- class CachedImage;
- class Document;
+class MediaDocument : public HTMLDocument {
+public:
+ static PassRefPtr<MediaDocument> create(Frame* frame)
+ {
+ return new MediaDocument(frame);
+ }
- void finishImageLoad(Document*, CachedImage*);
+ virtual void defaultEventHandler(Event*);
+private:
+ MediaDocument(Frame*);
+
+ virtual bool isMediaDocument() const { return true; }
+ virtual Tokenizer* createTokenizer();
+};
+
}
#endif
+#endif
diff --git a/WebCore/loader/NetscapePlugInStreamLoader.cpp b/WebCore/loader/NetscapePlugInStreamLoader.cpp
index 47c9599..e12ed07 100644
--- a/WebCore/loader/NetscapePlugInStreamLoader.cpp
+++ b/WebCore/loader/NetscapePlugInStreamLoader.cpp
@@ -46,7 +46,7 @@ NetscapePlugInStreamLoader::~NetscapePlugInStreamLoader()
PassRefPtr<NetscapePlugInStreamLoader> NetscapePlugInStreamLoader::create(Frame* frame, NetscapePlugInStreamLoaderClient* client)
{
- return new NetscapePlugInStreamLoader(frame, client);
+ return adoptRef(new NetscapePlugInStreamLoader(frame, client));
}
bool NetscapePlugInStreamLoader::isDone() const
@@ -76,7 +76,13 @@ void NetscapePlugInStreamLoader::didReceiveResponse(const ResourceResponse& resp
if (!m_client)
return;
- if (response.isHTTP() && (response.httpStatusCode() < 100 || response.httpStatusCode() >= 400))
+ if (!response.isHTTP())
+ return;
+
+ if (m_client->wantsAllStreams())
+ return;
+
+ if (response.httpStatusCode() < 100 || response.httpStatusCode() >= 400)
didCancel(frameLoader()->fileDoesNotExistError(response));
}
@@ -111,8 +117,13 @@ void NetscapePlugInStreamLoader::didCancel(const ResourceError& error)
{
RefPtr<NetscapePlugInStreamLoader> protect(this);
- m_documentLoader->removePlugInStreamLoader(this);
m_client->didFail(this, error);
+
+ // We need to remove the stream loader after the call to didFail, since didFail can
+ // spawn a new run loop and if the loader has been removed it won't be deferred when
+ // the document loader is asked to defer loading.
+ m_documentLoader->removePlugInStreamLoader(this);
+
ResourceLoader::didCancel(error);
}
diff --git a/WebCore/loader/NetscapePlugInStreamLoader.h b/WebCore/loader/NetscapePlugInStreamLoader.h
index 19b0428..092c6fc 100644
--- a/WebCore/loader/NetscapePlugInStreamLoader.h
+++ b/WebCore/loader/NetscapePlugInStreamLoader.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005, 2006, 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
@@ -30,15 +30,19 @@
#include <wtf/Forward.h>
namespace WebCore {
+
class NetscapePlugInStreamLoader;
class NetscapePlugInStreamLoaderClient {
public:
- virtual ~NetscapePlugInStreamLoaderClient() { }
virtual void didReceiveResponse(NetscapePlugInStreamLoader*, const ResourceResponse&) = 0;
virtual void didReceiveData(NetscapePlugInStreamLoader*, const char*, int) = 0;
virtual void didFail(NetscapePlugInStreamLoader*, const ResourceError&) = 0;
virtual void didFinishLoading(NetscapePlugInStreamLoader*) { }
+ virtual bool wantsAllStreams() const { return false; }
+
+ protected:
+ virtual ~NetscapePlugInStreamLoaderClient() { }
};
class NetscapePlugInStreamLoader : public ResourceLoader {
@@ -48,6 +52,7 @@ namespace WebCore {
bool isDone() const;
+ private:
virtual void didReceiveResponse(const ResourceResponse&);
virtual void didReceiveData(const char*, int, long long lengthReceived, bool allAtOnce);
virtual void didFinishLoading();
@@ -55,7 +60,6 @@ namespace WebCore {
virtual void releaseResources();
- private:
NetscapePlugInStreamLoader(Frame*, NetscapePlugInStreamLoaderClient*);
virtual void didCancel(const ResourceError& error);
diff --git a/WebCore/loader/PluginDocument.cpp b/WebCore/loader/PluginDocument.cpp
index 492c0f6..8be6ae2 100644
--- a/WebCore/loader/PluginDocument.cpp
+++ b/WebCore/loader/PluginDocument.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006, 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
@@ -88,7 +88,7 @@ void PluginTokenizer::createDocumentStructure()
m_embedElement->setAttribute(heightAttr, "100%");
m_embedElement->setAttribute(nameAttr, "plugin");
- m_embedElement->setSrc(m_doc->url());
+ m_embedElement->setSrc(m_doc->url().string());
m_embedElement->setType(m_doc->frame()->loader()->responseMIMEType());
body->appendChild(embedElement, ec);
@@ -96,28 +96,26 @@ void PluginTokenizer::createDocumentStructure()
bool PluginTokenizer::writeRawData(const char* data, int len)
{
- if (!m_embedElement) {
- createDocumentStructure();
+ ASSERT(!m_embedElement);
+ if (m_embedElement)
+ return false;
+
+ createDocumentStructure();
- if (Frame* frame = m_doc->frame()) {
- Settings* settings = frame->settings();
- if (settings && settings->arePluginsEnabled()) {
- m_doc->updateLayout();
-
- if (RenderWidget* renderer = static_cast<RenderWidget*>(m_embedElement->renderer())) {
- frame->loader()->client()->redirectDataToPlugin(renderer->widget());
- frame->loader()->activeDocumentLoader()->mainResourceLoader()->setShouldBufferData(false);
- }
-
- finish();
+ if (Frame* frame = m_doc->frame()) {
+ Settings* settings = frame->settings();
+ if (settings && settings->arePluginsEnabled()) {
+ m_doc->updateLayout();
+
+ if (RenderWidget* renderer = static_cast<RenderWidget*>(m_embedElement->renderer())) {
+ frame->loader()->client()->redirectDataToPlugin(renderer->widget());
+ frame->loader()->activeDocumentLoader()->mainResourceLoader()->setShouldBufferData(false);
}
- }
- return false;
+ finish();
+ }
}
-
- ASSERT_NOT_REACHED();
-
+
return false;
}
@@ -138,8 +136,8 @@ bool PluginTokenizer::isWaitingForScripts() const
return false;
}
-PluginDocument::PluginDocument(DOMImplementation* implementation, Frame* frame)
- : HTMLDocument(implementation, frame)
+PluginDocument::PluginDocument(Frame* frame)
+ : HTMLDocument(frame)
{
setParseMode(Compat);
}
diff --git a/WebCore/loader/PluginDocument.h b/WebCore/loader/PluginDocument.h
index 9e62753..35e4038 100644
--- a/WebCore/loader/PluginDocument.h
+++ b/WebCore/loader/PluginDocument.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006, 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
@@ -21,6 +21,7 @@
* (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 PluginDocument_h
#define PluginDocument_h
@@ -28,19 +29,20 @@
namespace WebCore {
-class DOMImplementation;
-class FrameView;
-
-class PluginDocument : public HTMLDocument
-{
+class PluginDocument : public HTMLDocument {
public:
- PluginDocument(DOMImplementation*, Frame*);
+ static PassRefPtr<PluginDocument> create(Frame* frame)
+ {
+ return new PluginDocument(frame);
+ }
+
+private:
+ PluginDocument(Frame*);
virtual bool isPluginDocument() const { return true; }
-
virtual Tokenizer* createTokenizer();
};
}
-#endif // ImageDocument_h
+#endif // PluginDocument_h
diff --git a/WebCore/loader/ResourceLoader.cpp b/WebCore/loader/ResourceLoader.cpp
index ea96e34..a6f90b3 100644
--- a/WebCore/loader/ResourceLoader.cpp
+++ b/WebCore/loader/ResourceLoader.cpp
@@ -54,16 +54,19 @@ PassRefPtr<SharedBuffer> ResourceLoader::resourceData()
}
ResourceLoader::ResourceLoader(Frame* frame, bool sendResourceLoadCallbacks, bool shouldContentSniff)
- : m_reachedTerminalState(false)
+ : m_frame(frame)
+ , m_documentLoader(frame->loader()->activeDocumentLoader())
+ , m_identifier(0)
+ , m_reachedTerminalState(false)
, m_cancelled(false)
, m_calledDidFinishLoad(false)
, m_sendResourceLoadCallbacks(sendResourceLoadCallbacks)
, m_shouldContentSniff(shouldContentSniff)
, m_shouldBufferData(true)
- , m_frame(frame)
- , m_documentLoader(frame->loader()->activeDocumentLoader())
- , m_identifier(0)
, m_defersLoading(frame->page()->defersLoading())
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ , m_wasLoadedFromApplicationCache(false)
+#endif
{
}
@@ -106,9 +109,7 @@ bool ResourceLoader::load(const ResourceRequest& r)
{
ASSERT(!m_handle);
ASSERT(m_deferredRequest.isNull());
- ASSERT(!frameLoader()->isArchiveLoadPending(this));
-
- m_originalURL = r.url();
+ ASSERT(!m_documentLoader->isSubstituteLoadPending(this));
ResourceRequest clientRequest(r);
willSendRequest(clientRequest, ResourceResponse());
@@ -117,9 +118,18 @@ bool ResourceLoader::load(const ResourceRequest& r)
return false;
}
- if (frameLoader()->willUseArchive(this, clientRequest, m_originalURL))
+#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
+ if (m_documentLoader->scheduleArchiveLoad(this, clientRequest, r.url()))
return true;
+#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ if (m_documentLoader->scheduleApplicationCacheLoad(this, clientRequest, r.url())) {
+ m_wasLoadedFromApplicationCache = true;
+ return true;
+ }
+#endif
+
if (m_defersLoading) {
m_deferredRequest = clientRequest;
return true;
@@ -165,7 +175,7 @@ void ResourceLoader::addData(const char* data, int length, bool allAtOnce)
return;
if (allAtOnce) {
- m_resourceData = new SharedBuffer(data, length);
+ m_resourceData = SharedBuffer::create(data, length);
return;
}
@@ -175,7 +185,7 @@ void ResourceLoader::addData(const char* data, int length, bool allAtOnce)
m_resourceData->append(data, length);
} else {
if (!m_resourceData)
- m_resourceData = new SharedBuffer(data, length);
+ m_resourceData = SharedBuffer::create(data, length);
else
m_resourceData->append(data, length);
}
@@ -207,6 +217,10 @@ void ResourceLoader::willSendRequest(ResourceRequest& request, const ResourceRes
m_request = request;
}
+void ResourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
+{
+}
+
void ResourceLoader::didReceiveResponse(const ResourceResponse& r)
{
ASSERT(!m_reachedTerminalState);
@@ -217,6 +231,9 @@ void ResourceLoader::didReceiveResponse(const ResourceResponse& r)
m_response = r;
+ if (FormData* data = m_request.httpBody())
+ data->removeGeneratedFilesIfNeeded();
+
if (m_sendResourceLoadCallbacks)
frameLoader()->didReceiveResponse(this, m_response);
}
@@ -247,7 +264,7 @@ void ResourceLoader::willStopBufferingData(const char* data, int length)
return;
ASSERT(!m_resourceData);
- m_resourceData = new SharedBuffer(data, length);
+ m_resourceData = SharedBuffer::create(data, length);
}
void ResourceLoader::didFinishLoading()
@@ -285,22 +302,23 @@ void ResourceLoader::didFail(const ResourceError& error)
// anything including possibly derefing this; one example of this is Radar 3266216.
RefPtr<ResourceLoader> protector(this);
+ if (FormData* data = m_request.httpBody())
+ data->removeGeneratedFilesIfNeeded();
+
if (m_sendResourceLoadCallbacks && !m_calledDidFinishLoad)
frameLoader()->didFailToLoad(this, error);
releaseResources();
}
-void ResourceLoader::wasBlocked()
-{
- didFail(blockedError());
-}
-
void ResourceLoader::didCancel(const ResourceError& error)
{
ASSERT(!m_cancelled);
ASSERT(!m_reachedTerminalState);
+ if (FormData* data = m_request.httpBody())
+ data->removeGeneratedFilesIfNeeded();
+
// This flag prevents bad behavior when loads that finish cause the
// load itself to be cancelled (which could happen with a javascript that
// changes the window location). This is used to prevent both the body
@@ -311,7 +329,7 @@ void ResourceLoader::didCancel(const ResourceError& error)
if (m_handle)
m_handle->clearAuthentication();
- frameLoader()->cancelPendingArchiveLoad(this);
+ m_documentLoader->cancelPendingSubstituteLoad(this);
if (m_handle) {
m_handle->cancel();
m_handle = 0;
@@ -352,11 +370,21 @@ ResourceError ResourceLoader::blockedError()
return frameLoader()->blockedError(m_request);
}
+ResourceError ResourceLoader::cannotShowURLError()
+{
+ return frameLoader()->cannotShowURLError(m_request);
+}
+
void ResourceLoader::willSendRequest(ResourceHandle*, ResourceRequest& request, const ResourceResponse& redirectResponse)
{
willSendRequest(request, redirectResponse);
}
+void ResourceLoader::didSendData(ResourceHandle*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
+{
+ didSendData(bytesSent, totalBytesToBeSent);
+}
+
void ResourceLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
{
didReceiveResponse(response);
@@ -379,7 +407,12 @@ void ResourceLoader::didFail(ResourceHandle*, const ResourceError& error)
void ResourceLoader::wasBlocked(ResourceHandle*)
{
- wasBlocked();
+ didFail(blockedError());
+}
+
+void ResourceLoader::cannotShowURL(ResourceHandle*)
+{
+ didFail(cannotShowURLError());
}
void ResourceLoader::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge)
diff --git a/WebCore/loader/ResourceLoader.h b/WebCore/loader/ResourceLoader.h
index 3f1784f..722f5fc 100644
--- a/WebCore/loader/ResourceLoader.h
+++ b/WebCore/loader/ResourceLoader.h
@@ -32,7 +32,6 @@
#include "ResourceHandleClient.h"
#include "ResourceRequest.h"
#include "ResourceResponse.h"
-#include "ResourceLoader.h"
#include <wtf/RefCounted.h>
#include "AuthenticationChallenge.h"
#include "KURL.h"
@@ -61,6 +60,7 @@ namespace WebCore {
virtual void cancel(const ResourceError&);
ResourceError cancelledError();
ResourceError blockedError();
+ ResourceError cannotShowURLError();
virtual void setDefersLoading(bool);
@@ -75,24 +75,26 @@ namespace WebCore {
void clearResourceData();
virtual void willSendRequest(ResourceRequest&, const ResourceResponse& redirectResponse);
+ virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent);
virtual void didReceiveResponse(const ResourceResponse&);
virtual void didReceiveData(const char*, int, long long lengthReceived, bool allAtOnce);
void willStopBufferingData(const char*, int);
virtual void didFinishLoading();
virtual void didFail(const ResourceError&);
- virtual void wasBlocked();
virtual void didReceiveAuthenticationChallenge(const AuthenticationChallenge&);
void didCancelAuthenticationChallenge(const AuthenticationChallenge&);
virtual void receivedCancellation(const AuthenticationChallenge&);
// ResourceHandleClient
- virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& redirectResponse);
+ virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse& redirectResponse);
+ virtual void didSendData(ResourceHandle*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent);
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&);
virtual void wasBlocked(ResourceHandle*);
+ virtual void cannotShowURL(ResourceHandle*);
virtual void willStopBufferingData(ResourceHandle*, const char* data, int length) { willStopBufferingData(data, length); }
virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) { didReceiveAuthenticationChallenge(challenge); }
virtual void didCancelAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) { didCancelAuthenticationChallenge(challenge); }
@@ -114,15 +116,20 @@ namespace WebCore {
void didFinishLoadingOnePart();
const ResourceRequest& request() const { return m_request; }
- void setRequest(const ResourceRequest& request) { m_request = request; }
bool reachedTerminalState() const { return m_reachedTerminalState; }
bool cancelled() const { return m_cancelled; }
bool defersLoading() const { return m_defersLoading; }
RefPtr<ResourceHandle> m_handle;
-
+ RefPtr<Frame> m_frame;
+ RefPtr<DocumentLoader> m_documentLoader;
+ ResourceResponse m_response;
+
private:
ResourceRequest m_request;
+ RefPtr<SharedBuffer> m_resourceData;
+
+ unsigned long m_identifier;
bool m_reachedTerminalState;
bool m_cancelled;
@@ -131,16 +138,10 @@ namespace WebCore {
bool m_sendResourceLoadCallbacks;
bool m_shouldContentSniff;
bool m_shouldBufferData;
-protected:
- // FIXME: Once everything is made cross platform, these can be private instead of protected
- RefPtr<Frame> m_frame;
- RefPtr<DocumentLoader> m_documentLoader;
- ResourceResponse m_response;
- unsigned long m_identifier;
-
- KURL m_originalURL;
- RefPtr<SharedBuffer> m_resourceData;
bool m_defersLoading;
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ bool m_wasLoadedFromApplicationCache;
+#endif
ResourceRequest m_deferredRequest;
};
diff --git a/WebCore/loader/SubresourceLoader.cpp b/WebCore/loader/SubresourceLoader.cpp
index 44f8548..bff48b2 100644
--- a/WebCore/loader/SubresourceLoader.cpp
+++ b/WebCore/loader/SubresourceLoader.cpp
@@ -38,23 +38,12 @@
#include "ResourceRequest.h"
#include "SubresourceLoaderClient.h"
#include "SharedBuffer.h"
+#include <wtf/RefCountedLeakCounter.h>
namespace WebCore {
-#ifndef NDEBUG
-WTFLogChannel LogWebCoreSubresourceLoaderLeaks = { 0x00000000, "", WTFLogChannelOn };
-
-struct SubresourceLoaderCounter {
- static unsigned count;
-
- ~SubresourceLoaderCounter()
- {
- if (count)
- LOG(WebCoreSubresourceLoaderLeaks, "LEAK: %u SubresourceLoader\n", count);
- }
-};
-unsigned SubresourceLoaderCounter::count = 0;
-static SubresourceLoaderCounter subresourceLoaderCounter;
+#ifndef NDEBUG
+static WTF::RefCountedLeakCounter subresourceLoaderCounter("SubresourceLoader");
#endif
SubresourceLoader::SubresourceLoader(Frame* frame, SubresourceLoaderClient* client, bool sendResourceLoadCallbacks, bool shouldContentSniff)
@@ -63,7 +52,7 @@ SubresourceLoader::SubresourceLoader(Frame* frame, SubresourceLoaderClient* clie
, m_loadingMultipartContent(false)
{
#ifndef NDEBUG
- ++SubresourceLoaderCounter::count;
+ subresourceLoaderCounter.increment();
#endif
m_documentLoader->addSubresourceLoader(this);
}
@@ -71,7 +60,7 @@ SubresourceLoader::SubresourceLoader(Frame* frame, SubresourceLoaderClient* clie
SubresourceLoader::~SubresourceLoader()
{
#ifndef NDEBUG
- --SubresourceLoaderCounter::count;
+ subresourceLoaderCounter.decrement();
#endif
}
@@ -95,8 +84,8 @@ PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, Subresourc
if (!skipCanLoadCheck
&& FrameLoader::restrictAccessToLocal()
- && !FrameLoader::canLoad(request.url(), frame->document())) {
- FrameLoader::reportLocalLoadFailed(frame->page(), request.url().string());
+ && !FrameLoader::canLoad(request.url(), String(), frame->document())) {
+ FrameLoader::reportLocalLoadFailed(frame, request.url().string());
return 0;
}
@@ -104,6 +93,7 @@ PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, Subresourc
newRequest.clearHTTPReferrer();
else if (!request.httpReferrer())
newRequest.setHTTPReferrer(fl->outgoingReferrer());
+ FrameLoader::addHTTPOriginIfNeeded(newRequest, fl->outgoingOrigin());
// Use the original request's cache policy for two reasons:
// 1. For POST requests, we mutate the cache policy for the main resource,
@@ -118,7 +108,7 @@ PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, Subresourc
fl->addExtraFieldsToRequest(newRequest, false, false);
- RefPtr<SubresourceLoader> subloader(new SubresourceLoader(frame, client, sendResourceLoadCallbacks, shouldContentSniff));
+ RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, client, sendResourceLoadCallbacks, shouldContentSniff)));
if (!subloader->load(newRequest))
return 0;
@@ -127,11 +117,22 @@ PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, Subresourc
void SubresourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
{
+ // Store the previous URL because the call to ResourceLoader::willSendRequest will modify it.
+ KURL previousURL = request().url();
+
ResourceLoader::willSendRequest(newRequest, redirectResponse);
- if (!newRequest.isNull() && m_originalURL != newRequest.url() && m_client)
+ if (!previousURL.isNull() && !newRequest.isNull() && previousURL != newRequest.url() && m_client)
m_client->willSendRequest(this, newRequest, redirectResponse);
}
+void SubresourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
+{
+ RefPtr<SubresourceLoader> protect(this);
+
+ if (m_client)
+ m_client->didSendData(this, bytesSent, totalBytesToBeSent);
+}
+
void SubresourceLoader::didReceiveResponse(const ResourceResponse& r)
{
ASSERT(!r.isNull());
diff --git a/WebCore/loader/SubresourceLoader.h b/WebCore/loader/SubresourceLoader.h
index 1811c30..b5bd34a 100644
--- a/WebCore/loader/SubresourceLoader.h
+++ b/WebCore/loader/SubresourceLoader.h
@@ -33,12 +33,6 @@
#include "ResourceLoader.h"
#include <wtf/PassRefPtr.h>
-#ifndef __OBJC__
-class NSArray;
-class NSDictionary;
-class NSMutableURLRequest;
-#endif
-
namespace WebCore {
class FormData;
@@ -56,12 +50,15 @@ namespace WebCore {
virtual bool load(const ResourceRequest&);
virtual void willSendRequest(ResourceRequest&, const ResourceResponse& redirectResponse);
+ virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent);
virtual void didReceiveResponse(const ResourceResponse&);
virtual void didReceiveData(const char*, int, long long lengthReceived, bool allAtOnce);
virtual void didFinishLoading();
virtual void didFail(const ResourceError&);
virtual void didReceiveAuthenticationChallenge(const AuthenticationChallenge&);
virtual void receivedCancellation(const AuthenticationChallenge&);
+
+ void clearClient() { m_client = 0; }
private:
SubresourceLoader(Frame*, SubresourceLoaderClient*, bool sendResourceLoadCallbacks, bool shouldContentSniff);
diff --git a/WebCore/loader/SubresourceLoaderClient.h b/WebCore/loader/SubresourceLoaderClient.h
index 8abe169..d2b9a12 100644
--- a/WebCore/loader/SubresourceLoaderClient.h
+++ b/WebCore/loader/SubresourceLoaderClient.h
@@ -40,9 +40,10 @@ class SubresourceLoader;
class SubresourceLoaderClient {
public:
virtual ~SubresourceLoaderClient() { }
-
+
// request may be modified
virtual void willSendRequest(SubresourceLoader*, ResourceRequest&, const ResourceResponse& redirectResponse) { }
+ virtual void didSendData(SubresourceLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) { }
virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&) { }
virtual void didReceiveData(SubresourceLoader*, const char*, int) { }
@@ -54,6 +55,6 @@ public:
};
-}
+} // namespace WebCore
-#endif
+#endif // SubresourceLoaderClient_h
diff --git a/WebCore/loader/SubstituteData.h b/WebCore/loader/SubstituteData.h
index d37da56..0b87b62 100644
--- a/WebCore/loader/SubstituteData.h
+++ b/WebCore/loader/SubstituteData.h
@@ -1,4 +1,3 @@
-// -*- mode: c++; c-basic-offset: 4 -*-
/*
* Copyright (C) 2007 Apple Inc. All rights reserved.
*
diff --git a/WebCore/loader/SubstituteResource.h b/WebCore/loader/SubstituteResource.h
new file mode 100644
index 0000000..15cbc6f
--- /dev/null
+++ b/WebCore/loader/SubstituteResource.h
@@ -0,0 +1,64 @@
+/*
+ * 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 SubstituteResource_h
+#define SubstituteResource_h
+
+#include <wtf/RefCounted.h>
+
+#include "KURL.h"
+#include "ResourceResponse.h"
+#include "SharedBuffer.h"
+
+#include <wtf/RefPtr.h>
+
+namespace WebCore {
+
+class SubstituteResource : public RefCounted<SubstituteResource> {
+public:
+ virtual ~SubstituteResource() { }
+
+ const KURL& url() const { return m_url; }
+ const ResourceResponse& response() const { return m_response; }
+ SharedBuffer* data() const { return m_data.get(); }
+
+protected:
+ SubstituteResource(const KURL& url, const ResourceResponse& response, PassRefPtr<SharedBuffer> data)
+ : m_url(url)
+ , m_response(response)
+ , m_data(data)
+ {
+ ASSERT(m_data);
+ }
+
+private:
+ KURL m_url;
+ ResourceResponse m_response;
+ RefPtr<SharedBuffer> m_data;
+};
+
+}
+
+#endif // SubstituteResource_h
diff --git a/WebCore/loader/TextDocument.cpp b/WebCore/loader/TextDocument.cpp
index b5124b4..6b06ede 100644
--- a/WebCore/loader/TextDocument.cpp
+++ b/WebCore/loader/TextDocument.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 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
@@ -38,6 +38,38 @@ namespace WebCore {
using namespace HTMLNames;
+class TextTokenizer : public Tokenizer {
+public:
+ TextTokenizer(Document*);
+ TextTokenizer(HTMLViewSourceDocument*);
+
+ virtual bool write(const SegmentedString&, bool appendData);
+ virtual void finish();
+ virtual bool isWaitingForScripts() const;
+
+ inline void checkBuffer(int len = 10)
+ {
+ if ((m_dest - m_buffer) > m_size - len) {
+ // Enlarge buffer
+ int newSize = std::max(m_size * 2, m_size + len);
+ int oldOffset = m_dest - m_buffer;
+ m_buffer = static_cast<UChar*>(fastRealloc(m_buffer, newSize * sizeof(UChar)));
+ m_dest = m_buffer + oldOffset;
+ m_size = newSize;
+ }
+ }
+
+private:
+ Document* m_doc;
+ Element* m_preElement;
+
+ bool m_skipLF;
+
+ int m_size;
+ UChar* m_buffer;
+ UChar* m_dest;
+};
+
TextTokenizer::TextTokenizer(Document* doc)
: m_doc(doc)
, m_preElement(0)
@@ -137,8 +169,8 @@ bool TextTokenizer::isWaitingForScripts() const
return false;
}
-TextDocument::TextDocument(DOMImplementation* implementation, Frame* frame)
- : HTMLDocument(implementation, frame)
+TextDocument::TextDocument(Frame* frame)
+ : HTMLDocument(frame)
{
}
@@ -147,4 +179,9 @@ Tokenizer* TextDocument::createTokenizer()
return new TextTokenizer(this);
}
+Tokenizer* createTextTokenizer(HTMLViewSourceDocument* document)
+{
+ return new TextTokenizer(document);
+}
+
}
diff --git a/WebCore/loader/TextDocument.h b/WebCore/loader/TextDocument.h
index 0c7e60b..c67fea5 100644
--- a/WebCore/loader/TextDocument.h
+++ b/WebCore/loader/TextDocument.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006, 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
@@ -21,58 +21,31 @@
* (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 TextDocument_h
#define TextDocument_h
#include "HTMLDocument.h"
-#include "Tokenizer.h"
namespace WebCore {
-class DOMImplementation;
-class FrameView;
class HTMLViewSourceDocument;
-class TextTokenizer : public Tokenizer {
+class TextDocument : public HTMLDocument {
public:
- TextTokenizer(Document* doc);
- TextTokenizer(HTMLViewSourceDocument* doc);
-
- virtual bool write(const SegmentedString&, bool appendData);
- virtual void finish();
- virtual bool isWaitingForScripts() const;
-
- inline void checkBuffer(int len = 10)
+ static PassRefPtr<TextDocument> create(Frame* frame)
{
- if ((m_dest - m_buffer) > m_size - len) {
- // Enlarge buffer
- int newSize = std::max(m_size * 2, m_size + len);
- int oldOffset = m_dest - m_buffer;
- m_buffer = static_cast<UChar*>(fastRealloc(m_buffer, newSize * sizeof(UChar)));
- m_dest = m_buffer + oldOffset;
- m_size = newSize;
- }
+ return new TextDocument(frame);
}
-
-private:
- Document* m_doc;
- Element* m_preElement;
-
- bool m_skipLF;
-
- int m_size;
- UChar* m_buffer;
- UChar* m_dest;
-};
-class TextDocument : public HTMLDocument
-{
-public:
- TextDocument(DOMImplementation*, Frame*);
+private:
+ TextDocument(Frame*);
virtual Tokenizer* createTokenizer();
};
+Tokenizer* createTextTokenizer(HTMLViewSourceDocument*);
+
}
#endif // TextDocument_h
diff --git a/WebCore/loader/TextResourceDecoder.cpp b/WebCore/loader/TextResourceDecoder.cpp
index ef067f8..4a0caa0 100644
--- a/WebCore/loader/TextResourceDecoder.cpp
+++ b/WebCore/loader/TextResourceDecoder.cpp
@@ -23,12 +23,11 @@
#include "config.h"
#include "TextResourceDecoder.h"
-#include "CString.h"
#include "DOMImplementation.h"
-#include "DeprecatedCString.h"
#include "HTMLNames.h"
#include "TextCodec.h"
#include <wtf/ASCIICType.h>
+#include <wtf/StringExtras.h>
using namespace WTF;
@@ -36,6 +35,60 @@ namespace WebCore {
using namespace HTMLNames;
+// You might think we should put these find functions elsewhere, perhaps with the
+// similar functions that operate on UChar, but arguably only the decoder has
+// a reason to process strings of char rather than UChar.
+
+static int find(const char* subject, size_t subjectLength, const char* target)
+{
+ size_t targetLength = strlen(target);
+ if (targetLength > subjectLength)
+ return -1;
+ for (size_t i = 0; i <= subjectLength - targetLength; ++i) {
+ bool match = true;
+ for (size_t j = 0; j < targetLength; ++j) {
+ if (subject[i + j] != target[j]) {
+ match = false;
+ break;
+ }
+ }
+ if (match)
+ return i;
+ }
+ return -1;
+}
+
+static int findIgnoringCase(const char* subject, size_t subjectLength, const char* target)
+{
+ size_t targetLength = strlen(target);
+ if (targetLength > subjectLength)
+ return -1;
+#ifndef NDEBUG
+ for (size_t i = 0; i < targetLength; ++i)
+ ASSERT(isASCIILower(target[i]));
+#endif
+ for (size_t i = 0; i <= subjectLength - targetLength; ++i) {
+ bool match = true;
+ for (size_t j = 0; j < targetLength; ++j) {
+ if (toASCIILower(subject[i + j]) != target[j]) {
+ match = false;
+ break;
+ }
+ }
+ if (match)
+ return i;
+ }
+ return -1;
+}
+
+static TextEncoding findTextEncoding(const char* encodingName, int length)
+{
+ Vector<char, 64> buffer(length + 1);
+ memcpy(buffer.data(), encodingName, length);
+ buffer[length] = '\0';
+ return buffer.data();
+}
+
class KanjiCode {
public:
enum Type { ASCII, JIS, EUC, SJIS, UTF16, UTF8 };
@@ -274,6 +327,7 @@ TextResourceDecoder::TextResourceDecoder(const String& mimeType, const TextEncod
, m_checkedForBOM(false)
, m_checkedForCSSCharset(false)
, m_checkedForHeadCharset(false)
+ , m_sawError(false)
{
}
@@ -287,7 +341,11 @@ void TextResourceDecoder::setEncoding(const TextEncoding& encoding, EncodingSour
if (!encoding.isValid())
return;
- if (source == EncodingFromMetaTag || source == EncodingFromXMLHeader || source == EncodingFromCSSCharset)
+ // When encoding comes from meta tag (i.e. it cannot be XML files sent via XHR),
+ // treat x-user-defined as windows-1252 (bug 18270)
+ if (source == EncodingFromMetaTag && strcasecmp(encoding.name(), "x-user-defined") == 0)
+ m_decoder.reset("windows-1252");
+ else if (source == EncodingFromMetaTag || source == EncodingFromXMLHeader || source == EncodingFromCSSCharset)
m_decoder.reset(encoding.closest8BitEquivalent());
else
m_decoder.reset(encoding);
@@ -296,29 +354,29 @@ void TextResourceDecoder::setEncoding(const TextEncoding& encoding, EncodingSour
}
// Returns the position of the encoding string.
-static int findXMLEncoding(const DeprecatedCString &str, int &encodingLength)
+static int findXMLEncoding(const char* str, int len, int& encodingLength)
{
- int len = str.length();
-
- int pos = str.find("encoding");
+ int pos = find(str, len, "encoding");
if (pos == -1)
return -1;
pos += 8;
// Skip spaces and stray control characters.
- while (str[pos] <= ' ' && pos != len)
+ while (pos < len && str[pos] <= ' ')
++pos;
// Skip equals sign.
- if (str[pos] != '=')
+ if (pos >= len || str[pos] != '=')
return -1;
++pos;
// Skip spaces and stray control characters.
- while (str[pos] <= ' ' && pos != len)
+ while (pos < len && str[pos] <= ' ')
++pos;
// Skip quotation mark.
+ if (pos >= len)
+ return - 1;
char quoteMark = str[pos];
if (quoteMark != '"' && quoteMark != '\'')
return -1;
@@ -326,12 +384,11 @@ static int findXMLEncoding(const DeprecatedCString &str, int &encodingLength)
// Find the trailing quotation mark.
int end = pos;
- while (str[end] != quoteMark)
+ while (end < len && str[end] != quoteMark)
++end;
-
- if (end == len)
+ if (end >= len)
return -1;
-
+
encodingLength = end - pos;
return pos;
}
@@ -421,14 +478,14 @@ bool TextResourceDecoder::checkForCSSCharset(const char* data, size_t len, bool&
if (pos == dataEnd)
return false;
- CString encodingName(dataStart, pos - dataStart + 1);
+ int encodingNameLength = pos - dataStart + 1;
++pos;
if (!skipWhitespace(pos, dataEnd))
return false;
if (*pos == ';')
- setEncoding(TextEncoding(encodingName.data()), EncodingFromCSSCharset);
+ setEncoding(findTextEncoding(dataStart, encodingNameLength), EncodingFromCSSCharset);
}
}
m_checkedForCSSCharset = true;
@@ -465,6 +522,8 @@ static inline void skipComment(const char*& ptr, const char* pEnd)
ptr = p;
}
+const int bytesToCheckUnconditionally = 1024; // That many input bytes will be checked for meta charset even if <head> section is over.
+
bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool& movedDataToBuffer)
{
if (m_source != DefaultEncoding) {
@@ -496,11 +555,11 @@ bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool
++xmlDeclarationEnd;
if (xmlDeclarationEnd == pEnd)
return false;
- DeprecatedCString str(ptr, xmlDeclarationEnd - ptr); // No need for +1, because we have an extra "?" to lose at the end of XML declaration.
- int len = 0;
- int pos = findXMLEncoding(str, len);
+ // No need for +1, because we have an extra "?" to lose at the end of XML declaration.
+ int len;
+ int pos = findXMLEncoding(ptr, xmlDeclarationEnd - ptr, len);
if (pos != -1)
- setEncoding(TextEncoding(str.mid(pos, len)), EncodingFromXMLHeader);
+ setEncoding(findTextEncoding(ptr + pos, len), EncodingFromXMLHeader);
// continue looking for a charset - it may be specified in an HTTP-Equiv meta
} else if (ptr[0] == '<' && ptr[1] == 0 && ptr[2] == '?' && ptr[3] == 0 && ptr[4] == 'x' && ptr[5] == 0) {
setEncoding(UTF16LittleEndianEncoding(), AutoDetectedEncoding);
@@ -530,9 +589,14 @@ bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool
// and <http://bugs.webkit.org/show_bug.cgi?id=12389>.
// Since many sites have charset declarations after <body> or other tags that are disallowed in <head>,
- // we don't bail out until we've checked at least 512 bytes of input.
+ // we don't bail out until we've checked at least bytesToCheckUnconditionally bytes of input.
AtomicStringImpl* enclosingTagName = 0;
+ bool inHeadSection = true; // Becomes false when </head> or any tag not allowed in head is encountered.
+
+ // the HTTP-EQUIV meta has no effect on XHTML
+ if (m_contentType == XML)
+ return true;
while (ptr + 3 < pEnd) { // +3 guarantees that "<!--" fits in the buffer - and certainly we aren't going to lose any "charset" that way.
if (*ptr == '<') {
@@ -543,13 +607,16 @@ bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool
if (ptr[0] == '!' && ptr[1] == '-' && ptr[2] == '-') {
ptr += 3;
skipComment(ptr, pEnd);
+ if (ptr - m_buffer.data() >= bytesToCheckUnconditionally && !inHeadSection) {
+ // Some pages that test bandwidth from within the browser do it by having
+ // huge comments and measuring the time they take to load. Repeatedly scanning
+ // these comments can take a lot of CPU time.
+ m_checkedForHeadCharset = true;
+ return true;
+ }
continue;
}
- // the HTTP-EQUIV meta has no effect on XHTML
- if (m_contentType == XML)
- return true;
-
if (*ptr == '/') {
++ptr;
end = true;
@@ -613,52 +680,56 @@ bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool
}
if (!end && tag == metaTag && !sawNamespace) {
- DeprecatedCString str(tagContentStart, ptr - tagContentStart);
- str = str.lower();
+ const char* str = tagContentStart;
+ int length = ptr - tagContentStart;
int pos = 0;
- while (pos < (int)str.length()) {
- if ((pos = str.find("charset", pos, false)) == -1)
+ while (pos < length) {
+ int charsetPos = findIgnoringCase(str + pos, length - pos, "charset");
+ if (charsetPos == -1)
break;
- pos += 7;
+ pos += charsetPos + 7;
// skip whitespace
- while (pos < (int)str.length() && str[pos] <= ' ')
+ while (pos < length && str[pos] <= ' ')
pos++;
- if (pos == (int)str.length())
+ if (pos == length)
break;
if (str[pos++] != '=')
continue;
- while (pos < (int)str.length() &&
+ while (pos < length &&
(str[pos] <= ' ') || str[pos] == '=' || str[pos] == '"' || str[pos] == '\'')
pos++;
// end ?
- if (pos == (int)str.length())
+ if (pos == length)
break;
- unsigned endpos = pos;
- while (endpos < str.length() &&
- str[endpos] != ' ' && str[endpos] != '"' && str[endpos] != '\'' &&
- str[endpos] != ';' && str[endpos] != '>')
- endpos++;
- setEncoding(TextEncoding(str.mid(pos, endpos - pos)), EncodingFromMetaTag);
+ int end = pos;
+ while (end < length &&
+ str[end] != ' ' && str[end] != '"' && str[end] != '\'' &&
+ str[end] != ';' && str[end] != '>')
+ end++;
+ setEncoding(findTextEncoding(str + pos, end - pos), EncodingFromMetaTag);
if (m_source == EncodingFromMetaTag)
return true;
- if (endpos >= str.length() || str[endpos] == '/' || str[endpos] == '>')
+ if (end >= length || str[end] == '/' || str[end] == '>')
break;
- pos = endpos + 1;
+ pos = end + 1;
+ }
+ } else {
+ if (!enclosingTagName && tag != scriptTag && tag != noscriptTag && tag != styleTag
+ && tag != linkTag && tag != metaTag && tag != objectTag && tag != titleTag && tag != baseTag
+ && (end || tag != htmlTag) && (end || tag != headTag) && isASCIIAlpha(tagBuffer[0])) {
+ inHeadSection = false;
+ }
+
+ if (ptr - m_buffer.data() >= bytesToCheckUnconditionally && !inHeadSection) {
+ m_checkedForHeadCharset = true;
+ return true;
}
- } else if (ptr - m_buffer.data() >= 512 && tag != scriptTag && tag != noscriptTag && tag != styleTag &&
- tag != linkTag && tag != metaTag && tag != objectTag &&
- tag != titleTag && tag != baseTag &&
- (end || tag != htmlTag) && !enclosingTagName &&
- (tag != headTag) && isASCIIAlpha(tagBuffer[0])) {
- m_checkedForHeadCharset = true;
- return true;
}
- }
- else
- ptr++;
+ } else
+ ++ptr;
}
return false;
}
@@ -705,7 +776,7 @@ String TextResourceDecoder::decode(const char* data, size_t len)
ASSERT(encoding().isValid());
if (m_buffer.isEmpty())
- return m_decoder.decode(data, len);
+ return m_decoder.decode(data, len, false, m_contentType == XML, m_sawError);
if (!movedDataToBuffer) {
size_t oldSize = m_buffer.size();
@@ -713,14 +784,14 @@ String TextResourceDecoder::decode(const char* data, size_t len)
memcpy(m_buffer.data() + oldSize, data, len);
}
- String result = m_decoder.decode(m_buffer.data(), m_buffer.size());
+ String result = m_decoder.decode(m_buffer.data(), m_buffer.size(), false, m_contentType == XML, m_sawError);
m_buffer.clear();
return result;
}
String TextResourceDecoder::flush()
{
- String result = m_decoder.decode(m_buffer.data(), m_buffer.size(), true);
+ String result = m_decoder.decode(m_buffer.data(), m_buffer.size(), true, m_contentType == XML, m_sawError);
m_buffer.clear();
return result;
}
diff --git a/WebCore/loader/TextResourceDecoder.h b/WebCore/loader/TextResourceDecoder.h
index 6917ab5..8bbe85e 100644
--- a/WebCore/loader/TextResourceDecoder.h
+++ b/WebCore/loader/TextResourceDecoder.h
@@ -1,9 +1,7 @@
/*
- This file is part of the KDE libraries
-
Copyright (C) 1999 Lars Knoll (knoll@mpi-hd.mpg.de)
Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
- Copyright (C) 2006 Apple Computer, Inc.
+ Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -25,10 +23,7 @@
#ifndef TextResourceDecoder_h
#define TextResourceDecoder_h
-#include "PlatformString.h"
-#include <wtf/RefCounted.h>
#include "TextDecoder.h"
-#include <wtf/Vector.h>
namespace WebCore {
@@ -44,7 +39,10 @@ public:
UserChosenEncoding
};
- TextResourceDecoder(const String& mimeType, const TextEncoding& defaultEncoding = TextEncoding());
+ static PassRefPtr<TextResourceDecoder> create(const String& mimeType, const TextEncoding& defaultEncoding = TextEncoding())
+ {
+ return adoptRef(new TextResourceDecoder(mimeType, defaultEncoding));
+ }
~TextResourceDecoder();
void setEncoding(const TextEncoding&, EncodingSource);
@@ -52,8 +50,12 @@ public:
String decode(const char* data, size_t length);
String flush();
+
+ bool sawError() const { return m_sawError; }
private:
+ TextResourceDecoder(const String& mimeType, const TextEncoding& defaultEncoding);
+
enum ContentType { PlainText, HTML, XML, CSS }; // PlainText is equivalent to directly using TextDecoder.
static ContentType determineContentType(const String& mimeType);
static const TextEncoding& defaultEncoding(ContentType, const TextEncoding& defaultEncoding);
@@ -70,6 +72,7 @@ private:
bool m_checkedForBOM;
bool m_checkedForCSSCharset;
bool m_checkedForHeadCharset;
+ bool m_sawError;
};
}
diff --git a/WebCore/loader/mac/UserStyleSheetLoader.cpp b/WebCore/loader/UserStyleSheetLoader.cpp
index 349a994..3afbc15 100644
--- a/WebCore/loader/mac/UserStyleSheetLoader.cpp
+++ b/WebCore/loader/UserStyleSheetLoader.cpp
@@ -41,7 +41,7 @@ UserStyleSheetLoader::UserStyleSheetLoader(PassRefPtr<Document> document, const
{
if (m_cachedSheet) {
m_document->addPendingSheet();
- m_cachedSheet->ref(this);
+ m_cachedSheet->addClient(this);
}
}
@@ -50,13 +50,13 @@ UserStyleSheetLoader::~UserStyleSheetLoader()
if (m_cachedSheet) {
if (!m_cachedSheet->isLoaded())
m_document->removePendingSheet();
- m_cachedSheet->deref(this);
+ m_cachedSheet->removeClient(this);
}
}
-void UserStyleSheetLoader::setCSSStyleSheet(const String& /*URL*/, const String& /*charset*/, const String& sheet)
+void UserStyleSheetLoader::setCSSStyleSheet(const String& /*URL*/, const String& /*charset*/, const CachedCSSStyleSheet* sheet)
{
m_document->removePendingSheet();
if (Frame* frame = m_document->frame())
- frame->setUserStyleSheet(sheet);
+ frame->setUserStyleSheet(sheet->sheetText());
}
diff --git a/WebCore/loader/mac/UserStyleSheetLoader.h b/WebCore/loader/UserStyleSheetLoader.h
index 9159273..6e7a1ba 100644
--- a/WebCore/loader/mac/UserStyleSheetLoader.h
+++ b/WebCore/loader/UserStyleSheetLoader.h
@@ -30,6 +30,7 @@
#define UserStyleSheetLoader_h
#include "CachedResourceClient.h"
+#include "CachedResourceHandle.h"
#include "Document.h"
@@ -45,10 +46,10 @@ namespace WebCore {
~UserStyleSheetLoader();
private:
- virtual void setCSSStyleSheet(const String& URL, const String& charset, const String& sheet);
+ virtual void setCSSStyleSheet(const String& URL, const String& charset, const CachedCSSStyleSheet* sheet);
RefPtr<Document> m_document;
- CachedCSSStyleSheet* m_cachedSheet;
+ CachedResourceHandle<CachedCSSStyleSheet> m_cachedSheet;
};
} // namespace WebCore
diff --git a/WebCore/loader/appcache/ApplicationCache.cpp b/WebCore/loader/appcache/ApplicationCache.cpp
new file mode 100644
index 0000000..d29eda8
--- /dev/null
+++ b/WebCore/loader/appcache/ApplicationCache.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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 index) 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& url)
+{
+ // FIXME: Implement
+}
+
+void ApplicationCache::setOnlineWhitelist(const HashSet<String>& onlineWhitelist)
+{
+ ASSERT(m_onlineWhitelist.isEmpty());
+ m_onlineWhitelist = onlineWhitelist;
+}
+
+bool ApplicationCache::isURLInOnlineWhitelist(const KURL& url)
+{
+ if (!url.hasRef())
+ return m_onlineWhitelist.contains(url);
+
+ KURL copy(url);
+ copy.setRef(String());
+ return m_onlineWhitelist.contains(copy);
+}
+
+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/WebCore/loader/appcache/ApplicationCache.h b/WebCore/loader/appcache/ApplicationCache.h
new file mode 100644
index 0000000..e536cbe
--- /dev/null
+++ b/WebCore/loader/appcache/ApplicationCache.h
@@ -0,0 +1,103 @@
+/*
+ * 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 <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+
+#include "StringHash.h"
+#include "PlatformString.h"
+
+namespace WebCore {
+
+class ApplicationCacheGroup;
+class ApplicationCacheResource;
+class DocumentLoader;
+class KURL;
+class ResourceRequest;
+
+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 HashSet<String>& onlineWhitelist);
+ const HashSet<String>& onlineWhitelist() const { return m_onlineWhitelist; }
+ bool isURLInOnlineWhitelist(const KURL&);
+
+#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;
+
+ HashSet<String> m_onlineWhitelist;
+
+ unsigned m_storageID;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#endif // ApplicationCache_h
diff --git a/WebCore/loader/appcache/ApplicationCacheGroup.cpp b/WebCore/loader/appcache/ApplicationCacheGroup.cpp
new file mode 100644
index 0000000..ff26767
--- /dev/null
+++ b/WebCore/loader/appcache/ApplicationCacheGroup.cpp
@@ -0,0 +1,715 @@
+/*
+ * 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* loader)
+{
+ if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
+ return 0;
+
+ ASSERT(loader->frame());
+ ASSERT(loader->frame()->page());
+ if (loader->frame() != loader->frame()->page()->mainFrame())
+ return 0;
+
+ if (ApplicationCacheGroup* group = cacheStorage().cacheGroupForURL(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();
+
+ // Check if the main resource is being loaded as part of navigation of the main frame
+ bool isMainFrame = frame->page()->mainFrame() == frame;
+
+ if (!isMainFrame) {
+ if (mainResourceCache && manifestURL != mainResourceCache->group()->manifestURL()) {
+ ApplicationCacheResource* resource = mainResourceCache->resourceForURL(documentLoader->originalURL());
+ ASSERT(resource);
+
+ resource->addType(ApplicationCacheResource::Foreign);
+ }
+
+ return;
+ }
+
+ if (mainResourceCache) {
+ if (manifestURL == mainResourceCache->group()->m_manifestURL) {
+ mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache);
+ mainResourceCache->group()->update(frame);
+ } else {
+ // FIXME: If the resource being loaded was loaded from an application cache and the URI of
+ // that application cache's manifest is not the same as the manifest URI with which the algorithm was invoked
+ // then we should "undo" the navigation.
+ }
+
+ 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();
+ bool isMainFrame = frame->page()->mainFrame() == frame;
+
+ if (isMainFrame && 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);
+ ASSERT(!m_cacheBeingUpdated);
+
+ 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 lengthReceived)
+{
+ 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;
+ }
+
+ // FIXME: Add the opportunistic caching namespaces and their fallbacks.
+
+ // 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::Opportunistic | 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);
+
+ m_cacheBeingUpdated->setOnlineWhitelist(manifest.onlineWhitelistedURLs);
+
+ 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());
+
+ m_status = Idle;
+ m_frame = 0;
+
+ 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);
+}
+
+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/WebCore/loader/appcache/ApplicationCacheGroup.h b/WebCore/loader/appcache/ApplicationCacheGroup.h
new file mode 100644
index 0000000..d5b7563
--- /dev/null
+++ b/WebCore/loader/appcache/ApplicationCacheGroup.h
@@ -0,0 +1,152 @@
+/*
+ * 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 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).
+ 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/WebCore/loader/appcache/ApplicationCacheResource.cpp b/WebCore/loader/appcache/ApplicationCacheResource.cpp
new file mode 100644
index 0000000..ee82ff5
--- /dev/null
+++ b/WebCore/loader/appcache/ApplicationCacheResource.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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)
+{
+ ASSERT(!m_storageID);
+ 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 & Opportunistic)
+ printf("opportunistic ");
+ if (type & Dynamic)
+ printf("dynamic ");
+
+ printf("\n");
+}
+#endif
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/WebCore/loader/appcache/ApplicationCacheResource.h b/WebCore/loader/appcache/ApplicationCacheResource.h
new file mode 100644
index 0000000..1d7b853
--- /dev/null
+++ b/WebCore/loader/appcache/ApplicationCacheResource.h
@@ -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.
+ */
+
+#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,
+ Opportunistic = 1 << 5,
+ Dynamic = 1 << 6
+ };
+
+ 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/WebCore/loader/appcache/ApplicationCacheStorage.cpp b/WebCore/loader/appcache/ApplicationCacheStorage.cpp
new file mode 100644
index 0000000..910d00c
--- /dev/null
+++ b/WebCore/loader/appcache/ApplicationCacheStorage.cpp
@@ -0,0 +1,663 @@
+/*
+ * Copyright (C) 2008 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ApplicationCacheStorage.h"
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include "ApplicationCache.h"
+#include "ApplicationCacheGroup.h"
+#include "ApplicationCacheResource.h"
+#include "FileSystem.h"
+#include "CString.h"
+#include "KURL.h"
+#include "SQLiteStatement.h"
+#include "SQLiteTransaction.h"
+
+namespace WebCore {
+
+static unsigned urlHostHash(const KURL& url)
+{
+ unsigned hostStart = url.hostStart();
+ unsigned hostEnd = url.hostEnd();
+
+ return AlreadyHashed::avoidDeletedValue(StringImpl::computeHash(url.string().characters() + hostStart, hostEnd - hostStart));
+}
+
+ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const KURL& manifestURL)
+{
+ openDatabase(false);
+ if (!m_database.isOpen())
+ return 0;
+
+ SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL AND manifestURL=?");
+ if (statement.prepare() != SQLResultOk)
+ return 0;
+
+ statement.bindText(1, manifestURL);
+
+ int result = statement.step();
+ if (result == SQLResultDone)
+ return 0;
+
+ if (result != SQLResultRow) {
+ LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg());
+ return 0;
+ }
+
+ unsigned newestCacheStorageID = (unsigned)statement.getColumnInt64(2);
+
+ RefPtr<ApplicationCache> cache = loadCache(newestCacheStorageID);
+ if (!cache)
+ return 0;
+
+ ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL);
+
+ group->setStorageID((unsigned)statement.getColumnInt64(0));
+ group->setNewestCache(cache.release());
+
+ return group;
+}
+
+ApplicationCacheGroup* ApplicationCacheStorage::findOrCreateCacheGroup(const KURL& manifestURL)
+{
+ std::pair<CacheGroupMap::iterator, bool> result = m_cachesInMemory.add(manifestURL, 0);
+
+ if (!result.second) {
+ ASSERT(result.first->second);
+
+ return result.first->second;
+ }
+
+ // Look up the group in the database
+ ApplicationCacheGroup* group = loadCacheGroup(manifestURL);
+
+ // If the group was not found we need to create it
+ if (!group) {
+ group = new ApplicationCacheGroup(manifestURL);
+ m_cacheHostSet.add(urlHostHash(manifestURL));
+ }
+
+ result.first->second = group;
+
+ return group;
+}
+
+void ApplicationCacheStorage::loadManifestHostHashes()
+{
+ static bool hasLoadedHashes = false;
+
+ if (hasLoadedHashes)
+ return;
+
+ // We set this flag to true before the database has been opened
+ // to avoid trying to open the database over and over if it doesn't exist.
+ hasLoadedHashes = true;
+
+ openDatabase(false);
+ if (!m_database.isOpen())
+ return;
+
+ // Fetch the host hashes.
+ SQLiteStatement statement(m_database, "SELECT manifestHostHash FROM CacheGroups");
+ if (statement.prepare() != SQLResultOk)
+ return;
+
+ int result;
+ while ((result = statement.step()) == SQLResultRow)
+ m_cacheHostSet.add((unsigned)statement.getColumnInt64(0));
+}
+
+ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url)
+{
+ loadManifestHostHashes();
+
+ // Hash the host name and see if there's a manifest with the same host.
+ if (!m_cacheHostSet.contains(urlHostHash(url)))
+ return 0;
+
+ // Check if a cache already exists in memory.
+ CacheGroupMap::const_iterator end = m_cachesInMemory.end();
+ for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) {
+ ApplicationCacheGroup* group = it->second;
+
+ if (!protocolHostAndPortAreEqual(url, group->manifestURL()))
+ continue;
+
+ if (ApplicationCache* cache = group->newestCache()) {
+ if (cache->resourceForURL(url))
+ return group;
+ }
+ }
+
+ if (!m_database.isOpen())
+ return 0;
+
+ // Check the database. Look for all cache groups with a newest cache.
+ SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL");
+ if (statement.prepare() != SQLResultOk)
+ return 0;
+
+ int result;
+ while ((result = statement.step()) == SQLResultRow) {
+ KURL manifestURL = KURL(statement.getColumnText(1));
+
+ if (!protocolHostAndPortAreEqual(url, manifestURL))
+ continue;
+
+ // We found a cache group that matches. Now check if the newest cache has a resource with
+ // a matching URL.
+ unsigned newestCacheID = (unsigned)statement.getColumnInt64(2);
+ RefPtr<ApplicationCache> cache = loadCache(newestCacheID);
+
+ if (!cache->resourceForURL(url))
+ continue;
+
+ ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL);
+
+ group->setStorageID((unsigned)statement.getColumnInt64(0));
+ group->setNewestCache(cache.release());
+
+ ASSERT(!m_cachesInMemory.contains(manifestURL));
+ m_cachesInMemory.set(group->manifestURL(), group);
+
+ return group;
+ }
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg());
+
+ return 0;
+}
+
+void ApplicationCacheStorage::cacheGroupDestroyed(ApplicationCacheGroup* group)
+{
+ ASSERT(m_cachesInMemory.get(group->manifestURL()) == group);
+
+ m_cachesInMemory.remove(group->manifestURL());
+
+ // If the cache is half-created, we don't want it in the saved set.
+ if (!group->savedNewestCachePointer())
+ m_cacheHostSet.remove(urlHostHash(group->manifestURL()));
+}
+
+void ApplicationCacheStorage::setCacheDirectory(const String& cacheDirectory)
+{
+ ASSERT(m_cacheDirectory.isNull());
+ ASSERT(!cacheDirectory.isNull());
+
+ m_cacheDirectory = cacheDirectory;
+}
+
+bool ApplicationCacheStorage::executeSQLCommand(const String& sql)
+{
+ ASSERT(m_database.isOpen());
+
+ bool result = m_database.executeCommand(sql);
+ if (!result)
+ LOG_ERROR("Application Cache Storage: failed to execute statement \"%s\" error \"%s\"",
+ sql.utf8().data(), m_database.lastErrorMsg());
+
+ return result;
+}
+
+static const int SchemaVersion = 2;
+
+void ApplicationCacheStorage::verifySchemaVersion()
+{
+ if (m_database.tableExists("SchemaVersion")) {
+ int version = SQLiteStatement(m_database, "SELECT version from SchemaVersion").getColumnInt(0);
+
+ if (version == SchemaVersion)
+ return;
+ }
+
+ m_database.clearAllTables();
+
+ SQLiteTransaction createSchemaVersionTable(m_database);
+ createSchemaVersionTable.begin();
+
+ executeSQLCommand("CREATE TABLE SchemaVersion (version INTEGER NOT NULL)");
+ SQLiteStatement statement(m_database, "INSERT INTO SchemaVersion (version) VALUES (?)");
+ if (statement.prepare() != SQLResultOk)
+ return;
+
+ statement.bindInt64(1, SchemaVersion);
+ executeStatement(statement);
+ createSchemaVersionTable.commit();
+}
+
+void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist)
+{
+ if (m_database.isOpen())
+ return;
+
+ // The cache directory should never be null, but if it for some weird reason is we bail out.
+ if (m_cacheDirectory.isNull())
+ return;
+
+ String applicationCachePath = pathByAppendingComponent(m_cacheDirectory, "ApplicationCache.db");
+ if (!createIfDoesNotExist && !fileExists(applicationCachePath))
+ return;
+
+ makeAllDirectories(m_cacheDirectory);
+ m_database.open(applicationCachePath);
+
+ if (!m_database.isOpen())
+ return;
+
+ verifySchemaVersion();
+
+ // Create tables
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheGroups (id INTEGER PRIMARY KEY AUTOINCREMENT, "
+ "manifestHostHash INTEGER NOT NULL ON CONFLICT FAIL, manifestURL TEXT UNIQUE ON CONFLICT FAIL, newestCache INTEGER)");
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS Caches (id INTEGER PRIMARY KEY AUTOINCREMENT, cacheGroup INTEGER)");
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheWhitelistURLs (url TEXT NOT NULL ON CONFLICT FAIL, cache INTEGER NOT NULL ON CONFLICT FAIL)");
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS FallbackURLs (namespace TEXT NOT NULL ON CONFLICT FAIL, fallbackURL TEXT NOT NULL ON CONFLICT FAIL, "
+ "cache INTEGER NOT NULL ON CONFLICT FAIL)");
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheEntries (cache INTEGER NOT NULL ON CONFLICT FAIL, type INTEGER, resource INTEGER NOT NULL)");
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResources (id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT NOT NULL ON CONFLICT FAIL, "
+ "statusCode INTEGER NOT NULL, responseURL TEXT NOT NULL, mimeType TEXT, textEncodingName TEXT, headers TEXT, data INTEGER NOT NULL ON CONFLICT FAIL)");
+ executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResourceData (id INTEGER PRIMARY KEY AUTOINCREMENT, data BLOB)");
+
+ // When a cache is deleted, all its entries and its whitelist should be deleted.
+ executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheDeleted AFTER DELETE ON Caches"
+ " FOR EACH ROW BEGIN"
+ " DELETE FROM CacheEntries WHERE cache = OLD.id;"
+ " DELETE FROM CacheWhitelistURLs WHERE cache = OLD.id;"
+ " DELETE FROM FallbackURLs WHERE cache = OLD.id;"
+ " END");
+
+ // When a cache resource is deleted, its data blob should also be deleted.
+ executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheResourceDeleted AFTER DELETE ON CacheResources"
+ " FOR EACH ROW BEGIN"
+ " DELETE FROM CacheResourceData WHERE id = OLD.data;"
+ " END");
+}
+
+bool ApplicationCacheStorage::executeStatement(SQLiteStatement& statement)
+{
+ bool result = statement.executeCommand();
+ if (!result)
+ LOG_ERROR("Application Cache Storage: failed to execute statement \"%s\" error \"%s\"",
+ statement.query().utf8().data(), m_database.lastErrorMsg());
+
+ return result;
+}
+
+bool ApplicationCacheStorage::store(ApplicationCacheGroup* group)
+{
+ ASSERT(group->storageID() == 0);
+
+ SQLiteStatement statement(m_database, "INSERT INTO CacheGroups (manifestHostHash, manifestURL) VALUES (?, ?)");
+ if (statement.prepare() != SQLResultOk)
+ return false;
+
+ statement.bindInt64(1, urlHostHash(group->manifestURL()));
+ statement.bindText(2, group->manifestURL());
+
+ if (!executeStatement(statement))
+ return false;
+
+ group->setStorageID((unsigned)m_database.lastInsertRowID());
+ return true;
+}
+
+bool ApplicationCacheStorage::store(ApplicationCache* cache)
+{
+ ASSERT(cache->storageID() == 0);
+ ASSERT(cache->group()->storageID() != 0);
+
+ SQLiteStatement statement(m_database, "INSERT INTO Caches (cacheGroup) VALUES (?)");
+ if (statement.prepare() != SQLResultOk)
+ return false;
+
+ statement.bindInt64(1, cache->group()->storageID());
+
+ if (!executeStatement(statement))
+ return false;
+
+ unsigned cacheStorageID = (unsigned)m_database.lastInsertRowID();
+
+ // Store all resources
+ {
+ ApplicationCache::ResourceMap::const_iterator end = cache->end();
+ for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) {
+ if (!store(it->second.get(), cacheStorageID))
+ return false;
+ }
+ }
+
+ // Store the online whitelist
+ const HashSet<String>& onlineWhitelist = cache->onlineWhitelist();
+ {
+ HashSet<String>::const_iterator end = onlineWhitelist.end();
+ for (HashSet<String>::const_iterator it = onlineWhitelist.begin(); it != end; ++it) {
+ SQLiteStatement statement(m_database, "INSERT INTO CacheWhitelistURLs (url, cache) VALUES (?, ?)");
+ statement.prepare();
+
+ statement.bindText(1, *it);
+ statement.bindInt64(2, cacheStorageID);
+
+ if (!executeStatement(statement))
+ return false;
+ }
+ }
+
+ cache->setStorageID(cacheStorageID);
+ return true;
+}
+
+bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned cacheStorageID)
+{
+ ASSERT(cacheStorageID);
+ ASSERT(!resource->storageID());
+
+ openDatabase(true);
+
+ // First, insert the data
+ SQLiteStatement dataStatement(m_database, "INSERT INTO CacheResourceData (data) VALUES (?)");
+ if (dataStatement.prepare() != SQLResultOk)
+ return false;
+
+ if (resource->data()->size())
+ dataStatement.bindBlob(1, resource->data()->data(), resource->data()->size());
+
+ if (!dataStatement.executeCommand())
+ return false;
+
+ unsigned dataId = (unsigned)m_database.lastInsertRowID();
+
+ // Then, insert the resource
+
+ // Serialize the headers
+ Vector<UChar> stringBuilder;
+
+ HTTPHeaderMap::const_iterator end = resource->response().httpHeaderFields().end();
+ for (HTTPHeaderMap::const_iterator it = resource->response().httpHeaderFields().begin(); it!= end; ++it) {
+ stringBuilder.append(it->first.characters(), it->first.length());
+ stringBuilder.append((UChar)':');
+ stringBuilder.append(it->second.characters(), it->second.length());
+ stringBuilder.append((UChar)'\n');
+ }
+
+ String headers = String::adopt(stringBuilder);
+
+ SQLiteStatement resourceStatement(m_database, "INSERT INTO CacheResources (url, statusCode, responseURL, headers, data, mimeType, textEncodingName) VALUES (?, ?, ?, ?, ?, ?, ?)");
+ if (resourceStatement.prepare() != SQLResultOk)
+ return false;
+
+ resourceStatement.bindText(1, resource->url());
+ resourceStatement.bindInt64(2, resource->response().httpStatusCode());
+ resourceStatement.bindText(3, resource->response().url());
+ resourceStatement.bindText(4, headers);
+ resourceStatement.bindInt64(5, dataId);
+ resourceStatement.bindText(6, resource->response().mimeType());
+ resourceStatement.bindText(7, resource->response().textEncodingName());
+
+ if (!executeStatement(resourceStatement))
+ return false;
+
+ unsigned resourceId = (unsigned)m_database.lastInsertRowID();
+
+ // Finally, insert the cache entry
+ SQLiteStatement entryStatement(m_database, "INSERT INTO CacheEntries (cache, type, resource) VALUES (?, ?, ?)");
+ if (entryStatement.prepare() != SQLResultOk)
+ return false;
+
+ entryStatement.bindInt64(1, cacheStorageID);
+ entryStatement.bindInt64(2, resource->type());
+ entryStatement.bindInt64(3, resourceId);
+
+ if (!executeStatement(entryStatement))
+ return false;
+
+ resource->setStorageID(resourceId);
+ return true;
+}
+
+void ApplicationCacheStorage::store(ApplicationCacheResource* resource, ApplicationCache* cache)
+{
+ ASSERT(cache->storageID());
+
+ openDatabase(true);
+
+ SQLiteTransaction storeResourceTransaction(m_database);
+ storeResourceTransaction.begin();
+
+ if (!store(resource, cache->storageID()))
+ return;
+
+ storeResourceTransaction.commit();
+}
+
+bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group)
+{
+ openDatabase(true);
+
+ SQLiteTransaction storeCacheTransaction(m_database);
+
+ storeCacheTransaction.begin();
+
+ if (!group->storageID()) {
+ // Store the group
+ if (!store(group))
+ return false;
+ }
+
+ ASSERT(group->newestCache());
+ ASSERT(!group->newestCache()->storageID());
+
+ // Store the newest cache
+ if (!store(group->newestCache()))
+ return false;
+
+ // Update the newest cache in the group.
+
+ SQLiteStatement statement(m_database, "UPDATE CacheGroups SET newestCache=? WHERE id=?");
+ if (statement.prepare() != SQLResultOk)
+ return false;
+
+ statement.bindInt64(1, group->newestCache()->storageID());
+ statement.bindInt64(2, group->storageID());
+
+ if (!executeStatement(statement))
+ return false;
+
+ storeCacheTransaction.commit();
+ return true;
+}
+
+static inline void parseHeader(const UChar* header, size_t headerLength, ResourceResponse& response)
+{
+ int pos = find(header, headerLength, ':');
+ ASSERT(pos != -1);
+
+ String headerName = String(header, pos);
+ String headerValue = String(header + pos + 1, headerLength - pos - 1);
+
+ response.setHTTPHeaderField(headerName, headerValue);
+}
+
+static inline void parseHeaders(const String& headers, ResourceResponse& response)
+{
+ int startPos = 0;
+ int endPos;
+ while ((endPos = headers.find('\n', startPos)) != -1) {
+ ASSERT(startPos != endPos);
+
+ parseHeader(headers.characters() + startPos, endPos - startPos, response);
+
+ startPos = endPos + 1;
+ }
+
+ if (startPos != static_cast<int>(headers.length()))
+ parseHeader(headers.characters(), headers.length(), response);
+}
+
+PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storageID)
+{
+ SQLiteStatement cacheStatement(m_database,
+ "SELECT url, type, mimeType, textEncodingName, headers, CacheResourceData.data FROM CacheEntries INNER JOIN CacheResources ON CacheEntries.resource=CacheResources.id "
+ "INNER JOIN CacheResourceData ON CacheResourceData.id=CacheResources.data WHERE CacheEntries.cache=?");
+ if (cacheStatement.prepare() != SQLResultOk) {
+ LOG_ERROR("Could not prepare cache statement, error \"%s\"", m_database.lastErrorMsg());
+ return 0;
+ }
+
+ cacheStatement.bindInt64(1, storageID);
+
+ RefPtr<ApplicationCache> cache = ApplicationCache::create();
+
+ int result;
+ while ((result = cacheStatement.step()) == SQLResultRow) {
+ KURL url(cacheStatement.getColumnText(0));
+
+ unsigned type = (unsigned)cacheStatement.getColumnInt64(1);
+
+ Vector<char> blob;
+ cacheStatement.getColumnBlobAsVector(5, blob);
+
+ RefPtr<SharedBuffer> data = SharedBuffer::adoptVector(blob);
+
+ String mimeType = cacheStatement.getColumnText(2);
+ String textEncodingName = cacheStatement.getColumnText(3);
+
+ ResourceResponse response(url, mimeType, data->size(), textEncodingName, "");
+
+ String headers = cacheStatement.getColumnText(4);
+ parseHeaders(headers, response);
+
+ RefPtr<ApplicationCacheResource> resource = ApplicationCacheResource::create(url, response, type, data.release());
+
+ if (type & ApplicationCacheResource::Manifest)
+ cache->setManifestResource(resource.release());
+ else
+ cache->addResource(resource.release());
+ }
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Could not load cache resources, error \"%s\"", m_database.lastErrorMsg());
+
+ // Load the online whitelist
+ SQLiteStatement whitelistStatement(m_database, "SELECT url FROM CacheWhitelistURLs WHERE cache=?");
+ if (whitelistStatement.prepare() != SQLResultOk)
+ return 0;
+ whitelistStatement.bindInt64(1, storageID);
+
+ HashSet<String> whitelist;
+ while ((result = whitelistStatement.step()) == SQLResultRow)
+ whitelist.add(whitelistStatement.getColumnText(0));
+
+ if (result != SQLResultDone)
+ LOG_ERROR("Could not load cache online whitelist, error \"%s\"", m_database.lastErrorMsg());
+
+ cache->setOnlineWhitelist(whitelist);
+
+ cache->setStorageID(storageID);
+
+ return cache.release();
+}
+
+void ApplicationCacheStorage::remove(ApplicationCache* cache)
+{
+ if (!cache->storageID())
+ return;
+
+ openDatabase(false);
+ if (!m_database.isOpen())
+ return;
+
+ SQLiteStatement statement(m_database, "DELETE FROM Caches WHERE id=?");
+ if (statement.prepare() != SQLResultOk)
+ return;
+
+ statement.bindInt64(1, cache->storageID());
+ executeStatement(statement);
+}
+
+void ApplicationCacheStorage::empty()
+{
+ openDatabase(false);
+
+ if (!m_database.isOpen())
+ return;
+
+ // Clear cache groups, caches and cache resources.
+ executeSQLCommand("DELETE FROM CacheGroups");
+ executeSQLCommand("DELETE FROM Caches");
+ executeSQLCommand("DELETE FROM CacheResources");
+
+ // Clear the storage IDs for the caches in memory.
+ // The caches will still work, but cached resources will not be saved to disk
+ // until a cache update process has been initiated.
+ CacheGroupMap::const_iterator end = m_cachesInMemory.end();
+ for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it)
+ it->second->clearStorageID();
+}
+
+bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, ApplicationCache* cache)
+{
+ // Create a new cache.
+ RefPtr<ApplicationCache> cacheCopy = ApplicationCache::create();
+
+ // Set the online whitelist
+ cacheCopy->setOnlineWhitelist(cache->onlineWhitelist());
+
+ // Traverse the cache and add copies of all resources.
+ ApplicationCache::ResourceMap::const_iterator end = cache->end();
+ for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) {
+ ApplicationCacheResource* resource = it->second.get();
+
+ RefPtr<ApplicationCacheResource> resourceCopy = ApplicationCacheResource::create(resource->url(), resource->response(), resource->type(), resource->data());
+
+ cacheCopy->addResource(resourceCopy.release());
+ }
+
+ // Now create a new cache group.
+ OwnPtr<ApplicationCacheGroup> groupCopy(new ApplicationCacheGroup(cache->group()->manifestURL(), true));
+
+ groupCopy->setNewestCache(cacheCopy);
+
+ ApplicationCacheStorage copyStorage;
+ copyStorage.setCacheDirectory(cacheDirectory);
+
+ // Empty the cache in case something was there before.
+ copyStorage.empty();
+
+ return copyStorage.storeNewestCache(groupCopy.get());
+}
+
+ApplicationCacheStorage& cacheStorage()
+{
+ static ApplicationCacheStorage storage;
+
+ return storage;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/WebCore/loader/appcache/ApplicationCacheStorage.h b/WebCore/loader/appcache/ApplicationCacheStorage.h
new file mode 100644
index 0000000..6bd9ba1
--- /dev/null
+++ b/WebCore/loader/appcache/ApplicationCacheStorage.h
@@ -0,0 +1,96 @@
+/*
+ * 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&);
+
+ ApplicationCacheGroup* cacheGroupForURL(const KURL&);
+
+ ApplicationCacheGroup* findOrCreateCacheGroup(const KURL& manifestURL);
+ void cacheGroupDestroyed(ApplicationCacheGroup*);
+
+ bool storeNewestCache(ApplicationCacheGroup*);
+ void store(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 determinate 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/WebCore/loader/appcache/DOMApplicationCache.cpp b/WebCore/loader/appcache/DOMApplicationCache.cpp
new file mode 100644
index 0000000..f872a50
--- /dev/null
+++ b/WebCore/loader/appcache/DOMApplicationCache.cpp
@@ -0,0 +1,286 @@
+/*
+ * 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 "DocumentLoader.h"
+#include "Event.h"
+#include "EventException.h"
+#include "EventListener.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "FrameLoader.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()->topLevelApplicationCache();
+}
+
+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;
+}
+
+unsigned DOMApplicationCache::length() const
+{
+ ApplicationCache* cache = associatedCache();
+ if (!cache)
+ return 0;
+
+ return cache->numDynamicEntries();
+}
+
+String DOMApplicationCache::item(unsigned item, ExceptionCode& ec)
+{
+ ApplicationCache* cache = associatedCache();
+ if (!cache) {
+ ec = INVALID_STATE_ERR;
+ return String();
+ }
+
+ if (item >= length()) {
+ ec = INDEX_SIZE_ERR;
+ return String();
+ }
+
+ return cache->dynamicEntry(item);
+}
+
+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 useCapture)
+{
+ 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->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/WebCore/loader/appcache/DOMApplicationCache.h b/WebCore/loader/appcache/DOMApplicationCache.h
new file mode 100644
index 0000000..30c0b7e
--- /dev/null
+++ b/WebCore/loader/appcache/DOMApplicationCache.h
@@ -0,0 +1,140 @@
+/*
+ * 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 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&);
+
+ unsigned length() const;
+ String item(unsigned item, 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/WebCore/loader/appcache/DOMApplicationCache.idl b/WebCore/loader/appcache/DOMApplicationCache.idl
new file mode 100644
index 0000000..94326f3
--- /dev/null
+++ b/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 unsigned long length;
+ DOMString item(in [IsIndex] unsigned long index)
+ 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/WebCore/loader/appcache/ManifestParser.cpp b/WebCore/loader/appcache/ManifestParser.cpp
new file mode 100644
index 0000000..778d22d
--- /dev/null
+++ b/WebCore/loader/appcache/ManifestParser.cpp
@@ -0,0 +1,166 @@
+/*
+ * 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"
+
+namespace WebCore {
+
+enum Mode { Explicit, Fallback, OnlineWhitelist };
+
+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 (mode == Explicit || mode == OnlineWhitelist) {
+ KURL url(manifestURL, line);
+
+ 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.add(url.string());
+
+ } 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;
+
+ // Check that the namespace URL has the same scheme/host/port as the manifest URL.
+ if (!protocolHostAndPortAreEqual(manifestURL, namespaceURL))
+ continue;
+
+ while (p < lineEnd && (*p == '\t' || *p == ' '))
+ p++;
+
+ KURL fallbackURL(String(p, line.length() - (p - line.characters())));
+
+ if (!fallbackURL.isValid())
+ continue;
+
+ if (!equalIgnoringCase(fallbackURL.protocol(), manifestURL.protocol()))
+ continue;
+
+ manifest.fallbackURLs.add(namespaceURL, fallbackURL);
+ } else
+ ASSERT_NOT_REACHED();
+ }
+
+ return true;
+}
+
+}
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
diff --git a/WebCore/loader/mac/ImageDocumentMac.mm b/WebCore/loader/appcache/ManifestParser.h
index 755f448..f4fe31a 100644
--- a/WebCore/loader/mac/ImageDocumentMac.mm
+++ b/WebCore/loader/appcache/ManifestParser.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * 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
@@ -10,38 +10,43 @@
* 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 COMPUTER, INC. ``AS IS'' AND ANY
+ * 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 COMPUTER, INC. OR
+ * 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.
*/
-#import "config.h"
-#import "ImageDocumentMac.h"
+#ifndef ManifestParser_h
+#define ManifestParser_h
-#include "CachedImage.h"
-#include "Document.h"
-#include "FrameLoader.h"
-#include "Frame.h"
-#include "WebCoreFrameBridge.h"
-#include "DocumentLoader.h"
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include "StringHash.h"
+#include "PlatformString.h"
namespace WebCore {
-
-void finishImageLoad(Document* document, CachedImage* image)
-{
- Frame* frame = document->frame();
- const ResourceResponse& response = frame->loader()->documentLoader()->response();
-
- IntSize size = image->imageSize();
- if (size.width())
- document->setTitle([frame->bridge() imageTitleForFilename:response.suggestedFilename() size:size]);
-}
-
+
+class KURL;
+
+struct Manifest {
+ HashSet<String> onlineWhitelistedURLs;
+ HashSet<String> explicitURLs;
+ HashMap<String, String> fallbackURLs;
+};
+
+bool parseManifest(const KURL& manifestURL, const char* data, int length, Manifest&);
+
}
+
+#endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
+
+#endif // ManifestParser_h
diff --git a/WebCore/loader/archive/Archive.h b/WebCore/loader/archive/Archive.h
new file mode 100644
index 0000000..af3d8b1
--- /dev/null
+++ b/WebCore/loader/archive/Archive.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 Archive_h
+#define Archive_h
+
+#include "ArchiveResource.h"
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class Archive : public RefCounted<Archive> {
+public:
+ ArchiveResource* mainResource() { return m_mainResource.get(); }
+ const Vector<RefPtr<ArchiveResource> >& subresources() const { return m_subresources; }
+ const Vector<RefPtr<Archive> >& subframeArchives() const { return m_subframeArchives; }
+
+protected:
+ // These methods are meant for subclasses for different archive types to add resources in to the archive,
+ // and should not be exposed as archives should be immutable to clients
+ void setMainResource(PassRefPtr<ArchiveResource> mainResource) { m_mainResource = mainResource; }
+ void addSubresource(PassRefPtr<ArchiveResource> subResource) { m_subresources.append(subResource); }
+ void addSubframeArchive(PassRefPtr<Archive> subframeArchive) { m_subframeArchives.append(subframeArchive); }
+
+private:
+ RefPtr<ArchiveResource> m_mainResource;
+ Vector<RefPtr<ArchiveResource> > m_subresources;
+ Vector<RefPtr<Archive> > m_subframeArchives;
+};
+
+}
+
+#endif // Archive
diff --git a/WebCore/loader/archive/ArchiveFactory.cpp b/WebCore/loader/archive/ArchiveFactory.cpp
new file mode 100644
index 0000000..c42b5df
--- /dev/null
+++ b/WebCore/loader/archive/ArchiveFactory.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "ArchiveFactory.h"
+
+#if PLATFORM(CF)
+#include "LegacyWebArchive.h"
+#endif
+#include "MIMETypeRegistry.h"
+#include "PlatformString.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+
+namespace WebCore {
+
+typedef PassRefPtr<Archive> RawDataCreationFunction(SharedBuffer*);
+
+// The create functions in the archive classes return PassRefPtr to concrete subclasses
+// of Archive. This adaptor makes the functions have a uniform return type.
+template <typename ArchiveClass> static PassRefPtr<Archive> archiveFactoryCreate(SharedBuffer* buffer)
+{
+ return ArchiveClass::create(buffer);
+}
+
+static HashMap<String, RawDataCreationFunction*, CaseFoldingHash>& archiveMIMETypes()
+{
+ static HashMap<String, RawDataCreationFunction*, CaseFoldingHash> mimeTypes;
+ static bool initialized = false;
+
+ if (initialized)
+ return mimeTypes;
+
+#if PLATFORM(CF)
+ mimeTypes.set("application/x-webarchive", archiveFactoryCreate<LegacyWebArchive>);
+#endif
+
+ initialized = true;
+ return mimeTypes;
+}
+
+bool ArchiveFactory::isArchiveMimeType(const String& mimeType)
+{
+ return archiveMIMETypes().contains(mimeType);
+}
+
+PassRefPtr<Archive> ArchiveFactory::create(SharedBuffer* data, const String& mimeType)
+{
+ RawDataCreationFunction* function = archiveMIMETypes().get(mimeType);
+ return function ? function(data) : 0;
+}
+
+void ArchiveFactory::registerKnownArchiveMIMETypes()
+{
+ HashSet<String>& mimeTypes = MIMETypeRegistry::getSupportedNonImageMIMETypes();
+ HashMap<String, RawDataCreationFunction*, CaseFoldingHash>::iterator i = archiveMIMETypes().begin();
+ HashMap<String, RawDataCreationFunction*, CaseFoldingHash>::iterator end = archiveMIMETypes().end();
+
+ for (; i != end; ++i)
+ mimeTypes.add(i->first);
+}
+
+}
diff --git a/WebCore/loader/archive/ArchiveFactory.h b/WebCore/loader/archive/ArchiveFactory.h
new file mode 100644
index 0000000..bf1d5c6
--- /dev/null
+++ b/WebCore/loader/archive/ArchiveFactory.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 ArchiveFactory_h
+#define ArchiveFactory_h
+
+#include "Archive.h"
+
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class SharedBuffer;
+class String;
+
+class ArchiveFactory {
+public:
+ static bool isArchiveMimeType(const String&);
+ static PassRefPtr<Archive> create(SharedBuffer* data, const String& mimeType);
+ static void registerKnownArchiveMIMETypes();
+};
+
+}
+
+#endif // ArchiveFactory_h
diff --git a/WebCore/loader/archive/ArchiveResource.cpp b/WebCore/loader/archive/ArchiveResource.cpp
new file mode 100644
index 0000000..691f66a
--- /dev/null
+++ b/WebCore/loader/archive/ArchiveResource.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "ArchiveResource.h"
+
+#include "SharedBuffer.h"
+
+namespace WebCore {
+
+PassRefPtr<ArchiveResource> ArchiveResource::create(PassRefPtr<SharedBuffer> data, const KURL& url, const ResourceResponse& response)
+{
+ return data ? adoptRef(new ArchiveResource(data, url, response)) : 0;
+}
+
+PassRefPtr<ArchiveResource> ArchiveResource::create(PassRefPtr<SharedBuffer> data, const KURL& url, const String& mimeType, const String& textEncoding, const String& frameName)
+{
+ return data ? adoptRef(new ArchiveResource(data, url, mimeType, textEncoding, frameName)) : 0;
+}
+
+PassRefPtr<ArchiveResource> ArchiveResource::create(PassRefPtr<SharedBuffer> data, const KURL& url, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse& resourceResponse)
+{
+ return data ? adoptRef(new ArchiveResource(data, url, mimeType, textEncoding, frameName, resourceResponse)) : 0;
+}
+
+ArchiveResource::ArchiveResource(PassRefPtr<SharedBuffer> data, const KURL& url, const ResourceResponse& response)
+ : SubstituteResource(url, response, data)
+ , m_mimeType(response.mimeType())
+ , m_textEncoding(response.textEncodingName())
+ , m_shouldIgnoreWhenUnarchiving(false)
+{
+}
+
+ArchiveResource::ArchiveResource(PassRefPtr<SharedBuffer> data, const KURL& url, const String& mimeType, const String& textEncoding, const String& frameName)
+ : SubstituteResource(url, ResourceResponse(url, mimeType, data ? data->size() : 0, textEncoding, String()), data)
+ , m_mimeType(mimeType)
+ , m_textEncoding(textEncoding)
+ , m_frameName(frameName)
+ , m_shouldIgnoreWhenUnarchiving(false)
+{
+}
+
+ArchiveResource::ArchiveResource(PassRefPtr<SharedBuffer> data, const KURL& url, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse& response)
+ : SubstituteResource(url, response.isNull() ? ResourceResponse(url, mimeType, data ? data->size() : 0, textEncoding, String()) : response, data)
+ , m_mimeType(mimeType)
+ , m_textEncoding(textEncoding)
+ , m_frameName(frameName)
+ , m_shouldIgnoreWhenUnarchiving(false)
+{
+}
+
+}
diff --git a/WebCore/loader/archive/ArchiveResource.h b/WebCore/loader/archive/ArchiveResource.h
new file mode 100644
index 0000000..d975e04
--- /dev/null
+++ b/WebCore/loader/archive/ArchiveResource.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 ArchiveResource_h
+#define ArchiveResource_h
+
+#include "SubstituteResource.h"
+
+#include "PlatformString.h"
+
+namespace WebCore {
+
+class ArchiveResource : public SubstituteResource {
+public:
+ static PassRefPtr<ArchiveResource> create(PassRefPtr<SharedBuffer>, const KURL&, const ResourceResponse&);
+ static PassRefPtr<ArchiveResource> create(PassRefPtr<SharedBuffer>, const KURL&, const String& mimeType, const String& textEncoding, const String& frameName);
+ static PassRefPtr<ArchiveResource> create(PassRefPtr<SharedBuffer>, const KURL&, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse&);
+
+ const String& mimeType() const { return m_mimeType; }
+ const String& textEncoding() const { return m_textEncoding; }
+ const String& frameName() const { return m_frameName; }
+
+ void ignoreWhenUnarchiving() { m_shouldIgnoreWhenUnarchiving = true; }
+ bool shouldIgnoreWhenUnarchiving() const { return m_shouldIgnoreWhenUnarchiving; }
+
+private:
+ ArchiveResource(PassRefPtr<SharedBuffer>, const KURL&, const ResourceResponse&);
+ ArchiveResource(PassRefPtr<SharedBuffer>, const KURL&, const String& mimeType, const String& textEncoding, const String& frameName);
+ ArchiveResource(PassRefPtr<SharedBuffer>, const KURL&, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse&);
+
+ String m_mimeType;
+ String m_textEncoding;
+ String m_frameName;
+
+ bool m_shouldIgnoreWhenUnarchiving;
+};
+
+}
+
+#endif // ArchiveResource_h
diff --git a/WebCore/loader/archive/ArchiveResourceCollection.cpp b/WebCore/loader/archive/ArchiveResourceCollection.cpp
new file mode 100644
index 0000000..6eb1237
--- /dev/null
+++ b/WebCore/loader/archive/ArchiveResourceCollection.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "ArchiveResourceCollection.h"
+
+namespace WebCore {
+
+ArchiveResourceCollection::ArchiveResourceCollection()
+{
+}
+
+void ArchiveResourceCollection::addAllResources(Archive* archive)
+{
+ ASSERT(archive);
+ if (!archive)
+ return;
+
+ const Vector<RefPtr<ArchiveResource> >& subresources = archive->subresources();
+ Vector<RefPtr<ArchiveResource> >::const_iterator iRes = subresources.begin();
+ Vector<RefPtr<ArchiveResource> >::const_iterator endRes = subresources.end();
+
+ for (; iRes != endRes; ++iRes)
+ m_subresources.set((*iRes)->url(), iRes->get());
+
+ const Vector<RefPtr<Archive> >& subframes = archive->subframeArchives();
+ Vector<RefPtr<Archive> >::const_iterator iFrame = subframes.begin();
+ Vector<RefPtr<Archive> >::const_iterator endFrame = subframes.end();
+
+ for (; iFrame != endFrame; ++iFrame) {
+ ASSERT((*iFrame)->mainResource());
+ const String& frameName = (*iFrame)->mainResource()->frameName();
+ if (!frameName.isNull())
+ m_subframes.set(frameName, iFrame->get());
+ }
+}
+
+// FIXME: Adding a resource directly to a DocumentLoader/ArchiveResourceCollection seems like bad design, but is API some apps rely on.
+// Can we change the design in a manner that will let us deprecate that API without reducing functionality of those apps?
+void ArchiveResourceCollection::addResource(PassRefPtr<ArchiveResource> resource)
+{
+ ASSERT(resource);
+ if (!resource)
+ return;
+
+ const KURL& url = resource->url(); // get before passing PassRefPtr (which sets it to 0)
+ m_subresources.set(url, resource);
+}
+
+ArchiveResource* ArchiveResourceCollection::archiveResourceForURL(const KURL& url)
+{
+ ArchiveResource* resource = m_subresources.get(url).get();
+ if (!resource)
+ return 0;
+
+ return resource;
+}
+
+PassRefPtr<Archive> ArchiveResourceCollection::popSubframeArchive(const String& frameName)
+{
+ return m_subframes.take(frameName);
+}
+
+}
diff --git a/WebCore/loader/archive/ArchiveResourceCollection.h b/WebCore/loader/archive/ArchiveResourceCollection.h
new file mode 100644
index 0000000..f898a8d
--- /dev/null
+++ b/WebCore/loader/archive/ArchiveResourceCollection.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 ArchiveResourceCollection_h
+#define ArchiveResourceCollection_h
+
+#include "Archive.h"
+#include "ArchiveResource.h"
+#include "KURL.h"
+#include "PlatformString.h"
+
+#include <wtf/HashMap.h>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+class ArchiveResourceCollection : Noncopyable {
+public:
+ ArchiveResourceCollection();
+
+ void addResource(PassRefPtr<ArchiveResource>);
+ void addAllResources(Archive*);
+
+ ArchiveResource* archiveResourceForURL(const KURL&);
+ PassRefPtr<Archive> popSubframeArchive(const String& frameName);
+
+private:
+ HashMap<String, RefPtr<ArchiveResource> > m_subresources;
+ HashMap<String, RefPtr<Archive> > m_subframes;
+};
+
+}
+
+#endif
diff --git a/WebCore/loader/archive/cf/LegacyWebArchive.cpp b/WebCore/loader/archive/cf/LegacyWebArchive.cpp
new file mode 100644
index 0000000..9d99588
--- /dev/null
+++ b/WebCore/loader/archive/cf/LegacyWebArchive.cpp
@@ -0,0 +1,562 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "LegacyWebArchive.h"
+
+#include "CString.h"
+#include "Document.h"
+#include "DocumentLoader.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameTree.h"
+#include "HTMLFrameOwnerElement.h"
+#include "HTMLNames.h"
+#include "KURL.h"
+#include "Logging.h"
+#include "markup.h"
+#include "Node.h"
+#include "Range.h"
+#include "SelectionController.h"
+#include "SharedBuffer.h"
+
+#include <wtf/RetainPtr.h>
+
+namespace WebCore {
+
+static const CFStringRef LegacyWebArchiveMainResourceKey = CFSTR("WebMainResource");
+static const CFStringRef LegacyWebArchiveSubresourcesKey = CFSTR("WebSubresources");
+static const CFStringRef LegacyWebArchiveSubframeArchivesKey = CFSTR("WebSubframeArchives");
+static const CFStringRef LegacyWebArchiveResourceDataKey = CFSTR("WebResourceData");
+static const CFStringRef LegacyWebArchiveResourceFrameNameKey = CFSTR("WebResourceFrameName");
+static const CFStringRef LegacyWebArchiveResourceMIMETypeKey = CFSTR("WebResourceMIMEType");
+static const CFStringRef LegacyWebArchiveResourceURLKey = CFSTR("WebResourceURL");
+static const CFStringRef LegacyWebArchiveResourceTextEncodingNameKey = CFSTR("WebResourceTextEncodingName");
+static const CFStringRef LegacyWebArchiveResourceResponseKey = CFSTR("WebResourceResponse");
+static const CFStringRef LegacyWebArchiveResourceResponseVersionKey = CFSTR("WebResourceResponseVersion");
+
+static RetainPtr<CFDictionaryRef> createPropertyListRepresentationFromResource(ArchiveResource* resource, bool mainResource)
+{
+ if (!resource) {
+ // The property list representation of a null/empty WebResource has the following 3 objects stored as nil
+ RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 3, 0, 0));
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceDataKey, 0);
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceURLKey, 0);
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceMIMETypeKey, 0);
+
+ return propertyList;
+ }
+
+ RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 6, 0, &kCFTypeDictionaryValueCallBacks));
+
+ // Resource data can be empty, but must be represented by an empty CFDataRef
+ SharedBuffer* data = resource->data();
+ RetainPtr<CFDataRef> cfData;
+ if (data)
+ cfData.adoptCF(data->createCFData());
+ else
+ cfData.adoptCF(CFDataCreate(0, 0, 0));
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceDataKey, cfData.get());
+
+ // Resource URL cannot be null
+ RetainPtr<CFStringRef> cfURL(AdoptCF, resource->url().string().createCFString());
+ if (cfURL)
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceURLKey, cfURL.get());
+ else {
+ LOG(Archives, "LegacyWebArchive - NULL resource URL is invalid - returning null property list");
+ return 0;
+ }
+
+ // FrameName should be left out if empty for subresources, but always included for main resources
+ const String& frameName(resource->frameName());
+ if (!frameName.isEmpty() || mainResource) {
+ RetainPtr<CFStringRef> cfFrameName(AdoptCF, frameName.createCFString());
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceFrameNameKey, cfFrameName.get());
+ }
+
+ // Set MIMEType, TextEncodingName, and ResourceResponse only if they actually exist
+ const String& mimeType(resource->mimeType());
+ if (!mimeType.isEmpty()) {
+ RetainPtr<CFStringRef> cfMIMEType(AdoptCF, mimeType.createCFString());
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceMIMETypeKey, cfMIMEType.get());
+ }
+
+ const String& textEncoding(resource->textEncoding());
+ if (!textEncoding.isEmpty()) {
+ RetainPtr<CFStringRef> cfTextEncoding(AdoptCF, textEncoding.createCFString());
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceTextEncodingNameKey, cfTextEncoding.get());
+ }
+
+ // Don't include the resource response for the main resource
+ if (!mainResource) {
+ RetainPtr<CFDataRef> resourceResponseData = propertyListDataFromResourceResponse(resource->response());
+ if (resourceResponseData)
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceResponseKey, resourceResponseData.get());
+ }
+
+ return propertyList;
+}
+
+static RetainPtr<CFDictionaryRef> createPropertyListRep(Archive* archive)
+{
+ RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 3, 0, &kCFTypeDictionaryValueCallBacks));
+
+ RetainPtr<CFDictionaryRef> mainResourceDict = createPropertyListRepresentationFromResource(archive->mainResource(), true);
+ if (!mainResourceDict)
+ return 0;
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveMainResourceKey, mainResourceDict.get());
+
+ RetainPtr<CFMutableArrayRef> subresourcesArray(AdoptCF, CFArrayCreateMutable(0, archive->subresources().size(), &kCFTypeArrayCallBacks));
+ const Vector<RefPtr<ArchiveResource> >& subresources(archive->subresources());
+ for (unsigned i = 0; i < subresources.size(); ++i) {
+ RetainPtr<CFDictionaryRef> subresource = createPropertyListRepresentationFromResource(subresources[i].get(), false);
+ if (subresource)
+ CFArrayAppendValue(subresourcesArray.get(), subresource.get());
+ else
+ LOG(Archives, "LegacyWebArchive - Failed to create property list for subresource");
+ }
+ if (CFArrayGetCount(subresourcesArray.get()))
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveSubresourcesKey, subresourcesArray.get());
+
+ RetainPtr<CFMutableArrayRef> subframesArray(AdoptCF, CFArrayCreateMutable(0, archive->subframeArchives().size(), &kCFTypeArrayCallBacks));
+ const Vector<RefPtr<Archive> >& subframeArchives(archive->subframeArchives());
+ for (unsigned i = 0; i < subframeArchives.size(); ++i) {
+ RetainPtr<CFDictionaryRef> subframeArchive = createPropertyListRep(subframeArchives[i].get());
+ if (subframeArchive)
+ CFArrayAppendValue(subframesArray.get(), subframeArchive.get());
+ else
+ LOG(Archives, "LegacyWebArchive - Failed to create property list for subframe archive");
+ }
+ if (CFArrayGetCount(subframesArray.get()))
+ CFDictionarySetValue(propertyList.get(), LegacyWebArchiveSubframeArchivesKey, subframesArray.get());
+
+ return propertyList;
+}
+
+static ResourceResponse createResourceResponseFromPropertyListData(CFDataRef data, CFStringRef responseDataType)
+{
+ ASSERT(data);
+ if (!data)
+ return ResourceResponse();
+
+ // If the ResourceResponseVersion (passed in as responseDataType) exists at all, this is a "new" webarchive that we can parse well in a cross platform manner
+ // If it doesn't exist, we will assume this is an "old" Cocoa-based WebArchive, and parse the ResourceResponse as such
+ if (!responseDataType)
+ return createResourceResponseFromMacArchivedData(data);
+
+ // FIXME: Parse the "new" format that the above comment references here
+ return ResourceResponse();
+}
+
+static PassRefPtr<ArchiveResource> createResource(CFDictionaryRef dictionary)
+{
+ ASSERT(dictionary);
+ if (!dictionary)
+ return 0;
+
+ CFDataRef resourceData = static_cast<CFDataRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceDataKey));
+ if (resourceData && CFGetTypeID(resourceData) != CFDataGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Resource data is not of type CFData, cannot create invalid resource");
+ return 0;
+ }
+
+ CFStringRef frameName = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceFrameNameKey));
+ if (frameName && CFGetTypeID(frameName) != CFStringGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Frame name is not of type CFString, cannot create invalid resource");
+ return 0;
+ }
+
+ CFStringRef mimeType = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceMIMETypeKey));
+ if (mimeType && CFGetTypeID(mimeType) != CFStringGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - MIME type is not of type CFString, cannot create invalid resource");
+ return 0;
+ }
+
+ CFStringRef url = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceURLKey));
+ if (url && CFGetTypeID(url) != CFStringGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - URL is not of type CFString, cannot create invalid resource");
+ return 0;
+ }
+
+ CFStringRef textEncoding = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceTextEncodingNameKey));
+ if (textEncoding && CFGetTypeID(textEncoding) != CFStringGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Text encoding is not of type CFString, cannot create invalid resource");
+ return 0;
+ }
+
+ ResourceResponse response;
+
+ CFDataRef resourceResponseData = static_cast<CFDataRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceResponseKey));
+ if (resourceResponseData) {
+ if (CFGetTypeID(resourceResponseData) != CFDataGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Resource response data is not of type CFData, cannot create invalid resource");
+ return 0;
+ }
+
+ CFStringRef resourceResponseVersion = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceResponseVersionKey));
+ if (resourceResponseVersion && CFGetTypeID(resourceResponseVersion) != CFStringGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Resource response version is not of type CFString, cannot create invalid resource");
+ return 0;
+ }
+
+ response = createResourceResponseFromPropertyListData(resourceResponseData, resourceResponseVersion);
+ }
+
+ return ArchiveResource::create(SharedBuffer::create(CFDataGetBytePtr(resourceData), CFDataGetLength(resourceData)), KURL(url), mimeType, textEncoding, frameName, response);
+}
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::create()
+{
+ return adoptRef(new LegacyWebArchive);
+}
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(SharedBuffer* data)
+{
+ LOG(Archives, "LegacyWebArchive - Creating from raw data");
+
+ RefPtr<LegacyWebArchive> archive = create();
+ if (!archive->init(data))
+ return 0;
+
+ return archive.release();
+}
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(PassRefPtr<ArchiveResource> mainResource, Vector<PassRefPtr<ArchiveResource> >& subresources, Vector<PassRefPtr<LegacyWebArchive> >& subframeArchives)
+{
+ ASSERT(mainResource);
+ if (!mainResource)
+ return 0;
+
+ RefPtr<LegacyWebArchive> archive = create();
+ archive->setMainResource(mainResource);
+
+ for (unsigned i = 0; i < subresources.size(); ++i)
+ archive->addSubresource(subresources[i]);
+
+ for (unsigned i = 0; i < subframeArchives.size(); ++i)
+ archive->addSubframeArchive(subframeArchives[i]);
+
+ return archive.release();
+}
+
+LegacyWebArchive::LegacyWebArchive()
+{
+}
+
+bool LegacyWebArchive::init(SharedBuffer* data)
+{
+ ASSERT(data);
+ if (!data)
+ return false;
+
+ RetainPtr<CFDataRef> cfData(AdoptCF, data->createCFData());
+ if (!cfData)
+ return false;
+
+ CFStringRef errorString = 0;
+
+ RetainPtr<CFDictionaryRef> plist(AdoptCF, static_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(0, cfData.get(), kCFPropertyListImmutable, &errorString)));
+ if (!plist) {
+#ifndef NDEBUG
+ const char* cError = errorString ? CFStringGetCStringPtr(errorString, kCFStringEncodingUTF8) : "unknown error";
+ LOG(Archives, "LegacyWebArchive - Error parsing PropertyList from archive data - %s", cError);
+#endif
+ if (errorString)
+ CFRelease(errorString);
+ return false;
+ }
+
+ if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Archive property list is not the expected CFDictionary, aborting invalid WebArchive");
+ return false;
+ }
+
+ return extract(plist.get());
+}
+
+bool LegacyWebArchive::extract(CFDictionaryRef dictionary)
+{
+ ASSERT(dictionary);
+ if (!dictionary) {
+ LOG(Archives, "LegacyWebArchive - Null root CFDictionary, aborting invalid WebArchive");
+ return false;
+ }
+
+ CFDictionaryRef mainResourceDict = static_cast<CFDictionaryRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveMainResourceKey));
+ if (!mainResourceDict) {
+ LOG(Archives, "LegacyWebArchive - No main resource in archive, aborting invalid WebArchive");
+ return false;
+ }
+ if (CFGetTypeID(mainResourceDict) != CFDictionaryGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Main resource is not the expected CFDictionary, aborting invalid WebArchive");
+ return false;
+ }
+
+ setMainResource(createResource(mainResourceDict));
+ if (!mainResource()) {
+ LOG(Archives, "LegacyWebArchive - Failed to parse main resource from CFDictionary or main resource does not exist, aborting invalid WebArchive");
+ return false;
+ }
+
+ CFArrayRef subresourceArray = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveSubresourcesKey));
+ if (subresourceArray && CFGetTypeID(subresourceArray) != CFArrayGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Subresources is not the expected Array, aborting invalid WebArchive");
+ return false;
+ }
+
+ if (subresourceArray) {
+ CFIndex count = CFArrayGetCount(subresourceArray);
+ for (CFIndex i = 0; i < count; ++i) {
+ CFDictionaryRef subresourceDict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(subresourceArray, i));
+ if (CFGetTypeID(subresourceDict) != CFDictionaryGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Subresource is not expected CFDictionary, aborting invalid WebArchive");
+ return false;
+ }
+ addSubresource(createResource(subresourceDict));
+ }
+ }
+
+ CFArrayRef subframeArray = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveSubframeArchivesKey));
+ if (subframeArray && CFGetTypeID(subframeArray) != CFArrayGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Subframe archives is not the expected Array, aborting invalid WebArchive");
+ return false;
+ }
+
+ if (subframeArray) {
+ CFIndex count = CFArrayGetCount(subframeArray);
+ for (CFIndex i = 0; i < count; ++i) {
+ CFDictionaryRef subframeDict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(subframeArray, i));
+ if (CFGetTypeID(subframeDict) != CFDictionaryGetTypeID()) {
+ LOG(Archives, "LegacyWebArchive - Subframe array is not expected CFDictionary, aborting invalid WebArchive");
+ return false;
+ }
+
+ RefPtr<LegacyWebArchive> subframeArchive = create();
+ if (subframeArchive->extract(subframeDict))
+ addSubframeArchive(subframeArchive.release());
+ else
+ LOG(Archives, "LegacyWebArchive - Invalid subframe archive skipped");
+ }
+ }
+
+ return true;
+}
+
+RetainPtr<CFDataRef> LegacyWebArchive::rawDataRepresentation()
+{
+ RetainPtr<CFDictionaryRef> propertyList = createPropertyListRep(this);
+ if (!propertyList) {
+ LOG(Archives, "LegacyWebArchive - Failed to create property list for archive, returning no data");
+ return 0;
+ }
+
+ // FIXME: On Mac, WebArchives have been written out as Binary Property Lists until this change.
+ // Unless we jump through CFWriteStream hoops, they'll now be textual XML data. Is this okay?
+ RetainPtr<CFDataRef> plistData(AdoptCF, CFPropertyListCreateXMLData(0, propertyList.get()));
+ if (!plistData) {
+ LOG(Archives, "LegacyWebArchive - Failed to convert property list into raw data, returning no data");
+ return 0;
+ }
+
+ return plistData;
+}
+
+#if !PLATFORM(MAC)
+// FIXME: Is it possible to parse in a Cocoa-style resource response manually,
+// without NSKeyed(Un)Archiver, manipulating plists directly?
+// If so, the code that does it will go here.
+// In the meantime, Mac will continue to NSKeyed(Un)Archive the response as it always has
+ResourceResponse createResourceResponseFromMacArchivedData(CFDataRef responseData)
+{
+ return ResourceResponse();
+}
+
+RetainPtr<CFDataRef> propertyListDataFromResourceResponse(const ResourceResponse& response)
+{
+ // FIXME: Write out the "new" format described in ::createResourceResponseFromPropertyListData() up above
+ return 0;
+}
+#endif
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Node* node)
+{
+ ASSERT(node);
+ if (!node)
+ return create();
+
+ Document* document = node->document();
+ Frame* frame = document ? document->frame() : 0;
+ if (!frame)
+ return create();
+
+ Vector<Node*> nodeList;
+ String markupString = createMarkup(node, IncludeNode, &nodeList);
+ Node::NodeType nodeType = node->nodeType();
+ if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE)
+ markupString = frame->documentTypeString() + markupString;
+
+ return create(markupString, frame, nodeList);
+}
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Frame* frame)
+{
+ ASSERT(frame);
+
+ DocumentLoader* documentLoader = frame->loader()->documentLoader();
+
+ if (!documentLoader)
+ return 0;
+
+ Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
+
+ unsigned children = frame->tree()->childCount();
+ for (unsigned i = 0; i < children; ++i) {
+ RefPtr<LegacyWebArchive> childFrameArchive = create(frame->tree()->child(i));
+ if (childFrameArchive)
+ subframeArchives.append(childFrameArchive.release());
+ }
+
+ Vector<PassRefPtr<ArchiveResource> > subresources;
+ documentLoader->getSubresources(subresources);
+
+ return LegacyWebArchive::create(documentLoader->mainResource(), subresources, subframeArchives);
+}
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Range* range)
+{
+ if (!range)
+ return 0;
+
+ Node* startContainer = range->startContainer();
+ if (!startContainer)
+ return 0;
+
+ Document* document = startContainer->document();
+ if (!document)
+ return 0;
+
+ Frame* frame = document->frame();
+ if (!frame)
+ return 0;
+
+ Vector<Node*> nodeList;
+
+ // FIXME: This is always "for interchange". Is that right? See the previous method.
+ String markupString = frame->documentTypeString() + createMarkup(range, &nodeList, AnnotateForInterchange);
+
+ return create(markupString, frame, nodeList);
+}
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString, Frame* frame, Vector<Node*>& nodes)
+{
+ ASSERT(frame);
+
+ const ResourceResponse& response = frame->loader()->documentLoader()->response();
+ KURL responseURL = response.url();
+
+ // it's possible to have a response without a URL here
+ // <rdar://problem/5454935>
+ if (responseURL.isNull())
+ responseURL = KURL("");
+
+ PassRefPtr<ArchiveResource> mainResource = ArchiveResource::create(utf8Buffer(markupString), responseURL, response.mimeType(), "UTF-8", frame->tree()->name());
+
+ Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
+ Vector<PassRefPtr<ArchiveResource> > subresources;
+ HashSet<String> uniqueSubresources;
+
+ Vector<Node*>::iterator it = nodes.begin();
+ Vector<Node*>::iterator end = nodes.end();
+
+ for (; it != end; ++it) {
+ Frame* childFrame;
+ if (((*it)->hasTagName(HTMLNames::frameTag) || (*it)->hasTagName(HTMLNames::iframeTag) || (*it)->hasTagName(HTMLNames::objectTag)) &&
+ (childFrame = static_cast<HTMLFrameOwnerElement*>(*it)->contentFrame())) {
+ RefPtr<LegacyWebArchive> subframeArchive;
+ if (Document* document = childFrame->document())
+ subframeArchive = LegacyWebArchive::create(document);
+ else
+ subframeArchive = create(childFrame);
+
+ if (subframeArchive)
+ subframeArchives.append(subframeArchive);
+ else
+ LOG_ERROR("Unabled to archive subframe %s", childFrame->tree()->name().string().utf8().data());
+ } else {
+ Vector<KURL> subresourceURLs;
+ (*it)->getSubresourceURLs(subresourceURLs);
+
+ DocumentLoader* documentLoader = frame->loader()->documentLoader();
+ for (unsigned i = 0; i < subresourceURLs.size(); ++i) {
+ if (uniqueSubresources.contains(subresourceURLs[i].string()))
+ continue;
+ uniqueSubresources.add(subresourceURLs[i].string());
+ RefPtr<ArchiveResource> resource = documentLoader->subresource(subresourceURLs[i]);
+ if (resource)
+ subresources.append(resource.release());
+ else
+ // FIXME: should do something better than spew to console here
+ LOG_ERROR("Failed to archive subresource for %s", subresourceURLs[i].string().utf8().data());
+ }
+ }
+ }
+
+ return create(mainResource, subresources, subframeArchives);
+}
+
+PassRefPtr<LegacyWebArchive> LegacyWebArchive::createFromSelection(Frame* frame)
+{
+ if (!frame)
+ return 0;
+
+ RefPtr<Range> selectionRange = frame->selection()->toRange();
+ Vector<Node*> nodeList;
+ String markupString = frame->documentTypeString() + createMarkup(selectionRange.get(), &nodeList, AnnotateForInterchange);
+
+ RefPtr<LegacyWebArchive> archive = create(markupString, frame, nodeList);
+
+ if (!frame->isFrameSet())
+ return archive.release();
+
+ // Wrap the frameset document in an iframe so it can be pasted into
+ // another document (which will have a body or frameset of its own).
+ String iframeMarkup = String::format("<iframe frameborder=\"no\" marginwidth=\"0\" marginheight=\"0\" width=\"98%%\" height=\"98%%\" src=\"%s\"></iframe>",
+ frame->loader()->documentLoader()->response().url().string().utf8().data());
+ RefPtr<ArchiveResource> iframeResource = ArchiveResource::create(utf8Buffer(iframeMarkup), blankURL(), "text/html", "UTF-8", String());
+
+ Vector<PassRefPtr<ArchiveResource> > subresources;
+
+ Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
+ subframeArchives.append(archive);
+
+ archive = LegacyWebArchive::create(iframeResource.release(), subresources, subframeArchives);
+
+ return archive.release();
+}
+
+}
diff --git a/WebCore/loader/archive/cf/LegacyWebArchive.h b/WebCore/loader/archive/cf/LegacyWebArchive.h
new file mode 100644
index 0000000..70faba5
--- /dev/null
+++ b/WebCore/loader/archive/cf/LegacyWebArchive.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 LegacyWebArchive_h
+#define LegacyWebArchive_h
+
+#include "Archive.h"
+
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class Frame;
+class Node;
+class Range;
+
+class LegacyWebArchive : public Archive {
+public:
+ static PassRefPtr<LegacyWebArchive> create();
+ static PassRefPtr<LegacyWebArchive> create(SharedBuffer*);
+ static PassRefPtr<LegacyWebArchive> create(PassRefPtr<ArchiveResource> mainResource, Vector<PassRefPtr<ArchiveResource> >& subresources, Vector<PassRefPtr<LegacyWebArchive> >& subframeArchives);
+ static PassRefPtr<LegacyWebArchive> create(Node*);
+ static PassRefPtr<LegacyWebArchive> create(Frame*);
+ static PassRefPtr<LegacyWebArchive> createFromSelection(Frame* frame);
+ static PassRefPtr<LegacyWebArchive> create(Range*);
+ static PassRefPtr<LegacyWebArchive> create(const String& markupString, Frame*, Vector<Node*>& nodes);
+
+ RetainPtr<CFDataRef> rawDataRepresentation();
+
+private:
+ LegacyWebArchive();
+ bool init(SharedBuffer*);
+ bool extract(CFDictionaryRef);
+
+};
+
+ResourceResponse createResourceResponseFromMacArchivedData(CFDataRef);
+RetainPtr<CFDataRef> propertyListDataFromResourceResponse(const ResourceResponse&);
+
+}
+
+#endif // Archive
diff --git a/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm b/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm
new file mode 100644
index 0000000..b853a15
--- /dev/null
+++ b/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm
@@ -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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "LegacyWebArchive.h"
+
+namespace WebCore {
+
+static const NSString *LegacyWebArchiveResourceResponseKey = @"WebResourceResponse";
+
+// FIXME: Is it possible to parse in a Cocoa-style resource response manually,
+// without NSKeyed(Un)Archiver, manipulating plists directly?
+ResourceResponse createResourceResponseFromMacArchivedData(CFDataRef responseData)
+{
+ ASSERT(responseData);
+ if (!responseData)
+ return ResourceResponse();
+
+ NSURLResponse *response = nil;
+ NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:(NSData *)responseData];
+ @try {
+ id responseObject = [unarchiver decodeObjectForKey:LegacyWebArchiveResourceResponseKey];
+ if ([responseObject isKindOfClass:[NSURLResponse class]])
+ response = responseObject;
+ [unarchiver finishDecoding];
+ } @catch(id) {
+ response = nil;
+ }
+ [unarchiver release];
+
+ return ResourceResponse(response);
+}
+
+RetainPtr<CFDataRef> propertyListDataFromResourceResponse(const ResourceResponse& response)
+{
+ NSURLResponse *nsResponse = response.nsURLResponse();
+ if (!nsResponse)
+ return 0;
+
+ NSMutableData *responseData = (NSMutableData *)CFDataCreateMutable(0, 0);
+ NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:responseData];
+ [archiver encodeObject:nsResponse forKey:LegacyWebArchiveResourceResponseKey];
+ [archiver finishEncoding];
+ [archiver release];
+
+ return RetainPtr<CFDataRef>(AdoptCF, (CFDataRef)responseData);
+}
+
+}
diff --git a/WebCore/loader/icon/IconDatabase.cpp b/WebCore/loader/icon/IconDatabase.cpp
index 79250b8..a47fb08 100644
--- a/WebCore/loader/icon/IconDatabase.cpp
+++ b/WebCore/loader/icon/IconDatabase.cpp
@@ -41,6 +41,8 @@
#include "SQLiteStatement.h"
#include "SQLiteTransaction.h"
#include "SystemTime.h"
+#include <runtime/InitializeThreading.h>
+#include <wtf/MainThread.h>
#if PLATFORM(WIN_OS)
#include <windows.h>
@@ -63,6 +65,10 @@
#define IS_ICON_SYNC_THREAD() (m_syncThread == currentThread())
#define ASSERT_ICON_SYNC_THREAD() ASSERT(IS_ICON_SYNC_THREAD())
+#if PLATFORM(QT)
+#define CAN_THEME_URL_ICON
+#endif
+
namespace WebCore {
static IconDatabase* sharedIconDatabase = 0;
@@ -101,7 +107,7 @@ static IconDatabaseClient* defaultClient()
IconDatabase* iconDatabase()
{
if (!sharedIconDatabase) {
- initializeThreading();
+ JSC::initializeThreading();
sharedIconDatabase = new IconDatabase;
}
return sharedIconDatabase;
@@ -144,7 +150,7 @@ bool IconDatabase::open(const String& databasePath)
// Lock here as well as first thing in the thread so the thread doesn't actually commence until the createThread() call
// completes and m_syncThreadRunning is properly set
m_syncLock.lock();
- m_syncThread = createThread(IconDatabase::iconDatabaseSyncThreadStart, this);
+ m_syncThread = createThread(IconDatabase::iconDatabaseSyncThreadStart, this, "WebCore::IconDatabase");
m_syncLock.unlock();
if (!m_syncThread)
return false;
@@ -220,16 +226,14 @@ void IconDatabase::removeAllIcons()
wakeSyncThread();
}
-Image* IconDatabase::iconForPageURL(const String& pageURLOriginal, const IntSize& size, bool cache)
+Image* IconDatabase::iconForPageURL(const String& pageURLOriginal, const IntSize& size)
{
ASSERT_NOT_SYNC_THREAD();
-
- ASSERT(!pageURLOriginal.isNull());
- // pageURLOriginal can not be stored without being deep copied first.
+ // pageURLOriginal cannot be stored without being deep copied first.
// We should go our of our way to only copy it if we have to store it
- if (!isOpen())
+ if (!isOpen() || pageURLOriginal.isEmpty())
return defaultIcon(size);
MutexLocker locker(m_urlAndIconLock);
@@ -335,10 +339,14 @@ String IconDatabase::iconURLForPageURL(const String& pageURLOriginal)
return pageRecord->iconRecord() ? pageRecord->iconRecord()->iconURL().copy() : String();
}
-Image* IconDatabase::defaultIcon(const IntSize& size)
+#ifdef CAN_THEME_URL_ICON
+static inline void loadDefaultIconRecord(IconRecord* defaultIconRecord)
+{
+ defaultIconRecord->loadImageFromResource("urlIcon");
+}
+#else
+static inline void loadDefaultIconRecord(IconRecord* defaultIconRecord)
{
- ASSERT_NOT_SYNC_THREAD();
-
static const unsigned char defaultIconData[] = { 0x4D, 0x4D, 0x00, 0x2A, 0x00, 0x00, 0x03, 0x32, 0x80, 0x00, 0x20, 0x50, 0x38, 0x24, 0x16, 0x0D, 0x07, 0x84, 0x42, 0x61, 0x50, 0xB8,
0x64, 0x08, 0x18, 0x0D, 0x0A, 0x0B, 0x84, 0xA2, 0xA1, 0xE2, 0x08, 0x5E, 0x39, 0x28, 0xAF, 0x48, 0x24, 0xD3, 0x53, 0x9A, 0x37, 0x1D, 0x18, 0x0E, 0x8A, 0x4B, 0xD1, 0x38,
0xB0, 0x7C, 0x82, 0x07, 0x03, 0x82, 0xA2, 0xE8, 0x6C, 0x2C, 0x03, 0x2F, 0x02, 0x82, 0x41, 0xA1, 0xE2, 0xF8, 0xC8, 0x84, 0x68, 0x6D, 0x1C, 0x11, 0x0A, 0xB7, 0xFA, 0x91,
@@ -377,11 +385,19 @@ Image* IconDatabase::defaultIcon(const IntSize& size)
0x00, 0x00, 0x01, 0x52, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x0A,
0xFC, 0x80, 0x00, 0x00, 0x27, 0x10, 0x00, 0x0A, 0xFC, 0x80, 0x00, 0x00, 0x27, 0x10 };
- static RefPtr<SharedBuffer> defaultIconBuffer(new SharedBuffer(defaultIconData, sizeof(defaultIconData)));
+ static RefPtr<SharedBuffer> defaultIconBuffer(SharedBuffer::create(defaultIconData, sizeof(defaultIconData)));
+ defaultIconRecord->setImageData(defaultIconBuffer);
+}
+#endif
+
+Image* IconDatabase::defaultIcon(const IntSize& size)
+{
+ ASSERT_NOT_SYNC_THREAD();
+
if (!m_defaultIconRecord) {
- m_defaultIconRecord = new IconRecord("urlIcon");
- m_defaultIconRecord->setImageData(defaultIconBuffer);
+ m_defaultIconRecord = IconRecord::create("urlIcon");
+ loadDefaultIconRecord(m_defaultIconRecord.get());
}
return m_defaultIconRecord->image(size);
@@ -518,10 +534,10 @@ void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> dataOriginal,
MutexLocker locker(m_urlAndIconLock);
// If this icon was pending a read, remove it from that set because this new data should override what is on disk
- IconRecord* icon = m_iconURLToRecordMap.get(iconURL);
+ RefPtr<IconRecord> icon = m_iconURLToRecordMap.get(iconURL);
if (icon) {
MutexLocker locker(m_pendingReadingLock);
- m_iconsPendingReading.remove(icon);
+ m_iconsPendingReading.remove(icon.get());
} else
icon = getOrCreateIconRecord(iconURL);
@@ -537,6 +553,12 @@ void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> dataOriginal,
MutexLocker locker(m_pendingSyncLock);
m_iconsPendingSync.set(iconURL, icon->snapshot());
}
+
+ if (icon->hasOneRef()) {
+ ASSERT(icon->retainingPageURLs().isEmpty());
+ LOG(IconDatabase, "Icon for icon url %s is about to be destroyed - removing mapping for it", urlForLogging(icon->iconURL()).ascii().data());
+ m_iconURLToRecordMap.remove(icon->iconURL());
+ }
}
// Send notification out regarding all PageURLs that retain this icon
@@ -593,8 +615,7 @@ void IconDatabase::setIconURLForPageURL(const String& iconURLOriginal, const Str
RefPtr<IconRecord> iconRecord = pageRecord->iconRecord();
// Otherwise, set the new icon record for this page
- IconRecord* newIconRecord = getOrCreateIconRecord(iconURL);
- pageRecord->setIconRecord(newIconRecord);
+ pageRecord->setIconRecord(getOrCreateIconRecord(iconURL));
// If the current icon has only a single ref left, it is about to get wiped out.
// Remove it from the in-memory records and don't bother reading it in from disk anymore
@@ -611,7 +632,7 @@ void IconDatabase::setIconURLForPageURL(const String& iconURLOriginal, const Str
MutexLocker locker(m_pendingSyncLock);
m_pageURLsPendingSync.set(pageURL, pageRecord->snapshot());
- // If the icon is on it's last ref, mark it for deletion
+ // If the icon is on its last ref, mark it for deletion
if (iconRecord && iconRecord->hasOneRef())
m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true));
}
@@ -839,18 +860,18 @@ String IconDatabase::defaultDatabaseFilename()
}
// Unlike getOrCreatePageURLRecord(), getOrCreateIconRecord() does not mark the icon as "interested in import"
-IconRecord* IconDatabase::getOrCreateIconRecord(const String& iconURL)
+PassRefPtr<IconRecord> IconDatabase::getOrCreateIconRecord(const String& iconURL)
{
// Clients of getOrCreateIconRecord() are required to acquire the m_urlAndIconLock before calling this method
ASSERT(!m_urlAndIconLock.tryLock());
if (IconRecord* icon = m_iconURLToRecordMap.get(iconURL))
return icon;
-
- IconRecord* newIcon = new IconRecord(iconURL);
- m_iconURLToRecordMap.set(iconURL, newIcon);
-
- return newIcon;
+
+ RefPtr<IconRecord> newIcon = IconRecord::create(iconURL);
+ m_iconURLToRecordMap.set(iconURL, newIcon.get());
+
+ return newIcon.release();
}
// This method retrieves the existing PageURLRecord, or creates a new one and marks it as "interested in the import" for later notification
@@ -1205,8 +1226,8 @@ void IconDatabase::performURLImport()
IconRecord* currentIcon = pageRecord->iconRecord();
if (!currentIcon || currentIcon->iconURL() != iconURL) {
- currentIcon = getOrCreateIconRecord(iconURL);
- pageRecord->setIconRecord(currentIcon);
+ pageRecord->setIconRecord(getOrCreateIconRecord(iconURL));
+ currentIcon = pageRecord->iconRecord();
}
// Regardless, the time stamp from disk still takes precedence. Until we read this icon from disk, we didn't think we'd seen it before
@@ -1335,6 +1356,18 @@ void* IconDatabase::syncThreadMainLoop()
bool didAnyWork = true;
while (didAnyWork) {
+#ifdef ANDROID_FIX
+ // We should write the pending icons to the database before trying
+ // to read any requested icons to ensure that a requested icon has
+ // the correct data.
+ bool didWrite = writeToDatabase();
+ if (shouldStopThreadActivity())
+ break;
+
+ didAnyWork = readFromDatabase();
+ if (shouldStopThreadActivity())
+ break;
+#else
didAnyWork = readFromDatabase();
if (shouldStopThreadActivity())
break;
@@ -1342,6 +1375,8 @@ void* IconDatabase::syncThreadMainLoop()
bool didWrite = writeToDatabase();
if (shouldStopThreadActivity())
break;
+#endif
+
// Prune unretained icons after the first time we sync anything out to the database
// This way, pruning won't be the only operation we perform to the database by itself
@@ -1536,22 +1571,20 @@ bool IconDatabase::writeToDatabase()
}
for (unsigned i = 0; i < pageSnapshots.size(); ++i) {
- String iconURL = pageSnapshots[i].iconURL;
-
// If the icon URL is empty, this page is meant to be deleted
// ASSERTs are sanity checks to make sure the mappings exist if they should and don't if they shouldn't
if (pageSnapshots[i].iconURL.isEmpty())
removePageURLFromSQLDatabase(pageSnapshots[i].pageURL);
- else {
+ else
setIconURLForPageURLInSQLDatabase(pageSnapshots[i].iconURL, pageSnapshots[i].pageURL);
- }
LOG(IconDatabase, "Committed IconURL for PageURL %s to database", urlForLogging(pageSnapshots[i].pageURL).ascii().data());
}
syncTransaction.commit();
// Check to make sure there are no dangling PageURLs - If there are, we want to output one log message but not spam the console potentially every few seconds
- checkForDanglingPageURLs(false);
+ if (didAnyWork)
+ checkForDanglingPageURLs(false);
LOG(IconDatabase, "Updating the database took %.4f seconds", currentTime() - timeStamp);
@@ -1638,19 +1671,21 @@ void IconDatabase::pruneUnretainedIcons()
void IconDatabase::checkForDanglingPageURLs(bool pruneIfFound)
{
ASSERT_ICON_SYNC_THREAD();
-
- // We don't want to keep performing this check and reporting this error if it has already found danglers so we keep track
+
+ // This check can be relatively expensive so we don't do it in a release build unless the caller has asked us to prune any dangling
+ // entries. We also don't want to keep performing this check and reporting this error if it has already found danglers before so we
+ // keep track of whether we've found any. We skip the check in the release build pretending to have already found danglers already.
+#ifndef NDEBUG
+ static bool danglersFound = true;
+#else
static bool danglersFound = false;
-
- // However, if the caller wants us to prune the danglers, we will reset this flag and prune every time
- if (pruneIfFound)
- danglersFound = false;
-
- if (!danglersFound && SQLiteStatement(m_syncDB, "SELECT url FROM PageURL WHERE PageURL.iconID NOT IN (SELECT iconID FROM IconInfo) LIMIT 1;").returnsAtLeastOneResult()) {
+#endif
+
+ if ((pruneIfFound || !danglersFound) && SQLiteStatement(m_syncDB, "SELECT url FROM PageURL WHERE PageURL.iconID NOT IN (SELECT iconID FROM IconInfo) LIMIT 1;").returnsAtLeastOneResult()) {
danglersFound = true;
- LOG_ERROR("Dangling PageURL entries found");
+ LOG(IconDatabase, "Dangling PageURL entries found");
if (pruneIfFound && !m_syncDB.executeCommand("DELETE FROM PageURL WHERE iconID NOT IN (SELECT iconID FROM IconInfo);"))
- LOG_ERROR("Unable to prune dangling PageURLs");
+ LOG(IconDatabase, "Unable to prune dangling PageURLs");
}
}
@@ -1905,8 +1940,7 @@ PassRefPtr<SharedBuffer> IconDatabase::getImageDataForIconURLFromSQLDatabase(con
if (result == SQLResultRow) {
Vector<char> data;
m_getImageDataForIconURLStatement->getColumnBlobAsVector(0, data);
- imageData = new SharedBuffer;
- imageData->append(data.data(), data.size());
+ imageData = SharedBuffer::create(data.data(), data.size());
} else if (result != SQLResultDone)
LOG_ERROR("getImageDataForIconURLFromSQLDatabase failed for url %s", urlForLogging(iconURL).ascii().data());
diff --git a/WebCore/loader/icon/IconDatabase.h b/WebCore/loader/icon/IconDatabase.h
index ea48c16..4303ae1 100644
--- a/WebCore/loader/icon/IconDatabase.h
+++ b/WebCore/loader/icon/IconDatabase.h
@@ -32,13 +32,14 @@
#endif
#include "StringHash.h"
-#if ENABLE(ICONDATABASE)
-#include "Threading.h"
-#endif
#include "Timer.h"
+#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
#include <wtf/Noncopyable.h>
#include <wtf/OwnPtr.h>
+#if ENABLE(ICONDATABASE)
+#include <wtf/Threading.h>
+#endif
namespace WebCore {
@@ -74,7 +75,7 @@ public:
void removeAllIcons();
- Image* iconForPageURL(const String&, const IntSize&, bool cache = true);
+ Image* iconForPageURL(const String&, const IntSize&);
void readIconForPageURLFromDisk(const String&);
String iconURLForPageURL(const String&);
Image* defaultIcon(const IntSize&);
@@ -123,7 +124,7 @@ private:
HashSet<RefPtr<DocumentLoader> > m_loadersPendingDecision;
- IconRecord* m_defaultIconRecord;
+ RefPtr<IconRecord> m_defaultIconRecord;
#endif // ENABLE(ICONDATABASE)
// *** Any Thread ***
@@ -134,7 +135,7 @@ public:
#if ENABLE(ICONDATABASE)
private:
- IconRecord* getOrCreateIconRecord(const String& iconURL);
+ PassRefPtr<IconRecord> getOrCreateIconRecord(const String& iconURL);
PageURLRecord* getOrCreatePageURLRecord(const String& pageURL);
bool m_isEnabled;
diff --git a/WebCore/loader/icon/IconDatabaseNone.cpp b/WebCore/loader/icon/IconDatabaseNone.cpp
index 734e119..c76a2c4 100644
--- a/WebCore/loader/icon/IconDatabaseNone.cpp
+++ b/WebCore/loader/icon/IconDatabaseNone.cpp
@@ -99,7 +99,7 @@ void IconDatabase::readIconForPageURLFromDisk(const String&)
}
-Image* IconDatabase::iconForPageURL(const String& pageURL, const IntSize& size, bool cache)
+Image* IconDatabase::iconForPageURL(const String& pageURL, const IntSize& size)
{
return defaultIcon(size);
}
diff --git a/WebCore/loader/icon/IconFetcher.cpp b/WebCore/loader/icon/IconFetcher.cpp
new file mode 100644
index 0000000..efa7e14
--- /dev/null
+++ b/WebCore/loader/icon/IconFetcher.cpp
@@ -0,0 +1,236 @@
+/*
+ * 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 "IconFetcher.h"
+
+#include "Document.h"
+#include "Frame.h"
+#include "HTMLHeadElement.h"
+#include "HTMLLinkElement.h"
+#include "HTMLNames.h"
+#include "MIMETypeRegistry.h"
+#include "ResourceHandle.h"
+#include "ResourceRequest.h"
+#include "SharedBuffer.h"
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+struct IconLinkEntry {
+public:
+ enum IconType {
+ Unknown,
+ ICNS,
+ ICO,
+ };
+
+ IconLinkEntry(IconType type, const KURL& url)
+ : m_type(type)
+ , m_url(url)
+ {
+ }
+
+ IconType type() const { return m_type; }
+ const KURL& url() const { return m_url; }
+
+ SharedBuffer* buffer()
+ {
+ if (!m_buffer)
+ m_buffer = SharedBuffer::create();
+
+ return m_buffer.get();
+ }
+
+private:
+ RefPtr<SharedBuffer> m_buffer;
+ IconType m_type;
+ KURL m_url;
+};
+
+#if PLATFORM(MAC)
+static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::ICNS;
+#elif PLATFORM(WIN)
+static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::ICO;
+#else
+static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::Unknown;
+#endif
+
+static void parseIconLink(HTMLLinkElement* link, Vector<IconLinkEntry>& entries)
+{
+ // FIXME: Parse the size attribute too.
+
+ IconLinkEntry::IconType type = IconLinkEntry::Unknown;
+ const KURL& url = link->href();
+
+ // Try to determine the file type.
+ String path = url.path();
+
+ int pos = path.reverseFind('.');
+ if (pos >= 0) {
+ String extension = path.substring(pos + 1);
+ if (equalIgnoringCase(extension, "icns"))
+ type = IconLinkEntry::ICNS;
+ else if (equalIgnoringCase(extension, "ico"))
+ type = IconLinkEntry::ICO;
+ }
+
+ entries.append(IconLinkEntry(type, url));
+}
+
+PassRefPtr<IconFetcher> IconFetcher::create(Frame* frame, IconFetcherClient* client)
+{
+ Document* document = frame->document();
+ if (!document)
+ return 0;
+
+ HTMLHeadElement* head = document->head();
+ if (!head)
+ return 0;
+
+ Vector<IconLinkEntry> entries;
+
+ for (Node* n = head; n; n = n->traverseNextNode()) {
+ if (!n->hasTagName(linkTag))
+ continue;
+
+ HTMLLinkElement* link = static_cast<HTMLLinkElement*>(n);
+ if (!link->isIcon())
+ continue;
+
+ parseIconLink(link, entries);
+ }
+
+ if (entries.isEmpty())
+ return 0;
+
+ // Check if any of the entries have the same type as the native icon type.
+
+ // FIXME: This should be way more sophisticated, and handle conversion
+ // of multisize formats for example.
+ for (unsigned i = 0; i < entries.size(); i++) {
+ const IconLinkEntry& entry = entries[i];
+ if (entry.type() == NativeIconType) {
+ RefPtr<IconFetcher> iconFetcher = adoptRef(new IconFetcher(frame, client));
+
+ iconFetcher->m_entries.append(entry);
+ iconFetcher->loadEntry();
+
+ return iconFetcher.release();
+ }
+ }
+
+ return 0;
+}
+
+IconFetcher::IconFetcher(Frame* frame, IconFetcherClient* client)
+ : m_frame(frame)
+ , m_client(client)
+ , m_currentEntry(0)
+{
+}
+
+IconFetcher::~IconFetcher()
+{
+ cancel();
+}
+
+void IconFetcher::cancel()
+{
+ if (m_handle)
+ m_handle->cancel();
+}
+
+PassRefPtr<SharedBuffer> IconFetcher::createIcon()
+{
+ ASSERT(!m_entries.isEmpty());
+
+ // For now, just return the data of the first entry.
+ return m_entries.first().buffer();
+}
+
+
+void IconFetcher::loadEntry()
+{
+ ASSERT(m_currentEntry < m_entries.size());
+ ASSERT(!m_handle);
+
+ m_handle = ResourceHandle::create(m_entries[m_currentEntry].url(), this, m_frame, false, false);
+}
+
+void IconFetcher::loadFailed()
+{
+ m_handle = 0;
+
+ m_client->finishedFetchingIcon(0);
+}
+
+void IconFetcher::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response)
+{
+ ASSERT(m_handle == handle);
+
+ int statusCode = response.httpStatusCode() / 100;
+ if (statusCode == 4 || statusCode == 5) {
+ loadFailed();
+ return;
+ }
+}
+
+void IconFetcher::didReceiveData(ResourceHandle* handle, const char* data, int length, int lengthReceived)
+{
+ ASSERT(m_handle == handle);
+
+ m_entries[m_currentEntry].buffer()->append(data, length);
+}
+
+void IconFetcher::didFinishLoading(ResourceHandle* handle)
+{
+ ASSERT(m_handle == handle);
+
+ if (m_currentEntry == m_entries.size() - 1) {
+ // We finished loading, create the icon
+ RefPtr<SharedBuffer> iconData = createIcon();
+
+ m_client->finishedFetchingIcon(iconData.release());
+ return;
+ }
+
+ // Load the next entry
+ m_currentEntry++;
+
+ loadEntry();
+}
+
+void IconFetcher::didFail(ResourceHandle* handle, const ResourceError&)
+{
+ ASSERT(m_handle == handle);
+
+ loadFailed();
+}
+
+
+} // namespace WebCore
diff --git a/WebCore/loader/icon/IconFetcher.h b/WebCore/loader/icon/IconFetcher.h
new file mode 100644
index 0000000..5327693
--- /dev/null
+++ b/WebCore/loader/icon/IconFetcher.h
@@ -0,0 +1,78 @@
+/*
+ * 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 IconFetcher_h
+#define IconFetcher_h
+
+#include <wtf/RefCounted.h>
+#include <wtf/Forward.h>
+#include <wtf/Vector.h>
+
+#include "ResourceHandleClient.h"
+
+namespace WebCore {
+
+class Frame;
+struct IconLinkEntry;
+class ResourceHandle;
+class SharedBuffer;
+
+class IconFetcherClient {
+public:
+ virtual void finishedFetchingIcon(PassRefPtr<SharedBuffer> iconData) = 0;
+
+ virtual ~IconFetcherClient() { }
+};
+
+class IconFetcher : public RefCounted<IconFetcher>, ResourceHandleClient {
+public:
+ static PassRefPtr<IconFetcher> create(Frame*, IconFetcherClient*);
+ ~IconFetcher();
+
+ void cancel();
+
+private:
+ IconFetcher(Frame*, IconFetcherClient*);
+ void loadEntry();
+ void loadFailed();
+
+ PassRefPtr<SharedBuffer> createIcon();
+
+ 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&);
+
+ Frame* m_frame;
+ IconFetcherClient* m_client;
+
+ unsigned m_currentEntry;
+ RefPtr<ResourceHandle> m_handle;
+ Vector<IconLinkEntry> m_entries;
+};
+
+} // namespace WebCore
+
+#endif // IconFetcher_h
diff --git a/WebCore/loader/icon/IconLoader.h b/WebCore/loader/icon/IconLoader.h
index aba70b4..a7194d8 100644
--- a/WebCore/loader/icon/IconLoader.h
+++ b/WebCore/loader/icon/IconLoader.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006, 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
@@ -26,15 +26,16 @@
#ifndef IconLoader_h
#define IconLoader_h
-#include "KURL.h"
#include "SubresourceLoaderClient.h"
#include <memory>
+#include <wtf/Forward.h>
#include <wtf/Noncopyable.h>
-#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
namespace WebCore {
class Frame;
+class KURL;
class SharedBuffer;
class IconLoader : private SubresourceLoaderClient, Noncopyable {
diff --git a/WebCore/loader/icon/IconRecord.cpp b/WebCore/loader/icon/IconRecord.cpp
index 8b3d5f9..f070cc9 100644
--- a/WebCore/loader/icon/IconRecord.cpp
+++ b/WebCore/loader/icon/IconRecord.cpp
@@ -66,12 +66,12 @@ void IconRecord::setImageData(PassRefPtr<SharedBuffer> data)
{
// It's okay to delete the raw image here. Any existing clients using this icon will be
// managing an image that was created with a copy of this raw image data.
- m_image.set(new BitmapImage());
+ m_image = BitmapImage::create();
// Copy the provided data into the buffer of the new Image object.
if (!m_image->setData(data, true)) {
LOG(IconDatabase, "Manual image data for iconURL '%s' FAILED - it was probably invalid image data", m_iconURL.ascii().data());
- m_image.set(0);
+ m_image.clear();
}
m_dataSet = true;
@@ -82,7 +82,7 @@ void IconRecord::loadImageFromResource(const char* resource)
if (!resource)
return;
- m_image.set(Image::loadPlatformResource(resource));
+ m_image = Image::loadPlatformResource(resource);
m_dataSet = true;
}
diff --git a/WebCore/loader/icon/IconRecord.h b/WebCore/loader/icon/IconRecord.h
index c6da7a6..aaea787 100644
--- a/WebCore/loader/icon/IconRecord.h
+++ b/WebCore/loader/icon/IconRecord.h
@@ -67,7 +67,10 @@ public:
class IconRecord : public RefCounted<IconRecord> {
friend class PageURLRecord;
public:
- IconRecord(const String& url);
+ static PassRefPtr<IconRecord> create(const String& url)
+ {
+ return adoptRef(new IconRecord(url));
+ }
~IconRecord();
time_t getTimestamp() { return m_stamp; }
@@ -85,10 +88,13 @@ public:
const HashSet<String>& retainingPageURLs() { return m_retainingPageURLs; }
IconSnapshot snapshot(bool forDeletion = false) const;
+
private:
+ IconRecord(const String& url);
+
String m_iconURL;
time_t m_stamp;
- OwnPtr<Image> m_image;
+ RefPtr<Image> m_image;
HashSet<String> m_retainingPageURLs;
diff --git a/WebCore/loader/loader.cpp b/WebCore/loader/loader.cpp
index 0c7545f..6221a6a 100644
--- a/WebCore/loader/loader.cpp
+++ b/WebCore/loader/loader.cpp
@@ -3,7 +3,7 @@
Copyright (C) 2001 Dirk Mueller (mueller@kde.org)
Copyright (C) 2002 Waldo Bastian (bastian@kde.org)
Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
- Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -27,6 +27,7 @@
#include "Cache.h"
#include "CachedImage.h"
#include "CachedResource.h"
+#include "CString.h"
#include "DocLoader.h"
#include "Frame.h"
#include "FrameLoader.h"
@@ -35,225 +36,453 @@
#include "ResourceHandle.h"
#include "ResourceRequest.h"
#include "ResourceResponse.h"
+#include "SecurityOrigin.h"
#include "SubresourceLoader.h"
#include <wtf/Assertions.h>
#include <wtf/Vector.h>
+#define REQUEST_MANAGEMENT_ENABLED 0
+#define REQUEST_DEBUG 0
+
namespace WebCore {
+#if REQUEST_MANAGEMENT_ENABLED
+// Match the parallel connection count used by the networking layer
+// FIXME should not hardcode something like this
+static const unsigned maxRequestsInFlightPerHost = 4;
+// Having a limit might still help getting more important resources first
+static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20;
+#else
+static const unsigned maxRequestsInFlightPerHost = 10000;
+static const unsigned maxRequestsInFlightForNonHTTPProtocols = 10000;
+#endif
+
+
Loader::Loader()
+ : m_nonHTTPProtocolHost(AtomicString(), maxRequestsInFlightForNonHTTPProtocols)
+ , m_requestTimer(this, &Loader::requestTimerFired)
{
- m_requestsPending.setAutoDelete(true);
}
Loader::~Loader()
+{
+ ASSERT_NOT_REACHED();
+}
+
+Loader::Priority Loader::determinePriority(const CachedResource* resource) const
+{
+#if REQUEST_MANAGEMENT_ENABLED
+ switch (resource->type()) {
+ case CachedResource::CSSStyleSheet:
+#if ENABLE(XSLT)
+ case CachedResource::XSLStyleSheet:
+#endif
+#if ENABLE(XBL)
+ case CachedResource::XBL:
+#endif
+ return High;
+ case CachedResource::Script:
+ case CachedResource::FontResource:
+ return Medium;
+ case CachedResource::ImageResource:
+ return Low;
+ }
+ ASSERT_NOT_REACHED();
+ return Low;
+#else
+ return High;
+#endif
+}
+
+void Loader::load(DocLoader* docLoader, CachedResource* resource, bool incremental, bool skipCanLoadCheck, bool sendResourceLoadCallbacks)
{
- deleteAllValues(m_requestsLoading);
+ ASSERT(docLoader);
+ Request* request = new Request(docLoader, resource, incremental, skipCanLoadCheck, sendResourceLoadCallbacks);
+
+ Host* host;
+ KURL url(resource->url());
+ bool isHTTP = url.protocolIs("http") || url.protocolIs("https");
+ if (isHTTP) {
+ AtomicString hostName = url.host();
+ host = m_hosts.get(hostName.impl());
+ if (!host) {
+ host = new Host(hostName, maxRequestsInFlightPerHost);
+ m_hosts.add(hostName.impl(), host);
+ }
+ } else
+ host = &m_nonHTTPProtocolHost;
+
+ bool hadRequests = host->hasRequests();
+ Priority priority = determinePriority(resource);
+ host->addRequest(request, priority);
+ docLoader->incrementRequestCount();
+
+ if (priority > Low || !isHTTP || !hadRequests) {
+ // Try to request important resources immediately
+ host->servePendingRequests(priority);
+ } else {
+ // Handle asynchronously so early low priority requests don't get scheduled before later high priority ones
+ scheduleServePendingRequests();
+ }
+}
+
+void Loader::scheduleServePendingRequests()
+{
+ if (!m_requestTimer.isActive())
+ m_requestTimer.startOneShot(0);
}
-void Loader::load(DocLoader* dl, CachedResource* object, bool incremental, bool skipCanLoadCheck, bool sendResourceLoadCallbacks)
+void Loader::requestTimerFired(Timer<Loader>*)
{
- ASSERT(dl);
- Request* req = new Request(dl, object, incremental, skipCanLoadCheck, sendResourceLoadCallbacks);
- m_requestsPending.append(req);
- dl->incrementRequestCount();
servePendingRequests();
}
-void Loader::servePendingRequests()
+void Loader::servePendingRequests(Priority minimumPriority)
{
- while (!m_requestsPending.isEmpty()) {
- // get the first pending request
- Request* req = m_requestsPending.take(0);
- DocLoader* dl = req->docLoader();
-#ifndef ANDROID_PRELOAD_CHANGES
- dl->decrementRequestCount();
-#endif
+ m_requestTimer.stop();
+
+ m_nonHTTPProtocolHost.servePendingRequests(minimumPriority);
+
+ Vector<Host*> hostsToServe;
+ copyValuesToVector(m_hosts, hostsToServe);
+ for (unsigned n = 0; n < hostsToServe.size(); ++n) {
+ Host* host = hostsToServe[n];
+ if (host->hasRequests())
+ host->servePendingRequests(minimumPriority);
+ else if (!host->processingResource()){
+ AtomicString name = host->name();
+ delete host;
+ m_hosts.remove(name.impl());
+ }
+ }
+}
+
+void Loader::cancelRequests(DocLoader* docLoader)
+{
+ if (m_nonHTTPProtocolHost.hasRequests())
+ m_nonHTTPProtocolHost.cancelRequests(docLoader);
+
+ Vector<Host*> hostsToCancel;
+ copyValuesToVector(m_hosts, hostsToCancel);
+ for (unsigned n = 0; n < hostsToCancel.size(); ++n) {
+ Host* host = hostsToCancel[n];
+ if (host->hasRequests())
+ host->cancelRequests(docLoader);
+ }
- ResourceRequest request(req->cachedResource()->url());
-#ifdef ANDROID
- request.setCachedResource(req->cachedResource());
-#endif
+ scheduleServePendingRequests();
+
+ if (docLoader->loadInProgress())
+ ASSERT(docLoader->requestCount() == 1);
+ else
+ ASSERT(docLoader->requestCount() == 0);
+}
+
+Loader::Host::Host(const AtomicString& name, unsigned maxRequestsInFlight)
+ : m_name(name)
+ , m_maxRequestsInFlight(maxRequestsInFlight)
+ , m_processingResource(false)
+{
+}
- if (!req->cachedResource()->accept().isEmpty())
- request.setHTTPAccept(req->cachedResource()->accept());
+Loader::Host::~Host()
+{
+ ASSERT(m_requestsLoading.isEmpty());
+ for (unsigned p = 0; p <= High; p++)
+ ASSERT(m_requestsPending[p].isEmpty());
+}
+
+void Loader::Host::addRequest(Request* request, Priority priority)
+{
+ m_requestsPending[priority].append(request);
+}
+
+bool Loader::Host::hasRequests() const
+{
+ if (!m_requestsLoading.isEmpty())
+ return true;
+ for (unsigned p = 0; p <= High; p++) {
+ if (!m_requestsPending[p].isEmpty())
+ return true;
+ }
+ return false;
+}
- KURL r = dl->doc()->url();
- if (r.protocol().startsWith("http") && r.path().isEmpty())
- r.setPath("/");
- request.setHTTPReferrer(r.string());
- DeprecatedString domain = r.host();
- if (dl->doc()->isHTMLDocument())
- domain = static_cast<HTMLDocument*>(dl->doc())->domain().deprecatedString();
-
- RefPtr<SubresourceLoader> loader = SubresourceLoader::create(dl->doc()->frame(),
- this, request, req->shouldSkipCanLoadCheck(), req->sendResourceLoadCallbacks());
+void Loader::Host::servePendingRequests(Loader::Priority minimumPriority)
+{
+ bool serveMore = true;
+ for (int priority = High; priority >= minimumPriority && serveMore; --priority)
+ servePendingRequests(m_requestsPending[priority], serveMore);
+}
- if (loader) {
- m_requestsLoading.add(loader.release(), req);
-#ifdef ANDROID_PRELOAD_CHANGES
- req->cachedResource()->setRequestedFromNetworkingLayer();
- } else {
- dl->decrementRequestCount();
- dl->setLoadInProgress(true);
- req->cachedResource()->error();
- dl->setLoadInProgress(false);
-
- delete req;
+void Loader::Host::servePendingRequests(RequestQueue& requestsPending, bool& serveLowerPriority)
+{
+ while (!requestsPending.isEmpty()) {
+ Request* request = requestsPending.first();
+ DocLoader* docLoader = request->docLoader();
+ bool resourceIsCacheValidator = request->cachedResource()->isCacheValidator();
+ // If the document is fully parsed and there are no pending stylesheets there won't be any more
+ // resources that we would want to push to the front of the queue. Just hand off the remaining resources
+ // to the networking layer.
+ bool parsedAndStylesheetsKnown = !docLoader->doc()->parsing() && docLoader->doc()->haveStylesheetsLoaded();
+ if (!parsedAndStylesheetsKnown && !resourceIsCacheValidator && m_requestsLoading.size() >= m_maxRequestsInFlight) {
+ serveLowerPriority = false;
+ return;
}
-#else
- dl->incrementRequestCount();
- break;
+ requestsPending.removeFirst();
+
+ ResourceRequest resourceRequest(request->cachedResource()->url());
+#ifdef ANDROID
+ resourceRequest.setCachedResource(request->cachedResource());
+#endif
+
+ if (!request->cachedResource()->accept().isEmpty())
+ resourceRequest.setHTTPAccept(request->cachedResource()->accept());
+
+ KURL referrer = docLoader->doc()->url();
+ if ((referrer.protocolIs("http") || referrer.protocolIs("https")) && referrer.path().isEmpty())
+ referrer.setPath("/");
+ resourceRequest.setHTTPReferrer(referrer.string());
+ FrameLoader::addHTTPOriginIfNeeded(resourceRequest, docLoader->doc()->securityOrigin()->toString());
+
+ if (resourceIsCacheValidator) {
+ CachedResource* resourceToRevalidate = request->cachedResource()->resourceToRevalidate();
+ ASSERT(resourceToRevalidate->canUseCacheValidator());
+ ASSERT(resourceToRevalidate->isLoaded());
+ const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified");
+ const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag");
+ if (!lastModified.isEmpty() || !eTag.isEmpty()) {
+ if (docLoader->cachePolicy() == CachePolicyReload || docLoader->cachePolicy() == CachePolicyRefresh)
+ resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
+ if (!lastModified.isEmpty())
+ resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
+ if (!eTag.isEmpty())
+ resourceRequest.setHTTPHeaderField("If-None-Match", eTag);
+ }
}
- dl->setLoadInProgress(true);
- req->cachedResource()->error();
- dl->setLoadInProgress(false);
-
- delete req;
+ RefPtr<SubresourceLoader> loader = SubresourceLoader::create(docLoader->doc()->frame(),
+ this, resourceRequest, request->shouldSkipCanLoadCheck(), request->sendResourceLoadCallbacks());
+ if (loader) {
+ m_requestsLoading.add(loader.release(), request);
+ request->cachedResource()->setRequestedFromNetworkingLayer();
+#if REQUEST_DEBUG
+ printf("HOST %s COUNT %d LOADING %s\n", resourceRequest.url().host().latin1().data(), m_requestsLoading.size(), request->cachedResource()->url().latin1().data());
#endif
+ } else {
+ docLoader->decrementRequestCount();
+ docLoader->setLoadInProgress(true);
+ request->cachedResource()->error();
+ docLoader->setLoadInProgress(false);
+ delete request;
+ }
}
}
-void Loader::didFinishLoading(SubresourceLoader* loader)
+void Loader::Host::didFinishLoading(SubresourceLoader* loader)
{
RequestMap::iterator i = m_requestsLoading.find(loader);
if (i == m_requestsLoading.end())
return;
+
+ m_processingResource = true;
- Request* req = i->second;
+ Request* request = i->second;
m_requestsLoading.remove(i);
- DocLoader* docLoader = req->docLoader();
- if (!req->isMultipart())
+ DocLoader* docLoader = request->docLoader();
+ if (!request->isMultipart())
docLoader->decrementRequestCount();
- CachedResource* object = req->cachedResource();
+ CachedResource* resource = request->cachedResource();
+ ASSERT(!resource->resourceToRevalidate());
- docLoader->setLoadInProgress(true);
- object->data(loader->resourceData(), true);
- docLoader->setLoadInProgress(false);
- object->finish();
+ // If we got a 4xx response, we're pretending to have received a network
+ // error, so we can't send the successful data() and finish() callbacks.
+ if (!resource->errorOccurred()) {
+ docLoader->setLoadInProgress(true);
+ resource->data(loader->resourceData(), true);
+ resource->finish();
+ }
+
+ delete request;
- delete req;
+ docLoader->setLoadInProgress(false);
-#ifdef ANDROID_PRELOAD_CHANGES
docLoader->checkForPendingPreloads();
-#endif
+#if REQUEST_DEBUG
+ KURL u(resource->url());
+ printf("HOST %s COUNT %d RECEIVED %s\n", u.host().latin1().data(), m_requestsLoading.size(), resource->url().latin1().data());
+#endif
servePendingRequests();
+
+ m_processingResource = false;
}
-void Loader::didFail(SubresourceLoader* loader, const ResourceError&)
+void Loader::Host::didFail(SubresourceLoader* loader, const ResourceError&)
{
didFail(loader);
}
-void Loader::didFail(SubresourceLoader* loader, bool cancelled)
+void Loader::Host::didFail(SubresourceLoader* loader, bool cancelled)
{
+ loader->clearClient();
+
RequestMap::iterator i = m_requestsLoading.find(loader);
if (i == m_requestsLoading.end())
return;
- Request* req = i->second;
+ m_processingResource = true;
+
+ Request* request = i->second;
m_requestsLoading.remove(i);
- DocLoader* docLoader = req->docLoader();
- if (!req->isMultipart())
+ DocLoader* docLoader = request->docLoader();
+ if (!request->isMultipart())
docLoader->decrementRequestCount();
- CachedResource* object = req->cachedResource();
+ CachedResource* resource = request->cachedResource();
+
+ if (resource->resourceToRevalidate())
+ cache()->revalidationFailed(resource);
if (!cancelled) {
docLoader->setLoadInProgress(true);
- object->error();
+ resource->error();
}
docLoader->setLoadInProgress(false);
-#ifdef ANDROID_PRELOAD_CHANGES
- if (cancelled || !object->isPreloaded())
-#endif
- cache()->remove(object);
-
- delete req;
+ if (cancelled || !resource->isPreloaded())
+ cache()->remove(resource);
+
+ delete request;
-#ifdef ANDROID_PRELOAD_CHANGES
docLoader->checkForPendingPreloads();
-#endif
servePendingRequests();
+
+ m_processingResource = false;
}
-void Loader::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response)
+void Loader::Host::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response)
{
- Request* req = m_requestsLoading.get(loader);
+ Request* request = m_requestsLoading.get(loader);
// FIXME: This is a workaround for <rdar://problem/5236843>
// If a load starts while the frame is still in the provisional state
// (this can be the case when loading the user style sheet), committing the load then causes all
- // requests to be removed from the m_requestsLoading map. This means that req might be null here.
+ // requests to be removed from the m_requestsLoading map. This means that request might be null here.
// In that case we just return early.
- // ASSERT(req);
- if (!req)
+ // ASSERT(request);
+ if (!request)
return;
- req->cachedResource()->setResponse(response);
+
+ CachedResource* resource = request->cachedResource();
+
+ if (resource->isCacheValidator()) {
+ if (response.httpStatusCode() == 304) {
+ // 304 Not modified / Use local copy
+ m_requestsLoading.remove(loader);
+ request->docLoader()->decrementRequestCount();
+
+ // Existing resource is ok, just use it updating the expiration time.
+ cache()->revalidationSucceeded(resource, response);
+
+ if (request->docLoader()->frame())
+ request->docLoader()->frame()->loader()->checkCompleted();
+
+ delete request;
+
+ servePendingRequests();
+ return;
+ }
+ // Did not get 304 response, continue as a regular resource load.
+ cache()->revalidationFailed(resource);
+ }
+
+ resource->setResponse(response);
String encoding = response.textEncodingName();
if (!encoding.isNull())
- req->cachedResource()->setEncoding(encoding);
+ request->cachedResource()->setEncoding(encoding);
- if (req->isMultipart()) {
- ASSERT(req->cachedResource()->isImage());
- static_cast<CachedImage*>(req->cachedResource())->clear();
- if (req->docLoader()->frame())
- req->docLoader()->frame()->loader()->checkCompleted();
+ if (request->isMultipart()) {
+ ASSERT(request->cachedResource()->isImage());
+ static_cast<CachedImage*>(request->cachedResource())->clear();
+ if (request->docLoader()->frame())
+ request->docLoader()->frame()->loader()->checkCompleted();
} else if (response.isMultipart()) {
- req->setIsMultipart(true);
+ request->setIsMultipart(true);
// We don't count multiParts in a DocLoader's request count
- req->docLoader()->decrementRequestCount();
+ request->docLoader()->decrementRequestCount();
// If we get a multipart response, we must have a handle
ASSERT(loader->handle());
- if (!req->cachedResource()->isImage())
+ if (!request->cachedResource()->isImage())
loader->handle()->cancel();
}
}
-void Loader::didReceiveData(SubresourceLoader* loader, const char* data, int size)
+void Loader::Host::didReceiveData(SubresourceLoader* loader, const char* data, int size)
{
Request* request = m_requestsLoading.get(loader);
if (!request)
return;
- CachedResource* object = request->cachedResource();
+ CachedResource* resource = request->cachedResource();
+ ASSERT(!resource->isCacheValidator());
+
+ if (resource->errorOccurred())
+ return;
+
+ m_processingResource = true;
+
+ if (resource->response().httpStatusCode() / 100 == 4) {
+ // Treat a 4xx response like a network error.
+ resource->error();
+ m_processingResource = false;
+ return;
+ }
// Set the data.
if (request->isMultipart()) {
// The loader delivers the data in a multipart section all at once, send eof.
// The resource data will change as the next part is loaded, so we need to make a copy.
- SharedBuffer* copiedData = new SharedBuffer(data, size);
- object->data(copiedData, true);
+ RefPtr<SharedBuffer> copiedData = SharedBuffer::create(data, size);
+ resource->data(copiedData.release(), true);
} else if (request->isIncremental())
- object->data(loader->resourceData(), false);
+ resource->data(loader->resourceData(), false);
+
+ m_processingResource = false;
}
-
-void Loader::cancelRequests(DocLoader* dl)
+
+void Loader::Host::cancelPendingRequests(RequestQueue& requestsPending, DocLoader* docLoader)
{
- DeprecatedPtrListIterator<Request> pIt(m_requestsPending);
- while (pIt.current()) {
- if (pIt.current()->docLoader() == dl) {
- cache()->remove(pIt.current()->cachedResource());
- m_requestsPending.remove(pIt);
- dl->decrementRequestCount();
+ RequestQueue remaining;
+ RequestQueue::iterator end = requestsPending.end();
+ for (RequestQueue::iterator it = requestsPending.begin(); it != end; ++it) {
+ Request* request = *it;
+ if (request->docLoader() == docLoader) {
+ cache()->remove(request->cachedResource());
+ delete request;
+ docLoader->decrementRequestCount();
} else
- ++pIt;
+ remaining.append(request);
}
+ requestsPending.swap(remaining);
+}
+
+void Loader::Host::cancelRequests(DocLoader* docLoader)
+{
+ for (unsigned p = 0; p <= High; p++)
+ cancelPendingRequests(m_requestsPending[p], docLoader);
Vector<SubresourceLoader*, 256> loadersToCancel;
RequestMap::iterator end = m_requestsLoading.end();
for (RequestMap::iterator i = m_requestsLoading.begin(); i != end; ++i) {
Request* r = i->second;
- if (r->docLoader() == dl)
+ if (r->docLoader() == docLoader)
loadersToCancel.append(i->first.get());
}
@@ -261,11 +490,6 @@ void Loader::cancelRequests(DocLoader* dl)
SubresourceLoader* loader = loadersToCancel[i];
didFail(loader, true);
}
-
- if (dl->loadInProgress())
- ASSERT(dl->requestCount() == 1);
- else
- ASSERT(dl->requestCount() == 0);
}
} //namespace WebCore
diff --git a/WebCore/loader/loader.h b/WebCore/loader/loader.h
index 7ad47f8..c51374c 100644
--- a/WebCore/loader/loader.h
+++ b/WebCore/loader/loader.h
@@ -1,7 +1,7 @@
/*
Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
- Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
+ Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
@@ -22,9 +22,14 @@
#ifndef loader_h
#define loader_h
-#include "DeprecatedPtrList.h"
+#include "AtomicString.h"
+#include "AtomicStringImpl.h"
+#include "PlatformString.h"
#include "SubresourceLoaderClient.h"
+#include "Timer.h"
+#include <wtf/Deque.h>
#include <wtf/HashMap.h>
+#include <wtf/Noncopyable.h>
namespace WebCore {
@@ -32,7 +37,7 @@ namespace WebCore {
class DocLoader;
class Request;
- class Loader : private SubresourceLoaderClient {
+ class Loader : Noncopyable {
public:
Loader();
~Loader();
@@ -40,19 +45,51 @@ namespace WebCore {
void load(DocLoader*, CachedResource*, bool incremental = true, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true);
void cancelRequests(DocLoader*);
+
+ enum Priority { Low, Medium, High };
+ void servePendingRequests(Priority minimumPriority = Low);
private:
- virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&);
- virtual void didReceiveData(SubresourceLoader*, const char*, int);
- virtual void didFinishLoading(SubresourceLoader*);
- virtual void didFail(SubresourceLoader*, const ResourceError&);
- void didFail(SubresourceLoader*, bool cancelled = false);
+ Priority determinePriority(const CachedResource*) const;
+ void scheduleServePendingRequests();
+
+ void requestTimerFired(Timer<Loader>*);
- void servePendingRequests();
-
- DeprecatedPtrList<Request> m_requestsPending;
- typedef HashMap<RefPtr<SubresourceLoader>, Request*> RequestMap;
- RequestMap m_requestsLoading;
+ class Host : private SubresourceLoaderClient {
+ public:
+ Host(const AtomicString& name, unsigned maxRequestsInFlight);
+ ~Host();
+
+ const AtomicString& name() const { return m_name; }
+ void addRequest(Request*, Priority);
+ void servePendingRequests(Priority minimumPriority = Low);
+ void cancelRequests(DocLoader*);
+ bool hasRequests() const;
+ bool processingResource() const { return m_processingResource; }
+
+ private:
+ virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&);
+ virtual void didReceiveData(SubresourceLoader*, const char*, int);
+ virtual void didFinishLoading(SubresourceLoader*);
+ virtual void didFail(SubresourceLoader*, const ResourceError&);
+
+ typedef Deque<Request*> RequestQueue;
+ void servePendingRequests(RequestQueue& requestsPending, bool& serveLowerPriority);
+ void didFail(SubresourceLoader*, bool cancelled = false);
+ void cancelPendingRequests(RequestQueue& requestsPending, DocLoader*);
+
+ RequestQueue m_requestsPending[High + 1];
+ typedef HashMap<RefPtr<SubresourceLoader>, Request*> RequestMap;
+ RequestMap m_requestsLoading;
+ const AtomicString m_name;
+ const int m_maxRequestsInFlight;
+ bool m_processingResource;
+ };
+ typedef HashMap<AtomicStringImpl*, Host*> HostMap;
+ HostMap m_hosts;
+ Host m_nonHTTPProtocolHost;
+
+ Timer<Loader> m_requestTimer;
};
}
diff --git a/WebCore/loader/mac/DocumentLoaderMac.cpp b/WebCore/loader/mac/DocumentLoaderMac.cpp
new file mode 100644
index 0000000..05c6e26
--- /dev/null
+++ b/WebCore/loader/mac/DocumentLoaderMac.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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 "DocumentLoader.h"
+#include "MainResourceLoader.h"
+#include "ResourceHandle.h"
+#include "ResourceLoader.h"
+
+namespace WebCore {
+
+#ifndef BUILDING_ON_TIGER
+static void scheduleAll(const ResourceLoaderSet& loaders, SchedulePair* pair)
+{
+ const ResourceLoaderSet copy = loaders;
+ ResourceLoaderSet::const_iterator end = copy.end();
+ for (ResourceLoaderSet::const_iterator it = copy.begin(); it != end; ++it)
+ if (ResourceHandle* handle = (*it)->handle())
+ handle->schedule(pair);
+}
+
+static void unscheduleAll(const ResourceLoaderSet& loaders, SchedulePair* pair)
+{
+ const ResourceLoaderSet copy = loaders;
+ ResourceLoaderSet::const_iterator end = copy.end();
+ for (ResourceLoaderSet::const_iterator it = copy.begin(); it != end; ++it)
+ if (ResourceHandle* handle = (*it)->handle())
+ handle->unschedule(pair);
+}
+#endif
+
+void DocumentLoader::schedule(SchedulePair* pair)
+{
+#ifndef BUILDING_ON_TIGER
+ if (m_mainResourceLoader && m_mainResourceLoader->handle())
+ m_mainResourceLoader->handle()->schedule(pair);
+ scheduleAll(m_subresourceLoaders, pair);
+ scheduleAll(m_plugInStreamLoaders, pair);
+ scheduleAll(m_multipartSubresourceLoaders, pair);
+#endif
+}
+
+void DocumentLoader::unschedule(SchedulePair* pair)
+{
+#ifndef BUILDING_ON_TIGER
+ if (m_mainResourceLoader && m_mainResourceLoader->handle())
+ m_mainResourceLoader->handle()->unschedule(pair);
+ unscheduleAll(m_subresourceLoaders, pair);
+ unscheduleAll(m_plugInStreamLoaders, pair);
+ unscheduleAll(m_multipartSubresourceLoaders, pair);
+#endif
+}
+
+} // namespace
diff --git a/WebCore/loader/mac/LoaderNSURLExtras.h b/WebCore/loader/mac/LoaderNSURLExtras.h
index 39ef222..ce5a490 100644
--- a/WebCore/loader/mac/LoaderNSURLExtras.h
+++ b/WebCore/loader/mac/LoaderNSURLExtras.h
@@ -32,17 +32,6 @@
extern "C" {
#endif
-NSURL *urlByRemovingComponent(NSURL *url, CFURLComponentType component);
-NSURL *urlByRemovingFragment(NSURL *url);
-NSString *urlOriginalDataAsString(NSURL *url);
-NSData *urlOriginalData(NSURL *url);
-NSURL *urlWithData(NSData *data);
-NSURL *urlWithDataRelativeToURL(NSData *data, NSURL *baseURL);
-NSURL *urlByRemovingResourceSpecifier(NSURL *url);
-BOOL urlIsFileURL(NSURL *url);
-BOOL stringIsFileURL(NSString *urlString);
-BOOL urlIsEmpty(NSURL *url);
-NSURL *canonicalURL(NSURL *url);
NSString *suggestedFilenameWithMIMEType(NSURL *url, NSString *MIMEType);
#ifdef __cplusplus
diff --git a/WebCore/loader/mac/LoaderNSURLExtras.m b/WebCore/loader/mac/LoaderNSURLExtras.m
deleted file mode 100644
index 76ad5c7..0000000
--- a/WebCore/loader/mac/LoaderNSURLExtras.m
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
- * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
- *
- * 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.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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.
- */
-
-#import "config.h"
-#import "LoaderNSURLExtras.h"
-
-#import <wtf/Assertions.h>
-#import <wtf/Vector.h>
-#import "KURL.h"
-#import "LocalizedStrings.h"
-#import "MIMETypeRegistry.h"
-#import "PlatformString.h"
-#import "WebCoreNSStringExtras.h"
-#import "WebCoreSystemInterface.h"
-
-using namespace WebCore;
-
-NSURL *urlByRemovingComponent(NSURL *url, CFURLComponentType component)
-{
- if (!url)
- return nil;
-
- CFRange fragRg = CFURLGetByteRangeForComponent((CFURLRef)url, component, NULL);
- // Check to see if a fragment exists before decomposing the URL.
- if (fragRg.location == kCFNotFound)
- return url;
-
- UInt8 *urlBytes, buffer[2048];
- CFIndex numBytes = CFURLGetBytes((CFURLRef)url, buffer, 2048);
- if (numBytes == -1) {
- numBytes = CFURLGetBytes((CFURLRef)url, NULL, 0);
- urlBytes = static_cast<UInt8*>(malloc(numBytes));
- CFURLGetBytes((CFURLRef)url, urlBytes, numBytes);
- } else
- urlBytes = buffer;
-
- NSURL *result = (NSURL *)CFMakeCollectable(CFURLCreateWithBytes(NULL, urlBytes, fragRg.location - 1, kCFStringEncodingUTF8, NULL));
- if (!result)
- result = (NSURL *)CFMakeCollectable(CFURLCreateWithBytes(NULL, urlBytes, fragRg.location - 1, kCFStringEncodingISOLatin1, NULL));
-
- if (urlBytes != buffer) free(urlBytes);
- return result ? [result autorelease] : url;
-}
-
-NSURL *urlByRemovingFragment(NSURL *url)
-{
- return urlByRemovingComponent(url, kCFURLComponentFragment);
-}
-
-NSString *urlOriginalDataAsString(NSURL *url)
-{
- return [[[NSString alloc] initWithData:urlOriginalData(url) encoding:NSISOLatin1StringEncoding] autorelease];
-}
-
-#define URL_BYTES_BUFFER_LENGTH 2048
-
-NSData *urlOriginalData(NSURL *url)
-{
- if (!url)
- return nil;
-
- UInt8 *buffer = (UInt8 *)malloc(URL_BYTES_BUFFER_LENGTH);
- CFIndex bytesFilled = CFURLGetBytes((CFURLRef)url, buffer, URL_BYTES_BUFFER_LENGTH);
- if (bytesFilled == -1) {
- CFIndex bytesToAllocate = CFURLGetBytes((CFURLRef)url, NULL, 0);
- buffer = (UInt8 *)realloc(buffer, bytesToAllocate);
- bytesFilled = CFURLGetBytes((CFURLRef)url, buffer, bytesToAllocate);
- ASSERT(bytesFilled == bytesToAllocate);
- }
-
- // buffer is adopted by the NSData
- NSData *data = [NSData dataWithBytesNoCopy:buffer length:bytesFilled freeWhenDone:YES];
-
- NSURL *baseURL = (NSURL *)CFURLGetBaseURL((CFURLRef)url);
- if (baseURL)
- return urlOriginalData(urlWithDataRelativeToURL(data, baseURL));
- return data;
-}
-
-NSURL *urlWithData(NSData *data)
-{
- if (data == nil)
- return nil;
-
- return urlWithDataRelativeToURL(data, nil);
-}
-
-static inline id WebCFAutorelease(CFTypeRef obj)
-{
- if (obj)
- CFMakeCollectable(obj);
- [(id)obj autorelease];
- return (id)obj;
-}
-
-NSURL *urlWithDataRelativeToURL(NSData *data, NSURL *baseURL)
-{
- if (data == nil)
- return nil;
-
- NSURL *result = nil;
- size_t length = [data length];
- if (length > 0) {
- // work around <rdar://4470771>: CFURLCreateAbsoluteURLWithBytes(.., TRUE) doesn't remove non-path components.
- baseURL = urlByRemovingResourceSpecifier(baseURL);
-
- const UInt8 *bytes = static_cast<const UInt8*>([data bytes]);
- // NOTE: We use UTF-8 here since this encoding is used when computing strings when returning URL components
- // (e.g calls to NSURL -path). However, this function is not tolerant of illegal UTF-8 sequences, which
- // could either be a malformed string or bytes in a different encoding, like shift-jis, so we fall back
- // onto using ISO Latin 1 in those cases.
- result = WebCFAutorelease(CFURLCreateAbsoluteURLWithBytes(NULL, bytes, length, kCFStringEncodingUTF8, (CFURLRef)baseURL, YES));
- if (!result)
- result = WebCFAutorelease(CFURLCreateAbsoluteURLWithBytes(NULL, bytes, length, kCFStringEncodingISOLatin1, (CFURLRef)baseURL, YES));
- } else
- result = [NSURL URLWithString:@""];
-
- return result;
-}
-
-NSURL *urlByRemovingResourceSpecifier(NSURL *url)
-{
- return urlByRemovingComponent(url, kCFURLComponentResourceSpecifier);
-}
-
-
-BOOL urlIsFileURL(NSURL *url)
-{
- if (!url)
- return false;
-
- return stringIsFileURL(urlOriginalDataAsString(url));
-}
-
-BOOL stringIsFileURL(NSString *urlString)
-{
- return [urlString rangeOfString:@"file:" options:(NSCaseInsensitiveSearch | NSAnchoredSearch)].location != NSNotFound;
-}
-
-BOOL urlIsEmpty(NSURL *url)
-{
- if (!url)
- return false;
- if (!CFURLGetBaseURL((CFURLRef)url))
- return CFURLGetBytes((CFURLRef)url, NULL, 0) == 0;
- return [urlOriginalData(url) length] == 0;
-}
-
-NSURL *canonicalURL(NSURL *url)
-{
- if (!url)
- return nil;
-
- NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
- Class concreteClass = wkNSURLProtocolClassForReqest(request);
- if (!concreteClass) {
- [request release];
- return url;
- }
-
- // This applies NSURL's concept of canonicalization, but not KURL's concept. It would
- // make sense to apply both, but when we tried that it caused a performance degradation
- // (see 5315926). It might make sense to apply only the KURL concept and not the NSURL
- // concept, but it's too risky to make that change for WebKit 3.0.
- NSURLRequest *newRequest = [concreteClass canonicalRequestForRequest:request];
- NSURL *newURL = [newRequest URL];
- NSURL *result = [[newURL retain] autorelease];
- [request release];
-
- return result;
-}
-
-static bool vectorContainsString(Vector<String> vector, String string)
-{
- int size = vector.size();
- for (int i = 0; i < size; i++)
- if (vector[i] == string)
- return true;
- return false;
-}
-
-NSString *suggestedFilenameWithMIMEType(NSURL *url, NSString *MIMEType)
-{
- // Get the filename from the URL. Try the lastPathComponent first.
- NSString *lastPathComponent = [[url path] lastPathComponent];
- NSString *filename = filenameByFixingIllegalCharacters(lastPathComponent);
- NSString *extension = nil;
-
- if ([filename length] == 0 || [lastPathComponent isEqualToString:@"/"]) {
- // lastPathComponent is no good, try the host.
- NSString *host = (NSString *)(KURL(url).host());
- filename = filenameByFixingIllegalCharacters(host);
- if ([filename length] == 0) {
- // Can't make a filename using this URL, use "unknown".
- filename = copyImageUnknownFileLabel();
- }
- } else {
- // Save the extension for later correction. Only correct the extension of the lastPathComponent.
- // For example, if the filename ends up being the host, we wouldn't want to correct ".com" in "www.apple.com".
- extension = [filename pathExtension];
- }
-
- // No mime type reported. Just return the filename we have now.
- if (!MIMEType) {
- return filename;
- }
-
- // Do not correct filenames that are reported with a mime type of tar, and
- // have a filename which has .tar in it or ends in .tgz
- if (([MIMEType isEqualToString:@"application/tar"] || [MIMEType isEqualToString:@"application/x-tar"])
- && (hasCaseInsensitiveSubstring(filename, @".tar")
- || hasCaseInsensitiveSuffix(filename, @".tgz"))) {
- return filename;
- }
-
- // I don't think we need to worry about this for the image case
- // If the type is known, check the extension and correct it if necessary.
- if (![MIMEType isEqualToString:@"application/octet-stream"] && ![MIMEType isEqualToString:@"text/plain"]) {
- Vector<String> extensions = MIMETypeRegistry::getExtensionsForMIMEType(MIMEType);
-
- if (extensions.isEmpty() || !vectorContainsString(extensions, extension)) {
- // The extension doesn't match the MIME type. Correct this.
- NSString *correctExtension = MIMETypeRegistry::getPreferredExtensionForMIMEType(MIMEType);
- if ([correctExtension length] != 0) {
- // Append the correct extension.
- filename = [filename stringByAppendingPathExtension:correctExtension];
- }
- }
- }
-
- return filename;
-}
diff --git a/WebCore/loader/mac/LoaderNSURLExtras.mm b/WebCore/loader/mac/LoaderNSURLExtras.mm
new file mode 100644
index 0000000..9a507f5
--- /dev/null
+++ b/WebCore/loader/mac/LoaderNSURLExtras.mm
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2005, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
+ *
+ * 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.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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.
+ */
+
+#import "config.h"
+#import "LoaderNSURLExtras.h"
+
+#import <wtf/Assertions.h>
+#import <wtf/Vector.h>
+#import "KURL.h"
+#import "LocalizedStrings.h"
+#import "MIMETypeRegistry.h"
+#import "PlatformString.h"
+#import "WebCoreNSStringExtras.h"
+
+using namespace WebCore;
+
+static bool vectorContainsString(const Vector<String>& vector, const String& string)
+{
+ int size = vector.size();
+ for (int i = 0; i < size; i++)
+ if (vector[i] == string)
+ return true;
+ return false;
+}
+
+NSString *suggestedFilenameWithMIMEType(NSURL *url, NSString *MIMEType)
+{
+ // Get the filename from the URL. Try the lastPathComponent first.
+ NSString *lastPathComponent = [[url path] lastPathComponent];
+ NSString *filename = filenameByFixingIllegalCharacters(lastPathComponent);
+ NSString *extension = nil;
+
+ if ([filename length] == 0 || [lastPathComponent isEqualToString:@"/"]) {
+ // lastPathComponent is no good, try the host.
+ NSString *host = KURL(url).host();
+ filename = filenameByFixingIllegalCharacters(host);
+ if ([filename length] == 0) {
+ // Can't make a filename using this URL, use "unknown".
+ filename = copyImageUnknownFileLabel();
+ }
+ } else {
+ // Save the extension for later correction. Only correct the extension of the lastPathComponent.
+ // For example, if the filename ends up being the host, we wouldn't want to correct ".com" in "www.apple.com".
+ extension = [filename pathExtension];
+ }
+
+ // No mime type reported. Just return the filename we have now.
+ if (!MIMEType) {
+ return filename;
+ }
+
+ // Do not correct filenames that are reported with a mime type of tar, and
+ // have a filename which has .tar in it or ends in .tgz
+ if (([MIMEType isEqualToString:@"application/tar"] || [MIMEType isEqualToString:@"application/x-tar"])
+ && (hasCaseInsensitiveSubstring(filename, @".tar")
+ || hasCaseInsensitiveSuffix(filename, @".tgz"))) {
+ return filename;
+ }
+
+ // I don't think we need to worry about this for the image case
+ // If the type is known, check the extension and correct it if necessary.
+ if (![MIMEType isEqualToString:@"application/octet-stream"] && ![MIMEType isEqualToString:@"text/plain"]) {
+ Vector<String> extensions = MIMETypeRegistry::getExtensionsForMIMEType(MIMEType);
+
+ if (extensions.isEmpty() || !vectorContainsString(extensions, extension)) {
+ // The extension doesn't match the MIME type. Correct this.
+ NSString *correctExtension = MIMETypeRegistry::getPreferredExtensionForMIMEType(MIMEType);
+ if ([correctExtension length] != 0) {
+ // Append the correct extension.
+ filename = [filename stringByAppendingPathExtension:correctExtension];
+ }
+ }
+ }
+
+ return filename;
+}