diff options
Diffstat (limited to 'src/3rdparty/webkit/WebCore/loader')
78 files changed, 4276 insertions, 2073 deletions
diff --git a/src/3rdparty/webkit/WebCore/loader/Cache.cpp b/src/3rdparty/webkit/WebCore/loader/Cache.cpp index 1db6ce6fdc..250ea0de93 100644 --- a/src/3rdparty/webkit/WebCore/loader/Cache.cpp +++ b/src/3rdparty/webkit/WebCore/loader/Cache.cpp @@ -34,8 +34,8 @@ #include "FrameView.h" #include "Image.h" #include "ResourceHandle.h" -#include "SystemTime.h" #include <stdio.h> +#include <wtf/CurrentTime.h> using namespace std; @@ -141,13 +141,6 @@ CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Typ if (resource->type() != type) return 0; -#if USE(LOW_BANDWIDTH_DISPLAY) - // addLowBandwidthDisplayRequest() returns true if requesting CSS or JS during low bandwidth display. - // Here, return 0 to not block parsing or layout. - if (docLoader->frame() && docLoader->frame()->loader()->addLowBandwidthDisplayRequest(resource)) - return 0; -#endif - if (!disabled()) { // This will move the resource to the front of its LRU list and increase its access count. resourceAccessed(resource); @@ -188,6 +181,7 @@ CachedCSSStyleSheet* Cache::requestUserCSSStyleSheet(DocLoader* docLoader, const void Cache::revalidateResource(CachedResource* resource, DocLoader* docLoader) { ASSERT(resource); + ASSERT(resource->inCache()); ASSERT(!disabled()); if (resource->resourceToRevalidate()) return; @@ -217,7 +211,7 @@ void Cache::revalidationSucceeded(CachedResource* revalidatingResource, const Re ASSERT(!m_resources.get(resource->url())); m_resources.set(resource->url(), resource); resource->setInCache(true); - resource->setExpirationDate(response.expirationDate()); + resource->updateResponseAfterRevalidation(response); insertInLRUList(resource); int delta = resource->size(); if (resource->decodedSize() && resource->hasClients()) @@ -274,7 +268,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. @@ -397,15 +391,6 @@ 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); diff --git a/src/3rdparty/webkit/WebCore/loader/CachedCSSStyleSheet.cpp b/src/3rdparty/webkit/WebCore/loader/CachedCSSStyleSheet.cpp index 10d566ea45..11d12137d9 100644 --- a/src/3rdparty/webkit/WebCore/loader/CachedCSSStyleSheet.cpp +++ b/src/3rdparty/webkit/WebCore/loader/CachedCSSStyleSheet.cpp @@ -29,6 +29,7 @@ #include "CachedResourceClient.h" #include "CachedResourceClientWalker.h" +#include "HTTPParsers.h" #include "TextResourceDecoder.h" #include "loader.h" #include <wtf/Vector.h> @@ -114,15 +115,6 @@ void CachedCSSStyleSheet::checkNotify() CachedResourceClientWalker w(m_clients); while (CachedResourceClient *c = w.next()) c->setCSSStyleSheet(m_response.url().string(), m_decoder->encoding().name(), this); - -#if USE(LOW_BANDWIDTH_DISPLAY) - // if checkNotify() is called from error(), client's setCSSStyleSheet(...) - // can't find "this" from url, so they can't do clean up if needed. - // call notifyFinished() to make sure they have a chance. - CachedResourceClientWalker n(m_clients); - while (CachedResourceClient* s = n.next()) - s->notifyFinished(this); -#endif } void CachedCSSStyleSheet::error() @@ -140,8 +132,14 @@ bool CachedCSSStyleSheet::canUseSheet(bool enforceMIMEType) const if (!enforceMIMEType) return true; - // This check exactly matches Firefox. - String mimeType = response().mimeType(); + // This check exactly matches Firefox. Note that we grab the Content-Type + // header directly because we want to see what the value is BEFORE content + // sniffing. Firefox does this by setting a "type hint" on the channel. + // This implementation should be observationally equivalent. + // + // This code defaults to allowing the stylesheet for non-HTTP protocols so + // folks can use standards mode for local HTML documents. + String mimeType = extractMIMETypeFromMediaType(response().httpHeaderField("Content-Type")); return mimeType.isEmpty() || equalIgnoringCase(mimeType, "text/css") || equalIgnoringCase(mimeType, "application/x-unknown-content-type"); } diff --git a/src/3rdparty/webkit/WebCore/loader/CachedFont.cpp b/src/3rdparty/webkit/WebCore/loader/CachedFont.cpp index 5fd9a22bb5..ef2f5a90ee 100644 --- a/src/3rdparty/webkit/WebCore/loader/CachedFont.cpp +++ b/src/3rdparty/webkit/WebCore/loader/CachedFont.cpp @@ -31,7 +31,7 @@ #include "CachedResourceClientWalker.h" #include "DOMImplementation.h" #include "FontPlatformData.h" -#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) +#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && PLATFORM(WIN_OS)) #include "FontCustomPlatformData.h" #endif #include "TextResourceDecoder.h" @@ -60,7 +60,7 @@ CachedFont::CachedFont(const String &url) CachedFont::~CachedFont() { -#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) +#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && PLATFORM(WIN_OS)) delete m_fontData; #endif } @@ -100,7 +100,7 @@ void CachedFont::beginLoadIfNeeded(DocLoader* dl) bool CachedFont::ensureCustomFontData() { -#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) +#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && PLATFORM(WIN_OS)) #if ENABLE(SVG_FONTS) ASSERT(!m_isSVGFont); #endif @@ -119,7 +119,7 @@ FontPlatformData CachedFont::platformDataFromCustomData(float size, bool bold, b if (m_externalSVGDocument) return FontPlatformData(size, bold, italic); #endif -#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) +#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && PLATFORM(WIN_OS)) ASSERT(m_fontData); return m_fontData->fontPlatformData(static_cast<int>(size), bold, italic, renderingMode); #else @@ -137,6 +137,7 @@ bool CachedFont::ensureSVGFontData() RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("application/xml"); m_externalSVGDocument->write(decoder->decode(m_data->data(), m_data->size())); + m_externalSVGDocument->write(decoder->flush()); if (decoder->sawError()) { m_externalSVGDocument.clear(); return 0; @@ -174,7 +175,7 @@ SVGFontElement* CachedFont::getSVGFontById(const String& fontName) const void CachedFont::allClientsRemoved() { -#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) +#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && PLATFORM(WIN_OS)) if (m_fontData) { delete m_fontData; m_fontData = 0; diff --git a/src/3rdparty/webkit/WebCore/loader/CachedFont.h b/src/3rdparty/webkit/WebCore/loader/CachedFont.h index fd19cdb6ee..e4414c638c 100644 --- a/src/3rdparty/webkit/WebCore/loader/CachedFont.h +++ b/src/3rdparty/webkit/WebCore/loader/CachedFont.h @@ -39,10 +39,11 @@ namespace WebCore { class DocLoader; class Cache; -class FontCustomPlatformData; class FontPlatformData; class SVGFontElement; +struct FontCustomPlatformData; + class CachedFont : public CachedResource { public: CachedFont(const String& url); diff --git a/src/3rdparty/webkit/WebCore/loader/CachedImage.cpp b/src/3rdparty/webkit/WebCore/loader/CachedImage.cpp index 98aee9e3f6..74f9b013c5 100644 --- a/src/3rdparty/webkit/WebCore/loader/CachedImage.cpp +++ b/src/3rdparty/webkit/WebCore/loader/CachedImage.cpp @@ -33,7 +33,7 @@ #include "FrameView.h" #include "Request.h" #include "Settings.h" -#include "SystemTime.h" +#include <wtf/CurrentTime.h> #include <wtf/StdLibExtras.h> #include <wtf/Vector.h> @@ -53,6 +53,7 @@ CachedImage::CachedImage(const String& url) : CachedResource(url, ImageResource) , m_image(0) , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired) + , m_httpStatusCodeErrorOccurred(false) { m_status = Unknown; } @@ -61,6 +62,7 @@ CachedImage::CachedImage(Image* image) : CachedResource(String(), ImageResource) , m_image(image) , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired) + , m_httpStatusCodeErrorOccurred(false) { m_status = Cached; m_loading = false; @@ -96,7 +98,7 @@ void CachedImage::addClient(CachedResourceClient* c) 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) @@ -305,6 +307,7 @@ void CachedImage::error() { clear(); m_errorOccurred = true; + m_data.clear(); notifyObservers(); m_loading = false; checkNotify(); diff --git a/src/3rdparty/webkit/WebCore/loader/CachedImage.h b/src/3rdparty/webkit/WebCore/loader/CachedImage.h index 9c3442fb18..22a3774332 100644 --- a/src/3rdparty/webkit/WebCore/loader/CachedImage.h +++ b/src/3rdparty/webkit/WebCore/loader/CachedImage.h @@ -67,7 +67,10 @@ public: virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived); virtual void error(); - + + virtual void httpStatusCodeError() { m_httpStatusCodeErrorOccurred = true; } + bool httpStatusCodeErrorOccurred() const { return m_httpStatusCodeErrorOccurred; } + virtual bool schedule() const { return true; } void checkNotify(); @@ -96,6 +99,7 @@ private: RefPtr<Image> m_image; Timer<CachedImage> m_decodedDataDeletionTimer; + bool m_httpStatusCodeErrorOccurred; }; } diff --git a/src/3rdparty/webkit/WebCore/loader/CachedResource.cpp b/src/3rdparty/webkit/WebCore/loader/CachedResource.cpp index c1e71911b4..dceeec0a30 100644 --- a/src/3rdparty/webkit/WebCore/loader/CachedResource.cpp +++ b/src/3rdparty/webkit/WebCore/loader/CachedResource.cpp @@ -32,7 +32,8 @@ #include "KURL.h" #include "PurgeableBuffer.h" #include "Request.h" -#include "SystemTime.h" +#include <wtf/CurrentTime.h> +#include <wtf/MathExtras.h> #include <wtf/RefCountedLeakCounter.h> #include <wtf/Vector.h> @@ -46,6 +47,7 @@ static RefCountedLeakCounter cachedResourceLeakCounter("CachedResource"); CachedResource::CachedResource(const String& url, Type type) : m_url(url) + , m_responseTimestamp(currentTime()) , m_lastDecodedAccessTime(0) , m_sendResourceLoadCallbacks(true) , m_preloadCount(0) @@ -57,7 +59,6 @@ CachedResource::CachedResource(const String& url, Type type) , m_handleCount(0) , m_resourceToRevalidate(0) , m_isBeingRevalidated(false) - , m_expirationDate(0) { #ifndef NDEBUG cachedResourceLeakCounter.increment(); @@ -116,16 +117,50 @@ void CachedResource::finish() bool CachedResource::isExpired() const { - if (!m_expirationDate) + if (m_response.isNull()) return false; - time_t now = time(0); - return difftime(now, m_expirationDate) >= 0; + + return currentAge() > freshnessLifetime(); +} + +double CachedResource::currentAge() const +{ + // RFC2616 13.2.3 + // No compensation for latency as that is not terribly important in practice + double dateValue = m_response.date(); + double apparentAge = isfinite(dateValue) ? max(0., m_responseTimestamp - dateValue) : 0; + double ageValue = m_response.age(); + double correctedReceivedAge = isfinite(ageValue) ? max(apparentAge, ageValue) : apparentAge; + double residentTime = currentTime() - m_responseTimestamp; + return correctedReceivedAge + residentTime; +} + +double CachedResource::freshnessLifetime() const +{ + // Cache non-http resources liberally + if (!m_response.url().protocolInHTTPFamily()) + return std::numeric_limits<double>::max(); + + // RFC2616 13.2.4 + double maxAgeValue = m_response.cacheControlMaxAge(); + if (isfinite(maxAgeValue)) + return maxAgeValue; + double expiresValue = m_response.expires(); + double dateValue = m_response.date(); + double creationTime = isfinite(dateValue) ? dateValue : m_responseTimestamp; + if (isfinite(expiresValue)) + return expiresValue - creationTime; + double lastModifiedValue = m_response.lastModified(); + if (isfinite(lastModifiedValue)) + return (creationTime - lastModifiedValue) * 0.1; + // If no cache headers are present, the specification leaves the decision to the UA. Other browsers seem to opt for 0. + return 0; } void CachedResource::setResponse(const ResourceResponse& response) { m_response = response; - m_expirationDate = response.expirationDate(); + m_responseTimestamp = currentTime(); } void CachedResource::setRequest(Request* request) @@ -259,7 +294,6 @@ void CachedResource::setResourceToRevalidate(CachedResource* resource) void CachedResource::clearResourceToRevalidate() { ASSERT(m_resourceToRevalidate); - ASSERT(m_resourceToRevalidate->m_isBeingRevalidated); m_resourceToRevalidate->m_isBeingRevalidated = false; m_resourceToRevalidate->deleteIfPossible(); m_handlesToRevalidate.clear(); @@ -270,6 +304,7 @@ void CachedResource::clearResourceToRevalidate() void CachedResource::switchClientsToRevalidatedResource() { ASSERT(m_resourceToRevalidate); + ASSERT(m_resourceToRevalidate->inCache()); ASSERT(!inCache()); HashSet<CachedResourceHandleBase*>::iterator end = m_handlesToRevalidate.end(); @@ -300,6 +335,23 @@ void CachedResource::switchClientsToRevalidatedResource() m_resourceToRevalidate->addClient(clientsToMove[n]); } +void CachedResource::updateResponseAfterRevalidation(const ResourceResponse& validatingResponse) +{ + m_responseTimestamp = currentTime(); + + DEFINE_STATIC_LOCAL(const AtomicString, contentHeaderPrefix, ("content-")); + // RFC2616 10.3.5 + // Update cached headers from the 304 response + const HTTPHeaderMap& newHeaders = validatingResponse.httpHeaderFields(); + HTTPHeaderMap::const_iterator end = newHeaders.end(); + for (HTTPHeaderMap::const_iterator it = newHeaders.begin(); it != end; ++it) { + // Don't allow 304 response to update content headers, these can't change but some servers send wrong values. + if (it->first.startsWith(contentHeaderPrefix, false)) + continue; + m_response.setHTTPHeaderField(it->first, it->second); + } +} + bool CachedResource::canUseCacheValidator() const { return !m_loading && (!m_response.httpHeaderField("Last-Modified").isEmpty() || !m_response.httpHeaderField("ETag").isEmpty()); @@ -310,9 +362,9 @@ bool CachedResource::mustRevalidate(CachePolicy cachePolicy) const if (m_loading) return false; - // FIXME: Also look at max-age, min-fresh, max-stale in Cache-Control if (cachePolicy == CachePolicyCache) return m_response.cacheControlContainsNoCache() || (isExpired() && m_response.cacheControlContainsMustRevalidate()); + return isExpired() || m_response.cacheControlContainsNoCache(); } @@ -379,18 +431,10 @@ bool CachedResource::wasPurged() const 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. + return sizeof(CachedResource) + m_response.memoryUsage() + 576; + /* + 576 = 192 + // average size of m_url + 384; // average size of m_clients hash map */ } diff --git a/src/3rdparty/webkit/WebCore/loader/CachedResource.h b/src/3rdparty/webkit/WebCore/loader/CachedResource.h index 63c250bbd4..16cce26f1a 100644 --- a/src/3rdparty/webkit/WebCore/loader/CachedResource.h +++ b/src/3rdparty/webkit/WebCore/loader/CachedResource.h @@ -82,6 +82,7 @@ public: virtual String encoding() const { return String(); } virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived) = 0; virtual void error() = 0; + virtual void httpStatusCodeError() { error(); } // Images keep loading in spite of HTTP errors (for legacy compat with <img>, etc.). const String &url() const { return m_url; } Type type() const { return m_type; } @@ -126,7 +127,8 @@ public: // Called by the cache if the object has been removed from the cache // while still being referenced. This means the object should delete itself // if the number of clients observing it ever drops to 0. - void setInCache(bool b) { m_inCache = b; } + // The resource can be brought back to cache after successful revalidation. + void setInCache(bool b) { m_inCache = b; if (b) m_isBeingRevalidated = false; } bool inCache() const { return m_inCache; } void setInLiveDecodedResourcesList(bool b) { m_inLiveDecodedResourcesList = b; } @@ -162,7 +164,7 @@ public: void decreasePreloadCount() { ASSERT(m_preloadCount); --m_preloadCount; } void registerHandle(CachedResourceHandleBase* h) { ++m_handleCount; if (m_resourceToRevalidate) m_handlesToRevalidate.add(h); } - void unregisterHandle(CachedResourceHandleBase* h) { --m_handleCount; if (m_resourceToRevalidate) m_handlesToRevalidate.remove(h); if (!m_handleCount) deleteIfPossible(); } + void unregisterHandle(CachedResourceHandleBase* h) { ASSERT(m_handleCount > 0); --m_handleCount; if (m_resourceToRevalidate) m_handlesToRevalidate.remove(h); if (!m_handleCount) deleteIfPossible(); } bool canUseCacheValidator() const; bool mustRevalidate(CachePolicy) const; @@ -172,12 +174,16 @@ public: bool isPurgeable() const; bool wasPurged() const; + // This is used by the archive machinery to get at a purged resource without + // triggering a load. We should make it protected again if we can find a + // better way to handle the archive case. + bool makePurgeable(bool purgeable); + protected: void setEncodedSize(unsigned); void setDecodedSize(unsigned); void didAccessDecodedData(double timeStamp); - bool makePurgeable(bool purgeable); bool isSafeToMakePurgeable() const; HashCountedSet<CachedResourceClient*> m_clients; @@ -187,6 +193,8 @@ protected: Request* m_request; ResourceResponse m_response; + double m_responseTimestamp; + RefPtr<SharedBuffer> m_data; OwnPtr<PurgeableBuffer> m_purgeableData; @@ -200,7 +208,10 @@ private: void setResourceToRevalidate(CachedResource*); void switchClientsToRevalidatedResource(); void clearResourceToRevalidate(); - void setExpirationDate(time_t expirationDate) { m_expirationDate = expirationDate; } + void updateResponseAfterRevalidation(const ResourceResponse& validatingResponse); + + double currentAge() const; + double freshnessLifetime() const; unsigned m_encodedSize; unsigned m_decodedSize; @@ -217,7 +228,6 @@ private: protected: bool m_inCache; bool m_loading; - bool m_expireDateChanged; #ifndef NDEBUG bool m_deleted; unsigned m_lruIndex; @@ -241,8 +251,6 @@ private: bool m_isBeingRevalidated; // These handles will need to be updated to point to the m_resourceToRevalidate in case we get 304 response. HashSet<CachedResourceHandleBase*> m_handlesToRevalidate; - - time_t m_expirationDate; }; } diff --git a/src/3rdparty/webkit/WebCore/loader/CachedScript.cpp b/src/3rdparty/webkit/WebCore/loader/CachedScript.cpp index 411521b67e..ebf0898799 100644 --- a/src/3rdparty/webkit/WebCore/loader/CachedScript.cpp +++ b/src/3rdparty/webkit/WebCore/loader/CachedScript.cpp @@ -29,21 +29,20 @@ #include "CachedResourceClient.h" #include "CachedResourceClientWalker.h" +#include "TextResourceDecoder.h" #include <wtf/Vector.h> namespace WebCore { CachedScript::CachedScript(const String& url, const String& charset) : CachedResource(url, Script) - , m_encoding(charset) + , m_decoder(TextResourceDecoder::create("application/javascript", charset)) , m_decodedDataDeletionTimer(this, &CachedScript::decodedDataDeletionTimerFired) { // It's javascript we want. // But some websites think their scripts are <some wrong mimetype here> // and refuse to serve them if we only accept application/x-javascript. setAccept("*/*"); - if (!m_encoding.isValid()) - m_encoding = Latin1Encoding(); } CachedScript::~CachedScript() @@ -64,14 +63,12 @@ void CachedScript::allClientsRemoved() void CachedScript::setEncoding(const String& chs) { - TextEncoding encoding(chs); - if (encoding.isValid()) - m_encoding = encoding; + m_decoder->setEncoding(chs, TextResourceDecoder::EncodingFromHTTPHeader); } String CachedScript::encoding() const { - return m_encoding.name(); + return m_decoder->encoding().name(); } const String& CachedScript::script() @@ -79,7 +76,8 @@ const String& CachedScript::script() ASSERT(!isPurgeable()); if (!m_script && m_data) { - m_script = m_encoding.decode(m_data->data(), encodedSize()); + m_script = m_decoder->decode(m_data->data(), encodedSize()); + m_script += m_decoder->flush(); setDecodedSize(m_script.length() * sizeof(UChar)); } diff --git a/src/3rdparty/webkit/WebCore/loader/CachedScript.h b/src/3rdparty/webkit/WebCore/loader/CachedScript.h index 1715e0631a..e1c3ee04d9 100644 --- a/src/3rdparty/webkit/WebCore/loader/CachedScript.h +++ b/src/3rdparty/webkit/WebCore/loader/CachedScript.h @@ -27,12 +27,12 @@ #define CachedScript_h #include "CachedResource.h" -#include "TextEncoding.h" #include "Timer.h" namespace WebCore { class DocLoader; + class TextResourceDecoder; class CachedScript : public CachedResource { public: @@ -59,7 +59,7 @@ namespace WebCore { void decodedDataDeletionTimerFired(Timer<CachedScript>*); String m_script; - TextEncoding m_encoding; + RefPtr<TextResourceDecoder> m_decoder; Timer<CachedScript> m_decodedDataDeletionTimer; }; } diff --git a/src/3rdparty/webkit/WebCore/loader/CrossOriginAccessControl.cpp b/src/3rdparty/webkit/WebCore/loader/CrossOriginAccessControl.cpp new file mode 100644 index 0000000000..f0f8b6afc8 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/loader/CrossOriginAccessControl.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "CrossOriginAccessControl.h" + +#include "AtomicString.h" +#include "HTTPParsers.h" +#include "ResourceResponse.h" +#include "SecurityOrigin.h" +#include <wtf/Threading.h> + +namespace WebCore { + +bool isOnAccessControlSimpleRequestMethodWhitelist(const String& method) +{ + return method == "GET" || method == "HEAD" || method == "POST"; +} + +bool isOnAccessControlSimpleRequestHeaderWhitelist(const String& name, const String& value) +{ + if (equalIgnoringCase(name, "accept") || equalIgnoringCase(name, "accept-language") || equalIgnoringCase(name, "content-language")) + return true; + + // Preflight is required for MIME types that can not be sent via form submission. + if (equalIgnoringCase(name, "content-type")) { + String mimeType = extractMIMETypeFromMediaType(value); + return equalIgnoringCase(mimeType, "application/x-www-form-urlencoded") + || equalIgnoringCase(mimeType, "multipart/form-data") + || equalIgnoringCase(mimeType, "text/plain"); + } + + return false; +} + +bool isSimpleCrossOriginAccessRequest(const String& method, const HTTPHeaderMap& headerMap) +{ + if (!isOnAccessControlSimpleRequestMethodWhitelist(method)) + return false; + + HTTPHeaderMap::const_iterator end = headerMap.end(); + for (HTTPHeaderMap::const_iterator it = headerMap.begin(); it != end; ++it) { + if (!isOnAccessControlSimpleRequestHeaderWhitelist(it->first, it->second)) + return false; + } + + return true; +} + +typedef HashSet<String, CaseFoldingHash> HTTPHeaderSet; +static HTTPHeaderSet* createAllowedCrossOriginResponseHeadersSet() +{ + HTTPHeaderSet* headerSet = new HashSet<String, CaseFoldingHash>; + + headerSet->add("cache-control"); + headerSet->add("content-language"); + headerSet->add("content-type"); + headerSet->add("expires"); + headerSet->add("last-modified"); + headerSet->add("pragma"); + + return headerSet; +} + +bool isOnAccessControlResponseHeaderWhitelist(const String& name) +{ + AtomicallyInitializedStatic(HTTPHeaderSet*, allowedCrossOriginResponseHeaders = createAllowedCrossOriginResponseHeadersSet()); + + return allowedCrossOriginResponseHeaders->contains(name); +} + +bool passesAccessControlCheck(const ResourceResponse& response, bool includeCredentials, SecurityOrigin* securityOrigin) +{ + // A wildcard Access-Control-Allow-Origin can not be used if credentials are to be sent, + // even with Access-Control-Allow-Credentials set to true. + const String& accessControlOriginString = response.httpHeaderField("Access-Control-Allow-Origin"); + if (accessControlOriginString == "*" && !includeCredentials) + return true; + + RefPtr<SecurityOrigin> accessControlOrigin = SecurityOrigin::createFromString(accessControlOriginString); + if (!accessControlOrigin->isSameSchemeHostPort(securityOrigin)) + return false; + + if (includeCredentials) { + const String& accessControlCredentialsString = response.httpHeaderField("Access-Control-Allow-Credentials"); + if (accessControlCredentialsString != "true") + return false; + } + + return true; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/loader/CrossOriginAccessControl.h b/src/3rdparty/webkit/WebCore/loader/CrossOriginAccessControl.h new file mode 100644 index 0000000000..267646f129 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/loader/CrossOriginAccessControl.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +namespace WebCore { + + class HTTPHeaderMap; + class ResourceResponse; + class SecurityOrigin; + class String; + + bool isSimpleCrossOriginAccessRequest(const String& method, const HTTPHeaderMap&); + bool isOnAccessControlSimpleRequestMethodWhitelist(const String&); + bool isOnAccessControlSimpleRequestHeaderWhitelist(const String& name, const String& value); + bool isOnAccessControlResponseHeaderWhitelist(const String&); + + bool passesAccessControlCheck(const ResourceResponse&, bool includeCredentials, SecurityOrigin*); + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/loader/CrossOriginPreflightResultCache.cpp b/src/3rdparty/webkit/WebCore/loader/CrossOriginPreflightResultCache.cpp new file mode 100644 index 0000000000..4bd05b2351 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/loader/CrossOriginPreflightResultCache.cpp @@ -0,0 +1,173 @@ +/* + * 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 + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "CrossOriginPreflightResultCache.h" + +#include "CrossOriginAccessControl.h" +#include "ResourceResponse.h" +#include <wtf/CurrentTime.h> + +namespace WebCore { + +// These values are at the discretion of the user agent. +static const unsigned defaultPreflightCacheTimeoutSeconds = 5; +static const unsigned maxPreflightCacheTimeoutSeconds = 600; // Should be short enough to minimize the risk of using a poisoned cache after switching to a secure network. + +static bool parseAccessControlMaxAge(const String& string, unsigned& expiryDelta) +{ + // FIXME: this will not do the correct thing for a number starting with a '+' + bool ok = false; + expiryDelta = string.toUIntStrict(&ok); + return ok; +} + +template<class HashType> +static void addToAccessControlAllowList(const String& string, unsigned start, unsigned end, HashSet<String, HashType>& set) +{ + StringImpl* stringImpl = string.impl(); + if (!stringImpl) + return; + + // Skip white space from start. + while (start <= end && isSpaceOrNewline((*stringImpl)[start])) + ++start; + + // only white space + if (start > end) + return; + + // Skip white space from end. + while (end && isSpaceOrNewline((*stringImpl)[end])) + --end; + + // substringCopy() is called on the strings because the cache is accessed on multiple threads. + set.add(string.substringCopy(start, end - start + 1)); +} + +template<class HashType> +static bool parseAccessControlAllowList(const String& string, HashSet<String, HashType>& set) +{ + int start = 0; + int end; + while ((end = string.find(',', start)) != -1) { + if (start == end) + return false; + + addToAccessControlAllowList(string, start, end - 1, set); + start = end + 1; + } + if (start != static_cast<int>(string.length())) + addToAccessControlAllowList(string, start, string.length() - 1, set); + + return true; +} + +bool CrossOriginPreflightResultCacheItem::parse(const ResourceResponse& response) +{ + m_methods.clear(); + if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Methods"), m_methods)) + return false; + + m_headers.clear(); + if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Headers"), m_headers)) + return false; + + unsigned expiryDelta; + if (parseAccessControlMaxAge(response.httpHeaderField("Access-Control-Max-Age"), expiryDelta)) { + if (expiryDelta > maxPreflightCacheTimeoutSeconds) + expiryDelta = maxPreflightCacheTimeoutSeconds; + } else + expiryDelta = defaultPreflightCacheTimeoutSeconds; + + m_absoluteExpiryTime = currentTime() + expiryDelta; + return true; +} + +bool CrossOriginPreflightResultCacheItem::allowsCrossOriginMethod(const String& method) const +{ + return m_methods.contains(method) || isOnAccessControlSimpleRequestMethodWhitelist(method); +} + +bool CrossOriginPreflightResultCacheItem::allowsCrossOriginHeaders(const HTTPHeaderMap& requestHeaders) const +{ + HTTPHeaderMap::const_iterator end = requestHeaders.end(); + for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) { + if (!m_headers.contains(it->first) && !isOnAccessControlSimpleRequestHeaderWhitelist(it->first, it->second)) + return false; + } + return true; +} + +bool CrossOriginPreflightResultCacheItem::allowsRequest(bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) const +{ + if (m_absoluteExpiryTime < currentTime()) + return false; + if (includeCredentials && !m_credentials) + return false; + if (!allowsCrossOriginMethod(method)) + return false; + if (!allowsCrossOriginHeaders(requestHeaders)) + return false; + return true; +} + +CrossOriginPreflightResultCache& CrossOriginPreflightResultCache::shared() +{ + AtomicallyInitializedStatic(CrossOriginPreflightResultCache&, cache = *new CrossOriginPreflightResultCache); + return cache; +} + +void CrossOriginPreflightResultCache::appendEntry(const String& origin, const KURL& url, CrossOriginPreflightResultCacheItem* preflightResult) +{ + MutexLocker lock(m_mutex); + // Note that the entry may already be present in the HashMap if another thread is accessing the same location. + m_preflightHashMap.set(std::make_pair(origin.copy(), url.copy()), preflightResult); +} + +bool CrossOriginPreflightResultCache::canSkipPreflight(const String& origin, const KURL& url, bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) +{ + MutexLocker lock(m_mutex); + CrossOriginPreflightResultHashMap::iterator cacheIt = m_preflightHashMap.find(std::make_pair(origin, url)); + if (cacheIt == m_preflightHashMap.end()) + return false; + + if (cacheIt->second->allowsRequest(includeCredentials, method, requestHeaders)) + return true; + + delete cacheIt->second; + m_preflightHashMap.remove(cacheIt); + return false; +} + +void CrossOriginPreflightResultCache::empty() +{ + MutexLocker lock(m_mutex); + deleteAllValues(m_preflightHashMap); + m_preflightHashMap.clear(); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/loader/CrossOriginPreflightResultCache.h b/src/3rdparty/webkit/WebCore/loader/CrossOriginPreflightResultCache.h new file mode 100644 index 0000000000..39c3cd1eb4 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/loader/CrossOriginPreflightResultCache.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "KURLHash.h" +#include "StringHash.h" + +namespace WebCore { + + class HTTPHeaderMap; + class ResourceResponse; + + class CrossOriginPreflightResultCacheItem : Noncopyable { + public: + CrossOriginPreflightResultCacheItem(bool credentials) + : m_absoluteExpiryTime(0) + , m_credentials(credentials) + { + } + + bool parse(const ResourceResponse&); + bool allowsCrossOriginMethod(const String&) const; + bool allowsCrossOriginHeaders(const HTTPHeaderMap&) const; + bool allowsRequest(bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) const; + + private: + typedef HashSet<String, CaseFoldingHash> HeadersSet; + + // FIXME: A better solution to holding onto the absolute expiration time might be + // to start a timer for the expiration delta that removes this from the cache when + // it fires. + double m_absoluteExpiryTime; + bool m_credentials; + HashSet<String> m_methods; + HeadersSet m_headers; + }; + + class CrossOriginPreflightResultCache : Noncopyable { + public: + static CrossOriginPreflightResultCache& shared(); + + void appendEntry(const String& origin, const KURL&, CrossOriginPreflightResultCacheItem*); + bool canSkipPreflight(const String& origin, const KURL&, bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders); + + void empty(); + + private: + CrossOriginPreflightResultCache() { } + + typedef HashMap<std::pair<String, KURL>, CrossOriginPreflightResultCacheItem*> CrossOriginPreflightResultHashMap; + + CrossOriginPreflightResultHashMap m_preflightHashMap; + Mutex m_mutex; + }; + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/loader/DocLoader.cpp b/src/3rdparty/webkit/WebCore/loader/DocLoader.cpp index 27c15527d7..c0e3f6f2d3 100644 --- a/src/3rdparty/webkit/WebCore/loader/DocLoader.cpp +++ b/src/3rdparty/webkit/WebCore/loader/DocLoader.cpp @@ -3,6 +3,7 @@ Copyright (C) 2001 Dirk Mueller (mueller@kde.org) Copyright (C) 2002 Waldo Bastian (bastian@kde.org) Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -36,6 +37,7 @@ #include "CString.h" #include "Document.h" #include "DOMWindow.h" +#include "HTMLElement.h" #include "Frame.h" #include "FrameLoader.h" #include "loader.h" @@ -64,6 +66,9 @@ DocLoader::~DocLoader() for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) it->second->setDocLoader(0); m_cache->removeDocLoader(this); + + // Make sure no requests still point to this DocLoader + ASSERT(m_requestCount == 0); } Frame* DocLoader::frame() const @@ -191,7 +196,7 @@ 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()) { @@ -266,11 +271,16 @@ void DocLoader::setAutoLoadImages(bool enable) CachePolicy DocLoader::cachePolicy() const { - return frame() ? frame()->loader()->cachePolicy() : CachePolicyVerify; + return frame() ? frame()->loader()->subresourceCachePolicy() : CachePolicyVerify; } void DocLoader::removeCachedResource(CachedResource* resource) const { +#ifndef NDEBUG + DocumentResourceMap::iterator it = m_documentResources.find(resource->url()); + if (it != m_documentResources.end()) + ASSERT(it->second.get() == resource); +#endif m_documentResources.remove(resource->url()); } @@ -339,7 +349,9 @@ void DocLoader::checkForPendingPreloads() return; for (unsigned i = 0; i < count; ++i) { PendingPreload& preload = m_pendingPreloads[i]; - requestPreload(preload.m_type, preload.m_url, preload.m_charset); + // Don't request preload if the resource already loaded normally (this will result in double load if the page is being reloaded with cached results ignored). + if (!cachedResource(m_doc->completeURL(preload.m_url))) + requestPreload(preload.m_type, preload.m_url, preload.m_charset); } m_pendingPreloads.clear(); } @@ -377,6 +389,11 @@ void DocLoader::clearPreloads() m_preloads.clear(); } +void DocLoader::clearPendingPreloads() +{ + m_pendingPreloads.clear(); +} + #if PRELOAD_DEBUG void DocLoader::printPreloadStats() { diff --git a/src/3rdparty/webkit/WebCore/loader/DocLoader.h b/src/3rdparty/webkit/WebCore/loader/DocLoader.h index 17edcc5274..13a57bdf33 100644 --- a/src/3rdparty/webkit/WebCore/loader/DocLoader.h +++ b/src/3rdparty/webkit/WebCore/loader/DocLoader.h @@ -2,6 +2,7 @@ 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) 2009 Torch Mobile Inc. http://www.torchmobile.com/ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -91,15 +92,12 @@ public: void setAllowStaleResources(bool allowStaleResources) { m_allowStaleResources = allowStaleResources; } -#if USE(LOW_BANDWIDTH_DISPLAY) - void replaceDocument(Document* doc) { m_doc = doc; } -#endif - void incrementRequestCount(); void decrementRequestCount(); int requestCount(); void clearPreloads(); + void clearPendingPreloads(); void preload(CachedResource::Type, const String& url, const String& charset, bool referencedFromBody); void checkForPendingPreloads(); void printPreloadStats(); diff --git a/src/3rdparty/webkit/WebCore/loader/DocumentLoader.cpp b/src/3rdparty/webkit/WebCore/loader/DocumentLoader.cpp index 88c54f6333..e4cbe4c5dc 100644 --- a/src/3rdparty/webkit/WebCore/loader/DocumentLoader.cpp +++ b/src/3rdparty/webkit/WebCore/loader/DocumentLoader.cpp @@ -95,9 +95,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; @@ -115,6 +112,10 @@ 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. + frame->document()->displayBufferModifiedByEncoding(buffer.characters(), buffer.length()); + return String::adopt(buffer); } @@ -150,6 +151,7 @@ DocumentLoader::DocumentLoader(const ResourceRequest& req, const SubstituteData& , m_loadingFromCachedPage(false) , m_stopRecordingResponses(false) , m_substituteResourceDeliveryTimer(this, &DocumentLoader::substituteResourceDeliveryTimerFired) + , m_didCreateGlobalHistoryEntry(false) #if ENABLE(OFFLINE_WEB_APPLICATIONS) , m_candidateApplicationCacheGroup(0) #endif @@ -169,9 +171,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 } @@ -253,10 +255,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); @@ -273,7 +279,7 @@ void DocumentLoader::mainReceivedError(const ResourceError& error, bool isComple // one document at a time, but one document may have many related resources. // stopLoading will stop all loads initiated by the data source, // but not loads initiated by child frames' data sources -- that's the WebFrame's job. -void DocumentLoader::stopLoading() +void DocumentLoader::stopLoading(DatabasePolicy databasePolicy) { // In some rare cases, calling FrameLoader::stopLoading could set m_loading to false. // (This can happen when there's a single XMLHttpRequest currently loading and stopLoading causes it @@ -285,8 +291,8 @@ void DocumentLoader::stopLoading() // still parsing. Failure to do so can cause a world leak. Document* doc = m_frame->document(); - if (loading || (doc && doc->parsing())) - m_frame->loader()->stopLoading(false); + if (loading || doc->parsing()) + m_frame->loader()->stopLoading(false, databasePolicy); } // Always cancel multipart loaders @@ -399,6 +405,10 @@ void DocumentLoader::setupForReplaceByMIMEType(const String& newMIMEType) void DocumentLoader::updateLoading() { + if (!m_frame) { + setLoading(false); + return; + } ASSERT(this == frameLoader()->activeDocumentLoader()); setLoading(frameLoader()->isLoading()); } @@ -461,13 +471,12 @@ bool DocumentLoader::isLoadingInAPISense() const return true; if (!m_subresourceLoaders.isEmpty()) return true; - if (Document* doc = m_frame->document()) { - if (doc->docLoader()->requestCount()) + Document* doc = m_frame->document(); + if (doc->docLoader()->requestCount()) + return true; + if (Tokenizer* tok = doc->tokenizer()) + if (tok->processingData()) return true; - if (Tokenizer* tok = doc->tokenizer()) - if (tok->processingData()) - return true; - } } return frameLoader()->subframeIsLoading(); } @@ -544,15 +553,20 @@ PassRefPtr<ArchiveResource> DocumentLoader::subresource(const KURL& url) const if (!isCommitted()) return 0; - Document* doc = m_frame->document(); - if (!doc) + CachedResource* resource = m_frame->document()->docLoader()->cachedResource(url); + if (!resource || !resource->isLoaded()) return archiveResourceForURL(url); - - CachedResource* resource = doc->docLoader()->cachedResource(url); - if (!resource || resource->preloadResult() == CachedResource::PreloadReferenced) - return archiveResourceForURL(url); - - return ArchiveResource::create(resource->data(), url, resource->response()); + + // FIXME: This has the side effect of making the resource non-purgeable. + // It would be better if it didn't have this permanent effect. + if (!resource->makePurgeable(false)) + return 0; + + RefPtr<SharedBuffer> data = resource->data(); + if (!data) + return 0; + + return ArchiveResource::create(data.release(), url, resource->response()); } void DocumentLoader::getSubresources(Vector<PassRefPtr<ArchiveResource> >& subresources) const @@ -561,8 +575,6 @@ void DocumentLoader::getSubresources(Vector<PassRefPtr<ArchiveResource> >& subre return; Document* document = m_frame->document(); - if (!document) - return; const DocLoader::DocumentResourceMap& allResources = document->docLoader()->allCachedResources(); DocLoader::DocumentResourceMap::const_iterator end = allResources.end(); @@ -864,7 +876,7 @@ ApplicationCache* DocumentLoader::mainResourceApplicationCache() const bool DocumentLoader::shouldLoadResourceFromApplicationCache(const ResourceRequest& request, ApplicationCacheResource*& resource) { ApplicationCache* cache = applicationCache(); - if (!cache) + if (!cache || !cache->isComplete()) return false; // If the resource is not a HTTP/HTTPS GET, then abort @@ -892,6 +904,8 @@ bool DocumentLoader::getApplicationCacheFallbackResource(const ResourceRequest& if (!cache) return false; } + if (!cache->isComplete()) + return false; // If the resource is not a HTTP/HTTPS GET, then abort if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) diff --git a/src/3rdparty/webkit/WebCore/loader/DocumentLoader.h b/src/3rdparty/webkit/WebCore/loader/DocumentLoader.h index 691a7e2356..d4ac427891 100644 --- a/src/3rdparty/webkit/WebCore/loader/DocumentLoader.h +++ b/src/3rdparty/webkit/WebCore/loader/DocumentLoader.h @@ -93,7 +93,7 @@ namespace WebCore { void replaceRequestURLForAnchorScroll(const KURL&); bool isStopping() const { return m_isStopping; } - void stopLoading(); + void stopLoading(DatabasePolicy = DatabasePolicyStop); void setCommitted(bool committed) { m_committed = committed; } bool isCommitted() const { return m_committed; } bool isLoading() const { return m_loading; } @@ -156,6 +156,21 @@ namespace WebCore { KURL urlForHistory() const; bool urlForHistoryReflectsFailure() const; + + // These accessors accomodate WebCore's somewhat fickle custom of creating history + // items for redirects, but only sometimes. For "source" and "destination", + // these accessors return the URL that would have been used if a history + // item were created. This allows WebKit to link history items reflecting + // redirects into a chain from start to finish. + String clientRedirectSourceForHistory() const { return m_clientRedirectSourceForHistory; } // null if no client redirect occurred. + String clientRedirectDestinationForHistory() const { return urlForHistory(); } + void setClientRedirectSourceForHistory(const String& clientedirectSourceForHistory) { m_clientRedirectSourceForHistory = clientedirectSourceForHistory; } + + String serverRedirectSourceForHistory() const { return urlForHistory() == url() ? String() : urlForHistory(); } // null if no server redirect occurred. + String serverRedirectDestinationForHistory() const { return url(); } + + bool didCreateGlobalHistoryEntry() const { return m_didCreateGlobalHistoryEntry; } + void setDidCreateGlobalHistoryEntry(bool didCreateGlobalHistoryEntry) { m_didCreateGlobalHistoryEntry = didCreateGlobalHistoryEntry; } void loadFromCachedPage(PassRefPtr<CachedPage>); void setLoadingFromCachedPage(bool loading) { m_loadingFromCachedPage = loading; } @@ -287,6 +302,9 @@ namespace WebCore { HashSet<String> m_resourcesClientKnowsAbout; Vector<String> m_resourcesLoadedFromMemoryCacheForClientNotification; + + String m_clientRedirectSourceForHistory; + bool m_didCreateGlobalHistoryEntry; #if ENABLE(OFFLINE_WEB_APPLICATIONS) // The application cache that the document loader is associated with (if any). diff --git a/src/3rdparty/webkit/WebCore/loader/DocumentThreadableLoader.cpp b/src/3rdparty/webkit/WebCore/loader/DocumentThreadableLoader.cpp new file mode 100644 index 0000000000..643c06755d --- /dev/null +++ b/src/3rdparty/webkit/WebCore/loader/DocumentThreadableLoader.cpp @@ -0,0 +1,199 @@ +/* + * 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 "DocumentThreadableLoader.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "ResourceRequest.h" +#include "SecurityOrigin.h" +#include "SubresourceLoader.h" +#include "ThreadableLoaderClient.h" + +namespace WebCore { + +void DocumentThreadableLoader::loadResourceSynchronously(Document* document, const ResourceRequest& request, ThreadableLoaderClient& client, StoredCredentials storedCredentials) +{ + bool sameOriginRequest = document->securityOrigin()->canRequest(request.url()); + + Vector<char> data; + ResourceError error; + ResourceResponse response; + unsigned long identifier = std::numeric_limits<unsigned long>::max(); + if (document->frame()) + identifier = document->frame()->loader()->loadResourceSynchronously(request, storedCredentials, error, response, data); + + // No exception for file:/// resources, see <rdar://problem/4962298>. + // Also, if we have an HTTP response, then it wasn't a network error in fact. + if (!error.isNull() && !request.url().isLocalFile() && response.httpStatusCode() <= 0) { + client.didFail(error); + return; + } + + // FIXME: This check along with the one in willSendRequest is specific to xhr and + // should be made more generic. + if (sameOriginRequest && !document->securityOrigin()->canRequest(response.url())) { + client.didFailRedirectCheck(); + return; + } + + client.didReceiveResponse(response); + + const char* bytes = static_cast<const char*>(data.data()); + int len = static_cast<int>(data.size()); + client.didReceiveData(bytes, len); + + client.didFinishLoading(identifier); +} + +PassRefPtr<DocumentThreadableLoader> DocumentThreadableLoader::create(Document* document, ThreadableLoaderClient* client, const ResourceRequest& request, LoadCallbacks callbacksSetting, ContentSniff contentSniff, StoredCredentials storedCredentials) +{ + ASSERT(document); + RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, client, request, callbacksSetting, contentSniff, storedCredentials)); + if (!loader->m_loader) + loader = 0; + return loader.release(); +} + +DocumentThreadableLoader::DocumentThreadableLoader(Document* document, ThreadableLoaderClient* client, const ResourceRequest& request, LoadCallbacks callbacksSetting, ContentSniff contentSniff, StoredCredentials storedCredentials) + : m_client(client) + , m_document(document) + , m_allowStoredCredentials(storedCredentials == AllowStoredCredentials) + , m_sameOriginRequest(document->securityOrigin()->canRequest(request.url())) +{ + 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* loader, ResourceRequest& request, const ResourceResponse&) +{ + ASSERT(m_client); + ASSERT_UNUSED(loader, loader == m_loader); + + // 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->didFailRedirectCheck(); + request = ResourceRequest(); + } +} + +void DocumentThreadableLoader::didSendData(SubresourceLoader* loader, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +{ + ASSERT(m_client); + ASSERT_UNUSED(loader, loader == m_loader); + + m_client->didSendData(bytesSent, totalBytesToBeSent); +} + +void DocumentThreadableLoader::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response) +{ + ASSERT(m_client); + ASSERT_UNUSED(loader, loader == m_loader); + + m_client->didReceiveResponse(response); +} + +void DocumentThreadableLoader::didReceiveData(SubresourceLoader* loader, const char* data, int lengthReceived) +{ + ASSERT(m_client); + ASSERT_UNUSED(loader, loader == m_loader); + + m_client->didReceiveData(data, lengthReceived); +} + +void DocumentThreadableLoader::didFinishLoading(SubresourceLoader* loader) +{ + ASSERT(loader == m_loader); + ASSERT(m_client); + m_client->didFinishLoading(loader->identifier()); +} + +void DocumentThreadableLoader::didFail(SubresourceLoader* loader, const ResourceError& error) +{ + ASSERT(m_client); + ASSERT_UNUSED(loader, loader == m_loader); + + m_client->didFail(error); +} + +bool DocumentThreadableLoader::getShouldUseCredentialStorage(SubresourceLoader* loader, bool& shouldUseCredentialStorage) +{ + ASSERT_UNUSED(loader, loader == m_loader); + + if (!m_allowStoredCredentials) { + shouldUseCredentialStorage = false; + return true; + } + + return false; // Only FrameLoaderClient can ultimately permit credential use. +} + +void DocumentThreadableLoader::didReceiveAuthenticationChallenge(SubresourceLoader* loader, const AuthenticationChallenge&) +{ + ASSERT(loader == m_loader); + // Users are not prompted for credentials for cross-origin requests. + if (!m_sameOriginRequest) { + RefPtr<DocumentThreadableLoader> protect(this); + m_client->didFail(loader->blockedError()); + cancel(); + } +} + +void DocumentThreadableLoader::receivedCancellation(SubresourceLoader* loader, const AuthenticationChallenge& challenge) +{ + ASSERT(m_client); + ASSERT_UNUSED(loader, loader == m_loader); + m_client->didReceiveAuthenticationCancellation(challenge.failureResponse()); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/loader/DocumentThreadableLoader.h b/src/3rdparty/webkit/WebCore/loader/DocumentThreadableLoader.h new file mode 100644 index 0000000000..c612f72067 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/loader/DocumentThreadableLoader.h @@ -0,0 +1,83 @@ +/* + * 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; + struct ResourceRequest; + class ThreadableLoaderClient; + + class DocumentThreadableLoader : public RefCounted<DocumentThreadableLoader>, public ThreadableLoader, private SubresourceLoaderClient { + public: + static void loadResourceSynchronously(Document*, const ResourceRequest&, ThreadableLoaderClient&, StoredCredentials); + static PassRefPtr<DocumentThreadableLoader> create(Document*, ThreadableLoaderClient*, const ResourceRequest&, LoadCallbacks, ContentSniff, StoredCredentials); + 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, StoredCredentials); + 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 bool getShouldUseCredentialStorage(SubresourceLoader*, bool& shouldUseCredentialStorage); + virtual void didReceiveAuthenticationChallenge(SubresourceLoader*, const AuthenticationChallenge&); + virtual void receivedCancellation(SubresourceLoader*, const AuthenticationChallenge&); + + RefPtr<SubresourceLoader> m_loader; + ThreadableLoaderClient* m_client; + Document* m_document; + bool m_allowStoredCredentials; + bool m_sameOriginRequest; + }; + +} // namespace WebCore + +#endif // DocumentThreadableLoader_h diff --git a/src/3rdparty/webkit/WebCore/loader/EmptyClients.h b/src/3rdparty/webkit/WebCore/loader/EmptyClients.h index 5562d130b0..107b8bede8 100644 --- a/src/3rdparty/webkit/WebCore/loader/EmptyClients.h +++ b/src/3rdparty/webkit/WebCore/loader/EmptyClients.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 Eric Seidel (eric@webkit.org) + * 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 @@ -28,12 +29,14 @@ #include "ChromeClient.h" #include "ContextMenuClient.h" -#include "DragClient.h" +#include "Console.h" #include "DocumentLoader.h" +#include "DragClient.h" #include "EditCommand.h" #include "EditorClient.h" -#include "FocusDirection.h" #include "FloatRect.h" +#include "FocusDirection.h" +#include "FormState.h" #include "FrameLoaderClient.h" #include "InspectorClient.h" #include "ResourceError.h" @@ -92,7 +95,7 @@ public: virtual void setResizable(bool) { } - virtual void addMessageToConsole(const String&, unsigned, const String&) { } + virtual void addMessageToConsole(MessageSource, MessageLevel, const String&, unsigned, const String&) { } virtual bool canRunBeforeUnloadConfirmPanel() { return false; } virtual bool runBeforeUnloadConfirmPanel(const String&, Frame*) { return true; } @@ -118,7 +121,7 @@ public: 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& s) const { } + virtual void contentsSizeChanged(Frame*, const IntSize&) const { } virtual void mouseDidMoveOverElement(const HitTestResult&, unsigned) { } @@ -126,11 +129,21 @@ public: virtual void print(Frame*) { } +#if ENABLE(DATABASE) virtual void exceededDatabaseQuota(Frame*, const String&) { } +#endif virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>) { } virtual void formStateDidChange(const Node*) { } + + virtual PassOwnPtr<HTMLParserQuirks> createHTMLParserQuirks() { return 0; } + + virtual bool setCursor(PlatformCursorHandle) { return false; } + + virtual void scrollRectIntoView(const IntRect&, const ScrollView*) const {} + + virtual void requestGeolocationPermissionForFrame(Frame*, Geolocation*) {} }; class EmptyFrameLoaderClient : public FrameLoaderClient { @@ -161,6 +174,7 @@ public: 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 dispatchDidLoadResourceByXMLHttpRequest(unsigned long, const ScriptString&) { } virtual void dispatchDidHandleOnloadEvents() { } virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() { } @@ -211,14 +225,14 @@ public: virtual void committedLoad(DocumentLoader*, const char*, int) { } virtual void finishedLoading(DocumentLoader*) { } - virtual ResourceError cancelledError(const ResourceRequest&) { return ResourceError(); } - virtual ResourceError blockedError(const ResourceRequest&) { return ResourceError(); } - virtual ResourceError cannotShowURLError(const ResourceRequest&) { return ResourceError(); } - virtual ResourceError interruptForPolicyChangeError(const ResourceRequest&) { return ResourceError(); } + virtual ResourceError cancelledError(const ResourceRequest&) { ResourceError error("", 0, "", ""); error.setIsCancellation(true); return error; } + virtual ResourceError blockedError(const ResourceRequest&) { return ResourceError("", 0, "", ""); } + virtual ResourceError cannotShowURLError(const ResourceRequest&) { return ResourceError("", 0, "", ""); } + virtual ResourceError interruptForPolicyChangeError(const ResourceRequest&) { return ResourceError("", 0, "", ""); } - virtual ResourceError cannotShowMIMETypeError(const ResourceResponse&) { return ResourceError(); } - virtual ResourceError fileDoesNotExistError(const ResourceResponse&) { return ResourceError(); } - virtual ResourceError pluginWillHandleLoadError(const ResourceResponse&) { return ResourceError(); } + virtual ResourceError cannotShowMIMETypeError(const ResourceResponse&) { return ResourceError("", 0, "", ""); } + virtual ResourceError fileDoesNotExistError(const ResourceResponse&) { return ResourceError("", 0, "", ""); } + virtual ResourceError pluginWillHandleLoadError(const ResourceResponse&) { return ResourceError("", 0, "", ""); } virtual bool shouldFallBack(const ResourceError&) { return false; } @@ -239,24 +253,26 @@ public: 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() { } + virtual void updateGlobalHistoryRedirectLinks() { } virtual bool shouldGoToHistoryItem(HistoryItem*) const { return false; } virtual void saveViewStateToItem(HistoryItem*) { } virtual bool canCachePage() const { return false; } 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 Widget* createPlugin(const IntSize&, HTMLPlugInElement*, const KURL&, const Vector<String>&, const Vector<String>&, const String&, bool) { return 0; } + virtual Widget* createJavaAppletWidget(const IntSize&, HTMLAppletElement*, const KURL&, const Vector<String>&, const Vector<String>&) { return 0; } virtual ObjectContentType objectContentType(const KURL&, const String&) { return ObjectContentType(); } virtual String overrideMediaType() const { return String(); } virtual void redirectDataToPlugin(Widget*) { } virtual void windowObjectCleared() { } + virtual void documentElementAvailable() { } virtual void didPerformFirstNavigation() const { } virtual void registerForIconNotification(bool) { } @@ -264,6 +280,9 @@ public: #if PLATFORM(MAC) virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long, NSCachedURLResponse* response) const { return response; } #endif +#if USE(CFNETWORK) + virtual bool shouldCacheResponse(DocumentLoader*, unsigned long, const ResourceResponse&, const unsigned char*, unsigned long long) { return true; } +#endif }; @@ -335,10 +354,32 @@ public: virtual NSArray* pasteboardTypesForSelection(Frame*) { return 0; } #endif #endif +#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + virtual void uppercaseWord() { } + virtual void lowercaseWord() { } + virtual void capitalizeWord() { } + virtual void showSubstitutionsPanel(bool) { } + virtual bool substitutionsPanelIsShowing() { return false; } + virtual void toggleSmartInsertDelete() { } + virtual bool isAutomaticQuoteSubstitutionEnabled() { return false; } + virtual void toggleAutomaticQuoteSubstitution() { } + virtual bool isAutomaticLinkDetectionEnabled() { return false; } + virtual void toggleAutomaticLinkDetection() { } + virtual bool isAutomaticDashSubstitutionEnabled() { return false; } + virtual void toggleAutomaticDashSubstitution() { } + virtual bool isAutomaticTextReplacementEnabled() { return false; } + virtual void toggleAutomaticTextReplacement() { } + virtual bool isAutomaticSpellingCorrectionEnabled() { return false; } + virtual void toggleAutomaticSpellingCorrection() { } +#endif virtual void ignoreWordInSpellDocument(const String&) { } virtual void learnWord(const String&) { } virtual void checkSpellingOfString(const UChar*, int, int*, int*) { } + virtual String getAutoCorrectSuggestionForMisspelledWord(const String&) { return String(); } virtual void checkGrammarOfString(const UChar*, int, Vector<GrammarDetail>&, int*, int*) { } +#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + virtual void checkTextOfParagraph(const UChar*, int, uint64_t, Vector<TextCheckingResult>&) { }; +#endif virtual void updateSpellingUIWithGrammarString(const String&, const GrammarDetail&) { } virtual void updateSpellingUIWithMisspelledWord(const String&) { } virtual void showSpellingUI(bool) { } @@ -391,6 +432,8 @@ public: virtual String localizedStringsURL() { return String(); } + virtual String hiddenPanels() { return String(); } + virtual void showWindow() { } virtual void closeWindow() { } @@ -411,3 +454,4 @@ public: } #endif // EmptyClients_h + diff --git a/src/3rdparty/webkit/WebCore/loader/FTPDirectoryDocument.cpp b/src/3rdparty/webkit/WebCore/loader/FTPDirectoryDocument.cpp index 08ef896e3d..ace4cfe426 100644 --- a/src/3rdparty/webkit/WebCore/loader/FTPDirectoryDocument.cpp +++ b/src/3rdparty/webkit/WebCore/loader/FTPDirectoryDocument.cpp @@ -57,7 +57,7 @@ class FTPDirectoryTokenizer : public HTMLTokenizer { public: FTPDirectoryTokenizer(HTMLDocument*); - virtual bool write(const SegmentedString&, bool appendData); + virtual void write(const SegmentedString&, bool appendData); virtual void finish(); virtual bool isWaitingForScripts() const { return false; } @@ -117,7 +117,7 @@ void FTPDirectoryTokenizer::appendEntry(const String& filename, const String& si RefPtr<Element> rowElement = m_tableElement->insertRow(-1, ec); rowElement->setAttribute("class", "ftpDirectoryEntryRow", ec); - RefPtr<Element> element = m_doc->createElementNS(xhtmlNamespaceURI, "td", ec); + RefPtr<Element> element = m_doc->createElement(tdTag, false); element->appendChild(new Text(m_doc, String(&noBreakSpace, 1)), ec); if (isDirectory) element->setAttribute("class", "ftpDirectoryIcon ftpDirectoryTypeDirectory", ec); @@ -129,12 +129,12 @@ void FTPDirectoryTokenizer::appendEntry(const String& filename, const String& si element->setAttribute("class", "ftpDirectoryFileName", ec); rowElement->appendChild(element, ec); - element = m_doc->createElementNS(xhtmlNamespaceURI, "td", ec); + element = m_doc->createElement(tdTag, false); element->appendChild(new Text(m_doc, date), ec); element->setAttribute("class", "ftpDirectoryFileDate", ec); rowElement->appendChild(element, ec); - element = m_doc->createElementNS(xhtmlNamespaceURI, "td", ec); + element = m_doc->createElement(tdTag, false); element->appendChild(new Text(m_doc, size), ec); element->setAttribute("class", "ftpDirectoryFileSize", ec); rowElement->appendChild(element, ec); @@ -150,11 +150,11 @@ PassRefPtr<Element> FTPDirectoryTokenizer::createTDForFilename(const String& fil else fullURL.append("/" + filename); - RefPtr<Element> anchorElement = m_doc->createElementNS(xhtmlNamespaceURI, "a", ec); + RefPtr<Element> anchorElement = m_doc->createElement(aTag, false); anchorElement->setAttribute("href", fullURL, ec); anchorElement->appendChild(new Text(m_doc, filename), ec); - RefPtr<Element> tdElement = m_doc->createElementNS(xhtmlNamespaceURI, "td", ec); + RefPtr<Element> tdElement = m_doc->createElement(tdTag, false); tdElement->appendChild(anchorElement, ec); return tdElement.release(); @@ -235,7 +235,11 @@ static struct tm *localTimeQt(const time_t *const timep, struct tm *result) #define localtime_r(x, y) localTimeQt(x, y) #elif PLATFORM(WIN_OS) && !defined(localtime_r) +#if defined(_MSC_VER) && (_MSC_VER >= 1400) #define localtime_r(x, y) localtime_s((y), (x)) +#else /* !_MSC_VER */ +#define localtime_r(x,y) (localtime(x)?(*(y)=*localtime(x),(y)):0) +#endif #endif static String processFileDateString(const FTPTime& fileTime) @@ -276,7 +280,7 @@ static String processFileDateString(const FTPTime& fileTime) return "Yesterday" + timeOfDay; } - if (now.tm_mday == 1 && (now.tm_mon == fileTime.tm_mon + 1 || now.tm_mon == 0 && fileTime.tm_mon == 11) && + if (now.tm_mday == 1 && (now.tm_mon == fileTime.tm_mon + 1 || (now.tm_mon == 0 && fileTime.tm_mon == 11)) && wasLastDayOfMonth(fileTime.tm_year, fileTime.tm_mon, fileTime.tm_mday)) return "Yesterday" + timeOfDay; } @@ -303,8 +307,9 @@ static String processFileDateString(const FTPTime& fileTime) void FTPDirectoryTokenizer::parseAndAppendOneLine(const String& inputLine) { ListResult result; + CString latin1Input = inputLine.latin1(); - FTPEntryType typeResult = parseOneFTPLine(inputLine.latin1().data(), m_listState, result); + FTPEntryType typeResult = parseOneFTPLine(latin1Input.data(), m_listState, result); // FTPMiscEntry is a comment or usage statistic which we don't care about, and junk is invalid data - bail in these 2 cases if (typeResult == FTPMiscEntry || typeResult == FTPJunkEntry) @@ -364,9 +369,9 @@ bool FTPDirectoryTokenizer::loadDocumentTemplate() return true; // Otherwise create one manually - ExceptionCode ec; - tableElement = m_doc->createElementNS(xhtmlNamespaceURI, "table", ec); + tableElement = m_doc->createElement(tableTag, false); m_tableElement = static_cast<HTMLTableElement*>(tableElement.get()); + ExceptionCode ec; m_tableElement->setAttribute("id", "ftpDirectoryTable", ec); // If we didn't find the table element, lets try to append our own to the body @@ -386,20 +391,20 @@ void FTPDirectoryTokenizer::createBasicDocument() // FIXME: Make this "basic document" more acceptable - ExceptionCode ec; - RefPtr<Element> bodyElement = m_doc->createElementNS(xhtmlNamespaceURI, "body", ec); + RefPtr<Element> bodyElement = m_doc->createElement(bodyTag, false); + ExceptionCode ec; m_doc->appendChild(bodyElement, ec); - RefPtr<Element> tableElement = m_doc->createElementNS(xhtmlNamespaceURI, "table", ec); + RefPtr<Element> tableElement = m_doc->createElement(tableTag, false); m_tableElement = static_cast<HTMLTableElement*>(tableElement.get()); m_tableElement->setAttribute("id", "ftpDirectoryTable", ec); bodyElement->appendChild(m_tableElement, ec); } -bool FTPDirectoryTokenizer::write(const SegmentedString& s, bool /*appendData*/) +void 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 @@ -439,7 +444,7 @@ bool FTPDirectoryTokenizer::write(const SegmentedString& s, bool /*appendData*/) if (!foundNewLine) { m_dest = m_buffer; - return false; + return; } UChar* start = m_buffer; @@ -460,8 +465,6 @@ bool FTPDirectoryTokenizer::write(const SegmentedString& s, bool /*appendData*/) // Copy the partial line we have left to the carryover buffer if (cursor - start > 1) m_carryOver.append(String(start, cursor - start - 1)); - - return false; } void FTPDirectoryTokenizer::finish() diff --git a/src/3rdparty/webkit/WebCore/loader/FTPDirectoryParser.cpp b/src/3rdparty/webkit/WebCore/loader/FTPDirectoryParser.cpp index 8c76e9796b..6573fb6538 100644 --- a/src/3rdparty/webkit/WebCore/loader/FTPDirectoryParser.cpp +++ b/src/3rdparty/webkit/WebCore/loader/FTPDirectoryParser.cpp @@ -50,7 +50,11 @@ static struct tm *gmtimeQt(const time_t *const timep, struct tm *result) #define gmtime_r(x, y) gmtimeQt(x, y) #elif PLATFORM(WIN_OS) && !defined(gmtime_r) +#if defined(_MSC_VER) && (_MSC_VER >= 1400) #define gmtime_r(x, y) gmtime_s((y), (x)) +#else /* !_MSC_VER */ +#define gmtime_r(x,y) (gmtime(x)?(*(y)=*gmtime(x),(y)):0) +#endif #endif FTPEntryType parseOneFTPLine(const char* line, ListState& state, ListResult& result) diff --git a/src/3rdparty/webkit/WebCore/loader/FormState.cpp b/src/3rdparty/webkit/WebCore/loader/FormState.cpp index c55b8aca14..bd37086879 100644 --- a/src/3rdparty/webkit/WebCore/loader/FormState.cpp +++ b/src/3rdparty/webkit/WebCore/loader/FormState.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 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 @@ -34,16 +34,16 @@ namespace WebCore { -PassRefPtr<FormState> FormState::create(PassRefPtr<HTMLFormElement> form, const HashMap<String, String>& values, PassRefPtr<Frame> sourceFrame) +inline FormState::FormState(PassRefPtr<HTMLFormElement> form, StringPairVector& textFieldValuesToAdopt, PassRefPtr<Frame> sourceFrame) + : m_form(form) + , m_sourceFrame(sourceFrame) { - return adoptRef(new FormState(form, values, sourceFrame)); + m_textFieldValues.swap(textFieldValuesToAdopt); } -FormState::FormState(PassRefPtr<HTMLFormElement> form, const HashMap<String, String>& values, PassRefPtr<Frame> sourceFrame) - : m_form(form) - , m_values(values) - , m_sourceFrame(sourceFrame) +PassRefPtr<FormState> FormState::create(PassRefPtr<HTMLFormElement> form, StringPairVector& textFieldValuesToAdopt, PassRefPtr<Frame> sourceFrame) { + return adoptRef(new FormState(form, textFieldValuesToAdopt, sourceFrame)); } } diff --git a/src/3rdparty/webkit/WebCore/loader/FormState.h b/src/3rdparty/webkit/WebCore/loader/FormState.h index 5370e8aa9a..03317b1118 100644 --- a/src/3rdparty/webkit/WebCore/loader/FormState.h +++ b/src/3rdparty/webkit/WebCore/loader/FormState.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 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,28 +29,28 @@ #ifndef FormState_h #define FormState_h -#include <wtf/RefCounted.h> -#include "StringHash.h" -#include <wtf/HashMap.h> +#include "PlatformString.h" namespace WebCore { class Frame; class HTMLFormElement; + typedef Vector<std::pair<String, String> > StringPairVector; + class FormState : public RefCounted<FormState> { public: - static PassRefPtr<FormState> create(PassRefPtr<HTMLFormElement> form, const HashMap<String, String>& values, PassRefPtr<Frame> sourceFrame); + static PassRefPtr<FormState> create(PassRefPtr<HTMLFormElement>, StringPairVector& textFieldValuesToAdopt, PassRefPtr<Frame>); HTMLFormElement* form() const { return m_form.get(); } - const HashMap<String, String>& values() const { return m_values; } + const StringPairVector& textFieldValues() const { return m_textFieldValues; } Frame* sourceFrame() const { return m_sourceFrame.get(); } private: - FormState(PassRefPtr<HTMLFormElement> form, const HashMap<String, String>& values, PassRefPtr<Frame> sourceFrame); + FormState(PassRefPtr<HTMLFormElement>, StringPairVector& textFieldValuesToAdopt, PassRefPtr<Frame>); RefPtr<HTMLFormElement> m_form; - HashMap<String, String> m_values; + StringPairVector m_textFieldValues; RefPtr<Frame> m_sourceFrame; }; diff --git a/src/3rdparty/webkit/WebCore/loader/FrameLoader.cpp b/src/3rdparty/webkit/WebCore/loader/FrameLoader.cpp index e36cb5647d..7a9ef32017 100644 --- a/src/3rdparty/webkit/WebCore/loader/FrameLoader.cpp +++ b/src/3rdparty/webkit/WebCore/loader/FrameLoader.cpp @@ -1,7 +1,7 @@ /* * 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/) + * Copyright (C) 2008, 2009 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 @@ -52,10 +52,10 @@ #include "Frame.h" #include "FrameLoadRequest.h" #include "FrameLoaderClient.h" -#include "FramePrivate.h" #include "FrameTree.h" #include "FrameView.h" #include "HTMLAnchorElement.h" +#include "HTMLAppletElement.h" #include "HTMLFormElement.h" #include "HTMLFrameElement.h" #include "HTMLNames.h" @@ -81,15 +81,16 @@ #include "ResourceRequest.h" #include "ScriptController.h" #include "ScriptSourceCode.h" +#include "ScriptString.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 <wtf/CurrentTime.h> #include <wtf/StdLibExtras.h> #if ENABLE(OFFLINE_WEB_APPLICATIONS) @@ -114,68 +115,79 @@ using namespace SVGNames; #endif using namespace HTMLNames; -#if USE(LOW_BANDWIDTH_DISPLAY) -const unsigned int cMaxPendingSourceLengthInLowBandwidthDisplay = 128 * 1024; -#endif +typedef HashSet<String, CaseFoldingHash> URLSchemesMap; -typedef HashSet<String, CaseFoldingHash> LocalSchemesMap; +static URLSchemesMap& localSchemes() +{ + DEFINE_STATIC_LOCAL(URLSchemesMap, localSchemes, ()); -struct FormSubmission { - const char* action; - String url; - RefPtr<FormData> data; - 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) - { + if (localSchemes.isEmpty()) { + localSchemes.add("file"); +#if PLATFORM(MAC) + localSchemes.add("applewebdata"); +#endif +#if PLATFORM(QT) + localSchemes.add("qrc"); +#endif } -}; -struct ScheduledRedirection { - enum Type { redirection, locationChange, historyNavigation, locationChangeDuringLoad }; - Type type; - double delay; - String url; - String referrer; - int historySteps; - bool lockHistory; - bool wasUserGesture; - bool wasRefresh; + return localSchemes; +} + +static URLSchemesMap& noAccessSchemes() +{ + DEFINE_STATIC_LOCAL(URLSchemesMap, noAccessSchemes, ()); - ScheduledRedirection(double redirectDelay, const String& redirectURL, bool redirectLockHistory, bool userGesture, bool refresh) + if (noAccessSchemes.isEmpty()) + noAccessSchemes.add("data"); + + return noAccessSchemes; +} + +struct ScheduledRedirection { + enum Type { redirection, locationChange, historyNavigation, formSubmission }; + + const Type type; + const double delay; + const String url; + const String referrer; + const FrameLoadRequest frameRequest; + const RefPtr<Event> event; + const RefPtr<FormState> formState; + const int historySteps; + const bool lockHistory; + const bool lockBackForwardList; + const bool wasUserGesture; + const bool wasRefresh; + const bool wasDuringLoad; + + 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) + , wasDuringLoad(false) { + ASSERT(!url.isEmpty()); } - ScheduledRedirection(Type locationChangeType, - const String& locationChangeURL, const String& locationChangeReferrer, - bool locationChangeLockHistory, bool locationChangeWasUserGesture, bool refresh) - : type(locationChangeType) + ScheduledRedirection(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh, bool duringLoad) + : type(locationChange) , delay(0) - , url(locationChangeURL) - , referrer(locationChangeReferrer) + , url(url) + , referrer(referrer) , historySteps(0) - , lockHistory(locationChangeLockHistory) - , wasUserGesture(locationChangeWasUserGesture) + , lockHistory(lockHistory) + , lockBackForwardList(lockBackForwardList) + , wasUserGesture(wasUserGesture) , wasRefresh(refresh) + , wasDuringLoad(duringLoad) { + ASSERT(!url.isEmpty()); } explicit ScheduledRedirection(int historyNavigationSteps) @@ -183,12 +195,38 @@ struct ScheduledRedirection { , delay(0) , historySteps(historyNavigationSteps) , lockHistory(false) + , lockBackForwardList(false) , wasUserGesture(false) , wasRefresh(false) + , wasDuringLoad(false) { } -}; + ScheduledRedirection(const FrameLoadRequest& frameRequest, + bool lockHistory, bool lockBackForwardList, PassRefPtr<Event> event, PassRefPtr<FormState> formState, + bool duringLoad) + : type(formSubmission) + , delay(0) + , frameRequest(frameRequest) + , event(event) + , formState(formState) + , historySteps(0) + , lockHistory(lockHistory) + , lockBackForwardList(lockBackForwardList) + , wasUserGesture(false) + , wasRefresh(false) + , wasDuringLoad(duringLoad) + { + ASSERT(!frameRequest.isEmpty()); + ASSERT(this->formState); + } +}; + +#if ENABLE(XHTMLMP) +static const char defaultAcceptHeader[] = "application/xml,application/vnd.wap.xhtml+xml,application/xhtml+xml;profile='http://www.wapforum.org/xhtml',text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; +#else +static const char defaultAcceptHeader[] = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; +#endif static double storedTimeOfLastCompletedLoad; static FrameLoader::LocalLoadPolicy localLoadPolicy = FrameLoader::AllowLocalLoadsForLocalOnly; @@ -197,10 +235,9 @@ 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: @@ -220,6 +257,11 @@ static int numRequests(Document* document) return document->docLoader()->requestCount(); } +static inline bool canReferToParentFrameEncoding(const Frame* frame, const Frame* parentFrame) +{ + return parentFrame && parentFrame->document()->securityOrigin()->canAccess(frame->document()->securityOrigin()); +} + FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client) : m_frame(frame) , m_client(client) @@ -233,11 +275,11 @@ FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client) , m_quickRedirectComing(false) , m_sentRedirectNotification(false) , m_inStopAllLoaders(false) - , m_navigationDuringLoad(false) , m_isExecutingJavaScriptFormAction(false) , m_isRunningScript(false) , m_didCallImplicitClose(false) , m_wasUnloadEventEmitted(false) + , m_unloadEventBeingDispatched(false) , m_isComplete(false) , m_isLoadingMainResource(false) , m_cancellingWithLoadInProgress(false) @@ -257,11 +299,6 @@ FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client) #ifndef NDEBUG , m_didDispatchDidCommitLoad(false) #endif -#if USE(LOW_BANDWIDTH_DISPLAY) - , m_useLowBandwidthDisplay(true) - , m_finishedParsingDuringLowBandwidthDisplay(false) - , m_needToSwitchOutLowBandwidthDisplay(false) -#endif #if ENABLE(WML) , m_forceReloadWmlDeck(false) #endif @@ -284,7 +321,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())); @@ -314,7 +351,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()->loadFrameRequest(request, false, false, 0, 0); if (Page* page = frame->page()) page->chrome()->focus(); created = false; @@ -372,13 +409,7 @@ bool FrameLoader::canHandleRequest(const ResourceRequest& request) return m_client->canHandleRequest(request); } -void FrameLoader::changeLocation(const String& url, const String& referrer, bool lockHistory, bool userGesture, bool refresh) -{ - changeLocation(completeURL(url), referrer, lockHistory, userGesture, refresh); -} - - -void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool userGesture, bool refresh) +void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool userGesture, bool refresh) { RefPtr<Frame> protect(m_frame); @@ -387,55 +418,41 @@ 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) -{ - FrameLoadRequest copy = request; - if (copy.resourceRequest().httpReferrer().isEmpty()) - copy.resourceRequest().setHTTPReferrer(m_outgoingReferrer); - addHTTPOriginIfNeeded(copy.resourceRequest(), outgoingOrigin()); - - loadFrameRequestWithFormAndValues(copy, lockHistory, event, 0, HashMap<String, String>()); -} - -void FrameLoader::urlSelected(const ResourceRequest& request, const String& _target, Event* triggeringEvent, bool lockHistory, bool userGesture) +void FrameLoader::urlSelected(const ResourceRequest& request, const String& passedTarget, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, bool userGesture) { if (executeIfJavaScriptURL(request.url(), userGesture, false)) return; - String target = _target; - if (target.isEmpty() && m_frame->document()) + String target = passedTarget; + if (target.isEmpty()) target = m_frame->document()->baseTarget(); FrameLoadRequest frameRequest(request, target); - urlSelected(frameRequest, triggeringEvent, lockHistory); + if (frameRequest.resourceRequest().httpReferrer().isEmpty()) + frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer); + addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin()); + + loadFrameRequest(frameRequest, lockHistory, lockBackForwardList, triggeringEvent, 0); } bool FrameLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName) { -#if USE(LOW_BANDWIDTH_DISPLAY) - // don't create sub-frame during low bandwidth display - if (frame()->document()->inLowBandwidthDisplay()) { - m_needToSwitchOutLowBandwidthDisplay = true; - return false; - } -#endif - // Support for <frame src="javascript:string"> KURL scriptURL; KURL url; - if (protocolIs(urlString, "javascript")) { - scriptURL = KURL(urlString); + if (protocolIsJavaScript(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); @@ -466,8 +483,7 @@ Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL } bool hideReferrer = shouldHideReferrer(url, referrer); - RefPtr<Frame> frame = m_client->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, - allowsScrolling, marginWidth, marginHeight); + RefPtr<Frame> frame = m_client->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, allowsScrolling, marginWidth, marginHeight); if (!frame) { checkCallImplicitClose(); @@ -497,71 +513,69 @@ Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL return frame.get(); } -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()); -} - 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, + bool lockHistory, bool lockBackForwardList, PassRefPtr<Event> event, PassRefPtr<FormState> formState) { + ASSERT(action); + ASSERT(strcmp(action, "GET") == 0 || strcmp(action, "POST") == 0); ASSERT(formData); + ASSERT(formState); + ASSERT(formState->sourceFrame() == m_frame); if (!m_frame->page()) return; KURL u = completeURL(url.isNull() ? "" : url); - // FIXME: Do we really need to special-case an empty URL? - // Would it be better to just go on with the form submisson and let the I/O fail? if (u.isEmpty()) return; - if (u.protocolIs("javascript")) { + if (protocolIsJavaScript(u)) { m_isExecutingJavaScriptFormAction = true; executeIfJavaScriptURL(u, false, false); m_isExecutingJavaScriptFormAction = false; return; } - if (m_isRunningScript) { - if (m_deferredFormSubmission) - return; - m_deferredFormSubmission.set(new FormSubmission(action, url, formData, target, - contentType, boundary, event)); + FrameLoadRequest frameRequest; + + String targetOrBaseTarget = target.isEmpty() ? m_frame->document()->baseTarget() : target; + Frame* targetFrame = findFrameForNavigation(targetOrBaseTarget); + if (!targetFrame) { + targetFrame = m_frame; + frameRequest.setFrameName(targetOrBaseTarget); + } + if (!targetFrame->page()) return; + + // 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, nor do we want to submit a single + // form more than once. This flag prevents these from happening; not sure how other browsers prevent this. + // The flag is reset in each time we start handle a new mouse or key down event, and + // also in setView since this part may get reused for a page from the back/forward cache. + // The form multi-submit logic here is only needed when we are submitting a form that affects this frame. + + // FIXME: Frame targeting is only one of the ways the submission could end up doing something other + // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly + // needed any more now that we reset m_submittedFormURL on each mouse or key down event. + + if (m_frame->tree()->isDescendantOf(targetFrame)) { + if (m_submittedFormURL == u) + return; + m_submittedFormURL = u; } formData->generateFiles(m_frame->page()->chrome()->client()); - FrameLoadRequest frameRequest; - if (!m_outgoingReferrer.isEmpty()) frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer); - frameRequest.setFrameName(target.isEmpty() ? m_frame->document()->baseTarget() : target); - - // Handle mailto: forms - bool isMailtoForm = equalIgnoringCase(u.protocol(), "mailto"); - if (isMailtoForm && strcmp(action, "GET") != 0) { - // Append body= for POST mailto, replace the whole query string for GET one. - String body = formData->flattenToString(); - String query = u.query(); - if (!query.isEmpty()) - query.append('&'); - u.setQuery(query + body); - } - - if (strcmp(action, "GET") == 0) { + if (strcmp(action, "GET") == 0) u.setQuery(formData->flattenToString()); - } else { - if (!isMailtoForm) - frameRequest.resourceRequest().setHTTPBody(formData.get()); + else { frameRequest.resourceRequest().setHTTPMethod("POST"); + frameRequest.resourceRequest().setHTTPBody(formData); // construct some user headers if necessary if (contentType.isNull() || contentType == "application/x-www-form-urlencoded") @@ -573,10 +587,23 @@ void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<F frameRequest.resourceRequest().setURL(u); addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin()); - submitForm(frameRequest, event); + // Navigation of a subframe during loading of the main frame does not create a new back/forward item. + // Strangely, we only implement this rule for form submission; time will tell if we need it for other types of navigation. + // The definition of "during load" is any time before the load event has been handled. + // See https://bugs.webkit.org/show_bug.cgi?id=14957 for the original motivation for this. + if (Page* targetPage = targetFrame->page()) { + Frame* mainFrame = targetPage->mainFrame(); + if (mainFrame != targetFrame) { + Document* document = mainFrame->document(); + if (!mainFrame->loader()->isComplete() || document && document->processingLoadEvent()) + lockBackForwardList = true; + } + } + + targetFrame->loader()->scheduleFormSubmission(frameRequest, lockHistory, lockBackForwardList, event, formState); } -void FrameLoader::stopLoading(bool sendUnload) +void FrameLoader::stopLoading(bool sendUnload, DatabasePolicy databasePolicy) { if (m_frame->document() && m_frame->document()->tokenizer()) m_frame->document()->tokenizer()->stopParsing(); @@ -587,18 +614,19 @@ void FrameLoader::stopLoading(bool sendUnload) Node* currentFocusedNode = m_frame->document()->focusedNode(); if (currentFocusedNode) currentFocusedNode->aboutToUnload(); - m_frame->document()->dispatchWindowEvent(eventNames().unloadEvent, false, false); + m_unloadEventBeingDispatched = true; + if (m_frame->domWindow()) + m_frame->domWindow()->dispatchUnloadEvent(); + m_unloadEventBeingDispatched = false; if (m_frame->document()) - m_frame->document()->updateRendering(); + m_frame->document()->updateStyleIfNeeded(); m_wasUnloadEventEmitted = true; - if (m_frame->eventHandler()->pendingFrameUnloadEventCount()) - m_frame->eventHandler()->clearPendingFrameUnloadEventCount(); - if (m_frame->eventHandler()->pendingFrameBeforeUnloadEventCount()) - m_frame->eventHandler()->clearPendingFrameBeforeUnloadEventCount(); } } + + // Dispatching the unload event could have made m_frame->document() null. if (m_frame->document() && !m_frame->document()->inPageCache()) - m_frame->document()->removeAllEventListenersFromAllNodes(); + m_frame->document()->removeAllEventListeners(); } m_isComplete = true; // to avoid calling completed() in finishedParsing() (David) @@ -617,7 +645,8 @@ void FrameLoader::stopLoading(bool sendUnload) cache()->loader()->cancelRequests(docLoader); #if ENABLE(DATABASE) - doc->stopDatabases(); + if (databasePolicy == DatabasePolicyStop) + doc->stopDatabases(); #endif } @@ -626,14 +655,6 @@ void FrameLoader::stopLoading(bool sendUnload) child->loader()->stopLoading(sendUnload); cancelRedirection(); - -#if USE(LOW_BANDWIDTH_DISPLAY) - if (m_frame->document() && m_frame->document()->inLowBandwidthDisplay()) { - // Since loading is forced to stop, reset the state without really switching. - m_needToSwitchOutLowBandwidthDisplay = false; - switchOutLowBandwidthDisplayIfReady(); - } -#endif } void FrameLoader::stop() @@ -642,16 +663,10 @@ void FrameLoader::stop() // The frame's last ref may be removed and it will be deleted by checkCompleted(). RefPtr<Frame> protector(m_frame); - if (m_frame->document()) { - if (m_frame->document()->tokenizer()) - m_frame->document()->tokenizer()->stopParsing(); - m_frame->document()->finishParsing(); - } else - // WebKit partially uses WebCore when loading non-HTML docs. In these cases doc==nil, but - // WebCore is enough involved that we need to checkCompleted() in order for m_bComplete to - // become true. An example is when a subframe is a pure text doc, and that subframe is the - // last one to complete. - checkCompleted(); + if (m_frame->document()->tokenizer()) + m_frame->document()->tokenizer()->stopParsing(); + m_frame->document()->finishParsing(); + if (m_iconLoader) m_iconLoader->stopLoading(); } @@ -680,11 +695,11 @@ KURL FrameLoader::iconURL() return KURL(); // If we have an iconURL from a Link element, return that - if (m_frame->document() && !m_frame->document()->iconURL().isEmpty()) + if (!m_frame->document()->iconURL().isEmpty()) return KURL(m_frame->document()->iconURL()); // Don't return a favicon iconURL unless we're http or https - if (!m_URL.protocolIs("http") && !m_URL.protocolIs("https")) + if (!m_URL.protocolInHTTPFamily()) return KURL(); KURL url; @@ -698,10 +713,11 @@ KURL FrameLoader::iconURL() bool FrameLoader::didOpenURL(const KURL& url) { - if (m_scheduledRedirection && m_scheduledRedirection->type == ScheduledRedirection::locationChangeDuringLoad) + if (m_scheduledRedirection && m_scheduledRedirection->wasDuringLoad) { // A redirect was scheduled before the document was created. // This can happen when one frame changes another frame's location. return false; + } cancelRedirection(); m_frame->editor()->clearLastEditCommand(); @@ -711,11 +727,15 @@ bool FrameLoader::didOpenURL(const KURL& url) m_isLoadingMainResource = true; m_didCallImplicitClose = false; - m_frame->setJSStatusBarText(String()); - m_frame->setJSDefaultStatusBarText(String()); - + // If we are still in the process of initializing an empty document then + // its frame is not in a consistent state for rendering, so avoid setJSStatusBarText + // since it may cause clients to attempt to render the frame. + if (!m_creatingInitialEmptyDocument) { + m_frame->setJSStatusBarText(String()); + m_frame->setJSDefaultStatusBarText(String()); + } m_URL = url; - if ((m_URL.protocolIs("http") || m_URL.protocolIs("https")) && !m_URL.host().isEmpty() && m_URL.path().isEmpty()) + if (m_URL.protocolInHTTPFamily() && !m_URL.host().isEmpty() && m_URL.path().isEmpty()) m_URL.setPath("/"); m_workingURL = m_URL; @@ -743,9 +763,12 @@ void FrameLoader::didExplicitOpen() bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool replaceDocument) { - if (!url.protocolIs("javascript")) + if (!protocolIsJavaScript(url)) return false; + if (m_frame->page() && !m_frame->page()->javaScriptURLsAreAllowed()) + return true; + String script = decodeURLEscapeSequences(url.string().substring(strlen("javascript:"))); ScriptValue result = executeScript(script, userGesture); @@ -754,13 +777,13 @@ bool FrameLoader::executeIfJavaScriptURL(const KURL& url, bool userGesture, bool return true; SecurityOrigin* currentSecurityOrigin = 0; - if (m_frame->document()) - currentSecurityOrigin = m_frame->document()->securityOrigin(); + currentSecurityOrigin = m_frame->document()->securityOrigin(); // FIXME: We should always replace the document, but doing so // synchronously can cause crashes: // http://bugs.webkit.org/show_bug.cgi?id=16782 if (replaceDocument) { + stopAllLoaders(); begin(m_URL, true, currentSecurityOrigin); write(scriptResult); end(); @@ -786,8 +809,7 @@ ScriptValue FrameLoader::executeScript(const ScriptSourceCode& sourceCode) if (!wasRunningScript) { m_isRunningScript = false; - submitFormAgain(); - Document::updateDocumentsRendering(); + Document::updateStyleForAllDocuments(); } return result; @@ -812,7 +834,7 @@ void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects) return; m_needsClear = false; - if (m_frame->document() && !m_frame->document()->inPageCache()) { + if (!m_frame->document()->inPageCache()) { m_frame->document()->cancelParsing(); m_frame->document()->stopActiveDOMObjects(); if (m_frame->document()->attached()) { @@ -866,10 +888,12 @@ void FrameLoader::receivedFirstData() dispatchDidCommitLoad(); dispatchWindowObjectAvailable(); - String ptitle = m_documentLoader->title(); - // If we have a title let the WebView know about it. - if (!ptitle.isNull()) - m_client->dispatchDidReceiveTitle(ptitle); + if (m_documentLoader) { + String ptitle = m_documentLoader->title(); + // If we have a title let the WebView know about it. + if (!ptitle.isNull()) + m_client->dispatchDidReceiveTitle(ptitle); + } m_workingURL = KURL(); @@ -909,12 +933,18 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) // might destroy the document that owns it. RefPtr<SecurityOrigin> forcedSecurityOrigin = origin; - bool resetScripting = !(m_isDisplayingInitialEmptyDocument && m_frame->document() && m_frame->document()->securityOrigin()->isSecureTransitionTo(url)); + RefPtr<Document> document; + + // Create a new document before clearing the frame, because it may need to inherit an aliased security context. + if (!m_isDisplayingInitialEmptyDocument && m_client->shouldUsePluginDocument(m_responseMIMEType)) + document = PluginDocument::create(m_frame); + else + document = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode()); + + bool resetScripting = !(m_isDisplayingInitialEmptyDocument && m_frame->document()->securityOrigin()->isSecureTransitionTo(url)); clear(resetScripting, resetScripting); if (resetScripting) m_frame->script()->updatePlatformScriptObjects(); - if (dispatch) - dispatchWindowObjectAvailable(); m_needsClear = true; m_isComplete = false; @@ -929,14 +959,11 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) m_outgoingReferrer = ref.string(); m_URL = url; - 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); + if (dispatch) + dispatchWindowObjectAvailable(); + document->setURL(m_URL); if (m_decoder) document->setDecoder(m_decoder.get()); @@ -946,7 +973,7 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) m_frame->domWindow()->setURL(document->url()); m_frame->domWindow()->setSecurityOrigin(document->securityOrigin()); - updatePolicyBaseURL(); + updateFirstPartyForCookies(); Settings* settings = document->settings(); document->docLoader()->setAutoLoadImages(settings && settings->loadsImagesAutomatically()); @@ -969,18 +996,6 @@ void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) if (m_frame->view()) m_frame->view()->setContentsSize(IntSize()); - -#if USE(LOW_BANDWIDTH_DISPLAY) - // Low bandwidth display is a first pass display without external resources - // used to give an instant visual feedback. We currently only enable it for - // HTML documents in the top frame. - if (document->isHTMLDocument() && !m_frame->tree()->parent() && m_useLowBandwidthDisplay) { - m_pendingSourceInLowBandwidthDisplay = String(); - m_finishedParsingDuringLowBandwidthDisplay = false; - m_needToSwitchOutLowBandwidthDisplay = false; - document->setLowBandwidthDisplay(true); - } -#endif } void FrameLoader::write(const char* str, int len, bool flush) @@ -999,12 +1014,28 @@ void FrameLoader::write(const char* str, int len, bool flush) } if (!m_decoder) { - Settings* settings = m_frame->settings(); - m_decoder = TextResourceDecoder::create(m_responseMIMEType, settings ? settings->defaultTextEncodingName() : String()); - if (m_encoding.isEmpty()) { + if (Settings* settings = m_frame->settings()) { + m_decoder = TextResourceDecoder::create(m_responseMIMEType, + settings->defaultTextEncodingName(), + settings->usesEncodingDetector()); Frame* parentFrame = m_frame->tree()->parent(); - if (parentFrame && parentFrame->document()->securityOrigin()->canAccess(m_frame->document()->securityOrigin())) - m_decoder->setEncoding(parentFrame->document()->inputEncoding(), TextResourceDecoder::DefaultEncoding); + // Set the hint encoding to the parent frame encoding only if + // the parent and the current frames share the security origin. + // We impose this condition because somebody can make a child frame + // containing a carefully crafted html/javascript in one encoding + // that can be mistaken for hintEncoding (or related encoding) by + // an auto detector. When interpreted in the latter, it could be + // an attack vector. + // FIXME: This might be too cautious for non-7bit-encodings and + // we may consider relaxing this later after testing. + if (canReferToParentFrameEncoding(m_frame, parentFrame)) + m_decoder->setHintEncoding(parentFrame->document()->decoder()); + } else + m_decoder = TextResourceDecoder::create(m_responseMIMEType, String()); + Frame* parentFrame = m_frame->tree()->parent(); + if (m_encoding.isEmpty()) { + if (canReferToParentFrameEncoding(m_frame, parentFrame)) + m_decoder->setEncoding(parentFrame->document()->inputEncoding(), TextResourceDecoder::EncodingFromParentFrame); } else { m_decoder->setEncoding(m_encoding, m_encodingWasChosenByUser ? TextResourceDecoder::UserChosenEncoding : TextResourceDecoder::EncodingFromHTTPHeader); @@ -1018,11 +1049,6 @@ void FrameLoader::write(const char* str, int len, bool flush) if (decoded.isEmpty()) return; -#if USE(LOW_BANDWIDTH_DISPLAY) - if (m_frame->document()->inLowBandwidthDisplay()) - m_pendingSourceInLowBandwidthDisplay.append(decoded); -#endif - if (!m_receivedData) { m_receivedData = true; if (m_decoder->encoding().usesVisualOrdering()) @@ -1058,7 +1084,7 @@ void FrameLoader::end() void FrameLoader::endIfNotLoadingMainResource() { - if (m_isLoadingMainResource || !m_frame->page()) + if (m_isLoadingMainResource || !m_frame->page() || !m_frame->document()) return; // http://bugs.webkit.org/show_bug.cgi?id=10854 @@ -1067,21 +1093,8 @@ void FrameLoader::endIfNotLoadingMainResource() RefPtr<Frame> protector(m_frame); // make sure nothing's left in there - if (m_frame->document()) { - write(0, 0, true); - m_frame->document()->finishParsing(); -#if USE(LOW_BANDWIDTH_DISPLAY) - if (m_frame->document()->inLowBandwidthDisplay()) { - m_finishedParsingDuringLowBandwidthDisplay = true; - switchOutLowBandwidthDisplayIfReady(); - } -#endif - } else - // WebKit partially uses WebCore when loading non-HTML docs. In these cases doc==nil, but - // WebCore is enough involved that we need to checkCompleted() in order for m_bComplete to - // become true. An example is when a subframe is a pure text doc, and that subframe is the - // last one to complete. - checkCompleted(); + write(0, 0, true); + m_frame->document()->finishParsing(); } void FrameLoader::iconLoadDecisionAvailable() @@ -1165,23 +1178,6 @@ bool FrameLoader::allowSubstituteDataAccessToLocal() return localLoadPolicy != FrameLoader::AllowLocalLoadsForLocalOnly; } -static LocalSchemesMap& localSchemes() -{ - DEFINE_STATIC_LOCAL(LocalSchemesMap, localSchemes, ()); - - if (localSchemes.isEmpty()) { - localSchemes.add("file"); -#if PLATFORM(MAC) - localSchemes.add("applewebdata"); -#endif -#if PLATFORM(QT) - localSchemes.add("qrc"); -#endif - } - - return localSchemes; -} - void FrameLoader::commitIconURLToIconDatabase(const KURL& icon) { ASSERT(iconDatabase()); @@ -1193,14 +1189,11 @@ void FrameLoader::commitIconURLToIconDatabase(const KURL& icon) void FrameLoader::restoreDocumentState() { Document* doc = m_frame->document(); - if (!doc) - return; HistoryItem* itemToRestore = 0; switch (loadType()) { case FrameLoadTypeReload: - case FrameLoadTypeReloadAllowingStaleData: case FrameLoadTypeReloadFromOrigin: case FrameLoadTypeSame: case FrameLoadTypeReplace: @@ -1208,14 +1201,15 @@ void FrameLoader::restoreDocumentState() case FrameLoadTypeBack: case FrameLoadTypeForward: case FrameLoadTypeIndexedBackForward: - case FrameLoadTypeRedirectWithLockedHistory: + case FrameLoadTypeRedirectWithLockedBackForwardList: case FrameLoadTypeStandard: 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()); } @@ -1225,7 +1219,7 @@ void FrameLoader::gotoAnchor() // OTOH If CSS target was set previously, we want to set it to 0, recalc // and possibly repaint because :target pseudo class may have been // set (see bug 11321). - if (!m_URL.hasRef() && !(m_frame->document() && m_frame->document()->getCSSTarget())) + if (!m_URL.hasRef() && !m_frame->document()->cssTarget()) return; String ref = m_URL.ref(); @@ -1263,12 +1257,14 @@ void FrameLoader::finishedParsing() void FrameLoader::loadDone() { - if (m_frame->document()) - checkCompleted(); + checkCompleted(); } void FrameLoader::checkCompleted() { + if (m_frame->view()) + m_frame->view()->checkStopDelayingDeferredRepaints(); + // Any frame that hasn't completed yet? for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) if (!child->loader()->m_isComplete) @@ -1279,19 +1275,12 @@ void FrameLoader::checkCompleted() return; // Are we still parsing? - if (m_frame->document() && m_frame->document()->parsing()) + if (m_frame->document()->parsing()) return; // Still waiting for images/scripts? - if (m_frame->document()) - if (numRequests(m_frame->document())) - return; - -#if USE(LOW_BANDWIDTH_DISPLAY) - // as switch will be called, don't complete yet - if (m_frame->document() && m_frame->document()->inLowBandwidthDisplay() && m_needToSwitchOutLowBandwidthDisplay) + if (numRequests(m_frame->document())) return; -#endif // OK, completed. m_isComplete = true; @@ -1335,7 +1324,7 @@ void FrameLoader::scheduleCheckLoadComplete() void FrameLoader::checkCallImplicitClose() { - if (m_didCallImplicitClose || !m_frame->document() || m_frame->document()->parsing()) + if (m_didCallImplicitClose || m_frame->document()->parsing()) return; for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) @@ -1344,8 +1333,7 @@ void FrameLoader::checkCallImplicitClose() m_didCallImplicitClose = true; m_wasUnloadEventEmitted = false; - if (m_frame->document()) - m_frame->document()->implicitClose(); + m_frame->document()->implicitClose(); } KURL FrameLoader::baseURL() const @@ -1354,12 +1342,6 @@ KURL FrameLoader::baseURL() const return m_frame->document()->baseURL(); } -String FrameLoader::baseTarget() const -{ - ASSERT(m_frame->document()); - return m_frame->document()->baseTarget(); -} - KURL FrameLoader::completeURL(const String& url) { ASSERT(m_frame->document()); @@ -1374,21 +1356,27 @@ void FrameLoader::scheduleHTTPRedirection(double delay, const String& url) if (!m_frame->page()) return; + if (url.isEmpty()) + return; + // We want a new history item if the refresh timeout is > 1 second. if (!m_scheduledRedirection || delay <= m_scheduledRedirection->delay) - scheduleRedirection(new ScheduledRedirection(delay, url, delay <= 1, false, false)); + scheduleRedirection(new ScheduledRedirection(delay, url, true, delay <= 1, false, false)); } -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; + if (url.isEmpty()) + return; + // If the URL we're going to navigate to is the same as the current one, except for the // fragment part, we don't need to schedule the location change. KURL parsedURL(url); if (parsedURL.hasRef() && equalIgnoringRef(m_URL, parsedURL)) { - changeLocation(url, referrer, lockHistory, wasUserGesture); + changeLocation(completeURL(url), referrer, lockHistory, lockBackForwardList, wasUserGesture); return; } @@ -1396,18 +1384,23 @@ void FrameLoader::scheduleLocationChange(const String& url, const String& referr // This may happen when a frame changes the location of another frame. bool duringLoad = !m_committedFirstRealDocumentLoad; - // If a redirect was scheduled during a load, then stop the current load. - // Otherwise when the current load transitions from a provisional to a - // committed state, pending redirects may be cancelled. - if (duringLoad) { - if (m_provisionalDocumentLoader) - m_provisionalDocumentLoader->stopLoading(); - stopLoading(true); - } + scheduleRedirection(new ScheduledRedirection(url, referrer, lockHistory, lockBackForwardList, wasUserGesture, false, duringLoad)); +} + +void FrameLoader::scheduleFormSubmission(const FrameLoadRequest& frameRequest, + bool lockHistory, bool lockBackForwardList, PassRefPtr<Event> event, PassRefPtr<FormState> formState) +{ + ASSERT(m_frame->page()); + ASSERT(!frameRequest.isEmpty()); - ScheduledRedirection::Type type = duringLoad - ? ScheduledRedirection::locationChangeDuringLoad : ScheduledRedirection::locationChange; - scheduleRedirection(new ScheduledRedirection(type, url, referrer, lockHistory, wasUserGesture, false)); + // FIXME: Do we need special handling for form submissions where the URL is the same + // as the current one except for the fragment part? See scheduleLocationChange above. + + // Handle a location change of a page with no document as a special case. + // This may happen when a frame changes the location of another frame. + bool duringLoad = !m_committedFirstRealDocumentLoad; + + scheduleRedirection(new ScheduledRedirection(frameRequest, lockHistory, lockBackForwardList, event, formState, duringLoad)); } void FrameLoader::scheduleRefresh(bool wasUserGesture) @@ -1415,19 +1408,10 @@ void FrameLoader::scheduleRefresh(bool wasUserGesture) if (!m_frame->page()) return; - // Handle a location change of a page with no document as a special case. - // This may happen when a frame requests a refresh of another frame. - bool duringLoad = !m_frame->document(); - - // If a refresh was scheduled during a load, then stop the current load. - // Otherwise when the current load transitions from a provisional to a - // committed state, pending redirects may be cancelled. - if (duringLoad) - stopLoading(true); + if (m_URL.isEmpty()) + return; - ScheduledRedirection::Type type = duringLoad - ? ScheduledRedirection::locationChangeDuringLoad : ScheduledRedirection::locationChange; - scheduleRedirection(new ScheduledRedirection(type, m_URL.string(), m_outgoingReferrer, true, wasUserGesture, true)); + scheduleRedirection(new ScheduledRedirection(m_URL.string(), m_outgoingReferrer, true, true, wasUserGesture, true, false)); } bool FrameLoader::isLocationChange(const ScheduledRedirection& redirection) @@ -1437,7 +1421,7 @@ bool FrameLoader::isLocationChange(const ScheduledRedirection& redirection) return false; case ScheduledRedirection::historyNavigation: case ScheduledRedirection::locationChange: - case ScheduledRedirection::locationChangeDuringLoad: + case ScheduledRedirection::formSubmission: return true; } ASSERT_NOT_REACHED(); @@ -1455,18 +1439,6 @@ void FrameLoader::scheduleHistoryNavigation(int steps) return; } - // If the steps to navigate is not zero (which needs to force a reload), and if we think the navigation is going to be a fragment load - // (when the URL we're going to navigate to is the same as the current one, except for the fragment part - but not exactly the same because that's a reload), - // then we don't need to schedule the navigation. - if (steps != 0) { - KURL destination = historyURL(steps); - // FIXME: This doesn't seem like a reliable way to tell whether or not the load will be a fragment load. - if (equalIgnoringRef(m_URL, destination) && m_URL != destination) { - goBackOrForward(steps); - return; - } - } - scheduleRedirection(new ScheduledRedirection(steps)); } @@ -1509,62 +1481,55 @@ void FrameLoader::redirectionTimerFired(Timer<FrameLoader>*) switch (redirection->type) { case ScheduledRedirection::redirection: case ScheduledRedirection::locationChange: - case ScheduledRedirection::locationChangeDuringLoad: - changeLocation(redirection->url, redirection->referrer, - redirection->lockHistory, redirection->wasUserGesture, redirection->wasRefresh); + changeLocation(KURL(redirection->url), redirection->referrer, + 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, // in both IE and NS (but not in Mozilla). We can't easily do that. goBackOrForward(redirection->historySteps); return; + case ScheduledRedirection::formSubmission: + // The submitForm function will find a target frame before using the redirection timer. + // Now that the timer has fired, we need to repeat the security check which normally is done when + // selecting a target, in case conditions have changed. Other code paths avoid this by targeting + // without leaving a time window. If we fail the check just silently drop the form submission. + if (!redirection->formState->sourceFrame()->loader()->shouldAllowNavigation(m_frame)) + return; + loadFrameRequest(redirection->frameRequest, redirection->lockHistory, redirection->lockBackForwardList, + redirection->event, redirection->formState); + return; } ASSERT_NOT_REACHED(); } -/* - In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree. - The item that was the target of the user's navigation is designated as the "targetItem". - When this method is called with doClip=YES we're able to create the whole tree except for the target's children, - which will be loaded in the future. That part of the tree will be filled out as the child loads are committed. -*/ void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, Frame* childFrame) { ASSERT(childFrame); + HistoryItem* parentItem = currentHistoryItem(); FrameLoadType loadType = this->loadType(); - FrameLoadType childLoadType = FrameLoadTypeRedirectWithLockedHistory; + FrameLoadType childLoadType = FrameLoadTypeRedirectWithLockedBackForwardList; KURL workingURL = url; - // If we're moving in the backforward list, we might want to replace the content + // If we're moving in the back/forward list, we might want to replace the content // of this child frame with whatever was there at that point. - // Reload will maintain the frame contents, LoadSame will not. - if (parentItem && parentItem->children().size() && - (isBackForwardLoadType(loadType) || loadType == FrameLoadTypeReloadAllowingStaleData)) - { - HistoryItem* childItem = parentItem->childItemWithName(childFrame->tree()->name()); + if (parentItem && parentItem->children().size() && isBackForwardLoadType(loadType)) { + HistoryItem* childItem = parentItem->childItemWithTarget(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()->m_provisionalHistoryItem = childItem; } } @@ -1573,7 +1538,7 @@ void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, if (subframeArchive) childFrame->loader()->loadArchive(subframeArchive.release()); else - childFrame->loader()->loadURL(workingURL, referer, String(), childLoadType, 0, 0); + childFrame->loader()->loadURL(workingURL, referer, String(), false, childLoadType, 0, 0); } void FrameLoader::loadArchive(PassRefPtr<Archive> prpArchive) @@ -1651,12 +1616,10 @@ bool FrameLoader::gotoAnchor(const String& name) // We need to update the layout before scrolling, otherwise we could // really mess things up if an anchor scroll comes at a bad moment. - if (m_frame->document()) { - m_frame->document()->updateRendering(); - // Only do a layout if changes have occurred that make it necessary. - if (m_frame->view() && m_frame->contentRenderer() && m_frame->contentRenderer()->needsLayout()) - m_frame->view()->layout(); - } + m_frame->document()->updateStyleIfNeeded(); + // Only do a layout if changes have occurred that make it necessary. + if (m_frame->view() && m_frame->contentRenderer() && m_frame->contentRenderer()->needsLayout()) + m_frame->view()->layout(); // Scroll nested layers and frames to reveal the anchor. // Align to the top and to the closest side (this matches other browsers). @@ -1668,8 +1631,11 @@ bool FrameLoader::gotoAnchor(const String& name) renderer = anchorNode->renderer(); rect = anchorNode->getRect(); } - if (renderer) - renderer->enclosingLayer()->scrollRectToVisible(rect, true, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways); + if (renderer) { + renderer->enclosingLayer()->scrollRectToVisible(rect, true, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways); + if (m_frame->view()) + m_frame->view()->setLockedToAnchor(true); + } return true; } @@ -1680,14 +1646,6 @@ bool FrameLoader::requestObject(RenderPart* renderer, const String& url, const A if (url.isEmpty() && mimeType.isEmpty()) return false; -#if USE(LOW_BANDWIDTH_DISPLAY) - // don't care object during low bandwidth display - if (frame()->document()->inLowBandwidthDisplay()) { - m_needToSwitchOutLowBandwidthDisplay = true; - return false; - } -#endif - KURL completedURL; if (!url.isEmpty()) completedURL = completeURL(url); @@ -1731,24 +1689,39 @@ bool FrameLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin; } +static HTMLPlugInElement* toPlugInElement(Node* node) +{ + if (!node) + return 0; + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + ASSERT(node->hasTagName(objectTag) || node->hasTagName(embedTag) + || node->hasTagName(videoTag) || node->hasTagName(audioTag) + || node->hasTagName(appletTag)); +#else + ASSERT(node->hasTagName(objectTag) || node->hasTagName(embedTag) + || node->hasTagName(appletTag)); +#endif + + return static_cast<HTMLPlugInElement*>(node); +} + bool FrameLoader::loadPlugin(RenderPart* renderer, const KURL& url, const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback) { Widget* widget = 0; if (renderer && !useFallback) { - Element* pluginElement = 0; - if (renderer->node() && renderer->node()->isElementNode()) - pluginElement = static_cast<Element*>(renderer->node()); + HTMLPlugInElement* element = toPlugInElement(renderer->node()); if (!canLoad(url, String(), frame()->document())) { FrameLoader::reportLocalLoadFailed(m_frame, url.string()); return false; } - widget = m_client->createPlugin(IntSize(renderer->contentWidth(), renderer->contentHeight()), - pluginElement, url, paramNames, paramValues, mimeType, - m_frame->document()->isPluginDocument()); + widget = m_client->createPlugin(IntSize(renderer->contentWidth(), renderer->contentHeight()), + element, url, paramNames, paramValues, mimeType, + m_frame->document()->isPluginDocument() && !m_containsPlugIns); if (widget) { renderer->setWidget(widget); m_containsPlugIns = true; @@ -1758,22 +1731,6 @@ bool FrameLoader::loadPlugin(RenderPart* renderer, const KURL& url, const String return widget != 0; } -void FrameLoader::clearRecordedFormValues() -{ - m_formAboutToBeSubmitted = 0; - m_formValuesAboutToBeSubmitted.clear(); -} - -void FrameLoader::setFormAboutToBeSubmitted(PassRefPtr<HTMLFormElement> element) -{ - m_formAboutToBeSubmitted = element; -} - -void FrameLoader::recordFormValue(const String& name, const String& value) -{ - m_formValuesAboutToBeSubmitted.set(name, value); -} - void FrameLoader::parentCompleted() { if (m_scheduledRedirection && !m_redirectionTimer.isActive()) @@ -1787,10 +1744,7 @@ String FrameLoader::outgoingReferrer() const String FrameLoader::outgoingOrigin() const { - if (m_frame->document()) - return m_frame->document()->securityOrigin()->toString(); - - return SecurityOrigin::createEmpty()->toString(); + return m_frame->document()->securityOrigin()->toString(); } Frame* FrameLoader::opener() @@ -1831,17 +1785,7 @@ void FrameLoader::handleFallbackContent() } void FrameLoader::provisionalLoadStarted() -{ - Page* page = m_frame->page(); - - // this is used to update the current history item - // in the event of a navigation aytime during loading - m_navigationDuringLoad = false; - if (page) { - Document *document = page->mainFrame()->document(); - m_navigationDuringLoad = !page->mainFrame()->loader()->isComplete() || (document && document->processingLoadEvent()); - } - +{ m_firstLayoutDone = false; cancelRedirection(true); m_client->provisionalLoadStarted(); @@ -1849,20 +1793,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 -} - -void FrameLoader::didNotOpenURL(const KURL& url) -{ - if (m_submittedFormURL == url) - m_submittedFormURL = KURL(); + 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::resetMultipleFormSubmissionProtection() @@ -1886,70 +1820,212 @@ 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. && !m_containsPlugIns && !m_URL.protocolIs("https") - && m_frame->document() - && !m_frame->document()->hasWindowEventListener(eventNames().unloadEvent) + && (!m_frame->domWindow() || !m_frame->domWindow()->hasEventListener(eventNames().unloadEvent)) #if ENABLE(DATABASE) && !m_frame->document()->hasOpenDatabases() #endif && !m_frame->document()->usingGeolocation() - && m_frame->page() - && m_frame->page()->backForwardList()->enabled() - && m_frame->page()->backForwardList()->capacity() > 0 - && m_frame->page()->settings()->usesPageCache() && m_currentHistoryItem - && !isQuickRedirectComing() - && loadType != FrameLoadTypeReload - && loadType != FrameLoadTypeReloadAllowingStaleData - && loadType != FrameLoadTypeReloadFromOrigin - && loadType != FrameLoadTypeSame + && !m_quickRedirectComing && !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() ; } -void FrameLoader::updatePolicyBaseURL() +bool FrameLoader::canCachePage() { - if (m_frame->tree()->parent() && m_frame->tree()->parent()->document()) - setPolicyBaseURL(m_frame->tree()->parent()->document()->policyBaseURL()); +#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 - setPolicyBaseURL(m_URL); + 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->domWindow() && m_frame->domWindow()->hasEventListener(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 (m_quickRedirectComing) + { 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::updateFirstPartyForCookies() +{ + if (m_frame->tree()->parent()) + setFirstPartyForCookies(m_frame->tree()->parent()->document()->firstPartyForCookies()); + else + setFirstPartyForCookies(m_URL); } -void FrameLoader::setPolicyBaseURL(const KURL& url) +void FrameLoader::setFirstPartyForCookies(const KURL& url) { - if (m_frame->document()) - m_frame->document()->setPolicyBaseURL(url); + m_frame->document()->setFirstPartyForCookies(url); for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - child->loader()->setPolicyBaseURL(url); + child->loader()->setFirstPartyForCookies(url); } // This does the same kind of work that didOpenURL does, except it relies on the fact @@ -1979,6 +2055,15 @@ void FrameLoader::scheduleRedirection(ScheduledRedirection* redirection) { ASSERT(m_frame->page()); + // If a redirect was scheduled during a load, then stop the current load. + // Otherwise when the current load transitions from a provisional to a + // committed state, pending redirects may be cancelled. + if (redirection->wasDuringLoad) { + if (m_provisionalDocumentLoader) + m_provisionalDocumentLoader->stopLoading(); + stopLoading(true); + } + stopRedirectionTimer(); m_scheduledRedirection.set(redirection); if (!m_isComplete && redirection->type != ScheduledRedirection::redirection) @@ -1996,14 +2081,17 @@ void FrameLoader::startRedirectionTimer() m_redirectionTimer.startOneShot(m_scheduledRedirection->delay); switch (m_scheduledRedirection->type) { - case ScheduledRedirection::redirection: case ScheduledRedirection::locationChange: - case ScheduledRedirection::locationChangeDuringLoad: + case ScheduledRedirection::redirection: clientRedirected(KURL(m_scheduledRedirection->url), m_scheduledRedirection->delay, currentTime() + m_redirectionTimer.nextFireInterval(), - m_scheduledRedirection->lockHistory, - m_isExecutingJavaScriptFormAction); + m_scheduledRedirection->lockBackForwardList); + return; + case ScheduledRedirection::formSubmission: + // FIXME: It would make sense to report form submissions as client redirects too. + // But we didn't do that in the past when form submission used a separate delay + // mechanism, so doing it will be a behavior change. return; case ScheduledRedirection::historyNavigation: // Don't report history navigations. @@ -2021,11 +2109,15 @@ void FrameLoader::stopRedirectionTimer() if (m_scheduledRedirection) { switch (m_scheduledRedirection->type) { - case ScheduledRedirection::redirection: case ScheduledRedirection::locationChange: - case ScheduledRedirection::locationChangeDuringLoad: + case ScheduledRedirection::redirection: clientRedirectCancelledOrFinished(m_cancellingWithLoadInProgress); return; + case ScheduledRedirection::formSubmission: + // FIXME: It would make sense to report form submissions as client redirects too. + // But we didn't do that in the past when form submission used a separate delay + // mechanism, so doing it will be a behavior change. + return; case ScheduledRedirection::historyNavigation: // Don't report history navigations. return; @@ -2041,7 +2133,8 @@ void FrameLoader::completed() child->loader()->parentCompleted(); if (Frame* parent = m_frame->tree()->parent()) parent->loader()->checkCompleted(); - submitFormAgain(); + if (m_frame->view()) + m_frame->view()->setLockedToAnchor(false); } void FrameLoader::started() @@ -2075,13 +2168,24 @@ void FrameLoader::setupForReplaceByMIMEType(const String& newMIMEType) activeDocumentLoader()->setupForReplaceByMIMEType(newMIMEType); } -void FrameLoader::loadFrameRequestWithFormAndValues(const FrameLoadRequest& request, bool lockHistory, Event* event, - HTMLFormElement* submitForm, const HashMap<String, String>& formValues) +// This is a hack to allow keep navigation to http/https feeds working. To remove this +// we need to introduce new API akin to registerURLSchemeAsLocal, that registers a +// protocols navigation policy. +static bool isFeedWithNestedProtocolInHTTPFamily(const KURL& url) { - RefPtr<FormState> formState; - if (submitForm) - formState = FormState::create(submitForm, formValues, m_frame); - + const String& urlString = url.string(); + if (!urlString.startsWith("feed", false)) + return false; + + return urlString.startsWith("feed://", false) + || urlString.startsWith("feed:http:", false) || urlString.startsWith("feed:https:", false) + || urlString.startsWith("feeds:http:", false) || urlString.startsWith("feeds:https:", false) + || urlString.startsWith("feedsearch:http:", false) || urlString.startsWith("feedsearch:https:", false); +} + +void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHistory, bool lockBackForwardList, + PassRefPtr<Event> event, PassRefPtr<FormState> formState) +{ KURL url = request.resourceRequest().url(); String referrer; @@ -2092,7 +2196,7 @@ void FrameLoader::loadFrameRequestWithFormAndValues(const FrameLoadRequest& requ referrer = m_outgoingReferrer; ASSERT(frame()->document()); - if (url.protocolIs("file")) { + if (shouldTreatURLAsLocal(url.string()) && !isFeedWithNestedProtocolInHTTPFamily(url)) { if (!canLoad(url, String(), frame()->document()) && !canLoad(url, referrer)) { FrameLoader::reportLocalLoadFailed(m_frame, url.string()); return; @@ -2102,29 +2206,31 @@ void FrameLoader::loadFrameRequestWithFormAndValues(const FrameLoadRequest& requ if (shouldHideReferrer(url, referrer)) referrer = String(); - Frame* targetFrame = findFrameForNavigation(request.frameName()); - - if (request.resourceRequest().httpMethod() != "POST") { - FrameLoadType loadType; - if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData) - loadType = FrameLoadTypeReload; - else if (lockHistory) - loadType = FrameLoadTypeRedirectWithLockedHistory; - else - loadType = FrameLoadTypeStandard; - - loadURL(request.resourceRequest().url(), referrer, request.frameName(), loadType, - event, formState.release()); - } else - loadPostRequest(request.resourceRequest(), referrer, request.frameName(), event, formState.release()); + FrameLoadType loadType; + if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData) + loadType = FrameLoadTypeReload; + else if (lockBackForwardList) + loadType = FrameLoadTypeRedirectWithLockedBackForwardList; + else + loadType = FrameLoadTypeStandard; - if (targetFrame && targetFrame != m_frame) + if (request.resourceRequest().httpMethod() == "POST") + loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.get()); + else + loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.get()); + + // FIXME: It's possible this targetFrame will not be the same frame that was targeted by the actual + // load if frame names have changed. + Frame* sourceFrame = formState ? formState->sourceFrame() : m_frame; + Frame* targetFrame = sourceFrame->loader()->findFrameForNavigation(request.frameName()); + if (targetFrame && targetFrame != sourceFrame) { if (Page* page = targetFrame->page()) page->chrome()->focus(); + } } -void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, FrameLoadType newLoadType, - Event* event, PassRefPtr<FormState> prpFormState) +void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType, + PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState) { RefPtr<FormState> formState = prpFormState; bool isFormSubmission = formState; @@ -2141,13 +2247,17 @@ void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const Stri ASSERT(newLoadType != FrameLoadTypeSame); + // The search for a target frame is done earlier in the case of form submission. + Frame* targetFrame = isFormSubmission ? 0 : findFrameForNavigation(frameName); + if (targetFrame && targetFrame != m_frame) { + targetFrame->loader()->loadURL(newURL, referrer, String(), lockHistory, newLoadType, event, formState.release()); + return; + } + NavigationAction action(newURL, newLoadType, isFormSubmission, event); - if (!frameName.isEmpty()) { - if (Frame* targetFrame = findFrameForNavigation(frameName)) - targetFrame->loader()->loadURL(newURL, referrer, String(), newLoadType, event, formState); - else - checkNewWindowPolicy(action, request, formState, frameName); + if (!targetFrame && !frameName.isEmpty()) { + checkNewWindowPolicy(action, request, formState.release(), frameName); return; } @@ -2161,12 +2271,12 @@ void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const Stri if (shouldScrollToAnchor(isFormSubmission, newLoadType, newURL)) { oldDocumentLoader->setTriggeringAction(action); stopPolicyCheck(); - checkNavigationPolicy(request, oldDocumentLoader.get(), formState, + checkNavigationPolicy(request, oldDocumentLoader.get(), formState.release(), callContinueFragmentScrollAfterNavigationPolicy, this); } 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.release()); if (isRedirect) { m_quickRedirectComing = false; if (m_provisionalDocumentLoader) @@ -2179,40 +2289,45 @@ 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); + if (lockHistory && m_documentLoader) + loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory() : m_documentLoader->clientRedirectSourceForHistory()); + 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()); + if (lockHistory && m_documentLoader) + loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory() : m_documentLoader->clientRedirectSourceForHistory()); loader->setTriggeringAction(action); if (m_documentLoader) @@ -2280,6 +2395,8 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t stopPolicyCheck(); setPolicyDocumentLoader(loader); + if (loader->triggeringAction().isEmpty()) + loader->setTriggeringAction(NavigationAction(newURL, m_policyLoadType, isFormSubmission)); checkNavigationPolicy(loader->request(), loader, formState, callContinueLoadAfterNavigationPolicy, this); @@ -2288,18 +2405,22 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t bool FrameLoader::canLoad(const KURL& url, const String& referrer, const Document* doc) { - // We can always load any URL that isn't considered local (e.g. http URLs) + return canLoad(url, referrer, doc ? doc->securityOrigin() : 0); +} + +bool FrameLoader::canLoad(const KURL& url, const String& referrer, const SecurityOrigin* securityOrigin) +{ + // We can always load any URL that isn't considered local (e.g. http URLs). if (!shouldTreatURLAsLocal(url.string())) return true; // If we were provided a document, we let its local file policy dictate the result, // otherwise we allow local loads only if the supplied referrer is also local. - if (doc) - return doc->securityOrigin()->canLoadLocalResources(); - else if (!referrer.isEmpty()) + if (securityOrigin) + return securityOrigin->canLoadLocalResources(); + if (!referrer.isEmpty()) return shouldTreatURLAsLocal(referrer); - else - return false; + return false; } void FrameLoader::reportLocalLoadFailed(Frame* frame, const String& url) @@ -2407,7 +2528,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; @@ -2424,7 +2545,7 @@ void FrameLoader::reloadAllowingStaleData(const String& encoding) loader->setOverrideEncoding(encoding); - loadWithDocumentLoader(loader.get(), FrameLoadTypeReloadAllowingStaleData, 0); + loadWithDocumentLoader(loader.get(), FrameLoadTypeReload, 0); } void FrameLoader::reload(bool endToEndReload) @@ -2537,8 +2658,11 @@ void FrameLoader::stopLoadingSubframes() child->loader()->stopAllLoaders(); } -void FrameLoader::stopAllLoaders() +void FrameLoader::stopAllLoaders(DatabasePolicy databasePolicy) { + if (m_unloadEventBeingDispatched) + return; + // If this method is called from within this method, infinite recursion can occur (3442218). Avoid this. if (m_inStopAllLoaders) return; @@ -2549,9 +2673,9 @@ void FrameLoader::stopAllLoaders() stopLoadingSubframes(); if (m_provisionalDocumentLoader) - m_provisionalDocumentLoader->stopLoading(); + m_provisionalDocumentLoader->stopLoading(databasePolicy); if (m_documentLoader) - m_documentLoader->stopLoading(); + m_documentLoader->stopLoading(databasePolicy); setProvisionalDocumentLoader(0); @@ -2607,11 +2731,6 @@ void FrameLoader::setDocumentLoader(DocumentLoader* loader) m_documentLoader = loader; } -DocumentLoader* FrameLoader::documentLoader() const -{ - return m_documentLoader.get(); -} - void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader) { if (m_policyDocumentLoader == loader) @@ -2628,16 +2747,6 @@ void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader) m_policyDocumentLoader = loader; } -DocumentLoader* FrameLoader::policyDocumentLoader() const -{ - return m_policyDocumentLoader.get(); -} - -DocumentLoader* FrameLoader::provisionalDocumentLoader() const -{ - return m_provisionalDocumentLoader.get(); -} - void FrameLoader::setProvisionalDocumentLoader(DocumentLoader* loader) { ASSERT(!loader || !m_provisionalDocumentLoader); @@ -2649,11 +2758,6 @@ void FrameLoader::setProvisionalDocumentLoader(DocumentLoader* loader) m_provisionalDocumentLoader = loader; } -FrameState FrameLoader::state() const -{ - return m_state; -} - double FrameLoader::timeOfLastCompletedLoad() { return storedTimeOfLastCompletedLoad; @@ -2690,14 +2794,12 @@ 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 (Document* document = m_frame->document()) - document->suspendActiveDOMObjects(); - cachePageForHistoryItem(m_currentHistoryItem.get()); - } + cachePageForHistoryItem(m_currentHistoryItem.get()); if (m_loadType != FrameLoadTypeReplace) closeOldDataSources(); @@ -2728,7 +2830,40 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) didOpenURL(url); } - opened(); + + LOG(Loading, "WebCoreLoading %s: Finished committing provisional load to URL %s", m_frame->tree()->name().string().utf8().data(), m_URL.string().utf8().data()); + + if (m_loadType == FrameLoadTypeStandard && m_documentLoader->isClientRedirect()) + updateHistoryForClientRedirect(); + + if (m_documentLoader->isLoadingFromCachedPage()) { + m_frame->document()->documentDidBecomeActive(); + + // Force a layout to update view size and thereby update scrollbars. + m_client->forceLayout(); + + const ResponseVector& responses = m_documentLoader->responses(); + size_t count = responses.size(); + for (size_t i = 0; i < count; i++) { + const ResourceResponse& response = responses[i]; + // FIXME: If the WebKit client changes or cancels the request, this is not respected. + ResourceError error; + unsigned long identifier; + ResourceRequest request(response.url()); + requestFromDelegate(request, identifier, error); + // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing. + // However, with today's computers and networking speeds, this won't happen in practice. + // Could be an issue with a giant local file. + sendRemainingDelegateMessages(identifier, response, static_cast<int>(response.expectedContentLength()), error); + } + + pageCache()->remove(m_currentHistoryItem.get()); + + m_documentLoader->setPrimaryLoadComplete(true); + + // FIXME: Why only this frame and not parent frames? + checkLoadCompleteForThisFrame(); + } } void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) @@ -2778,7 +2913,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(); @@ -2793,11 +2928,6 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) 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 @@ -2809,8 +2939,8 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) m_client->transitionToCommittedForNewPage(); break; - case FrameLoadTypeRedirectWithLockedHistory: - updateHistoryForRedirectWithLockedHistory(); + case FrameLoadTypeRedirectWithLockedBackForwardList: + updateHistoryForRedirectWithLockedBackForwardList(); m_client->transitionToCommittedForNewPage(); break; @@ -2827,9 +2957,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(); @@ -2853,7 +2983,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) { m_client->dispatchWillPerformClientRedirect(url, seconds, fireDate); @@ -2861,10 +2991,11 @@ void FrameLoader::clientRedirected(const KURL& url, double seconds, double fireD // the next provisional load, we can send a corresponding -webView:didCancelClientRedirectForFrame: m_sentRedirectNotification = true; - // 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 + // If a "quick" redirect comes in, we set a special mode so we treat the next + // load as part of the original 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; + // Loads triggered by JavaScript form submissions never count as quick redirects. + m_quickRedirectComing = lockBackForwardList && m_documentLoader && !m_isExecutingJavaScriptFormAction; } #if ENABLE(WML) @@ -2906,6 +3037,7 @@ void FrameLoader::closeOldDataSources() void FrameLoader::open(CachedPage& cachedPage) { + ASSERT(!m_frame->tree()->parent()); ASSERT(m_frame->page()); ASSERT(m_frame->page()->mainFrame() == m_frame); @@ -2913,11 +3045,6 @@ void FrameLoader::open(CachedPage& cachedPage) // We still have to close the previous part page. closeURL(); - - m_isComplete = false; - - // Don't re-emit the load event. - m_didCallImplicitClose = true; // Delete old status bar messages (if it _was_ activated on last URL). if (m_frame->script()->isEnabled()) { @@ -2925,9 +3052,21 @@ void FrameLoader::open(CachedPage& cachedPage) m_frame->setJSDefaultStatusBarText(String()); } - KURL url = cachedPage.url(); + open(*cachedPage.cachedMainFrame()); - if ((url.protocolIs("http") || url.protocolIs("https")) && !url.host().isEmpty() && url.path().isEmpty()) + checkCompleted(); +} + +void FrameLoader::open(CachedFrame& cachedFrame) +{ + m_isComplete = false; + + // Don't re-emit the load event. + m_didCallImplicitClose = true; + + KURL url = cachedFrame.url(); + + if (url.protocolInHTTPFamily() && !url.host().isEmpty() && url.path().isEmpty()) url.setPath("/"); m_URL = url; @@ -2937,7 +3076,7 @@ void FrameLoader::open(CachedPage& cachedPage) clear(); - Document* document = cachedPage.document(); + Document* document = cachedFrame.document(); ASSERT(document); document->setInPageCache(false); @@ -2946,28 +3085,24 @@ void FrameLoader::open(CachedPage& cachedPage) m_didCallImplicitClose = false; m_outgoingReferrer = url.string(); - FrameView* view = cachedPage.view(); + FrameView* view = cachedFrame.view(); + + // When navigating to a CachedFrame its FrameView should never be null. If it is we'll crash in creative ways downstream. + ASSERT(view); if (view) view->setWasScrolledByUser(false); m_frame->setView(view); m_frame->setDocument(document); - m_frame->setDOMWindow(cachedPage.domWindow()); + m_frame->setDOMWindow(cachedFrame.domWindow()); m_frame->domWindow()->setURL(document->url()); m_frame->domWindow()->setSecurityOrigin(document->securityOrigin()); m_decoder = document->decoder(); - updatePolicyBaseURL(); - - cachedPage.restore(m_frame->page()); - document->resumeActiveDOMObjects(); + updateFirstPartyForCookies(); - // It is necessary to update any platform script objects after restoring the - // cached page. - m_frame->script()->updatePlatformScriptObjects(); - - checkCompleted(); + cachedFrame.restore(); } bool FrameLoader::isStopping() const @@ -3060,7 +3195,17 @@ void FrameLoader::finishedLoadingDocument(DocumentLoader* loader) ArchiveResource* mainResource = archive->mainResource(); loader->setParsedArchiveData(mainResource->data()); - continueLoadWithData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), mainResource->url()); + + m_responseMIMEType = mainResource->mimeType(); + didOpenURL(mainResource->url()); + + String userChosenEncoding = documentLoader()->overrideEncoding(); + bool encodingIsUserChosen = !userChosenEncoding.isNull(); + setEncoding(encodingIsUserChosen ? userChosenEncoding : mainResource->textEncoding(), encodingIsUserChosen); + + ASSERT(m_frame->document()); + + addData(mainResource->data()->data(), mainResource->data()->size()); } bool FrameLoader::isReplacing() const @@ -3103,20 +3248,28 @@ FrameLoadType FrameLoader::loadType() const return m_loadType; } -CachePolicy FrameLoader::cachePolicy() const +CachePolicy FrameLoader::subresourceCachePolicy() const { if (m_isComplete) return CachePolicyVerify; - + if (m_loadType == FrameLoadTypeReloadFromOrigin) return CachePolicyReload; - + if (Frame* parentFrame = m_frame->tree()->parent()) { - CachePolicy parentCachePolicy = parentFrame->loader()->cachePolicy(); + CachePolicy parentCachePolicy = parentFrame->loader()->subresourceCachePolicy(); if (parentCachePolicy != CachePolicyVerify) return parentCachePolicy; } + // FIXME: POST documents are always Reloads, but their subresources should still be Revalidate. + // If we bring the CachePolicy.h and ResourceRequest cache policy enums in sync with each other and + // remember "Revalidate" in ResourceRequests, we can remove this "POST" check and return either "Reload" + // or "Revalidate" if the DocumentLoader was requested with either. + const ResourceRequest& request(documentLoader()->request()); + if (request.cachePolicy() == ReloadIgnoringCacheData && !equalIgnoringCase(request.httpMethod(), "post")) + return CachePolicyRevalidate; + if (m_loadType == FrameLoadTypeReload) return CachePolicyRevalidate; @@ -3156,7 +3309,7 @@ void FrameLoader::checkLoadCompleteForThisFrame() item = m_currentHistoryItem; bool shouldReset = true; - if (!pdl->isLoadingInAPISense()) { + if (!(pdl->isLoadingInAPISense() && !pdl->isStopping())) { m_delegateIsHandlingProvisionalLoadError = true; m_client->dispatchDidFailProvisionalLoad(error); m_delegateIsHandlingProvisionalLoadError = false; @@ -3188,7 +3341,7 @@ void FrameLoader::checkLoadCompleteForThisFrame() case FrameStateCommittedPage: { DocumentLoader* dl = m_documentLoader.get(); - if (!dl || dl->isLoadingInAPISense()) + if (!dl || (dl->isLoadingInAPISense() && !dl->isStopping())) return; markLoadComplete(); @@ -3222,9 +3375,7 @@ void FrameLoader::checkLoadCompleteForThisFrame() } case FrameStateComplete: - // Even if already complete, we might have set a previous item on a frame that - // didn't do any data loading on the past transaction. Make sure to clear these out. - m_client->frameLoadCompleted(); + frameLoadCompleted(); return; } @@ -3285,8 +3436,14 @@ void FrameLoader::didFirstVisuallyNonEmptyLayout() void FrameLoader::frameLoadCompleted() { + // Note: Can be called multiple times. + m_client->frameLoadCompleted(); + // Even if already complete, we might have set a previous item on a frame that + // didn't do any data loading on the past transaction. Make sure to clear these out. + m_previousHistoryItem = 0; + // After a canceled provisional load, firstLayoutDone is false. // Reset it to true if we're displaying a page. if (m_documentLoader) @@ -3298,11 +3455,6 @@ bool FrameLoader::firstLayoutDone() const return m_firstLayoutDone; } -bool FrameLoader::isQuickRedirectComing() const -{ - return m_quickRedirectComing; -} - void FrameLoader::detachChildren() { // FIXME: Is it really necessary to do this in reverse order? @@ -3313,6 +3465,18 @@ void FrameLoader::detachChildren() } } +void FrameLoader::closeAndRemoveChild(Frame* child) +{ + child->tree()->detachFromParent(); + + child->setView(0); + if (child->ownerElement()) + child->page()->decrementFrameCount(); + child->pageDestroyed(); + + m_frame->tree()->removeChild(child); +} + void FrameLoader::recursiveCheckLoadComplete() { Vector<RefPtr<Frame>, 10> frames; @@ -3349,36 +3513,6 @@ int FrameLoader::numPendingOrLoadingRequests(bool recurse) const return count; } -FrameLoaderClient* FrameLoader::client() const -{ - return m_client; -} - -void FrameLoader::submitForm(const FrameLoadRequest& request, Event* event) -{ - // 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, - // nor do we want to submit a single form more than once. - // This flag prevents these from happening; not sure how other browsers prevent this. - // The flag is reset in each time we start handle a new mouse or key down event, and - // also in setView since this part may get reused for a page from the back/forward cache. - // The form multi-submit logic here is only needed when we are submitting a form that affects this frame. - // FIXME: Frame targeting is only one of the ways the submission could end up doing something other - // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly - // needed any more now that we reset m_submittedFormURL on each mouse or key down event. - Frame* target = m_frame->tree()->find(request.frameName()); - if (m_frame->tree()->isDescendantOf(target)) { - if (m_submittedFormURL == request.resourceRequest().url()) - return; - 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); - - clearRecordedFormValues(); -} - String FrameLoader::userAgent(const KURL& url) const { return m_client->userAgent(url); @@ -3386,9 +3520,6 @@ String FrameLoader::userAgent(const KURL& url) const void FrameLoader::tokenizerProcessedData() { -// ASSERT(m_frame->page()); -// ASSERT(m_frame->document()); - checkCompleted(); } @@ -3400,8 +3531,7 @@ void FrameLoader::handledOnloadEvents() void FrameLoader::frameDetached() { stopAllLoaders(); - if (Document* document = m_frame->document()) - document->stopActiveDOMObjects(); + m_frame->document()->stopActiveDOMObjects(); detachFromParent(); } @@ -3421,7 +3551,7 @@ void FrameLoader::detachFromParent() setDocumentLoader(0); m_client->detachedFromParent3(); if (Frame* parent = m_frame->tree()->parent()) { - parent->tree()->removeChild(m_frame); + parent->loader()->closeAndRemoveChild(m_frame); parent->loader()->scheduleCheckCompleted(); } else { m_frame->setView(0); @@ -3441,6 +3571,19 @@ void FrameLoader::addExtraFieldsToMainResourceRequest(ResourceRequest& request) void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadType loadType, bool mainResource, bool cookiePolicyURLFromRequest) { + // Don't set the cookie policy URL if it's already been set. + // But make sure to set it on all requests, as it has significance beyond the cookie policy for all protocols (<rdar://problem/6616664>). + if (request.firstPartyForCookies().isEmpty()) { + if (mainResource && (isLoadingMainFrame() || cookiePolicyURLFromRequest)) + request.setFirstPartyForCookies(request.url()); + else if (Document* document = m_frame->document()) + request.setFirstPartyForCookies(document->firstPartyForCookies()); + } + + // The remaining modifications are only necessary for HTTP and HTTPS. + if (!request.url().isEmpty() && !request.url().protocolInHTTPFamily()) + return; + applyUserAgent(request); if (loadType == FrameLoadTypeReload) { @@ -3452,16 +3595,8 @@ void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadTyp 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() || cookiePolicyURLFromRequest)) - request.setMainDocumentURL(request.url()); - else if (Page* page = m_frame->page()) - request.setMainDocumentURL(page->mainFrame()->loader()->url()); - } - if (mainResource) - request.setHTTPAccept("application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"); + request.setHTTPAccept(defaultAcceptHeader); // Make sure we send the Origin header. addHTTPOriginIfNeeded(request, String()); @@ -3505,8 +3640,7 @@ void FrameLoader::committedLoad(DocumentLoader* loader, const char* data, int le m_client->committedLoad(loader, data, length); } -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, PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState) { RefPtr<FormState> formState = prpFormState; @@ -3533,29 +3667,22 @@ void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String workingResourceRequest.setHTTPMethod("POST"); workingResourceRequest.setHTTPBody(formData); workingResourceRequest.setHTTPContentType(contentType); - addExtraFieldsToRequest(workingResourceRequest, FrameLoadTypeStandard, 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()); + // The search for a target frame is done earlier in the case of form submission. + if (Frame* targetFrame = formState ? 0 : findFrameForNavigation(frameName)) + targetFrame->loader()->loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release()); else checkNewWindowPolicy(action, workingResourceRequest, formState.release(), frameName); } else - loadWithNavigationAction(workingResourceRequest, action, FrameLoadTypeStandard, formState.release()); + loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release()); } -void FrameLoader::loadEmptyDocumentSynchronously() +unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data) { - ResourceRequest request(KURL("")); - load(request); -} - -unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) -{ - // Since this is a subresource, we can load any URL (we ignore the return value). - // But we still want to know whether we should hide the referrer or not, so we call the canLoad method. String referrer = m_outgoingReferrer; if (shouldHideReferrer(request.url(), referrer)) referrer = String(); @@ -3573,7 +3700,7 @@ unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& requ addHTTPOriginIfNeeded(initialRequest, outgoingOrigin()); if (Page* page = m_frame->page()) - initialRequest.setMainDocumentURL(page->mainFrame()->loader()->documentLoader()->request().url()); + initialRequest.setFirstPartyForCookies(page->mainFrame()->loader()->documentLoader()->request().url()); initialRequest.setHTTPUserAgent(client()->userAgent(request.url())); unsigned long identifier = 0; @@ -3593,7 +3720,7 @@ unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& requ error = cannotShowURLError(newRequest); } else { #endif - ResourceHandle::loadResourceSynchronously(newRequest, error, response, data, m_frame); + ResourceHandle::loadResourceSynchronously(newRequest, storedCredentials, 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, @@ -3651,6 +3778,11 @@ void FrameLoader::didFailToLoad(ResourceLoader* loader, const ResourceError& err m_client->dispatchDidFailLoading(loader->documentLoader(), loader->identifier(), error); } +void FrameLoader::didLoadResourceByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString) +{ + m_client->dispatchDidLoadResourceByXMLHttpRequest(identifier, sourceString); +} + const ResourceRequest& FrameLoader::originalRequest() const { return activeDocumentLoader()->originalRequestCopy(); @@ -3672,8 +3804,8 @@ void FrameLoader::receivedMainResourceError(const ResourceError& error, bool isC } if (m_state == FrameStateProvisional && m_provisionalDocumentLoader) { - KURL failedURL = m_provisionalDocumentLoader->originalRequestCopy().url(); - didNotOpenURL(failedURL); + if (m_submittedFormURL == m_provisionalDocumentLoader->originalRequestCopy().url()) + m_submittedFormURL = KURL(); // We might have made a page cache item, but now we're bailing out due to an error before we ever // transitioned to the new page (before WebFrameState == commit). The goal here is to restore any state @@ -3702,10 +3834,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) @@ -3758,42 +3887,7 @@ bool FrameLoader::shouldScrollToAnchor(bool isFormSubmission, FrameLoadType load && !shouldReload(this->url(), url) // We don't want to just scroll if a link from within a // frameset is trying to reload the frameset into _top. - && !m_frame->isFrameSet(); -} - -void FrameLoader::opened() -{ - if (m_loadType == FrameLoadTypeStandard && m_documentLoader->isClientRedirect()) - updateHistoryForClientRedirect(); - - if (m_documentLoader->isLoadingFromCachedPage()) { - m_frame->document()->documentDidBecomeActive(); - - // Force a layout to update view size and thereby update scrollbars. - m_client->forceLayout(); - - const ResponseVector& responses = m_documentLoader->responses(); - size_t count = responses.size(); - for (size_t i = 0; i < count; i++) { - const ResourceResponse& response = responses[i]; - // FIXME: If the WebKit client changes or cancels the request, this is not respected. - ResourceError error; - unsigned long identifier; - ResourceRequest request(response.url()); - requestFromDelegate(request, identifier, error); - // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing. - // However, with today's computers and networking speeds, this won't happen in practice. - // Could be an issue with a giant local file. - sendRemainingDelegateMessages(identifier, response, static_cast<int>(response.expectedContentLength()), error); - } - - pageCache()->remove(m_currentHistoryItem.get()); - - m_documentLoader->setPrimaryLoadComplete(true); - - // FIXME: Why only this frame and not parent frames? - checkLoadCompleteForThisFrame(); - } + && !m_frame->document()->isFrameSet(); } void FrameLoader::checkNewWindowPolicy(const NavigationAction& action, const ResourceRequest& request, @@ -3944,7 +4038,14 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass // might detach the current FrameLoader, in which case we should bail on this newly defunct load. if (!m_frame->page()) return; - + +#if ENABLE(JAVASCRIPT_DEBUGGER) + if (Page* page = m_frame->page()) { + if (page->mainFrame() == m_frame) + page->inspectorController()->resumeDebugger(); + } +#endif + setProvisionalDocumentLoader(m_policyDocumentLoader.get()); m_loadType = type; setState(FrameStateProvisional); @@ -3985,7 +4086,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) @@ -4059,6 +4160,24 @@ void FrameLoader::applyUserAgent(ResourceRequest& request) request.setHTTPUserAgent(userAgent); } +bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, const KURL& url) +{ + Frame* topFrame = m_frame->tree()->top(); + if (m_frame == topFrame) + return false; + + if (equalIgnoringCase(content, "deny")) + return true; + + if (equalIgnoringCase(content, "sameorigin")) { + RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url); + if (!origin->isSameSchemeHostPort(topFrame->document()->securityOrigin())) + return true; + } + + return false; +} + bool FrameLoader::canGoBackOrForward(int distance) const { if (Page* page = m_frame->page()) { @@ -4079,28 +4198,6 @@ int FrameLoader::getHistoryLength() return 0; } -KURL FrameLoader::historyURL(int distance) -{ - if (Page* page = m_frame->page()) { - BackForwardList* list = page->backForwardList(); - HistoryItem* item = list->itemAtIndex(distance); - if (!item) { - if (distance > 0) { - int forwardListCount = list->forwardListCount(); - if (forwardListCount > 0) - item = list->itemAtIndex(forwardListCount); - } else { - int backListCount = list->backListCount(); - if (backListCount > 0) - item = list->itemAtIndex(-backListCount); - } - } - if (item) - return item->url(); - } - return KURL(); -} - void FrameLoader::addHistoryItemForFragmentScroll() { addBackForwardItemClippedAtTarget(false); @@ -4117,12 +4214,11 @@ bool FrameLoader::loadProvisionalItemFromCachedPage() void FrameLoader::cachePageForHistoryItem(HistoryItem* item) { + if (!canCachePage() || item->isInPageCache()) + return; + if (Page* page = m_frame->page()) { RefPtr<CachedPage> cachedPage = CachedPage::create(page); - cachedPage->setTimeStampToNow(); - cachedPage->setDocumentLoader(documentLoader()); - m_client->savePlatformDataToCachedPage(cachedPage.get()); - pageCache()->add(item, cachedPage.release()); } } @@ -4193,6 +4289,11 @@ PassRefPtr<HistoryItem> FrameLoader::createHistoryItem(bool useOriginal) void FrameLoader::addBackForwardItemClippedAtTarget(bool doClip) { + // In the case of saving state about a page with frames, we store a tree of items that mirrors the frame tree. + // The item that was the target of the user's navigation is designated as the "targetItem". + // When this function is called with doClip=true we're able to create the whole tree except for the target's children, + // which will be loaded in the future. That part of the tree will be filled out as the child loads are committed. + Page* page = m_frame->page(); if (!page) return; @@ -4242,9 +4343,9 @@ PassRefPtr<HistoryItem> FrameLoader::createHistoryItemTree(Frame* targetFrame, b Frame* FrameLoader::findFrameForNavigation(const AtomicString& name) { Frame* frame = m_frame->tree()->find(name); - if (shouldAllowNavigation(frame)) - return frame; - return 0; + if (!shouldAllowNavigation(frame)) + return 0; + return frame; } void FrameLoader::saveScrollPositionAndViewStateToItem(HistoryItem* item) @@ -4334,7 +4435,7 @@ void FrameLoader::saveDocumentState() Document* document = m_frame->document(); ASSERT(document); - if (document && item->isCurrentDocument(document)) { + if (item->isCurrentDocument(document)) { LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->name().string().utf8().data(), item); item->setDocumentState(document->formElementsState()); } @@ -4357,13 +4458,16 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) // Note if we have child frames we do a real reload, since the child frames might not // match our current frame structure, or they might not have the right content. We could // check for all that as an additional optimization. - // We also do not do anchor-style navigation if we're posting a form. - + // We also do not do anchor-style navigation if we're posting a form or navigating from + // a page that was resulted from a form post. + bool shouldScroll = !formData && !(m_currentHistoryItem && m_currentHistoryItem->formData()) && urlsMatchItem(item); + #if ENABLE(WML) - if (!formData && urlsMatchItem(item) && !m_frame->document()->isWMLDocument()) { -#else - if (!formData && urlsMatchItem(item)) { + if (m_frame->document()->isWMLDocument()) + shouldScroll = false; #endif + + if (shouldScroll) { // Must do this maintenance here, since we don't go through a real page reload saveScrollPositionAndViewStateToItem(m_currentHistoryItem.get()); @@ -4417,6 +4521,9 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) 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) { @@ -4424,10 +4531,9 @@ 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. @@ -4462,12 +4568,9 @@ 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(); } @@ -4478,7 +4581,7 @@ void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) if (!addedExtraFields) addExtraFieldsToRequest(request, m_loadType, true, formData); - loadWithNavigationAction(request, action, loadType, 0); + loadWithNavigationAction(request, action, false, loadType, 0); } } } @@ -4575,10 +4678,10 @@ void FrameLoader::recursiveGoToItem(HistoryItem* item, HistoryItem* fromItem, Fr int size = childItems.size(); for (int i = 0; i < size; ++i) { - String childName = childItems[i]->target(); - HistoryItem* fromChildItem = fromItem->childItemWithName(childName); + String childFrameName = childItems[i]->target(); + HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName); ASSERT(fromChildItem || fromItem->isTargetItem()); - Frame* childFrame = m_frame->tree()->child(childName); + Frame* childFrame = m_frame->tree()->child(childFrameName); ASSERT(childFrame); childFrame->loader()->recursiveGoToItem(childItems[i].get(), fromChildItem, type); } @@ -4596,9 +4699,10 @@ bool FrameLoader::childFramesMatchItem(HistoryItem* item) const return false; unsigned size = childItems.size(); - for (unsigned i = 0; i < size; ++i) + for (unsigned i = 0; i < size; ++i) { if (!m_frame->tree()->child(childItems[i]->target())) return false; + } // Found matches for all item targets return true; @@ -4618,21 +4722,15 @@ void FrameLoader::updateHistoryForStandardLoad() bool needPrivacy = !settings || settings->privateBrowsingEnabled(); const KURL& historyURL = documentLoader()->urlForHistory(); - // If the navigation occured during load and this is a subframe, update the current - // back/forward item rather than adding a new one and don't add the new URL to global - // history at all. But do add it to visited links. <rdar://problem/5333496> - bool frameNavigationDuringLoad = false; - if (m_navigationDuringLoad) { - HTMLFrameOwnerElement* owner = m_frame->ownerElement(); - frameNavigationDuringLoad = owner && !owner->createdByParser(); - m_navigationDuringLoad = false; - } - - if (!frameNavigationDuringLoad && !documentLoader()->isClientRedirect()) { + if (!documentLoader()->isClientRedirect()) { if (!historyURL.isEmpty()) { addBackForwardItemClippedAtTarget(true); - if (!needPrivacy) + if (!needPrivacy) { m_client->updateGlobalHistory(); + m_documentLoader->setDidCreateGlobalHistoryEntry(true); + if (m_documentLoader->unreachableURL().isEmpty()) + m_client->updateGlobalHistoryRedirectLinks(); + } if (Page* page = m_frame->page()) page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForwardList()->currentItem()); } @@ -4644,6 +4742,9 @@ void FrameLoader::updateHistoryForStandardLoad() if (!historyURL.isEmpty() && !needPrivacy) { if (Page* page = m_frame->page()) page->group().addVisitedLink(historyURL); + + if (!m_documentLoader->didCreateGlobalHistoryEntry() && documentLoader()->unreachableURL().isEmpty() && !url().isEmpty()) + m_client->updateGlobalHistoryRedirectLinks(); } } @@ -4701,11 +4802,11 @@ 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(); @@ -4714,10 +4815,14 @@ void FrameLoader::updateHistoryForRedirectWithLockedHistory() if (documentLoader()->isClientRedirect()) { if (!m_currentHistoryItem && !m_frame->tree()->parent()) { - addBackForwardItemClippedAtTarget(true); if (!historyURL.isEmpty()) { - if (!needPrivacy) + addBackForwardItemClippedAtTarget(true); + if (!needPrivacy) { m_client->updateGlobalHistory(); + m_documentLoader->setDidCreateGlobalHistoryEntry(true); + if (m_documentLoader->unreachableURL().isEmpty()) + m_client->updateGlobalHistoryRedirectLinks(); + } if (Page* page = m_frame->page()) page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForwardList()->currentItem()); } @@ -4729,12 +4834,15 @@ void FrameLoader::updateHistoryForRedirectWithLockedHistory() } else { Frame* parentFrame = m_frame->tree()->parent(); if (parentFrame && parentFrame->loader()->m_currentHistoryItem) - parentFrame->loader()->m_currentHistoryItem->addChildItem(createHistoryItem(true)); + parentFrame->loader()->m_currentHistoryItem->setChildItem(createHistoryItem(true)); } if (!historyURL.isEmpty() && !needPrivacy) { if (Page* page = m_frame->page()) page->group().addVisitedLink(historyURL); + + if (!m_documentLoader->didCreateGlobalHistoryEntry() && documentLoader()->unreachableURL().isEmpty() && !url().isEmpty()) + m_client->updateGlobalHistoryRedirectLinks(); } } @@ -4784,38 +4892,16 @@ void FrameLoader::saveDocumentAndScrollState() } } -// FIXME: These 6 setter/getters are here for a dwindling number of users in WebKit, WebFrame -// being the primary one. After they're no longer needed there, they can be removed! HistoryItem* FrameLoader::currentHistoryItem() { return m_currentHistoryItem.get(); } -HistoryItem* FrameLoader::previousHistoryItem() -{ - return m_previousHistoryItem.get(); -} - -HistoryItem* FrameLoader::provisionalHistoryItem() -{ - return m_provisionalHistoryItem.get(); -} - void FrameLoader::setCurrentHistoryItem(PassRefPtr<HistoryItem> item) { m_currentHistoryItem = item; } -void FrameLoader::setPreviousHistoryItem(PassRefPtr<HistoryItem> item) -{ - m_previousHistoryItem = item; -} - -void FrameLoader::setProvisionalHistoryItem(PassRefPtr<HistoryItem> item) -{ - m_provisionalHistoryItem = item; -} - void FrameLoader::setMainDocumentError(DocumentLoader* loader, const ResourceError& error) { m_client->setMainDocumentError(loader, error); @@ -4982,6 +5068,11 @@ String FrameLoader::referrer() const return documentLoader()->request().httpReferrer(); } +void FrameLoader::dispatchDocumentElementAvailable() +{ + m_client->documentElementAvailable(); +} + void FrameLoader::dispatchWindowObjectAvailable() { if (!m_frame->script()->isEnabled() || !m_frame->script()->haveWindowShell()) @@ -4997,27 +5088,39 @@ void FrameLoader::dispatchWindowObjectAvailable() } } -Widget* FrameLoader::createJavaAppletWidget(const IntSize& size, Element* element, const HashMap<String, String>& args) +Widget* FrameLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement* element, const HashMap<String, String>& args) { String baseURLString; + String codeBaseURLString; Vector<String> paramNames; Vector<String> paramValues; HashMap<String, String>::const_iterator end = args.end(); for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) { if (equalIgnoringCase(it->first, "baseurl")) baseURLString = it->second; + else if (equalIgnoringCase(it->first, "codebase")) + codeBaseURLString = it->second; paramNames.append(it->first); paramValues.append(it->second); } - + + if (!codeBaseURLString.isEmpty()) { + KURL codeBaseURL = completeURL(codeBaseURLString); + if (!canLoad(codeBaseURL, String(), element->document())) { + FrameLoader::reportLocalLoadFailed(m_frame, codeBaseURL.string()); + return 0; + } + } + if (baseURLString.isEmpty()) baseURLString = m_frame->document()->baseURL().string(); KURL baseURL = completeURL(baseURLString); Widget* widget = m_client->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues); - if (widget) - m_containsPlugIns = true; - + if (!widget) + return 0; + + m_containsPlugIns = true; return widget; } @@ -5037,24 +5140,6 @@ void FrameLoader::didChangeTitle(DocumentLoader* loader) } } -void FrameLoader::continueLoadWithData(SharedBuffer* buffer, const String& mimeType, const String& textEncoding, const KURL& url) -{ - m_responseMIMEType = mimeType; - didOpenURL(url); - - String encoding; - if (m_frame) - encoding = documentLoader()->overrideEncoding(); - bool userChosen = !encoding.isNull(); - if (encoding.isNull()) - encoding = textEncoding; - setEncoding(encoding, userChosen); - - ASSERT(m_frame->document()); - - addData(buffer->data(), buffer->size()); -} - void FrameLoader::registerURLSchemeAsLocal(const String& scheme) { localSchemes().add(scheme); @@ -5080,7 +5165,7 @@ bool FrameLoader::shouldTreatURLAsLocal(const String& url) return localSchemes().contains(scheme); } -bool FrameLoader::shouldTreatSchemeAsLocal(const String& scheme) +bool FrameLoader::shouldTreatURLSchemeAsLocal(const String& scheme) { // This avoids an allocation of another String and the HashSet contains() // call for the file: and http: schemes. @@ -5098,6 +5183,16 @@ bool FrameLoader::shouldTreatSchemeAsLocal(const String& scheme) return localSchemes().contains(scheme); } +void FrameLoader::registerURLSchemeAsNoAccess(const String& scheme) +{ + noAccessSchemes().add(scheme); +} + +bool FrameLoader::shouldTreatURLSchemeAsNoAccess(const String& scheme) +{ + return noAccessSchemes().contains(scheme); +} + void FrameLoader::dispatchDidCommitLoad() { if (m_creatingInitialEmptyDocument) @@ -5129,7 +5224,7 @@ void FrameLoader::dispatchWillSendRequest(DocumentLoader* loader, unsigned long 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 (oldRequestURL != request.url().string().impl()) + if (!request.isNull() && oldRequestURL != request.url().string().impl()) m_documentLoader->didTellClientAboutLoad(request.url()); if (Page* page = m_frame->page()) @@ -5187,124 +5282,9 @@ void FrameLoader::tellClientAboutPastMemoryCacheLoads() } } -#if USE(LOW_BANDWIDTH_DISPLAY) - -bool FrameLoader::addLowBandwidthDisplayRequest(CachedResource* cache) -{ - if (m_frame->document()->inLowBandwidthDisplay() == false) - return false; - - // if cache is loaded, don't add to the list, where notifyFinished() is expected. - if (cache->isLoaded()) - return false; - - switch (cache->type()) { - case CachedResource::CSSStyleSheet: - case CachedResource::Script: - m_needToSwitchOutLowBandwidthDisplay = true; - m_externalRequestsInLowBandwidthDisplay.add(cache); - cache->addClient(this); - return true; - case CachedResource::ImageResource: - case CachedResource::FontResource: -#if ENABLE(XSLT) - case CachedResource::XSLStyleSheet: -#endif -#if ENABLE(XBL) - case CachedResource::XBLStyleSheet: -#endif - return false; - } - - ASSERT_NOT_REACHED(); - return false; -} - -void FrameLoader::removeAllLowBandwidthDisplayRequests() -{ - HashSet<CachedResource*>::iterator end = m_externalRequestsInLowBandwidthDisplay.end(); - for (HashSet<CachedResource*>::iterator it = m_externalRequestsInLowBandwidthDisplay.begin(); it != end; ++it) - (*it)->removeClient(this); - m_externalRequestsInLowBandwidthDisplay.clear(); -} - -void FrameLoader::notifyFinished(CachedResource* script) -{ - HashSet<CachedResource*>::iterator it = m_externalRequestsInLowBandwidthDisplay.find(script); - if (it != m_externalRequestsInLowBandwidthDisplay.end()) { - (*it)->removeClient(this); - m_externalRequestsInLowBandwidthDisplay.remove(it); - switchOutLowBandwidthDisplayIfReady(); - } -} - -void FrameLoader::switchOutLowBandwidthDisplayIfReady() +bool FrameLoaderClient::hasHTMLView() const { - RefPtr<Document> oldDoc = m_frame->document(); - if (oldDoc->inLowBandwidthDisplay()) { - if (!m_needToSwitchOutLowBandwidthDisplay) { - // no need to switch, just reset state - oldDoc->setLowBandwidthDisplay(false); - removeAllLowBandwidthDisplayRequests(); - m_pendingSourceInLowBandwidthDisplay = String(); - m_finishedParsingDuringLowBandwidthDisplay = false; - return; - } else if (m_externalRequestsInLowBandwidthDisplay.isEmpty() || - m_pendingSourceInLowBandwidthDisplay.length() > cMaxPendingSourceLengthInLowBandwidthDisplay) { - // clear the flag first - oldDoc->setLowBandwidthDisplay(false); - - // similar to clear(), should be refactored to share more code - oldDoc->cancelParsing(); - oldDoc->detach(); - if (m_frame->script()->isEnabled()) - m_frame->script()->clearWindowShell(); - if (m_frame->view()) - m_frame->view()->clear(); - - // similar to begin(), should be refactored to share more code - RefPtr<Document> newDoc = DOMImplementation::createDocument(m_responseMIMEType, m_frame, m_frame->inViewSourceMode()); - m_frame->setDocument(newDoc); - newDoc->setURL(m_URL); - if (m_decoder) - newDoc->setDecoder(m_decoder.get()); - restoreDocumentState(); - dispatchWindowObjectAvailable(); - newDoc->implicitOpen(); - - // swap DocLoader ownership - DocLoader* docLoader = newDoc->docLoader(); - newDoc->setDocLoader(oldDoc->docLoader()); - newDoc->docLoader()->replaceDocument(newDoc.get()); - docLoader->replaceDocument(oldDoc.get()); - oldDoc->setDocLoader(docLoader); - - // drop the old doc - oldDoc = 0; - - // write decoded data to the new doc, similar to write() - if (m_pendingSourceInLowBandwidthDisplay.length()) { - if (m_decoder->encoding().usesVisualOrdering()) - newDoc->setVisuallyOrdered(); - newDoc->recalcStyle(Node::Force); - newDoc->tokenizer()->write(m_pendingSourceInLowBandwidthDisplay, true); - - if (m_finishedParsingDuringLowBandwidthDisplay) - newDoc->finishParsing(); - } - - // update rendering - newDoc->updateRendering(); - - // reset states - removeAllLowBandwidthDisplayRequests(); - m_pendingSourceInLowBandwidthDisplay = String(); - m_finishedParsingDuringLowBandwidthDisplay = false; - m_needToSwitchOutLowBandwidthDisplay = false; - } - } + return true; } -#endif - } // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/loader/FrameLoader.h b/src/3rdparty/webkit/WebCore/loader/FrameLoader.h index dd55552d04..a0c1c2984d 100644 --- a/src/3rdparty/webkit/WebCore/loader/FrameLoader.h +++ b/src/3rdparty/webkit/WebCore/loader/FrameLoader.h @@ -31,29 +31,27 @@ #define FrameLoader_h #include "CachePolicy.h" -#include "FormState.h" #include "FrameLoaderTypes.h" #include "ResourceRequest.h" +#include "ThreadableLoader.h" #include "Timer.h" -#if USE(LOW_BANDWIDTH_DISPLAY) -#include "CachedResourceClient.h" -#endif - namespace WebCore { class Archive; class AuthenticationChallenge; + class CachedFrame; class CachedPage; class CachedResource; class Document; class DocumentLoader; - class Element; class Event; class FormData; + class FormState; class Frame; class FrameLoaderClient; class HistoryItem; + class HTMLAppletElement; class HTMLFormElement; class HTMLFrameOwnerElement; class IconLoader; @@ -64,6 +62,7 @@ namespace WebCore { class ResourceLoader; class ResourceResponse; class ScriptSourceCode; + class ScriptString; class ScriptValue; class SecurityOrigin; class SharedBuffer; @@ -71,7 +70,6 @@ namespace WebCore { class TextResourceDecoder; class Widget; - struct FormSubmission; struct FrameLoadRequest; struct ScheduledRedirection; struct WindowFeatures; @@ -113,11 +111,7 @@ namespace WebCore { void* m_argument; }; - class FrameLoader : Noncopyable -#if USE(LOW_BANDWIDTH_DISPLAY) - , private CachedResourceClient -#endif - { + class FrameLoader : Noncopyable { public: FrameLoader(Frame*, FrameLoaderClient*); ~FrameLoader(); @@ -126,50 +120,42 @@ namespace WebCore { Frame* frame() const { return m_frame; } - // FIXME: This is not cool, people. We should aim to consolidate these variety of loading related methods into a smaller set, - // and try to reuse more of the same logic by extracting common code paths. + // FIXME: This is not cool, people. There are too many different functions that all start loads. + // We should aim to consolidate these into a smaller set of functions, and try to reuse more of + // the logic by extracting common code paths. + void prepareForLoadStart(); void setupForReplace(); void setupForReplaceByMIMEType(const String& newMIMEType); - void loadWithDocumentLoader(DocumentLoader*, FrameLoadType, PassRefPtr<FormState>); // Calls continueLoadAfterNavigationPolicy - void load(DocumentLoader*); // Calls loadWithDocumentLoader - - void loadWithNavigationAction(const ResourceRequest&, const NavigationAction&, // Calls loadWithDocumentLoader() - FrameLoadType, PassRefPtr<FormState>); - - void loadPostRequest(const ResourceRequest& inRequest, const String& referrer, // Called by loadFrameRequestWithFormAndValues(), calls loadWithNavigationAction - const String& frameName, Event* event, PassRefPtr<FormState> prpFormState); - - void loadURL(const KURL& newURL, const String& referrer, const String& frameName, // Called by loadFrameRequestWithFormAndValues(), calls loadWithNavigationAction or else dispatches to navigation policy delegate - FrameLoadType, Event* event, PassRefPtr<FormState> prpFormState); void loadURLIntoChildFrame(const KURL&, const String& referer, Frame*); - void loadFrameRequestWithFormAndValues(const FrameLoadRequest&, bool lockHistory, // Called by submitForm, calls loadPostRequest() - Event*, HTMLFormElement*, const HashMap<String, String>& formValues); + void loadFrameRequest(const FrameLoadRequest&, bool lockHistory, bool lockBackForwardList, // Called by submitForm, calls loadPostRequest and loadURL. + PassRefPtr<Event>, PassRefPtr<FormState>); - 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 load(ResourceRequest, SubstituteData). + void load(const ResourceRequest&, const SubstituteData&, bool lockHistory); // Called both by WebFrame and internally, calls load(DocumentLoader*). + void load(const ResourceRequest&, const String& frameName, bool lockHistory); // Called by WebPluginController. - void loadArchive(PassRefPtr<Archive> archive); + void loadArchive(PassRefPtr<Archive>); - // Returns true for any non-local URL. If Document parameter is supplied, its local load policy dictates, + // Returns true for any non-local URL. If document parameter is supplied, its local load policy dictates, // otherwise if referrer is non-empty and represents a local file, then the local load is allowed. - static bool canLoad(const KURL&, const String& referrer, const Document* theDocument = 0); + static bool canLoad(const KURL&, const String& referrer, const Document*); + static bool canLoad(const KURL&, const String& referrer, const SecurityOrigin* = 0); static void reportLocalLoadFailed(Frame*, const String& url); - static bool shouldHideReferrer(const KURL& url, const String& referrer); + static bool shouldHideReferrer(const KURL&, const String& referrer); // Called by createWindow in JSDOMWindowBase.cpp, e.g. to fulfill a modal dialog creation Frame* createWindow(FrameLoader* frameLoaderForFrameLookup, const FrameLoadRequest&, const WindowFeatures&, bool& created); - unsigned long loadResourceSynchronously(const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data); + unsigned long loadResourceSynchronously(const ResourceRequest&, StoredCredentials, ResourceError&, ResourceResponse&, Vector<char>& data); bool canHandleRequest(const ResourceRequest&); // Also not cool. - void stopAllLoaders(); + void stopAllLoaders(DatabasePolicy = DatabasePolicyStop); void stopForUserCancel(bool deferCheckLoadComplete = false); bool isLoadingMainResource() const { return m_isLoadingMainResource; } @@ -180,13 +166,12 @@ namespace WebCore { String referrer() const; String outgoingReferrer() const; String outgoingOrigin() const; - void loadEmptyDocumentSynchronously(); DocumentLoader* activeDocumentLoader() const; - DocumentLoader* documentLoader() const; - DocumentLoader* policyDocumentLoader() const; - DocumentLoader* provisionalDocumentLoader() const; - FrameState state() const; + DocumentLoader* documentLoader() const { return m_documentLoader.get(); } + DocumentLoader* policyDocumentLoader() const { return m_policyDocumentLoader.get(); } + DocumentLoader* provisionalDocumentLoader() const { return m_provisionalDocumentLoader.get(); } + FrameState state() const { return m_state; } static double timeOfLastCompletedLoad(); bool shouldUseCredentialStorage(ResourceLoader*); @@ -199,6 +184,7 @@ namespace WebCore { void didReceiveData(ResourceLoader*, const char*, int, int lengthReceived); void didFinishLoad(ResourceLoader*); void didFailToLoad(ResourceLoader*, const ResourceError&); + void didLoadResourceByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString); const ResourceRequest& originalRequest() const; const ResourceRequest& initialRequest() const; void receivedMainResourceError(const ResourceError&, bool isComplete); @@ -223,14 +209,12 @@ namespace WebCore { bool representationExistsForURLScheme(const String& URLScheme); String generatedMIMETypeForURLScheme(const String& URLScheme); - void notifyIconChanged(); - void checkNavigationPolicy(const ResourceRequest&, NavigationPolicyDecisionFunction function, void* argument); void checkContentPolicy(const String& MIMEType, ContentPolicyDecisionFunction, void* argument); void cancelContentPolicyCheck(); void reload(bool endToEndReload = false); - void reloadAllowingStaleData(const String& overrideEncoding); + void reloadWithOverrideEncoding(const String& overrideEncoding); void didReceiveServerRedirectForProvisionalLoadForFrame(); void finishedLoadingDocument(DocumentLoader*); @@ -245,55 +229,42 @@ namespace WebCore { void didChangeTitle(DocumentLoader*); FrameLoadType loadType() const; - CachePolicy cachePolicy() const; + CachePolicy subresourceCachePolicy() const; void didFirstLayout(); bool firstLayoutDone() const; void didFirstVisuallyNonEmptyLayout(); - void clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress); - void clientRedirected(const KURL&, double delay, double fireDate, bool lockHistory, 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 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, bool refresh = false); - void changeLocation(const KURL&, const String& referrer, bool lockHistory = true, bool userGesture = false, bool refresh = false); - void urlSelected(const ResourceRequest&, const String& target, Event*, bool lockHistory, bool userGesture); - void urlSelected(const FrameLoadRequest&, Event*, bool lockHistory); - + 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, PassRefPtr<Event>, bool lockHistory, bool lockBackForwardList, bool userGesture); 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 submitFormAgain(); - void submitForm(const FrameLoadRequest&, Event*); + void submitForm(const char* action, const String& url, + PassRefPtr<FormData>, const String& target, const String& contentType, const String& boundary, + bool lockHistory, bool lockBackForwardList, PassRefPtr<Event>, PassRefPtr<FormState>); void stop(); - void stopLoading(bool sendUnload); + void stopLoading(bool sendUnload, DatabasePolicy = DatabasePolicyStop); bool closeURL(); void didExplicitOpen(); @@ -302,24 +273,21 @@ namespace WebCore { void commitIconURLToIconDatabase(const KURL&); KURL baseURL() const; - String baseTarget() const; - KURL dataURLBaseFromRequest(const ResourceRequest& request) const; 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); bool canGoBackOrForward(int distance) const; void goBackOrForward(int distance); int getHistoryLength(); - KURL historyURL(int distance); void begin(); void begin(const KURL&, bool dispatchWindowObjectAvailable = true, SecurityOrigin* forcedSecurityOrigin = 0); - void write(const char* str, int len = -1, bool flush = false); + void write(const char* string, int length = -1, bool flush = false); void write(const String&); void end(); void endIfNotLoadingMainResource(); @@ -327,24 +295,20 @@ namespace WebCore { void setEncoding(const String& encoding, bool userChosen); String encoding() const; - // Returns true if url is a JavaScript URL. - bool executeIfJavaScriptURL(const KURL& url, bool userGesture = false, bool replaceDocument = true); - 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 - void scrollToAnchor(const KURL&); void tokenizerProcessedData(); void handledOnloadEvents(); String userAgent(const KURL&) const; - Widget* createJavaAppletWidget(const IntSize&, Element*, const HashMap<String, String>& args); + Widget* createJavaAppletWidget(const IntSize&, HTMLAppletElement*, const HashMap<String, String>& args); void dispatchWindowObjectAvailable(); + void dispatchDocumentElementAvailable(); void restoreDocumentState(); Frame* opener(); @@ -352,26 +316,18 @@ namespace WebCore { bool openedByDOM() const; void setOpenedByDOM(); - void provisionalLoadStarted(); - bool userGestureHint(); void resetMultipleFormSubmissionProtection(); - void didNotOpenURL(const KURL&); void addData(const char* bytes, int length); - bool canCachePage(); - void checkCallImplicitClose(); - bool didOpenURL(const KURL&); void frameDetached(); const KURL& url() const { return m_URL; } - void updateBaseURLForEmptyDocument(); - void setResponseMIMEType(const String&); const String& responseMIMEType() const; @@ -380,12 +336,6 @@ namespace WebCore { void loadDone(); void finishedParsing(); void checkCompleted(); - void scheduleCheckCompleted(); - void scheduleCheckLoadComplete(); - - void clearRecordedFormValues(); - void setFormAboutToBeSubmitted(PassRefPtr<HTMLFormElement> element); - void recordFormValue(const String& name, const String& value); bool isComplete() const; @@ -394,53 +344,33 @@ namespace WebCore { KURL completeURL(const String& url); - KURL originalRequestURL() const; - void cancelAndClear(); void setTitle(const String&); - - bool shouldTreatURLAsSameAsCurrent(const KURL&) const; void commitProvisionalLoad(PassRefPtr<CachedPage>); void goToItem(HistoryItem*, FrameLoadType); void saveDocumentAndScrollState(); - void saveScrollPositionAndViewStateToItem(HistoryItem*); - // FIXME: These accessors are here for a dwindling number of users in WebKit, WebFrame - // being the primary one. After they're no longer needed there, they can be removed! HistoryItem* currentHistoryItem(); - HistoryItem* previousHistoryItem(); - HistoryItem* provisionalHistoryItem(); void setCurrentHistoryItem(PassRefPtr<HistoryItem>); - void setPreviousHistoryItem(PassRefPtr<HistoryItem>); - void setProvisionalHistoryItem(PassRefPtr<HistoryItem>); - - void continueLoadWithData(SharedBuffer*, const String& mimeType, const String& textEncoding, const KURL&); enum LocalLoadPolicy { - AllowLocalLoadsForAll, // No restriction on local loads. - AllowLocalLoadsForLocalAndSubstituteData, - AllowLocalLoadsForLocalOnly, + AllowLocalLoadsForAll, // No restriction on local loads. + AllowLocalLoadsForLocalAndSubstituteData, + AllowLocalLoadsForLocalOnly, }; static void setLocalLoadPolicy(LocalLoadPolicy); static bool restrictAccessToLocal(); static bool allowSubstituteDataAccessToLocal(); - static void registerURLSchemeAsLocal(const String& scheme); + static void registerURLSchemeAsLocal(const String&); static bool shouldTreatURLAsLocal(const String&); - static bool shouldTreatSchemeAsLocal(const String&); - -#if USE(LOW_BANDWIDTH_DISPLAY) - bool addLowBandwidthDisplayRequest(CachedResource*); - void needToSwitchOutLowBandwidthDisplay() { m_needToSwitchOutLowBandwidthDisplay = true; } + static bool shouldTreatURLSchemeAsLocal(const String&); - // Client can control whether to use low bandwidth display on a per frame basis. - // However, this should only be used for the top frame, not sub-frame. - void setUseLowBandwidthDisplay(bool lowBandwidth) { m_useLowBandwidthDisplay = lowBandwidth; } - bool useLowBandwidthDisplay() const { return m_useLowBandwidthDisplay; } -#endif + static void registerURLSchemeAsNoAccess(const String&); + static bool shouldTreatURLSchemeAsNoAccess(const String&); bool committingFirstRealLoad() const { return !m_creatingInitialEmptyDocument && !m_committedFirstRealDocumentLoad; } @@ -453,10 +383,18 @@ namespace WebCore { void applyUserAgent(ResourceRequest& request); + bool shouldInterruptLoadForXFrameOptions(const String&, const KURL&); + private: 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(); @@ -469,7 +407,7 @@ namespace WebCore { void updateHistoryForBackForwardNavigation(); void updateHistoryForReload(); void updateHistoryForStandardLoad(); - void updateHistoryForRedirectWithLockedHistory(); + void updateHistoryForRedirectWithLockedBackForwardList(); void updateHistoryForClientRedirect(); void updateHistoryForCommit(); void updateHistoryForAnchorScroll(); @@ -494,8 +432,8 @@ namespace WebCore { void receivedFirstData(); - void updatePolicyBaseURL(); - void setPolicyBaseURL(const KURL&); + void updateFirstPartyForCookies(); + void setFirstPartyForCookies(const KURL&); void addExtraFieldsToRequest(ResourceRequest&, FrameLoadType loadType, bool isMainResource, bool cookiePolicyURLFromRequest); @@ -511,10 +449,8 @@ namespace WebCore { void setLoadType(FrameLoadType); - void checkNavigationPolicy(const ResourceRequest&, DocumentLoader*, PassRefPtr<FormState>, - NavigationPolicyDecisionFunction, void* argument); - void checkNewWindowPolicy(const NavigationAction&, const ResourceRequest&, - PassRefPtr<FormState>, const String& frameName); + void checkNavigationPolicy(const ResourceRequest&, DocumentLoader*, PassRefPtr<FormState>, NavigationPolicyDecisionFunction, void* argument); + void checkNewWindowPolicy(const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName); void continueAfterNavigationPolicy(PolicyAction); void continueAfterNewWindowPolicy(PolicyAction); @@ -527,13 +463,11 @@ namespace WebCore { void continueLoadAfterNewWindowPolicy(const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, bool shouldContinue); static void callContinueFragmentScrollAfterNavigationPolicy(void*, const ResourceRequest&, PassRefPtr<FormState>, bool shouldContinue); void continueFragmentScrollAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue); - bool shouldScrollToAnchor(bool isFormSubmission, FrameLoadType loadType, const KURL& url); + bool shouldScrollToAnchor(bool isFormSubmission, FrameLoadType, const KURL&); void addHistoryItemForFragmentScroll(); void stopPolicyCheck(); - void closeDocument(); - void checkLoadCompleteForThisFrame(); void setDocumentLoader(DocumentLoader*); @@ -544,7 +478,8 @@ namespace WebCore { void closeOldDataSources(); void open(CachedPage&); - void opened(); + void open(CachedFrame&); + void updateHistoryAfterClientRedirect(); void clear(bool clearWindowProperties = true, bool clearScriptObjects = true); @@ -556,14 +491,6 @@ namespace WebCore { void startRedirectionTimer(); void stopRedirectionTimer(); -#if USE(LOW_BANDWIDTH_DISPLAY) - // implementation of CachedResourceClient - virtual void notifyFinished(CachedResource*); - - void removeAllLowBandwidthDisplayRequests(); - void switchOutLowBandwidthDisplayIfReady(); -#endif - void dispatchDidCommitLoad(); void dispatchAssignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&); void dispatchWillSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse); @@ -572,6 +499,53 @@ namespace WebCore { void dispatchDidFinishLoading(DocumentLoader*, unsigned long identifier); static bool isLocationChange(const ScheduledRedirection&); + void scheduleFormSubmission(const FrameLoadRequest&, bool lockHistory, bool lockBackForwardList, PassRefPtr<Event>, PassRefPtr<FormState>); + + void loadWithDocumentLoader(DocumentLoader*, FrameLoadType, PassRefPtr<FormState>); // Calls continueLoadAfterNavigationPolicy + void load(DocumentLoader*); // Calls loadWithDocumentLoader + + void loadWithNavigationAction(const ResourceRequest&, const NavigationAction&, // Calls loadWithDocumentLoader + bool lockHistory, FrameLoadType, PassRefPtr<FormState>); + + void loadPostRequest(const ResourceRequest&, const String& referrer, // Called by loadFrameRequest, calls loadWithNavigationAction + const String& frameName, bool lockHistory, FrameLoadType, PassRefPtr<Event>, PassRefPtr<FormState>); + void loadURL(const KURL&, const String& referrer, const String& frameName, // Called by loadFrameRequest, calls loadWithNavigationAction or dispatches to navigation policy delegate + bool lockHistory, FrameLoadType, PassRefPtr<Event>, PassRefPtr<FormState>); + + void clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress); + void clientRedirected(const KURL&, double delay, double fireDate, bool lockBackForwardList); + bool shouldReload(const KURL& currentURL, const KURL& destinationURL); + + void sendRemainingDelegateMessages(unsigned long identifier, const ResourceResponse&, int length, const ResourceError&); + void requestFromDelegate(ResourceRequest&, unsigned long& identifier, ResourceError&); + + void recursiveCheckLoadComplete(); + + void detachChildren(); + void closeAndRemoveChild(Frame*); + + Frame* loadSubframe(HTMLFrameOwnerElement*, const KURL&, const String& name, const String& referrer); + + // Returns true if argument is a JavaScript URL. + bool executeIfJavaScriptURL(const KURL&, bool userGesture = false, bool replaceDocument = true); + + bool gotoAnchor(const String& name); // returns true if the anchor was found + void scrollToAnchor(const KURL&); + + void provisionalLoadStarted(); + + bool canCachePage(); + + bool didOpenURL(const KURL&); + + void scheduleCheckCompleted(); + void scheduleCheckLoadComplete(); + + KURL originalRequestURL() const; + + bool shouldTreatURLAsSameAsCurrent(const KURL&) const; + + void saveScrollPositionAndViewStateToItem(HistoryItem*); Frame* m_frame; FrameLoaderClient* m_client; @@ -601,12 +575,9 @@ namespace WebCore { bool m_quickRedirectComing; bool m_sentRedirectNotification; bool m_inStopAllLoaders; - bool m_navigationDuringLoad; String m_outgoingReferrer; - OwnPtr<FormSubmission> m_deferredFormSubmission; - bool m_isExecutingJavaScriptFormAction; bool m_isRunningScript; @@ -614,6 +585,7 @@ namespace WebCore { bool m_didCallImplicitClose; bool m_wasUnloadEventEmitted; + bool m_unloadEventBeingDispatched; bool m_isComplete; bool m_isLoadingMainResource; @@ -636,8 +608,6 @@ namespace WebCore { bool m_containsPlugIns; - RefPtr<HTMLFormElement> m_formAboutToBeSubmitted; - HashMap<String, String> m_formValuesAboutToBeSubmitted; KURL m_submittedFormURL; Timer<FrameLoader> m_redirectionTimer; @@ -663,26 +633,11 @@ namespace WebCore { bool m_didDispatchDidCommitLoad; #endif -#if USE(LOW_BANDWIDTH_DISPLAY) - // whether to use low bandwidth dislay, set by client - bool m_useLowBandwidthDisplay; - - // whether to call finishParsing() in switchOutLowBandwidthDisplayIfReady() - bool m_finishedParsingDuringLowBandwidthDisplay; - - // whether to call switchOutLowBandwidthDisplayIfReady; - // true if there is external css, javascript, or subframe/plugin - bool m_needToSwitchOutLowBandwidthDisplay; - - String m_pendingSourceInLowBandwidthDisplay; - HashSet<CachedResource*> m_externalRequestsInLowBandwidthDisplay; -#endif - #if ENABLE(WML) bool m_forceReloadWmlDeck; #endif }; -} +} // namespace WebCore -#endif +#endif // FrameLoader_h diff --git a/src/3rdparty/webkit/WebCore/loader/FrameLoaderClient.cpp b/src/3rdparty/webkit/WebCore/loader/FrameLoaderClient.cpp deleted file mode 100644 index 9610fd16ee..0000000000 --- a/src/3rdparty/webkit/WebCore/loader/FrameLoaderClient.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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/src/3rdparty/webkit/WebCore/loader/FrameLoaderClient.h b/src/3rdparty/webkit/WebCore/loader/FrameLoaderClient.h index 44ab4a4a2d..b862002551 100644 --- a/src/3rdparty/webkit/WebCore/loader/FrameLoaderClient.h +++ b/src/3rdparty/webkit/WebCore/loader/FrameLoaderClient.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 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 @@ -45,7 +45,7 @@ class NSView; namespace WebCore { class AuthenticationChallenge; - class CachedPage; + class CachedFrame; class Color; class DocumentLoader; class Element; @@ -53,31 +53,38 @@ namespace WebCore { class Frame; class FrameLoader; class HistoryItem; + class HTMLAppletElement; class HTMLFrameOwnerElement; + class HTMLPlugInElement; class IntSize; class KURL; class NavigationAction; class ResourceError; class ResourceHandle; class ResourceLoader; + struct ResourceRequest; class ResourceResponse; + class ScriptString; class SharedBuffer; class SubstituteData; class String; class Widget; - class ResourceRequest; - typedef void (FrameLoader::*FramePolicyFunction)(PolicyAction); class FrameLoaderClient { public: - virtual ~FrameLoaderClient(); + // An inline function cannot be the first non-abstract virtual function declared + // in the class as it results in the vtable being generated as a weak symbol. + // This hurts performance (in Mac OS X at least, when loadig frameworks), so we + // don't want to do it in WebKit. + virtual bool hasHTMLView() const; + + virtual ~FrameLoaderClient() { } + virtual void frameLoaderDestroyed() = 0; - - virtual bool hasWebView() const = 0; // mainly for assertions - virtual bool hasHTMLView() const { return true; } + virtual bool hasWebView() const = 0; // mainly for assertions virtual void makeRepresentation(DocumentLoader*) = 0; virtual void forceLayout() = 0; @@ -99,6 +106,7 @@ namespace WebCore { virtual void dispatchDidFinishLoading(DocumentLoader*, unsigned long identifier) = 0; virtual void dispatchDidFailLoading(DocumentLoader*, unsigned long identifier, const ResourceError&) = 0; virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int length) = 0; + virtual void dispatchDidLoadResourceByXMLHttpRequest(unsigned long identifier, const ScriptString&) = 0; virtual void dispatchDidHandleOnloadEvents() = 0; virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() = 0; @@ -151,6 +159,8 @@ namespace WebCore { virtual void finishedLoading(DocumentLoader*) = 0; virtual void updateGlobalHistory() = 0; + virtual void updateGlobalHistoryRedirectLinks() = 0; + virtual bool shouldGoToHistoryItem(HistoryItem*) const = 0; virtual ResourceError cancelledError(const ResourceRequest&) = 0; @@ -181,8 +191,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; @@ -190,15 +200,16 @@ namespace WebCore { virtual PassRefPtr<Frame> createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement, const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight) = 0; - virtual Widget* createPlugin(const IntSize&, Element*, const KURL&, const Vector<String>&, const Vector<String>&, const String&, bool loadManually) = 0; + virtual Widget* createPlugin(const IntSize&, HTMLPlugInElement*, const KURL&, const Vector<String>&, const Vector<String>&, const String&, bool loadManually) = 0; virtual void redirectDataToPlugin(Widget* pluginWidget) = 0; - virtual Widget* createJavaAppletWidget(const IntSize&, Element*, const KURL& baseURL, const Vector<String>& paramNames, const Vector<String>& paramValues) = 0; + virtual Widget* createJavaAppletWidget(const IntSize&, HTMLAppletElement*, const KURL& baseURL, const Vector<String>& paramNames, const Vector<String>& paramValues) = 0; virtual ObjectContentType objectContentType(const KURL& url, const String& mimeType) = 0; virtual String overrideMediaType() const = 0; virtual void windowObjectCleared() = 0; + virtual void documentElementAvailable() = 0; virtual void didPerformFirstNavigation() const = 0; // "Navigation" here means a transition from one page to another that ends up in the back/forward list. virtual void registerForIconNotification(bool listen = true) = 0; @@ -209,12 +220,11 @@ namespace WebCore { #endif virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long identifier, NSCachedURLResponse*) const = 0; #endif +#if USE(CFNETWORK) + virtual bool shouldCacheResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&, const unsigned char* data, unsigned long long length) = 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); + virtual bool shouldUsePluginDocument(const String& /*mimeType*/) const { return false; } }; } // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/loader/FrameLoaderTypes.h b/src/3rdparty/webkit/WebCore/loader/FrameLoaderTypes.h index b661f9f050..c264b470aa 100644 --- a/src/3rdparty/webkit/WebCore/loader/FrameLoaderTypes.h +++ b/src/3rdparty/webkit/WebCore/loader/FrameLoaderTypes.h @@ -51,9 +51,8 @@ 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, + FrameLoadTypeRedirectWithLockedBackForwardList, // FIXME: Merge "lockBackForwardList", "lockHistory", "quickRedirect" and "clientRedirect" into a single concept of redirect. FrameLoadTypeReplace, FrameLoadTypeReloadFromOrigin }; @@ -67,6 +66,11 @@ namespace WebCore { NavigationTypeOther }; + enum DatabasePolicy { + DatabasePolicyStop, // The database thread should be stopped and database connections closed. + DatabasePolicyContinue + }; + enum ObjectContentType { ObjectContentNone, ObjectContentImage, diff --git a/src/3rdparty/webkit/WebCore/loader/ImageDocument.cpp b/src/3rdparty/webkit/WebCore/loader/ImageDocument.cpp index 8f58179cc7..08f2e9afd3 100644 --- a/src/3rdparty/webkit/WebCore/loader/ImageDocument.cpp +++ b/src/3rdparty/webkit/WebCore/loader/ImageDocument.cpp @@ -25,6 +25,7 @@ #include "config.h" #include "ImageDocument.h" +#include "CSSStyleDeclaration.h" #include "CachedImage.h" #include "DocumentLoader.h" #include "Element.h" @@ -37,6 +38,7 @@ #include "HTMLNames.h" #include "LocalizedStrings.h" #include "MouseEvent.h" +#include "NotImplemented.h" #include "Page.h" #include "SegmentedString.h" #include "Settings.h" @@ -63,7 +65,7 @@ class ImageTokenizer : public Tokenizer { public: ImageTokenizer(ImageDocument* doc) : m_doc(doc) {} - virtual bool write(const SegmentedString&, bool appendData); + virtual void write(const SegmentedString&, bool appendData); virtual void finish(); virtual bool isWaitingForScripts() const; @@ -91,10 +93,10 @@ private: // -------- -bool ImageTokenizer::write(const SegmentedString&, bool) +void ImageTokenizer::write(const SegmentedString&, bool) { - ASSERT_NOT_REACHED(); - return false; + // <https://bugs.webkit.org/show_bug.cgi?id=25397>: JS code can always call document.write, we need to handle it. + notImplemented(); } bool ImageTokenizer::writeRawData(const char*, int) @@ -125,9 +127,9 @@ void ImageTokenizer::finish() IntSize size = cachedImage->imageSize(m_doc->frame()->pageZoomFactor()); if (size.width()) { - // Compute the title, we use the filename of the resource, falling - // back on the hostname if there is no path. - String fileName = m_doc->url().lastPathComponent(); + // Compute the title, we use the decoded filename of the resource, falling + // back on the (decoded) hostname if there is no path. + String fileName = decodeURLEscapeSequences(m_doc->url().lastPathComponent()); if (fileName.isEmpty()) fileName = m_doc->url().host(); m_doc->setTitle(imageTitle(fileName, size)); @@ -166,10 +168,10 @@ void ImageDocument::createDocumentStructure() { ExceptionCode ec; - RefPtr<Element> rootElement = createElementNS(xhtmlNamespaceURI, "html", ec); + RefPtr<Element> rootElement = Document::createElement(htmlTag, false); appendChild(rootElement, ec); - RefPtr<Element> body = createElementNS(xhtmlNamespaceURI, "body", ec); + RefPtr<Element> body = Document::createElement(bodyTag, false); body->setAttribute(styleAttr, "margin: 0px;"); rootElement->appendChild(body, ec); @@ -185,7 +187,8 @@ void ImageDocument::createDocumentStructure() if (shouldShrinkToFit()) { // Add event listeners RefPtr<EventListener> listener = ImageEventListener::create(this); - addWindowEventListener("resize", listener, false); + if (DOMWindow* domWindow = this->domWindow()) + domWindow->addEventListener("resize", listener, false); imageElement->addEventListener("click", listener.release(), false); } diff --git a/src/3rdparty/webkit/WebCore/loader/ImageLoader.cpp b/src/3rdparty/webkit/WebCore/loader/ImageLoader.cpp index 43e08c092a..b183a662f7 100644 --- a/src/3rdparty/webkit/WebCore/loader/ImageLoader.cpp +++ b/src/3rdparty/webkit/WebCore/loader/ImageLoader.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2009 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 @@ -31,8 +31,33 @@ namespace WebCore { -ImageLoader::ImageLoader(Element* elt) - : m_element(elt) +class ImageLoadEventSender { +public: + ImageLoadEventSender(); + + void dispatchLoadEventSoon(ImageLoader*); + void cancelLoadEvent(ImageLoader*); + + void dispatchPendingLoadEvents(); + +private: + ~ImageLoadEventSender(); + + void timerFired(Timer<ImageLoadEventSender>*); + + Timer<ImageLoadEventSender> m_timer; + Vector<ImageLoader*> m_dispatchSoonList; + Vector<ImageLoader*> m_dispatchingList; +}; + +static ImageLoadEventSender& loadEventSender() +{ + DEFINE_STATIC_LOCAL(ImageLoadEventSender, sender, ()); + return sender; +} + +ImageLoader::ImageLoader(Element* element) + : m_element(element) , m_image(0) , m_firedLoad(true) , m_imageComplete(true) @@ -44,7 +69,7 @@ ImageLoader::~ImageLoader() { if (m_image) m_image->removeClient(this); - m_element->document()->removeImage(this); + loadEventSender().cancelLoadEvent(this); } void ImageLoader::setImage(CachedImage* newImage) @@ -61,11 +86,11 @@ void ImageLoader::setImage(CachedImage* newImage) oldImage->removeClient(this); } - if (RenderObject* renderer = element()->renderer()) { + if (RenderObject* renderer = m_element->renderer()) { if (!renderer->isImage()) return; - static_cast<RenderImage*>(renderer)->resetAnimation(); + toRenderImage(renderer)->resetAnimation(); } } @@ -80,12 +105,11 @@ void ImageLoader::updateFromElement() { // If we're not making renderers for the page, then don't load images. We don't want to slow // down the raw HTML parsing case by loading images we don't intend to display. - Element* elem = element(); - Document* doc = elem->document(); - if (!doc->renderer()) + Document* document = m_element->document(); + if (!document->renderer()) return; - AtomicString attr = elem->getAttribute(elem->imageSourceAttributeName()); + AtomicString attr = m_element->getAttribute(m_element->imageSourceAttributeName()); if (attr == m_failedLoadURL) return; @@ -95,15 +119,15 @@ void ImageLoader::updateFromElement() // a quirk that preserves old behavior that Dashboard widgets // need (<rdar://problem/5994621>). CachedImage* newImage = 0; - if (!(attr.isNull() || attr.isEmpty() && doc->baseURI().isLocalFile())) { + if (!(attr.isNull() || (attr.isEmpty() && document->baseURI().isLocalFile()))) { if (m_loadManually) { - doc->docLoader()->setAutoLoadImages(false); + document->docLoader()->setAutoLoadImages(false); newImage = new CachedImage(sourceURI(attr)); newImage->setLoading(true); - newImage->setDocLoader(doc->docLoader()); - doc->docLoader()->m_documentResources.set(newImage->url(), newImage); + newImage->setDocLoader(document->docLoader()); + document->docLoader()->m_documentResources.set(newImage->url(), newImage); } else - newImage = doc->docLoader()->requestImage(sourceURI(attr)); + newImage = document->docLoader()->requestImage(sourceURI(attr)); // If we do not have an image here, it means that a cross-site // violation occurred. @@ -119,11 +143,11 @@ void ImageLoader::updateFromElement() oldImage->removeClient(this); } - if (RenderObject* renderer = elem->renderer()) { + if (RenderObject* renderer = m_element->renderer()) { if (!renderer->isImage()) return; - static_cast<RenderImage*>(renderer)->resetAnimation(); + toRenderImage(renderer)->resetAnimation(); } } @@ -139,15 +163,85 @@ void ImageLoader::notifyFinished(CachedResource*) ASSERT(m_failedLoadURL.isEmpty()); m_imageComplete = true; - Element* elem = element(); - elem->document()->dispatchImageLoadEventSoon(this); + loadEventSender().dispatchLoadEventSoon(this); - if (RenderObject* renderer = elem->renderer()) { + if (RenderObject* renderer = m_element->renderer()) { if (!renderer->isImage()) return; - static_cast<RenderImage*>(renderer)->setCachedImage(m_image.get()); + toRenderImage(renderer)->setCachedImage(m_image.get()); } } +void ImageLoader::dispatchPendingLoadEvent() +{ + if (m_firedLoad) + return; + if (!m_image) + return; + if (!m_element->document()->attached()) + return; + m_firedLoad = true; + dispatchLoadEvent(); +} + +void ImageLoader::dispatchPendingLoadEvents() +{ + loadEventSender().dispatchPendingLoadEvents(); +} + +ImageLoadEventSender::ImageLoadEventSender() + : m_timer(this, &ImageLoadEventSender::timerFired) +{ +} + +void ImageLoadEventSender::dispatchLoadEventSoon(ImageLoader* loader) +{ + m_dispatchSoonList.append(loader); + if (!m_timer.isActive()) + m_timer.startOneShot(0); +} + +void ImageLoadEventSender::cancelLoadEvent(ImageLoader* loader) +{ + // Remove instances of this loader from both lists. + // Use loops because we allow multiple instances to get into the lists. + size_t size = m_dispatchSoonList.size(); + for (size_t i = 0; i < size; ++i) { + if (m_dispatchSoonList[i] == loader) + m_dispatchSoonList[i] = 0; + } + size = m_dispatchingList.size(); + for (size_t i = 0; i < size; ++i) { + if (m_dispatchingList[i] == loader) + m_dispatchingList[i] = 0; + } + if (m_dispatchSoonList.isEmpty()) + m_timer.stop(); +} + +void ImageLoadEventSender::dispatchPendingLoadEvents() +{ + // Need to avoid re-entering this function; if new dispatches are + // scheduled before the parent finishes processing the list, they + // will set a timer and eventually be processed. + if (!m_dispatchingList.isEmpty()) + return; + + m_timer.stop(); + + m_dispatchingList.swap(m_dispatchSoonList); + size_t size = m_dispatchingList.size(); + for (size_t i = 0; i < size; ++i) { + if (ImageLoader* loader = m_dispatchingList[i]) + loader->dispatchPendingLoadEvent(); + } + m_dispatchingList.clear(); +} + +void ImageLoadEventSender::timerFired(Timer<ImageLoadEventSender>*) +{ + dispatchPendingLoadEvents(); +} + } diff --git a/src/3rdparty/webkit/WebCore/loader/ImageLoader.h b/src/3rdparty/webkit/WebCore/loader/ImageLoader.h index fc3a58ac51..3496f75c75 100644 --- a/src/3rdparty/webkit/WebCore/loader/ImageLoader.h +++ b/src/3rdparty/webkit/WebCore/loader/ImageLoader.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2004 Apple Computer, Inc. + * Copyright (C) 2004, 2009 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,22 +30,21 @@ namespace WebCore { class Element; +class ImageLoadEventSender; class ImageLoader : public CachedResourceClient { public: ImageLoader(Element*); virtual ~ImageLoader(); + // This function should be called when the element is attached to a document; starts + // loading if a load hasn't already been started. 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. + // This function should be called whenever the 'src' attribute is set, even if its value + // doesn't change; starts new load unconditionally (matches Firefox and Opera behavior). void updateFromElementIgnoringPreviousError(); - virtual void dispatchLoadEvent() = 0; - virtual String sourceURI(const AtomicString&) const = 0; - Element* element() const { return m_element; } bool imageComplete() const { return m_imageComplete; } @@ -54,16 +53,22 @@ public: void setLoadManually(bool loadManually) { m_loadManually = loadManually; } - // CachedResourceClient API - virtual void notifyFinished(CachedResource*); - bool haveFiredLoadEvent() const { return m_firedLoad; } + + static void dispatchPendingLoadEvents(); + protected: - void setLoadingImage(CachedImage*); - - void setHaveFiredLoadEvent(bool firedLoad) { m_firedLoad = firedLoad; } + virtual void notifyFinished(CachedResource*); private: + virtual void dispatchLoadEvent() = 0; + virtual String sourceURI(const AtomicString&) const = 0; + + friend class ImageLoadEventSender; + void dispatchPendingLoadEvent(); + + void setLoadingImage(CachedImage*); + Element* m_element; CachedResourceHandle<CachedImage> m_image; AtomicString m_failedLoadURL; diff --git a/src/3rdparty/webkit/WebCore/loader/MainResourceLoader.cpp b/src/3rdparty/webkit/WebCore/loader/MainResourceLoader.cpp index 325809b7ba..39e5b90195 100644 --- a/src/3rdparty/webkit/WebCore/loader/MainResourceLoader.cpp +++ b/src/3rdparty/webkit/WebCore/loader/MainResourceLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, 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 @@ -30,12 +30,8 @@ #include "config.h" #include "MainResourceLoader.h" -#if ENABLE(OFFLINE_WEB_APPLICATIONS) -#include "ApplicationCache.h" -#include "ApplicationCacheGroup.h" -#include "ApplicationCacheResource.h" -#endif #include "DocumentLoader.h" +#include "FormState.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" @@ -45,6 +41,12 @@ #include "ResourceHandle.h" #include "Settings.h" +#if ENABLE(OFFLINE_WEB_APPLICATIONS) +#include "ApplicationCache.h" +#include "ApplicationCacheGroup.h" +#include "ApplicationCacheResource.h" +#endif + // FIXME: More that is in common with SubresourceLoader should move up into ResourceLoader. namespace WebCore { @@ -161,7 +163,7 @@ void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const Reso // Update cookie policy base URL as URL changes, except for subframes, which use the // URL of the main frame which doesn't change when we redirect. if (frameLoader()->isLoadingMainFrame()) - newRequest.setMainDocumentURL(newRequest.url()); + newRequest.setFirstPartyForCookies(newRequest.url()); // If we're fielding a redirect in response to a POST, force a load from origin, since // this is a common site technique to return to a page viewing some data that the POST @@ -179,14 +181,19 @@ void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const Reso // listener. But there's no way to do that in practice. So instead we cancel later if the // listener tells us to. In practice that means the navigation policy needs to be decided // synchronously for these redirect cases. - - ref(); // balanced by deref in continueAfterNavigationPolicy - frameLoader()->checkNavigationPolicy(newRequest, callContinueAfterNavigationPolicy, this); + if (!redirectResponse.isNull()) { + ref(); // balanced by deref in continueAfterNavigationPolicy + frameLoader()->checkNavigationPolicy(newRequest, callContinueAfterNavigationPolicy, this); + } } static bool shouldLoadAsEmptyDocument(const KURL& url) { - return url.isEmpty() || equalIgnoringCase(String(url.protocol()), "about"); +#if PLATFORM(TORCHMOBILE) + return url.isEmpty() || (url.protocolIs("about") && equalIgnoringRef(url, blankURL())); +#else + return url.isEmpty() || url.protocolIs("about"); +#endif } void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy, const ResourceResponse& r) @@ -248,7 +255,7 @@ void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy, if (!reachedTerminalState()) ResourceLoader::didReceiveResponse(r); - if (frameLoader() && !frameLoader()->isStopping()) + if (frameLoader() && !frameLoader()->isStopping()) { if (m_substituteData.isValid()) { if (m_substituteData.content()->size()) didReceiveData(m_substituteData.content()->data(), m_substituteData.content()->size(), m_substituteData.content()->size(), true); @@ -256,6 +263,7 @@ void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy, didFinishLoading(); } else if (shouldLoadAsEmptyDocument(url) || frameLoader()->representationExistsForURLScheme(url.protocol())) didFinishLoading(); + } } void MainResourceLoader::callContinueAfterContentPolicy(void* argument, PolicyAction policy) @@ -286,6 +294,15 @@ void MainResourceLoader::didReceiveResponse(const ResourceResponse& r) } #endif + HTTPHeaderMap::const_iterator it = r.httpHeaderFields().find(AtomicString("x-frame-options")); + if (it != r.httpHeaderFields().end()) { + String content = it->second; + if (m_frame->loader()->shouldInterruptLoadForXFrameOptions(content, r.url())) { + cancel(); + return; + } + } + // 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) @@ -319,6 +336,17 @@ void MainResourceLoader::didReceiveData(const char* data, int length, long long ASSERT(data); ASSERT(length != 0); + ASSERT(!m_response.isNull()); + +#if USE(CFNETWORK) || (PLATFORM(MAC) && !defined(BUILDING_ON_TIGER)) + // Workaround for <rdar://problem/6060782> + if (m_response.isNull()) { + m_response = ResourceResponse(KURL(), "text/html", 0, String(), String()); + if (DocumentLoader* documentLoader = frameLoader()->activeDocumentLoader()) + documentLoader->setResponse(m_response); + } +#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) @@ -396,7 +424,7 @@ void MainResourceLoader::handleEmptyLoad(const KURL& url, bool forURLScheme) didReceiveResponse(response); } -void MainResourceLoader::handleDataLoadNow(Timer<MainResourceLoader>*) +void MainResourceLoader::handleDataLoadNow(MainResourceLoaderTimer*) { RefPtr<MainResourceLoader> protect(this); @@ -408,12 +436,22 @@ void MainResourceLoader::handleDataLoadNow(Timer<MainResourceLoader>*) didReceiveResponse(response); } +void MainResourceLoader::startDataLoadTimer() +{ + m_dataLoadTimer.startOneShot(0); + +#if HAVE(RUNLOOP_TIMER) + if (SchedulePairHashSet* scheduledPairs = m_frame->page()->scheduledRunLoopPairs()) + m_dataLoadTimer.schedule(*scheduledPairs); +#endif +} + void MainResourceLoader::handleDataLoadSoon(ResourceRequest& r) { m_initialRequest = r; if (m_documentLoader->deferMainResourceDataLoad()) - m_dataLoadTimer.startOneShot(0); + startDataLoadTimer(); else handleDataLoadNow(0); } @@ -497,17 +535,16 @@ bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& su void MainResourceLoader::setDefersLoading(bool defers) { ResourceLoader::setDefersLoading(defers); - + if (defers) { if (m_dataLoadTimer.isActive()) m_dataLoadTimer.stop(); } else { if (m_initialRequest.isNull()) return; - - if (m_substituteData.isValid() && - m_documentLoader->deferMainResourceDataLoad()) - m_dataLoadTimer.startOneShot(0); + + if (m_substituteData.isValid() && m_documentLoader->deferMainResourceDataLoad()) + startDataLoadTimer(); else { ResourceRequest r(m_initialRequest); m_initialRequest = ResourceRequest(); diff --git a/src/3rdparty/webkit/WebCore/loader/MainResourceLoader.h b/src/3rdparty/webkit/WebCore/loader/MainResourceLoader.h index 6c69c1f79d..d9ce2f9b21 100644 --- a/src/3rdparty/webkit/WebCore/loader/MainResourceLoader.h +++ b/src/3rdparty/webkit/WebCore/loader/MainResourceLoader.h @@ -29,16 +29,21 @@ #include "FrameLoaderTypes.h" #include "ResourceLoader.h" #include "SubstituteData.h" -#include "Timer.h" #include <wtf/Forward.h> +#if HAVE(RUNLOOP_TIMER) +#include "RunLoopTimer.h" +#else +#include "Timer.h" +#endif + namespace WebCore { #if ENABLE(OFFLINE_WEB_APPLICATIONS) class ApplicationCache; #endif class FormState; - class ResourceRequest; + struct ResourceRequest; class MainResourceLoader : public ResourceLoader { public: @@ -56,7 +61,13 @@ namespace WebCore { virtual void didFinishLoading(); virtual void didFail(const ResourceError&); - void handleDataLoadNow(Timer<MainResourceLoader>*); +#if HAVE(RUNLOOP_TIMER) + typedef RunLoopTimer<MainResourceLoader> MainResourceLoaderTimer; +#else + typedef Timer<MainResourceLoader> MainResourceLoaderTimer; +#endif + + void handleDataLoadNow(MainResourceLoaderTimer*); bool isLoadingMultipartContent() const { return m_loadingMultipartContent; } @@ -74,6 +85,7 @@ namespace WebCore { void handleEmptyLoad(const KURL&, bool forURLScheme); void handleDataLoadSoon(ResourceRequest& r); + void startDataLoadTimer(); void handleDataLoad(ResourceRequest&); void receivedError(const ResourceError&); @@ -90,7 +102,8 @@ namespace WebCore { ResourceRequest m_initialRequest; SubstituteData m_substituteData; - Timer<MainResourceLoader> m_dataLoadTimer; + + MainResourceLoaderTimer m_dataLoadTimer; #if ENABLE(OFFLINE_WEB_APPLICATIONS) // The application cache that the main resource was loaded from (if any). diff --git a/src/3rdparty/webkit/WebCore/loader/MediaDocument.cpp b/src/3rdparty/webkit/WebCore/loader/MediaDocument.cpp index 8ed5b45b59..0b1fd594ef 100644 --- a/src/3rdparty/webkit/WebCore/loader/MediaDocument.cpp +++ b/src/3rdparty/webkit/WebCore/loader/MediaDocument.cpp @@ -38,7 +38,9 @@ #include "HTMLEmbedElement.h" #include "HTMLNames.h" #include "HTMLVideoElement.h" +#include "KeyboardEvent.h" #include "MainResourceLoader.h" +#include "NodeList.h" #include "Page.h" #include "SegmentedString.h" #include "Settings.h" @@ -54,7 +56,7 @@ public: MediaTokenizer(Document* doc) : m_doc(doc), m_mediaElement(0) {} private: - virtual bool write(const SegmentedString&, bool appendData); + virtual void write(const SegmentedString&, bool appendData); virtual void stopParsing(); virtual void finish(); virtual bool isWaitingForScripts() const; @@ -68,24 +70,23 @@ private: HTMLMediaElement* m_mediaElement; }; -bool MediaTokenizer::write(const SegmentedString&, bool) +void MediaTokenizer::write(const SegmentedString&, bool) { ASSERT_NOT_REACHED(); - return false; } void MediaTokenizer::createDocumentStructure() { ExceptionCode ec; - RefPtr<Element> rootElement = m_doc->createElementNS(xhtmlNamespaceURI, "html", ec); + RefPtr<Element> rootElement = m_doc->createElement(htmlTag, false); m_doc->appendChild(rootElement, ec); - RefPtr<Element> body = m_doc->createElementNS(xhtmlNamespaceURI, "body", ec); + RefPtr<Element> body = m_doc->createElement(bodyTag, false); body->setAttribute(styleAttr, "background-color: rgb(38,38,38);"); rootElement->appendChild(body, ec); - RefPtr<Element> mediaElement = m_doc->createElementNS(xhtmlNamespaceURI, "video", ec); + RefPtr<Element> mediaElement = m_doc->createElement(videoTag, false); m_mediaElement = static_cast<HTMLVideoElement*>(mediaElement.get()); m_mediaElement->setAttribute(controlsAttr, ""); @@ -134,10 +135,16 @@ bool MediaTokenizer::isWaitingForScripts() const MediaDocument::MediaDocument(Frame* frame) : HTMLDocument(frame) + , m_replaceMediaElementTimer(this, &MediaDocument::replaceMediaElementTimerFired) { setParseMode(Compat); } +MediaDocument::~MediaDocument() +{ + ASSERT(!m_replaceMediaElementTimer.isActive()); +} + Tokenizer* MediaDocument::createTokenizer() { return new MediaTokenizer(this); @@ -147,23 +154,85 @@ void MediaDocument::defaultEventHandler(Event* event) { // Match the default Quicktime plugin behavior to allow // clicking and double-clicking to pause and play the media. - EventTargetNode* targetNode = event->target()->toNode(); + Node* targetNode = event->target()->toNode(); if (targetNode && targetNode->hasTagName(videoTag)) { HTMLVideoElement* video = static_cast<HTMLVideoElement*>(targetNode); - ExceptionCode ec; if (event->type() == eventNames().clickEvent) { if (!video->canPlay()) { - video->pause(ec); + video->pause(); event->setDefaultHandled(); } } else if (event->type() == eventNames().dblclickEvent) { if (video->canPlay()) { - video->play(ec); + video->play(); + event->setDefaultHandled(); + } + } + } + + if (event->type() == eventNames().keydownEvent && event->isKeyboardEvent()) { + HTMLVideoElement* video = 0; + if (targetNode) { + if (targetNode->hasTagName(videoTag)) + video = static_cast<HTMLVideoElement*>(targetNode); + else { + RefPtr<NodeList> nodeList = targetNode->getElementsByTagName("video"); + if (nodeList.get()->length() > 0) + video = static_cast<HTMLVideoElement*>(nodeList.get()->item(0)); + } + } + if (video) { + KeyboardEvent* keyboardEvent = static_cast<KeyboardEvent*>(event); + if (keyboardEvent->keyIdentifier() == "U+0020") { // space + if (video->paused()) { + if (video->canPlay()) + video->play(); + } else + video->pause(); event->setDefaultHandled(); } } } } +void MediaDocument::mediaElementSawUnsupportedTracks() +{ + // The HTMLMediaElement was told it has something that the underlying + // MediaPlayer cannot handle so we should switch from <video> to <embed> + // and let the plugin handle this. Don't do it immediately as this + // function may be called directly from a media engine callback, and + // replaceChild will destroy the element, media player, and media engine. + m_replaceMediaElementTimer.startOneShot(0); +} + +void MediaDocument::replaceMediaElementTimerFired(Timer<MediaDocument>*) +{ + HTMLElement* htmlBody = body(); + if (!htmlBody) + return; + + // Set body margin width and height to 0 as that is what a PluginDocument uses. + htmlBody->setAttribute(marginwidthAttr, "0"); + htmlBody->setAttribute(marginheightAttr, "0"); + + RefPtr<NodeList> nodeList = htmlBody->getElementsByTagName("video"); + + if (nodeList.get()->length() > 0) { + HTMLVideoElement* videoElement = static_cast<HTMLVideoElement*>(nodeList.get()->item(0)); + + RefPtr<Element> element = Document::createElement(embedTag, false); + HTMLEmbedElement* embedElement = static_cast<HTMLEmbedElement*>(element.get()); + + embedElement->setAttribute(widthAttr, "100%"); + embedElement->setAttribute(heightAttr, "100%"); + embedElement->setAttribute(nameAttr, "plugin"); + embedElement->setSrc(url().string()); + embedElement->setType(frame()->loader()->responseMIMEType()); + + ExceptionCode ec; + videoElement->parent()->replaceChild(embedElement, videoElement, ec); + } +} + } #endif diff --git a/src/3rdparty/webkit/WebCore/loader/MediaDocument.h b/src/3rdparty/webkit/WebCore/loader/MediaDocument.h index e5167e4e7d..ac286f0af6 100644 --- a/src/3rdparty/webkit/WebCore/loader/MediaDocument.h +++ b/src/3rdparty/webkit/WebCore/loader/MediaDocument.h @@ -41,11 +41,17 @@ public: virtual void defaultEventHandler(Event*); + void mediaElementSawUnsupportedTracks(); + private: MediaDocument(Frame*); + virtual ~MediaDocument(); + Timer<MediaDocument> m_replaceMediaElementTimer; virtual bool isMediaDocument() const { return true; } virtual Tokenizer* createTokenizer(); + + void replaceMediaElementTimerFired(Timer<MediaDocument>*); }; } diff --git a/src/3rdparty/webkit/WebCore/loader/NetscapePlugInStreamLoader.cpp b/src/3rdparty/webkit/WebCore/loader/NetscapePlugInStreamLoader.cpp index e12ed07e3e..9d0e81b4f2 100644 --- a/src/3rdparty/webkit/WebCore/loader/NetscapePlugInStreamLoader.cpp +++ b/src/3rdparty/webkit/WebCore/loader/NetscapePlugInStreamLoader.cpp @@ -119,6 +119,11 @@ void NetscapePlugInStreamLoader::didCancel(const ResourceError& error) m_client->didFail(this, error); + // If calling didFail spins the run loop the stream loader can reach the terminal state. + // If that's the case we just return early. + if (reachedTerminalState()) + return; + // We need to remove the stream loader after the call to didFail, since didFail can // spawn a new run loop and if the loader has been removed it won't be deferred when // the document loader is asked to defer loading. diff --git a/src/3rdparty/webkit/WebCore/loader/PluginDocument.cpp b/src/3rdparty/webkit/WebCore/loader/PluginDocument.cpp index 42c801ca94..373126faca 100644 --- a/src/3rdparty/webkit/WebCore/loader/PluginDocument.cpp +++ b/src/3rdparty/webkit/WebCore/loader/PluginDocument.cpp @@ -49,7 +49,7 @@ public: PluginTokenizer(Document* doc) : m_doc(doc), m_embedElement(0) {} private: - virtual bool write(const SegmentedString&, bool appendData); + virtual void write(const SegmentedString&, bool appendData); virtual void stopParsing(); virtual void finish(); virtual bool isWaitingForScripts() const; @@ -63,26 +63,25 @@ private: HTMLEmbedElement* m_embedElement; }; -bool PluginTokenizer::write(const SegmentedString&, bool) +void PluginTokenizer::write(const SegmentedString&, bool) { ASSERT_NOT_REACHED(); - return false; } void PluginTokenizer::createDocumentStructure() { ExceptionCode ec; - RefPtr<Element> rootElement = m_doc->createElementNS(xhtmlNamespaceURI, "html", ec); + RefPtr<Element> rootElement = m_doc->createElement(htmlTag, false); m_doc->appendChild(rootElement, ec); - - RefPtr<Element> body = m_doc->createElementNS(xhtmlNamespaceURI, "body", ec); + + RefPtr<Element> body = m_doc->createElement(bodyTag, false); body->setAttribute(marginwidthAttr, "0"); body->setAttribute(marginheightAttr, "0"); body->setAttribute(bgcolorAttr, "rgb(38,38,38)"); rootElement->appendChild(body, ec); - RefPtr<Element> embedElement = m_doc->createElementNS(xhtmlNamespaceURI, "embed", ec); + RefPtr<Element> embedElement = m_doc->createElement(embedTag, false); m_embedElement = static_cast<HTMLEmbedElement*>(embedElement.get()); m_embedElement->setAttribute(widthAttr, "100%"); diff --git a/src/3rdparty/webkit/WebCore/loader/ProgressTracker.cpp b/src/3rdparty/webkit/WebCore/loader/ProgressTracker.cpp index 56aa976d82..e682b9b867 100644 --- a/src/3rdparty/webkit/WebCore/loader/ProgressTracker.cpp +++ b/src/3rdparty/webkit/WebCore/loader/ProgressTracker.cpp @@ -26,11 +26,12 @@ #include "config.h" #include "ProgressTracker.h" +#include "DocumentLoader.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" #include "ResourceResponse.h" -#include "SystemTime.h" +#include <wtf/CurrentTime.h> using std::min; @@ -196,8 +197,11 @@ void ProgressTracker::incrementProgress(unsigned long identifier, const char*, i else percentOfRemainingBytes = 1.0; - // Treat the first layout as the half-way point. - double maxProgressValue = m_originatingProgressFrame->loader()->firstLayoutDone() ? finalProgressValue : .5; + // For documents that use WebCore's layout system, treat first layout as the half-way point. + // FIXME: The hasHTMLView function is a sort of roundabout way of asking "do you use WebCore's layout system". + bool useClampedMaxProgress = m_originatingProgressFrame->loader()->client()->hasHTMLView() + && !m_originatingProgressFrame->loader()->firstLayoutDone(); + double maxProgressValue = useClampedMaxProgress ? 0.5 : finalProgressValue; increment = (maxProgressValue - m_progressValue) * percentOfRemainingBytes; m_progressValue += increment; m_progressValue = min(m_progressValue, maxProgressValue); diff --git a/src/3rdparty/webkit/WebCore/loader/ResourceLoader.cpp b/src/3rdparty/webkit/WebCore/loader/ResourceLoader.cpp index 3bac2a7556..e2fc7ba3ed 100644 --- a/src/3rdparty/webkit/WebCore/loader/ResourceLoader.cpp +++ b/src/3rdparty/webkit/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 { } @@ -122,10 +119,8 @@ bool ResourceLoader::load(const ResourceRequest& r) return true; #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) { @@ -200,7 +195,6 @@ bool ResourceLoader::scheduleLoadFallbackResourceFromApplicationCache(Applicatio { if (documentLoader()->scheduleLoadFallbackResourceFromApplicationCache(this, m_request, cache)) { handle()->cancel(); - m_wasLoadedFromApplicationCache = true; return true; } return false; diff --git a/src/3rdparty/webkit/WebCore/loader/ResourceLoader.h b/src/3rdparty/webkit/WebCore/loader/ResourceLoader.h index 83e56f5c0c..d3e7b80163 100644 --- a/src/3rdparty/webkit/WebCore/loader/ResourceLoader.h +++ b/src/3rdparty/webkit/WebCore/loader/ResourceLoader.h @@ -106,6 +106,9 @@ namespace WebCore { #if PLATFORM(MAC) virtual NSCachedURLResponse* willCacheResponse(ResourceHandle*, NSCachedURLResponse*); #endif +#if USE(CFNETWORK) + virtual bool shouldCacheResponse(ResourceHandle*, CFCachedURLResponseRef); +#endif ResourceHandle* handle() const { return m_handle.get(); } bool sendResourceLoadCallbacks() const { return m_sendResourceLoadCallbacks; } @@ -146,9 +149,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/src/3rdparty/webkit/WebCore/loader/SubresourceLoader.cpp b/src/3rdparty/webkit/WebCore/loader/SubresourceLoader.cpp index 4a339ef8aa..047cc6d00b 100644 --- a/src/3rdparty/webkit/WebCore/loader/SubresourceLoader.cpp +++ b/src/3rdparty/webkit/WebCore/loader/SubresourceLoader.cpp @@ -66,7 +66,7 @@ PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, Subresourc return 0; FrameLoader* fl = frame->loader(); - if (!skipCanLoadCheck && fl->state() == FrameStateProvisional) + if (!skipCanLoadCheck && (fl->state() == FrameStateProvisional || fl->activeDocumentLoader()->isStopping())) return 0; ResourceRequest newRequest = request; @@ -222,6 +222,13 @@ void SubresourceLoader::didCancel(const ResourceError& error) if (cancelled()) return; + + // The only way the subresource loader can reach the terminal state here is if the run loop spins when calling + // m_client->didFail. This should in theory not happen which is why the assert is here. + ASSERT(!reachedTerminalState()); + if (reachedTerminalState()) + return; + m_documentLoader->removeSubresourceLoader(this); ResourceLoader::didCancel(error); } diff --git a/src/3rdparty/webkit/WebCore/loader/SubresourceLoader.h b/src/3rdparty/webkit/WebCore/loader/SubresourceLoader.h index 1a94c73b71..0fce930412 100644 --- a/src/3rdparty/webkit/WebCore/loader/SubresourceLoader.h +++ b/src/3rdparty/webkit/WebCore/loader/SubresourceLoader.h @@ -33,7 +33,7 @@ namespace WebCore { - class ResourceRequest; + struct ResourceRequest; class SubresourceLoaderClient; class SubresourceLoader : public ResourceLoader { diff --git a/src/3rdparty/webkit/WebCore/loader/SubresourceLoaderClient.h b/src/3rdparty/webkit/WebCore/loader/SubresourceLoaderClient.h index 4e14deac2a..acf8e6afea 100644 --- a/src/3rdparty/webkit/WebCore/loader/SubresourceLoaderClient.h +++ b/src/3rdparty/webkit/WebCore/loader/SubresourceLoaderClient.h @@ -33,7 +33,7 @@ namespace WebCore { class AuthenticationChallenge; class ResourceError; -class ResourceRequest; +struct ResourceRequest; class ResourceResponse; class SubresourceLoader; @@ -46,7 +46,7 @@ public: 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&) { } diff --git a/src/3rdparty/webkit/WebCore/loader/TextDocument.cpp b/src/3rdparty/webkit/WebCore/loader/TextDocument.cpp index bd2c446053..0d86c1b5dd 100644 --- a/src/3rdparty/webkit/WebCore/loader/TextDocument.cpp +++ b/src/3rdparty/webkit/WebCore/loader/TextDocument.cpp @@ -41,9 +41,10 @@ using namespace HTMLNames; class TextTokenizer : public Tokenizer { public: TextTokenizer(Document*); + virtual ~TextTokenizer(); TextTokenizer(HTMLViewSourceDocument*); - virtual bool write(const SegmentedString&, bool appendData); + virtual void write(const SegmentedString&, bool appendData); virtual void finish(); virtual bool isWaitingForScripts() const; @@ -91,9 +92,15 @@ TextTokenizer::TextTokenizer(HTMLViewSourceDocument* doc) m_size = 254; m_buffer = static_cast<UChar*>(fastMalloc(sizeof(UChar) * m_size)); m_dest = m_buffer; -} +} + +TextTokenizer::~TextTokenizer() +{ + // finish() should have been called to prevent any leaks + ASSERT(!m_buffer); +} -bool TextTokenizer::write(const SegmentedString& s, bool) +void TextTokenizer::write(const SegmentedString& s, bool) { ExceptionCode ec; @@ -125,13 +132,13 @@ bool TextTokenizer::write(const SegmentedString& s, bool) } if (!m_preElement && !inViewSourceMode()) { - RefPtr<Element> rootElement = m_doc->createElementNS(xhtmlNamespaceURI, "html", ec); + RefPtr<Element> rootElement = m_doc->createElement(htmlTag, false); m_doc->appendChild(rootElement, ec); - RefPtr<Element> body = m_doc->createElementNS(xhtmlNamespaceURI, "body", ec); + RefPtr<Element> body = m_doc->createElement(bodyTag, false); rootElement->appendChild(body, ec); - RefPtr<Element> preElement = m_doc->createElementNS(xhtmlNamespaceURI, "pre", ec); + RefPtr<Element> preElement = m_doc->createElement(preTag, false); preElement->setAttribute("style", "word-wrap: break-word; white-space: pre-wrap;", ec); body->appendChild(preElement, ec); @@ -142,7 +149,7 @@ bool TextTokenizer::write(const SegmentedString& s, bool) String string = String(m_buffer, m_dest - m_buffer); if (inViewSourceMode()) { static_cast<HTMLViewSourceDocument*>(m_doc)->addViewSourceText(string); - return false; + return; } unsigned charsLeft = string.length(); @@ -151,15 +158,15 @@ bool TextTokenizer::write(const SegmentedString& s, bool) RefPtr<Text> text = Text::createWithLengthLimit(m_doc, string, charsLeft); m_preElement->appendChild(text, ec); } - - return false; } void TextTokenizer::finish() { m_preElement = 0; fastFree(m_buffer); - + m_buffer = 0; + m_dest = 0; + m_doc->finishedParsing(); } diff --git a/src/3rdparty/webkit/WebCore/loader/TextResourceDecoder.cpp b/src/3rdparty/webkit/WebCore/loader/TextResourceDecoder.cpp index f37d8f71b2..ee81326343 100644 --- a/src/3rdparty/webkit/WebCore/loader/TextResourceDecoder.cpp +++ b/src/3rdparty/webkit/WebCore/loader/TextResourceDecoder.cpp @@ -1,6 +1,6 @@ /* Copyright (C) 1999 Lars Knoll (knoll@mpi-hd.mpg.de) - Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. Copyright (C) 2005, 2006, 2007 Alexey Proskuryakov (ap@nypop.com) This library is free software; you can redistribute it and/or @@ -26,6 +26,9 @@ #include "DOMImplementation.h" #include "HTMLNames.h" #include "TextCodec.h" +#include "TextEncoding.h" +#include "TextEncodingDetector.h" +#include "TextEncodingRegistry.h" #include <wtf/ASCIICType.h> #include <wtf/StringExtras.h> @@ -320,14 +323,17 @@ const TextEncoding& TextResourceDecoder::defaultEncoding(ContentType contentType return specifiedDefaultEncoding; } -TextResourceDecoder::TextResourceDecoder(const String& mimeType, const TextEncoding& specifiedDefaultEncoding) +TextResourceDecoder::TextResourceDecoder(const String& mimeType, const TextEncoding& specifiedDefaultEncoding, bool usesEncodingDetector) : m_contentType(determineContentType(mimeType)) - , m_decoder(defaultEncoding(m_contentType, specifiedDefaultEncoding)) + , m_encoding(defaultEncoding(m_contentType, specifiedDefaultEncoding)) , m_source(DefaultEncoding) + , m_hintEncoding(0) , m_checkedForBOM(false) , m_checkedForCSSCharset(false) , m_checkedForHeadCharset(false) + , m_useLenientXMLDecoding(false) , m_sawError(false) + , m_usesEncodingDetector(usesEncodingDetector) { } @@ -344,12 +350,13 @@ void TextResourceDecoder::setEncoding(const TextEncoding& encoding, EncodingSour // When encoding comes from meta tag (i.e. it cannot be XML files sent via XHR), // treat x-user-defined as windows-1252 (bug 18270) if (source == EncodingFromMetaTag && strcasecmp(encoding.name(), "x-user-defined") == 0) - m_decoder.reset("windows-1252"); + m_encoding = "windows-1252"; else if (source == EncodingFromMetaTag || source == EncodingFromXMLHeader || source == EncodingFromCSSCharset) - m_decoder.reset(encoding.closestByteBasedEquivalent()); + m_encoding = encoding.closestByteBasedEquivalent(); else - m_decoder.reset(encoding); + m_encoding = encoding; + m_codec.clear(); m_source = source; } @@ -401,51 +408,54 @@ static inline bool skipWhitespace(const char*& pos, const char* dataEnd) return pos != dataEnd; } -void TextResourceDecoder::checkForBOM(const char* data, size_t len) +size_t TextResourceDecoder::checkForBOM(const char* data, size_t len) { // Check for UTF-16/32 or UTF-8 BOM mark at the beginning, which is a sure sign of a Unicode encoding. + // We let it override even a user-chosen encoding. + ASSERT(!m_checkedForBOM); - if (m_source == UserChosenEncoding) { - // FIXME: Maybe a BOM should override even a user-chosen encoding. - m_checkedForBOM = true; - return; - } + size_t lengthOfBOM = 0; - // Check if we have enough data. size_t bufferLength = m_buffer.size(); - if (bufferLength + len < 4) - return; - - m_checkedForBOM = true; - // Extract the first four bytes. - // Handle the case where some of bytes are already in the buffer. - // The last byte is always guaranteed to not be in the buffer. - const unsigned char* udata = reinterpret_cast<const unsigned char*>(data); - unsigned char c1 = bufferLength >= 1 ? m_buffer[0] : *udata++; - unsigned char c2 = bufferLength >= 2 ? m_buffer[1] : *udata++; - unsigned char c3 = bufferLength >= 3 ? m_buffer[2] : *udata++; - ASSERT(bufferLength < 4); - unsigned char c4 = *udata; + size_t buf1Len = bufferLength; + size_t buf2Len = len; + const unsigned char* buf1 = reinterpret_cast<const unsigned char*>(m_buffer.data()); + const unsigned char* buf2 = reinterpret_cast<const unsigned char*>(data); + unsigned char c1 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0; + unsigned char c2 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0; + unsigned char c3 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0; + unsigned char c4 = buf2Len ? (--buf2Len, *buf2++) : 0; // Check for the BOM. if (c1 == 0xFF && c2 == 0xFE) { - if (c3 !=0 || c4 != 0) + if (c3 != 0 || c4 != 0) { setEncoding(UTF16LittleEndianEncoding(), AutoDetectedEncoding); - else + lengthOfBOM = 2; + } else { setEncoding(UTF32LittleEndianEncoding(), AutoDetectedEncoding); - } - else if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) + lengthOfBOM = 4; + } + } else if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) { setEncoding(UTF8Encoding(), AutoDetectedEncoding); - else if (c1 == 0xFE && c2 == 0xFF) + lengthOfBOM = 3; + } else if (c1 == 0xFE && c2 == 0xFF) { setEncoding(UTF16BigEndianEncoding(), AutoDetectedEncoding); - else if (c1 == 0 && c2 == 0 && c3 == 0xFE && c4 == 0xFF) + lengthOfBOM = 2; + } else if (c1 == 0 && c2 == 0 && c3 == 0xFE && c4 == 0xFF) { setEncoding(UTF32BigEndianEncoding(), AutoDetectedEncoding); + lengthOfBOM = 4; + } + + if (lengthOfBOM || bufferLength + len >= 4) + m_checkedForBOM = true; + + return lengthOfBOM; } bool TextResourceDecoder::checkForCSSCharset(const char* data, size_t len, bool& movedDataToBuffer) { - if (m_source != DefaultEncoding) { + if (m_source != DefaultEncoding && m_source != EncodingFromParentFrame) { m_checkedForCSSCharset = true; return true; } @@ -526,7 +536,7 @@ const int bytesToCheckUnconditionally = 1024; // That many input bytes will be c bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool& movedDataToBuffer) { - if (m_source != DefaultEncoding) { + if (m_source != DefaultEncoding && m_source != EncodingFromParentFrame) { m_checkedForHeadCharset = true; return true; } @@ -636,7 +646,7 @@ bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool ptr++; continue; } - if (c >= 'a' && c <= 'z' || c >= '0' && c <= '9') + if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) ; else if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; @@ -695,8 +705,8 @@ bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool break; if (str[pos++] != '=') continue; - while (pos < length && - (str[pos] <= ' ') || str[pos] == '=' || str[pos] == '"' || str[pos] == '\'') + while ((pos < length) && + (str[pos] <= ' ' || str[pos] == '=' || str[pos] == '"' || str[pos] == '\'')) pos++; // end ? @@ -753,10 +763,28 @@ void TextResourceDecoder::detectJapaneseEncoding(const char* data, size_t len) } } +// We use the encoding detector in two cases: +// 1. Encoding detector is turned ON and no other encoding source is +// available (that is, it's DefaultEncoding). +// 2. Encoding detector is turned ON and the encoding is set to +// the encoding of the parent frame, which is also auto-detected. +// Note that condition #2 is NOT satisfied unless parent-child frame +// relationship is compliant to the same-origin policy. If they're from +// different domains, |m_source| would not be set to EncodingFromParentFrame +// in the first place. +bool TextResourceDecoder::shouldAutoDetect() const +{ + // Just checking m_hintEncoding suffices here because it's only set + // in setHintEncoding when the source is AutoDetectedEncoding. + return m_usesEncodingDetector + && (m_source == DefaultEncoding || (m_source == EncodingFromParentFrame && m_hintEncoding)); +} + String TextResourceDecoder::decode(const char* data, size_t len) { + size_t lengthOfBOM = 0; if (!m_checkedForBOM) - checkForBOM(data, len); + lengthOfBOM = checkForBOM(data, len); bool movedDataToBuffer = false; @@ -768,15 +796,32 @@ String TextResourceDecoder::decode(const char* data, size_t len) if (!checkForHeadCharset(data, len, movedDataToBuffer)) return ""; - // Do the auto-detect if our default encoding is one of the Japanese ones. - // FIXME: It seems wrong to change our encoding downstream after we have already done some decoding. - if (m_source != UserChosenEncoding && m_source != AutoDetectedEncoding && encoding().isJapanese()) + // FIXME: It seems wrong to change our encoding downstream after + // we have already done some decoding. However, it's not possible + // to avoid in a sense in two cases below because triggering conditions + // for both cases depend on the information that won't be available + // until we do partial read. + // The first case had better be removed altogether (see bug 21990) + // or at least be made to be invoked only when the encoding detection + // is turned on. + // Do the auto-detect 1) using Japanese detector if our default encoding is + // one of the Japanese detector or 2) using detectTextEncoding if encoding + // detection is turned on. + if (m_source != UserChosenEncoding && m_source != AutoDetectedEncoding && m_encoding.isJapanese()) detectJapaneseEncoding(data, len); + else if (shouldAutoDetect()) { + TextEncoding detectedEncoding; + if (detectTextEncoding(data, len, m_hintEncoding, &detectedEncoding)) + setEncoding(detectedEncoding, AutoDetectedEncoding); + } + + ASSERT(m_encoding.isValid()); - ASSERT(encoding().isValid()); + if (!m_codec) + m_codec.set(newTextCodec(m_encoding).release()); if (m_buffer.isEmpty()) - return m_decoder.decode(data, len, false, m_contentType == XML, m_sawError); + return m_codec->decode(data + lengthOfBOM, len - lengthOfBOM, false, m_contentType == XML, m_sawError); if (!movedDataToBuffer) { size_t oldSize = m_buffer.size(); @@ -784,16 +829,31 @@ String TextResourceDecoder::decode(const char* data, size_t len) memcpy(m_buffer.data() + oldSize, data, len); } - String result = m_decoder.decode(m_buffer.data(), m_buffer.size(), false, m_contentType == XML, m_sawError); + String result = m_codec->decode(m_buffer.data() + lengthOfBOM, m_buffer.size() - lengthOfBOM, false, m_contentType == XML && !m_useLenientXMLDecoding, m_sawError); m_buffer.clear(); return result; } String TextResourceDecoder::flush() { - String result = m_decoder.decode(m_buffer.data(), m_buffer.size(), true, m_contentType == XML, m_sawError); + // If we can not identify the encoding even after a document is completely + // loaded, we need to detect the encoding if other conditions for + // autodetection is satisfied. + if (m_buffer.size() && shouldAutoDetect() + && ((!m_checkedForHeadCharset && (m_contentType == HTML || m_contentType == XML)) || (!m_checkedForCSSCharset && (m_contentType == CSS)))) { + TextEncoding detectedEncoding; + if (detectTextEncoding(m_buffer.data(), m_buffer.size(), + m_hintEncoding, &detectedEncoding)) + setEncoding(detectedEncoding, AutoDetectedEncoding); + } + + if (!m_codec) + m_codec.set(newTextCodec(m_encoding).release()); + + String result = m_codec->decode(m_buffer.data(), m_buffer.size(), true, m_contentType == XML && !m_useLenientXMLDecoding, m_sawError); m_buffer.clear(); - m_decoder.reset(m_decoder.encoding()); + m_codec.clear(); + m_checkedForBOM = false; // Skip BOM again when re-decoding. return result; } diff --git a/src/3rdparty/webkit/WebCore/loader/TextResourceDecoder.h b/src/3rdparty/webkit/WebCore/loader/TextResourceDecoder.h index 8bbe85e3c4..fb755c9664 100644 --- a/src/3rdparty/webkit/WebCore/loader/TextResourceDecoder.h +++ b/src/3rdparty/webkit/WebCore/loader/TextResourceDecoder.h @@ -23,7 +23,7 @@ #ifndef TextResourceDecoder_h #define TextResourceDecoder_h -#include "TextDecoder.h" +#include "TextEncoding.h" namespace WebCore { @@ -36,43 +36,59 @@ public: EncodingFromMetaTag, EncodingFromCSSCharset, EncodingFromHTTPHeader, - UserChosenEncoding + UserChosenEncoding, + EncodingFromParentFrame }; - static PassRefPtr<TextResourceDecoder> create(const String& mimeType, const TextEncoding& defaultEncoding = TextEncoding()) + static PassRefPtr<TextResourceDecoder> create(const String& mimeType, const TextEncoding& defaultEncoding = TextEncoding(), bool usesEncodingDetector = false) { - return adoptRef(new TextResourceDecoder(mimeType, defaultEncoding)); + return adoptRef(new TextResourceDecoder(mimeType, defaultEncoding, usesEncodingDetector)); } ~TextResourceDecoder(); void setEncoding(const TextEncoding&, EncodingSource); - const TextEncoding& encoding() const { return m_decoder.encoding(); } + const TextEncoding& encoding() const { return m_encoding; } String decode(const char* data, size_t length); String flush(); - + + void setHintEncoding(const TextResourceDecoder* hintDecoder) + { + // hintEncoding is for use with autodetection, which should be + // only invoked when hintEncoding comes from auto-detection. + if (hintDecoder && hintDecoder->m_source == AutoDetectedEncoding) + m_hintEncoding = hintDecoder->encoding().name(); + } + + void useLenientXMLDecoding() { m_useLenientXMLDecoding = true; } bool sawError() const { return m_sawError; } private: - TextResourceDecoder(const String& mimeType, const TextEncoding& defaultEncoding); + TextResourceDecoder(const String& mimeType, const TextEncoding& defaultEncoding, + bool usesEncodingDetector); - enum ContentType { PlainText, HTML, XML, CSS }; // PlainText is equivalent to directly using TextDecoder. + enum ContentType { PlainText, HTML, XML, CSS }; // PlainText only checks for BOM. static ContentType determineContentType(const String& mimeType); static const TextEncoding& defaultEncoding(ContentType, const TextEncoding& defaultEncoding); - void checkForBOM(const char*, size_t); + size_t checkForBOM(const char*, size_t); bool checkForCSSCharset(const char*, size_t, bool& movedDataToBuffer); bool checkForHeadCharset(const char*, size_t, bool& movedDataToBuffer); void detectJapaneseEncoding(const char*, size_t); + bool shouldAutoDetect() const; ContentType m_contentType; - TextDecoder m_decoder; + TextEncoding m_encoding; + OwnPtr<TextCodec> m_codec; EncodingSource m_source; + const char* m_hintEncoding; Vector<char> m_buffer; bool m_checkedForBOM; bool m_checkedForCSSCharset; bool m_checkedForHeadCharset; + bool m_useLenientXMLDecoding; // Don't stop on XML decoding errors. bool m_sawError; + bool m_usesEncodingDetector; }; } diff --git a/src/3rdparty/webkit/WebCore/loader/ThreadableLoader.cpp b/src/3rdparty/webkit/WebCore/loader/ThreadableLoader.cpp new file mode 100644 index 0000000000..3927624216 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/loader/ThreadableLoader.cpp @@ -0,0 +1,72 @@ +/* + * 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 "ScriptExecutionContext.h" +#include "Document.h" +#include "DocumentThreadableLoader.h" +#include "WorkerContext.h" +#include "WorkerRunLoop.h" +#include "WorkerThreadableLoader.h" + +namespace WebCore { + +PassRefPtr<ThreadableLoader> ThreadableLoader::create(ScriptExecutionContext* context, ThreadableLoaderClient* client, const ResourceRequest& request, LoadCallbacks callbacksSetting, ContentSniff contentSniff, StoredCredentials storedCredentials) +{ + ASSERT(client); + ASSERT(context); + +#if ENABLE(WORKERS) + if (context->isWorkerContext()) + return WorkerThreadableLoader::create(static_cast<WorkerContext*>(context), client, WorkerRunLoop::defaultMode(), request, callbacksSetting, contentSniff, storedCredentials); +#endif // ENABLE(WORKERS) + + ASSERT(context->isDocument()); + return DocumentThreadableLoader::create(static_cast<Document*>(context), client, request, callbacksSetting, contentSniff, storedCredentials); +} + +void ThreadableLoader::loadResourceSynchronously(ScriptExecutionContext* context, const ResourceRequest& request, ThreadableLoaderClient& client, StoredCredentials storedCredentials) +{ + ASSERT(context); + +#if ENABLE(WORKERS) + if (context->isWorkerContext()) { + WorkerThreadableLoader::loadResourceSynchronously(static_cast<WorkerContext*>(context), request, client, storedCredentials); + return; + } +#endif // ENABLE(WORKERS) + + ASSERT(context->isDocument()); + DocumentThreadableLoader::loadResourceSynchronously(static_cast<Document*>(context), request, client, storedCredentials); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/loader/ThreadableLoader.h b/src/3rdparty/webkit/WebCore/loader/ThreadableLoader.h new file mode 100644 index 0000000000..b0cff7dd1c --- /dev/null +++ b/src/3rdparty/webkit/WebCore/loader/ThreadableLoader.h @@ -0,0 +1,80 @@ +/* + * 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; + struct ResourceRequest; + class ResourceResponse; + class ScriptExecutionContext; + class ThreadableLoaderClient; + + enum LoadCallbacks { + SendLoadCallbacks, + DoNotSendLoadCallbacks + }; + + enum ContentSniff { + SniffContent, + DoNotSniffContent + }; + + enum StoredCredentials { + AllowStoredCredentials, + DoNotAllowStoredCredentials + }; + + // 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 void loadResourceSynchronously(ScriptExecutionContext*, const ResourceRequest&, ThreadableLoaderClient&, StoredCredentials); + static PassRefPtr<ThreadableLoader> create(ScriptExecutionContext*, ThreadableLoaderClient*, const ResourceRequest&, LoadCallbacks, ContentSniff, StoredCredentials); + + 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/src/3rdparty/webkit/WebCore/loader/ThreadableLoaderClient.h b/src/3rdparty/webkit/WebCore/loader/ThreadableLoaderClient.h new file mode 100644 index 0000000000..93a8e869df --- /dev/null +++ b/src/3rdparty/webkit/WebCore/loader/ThreadableLoaderClient.h @@ -0,0 +1,57 @@ +/* + * 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 ResourceError; + 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 /*identifier*/) { } + virtual void didFail(const ResourceError&) { } + virtual void didFailRedirectCheck() { } + + virtual void didReceiveAuthenticationCancellation(const ResourceResponse&) { } + + protected: + virtual ~ThreadableLoaderClient() { } + }; + +} // namespace WebCore + +#endif // ThreadableLoaderClient_h diff --git a/src/3rdparty/webkit/WebCore/loader/ThreadableLoaderClientWrapper.h b/src/3rdparty/webkit/WebCore/loader/ThreadableLoaderClientWrapper.h new file mode 100644 index 0000000000..d3c1a9feea --- /dev/null +++ b/src/3rdparty/webkit/WebCore/loader/ThreadableLoaderClientWrapper.h @@ -0,0 +1,117 @@ +/* + * 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 ThreadableLoaderClientWrapper_h +#define ThreadableLoaderClientWrapper_h + +#include "ThreadableLoaderClient.h" +#include <wtf/Noncopyable.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Threading.h> + +namespace WebCore { + + class ThreadableLoaderClientWrapper : public ThreadSafeShared<ThreadableLoaderClientWrapper> { + public: + static PassRefPtr<ThreadableLoaderClientWrapper> create(ThreadableLoaderClient* client) + { + return adoptRef(new ThreadableLoaderClientWrapper(client)); + } + + void clearClient() + { + m_done = true; + m_client = 0; + } + + bool done() const + { + return m_done; + } + + void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) + { + if (m_client) + m_client->didSendData(bytesSent, totalBytesToBeSent); + } + + void didReceiveResponse(const ResourceResponse& response) + { + if (m_client) + m_client->didReceiveResponse(response); + } + + void didReceiveData(const char* data, int lengthReceived) + { + if (m_client) + m_client->didReceiveData(data, lengthReceived); + } + + void didFinishLoading(unsigned long identifier) + { + m_done = true; + if (m_client) + m_client->didFinishLoading(identifier); + } + + void didFail(const ResourceError& error) + { + m_done = true; + if (m_client) + m_client->didFail(error); + } + + void didFailRedirectCheck() + { + m_done = true; + if (m_client) + m_client->didFailRedirectCheck(); + } + + void didReceiveAuthenticationCancellation(const ResourceResponse& response) + { + if (m_client) + m_client->didReceiveResponse(response); + } + + protected: + ThreadableLoaderClientWrapper(ThreadableLoaderClient* client) + : m_client(client) + , m_done(false) + { + } + + ThreadableLoaderClient* m_client; + bool m_done; + }; + +} // namespace WebCore + +#endif // ThreadableLoaderClientWrapper_h diff --git a/src/3rdparty/webkit/WebCore/loader/WorkerThreadableLoader.cpp b/src/3rdparty/webkit/WebCore/loader/WorkerThreadableLoader.cpp new file mode 100644 index 0000000000..beaf061d04 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/loader/WorkerThreadableLoader.cpp @@ -0,0 +1,261 @@ +/* + * 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" + +#if ENABLE(WORKERS) + +#include "WorkerThreadableLoader.h" + +#include "GenericWorkerTask.h" +#include "ResourceError.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include "ThreadableLoader.h" +#include "WorkerContext.h" +#include "WorkerMessagingProxy.h" +#include "WorkerThread.h" +#include <memory> +#include <wtf/OwnPtr.h> +#include <wtf/Threading.h> +#include <wtf/Vector.h> + +using namespace std; + +namespace WebCore { + +static const char loadResourceSynchronouslyMode[] = "loadResourceSynchronouslyMode"; + +// FIXME: The assumption that we can upcast worker object proxy to WorkerMessagingProxy will not be true in multi-process implementation. +WorkerThreadableLoader::WorkerThreadableLoader(WorkerContext* workerContext, ThreadableLoaderClient* client, const String& taskMode, const ResourceRequest& request, LoadCallbacks callbacksSetting, + ContentSniff contentSniff, StoredCredentials storedCredentials) + : m_workerContext(workerContext) + , m_workerClientWrapper(ThreadableLoaderClientWrapper::create(client)) + , m_bridge(*(new MainThreadBridge(m_workerClientWrapper, *(static_cast<WorkerMessagingProxy*>(m_workerContext->thread()->workerObjectProxy())), taskMode, request, callbacksSetting, + contentSniff, storedCredentials))) +{ +} + +WorkerThreadableLoader::~WorkerThreadableLoader() +{ + m_bridge.destroy(); +} + +void WorkerThreadableLoader::loadResourceSynchronously(WorkerContext* workerContext, const ResourceRequest& request, ThreadableLoaderClient& client, StoredCredentials storedCredentials) +{ + WorkerRunLoop& runLoop = workerContext->thread()->runLoop(); + + // Create a unique mode just for this synchronous resource load. + String mode = loadResourceSynchronouslyMode; + mode.append(String::number(runLoop.createUniqueId())); + + ContentSniff contentSniff = request.url().isLocalFile() ? SniffContent : DoNotSniffContent; + RefPtr<WorkerThreadableLoader> loader = WorkerThreadableLoader::create(workerContext, &client, mode, request, DoNotSendLoadCallbacks, contentSniff, storedCredentials); + + MessageQueueWaitResult result = MessageQueueMessageReceived; + while (!loader->done() && result != MessageQueueTerminated) + result = runLoop.runInMode(workerContext, mode); + + if (!loader->done() && result == MessageQueueTerminated) + loader->cancel(); +} + +void WorkerThreadableLoader::cancel() +{ + m_bridge.cancel(); +} + +WorkerThreadableLoader::MainThreadBridge::MainThreadBridge(PassRefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, WorkerMessagingProxy& messagingProxy, const String& taskMode, + const ResourceRequest& request, LoadCallbacks callbacksSetting, ContentSniff contentSniff, StoredCredentials storedCredentials) + : m_workerClientWrapper(workerClientWrapper) + , m_messagingProxy(messagingProxy) + , m_taskMode(taskMode.copy()) +{ + ASSERT(m_workerClientWrapper.get()); + m_messagingProxy.postTaskToWorkerObject(createCallbackTask(&MainThreadBridge::mainThreadCreateLoader, this, request, callbacksSetting, contentSniff, storedCredentials)); +} + +WorkerThreadableLoader::MainThreadBridge::~MainThreadBridge() +{ +} + +void WorkerThreadableLoader::MainThreadBridge::mainThreadCreateLoader(ScriptExecutionContext* context, MainThreadBridge* thisPtr, auto_ptr<CrossThreadResourceRequestData> requestData, LoadCallbacks callbacksSetting, ContentSniff contentSniff, StoredCredentials storedCredentials) +{ + // FIXME: This assert fails for nested workers. Removing the assert would allow it to work, + // but then there would be one WorkerThreadableLoader in every intermediate worker simply + // chaining the requests, which is not very good. Instead, the postTaskToWorkerObject should be a + // postTaskToDocumentContext. + ASSERT(isMainThread()); + ASSERT(context->isDocument()); + + if (thisPtr->m_messagingProxy.askedToTerminate()) + return; + + // FIXME: the created loader has no knowledge of the origin of the worker doing the load request. + // Basically every setting done in SubresourceLoader::create (including the contents of addExtraFieldsToRequest) + // needs to be examined for how it should take into account a different originator. + OwnPtr<ResourceRequest> request(ResourceRequest::adopt(requestData)); + // FIXME: If the a site requests a local resource, then this will return a non-zero value but the sync path + // will return a 0 value. Either this should return 0 or the other code path should do a callback with + // a failure. + thisPtr->m_mainThreadLoader = ThreadableLoader::create(context, thisPtr, *request, callbacksSetting, contentSniff, storedCredentials); + ASSERT(thisPtr->m_mainThreadLoader); +} + +void WorkerThreadableLoader::MainThreadBridge::mainThreadDestroy(ScriptExecutionContext* context, MainThreadBridge* thisPtr) +{ + ASSERT(isMainThread()); + ASSERT_UNUSED(context, context->isDocument()); + delete thisPtr; +} + +void WorkerThreadableLoader::MainThreadBridge::destroy() +{ + // Ensure that no more client callbacks are done in the worker context's thread. + clearClientWrapper(); + + // "delete this" and m_mainThreadLoader::deref() on the worker object's thread. + m_messagingProxy.postTaskToWorkerObject(createCallbackTask(&MainThreadBridge::mainThreadDestroy, this)); +} + +void WorkerThreadableLoader::MainThreadBridge::mainThreadCancel(ScriptExecutionContext* context, MainThreadBridge* thisPtr) +{ + ASSERT(isMainThread()); + ASSERT_UNUSED(context, context->isDocument()); + + if (!thisPtr->m_mainThreadLoader) + return; + thisPtr->m_mainThreadLoader->cancel(); + thisPtr->m_mainThreadLoader = 0; +} + +void WorkerThreadableLoader::MainThreadBridge::cancel() +{ + m_messagingProxy.postTaskToWorkerObject(createCallbackTask(&MainThreadBridge::mainThreadCancel, this)); + ThreadableLoaderClientWrapper* clientWrapper = static_cast<ThreadableLoaderClientWrapper*>(m_workerClientWrapper.get()); + if (!clientWrapper->done()) { + // If the client hasn't reached a termination state, then transition it by sending a cancellation error. + // Note: no more client callbacks will be done after this method -- the clearClientWrapper() call ensures that. + ResourceError error(String(), 0, String(), String()); + error.setIsCancellation(true); + clientWrapper->didFail(error); + } + clearClientWrapper(); +} + +void WorkerThreadableLoader::MainThreadBridge::clearClientWrapper() +{ + static_cast<ThreadableLoaderClientWrapper*>(m_workerClientWrapper.get())->clearClient(); +} + +static void workerContextDidSendData(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +{ + ASSERT_UNUSED(context, context->isWorkerContext()); + workerClientWrapper->didSendData(bytesSent, totalBytesToBeSent); +} + +void WorkerThreadableLoader::MainThreadBridge::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) +{ + m_messagingProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidSendData, m_workerClientWrapper, bytesSent, totalBytesToBeSent), m_taskMode); +} + +static void workerContextDidReceiveResponse(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, auto_ptr<CrossThreadResourceResponseData> responseData) +{ + ASSERT_UNUSED(context, context->isWorkerContext()); + OwnPtr<ResourceResponse> response(ResourceResponse::adopt(responseData)); + workerClientWrapper->didReceiveResponse(*response); +} + +void WorkerThreadableLoader::MainThreadBridge::didReceiveResponse(const ResourceResponse& response) +{ + m_messagingProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidReceiveResponse, m_workerClientWrapper, response), m_taskMode); +} + +static void workerContextDidReceiveData(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, auto_ptr<Vector<char> > vectorData) +{ + ASSERT_UNUSED(context, context->isWorkerContext()); + workerClientWrapper->didReceiveData(vectorData->data(), vectorData->size()); +} + +void WorkerThreadableLoader::MainThreadBridge::didReceiveData(const char* data, int lengthReceived) +{ + auto_ptr<Vector<char> > vector(new Vector<char>(lengthReceived)); // needs to be an auto_ptr for usage with createCallbackTask. + memcpy(vector->data(), data, lengthReceived); + m_messagingProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidReceiveData, m_workerClientWrapper, vector), m_taskMode); +} + +static void workerContextDidFinishLoading(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, unsigned long identifier) +{ + ASSERT_UNUSED(context, context->isWorkerContext()); + workerClientWrapper->didFinishLoading(identifier); +} + +void WorkerThreadableLoader::MainThreadBridge::didFinishLoading(unsigned long identifier) +{ + m_messagingProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidFinishLoading, m_workerClientWrapper, identifier), m_taskMode); +} + +static void workerContextDidFail(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, const ResourceError& error) +{ + ASSERT_UNUSED(context, context->isWorkerContext()); + workerClientWrapper->didFail(error); +} + +void WorkerThreadableLoader::MainThreadBridge::didFail(const ResourceError& error) +{ + m_messagingProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidFail, m_workerClientWrapper, error), m_taskMode); +} + +static void workerContextDidFailRedirectCheck(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper) +{ + ASSERT_UNUSED(context, context->isWorkerContext()); + workerClientWrapper->didFailRedirectCheck(); +} + +void WorkerThreadableLoader::MainThreadBridge::didFailRedirectCheck() +{ + m_messagingProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidFailRedirectCheck, m_workerClientWrapper), m_taskMode); +} + +static void workerContextDidReceiveAuthenticationCancellation(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, auto_ptr<CrossThreadResourceResponseData> responseData) +{ + ASSERT_UNUSED(context, context->isWorkerContext()); + OwnPtr<ResourceResponse> response(ResourceResponse::adopt(responseData)); + workerClientWrapper->didReceiveAuthenticationCancellation(*response); +} + +void WorkerThreadableLoader::MainThreadBridge::didReceiveAuthenticationCancellation(const ResourceResponse& response) +{ + m_messagingProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidReceiveAuthenticationCancellation, m_workerClientWrapper, response), m_taskMode); +} + +} // namespace WebCore + +#endif // ENABLE(WORKERS) diff --git a/src/3rdparty/webkit/WebCore/loader/WorkerThreadableLoader.h b/src/3rdparty/webkit/WebCore/loader/WorkerThreadableLoader.h new file mode 100644 index 0000000000..9817fea148 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/loader/WorkerThreadableLoader.h @@ -0,0 +1,147 @@ +/* + * 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 WorkerThreadableLoader_h +#define WorkerThreadableLoader_h + +#if ENABLE(WORKERS) + +#include "PlatformString.h" +#include "ThreadableLoader.h" +#include "ThreadableLoaderClient.h" +#include "ThreadableLoaderClientWrapper.h" + +#include <memory> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/Threading.h> + +namespace WebCore { + + class ResourceError; + struct ResourceRequest; + class WorkerContext; + class WorkerMessagingProxy; + struct CrossThreadResourceResponseData; + struct CrossThreadResourceRequestData; + + class WorkerThreadableLoader : public RefCounted<WorkerThreadableLoader>, public ThreadableLoader { + public: + static void loadResourceSynchronously(WorkerContext*, const ResourceRequest&, ThreadableLoaderClient&, StoredCredentials); + static PassRefPtr<WorkerThreadableLoader> create(WorkerContext* workerContext, ThreadableLoaderClient* client, const String& taskMode, const ResourceRequest& request, LoadCallbacks callbacksSetting, ContentSniff contentSniff, StoredCredentials storedCredentials) + { + return adoptRef(new WorkerThreadableLoader(workerContext, client, taskMode, request, callbacksSetting, contentSniff, storedCredentials)); + } + + ~WorkerThreadableLoader(); + + virtual void cancel(); + + bool done() const { return m_workerClientWrapper->done(); } + + using RefCounted<WorkerThreadableLoader>::ref; + using RefCounted<WorkerThreadableLoader>::deref; + + protected: + virtual void refThreadableLoader() { ref(); } + virtual void derefThreadableLoader() { deref(); } + + private: + // Creates a loader on the main thread and bridges communication between + // the main thread and the worker context's thread where WorkerThreadableLoader runs. + // + // Regarding the bridge and lifetimes of items used in callbacks, there are a few cases: + // + // all cases. All tasks posted from the worker context's thread are ok because + // the last task posted always is "mainThreadDestroy", so MainThreadBridge is + // around for all tasks that use it on the mian thread. + // + // case 1. worker.terminate is called. + // In this case, no more tasks are posted from the worker object's thread to the worker + // context's thread -- WorkerMessagingProxy enforces this. + // + // case 2. xhr gets aborted and the worker context continues running. + // The ThreadableLoaderClientWrapper has the underlying client cleared, so no more calls + // go through it. All tasks posted from the worker object's thread to the worker context's + // thread do "ThreadableLoaderClientWrapper::ref" (automatically inside of the cross thread copy + // done in createCallbackTask), so the ThreadableLoaderClientWrapper instance is there until all + // tasks are executed. + class MainThreadBridge : ThreadableLoaderClient { + public: + // All executed on the worker context's thread. + MainThreadBridge(PassRefPtr<ThreadableLoaderClientWrapper>, WorkerMessagingProxy&, const String& taskMode, const ResourceRequest&, LoadCallbacks, ContentSniff, StoredCredentials); + void cancel(); + void destroy(); + + private: + // Executed on the worker context's thread. + void clearClientWrapper(); + + // All executed on the main thread. + static void mainThreadDestroy(ScriptExecutionContext*, MainThreadBridge*); + ~MainThreadBridge(); + + static void mainThreadCreateLoader(ScriptExecutionContext*, MainThreadBridge*, std::auto_ptr<CrossThreadResourceRequestData>, LoadCallbacks, ContentSniff, StoredCredentials); + static void mainThreadCancel(ScriptExecutionContext*, MainThreadBridge*); + 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 identifier); + virtual void didFail(const ResourceError&); + virtual void didFailRedirectCheck(); + virtual void didReceiveAuthenticationCancellation(const ResourceResponse&); + + // Only to be used on the main thread. + RefPtr<ThreadableLoader> m_mainThreadLoader; + + // ThreadableLoaderClientWrapper is to be used on the worker context thread. + // The ref counting is done on either thread. + RefPtr<ThreadSafeShared<ThreadableLoaderClientWrapper> > m_workerClientWrapper; + + // May be used on either thread. + WorkerMessagingProxy& m_messagingProxy; + + // For use on the main thread. + String m_taskMode; + }; + + WorkerThreadableLoader(WorkerContext*, ThreadableLoaderClient*, const String& taskMode, const ResourceRequest&, LoadCallbacks, ContentSniff, StoredCredentials); + + RefPtr<WorkerContext> m_workerContext; + RefPtr<ThreadableLoaderClientWrapper> m_workerClientWrapper; + MainThreadBridge& m_bridge; + }; + +} // namespace WebCore + +#endif // ENABLE(WORKERS) + +#endif // WorkerThreadableLoader_h diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.cpp b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.cpp index d7387995ba..42f5b6a5e3 100644 --- a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.cpp +++ b/src/3rdparty/webkit/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::Master)); // Add the resource to the storage. cacheStorage().store(resource.get(), this); @@ -105,7 +111,7 @@ ApplicationCacheResource* ApplicationCache::resourceForURL(const String& url) bool ApplicationCache::requestIsHTTPOrHTTPSGet(const ResourceRequest& request) { - if (!request.url().protocolIs("http") && !request.url().protocolIs("https")) + if (!request.url().protocolInHTTPFamily()) return false; if (!equalIgnoringCase(request.httpMethod(), "GET")) @@ -140,13 +146,13 @@ bool ApplicationCache::addDynamicEntry(const String& url) if (!equalIgnoringCase(m_group->manifestURL().protocol(), KURL(url).protocol())) return false; - // FIXME: Implement + // FIXME: Implement (be sure to respect private browsing state). return true; } void ApplicationCache::removeDynamicEntry(const String&) { - // FIXME: Implement + // FIXME: Implement (be sure to respect private browsing state). } void ApplicationCache::setOnlineWhitelist(const Vector<KURL>& onlineWhitelist) diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.h b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.h index 9bfa16aa08..afdab275c3 100644 --- a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.h +++ b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCache.h @@ -41,7 +41,8 @@ class ApplicationCacheGroup; class ApplicationCacheResource; class DocumentLoader; class KURL; -class ResourceRequest; + +struct ResourceRequest; typedef Vector<std::pair<KURL, KURL> > FallbackURLVector; @@ -58,7 +59,9 @@ public: void setGroup(ApplicationCacheGroup*); ApplicationCacheGroup* group() const { return m_group; } - + + bool isComplete() const; + ApplicationCacheResource* resourceForRequest(const ResourceRequest&); ApplicationCacheResource* resourceForURL(const String& url); diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheGroup.cpp b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheGroup.cpp index b53787feb7..48d3942ad6 100644 --- a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheGroup.cpp +++ b/src/3rdparty/webkit/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,12 @@ namespace WebCore { ApplicationCacheGroup::ApplicationCacheGroup(const KURL& manifestURL, bool isCopy) : m_manifestURL(manifestURL) - , m_status(Idle) - , m_savedNewestCachePointer(0) + , m_updateStatus(Idle) + , m_downloadingPendingMasterResourceLoadersCount(0) , m_frame(0) , m_storageID(0) + , m_isObsolete(false) + , m_completionType(None) , m_isCopy(isCopy) { } @@ -62,7 +64,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; @@ -83,6 +85,7 @@ ApplicationCache* ApplicationCacheGroup::cacheForMainRequest(const ResourceReque if (ApplicationCacheGroup* group = cacheStorage().cacheGroupForURL(request.url())) { ASSERT(group->newestCache()); + ASSERT(!group->isObsolete()); return group->newestCache(); } @@ -97,7 +100,8 @@ ApplicationCache* ApplicationCacheGroup::fallbackCacheForMainRequest(const Resou if (ApplicationCacheGroup* group = cacheStorage().fallbackCacheGroupForURL(request.url())) { ASSERT(group->newestCache()); - + ASSERT(!group->isObsolete()); + return group->newestCache(); } @@ -124,7 +128,7 @@ void ApplicationCacheGroup::selectCache(Frame* frame, const KURL& manifestURL) if (mainResourceCache) { if (manifestURL == mainResourceCache->group()->m_manifestURL) { mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); - mainResourceCache->group()->update(frame); + mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext); } else { // The main resource was loaded from cache, so the cache must have an entry for it. Mark it as foreign. ApplicationCacheResource* resource = mainResourceCache->resourceForURL(documentLoader->url()); @@ -145,53 +149,28 @@ 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; + + // Don't change anything on disk if private browsing is enabled. + if (!frame->settings() || frame->settings()->privateBrowsingEnabled()) { + postListenerTask(&DOMApplicationCache::callCheckingListener, documentLoader); + postListenerTask(&DOMApplicationCache::callErrorListener, documentLoader); 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); + group->m_downloadingPendingMasterResourceLoadersCount++; + + ASSERT(!group->m_cacheBeingUpdated || group->m_updateStatus != Idle); + group->update(frame, ApplicationCacheUpdateWithBrowsingContext); } void ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame* frame) @@ -206,43 +185,101 @@ void ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame* frame) if (mainResourceCache) { mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); - mainResourceCache->group()->update(frame); + mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext); } } void ApplicationCacheGroup::finishedLoadingMainResource(DocumentLoader* loader) { - const KURL& url = loader->originalURL(); - - if (ApplicationCache* cache = loader->applicationCache()) { - RefPtr<ApplicationCacheResource> resource = ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Implicit, loader->mainResourceData()); - cache->addResource(resource.release()); - - if (!m_cacheBeingUpdated) - return; - } - - ASSERT(m_pendingEntries.contains(url)); - - EntryMap::iterator it = m_pendingEntries.find(url); - ASSERT(it->second & ApplicationCacheResource::Implicit); + ASSERT(m_pendingMasterResourceLoaders.contains(loader)); + ASSERT(m_completionType == None || m_pendingEntries.isEmpty()); + const KURL& url = loader->url(); - 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::Master)) { + resource->addType(ApplicationCacheResource::Master); + ASSERT(!resource->storageID()); + } + } else + m_newestCache->addResource(ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Master, 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::Master)) { + resource->addType(ApplicationCacheResource::Master); + ASSERT(!resource->storageID()); + } + } else + m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Master, 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_downloadingPendingMasterResourceLoadersCount--; 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_downloadingPendingMasterResourceLoadersCount--; + checkIfLoadIsComplete(); } void ApplicationCacheGroup::stopLoading() @@ -265,95 +302,129 @@ 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 (isObsolete()) + return; + + m_isObsolete = true; + cacheStorage().cacheGroupMadeObsolete(this); + ASSERT(!m_storageID); +} + +void ApplicationCacheGroup::update(Frame* frame, ApplicationCacheUpdateOption updateOption) { - if (m_status == Checking || m_status == Downloading) + 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; + } + + // Don't change anything on disk if private browsing is enabled. + if (!frame->settings() || frame->settings()->privateBrowsingEnabled()) { + ASSERT(m_pendingMasterResourceLoaders.isEmpty()); + ASSERT(m_pendingEntries.isEmpty()); + ASSERT(!m_cacheBeingUpdated); + postListenerTask(&DOMApplicationCache::callCheckingListener, frame->loader()->documentLoader()); + postListenerTask(&DOMApplicationCache::callNoUpdateListener, 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_manifestHandle = createResourceHandle(m_manifestURL, m_newestCache ? m_newestCache->manifestResource() : 0); +} + +PassRefPtr<ResourceHandle> ApplicationCacheGroup::createResourceHandle(const KURL& url, ApplicationCacheResource* newestCachedResource) +{ + ResourceRequest request(url); m_frame->loader()->applyUserAgent(request); + request.setHTTPHeaderField("Cache-Control", "max-age=0"); + + if (newestCachedResource) { + const String& lastModified = newestCachedResource->response().httpHeaderField("Last-Modified"); + const String& eTag = newestCachedResource->response().httpHeaderField("ETag"); + if (!lastModified.isEmpty() || !eTag.isEmpty()) { + if (!lastModified.isEmpty()) + request.setHTTPHeaderField("If-Modified-Since", lastModified); + if (!eTag.isEmpty()) + request.setHTTPHeaderField("If-None-Match", eTag); + } + } - m_manifestHandle = ResourceHandle::create(request, this, m_frame, false, true, false); + return ResourceHandle::create(request, this, m_frame, false, true, false); } - + void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response) { if (handle == m_manifestHandle) { @@ -362,14 +433,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); @@ -377,9 +441,48 @@ void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const Res unsigned type = m_pendingEntries.get(url); - // If this is an initial cache attempt, we should not get implicit resources delivered here. + // If this is an initial cache attempt, we should not get master resources delivered here. if (!m_newestCache) - ASSERT(!(type & ApplicationCacheResource::Implicit)); + ASSERT(!(type & ApplicationCacheResource::Master)); + + if (m_newestCache && response.httpStatusCode() == 304) { // Not modified. + ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(handle->request().url()); + if (newestCachedResource) { + m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data())); + m_currentHandle->cancel(); + m_currentHandle = 0; + // Load the next resource, if any. + startLoadingEntry(); + return; + } + // The server could return 304 for an unconditional request - in this case, we handle the response as a normal error. + } + + 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* newestCachedResource = m_newestCache->resourceForURL(handle->request().url()); + ASSERT(newestCachedResource); + m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data())); + m_currentHandle->cancel(); + m_currentHandle = 0; + // Load the next resource, if any. + startLoadingEntry(); + } + return; + } m_currentResource = ApplicationCacheResource::create(url, response, type); } @@ -414,105 +517,121 @@ 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()); + const KURL& url = handle->request().url(); + + ASSERT(!m_currentResource || !m_pendingEntries.contains(url)); + m_currentResource = 0; + m_pendingEntries.remove(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* newestCachedResource = m_newestCache->resourceForURL(url); + ASSERT(newestCachedResource); + m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->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() == 304) + return; + + 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); } void ApplicationCacheGroup::didReceiveManifestData(const char* data, int length) { - ASSERT(m_manifestResource); - m_manifestResource->data()->append(data, length); + if (m_manifestResource) + m_manifestResource->data()->append(data, length); } void ApplicationCacheGroup::didFinishLoadingManifest() { - if (!m_manifestResource) { - didFailToLoadManifest(); + bool isUpgradeAttempt = m_newestCache; + + if (!isUpgradeAttempt && !m_manifestResource) { + // The server returned 304 Not Modified even though we didn't send a conditional request. + cacheUpdateFailed(); return; } - bool isUpgradeAttempt = m_newestCache; - m_manifestHandle = 0; - // Check if the manifest is byte-for-byte identical. + // Check if the manifest was not modified. if (isUpgradeAttempt) { ApplicationCacheResource* newestManifest = m_newestCache->manifestResource(); ASSERT(newestManifest); - if (newestManifest->data()->size() == m_manifestResource->data()->size() && - !memcmp(newestManifest->data()->data(), m_manifestResource->data()->data(), newestManifest->data()->size())) { - - callListenersOnAssociatedDocuments(&DOMApplicationCache::callNoUpdateListener); - - m_status = Idle; - m_frame = 0; + if (!m_manifestResource || // The resource will be null if HTTP response was 304 Not Modified. + newestManifest->data()->size() == m_manifestResource->data()->size() && !memcmp(newestManifest->data()->data(), m_manifestResource->data()->data(), newestManifest->data()->size())) { + + 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; } - + + 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::Implicit | ApplicationCacheResource::Dynamic)) + if (type & (ApplicationCacheResource::Master | ApplicationCacheResource::Dynamic)) addEntry(it->first, type); } } @@ -534,81 +653,125 @@ 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_downloadingPendingMasterResourceLoadersCount = 0; + 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) - return; - - if (!m_pendingEntries.isEmpty()) + if (m_manifestHandle || !m_pendingEntries.isEmpty() || m_downloadingPendingMasterResourceLoadersCount) 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()); - - 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); + + // The storage could have been manually emptied by the user. + if (!m_storageID) + cacheStorage().storeNewestCache(this); + + 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()); + if (cacheStorage().storeNewestCache(this)) { + // New cache stored, now remove the old cache. + if (oldNewestCache) + cacheStorage().remove(oldNewestCache.get()); + // Fire the success events. + postListenerTask(isUpgradeAttempt ? &DOMApplicationCache::callUpdateReadyListener : &DOMApplicationCache::callCachedListener, m_associatedDocumentLoaders); + } else { + // Run the "cache failure steps" + // Fire the error events to all pending master entries, as well any other cache hosts + // currently associated with a cache in this group. + postListenerTask(&DOMApplicationCache::callErrorListener, m_associatedDocumentLoaders); + // Disassociate the pending master entries from the failed new cache. Note that + // all other loaders in the m_associatedDocumentLoaders are still associated with + // some other cache in this group. They are not associated with the failed new cache. + + // 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) + disassociateDocumentLoader(loaders[i]); // This can delete this group. + + // Reinstate the oldNewestCache, if there was one. + if (oldNewestCache) { + // This will discard the failed new cache. + setNewestCache(oldNewestCache.release()); + } else { + // We must have been deleted by the last call to disassociateDocumentLoader(). + return; + } + } + break; + } } - - setNewestCache(m_cacheBeingUpdated.release()); - - // Store the cache - cacheStorage().storeNewestCache(this); - - callListeners(isUpgradeAttempt ? &DOMApplicationCache::callUpdateReadyListener : &DOMApplicationCache::callCachedListener, - documentLoaders); - m_status = Idle; + // Empty cache group's list of pending master entries. + m_pendingMasterResourceLoaders.clear(); + m_completionType = None; + m_updateStatus = Idle; m_frame = 0; } @@ -617,52 +780,58 @@ 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; + postListenerTask(&DOMApplicationCache::callProgressListener, m_associatedDocumentLoaders); - if (it == end) - return; - } - } - - callListenersOnAssociatedDocuments(&DOMApplicationCache::callProgressListener); - - // FIXME: If this is an upgrade attempt, the newest cache should be used as an HTTP cache. - ASSERT(!m_currentHandle); - ResourceRequest request(it->first); - m_frame->loader()->applyUserAgent(request); + m_currentHandle = createResourceHandle(KURL(it->first), m_newestCache ? m_newestCache->resourceForURL(it->first) : 0); +} - 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 + // Don't add the URL if we already have an master 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(resource->type() & ApplicationCacheResource::Master); + 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; } @@ -675,33 +844,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)); + } + + virtual void performTask(ScriptExecutionContext* context) + { + ASSERT(context->isDocument()); + if (DOMWindow* window = static_cast<Document*>(context)->domWindow()) { + if (DOMApplicationCache* domCache = window->optionalApplicationCache()) + (domCache->*m_listenerFunction)(); + } + } - callListeners(listenerFunction, loaders); +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/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheGroup.h b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheGroup.h index 8928e7547a..063fb3b781 100644 --- a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheGroup.h +++ b/src/3rdparty/webkit/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,12 +47,17 @@ 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*); @@ -61,30 +66,38 @@ public: 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); - + static void postListenerTask(ListenerFunction, const HashSet<DocumentLoader*>&); + static void postListenerTask(ListenerFunction, const Vector<RefPtr<DocumentLoader> >& loaders); + static void postListenerTask(ListenerFunction, DocumentLoader*); + + PassRefPtr<ResourceHandle> createResourceHandle(const KURL&, ApplicationCacheResource* newestCachedResource); + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); virtual void didFinishLoading(ResourceHandle*); @@ -93,11 +106,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); @@ -106,37 +120,47 @@ 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). Note that cache updating does not immediately create a new - // ApplicationCache object, so this may be null even when status is not Idle. + // 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; + // How many of the above pending master entries have not yet finished downloading. + int m_downloadingPendingMasterResourceLoadersCount; // 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/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.cpp b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.cpp index d78cf7f4c5..7c1241b50d 100644 --- a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.cpp +++ b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.cpp @@ -47,8 +47,8 @@ void ApplicationCacheResource::addType(unsigned type) #ifndef NDEBUG void ApplicationCacheResource::dumpType(unsigned type) { - if (type & Implicit) - printf("implicit "); + if (type & Master) + printf("master "); if (type & Manifest) printf("manifest "); if (type & Explicit) diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.h b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.h index 96f5a0e9a0..28d8280cf0 100644 --- a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.h +++ b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheResource.h @@ -35,7 +35,7 @@ namespace WebCore { class ApplicationCacheResource : public SubstituteResource { public: enum Type { - Implicit = 1 << 0, + Master = 1 << 0, Manifest = 1 << 1, Explicit = 1 << 2, Foreign = 1 << 3, diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.cpp b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.cpp index 97a6d5a1c6..1c59581d50 100644 --- a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.cpp +++ b/src/3rdparty/webkit/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 @@ -43,6 +43,44 @@ using namespace std; namespace WebCore { +class ResourceStorageIDJournal { +public: + ~ResourceStorageIDJournal() + { + size_t size = m_records.size(); + for (size_t i = 0; i < size; ++i) + m_records[i].restore(); + } + + void add(ApplicationCacheResource* resource, unsigned storageID) + { + m_records.append(Record(resource, storageID)); + } + + void commit() + { + m_records.clear(); + } + +private: + class Record { + public: + Record() : m_resource(0), m_storageID(0) { } + Record(ApplicationCacheResource* resource, unsigned storageID) : m_resource(resource), m_storageID(storageID) { } + + void restore() + { + m_resource->setStorageID(m_storageID); + } + + private: + ApplicationCacheResource* m_resource; + unsigned m_storageID; + }; + + Vector<Record> m_records; +}; + static unsigned urlHostHash(const KURL& url) { unsigned hostStart = url.hostStart(); @@ -147,7 +185,9 @@ 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; @@ -213,6 +253,8 @@ ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const K 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)) @@ -271,15 +313,33 @@ ApplicationCacheGroup* ApplicationCacheStorage::fallbackCacheGroupForURL(const K 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()); @@ -321,8 +381,8 @@ void ApplicationCacheStorage::verifySchemaVersion() setDatabaseVersion.begin(); char userVersionSQL[32]; - int numBytes = snprintf(userVersionSQL, sizeof(userVersionSQL), "PRAGMA user_version=%d", schemaVersion); - ASSERT_UNUSED(numBytes, static_cast<int>(sizeof(userVersionSQL)) >= numBytes); + 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) @@ -372,7 +432,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" @@ -408,10 +474,11 @@ bool ApplicationCacheStorage::store(ApplicationCacheGroup* group) return true; } -bool ApplicationCacheStorage::store(ApplicationCache* cache) +bool ApplicationCacheStorage::store(ApplicationCache* cache, ResourceStorageIDJournal* storageIDJournal) { ASSERT(cache->storageID() == 0); ASSERT(cache->group()->storageID() != 0); + ASSERT(storageIDJournal); SQLiteStatement statement(m_database, "INSERT INTO Caches (cacheGroup) VALUES (?)"); if (statement.prepare() != SQLResultOk) @@ -428,8 +495,13 @@ bool ApplicationCacheStorage::store(ApplicationCache* cache) { ApplicationCache::ResourceMap::const_iterator end = cache->end(); for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) { + unsigned oldStorageID = it->second->storageID(); if (!store(it->second.get(), cacheStorageID)) return false; + + // Storing the resource succeeded. Log its old storageID in case + // it needs to be restored later. + storageIDJournal->add(it->second.get(), oldStorageID); } } @@ -587,10 +659,16 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group) } ASSERT(group->newestCache()); + ASSERT(!group->isObsolete()); ASSERT(!group->newestCache()->storageID()); + // Log the storageID changes to the in-memory resource objects. The journal + // object will roll them back automatically in case a database operation + // fails and this method returns early. + ResourceStorageIDJournal storageIDJournal; + // Store the newest cache - if (!store(group->newestCache())) + if (!store(group->newestCache(), &storageIDJournal)) return false; // Update the newest cache in the group. @@ -605,6 +683,7 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group) if (!executeStatement(statement)) return false; + storageIDJournal.commit(); storeCacheTransaction.commit(); return true; } @@ -724,12 +803,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() @@ -742,7 +839,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 diff --git a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.h b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.h index cf64dd44be..b13b5966a9 100644 --- a/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.h +++ b/src/3rdparty/webkit/WebCore/loader/appcache/ApplicationCacheStorage.h @@ -40,6 +40,7 @@ class ApplicationCache; class ApplicationCacheGroup; class ApplicationCacheResource; class KURL; +class ResourceStorageIDJournal; class ApplicationCacheStorage { public: @@ -51,11 +52,13 @@ public: 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(); @@ -67,7 +70,7 @@ private: ApplicationCacheGroup* loadCacheGroup(const KURL& manifestURL); bool store(ApplicationCacheGroup*); - bool store(ApplicationCache*); + bool store(ApplicationCache*, ResourceStorageIDJournal*); bool store(ApplicationCacheResource*, unsigned cacheStorageID); void loadManifestHostHashes(); @@ -84,11 +87,11 @@ private: SQLiteDatabase m_database; // In order to quickly determine if a given resource exists in an application cache, - // we keep a hash set of the hosts of the manifest URLs of all cache groups. + // 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/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.cpp b/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.cpp index a07442b277..90d293051a 100644 --- a/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.cpp +++ b/src/3rdparty/webkit/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 @@ -65,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; } @@ -92,7 +92,7 @@ void DOMApplicationCache::update(ExceptionCode& ec) return; } - cache->group()->update(m_frame); + cache->group()->update(m_frame, ApplicationCacheUpdateWithoutBrowsingContext); } bool DOMApplicationCache::swapCache() @@ -103,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; @@ -126,7 +132,7 @@ PassRefPtr<DOMStringList> DOMApplicationCache::items() Vector<String> result; if (ApplicationCache* cache = associatedCache()) { unsigned numEntries = cache->numDynamicEntries(); - result.reserveCapacity(numEntries); + result.reserveInitialCapacity(numEntries); for (unsigned i = 0; i < numEntries; ++i) result.append(cache->dynamicEntry(i)); } @@ -287,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/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.h b/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.h index 9371cd79fc..b76542d44f 100644 --- a/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.h +++ b/src/3rdparty/webkit/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 @@ -56,6 +56,7 @@ public: CHECKING = 2, DOWNLOADING = 3, UPDATEREADY = 4, + OBSOLETE = 5 }; unsigned short status() const; @@ -100,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; } @@ -110,6 +114,7 @@ public: void callProgressListener(); void callUpdateReadyListener(); void callCachedListener(); + void callObsoleteListener(); private: DOMApplicationCache(Frame*); @@ -128,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/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.idl b/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.idl index f38cd74b47..1156c9c2f7 100644 --- a/src/3rdparty/webkit/WebCore/loader/appcache/DOMApplicationCache.idl +++ b/src/3rdparty/webkit/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,13 +35,15 @@ 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 defined(ENABLE_APPLICATION_CACHE_DYNAMIC_ENTRIES) && ENABLE_APPLICATION_CACHE_DYNAMIC_ENTRIES // dynamic entries readonly attribute DOMStringList items; [Custom] boolean hasItem(in DOMString url) @@ -50,6 +52,7 @@ module offline { 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/src/3rdparty/webkit/WebCore/loader/appcache/ManifestParser.cpp b/src/3rdparty/webkit/WebCore/loader/appcache/ManifestParser.cpp index f1b60fcb57..4169313fc0 100644 --- a/src/3rdparty/webkit/WebCore/loader/appcache/ManifestParser.cpp +++ b/src/3rdparty/webkit/WebCore/loader/appcache/ManifestParser.cpp @@ -30,7 +30,7 @@ #include "CharacterNames.h" #include "KURL.h" -#include "TextEncoding.h" +#include "TextResourceDecoder.h" using namespace std; @@ -45,36 +45,27 @@ bool parseManifest(const KURL& manifestURL, const char* data, int length, Manife ASSERT(manifest.fallbackURLs.isEmpty()); Mode mode = Explicit; - String s = UTF8Encoding().decode(data, length); - - if (s.isEmpty()) - return false; - - // Replace nulls with U+FFFD REPLACEMENT CHARACTER - s.replace(0, replacementCharacter); + + RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("text/cache-manifest", "UTF-8"); + String s = decoder->decode(data, length); + s += decoder->flush(); - // 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 TextResourceDecoder). + // 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')) diff --git a/src/3rdparty/webkit/WebCore/loader/archive/cf/LegacyWebArchive.cpp b/src/3rdparty/webkit/WebCore/loader/archive/cf/LegacyWebArchive.cpp index 81e2b78bbe..d1269cc446 100644 --- a/src/3rdparty/webkit/WebCore/loader/archive/cf/LegacyWebArchive.cpp +++ b/src/3rdparty/webkit/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 @@ -38,6 +38,8 @@ #include "FrameTree.h" #include "HTMLFrameOwnerElement.h" #include "HTMLNames.h" +#include "IconDatabase.h" +#include "Image.h" #include "KURLHash.h" #include "Logging.h" #include "markup.h" @@ -61,15 +63,16 @@ static const CFStringRef LegacyWebArchiveResourceTextEncodingNameKey = CFSTR("We static const CFStringRef LegacyWebArchiveResourceResponseKey = CFSTR("WebResourceResponse"); static const CFStringRef LegacyWebArchiveResourceResponseVersionKey = CFSTR("WebResourceResponseVersion"); -static RetainPtr<CFDictionaryRef> createPropertyListRepresentationFromResource(ArchiveResource* resource, bool mainResource) +RetainPtr<CFDictionaryRef> LegacyWebArchive::createPropertyListRepresentation(ArchiveResource* resource, MainResourceStatus isMainResource) { if (!resource) { - // The property list representation of a null/empty WebResource has the following 3 objects stored as nil + // The property list representation of a null/empty WebResource has the following 3 objects stored as nil. + // FIXME: 0 is not serializable. Presumably we need to use kCFNull here instead for compatibility. + // FIXME: But why do we need to support a resource of 0? Who relies on that? RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 3, 0, 0)); CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceDataKey, 0); CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceURLKey, 0); CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceMIMETypeKey, 0); - return propertyList; } @@ -95,7 +98,7 @@ static RetainPtr<CFDictionaryRef> createPropertyListRepresentationFromResource(A // FrameName should be left out if empty for subresources, but always included for main resources const String& frameName(resource->frameName()); - if (!frameName.isEmpty() || mainResource) { + if (!frameName.isEmpty() || isMainResource) { RetainPtr<CFStringRef> cfFrameName(AdoptCF, frameName.createCFString()); CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceFrameNameKey, cfFrameName.get()); } @@ -114,8 +117,8 @@ static RetainPtr<CFDictionaryRef> createPropertyListRepresentationFromResource(A } // Don't include the resource response for the main resource - if (!mainResource) { - RetainPtr<CFDataRef> resourceResponseData = propertyListDataFromResourceResponse(resource->response()); + if (!isMainResource) { + RetainPtr<CFDataRef> resourceResponseData = createPropertyListRepresentation(resource->response()); if (resourceResponseData) CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceResponseKey, resourceResponseData.get()); } @@ -123,19 +126,20 @@ static RetainPtr<CFDictionaryRef> createPropertyListRepresentationFromResource(A return propertyList; } -static RetainPtr<CFDictionaryRef> createPropertyListRep(Archive* archive) +RetainPtr<CFDictionaryRef> LegacyWebArchive::createPropertyListRepresentation(Archive* archive) { RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 3, 0, &kCFTypeDictionaryValueCallBacks)); - RetainPtr<CFDictionaryRef> mainResourceDict = createPropertyListRepresentationFromResource(archive->mainResource(), true); + RetainPtr<CFDictionaryRef> mainResourceDict = createPropertyListRepresentation(archive->mainResource(), MainResource); + ASSERT(mainResourceDict); if (!mainResourceDict) return 0; CFDictionarySetValue(propertyList.get(), LegacyWebArchiveMainResourceKey, mainResourceDict.get()); - + RetainPtr<CFMutableArrayRef> subresourcesArray(AdoptCF, CFArrayCreateMutable(0, archive->subresources().size(), &kCFTypeArrayCallBacks)); const Vector<RefPtr<ArchiveResource> >& subresources(archive->subresources()); for (unsigned i = 0; i < subresources.size(); ++i) { - RetainPtr<CFDictionaryRef> subresource = createPropertyListRepresentationFromResource(subresources[i].get(), false); + RetainPtr<CFDictionaryRef> subresource = createPropertyListRepresentation(subresources[i].get(), Subresource); if (subresource) CFArrayAppendValue(subresourcesArray.get(), subresource.get()); else @@ -147,7 +151,7 @@ static RetainPtr<CFDictionaryRef> createPropertyListRep(Archive* archive) RetainPtr<CFMutableArrayRef> subframesArray(AdoptCF, CFArrayCreateMutable(0, archive->subframeArchives().size(), &kCFTypeArrayCallBacks)); const Vector<RefPtr<Archive> >& subframeArchives(archive->subframeArchives()); for (unsigned i = 0; i < subframeArchives.size(); ++i) { - RetainPtr<CFDictionaryRef> subframeArchive = createPropertyListRep(subframeArchives[i].get()); + RetainPtr<CFDictionaryRef> subframeArchive = createPropertyListRepresentation(subframeArchives[i].get()); if (subframeArchive) CFArrayAppendValue(subframesArray.get(), subframeArchive.get()); else @@ -159,22 +163,23 @@ static RetainPtr<CFDictionaryRef> createPropertyListRep(Archive* archive) return propertyList; } -static ResourceResponse createResourceResponseFromPropertyListData(CFDataRef data, CFStringRef responseDataType) +ResourceResponse LegacyWebArchive::createResourceResponseFromPropertyListData(CFDataRef data, CFStringRef responseDataType) { ASSERT(data); if (!data) return ResourceResponse(); - // If the ResourceResponseVersion (passed in as responseDataType) exists at all, this is a "new" webarchive that we can parse well in a cross platform manner - // If it doesn't exist, we will assume this is an "old" Cocoa-based WebArchive, and parse the ResourceResponse as such + // If the ResourceResponseVersion (passed in as responseDataType) exists at all, this is a "new" web archive that we + // can parse well in a cross platform manner If it doesn't exist, we will assume this is an "old" web archive with, + // NSURLResponse objects in it and parse the ResourceResponse as such. if (!responseDataType) return createResourceResponseFromMacArchivedData(data); - // FIXME: Parse the "new" format that the above comment references here + // FIXME: Parse the "new" format that the above comment references here. This format doesn't exist yet. return ResourceResponse(); } -static PassRefPtr<ArchiveResource> createResource(CFDictionaryRef dictionary) +PassRefPtr<ArchiveResource> LegacyWebArchive::createResource(CFDictionaryRef dictionary) { ASSERT(dictionary); if (!dictionary) @@ -236,17 +241,6 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::create() return adoptRef(new LegacyWebArchive); } -PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(SharedBuffer* data) -{ - LOG(Archives, "LegacyWebArchive - Creating from raw data"); - - RefPtr<LegacyWebArchive> archive = create(); - if (!archive->init(data)) - return 0; - - return archive.release(); -} - PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(PassRefPtr<ArchiveResource> mainResource, Vector<PassRefPtr<ArchiveResource> >& subresources, Vector<PassRefPtr<LegacyWebArchive> >& subframeArchives) { ASSERT(mainResource); @@ -265,19 +259,19 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(PassRefPtr<ArchiveResource return archive.release(); } -LegacyWebArchive::LegacyWebArchive() -{ -} - -bool LegacyWebArchive::init(SharedBuffer* data) +PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(SharedBuffer* data) { + LOG(Archives, "LegacyWebArchive - Creating from raw data"); + + RefPtr<LegacyWebArchive> archive = create(); + ASSERT(data); if (!data) - return false; + return 0; RetainPtr<CFDataRef> cfData(AdoptCF, data->createCFData()); if (!cfData) - return false; + return 0; CFStringRef errorString = 0; @@ -289,15 +283,18 @@ bool LegacyWebArchive::init(SharedBuffer* data) #endif if (errorString) CFRelease(errorString); - return false; + return 0; } if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) { LOG(Archives, "LegacyWebArchive - Archive property list is not the expected CFDictionary, aborting invalid WebArchive"); - return false; + return 0; } - return extract(plist.get()); + if (!archive->extract(plist.get())) + return 0; + + return archive.release(); } bool LegacyWebArchive::extract(CFDictionaryRef dictionary) @@ -370,38 +367,47 @@ bool LegacyWebArchive::extract(CFDictionaryRef dictionary) RetainPtr<CFDataRef> LegacyWebArchive::rawDataRepresentation() { - RetainPtr<CFDictionaryRef> propertyList = createPropertyListRep(this); + RetainPtr<CFDictionaryRef> propertyList = createPropertyListRepresentation(this); + ASSERT(propertyList); if (!propertyList) { LOG(Archives, "LegacyWebArchive - Failed to create property list for archive, returning no data"); return 0; } - - // FIXME: On Mac, WebArchives have been written out as Binary Property Lists until this change. - // Unless we jump through CFWriteStream hoops, they'll now be textual XML data. Is this okay? - RetainPtr<CFDataRef> plistData(AdoptCF, CFPropertyListCreateXMLData(0, propertyList.get())); + + 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))); + ASSERT(plistData); + + CFWriteStreamClose(stream.get()); + if (!plistData) { LOG(Archives, "LegacyWebArchive - Failed to convert property list into raw data, returning no data"); return 0; } - + return plistData; } #if !PLATFORM(MAC) -// FIXME: Is it possible to parse in a Cocoa-style resource response manually, -// without NSKeyed(Un)Archiver, manipulating plists directly? -// If so, the code that does it will go here. -// In the meantime, Mac will continue to NSKeyed(Un)Archive the response as it always has -ResourceResponse createResourceResponseFromMacArchivedData(CFDataRef responseData) + +ResourceResponse LegacyWebArchive::createResourceResponseFromMacArchivedData(CFDataRef responseData) { + // FIXME: If is is possible to parse in a serialized NSURLResponse manually, without using + // NSKeyedUnarchiver, manipulating plists directly, then we want to do that here. + // Until then, this can be done on Mac only. return ResourceResponse(); } -RetainPtr<CFDataRef> propertyListDataFromResourceResponse(const ResourceResponse& response) +RetainPtr<CFDataRef> LegacyWebArchive::createPropertyListRepresentation(const ResourceResponse& response) { - // FIXME: Write out the "new" format described in ::createResourceResponseFromPropertyListData() up above + // FIXME: Write out the "new" format described in createResourceResponseFromPropertyListData once we invent it. return 0; } + #endif PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Node* node) @@ -445,7 +451,7 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Frame* frame) Vector<PassRefPtr<ArchiveResource> > subresources; documentLoader->getSubresources(subresources); - return LegacyWebArchive::create(documentLoader->mainResource(), subresources, subframeArchives); + return create(documentLoader->mainResource(), subresources, subframeArchives); } PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Range* range) @@ -473,7 +479,7 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Range* range) return create(markupString, frame, nodeList); } -PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString, Frame* frame, Vector<Node*>& nodes) +PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString, Frame* frame, const Vector<Node*>& nodes) { ASSERT(frame); @@ -490,19 +496,14 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString Vector<PassRefPtr<LegacyWebArchive> > subframeArchives; Vector<PassRefPtr<ArchiveResource> > subresources; HashSet<KURL> uniqueSubresources; - - Vector<Node*>::iterator it = nodes.begin(); - Vector<Node*>::iterator end = nodes.end(); - - for (; it != end; ++it) { + + size_t nodesSize = nodes.size(); + for (size_t i = 0; i < nodesSize; ++i) { + Node* node = nodes[i]; Frame* childFrame; - if (((*it)->hasTagName(HTMLNames::frameTag) || (*it)->hasTagName(HTMLNames::iframeTag) || (*it)->hasTagName(HTMLNames::objectTag)) && - (childFrame = static_cast<HTMLFrameOwnerElement*>(*it)->contentFrame())) { - RefPtr<LegacyWebArchive> subframeArchive; - if (Document* document = childFrame->document()) - subframeArchive = LegacyWebArchive::create(document); - else - subframeArchive = create(childFrame); + if ((node->hasTagName(HTMLNames::frameTag) || node->hasTagName(HTMLNames::iframeTag) || node->hasTagName(HTMLNames::objectTag)) && + (childFrame = static_cast<HTMLFrameOwnerElement*>(node)->contentFrame())) { + RefPtr<LegacyWebArchive> subframeArchive = create(childFrame->document()); if (subframeArchive) subframeArchives.append(subframeArchive); @@ -510,7 +511,7 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString LOG_ERROR("Unabled to archive subframe %s", childFrame->tree()->name().string().utf8().data()); } else { ListHashSet<KURL> subresourceURLs; - (*it)->getSubresourceURLs(subresourceURLs); + node->getSubresourceURLs(subresourceURLs); DocumentLoader* documentLoader = frame->loader()->documentLoader(); ListHashSet<KURL>::iterator iterEnd = subresourceURLs.end(); @@ -541,7 +542,18 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString } } } - + + // Add favicon if one exists for this page, if we are archiving the entire page. + if (nodesSize && nodes[0]->isDocumentNode() && iconDatabase() && iconDatabase()->isEnabled()) { + const String& iconURL = iconDatabase()->iconURLForPageURL(responseURL); + if (!iconURL.isEmpty() && iconDatabase()->iconDataKnownForIconURL(iconURL)) { + if (Image* iconImage = iconDatabase()->iconForPageURL(responseURL, IntSize(16, 16))) { + if (RefPtr<ArchiveResource> resource = ArchiveResource::create(iconImage->data(), KURL(iconURL), "image/x-icon", "", "")) + subresources.append(resource.release()); + } + } + } + return create(mainResource, subresources, subframeArchives); } @@ -550,13 +562,13 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::createFromSelection(Frame* frame) if (!frame) return 0; - RefPtr<Range> selectionRange = frame->selection()->toRange(); + RefPtr<Range> selectionRange = frame->selection()->toNormalizedRange(); Vector<Node*> nodeList; String markupString = frame->documentTypeString() + createMarkup(selectionRange.get(), &nodeList, AnnotateForInterchange); RefPtr<LegacyWebArchive> archive = create(markupString, frame, nodeList); - if (!frame->isFrameSet()) + if (!frame->document() || !frame->document()->isFrameSet()) return archive.release(); // Wrap the frameset document in an iframe so it can be pasted into @@ -570,7 +582,7 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::createFromSelection(Frame* frame) Vector<PassRefPtr<LegacyWebArchive> > subframeArchives; subframeArchives.append(archive); - archive = LegacyWebArchive::create(iframeResource.release(), subresources, subframeArchives); + archive = create(iframeResource.release(), subresources, subframeArchives); return archive.release(); } diff --git a/src/3rdparty/webkit/WebCore/loader/archive/cf/LegacyWebArchive.h b/src/3rdparty/webkit/WebCore/loader/archive/cf/LegacyWebArchive.h index 70faba5784..8c8f2e4ff6 100644 --- a/src/3rdparty/webkit/WebCore/loader/archive/cf/LegacyWebArchive.h +++ b/src/3rdparty/webkit/WebCore/loader/archive/cf/LegacyWebArchive.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 @@ -31,8 +31,6 @@ #include "Archive.h" -#include <wtf/PassRefPtr.h> - namespace WebCore { class Frame; @@ -40,28 +38,33 @@ class Node; class Range; class LegacyWebArchive : public Archive { -public: +public: static PassRefPtr<LegacyWebArchive> create(); static PassRefPtr<LegacyWebArchive> create(SharedBuffer*); static PassRefPtr<LegacyWebArchive> create(PassRefPtr<ArchiveResource> mainResource, Vector<PassRefPtr<ArchiveResource> >& subresources, Vector<PassRefPtr<LegacyWebArchive> >& subframeArchives); static PassRefPtr<LegacyWebArchive> create(Node*); static PassRefPtr<LegacyWebArchive> create(Frame*); - static PassRefPtr<LegacyWebArchive> createFromSelection(Frame* frame); + static PassRefPtr<LegacyWebArchive> createFromSelection(Frame*); static PassRefPtr<LegacyWebArchive> create(Range*); - static PassRefPtr<LegacyWebArchive> create(const String& markupString, Frame*, Vector<Node*>& nodes); RetainPtr<CFDataRef> rawDataRepresentation(); private: - LegacyWebArchive(); - bool init(SharedBuffer*); + LegacyWebArchive() { } + + enum MainResourceStatus { Subresource, MainResource }; + + static PassRefPtr<LegacyWebArchive> create(const String& markupString, Frame*, const Vector<Node*>& nodes); + static PassRefPtr<ArchiveResource> createResource(CFDictionaryRef); + static ResourceResponse createResourceResponseFromMacArchivedData(CFDataRef); + static ResourceResponse createResourceResponseFromPropertyListData(CFDataRef, CFStringRef responseDataType); + static RetainPtr<CFDataRef> createPropertyListRepresentation(const ResourceResponse&); + static RetainPtr<CFDictionaryRef> createPropertyListRepresentation(Archive*); + static RetainPtr<CFDictionaryRef> createPropertyListRepresentation(ArchiveResource*, MainResourceStatus); + bool extract(CFDictionaryRef); - }; -ResourceResponse createResourceResponseFromMacArchivedData(CFDataRef); -RetainPtr<CFDataRef> propertyListDataFromResourceResponse(const ResourceResponse&); - } #endif // Archive diff --git a/src/3rdparty/webkit/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm b/src/3rdparty/webkit/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm index b853a1595f..c474bbad50 100644 --- a/src/3rdparty/webkit/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm +++ b/src/3rdparty/webkit/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm @@ -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 @@ -33,9 +33,9 @@ namespace WebCore { static const NSString *LegacyWebArchiveResourceResponseKey = @"WebResourceResponse"; -// FIXME: Is it possible to parse in a Cocoa-style resource response manually, -// without NSKeyed(Un)Archiver, manipulating plists directly? -ResourceResponse createResourceResponseFromMacArchivedData(CFDataRef responseData) +// FIXME: If is is possible to parse in a serialized NSURLResponse manually, without using +// NSKeyedUnarchiver, manipulating plists directly, we would prefer to do that instead. +ResourceResponse LegacyWebArchive::createResourceResponseFromMacArchivedData(CFDataRef responseData) { ASSERT(responseData); if (!responseData) @@ -56,19 +56,21 @@ ResourceResponse createResourceResponseFromMacArchivedData(CFDataRef responseDat return ResourceResponse(response); } -RetainPtr<CFDataRef> propertyListDataFromResourceResponse(const ResourceResponse& response) +RetainPtr<CFDataRef> LegacyWebArchive::createPropertyListRepresentation(const ResourceResponse& response) { NSURLResponse *nsResponse = response.nsURLResponse(); + ASSERT(nsResponse); if (!nsResponse) return 0; - - NSMutableData *responseData = (NSMutableData *)CFDataCreateMutable(0, 0); - NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:responseData]; + + CFMutableDataRef responseData = CFDataCreateMutable(0, 0); + + NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:(NSMutableData *)responseData]; [archiver encodeObject:nsResponse forKey:LegacyWebArchiveResourceResponseKey]; [archiver finishEncoding]; [archiver release]; - return RetainPtr<CFDataRef>(AdoptCF, (CFDataRef)responseData); + return RetainPtr<CFDataRef>(AdoptCF, responseData); } } diff --git a/src/3rdparty/webkit/WebCore/loader/cf/ResourceLoaderCFNet.cpp b/src/3rdparty/webkit/WebCore/loader/cf/ResourceLoaderCFNet.cpp new file mode 100644 index 0000000000..83642c613a --- /dev/null +++ b/src/3rdparty/webkit/WebCore/loader/cf/ResourceLoaderCFNet.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 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 + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ResourceLoader.h" + +#include "FrameLoader.h" +#include "FrameLoaderClient.h" + +namespace WebCore { + +bool ResourceLoader::shouldCacheResponse(ResourceHandle*, CFCachedURLResponseRef cachedResponse) +{ + if (!m_sendResourceLoadCallbacks) + return 0; + + CFURLResponseRef response = CFCachedURLResponseGetWrappedResponse(cachedResponse); + CFDataRef data = CFCachedURLResponseGetReceiverData(cachedResponse); + return frameLoader()->client()->shouldCacheResponse(documentLoader(), identifier(), response, CFDataGetBytePtr(data), CFDataGetLength(data)); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/loader/icon/IconDatabase.cpp b/src/3rdparty/webkit/WebCore/loader/icon/IconDatabase.cpp index d033b24b4b..3ded4f386b 100644 --- a/src/3rdparty/webkit/WebCore/loader/icon/IconDatabase.cpp +++ b/src/3rdparty/webkit/WebCore/loader/icon/IconDatabase.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) * * Redistribution and use in source and binary forms, with or without @@ -28,35 +28,20 @@ #include "IconDatabase.h" #include "AutodrainedPool.h" -#include "CString.h" #include "DocumentLoader.h" #include "FileSystem.h" #include "IconDatabaseClient.h" #include "IconRecord.h" -#include "Image.h" #include "IntSize.h" -#include "KURL.h" #include "Logging.h" -#include "PageURLRecord.h" #include "SQLiteStatement.h" #include "SQLiteTransaction.h" -#include "SystemTime.h" +#include "SuddenTermination.h" #include <runtime/InitializeThreading.h> +#include <wtf/CurrentTime.h> #include <wtf/MainThread.h> #include <wtf/StdLibExtras.h> -#if PLATFORM(WIN_OS) -#include <windows.h> -#include <winbase.h> -#include <shlobj.h> -#else -#include <sys/stat.h> -#endif - -#if PLATFORM(DARWIN) -#include <pthread.h> -#endif - // 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()) @@ -64,7 +49,7 @@ #define IS_ICON_SYNC_THREAD() (m_syncThread == currentThread()) #define ASSERT_ICON_SYNC_THREAD() ASSERT(IS_ICON_SYNC_THREAD()) -#if PLATFORM(QT) +#if PLATFORM(QT) || PLATFORM(GTK) #define CAN_THEME_URL_ICON #endif @@ -77,12 +62,12 @@ static int databaseCleanupCounter = 0; // Currently, a mismatched schema causes the DB to be wiped and reset. This isn't // so bad during development but in the future, we would need to write a conversion // function to advance older released schemas to "current" -const int currentDatabaseVersion = 6; +static const int currentDatabaseVersion = 6; // Icons expire once every 4 days -const int iconExpirationTime = 60*60*24*4; +static const int iconExpirationTime = 60*60*24*4; -const int updateTimerDelay = 5; +static const int updateTimerDelay = 5; static bool checkIntegrityOnOpen = false; @@ -149,7 +134,8 @@ bool IconDatabase::open(const String& databasePath) // Lock here as well as first thing in the thread so the thread doesn't actually commence until the createThread() call // completes and m_syncThreadRunning is properly set m_syncLock.lock(); - m_syncThread = createThread(IconDatabase::iconDatabaseSyncThreadStart, this, "WebCore::IconDatabase"); + m_syncThread = createThread(IconDatabase::iconDatabaseSyncThreadStart, this, "WebCore: IconDatabase"); + m_syncThreadRunning = m_syncThread; m_syncLock.unlock(); if (!m_syncThread) return false; @@ -174,6 +160,9 @@ void IconDatabase::close() m_syncThreadRunning = false; m_threadTerminationRequested = false; m_removeIconsRequested = false; + + m_syncDB.close(); + ASSERT(!isOpen()); } void IconDatabase::removeAllIcons() @@ -765,8 +754,8 @@ size_t IconDatabase::iconRecordCountWithData() } IconDatabase::IconDatabase() - : m_syncThreadRunning(false) - , m_defaultIconRecord(0) + : m_syncTimer(this, &IconDatabase::syncTimerFired) + , m_syncThreadRunning(false) , m_isEnabled(false) , m_privateBrowsingEnabled(false) , m_threadTerminationRequested(false) @@ -777,9 +766,7 @@ IconDatabase::IconDatabase() , m_imported(false) , m_isImportedSet(false) { -#if PLATFORM(DARWIN) - ASSERT(pthread_main_np()); -#endif + ASSERT(isMainThread()); } IconDatabase::~IconDatabase() @@ -812,6 +799,12 @@ void IconDatabase::notifyPendingLoadDecisions() void IconDatabase::wakeSyncThread() { + // The following is balanced by the call to enableSuddenTermination in the + // syncThreadMainLoop function. + // FIXME: It would be better to only disable sudden termination if we have + // something to write, not just if we have something to read. + disableSuddenTermination(); + MutexLocker locker(m_syncLock); m_syncCondition.signal(); } @@ -819,16 +812,24 @@ void IconDatabase::wakeSyncThread() void IconDatabase::scheduleOrDeferSyncTimer() { ASSERT_NOT_SYNC_THREAD(); - if (!m_syncTimer) - m_syncTimer.set(new Timer<IconDatabase>(this, &IconDatabase::syncTimerFired)); - m_syncTimer->startOneShot(updateTimerDelay); + if (!m_syncTimer.isActive()) { + // The following is balanced by the call to enableSuddenTermination in the + // syncTimerFired function. + disableSuddenTermination(); + } + + m_syncTimer.startOneShot(updateTimerDelay); } void IconDatabase::syncTimerFired(Timer<IconDatabase>*) { ASSERT_NOT_SYNC_THREAD(); wakeSyncThread(); + + // The following is balanced by the call to disableSuddenTermination in the + // scheduleOrDeferSyncTimer function. + enableSuddenTermination(); } // ****************** @@ -1325,7 +1326,8 @@ void IconDatabase::performURLImport() void* IconDatabase::syncThreadMainLoop() { ASSERT_ICON_SYNC_THREAD(); - static bool prunedUnretainedIcons = false; + + bool shouldReenableSuddenTermination = false; m_syncLock.lock(); @@ -1364,6 +1366,7 @@ void* IconDatabase::syncThreadMainLoop() // or if private browsing is enabled // We also don't want to prune if the m_databaseCleanupCounter count is non-zero - that means someone // has asked to delay pruning + static bool prunedUnretainedIcons = false; if (didWrite && !m_privateBrowsingEnabled && !prunedUnretainedIcons && !databaseCleanupCounter) { #ifndef NDEBUG double time = currentTime(); @@ -1396,13 +1399,32 @@ void* IconDatabase::syncThreadMainLoop() // We handle those at the top of this main loop so continue to jump back up there if (shouldStopThreadActivity()) continue; - - m_syncCondition.wait(m_syncLock); + + if (shouldReenableSuddenTermination) { + // The following is balanced by the call to disableSuddenTermination in the + // wakeSyncThread function. Any time we wait on the condition, we also have + // to enableSuddenTermation, after doing the next batch of work. + enableSuddenTermination(); + } + + m_syncCondition.wait(m_syncLock); + + shouldReenableSuddenTermination = true; } + m_syncLock.unlock(); // Thread is terminating at this point - return cleanupSyncThread(); + cleanupSyncThread(); + + if (shouldReenableSuddenTermination) { + // The following is balanced by the call to disableSuddenTermination in the + // wakeSyncThread function. Any time we wait on the condition, we also have + // to enableSuddenTermation, after doing the next batch of work. + enableSuddenTermination(); + } + + return 0; } bool IconDatabase::readFromDatabase() @@ -1689,23 +1711,23 @@ void IconDatabase::removeAllIconsOnThread() } void IconDatabase::deleteAllPreparedStatements() -{ +{ ASSERT_ICON_SYNC_THREAD(); - m_setIconIDForPageURLStatement.set(0); - m_removePageURLStatement.set(0); - m_getIconIDForIconURLStatement.set(0); - m_getImageDataForIconURLStatement.set(0); - m_addIconToIconInfoStatement.set(0); - m_addIconToIconDataStatement.set(0); - m_getImageDataStatement.set(0); - m_deletePageURLsForIconURLStatement.set(0); - m_deleteIconFromIconInfoStatement.set(0); - m_deleteIconFromIconDataStatement.set(0); - m_updateIconInfoStatement.set(0); - m_updateIconDataStatement.set(0); - m_setIconInfoStatement.set(0); - m_setIconDataStatement.set(0); + m_setIconIDForPageURLStatement.clear(); + m_removePageURLStatement.clear(); + m_getIconIDForIconURLStatement.clear(); + m_getImageDataForIconURLStatement.clear(); + m_addIconToIconInfoStatement.clear(); + m_addIconToIconDataStatement.clear(); + m_getImageDataStatement.clear(); + m_deletePageURLsForIconURLStatement.clear(); + m_deleteIconFromIconInfoStatement.clear(); + m_deleteIconFromIconDataStatement.clear(); + m_updateIconInfoStatement.clear(); + m_updateIconDataStatement.clear(); + m_setIconInfoStatement.clear(); + m_setIconDataStatement.clear(); } void* IconDatabase::cleanupSyncThread() diff --git a/src/3rdparty/webkit/WebCore/loader/icon/IconDatabase.h b/src/3rdparty/webkit/WebCore/loader/icon/IconDatabase.h index 4303ae1201..40f641a298 100644 --- a/src/3rdparty/webkit/WebCore/loader/icon/IconDatabase.h +++ b/src/3rdparty/webkit/WebCore/loader/icon/IconDatabase.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) * * Redistribution and use in source and binary forms, with or without @@ -27,17 +27,15 @@ #ifndef IconDatabase_h #define IconDatabase_h -#if ENABLE(ICONDATABASE) -#include "SQLiteDatabase.h" -#endif - #include "StringHash.h" #include "Timer.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/Noncopyable.h> #include <wtf/OwnPtr.h> + #if ENABLE(ICONDATABASE) +#include "SQLiteDatabase.h" #include <wtf/Threading.h> #endif @@ -116,9 +114,9 @@ private: void wakeSyncThread(); void scheduleOrDeferSyncTimer(); - OwnPtr<Timer<IconDatabase> > m_syncTimer; void syncTimerFired(Timer<IconDatabase>*); + Timer<IconDatabase> m_syncTimer; ThreadIdentifier m_syncThread; bool m_syncThreadRunning; diff --git a/src/3rdparty/webkit/WebCore/loader/icon/IconDatabaseNone.cpp b/src/3rdparty/webkit/WebCore/loader/icon/IconDatabaseNone.cpp index c76a2c4933..a7fb88d677 100644 --- a/src/3rdparty/webkit/WebCore/loader/icon/IconDatabaseNone.cpp +++ b/src/3rdparty/webkit/WebCore/loader/icon/IconDatabaseNone.cpp @@ -25,7 +25,10 @@ #include "config.h" #include "IconDatabase.h" + +#include "PlatformString.h" #include "SharedBuffer.h" +#include <wtf/StdLibExtras.h> namespace WebCore { @@ -47,8 +50,8 @@ const int updateTimerDelay = 5; String IconDatabase::defaultDatabaseFilename() { - static String defaultDatabaseFilename = "Icons.db"; - return defaultDatabaseFilename; + DEFINE_STATIC_LOCAL(String, defaultDatabaseFilename, ("Icons.db")); + return defaultDatabaseFilename.copy(); } IconDatabase* iconDatabase() @@ -62,7 +65,7 @@ IconDatabase::IconDatabase() { } -bool IconDatabase::open(const String& databasePath) +bool IconDatabase::open(const String& /*databasePath*/) { return false; } @@ -85,7 +88,7 @@ void IconDatabase::removeAllIcons() { } -void IconDatabase::setPrivateBrowsingEnabled(bool flag) +void IconDatabase::setPrivateBrowsingEnabled(bool /*flag*/) { } @@ -99,7 +102,7 @@ void IconDatabase::readIconForPageURLFromDisk(const String&) } -Image* IconDatabase::iconForPageURL(const String& pageURL, const IntSize& size) +Image* IconDatabase::iconForPageURL(const String& /*pageURL*/, const IntSize& size) { return defaultIcon(size); } @@ -115,33 +118,33 @@ bool IconDatabase::iconDataKnownForIconURL(const String&) return false; } -String IconDatabase::iconURLForPageURL(const String& pageURL) +String IconDatabase::iconURLForPageURL(const String& /*pageURL*/) { return String(); } -Image* IconDatabase::defaultIcon(const IntSize& size) +Image* IconDatabase::defaultIcon(const IntSize& /*size*/) { return 0; } -void IconDatabase::retainIconForPageURL(const String& pageURL) +void IconDatabase::retainIconForPageURL(const String& /*pageURL*/) { } -void IconDatabase::releaseIconForPageURL(const String& pageURL) +void IconDatabase::releaseIconForPageURL(const String& /*pageURL*/) { } -void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> data, const String& iconURL) +void IconDatabase::setIconDataForIconURL(PassRefPtr<SharedBuffer> /*data*/, const String& /*iconURL*/) { } -void IconDatabase::setIconURLForPageURL(const String& iconURL, const String& pageURL) +void IconDatabase::setIconURLForPageURL(const String& /*iconURL*/, const String& /*pageURL*/) { } -void IconDatabase::setEnabled(bool enabled) +void IconDatabase::setEnabled(bool /*enabled*/) { } @@ -167,6 +170,26 @@ void IconDatabase::allowDatabaseCleanup() { } +size_t IconDatabase::pageURLMappingCount() +{ + return 0; +} + +size_t IconDatabase::retainedPageURLCount() +{ + return 0; +} + +size_t IconDatabase::iconRecordCount() +{ + return 0; +} + +size_t IconDatabase::iconRecordCountWithData() +{ + return 0; +} + void IconDatabase::setClient(IconDatabaseClient*) { } diff --git a/src/3rdparty/webkit/WebCore/loader/icon/IconFetcher.cpp b/src/3rdparty/webkit/WebCore/loader/icon/IconFetcher.cpp index efa7e1442b..d1aa2f3634 100644 --- a/src/3rdparty/webkit/WebCore/loader/icon/IconFetcher.cpp +++ b/src/3rdparty/webkit/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 { @@ -105,8 +101,6 @@ static void parseIconLink(HTMLLinkElement* link, Vector<IconLinkEntry>& entries) PassRefPtr<IconFetcher> IconFetcher::create(Frame* frame, IconFetcherClient* client) { Document* document = frame->document(); - if (!document) - return 0; HTMLHeadElement* head = document->head(); if (!head) @@ -164,7 +158,7 @@ void IconFetcher::cancel() if (m_handle) m_handle->cancel(); } - + PassRefPtr<SharedBuffer> IconFetcher::createIcon() { ASSERT(!m_entries.isEmpty()); @@ -172,8 +166,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 +184,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 +193,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 +220,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/src/3rdparty/webkit/WebCore/loader/icon/IconLoader.cpp b/src/3rdparty/webkit/WebCore/loader/icon/IconLoader.cpp index 933a6abfa0..5dd000e190 100644 --- a/src/3rdparty/webkit/WebCore/loader/icon/IconLoader.cpp +++ b/src/3rdparty/webkit/WebCore/loader/icon/IconLoader.cpp @@ -35,7 +35,9 @@ #include "ResourceHandle.h" #include "ResourceResponse.h" #include "ResourceRequest.h" +#include "SharedBuffer.h" #include "SubresourceLoader.h" +#include <wtf/UnusedParam.h> using namespace std; @@ -61,14 +63,6 @@ void IconLoader::startLoading() if (m_resourceLoader) return; - // FIXME: http://bugs.webkit.org/show_bug.cgi?id=10902 - // Once ResourceHandle will load without a DocLoader, we can remove this check. - // A frame may be documentless - one example is a frame containing only a PDF. - if (!m_frame->document()) { - LOG(IconDatabase, "Documentless-frame - icon won't be loaded"); - return; - } - // Set flag so we can detect the case where the load completes before // SubresourceLoader::create returns. m_loadIsInProgress = true; @@ -101,9 +95,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&) @@ -153,9 +151,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/src/3rdparty/webkit/WebCore/loader/icon/IconRecord.cpp b/src/3rdparty/webkit/WebCore/loader/icon/IconRecord.cpp index 599aad326c..ffea318ff8 100644 --- a/src/3rdparty/webkit/WebCore/loader/icon/IconRecord.cpp +++ b/src/3rdparty/webkit/WebCore/loader/icon/IconRecord.cpp @@ -34,7 +34,6 @@ #include "Logging.h" #include "SQLiteStatement.h" #include "SQLiteTransaction.h" -#include "SystemTime.h" #include <limits.h> diff --git a/src/3rdparty/webkit/WebCore/loader/loader.cpp b/src/3rdparty/webkit/WebCore/loader/loader.cpp index 26b8ad1ce5..ad87b1d524 100644 --- a/src/3rdparty/webkit/WebCore/loader/loader.cpp +++ b/src/3rdparty/webkit/WebCore/loader/loader.cpp @@ -48,20 +48,20 @@ namespace WebCore { #if REQUEST_MANAGEMENT_ENABLED // Match the parallel connection count used by the networking layer -// FIXME should not hardcode something like this -static const unsigned maxRequestsInFlightPerHost = 4; +static unsigned maxRequestsInFlightPerHost; // Having a limit might still help getting more important resources first static const unsigned maxRequestsInFlightForNonHTTPProtocols = 20; #else static const unsigned maxRequestsInFlightPerHost = 10000; static const unsigned maxRequestsInFlightForNonHTTPProtocols = 10000; #endif - - + Loader::Loader() : m_nonHTTPProtocolHost(AtomicString(), maxRequestsInFlightForNonHTTPProtocols) , m_requestTimer(this, &Loader::requestTimerFired) + , m_isSuspendingPendingRequests(false) { + maxRequestsInFlightPerHost = initializeMaximumHTTPConnectionCountPerHost(); } Loader::~Loader() @@ -101,8 +101,7 @@ void Loader::load(DocLoader* docLoader, CachedResource* resource, bool increment Host* host; KURL url(resource->url()); - bool isHTTP = url.protocolIs("http") || url.protocolIs("https"); - if (isHTTP) { + if (url.protocolInHTTPFamily()) { AtomicString hostName = url.host(); host = m_hosts.get(hostName.impl()); if (!host) { @@ -117,7 +116,7 @@ void Loader::load(DocLoader* docLoader, CachedResource* resource, bool increment host->addRequest(request, priority); docLoader->incrementRequestCount(); - if (priority > Low || !isHTTP || !hadRequests) { + if (priority > Low || !url.protocolInHTTPFamily() || !hadRequests) { // Try to request important resources immediately host->servePendingRequests(priority); } else { @@ -139,6 +138,9 @@ void Loader::requestTimerFired(Timer<Loader>*) void Loader::servePendingRequests(Priority minimumPriority) { + if (m_isSuspendingPendingRequests) + return; + m_requestTimer.stop(); m_nonHTTPProtocolHost.servePendingRequests(minimumPriority); @@ -156,9 +158,25 @@ void Loader::servePendingRequests(Priority minimumPriority) } } } - + +void Loader::suspendPendingRequests() +{ + ASSERT(!m_isSuspendingPendingRequests); + m_isSuspendingPendingRequests = true; +} + +void Loader::resumePendingRequests() +{ + ASSERT(m_isSuspendingPendingRequests); + m_isSuspendingPendingRequests = false; + if (!m_hosts.isEmpty() || m_nonHTTPProtocolHost.hasRequests()) + scheduleServePendingRequests(); +} + void Loader::cancelRequests(DocLoader* docLoader) { + docLoader->clearPendingPreloads(); + if (m_nonHTTPProtocolHost.hasRequests()) m_nonHTTPProtocolHost.cancelRequests(docLoader); @@ -172,12 +190,9 @@ void Loader::cancelRequests(DocLoader* docLoader) scheduleServePendingRequests(); - if (docLoader->loadInProgress()) - ASSERT(docLoader->requestCount() == 1); - else - ASSERT(docLoader->requestCount() == 0); + ASSERT(docLoader->requestCount() == (docLoader->loadInProgress() ? 1 : 0)); } - + Loader::Host::Host(const AtomicString& name, unsigned maxRequestsInFlight) : m_name(name) , m_maxRequestsInFlight(maxRequestsInFlight) @@ -210,6 +225,9 @@ bool Loader::Host::hasRequests() const void Loader::Host::servePendingRequests(Loader::Priority minimumPriority) { + if (cache()->loader()->isSuspendingPendingRequests()) + return; + bool serveMore = true; for (int priority = High; priority >= minimumPriority && serveMore; --priority) servePendingRequests(m_requestsPending[priority], serveMore); @@ -236,11 +254,7 @@ void Loader::Host::servePendingRequests(RequestQueue& requestsPending, bool& ser if (!request->cachedResource()->accept().isEmpty()) resourceRequest.setHTTPAccept(request->cachedResource()->accept()); - KURL referrer = docLoader->doc()->url(); - if ((referrer.protocolIs("http") || referrer.protocolIs("https")) && referrer.path().isEmpty()) - referrer.setPath("/"); - resourceRequest.setHTTPReferrer(referrer.string()); - FrameLoader::addHTTPOriginIfNeeded(resourceRequest, docLoader->doc()->securityOrigin()->toString()); + // Do not set the referrer or HTTP origin here. That's handled by SubresourceLoader::create. if (resourceIsCacheValidator) { CachedResource* resourceToRevalidate = request->cachedResource()->resourceToRevalidate(); @@ -260,7 +274,7 @@ void Loader::Host::servePendingRequests(RequestQueue& requestsPending, bool& ser } RefPtr<SubresourceLoader> loader = SubresourceLoader::create(docLoader->doc()->frame(), - this, resourceRequest, request->shouldSkipCanLoadCheck(), request->sendResourceLoadCallbacks()); + this, resourceRequest, request->shouldSkipCanLoadCheck(), request->sendResourceLoadCallbacks()); if (loader) { m_requestsLoading.add(loader.release(), request); request->cachedResource()->setRequestedFromNetworkingLayer(); @@ -288,6 +302,9 @@ void Loader::Host::didFinishLoading(SubresourceLoader* loader) Request* request = i->second; m_requestsLoading.remove(i); DocLoader* docLoader = request->docLoader(); + // Prevent the document from being destroyed before we are done with + // the docLoader that it will delete when the document gets deleted. + DocPtr<Document> protector(docLoader->doc()); if (!request->isMultipart()) docLoader->decrementRequestCount(); @@ -333,6 +350,9 @@ void Loader::Host::didFail(SubresourceLoader* loader, bool cancelled) Request* request = i->second; m_requestsLoading.remove(i); DocLoader* docLoader = request->docLoader(); + // Prevent the document from being destroyed before we are done with + // the docLoader that it will delete when the document gets deleted. + DocPtr<Document> protector(docLoader->doc()); if (!request->isMultipart()) docLoader->decrementRequestCount(); @@ -433,8 +453,9 @@ void Loader::Host::didReceiveData(SubresourceLoader* loader, const char* data, i ProcessingResource processingResource(this); if (resource->response().httpStatusCode() / 100 == 4) { - // Treat a 4xx response like a network error. - resource->error(); + // Treat a 4xx response like a network error for all resources but images (which will ignore the error and continue to load for + // legacy compatibility). + resource->httpStatusCodeError(); return; } diff --git a/src/3rdparty/webkit/WebCore/loader/loader.h b/src/3rdparty/webkit/WebCore/loader/loader.h index 19c3fda7dc..888da110e2 100644 --- a/src/3rdparty/webkit/WebCore/loader/loader.h +++ b/src/3rdparty/webkit/WebCore/loader/loader.h @@ -49,6 +49,10 @@ namespace WebCore { enum Priority { Low, Medium, High }; void servePendingRequests(Priority minimumPriority = Low); + bool isSuspendingPendingRequests() { return m_isSuspendingPendingRequests; } + void suspendPendingRequests(); + void resumePendingRequests(); + private: Priority determinePriority(const CachedResource*) const; void scheduleServePendingRequests(); @@ -108,6 +112,8 @@ namespace WebCore { Host m_nonHTTPProtocolHost; Timer<Loader> m_requestTimer; + + bool m_isSuspendingPendingRequests; }; } |