diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-05 14:34:32 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-05 14:34:32 -0800 |
commit | 635860845790a19bf50bbc51ba8fb66a96dde068 (patch) | |
tree | ef6ad9ff73a5b57f65249d4232a202fa77e6a140 /WebCore/loader | |
parent | 8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2 (diff) | |
download | external_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.zip external_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.tar.gz external_webkit-635860845790a19bf50bbc51ba8fb66a96dde068.tar.bz2 |
auto import from //depot/cupcake/@136594
Diffstat (limited to 'WebCore/loader')
74 files changed, 2885 insertions, 1333 deletions
diff --git a/WebCore/loader/Cache.cpp b/WebCore/loader/Cache.cpp index 7d30e5f..212fca3 100644 --- a/WebCore/loader/Cache.cpp +++ b/WebCore/loader/Cache.cpp @@ -37,8 +37,8 @@ #include "FrameView.h" #include "Image.h" #include "ResourceHandle.h" -#include "SystemTime.h" #include <stdio.h> +#include <wtf/CurrentTime.h> using namespace std; @@ -95,7 +95,7 @@ static CachedResource* createResource(CachedResource::Type type, const KURL& url return 0; } -CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Type type, const KURL& url, const String& charset, bool isPreload) +CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Type type, const KURL& url, const String& charset, bool requestIsPreload) { // 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,25 +103,19 @@ CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Typ return 0; // Look up the resource in our map. - CachedResource* resource = m_resources.get(url.string()); - - if (resource) { - if (isPreload && !resource->isPreloaded()) - return 0; - if (FrameLoader::restrictAccessToLocal() && !FrameLoader::canLoad(url, String(), docLoader->doc())) { - Document* doc = docLoader->doc(); - if(doc && !isPreload) - FrameLoader::reportLocalLoadFailed(doc->frame(), resource->url()); - return 0; - } - } else { - if (FrameLoader::restrictAccessToLocal() && !FrameLoader::canLoad(url, String(), docLoader->doc())) { - Document* doc = docLoader->doc(); - if(doc && !isPreload) - FrameLoader::reportLocalLoadFailed(doc->frame(), url.string()); - return 0; - } - + CachedResource* resource = resourceForURL(url.string()); + + if (resource && requestIsPreload && !resource->isPreloaded()) + return 0; + + if (FrameLoader::restrictAccessToLocal() && !FrameLoader::canLoad(url, String(), docLoader->doc())) { + Document* doc = docLoader->doc(); + if (doc && !requestIsPreload) + FrameLoader::reportLocalLoadFailed(doc->frame(), url.string()); + return 0; + } + + if (!resource) { // The resource does not exist. Create it. resource = createResource(type, url, charset); ASSERT(resource); @@ -169,7 +163,7 @@ CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Typ CachedCSSStyleSheet* Cache::requestUserCSSStyleSheet(DocLoader* docLoader, const String& url, const String& charset) { CachedCSSStyleSheet* userSheet; - if (CachedResource* existing = m_resources.get(url)) { + if (CachedResource* existing = resourceForURL(url)) { if (existing->type() != CachedResource::CSSStyleSheet) return 0; userSheet = static_cast<CachedCSSStyleSheet*>(existing); @@ -248,7 +242,13 @@ void Cache::revalidationFailed(CachedResource* revalidatingResource) CachedResource* Cache::resourceForURL(const String& url) { - return m_resources.get(url); + CachedResource* resource = m_resources.get(url); + if (resource && !resource->makePurgeable(false)) { + ASSERT(!resource->hasClients()); + evict(resource); + return 0; + } + return resource; } unsigned Cache::deadCapacity() const @@ -278,7 +278,7 @@ void Cache::pruneLiveResources() unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again. double currentTime = FrameView::currentPaintTimeStamp(); if (!currentTime) // In case prune is called directly, outside of a Frame paint. - currentTime = WebCore::currentTime(); + currentTime = WTF::currentTime(); // Destroy any decoded data in live objects that we can. // Start from the tail, since this is the least recently accessed of the objects. @@ -315,6 +315,25 @@ void Cache::pruneDeadResources() unsigned targetSize = static_cast<unsigned>(capacity * cTargetPrunePercentage); // Cut by a percentage to avoid immediately pruning again. int size = m_allResources.size(); + + if (!m_inPruneDeadResources) { + // See if we have any purged resources we can evict. + for (int i = 0; i < size; i++) { + CachedResource* current = m_allResources[i].m_tail; + while (current) { + CachedResource* prev = current->m_prevInAllResourcesList; + if (current->wasPurged()) { + ASSERT(!current->hasClients()); + ASSERT(!current->isPreloaded()); + evict(current); + } + current = prev; + } + } + if (targetSize && m_deadSize <= targetSize) + return; + } + bool canShrinkLRULists = true; m_inPruneDeadResources = true; for (int i = size - 1; i >= 0; i--) { @@ -324,7 +343,7 @@ void Cache::pruneDeadResources() // First flush all the decoded data in this queue. while (current) { CachedResource* prev = current->m_prevInAllResourcesList; - if (!current->hasClients() && !current->isPreloaded() && current->isLoaded() && current->decodedSize()) { + if (!current->hasClients() && !current->isPreloaded() && current->isLoaded()) { // Destroy our decoded data. This will remove us from // m_liveDecodedResources, and possibly move us to a differnt // LRU list in m_allResources. @@ -382,6 +401,15 @@ 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>. if (resource->inCache()) { + if (!resource->isCacheValidator()) { + // Notify all doc loaders that might be observing this object still that it has been + // extracted from the set of resources. + // No need to do this for cache validator resources, they are replaced automatically by using CachedResourceHandles. + HashSet<DocLoader*>::iterator end = m_docLoaders.end(); + for (HashSet<DocLoader*>::iterator itr = m_docLoaders.begin(); itr != end; ++itr) + (*itr)->removeCachedResource(resource); + } + // Remove from the resource map. m_resources.remove(resource->url()); resource->setInCache(false); @@ -389,12 +417,6 @@ void Cache::evict(CachedResource* resource) // Remove from the appropriate LRU list. removeFromLRUList(resource); removeFromLiveDecodedResourcesList(resource); - - // Notify all doc loaders that might be observing this object still that it has been - // extracted from the set of resources. - HashSet<DocLoader*>::iterator end = m_docLoaders.end(); - for (HashSet<DocLoader*>::iterator itr = m_docLoaders.begin(); itr != end; ++itr) - (*itr)->removeCachedResource(resource); // Subtract from our size totals. int delta = -static_cast<int>(resource->size()); @@ -534,6 +556,10 @@ void Cache::resourceAccessed(CachedResource* resource) // the queue will possibly change. removeFromLRUList(resource); + // If this is the first time the resource has been accessed, adjust the size of the cache to account for its initial size. + if (!resource->accessCount()) + adjustSize(resource->hasClients(), resource->size()); + // Add to our access count. resource->increaseAccessCount(); @@ -631,60 +657,52 @@ void Cache::adjustSize(bool live, int delta) } } +void Cache::TypeStatistic::addResource(CachedResource* o) +{ + bool purged = o->wasPurged(); + bool purgeable = o->isPurgeable() && !purged; + int pageSize = (o->encodedSize() + o->overheadSize() + 4095) & ~4095; + count++; + size += purged ? 0 : o->size(); + liveSize += o->hasClients() ? o->size() : 0; + decodedSize += o->decodedSize(); + purgeableSize += purgeable ? pageSize : 0; + purgedSize += purged ? pageSize : 0; +} + Cache::Statistics Cache::getStatistics() { Statistics stats; CachedResourceMap::iterator e = m_resources.end(); for (CachedResourceMap::iterator i = m_resources.begin(); i != e; ++i) { - CachedResource *o = i->second; - switch (o->type()) { - case CachedResource::ImageResource: - stats.images.count++; - stats.images.size += o->size(); - 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->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->hasClients() ? o->size() : 0; - stats.scripts.decodedSize += o->decodedSize(); - break; + CachedResource* resource = i->second; + switch (resource->type()) { + case CachedResource::ImageResource: + stats.images.addResource(resource); + break; + case CachedResource::CSSStyleSheet: + stats.cssStyleSheets.addResource(resource); + break; + case CachedResource::Script: + stats.scripts.addResource(resource); + break; #if ENABLE(XSLT) - case CachedResource::XSLStyleSheet: - stats.xslStyleSheets.count++; - stats.xslStyleSheets.size += o->size(); - stats.xslStyleSheets.liveSize += o->hasClients() ? o->size() : 0; - stats.xslStyleSheets.decodedSize += o->decodedSize(); - break; + case CachedResource::XSLStyleSheet: + stats.xslStyleSheets.addResource(resource); + break; #endif - case CachedResource::FontResource: - stats.fonts.count++; - stats.fonts.size += o->size(); - stats.fonts.liveSize += o->hasClients() ? o->size() : 0; - stats.fonts.decodedSize += o->decodedSize(); - break; + case CachedResource::FontResource: + stats.fonts.addResource(resource); + break; #if ENABLE(XBL) - case CachedResource::XBL: - stats.xblDocs.count++; - stats.xblDocs.size += o->size(); - stats.xblDocs.liveSize += o->hasClients() ? o->size() : 0; - stats.xblDocs.decodedSize += o->decodedSize(); - break; + case CachedResource::XBL: + stats.xblDocs.addResource(resource) + break; #endif - default: - break; + default: + break; } } - return stats; } @@ -703,6 +721,21 @@ void Cache::setDisabled(bool disabled) } #ifndef NDEBUG +void Cache::dumpStats() +{ + Statistics s = getStatistics(); + printf("%-11s %-11s %-11s %-11s %-11s %-11s %-11s\n", "", "Count", "Size", "LiveSize", "DecodedSize", "PurgeableSize", "PurgedSize"); + printf("%-11s %-11s %-11s %-11s %-11s %-11s %-11s\n", "-----------", "-----------", "-----------", "-----------", "-----------", "-----------", "-----------"); + printf("%-11s %11d %11d %11d %11d %11d %11d\n", "Images", s.images.count, s.images.size, s.images.liveSize, s.images.decodedSize, s.images.purgeableSize, s.images.purgedSize); + printf("%-11s %11d %11d %11d %11d %11d %11d\n", "CSS", s.cssStyleSheets.count, s.cssStyleSheets.size, s.cssStyleSheets.liveSize, s.cssStyleSheets.decodedSize, s.cssStyleSheets.purgeableSize, s.cssStyleSheets.purgedSize); +#if ENABLE(XSLT) + printf("%-11s %11d %11d %11d %11d %11d %11d\n", "XSL", s.xslStyleSheets.count, s.xslStyleSheets.size, s.xslStyleSheets.liveSize, s.xslStyleSheets.decodedSize, s.xslStyleSheets.purgeableSize, s.xslStyleSheets.purgedSize); +#endif + printf("%-11s %11d %11d %11d %11d %11d %11d\n", "JavaScript", s.scripts.count, s.scripts.size, s.scripts.liveSize, s.scripts.decodedSize, s.scripts.purgeableSize, s.scripts.purgedSize); + printf("%-11s %11d %11d %11d %11d %11d %11d\n", "Fonts", s.fonts.count, s.fonts.size, s.fonts.liveSize, s.fonts.decodedSize, s.fonts.purgeableSize, s.fonts.purgedSize); + printf("%-11s %-11s %-11s %-11s %-11s %-11s %-11s\n\n", "-----------", "-----------", "-----------", "-----------", "-----------", "-----------", "-----------"); +} + void Cache::dumpLRULists(bool includeLive) const { printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced):\n"); @@ -714,7 +747,7 @@ void Cache::dumpLRULists(bool includeLive) const while (current) { CachedResource* prev = current->m_prevInAllResourcesList; if (includeLive || !current->hasClients()) - printf("(%.1fK, %.1fK, %uA, %dR); ", current->decodedSize() / 1024.0f, current->encodedSize() / 1024.0f, current->accessCount(), current->hasClients()); + printf("(%.1fK, %.1fK, %uA, %dR); ", current->decodedSize() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, current->accessCount(), current->hasClients()); current = prev; } } diff --git a/WebCore/loader/Cache.h b/WebCore/loader/Cache.h index ec0ea0e..9d81dc6 100644 --- a/WebCore/loader/Cache.h +++ b/WebCore/loader/Cache.h @@ -1,6 +1,4 @@ /* - 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) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. @@ -74,7 +72,10 @@ public: int size; int liveSize; int decodedSize; - TypeStatistic() : count(0), size(0), liveSize(0), decodedSize(0) { } + int purgeableSize; + int purgedSize; + TypeStatistic() : count(0), size(0), liveSize(0), decodedSize(0), purgeableSize(0), purgedSize(0) { } + void addResource(CachedResource*); }; struct Statistics { @@ -165,6 +166,7 @@ private: LRUList* lruListFor(CachedResource*); void resourceAccessed(CachedResource*); #ifndef NDEBUG + void dumpStats(); void dumpLRULists(bool includeLive) const; #endif diff --git a/WebCore/loader/CachePolicy.h b/WebCore/loader/CachePolicy.h index 16af78a..93f92b1 100644 --- a/WebCore/loader/CachePolicy.h +++ b/WebCore/loader/CachePolicy.h @@ -31,7 +31,7 @@ namespace WebCore { enum CachePolicy { CachePolicyCache, CachePolicyVerify, - CachePolicyRefresh, + CachePolicyRevalidate, CachePolicyReload }; diff --git a/WebCore/loader/CachedCSSStyleSheet.cpp b/WebCore/loader/CachedCSSStyleSheet.cpp index 9059f25..10d566e 100644 --- a/WebCore/loader/CachedCSSStyleSheet.cpp +++ b/WebCore/loader/CachedCSSStyleSheet.cpp @@ -55,6 +55,12 @@ void CachedCSSStyleSheet::addClient(CachedResourceClient *c) if (!m_loading) c->setCSSStyleSheet(m_url, m_decoder->encoding().name(), this); } + +void CachedCSSStyleSheet::allClientsRemoved() +{ + if (isSafeToMakePurgeable()) + makePurgeable(true); +} void CachedCSSStyleSheet::setEncoding(const String& chs) { @@ -65,6 +71,22 @@ String CachedCSSStyleSheet::encoding() const { return m_decoder->encoding().name(); } + +const String CachedCSSStyleSheet::sheetText(bool enforceMIMEType) const +{ + ASSERT(!isPurgeable()); + + if (!m_data || m_data->isEmpty() || !canUseSheet(enforceMIMEType)) + return String(); + + if (!m_decodedSheetText.isNull()) + return m_decodedSheetText; + + // Don't cache the decoded text, regenerating is cheap and it can use quite a bit of memory + String sheetText = m_decoder->decode(m_data->data(), m_data->size()); + sheetText += m_decoder->flush(); + return sheetText; +} void CachedCSSStyleSheet::data(PassRefPtr<SharedBuffer> data, bool allDataReceived) { @@ -73,16 +95,15 @@ void CachedCSSStyleSheet::data(PassRefPtr<SharedBuffer> data, bool allDataReceiv m_data = data; setEncodedSize(m_data.get() ? m_data->size() : 0); - if (m_data.get()) { - m_sheet = m_decoder->decode(m_data->data(), encodedSize()); - m_sheet += m_decoder->flush(); -#ifdef ANDROID_FIX // FIXME Newer webkit makes decode temporary; remove on webkit update - // report decoded size too - setDecodedSize(m_sheet.length() * sizeof(UChar)); -#endif + // Decode the data to find out the encoding and keep the sheet text around during checkNotify() + if (m_data) { + m_decodedSheetText = m_decoder->decode(m_data->data(), m_data->size()); + m_decodedSheetText += m_decoder->flush(); } m_loading = false; checkNotify(); + // Clear the decoded text as it is unlikely to be needed immediately again and is cheap to regenerate. + m_decodedSheetText = String(); } void CachedCSSStyleSheet::checkNotify() diff --git a/WebCore/loader/CachedCSSStyleSheet.h b/WebCore/loader/CachedCSSStyleSheet.h index b9129ba..fa0b31a 100644 --- a/WebCore/loader/CachedCSSStyleSheet.h +++ b/WebCore/loader/CachedCSSStyleSheet.h @@ -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) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + 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 @@ -42,10 +40,12 @@ namespace WebCore { CachedCSSStyleSheet(const String& URL, const String& charset); virtual ~CachedCSSStyleSheet(); - const String sheetText(bool enforceMIMEType = true) const { return canUseSheet(enforceMIMEType) ? m_sheet : ""; } + const String sheetText(bool enforceMIMEType = true) const; virtual void addClient(CachedResourceClient*); - + + virtual void allClientsRemoved(); + virtual void setEncoding(const String&); virtual String encoding() const; virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived); @@ -59,8 +59,8 @@ namespace WebCore { bool canUseSheet(bool enforceMIMEType) const; protected: - String m_sheet; RefPtr<TextResourceDecoder> m_decoder; + String m_decodedSheetText; }; } diff --git a/WebCore/loader/CachedFont.cpp b/WebCore/loader/CachedFont.cpp index 20479ef..0cbdc32 100644 --- a/WebCore/loader/CachedFont.cpp +++ b/WebCore/loader/CachedFont.cpp @@ -65,7 +65,7 @@ CachedFont::~CachedFont() #endif } -void CachedFont::load(DocLoader* docLoader) +void CachedFont::load(DocLoader*) { // Don't load the file yet. Wait for an access before triggering the load. m_loading = true; diff --git a/WebCore/loader/CachedImage.cpp b/WebCore/loader/CachedImage.cpp index 3327c38..5e06eb4 100644 --- a/WebCore/loader/CachedImage.cpp +++ b/WebCore/loader/CachedImage.cpp @@ -33,7 +33,8 @@ #include "FrameView.h" #include "Request.h" #include "Settings.h" -#include "SystemTime.h" +#include <wtf/CurrentTime.h> +#include <wtf/StdLibExtras.h> #include <wtf/Vector.h> #if PLATFORM(CG) @@ -93,8 +94,13 @@ void CachedImage::addClient(CachedResourceClient* c) if (m_decodedDataDeletionTimer.isActive()) m_decodedDataDeletionTimer.stop(); + + if (m_data && !m_image && !m_errorOccurred) { + createImage(); + m_image->setData(m_data, true); + } - if (m_image && !m_image->rect().isEmpty()) + if (m_image && !m_image->isNull()) c->imageChanged(this); if (!m_loading) @@ -111,20 +117,20 @@ void CachedImage::allClientsRemoved() static Image* brokenImage() { - static RefPtr<Image> brokenImage; - if (!brokenImage) - brokenImage = Image::loadPlatformResource("missingImage"); + DEFINE_STATIC_LOCAL(RefPtr<Image>, brokenImage, (Image::loadPlatformResource("missingImage"))); return brokenImage.get(); } static Image* nullImage() { - static RefPtr<BitmapImage> nullImage = BitmapImage::create(); + DEFINE_STATIC_LOCAL(RefPtr<BitmapImage>, nullImage, (BitmapImage::create())); return nullImage.get(); } Image* CachedImage::image() const { + ASSERT(!isPurgeable()); + if (m_errorOccurred) return brokenImage(); @@ -166,6 +172,8 @@ bool CachedImage::imageHasRelativeHeight() const IntSize CachedImage::imageSize(float multiplier) const { + ASSERT(!isPurgeable()); + if (!m_image) return IntSize(); if (multiplier == 1.0f) @@ -185,6 +193,8 @@ IntSize CachedImage::imageSize(float multiplier) const IntRect CachedImage::imageRect(float multiplier) const { + ASSERT(!isPurgeable()); + if (!m_image) return IntRect(); if (multiplier == 1.0f || (!m_image->hasRelativeWidth() && !m_image->hasRelativeHeight())) @@ -210,11 +220,11 @@ IntRect CachedImage::imageRect(float multiplier) const return IntRect(x, y, width, height); } -void CachedImage::notifyObservers() +void CachedImage::notifyObservers(const IntRect* changeRect) { CachedResourceClientWalker w(m_clients); while (CachedResourceClient* c = w.next()) - c->imageChanged(this); + c->imageChanged(this, changeRect); } void CachedImage::clear() @@ -284,6 +294,8 @@ void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived) return; } + // It would be nice to only redraw the decoded band of the image, but with the current design + // (decoding delayed until painting) that seems hard. notifyObservers(); if (m_image) @@ -317,7 +329,14 @@ void CachedImage::checkNotify() void CachedImage::destroyDecodedData() { - if (m_image && !m_errorOccurred) + bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage()); + if (isSafeToMakePurgeable() && canDeleteImage && !m_loading) { + // Image refs the data buffer so we should not make it purgeable while the image is alive. + // Invoking addClient() will reconstruct the image object. + m_image = 0; + setDecodedSize(0); + makePurgeable(true); + } else if (m_image && !m_errorOccurred) m_image->destroyDecodedData(); } @@ -361,4 +380,10 @@ void CachedImage::animationAdvanced(const Image* image) notifyObservers(); } +void CachedImage::changedInRect(const Image* image, const IntRect& rect) +{ + if (image == m_image) + notifyObservers(&rect); +} + } //namespace WebCore diff --git a/WebCore/loader/CachedImage.h b/WebCore/loader/CachedImage.h index f24e2fb..9c3442f 100644 --- a/WebCore/loader/CachedImage.h +++ b/WebCore/loader/CachedImage.h @@ -85,11 +85,13 @@ public: virtual bool shouldPauseAnimation(const Image*); virtual void animationAdvanced(const Image*); + virtual void changedInRect(const Image*, const IntRect&); private: void createImage(); size_t maximumDecodedImageSize(); - void notifyObservers(); + // If not null, changeRect is the changed part of the image. + void notifyObservers(const IntRect* changeRect = 0); void decodedDataDeletionTimerFired(Timer<CachedImage>*); RefPtr<Image> m_image; diff --git a/WebCore/loader/CachedResource.cpp b/WebCore/loader/CachedResource.cpp index 6d0af9b..f5ce737 100644 --- a/WebCore/loader/CachedResource.cpp +++ b/WebCore/loader/CachedResource.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 @@ -30,8 +30,8 @@ #include "Frame.h" #include "FrameLoader.h" #include "KURL.h" +#include "PurgeableBuffer.h" #include "Request.h" -#include "SystemTime.h" #include <wtf/RefCountedLeakCounter.h> #include <wtf/Vector.h> @@ -120,13 +120,13 @@ bool CachedResource::isExpired() const time_t now = time(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) @@ -138,6 +138,8 @@ void CachedResource::setRequest(Request* request) void CachedResource::addClient(CachedResourceClient *c) { + ASSERT(!isPurgeable()); + if (m_preloadResult == PreloadNotReferenced) { if (isLoaded()) m_preloadResult = PreloadReferencedWhileComplete; @@ -305,12 +307,90 @@ bool CachedResource::canUseCacheValidator() const 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. + return false; + + // FIXME: Also look at max-age, min-fresh, max-stale in Cache-Control if (cachePolicy == CachePolicyCache) - return !cacheControl.isEmpty() && (cacheControl.contains("no-cache", false) || (isExpired() && cacheControl.contains("must-revalidate", false))); - return isExpired() || cacheControl.contains("no-cache", false); + return m_response.cacheControlContainsNoCache() || (isExpired() && m_response.cacheControlContainsMustRevalidate()); + return isExpired() || m_response.cacheControlContainsNoCache(); +} + +bool CachedResource::isSafeToMakePurgeable() const +{ + return !hasClients() && !m_isBeingRevalidated && !m_resourceToRevalidate; +} + +bool CachedResource::makePurgeable(bool purgeable) +{ + if (purgeable) { + ASSERT(isSafeToMakePurgeable()); + + if (m_purgeableData) { + ASSERT(!m_data); + return true; + } + if (!m_data) + return false; + + // Should not make buffer purgeable if it has refs othen than this since we don't want two copies. + if (!m_data->hasOneRef()) + return false; + + // Purgeable buffers are allocated in multiples of the page size (4KB in common CPUs) so it does not make sense for very small buffers. + const size_t purgeableThreshold = 4 * 4096; + if (m_data->size() < purgeableThreshold) + return false; + + if (m_data->hasPurgeableBuffer()) { + m_purgeableData.set(m_data->releasePurgeableBuffer()); + } else { + m_purgeableData.set(PurgeableBuffer::create(m_data->data(), m_data->size())); + if (!m_purgeableData) + return false; + } + + m_purgeableData->makePurgeable(true); + m_data.clear(); + return true; + } + + if (!m_purgeableData) + return true; + ASSERT(!m_data); + ASSERT(!hasClients()); + + if (!m_purgeableData->makePurgeable(false)) + return false; + + m_data = SharedBuffer::adoptPurgeableBuffer(m_purgeableData.release()); + return true; +} + +bool CachedResource::isPurgeable() const +{ + return m_purgeableData && m_purgeableData->isPurgeable(); +} + +bool CachedResource::wasPurged() const +{ + return m_purgeableData && m_purgeableData->wasPurged(); +} + +unsigned CachedResource::overheadSize() const +{ + + // FIXME: Find some programmatic lighweight way to calculate response size, and size of the different CachedResource classes. + // This is a rough estimate of resource overhead based on stats collected from the stress test. + return sizeof(CachedResource) + 3648; + + /* sizeof(CachedResource) + + 192 + // average size of m_url. + 384 + // average size of m_clients hash map. + 1280 * 2 + // average size of ResourceResponse. Doubled to account for the WebCore copy and the CF copy. + // Mostly due to the size of the hash maps, the Header Map strings and the URL. + 256 * 2 // Overhead from ResourceRequest, doubled to account for WebCore copy and CF copy. + // Mostly due to the URL and Header Map. + */ } } diff --git a/WebCore/loader/CachedResource.h b/WebCore/loader/CachedResource.h index c56a889..63c250b 100644 --- a/WebCore/loader/CachedResource.h +++ b/WebCore/loader/CachedResource.h @@ -29,6 +29,7 @@ #include "SharedBuffer.h" #include <wtf/HashCountedSet.h> #include <wtf/HashSet.h> +#include <wtf/OwnPtr.h> #include <wtf/Vector.h> #include <time.h> @@ -38,13 +39,16 @@ class Cache; class CachedResourceClient; class CachedResourceHandleBase; class DocLoader; +class InspectorResource; class Request; +class PurgeableBuffer; // A resource that is held in the cache. Classes who want to use this object should derive // from CachedResourceClient, to get the function calls in case the requested data has arrived. // This class also does the actual communication with the loader to obtain the resource from the network. class CachedResource { friend class Cache; + friend class InspectorResource; public: enum Type { @@ -96,15 +100,16 @@ public: PreloadResult preloadResult() const { return m_preloadResult; } void setRequestedFromNetworkingLayer() { m_requestedFromNetworkingLayer = true; } - virtual void allClientsRemoved() { }; + virtual void allClientsRemoved() { } unsigned count() const { return m_clients.size(); } Status status() const { return m_status; } - unsigned size() const { return encodedSize() + decodedSize(); } + unsigned size() const { return encodedSize() + decodedSize() + overheadSize(); } unsigned encodedSize() const { return m_encodedSize; } unsigned decodedSize() const { return m_decodedSize; } + unsigned overheadSize() const; bool isLoaded() const { return !m_loading; } void setLoading(bool b) { m_loading = b; } @@ -129,7 +134,7 @@ public: void setRequest(Request*); - SharedBuffer* data() const { return m_data.get(); } + SharedBuffer* data() const { ASSERT(!m_purgeableData); return m_data.get(); } void setResponse(const ResourceResponse&); const ResourceResponse& response() const { return m_response; } @@ -148,7 +153,7 @@ public: bool errorOccurred() const { return m_errorOccurred; } bool sendResourceLoadCallbacks() const { return m_sendResourceLoadCallbacks; } - virtual void destroyDecodedData() {}; + virtual void destroyDecodedData() { } void setDocLoader(DocLoader* docLoader) { m_docLoader = docLoader; } @@ -164,11 +169,17 @@ public: bool isCacheValidator() const { return m_resourceToRevalidate; } CachedResource* resourceToRevalidate() const { return m_resourceToRevalidate; } + bool isPurgeable() const; + bool wasPurged() const; + protected: void setEncodedSize(unsigned); void setDecodedSize(unsigned); void didAccessDecodedData(double timeStamp); + bool makePurgeable(bool purgeable); + bool isSafeToMakePurgeable() const; + HashCountedSet<CachedResourceClient*> m_clients; String m_url; @@ -177,6 +188,7 @@ protected: ResourceResponse m_response; RefPtr<SharedBuffer> m_data; + OwnPtr<PurgeableBuffer> m_purgeableData; Type m_type; Status m_status; diff --git a/WebCore/loader/CachedResourceClient.h b/WebCore/loader/CachedResourceClient.h index 1d8d45e..2e0b15b 100644 --- a/WebCore/loader/CachedResourceClient.h +++ b/WebCore/loader/CachedResourceClient.h @@ -1,9 +1,7 @@ /* - 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) 2004, 2005, 2006 Apple Computer, Inc. + 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 @@ -56,8 +54,8 @@ namespace WebCore { virtual ~CachedResourceClient() { } // Called whenever a frame of an image changes, either because we got more data from the network or - // because we are animating. - virtual void imageChanged(CachedImage*) { }; + // because we are animating. If not null, the IntRect is the changed rect of the image. + virtual void imageChanged(CachedImage*, const IntRect* = 0) { }; // Called to find out if this client wants to actually display the image. Used to tell when we // can halt animation. Content nodes that hold image refs for example would not render the image, diff --git a/WebCore/loader/CachedResourceClientWalker.cpp b/WebCore/loader/CachedResourceClientWalker.cpp index 970b0e0..142a2a1 100644 --- a/WebCore/loader/CachedResourceClientWalker.cpp +++ b/WebCore/loader/CachedResourceClientWalker.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, 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 diff --git a/WebCore/loader/CachedResourceClientWalker.h b/WebCore/loader/CachedResourceClientWalker.h index 0bf98e4..d079584 100644 --- a/WebCore/loader/CachedResourceClientWalker.h +++ b/WebCore/loader/CachedResourceClientWalker.h @@ -1,9 +1,7 @@ /* - 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) 2004, 2005, 2006 Apple Computer, Inc. + 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 diff --git a/WebCore/loader/CachedScript.cpp b/WebCore/loader/CachedScript.cpp index c8caea8..411521b 100644 --- a/WebCore/loader/CachedScript.cpp +++ b/WebCore/loader/CachedScript.cpp @@ -1,11 +1,9 @@ /* - 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) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + 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 @@ -38,6 +36,7 @@ namespace WebCore { CachedScript::CachedScript(const String& url, const String& charset) : CachedResource(url, Script) , m_encoding(charset) + , m_decodedDataDeletionTimer(this, &CachedScript::decodedDataDeletionTimerFired) { // It's javascript we want. // But some websites think their scripts are <some wrong mimetype here> @@ -58,6 +57,11 @@ void CachedScript::addClient(CachedResourceClient* c) c->notifyFinished(this); } +void CachedScript::allClientsRemoved() +{ + m_decodedDataDeletionTimer.startOneShot(0); +} + void CachedScript::setEncoding(const String& chs) { TextEncoding encoding(chs); @@ -70,6 +74,19 @@ String CachedScript::encoding() const return m_encoding.name(); } +const String& CachedScript::script() +{ + ASSERT(!isPurgeable()); + + if (!m_script && m_data) { + m_script = m_encoding.decode(m_data->data(), encodedSize()); + setDecodedSize(m_script.length() * sizeof(UChar)); + } + + m_decodedDataDeletionTimer.startOneShot(0); + return m_script; +} + void CachedScript::data(PassRefPtr<SharedBuffer> data, bool allDataReceived) { if (!allDataReceived) @@ -77,12 +94,6 @@ void CachedScript::data(PassRefPtr<SharedBuffer> data, bool allDataReceived) m_data = data; setEncodedSize(m_data.get() ? m_data->size() : 0); - if (m_data.get()) - m_script = m_encoding.decode(m_data->data(), encodedSize()); -#ifdef ANDROID_FIX // FIXME Newer webkit calls setDecodedSize in CachedScript::script(); remove on webkit update - // report decoded size too - setDecodedSize(m_script.length() * sizeof(UChar)); -#endif m_loading = false; checkNotify(); } @@ -104,4 +115,17 @@ void CachedScript::error() checkNotify(); } +void CachedScript::destroyDecodedData() +{ + m_script = String(); + setDecodedSize(0); + if (isSafeToMakePurgeable()) + makePurgeable(true); +} + +void CachedScript::decodedDataDeletionTimerFired(Timer<CachedScript>*) +{ + destroyDecodedData(); } + +} // namespace WebCore diff --git a/WebCore/loader/CachedScript.h b/WebCore/loader/CachedScript.h index 4580cfe..1715e06 100644 --- a/WebCore/loader/CachedScript.h +++ b/WebCore/loader/CachedScript.h @@ -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) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + 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 @@ -30,6 +28,7 @@ #include "CachedResource.h" #include "TextEncoding.h" +#include "Timer.h" namespace WebCore { @@ -40,9 +39,10 @@ namespace WebCore { CachedScript(const String& url, const String& charset); virtual ~CachedScript(); - const String& script() const { return m_script; } + const String& script(); virtual void addClient(CachedResourceClient*); + virtual void allClientsRemoved(); virtual void setEncoding(const String&); virtual String encoding() const; @@ -53,9 +53,14 @@ namespace WebCore { void checkNotify(); + virtual void destroyDecodedData(); + private: + void decodedDataDeletionTimerFired(Timer<CachedScript>*); + String m_script; TextEncoding m_encoding; + Timer<CachedScript> m_decodedDataDeletionTimer; }; } diff --git a/WebCore/loader/CachedXBLDocument.cpp b/WebCore/loader/CachedXBLDocument.cpp index 1ef3bae..0ff17f2 100644 --- a/WebCore/loader/CachedXBLDocument.cpp +++ b/WebCore/loader/CachedXBLDocument.cpp @@ -1,11 +1,9 @@ /* - 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) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + 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 diff --git a/WebCore/loader/CachedXBLDocument.h b/WebCore/loader/CachedXBLDocument.h index a66a0cb..b92a255 100644 --- a/WebCore/loader/CachedXBLDocument.h +++ b/WebCore/loader/CachedXBLDocument.h @@ -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) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + 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 diff --git a/WebCore/loader/CachedXSLStyleSheet.cpp b/WebCore/loader/CachedXSLStyleSheet.cpp index 009b2af..f221664 100644 --- a/WebCore/loader/CachedXSLStyleSheet.cpp +++ b/WebCore/loader/CachedXSLStyleSheet.cpp @@ -1,11 +1,9 @@ /* - 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) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + 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 diff --git a/WebCore/loader/CachedXSLStyleSheet.h b/WebCore/loader/CachedXSLStyleSheet.h index c5326fa..9eca098 100644 --- a/WebCore/loader/CachedXSLStyleSheet.h +++ b/WebCore/loader/CachedXSLStyleSheet.h @@ -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) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + 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 diff --git a/WebCore/loader/DocLoader.cpp b/WebCore/loader/DocLoader.cpp index 4dbc6a0..28bbde7 100644 --- a/WebCore/loader/DocLoader.cpp +++ b/WebCore/loader/DocLoader.cpp @@ -48,7 +48,6 @@ namespace WebCore { DocLoader::DocLoader(Document* doc) : m_cache(cache()) - , m_cachePolicy(CachePolicyVerify) , m_doc(doc) , m_requestCount(0) #ifdef ANDROID_BLOCK_NETWORK_IMAGE @@ -64,8 +63,8 @@ DocLoader::DocLoader(Document* doc) DocLoader::~DocLoader() { clearPreloads(); - HashMap<String, CachedResource*>::iterator end = m_docResources.end(); - for (HashMap<String, CachedResource*>::iterator it = m_docResources.begin(); it != end; ++it) + DocumentResourceMap::iterator end = m_documentResources.end(); + for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) it->second->setDocLoader(0); m_cache->removeDocLoader(this); } @@ -82,25 +81,36 @@ void DocLoader::checkForReload(const KURL& fullURL) if (fullURL.isEmpty()) return; + + if (m_reloadedURLs.contains(fullURL.string())) + return; + + CachedResource* existing = cache()->resourceForURL(fullURL.string()); + if (!existing || existing->isPreloaded()) + return; - if (m_cachePolicy == CachePolicyVerify || m_cachePolicy == CachePolicyCache) { - if (!m_reloadedURLs.contains(fullURL.string())) { - CachedResource* existing = cache()->resourceForURL(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()); - 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()); - } - } + switch (cachePolicy()) { + case CachePolicyVerify: + if (!existing->mustRevalidate(CachePolicyVerify)) + return; + cache()->revalidateResource(existing, this); + break; + case CachePolicyCache: + if (!existing->mustRevalidate(CachePolicyCache)) + return; + cache()->revalidateResource(existing, this); + break; + case CachePolicyReload: + cache()->remove(existing); + break; + case CachePolicyRevalidate: + cache()->revalidateResource(existing, this); + break; + default: + ASSERT_NOT_REACHED(); } + + m_reloadedURLs.add(fullURL.string()); } CachedImage* DocLoader::requestImage(const String& url) @@ -189,21 +199,18 @@ CachedResource* DocLoader::requestResource(CachedResource::Type type, const Stri { KURL fullURL = m_doc->completeURL(url); - if (!canRequest(type, fullURL)) + if (!fullURL.isValid() || !canRequest(type, fullURL)) return 0; if (cache()->disabled()) { - HashMap<String, CachedResource*>::iterator it = m_docResources.find(fullURL.string()); + DocumentResourceMap::iterator it = m_documentResources.find(fullURL.string()); - if (it != m_docResources.end()) { + if (it != m_documentResources.end()) { it->second->setDocLoader(0); - m_docResources.remove(it); + m_documentResources.remove(it); } } - if (frame() && frame()->loader()->isReloading()) - setCachePolicy(CachePolicyReload); - checkForReload(fullURL); CachedResource* resource = cache()->requestResource(this, type, fullURL, charset, isPreload); if (resource) { @@ -212,7 +219,7 @@ CachedResource* DocLoader::requestResource(CachedResource::Type type, const Stri if (!canRequest(type, KURL(resource->url()))) return 0; - m_docResources.set(resource->url(), resource); + m_documentResources.set(resource->url(), resource); checkCacheObjectStatus(resource); } return resource; @@ -252,9 +259,9 @@ void DocLoader::setAutoLoadImages(bool enable) if (!m_autoLoadImages) return; - HashMap<String, CachedResource*>::iterator end = m_docResources.end(); - for (HashMap<String, CachedResource*>::iterator it = m_docResources.begin(); it != end; ++it) { - CachedResource* resource = it->second; + DocumentResourceMap::iterator end = m_documentResources.end(); + for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) { + CachedResource* resource = it->second.get(); if (resource->type() == CachedResource::ImageResource) { CachedImage* image = const_cast<CachedImage*>(static_cast<const CachedImage*>(resource)); #ifdef ANDROID_BLOCK_NETWORK_IMAGE @@ -291,9 +298,9 @@ void DocLoader::setBlockNetworkImage(bool block) if (!m_autoLoadImages || m_blockNetworkImage) return; - HashMap<String, CachedResource*>::iterator end = m_docResources.end(); - for (HashMap<String, CachedResource*>::iterator it = m_docResources.begin(); it != end; ++it) { - CachedResource* resource = it->second; + DocumentResourceMap::iterator end = m_documentResources.end(); + for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) { + CachedResource* resource = it->second.get(); if (resource->type() == CachedResource::ImageResource) { CachedImage* image = const_cast<CachedImage*>(static_cast<const CachedImage*>(resource)); if (image->stillNeedsLoad()) @@ -303,14 +310,14 @@ void DocLoader::setBlockNetworkImage(bool block) } #endif -void DocLoader::setCachePolicy(CachePolicy cachePolicy) +CachePolicy DocLoader::cachePolicy() const { - m_cachePolicy = cachePolicy; + return frame() ? frame()->loader()->cachePolicy() : CachePolicyVerify; } void DocLoader::removeCachedResource(CachedResource* resource) const { - m_docResources.remove(resource->url()); + m_documentResources.remove(resource->url()); } void DocLoader::setLoadInProgress(bool load) diff --git a/WebCore/loader/DocLoader.h b/WebCore/loader/DocLoader.h index 407d85a..b87b622 100644 --- a/WebCore/loader/DocLoader.h +++ b/WebCore/loader/DocLoader.h @@ -26,6 +26,7 @@ #define DocLoader_h #include "CachedResource.h" +#include "CachedResourceHandle.h" #include "CachePolicy.h" #include "StringHash.h" #include <wtf/HashMap.h> @@ -70,8 +71,10 @@ public: // 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; } + CachedResource* cachedResource(const String& url) const { return m_documentResources.get(url).get(); } + + typedef HashMap<String, CachedResourceHandle<CachedResource> > DocumentResourceMap; + const DocumentResourceMap& allCachedResources() const { return m_documentResources; } bool autoLoadImages() const { return m_autoLoadImages; } void setAutoLoadImages(bool); @@ -82,8 +85,7 @@ public: bool shouldBlockNetworkImage(const String& url) const; #endif - CachePolicy cachePolicy() const { return m_cachePolicy; } - void setCachePolicy(CachePolicy); + CachePolicy cachePolicy() const; Frame* frame() const; // Can be NULL Document* doc() const { return m_doc; } @@ -118,8 +120,7 @@ private: Cache* m_cache; HashSet<String> m_reloadedURLs; - mutable HashMap<String, CachedResource*> m_docResources; - CachePolicy m_cachePolicy; + mutable DocumentResourceMap m_documentResources; Document* m_doc; int m_requestCount; diff --git a/WebCore/loader/DocumentLoader.cpp b/WebCore/loader/DocumentLoader.cpp index d9d99ea..12be864 100644 --- a/WebCore/loader/DocumentLoader.cpp +++ b/WebCore/loader/DocumentLoader.cpp @@ -99,9 +99,6 @@ static inline String canonicalizedTitle(const String& title, Frame* frame) continue; buffer[builderIndex++] = ' '; previousCharWasWS = true; - } else if (c == '\\') { - buffer[builderIndex++] = frame->backslashAsCurrencySymbol(); - previousCharWasWS = false; } else { buffer[builderIndex++] = c; previousCharWasWS = false; @@ -119,6 +116,11 @@ static inline String canonicalizedTitle(const String& title, Frame* frame) return ""; buffer.shrink(builderIndex + 1); + + // Replace the backslashes with currency symbols if the encoding requires it. + if (frame->document()) + frame->document()->displayBufferModifiedByEncoding(buffer.characters(), buffer.length()); + return String::adopt(buffer); } @@ -154,6 +156,7 @@ DocumentLoader::DocumentLoader(const ResourceRequest& req, const SubstituteData& , m_loadingFromCachedPage(false) , m_stopRecordingResponses(false) , m_substituteResourceDeliveryTimer(this, &DocumentLoader::substituteResourceDeliveryTimerFired) + , m_urlForHistoryReflectsClientRedirect(false) #if ENABLE(OFFLINE_WEB_APPLICATIONS) , m_candidateApplicationCacheGroup(0) #endif @@ -173,9 +176,9 @@ DocumentLoader::~DocumentLoader() #if ENABLE(OFFLINE_WEB_APPLICATIONS) if (m_applicationCache) - m_applicationCache->group()->documentLoaderDestroyed(this); + m_applicationCache->group()->disassociateDocumentLoader(this); else if (m_candidateApplicationCacheGroup) - m_candidateApplicationCacheGroup->documentLoaderDestroyed(this); + m_candidateApplicationCacheGroup->disassociateDocumentLoader(this); #endif } @@ -257,10 +260,14 @@ void DocumentLoader::clearErrors() void DocumentLoader::mainReceivedError(const ResourceError& error, bool isComplete) { + ASSERT(!error.isNull()); + #if ENABLE(OFFLINE_WEB_APPLICATIONS) ApplicationCacheGroup* group = m_candidateApplicationCacheGroup; - if (!group && m_applicationCache && !mainResourceApplicationCache()) + if (!group && m_applicationCache) { + ASSERT(!mainResourceApplicationCache()); // If the main resource were loaded from a cache, it wouldn't fail. group = m_applicationCache->group(); + } if (group) group->failedLoadingMainResource(this); @@ -452,7 +459,9 @@ void DocumentLoader::setPrimaryLoadComplete(bool flag) #endif m_mainResourceLoader = 0; } - updateLoading(); + + if (this == frameLoader()->activeDocumentLoader()) + updateLoading(); } } @@ -569,9 +578,9 @@ void DocumentLoader::getSubresources(Vector<PassRefPtr<ArchiveResource> >& subre 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) { + const DocLoader::DocumentResourceMap& allResources = document->docLoader()->allCachedResources(); + DocLoader::DocumentResourceMap::const_iterator end = allResources.end(); + for (DocLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) { RefPtr<ArchiveResource> subresource = this->subresource(KURL(it->second->url())); if (subresource) subresources.append(subresource.release()); @@ -696,6 +705,11 @@ KURL DocumentLoader::urlForHistory() const return m_originalRequestCopy.url(); } +bool DocumentLoader::urlForHistoryReflectsFailure() const +{ + return m_substituteData.isValid() || m_response.httpStatusCode() >= 400; +} + void DocumentLoader::loadFromCachedPage(PassRefPtr<CachedPage> cachedPage) { LOG(PageCache, "WebCorePageCache: DocumentLoader %p loading from cached page %p", this, cachedPage.get()); @@ -805,7 +819,7 @@ bool DocumentLoader::startLoadingMainResource(unsigned long identifier) // FIXME: Is there any way the extra fields could have not been added by now? // If not, it would be great to remove this line of code. - frameLoader()->addExtraFieldsToRequest(m_request, true, false); + frameLoader()->addExtraFieldsToMainResourceRequest(m_request); if (!m_mainResourceLoader->load(m_request, m_substituteData)) { // FIXME: If this should really be caught, we should just ASSERT this doesn't happen; @@ -855,20 +869,6 @@ void DocumentLoader::setApplicationCache(PassRefPtr<ApplicationCache> applicatio 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) @@ -880,23 +880,49 @@ ApplicationCache* DocumentLoader::mainResourceApplicationCache() const bool DocumentLoader::shouldLoadResourceFromApplicationCache(const ResourceRequest& request, ApplicationCacheResource*& resource) { - ApplicationCache* cache = topLevelApplicationCache(); - if (!cache) + ApplicationCache* cache = applicationCache(); + if (!cache || !cache->isComplete()) 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; - + + // If the resource's URL is an master entry, the manifest, an explicit entry, a fallback entry, or a dynamic entry + // in the application cache, then get the resource from the cache (instead of fetching it). resource = cache->resourceForURL(request.url()); + + // Resources that match fallback namespaces or online whitelist entries are fetched from the network, + // unless they are also cached. + if (!resource && (cache->urlMatchesFallbackNamespace(request.url()) || cache->isURLInOnlineWhitelist(request.url()))) + return false; + + // Resources that are not present in the manifest will always fail to load (at least, after the + // cache has been primed the first time), making the testing of offline applications simpler. + return true; +} + +bool DocumentLoader::getApplicationCacheFallbackResource(const ResourceRequest& request, ApplicationCacheResource*& resource, ApplicationCache* cache) +{ + if (!cache) { + cache = applicationCache(); + if (!cache) + return false; + } + if (!cache->isComplete()) + return false; - // Don't load foreign resources. - if (resource && (resource->type() & ApplicationCacheResource::Foreign)) - resource = 0; - + // If the resource is not a HTTP/HTTPS GET, then abort + if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) + return false; + + KURL fallbackURL; + if (!cache->urlMatchesFallbackNamespace(request.url(), &fallbackURL)) + return false; + + resource = cache->resourceForURL(fallbackURL); + ASSERT(resource); + return true; } @@ -910,7 +936,6 @@ bool DocumentLoader::scheduleApplicationCacheLoad(ResourceLoader* loader, const ApplicationCacheResource* resource; if (!shouldLoadResourceFromApplicationCache(request, resource)) - // FIXME: Handle opportunistic caching namespaces return false; m_pendingSubstituteResources.set(loader, resource); @@ -919,6 +944,21 @@ bool DocumentLoader::scheduleApplicationCacheLoad(ResourceLoader* loader, const return true; } +bool DocumentLoader::scheduleLoadFallbackResourceFromApplicationCache(ResourceLoader* loader, const ResourceRequest& request, ApplicationCache* cache) +{ + if (!frameLoader()->frame()->settings() || !frameLoader()->frame()->settings()->offlineWebApplicationCacheEnabled()) + return false; + + ApplicationCacheResource* resource; + if (!getApplicationCacheFallbackResource(request, resource, cache)) + 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 aef4f49..85cceef 100644 --- a/WebCore/loader/DocumentLoader.h +++ b/WebCore/loader/DocumentLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,25 +29,18 @@ #ifndef DocumentLoader_h #define DocumentLoader_h -#include "IconDatabase.h" #include "NavigationAction.h" -#include <wtf/RefCounted.h> -#include "PlatformString.h" #include "ResourceError.h" #include "ResourceRequest.h" #include "ResourceResponse.h" #include "SubstituteData.h" -#include <wtf/HashSet.h> -#include <wtf/RefPtr.h> -#include <wtf/Vector.h> +#include "Timer.h" 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; @@ -56,13 +49,10 @@ namespace WebCore { class CachedPage; class Frame; class FrameLoader; - class HistoryItem; - class KURL; class MainResourceLoader; class ResourceLoader; class SchedulePair; class SharedBuffer; - class SubstituteData; class SubstituteResource; typedef HashSet<RefPtr<ResourceLoader> > ResourceLoaderSet; @@ -167,7 +157,12 @@ namespace WebCore { void stopRecordingResponses(); const String& title() const { return m_pageTitle; } + KURL urlForHistory() const; + bool urlForHistoryReflectsFailure() const; + bool urlForHistoryReflectsServerRedirect() const { return urlForHistory() != url(); } + bool urlForHistoryReflectsClientRedirect() const { return m_urlForHistoryReflectsClientRedirect; } + void setURLForHistoryReflectsClientRedirect(bool b) { m_urlForHistoryReflectsClientRedirect = b; } void loadFromCachedPage(PassRefPtr<CachedPage>); void setLoadingFromCachedPage(bool loading) { m_loadingFromCachedPage = loading; } @@ -198,16 +193,22 @@ namespace WebCore { void setDeferMainResourceDataLoad(bool defer) { m_deferMainResourceDataLoad = defer; } bool deferMainResourceDataLoad() const { return m_deferMainResourceDataLoad; } + void didTellClientAboutLoad(const String& url) { m_resourcesClientKnowsAbout.add(url); } + bool haveToldClientAboutLoad(const String& url) { return m_resourcesClientKnowsAbout.contains(url); } + void recordMemoryCacheLoadForFutureClientNotification(const String& url); + void takeMemoryCacheLoadsForClientNotification(Vector<String>& loads); + #if ENABLE(OFFLINE_WEB_APPLICATIONS) bool scheduleApplicationCacheLoad(ResourceLoader*, const ResourceRequest&, const KURL& originalURL); + bool scheduleLoadFallbackResourceFromApplicationCache(ResourceLoader*, const ResourceRequest&, ApplicationCache* = 0); bool shouldLoadResourceFromApplicationCache(const ResourceRequest&, ApplicationCacheResource*&); + bool getApplicationCacheFallbackResource(const ResourceRequest&, ApplicationCacheResource*&, ApplicationCache* = 0); 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 @@ -292,7 +293,12 @@ namespace WebCore { OwnPtr<ArchiveResourceCollection> m_archiveResourceCollection; RefPtr<SharedBuffer> m_parsedArchiveData; #endif + + HashSet<String> m_resourcesClientKnowsAbout; + Vector<String> m_resourcesLoadedFromMemoryCacheForClientNotification; + bool m_urlForHistoryReflectsClientRedirect; + #if ENABLE(OFFLINE_WEB_APPLICATIONS) // The application cache that the document loader is associated with (if any). RefPtr<ApplicationCache> m_applicationCache; @@ -306,6 +312,17 @@ namespace WebCore { #endif }; + inline void DocumentLoader::recordMemoryCacheLoadForFutureClientNotification(const String& url) + { + m_resourcesLoadedFromMemoryCacheForClientNotification.append(url); + } + + inline void DocumentLoader::takeMemoryCacheLoadsForClientNotification(Vector<String>& loadsSet) + { + loadsSet.swap(m_resourcesLoadedFromMemoryCacheForClientNotification); + m_resourcesLoadedFromMemoryCacheForClientNotification.clear(); + } + } #endif // DocumentLoader_h diff --git a/WebCore/loader/DocumentThreadableLoader.cpp b/WebCore/loader/DocumentThreadableLoader.cpp new file mode 100644 index 0000000..05f8667 --- /dev/null +++ b/WebCore/loader/DocumentThreadableLoader.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2009, Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "DocumentThreadableLoader.h" + +#include "AuthenticationChallenge.h" +#include "Document.h" +#include "ResourceRequest.h" +#include "SecurityOrigin.h" +#include "SubresourceLoader.h" +#include "ThreadableLoaderClient.h" + +namespace WebCore { + +PassRefPtr<DocumentThreadableLoader> DocumentThreadableLoader::create(Document* document, ThreadableLoaderClient* client, const ResourceRequest& request, LoadCallbacks callbacksSetting, ContentSniff contentSniff) +{ + RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, client, request, callbacksSetting, contentSniff)); + if (!loader->m_loader) + loader = 0; + return loader.release(); +} + +DocumentThreadableLoader::DocumentThreadableLoader(Document* document, ThreadableLoaderClient* client, const ResourceRequest& request, LoadCallbacks callbacksSetting, ContentSniff contentSniff) + : m_client(client) + , m_document(document) +{ + ASSERT(document); + ASSERT(client); + m_loader = SubresourceLoader::create(document->frame(), this, request, false, callbacksSetting == SendLoadCallbacks, contentSniff == SniffContent); +} + +DocumentThreadableLoader::~DocumentThreadableLoader() +{ + if (m_loader) + m_loader->clearClient(); +} + +void DocumentThreadableLoader::cancel() +{ + if (!m_loader) + return; + + m_loader->cancel(); + m_loader->clearClient(); + m_loader = 0; + m_client = 0; +} + +void DocumentThreadableLoader::willSendRequest(SubresourceLoader*, ResourceRequest& request, const ResourceResponse&) +{ + ASSERT(m_client); + + // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. + if (!m_document->securityOrigin()->canRequest(request.url())) { + RefPtr<DocumentThreadableLoader> protect(this); + m_client->didFail(); + cancel(); + } +} + +void DocumentThreadableLoader::didSendData(SubresourceLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +{ + ASSERT(m_client); + m_client->didSendData(bytesSent, totalBytesToBeSent); +} + +void DocumentThreadableLoader::didReceiveResponse(SubresourceLoader*, const ResourceResponse& response) +{ + ASSERT(m_client); + m_client->didReceiveResponse(response); +} + +void DocumentThreadableLoader::didReceiveData(SubresourceLoader*, const char* data, int lengthReceived) +{ + ASSERT(m_client); + m_client->didReceiveData(data, lengthReceived); +} + +void DocumentThreadableLoader::didFinishLoading(SubresourceLoader* loader) +{ + ASSERT(loader); + ASSERT(m_client); + m_client->didFinishLoading(loader->identifier()); +} + +void DocumentThreadableLoader::didFail(SubresourceLoader*, const ResourceError& error) +{ + ASSERT(m_client); + if (error.isCancellation()) + m_client->didGetCancelled(); + else + m_client->didFail(); +} + +void DocumentThreadableLoader::receivedCancellation(SubresourceLoader*, const AuthenticationChallenge& challenge) +{ + ASSERT(m_client); + m_client->didReceiveAuthenticationCancellation(challenge.failureResponse()); +} + +} // namespace WebCore diff --git a/WebCore/loader/DocumentThreadableLoader.h b/WebCore/loader/DocumentThreadableLoader.h new file mode 100644 index 0000000..d909091 --- /dev/null +++ b/WebCore/loader/DocumentThreadableLoader.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2009, Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 DocumentThreadableLoader_h +#define DocumentThreadableLoader_h + +#include "SubresourceLoaderClient.h" +#include "ThreadableLoader.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + class Document; + class ResourceRequest; + class ThreadableLoaderClient; + + class DocumentThreadableLoader : public RefCounted<DocumentThreadableLoader>, public ThreadableLoader, private SubresourceLoaderClient { + public: + static PassRefPtr<DocumentThreadableLoader> create(Document*, ThreadableLoaderClient*, const ResourceRequest&, LoadCallbacks, ContentSniff); + virtual ~DocumentThreadableLoader(); + + virtual void cancel(); + + using RefCounted<DocumentThreadableLoader>::ref; + using RefCounted<DocumentThreadableLoader>::deref; + + protected: + virtual void refThreadableLoader() { ref(); } + virtual void derefThreadableLoader() { deref(); } + + private: + DocumentThreadableLoader(Document*, ThreadableLoaderClient*, const ResourceRequest&, LoadCallbacks, ContentSniff); + 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 lengthReceived); + virtual void didFinishLoading(SubresourceLoader*); + virtual void didFail(SubresourceLoader*, const ResourceError&); + + virtual void receivedCancellation(SubresourceLoader*, const AuthenticationChallenge&); + + RefPtr<SubresourceLoader> m_loader; + ThreadableLoaderClient* m_client; + Document* m_document; + }; + +} // namespace WebCore + +#endif // DocumentThreadableLoader_h diff --git a/WebCore/loader/EmptyClients.h b/WebCore/loader/EmptyClients.h index 8cab747..1a412f7 100644 --- a/WebCore/loader/EmptyClients.h +++ b/WebCore/loader/EmptyClients.h @@ -92,16 +92,16 @@ public: virtual void setResizable(bool) { } - virtual void addMessageToConsole(const String& message, unsigned int lineNumber, const String& sourceID) { } + virtual void addMessageToConsole(const String&, unsigned, const String&) { } virtual bool canRunBeforeUnloadConfirmPanel() { return false; } - virtual bool runBeforeUnloadConfirmPanel(const String& message, Frame* frame) { return true; } + virtual bool runBeforeUnloadConfirmPanel(const String&, 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 runJavaScriptPrompt(Frame*, const String&, const String&, String&) { return false; } virtual bool shouldInterruptJavaScript() { return false; } virtual void setStatusbarText(const String&) { } @@ -110,16 +110,17 @@ public: 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 scrollBackingStore(int, int, const IntRect&, const IntRect&) { } 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 void repaint(const IntRect&, bool, bool, bool) { } + virtual void scroll(const IntSize&, const IntRect&, const IntRect&) { } 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 contentsSizeChanged(Frame*, const IntSize&) const { } - virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned modifierFlags) { } + virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned) { } virtual void setToolTip(const String&) { } @@ -128,6 +129,8 @@ public: virtual void exceededDatabaseQuota(Frame*, const String&) { } virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>) { } + + virtual void formStateDidChange(const Node*) { } }; class EmptyFrameLoaderClient : public FrameLoaderClient { @@ -141,15 +144,6 @@ public: 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() { } @@ -157,37 +151,39 @@ public: 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 assignIdentifierToInitialRequest(unsigned long, DocumentLoader*, const ResourceRequest&) { } + virtual bool shouldUseCredentialStorage(DocumentLoader*, unsigned long) { return false; } + virtual void dispatchWillSendRequest(DocumentLoader*, unsigned long, ResourceRequest&, const ResourceResponse&) { } + virtual void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long, const AuthenticationChallenge&) { } + virtual void dispatchDidCancelAuthenticationChallenge(DocumentLoader*, unsigned long, const AuthenticationChallenge&) { } + virtual void dispatchDidReceiveResponse(DocumentLoader*, unsigned long, const ResourceResponse&) { } + virtual void dispatchDidReceiveContentLength(DocumentLoader*, unsigned long, int) { } + virtual void dispatchDidFinishLoading(DocumentLoader*, unsigned long) { } + virtual void dispatchDidFailLoading(DocumentLoader*, unsigned long, const ResourceError&) { } + virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int) { return false; } virtual void dispatchDidHandleOnloadEvents() { } virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() { } virtual void dispatchDidCancelClientRedirect() { } - virtual void dispatchWillPerformClientRedirect(const KURL&, double interval, double fireDate) { } + virtual void dispatchWillPerformClientRedirect(const KURL&, double, double) { } virtual void dispatchDidChangeLocationWithinPage() { } virtual void dispatchWillClose() { } virtual void dispatchDidReceiveIcon() { } virtual void dispatchDidStartProvisionalLoad() { } - virtual void dispatchDidReceiveTitle(const String& title) { } + virtual void dispatchDidReceiveTitle(const String&) { } virtual void dispatchDidCommitLoad() { } virtual void dispatchDidFailProvisionalLoad(const ResourceError&) { } virtual void dispatchDidFailLoad(const ResourceError&) { } virtual void dispatchDidFinishDocumentLoad() { } virtual void dispatchDidFinishLoad() { } virtual void dispatchDidFirstLayout() { } + virtual void dispatchDidFirstVisuallyNonEmptyLayout() { } 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 dispatchDecidePolicyForMIMEType(FramePolicyFunction, const String&, const ResourceRequest&) { } + virtual void dispatchDecidePolicyForNewWindowAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String&) { } virtual void dispatchDecidePolicyForNavigationAction(FramePolicyFunction, const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>) { } virtual void cancelPolicyCheck() { } @@ -227,48 +223,47 @@ public: 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 bool canShowMIMEType(const String&) const { return false; } + virtual bool representationExistsForURLScheme(const String&) const { return false; } + virtual String generatedMIMETypeForURLScheme(const String&) 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 void setTitle(const String&, const KURL&) { } virtual String userAgent(const KURL&) { return ""; } - virtual void savePlatformDataToCachedPage(CachedPage*) { } - virtual void transitionToCommittedFromCachedPage(CachedPage*) { } + virtual void savePlatformDataToCachedFrame(CachedFrame*) { } + virtual void transitionToCommittedFromCachedFrame(CachedFrame*) { } virtual void transitionToCommittedForNewPage() { } - virtual void updateGlobalHistory(const KURL&) { } + virtual void updateGlobalHistory() { } + virtual void updateGlobalHistoryForRedirectWithoutHistoryItem() { } 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 PassRefPtr<Frame> createFrame(const KURL&, const String&, HTMLFrameOwnerElement*, const String&, bool, int, int) { 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 ObjectContentType objectContentType(const KURL&, const String&) { return ObjectContentType(); } virtual String overrideMediaType() const { return String(); } - virtual void redirectDataToPlugin(Widget*) {} - virtual void windowObjectCleared() {} - virtual void didPerformFirstNavigation() const {} + virtual void redirectDataToPlugin(Widget*) { } + virtual void windowObjectCleared() { } + virtual void didPerformFirstNavigation() const { } - virtual void registerForIconNotification(bool listen) {} + virtual void registerForIconNotification(bool) { } #if PLATFORM(MAC) - virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long identifier, NSCachedURLResponse* response) const { return response; } + virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long, NSCachedURLResponse* response) const { return response; } #endif }; @@ -281,6 +276,7 @@ public: virtual bool shouldDeleteRange(Range*) { return false; } virtual bool shouldShowDeleteInterface(HTMLElement*) { return false; } virtual bool smartInsertDeleteEnabled() { return false; } + virtual bool isSelectTrailingWhitespaceEnabled() { return false; } virtual bool isContinuousSpellCheckingEnabled() { return false; } virtual void toggleContinuousSpellChecking() { } virtual bool isGrammarCheckingEnabled() { return false; } @@ -295,7 +291,7 @@ public: 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 shouldChangeSelectedRange(Range*, Range*, EAffinity, bool) { return false; } virtual bool shouldApplyStyle(CSSStyleDeclaration*, Range*) { return false; } virtual bool shouldMoveRangeAfterDelete(Range*, Range*) { return false; } @@ -342,14 +338,14 @@ public: #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 checkSpellingOfString(const UChar*, int, int*, int*) { } + virtual void checkGrammarOfString(const UChar*, int, Vector<GrammarDetail>&, int*, int*) { } virtual void updateSpellingUIWithGrammarString(const String&, const GrammarDetail&) { } virtual void updateSpellingUIWithMisspelledWord(const String&) { } - virtual void showSpellingUI(bool show) { } + virtual void showSpellingUI(bool) { } virtual bool spellingUIIsShowing() { return false; } - virtual void getGuessesForWord(const String&, Vector<String>& guesses) { } - virtual void setInputMethodState(bool enabled) { } + virtual void getGuessesForWord(const String&, Vector<String>&) { } + virtual void setInputMethodState(bool) { } }; @@ -362,7 +358,7 @@ public: virtual PlatformMenuDescription getCustomMenuFromDefaultItems(ContextMenu*) { return 0; } virtual void contextMenuItemSelected(ContextMenuItem*, const ContextMenu*) { } - virtual void downloadURL(const KURL& url) { } + virtual void downloadURL(const KURL&) { } virtual void copyImageToClipboard(const HitTestResult&) { } virtual void searchWithGoogle(const Frame*) { } virtual void lookUpInDictionary(Frame*) { } @@ -382,7 +378,7 @@ public: 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 DragImageRef createDragImageForLink(KURL&, const String&, Frame*) { return 0; } virtual void dragControllerDestroyed() { } }; @@ -406,11 +402,11 @@ public: virtual void highlight(Node*) { } virtual void hideHighlight() { } - virtual void inspectedURLChanged(const String& newURL) { } + virtual void inspectedURLChanged(const String&) { } - virtual void populateSetting(const String& key, InspectorController::Setting&) { } - virtual void storeSetting(const String& key, const InspectorController::Setting&) { } - virtual void removeSetting(const String& key) { } + virtual void populateSetting(const String&, InspectorController::Setting&) { } + virtual void storeSetting(const String&, const InspectorController::Setting&) { } + virtual void removeSetting(const String&) { } }; } diff --git a/WebCore/loader/FTPDirectoryDocument.cpp b/WebCore/loader/FTPDirectoryDocument.cpp index 0a7e91e..08ef896 100644 --- a/WebCore/loader/FTPDirectoryDocument.cpp +++ b/WebCore/loader/FTPDirectoryDocument.cpp @@ -38,6 +38,7 @@ #include "Settings.h" #include "SharedBuffer.h" #include "Text.h" +#include <wtf/StdLibExtras.h> #if PLATFORM(QT) #include <QDateTime> @@ -323,20 +324,22 @@ void FTPDirectoryTokenizer::parseAndAppendOneLine(const String& inputLine) appendEntry(filename, processFilesizeString(result.fileSize, result.type == FTPDirectoryEntry), processFileDateString(result.modifiedTime), result.type == FTPDirectoryEntry); } +static inline PassRefPtr<SharedBuffer> createTemplateDocumentData(Settings* settings) +{ + RefPtr<SharedBuffer> buffer = 0; + if (settings) + buffer = SharedBuffer::createWithContentsOfFile(settings->ftpDirectoryTemplatePath()); + if (buffer) + LOG(FTP, "Loaded FTPDirectoryTemplate of length %i\n", buffer->size()); + return buffer.release(); +} + bool FTPDirectoryTokenizer::loadDocumentTemplate() { - static RefPtr<SharedBuffer> templateDocumentData; + DEFINE_STATIC_LOCAL(RefPtr<SharedBuffer>, templateDocumentData, (createTemplateDocumentData(m_doc->settings()))); // FIXME: Instead of storing the data, we'd rather actually parse the template data into the template Document once, // store that document, then "copy" it whenever we get an FTP directory listing. There are complexities with this // approach that make it worth putting this off. - - if (!templateDocumentData) { - Settings* settings = m_doc->settings(); - if (settings) - templateDocumentData = SharedBuffer::createWithContentsOfFile(settings->ftpDirectoryTemplatePath()); - if (templateDocumentData) - LOG(FTP, "Loaded FTPDirectoryTemplate of length %i\n", templateDocumentData->size()); - } if (!templateDocumentData) { LOG_ERROR("Could not load templateData"); @@ -396,7 +399,7 @@ void FTPDirectoryTokenizer::createBasicDocument() bodyElement->appendChild(m_tableElement, ec); } -bool FTPDirectoryTokenizer::write(const SegmentedString& s, bool appendData) +bool FTPDirectoryTokenizer::write(const SegmentedString& s, bool /*appendData*/) { // Make sure we have the table element to append to by loading the template set in the pref, or // creating a very basic document with the appropriate table diff --git a/WebCore/loader/FrameLoader.cpp b/WebCore/loader/FrameLoader.cpp index 0285a8c..adaea61 100644 --- a/WebCore/loader/FrameLoader.cpp +++ b/WebCore/loader/FrameLoader.cpp @@ -1,6 +1,7 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -53,9 +54,9 @@ #include "Frame.h" #include "FrameLoadRequest.h" #include "FrameLoaderClient.h" -#include "FramePrivate.h" #include "FrameTree.h" #include "FrameView.h" +#include "HTMLAnchorElement.h" #include "HTMLFormElement.h" #include "HTMLFrameElement.h" #include "HTMLNames.h" @@ -72,24 +73,25 @@ #include "PageCache.h" #include "PageGroup.h" #include "PluginData.h" +#include "PluginDocument.h" #include "ProgressTracker.h" #include "RenderPart.h" -#include "RenderWidget.h" #include "RenderView.h" +#include "RenderWidget.h" #include "ResourceHandle.h" #include "ResourceRequest.h" +#include "ScriptController.h" +#include "ScriptSourceCode.h" +#include "ScriptValue.h" #include "SecurityOrigin.h" #include "SegmentedString.h" #include "Settings.h" -#include "SystemTime.h" #include "TextResourceDecoder.h" #include "WindowFeatures.h" #include "XMLHttpRequest.h" #include "XMLTokenizer.h" -#include "JSDOMBinding.h" -#include "ScriptController.h" -#include <runtime/JSLock.h> -#include <runtime/JSObject.h> +#include <wtf/CurrentTime.h> +#include <wtf/StdLibExtras.h> #if ENABLE(OFFLINE_WEB_APPLICATIONS) #include "ApplicationCache.h" @@ -108,10 +110,9 @@ #ifdef ANDROID_INSTRUMENT #include "TimeCounter.h" +#include "RenderArena.h" #endif -using namespace JSC; - namespace WebCore { #if ENABLE(SVG) @@ -123,26 +124,33 @@ using namespace HTMLNames; const unsigned int cMaxPendingSourceLengthInLowBandwidthDisplay = 128 * 1024; #endif +typedef HashSet<String, CaseFoldingHash> LocalSchemesMap; + struct FormSubmission { + FormSubmission(const char* action, const String& url, PassRefPtr<FormData> formData, + const String& target, const String& contentType, const String& boundary, + PassRefPtr<Event> event, bool lockHistory, bool lockBackForwardList) + : action(action) + , url(url) + , formData(formData) + , target(target) + , contentType(contentType) + , boundary(boundary) + , event(event) + , lockHistory(lockHistory) + , lockBackForwardList(lockBackForwardList) + { + } + const char* action; String url; - RefPtr<FormData> data; + RefPtr<FormData> formData; String target; String contentType; String boundary; RefPtr<Event> event; - - FormSubmission(const char* a, const String& u, PassRefPtr<FormData> d, const String& t, - const String& ct, const String& b, PassRefPtr<Event> e) - : action(a) - , url(u) - , data(d) - , target(t) - , contentType(ct) - , boundary(b) - , event(e) - { - } + bool lockHistory; + bool lockBackForwardList; }; struct ScheduledRedirection { @@ -153,28 +161,32 @@ struct ScheduledRedirection { String referrer; int historySteps; bool lockHistory; + bool lockBackForwardList; bool wasUserGesture; + bool wasRefresh; - ScheduledRedirection(double redirectDelay, const String& redirectURL, bool redirectLockHistory, bool userGesture) + ScheduledRedirection(double delay, const String& url, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh) : type(redirection) - , delay(redirectDelay) - , url(redirectURL) + , delay(delay) + , url(url) , historySteps(0) - , lockHistory(redirectLockHistory) - , wasUserGesture(userGesture) + , lockHistory(lockHistory) + , lockBackForwardList(lockBackForwardList) + , wasUserGesture(wasUserGesture) + , wasRefresh(refresh) { } - ScheduledRedirection(Type locationChangeType, - const String& locationChangeURL, const String& locationChangeReferrer, - bool locationChangeLockHistory, bool locationChangeWasUserGesture) + ScheduledRedirection(Type locationChangeType, const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh) : type(locationChangeType) , delay(0) - , url(locationChangeURL) - , referrer(locationChangeReferrer) + , url(url) + , referrer(referrer) , historySteps(0) - , lockHistory(locationChangeLockHistory) - , wasUserGesture(locationChangeWasUserGesture) + , lockHistory(lockHistory) + , lockBackForwardList(lockBackForwardList) + , wasUserGesture(wasUserGesture) + , wasRefresh(refresh) { } @@ -184,6 +196,7 @@ struct ScheduledRedirection { , historySteps(historyNavigationSteps) , lockHistory(false) , wasUserGesture(false) + , wasRefresh(false) { } }; @@ -191,26 +204,14 @@ struct ScheduledRedirection { static double storedTimeOfLastCompletedLoad; static FrameLoader::LocalLoadPolicy localLoadPolicy = FrameLoader::AllowLocalLoadsForLocalOnly; -static bool getString(JSValue* result, String& string) -{ - if (!result) - return false; - JSLock lock(false); - UString ustring; - if (!result->getString(ustring)) - return false; - string = ustring; - return true; -} - bool isBackForwardLoadType(FrameLoadType type) { switch (type) { case FrameLoadTypeStandard: case FrameLoadTypeReload: - case FrameLoadTypeReloadAllowingStaleData: + case FrameLoadTypeReloadFromOrigin: case FrameLoadTypeSame: - case FrameLoadTypeRedirectWithLockedHistory: + case FrameLoadTypeRedirectWithLockedBackForwardList: case FrameLoadTypeReplace: return false; case FrameLoadTypeBack: @@ -244,7 +245,6 @@ FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client) , m_sentRedirectNotification(false) , m_inStopAllLoaders(false) , m_navigationDuringLoad(false) - , m_cachePolicy(CachePolicyVerify) , m_isExecutingJavaScriptFormAction(false) , m_isRunningScript(false) , m_didCallImplicitClose(false) @@ -273,6 +273,9 @@ FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client) , m_finishedParsingDuringLowBandwidthDisplay(false) , m_needToSwitchOutLowBandwidthDisplay(false) #endif +#if ENABLE(WML) + , m_forceReloadWmlDeck(false) +#endif { } @@ -292,7 +295,7 @@ void FrameLoader::init() // this somewhat odd set of steps is needed to give the frame an initial empty document m_isDisplayingInitialEmptyDocument = false; m_creatingInitialEmptyDocument = true; - setPolicyDocumentLoader(m_client->createDocumentLoader(ResourceRequest(String("")), SubstituteData()).get()); + setPolicyDocumentLoader(m_client->createDocumentLoader(ResourceRequest(KURL("")), SubstituteData()).get()); setProvisionalDocumentLoader(m_policyDocumentLoader.get()); setState(FrameStateProvisional); m_provisionalDocumentLoader->setResponse(ResourceResponse(KURL(), "text/html", 0, String(), String())); @@ -322,7 +325,7 @@ Frame* FrameLoader::createWindow(FrameLoader* frameLoaderForFrameLookup, const F Frame* frame = frameLoaderForFrameLookup->frame()->tree()->find(request.frameName()); if (frame && shouldAllowNavigation(frame)) { if (!request.resourceRequest().url().isEmpty()) - frame->loader()->loadFrameRequestWithFormAndValues(request, false, 0, 0, HashMap<String, String>()); + frame->loader()->loadFrameRequestWithFormAndValues(request, false, false, 0, 0, HashMap<String, String>()); if (Page* page = frame->page()) page->chrome()->focus(); created = false; @@ -380,19 +383,17 @@ bool FrameLoader::canHandleRequest(const ResourceRequest& request) return m_client->canHandleRequest(request); } -void FrameLoader::changeLocation(const String& url, const String& referrer, bool lockHistory, bool userGesture) +void FrameLoader::changeLocation(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool userGesture, bool refresh) { - changeLocation(completeURL(url), referrer, lockHistory, userGesture); + changeLocation(completeURL(url), referrer, lockHistory, lockBackForwardList, userGesture, refresh); } -void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool userGesture) +void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool userGesture, bool refresh) { RefPtr<Frame> protect(m_frame); - ResourceRequestCachePolicy policy = (m_cachePolicy == CachePolicyReload) || (m_cachePolicy == CachePolicyRefresh) - ? ReloadIgnoringCacheData : UseProtocolCachePolicy; - ResourceRequest request(url, referrer, policy); + ResourceRequest request(url, referrer, refresh ? ReloadIgnoringCacheData : UseProtocolCachePolicy); #ifdef ANDROID_USER_GESTURE request.setUserGesture(userGesture); #endif @@ -400,20 +401,20 @@ void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool l if (executeIfJavaScriptURL(request.url(), userGesture)) return; - urlSelected(request, "_self", 0, lockHistory, userGesture); + urlSelected(request, "_self", 0, lockHistory, lockBackForwardList, userGesture); } -void FrameLoader::urlSelected(const FrameLoadRequest& request, Event* event, bool lockHistory) +void FrameLoader::urlSelected(const FrameLoadRequest& request, Event* event, bool lockHistory, bool lockBackForwardList) { 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>()); + loadFrameRequestWithFormAndValues(copy, lockHistory, lockBackForwardList, event, 0, HashMap<String, String>()); } -void FrameLoader::urlSelected(const ResourceRequest& request, const String& _target, Event* triggeringEvent, bool lockHistory, bool userGesture) +void FrameLoader::urlSelected(const ResourceRequest& request, const String& _target, Event* triggeringEvent, bool lockHistory, bool lockBackForwardList, bool userGesture) { if (executeIfJavaScriptURL(request.url(), userGesture, false)) return; @@ -427,7 +428,7 @@ void FrameLoader::urlSelected(const ResourceRequest& request, const String& _tar frameRequest.setWasUserGesture(userGesture); #endif - urlSelected(frameRequest, triggeringEvent, lockHistory); + urlSelected(frameRequest, triggeringEvent, lockHistory, lockBackForwardList); } bool FrameLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName) @@ -444,14 +445,14 @@ bool FrameLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String KURL scriptURL; KURL url; if (protocolIs(urlString, "javascript")) { - scriptURL = KURL(urlString); + scriptURL = completeURL(urlString); // completeURL() encodes the URL. url = blankURL(); } else url = completeURL(urlString); Frame* frame = ownerElement->contentFrame(); if (frame) - frame->loader()->scheduleLocationChange(url.string(), m_outgoingReferrer, true, userGestureHint()); + frame->loader()->scheduleLocationChange(url.string(), m_outgoingReferrer, true, true, userGestureHint()); else frame = loadSubframe(ownerElement, url, frameName, m_outgoingReferrer); @@ -518,13 +519,13 @@ void FrameLoader::submitFormAgain() if (m_isRunningScript) return; OwnPtr<FormSubmission> form(m_deferredFormSubmission.release()); - if (form) - submitForm(form->action, form->url, form->data, form->target, - form->contentType, form->boundary, form->event.get()); + if (!form) + return; + submitForm(form->action, form->url, form->formData, form->target, form->contentType, form->boundary, form->event.get(), form->lockHistory, form->lockBackForwardList); } void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<FormData> formData, - const String& target, const String& contentType, const String& boundary, Event* event) + const String& target, const String& contentType, const String& boundary, Event* event, bool lockHistory, bool lockBackForwardList) { ASSERT(formData); @@ -547,8 +548,7 @@ void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<F if (m_isRunningScript) { if (m_deferredFormSubmission) return; - m_deferredFormSubmission.set(new FormSubmission(action, url, formData, target, - contentType, boundary, event)); + m_deferredFormSubmission.set(new FormSubmission(action, url, formData, target, contentType, boundary, event, lockHistory, lockBackForwardList)); return; } @@ -592,7 +592,7 @@ void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<F frameRequest.resourceRequest().setURL(u); addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin()); - submitForm(frameRequest, event); + submitForm(frameRequest, event, lockHistory, lockBackForwardList); } void FrameLoader::stopLoading(bool sendUnload) @@ -623,7 +623,6 @@ void FrameLoader::stopLoading(bool sendUnload) m_isComplete = true; // to avoid calling completed() in finishedParsing() (David) m_isLoadingMainResource = false; m_didCallImplicitClose = true; // don't want that one either - m_cachePolicy = CachePolicyVerify; // Why here? if (m_frame->document() && m_frame->document()->parsing()) { finishedParsing(); @@ -636,8 +635,6 @@ void FrameLoader::stopLoading(bool sendUnload) if (DocLoader* docLoader = doc->docLoader()) cache()->loader()->cancelRequests(docLoader); - doc->stopActiveDOMObjects(); - #if ENABLE(DATABASE) doc->stopDatabases(); #endif @@ -769,10 +766,10 @@ bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool return false; String script = decodeURLEscapeSequences(url.string().substring(strlen("javascript:"))); - JSValue* result = executeScript(script, userGesture); + ScriptValue result = executeScript(script, userGesture); String scriptResult; - if (!getString(result, scriptResult)) + if (!result.getString(scriptResult)) return true; SecurityOrigin* currentSecurityOrigin = 0; @@ -791,20 +788,20 @@ bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool return true; } -JSValue* FrameLoader::executeScript(const String& script, bool forceUserGesture) +ScriptValue FrameLoader::executeScript(const String& script, bool forceUserGesture) { - return executeScript(forceUserGesture ? String() : m_URL.string(), 1, script); + return executeScript(ScriptSourceCode(script, forceUserGesture ? KURL() : m_URL)); } -JSValue* FrameLoader::executeScript(const String& url, int baseLine, const String& script) +ScriptValue FrameLoader::executeScript(const ScriptSourceCode& sourceCode) { if (!m_frame->script()->isEnabled() || m_frame->script()->isPaused()) - return noValue(); + return ScriptValue(); bool wasRunningScript = m_isRunningScript; m_isRunningScript = true; - JSValue* result = m_frame->script()->evaluate(url, baseLine, script); + ScriptValue result = m_frame->script()->evaluate(sourceCode); if (!wasRunningScript) { m_isRunningScript = false; @@ -823,14 +820,11 @@ void FrameLoader::cancelAndClear() closeURL(); clear(false); + m_frame->script()->updatePlatformScriptObjects(); } void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects) { - // FIXME: Commenting out the below line causes <http://bugs.webkit.org/show_bug.cgi?id=11212>, but putting it - // back causes a measurable performance regression which we will need to fix to restore the correct behavior - // urlsBridgeKnowsAbout.clear(); - m_frame->editor()->clear(); if (!m_needsClear) @@ -839,6 +833,7 @@ void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects) if (m_frame->document() && !m_frame->document()->inPageCache()) { m_frame->document()->cancelParsing(); + m_frame->document()->stopActiveDOMObjects(); if (m_frame->document()->attached()) { m_frame->document()->willRemove(); m_frame->document()->detach(); @@ -895,7 +890,6 @@ void FrameLoader::receivedFirstData() if (!ptitle.isNull()) m_client->dispatchDidReceiveTitle(ptitle); - m_frame->document()->docLoader()->setCachePolicy(m_cachePolicy); m_workingURL = KURL(); double delay; @@ -936,6 +930,8 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) bool resetScripting = !(m_isDisplayingInitialEmptyDocument && m_frame->document() && m_frame->document()->securityOrigin()->isSecureTransitionTo(url)); clear(resetScripting, resetScripting); + if (resetScripting) + m_frame->script()->updatePlatformScriptObjects(); if (dispatch) dispatchWindowObjectAvailable(); @@ -952,7 +948,12 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) m_outgoingReferrer = ref.string(); m_URL = url; - RefPtr<Document> document = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode()); + RefPtr<Document> document; + + if (!m_isDisplayingInitialEmptyDocument && m_client->shouldUsePluginDocument(m_responseMIMEType)) + document = PluginDocument::create(m_frame); + else + document = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode()); m_frame->setDocument(document); document->setURL(m_URL); @@ -1041,9 +1042,7 @@ void FrameLoader::write(const char* str, int len, bool flush) #if USE(LOW_BANDWIDTH_DISPLAY) if (m_frame->document()->inLowBandwidthDisplay()) - m_pendingSourceInLowBandwidthDisplay.append(decoded); - else // reset policy which is changed in switchOutLowBandwidthDisplayIfReady() - m_frame->document()->docLoader()->setCachePolicy(m_cachePolicy); + m_pendingSourceInLowBandwidthDisplay.append(decoded); #endif if (!m_receivedData) { @@ -1132,7 +1131,7 @@ void FrameLoader::startIconLoader() return; // If we're not reloading and the icon database doesn't say to load now then bail before we actually start the load - if (loadType() != FrameLoadTypeReload) { + if (loadType() != FrameLoadTypeReload && loadType() != FrameLoadTypeReloadFromOrigin) { IconLoadDecision decision = iconDatabase()->loadDecisionForIconURL(urlString, m_documentLoader.get()); if (decision == IconLoadNo) { LOG(IconDatabase, "FrameLoader::startIconLoader() - Told not to load this icon, committing iconURL %s to database for pageURL mapping", urlString.ascii().data()); @@ -1188,9 +1187,9 @@ bool FrameLoader::allowSubstituteDataAccessToLocal() return localLoadPolicy != FrameLoader::AllowLocalLoadsForLocalOnly; } -static HashSet<String, CaseFoldingHash>& localSchemes() +static LocalSchemesMap& localSchemes() { - static HashSet<String, CaseFoldingHash> localSchemes; + DEFINE_STATIC_LOCAL(LocalSchemesMap, localSchemes, ()); if (localSchemes.isEmpty()) { localSchemes.add("file"); @@ -1223,26 +1222,22 @@ void FrameLoader::restoreDocumentState() switch (loadType()) { case FrameLoadTypeReload: -#ifndef ANDROID_HISTORY_CLIENT - case FrameLoadTypeReloadAllowingStaleData: -#endif + case FrameLoadTypeReloadFromOrigin: case FrameLoadTypeSame: case FrameLoadTypeReplace: break; case FrameLoadTypeBack: case FrameLoadTypeForward: case FrameLoadTypeIndexedBackForward: - case FrameLoadTypeRedirectWithLockedHistory: + case FrameLoadTypeRedirectWithLockedBackForwardList: case FrameLoadTypeStandard: -#ifdef ANDROID_HISTORY_CLIENT - case FrameLoadTypeReloadAllowingStaleData: -#endif itemToRestore = m_currentHistoryItem.get(); } if (!itemToRestore) return; - + + LOG(Loading, "WebCoreLoading %s: restoring form state from %p", m_frame->tree()->name().string().utf8().data(), itemToRestore); doc->setStateForNewFormElements(itemToRestore->documentState()); } @@ -1274,6 +1269,8 @@ void FrameLoader::finishedParsing() // Null-checking the FrameView indicates whether or not we're in the destructor. RefPtr<Frame> protector = m_frame->view() ? m_frame : 0; + m_client->dispatchDidFinishDocumentLoad(); + checkCompleted(); if (!m_frame->view()) @@ -1283,8 +1280,6 @@ void FrameLoader::finishedParsing() // If not, remove them, relayout, and repaint. m_frame->view()->restoreScrollbar(); - m_client->dispatchDidFinishDocumentLoad(); - gotoAnchor(); } @@ -1409,14 +1404,14 @@ void FrameLoader::scheduleHTTPRedirection(double delay, const String& url) DocumentLoader* docLoader = activeDocumentLoader(); if (docLoader) wasUserGesture = docLoader->request().userGesture(); - scheduleRedirection(new ScheduledRedirection(delay, url, delay <= 1, wasUserGesture)); + scheduleRedirection(new ScheduledRedirection(delay, url, true, delay <= 1, wasUserGesture, false)); } #else - scheduleRedirection(new ScheduledRedirection(delay, url, delay <= 1, false)); + scheduleRedirection(new ScheduledRedirection(delay, url, true, delay <= 1, false, false)); #endif } -void FrameLoader::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool wasUserGesture) +void FrameLoader::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool wasUserGesture) { if (!m_frame->page()) return; @@ -1425,7 +1420,7 @@ void FrameLoader::scheduleLocationChange(const String& url, const String& referr // fragment part, we don't need to schedule the location change. KURL parsedURL(url); if (parsedURL.hasRef() && equalIgnoringRef(m_URL, parsedURL)) { - changeLocation(url, referrer, lockHistory, wasUserGesture); + changeLocation(url, referrer, lockHistory, lockBackForwardList, wasUserGesture); return; } @@ -1444,7 +1439,7 @@ void FrameLoader::scheduleLocationChange(const String& url, const String& referr ScheduledRedirection::Type type = duringLoad ? ScheduledRedirection::locationChangeDuringLoad : ScheduledRedirection::locationChange; - scheduleRedirection(new ScheduledRedirection(type, url, referrer, lockHistory, wasUserGesture)); + scheduleRedirection(new ScheduledRedirection(type, url, referrer, lockHistory, lockBackForwardList, wasUserGesture, false)); } void FrameLoader::scheduleRefresh(bool wasUserGesture) @@ -1464,8 +1459,7 @@ void FrameLoader::scheduleRefresh(bool wasUserGesture) ScheduledRedirection::Type type = duringLoad ? ScheduledRedirection::locationChangeDuringLoad : ScheduledRedirection::locationChange; - scheduleRedirection(new ScheduledRedirection(type, m_URL.string(), m_outgoingReferrer, true, wasUserGesture)); - m_cachePolicy = CachePolicyRefresh; + scheduleRedirection(new ScheduledRedirection(type, m_URL.string(), m_outgoingReferrer, true, true, wasUserGesture, true)); } bool FrameLoader::isLocationChange(const ScheduledRedirection& redirection) @@ -1549,12 +1543,12 @@ void FrameLoader::redirectionTimerFired(Timer<FrameLoader>*) case ScheduledRedirection::locationChange: case ScheduledRedirection::locationChangeDuringLoad: changeLocation(redirection->url, redirection->referrer, - redirection->lockHistory, redirection->wasUserGesture); + redirection->lockHistory, redirection->lockBackForwardList, redirection->wasUserGesture, redirection->wasRefresh); return; case ScheduledRedirection::historyNavigation: if (redirection->historySteps == 0) { // Special case for go(0) from a frame -> reload only the frame - urlSelected(m_URL, "", 0, redirection->lockHistory, redirection->wasUserGesture); + urlSelected(m_URL, "", 0, redirection->lockHistory, redirection->lockBackForwardList, redirection->wasUserGesture); return; } // go(i!=0) from a frame navigates into the history of the frame only, @@ -1577,32 +1571,21 @@ void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, ASSERT(childFrame); HistoryItem* parentItem = currentHistoryItem(); FrameLoadType loadType = this->loadType(); - FrameLoadType childLoadType = FrameLoadTypeRedirectWithLockedHistory; + FrameLoadType childLoadType = FrameLoadTypeRedirectWithLockedBackForwardList; 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)) - { + if (parentItem && parentItem->children().size() && isBackForwardLoadType(loadType)) { 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); - } + childFrame->loader()->setProvisionalHistoryItem(childItem); } } @@ -1614,9 +1597,9 @@ void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, else #endif #ifdef ANDROID_USER_GESTURE - childFrame->loader()->loadURL(workingURL, referer, String(), childLoadType, 0, 0, false); + childFrame->loader()->loadURL(workingURL, referer, String(), false, childLoadType, 0, 0, false); #else - childFrame->loader()->loadURL(workingURL, referer, String(), childLoadType, 0, 0); + childFrame->loader()->loadURL(workingURL, referer, String(), false, childLoadType, 0, 0); #endif } @@ -1664,9 +1647,7 @@ bool FrameLoader::gotoAnchor(const String& name) m_frame->document()->setGotoAnchorNeededAfterStylesheetsLoad(false); - Node* anchorNode = m_frame->document()->getElementById(AtomicString(name)); - if (!anchorNode && !name.isEmpty()) - anchorNode = m_frame->document()->anchors()->namedItem(name, !m_frame->document()->inCompatMode()); + Element* anchorNode = m_frame->document()->findAnchor(name); #if ENABLE(SVG) if (m_frame->document()->isSVGDocument()) { @@ -1758,10 +1739,16 @@ bool FrameLoader::requestObject(RenderPart* renderer, const String& url, const A bool FrameLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback) { + if (m_client->shouldUsePluginDocument(mimeType)) { + useFallback = false; + return true; + } + // 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 (m_frame->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) { - String pluginName = m_frame->page()->pluginData()->pluginNameForMimeType(mimeType); + const PluginData* pluginData = m_frame->page()->pluginData(); + String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String(); if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false)) return true; } @@ -1896,14 +1883,10 @@ void FrameLoader::provisionalLoadStarted() bool FrameLoader::userGestureHint() { - Frame* rootFrame = m_frame; - while (rootFrame->tree()->parent()) - rootFrame = rootFrame->tree()->parent(); - - if (rootFrame->script()->isEnabled()) - return rootFrame->script()->processingUserGesture(); - - return true; // If JavaScript is disabled, a user gesture must have initiated the navigation + Frame* frame = m_frame->tree()->top(); + if (!frame->script()->isEnabled()) + return true; // If JavaScript is disabled, a user gesture must have initiated the navigation. + return frame->script()->processingUserGesture(); // FIXME: Use pageIsProcessingUserGesture. } void FrameLoader::didNotOpenURL(const KURL& url) @@ -1933,23 +1916,13 @@ void FrameLoader::addData(const char* bytes, int length) write(bytes, length); } -bool FrameLoader::canCachePage() -{ - // Cache the page, if possible. - // Don't write to the cache if in the middle of a redirect, since we will want to - // store the final page we end up on. - // No point writing to the cache on a reload or loadSame, since we will just write - // over it again when we leave that page. - // FIXME: <rdar://problem/4886592> - We should work out the complexities of caching pages with frames as they - // are the most interesting pages on the web, and often those that would benefit the most from caching! - FrameLoadType loadType = this->loadType(); - +bool FrameLoader::canCachePageContainingThisFrame() +{ return m_documentLoader && m_documentLoader->mainDocumentError().isNull() && !m_frame->tree()->childCount() - && !m_frame->tree()->parent() - // FIXME: If we ever change this so that pages with plug-ins will be cached, - // we need to make sure that we don't cache pages that have outstanding NPObjects + // FIXME: If we ever change this so that frames with plug-ins will be cached, + // we need to make sure that we don't cache frames that have outstanding NPObjects // (objects created by the plug-in). Since there is no way to pause/resume a Netscape plug-in, // 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. @@ -1961,26 +1934,187 @@ bool FrameLoader::canCachePage() && !m_frame->document()->hasOpenDatabases() #endif && !m_frame->document()->usingGeolocation() - && m_frame->page() - && m_frame->page()->backForwardList()->enabled() - && m_frame->page()->backForwardList()->capacity() > 0 - && m_frame->page()->settings()->usesPageCache() && m_currentHistoryItem && !isQuickRedirectComing() - && loadType != FrameLoadTypeReload - && loadType != FrameLoadTypeReloadAllowingStaleData - && loadType != FrameLoadTypeSame && !m_documentLoader->isLoadingInAPISense() && !m_documentLoader->isStopping() + && m_frame->document()->canSuspendActiveDOMObjects() #if ENABLE(OFFLINE_WEB_APPLICATIONS) - // FIXME: We should investigating caching pages that have an associated + // FIXME: We should investigating caching frames that have an associated // application cache. <rdar://problem/5917899> tracks that work. && !m_documentLoader->applicationCache() && !m_documentLoader->candidateApplicationCacheGroup() #endif + && m_client->canCachePage() ; } +bool FrameLoader::canCachePage() +{ +#ifndef NDEBUG + logCanCachePageDecision(); +#endif + + // Cache the page, if possible. + // Don't write to the cache if in the middle of a redirect, since we will want to + // store the final page we end up on. + // No point writing to the cache on a reload or loadSame, since we will just write + // over it again when we leave that page. + // FIXME: <rdar://problem/4886592> - We should work out the complexities of caching pages with frames as they + // are the most interesting pages on the web, and often those that would benefit the most from caching! + FrameLoadType loadType = this->loadType(); + + return !m_frame->tree()->parent() + && canCachePageContainingThisFrame() + && m_frame->page() + && m_frame->page()->backForwardList()->enabled() + && m_frame->page()->backForwardList()->capacity() > 0 + && m_frame->page()->settings()->usesPageCache() + && loadType != FrameLoadTypeReload + && loadType != FrameLoadTypeReloadFromOrigin + && loadType != FrameLoadTypeSame + ; +} + +#ifndef NDEBUG +static String& pageCacheLogPrefix(int indentLevel) +{ + static int previousIndent = -1; + DEFINE_STATIC_LOCAL(String, prefix, ()); + + if (indentLevel != previousIndent) { + previousIndent = indentLevel; + prefix.truncate(0); + for (int i = 0; i < previousIndent; ++i) + prefix += " "; + } + + return prefix; +} + +static void pageCacheLog(const String& prefix, const String& message) +{ + LOG(PageCache, "%s%s", prefix.utf8().data(), message.utf8().data()); +} + +#define PCLOG(...) pageCacheLog(pageCacheLogPrefix(indentLevel), String::format(__VA_ARGS__)) + +void FrameLoader::logCanCachePageDecision() +{ + // Only bother logging for main frames that have actually loaded and have content. + if (m_creatingInitialEmptyDocument) + return; + KURL currentURL = m_documentLoader ? m_documentLoader->url() : KURL(); + if (currentURL.isEmpty()) + return; + + int indentLevel = 0; + PCLOG("--------\n Determining if page can be cached:"); + + bool cannotCache = !logCanCacheFrameDecision(1); + + FrameLoadType loadType = this->loadType(); + do { + if (m_frame->tree()->parent()) + { PCLOG(" -Frame has a parent frame"); cannotCache = true; } + if (!m_frame->page()) { + PCLOG(" -There is no Page object"); + cannotCache = true; + break; + } + if (!m_frame->page()->backForwardList()->enabled()) + { PCLOG(" -The back/forward list is disabled"); cannotCache = true; } + if (!(m_frame->page()->backForwardList()->capacity() > 0)) + { PCLOG(" -The back/forward list has a 0 capacity"); cannotCache = true; } + if (!m_frame->page()->settings()->usesPageCache()) + { PCLOG(" -Page settings says b/f cache disabled"); cannotCache = true; } + if (loadType == FrameLoadTypeReload) + { PCLOG(" -Load type is: Reload"); cannotCache = true; } + if (loadType == FrameLoadTypeReloadFromOrigin) + { PCLOG(" -Load type is: Reload from origin"); cannotCache = true; } + if (loadType == FrameLoadTypeSame) + { PCLOG(" -Load type is: Same"); cannotCache = true; } + } while (false); + + PCLOG(cannotCache ? " Page CANNOT be cached\n--------" : " Page CAN be cached\n--------"); +} + +bool FrameLoader::logCanCacheFrameDecision(int indentLevel) +{ + // Only bother logging for frames that have actually loaded and have content. + if (m_creatingInitialEmptyDocument) + return false; + KURL currentURL = m_documentLoader ? m_documentLoader->url() : KURL(); + if (currentURL.isEmpty()) + return false; + + PCLOG("+---"); + KURL newURL = m_provisionalDocumentLoader ? m_provisionalDocumentLoader->url() : KURL(); + if (!newURL.isEmpty()) + PCLOG(" Determining if frame can be cached navigating from (%s) to (%s):", currentURL.string().utf8().data(), newURL.string().utf8().data()); + else + PCLOG(" Determining if subframe with URL (%s) can be cached:", currentURL.string().utf8().data()); + + bool cannotCache = false; + + do { + if (!m_documentLoader) { + PCLOG(" -There is no DocumentLoader object"); + cannotCache = true; + break; + } + if (!m_documentLoader->mainDocumentError().isNull()) + { PCLOG(" -Main document has an error"); cannotCache = true; } + if (m_frame->tree()->childCount()) + { PCLOG(" -Frame has child frames"); cannotCache = true; } + if (m_containsPlugIns) + { PCLOG(" -Frame contains plugins"); cannotCache = true; } + if (m_URL.protocolIs("https")) + { PCLOG(" -Frame is HTTPS"); cannotCache = true; } + if (!m_frame->document()) { + PCLOG(" -There is no Document object"); + cannotCache = true; + break; + } + if (m_frame->document()->hasWindowEventListener(eventNames().unloadEvent)) + { PCLOG(" -Frame has an unload event listener"); cannotCache = true; } +#if ENABLE(DATABASE) + if (m_frame->document()->hasOpenDatabases()) + { PCLOG(" -Frame has open database handles"); cannotCache = true; } +#endif + if (m_frame->document()->usingGeolocation()) + { PCLOG(" -Frame uses Geolocation"); cannotCache = true; } + if (!m_currentHistoryItem) + { PCLOG(" -No current history item"); cannotCache = true; } + if (isQuickRedirectComing()) + { PCLOG(" -Quick redirect is coming"); cannotCache = true; } + if (m_documentLoader->isLoadingInAPISense()) + { PCLOG(" -DocumentLoader is still loading in API sense"); cannotCache = true; } + if (m_documentLoader->isStopping()) + { PCLOG(" -DocumentLoader is in the middle of stopping"); cannotCache = true; } + if (!m_frame->document()->canSuspendActiveDOMObjects()) + { PCLOG(" -The document cannot suspect its active DOM Objects"); cannotCache = true; } +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (m_documentLoader->applicationCache()) + { PCLOG(" -The DocumentLoader has an active application cache"); cannotCache = true; } + if (m_documentLoader->candidateApplicationCacheGroup()) + { PCLOG(" -The DocumentLoader has a candidateApplicationCacheGroup"); cannotCache = true; } +#endif + if (!m_client->canCachePage()) + { PCLOG(" -The client says this frame cannot be cached"); cannotCache = true; } + } while (false); + + for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) + if (!child->loader()->logCanCacheFrameDecision(indentLevel + 1)) + cannotCache = true; + + PCLOG(cannotCache ? " Frame CANNOT be cached" : " Frame CAN be cached"); + PCLOG("+---"); + + return !cannotCache; +} +#endif + void FrameLoader::updatePolicyBaseURL() { if (m_frame->tree()->parent() && m_frame->tree()->parent()->document()) @@ -2047,7 +2181,7 @@ void FrameLoader::startRedirectionTimer() clientRedirected(KURL(m_scheduledRedirection->url), m_scheduledRedirection->delay, currentTime() + m_redirectionTimer.nextFireInterval(), - m_scheduledRedirection->lockHistory, + m_scheduledRedirection->lockBackForwardList, m_isExecutingJavaScriptFormAction); return; case ScheduledRedirection::historyNavigation: @@ -2120,18 +2254,22 @@ void FrameLoader::setupForReplaceByMIMEType(const String& newMIMEType) activeDocumentLoader()->setupForReplaceByMIMEType(newMIMEType); } -void FrameLoader::loadFrameRequestWithFormState(const FrameLoadRequest& request, bool lockHistory, Event* event, PassRefPtr<FormState> prpFormState) +void FrameLoader::loadFrameRequestWithFormAndValues(const FrameLoadRequest& request, bool lockHistory, bool lockBackForwardList, Event* event, + HTMLFormElement* submitForm, const HashMap<String, String>& formValues) { - RefPtr<FormState> formState = prpFormState; + RefPtr<FormState> formState; + if (submitForm) + formState = FormState::create(submitForm, formValues, m_frame); + KURL url = request.resourceRequest().url(); - + String referrer; String argsReferrer = request.resourceRequest().httpReferrer(); if (!argsReferrer.isEmpty()) referrer = argsReferrer; else referrer = m_outgoingReferrer; - + ASSERT(frame()->document()); if (url.protocolIs("file")) { if (!canLoad(url, String(), frame()->document()) && !canLoad(url, referrer)) { @@ -2143,51 +2281,38 @@ void FrameLoader::loadFrameRequestWithFormState(const FrameLoadRequest& request, if (shouldHideReferrer(url, referrer)) referrer = String(); - Frame* targetFrame = findFrameForNavigation(request.frameName()); + FrameLoadType loadType; + if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData) + loadType = FrameLoadTypeReload; + else if (lockBackForwardList) + loadType = FrameLoadTypeRedirectWithLockedBackForwardList; + else + loadType = FrameLoadTypeStandard; - if (request.resourceRequest().httpMethod() != "POST") { - FrameLoadType loadType; - if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData) - loadType = FrameLoadTypeReload; - else if (lockHistory) - loadType = FrameLoadTypeRedirectWithLockedHistory; - else - loadType = FrameLoadTypeStandard; - + if (request.resourceRequest().httpMethod() == "POST") #ifdef ANDROID_USER_GESTURE - loadURL(request.resourceRequest().url(), referrer, request.frameName(), loadType, - event, formState.release(), request.wasUserGesture()); + loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.release(), request.wasUserGesture()); #else - loadURL(request.resourceRequest().url(), referrer, request.frameName(), loadType, - event, formState.release()); + loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.release()); #endif - } else + else #ifdef ANDROID_USER_GESTURE - loadPostRequest(request.resourceRequest(), referrer, request.frameName(), event, formState.release(), request.wasUserGesture()); + loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.release(), request.wasUserGesture()); #else - loadPostRequest(request.resourceRequest(), referrer, request.frameName(), event, formState.release()); + loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.release()); #endif + Frame* targetFrame = findFrameForNavigation(request.frameName()); if (targetFrame && targetFrame != m_frame) if (Page* page = targetFrame->page()) 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::loadURL(const KURL& newURL, const String& referrer, const String& frameName, FrameLoadType newLoadType, +void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType, Event* event, PassRefPtr<FormState> prpFormState, bool userGesture) #else -void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, FrameLoadType newLoadType, +void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType, Event* event, PassRefPtr<FormState> prpFormState) #endif { @@ -2203,8 +2328,8 @@ void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const Stri RefPtr<SecurityOrigin> referrerOrigin = SecurityOrigin::createFromString(referrer); addHTTPOriginIfNeeded(request, referrerOrigin->toString()); } - addExtraFieldsToRequest(request, true, event || isFormSubmission); - if (newLoadType == FrameLoadTypeReload) + addExtraFieldsToRequest(request, newLoadType, true, event || isFormSubmission); + if (newLoadType == FrameLoadTypeReload || newLoadType == FrameLoadTypeReloadFromOrigin) request.setCachePolicy(ReloadIgnoringCacheData); ASSERT(newLoadType != FrameLoadTypeSame); @@ -2214,9 +2339,9 @@ void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const Stri if (!frameName.isEmpty()) { if (Frame* targetFrame = findFrameForNavigation(frameName)) #ifdef ANDROID_USER_GESTURE - targetFrame->loader()->loadURL(newURL, referrer, String(), newLoadType, event, formState, userGesture); + targetFrame->loader()->loadURL(newURL, referrer, String(), lockHistory, newLoadType, event, formState, userGesture); #else - targetFrame->loader()->loadURL(newURL, referrer, String(), newLoadType, event, formState); + targetFrame->loader()->loadURL(newURL, referrer, String(), lockHistory, newLoadType, event, formState); #endif else checkNewWindowPolicy(action, request, formState, frameName); @@ -2238,16 +2363,12 @@ void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const Stri } else { // must grab this now, since this load may stop the previous load and clear this flag bool isRedirect = m_quickRedirectComing; - loadWithNavigationAction(request, action, newLoadType, formState); + loadWithNavigationAction(request, action, lockHistory, newLoadType, formState); if (isRedirect) { m_quickRedirectComing = false; if (m_provisionalDocumentLoader) m_provisionalDocumentLoader->setIsClientRedirect(true); -#ifdef ANDROID_HISTORY_CLIENT - } else if (sameURL && (newLoadType != FrameLoadTypeReloadAllowingStaleData)) -#else } else if (sameURL) -#endif // Example of this case are sites that reload the same URL with a different cookie // driving the generated content, or a master frame with links that drive a target // frame, where the user has clicked on the same link repeatedly. @@ -2255,40 +2376,43 @@ void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const Stri } } -void FrameLoader::load(const ResourceRequest& request) +void FrameLoader::load(const ResourceRequest& request, bool lockHistory) { - load(request, SubstituteData()); + load(request, SubstituteData(), lockHistory); } -void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData) +void FrameLoader::load(const ResourceRequest& request, const SubstituteData& substituteData, bool lockHistory) { if (m_inStopAllLoaders) return; // FIXME: is this the right place to reset loadType? Perhaps this should be done after loading is finished or aborted. m_loadType = FrameLoadTypeStandard; - load(m_client->createDocumentLoader(request, substituteData).get()); + RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, substituteData); + loader->setURLForHistoryReflectsClientRedirect(lockHistory); + load(loader.get()); } -void FrameLoader::load(const ResourceRequest& request, const String& frameName) +void FrameLoader::load(const ResourceRequest& request, const String& frameName, bool lockHistory) { if (frameName.isEmpty()) { - load(request); + load(request, lockHistory); return; } Frame* frame = findFrameForNavigation(frameName); if (frame) { - frame->loader()->load(request); + frame->loader()->load(request, lockHistory); return; } checkNewWindowPolicy(NavigationAction(request.url(), NavigationTypeOther), request, 0, frameName); } -void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, FrameLoadType type, PassRefPtr<FormState> formState) +void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, bool lockHistory, FrameLoadType type, PassRefPtr<FormState> formState) { RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData()); + loader->setURLForHistoryReflectsClientRedirect(lockHistory); loader->setTriggeringAction(action); if (m_documentLoader) @@ -2300,7 +2424,7 @@ void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const void FrameLoader::load(DocumentLoader* newDocumentLoader) { ResourceRequest& r = newDocumentLoader->request(); - addExtraFieldsToRequest(r, true, false); + addExtraFieldsToMainResourceRequest(r); FrameLoadType type; if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->originalRequest().url())) { @@ -2483,7 +2607,7 @@ bool FrameLoader::shouldReloadToHandleUnreachableURL(DocumentLoader* docLoader) return compareDocumentLoader && unreachableURL == compareDocumentLoader->request().url(); } -void FrameLoader::reloadAllowingStaleData(const String& encoding) +void FrameLoader::reloadWithOverrideEncoding(const String& encoding) { if (!m_documentLoader) return; @@ -2500,25 +2624,25 @@ void FrameLoader::reloadAllowingStaleData(const String& encoding) loader->setOverrideEncoding(encoding); - loadWithDocumentLoader(loader.get(), FrameLoadTypeReloadAllowingStaleData, 0); + loadWithDocumentLoader(loader.get(), FrameLoadTypeReload, 0); } -void FrameLoader::reload() +void FrameLoader::reload(bool endToEndReload) { if (!m_documentLoader) return; - ResourceRequest& initialRequest = m_documentLoader->request(); - // If a window is created by javascript, its main frame can have an empty but non-nil URL. // Reloading in this case will lose the current contents (see 4151001). - if (initialRequest.url().isEmpty()) + if (m_documentLoader->request().url().isEmpty()) return; + ResourceRequest initialRequest = m_documentLoader->request(); + // Replace error-page URL with the URL we were trying to reach. KURL unreachableURL = m_documentLoader->unreachableURL(); if (!unreachableURL.isEmpty()) - initialRequest = ResourceRequest(unreachableURL); + initialRequest.setURL(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. @@ -2526,8 +2650,8 @@ void FrameLoader::reload() ResourceRequest& request = loader->request(); + // FIXME: We don't have a mechanism to revalidate the main resource without reloading at the moment. request.setCachePolicy(ReloadIgnoringCacheData); - request.setHTTPHeaderField("Cache-Control", "max-age=0"); // If we're about to re-post, set up action so the application can warn the user. if (request.httpMethod() == "POST") @@ -2535,7 +2659,7 @@ void FrameLoader::reload() loader->setOverrideEncoding(m_documentLoader->overrideEncoding()); - loadWithDocumentLoader(loader.get(), FrameLoadTypeReload, 0); + loadWithDocumentLoader(loader.get(), endToEndReload ? FrameLoadTypeReloadFromOrigin : FrameLoadTypeReload, 0); } static bool canAccessAncestor(const SecurityOrigin* activeSecurityOrigin, Frame* targetFrame) @@ -2768,19 +2892,15 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) { RefPtr<CachedPage> cachedPage = prpCachedPage; RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader; - + + LOG(Loading, "WebCoreLoading %s: About to commit provisional load from previous URL %s", m_frame->tree()->name().string().utf8().data(), m_URL.string().utf8().data()); + // Check to see if we need to cache the page we are navigating away from into the back/forward cache. // 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()) + if (canCachePage() && !m_currentHistoryItem->isInPageCache()) { + if (Document* document = m_frame->document()) + document->suspendActiveDOMObjects(); 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) @@ -2812,6 +2932,9 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) didOpenURL(url); } + + LOG(Loading, "WebCoreLoading %s: Finished committing provisional load to URL %s", m_frame->tree()->name().string().utf8().data(), m_URL.string().utf8().data()); + opened(); } @@ -2862,7 +2985,7 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) DocumentLoader* cachedDocumentLoader = cachedPage->documentLoader(); ASSERT(cachedDocumentLoader); cachedDocumentLoader->setFrame(m_frame); - m_client->transitionToCommittedFromCachedPage(cachedPage.get()); + m_client->transitionToCommittedFromCachedFrame(cachedPage->cachedMainFrame()); } else m_client->transitionToCommittedForNewPage(); @@ -2870,17 +2993,13 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) break; case FrameLoadTypeReload: + case FrameLoadTypeReloadFromOrigin: case FrameLoadTypeSame: case FrameLoadTypeReplace: updateHistoryForReload(); m_client->transitionToCommittedForNewPage(); break; - // FIXME - just get rid of this case, and merge FrameLoadTypeReloadAllowingStaleData with the above case - case FrameLoadTypeReloadAllowingStaleData: - m_client->transitionToCommittedForNewPage(); - break; - case FrameLoadTypeStandard: updateHistoryForStandardLoad(); #ifndef BUILDING_ON_TIGER @@ -2892,8 +3011,8 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) m_client->transitionToCommittedForNewPage(); break; - case FrameLoadTypeRedirectWithLockedHistory: - updateHistoryForRedirectWithLockedHistory(); + case FrameLoadTypeRedirectWithLockedBackForwardList: + updateHistoryForRedirectWithLockedBackForwardList(); m_client->transitionToCommittedForNewPage(); break; @@ -2910,9 +3029,9 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) if (m_creatingInitialEmptyDocument) return; - - m_committedFirstRealDocumentLoad = true; + m_committedFirstRealDocumentLoad = true; + // For non-cached HTML pages, these methods are called in FrameLoader::begin. if (cachedPage || !m_client->hasHTMLView()) { dispatchDidCommitLoad(); @@ -2936,7 +3055,7 @@ void FrameLoader::clientRedirectCancelledOrFinished(bool cancelWithLoadInProgres m_sentRedirectNotification = false; } -void FrameLoader::clientRedirected(const KURL& url, double seconds, double fireDate, bool lockHistory, bool isJavaScriptFormAction) +void FrameLoader::clientRedirected(const KURL& url, double seconds, double fireDate, bool lockBackForwardList, bool isJavaScriptFormAction) { m_client->dispatchWillPerformClientRedirect(url, seconds, fireDate); @@ -2947,11 +3066,24 @@ void FrameLoader::clientRedirected(const KURL& url, double seconds, double fireD // If a "quick" redirect comes in an, we set a special mode so we treat the next // load as part of the same navigation. If we don't have a document loader, we have // no "original" load on which to base a redirect, so we treat the redirect as a normal load. - m_quickRedirectComing = lockHistory && m_documentLoader && !isJavaScriptFormAction; + m_quickRedirectComing = lockBackForwardList && m_documentLoader && !isJavaScriptFormAction; } +#if ENABLE(WML) +void FrameLoader::setForceReloadWmlDeck(bool reload) +{ + m_forceReloadWmlDeck = reload; +} +#endif + bool FrameLoader::shouldReload(const KURL& currentURL, const KURL& destinationURL) { +#if ENABLE(WML) + // As for WML deck, sometimes it's supposed to be reloaded even if the same URL with fragment + if (m_forceReloadWmlDeck) + return true; +#endif + // 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." @@ -3022,6 +3154,7 @@ void FrameLoader::open(CachedPage& cachedPage) m_frame->setView(view); m_frame->setDocument(document); + m_frame->setDOMWindow(cachedPage.domWindow()); m_frame->domWindow()->setURL(document->url()); m_frame->domWindow()->setSecurityOrigin(document->securityOrigin()); @@ -3030,6 +3163,11 @@ void FrameLoader::open(CachedPage& cachedPage) updatePolicyBaseURL(); cachedPage.restore(m_frame->page()); + document->resumeActiveDOMObjects(); + + // It is necessary to update any platform script objects after restoring the + // cached page. + m_frame->script()->updatePlatformScriptObjects(); checkCompleted(); } @@ -3170,6 +3308,26 @@ FrameLoadType FrameLoader::loadType() const { return m_loadType; } + +CachePolicy FrameLoader::cachePolicy() const +{ + if (m_isComplete) + return CachePolicyVerify; + + if (m_loadType == FrameLoadTypeReloadFromOrigin) + return CachePolicyReload; + + if (Frame* parentFrame = m_frame->tree()->parent()) { + CachePolicy parentCachePolicy = parentFrame->loader()->cachePolicy(); + if (parentCachePolicy != CachePolicyVerify) + return parentCachePolicy; + } + + if (m_loadType == FrameLoadTypeReload) + return CachePolicyRevalidate; + + return CachePolicyVerify; +} void FrameLoader::stopPolicyCheck() { @@ -3226,8 +3384,11 @@ void FrameLoader::checkLoadCompleteForThisFrame() } } if (shouldReset && item) - if (Page* page = m_frame->page()) + if (Page* page = m_frame->page()) { page->backForwardList()->goToItem(item.get()); + Settings* settings = m_frame->settings(); + page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : item.get()); + } return; } @@ -3246,7 +3407,7 @@ void FrameLoader::checkLoadCompleteForThisFrame() // If the user had a scroll point, scroll to it, overriding the anchor point if any. if (Page* page = m_frame->page()) - if ((isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload) && page->backForwardList()) + if ((isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload || m_loadType == FrameLoadTypeReloadFromOrigin) && page->backForwardList()) restoreScrollPositionAndViewState(); if (m_creatingInitialEmptyDocument || !m_committedFirstRealDocumentLoad) @@ -3265,8 +3426,9 @@ void FrameLoader::checkLoadCompleteForThisFrame() page->progress()->progressCompleted(m_frame); #ifdef ANDROID_INSTRUMENT - if (!m_frame->tree()->parent()) - android::TimeCounter::report(m_URL, cache()->getLiveSize(), cache()->getDeadSize()); + if (!m_frame->tree()->parent() && m_frame->document()->renderArena()) + android::TimeCounter::report(m_URL, cache()->getLiveSize(), cache()->getDeadSize(), + m_frame->document()->renderArena()->reportPoolSize()); #endif return; } @@ -3334,6 +3496,11 @@ void FrameLoader::didFirstLayout() m_client->dispatchDidFirstLayout(); } +void FrameLoader::didFirstVisuallyNonEmptyLayout() +{ + m_client->dispatchDidFirstVisuallyNonEmptyLayout(); +} + void FrameLoader::frameLoadCompleted() { m_client->frameLoadCompleted(); @@ -3400,12 +3567,7 @@ int FrameLoader::numPendingOrLoadingRequests(bool recurse) const return count; } -FrameLoaderClient* FrameLoader::client() const -{ - return m_client; -} - -void FrameLoader::submitForm(const FrameLoadRequest& request, Event* event) +void FrameLoader::submitForm(const FrameLoadRequest& request, Event* event, bool lockHistory, bool lockBackForwardList) { // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way. // We do not want to submit more than one form from the same page, @@ -3424,8 +3586,7 @@ void FrameLoader::submitForm(const FrameLoadRequest& request, Event* event) m_submittedFormURL = request.resourceRequest().url(); } - // FIXME: We should probably call userGestureHint() to tell whether this form submission was the result of a user gesture. - loadFrameRequestWithFormAndValues(request, false, event, m_formAboutToBeSubmitted.get(), m_formValuesAboutToBeSubmitted); + loadFrameRequestWithFormAndValues(request, lockHistory, lockBackForwardList, event, m_formAboutToBeSubmitted.get(), m_formValuesAboutToBeSubmitted); clearRecordedFormValues(); } @@ -3443,16 +3604,6 @@ void FrameLoader::tokenizerProcessedData() checkCompleted(); } -void FrameLoader::didTellClientAboutLoad(const String& url) -{ - m_urlsClientKnowsAbout.add(url); -} - -bool FrameLoader::haveToldClientAboutLoad(const String& url) -{ - return m_urlsClientKnowsAbout.contains(url); -} - void FrameLoader::handledOnloadEvents() { m_client->dispatchDidHandleOnloadEvents(); @@ -3461,6 +3612,8 @@ void FrameLoader::handledOnloadEvents() void FrameLoader::frameDetached() { stopAllLoaders(); + if (Document* document = m_frame->document()) + document->stopActiveDOMObjects(); detachFromParent(); } @@ -3487,19 +3640,33 @@ void FrameLoader::detachFromParent() m_frame->pageDestroyed(); } } + +void FrameLoader::addExtraFieldsToSubresourceRequest(ResourceRequest& request) +{ + addExtraFieldsToRequest(request, m_loadType, false, false); +} -void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, bool mainResource, bool alwaysFromRequest) +void FrameLoader::addExtraFieldsToMainResourceRequest(ResourceRequest& request) +{ + addExtraFieldsToRequest(request, m_loadType, true, false); +} + +void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadType loadType, bool mainResource, bool cookiePolicyURLFromRequest) { applyUserAgent(request); - if (m_loadType == FrameLoadTypeReload) { + if (loadType == FrameLoadTypeReload) { request.setCachePolicy(ReloadIgnoringCacheData); request.setHTTPHeaderField("Cache-Control", "max-age=0"); + } else if (loadType == FrameLoadTypeReloadFromOrigin) { + request.setCachePolicy(ReloadIgnoringCacheData); + request.setHTTPHeaderField("Cache-Control", "no-cache"); + request.setHTTPHeaderField("Pragma", "no-cache"); } // Don't set the cookie policy URL if it's already been set. if (request.mainDocumentURL().isEmpty()) { - if (mainResource && (isLoadingMainFrame() || alwaysFromRequest)) + if (mainResource && (isLoadingMainFrame() || cookiePolicyURLFromRequest)) request.setMainDocumentURL(request.url()); else if (Page* page = m_frame->page()) request.setMainDocumentURL(page->mainFrame()->loader()->url()); @@ -3510,6 +3677,11 @@ void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, bool mainRes // Make sure we send the Origin header. addHTTPOriginIfNeeded(request, String()); + + // Always try UTF-8. If that fails, try frame encoding (if any) and then the default. + // For a newly opened frame with an empty URL, encoding() should not be used, because this methods asks decoder, which uses ISO-8859-1. + Settings* settings = m_frame->settings(); + request.setResponseContentDispositionEncodingFallbackArray("UTF-8", m_URL.isEmpty() ? m_encoding : encoding(), settings ? settings->defaultTextEncodingName() : String()); } void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, String origin) @@ -3548,11 +3720,9 @@ void FrameLoader::committedLoad(DocumentLoader* loader, const char* data, int le } #ifdef ANDROID_USER_GESTURE -void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, - Event* event, PassRefPtr<FormState> prpFormState, bool userGesture) +void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType loadType, Event* event, PassRefPtr<FormState> prpFormState, bool userGesture) #else -void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, - Event* event, PassRefPtr<FormState> prpFormState) +void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType loadType, Event* event, PassRefPtr<FormState> prpFormState) #endif { RefPtr<FormState> formState = prpFormState; @@ -3583,28 +3753,23 @@ void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String workingResourceRequest.setHTTPMethod("POST"); workingResourceRequest.setHTTPBody(formData); workingResourceRequest.setHTTPContentType(contentType); - addExtraFieldsToRequest(workingResourceRequest, true, true); + addExtraFieldsToRequest(workingResourceRequest, loadType, true, true); - NavigationAction action(url, FrameLoadTypeStandard, true, event); + NavigationAction action(url, loadType, true, event); if (!frameName.isEmpty()) { if (Frame* targetFrame = findFrameForNavigation(frameName)) - targetFrame->loader()->loadWithNavigationAction(workingResourceRequest, action, FrameLoadTypeStandard, formState.release()); + targetFrame->loader()->loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release()); else checkNewWindowPolicy(action, workingResourceRequest, formState.release(), frameName); } else - loadWithNavigationAction(workingResourceRequest, action, FrameLoadTypeStandard, formState.release()); -} - -bool FrameLoader::isReloading() const -{ - return documentLoader()->request().cachePolicy() == ReloadIgnoringCacheData; + loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release()); } void FrameLoader::loadEmptyDocumentSynchronously() { ResourceRequest request(KURL("")); - load(request); + load(request, false); } unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) @@ -3637,19 +3802,34 @@ unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& requ if (error.isNull()) { ASSERT(!newRequest.isNull()); - didTellClientAboutLoad(newRequest.url().string()); #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()); + data.append(resource->data()->data(), resource->data()->size()); } else error = cannotShowURLError(newRequest); - } else + } else { #endif ResourceHandle::loadResourceSynchronously(newRequest, error, response, data, m_frame); + +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + // If normal loading results in a redirect to a resource with another origin (indicative of a captive portal), or a 4xx or 5xx status code or equivalent, + // or if there were network errors (but not if the user canceled the download), then instead get, from the cache, the resource of the fallback entry + // corresponding to the matched namespace. + if ((!error.isNull() && !error.isCancellation()) + || response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5 + || !protocolHostAndPortAreEqual(newRequest.url(), response.url())) { + if (documentLoader()->getApplicationCacheFallbackResource(newRequest, resource)) { + response = resource->response(); + data.clear(); + data.append(resource->data()->data(), resource->data()->size()); + } + } + } +#endif } sendRemainingDelegateMessages(identifier, response, data.size(), error); @@ -3742,10 +3922,7 @@ void FrameLoader::callContinueFragmentScrollAfterNavigationPolicy(void* argument void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue) { - // FIXME: - // some functions check m_quickRedirectComing, and others check for - // FrameLoadTypeRedirectWithLockedHistory. - bool isRedirect = m_quickRedirectComing || m_policyLoadType == FrameLoadTypeRedirectWithLockedHistory; + bool isRedirect = m_quickRedirectComing || m_policyLoadType == FrameLoadTypeRedirectWithLockedBackForwardList; m_quickRedirectComing = false; if (!shouldContinue) @@ -3793,6 +3970,7 @@ bool FrameLoader::shouldScrollToAnchor(bool isFormSubmission, FrameLoadType load return !isFormSubmission && loadType != FrameLoadTypeReload + && loadType != FrameLoadTypeReloadFromOrigin && loadType != FrameLoadTypeSame && !shouldReload(this->url(), url) // We don't want to just scroll if a link from within a @@ -3937,7 +4115,7 @@ void FrameLoader::callContinueLoadAfterNavigationPolicy(void* argument, loader->continueLoadAfterNavigationPolicy(request, formState, shouldContinue); } -void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue) +void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue) { // If we loaded an alternate page to replace an unreachableURL, we'll get in here with a // nil policyDataSource because loading the alternate page will have passed @@ -3967,8 +4145,11 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest& reque if ((isTargetItem || isLoadingMainFrame()) && isBackForwardLoadType(m_policyLoadType)) if (Page* page = m_frame->page()) { Frame* mainFrame = page->mainFrame(); - if (HistoryItem* resetItem = mainFrame->loader()->m_currentHistoryItem.get()) + if (HistoryItem* resetItem = mainFrame->loader()->m_currentHistoryItem.get()) { page->backForwardList()->goToItem(resetItem); + Settings* settings = m_frame->settings(); + page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : resetItem); + } } return; } @@ -4021,7 +4202,7 @@ void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& reques mainFrame->loader()->setOpenedByDOM(); mainFrame->loader()->m_client->dispatchShow(); mainFrame->loader()->setOpener(frame.get()); - mainFrame->loader()->loadWithNavigationAction(request, NavigationAction(), FrameLoadTypeStandard, formState); + mainFrame->loader()->loadWithNavigationAction(request, NavigationAction(), false, FrameLoadTypeStandard, formState); } void FrameLoader::sendRemainingDelegateMessages(unsigned long identifier, const ResourceResponse& response, int length, const ResourceError& error) @@ -4061,29 +4242,31 @@ void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& i void FrameLoader::loadedResourceFromMemoryCache(const CachedResource* resource) { - ResourceRequest request(resource->url()); - const ResourceResponse& response = resource->response(); - SharedBuffer* data = resource->data(); - int length = data ? data->size() : 0; + Page* page = m_frame->page(); + if (!page) + return; - if (Page* page = m_frame->page()) - page->inspectorController()->didLoadResourceFromMemoryCache(m_documentLoader.get(), request, response, length); + page->inspectorController()->didLoadResourceFromMemoryCache(m_documentLoader.get(), resource); + + if (!resource->sendResourceLoadCallbacks() || m_documentLoader->haveToldClientAboutLoad(resource->url())) + return; - if (!resource->sendResourceLoadCallbacks() || haveToldClientAboutLoad(resource->url())) + if (!page->areMemoryCacheClientCallsEnabled()) { + m_documentLoader->recordMemoryCacheLoadForFutureClientNotification(resource->url()); + m_documentLoader->didTellClientAboutLoad(resource->url()); return; + } - if (m_client->dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, response, length)) { - didTellClientAboutLoad(resource->url()); + ResourceRequest request(resource->url()); + if (m_client->dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, resource->response(), resource->encodedSize())) { + m_documentLoader->didTellClientAboutLoad(resource->url()); return; } unsigned long identifier; ResourceError error; - ResourceRequest r(request); - requestFromDelegate(r, identifier, error); - sendRemainingDelegateMessages(identifier, response, length, error); - - didTellClientAboutLoad(resource->url()); + requestFromDelegate(request, identifier, error); + sendRemainingDelegateMessages(identifier, resource->response(), resource->encodedSize(), error); } void FrameLoader::applyUserAgent(ResourceRequest& request) @@ -4153,9 +4336,7 @@ void FrameLoader::cachePageForHistoryItem(HistoryItem* item) { if (Page* page = m_frame->page()) { RefPtr<CachedPage> cachedPage = CachedPage::create(page); - cachedPage->setTimeStampToNow(); - cachedPage->setDocumentLoader(documentLoader()); - m_client->savePlatformDataToCachedPage(cachedPage.get()); + m_client->savePlatformDataToCachedFrame(cachedPage->cachedMainFrame()); pageCache()->add(item, cachedPage.release()); } @@ -4185,7 +4366,7 @@ PassRefPtr<HistoryItem> FrameLoader::createHistoryItem(bool useOriginal) if (useOriginal) url = originalURL; else if (docLoader) - url = docLoader->requestURL(); + url = docLoader->requestURL(); } LOG(History, "WebCoreHistory: Creating item for %s", url.string().ascii().data()); @@ -4206,7 +4387,10 @@ PassRefPtr<HistoryItem> FrameLoader::createHistoryItem(bool useOriginal) RefPtr<HistoryItem> item = HistoryItem::create(url, m_frame->tree()->name(), parent, title); item->setOriginalURLString(originalURL.string()); - + + if (!unreachableURL.isEmpty() || !docLoader || docLoader->response().httpStatusCode() >= 400) + item->setLastVisitWasFailure(true); + // Save form state if this is a POST if (docLoader) { if (useOriginal) @@ -4390,7 +4574,11 @@ 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 ENABLE(WML) + if (!formData && urlsMatchItem(item) && !m_frame->document()->isWMLDocument()) { +#else if (!formData && urlsMatchItem(item)) { +#endif // Must do this maintenance here, since we don't go through a real page reload saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get()); @@ -4441,8 +4629,12 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) } if (!inPageCache) { + bool addedExtraFields = false; ResourceRequest request(itemURL); + if (!item->referrer().isNull()) + request.setHTTPReferrer(item->referrer()); + // If this was a repost that failed the page cache, we might try to repost the form. NavigationAction action; if (formData) { @@ -4450,12 +4642,16 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) 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()); + RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item->referrer()); addHTTPOriginIfNeeded(request, securityOrigin->toString()); + // Make sure to add extra fields to the request after the Origin header is added for the FormData case. + // See https://bugs.webkit.org/show_bug.cgi?id=22194 for more discussion. + addExtraFieldsToRequest(request, m_loadType, true, formData); + addedExtraFields = true; + // 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 // we show the DoYouReallyWantToRepost nag. @@ -4473,6 +4669,7 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) } else { switch (loadType) { case FrameLoadTypeReload: + case FrameLoadTypeReloadFromOrigin: request.setCachePolicy(ReloadIgnoringCacheData); break; case FrameLoadTypeBack: @@ -4482,21 +4679,20 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) request.setCachePolicy(ReturnCacheDataElseLoad); break; case FrameLoadTypeStandard: - case FrameLoadTypeRedirectWithLockedHistory: - // no-op: leave as protocol default - // FIXME: I wonder if we ever hit this case + case FrameLoadTypeRedirectWithLockedBackForwardList: break; case FrameLoadTypeSame: - case FrameLoadTypeReloadAllowingStaleData: default: ASSERT_NOT_REACHED(); } action = NavigationAction(itemOriginalURL, loadType, false); } + + if (!addedExtraFields) + addExtraFieldsToRequest(request, m_loadType, true, formData); - addExtraFieldsToRequest(request, true, formData); - loadWithNavigationAction(request, action, loadType, 0); + loadWithNavigationAction(request, action, false, loadType, 0); } } } @@ -4542,6 +4738,8 @@ void FrameLoader::goToItem(HistoryItem* targetItem, FrameLoadType type) BackForwardList* bfList = page->backForwardList(); HistoryItem* currentItem = bfList->currentItem(); bfList->goToItem(targetItem); + Settings* settings = m_frame->settings(); + page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : targetItem); recursiveGoToItem(targetItem, currentItem, type); } @@ -4644,11 +4842,16 @@ void FrameLoader::updateHistoryForStandardLoad() m_navigationDuringLoad = false; } + bool didUpdateGlobalHistory = false; if (!frameNavigationDuringLoad && !documentLoader()->isClientRedirect()) { if (!historyURL.isEmpty()) { addBackForwardItemClippedAtTarget(true); - if (!needPrivacy) - m_client->updateGlobalHistory(historyURL); + if (!needPrivacy) { + m_client->updateGlobalHistory(); + didUpdateGlobalHistory = true; + } + if (Page* page = m_frame->page()) + page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForwardList()->currentItem()); } } else if (documentLoader()->unreachableURL().isEmpty() && m_currentHistoryItem) { m_currentHistoryItem->setURL(documentLoader()->url()); @@ -4658,6 +4861,9 @@ void FrameLoader::updateHistoryForStandardLoad() if (!historyURL.isEmpty() && !needPrivacy) { if (Page* page = m_frame->page()) page->group().addVisitedLink(historyURL); + + if (!didUpdateGlobalHistory && !url().isEmpty()) + m_client->updateGlobalHistoryForRedirectWithoutHistoryItem(); } } @@ -4706,7 +4912,7 @@ void FrameLoader::updateHistoryForReload() if (m_currentHistoryItem) { pageCache()->remove(m_currentHistoryItem.get()); - if (loadType() == FrameLoadTypeReload) + if (loadType() == FrameLoadTypeReload || loadType() == FrameLoadTypeReloadFromOrigin) saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get()); // Sometimes loading a page again leads to a different result because of cookies. Bugzilla 4072 @@ -4715,22 +4921,29 @@ void FrameLoader::updateHistoryForReload() } } -void FrameLoader::updateHistoryForRedirectWithLockedHistory() +void FrameLoader::updateHistoryForRedirectWithLockedBackForwardList() { #if !LOG_DISABLED if (documentLoader()) - LOG(History, "WebCoreHistory: Updating History for internal load in frame %s", documentLoader()->title().utf8().data()); + LOG(History, "WebCoreHistory: Updating History for redirect load in frame %s", documentLoader()->title().utf8().data()); #endif Settings* settings = m_frame->settings(); bool needPrivacy = !settings || settings->privateBrowsingEnabled(); const KURL& historyURL = documentLoader()->urlForHistory(); + bool didUpdateGlobalHistory = false; if (documentLoader()->isClientRedirect()) { if (!m_currentHistoryItem && !m_frame->tree()->parent()) { - addBackForwardItemClippedAtTarget(true); - if (!needPrivacy && !historyURL.isEmpty()) - m_client->updateGlobalHistory(historyURL); + if (!historyURL.isEmpty()) { + addBackForwardItemClippedAtTarget(true); + if (!needPrivacy) { + m_client->updateGlobalHistory(); + didUpdateGlobalHistory = true; + } + if (Page* page = m_frame->page()) + page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForwardList()->currentItem()); + } } if (m_currentHistoryItem) { m_currentHistoryItem->setURL(documentLoader()->url()); @@ -4745,6 +4958,9 @@ void FrameLoader::updateHistoryForRedirectWithLockedHistory() if (!historyURL.isEmpty() && !needPrivacy) { if (Page* page = m_frame->page()) page->group().addVisitedLink(historyURL); + + if (!didUpdateGlobalHistory && !url().isEmpty()) + m_client->updateGlobalHistoryForRedirectWithoutHistoryItem(); } } @@ -4756,7 +4972,7 @@ void FrameLoader::updateHistoryForCommit() #endif FrameLoadType type = loadType(); if (isBackForwardLoadType(type) || - (type == FrameLoadTypeReload && !provisionalDocumentLoader()->unreachableURL().isEmpty())) { + ((type == FrameLoadTypeReload || type == FrameLoadTypeReloadFromOrigin) && !provisionalDocumentLoader()->unreachableURL().isEmpty())) { // Once committed, we want to use current item for saving DocState, and // the provisional item for restoring state. // Note previousItem must be set before we close the URL, which will @@ -4831,7 +5047,7 @@ void FrameLoader::setMainDocumentError(DocumentLoader* loader, const ResourceErr m_client->setMainDocumentError(loader, error); } -void FrameLoader::mainReceivedCompleteError(DocumentLoader* loader, const ResourceError& error) +void FrameLoader::mainReceivedCompleteError(DocumentLoader* loader, const ResourceError&) { loader->setPrimaryLoadComplete(true); m_client->dispatchDidLoadMainResource(activeDocumentLoader()); @@ -4874,6 +5090,11 @@ void FrameLoader::didFinishLoad(ResourceLoader* loader) dispatchDidFinishLoading(loader->documentLoader(), loader->identifier()); } +bool FrameLoader::shouldUseCredentialStorage(ResourceLoader* loader) +{ + return m_client->shouldUseCredentialStorage(loader->documentLoader(), loader->identifier()); +} + void FrameLoader::didReceiveAuthenticationChallenge(ResourceLoader* loader, const AuthenticationChallenge& currentWebChallenge) { m_client->dispatchDidReceiveAuthenticationChallenge(loader->documentLoader(), loader->identifier(), currentWebChallenge); @@ -5128,8 +5349,15 @@ void FrameLoader::dispatchAssignIdentifierToInitialRequest(unsigned long identif void FrameLoader::dispatchWillSendRequest(DocumentLoader* loader, unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse) { + StringImpl* oldRequestURL = request.url().string().impl(); + m_documentLoader->didTellClientAboutLoad(request.url()); + m_client->dispatchWillSendRequest(loader, identifier, request, redirectResponse); + // If the URL changed, then we want to put that new URL in the "did tell client" set too. + if (!request.isNull() && oldRequestURL != request.url().string().impl()) + m_documentLoader->didTellClientAboutLoad(request.url()); + if (Page* page = m_frame->page()) page->inspectorController()->willSendRequest(loader, identifier, request, redirectResponse); } @@ -5158,6 +5386,33 @@ void FrameLoader::dispatchDidFinishLoading(DocumentLoader* loader, unsigned long page->inspectorController()->didFinishLoading(loader, identifier); } +void FrameLoader::tellClientAboutPastMemoryCacheLoads() +{ + ASSERT(m_frame->page()); + ASSERT(m_frame->page()->areMemoryCacheClientCallsEnabled()); + + if (!m_documentLoader) + return; + + Vector<String> pastLoads; + m_documentLoader->takeMemoryCacheLoadsForClientNotification(pastLoads); + + size_t size = pastLoads.size(); + for (size_t i = 0; i < size; ++i) { + CachedResource* resource = cache()->resourceForURL(pastLoads[i]); + + // FIXME: These loads, loaded from cache, but now gone from the cache by the time + // Page::setMemoryCacheClientCallsEnabled(true) is called, will not be seen by the client. + // Consider if there's some efficient way of remembering enough to deliver this client call. + // We have the URL, but not the rest of the response or the length. + if (!resource) + continue; + + ResourceRequest request(resource->url()); + m_client->dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, resource->response(), resource->encodedSize()); + } +} + #if USE(LOW_BANDWIDTH_DISPLAY) bool FrameLoader::addLowBandwidthDisplayRequest(CachedResource* cache) @@ -5255,8 +5510,6 @@ void FrameLoader::switchOutLowBandwidthDisplayIfReady() // write decoded data to the new doc, similar to write() if (m_pendingSourceInLowBandwidthDisplay.length()) { - // set cachePolicy to Cache to use the loaded resource - newDoc->docLoader()->setCachePolicy(CachePolicyCache); if (m_decoder->encoding().usesVisualOrdering()) newDoc->setVisuallyOrdered(); newDoc->recalcStyle(Node::Force); diff --git a/WebCore/loader/FrameLoader.h b/WebCore/loader/FrameLoader.h index 47a56af..4650b03 100644 --- a/WebCore/loader/FrameLoader.h +++ b/WebCore/loader/FrameLoader.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,20 +30,12 @@ #ifndef FrameLoader_h #define FrameLoader_h -#include "CachedResource.h" #include "CachePolicy.h" #include "FormState.h" #include "FrameLoaderTypes.h" -#include "KURL.h" -#include "StringHash.h" -#include "Timer.h" -#include <wtf/Forward.h> -#include <wtf/HashSet.h> -#include <wtf/HashMap.h> -#include <wtf/Noncopyable.h> -#include <wtf/OwnPtr.h> -#include <wtf/RefPtr.h> #include "ResourceRequest.h" +#include "Timer.h" + #if USE(LOW_BANDWIDTH_DISPLAY) #include "CachedResourceClient.h" #endif @@ -51,10 +44,10 @@ namespace WebCore { #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size class Archive; - class ArchiveResource; #endif class AuthenticationChallenge; class CachedPage; + class CachedResource; class Document; class DocumentLoader; class Element; @@ -68,13 +61,12 @@ namespace WebCore { class IconLoader; class IntSize; class NavigationAction; - class Node; - class Page; class RenderPart; class ResourceError; class ResourceLoader; - class ResourceRequest; class ResourceResponse; + class ScriptSourceCode; + class ScriptValue; class SecurityOrigin; class SharedBuffer; class SubstituteData; @@ -125,7 +117,7 @@ namespace WebCore { class FrameLoader : Noncopyable #if USE(LOW_BANDWIDTH_DISPLAY) - , private CachedResourceClient + , private CachedResourceClient #endif { public: @@ -146,30 +138,30 @@ namespace WebCore { void load(DocumentLoader*); // Calls loadWithDocumentLoader void loadWithNavigationAction(const ResourceRequest&, const NavigationAction&, // Calls loadWithDocumentLoader() - FrameLoadType, PassRefPtr<FormState>); + bool lockHistory, FrameLoadType, PassRefPtr<FormState>); #ifdef ANDROID_USER_GESTURE - void loadPostRequest(const ResourceRequest& inRequest, const String& referrer, // Called by loadFrameRequestWithFormAndValues(), calls loadWithNavigationAction - const String& frameName, Event* event, PassRefPtr<FormState> prpFormState, bool userGesture); + void loadPostRequest(const ResourceRequest&, const String& referrer, // Called by loadFrameRequestWithFormAndValues(), calls loadWithNavigationAction + const String& frameName, bool lockHistory, FrameLoadType, Event*, PassRefPtr<FormState>, 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); + bool lockHistory, FrameLoadType, Event*, PassRefPtr<FormState>, bool userGesture); #else - void loadPostRequest(const ResourceRequest& inRequest, const String& referrer, // Called by loadFrameRequestWithFormAndValues(), calls loadWithNavigationAction - const String& frameName, Event* event, PassRefPtr<FormState> prpFormState); + void loadPostRequest(const ResourceRequest&, const String& referrer, // Called by loadFrameRequestWithFormAndValues(), calls loadWithNavigationAction + const String& frameName, bool lockHistory, FrameLoadType, Event*, PassRefPtr<FormState>); 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 lockHistory, FrameLoadType, Event*, PassRefPtr<FormState>); #endif + void loadURLIntoChildFrame(const KURL&, const String& referer, Frame*); - void loadFrameRequestWithFormState(const FrameLoadRequest&, bool lockHistory, Event*, PassRefPtr<FormState>); - void loadFrameRequestWithFormAndValues(const FrameLoadRequest&, bool lockHistory, // Called by submitForm, calls loadPostRequest() + void loadFrameRequestWithFormAndValues(const FrameLoadRequest&, bool lockHistory, bool lockBackForwardList, // 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(const ResourceRequest&, bool lockHistory); // Called by WebFrame, calls (ResourceRequest, SubstituteData) + void load(const ResourceRequest&, const SubstituteData&, bool lockHistory); // Called both by WebFrame and internally, calls (DocumentLoader*) + void load(const ResourceRequest&, const String& frameName, bool lockHistory); // Called by WebPluginController #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size void loadArchive(PassRefPtr<Archive> archive); @@ -198,7 +190,6 @@ namespace WebCore { bool frameHasLoaded() const; int numPendingOrLoadingRequests(bool recurse) const; - bool isReloading() const; String referrer() const; String outgoingReferrer() const; String outgoingOrigin() const; @@ -210,7 +201,8 @@ namespace WebCore { DocumentLoader* provisionalDocumentLoader() const; FrameState state() const; static double timeOfLastCompletedLoad(); - + + bool shouldUseCredentialStorage(ResourceLoader*); void didReceiveAuthenticationChallenge(ResourceLoader*, const AuthenticationChallenge&); void didCancelAuthenticationChallenge(ResourceLoader*, const AuthenticationChallenge&); @@ -250,8 +242,8 @@ namespace WebCore { void checkContentPolicy(const String& MIMEType, ContentPolicyDecisionFunction, void* argument); void cancelContentPolicyCheck(); - void reload(); - void reloadAllowingStaleData(const String& overrideEncoding); + void reload(bool endToEndReload = false); + void reloadWithOverrideEncoding(const String& overrideEncoding); void didReceiveServerRedirectForProvisionalLoadForFrame(); void finishedLoadingDocument(DocumentLoader*); @@ -266,43 +258,52 @@ namespace WebCore { void didChangeTitle(DocumentLoader*); FrameLoadType loadType() const; + CachePolicy cachePolicy() const; void didFirstLayout(); bool firstLayoutDone() const; + void didFirstVisuallyNonEmptyLayout(); + void clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress); - void clientRedirected(const KURL&, double delay, double fireDate, bool lockHistory, bool isJavaScriptFormAction); + void clientRedirected(const KURL&, double delay, double fireDate, bool lockBackForwardList, bool isJavaScriptFormAction); bool shouldReload(const KURL& currentURL, const KURL& destinationURL); +#if ENABLE(WML) + void setForceReloadWmlDeck(bool); +#endif bool isQuickRedirectComing() const; void sendRemainingDelegateMessages(unsigned long identifier, const ResourceResponse&, int length, const ResourceError&); void requestFromDelegate(ResourceRequest&, unsigned long& identifier, ResourceError&); void loadedResourceFromMemoryCache(const CachedResource*); + void tellClientAboutPastMemoryCacheLoads(); void recursiveCheckLoadComplete(); void checkLoadComplete(); void detachFromParent(); void detachChildren(); - void addExtraFieldsToRequest(ResourceRequest&, bool isMainResource, bool alwaysFromRequest); + void addExtraFieldsToSubresourceRequest(ResourceRequest&); + void addExtraFieldsToMainResourceRequest(ResourceRequest&); + static void addHTTPOriginIfNeeded(ResourceRequest&, String origin); - FrameLoaderClient* client() const; + FrameLoaderClient* client() const { return m_client; } void setDefersLoading(bool); - 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); + void changeLocation(const String& url, const String& referrer, bool lockHistory = true, bool lockBackForwardList = true, bool userGesture = false, bool refresh = false); + void changeLocation(const KURL&, const String& referrer, bool lockHistory = true, bool lockBackForwardList = true, bool userGesture = false, bool refresh = false); + void urlSelected(const ResourceRequest&, const String& target, Event*, bool lockHistory, bool lockBackForwardList, bool userGesture); + void urlSelected(const FrameLoadRequest&, Event*, bool lockHistory, bool lockBackForwardList); bool requestFrame(HTMLFrameOwnerElement*, const String& url, const AtomicString& frameName); Frame* loadSubframe(HTMLFrameOwnerElement*, const KURL&, const String& name, const String& referrer); - void submitForm(const char* action, const String& url, PassRefPtr<FormData>, const String& target, const String& contentType, const String& boundary, Event*); + void submitForm(const char* action, const String& url, PassRefPtr<FormData>, const String& target, const String& contentType, const String& boundary, Event*, bool lockHistory, bool lockBackForwardList); void submitFormAgain(); - void submitForm(const FrameLoadRequest&, Event*); + void submitForm(const FrameLoadRequest&, Event*, bool lockHistory, bool lockBackForwardList); void stop(); void stopLoading(bool sendUnload); @@ -319,7 +320,7 @@ namespace WebCore { bool isScheduledLocationChangePending() const { return m_scheduledRedirection && isLocationChange(*m_scheduledRedirection); } void scheduleHTTPRedirection(double delay, const String& url); - void scheduleLocationChange(const String& url, const String& referrer, bool lockHistory = true, bool userGesture = false); + void scheduleLocationChange(const String& url, const String& referrer, bool lockHistory = true, bool lockBackForwardList = true, bool userGesture = false); void scheduleRefresh(bool userGesture = false); void scheduleHistoryNavigation(int steps); @@ -342,8 +343,8 @@ namespace WebCore { // Returns true if url is a JavaScript URL. bool executeIfJavaScriptURL(const KURL& url, bool userGesture = false, bool replaceDocument = true); - JSC::JSValue* executeScript(const String& url, int baseLine, const String& script); - JSC::JSValue* executeScript(const String& script, bool forceUserGesture = false); + ScriptValue executeScript(const ScriptSourceCode&); + ScriptValue executeScript(const String& script, bool forceUserGesture = false); void gotoAnchor(); bool gotoAnchor(const String& name); // returns true if the anchor was found @@ -406,9 +407,6 @@ namespace WebCore { KURL completeURL(const String& url); - void didTellClientAboutLoad(const String& url); - bool haveToldClientAboutLoad(const String& url); - KURL originalRequestURL() const; void cancelAndClear(); @@ -472,6 +470,12 @@ namespace WebCore { PassRefPtr<HistoryItem> createHistoryItem(bool useOriginal); PassRefPtr<HistoryItem> createHistoryItemTree(Frame* targetFrame, bool clipAtTarget); + bool canCachePageContainingThisFrame(); +#ifndef NDEBUG + void logCanCachePageDecision(); + bool logCanCacheFrameDecision(int indentLevel); +#endif + void addBackForwardItemClippedAtTarget(bool doClip); void restoreScrollPositionAndViewState(); void saveDocumentState(); @@ -484,7 +488,7 @@ namespace WebCore { void updateHistoryForBackForwardNavigation(); void updateHistoryForReload(); void updateHistoryForStandardLoad(); - void updateHistoryForRedirectWithLockedHistory(); + void updateHistoryForRedirectWithLockedBackForwardList(); void updateHistoryForClientRedirect(); void updateHistoryForCommit(); void updateHistoryForAnchorScroll(); @@ -511,6 +515,8 @@ namespace WebCore { void updatePolicyBaseURL(); void setPolicyBaseURL(const KURL&); + + void addExtraFieldsToRequest(ResourceRequest&, FrameLoadType loadType, bool isMainResource, bool cookiePolicyURLFromRequest); // Also not cool. void stopLoadingSubframes(); @@ -618,10 +624,6 @@ namespace WebCore { String m_outgoingReferrer; - CachePolicy m_cachePolicy; - - HashSet<String> m_urlsClientKnowsAbout; - OwnPtr<FormSubmission> m_deferredFormSubmission; bool m_isExecutingJavaScriptFormAction; @@ -694,6 +696,10 @@ namespace WebCore { String m_pendingSourceInLowBandwidthDisplay; HashSet<CachedResource*> m_externalRequestsInLowBandwidthDisplay; #endif + +#if ENABLE(WML) + bool m_forceReloadWmlDeck; +#endif }; } diff --git a/WebCore/loader/FrameLoaderClient.cpp b/WebCore/loader/FrameLoaderClient.cpp new file mode 100644 index 0000000..9610fd1 --- /dev/null +++ b/WebCore/loader/FrameLoaderClient.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Holger Hans Peter Freyther + * + * 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 "FrameLoaderClient.h" + +#include "Color.h" +#include "Frame.h" +#include "FrameView.h" +#include "HTMLFrameOwnerElement.h" +#include "Page.h" +#include "RenderPart.h" + +namespace WebCore { + +FrameLoaderClient::~FrameLoaderClient() +{} + +void FrameLoaderClient::transitionToCommittedForNewPage(Frame* frame, + const IntSize& viewportSize, + const Color& backgroundColor, bool transparent, + const IntSize& fixedLayoutSize, bool useFixedLayout, + ScrollbarMode horizontalScrollbarMode, ScrollbarMode verticalScrollbarMode) +{ + ASSERT(frame); + + Page* page = frame->page(); + ASSERT(page); + + bool isMainFrame = frame == page->mainFrame(); + + if (isMainFrame && frame->view()) + frame->view()->setParentVisible(false); + + frame->setView(0); + + FrameView* frameView; + if (isMainFrame) { + frameView = new FrameView(frame, viewportSize); + frameView->setFixedLayoutSize(fixedLayoutSize); + frameView->setUseFixedLayout(useFixedLayout); + } else + frameView = new FrameView(frame); + + frameView->setScrollbarModes(horizontalScrollbarMode, verticalScrollbarMode); + frameView->updateDefaultScrollbarState(); + + frame->setView(frameView); + // FrameViews are created with a ref count of 1. Release this ref since we've assigned it to frame. + frameView->deref(); + + if (backgroundColor.isValid()) + frameView->updateBackgroundRecursively(backgroundColor, transparent); + + if (isMainFrame) + frameView->setParentVisible(true); + + if (frame->ownerRenderer()) + frame->ownerRenderer()->setWidget(frameView); + + if (HTMLFrameOwnerElement* owner = frame->ownerElement()) + frame->view()->setCanHaveScrollbars(owner->scrollingMode() != ScrollbarAlwaysOff); +} + +} + diff --git a/WebCore/loader/FrameLoaderClient.h b/WebCore/loader/FrameLoaderClient.h index 6e2aba9..52dcfab 100644 --- a/WebCore/loader/FrameLoaderClient.h +++ b/WebCore/loader/FrameLoaderClient.h @@ -30,6 +30,7 @@ #define FrameLoaderClient_h #include "FrameLoaderTypes.h" +#include "ScrollTypes.h" #include <wtf/Forward.h> #include <wtf/Platform.h> #include <wtf/Vector.h> @@ -44,7 +45,8 @@ class NSView; namespace WebCore { class AuthenticationChallenge; - class CachedPage; + class CachedFrame; + class Color; class DocumentLoader; class Element; class FormState; @@ -74,7 +76,7 @@ namespace WebCore { class FrameLoaderClient { public: - virtual ~FrameLoaderClient() { } + virtual ~FrameLoaderClient(); virtual void frameLoaderDestroyed() = 0; virtual bool hasWebView() const = 0; // mainly for assertions @@ -93,6 +95,7 @@ namespace WebCore { virtual void assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&) = 0; virtual void dispatchWillSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse) = 0; + virtual bool shouldUseCredentialStorage(DocumentLoader*, unsigned long identifier) = 0; virtual void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) = 0; virtual void dispatchDidCancelAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) = 0; virtual void dispatchDidReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&) = 0; @@ -116,6 +119,7 @@ namespace WebCore { virtual void dispatchDidFinishDocumentLoad() = 0; virtual void dispatchDidFinishLoad() = 0; virtual void dispatchDidFirstLayout() = 0; + virtual void dispatchDidFirstVisuallyNonEmptyLayout() = 0; virtual Frame* dispatchCreatePage() = 0; virtual void dispatchShow() = 0; @@ -150,7 +154,9 @@ namespace WebCore { virtual void committedLoad(DocumentLoader*, const char*, int) = 0; virtual void finishedLoading(DocumentLoader*) = 0; - virtual void updateGlobalHistory(const KURL&) = 0; + virtual void updateGlobalHistory() = 0; + virtual void updateGlobalHistoryForRedirectWithoutHistoryItem() = 0; + virtual bool shouldGoToHistoryItem(HistoryItem*) const = 0; #ifdef ANDROID_HISTORY_CLIENT virtual void dispatchDidAddHistoryItem(HistoryItem*) const = 0; @@ -186,8 +192,8 @@ namespace WebCore { virtual String userAgent(const KURL&) = 0; - virtual void savePlatformDataToCachedPage(CachedPage*) = 0; - virtual void transitionToCommittedFromCachedPage(CachedPage*) = 0; + virtual void savePlatformDataToCachedFrame(CachedFrame*) = 0; + virtual void transitionToCommittedFromCachedFrame(CachedFrame*) = 0; virtual void transitionToCommittedForNewPage() = 0; virtual bool canCachePage() const = 0; @@ -214,6 +220,12 @@ namespace WebCore { #endif virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long identifier, NSCachedURLResponse*) const = 0; #endif + + virtual bool shouldUsePluginDocument(const String& /*mimeType*/) const { return false; } + + protected: + static void transitionToCommittedForNewPage(Frame*, const IntSize&, const Color&, bool, const IntSize &, bool, + ScrollbarMode = ScrollbarAuto, ScrollbarMode = ScrollbarAuto); }; } // namespace WebCore diff --git a/WebCore/loader/FrameLoaderTypes.h b/WebCore/loader/FrameLoaderTypes.h index 86d8a5b..4fe1176 100644 --- a/WebCore/loader/FrameLoaderTypes.h +++ b/WebCore/loader/FrameLoaderTypes.h @@ -51,10 +51,10 @@ namespace WebCore { FrameLoadTypeForward, FrameLoadTypeIndexedBackForward, // a multi-item hop in the backforward list FrameLoadTypeReload, - FrameLoadTypeReloadAllowingStaleData, FrameLoadTypeSame, // user loads same URL again (but not reload button) - FrameLoadTypeRedirectWithLockedHistory, - FrameLoadTypeReplace + FrameLoadTypeRedirectWithLockedBackForwardList, // FIXME: Merge "lockBackForwardList", "lockHistory", "quickRedirect" and "clientRedirect" into a single concept of redirect. + FrameLoadTypeReplace, + FrameLoadTypeReloadFromOrigin }; enum NavigationType { diff --git a/WebCore/loader/ImageDocument.cpp b/WebCore/loader/ImageDocument.cpp index 39b1db6..8f58179 100644 --- a/WebCore/loader/ImageDocument.cpp +++ b/WebCore/loader/ImageDocument.cpp @@ -76,7 +76,12 @@ private: class ImageDocumentElement : public HTMLImageElement { public: - ImageDocumentElement(ImageDocument* doc) : HTMLImageElement(doc), m_imageDocument(doc) { } + ImageDocumentElement(ImageDocument* doc) + : HTMLImageElement(imgTag, doc) + , m_imageDocument(doc) + { + } + virtual ~ImageDocumentElement(); virtual void willMoveToNewOwnerDocument(); @@ -86,13 +91,13 @@ private: // -------- -bool ImageTokenizer::write(const SegmentedString& s, bool appendData) +bool ImageTokenizer::write(const SegmentedString&, bool) { ASSERT_NOT_REACHED(); return false; } -bool ImageTokenizer::writeRawData(const char* data, int len) +bool ImageTokenizer::writeRawData(const char*, int) { CachedImage* cachedImage = m_doc->cachedImage(); cachedImage->data(m_doc->frame()->loader()->documentLoader()->mainResourceData(), false); @@ -336,7 +341,7 @@ bool ImageDocument::shouldShrinkToFit() const // -------- -void ImageEventListener::handleEvent(Event* event, bool isWindowEvent) +void ImageEventListener::handleEvent(Event* event, bool) { if (event->type() == eventNames().resizeEvent) m_doc->windowSizeChanged(); diff --git a/WebCore/loader/ImageLoader.cpp b/WebCore/loader/ImageLoader.cpp index da159b4..43e08c0 100644 --- a/WebCore/loader/ImageLoader.cpp +++ b/WebCore/loader/ImageLoader.cpp @@ -49,6 +49,7 @@ ImageLoader::~ImageLoader() void ImageLoader::setImage(CachedImage* newImage) { + ASSERT(m_failedLoadURL.isEmpty()); CachedImage* oldImage = m_image.get(); if (newImage != oldImage) { setLoadingImage(newImage); @@ -86,6 +87,9 @@ void ImageLoader::updateFromElement() AtomicString attr = elem->getAttribute(elem->imageSourceAttributeName()); + if (attr == m_failedLoadURL) + return; + // 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 @@ -97,9 +101,13 @@ void ImageLoader::updateFromElement() newImage = new CachedImage(sourceURI(attr)); newImage->setLoading(true); newImage->setDocLoader(doc->docLoader()); - doc->docLoader()->m_docResources.set(newImage->url(), newImage); + doc->docLoader()->m_documentResources.set(newImage->url(), newImage); } else newImage = doc->docLoader()->requestImage(sourceURI(attr)); + + // If we do not have an image here, it means that a cross-site + // violation occurred. + m_failedLoadURL = !newImage ? attr : AtomicString(); } CachedImage* oldImage = m_image.get(); @@ -119,8 +127,16 @@ void ImageLoader::updateFromElement() } } -void ImageLoader::notifyFinished(CachedResource *image) +void ImageLoader::updateFromElementIgnoringPreviousError() +{ + // Clear previous error. + m_failedLoadURL = AtomicString(); + updateFromElement(); +} + +void ImageLoader::notifyFinished(CachedResource*) { + ASSERT(m_failedLoadURL.isEmpty()); m_imageComplete = true; Element* elem = element(); diff --git a/WebCore/loader/ImageLoader.h b/WebCore/loader/ImageLoader.h index 39b5cc4..fc3a58a 100644 --- a/WebCore/loader/ImageLoader.h +++ b/WebCore/loader/ImageLoader.h @@ -1,6 +1,4 @@ /* - * 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. @@ -25,12 +23,12 @@ #ifndef ImageLoader_h #define ImageLoader_h +#include "AtomicString.h" #include "CachedResourceClient.h" #include "CachedResourceHandle.h" namespace WebCore { -class AtomicString; class Element; class ImageLoader : public CachedResourceClient { @@ -40,6 +38,11 @@ public: void updateFromElement(); + // This method should be called after the 'src' attribute + // is set (even when it is not modified) to force the update + // and match Firefox and Opera. + void updateFromElementIgnoringPreviousError(); + virtual void dispatchLoadEvent() = 0; virtual String sourceURI(const AtomicString&) const = 0; @@ -63,6 +66,7 @@ protected: private: Element* m_element; CachedResourceHandle<CachedImage> m_image; + AtomicString m_failedLoadURL; bool m_firedLoad : 1; bool m_imageComplete : 1; bool m_loadManually : 1; diff --git a/WebCore/loader/MainResourceLoader.cpp b/WebCore/loader/MainResourceLoader.cpp index 079bd79..325809b 100644 --- a/WebCore/loader/MainResourceLoader.cpp +++ b/WebCore/loader/MainResourceLoader.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -119,7 +120,7 @@ void MainResourceLoader::callContinueAfterNavigationPolicy(void* argument, const static_cast<MainResourceLoader*>(argument)->continueAfterNavigationPolicy(request, shouldContinue); } -void MainResourceLoader::continueAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue) +void MainResourceLoader::continueAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue) { if (!shouldContinue) stopLoadingForPolicyChange(); @@ -208,6 +209,11 @@ void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy, } case PolicyDownload: + // m_handle can be null, e.g. when loading a substitute resource from application cache. + if (!m_handle) { + receivedError(cannotShowURLError()); + return; + } frameLoader()->client()->download(m_handle.get(), request(), m_handle.get()->request(), r); // It might have gone missing if (frameLoader()) @@ -268,6 +274,18 @@ void MainResourceLoader::continueAfterContentPolicy(PolicyAction policy) void MainResourceLoader::didReceiveResponse(const ResourceResponse& r) { +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (r.httpStatusCode() / 100 == 4 || r.httpStatusCode() / 100 == 5) { + ASSERT(!m_applicationCache); + if (m_frame->settings() && m_frame->settings()->offlineWebApplicationCacheEnabled()) { + m_applicationCache = ApplicationCacheGroup::fallbackCacheForMainRequest(request(), documentLoader()); + + if (scheduleLoadFallbackResourceFromApplicationCache(m_applicationCache.get())) + return; + } + } +#endif + // 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) @@ -345,6 +363,18 @@ void MainResourceLoader::didFinishLoading() void MainResourceLoader::didFail(const ResourceError& error) { +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (!error.isCancellation()) { + ASSERT(!m_applicationCache); + if (m_frame->settings() && m_frame->settings()->offlineWebApplicationCacheEnabled()) { + m_applicationCache = ApplicationCacheGroup::fallbackCacheForMainRequest(request(), documentLoader()); + + if (scheduleLoadFallbackResourceFromApplicationCache(m_applicationCache.get())) + return; + } + } +#endif + // 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) @@ -432,21 +462,14 @@ bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& su 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(); - } - + m_applicationCache = ApplicationCacheGroup::cacheForMainRequest(r, m_documentLoader.get()); + 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()); - } + // Get the resource from the application cache. By definition, cacheForMainRequest() returns a cache that contains the resource. + ApplicationCacheResource* resource = m_applicationCache->resourceForRequest(r); + m_substituteData = SubstituteData(resource->data(), + resource->response().mimeType(), + resource->response().textEncodingName(), KURL()); } } #endif diff --git a/WebCore/loader/MediaDocument.cpp b/WebCore/loader/MediaDocument.cpp index 5689457..8ed5b45 100644 --- a/WebCore/loader/MediaDocument.cpp +++ b/WebCore/loader/MediaDocument.cpp @@ -53,6 +53,7 @@ class MediaTokenizer : public Tokenizer { public: MediaTokenizer(Document* doc) : m_doc(doc), m_mediaElement(0) {} +private: virtual bool write(const SegmentedString&, bool appendData); virtual void stopParsing(); virtual void finish(); @@ -62,12 +63,12 @@ public: 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) +bool MediaTokenizer::write(const SegmentedString&, bool) { ASSERT_NOT_REACHED(); return false; @@ -103,7 +104,7 @@ void MediaTokenizer::createDocumentStructure() frame->loader()->activeDocumentLoader()->mainResourceLoader()->setShouldBufferData(false); } -bool MediaTokenizer::writeRawData(const char* data, int len) +bool MediaTokenizer::writeRawData(const char*, int) { ASSERT(!m_mediaElement); if (m_mediaElement) diff --git a/WebCore/loader/NavigationAction.cpp b/WebCore/loader/NavigationAction.cpp index 6bd65ae..ed68e43 100644 --- a/WebCore/loader/NavigationAction.cpp +++ b/WebCore/loader/NavigationAction.cpp @@ -40,7 +40,7 @@ static NavigationType navigationType(FrameLoadType frameLoadType, bool isFormSub return NavigationTypeFormSubmitted; if (haveEvent) return NavigationTypeLinkClicked; - if (frameLoadType == FrameLoadTypeReload) + if (frameLoadType == FrameLoadTypeReload || frameLoadType == FrameLoadTypeReloadFromOrigin) return NavigationTypeReload; if (isBackForwardLoadType(frameLoadType)) return NavigationTypeBackForward; diff --git a/WebCore/loader/PluginDocument.cpp b/WebCore/loader/PluginDocument.cpp index 8be6ae2..42c801c 100644 --- a/WebCore/loader/PluginDocument.cpp +++ b/WebCore/loader/PluginDocument.cpp @@ -48,6 +48,7 @@ class PluginTokenizer : public Tokenizer { public: PluginTokenizer(Document* doc) : m_doc(doc), m_embedElement(0) {} +private: virtual bool write(const SegmentedString&, bool appendData); virtual void stopParsing(); virtual void finish(); @@ -57,12 +58,12 @@ public: virtual bool writeRawData(const char* data, int len); void createDocumentStructure(); -private: + Document* m_doc; HTMLEmbedElement* m_embedElement; }; -bool PluginTokenizer::write(const SegmentedString& s, bool appendData) +bool PluginTokenizer::write(const SegmentedString&, bool) { ASSERT_NOT_REACHED(); return false; @@ -94,7 +95,7 @@ void PluginTokenizer::createDocumentStructure() body->appendChild(embedElement, ec); } -bool PluginTokenizer::writeRawData(const char* data, int len) +bool PluginTokenizer::writeRawData(const char*, int) { ASSERT(!m_embedElement); if (m_embedElement) diff --git a/WebCore/loader/ProgressTracker.cpp b/WebCore/loader/ProgressTracker.cpp index 56aa976..38df80f 100644 --- a/WebCore/loader/ProgressTracker.cpp +++ b/WebCore/loader/ProgressTracker.cpp @@ -30,7 +30,7 @@ #include "FrameLoader.h" #include "FrameLoaderClient.h" #include "ResourceResponse.h" -#include "SystemTime.h" +#include <wtf/CurrentTime.h> using std::min; diff --git a/WebCore/loader/ResourceLoader.cpp b/WebCore/loader/ResourceLoader.cpp index a6f90b3..4f55981 100644 --- a/WebCore/loader/ResourceLoader.cpp +++ b/WebCore/loader/ResourceLoader.cpp @@ -64,9 +64,6 @@ ResourceLoader::ResourceLoader(Frame* frame, bool sendResourceLoadCallbacks, boo , m_shouldContentSniff(shouldContentSniff) , m_shouldBufferData(true) , m_defersLoading(frame->page()->defersLoading()) -#if ENABLE(OFFLINE_WEB_APPLICATIONS) - , m_wasLoadedFromApplicationCache(false) -#endif { } @@ -124,10 +121,8 @@ bool ResourceLoader::load(const ResourceRequest& r) #endif #if ENABLE(OFFLINE_WEB_APPLICATIONS) - if (m_documentLoader->scheduleApplicationCacheLoad(this, clientRequest, r.url())) { - m_wasLoadedFromApplicationCache = true; + if (m_documentLoader->scheduleApplicationCacheLoad(this, clientRequest, r.url())) return true; - } #endif if (m_defersLoading) { @@ -197,6 +192,17 @@ void ResourceLoader::clearResourceData() m_resourceData->clear(); } +#if ENABLE(OFFLINE_WEB_APPLICATIONS) +bool ResourceLoader::scheduleLoadFallbackResourceFromApplicationCache(ApplicationCache* cache) +{ + if (documentLoader()->scheduleLoadFallbackResourceFromApplicationCache(this, m_request, cache)) { + handle()->cancel(); + return true; + } + return false; +} +#endif + void ResourceLoader::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse) { // Protect this in this delegate method since the additional processing can do @@ -217,7 +223,7 @@ void ResourceLoader::willSendRequest(ResourceRequest& request, const ResourceRes m_request = request; } -void ResourceLoader::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +void ResourceLoader::didSendData(unsigned long long, unsigned long long) { } @@ -377,6 +383,12 @@ ResourceError ResourceLoader::cannotShowURLError() void ResourceLoader::willSendRequest(ResourceHandle*, ResourceRequest& request, const ResourceResponse& redirectResponse) { +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (!redirectResponse.isNull() && !protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) { + if (scheduleLoadFallbackResourceFromApplicationCache()) + return; + } +#endif willSendRequest(request, redirectResponse); } @@ -387,6 +399,12 @@ void ResourceLoader::didSendData(ResourceHandle*, unsigned long long bytesSent, void ResourceLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) { +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (response.httpStatusCode() / 100 == 4 || response.httpStatusCode() / 100 == 5) { + if (scheduleLoadFallbackResourceFromApplicationCache()) + return; + } +#endif didReceiveResponse(response); } @@ -402,6 +420,12 @@ void ResourceLoader::didFinishLoading(ResourceHandle*) void ResourceLoader::didFail(ResourceHandle*, const ResourceError& error) { +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (!error.isCancellation()) { + if (documentLoader()->scheduleLoadFallbackResourceFromApplicationCache(this, m_request)) + return; + } +#endif didFail(error); } @@ -415,6 +439,12 @@ void ResourceLoader::cannotShowURL(ResourceHandle*) didFail(cannotShowURLError()); } +bool ResourceLoader::shouldUseCredentialStorage() +{ + RefPtr<ResourceLoader> protector(this); + return frameLoader()->shouldUseCredentialStorage(this); +} + void ResourceLoader::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge) { // Protect this in this delegate method since the additional processing can do diff --git a/WebCore/loader/ResourceLoader.h b/WebCore/loader/ResourceLoader.h index 722f5fc..c8cc208 100644 --- a/WebCore/loader/ResourceLoader.h +++ b/WebCore/loader/ResourceLoader.h @@ -40,6 +40,7 @@ namespace WebCore { + class ApplicationCache; class DocumentLoader; class Frame; class FrameLoader; @@ -82,6 +83,7 @@ namespace WebCore { virtual void didFinishLoading(); virtual void didFail(const ResourceError&); + virtual bool shouldUseCredentialStorage(); virtual void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); void didCancelAuthenticationChallenge(const AuthenticationChallenge&); virtual void receivedCancellation(const AuthenticationChallenge&); @@ -96,6 +98,7 @@ namespace WebCore { virtual void wasBlocked(ResourceHandle*); virtual void cannotShowURL(ResourceHandle*); virtual void willStopBufferingData(ResourceHandle*, const char* data, int length) { willStopBufferingData(data, length); } + virtual bool shouldUseCredentialStorage(ResourceHandle*) { return shouldUseCredentialStorage(); } virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) { didReceiveAuthenticationChallenge(challenge); } virtual void didCancelAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) { didCancelAuthenticationChallenge(challenge); } virtual void receivedCancellation(ResourceHandle*, const AuthenticationChallenge& challenge) { receivedCancellation(challenge); } @@ -112,6 +115,10 @@ namespace WebCore { protected: ResourceLoader(Frame*, bool sendResourceLoadCallbacks, bool shouldContentSniff); +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + bool scheduleLoadFallbackResourceFromApplicationCache(ApplicationCache* = 0); +#endif + virtual void didCancel(const ResourceError&); void didFinishLoadingOnePart(); @@ -139,9 +146,6 @@ namespace WebCore { bool m_shouldContentSniff; bool m_shouldBufferData; 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 bff48b2..4a339ef 100644 --- a/WebCore/loader/SubresourceLoader.cpp +++ b/WebCore/loader/SubresourceLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,15 +29,11 @@ #include "config.h" #include "SubresourceLoader.h" -#include "Document.h" #include "DocumentLoader.h" #include "Frame.h" #include "FrameLoader.h" -#include "Logging.h" #include "ResourceHandle.h" -#include "ResourceRequest.h" #include "SubresourceLoaderClient.h" -#include "SharedBuffer.h" #include <wtf/RefCountedLeakCounter.h> namespace WebCore { @@ -64,13 +60,6 @@ SubresourceLoader::~SubresourceLoader() #endif } -bool SubresourceLoader::load(const ResourceRequest& r) -{ - m_frame->loader()->didTellClientAboutLoad(r.url().string()); - - return ResourceLoader::load(r); -} - PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, SubresourceLoaderClient* client, const ResourceRequest& request, bool skipCanLoadCheck, bool sendResourceLoadCallbacks, bool shouldContentSniff) { if (!frame) @@ -106,7 +95,7 @@ PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, Subresourc else newRequest.setCachePolicy(fl->originalRequest().cachePolicy()); - fl->addExtraFieldsToRequest(newRequest, false, false); + fl->addExtraFieldsToSubresourceRequest(newRequest); RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, client, sendResourceLoadCallbacks, shouldContentSniff))); if (!subloader->load(newRequest)) @@ -237,6 +226,17 @@ void SubresourceLoader::didCancel(const ResourceError& error) ResourceLoader::didCancel(error); } +bool SubresourceLoader::shouldUseCredentialStorage() +{ + RefPtr<SubresourceLoader> protect(this); + + bool shouldUse; + if (m_client && m_client->getShouldUseCredentialStorage(this, shouldUse)) + return shouldUse; + + return ResourceLoader::shouldUseCredentialStorage(); +} + void SubresourceLoader::didReceiveAuthenticationChallenge(const AuthenticationChallenge& challenge) { RefPtr<SubresourceLoader> protect(this); diff --git a/WebCore/loader/SubresourceLoader.h b/WebCore/loader/SubresourceLoader.h index b5bd34a..1a94c73 100644 --- a/WebCore/loader/SubresourceLoader.h +++ b/WebCore/loader/SubresourceLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2009 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,25 +29,22 @@ #ifndef SubresourceLoader_h #define SubresourceLoader_h -#include "ResourceHandleClient.h" #include "ResourceLoader.h" -#include <wtf/PassRefPtr.h> namespace WebCore { - class FormData; - class String; - class ResourceHandle; class ResourceRequest; class SubresourceLoaderClient; class SubresourceLoader : public ResourceLoader { public: static PassRefPtr<SubresourceLoader> create(Frame*, SubresourceLoaderClient*, const ResourceRequest&, bool skipCanLoadCheck = false, bool sendResourceLoadCallbacks = true, bool shouldContentSniff = true); - - virtual ~SubresourceLoader(); - virtual bool load(const ResourceRequest&); + void clearClient() { m_client = 0; } + + private: + SubresourceLoader(Frame*, SubresourceLoaderClient*, bool sendResourceLoadCallbacks, bool shouldContentSniff); + virtual ~SubresourceLoader(); virtual void willSendRequest(ResourceRequest&, const ResourceResponse& redirectResponse); virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent); @@ -55,15 +52,11 @@ namespace WebCore { virtual void didReceiveData(const char*, int, long long lengthReceived, bool allAtOnce); virtual void didFinishLoading(); virtual void didFail(const ResourceError&); + virtual bool shouldUseCredentialStorage(); virtual void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); - virtual void receivedCancellation(const AuthenticationChallenge&); - - void clearClient() { m_client = 0; } - - private: - SubresourceLoader(Frame*, SubresourceLoaderClient*, bool sendResourceLoadCallbacks, bool shouldContentSniff); - + virtual void receivedCancellation(const AuthenticationChallenge&); virtual void didCancel(const ResourceError&); + SubresourceLoaderClient* m_client; bool m_loadingMultipartContent; }; diff --git a/WebCore/loader/SubresourceLoaderClient.h b/WebCore/loader/SubresourceLoaderClient.h index d2b9a12..76fde47 100644 --- a/WebCore/loader/SubresourceLoaderClient.h +++ b/WebCore/loader/SubresourceLoaderClient.h @@ -42,14 +42,15 @@ 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 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) { } + virtual void didReceiveData(SubresourceLoader*, const char*, int /*lengthReceived*/) { } virtual void didFinishLoading(SubresourceLoader*) { } virtual void didFail(SubresourceLoader*, const ResourceError&) { } + virtual bool getShouldUseCredentialStorage(SubresourceLoader*, bool& /*shouldUseCredentialStorage*/) { return false; } virtual void didReceiveAuthenticationChallenge(SubresourceLoader*, const AuthenticationChallenge&) { } virtual void receivedCancellation(SubresourceLoader*, const AuthenticationChallenge&) { } diff --git a/WebCore/loader/TextDocument.cpp b/WebCore/loader/TextDocument.cpp index 6b06ede..bd2c446 100644 --- a/WebCore/loader/TextDocument.cpp +++ b/WebCore/loader/TextDocument.cpp @@ -93,7 +93,7 @@ TextTokenizer::TextTokenizer(HTMLViewSourceDocument* doc) m_dest = m_buffer; } -bool TextTokenizer::write(const SegmentedString& s, bool appendData) +bool TextTokenizer::write(const SegmentedString& s, bool) { ExceptionCode ec; diff --git a/WebCore/loader/TextResourceDecoder.cpp b/WebCore/loader/TextResourceDecoder.cpp index 4a0caa0..f37d8f7 100644 --- a/WebCore/loader/TextResourceDecoder.cpp +++ b/WebCore/loader/TextResourceDecoder.cpp @@ -346,7 +346,7 @@ void TextResourceDecoder::setEncoding(const TextEncoding& encoding, EncodingSour 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()); + m_decoder.reset(encoding.closestByteBasedEquivalent()); else m_decoder.reset(encoding); @@ -793,6 +793,7 @@ String TextResourceDecoder::flush() { String result = m_decoder.decode(m_buffer.data(), m_buffer.size(), true, m_contentType == XML, m_sawError); m_buffer.clear(); + m_decoder.reset(m_decoder.encoding()); return result; } diff --git a/WebCore/loader/ThreadableLoader.cpp b/WebCore/loader/ThreadableLoader.cpp new file mode 100644 index 0000000..0f22fbe --- /dev/null +++ b/WebCore/loader/ThreadableLoader.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2009, Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 "ThreadableLoader.h" + +#include "DocumentThreadableLoader.h" +#include "Document.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "ScriptExecutionContext.h" + +namespace WebCore { + +PassRefPtr<ThreadableLoader> ThreadableLoader::create(ScriptExecutionContext* context, ThreadableLoaderClient* client, const ResourceRequest& request, LoadCallbacks callbacksSetting, ContentSniff contentSniff) +{ + ASSERT(client); + ASSERT(context); + ASSERT(context->isDocument()); + + return DocumentThreadableLoader::create(static_cast<Document*>(context), client, request, callbacksSetting, contentSniff); +} + +unsigned long ThreadableLoader::loadResourceSynchronously(ScriptExecutionContext* context, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) +{ + ASSERT(context); + ASSERT(context->isDocument()); + + Document* document = static_cast<Document*>(context); + if (!document->frame()) + return std::numeric_limits<unsigned long>::max(); + return document->frame()->loader()->loadResourceSynchronously(request, error, response, data); +} + +} // namespace WebCore diff --git a/WebCore/loader/ThreadableLoader.h b/WebCore/loader/ThreadableLoader.h new file mode 100644 index 0000000..e8ff058 --- /dev/null +++ b/WebCore/loader/ThreadableLoader.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009, Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 ThreadableLoader_h +#define ThreadableLoader_h + +#include <wtf/Noncopyable.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + + class ResourceError; + class ResourceRequest; + class ResourceResponse; + class ScriptExecutionContext; + class ThreadableLoaderClient; + + enum LoadCallbacks { + SendLoadCallbacks, + DoNotSendLoadCallbacks + }; + + enum ContentSniff { + SniffContent, + DoNotSniffContent + }; + + // Useful for doing loader operations from any thread (not threadsafe, + // just able to run on threads other than the main thread). + class ThreadableLoader : Noncopyable { + public: + static PassRefPtr<ThreadableLoader> create(ScriptExecutionContext*, ThreadableLoaderClient*, const ResourceRequest&, LoadCallbacks, ContentSniff); + static unsigned long loadResourceSynchronously(ScriptExecutionContext*, const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data); + + virtual void cancel() = 0; + void ref() { refThreadableLoader(); } + void deref() { derefThreadableLoader(); } + + protected: + virtual ~ThreadableLoader() { } + virtual void refThreadableLoader() = 0; + virtual void derefThreadableLoader() = 0; + }; + +} // namespace WebCore + +#endif // ThreadableLoader_h diff --git a/WebCore/loader/ThreadableLoaderClient.h b/WebCore/loader/ThreadableLoaderClient.h new file mode 100644 index 0000000..4fde404 --- /dev/null +++ b/WebCore/loader/ThreadableLoaderClient.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2009, Google 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: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Google Inc. 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 THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT + * OWNER 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 ThreadableLoaderClient_h +#define ThreadableLoaderClient_h + +namespace WebCore { + + class ResourceResponse; + + class ThreadableLoaderClient { + public: + virtual void didSendData(unsigned long long /*bytesSent*/, unsigned long long /*totalBytesToBeSent*/) { } + + virtual void didReceiveResponse(const ResourceResponse&) { } + virtual void didReceiveData(const char*, int /*lengthReceived*/) { } + virtual void didFinishLoading(unsigned long /*identifer*/) { } + virtual void didFail() { } + virtual void didGetCancelled() { } + + virtual void didReceiveAuthenticationCancellation(const ResourceResponse&) { } + + protected: + virtual ~ThreadableLoaderClient() { } + }; + +} // namespace WebCore + +#endif // ThreadableLoaderClient_h diff --git a/WebCore/loader/appcache/ApplicationCache.cpp b/WebCore/loader/appcache/ApplicationCache.cpp index d29eda8..abe8b22 100644 --- a/WebCore/loader/appcache/ApplicationCache.cpp +++ b/WebCore/loader/appcache/ApplicationCache.cpp @@ -51,10 +51,15 @@ ApplicationCache::~ApplicationCache() void ApplicationCache::setGroup(ApplicationCacheGroup* group) { - ASSERT(!m_group); + ASSERT(!m_group || group == m_group); m_group = group; } +bool ApplicationCache::isComplete() const +{ + return !m_group->cacheIsBeingUpdated(this); +} + void ApplicationCache::setManifestResource(PassRefPtr<ApplicationCacheResource> manifest) { ASSERT(manifest); @@ -76,6 +81,7 @@ void ApplicationCache::addResource(PassRefPtr<ApplicationCacheResource> resource if (m_storageID) { ASSERT(!resource->storageID()); + ASSERT(resource->type() & (ApplicationCacheResource::Dynamic | ApplicationCacheResource::Implicit)); // Add the resource to the storage. cacheStorage().store(resource.get(), this); @@ -129,7 +135,7 @@ unsigned ApplicationCache::numDynamicEntries() const return 0; } -String ApplicationCache::dynamicEntry(unsigned index) const +String ApplicationCache::dynamicEntry(unsigned) const { // FIXME: Implement return String(); @@ -137,20 +143,19 @@ String ApplicationCache::dynamicEntry(unsigned index) const bool ApplicationCache::addDynamicEntry(const String& url) { - if (!equalIgnoringCase(m_group->manifestURL().protocol(), - KURL(url).protocol())) + if (!equalIgnoringCase(m_group->manifestURL().protocol(), KURL(url).protocol())) return false; // FIXME: Implement return true; } -void ApplicationCache::removeDynamicEntry(const String& url) +void ApplicationCache::removeDynamicEntry(const String&) { // FIXME: Implement } -void ApplicationCache::setOnlineWhitelist(const HashSet<String>& onlineWhitelist) +void ApplicationCache::setOnlineWhitelist(const Vector<KURL>& onlineWhitelist) { ASSERT(m_onlineWhitelist.isEmpty()); m_onlineWhitelist = onlineWhitelist; @@ -158,14 +163,33 @@ void ApplicationCache::setOnlineWhitelist(const HashSet<String>& 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); + size_t whitelistSize = m_onlineWhitelist.size(); + for (size_t i = 0; i < whitelistSize; ++i) { + if (protocolHostAndPortAreEqual(url, m_onlineWhitelist[i]) && url.string().startsWith(m_onlineWhitelist[i].string())) + return true; + } + return false; } - + +void ApplicationCache::setFallbackURLs(const FallbackURLVector& fallbackURLs) +{ + ASSERT(m_fallbackURLs.isEmpty()); + m_fallbackURLs = fallbackURLs; +} + +bool ApplicationCache::urlMatchesFallbackNamespace(const KURL& url, KURL* fallbackURL) +{ + size_t fallbackCount = m_fallbackURLs.size(); + for (size_t i = 0; i < fallbackCount; ++i) { + if (protocolHostAndPortAreEqual(url, m_fallbackURLs[i].first) && url.string().startsWith(m_fallbackURLs[i].first.string())) { + if (fallbackURL) + *fallbackURL = m_fallbackURLs[i].second; + return true; + } + } + return false; +} + void ApplicationCache::clearStorageID() { m_storageID = 0; diff --git a/WebCore/loader/appcache/ApplicationCache.h b/WebCore/loader/appcache/ApplicationCache.h index e536cbe..77653f7 100644 --- a/WebCore/loader/appcache/ApplicationCache.h +++ b/WebCore/loader/appcache/ApplicationCache.h @@ -28,14 +28,13 @@ #if ENABLE(OFFLINE_WEB_APPLICATIONS) +#include "PlatformString.h" +#include "StringHash.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> -#include "StringHash.h" -#include "PlatformString.h" - namespace WebCore { class ApplicationCacheGroup; @@ -43,7 +42,9 @@ class ApplicationCacheResource; class DocumentLoader; class KURL; class ResourceRequest; - + +typedef Vector<std::pair<KURL, KURL> > FallbackURLVector; + class ApplicationCache : public RefCounted<ApplicationCache> { public: static PassRefPtr<ApplicationCache> create() { return adoptRef(new ApplicationCache); } @@ -57,7 +58,9 @@ public: void setGroup(ApplicationCacheGroup*); ApplicationCacheGroup* group() const { return m_group; } - + + bool isComplete() const; + ApplicationCacheResource* resourceForRequest(const ResourceRequest&); ApplicationCacheResource* resourceForURL(const String& url); @@ -67,9 +70,13 @@ public: 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&); + void setOnlineWhitelist(const Vector<KURL>& onlineWhitelist); + const Vector<KURL>& onlineWhitelist() const { return m_onlineWhitelist; } + bool isURLInOnlineWhitelist(const KURL&); // There is an entry in online whitelist that has the same origin as the resource's URL and that is a prefix match for the resource's URL. + + void setFallbackURLs(const FallbackURLVector&); + const FallbackURLVector& fallbackURLs() const { return m_fallbackURLs; } + bool urlMatchesFallbackNamespace(const KURL&, KURL* fallbackURL = 0); #ifndef NDEBUG void dump(); @@ -84,15 +91,20 @@ public: void clearStorageID(); static bool requestIsHTTPOrHTTPSGet(const ResourceRequest&); + private: ApplicationCache(); ApplicationCacheGroup* m_group; ResourceMap m_resources; ApplicationCacheResource* m_manifest; - - HashSet<String> m_onlineWhitelist; - + + Vector<KURL> m_onlineWhitelist; + FallbackURLVector m_fallbackURLs; + + // While an update is in progress, changes in dynamic entries are queued for later execution. + Vector<std::pair<KURL, bool> > m_pendingDynamicEntryActions; + unsigned m_storageID; }; diff --git a/WebCore/loader/appcache/ApplicationCacheGroup.cpp b/WebCore/loader/appcache/ApplicationCacheGroup.cpp index ff26767..2367e79 100644 --- a/WebCore/loader/appcache/ApplicationCacheGroup.cpp +++ b/WebCore/loader/appcache/ApplicationCacheGroup.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -46,10 +46,11 @@ namespace WebCore { ApplicationCacheGroup::ApplicationCacheGroup(const KURL& manifestURL, bool isCopy) : m_manifestURL(manifestURL) - , m_status(Idle) - , m_savedNewestCachePointer(0) + , m_updateStatus(Idle) , m_frame(0) , m_storageID(0) + , m_isObsolete(false) + , m_completionType(None) , m_isCopy(isCopy) { } @@ -62,7 +63,7 @@ ApplicationCacheGroup::~ApplicationCacheGroup() ASSERT(m_caches.contains(m_newestCache.get())); ASSERT(!m_cacheBeingUpdated); ASSERT(m_associatedDocumentLoaders.isEmpty()); - ASSERT(m_cacheCandidates.isEmpty()); + ASSERT(m_pendingMasterResourceLoaders.isEmpty()); ASSERT(m_newestCache->group() == this); return; @@ -76,18 +77,14 @@ ApplicationCacheGroup::~ApplicationCacheGroup() cacheStorage().cacheGroupDestroyed(this); } -ApplicationCache* ApplicationCacheGroup::cacheForMainRequest(const ResourceRequest& request, DocumentLoader* loader) +ApplicationCache* ApplicationCacheGroup::cacheForMainRequest(const ResourceRequest& request, DocumentLoader*) { 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()); + ASSERT(!group->isObsolete()); return group->newestCache(); } @@ -95,6 +92,21 @@ ApplicationCache* ApplicationCacheGroup::cacheForMainRequest(const ResourceReque return 0; } +ApplicationCache* ApplicationCacheGroup::fallbackCacheForMainRequest(const ResourceRequest& request, DocumentLoader*) +{ + if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) + return 0; + + if (ApplicationCacheGroup* group = cacheStorage().fallbackCacheGroupForURL(request.url())) { + ASSERT(group->newestCache()); + ASSERT(!group->isObsolete()); + + return group->newestCache(); + } + + return 0; +} + void ApplicationCacheGroup::selectCache(Frame* frame, const KURL& manifestURL) { ASSERT(frame && frame->page()); @@ -112,28 +124,22 @@ void ApplicationCacheGroup::selectCache(Frame* frame, const KURL& manifestURL) 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); + mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext); } 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. + // The main resource was loaded from cache, so the cache must have an entry for it. Mark it as foreign. + ApplicationCacheResource* resource = mainResourceCache->resourceForURL(documentLoader->url()); + bool inStorage = resource->storageID(); + resource->addType(ApplicationCacheResource::Foreign); + if (inStorage) + cacheStorage().storeUpdatedType(resource, mainResourceCache); + + // Restart the current navigation from the top of the navigation algorithm, undoing any changes that were made + // as part of the initial load. + // The navigation will not result in the same resource being loaded, because "foreign" entries are never picked during navigation. + frame->loader()->scheduleLocationChange(documentLoader->url(), frame->loader()->referrer(), true); } return; @@ -142,53 +148,20 @@ void ApplicationCacheGroup::selectCache(Frame* frame, const KURL& manifestURL) // 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); + if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) return; - } // Check that the resource URL has the same scheme/host/port as the manifest URL. - if (!protocolHostAndPortAreEqual(manifestURL, request.url())) { - selectCacheWithoutManifestURL(frame); + if (!protocolHostAndPortAreEqual(manifestURL, request.url())) 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); - } + + documentLoader->setCandidateApplicationCacheGroup(group); + group->m_pendingMasterResourceLoaders.add(documentLoader); + + ASSERT(!group->m_cacheBeingUpdated || group->m_updateStatus != Idle); + group->update(frame, ApplicationCacheUpdateWithBrowsingContext); } void ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame* frame) @@ -200,54 +173,110 @@ void ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame* frame) ASSERT(!documentLoader->applicationCache()); ApplicationCache* mainResourceCache = documentLoader->mainResourceApplicationCache(); - bool isMainFrame = frame->page()->mainFrame() == frame; - if (isMainFrame && mainResourceCache) { + if (mainResourceCache) { mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); - mainResourceCache->group()->update(frame); + mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext); } } void ApplicationCacheGroup::finishedLoadingMainResource(DocumentLoader* loader) { + ASSERT(m_pendingMasterResourceLoaders.contains(loader)); + ASSERT(m_completionType == None || m_pendingEntries.isEmpty()); 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()); + switch (m_completionType) { + case None: + // The main resource finished loading before the manifest was ready. It will be handled via dispatchMainResources() later. + return; + case NoUpdate: + ASSERT(!m_cacheBeingUpdated); + associateDocumentLoaderWithCache(loader, m_newestCache.get()); + + if (ApplicationCacheResource* resource = m_newestCache->resourceForURL(url)) { + if (!(resource->type() & ApplicationCacheResource::Implicit)) { + resource->addType(ApplicationCacheResource::Implicit); + ASSERT(!resource->storageID()); + } + } else + m_newestCache->addResource(ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Implicit, loader->mainResourceData())); + + break; + case Failure: + // Cache update has been a failure, so there is no reason to keep the document associated with the incomplete cache + // (its main resource was not cached yet, so it is likely that the application changed significantly server-side). + ASSERT(!m_cacheBeingUpdated); // Already cleared out by stopLoading(). + loader->setApplicationCache(0); // Will unset candidate, too. + m_associatedDocumentLoaders.remove(loader); + postListenerTask(&DOMApplicationCache::callErrorListener, loader); + break; + case Completed: + ASSERT(m_associatedDocumentLoaders.contains(loader)); + + if (ApplicationCacheResource* resource = m_cacheBeingUpdated->resourceForURL(url)) { + if (!(resource->type() & ApplicationCacheResource::Implicit)) { + resource->addType(ApplicationCacheResource::Implicit); + ASSERT(!resource->storageID()); + } + } else + m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Implicit, loader->mainResourceData())); + // The "cached" event will be posted to all associated documents once update is complete. + break; + } - ASSERT(m_cacheBeingUpdated); - m_cacheBeingUpdated->addResource(resource.release()); - - m_pendingEntries.remove(it); - + m_pendingMasterResourceLoaders.remove(loader); checkIfLoadIsComplete(); } void ApplicationCacheGroup::failedLoadingMainResource(DocumentLoader* loader) { - ASSERT(m_cacheCandidates.contains(loader) || m_associatedDocumentLoaders.contains(loader)); + ASSERT(m_pendingMasterResourceLoaders.contains(loader)); + ASSERT(m_completionType == None || m_pendingEntries.isEmpty()); - // Note that cacheUpdateFailed() can cause the cache group to be deleted. - cacheUpdateFailed(); + switch (m_completionType) { + case None: + // The main resource finished loading before the manifest was ready. It will be handled via dispatchMainResources() later. + return; + case NoUpdate: + ASSERT(!m_cacheBeingUpdated); + + // The manifest didn't change, and we have a relevant cache - but the main resource download failed mid-way, so it cannot be stored to the cache, + // and the loader does not get associated to it. If there are other main resources being downloaded for this cache group, they may still succeed. + postListenerTask(&DOMApplicationCache::callErrorListener, loader); + + break; + case Failure: + // Cache update failed, too. + ASSERT(!m_cacheBeingUpdated); // Already cleared out by stopLoading(). + ASSERT(!loader->applicationCache() || loader->applicationCache() == m_cacheBeingUpdated); + + loader->setApplicationCache(0); // Will unset candidate, too. + m_associatedDocumentLoaders.remove(loader); + postListenerTask(&DOMApplicationCache::callErrorListener, loader); + break; + case Completed: + // The cache manifest didn't list this main resource, and all cache entries were already updated successfully - but the main resource failed to load, + // so it cannot be stored to the cache. If there are other main resources being downloaded for this cache group, they may still succeed. + ASSERT(m_associatedDocumentLoaders.contains(loader)); + ASSERT(loader->applicationCache() == m_cacheBeingUpdated); + ASSERT(!loader->candidateApplicationCacheGroup()); + m_associatedDocumentLoaders.remove(loader); + loader->setApplicationCache(0); + + postListenerTask(&DOMApplicationCache::callErrorListener, loader); + + break; + } + + m_pendingMasterResourceLoaders.remove(loader); + checkIfLoadIsComplete(); } void ApplicationCacheGroup::stopLoading() -{ +{ if (m_manifestHandle) { ASSERT(!m_currentHandle); - ASSERT(!m_cacheBeingUpdated); m_manifestHandle->setClient(0); m_manifestHandle->cancel(); @@ -264,91 +293,102 @@ void ApplicationCacheGroup::stopLoading() } m_cacheBeingUpdated = 0; + m_pendingEntries.clear(); } -void ApplicationCacheGroup::documentLoaderDestroyed(DocumentLoader* loader) +void ApplicationCacheGroup::disassociateDocumentLoader(DocumentLoader* loader) { HashSet<DocumentLoader*>::iterator it = m_associatedDocumentLoaders.find(loader); - - if (it != m_associatedDocumentLoaders.end()) { - ASSERT(!m_cacheCandidates.contains(loader)); - + if (it != m_associatedDocumentLoaders.end()) m_associatedDocumentLoaders.remove(it); - } else { - ASSERT(m_cacheCandidates.contains(loader)); - m_cacheCandidates.remove(loader); - } - - if (!m_associatedDocumentLoaders.isEmpty() || !m_cacheCandidates.isEmpty()) + + m_pendingMasterResourceLoaders.remove(loader); + + loader->setApplicationCache(0); // Will set candidate to 0, too. + + if (!m_associatedDocumentLoaders.isEmpty() || !m_pendingMasterResourceLoaders.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; - + + if (m_caches.isEmpty()) { + // There is an initial cache attempt in progress. + ASSERT(!m_newestCache); + // Delete ourselves, causing the cache attempt to be stopped. + delete this; 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; -} + + ASSERT(m_caches.contains(m_newestCache.get())); + + // Release our reference to the newest cache. This could cause us to be deleted. + // Any ongoing updates will be stopped from destructor. + m_newestCache.release(); +} void ApplicationCacheGroup::cacheDestroyed(ApplicationCache* cache) { - ASSERT(m_caches.contains(cache)); + if (!m_caches.contains(cache)) + return; m_caches.remove(cache); - - if (cache != m_savedNewestCachePointer) - cacheStorage().remove(cache); - if (m_caches.isEmpty()) + if (m_caches.isEmpty()) { + ASSERT(m_associatedDocumentLoaders.isEmpty()); + ASSERT(m_pendingMasterResourceLoaders.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_newestCache = newestCache; + m_caches.add(m_newestCache.get()); m_newestCache->setGroup(this); } -void ApplicationCacheGroup::update(Frame* frame) +void ApplicationCacheGroup::makeObsolete() { - if (m_status == Checking || m_status == Downloading) + if (isObsolete()) + return; + + m_isObsolete = true; + cacheStorage().cacheGroupMadeObsolete(this); + ASSERT(!m_storageID); +} + +void ApplicationCacheGroup::update(Frame* frame, ApplicationCacheUpdateOption updateOption) +{ + if (m_updateStatus == Checking || m_updateStatus == Downloading) { + if (updateOption == ApplicationCacheUpdateWithBrowsingContext) { + postListenerTask(&DOMApplicationCache::callCheckingListener, frame->loader()->documentLoader()); + if (m_updateStatus == Downloading) + postListenerTask(&DOMApplicationCache::callDownloadingListener, frame->loader()->documentLoader()); + } return; + } ASSERT(!m_frame); m_frame = frame; - m_status = Checking; + m_updateStatus = Checking; - callListenersOnAssociatedDocuments(&DOMApplicationCache::callCheckingListener); + postListenerTask(&DOMApplicationCache::callCheckingListener, m_associatedDocumentLoaders); + if (!m_newestCache) { + ASSERT(updateOption == ApplicationCacheUpdateWithBrowsingContext); + postListenerTask(&DOMApplicationCache::callCheckingListener, frame->loader()->documentLoader()); + } ASSERT(!m_manifestHandle); ASSERT(!m_manifestResource); - + ASSERT(m_completionType == None); + // FIXME: Handle defer loading ResourceRequest request(m_manifestURL); m_frame->loader()->applyUserAgent(request); + // FIXME: Should ask to revalidate from origin. m_manifestHandle = ResourceHandle::create(request, this, m_frame, false, true, false); } @@ -361,14 +401,7 @@ void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const Res } 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); @@ -379,11 +412,37 @@ void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const Res // If this is an initial cache attempt, we should not get implicit resources delivered here. if (!m_newestCache) ASSERT(!(type & ApplicationCacheResource::Implicit)); + + if (response.httpStatusCode() / 100 != 2 || response.url() != m_currentHandle->request().url()) { + if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) { + // Note that cacheUpdateFailed() can cause the cache group to be deleted. + cacheUpdateFailed(); + } else if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) { + // Skip this resource. It is dropped from the cache. + m_currentHandle->cancel(); + m_currentHandle = 0; + m_pendingEntries.remove(handle->request().url()); + // Load the next resource, if any. + startLoadingEntry(); + } else { + // Copy the resource and its metadata from the newest application cache in cache group whose completeness flag is complete, and act + // as if that was the fetched resource, ignoring the resource obtained from the network. + ASSERT(m_newestCache); + ApplicationCacheResource* resource = m_newestCache->resourceForURL(handle->request().url()); + ASSERT(resource); + m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(handle->request().url(), resource->response(), resource->type(), resource->data())); + // Load the next resource, if any. + m_currentHandle->cancel(); + m_currentHandle = 0; + startLoadingEntry(); + } + return; + } m_currentResource = ApplicationCacheResource::create(url, response, type); } -void ApplicationCacheGroup::didReceiveData(ResourceHandle* handle, const char* data, int length, int lengthReceived) +void ApplicationCacheGroup::didReceiveData(ResourceHandle* handle, const char* data, int length, int) { if (handle == m_manifestHandle) { didReceiveManifestData(data, length); @@ -413,38 +472,53 @@ void ApplicationCacheGroup::didFinishLoading(ResourceHandle* handle) m_cacheBeingUpdated->addResource(m_currentResource.release()); m_currentHandle = 0; - // Load the next file. - if (!m_pendingEntries.isEmpty()) { - startLoadingEntry(); - return; - } - - checkIfLoadIsComplete(); + // Load the next resource, if any. + startLoadingEntry(); } void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError&) { if (handle == m_manifestHandle) { - didFailToLoadManifest(); + cacheUpdateFailed(); return; } - - // Note that cacheUpdateFailed() can cause the cache group to be deleted. - cacheUpdateFailed(); + + unsigned type = m_currentResource ? m_currentResource->type() : m_pendingEntries.get(handle->request().url()); + + ASSERT(!m_currentResource || !m_pendingEntries.contains(handle->request().url())); + m_currentResource = 0; + m_pendingEntries.remove(handle->request().url()); + + if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) { + // Note that cacheUpdateFailed() can cause the cache group to be deleted. + cacheUpdateFailed(); + } else { + // Copy the resource and its metadata from the newest application cache in cache group whose completeness flag is complete, and act + // as if that was the fetched resource, ignoring the resource obtained from the network. + ASSERT(m_newestCache); + ApplicationCacheResource* resource = m_newestCache->resourceForURL(handle->request().url()); + ASSERT(resource); + m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(handle->request().url(), resource->response(), resource->type(), resource->data())); + // Load the next resource, if any. + startLoadingEntry(); + } } void ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& response) { - int statusCode = response.httpStatusCode() / 100; + ASSERT(!m_manifestResource); + ASSERT(m_manifestHandle); - if (statusCode == 4 || statusCode == 5 || - !equalIgnoringCase(response.mimeType(), "text/cache-manifest")) { - didFailToLoadManifest(); + if (response.httpStatusCode() == 404 || response.httpStatusCode() == 410) { + manifestNotFound(); return; } - - ASSERT(!m_manifestResource); - ASSERT(m_manifestHandle); + + if (response.httpStatusCode() / 100 != 2 || response.url() != m_manifestHandle->request().url() || !equalIgnoringCase(response.mimeType(), "text/cache-manifest")) { + cacheUpdateFailed(); + return; + } + m_manifestResource = ApplicationCacheResource::create(m_manifestHandle->request().url(), response, ApplicationCacheResource::Manifest); } @@ -458,7 +532,7 @@ void ApplicationCacheGroup::didReceiveManifestData(const char* data, int length) void ApplicationCacheGroup::didFinishLoadingManifest() { if (!m_manifestResource) { - didFailToLoadManifest(); + cacheUpdateFailed(); return; } @@ -473,47 +547,41 @@ void ApplicationCacheGroup::didFinishLoadingManifest() 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_completionType = NoUpdate; m_manifestResource = 0; + deliverDelayedMainResources(); + return; } } Manifest manifest; if (!parseManifest(m_manifestURL, m_manifestResource->data()->data(), m_manifestResource->data()->size(), manifest)) { - didFailToLoadManifest(); + cacheUpdateFailed(); return; } - - // FIXME: Add the opportunistic caching namespaces and their fallbacks. - + + ASSERT(!m_cacheBeingUpdated); + m_cacheBeingUpdated = ApplicationCache::create(); + m_cacheBeingUpdated->setGroup(this); + + HashSet<DocumentLoader*>::const_iterator masterEnd = m_pendingMasterResourceLoaders.end(); + for (HashSet<DocumentLoader*>::const_iterator iter = m_pendingMasterResourceLoaders.begin(); iter != masterEnd; ++iter) + associateDocumentLoaderWithCache(*iter, m_cacheBeingUpdated.get()); + // We have the manifest, now download the resources. - m_status = Downloading; + m_updateStatus = Downloading; - callListenersOnAssociatedDocuments(&DOMApplicationCache::callDownloadingListener); + postListenerTask(&DOMApplicationCache::callDownloadingListener, m_associatedDocumentLoaders); + + ASSERT(m_pendingEntries.isEmpty()); -#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)) + if (type & (ApplicationCacheResource::Implicit | ApplicationCacheResource::Dynamic)) addEntry(it->first, type); } } @@ -521,8 +589,13 @@ void ApplicationCacheGroup::didFinishLoadingManifest() HashSet<String>::const_iterator end = manifest.explicitURLs.end(); for (HashSet<String>::const_iterator it = manifest.explicitURLs.begin(); it != end; ++it) addEntry(*it, ApplicationCacheResource::Explicit); + + size_t fallbackCount = manifest.fallbackURLs.size(); + for (size_t i = 0; i < fallbackCount; ++i) + addEntry(manifest.fallbackURLs[i].second, ApplicationCacheResource::Fallback); m_cacheBeingUpdated->setOnlineWhitelist(manifest.onlineWhitelistedURLs); + m_cacheBeingUpdated->setFallbackURLs(manifest.fallbackURLs); startLoadingEntry(); } @@ -530,82 +603,94 @@ void ApplicationCacheGroup::didFinishLoadingManifest() void ApplicationCacheGroup::cacheUpdateFailed() { stopLoading(); - - callListenersOnAssociatedDocuments(&DOMApplicationCache::callErrorListener); + m_manifestResource = 0; - m_pendingEntries.clear(); + // Wait for master resource loads to finish. + m_completionType = Failure; + deliverDelayedMainResources(); +} + +void ApplicationCacheGroup::manifestNotFound() +{ + makeObsolete(); + + postListenerTask(&DOMApplicationCache::callObsoleteListener, m_associatedDocumentLoaders); + postListenerTask(&DOMApplicationCache::callErrorListener, m_pendingMasterResourceLoaders); + + stopLoading(); + + ASSERT(m_pendingEntries.isEmpty()); m_manifestResource = 0; - while (!m_cacheCandidates.isEmpty()) { - HashSet<DocumentLoader*>::iterator it = m_cacheCandidates.begin(); + while (!m_pendingMasterResourceLoaders.isEmpty()) { + HashSet<DocumentLoader*>::iterator it = m_pendingMasterResourceLoaders.begin(); ASSERT((*it)->candidateApplicationCacheGroup() == this); + ASSERT(!(*it)->applicationCache()); (*it)->setCandidateApplicationCacheGroup(0); - m_cacheCandidates.remove(it); + m_pendingMasterResourceLoaders.remove(it); } - - m_status = Idle; + + m_updateStatus = Idle; m_frame = 0; - // If there are no associated caches, delete ourselves - if (m_associatedDocumentLoaders.isEmpty()) + if (m_caches.isEmpty()) { + ASSERT(m_associatedDocumentLoaders.isEmpty()); + ASSERT(!m_cacheBeingUpdated); 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) + if (m_manifestHandle || !m_pendingEntries.isEmpty() || !m_pendingMasterResourceLoaders.isEmpty()) return; - if (!m_pendingEntries.isEmpty()) - return; - - // We're done + // We're done, all resources have finished downloading (successfully or not). + 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); + switch (m_completionType) { + case None: + ASSERT_NOT_REACHED(); + return; + case NoUpdate: + ASSERT(isUpgradeAttempt); + ASSERT(!m_cacheBeingUpdated); + ASSERT(m_storageID); + postListenerTask(&DOMApplicationCache::callNoUpdateListener, m_associatedDocumentLoaders); + break; + case Failure: + ASSERT(!m_cacheBeingUpdated); + postListenerTask(&DOMApplicationCache::callErrorListener, m_associatedDocumentLoaders); + if (m_caches.isEmpty()) { + ASSERT(m_associatedDocumentLoaders.isEmpty()); + delete this; + return; } + break; + case Completed: { + // FIXME: Fetch the resource from manifest URL again, and check whether it is identical to the one used for update (in case the application was upgraded server-side in the meanwhile). (<rdar://problem/6467625>) + + ASSERT(m_cacheBeingUpdated); + m_cacheBeingUpdated->setManifestResource(m_manifestResource.release()); + + RefPtr<ApplicationCache> oldNewestCache = (m_newestCache == m_cacheBeingUpdated) ? 0 : m_newestCache; + + setNewestCache(m_cacheBeingUpdated.release()); + cacheStorage().storeNewestCache(this); + + if (oldNewestCache) + cacheStorage().remove(oldNewestCache.get()); + + postListenerTask(isUpgradeAttempt ? &DOMApplicationCache::callUpdateReadyListener : &DOMApplicationCache::callCachedListener, m_associatedDocumentLoaders); + break; } - - setNewestCache(m_cacheBeingUpdated.release()); - - // Store the cache - cacheStorage().storeNewestCache(this); - - callListeners(isUpgradeAttempt ? &DOMApplicationCache::callUpdateReadyListener : &DOMApplicationCache::callCachedListener, - documentLoaders); + } + + m_completionType = None; + m_updateStatus = Idle; + m_frame = 0; } void ApplicationCacheGroup::startLoadingEntry() @@ -613,27 +698,14 @@ void ApplicationCacheGroup::startLoadingEntry() ASSERT(m_cacheBeingUpdated); if (m_pendingEntries.isEmpty()) { - checkIfLoadIsComplete(); + m_completionType = Completed; + deliverDelayedMainResources(); 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); + postListenerTask(&DOMApplicationCache::callProgressListener, m_associatedDocumentLoaders); // FIXME: If this is an upgrade attempt, the newest cache should be used as an HTTP cache. @@ -641,24 +713,49 @@ void ApplicationCacheGroup::startLoadingEntry() ResourceRequest request(it->first); m_frame->loader()->applyUserAgent(request); + // FIXME: Should ask to revalidate from origin. m_currentHandle = ResourceHandle::create(request, this, m_frame, false, true, false); } +void ApplicationCacheGroup::deliverDelayedMainResources() +{ + // Need to copy loaders, because the cache group may be destroyed at the end of iteration. + Vector<DocumentLoader*> loaders; + copyToVector(m_pendingMasterResourceLoaders, loaders); + size_t count = loaders.size(); + for (size_t i = 0; i != count; ++i) { + DocumentLoader* loader = loaders[i]; + if (loader->isLoadingMainResource()) + continue; + + const ResourceError& error = loader->mainDocumentError(); + if (error.isNull()) + finishedLoadingMainResource(loader); + else + failedLoadingMainResource(loader); + } + if (!count) + checkIfLoadIsComplete(); +} + 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 + // (i.e., the main resource finished loading before the manifest). if (ApplicationCacheResource* resource = m_cacheBeingUpdated->resourceForURL(url)) { ASSERT(resource->type() & ApplicationCacheResource::Implicit); + ASSERT(!m_frame->loader()->documentLoader()->isLoadingMainResource()); 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) { + ASSERT(m_manifestResource); + if (m_manifestResource->url() == url) { m_manifestResource->addType(type); return; } @@ -671,33 +768,60 @@ void ApplicationCacheGroup::addEntry(const String& url, unsigned type) void ApplicationCacheGroup::associateDocumentLoaderWithCache(DocumentLoader* loader, ApplicationCache* cache) { + // If teardown started already, revive the group. + if (!m_newestCache && !m_cacheBeingUpdated) + m_newestCache = cache; + + ASSERT(!m_isObsolete); + 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); +class CallCacheListenerTask : public ScriptExecutionContext::Task { + typedef void (DOMApplicationCache::*ListenerFunction)(); +public: + static PassRefPtr<CallCacheListenerTask> create(ListenerFunction listenerFunction) + { + return adoptRef(new CallCacheListenerTask(listenerFunction)); + } - callListeners(listenerFunction, loaders); + virtual void performTask(ScriptExecutionContext* context) + { + ASSERT(context->isDocument()); + if (DOMWindow* window = static_cast<Document*>(context)->domWindow()) { + if (DOMApplicationCache* domCache = window->optionalApplicationCache()) + (domCache->*m_listenerFunction)(); + } + } + +private: + CallCacheListenerTask(ListenerFunction listenerFunction) + : m_listenerFunction(listenerFunction) + { + } + + ListenerFunction m_listenerFunction; +}; + +void ApplicationCacheGroup::postListenerTask(ListenerFunction listenerFunction, const HashSet<DocumentLoader*>& loaderSet) +{ + HashSet<DocumentLoader*>::const_iterator loaderSetEnd = loaderSet.end(); + for (HashSet<DocumentLoader*>::const_iterator iter = loaderSet.begin(); iter != loaderSetEnd; ++iter) + postListenerTask(listenerFunction, *iter); } - -void ApplicationCacheGroup::callListeners(ListenerFunction listenerFunction, const Vector<RefPtr<DocumentLoader> >& loaders) + +void ApplicationCacheGroup::postListenerTask(ListenerFunction listenerFunction, DocumentLoader* loader) { - 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)(); - } + Frame* frame = loader->frame(); + if (!frame) + return; + + ASSERT(frame->loader()->documentLoader() == loader); + + frame->document()->postTask(CallCacheListenerTask::create(listenerFunction)); } void ApplicationCacheGroup::clearStorageID() diff --git a/WebCore/loader/appcache/ApplicationCacheGroup.h b/WebCore/loader/appcache/ApplicationCacheGroup.h index d5b7563..e4b2d3e 100644 --- a/WebCore/loader/appcache/ApplicationCacheGroup.h +++ b/WebCore/loader/appcache/ApplicationCacheGroup.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -47,43 +47,55 @@ class Document; class DocumentLoader; class Frame; +enum ApplicationCacheUpdateOption { + ApplicationCacheUpdateWithBrowsingContext, + ApplicationCacheUpdateWithoutBrowsingContext +}; + class ApplicationCacheGroup : Noncopyable, ResourceHandleClient { public: ApplicationCacheGroup(const KURL& manifestURL, bool isCopy = false); ~ApplicationCacheGroup(); - enum Status { Idle, Checking, Downloading }; + enum UpdateStatus { Idle, Checking, Downloading }; static ApplicationCache* cacheForMainRequest(const ResourceRequest&, DocumentLoader*); + static ApplicationCache* fallbackCacheForMainRequest(const ResourceRequest&, DocumentLoader*); static void selectCache(Frame*, const KURL& manifestURL); static void selectCacheWithoutManifestURL(Frame*); const KURL& manifestURL() const { return m_manifestURL; } - Status status() const { return m_status; } - + UpdateStatus updateStatus() const { return m_updateStatus; } + void setStorageID(unsigned storageID) { m_storageID = storageID; } unsigned storageID() const { return m_storageID; } void clearStorageID(); - void update(Frame*); + void update(Frame*, ApplicationCacheUpdateOption); // FIXME: Frame should not bee needed when updating witout browsing context. void cacheDestroyed(ApplicationCache*); - + + bool cacheIsBeingUpdated(const ApplicationCache* cache) const { return cache == m_cacheBeingUpdated; } + ApplicationCache* newestCache() const { return m_newestCache.get(); } - ApplicationCache* savedNewestCachePointer() const { return m_savedNewestCachePointer; } - + void setNewestCache(PassRefPtr<ApplicationCache>); + + void makeObsolete(); + bool isObsolete() const { return m_isObsolete; } + void finishedLoadingMainResource(DocumentLoader*); void failedLoadingMainResource(DocumentLoader*); - void documentLoaderDestroyed(DocumentLoader*); - void setNewestCache(PassRefPtr<ApplicationCache> newestCache); + void disassociateDocumentLoader(DocumentLoader*); bool isCopy() const { return m_isCopy; } + private: typedef void (DOMApplicationCache::*ListenerFunction)(); - void callListenersOnAssociatedDocuments(ListenerFunction); - void callListeners(ListenerFunction, const Vector<RefPtr<DocumentLoader> >& loaders); - + void postListenerTask(ListenerFunction, const HashSet<DocumentLoader*>&); + void postListenerTask(ListenerFunction, const Vector<RefPtr<DocumentLoader> >& loaders); + void postListenerTask(ListenerFunction, DocumentLoader*); + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); virtual void didFinishLoading(ResourceHandle*); @@ -92,11 +104,12 @@ private: void didReceiveManifestResponse(const ResourceResponse&); void didReceiveManifestData(const char*, int); void didFinishLoadingManifest(); - void didFailToLoadManifest(); void startLoadingEntry(); + void deliverDelayedMainResources(); void checkIfLoadIsComplete(); void cacheUpdateFailed(); + void manifestNotFound(); void addEntry(const String&, unsigned type); @@ -105,36 +118,45 @@ private: void stopLoading(); KURL m_manifestURL; - Status m_status; + UpdateStatus m_updateStatus; - // This is the newest cache in the group. + // This is the newest complete 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. + // All complete caches in this cache group. HashSet<ApplicationCache*> m_caches; - // The cache being updated (if any). + // The cache being updated (if any). Note that cache updating does not immediately create a new + // ApplicationCache object, so this may be null even when update status is not Idle. RefPtr<ApplicationCache> m_cacheBeingUpdated; - // When a cache group does not yet have a complete cache, this contains the document loaders - // that should be associated with the cache once it has been downloaded. - HashSet<DocumentLoader*> m_cacheCandidates; + // List of pending master entries, used during the update process to ensure that new master entries are cached. + HashSet<DocumentLoader*> m_pendingMasterResourceLoaders; // 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 used for fetching resources when updating. + // FIXME: An update started by a particular frame should not stop if it is destroyed, but there are other frames associated with the same cache group. Frame* m_frame; + // An obsolete cache group is never stored, but the opposite is not true - storing may fail for multiple reasons, such as exceeding disk quota. unsigned m_storageID; - + bool m_isObsolete; + + // During update, this is used to handle asynchronously arriving results. + enum CompletionType { + None, + NoUpdate, + Failure, + Completed + }; + CompletionType m_completionType; + // Whether this cache group is a copy that's only used for transferring the cache to another file. bool m_isCopy; diff --git a/WebCore/loader/appcache/ApplicationCacheResource.cpp b/WebCore/loader/appcache/ApplicationCacheResource.cpp index ee82ff5..d78cf7f 100644 --- a/WebCore/loader/appcache/ApplicationCacheResource.cpp +++ b/WebCore/loader/appcache/ApplicationCacheResource.cpp @@ -39,8 +39,8 @@ ApplicationCacheResource::ApplicationCacheResource(const KURL& url, const Resour } void ApplicationCacheResource::addType(unsigned type) -{ - ASSERT(!m_storageID); +{ + // Caller should take care of storing the new type in database. m_type |= type; } @@ -57,8 +57,6 @@ void ApplicationCacheResource::dumpType(unsigned type) printf("foreign "); if (type & Fallback) printf("fallback "); - if (type & Opportunistic) - printf("opportunistic "); if (type & Dynamic) printf("dynamic "); diff --git a/WebCore/loader/appcache/ApplicationCacheResource.h b/WebCore/loader/appcache/ApplicationCacheResource.h index 1d7b853..96f5a0e 100644 --- a/WebCore/loader/appcache/ApplicationCacheResource.h +++ b/WebCore/loader/appcache/ApplicationCacheResource.h @@ -40,8 +40,7 @@ public: Explicit = 1 << 2, Foreign = 1 << 3, Fallback = 1 << 4, - Opportunistic = 1 << 5, - Dynamic = 1 << 6 + Dynamic = 1 << 5 }; static PassRefPtr<ApplicationCacheResource> create(const KURL& url, const ResourceResponse& response, unsigned type, PassRefPtr<SharedBuffer> buffer = SharedBuffer::create()) diff --git a/WebCore/loader/appcache/ApplicationCacheStorage.cpp b/WebCore/loader/appcache/ApplicationCacheStorage.cpp index 910d00c..791df22 100644 --- a/WebCore/loader/appcache/ApplicationCacheStorage.cpp +++ b/WebCore/loader/appcache/ApplicationCacheStorage.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,11 +31,15 @@ #include "ApplicationCache.h" #include "ApplicationCacheGroup.h" #include "ApplicationCacheResource.h" -#include "FileSystem.h" #include "CString.h" +#include "FileSystem.h" #include "KURL.h" #include "SQLiteStatement.h" #include "SQLiteTransaction.h" +#include <wtf/StdLibExtras.h> +#include <wtf/StringExtras.h> + +using namespace std; namespace WebCore { @@ -68,7 +72,7 @@ ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const KURL& manif return 0; } - unsigned newestCacheStorageID = (unsigned)statement.getColumnInt64(2); + unsigned newestCacheStorageID = static_cast<unsigned>(statement.getColumnInt64(2)); RefPtr<ApplicationCache> cache = loadCache(newestCacheStorageID); if (!cache) @@ -76,7 +80,7 @@ ApplicationCacheGroup* ApplicationCacheStorage::loadCacheGroup(const KURL& manif ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL); - group->setStorageID((unsigned)statement.getColumnInt64(0)); + group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0))); group->setNewestCache(cache.release()); return group; @@ -128,7 +132,7 @@ void ApplicationCacheStorage::loadManifestHostHashes() int result; while ((result = statement.step()) == SQLResultRow) - m_cacheHostSet.add((unsigned)statement.getColumnInt64(0)); + m_cacheHostSet.add(static_cast<unsigned>(statement.getColumnInt64(0))); } ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url) @@ -143,13 +147,19 @@ ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url CacheGroupMap::const_iterator end = m_cachesInMemory.end(); for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) { ApplicationCacheGroup* group = it->second; - + + ASSERT(!group->isObsolete()); + if (!protocolHostAndPortAreEqual(url, group->manifestURL())) continue; if (ApplicationCache* cache = group->newestCache()) { - if (cache->resourceForURL(url)) - return group; + ApplicationCacheResource* resource = cache->resourceForURL(url); + if (!resource) + continue; + if (resource->type() & ApplicationCacheResource::Foreign) + continue; + return group; } } @@ -165,23 +175,93 @@ ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url while ((result = statement.step()) == SQLResultRow) { KURL manifestURL = KURL(statement.getColumnText(1)); + if (m_cachesInMemory.contains(manifestURL)) + continue; + if (!protocolHostAndPortAreEqual(url, manifestURL)) continue; // We found a cache group that matches. Now check if the newest cache has a resource with // a matching URL. - unsigned newestCacheID = (unsigned)statement.getColumnInt64(2); + unsigned newestCacheID = static_cast<unsigned>(statement.getColumnInt64(2)); + RefPtr<ApplicationCache> cache = loadCache(newestCacheID); + + ApplicationCacheResource* resource = cache->resourceForURL(url); + if (!resource) + continue; + if (resource->type() & ApplicationCacheResource::Foreign) + continue; + + ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL); + + group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0))); + group->setNewestCache(cache.release()); + + m_cachesInMemory.set(group->manifestURL(), group); + + return group; + } + + if (result != SQLResultDone) + LOG_ERROR("Could not load cache group, error \"%s\"", m_database.lastErrorMsg()); + + return 0; +} + +ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const KURL& url) +{ + // Check if an appropriate cache already exists in memory. + CacheGroupMap::const_iterator end = m_cachesInMemory.end(); + for (CacheGroupMap::const_iterator it = m_cachesInMemory.begin(); it != end; ++it) { + ApplicationCacheGroup* group = it->second; + + ASSERT(!group->isObsolete()); + + if (ApplicationCache* cache = group->newestCache()) { + KURL fallbackURL; + if (!cache->urlMatchesFallbackNamespace(url, &fallbackURL)) + continue; + if (cache->resourceForURL(fallbackURL)->type() & ApplicationCacheResource::Foreign) + continue; + return group; + } + } + + if (!m_database.isOpen()) + return 0; + + // Check the database. Look for all cache groups with a newest cache. + SQLiteStatement statement(m_database, "SELECT id, manifestURL, newestCache FROM CacheGroups WHERE newestCache IS NOT NULL"); + if (statement.prepare() != SQLResultOk) + return 0; + + int result; + while ((result = statement.step()) == SQLResultRow) { + KURL manifestURL = KURL(statement.getColumnText(1)); + + if (m_cachesInMemory.contains(manifestURL)) + continue; + + // Fallback namespaces always have the same origin as manifest URL, so we can avoid loading caches that cannot match. + if (!protocolHostAndPortAreEqual(url, manifestURL)) + continue; + + // We found a cache group that matches. Now check if the newest cache has a resource with + // a matching fallback namespace. + unsigned newestCacheID = static_cast<unsigned>(statement.getColumnInt64(2)); RefPtr<ApplicationCache> cache = loadCache(newestCacheID); - if (!cache->resourceForURL(url)) + KURL fallbackURL; + if (!cache->urlMatchesFallbackNamespace(url, &fallbackURL)) + continue; + if (cache->resourceForURL(fallbackURL)->type() & ApplicationCacheResource::Foreign) continue; ApplicationCacheGroup* group = new ApplicationCacheGroup(manifestURL); - group->setStorageID((unsigned)statement.getColumnInt64(0)); + group->setStorageID(static_cast<unsigned>(statement.getColumnInt64(0))); group->setNewestCache(cache.release()); - ASSERT(!m_cachesInMemory.contains(manifestURL)); m_cachesInMemory.set(group->manifestURL(), group); return group; @@ -195,15 +275,33 @@ ApplicationCacheGroup* ApplicationCacheStorage::cacheGroupForURL(const KURL& url void ApplicationCacheStorage::cacheGroupDestroyed(ApplicationCacheGroup* group) { + if (group->isObsolete()) { + ASSERT(!group->storageID()); + ASSERT(m_cachesInMemory.get(group->manifestURL()) != group); + return; + } + 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()) + // If the cache group is half-created, we don't want it in the saved set (as it is not stored in database). + if (!group->storageID()) m_cacheHostSet.remove(urlHostHash(group->manifestURL())); } +void ApplicationCacheStorage::cacheGroupMadeObsolete(ApplicationCacheGroup* group) +{ + ASSERT(m_cachesInMemory.get(group->manifestURL()) == group); + ASSERT(m_cacheHostSet.contains(urlHostHash(group->manifestURL()))); + + if (ApplicationCache* newestCache = group->newestCache()) + remove(newestCache); + + m_cachesInMemory.remove(group->manifestURL()); + m_cacheHostSet.remove(urlHostHash(group->manifestURL())); +} + void ApplicationCacheStorage::setCacheDirectory(const String& cacheDirectory) { ASSERT(m_cacheDirectory.isNull()); @@ -212,6 +310,12 @@ void ApplicationCacheStorage::setCacheDirectory(const String& cacheDirectory) m_cacheDirectory = cacheDirectory; } +const String& ApplicationCacheStorage::cacheDirectory() const +{ + return m_cacheDirectory; +} + + bool ApplicationCacheStorage::executeSQLCommand(const String& sql) { ASSERT(m_database.isOpen()); @@ -224,30 +328,30 @@ bool ApplicationCacheStorage::executeSQLCommand(const String& sql) return result; } -static const int SchemaVersion = 2; +static const int schemaVersion = 3; void ApplicationCacheStorage::verifySchemaVersion() { - if (m_database.tableExists("SchemaVersion")) { - int version = SQLiteStatement(m_database, "SELECT version from SchemaVersion").getColumnInt(0); - - if (version == SchemaVersion) - return; - } - + int version = SQLiteStatement(m_database, "PRAGMA user_version").getColumnInt(0); + if (version == schemaVersion) + return; + m_database.clearAllTables(); - SQLiteTransaction createSchemaVersionTable(m_database); - createSchemaVersionTable.begin(); + // Update user version. + SQLiteTransaction setDatabaseVersion(m_database); + setDatabaseVersion.begin(); - executeSQLCommand("CREATE TABLE SchemaVersion (version INTEGER NOT NULL)"); - SQLiteStatement statement(m_database, "INSERT INTO SchemaVersion (version) VALUES (?)"); + char userVersionSQL[32]; + int unusedNumBytes = snprintf(userVersionSQL, sizeof(userVersionSQL), "PRAGMA user_version=%d", schemaVersion); + ASSERT_UNUSED(unusedNumBytes, static_cast<int>(sizeof(userVersionSQL)) >= unusedNumBytes); + + SQLiteStatement statement(m_database, userVersionSQL); if (statement.prepare() != SQLResultOk) return; - statement.bindInt64(1, SchemaVersion); executeStatement(statement); - createSchemaVersionTable.commit(); + setDatabaseVersion.commit(); } void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist) @@ -290,7 +394,13 @@ void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist) " DELETE FROM CacheWhitelistURLs WHERE cache = OLD.id;" " DELETE FROM FallbackURLs WHERE cache = OLD.id;" " END"); - + + // When a cache entry is deleted, its resource should also be deleted. + executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheEntryDeleted AFTER DELETE ON CacheEntries" + " FOR EACH ROW BEGIN" + " DELETE FROM CacheResources WHERE id = OLD.resource;" + " 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" @@ -322,7 +432,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheGroup* group) if (!executeStatement(statement)) return false; - group->setStorageID((unsigned)m_database.lastInsertRowID()); + group->setStorageID(static_cast<unsigned>(m_database.lastInsertRowID())); return true; } @@ -340,7 +450,7 @@ bool ApplicationCacheStorage::store(ApplicationCache* cache) if (!executeStatement(statement)) return false; - unsigned cacheStorageID = (unsigned)m_database.lastInsertRowID(); + unsigned cacheStorageID = static_cast<unsigned>(m_database.lastInsertRowID()); // Store all resources { @@ -352,14 +462,14 @@ bool ApplicationCacheStorage::store(ApplicationCache* cache) } // Store the online whitelist - const HashSet<String>& onlineWhitelist = cache->onlineWhitelist(); + const Vector<KURL>& onlineWhitelist = cache->onlineWhitelist(); { - HashSet<String>::const_iterator end = onlineWhitelist.end(); - for (HashSet<String>::const_iterator it = onlineWhitelist.begin(); it != end; ++it) { + size_t whitelistSize = onlineWhitelist.size(); + for (size_t i = 0; i < whitelistSize; ++i) { SQLiteStatement statement(m_database, "INSERT INTO CacheWhitelistURLs (url, cache) VALUES (?, ?)"); statement.prepare(); - statement.bindText(1, *it); + statement.bindText(1, onlineWhitelist[i]); statement.bindInt64(2, cacheStorageID); if (!executeStatement(statement)) @@ -367,6 +477,23 @@ bool ApplicationCacheStorage::store(ApplicationCache* cache) } } + // Store fallback URLs. + const FallbackURLVector& fallbackURLs = cache->fallbackURLs(); + { + size_t fallbackCount = fallbackURLs.size(); + for (size_t i = 0; i < fallbackCount; ++i) { + SQLiteStatement statement(m_database, "INSERT INTO FallbackURLs (namespace, fallbackURL, cache) VALUES (?, ?, ?)"); + statement.prepare(); + + statement.bindText(1, fallbackURLs[i].first); + statement.bindText(2, fallbackURLs[i].second); + statement.bindInt64(3, cacheStorageID); + + if (!executeStatement(statement)) + return false; + } + } + cache->setStorageID(cacheStorageID); return true; } @@ -389,7 +516,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned if (!dataStatement.executeCommand()) return false; - unsigned dataId = (unsigned)m_database.lastInsertRowID(); + unsigned dataId = static_cast<unsigned>(m_database.lastInsertRowID()); // Then, insert the resource @@ -421,7 +548,7 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned if (!executeStatement(resourceStatement)) return false; - unsigned resourceId = (unsigned)m_database.lastInsertRowID(); + unsigned resourceId = static_cast<unsigned>(m_database.lastInsertRowID()); // Finally, insert the cache entry SQLiteStatement entryStatement(m_database, "INSERT INTO CacheEntries (cache, type, resource) VALUES (?, ?, ?)"); @@ -439,6 +566,25 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned return true; } +bool ApplicationCacheStorage::storeUpdatedType(ApplicationCacheResource* resource, ApplicationCache* cache) +{ + ASSERT_UNUSED(cache, cache->storageID()); + ASSERT(resource->storageID()); + + // FIXME: If the resource gained a Dynamic bit, it should be re-inserted at the end for correct order. + ASSERT(!(resource->type() & ApplicationCacheResource::Dynamic)); + + // First, insert the data + SQLiteStatement entryStatement(m_database, "UPDATE CacheEntries SET type=? WHERE resource=?"); + if (entryStatement.prepare() != SQLResultOk) + return false; + + entryStatement.bindInt64(1, resource->type()); + entryStatement.bindInt64(2, resource->storageID()); + + return executeStatement(entryStatement); +} + void ApplicationCacheStorage::store(ApplicationCacheResource* resource, ApplicationCache* cache) { ASSERT(cache->storageID()); @@ -469,6 +615,7 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group) } ASSERT(group->newestCache()); + ASSERT(!group->isObsolete()); ASSERT(!group->newestCache()->storageID()); // Store the newest cache @@ -496,7 +643,7 @@ static inline void parseHeader(const UChar* header, size_t headerLength, Resourc int pos = find(header, headerLength, ':'); ASSERT(pos != -1); - String headerName = String(header, pos); + AtomicString headerName = AtomicString(header, pos); String headerValue = String(header + pos + 1, headerLength - pos - 1); response.setHTTPHeaderField(headerName, headerValue); @@ -536,7 +683,7 @@ PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storage while ((result = cacheStatement.step()) == SQLResultRow) { KURL url(cacheStatement.getColumnText(0)); - unsigned type = (unsigned)cacheStatement.getColumnInt64(1); + unsigned type = static_cast<unsigned>(cacheStatement.getColumnInt64(1)); Vector<char> blob; cacheStatement.getColumnBlobAsVector(5, blob); @@ -568,14 +715,29 @@ PassRefPtr<ApplicationCache> ApplicationCacheStorage::loadCache(unsigned storage return 0; whitelistStatement.bindInt64(1, storageID); - HashSet<String> whitelist; + Vector<KURL> whitelist; while ((result = whitelistStatement.step()) == SQLResultRow) - whitelist.add(whitelistStatement.getColumnText(0)); + whitelist.append(whitelistStatement.getColumnText(0)); if (result != SQLResultDone) LOG_ERROR("Could not load cache online whitelist, error \"%s\"", m_database.lastErrorMsg()); cache->setOnlineWhitelist(whitelist); + + // Load fallback URLs. + SQLiteStatement fallbackStatement(m_database, "SELECT namespace, fallbackURL FROM FallbackURLs WHERE cache=?"); + if (fallbackStatement.prepare() != SQLResultOk) + return 0; + fallbackStatement.bindInt64(1, storageID); + + FallbackURLVector fallbackURLs; + while ((result = fallbackStatement.step()) == SQLResultRow) + fallbackURLs.append(make_pair(fallbackStatement.getColumnText(0), fallbackStatement.getColumnText(1))); + + if (result != SQLResultDone) + LOG_ERROR("Could not load fallback URLs, error \"%s\"", m_database.lastErrorMsg()); + + cache->setFallbackURLs(fallbackURLs); cache->setStorageID(storageID); @@ -591,12 +753,30 @@ void ApplicationCacheStorage::remove(ApplicationCache* cache) if (!m_database.isOpen()) return; + ASSERT(cache->group()); + ASSERT(cache->group()->storageID()); + + // All associated data will be deleted by database triggers. SQLiteStatement statement(m_database, "DELETE FROM Caches WHERE id=?"); if (statement.prepare() != SQLResultOk) return; statement.bindInt64(1, cache->storageID()); executeStatement(statement); + + cache->clearStorageID(); + + if (cache->group()->newestCache() == cache) { + // Currently, there are no triggers on the cache group, which is why the cache had to be removed separately above. + SQLiteStatement groupStatement(m_database, "DELETE FROM CacheGroups WHERE id=?"); + if (groupStatement.prepare() != SQLResultOk) + return; + + groupStatement.bindInt64(1, cache->group()->storageID()); + executeStatement(groupStatement); + + cache->group()->clearStorageID(); + } } void ApplicationCacheStorage::empty() @@ -609,7 +789,6 @@ void ApplicationCacheStorage::empty() // 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 @@ -623,10 +802,10 @@ bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, App { // Create a new cache. RefPtr<ApplicationCache> cacheCopy = ApplicationCache::create(); - - // Set the online whitelist + cacheCopy->setOnlineWhitelist(cache->onlineWhitelist()); - + cacheCopy->setFallbackURLs(cache->fallbackURLs()); + // Traverse the cache and add copies of all resources. ApplicationCache::ResourceMap::const_iterator end = cache->end(); for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) { @@ -653,7 +832,7 @@ bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, App ApplicationCacheStorage& cacheStorage() { - static ApplicationCacheStorage storage; + DEFINE_STATIC_LOCAL(ApplicationCacheStorage, storage, ()); return storage; } diff --git a/WebCore/loader/appcache/ApplicationCacheStorage.h b/WebCore/loader/appcache/ApplicationCacheStorage.h index 6bd9ba1..a0eb4ee 100644 --- a/WebCore/loader/appcache/ApplicationCacheStorage.h +++ b/WebCore/loader/appcache/ApplicationCacheStorage.h @@ -44,20 +44,26 @@ class KURL; class ApplicationCacheStorage { public: void setCacheDirectory(const String&); + const String& cacheDirectory() const; - ApplicationCacheGroup* cacheGroupForURL(const KURL&); + ApplicationCacheGroup* cacheGroupForURL(const KURL&); // Cache to load a main resource from. + ApplicationCacheGroup* fallbackCacheGroupForURL(const KURL&); // Cache that has a fallback entry to load a main resource from if normal loading fails. ApplicationCacheGroup* findOrCreateCacheGroup(const KURL& manifestURL); void cacheGroupDestroyed(ApplicationCacheGroup*); + void cacheGroupMadeObsolete(ApplicationCacheGroup*); - bool storeNewestCache(ApplicationCacheGroup*); + bool storeNewestCache(ApplicationCacheGroup*); // Updates the cache group, but doesn't remove old cache. void store(ApplicationCacheResource*, ApplicationCache*); + bool storeUpdatedType(ApplicationCacheResource*, ApplicationCache*); + // Removes the group if the cache to be removed is the newest one (so, storeNewestCache() needs to be called beforehand when updating). void remove(ApplicationCache*); void empty(); static bool storeCopyOfCache(const String& cacheDirectory, ApplicationCache*); + private: PassRefPtr<ApplicationCache> loadCache(unsigned storageID); ApplicationCacheGroup* loadCacheGroup(const KURL& manifestURL); @@ -78,13 +84,13 @@ private: 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. + + // In order to quickly determine if a given resource exists in an application cache, + // we keep a hash set of the hosts of the manifest URLs of all non-obsolete cache groups. HashCountedSet<unsigned, AlreadyHashed> m_cacheHostSet; typedef HashMap<String, ApplicationCacheGroup*> CacheGroupMap; - CacheGroupMap m_cachesInMemory; + CacheGroupMap m_cachesInMemory; // Excludes obsolete cache groups. }; ApplicationCacheStorage& cacheStorage(); diff --git a/WebCore/loader/appcache/DOMApplicationCache.cpp b/WebCore/loader/appcache/DOMApplicationCache.cpp index f872a50..5bc4420 100644 --- a/WebCore/loader/appcache/DOMApplicationCache.cpp +++ b/WebCore/loader/appcache/DOMApplicationCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2009 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,6 +30,7 @@ #include "ApplicationCache.h" #include "ApplicationCacheGroup.h" +#include "ApplicationCacheResource.h" #include "DocumentLoader.h" #include "Event.h" #include "EventException.h" @@ -37,6 +38,7 @@ #include "EventNames.h" #include "Frame.h" #include "FrameLoader.h" +#include "StaticStringList.h" namespace WebCore { @@ -55,7 +57,7 @@ ApplicationCache* DOMApplicationCache::associatedCache() const if (!m_frame) return 0; - return m_frame->loader()->documentLoader()->topLevelApplicationCache(); + return m_frame->loader()->documentLoader()->applicationCache(); } unsigned short DOMApplicationCache::status() const @@ -63,22 +65,22 @@ unsigned short DOMApplicationCache::status() const ApplicationCache* cache = associatedCache(); if (!cache) return UNCACHED; - - switch (cache->group()->status()) { + + switch (cache->group()->updateStatus()) { case ApplicationCacheGroup::Checking: return CHECKING; case ApplicationCacheGroup::Downloading: return DOWNLOADING; case ApplicationCacheGroup::Idle: { + if (cache->group()->isObsolete()) + return OBSOLETE; if (cache != cache->group()->newestCache()) return UPDATEREADY; - return IDLE; } - default: - ASSERT_NOT_REACHED(); } - + + ASSERT_NOT_REACHED(); return 0; } @@ -90,7 +92,7 @@ void DOMApplicationCache::update(ExceptionCode& ec) return; } - cache->group()->update(m_frame); + cache->group()->update(m_frame, ApplicationCacheUpdateWithoutBrowsingContext); } bool DOMApplicationCache::swapCache() @@ -101,8 +103,14 @@ bool DOMApplicationCache::swapCache() ApplicationCache* cache = m_frame->loader()->documentLoader()->applicationCache(); if (!cache) return false; - - // Check if we already have the newest cache + + // If the group of application caches to which cache belongs has the lifecycle status obsolete, unassociate document from cache. + if (cache->group()->isObsolete()) { + cache->group()->disassociateDocumentLoader(m_frame->loader()->documentLoader()); + return true; + } + + // If there is no newer cache, raise an INVALID_STATE_ERR exception. ApplicationCache* newestCache = cache->group()->newestCache(); if (cache == newestCache) return false; @@ -119,29 +127,33 @@ void DOMApplicationCache::swapCache(ExceptionCode& ec) ec = INVALID_STATE_ERR; } -unsigned DOMApplicationCache::length() const +PassRefPtr<DOMStringList> DOMApplicationCache::items() { - ApplicationCache* cache = associatedCache(); - if (!cache) - return 0; - - return cache->numDynamicEntries(); + Vector<String> result; + if (ApplicationCache* cache = associatedCache()) { + unsigned numEntries = cache->numDynamicEntries(); + result.reserveCapacity(numEntries); + for (unsigned i = 0; i < numEntries; ++i) + result.append(cache->dynamicEntry(i)); + } + return StaticStringList::adopt(result); } - -String DOMApplicationCache::item(unsigned item, ExceptionCode& ec) + +bool DOMApplicationCache::hasItem(const KURL& url, ExceptionCode& ec) { ApplicationCache* cache = associatedCache(); if (!cache) { ec = INVALID_STATE_ERR; - return String(); + return false; } - if (item >= length()) { - ec = INDEX_SIZE_ERR; - return String(); + if (!url.isValid()) { + ec = SYNTAX_ERR; + return false; } - - return cache->dynamicEntry(item); + + ApplicationCacheResource* resource = cache->resourceForURL(url.string()); + return resource && (resource->type() & ApplicationCacheResource::Dynamic); } void DOMApplicationCache::add(const KURL& url, ExceptionCode& ec) @@ -198,7 +210,7 @@ void DOMApplicationCache::addEventListener(const AtomicString& eventType, PassRe } } -void DOMApplicationCache::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool useCapture) +void DOMApplicationCache::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool) { EventListenersMap::iterator iter = m_eventListeners.find(eventType); if (iter == m_eventListeners.end()) @@ -215,7 +227,7 @@ void DOMApplicationCache::removeEventListener(const AtomicString& eventType, Eve bool DOMApplicationCache::dispatchEvent(PassRefPtr<Event> event, ExceptionCode& ec) { - if (event->type().isEmpty()) { + if (!event || event->type().isEmpty()) { ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR; return true; } @@ -281,6 +293,11 @@ void DOMApplicationCache::callCachedListener() callListener(eventNames().cachedEvent, m_onCachedListener.get()); } +void DOMApplicationCache::callObsoleteListener() +{ + callListener(eventNames().obsoleteEvent, m_onObsoleteListener.get()); +} + } // namespace WebCore #endif // ENABLE(OFFLINE_WEB_APPLICATIONS) diff --git a/WebCore/loader/appcache/DOMApplicationCache.h b/WebCore/loader/appcache/DOMApplicationCache.h index 30c0b7e..b76542d 100644 --- a/WebCore/loader/appcache/DOMApplicationCache.h +++ b/WebCore/loader/appcache/DOMApplicationCache.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -40,6 +40,7 @@ namespace WebCore { class ApplicationCache; class AtomicStringImpl; +class DOMStringList; class Frame; class KURL; class String; @@ -55,6 +56,7 @@ public: CHECKING = 2, DOWNLOADING = 3, UPDATEREADY = 4, + OBSOLETE = 5 }; unsigned short status() const; @@ -62,8 +64,8 @@ public: void update(ExceptionCode&); void swapCache(ExceptionCode&); - unsigned length() const; - String item(unsigned item, ExceptionCode&); + PassRefPtr<DOMStringList> items(); + bool hasItem(const KURL&, ExceptionCode&); void add(const KURL&, ExceptionCode&); void remove(const KURL&, ExceptionCode&); @@ -99,6 +101,9 @@ public: void setOncached(PassRefPtr<EventListener> eventListener) { m_onCachedListener = eventListener; } EventListener* oncached() const { return m_onCachedListener.get(); } + void setOnobsolete(PassRefPtr<EventListener> eventListener) { m_onObsoleteListener = eventListener; } + EventListener* onobsolete() const { return m_onObsoleteListener.get(); } + virtual ScriptExecutionContext* scriptExecutionContext() const; DOMApplicationCache* toDOMApplicationCache() { return this; } @@ -109,6 +114,7 @@ public: void callProgressListener(); void callUpdateReadyListener(); void callCachedListener(); + void callObsoleteListener(); private: DOMApplicationCache(Frame*); @@ -127,6 +133,7 @@ private: RefPtr<EventListener> m_onProgressListener; RefPtr<EventListener> m_onUpdateReadyListener; RefPtr<EventListener> m_onCachedListener; + RefPtr<EventListener> m_onObsoleteListener; EventListenersMap m_eventListeners; diff --git a/WebCore/loader/appcache/DOMApplicationCache.idl b/WebCore/loader/appcache/DOMApplicationCache.idl index 94326f3..4cf8f3a 100644 --- a/WebCore/loader/appcache/DOMApplicationCache.idl +++ b/WebCore/loader/appcache/DOMApplicationCache.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2009 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,21 +35,24 @@ module offline { const unsigned short CHECKING = 2; const unsigned short DOWNLOADING = 3; const unsigned short UPDATEREADY = 4; + const unsigned short OBSOLETE = 5; readonly attribute unsigned short status; void update() raises(DOMException); void swapCache() raises(DOMException); - + +#if ENABLE_APPLICATION_CAHE_DYNAMIC_ENTRIES // dynamic entries - readonly attribute unsigned long length; - DOMString item(in [IsIndex] unsigned long index) + readonly attribute DOMStringList items; + [Custom] boolean hasItem(in DOMString url) raises(DOMException); [Custom] void add(in DOMString uri) raises(DOMException); [Custom] void remove(in DOMString uri) raises(DOMException); +#endif // events attribute EventListener onchecking; @@ -59,6 +62,7 @@ module offline { attribute EventListener onprogress; attribute EventListener onupdateready; attribute EventListener oncached; + attribute EventListener onobsolete; // EventTarget interface [Custom] void addEventListener(in DOMString type, diff --git a/WebCore/loader/appcache/ManifestParser.cpp b/WebCore/loader/appcache/ManifestParser.cpp index 778d22d..a83303a 100644 --- a/WebCore/loader/appcache/ManifestParser.cpp +++ b/WebCore/loader/appcache/ManifestParser.cpp @@ -32,9 +32,11 @@ #include "KURL.h" #include "TextEncoding.h" +using namespace std; + namespace WebCore { -enum Mode { Explicit, Fallback, OnlineWhitelist }; +enum Mode { Explicit, Fallback, OnlineWhitelist, Unknown }; bool parseManifest(const KURL& manifestURL, const char* data, int length, Manifest& manifest) { @@ -45,34 +47,22 @@ bool parseManifest(const KURL& manifestURL, const char* data, int length, Manife 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. + // Look for the magic signature: "^\xFEFF?CACHE MANIFEST[ \t]?" (the BOM is removed by decode()). + // Example: "CACHE MANIFEST #comment" is a valid signature. + // Example: "CACHE MANIFEST;V2" is not. + if (!s.startsWith("CACHE MANIFEST")) 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 + + if (p < end && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r') return false; - } - + + // Skip to the end of the line. + while (p < end && *p != '\r' && *p != '\n') + p++; + while (1) { // Skip whitespace while (p < end && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t')) @@ -104,8 +94,19 @@ bool parseManifest(const KURL& manifestURL, const char* data, int length, Manife mode = Fallback; else if (line == "NETWORK:") mode = OnlineWhitelist; + else if (line.endsWith(":")) + mode = Unknown; + else if (mode == Unknown) + continue; else if (mode == Explicit || mode == OnlineWhitelist) { - KURL url(manifestURL, line); + const UChar* p = line.characters(); + const UChar* lineEnd = p + line.length(); + + // Look for whitespace separating the URL from subsequent ignored tokens. + while (p < lineEnd && *p != '\t' && *p != ' ') + p++; + + KURL url(manifestURL, String(line.characters(), p - line.characters())); if (!url.isValid()) continue; @@ -119,11 +120,11 @@ bool parseManifest(const KURL& manifestURL, const char* data, int length, Manife if (mode == Explicit) manifest.explicitURLs.add(url.string()); else - manifest.onlineWhitelistedURLs.add(url.string()); + manifest.onlineWhitelistedURLs.append(url); } else if (mode == Fallback) { - const UChar *p = line.characters(); - const UChar *lineEnd = p + line.length(); + const UChar* p = line.characters(); + const UChar* lineEnd = p + line.length(); // Look for whitespace separating the two URLs while (p < lineEnd && *p != '\t' && *p != ' ') @@ -137,23 +138,31 @@ bool parseManifest(const KURL& manifestURL, const char* data, int length, Manife 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 (namespaceURL.hasRef()) + namespaceURL.setRef(String()); + if (!protocolHostAndPortAreEqual(manifestURL, namespaceURL)) continue; + // Skip whitespace separating fallback namespace from URL. while (p < lineEnd && (*p == '\t' || *p == ' ')) p++; - KURL fallbackURL(String(p, line.length() - (p - line.characters()))); + // Look for whitespace separating the URL from subsequent ignored tokens. + const UChar* fallbackStart = p; + while (p < lineEnd && *p != '\t' && *p != ' ') + p++; + KURL fallbackURL(manifestURL, String(fallbackStart, p - fallbackStart)); if (!fallbackURL.isValid()) continue; - - if (!equalIgnoringCase(fallbackURL.protocol(), manifestURL.protocol())) + if (fallbackURL.hasRef()) + fallbackURL.setRef(String()); + + if (!protocolHostAndPortAreEqual(manifestURL, fallbackURL)) continue; - - manifest.fallbackURLs.add(namespaceURL, fallbackURL); + + manifest.fallbackURLs.append(make_pair(namespaceURL, fallbackURL)); } else ASSERT_NOT_REACHED(); } diff --git a/WebCore/loader/appcache/ManifestParser.h b/WebCore/loader/appcache/ManifestParser.h index f4fe31a..c03cf27 100644 --- a/WebCore/loader/appcache/ManifestParser.h +++ b/WebCore/loader/appcache/ManifestParser.h @@ -28,22 +28,19 @@ #if ENABLE(OFFLINE_WEB_APPLICATIONS) -#include <wtf/HashMap.h> -#include <wtf/HashSet.h> -#include "StringHash.h" -#include "PlatformString.h" +#include "ApplicationCache.h" namespace WebCore { -class KURL; + class KURL; -struct Manifest { - HashSet<String> onlineWhitelistedURLs; - HashSet<String> explicitURLs; - HashMap<String, String> fallbackURLs; -}; + struct Manifest { + Vector<KURL> onlineWhitelistedURLs; + HashSet<String> explicitURLs; + FallbackURLVector fallbackURLs; + }; -bool parseManifest(const KURL& manifestURL, const char* data, int length, Manifest&); + bool parseManifest(const KURL& manifestURL, const char* data, int length, Manifest&); } diff --git a/WebCore/loader/archive/ArchiveFactory.cpp b/WebCore/loader/archive/ArchiveFactory.cpp index c42b5df..1322dbb 100644 --- a/WebCore/loader/archive/ArchiveFactory.cpp +++ b/WebCore/loader/archive/ArchiveFactory.cpp @@ -37,10 +37,12 @@ #include <wtf/HashMap.h> #include <wtf/HashSet.h> +#include <wtf/StdLibExtras.h> namespace WebCore { typedef PassRefPtr<Archive> RawDataCreationFunction(SharedBuffer*); +typedef HashMap<String, RawDataCreationFunction*, CaseFoldingHash> ArchiveMIMETypesMap; // The create functions in the archive classes return PassRefPtr to concrete subclasses // of Archive. This adaptor makes the functions have a uniform return type. @@ -49,9 +51,9 @@ template <typename ArchiveClass> static PassRefPtr<Archive> archiveFactoryCreate return ArchiveClass::create(buffer); } -static HashMap<String, RawDataCreationFunction*, CaseFoldingHash>& archiveMIMETypes() +static ArchiveMIMETypesMap& archiveMIMETypes() { - static HashMap<String, RawDataCreationFunction*, CaseFoldingHash> mimeTypes; + DEFINE_STATIC_LOCAL(ArchiveMIMETypesMap, mimeTypes, ()); static bool initialized = false; if (initialized) @@ -79,8 +81,8 @@ PassRefPtr<Archive> ArchiveFactory::create(SharedBuffer* data, const String& mim void ArchiveFactory::registerKnownArchiveMIMETypes() { HashSet<String>& mimeTypes = MIMETypeRegistry::getSupportedNonImageMIMETypes(); - HashMap<String, RawDataCreationFunction*, CaseFoldingHash>::iterator i = archiveMIMETypes().begin(); - HashMap<String, RawDataCreationFunction*, CaseFoldingHash>::iterator end = archiveMIMETypes().end(); + ArchiveMIMETypesMap::iterator i = archiveMIMETypes().begin(); + ArchiveMIMETypesMap::iterator end = archiveMIMETypes().end(); for (; i != end; ++i) mimeTypes.add(i->first); diff --git a/WebCore/loader/archive/cf/LegacyWebArchive.cpp b/WebCore/loader/archive/cf/LegacyWebArchive.cpp index 9d99588..b00d93c 100644 --- a/WebCore/loader/archive/cf/LegacyWebArchive.cpp +++ b/WebCore/loader/archive/cf/LegacyWebArchive.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 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,6 +30,7 @@ #include "LegacyWebArchive.h" #include "CString.h" +#include "Cache.h" #include "Document.h" #include "DocumentLoader.h" #include "Frame.h" @@ -37,14 +38,15 @@ #include "FrameTree.h" #include "HTMLFrameOwnerElement.h" #include "HTMLNames.h" -#include "KURL.h" +#include "IconDatabase.h" +#include "KURLHash.h" #include "Logging.h" #include "markup.h" #include "Node.h" #include "Range.h" #include "SelectionController.h" #include "SharedBuffer.h" - +#include <wtf/ListHashSet.h> #include <wtf/RetainPtr.h> namespace WebCore { @@ -374,15 +376,21 @@ RetainPtr<CFDataRef> LegacyWebArchive::rawDataRepresentation() 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())); + + RetainPtr<CFWriteStreamRef> stream(AdoptCF, CFWriteStreamCreateWithAllocatedBuffers(0, 0)); + + CFWriteStreamOpen(stream.get()); + CFPropertyListWriteToStream(propertyList.get(), stream.get(), kCFPropertyListBinaryFormat_v1_0, 0); + + RetainPtr<CFDataRef> plistData(AdoptCF, static_cast<CFDataRef>(CFWriteStreamCopyProperty(stream.get(), kCFStreamPropertyDataWritten))); + + CFWriteStreamClose(stream.get()); + if (!plistData) { LOG(Archives, "LegacyWebArchive - Failed to convert property list into raw data, returning no data"); return 0; } - + return plistData; } @@ -488,7 +496,7 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString Vector<PassRefPtr<LegacyWebArchive> > subframeArchives; Vector<PassRefPtr<ArchiveResource> > subresources; - HashSet<String> uniqueSubresources; + HashSet<KURL> uniqueSubresources; Vector<Node*>::iterator it = nodes.begin(); Vector<Node*>::iterator end = nodes.end(); @@ -508,24 +516,50 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString else LOG_ERROR("Unabled to archive subframe %s", childFrame->tree()->name().string().utf8().data()); } else { - Vector<KURL> subresourceURLs; + ListHashSet<KURL> subresourceURLs; (*it)->getSubresourceURLs(subresourceURLs); DocumentLoader* documentLoader = frame->loader()->documentLoader(); - for (unsigned i = 0; i < subresourceURLs.size(); ++i) { - if (uniqueSubresources.contains(subresourceURLs[i].string())) + ListHashSet<KURL>::iterator iterEnd = subresourceURLs.end(); + for (ListHashSet<KURL>::iterator iter = subresourceURLs.begin(); iter != iterEnd; ++iter) { + const KURL& subresourceURL = *iter; + if (uniqueSubresources.contains(subresourceURL)) continue; - uniqueSubresources.add(subresourceURLs[i].string()); - RefPtr<ArchiveResource> resource = documentLoader->subresource(subresourceURLs[i]); - if (resource) + + uniqueSubresources.add(subresourceURL); + + RefPtr<ArchiveResource> resource = documentLoader->subresource(subresourceURL); + 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()); + continue; + } + + CachedResource *cachedResource = cache()->resourceForURL(subresourceURL); + if (cachedResource) { + resource = ArchiveResource::create(cachedResource->data(), subresourceURL, cachedResource->response()); + if (resource) { + subresources.append(resource.release()); + continue; + } + } + + // FIXME: should do something better than spew to console here + LOG_ERROR("Failed to archive subresource for %s", subresourceURL.string().utf8().data()); } } } - + + // Add favicon if one exists for this page + if (iconDatabase() && iconDatabase()->isEnabled()) { + const String& iconURL = iconDatabase()->iconURLForPageURL(responseURL); + if (!iconURL.isEmpty() && iconDatabase()->iconDataKnownForIconURL(iconURL)) { + if (Image* iconImage = iconDatabase()->iconForPageURL(responseURL, IntSize(16, 16))) { + RefPtr<ArchiveResource> resource = ArchiveResource::create(iconImage->data(), KURL(iconURL), "image/x-icon", "", ""); + subresources.append(resource.release()); + } + } + } + return create(mainResource, subresources, subframeArchives); } diff --git a/WebCore/loader/icon/IconDatabase.cpp b/WebCore/loader/icon/IconDatabase.cpp index 72e57fe..5705f7a 100644 --- a/WebCore/loader/icon/IconDatabase.cpp +++ b/WebCore/loader/icon/IconDatabase.cpp @@ -40,9 +40,10 @@ #include "PageURLRecord.h" #include "SQLiteStatement.h" #include "SQLiteTransaction.h" -#include "SystemTime.h" #include <runtime/InitializeThreading.h> +#include <wtf/CurrentTime.h> #include <wtf/MainThread.h> +#include <wtf/StdLibExtras.h> #if PLATFORM(WIN_OS) #include <windows.h> @@ -56,8 +57,6 @@ #include <pthread.h> #endif -#include <errno.h> - // For methods that are meant to support API from the main thread - should not be called internally #define ASSERT_NOT_SYNC_THREAD() ASSERT(!m_syncThreadRunning || !IS_ICON_SYNC_THREAD()) @@ -180,6 +179,9 @@ void IconDatabase::close() m_syncThreadRunning = false; m_threadTerminationRequested = false; m_removeIconsRequested = false; + + m_syncDB.close(); + ASSERT(!isOpen()); } void IconDatabase::removeAllIcons() @@ -385,7 +387,7 @@ static inline void loadDefaultIconRecord(IconRecord* defaultIconRecord) 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(SharedBuffer::create(defaultIconData, sizeof(defaultIconData))); + DEFINE_STATIC_LOCAL(RefPtr<SharedBuffer>, defaultIconBuffer, (SharedBuffer::create(defaultIconData, sizeof(defaultIconData)))); defaultIconRecord->setImageData(defaultIconBuffer); } #endif @@ -855,7 +857,7 @@ String IconDatabase::databasePath() const String IconDatabase::defaultDatabaseFilename() { - static String defaultDatabaseFilename = "WebpageIcons.db"; + DEFINE_STATIC_LOCAL(String, defaultDatabaseFilename, ("WebpageIcons.db")); return defaultDatabaseFilename.copy(); } @@ -1359,7 +1361,7 @@ void* IconDatabase::syncThreadMainLoop() bool didWrite = writeToDatabase(); if (shouldStopThreadActivity()) break; - + didAnyWork = readFromDatabase(); if (shouldStopThreadActivity()) break; diff --git a/WebCore/loader/icon/IconDatabaseClient.h b/WebCore/loader/icon/IconDatabaseClient.h index e642895..8806406 100644 --- a/WebCore/loader/icon/IconDatabaseClient.h +++ b/WebCore/loader/icon/IconDatabaseClient.h @@ -41,8 +41,9 @@ public: virtual ~IconDatabaseClient() { } virtual bool performImport() { return true; } virtual void dispatchDidRemoveAllIcons() { } - virtual void dispatchDidAddIconForPageURL(const String& pageURL) { } + virtual void dispatchDidAddIconForPageURL(const String& /*pageURL*/) { } }; } // namespace WebCore + #endif diff --git a/WebCore/loader/icon/IconFetcher.cpp b/WebCore/loader/icon/IconFetcher.cpp index efa7e14..69eeb7c 100644 --- a/WebCore/loader/icon/IconFetcher.cpp +++ b/WebCore/loader/icon/IconFetcher.cpp @@ -26,16 +26,12 @@ #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 { @@ -164,7 +160,7 @@ void IconFetcher::cancel() if (m_handle) m_handle->cancel(); } - + PassRefPtr<SharedBuffer> IconFetcher::createIcon() { ASSERT(!m_entries.isEmpty()); @@ -172,8 +168,7 @@ PassRefPtr<SharedBuffer> IconFetcher::createIcon() // For now, just return the data of the first entry. return m_entries.first().buffer(); } - - + void IconFetcher::loadEntry() { ASSERT(m_currentEntry < m_entries.size()); @@ -191,7 +186,7 @@ void IconFetcher::loadFailed() void IconFetcher::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response) { - ASSERT(m_handle == handle); + ASSERT_UNUSED(handle, m_handle == handle); int statusCode = response.httpStatusCode() / 100; if (statusCode == 4 || statusCode == 5) { @@ -200,16 +195,16 @@ void IconFetcher::didReceiveResponse(ResourceHandle* handle, const ResourceRespo } } -void IconFetcher::didReceiveData(ResourceHandle* handle, const char* data, int length, int lengthReceived) +void IconFetcher::didReceiveData(ResourceHandle* handle, const char* data, int length, int) { - ASSERT(m_handle == handle); + ASSERT_UNUSED(handle, m_handle == handle); m_entries[m_currentEntry].buffer()->append(data, length); } void IconFetcher::didFinishLoading(ResourceHandle* handle) { - ASSERT(m_handle == handle); + ASSERT_UNUSED(handle, m_handle == handle); if (m_currentEntry == m_entries.size() - 1) { // We finished loading, create the icon @@ -227,10 +222,9 @@ void IconFetcher::didFinishLoading(ResourceHandle* handle) void IconFetcher::didFail(ResourceHandle* handle, const ResourceError&) { - ASSERT(m_handle == handle); + ASSERT_UNUSED(handle, m_handle == handle); loadFailed(); } - } // namespace WebCore diff --git a/WebCore/loader/icon/IconLoader.cpp b/WebCore/loader/icon/IconLoader.cpp index 4337f51..b7bf115 100644 --- a/WebCore/loader/icon/IconLoader.cpp +++ b/WebCore/loader/icon/IconLoader.cpp @@ -36,6 +36,7 @@ #include "ResourceResponse.h" #include "ResourceRequest.h" #include "SubresourceLoader.h" +#include <wtf/UnusedParam.h> using namespace std; @@ -101,9 +102,13 @@ void IconLoader::didReceiveResponse(SubresourceLoader* resourceLoader, const Res } } -void IconLoader::didReceiveData(SubresourceLoader* loader, const char*, int size) +void IconLoader::didReceiveData(SubresourceLoader* unusedLoader, const char*, int unusedSize) { - LOG(IconDatabase, "IconLoader::didReceiveData() - Loader %p, number of bytes %i", loader, size); +#if LOG_DISABLED + UNUSED_PARAM(unusedLoader); + UNUSED_PARAM(unusedSize); +#endif + LOG(IconDatabase, "IconLoader::didReceiveData() - Loader %p, number of bytes %i", unusedLoader, unusedSize); } void IconLoader::didFail(SubresourceLoader* resourceLoader, const ResourceError&) @@ -121,7 +126,7 @@ void IconLoader::didFail(SubresourceLoader* resourceLoader, const ResourceError& } } -void IconLoader::didReceiveAuthenticationChallenge(SubresourceLoader* resourceLoader, const AuthenticationChallenge& challenge) +void IconLoader::didReceiveAuthenticationChallenge(SubresourceLoader*, const AuthenticationChallenge&) { // We don't ever want to prompt for authentication just for a site icon, so // implement this method to cancel the resource load @@ -153,9 +158,12 @@ void IconLoader::finishLoading(const KURL& iconURL, PassRefPtr<SharedBuffer> dat // <rdar://problem/5463392> tracks that enhancement if (!iconURL.isEmpty() && m_loadIsInProgress) { - iconDatabase()->setIconDataForIconURL(data, iconURL.string()); LOG(IconDatabase, "IconLoader::finishLoading() - Committing iconURL %s to database", iconURL.string().ascii().data()); m_frame->loader()->commitIconURLToIconDatabase(iconURL); + // Setting the icon data only after committing to the database ensures that the data is + // kept in memory (so it does not have to be read from the database asynchronously), since + // there is a page URL referencing it. + iconDatabase()->setIconDataForIconURL(data, iconURL.string()); m_frame->loader()->client()->dispatchDidReceiveIcon(); } diff --git a/WebCore/loader/icon/IconRecord.cpp b/WebCore/loader/icon/IconRecord.cpp index f070cc9..ffea318 100644 --- a/WebCore/loader/icon/IconRecord.cpp +++ b/WebCore/loader/icon/IconRecord.cpp @@ -34,7 +34,6 @@ #include "Logging.h" #include "SQLiteStatement.h" #include "SQLiteTransaction.h" -#include "SystemTime.h" #include <limits.h> @@ -53,7 +52,7 @@ IconRecord::~IconRecord() LOG(IconDatabase, "Destroying IconRecord for icon url %s", m_iconURL.ascii().data()); } -Image* IconRecord::image(const IntSize& size) +Image* IconRecord::image(const IntSize&) { // FIXME rdar://4680377 - For size right now, we are returning our one and only image and the Bridge // is resizing it in place. We need to actually store all the original representations here and return a native diff --git a/WebCore/loader/loader.cpp b/WebCore/loader/loader.cpp index 6221a6a..22fb158 100644 --- a/WebCore/loader/loader.cpp +++ b/WebCore/loader/loader.cpp @@ -181,7 +181,7 @@ void Loader::cancelRequests(DocLoader* docLoader) Loader::Host::Host(const AtomicString& name, unsigned maxRequestsInFlight) : m_name(name) , m_maxRequestsInFlight(maxRequestsInFlight) - , m_processingResource(false) + , m_numResourcesProcessing(0) { } @@ -252,7 +252,8 @@ void Loader::Host::servePendingRequests(RequestQueue& requestsPending, bool& ser 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) + ASSERT(docLoader->cachePolicy() != CachePolicyReload); + if (docLoader->cachePolicy() == CachePolicyRevalidate) resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0"); if (!lastModified.isEmpty()) resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified); @@ -285,7 +286,7 @@ void Loader::Host::didFinishLoading(SubresourceLoader* loader) if (i == m_requestsLoading.end()) return; - m_processingResource = true; + ProcessingResource processingResource(this); Request* request = i->second; m_requestsLoading.remove(i); @@ -315,8 +316,6 @@ void Loader::Host::didFinishLoading(SubresourceLoader* loader) 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::Host::didFail(SubresourceLoader* loader, const ResourceError&) @@ -332,7 +331,7 @@ void Loader::Host::didFail(SubresourceLoader* loader, bool cancelled) if (i == m_requestsLoading.end()) return; - m_processingResource = true; + ProcessingResource processingResource(this); Request* request = i->second; m_requestsLoading.remove(i); @@ -359,8 +358,6 @@ void Loader::Host::didFail(SubresourceLoader* loader, bool cancelled) docLoader->checkForPendingPreloads(); servePendingRequests(); - - m_processingResource = false; } void Loader::Host::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response) @@ -382,6 +379,7 @@ void Loader::Host::didReceiveResponse(SubresourceLoader* loader, const ResourceR if (response.httpStatusCode() == 304) { // 304 Not modified / Use local copy m_requestsLoading.remove(loader); + loader->clearClient(); request->docLoader()->decrementRequestCount(); // Existing resource is ok, just use it updating the expiration time. @@ -398,16 +396,16 @@ void Loader::Host::didReceiveResponse(SubresourceLoader* loader, const ResourceR // Did not get 304 response, continue as a regular resource load. cache()->revalidationFailed(resource); } - + resource->setResponse(response); - + String encoding = response.textEncodingName(); if (!encoding.isNull()) - request->cachedResource()->setEncoding(encoding); + resource->setEncoding(encoding); if (request->isMultipart()) { - ASSERT(request->cachedResource()->isImage()); - static_cast<CachedImage*>(request->cachedResource())->clear(); + ASSERT(resource->isImage()); + static_cast<CachedImage*>(resource)->clear(); if (request->docLoader()->frame()) request->docLoader()->frame()->loader()->checkCompleted(); } else if (response.isMultipart()) { @@ -415,10 +413,10 @@ void Loader::Host::didReceiveResponse(SubresourceLoader* loader, const ResourceR // We don't count multiParts in a DocLoader's request count request->docLoader()->decrementRequestCount(); - + // If we get a multipart response, we must have a handle ASSERT(loader->handle()); - if (!request->cachedResource()->isImage()) + if (!resource->isImage()) loader->handle()->cancel(); } } @@ -435,12 +433,11 @@ void Loader::Host::didReceiveData(SubresourceLoader* loader, const char* data, i if (resource->errorOccurred()) return; - m_processingResource = true; + ProcessingResource processingResource(this); if (resource->response().httpStatusCode() / 100 == 4) { // Treat a 4xx response like a network error. resource->error(); - m_processingResource = false; return; } @@ -452,8 +449,6 @@ void Loader::Host::didReceiveData(SubresourceLoader* loader, const char* data, i resource->data(copiedData.release(), true); } else if (request->isIncremental()) resource->data(loader->resourceData(), false); - - m_processingResource = false; } void Loader::Host::cancelPendingRequests(RequestQueue& requestsPending, DocLoader* docLoader) diff --git a/WebCore/loader/loader.h b/WebCore/loader/loader.h index c51374c..19c3fda 100644 --- a/WebCore/loader/loader.h +++ b/WebCore/loader/loader.h @@ -65,9 +65,27 @@ namespace WebCore { void servePendingRequests(Priority minimumPriority = Low); void cancelRequests(DocLoader*); bool hasRequests() const; - bool processingResource() const { return m_processingResource; } - + + bool processingResource() const { return m_numResourcesProcessing != 0; } + private: + class ProcessingResource { + public: + ProcessingResource(Host* host) + : m_host(host) + { + m_host->m_numResourcesProcessing++; + } + + ~ProcessingResource() + { + m_host->m_numResourcesProcessing--; + } + + private: + Host* m_host; + }; + virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&); virtual void didReceiveData(SubresourceLoader*, const char*, int); virtual void didFinishLoading(SubresourceLoader*); @@ -83,7 +101,7 @@ namespace WebCore { RequestMap m_requestsLoading; const AtomicString m_name; const int m_maxRequestsInFlight; - bool m_processingResource; + int m_numResourcesProcessing; }; typedef HashMap<AtomicStringImpl*, Host*> HostMap; HostMap m_hosts; diff --git a/WebCore/loader/mac/DocumentLoaderMac.cpp b/WebCore/loader/mac/DocumentLoaderMac.cpp index 05c6e26..8cc40d2 100644 --- a/WebCore/loader/mac/DocumentLoaderMac.cpp +++ b/WebCore/loader/mac/DocumentLoaderMac.cpp @@ -31,6 +31,7 @@ #include "MainResourceLoader.h" #include "ResourceHandle.h" #include "ResourceLoader.h" +#include <wtf/UnusedParam.h> namespace WebCore { @@ -62,6 +63,8 @@ void DocumentLoader::schedule(SchedulePair* pair) scheduleAll(m_subresourceLoaders, pair); scheduleAll(m_plugInStreamLoaders, pair); scheduleAll(m_multipartSubresourceLoaders, pair); +#else + UNUSED_PARAM(pair); #endif } @@ -73,6 +76,8 @@ void DocumentLoader::unschedule(SchedulePair* pair) unscheduleAll(m_subresourceLoaders, pair); unscheduleAll(m_plugInStreamLoaders, pair); unscheduleAll(m_multipartSubresourceLoaders, pair); +#else + UNUSED_PARAM(pair); #endif } diff --git a/WebCore/loader/mac/ResourceLoaderMac.mm b/WebCore/loader/mac/ResourceLoaderMac.mm index 9769ac9..d6ee923 100644 --- a/WebCore/loader/mac/ResourceLoaderMac.mm +++ b/WebCore/loader/mac/ResourceLoaderMac.mm @@ -35,7 +35,7 @@ namespace WebCore { -NSCachedURLResponse* ResourceLoader::willCacheResponse(ResourceHandle* handle, NSCachedURLResponse* response) +NSCachedURLResponse* ResourceLoader::willCacheResponse(ResourceHandle*, NSCachedURLResponse* response) { return frameLoader()->client()->willCacheResponse(documentLoader(), identifier(), response); } |