diff options
author | Feng Qian <> | 2009-04-10 18:11:29 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-04-10 18:11:29 -0700 |
commit | 8f72e70a9fd78eec56623b3a62e68f16b7b27e28 (patch) | |
tree | 181bf9a400c30a1bf34ea6d72560e8d00111d549 /WebCore/loader | |
parent | 7ed56f225e0ade046e1c2178977f72b2d896f196 (diff) | |
download | external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.zip external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.tar.gz external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.tar.bz2 |
AI 145796: Land the WebKit merge @r42026.
Automated import of CL 145796
Diffstat (limited to 'WebCore/loader')
59 files changed, 2057 insertions, 1008 deletions
diff --git a/WebCore/loader/Cache.cpp b/WebCore/loader/Cache.cpp index 212fca3..7f58bf2 100644 --- a/WebCore/loader/Cache.cpp +++ b/WebCore/loader/Cache.cpp @@ -145,13 +145,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); diff --git a/WebCore/loader/CachedCSSStyleSheet.cpp b/WebCore/loader/CachedCSSStyleSheet.cpp index 10d566e..1fb1a9180 100644 --- a/WebCore/loader/CachedCSSStyleSheet.cpp +++ b/WebCore/loader/CachedCSSStyleSheet.cpp @@ -114,15 +114,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() diff --git a/WebCore/loader/CachedFont.cpp b/WebCore/loader/CachedFont.cpp index 0cbdc32..922fc91 100644 --- a/WebCore/loader/CachedFont.cpp +++ b/WebCore/loader/CachedFont.cpp @@ -31,7 +31,7 @@ #include "CachedResourceClientWalker.h" #include "DOMImplementation.h" #include "FontPlatformData.h" -#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) || PLATFORM(SGL) +#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && PLATFORM(WIN_OS)) || PLATFORM(SGL) #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) || PLATFORM(SGL) +#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && PLATFORM(WIN_OS)) || PLATFORM(SGL) #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) || PLATFORM(SGL) +#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && PLATFORM(WIN_OS)) || PLATFORM(SGL) 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/WebCore/loader/CachedResource.cpp b/WebCore/loader/CachedResource.cpp index f5ce737..b4ee533 100644 --- a/WebCore/loader/CachedResource.cpp +++ b/WebCore/loader/CachedResource.cpp @@ -378,18 +378,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/WebCore/loader/CachedScript.cpp b/WebCore/loader/CachedScript.cpp index 411521b..ebf0898 100644 --- a/WebCore/loader/CachedScript.cpp +++ b/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/WebCore/loader/CachedScript.h b/WebCore/loader/CachedScript.h index 1715e06..e1c3ee0 100644 --- a/WebCore/loader/CachedScript.h +++ b/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/WebCore/loader/CrossOriginAccessControl.cpp b/WebCore/loader/CrossOriginAccessControl.cpp new file mode 100644 index 0000000..f0f8b6a --- /dev/null +++ b/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/WebCore/loader/CrossOriginAccessControl.h b/WebCore/loader/CrossOriginAccessControl.h new file mode 100644 index 0000000..267646f --- /dev/null +++ b/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/WebCore/loader/CrossOriginPreflightResultCache.cpp b/WebCore/loader/CrossOriginPreflightResultCache.cpp new file mode 100644 index 0000000..4bd05b2 --- /dev/null +++ b/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/WebCore/loader/CrossOriginPreflightResultCache.h b/WebCore/loader/CrossOriginPreflightResultCache.h new file mode 100644 index 0000000..39c3cd1 --- /dev/null +++ b/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/WebCore/loader/DocLoader.cpp b/WebCore/loader/DocLoader.cpp index 28bbde7..c96348d 100644 --- a/WebCore/loader/DocLoader.cpp +++ b/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 @@ -67,6 +68,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 @@ -423,6 +427,11 @@ void DocLoader::clearPreloads() m_preloads.clear(); } +void DocLoader::clearPendingPreloads() +{ + m_pendingPreloads.clear(); +} + #if PRELOAD_DEBUG void DocLoader::printPreloadStats() { diff --git a/WebCore/loader/DocLoader.h b/WebCore/loader/DocLoader.h index b87b622..356349e 100644 --- a/WebCore/loader/DocLoader.h +++ b/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 @@ -97,15 +98,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/WebCore/loader/DocumentLoader.cpp b/WebCore/loader/DocumentLoader.cpp index 12be864..bdf83a8 100644 --- a/WebCore/loader/DocumentLoader.cpp +++ b/WebCore/loader/DocumentLoader.cpp @@ -118,8 +118,7 @@ static inline String canonicalizedTitle(const String& title, Frame* frame) buffer.shrink(builderIndex + 1); // Replace the backslashes with currency symbols if the encoding requires it. - if (frame->document()) - frame->document()->displayBufferModifiedByEncoding(buffer.characters(), buffer.length()); + frame->document()->displayBufferModifiedByEncoding(buffer.characters(), buffer.length()); return String::adopt(buffer); } @@ -156,7 +155,7 @@ DocumentLoader::DocumentLoader(const ResourceRequest& req, const SubstituteData& , m_loadingFromCachedPage(false) , m_stopRecordingResponses(false) , m_substituteResourceDeliveryTimer(this, &DocumentLoader::substituteResourceDeliveryTimerFired) - , m_urlForHistoryReflectsClientRedirect(false) + , m_didCreateGlobalHistoryEntry(false) #if ENABLE(OFFLINE_WEB_APPLICATIONS) , m_candidateApplicationCacheGroup(0) #endif @@ -296,7 +295,7 @@ void DocumentLoader::stopLoading() // still parsing. Failure to do so can cause a world leak. Document* doc = m_frame->document(); - if (loading || (doc && doc->parsing())) + if (loading || doc->parsing()) m_frame->loader()->stopLoading(false); } @@ -474,13 +473,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(); } @@ -559,8 +557,6 @@ PassRefPtr<ArchiveResource> DocumentLoader::subresource(const KURL& url) const return 0; Document* doc = m_frame->document(); - if (!doc) - return archiveResourceForURL(url); CachedResource* resource = doc->docLoader()->cachedResource(url); if (!resource || resource->preloadResult() == CachedResource::PreloadReferenced) @@ -575,8 +571,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(); diff --git a/WebCore/loader/DocumentLoader.h b/WebCore/loader/DocumentLoader.h index 85cceef..a861457 100644 --- a/WebCore/loader/DocumentLoader.h +++ b/WebCore/loader/DocumentLoader.h @@ -160,9 +160,21 @@ namespace WebCore { KURL urlForHistory() const; bool urlForHistoryReflectsFailure() const; - bool urlForHistoryReflectsServerRedirect() const { return urlForHistory() != url(); } - bool urlForHistoryReflectsClientRedirect() const { return m_urlForHistoryReflectsClientRedirect; } - void setURLForHistoryReflectsClientRedirect(bool b) { m_urlForHistoryReflectsClientRedirect = b; } + + // 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; } @@ -297,7 +309,8 @@ namespace WebCore { HashSet<String> m_resourcesClientKnowsAbout; Vector<String> m_resourcesLoadedFromMemoryCacheForClientNotification; - bool m_urlForHistoryReflectsClientRedirect; + 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/WebCore/loader/DocumentThreadableLoader.cpp b/WebCore/loader/DocumentThreadableLoader.cpp index 05f8667..685db8c 100644 --- a/WebCore/loader/DocumentThreadableLoader.cpp +++ b/WebCore/loader/DocumentThreadableLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Google Inc. All rights reserved. + * 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 @@ -33,6 +33,9 @@ #include "AuthenticationChallenge.h" #include "Document.h" +#include "DocumentThreadableLoader.h" +#include "Frame.h" +#include "FrameLoader.h" #include "ResourceRequest.h" #include "SecurityOrigin.h" #include "SubresourceLoader.h" @@ -40,8 +43,43 @@ namespace WebCore { +void DocumentThreadableLoader::loadResourceSynchronously(Document* document, const ResourceRequest& request, ThreadableLoaderClient& client) +{ + 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, 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) { + ASSERT(document); RefPtr<DocumentThreadableLoader> loader = adoptRef(new DocumentThreadableLoader(document, client, request, callbacksSetting, contentSniff)); if (!loader->m_loader) loader = 0; @@ -81,7 +119,7 @@ void DocumentThreadableLoader::willSendRequest(SubresourceLoader*, ResourceReque // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. if (!m_document->securityOrigin()->canRequest(request.url())) { RefPtr<DocumentThreadableLoader> protect(this); - m_client->didFail(); + m_client->didFailRedirectCheck(); cancel(); } } @@ -114,10 +152,7 @@ void DocumentThreadableLoader::didFinishLoading(SubresourceLoader* loader) void DocumentThreadableLoader::didFail(SubresourceLoader*, const ResourceError& error) { ASSERT(m_client); - if (error.isCancellation()) - m_client->didGetCancelled(); - else - m_client->didFail(); + m_client->didFail(error); } void DocumentThreadableLoader::receivedCancellation(SubresourceLoader*, const AuthenticationChallenge& challenge) diff --git a/WebCore/loader/DocumentThreadableLoader.h b/WebCore/loader/DocumentThreadableLoader.h index d909091..ddf8570 100644 --- a/WebCore/loader/DocumentThreadableLoader.h +++ b/WebCore/loader/DocumentThreadableLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Google Inc. All rights reserved. + * 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 @@ -39,11 +39,12 @@ namespace WebCore { class Document; - class ResourceRequest; + struct ResourceRequest; class ThreadableLoaderClient; class DocumentThreadableLoader : public RefCounted<DocumentThreadableLoader>, public ThreadableLoader, private SubresourceLoaderClient { public: + static void loadResourceSynchronously(Document*, const ResourceRequest&, ThreadableLoaderClient&); static PassRefPtr<DocumentThreadableLoader> create(Document*, ThreadableLoaderClient*, const ResourceRequest&, LoadCallbacks, ContentSniff); virtual ~DocumentThreadableLoader(); diff --git a/WebCore/loader/EmptyClients.h b/WebCore/loader/EmptyClients.h index 5871f9f..ace7e9e 100644 --- a/WebCore/loader/EmptyClients.h +++ b/WebCore/loader/EmptyClients.h @@ -211,14 +211,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; } @@ -244,7 +244,7 @@ public: virtual void transitionToCommittedForNewPage() { } virtual void updateGlobalHistory() { } - virtual void updateGlobalHistoryForRedirectWithoutHistoryItem() { } + virtual void updateGlobalHistoryRedirectLinks() { } virtual bool shouldGoToHistoryItem(HistoryItem*) const { return false; } #ifdef ANDROID_HISTORY_CLIENT virtual void dispatchDidAddHistoryItem(HistoryItem*) const {} @@ -255,14 +255,15 @@ public: 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) { } @@ -270,6 +271,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 }; @@ -345,6 +349,9 @@ public: virtual void learnWord(const String&) { } virtual void checkSpellingOfString(const UChar*, int, int*, int*) { } virtual void checkGrammarOfString(const UChar*, int, Vector<GrammarDetail>&, int*, int*) { } +#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + virtual void checkSpellingAndGrammarOfParagraph(const UChar*, int, bool, Vector<TextCheckingResult>&) { } +#endif virtual void updateSpellingUIWithGrammarString(const String&, const GrammarDetail&) { } virtual void updateSpellingUIWithMisspelledWord(const String&) { } virtual void showSpellingUI(bool) { } @@ -397,6 +404,8 @@ public: virtual String localizedStringsURL() { return String(); } + virtual String hiddenPanels() { return String(); } + virtual void showWindow() { } virtual void closeWindow() { } diff --git a/WebCore/loader/FTPDirectoryDocument.cpp b/WebCore/loader/FTPDirectoryDocument.cpp index 08ef896..188c84c 100644 --- a/WebCore/loader/FTPDirectoryDocument.cpp +++ b/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(); @@ -276,7 +276,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 +303,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 +365,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 +387,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 +440,7 @@ bool FTPDirectoryTokenizer::write(const SegmentedString& s, bool /*appendData*/) if (!foundNewLine) { m_dest = m_buffer; - return false; + return; } UChar* start = m_buffer; @@ -460,8 +461,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/WebCore/loader/FrameLoader.cpp b/WebCore/loader/FrameLoader.cpp index edcdbf0..bfc2686 100644 --- a/WebCore/loader/FrameLoader.cpp +++ b/WebCore/loader/FrameLoader.cpp @@ -57,6 +57,7 @@ #include "FrameTree.h" #include "FrameView.h" #include "HTMLAnchorElement.h" +#include "HTMLAppletElement.h" #include "HTMLFormElement.h" #include "HTMLFrameElement.h" #include "HTMLNames.h" @@ -124,11 +125,34 @@ using namespace SVGNames; #endif using namespace HTMLNames; -#if USE(LOW_BANDWIDTH_DISPLAY) -const unsigned int cMaxPendingSourceLengthInLowBandwidthDisplay = 128 * 1024; +typedef HashSet<String, CaseFoldingHash> URLSchemesMap; + +static URLSchemesMap& localSchemes() +{ + DEFINE_STATIC_LOCAL(URLSchemesMap, localSchemes, ()); + + if (localSchemes.isEmpty()) { + localSchemes.add("file"); +#if PLATFORM(MAC) + localSchemes.add("applewebdata"); +#endif +#if PLATFORM(QT) + localSchemes.add("qrc"); #endif + } + + return localSchemes; +} + +static URLSchemesMap& noAccessSchemes() +{ + DEFINE_STATIC_LOCAL(URLSchemesMap, noAccessSchemes, ()); + + if (noAccessSchemes.isEmpty()) + noAccessSchemes.add("data"); -typedef HashSet<String, CaseFoldingHash> LocalSchemesMap; + return noAccessSchemes; +} struct FormSubmission { FormSubmission(const char* action, const String& url, PassRefPtr<FormData> formData, @@ -159,15 +183,16 @@ struct FormSubmission { struct ScheduledRedirection { enum Type { redirection, locationChange, historyNavigation, locationChangeDuringLoad }; - Type type; - double delay; - String url; - String referrer; - int historySteps; - bool lockHistory; - bool lockBackForwardList; - bool wasUserGesture; - bool wasRefresh; + + const Type type; + const double delay; + const String url; + const String referrer; + const int historySteps; + const bool lockHistory; + const bool lockBackForwardList; + const bool wasUserGesture; + const bool wasRefresh; ScheduledRedirection(double delay, const String& url, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh) : type(redirection) @@ -179,6 +204,7 @@ struct ScheduledRedirection { , wasUserGesture(wasUserGesture) , wasRefresh(refresh) { + ASSERT(!url.isEmpty()); } ScheduledRedirection(Type locationChangeType, const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh) @@ -192,6 +218,8 @@ struct ScheduledRedirection { , wasUserGesture(wasUserGesture) , wasRefresh(refresh) { + ASSERT(locationChangeType == locationChange || locationChangeType == locationChangeDuringLoad); + ASSERT(!url.isEmpty()); } explicit ScheduledRedirection(int historyNavigationSteps) @@ -199,6 +227,7 @@ struct ScheduledRedirection { , delay(0) , historySteps(historyNavigationSteps) , lockHistory(false) + , lockBackForwardList(false) , wasUserGesture(false) , wasRefresh(false) { @@ -235,6 +264,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) @@ -272,11 +306,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 @@ -392,7 +421,6 @@ void FrameLoader::changeLocation(const String& url, const String& referrer, bool changeLocation(completeURL(url), referrer, lockHistory, lockBackForwardList, userGesture, refresh); } - void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool userGesture, bool refresh) { RefPtr<Frame> protect(m_frame); @@ -424,7 +452,7 @@ void FrameLoader::urlSelected(const ResourceRequest& request, const String& _tar return; String target = _target; - if (target.isEmpty() && m_frame->document()) + if (target.isEmpty()) target = m_frame->document()->baseTarget(); FrameLoadRequest frameRequest(request, target); @@ -437,14 +465,6 @@ void FrameLoader::urlSelected(const ResourceRequest& request, const String& _tar 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; @@ -649,14 +669,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() @@ -665,16 +677,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(); } @@ -703,7 +709,7 @@ 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 @@ -777,13 +783,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(); @@ -835,7 +841,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()) { @@ -932,12 +938,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; @@ -952,14 +964,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()); @@ -995,18 +1004,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) @@ -1025,12 +1022,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); @@ -1044,11 +1057,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()) @@ -1084,7 +1092,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 @@ -1093,21 +1101,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() @@ -1191,23 +1186,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()); @@ -1219,8 +1197,6 @@ void FrameLoader::commitIconURLToIconDatabase(const KURL& icon) void FrameLoader::restoreDocumentState() { Document* doc = m_frame->document(); - if (!doc) - return; HistoryItem* itemToRestore = 0; @@ -1251,7 +1227,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(); @@ -1289,12 +1265,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) @@ -1305,19 +1283,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; @@ -1361,7 +1332,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()) @@ -1370,8 +1341,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 @@ -1400,6 +1370,9 @@ 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) #ifdef ANDROID_USER_GESTURE @@ -1420,6 +1393,9 @@ void FrameLoader::scheduleLocationChange(const String& url, const String& referr 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); @@ -1451,18 +1427,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; + ScheduledRedirection::Type type = ScheduledRedirection::locationChange; scheduleRedirection(new ScheduledRedirection(type, m_URL.string(), m_outgoingReferrer, true, true, wasUserGesture, true)); } @@ -1573,13 +1541,14 @@ void FrameLoader::redirectionTimerFired(Timer<FrameLoader>*) void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, Frame* childFrame) { ASSERT(childFrame); + HistoryItem* parentItem = currentHistoryItem(); FrameLoadType loadType = this->loadType(); 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. if (parentItem && parentItem->children().size() && isBackForwardLoadType(loadType)) { HistoryItem* childItem = parentItem->childItemWithName(childFrame->tree()->name()); @@ -1684,12 +1653,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()->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(); // Scroll nested layers and frames to reveal the anchor. // Align to the top and to the closest side (this matches other browsers). @@ -1705,10 +1672,11 @@ bool FrameLoader::gotoAnchor(const String& name) android::WebFrame::getWebFrame(m_frame)->setUserInitiatedClick(true); #endif if (renderer) - renderer->enclosingLayer()->scrollRectToVisible(rect, true, RenderLayer::gAlignToEdgeIfNeeded, RenderLayer::gAlignTopAlways); + renderer->enclosingLayer()->scrollRectToVisible(rect, true, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways); #ifdef ANDROID_SCROLL_ON_GOTO_ANCHOR android::WebFrame::getWebFrame(m_frame)->setUserInitiatedClick(false); #endif + return true; } @@ -1718,14 +1686,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); @@ -1769,15 +1729,30 @@ 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()); @@ -1785,7 +1760,7 @@ bool FrameLoader::loadPlugin(RenderPart* renderer, const KURL& url, const String } widget = m_client->createPlugin(IntSize(renderer->contentWidth(), renderer->contentHeight()), - pluginElement, url, paramNames, paramValues, mimeType, + element, url, paramNames, paramValues, mimeType, m_frame->document()->isPluginDocument()); if (widget) { renderer->setWidget(widget); @@ -1825,10 +1800,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() @@ -1937,7 +1909,6 @@ bool FrameLoader::canCachePageContainingThisFrame() // 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) #if ENABLE(DATABASE) && !m_frame->document()->hasOpenDatabases() @@ -2080,11 +2051,6 @@ bool FrameLoader::logCanCacheFrameDecision(int indentLevel) { PCLOG(" -Frame contains plugins"); cannotCache = true; } if (m_URL.protocolIs("https")) { PCLOG(" -Frame is HTTPS"); cannotCache = true; } - if (!m_frame->document()) { - PCLOG(" -There is no Document object"); - cannotCache = true; - break; - } if (m_frame->document()->hasWindowEventListener(eventNames().unloadEvent)) { PCLOG(" -Frame has an unload event listener"); cannotCache = true; } #if ENABLE(DATABASE) @@ -2126,7 +2092,7 @@ bool FrameLoader::logCanCacheFrameDecision(int indentLevel) void FrameLoader::updatePolicyBaseURL() { - if (m_frame->tree()->parent() && m_frame->tree()->parent()->document()) + if (m_frame->tree()->parent()) setPolicyBaseURL(m_frame->tree()->parent()->document()->policyBaseURL()); else setPolicyBaseURL(m_URL); @@ -2134,8 +2100,7 @@ void FrameLoader::updatePolicyBaseURL() void FrameLoader::setPolicyBaseURL(const KURL& url) { - if (m_frame->document()) - m_frame->document()->setPolicyBaseURL(url); + m_frame->document()->setPolicyBaseURL(url); for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) child->loader()->setPolicyBaseURL(url); } @@ -2398,7 +2363,8 @@ void FrameLoader::load(const ResourceRequest& request, const SubstituteData& sub // FIXME: is this the right place to reset loadType? Perhaps this should be done after loading is finished or aborted. m_loadType = FrameLoadTypeStandard; RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, substituteData); - loader->setURLForHistoryReflectsClientRedirect(lockHistory); + if (lockHistory && m_documentLoader) + loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory() : m_documentLoader->clientRedirectSourceForHistory()); load(loader.get()); } @@ -2421,7 +2387,8 @@ void FrameLoader::load(const ResourceRequest& request, const String& frameName, void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, bool lockHistory, FrameLoadType type, PassRefPtr<FormState> formState) { RefPtr<DocumentLoader> loader = m_client->createDocumentLoader(request, SubstituteData()); - loader->setURLForHistoryReflectsClientRedirect(lockHistory); + if (lockHistory && m_documentLoader) + loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory() : m_documentLoader->clientRedirectSourceForHistory()); loader->setTriggeringAction(action); if (m_documentLoader) @@ -2818,11 +2785,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) @@ -2839,16 +2801,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); @@ -2860,11 +2812,6 @@ void FrameLoader::setProvisionalDocumentLoader(DocumentLoader* loader) m_provisionalDocumentLoader = loader; } -FrameState FrameLoader::state() const -{ - return m_state; -} - double FrameLoader::timeOfLastCompletedLoad() { return storedTimeOfLastCompletedLoad; @@ -2906,11 +2853,7 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) // 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_currentHistoryItem->isInPageCache()) { - if (Document* document = m_frame->document()) - document->suspendActiveDOMObjects(); - cachePageForHistoryItem(m_currentHistoryItem.get()); - } + cachePageForHistoryItem(m_currentHistoryItem.get()); if (m_loadType != FrameLoadTypeReplace) closeOldDataSources(); @@ -2944,7 +2887,37 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) LOG(Loading, "WebCoreLoading %s: Finished committing provisional load to URL %s", m_frame->tree()->name().string().utf8().data(), m_URL.string().utf8().data()); - 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(); + } } void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) @@ -3117,6 +3090,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); @@ -3124,11 +3098,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()) { @@ -3136,7 +3105,19 @@ void FrameLoader::open(CachedPage& cachedPage) m_frame->setJSDefaultStatusBarText(String()); } - KURL url = cachedPage.url(); + open(*cachedPage.cachedMainFrame()); + + 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.protocolIs("http") || url.protocolIs("https")) && !url.host().isEmpty() && url.path().isEmpty()) url.setPath("/"); @@ -3148,7 +3129,7 @@ void FrameLoader::open(CachedPage& cachedPage) clear(); - Document* document = cachedPage.document(); + Document* document = cachedFrame.document(); ASSERT(document); document->setInPageCache(false); @@ -3157,13 +3138,16 @@ 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()); @@ -3171,14 +3155,7 @@ void FrameLoader::open(CachedPage& cachedPage) updatePolicyBaseURL(); - cachedPage.restore(m_frame->page()); - document->resumeActiveDOMObjects(); - - // It is necessary to update any platform script objects after restoring the - // cached page. - m_frame->script()->updatePlatformScriptObjects(); - - checkCompleted(); + cachedFrame.restore(); } bool FrameLoader::isStopping() const @@ -3323,7 +3300,7 @@ CachePolicy FrameLoader::cachePolicy() const if (m_isComplete) return CachePolicyVerify; - if (m_loadType == FrameLoadTypeReloadFromOrigin) + if (m_loadType == FrameLoadTypeReloadFromOrigin || documentLoader()->request().cachePolicy() == ReloadIgnoringCacheData) return CachePolicyReload; if (Frame* parentFrame = m_frame->tree()->parent()) { @@ -3443,9 +3420,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; } @@ -3506,8 +3481,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) @@ -3534,6 +3515,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; @@ -3615,8 +3608,7 @@ void FrameLoader::handledOnloadEvents() void FrameLoader::frameDetached() { stopAllLoaders(); - if (Document* document = m_frame->document()) - document->stopActiveDOMObjects(); + m_frame->document()->stopActiveDOMObjects(); detachFromParent(); } @@ -3636,7 +3628,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); @@ -3656,6 +3648,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.mainDocumentURL().isEmpty()) { + if (mainResource && (isLoadingMainFrame() || cookiePolicyURLFromRequest)) + request.setMainDocumentURL(request.url()); + else if (Page* page = m_frame->page()) + request.setMainDocumentURL(page->mainFrame()->loader()->url()); + } + + // The remaining modifications are only necessary for HTTP and HTTPS. + if (!request.url().isEmpty() && !request.url().protocolInHTTPFamily()) + return; + applyUserAgent(request); if (loadType == FrameLoadTypeReload) { @@ -3667,14 +3672,6 @@ 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"); @@ -3978,42 +3975,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, @@ -4337,10 +4299,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); - m_client->savePlatformDataToCachedFrame(cachedPage->cachedMainFrame()); - pageCache()->add(item, cachedPage.release()); } } @@ -4552,7 +4515,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()); } @@ -4845,13 +4808,12 @@ void FrameLoader::updateHistoryForStandardLoad() m_navigationDuringLoad = false; } - bool didUpdateGlobalHistory = false; if (!frameNavigationDuringLoad && !documentLoader()->isClientRedirect()) { if (!historyURL.isEmpty()) { addBackForwardItemClippedAtTarget(true); if (!needPrivacy) { m_client->updateGlobalHistory(); - didUpdateGlobalHistory = true; + m_documentLoader->setDidCreateGlobalHistoryEntry(true); } if (Page* page = m_frame->page()) page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForwardList()->currentItem()); @@ -4865,8 +4827,8 @@ void FrameLoader::updateHistoryForStandardLoad() if (Page* page = m_frame->page()) page->group().addVisitedLink(historyURL); - if (!didUpdateGlobalHistory && !url().isEmpty()) - m_client->updateGlobalHistoryForRedirectWithoutHistoryItem(); + if (!m_documentLoader->didCreateGlobalHistoryEntry() && documentLoader()->unreachableURL().isEmpty() && !url().isEmpty()) + m_client->updateGlobalHistoryRedirectLinks(); } } @@ -4935,14 +4897,13 @@ void FrameLoader::updateHistoryForRedirectWithLockedBackForwardList() bool needPrivacy = !settings || settings->privateBrowsingEnabled(); const KURL& historyURL = documentLoader()->urlForHistory(); - bool didUpdateGlobalHistory = false; if (documentLoader()->isClientRedirect()) { if (!m_currentHistoryItem && !m_frame->tree()->parent()) { if (!historyURL.isEmpty()) { addBackForwardItemClippedAtTarget(true); if (!needPrivacy) { m_client->updateGlobalHistory(); - didUpdateGlobalHistory = true; + m_documentLoader->setDidCreateGlobalHistoryEntry(true); } if (Page* page = m_frame->page()) page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForwardList()->currentItem()); @@ -4962,8 +4923,8 @@ void FrameLoader::updateHistoryForRedirectWithLockedBackForwardList() if (Page* page = m_frame->page()) page->group().addVisitedLink(historyURL); - if (!didUpdateGlobalHistory && !url().isEmpty()) - m_client->updateGlobalHistoryForRedirectWithoutHistoryItem(); + if (!m_documentLoader->didCreateGlobalHistoryEntry() && documentLoader()->unreachableURL().isEmpty() && !url().isEmpty()) + m_client->updateGlobalHistoryRedirectLinks(); } } @@ -5013,33 +4974,18 @@ void FrameLoader::saveDocumentAndScrollState() } } -// FIXME: These 6 setter/getters are here for a dwindling number of users in WebKit, WebFrame +// FIXME: These 3 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; @@ -5211,6 +5157,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()) @@ -5226,15 +5177,18 @@ 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); } @@ -5243,10 +5197,14 @@ Widget* FrameLoader::createJavaAppletWidget(const IntSize& size, Element* elemen 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; - + Widget* widget = 0; + KURL codeBaseURL = completeURL(codeBaseURLString); + if (canLoad(codeBaseURL, String(), element->document())) { + widget = m_client->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues); + if (widget) + m_containsPlugIns = true; + } + return widget; } @@ -5309,7 +5267,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. @@ -5327,6 +5285,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) @@ -5416,124 +5384,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() +bool FrameLoaderClient::hasHTMLView() const { - 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() -{ - 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/WebCore/loader/FrameLoader.h b/WebCore/loader/FrameLoader.h index 4650b03..9b68601 100644 --- a/WebCore/loader/FrameLoader.h +++ b/WebCore/loader/FrameLoader.h @@ -36,16 +36,13 @@ #include "ResourceRequest.h" #include "Timer.h" -#if USE(LOW_BANDWIDTH_DISPLAY) -#include "CachedResourceClient.h" -#endif - namespace WebCore { #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size class Archive; #endif class AuthenticationChallenge; + class CachedFrame; class CachedPage; class CachedResource; class Document; @@ -56,6 +53,7 @@ namespace WebCore { class Frame; class FrameLoaderClient; class HistoryItem; + class HTMLAppletElement; class HTMLFormElement; class HTMLFrameOwnerElement; class IconLoader; @@ -115,11 +113,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(); @@ -196,10 +190,10 @@ namespace WebCore { 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*); @@ -283,6 +277,7 @@ namespace WebCore { void checkLoadComplete(); void detachFromParent(); void detachChildren(); + void closeAndRemoveChild(Frame*); void addExtraFieldsToSubresourceRequest(ResourceRequest&); void addExtraFieldsToMainResourceRequest(ResourceRequest&); @@ -355,9 +350,10 @@ namespace WebCore { 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(); @@ -424,10 +420,7 @@ namespace WebCore { // 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&); @@ -441,19 +434,12 @@ namespace WebCore { 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; } @@ -563,7 +549,8 @@ namespace WebCore { void closeOldDataSources(); void open(CachedPage&); - void opened(); + void open(CachedFrame&); + void updateHistoryAfterClientRedirect(); void clear(bool clearWindowProperties = true, bool clearScriptObjects = true); @@ -575,14 +562,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); @@ -682,26 +661,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/WebCore/loader/FrameLoaderClient.cpp b/WebCore/loader/FrameLoaderClient.cpp deleted file mode 100644 index 9610fd1..0000000 --- a/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/WebCore/loader/FrameLoaderClient.h b/WebCore/loader/FrameLoaderClient.h index 52dcfab..4f2b86e 100644 --- a/WebCore/loader/FrameLoaderClient.h +++ b/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 @@ -53,21 +53,22 @@ 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 SharedBuffer; class SubstituteData; class String; class Widget; - class ResourceRequest; - #ifdef ANDROID_HISTORY_CLIENT class BackForwardList; #endif @@ -76,12 +77,17 @@ namespace WebCore { 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; @@ -155,7 +161,7 @@ namespace WebCore { virtual void finishedLoading(DocumentLoader*) = 0; virtual void updateGlobalHistory() = 0; - virtual void updateGlobalHistoryForRedirectWithoutHistoryItem() = 0; + virtual void updateGlobalHistoryRedirectLinks() = 0; virtual bool shouldGoToHistoryItem(HistoryItem*) const = 0; #ifdef ANDROID_HISTORY_CLIENT @@ -201,15 +207,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; @@ -220,12 +227,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); }; } // namespace WebCore diff --git a/WebCore/loader/ImageDocument.cpp b/WebCore/loader/ImageDocument.cpp index 8f58179..ca3f785 100644 --- a/WebCore/loader/ImageDocument.cpp +++ b/WebCore/loader/ImageDocument.cpp @@ -63,7 +63,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 +91,9 @@ private: // -------- -bool ImageTokenizer::write(const SegmentedString&, bool) +void ImageTokenizer::write(const SegmentedString&, bool) { ASSERT_NOT_REACHED(); - return false; } bool ImageTokenizer::writeRawData(const char*, int) @@ -166,10 +165,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); diff --git a/WebCore/loader/ImageLoader.cpp b/WebCore/loader/ImageLoader.cpp index 43e08c0..b183a66 100644 --- a/WebCore/loader/ImageLoader.cpp +++ b/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/WebCore/loader/ImageLoader.h b/WebCore/loader/ImageLoader.h index fc3a58a..3496f75 100644 --- a/WebCore/loader/ImageLoader.h +++ b/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/WebCore/loader/MainResourceLoader.cpp b/WebCore/loader/MainResourceLoader.cpp index 325809b..5aa1cde 100644 --- a/WebCore/loader/MainResourceLoader.cpp +++ b/WebCore/loader/MainResourceLoader.cpp @@ -186,7 +186,11 @@ void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const Reso 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 +252,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 +260,7 @@ void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy, didFinishLoading(); } else if (shouldLoadAsEmptyDocument(url) || frameLoader()->representationExistsForURLScheme(url.protocol())) didFinishLoading(); + } } void MainResourceLoader::callContinueAfterContentPolicy(void* argument, PolicyAction policy) @@ -319,6 +324,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 +412,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 +424,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 +523,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/WebCore/loader/MainResourceLoader.h b/WebCore/loader/MainResourceLoader.h index 6c69c1f..d9ce2f9 100644 --- a/WebCore/loader/MainResourceLoader.h +++ b/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/WebCore/loader/MediaDocument.cpp b/WebCore/loader/MediaDocument.cpp index 8ed5b45..b89ac10 100644 --- a/WebCore/loader/MediaDocument.cpp +++ b/WebCore/loader/MediaDocument.cpp @@ -54,7 +54,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 +68,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, ""); @@ -147,18 +146,17 @@ 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(); } } diff --git a/WebCore/loader/NetscapePlugInStreamLoader.cpp b/WebCore/loader/NetscapePlugInStreamLoader.cpp index e12ed07..9d0e81b 100644 --- a/WebCore/loader/NetscapePlugInStreamLoader.cpp +++ b/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/WebCore/loader/PluginDocument.cpp b/WebCore/loader/PluginDocument.cpp index 42c801c..373126f 100644 --- a/WebCore/loader/PluginDocument.cpp +++ b/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/WebCore/loader/ResourceLoader.h b/WebCore/loader/ResourceLoader.h index c8cc208..d3e7b80 100644 --- a/WebCore/loader/ResourceLoader.h +++ b/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; } diff --git a/WebCore/loader/SubresourceLoader.h b/WebCore/loader/SubresourceLoader.h index 1a94c73..0fce930 100644 --- a/WebCore/loader/SubresourceLoader.h +++ b/WebCore/loader/SubresourceLoader.h @@ -33,7 +33,7 @@ namespace WebCore { - class ResourceRequest; + struct ResourceRequest; class SubresourceLoaderClient; class SubresourceLoader : public ResourceLoader { diff --git a/WebCore/loader/SubresourceLoaderClient.h b/WebCore/loader/SubresourceLoaderClient.h index 76fde47..acf8e6a 100644 --- a/WebCore/loader/SubresourceLoaderClient.h +++ b/WebCore/loader/SubresourceLoaderClient.h @@ -33,7 +33,7 @@ namespace WebCore { class AuthenticationChallenge; class ResourceError; -class ResourceRequest; +struct ResourceRequest; class ResourceResponse; class SubresourceLoader; diff --git a/WebCore/loader/TextDocument.cpp b/WebCore/loader/TextDocument.cpp index bd2c446..0d86c1b 100644 --- a/WebCore/loader/TextDocument.cpp +++ b/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/WebCore/loader/TextResourceDecoder.cpp b/WebCore/loader/TextResourceDecoder.cpp index f37d8f7..ee81326 100644 --- a/WebCore/loader/TextResourceDecoder.cpp +++ b/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/WebCore/loader/TextResourceDecoder.h b/WebCore/loader/TextResourceDecoder.h index 8bbe85e..fb755c9 100644 --- a/WebCore/loader/TextResourceDecoder.h +++ b/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/WebCore/loader/ThreadableLoader.cpp b/WebCore/loader/ThreadableLoader.cpp index 0f22fbe..1b2cc88 100644 --- a/WebCore/loader/ThreadableLoader.cpp +++ b/WebCore/loader/ThreadableLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Google Inc. All rights reserved. + * 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 @@ -31,11 +31,12 @@ #include "config.h" #include "ThreadableLoader.h" -#include "DocumentThreadableLoader.h" -#include "Document.h" -#include "Frame.h" -#include "FrameLoader.h" #include "ScriptExecutionContext.h" +#include "Document.h" +#include "DocumentThreadableLoader.h" +#include "WorkerContext.h" +#include "WorkerRunLoop.h" +#include "WorkerThreadableLoader.h" namespace WebCore { @@ -43,20 +44,29 @@ PassRefPtr<ThreadableLoader> ThreadableLoader::create(ScriptExecutionContext* co { ASSERT(client); ASSERT(context); - ASSERT(context->isDocument()); +#if ENABLE(WORKERS) + if (context->isWorkerContext()) + return WorkerThreadableLoader::create(static_cast<WorkerContext*>(context), client, WorkerRunLoop::defaultMode(), request, callbacksSetting, contentSniff); +#endif // ENABLE(WORKERS) + + ASSERT(context->isDocument()); return DocumentThreadableLoader::create(static_cast<Document*>(context), client, request, callbacksSetting, contentSniff); } -unsigned long ThreadableLoader::loadResourceSynchronously(ScriptExecutionContext* context, const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) +void ThreadableLoader::loadResourceSynchronously(ScriptExecutionContext* context, const ResourceRequest& request, ThreadableLoaderClient& client) { ASSERT(context); - ASSERT(context->isDocument()); - Document* document = static_cast<Document*>(context); - if (!document->frame()) - return std::numeric_limits<unsigned long>::max(); - return document->frame()->loader()->loadResourceSynchronously(request, error, response, data); +#if ENABLE(WORKERS) + if (context->isWorkerContext()) { + WorkerThreadableLoader::loadResourceSynchronously(static_cast<WorkerContext*>(context), request, client); + return; + } +#endif // ENABLE(WORKERS) + + ASSERT(context->isDocument()); + DocumentThreadableLoader::loadResourceSynchronously(static_cast<Document*>(context), request, client); } } // namespace WebCore diff --git a/WebCore/loader/ThreadableLoader.h b/WebCore/loader/ThreadableLoader.h index e8ff058..b051b32 100644 --- a/WebCore/loader/ThreadableLoader.h +++ b/WebCore/loader/ThreadableLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Google Inc. All rights reserved. + * 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 @@ -38,7 +38,7 @@ namespace WebCore { class ResourceError; - class ResourceRequest; + struct ResourceRequest; class ResourceResponse; class ScriptExecutionContext; class ThreadableLoaderClient; @@ -57,8 +57,8 @@ namespace WebCore { // just able to run on threads other than the main thread). class ThreadableLoader : Noncopyable { public: + static void loadResourceSynchronously(ScriptExecutionContext*, const ResourceRequest&, ThreadableLoaderClient&); static PassRefPtr<ThreadableLoader> create(ScriptExecutionContext*, ThreadableLoaderClient*, const ResourceRequest&, LoadCallbacks, ContentSniff); - static unsigned long loadResourceSynchronously(ScriptExecutionContext*, const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>& data); virtual void cancel() = 0; void ref() { refThreadableLoader(); } diff --git a/WebCore/loader/ThreadableLoaderClient.h b/WebCore/loader/ThreadableLoaderClient.h index 4fde404..93a8e86 100644 --- a/WebCore/loader/ThreadableLoaderClient.h +++ b/WebCore/loader/ThreadableLoaderClient.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Google Inc. All rights reserved. + * 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 @@ -33,6 +33,7 @@ namespace WebCore { + class ResourceError; class ResourceResponse; class ThreadableLoaderClient { @@ -41,9 +42,9 @@ namespace WebCore { virtual void didReceiveResponse(const ResourceResponse&) { } virtual void didReceiveData(const char*, int /*lengthReceived*/) { } - virtual void didFinishLoading(unsigned long /*identifer*/) { } - virtual void didFail() { } - virtual void didGetCancelled() { } + virtual void didFinishLoading(unsigned long /*identifier*/) { } + virtual void didFail(const ResourceError&) { } + virtual void didFailRedirectCheck() { } virtual void didReceiveAuthenticationCancellation(const ResourceResponse&) { } diff --git a/WebCore/loader/ThreadableLoaderClientWrapper.h b/WebCore/loader/ThreadableLoaderClientWrapper.h new file mode 100644 index 0000000..d3c1a9f --- /dev/null +++ b/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/WebCore/loader/WorkerThreadableLoader.cpp b/WebCore/loader/WorkerThreadableLoader.cpp new file mode 100644 index 0000000..731ffcb --- /dev/null +++ b/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) + : 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))) +{ +} + +WorkerThreadableLoader::~WorkerThreadableLoader() +{ + m_bridge.destroy(); +} + +void WorkerThreadableLoader::loadResourceSynchronously(WorkerContext* workerContext, const ResourceRequest& request, ThreadableLoaderClient& client) +{ + 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); + + 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) + : m_workerClientWrapper(workerClientWrapper) + , m_messagingProxy(messagingProxy) + , m_taskMode(taskMode.copy()) +{ + ASSERT(m_workerClientWrapper.get()); + m_messagingProxy.postTaskToWorkerObject(createCallbackTask(&MainThreadBridge::mainThreadCreateLoader, this, request, callbacksSetting, contentSniff)); +} + +WorkerThreadableLoader::MainThreadBridge::~MainThreadBridge() +{ +} + +void WorkerThreadableLoader::MainThreadBridge::mainThreadCreateLoader(ScriptExecutionContext* context, MainThreadBridge* thisPtr, auto_ptr<CrossThreadResourceRequestData> requestData, LoadCallbacks callbacksSetting, ContentSniff contentSniff) +{ + // 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); + 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/WebCore/loader/WorkerThreadableLoader.h b/WebCore/loader/WorkerThreadableLoader.h new file mode 100644 index 0000000..5462a97 --- /dev/null +++ b/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&); + static PassRefPtr<WorkerThreadableLoader> create(WorkerContext* workerContext, ThreadableLoaderClient* client, const String& taskMode, const ResourceRequest& request, LoadCallbacks callbacksSetting, ContentSniff contentSniff) + { + return adoptRef(new WorkerThreadableLoader(workerContext, client, taskMode, request, callbacksSetting, contentSniff)); + } + + ~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); + 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); + 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); + + RefPtr<WorkerContext> m_workerContext; + RefPtr<ThreadableLoaderClientWrapper> m_workerClientWrapper; + MainThreadBridge& m_bridge; + }; + +} // namespace WebCore + +#endif // ENABLE(WORKERS) + +#endif // WorkerThreadableLoader_h diff --git a/WebCore/loader/appcache/ApplicationCache.cpp b/WebCore/loader/appcache/ApplicationCache.cpp index abe8b22..e411852 100644 --- a/WebCore/loader/appcache/ApplicationCache.cpp +++ b/WebCore/loader/appcache/ApplicationCache.cpp @@ -81,7 +81,7 @@ void ApplicationCache::addResource(PassRefPtr<ApplicationCacheResource> resource if (m_storageID) { ASSERT(!resource->storageID()); - ASSERT(resource->type() & (ApplicationCacheResource::Dynamic | ApplicationCacheResource::Implicit)); + ASSERT(resource->type() & (ApplicationCacheResource::Dynamic | ApplicationCacheResource::Master)); // Add the resource to the storage. cacheStorage().store(resource.get(), this); @@ -146,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/WebCore/loader/appcache/ApplicationCacheGroup.cpp b/WebCore/loader/appcache/ApplicationCacheGroup.cpp index 2367e79..1b16b50 100644 --- a/WebCore/loader/appcache/ApplicationCacheGroup.cpp +++ b/WebCore/loader/appcache/ApplicationCacheGroup.cpp @@ -154,7 +154,14 @@ void ApplicationCacheGroup::selectCache(Frame* frame, const KURL& manifestURL) // Check that the resource URL has the same scheme/host/port as the manifest URL. 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); documentLoader->setCandidateApplicationCacheGroup(group); @@ -184,7 +191,7 @@ void ApplicationCacheGroup::finishedLoadingMainResource(DocumentLoader* loader) { ASSERT(m_pendingMasterResourceLoaders.contains(loader)); ASSERT(m_completionType == None || m_pendingEntries.isEmpty()); - const KURL& url = loader->originalURL(); + const KURL& url = loader->url(); switch (m_completionType) { case None: @@ -195,12 +202,12 @@ void ApplicationCacheGroup::finishedLoadingMainResource(DocumentLoader* loader) associateDocumentLoaderWithCache(loader, m_newestCache.get()); if (ApplicationCacheResource* resource = m_newestCache->resourceForURL(url)) { - if (!(resource->type() & ApplicationCacheResource::Implicit)) { - resource->addType(ApplicationCacheResource::Implicit); + if (!(resource->type() & ApplicationCacheResource::Master)) { + resource->addType(ApplicationCacheResource::Master); ASSERT(!resource->storageID()); } } else - m_newestCache->addResource(ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Implicit, loader->mainResourceData())); + m_newestCache->addResource(ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Master, loader->mainResourceData())); break; case Failure: @@ -215,12 +222,12 @@ void ApplicationCacheGroup::finishedLoadingMainResource(DocumentLoader* loader) ASSERT(m_associatedDocumentLoaders.contains(loader)); if (ApplicationCacheResource* resource = m_cacheBeingUpdated->resourceForURL(url)) { - if (!(resource->type() & ApplicationCacheResource::Implicit)) { - resource->addType(ApplicationCacheResource::Implicit); + if (!(resource->type() & ApplicationCacheResource::Master)) { + resource->addType(ApplicationCacheResource::Master); ASSERT(!resource->storageID()); } } else - m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, loader->response(), ApplicationCacheResource::Implicit, loader->mainResourceData())); + 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; } @@ -369,6 +376,16 @@ void ApplicationCacheGroup::update(Frame* frame, ApplicationCacheUpdateOption up 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; @@ -385,14 +402,29 @@ void ApplicationCacheGroup::update(Frame* frame, ApplicationCacheUpdateOption up 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); - // FIXME: Should ask to revalidate from origin. + 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) { @@ -409,11 +441,24 @@ 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 (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(); @@ -428,12 +473,12 @@ void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const Res // Copy the resource and its metadata from the newest application cache in cache group whose completeness flag is complete, and act // as if that was the fetched resource, ignoring the resource obtained from the network. ASSERT(m_newestCache); - ApplicationCacheResource* resource = m_newestCache->resourceForURL(handle->request().url()); - ASSERT(resource); - m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(handle->request().url(), resource->response(), resource->type(), resource->data())); - // Load the next resource, if any. + 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; @@ -484,10 +529,11 @@ void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError& } 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(handle->request().url())); + ASSERT(!m_currentResource || !m_pendingEntries.contains(url)); m_currentResource = 0; - m_pendingEntries.remove(handle->request().url()); + m_pendingEntries.remove(url); if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) { // Note that cacheUpdateFailed() can cause the cache group to be deleted. @@ -496,9 +542,9 @@ void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError& // Copy the resource and its metadata from the newest application cache in cache group whose completeness flag is complete, and act // as if that was the fetched resource, ignoring the resource obtained from the network. ASSERT(m_newestCache); - ApplicationCacheResource* resource = m_newestCache->resourceForURL(handle->request().url()); - ASSERT(resource); - m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(handle->request().url(), resource->response(), resource->type(), resource->data())); + 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(); } @@ -514,6 +560,9 @@ void ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& r return; } + if (response.httpStatusCode() == 304) + return; + if (response.httpStatusCode() / 100 != 2 || response.url() != m_manifestHandle->request().url() || !equalIgnoringCase(response.mimeType(), "text/cache-manifest")) { cacheUpdateFailed(); return; @@ -525,28 +574,29 @@ void ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& r 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) { + 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())) { + 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; @@ -581,7 +631,7 @@ void ApplicationCacheGroup::didFinishLoadingManifest() 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); } } @@ -657,7 +707,11 @@ void ApplicationCacheGroup::checkIfLoadIsComplete() case NoUpdate: ASSERT(isUpgradeAttempt); ASSERT(!m_cacheBeingUpdated); - ASSERT(m_storageID); + + // The storage could have been manually emptied by the user. + if (!m_storageID) + cacheStorage().storeNewestCache(this); + postListenerTask(&DOMApplicationCache::callNoUpdateListener, m_associatedDocumentLoaders); break; case Failure: @@ -707,15 +761,9 @@ void ApplicationCacheGroup::startLoadingEntry() postListenerTask(&DOMApplicationCache::callProgressListener, m_associatedDocumentLoaders); - // 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); - // FIXME: Should ask to revalidate from origin. - - m_currentHandle = ResourceHandle::create(request, this, m_frame, false, true, false); + m_currentHandle = createResourceHandle(KURL(it->first), m_newestCache ? m_newestCache->resourceForURL(it->first) : 0); } void ApplicationCacheGroup::deliverDelayedMainResources() @@ -743,10 +791,10 @@ 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); diff --git a/WebCore/loader/appcache/ApplicationCacheGroup.h b/WebCore/loader/appcache/ApplicationCacheGroup.h index e4b2d3e..aebf0ab 100644 --- a/WebCore/loader/appcache/ApplicationCacheGroup.h +++ b/WebCore/loader/appcache/ApplicationCacheGroup.h @@ -92,9 +92,11 @@ public: private: typedef void (DOMApplicationCache::*ListenerFunction)(); - void postListenerTask(ListenerFunction, const HashSet<DocumentLoader*>&); - void postListenerTask(ListenerFunction, const Vector<RefPtr<DocumentLoader> >& loaders); - void postListenerTask(ListenerFunction, DocumentLoader*); + 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); diff --git a/WebCore/loader/appcache/ApplicationCacheResource.cpp b/WebCore/loader/appcache/ApplicationCacheResource.cpp index d78cf7f..7c1241b 100644 --- a/WebCore/loader/appcache/ApplicationCacheResource.cpp +++ b/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/WebCore/loader/appcache/ApplicationCacheResource.h b/WebCore/loader/appcache/ApplicationCacheResource.h index 96f5a0e..28d8280 100644 --- a/WebCore/loader/appcache/ApplicationCacheResource.h +++ b/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/WebCore/loader/appcache/DOMApplicationCache.cpp b/WebCore/loader/appcache/DOMApplicationCache.cpp index 5bc4420..90d2930 100644 --- a/WebCore/loader/appcache/DOMApplicationCache.cpp +++ b/WebCore/loader/appcache/DOMApplicationCache.cpp @@ -132,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)); } diff --git a/WebCore/loader/appcache/ManifestParser.cpp b/WebCore/loader/appcache/ManifestParser.cpp index a83303a..4169313 100644 --- a/WebCore/loader/appcache/ManifestParser.cpp +++ b/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,9 +45,12 @@ bool parseManifest(const KURL& manifestURL, const char* data, int length, Manife ASSERT(manifest.fallbackURLs.isEmpty()); Mode mode = Explicit; - String s = UTF8Encoding().decode(data, length); + + RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("text/cache-manifest", "UTF-8"); + String s = decoder->decode(data, length); + s += decoder->flush(); - // Look for the magic signature: "^\xFEFF?CACHE MANIFEST[ \t]?" (the BOM is removed by decode()). + // 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")) diff --git a/WebCore/loader/archive/cf/LegacyWebArchive.cpp b/WebCore/loader/archive/cf/LegacyWebArchive.cpp index b00d93c..d1269cc 100644 --- a/WebCore/loader/archive/cf/LegacyWebArchive.cpp +++ b/WebCore/loader/archive/cf/LegacyWebArchive.cpp @@ -39,6 +39,7 @@ #include "HTMLFrameOwnerElement.h" #include "HTMLNames.h" #include "IconDatabase.h" +#include "Image.h" #include "KURLHash.h" #include "Logging.h" #include "markup.h" @@ -62,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; } @@ -96,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()); } @@ -115,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()); } @@ -124,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 @@ -148,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 @@ -160,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) @@ -237,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); @@ -266,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; @@ -290,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) @@ -371,7 +367,8 @@ 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; @@ -383,6 +380,7 @@ RetainPtr<CFDataRef> LegacyWebArchive::rawDataRepresentation() 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()); @@ -395,20 +393,21 @@ RetainPtr<CFDataRef> LegacyWebArchive::rawDataRepresentation() } #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) @@ -452,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) @@ -480,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); @@ -497,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); @@ -517,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(); @@ -549,13 +543,13 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString } } - // Add favicon if one exists for this page - if (iconDatabase() && iconDatabase()->isEnabled()) { + // 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))) { - RefPtr<ArchiveResource> resource = ArchiveResource::create(iconImage->data(), KURL(iconURL), "image/x-icon", "", ""); - subresources.append(resource.release()); + if (RefPtr<ArchiveResource> resource = ArchiveResource::create(iconImage->data(), KURL(iconURL), "image/x-icon", "", "")) + subresources.append(resource.release()); } } } @@ -568,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 @@ -588,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/WebCore/loader/archive/cf/LegacyWebArchive.h b/WebCore/loader/archive/cf/LegacyWebArchive.h index 70faba5..8c8f2e4 100644 --- a/WebCore/loader/archive/cf/LegacyWebArchive.h +++ b/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/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm b/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm index b853a15..c474bba 100644 --- a/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm +++ b/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/WebCore/loader/cf/ResourceLoaderCFNet.cpp b/WebCore/loader/cf/ResourceLoaderCFNet.cpp new file mode 100644 index 0000000..83642c6 --- /dev/null +++ b/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/WebCore/loader/icon/IconDatabase.cpp b/WebCore/loader/icon/IconDatabase.cpp index 5705f7a..0521381 100644 --- a/WebCore/loader/icon/IconDatabase.cpp +++ b/WebCore/loader/icon/IconDatabase.cpp @@ -149,7 +149,7 @@ bool IconDatabase::open(const String& databasePath) // Lock here as well as first thing in the thread so the thread doesn't actually commence until the createThread() call // completes and m_syncThreadRunning is properly set m_syncLock.lock(); - m_syncThread = createThread(IconDatabase::iconDatabaseSyncThreadStart, this, "WebCore::IconDatabase"); + m_syncThread = createThread(IconDatabase::iconDatabaseSyncThreadStart, this, "WebCore: IconDatabase"); m_syncLock.unlock(); if (!m_syncThread) return false; diff --git a/WebCore/loader/icon/IconDatabaseNone.cpp b/WebCore/loader/icon/IconDatabaseNone.cpp index c76a2c4..a7fb88d 100644 --- a/WebCore/loader/icon/IconDatabaseNone.cpp +++ b/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/WebCore/loader/icon/IconFetcher.cpp b/WebCore/loader/icon/IconFetcher.cpp index 69eeb7c..d1aa2f3 100644 --- a/WebCore/loader/icon/IconFetcher.cpp +++ b/WebCore/loader/icon/IconFetcher.cpp @@ -101,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) diff --git a/WebCore/loader/icon/IconLoader.cpp b/WebCore/loader/icon/IconLoader.cpp index b7bf115..5dd000e 100644 --- a/WebCore/loader/icon/IconLoader.cpp +++ b/WebCore/loader/icon/IconLoader.cpp @@ -35,6 +35,7 @@ #include "ResourceHandle.h" #include "ResourceResponse.h" #include "ResourceRequest.h" +#include "SharedBuffer.h" #include "SubresourceLoader.h" #include <wtf/UnusedParam.h> @@ -62,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; diff --git a/WebCore/loader/loader.cpp b/WebCore/loader/loader.cpp index 22fb158..b8d1ab7 100644 --- a/WebCore/loader/loader.cpp +++ b/WebCore/loader/loader.cpp @@ -159,6 +159,8 @@ void Loader::servePendingRequests(Priority minimumPriority) void Loader::cancelRequests(DocLoader* docLoader) { + docLoader->clearPendingPreloads(); + if (m_nonHTTPProtocolHost.hasRequests()) m_nonHTTPProtocolHost.cancelRequests(docLoader); @@ -172,10 +174,7 @@ 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) @@ -291,6 +290,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(); @@ -336,6 +338,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(); diff --git a/WebCore/loader/mac/ResourceLoaderMac.mm b/WebCore/loader/mac/ResourceLoaderMac.mm index d6ee923..25fe7bd 100644 --- a/WebCore/loader/mac/ResourceLoaderMac.mm +++ b/WebCore/loader/mac/ResourceLoaderMac.mm @@ -37,6 +37,8 @@ namespace WebCore { NSCachedURLResponse* ResourceLoader::willCacheResponse(ResourceHandle*, NSCachedURLResponse* response) { + if (!m_sendResourceLoadCallbacks) + return 0; return frameLoader()->client()->willCacheResponse(documentLoader(), identifier(), response); } |