diff options
Diffstat (limited to 'WebCore/loader')
122 files changed, 5437 insertions, 4747 deletions
diff --git a/WebCore/loader/CachedMetadata.h b/WebCore/loader/CachedMetadata.h new file mode 100644 index 0000000..120e4c0 --- /dev/null +++ b/WebCore/loader/CachedMetadata.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2010 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 CachedMetadata_h +#define CachedMetadata_h + +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +// Metadata retrieved from the embedding application's cache. +// +// Serialized data is NOT portable across architectures. However, reading the +// data type ID will reject data generated with a different byte-order. +class CachedMetadata : public RefCounted<CachedMetadata> { +public: + static PassRefPtr<CachedMetadata> create(unsigned dataTypeID, const char* data, size_t size) + { + return adoptRef(new CachedMetadata(dataTypeID, data, size)); + } + + static PassRefPtr<CachedMetadata> deserialize(const char* data, size_t size) + { + return adoptRef(new CachedMetadata(data, size)); + } + + const Vector<char>& serialize() const + { + return m_serializedData; + } + + ~CachedMetadata() { } + + unsigned dataTypeID() const + { + return readUnsigned(dataTypeIDStart); + } + + const char* data() const + { + if (m_serializedData.size() < dataStart) + return 0; + return m_serializedData.data() + dataStart; + } + + size_t size() const + { + if (m_serializedData.size() < dataStart) + return 0; + return m_serializedData.size() - dataStart; + } + +private: + // Reads an unsigned value at position. Returns 0 on error. + unsigned readUnsigned(size_t position) const + { + if (m_serializedData.size() < position + sizeof(unsigned)) + return 0; + return *reinterpret_cast_ptr<unsigned*>(const_cast<char*>(m_serializedData.data() + position)); + } + + // Appends an unsigned value to the end of the serialized data. + void appendUnsigned(unsigned value) + { + m_serializedData.append(reinterpret_cast<const char*>(&value), sizeof(unsigned)); + } + + CachedMetadata(const char* data, size_t size) + { + // Serialized metadata should have non-empty data. + ASSERT(size > dataStart); + + m_serializedData.append(data, size); + } + + CachedMetadata(unsigned dataTypeID, const char* data, size_t size) + { + // Don't allow an ID of 0, it is used internally to indicate errors. + ASSERT(dataTypeID); + ASSERT(data); + + appendUnsigned(dataTypeID); + m_serializedData.append(data, size); + } + + // Serialization offsets. Format: [DATA_TYPE_ID][DATA]. + static const size_t dataTypeIDStart = 0; + static const size_t dataStart = sizeof(unsigned); + + // Since the serialization format supports random access, storing it in + // serialized form avoids need for a copy during serialization. + Vector<char> m_serializedData; +}; + +} + +#endif diff --git a/WebCore/loader/CachedXBLDocument.cpp b/WebCore/loader/CachedXBLDocument.cpp deleted file mode 100644 index 0ff17f2..0000000 --- a/WebCore/loader/CachedXBLDocument.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) - Copyright (C) 2001 Dirk Mueller (mueller@kde.org) - Copyright (C) 2002 Waldo Bastian (bastian@kde.org) - Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. - - This class provides all functionality needed for loading images, style sheets and html - pages from the web. It has a memory cache for these objects. -*/ - -#include "config.h" - -#if ENABLE(XBL) - -#include "CachedXBLDocument.h" - -#include "CachedResourceClientWalker.h" -#include "TextResourceDecoder.h" -#include <wtf/Vector.h> - -namespace WebCore { - -CachedXBLDocument::CachedXBLDocument(const String &url) -: CachedResource(url, XBL), m_document(0) -{ - // It's XML we want. - setAccept("text/xml, application/xml, application/xhtml+xml, text/xsl, application/rss+xml, application/atom+xml"); - - m_decoder = new TextResourceDecoder("application/xml"); -} - -CachedXBLDocument::~CachedXBLDocument() -{ - if (m_document) - m_document->deref(); -} - -void CachedXBLDocument::ref(CachedResourceClient *c) -{ - CachedResource::ref(c); - if (!m_loading) - c->setXBLDocument(m_url, m_document); -} - -void CachedXBLDocument::setEncoding(const String& chs) -{ - m_decoder->setEncoding(chs, TextResourceDecoder::EncodingFromHTTPHeader); -} - -String CachedXBLDocument::encoding() const -{ - return m_decoder->encoding().name(); -} - -void CachedXBLDocument::data(Vector<char>& data, bool ) -{ - if (!allDataReceived) - return; - - ASSERT(!m_document); - - m_document = new XBL::XBLDocument(); - m_document->ref(); - m_document->open(); - - m_document->write(m_decoder->decode(data.data(), data.size())); - setSize(data.size()); - - m_document->finishParsing(); - m_document->close(); - m_loading = false; - checkNotify(); -} - -void CachedXBLDocument::checkNotify() -{ - if (m_loading) - return; - - CachedResourceClientWalker w(m_clients); - while (CachedResourceClient *c = w.next()) - c->setXBLDocument(m_url, m_document); -} - -void CachedXBLDocument::error() -{ - m_loading = false; - m_errorOccurred = true; - checkNotify(); -} - -} - -#endif diff --git a/WebCore/loader/CachedXBLDocument.h b/WebCore/loader/CachedXBLDocument.h deleted file mode 100644 index b92a255..0000000 --- a/WebCore/loader/CachedXBLDocument.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) - Copyright (C) 2001 Dirk Mueller <mueller@kde.org> - Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. - - This class provides all functionality needed for loading images, style sheets and html - pages from the web. It has a memory cache for these objects. -*/ - -#ifndef CachedXBLDocument_h -#define CachedXBLDocument_h - -#include "CachedResource.h" -#include <wtf/Vector.h> - -namespace WebCore { - class CachedResource; - class Request; - class DocLoader; - class TextResourceDecoder; - class CachedResourceClient; - -#if ENABLE(XBL) - class CachedXBLDocument : public CachedResource { - public: - CachedXBLDocument(const String& url); - virtual ~CachedXBLDocument(); - - XBL::XBLDocument* document() const { return m_document; } - - virtual void addClient(CachedResourceClient*); - - virtual void setEncoding(const String&); - virtual String encoding() const; - virtual void data(Vector<char>&, bool allDataReceived); - virtual void error(); - - virtual bool schedule() const { return true; } - - void checkNotify(); - - protected: - XBL::XBLDocument* m_document; - RefPtr<TextResourceDecoder> m_decoder; - }; - -#endif - -} - -#endif diff --git a/WebCore/loader/CrossOriginAccessControl.cpp b/WebCore/loader/CrossOriginAccessControl.cpp index 01596e2..f510704 100644 --- a/WebCore/loader/CrossOriginAccessControl.cpp +++ b/WebCore/loader/CrossOriginAccessControl.cpp @@ -27,11 +27,11 @@ #include "config.h" #include "CrossOriginAccessControl.h" -#include "AtomicString.h" #include "HTTPParsers.h" #include "ResourceResponse.h" #include "SecurityOrigin.h" #include <wtf/Threading.h> +#include <wtf/text/AtomicString.h> namespace WebCore { @@ -71,9 +71,9 @@ bool isSimpleCrossOriginAccessRequest(const String& method, const HTTPHeaderMap& } typedef HashSet<String, CaseFoldingHash> HTTPHeaderSet; -static HTTPHeaderSet* createAllowedCrossOriginResponseHeadersSet() +static PassOwnPtr<HTTPHeaderSet> createAllowedCrossOriginResponseHeadersSet() { - HTTPHeaderSet* headerSet = new HashSet<String, CaseFoldingHash>; + OwnPtr<HTTPHeaderSet> headerSet = adoptPtr(new HashSet<String, CaseFoldingHash>); headerSet->add("cache-control"); headerSet->add("content-language"); @@ -82,17 +82,17 @@ static HTTPHeaderSet* createAllowedCrossOriginResponseHeadersSet() headerSet->add("last-modified"); headerSet->add("pragma"); - return headerSet; + return headerSet.release(); } bool isOnAccessControlResponseHeaderWhitelist(const String& name) { - AtomicallyInitializedStatic(HTTPHeaderSet*, allowedCrossOriginResponseHeaders = createAllowedCrossOriginResponseHeadersSet()); + AtomicallyInitializedStatic(HTTPHeaderSet*, allowedCrossOriginResponseHeaders = createAllowedCrossOriginResponseHeadersSet().leakPtr()); return allowedCrossOriginResponseHeaders->contains(name); } -bool passesAccessControlCheck(const ResourceResponse& response, bool includeCredentials, SecurityOrigin* securityOrigin) +bool passesAccessControlCheck(const ResourceResponse& response, bool includeCredentials, SecurityOrigin* securityOrigin, String& errorDescription) { // 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. @@ -100,17 +100,25 @@ bool passesAccessControlCheck(const ResourceResponse& response, bool includeCred if (accessControlOriginString == "*" && !includeCredentials) return true; - if (securityOrigin->isUnique()) + if (securityOrigin->isUnique()) { + errorDescription = "Cannot make any requests from " + securityOrigin->toString() + "."; return false; + } + // FIXME: Access-Control-Allow-Origin can contain a list of origins. RefPtr<SecurityOrigin> accessControlOrigin = SecurityOrigin::createFromString(accessControlOriginString); - if (!accessControlOrigin->isSameSchemeHostPort(securityOrigin)) + if (!accessControlOrigin->isSameSchemeHostPort(securityOrigin)) { + errorDescription = (accessControlOriginString == "*") ? "Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true." + : "Origin " + securityOrigin->toString() + " is not allowed by Access-Control-Allow-Origin."; return false; + } if (includeCredentials) { const String& accessControlCredentialsString = response.httpHeaderField("Access-Control-Allow-Credentials"); - if (accessControlCredentialsString != "true") + if (accessControlCredentialsString != "true") { + errorDescription = "Credentials flag is true, but Access-Control-Allow-Credentials is not \"true\"."; return false; + } } return true; diff --git a/WebCore/loader/CrossOriginAccessControl.h b/WebCore/loader/CrossOriginAccessControl.h index 267646f..c44963b 100644 --- a/WebCore/loader/CrossOriginAccessControl.h +++ b/WebCore/loader/CrossOriginAccessControl.h @@ -24,18 +24,19 @@ * */ +#include <wtf/Forward.h> + 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*); + bool passesAccessControlCheck(const ResourceResponse&, bool includeCredentials, SecurityOrigin*, String& errorDescription); } // namespace WebCore diff --git a/WebCore/loader/CrossOriginPreflightResultCache.cpp b/WebCore/loader/CrossOriginPreflightResultCache.cpp index cea66b1..18e4be2 100644 --- a/WebCore/loader/CrossOriginPreflightResultCache.cpp +++ b/WebCore/loader/CrossOriginPreflightResultCache.cpp @@ -35,6 +35,8 @@ namespace WebCore { +using namespace std; + // 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. @@ -72,30 +74,34 @@ static void addToAccessControlAllowList(const String& string, unsigned start, un template<class HashType> static bool parseAccessControlAllowList(const String& string, HashSet<String, HashType>& set) { - int start = 0; - int end; - while ((end = string.find(',', start)) != -1) { + unsigned start = 0; + size_t end; + while ((end = string.find(',', start)) != notFound) { if (start == end) return false; addToAccessControlAllowList(string, start, end - 1, set); start = end + 1; } - if (start != static_cast<int>(string.length())) + if (start != string.length()) addToAccessControlAllowList(string, start, string.length() - 1, set); return true; } -bool CrossOriginPreflightResultCacheItem::parse(const ResourceResponse& response) +bool CrossOriginPreflightResultCacheItem::parse(const ResourceResponse& response, String& errorDescription) { m_methods.clear(); - if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Methods"), m_methods)) + if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Methods"), m_methods)) { + errorDescription = "Cannot parse Access-Control-Allow-Methods response header field."; return false; + } m_headers.clear(); - if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Headers"), m_headers)) + if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Headers"), m_headers)) { + errorDescription = "Cannot parse Access-Control-Allow-Headers response header field."; return false; + } unsigned expiryDelta; if (parseAccessControlMaxAge(response.httpHeaderField("Access-Control-Max-Age"), expiryDelta)) { @@ -108,30 +114,37 @@ bool CrossOriginPreflightResultCacheItem::parse(const ResourceResponse& response return true; } -bool CrossOriginPreflightResultCacheItem::allowsCrossOriginMethod(const String& method) const +bool CrossOriginPreflightResultCacheItem::allowsCrossOriginMethod(const String& method, String& errorDescription) const { - return m_methods.contains(method) || isOnAccessControlSimpleRequestMethodWhitelist(method); + if (m_methods.contains(method) || isOnAccessControlSimpleRequestMethodWhitelist(method)) + return true; + + errorDescription = "Method " + method + " is not allowed by Access-Control-Allow-Methods."; + return false; } -bool CrossOriginPreflightResultCacheItem::allowsCrossOriginHeaders(const HTTPHeaderMap& requestHeaders) const +bool CrossOriginPreflightResultCacheItem::allowsCrossOriginHeaders(const HTTPHeaderMap& requestHeaders, String& errorDescription) 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)) + if (!m_headers.contains(it->first) && !isOnAccessControlSimpleRequestHeaderWhitelist(it->first, it->second)) { + errorDescription = "Request header field " + it->first.string() + " is not allowed by Access-Control-Allow-Headers."; return false; + } } return true; } bool CrossOriginPreflightResultCacheItem::allowsRequest(bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) const { + String ignoredExplanation; if (m_absoluteExpiryTime < currentTime()) return false; if (includeCredentials && !m_credentials) return false; - if (!allowsCrossOriginMethod(method)) + if (!allowsCrossOriginMethod(method, ignoredExplanation)) return false; - if (!allowsCrossOriginHeaders(requestHeaders)) + if (!allowsCrossOriginHeaders(requestHeaders, ignoredExplanation)) return false; return true; } @@ -143,10 +156,15 @@ CrossOriginPreflightResultCache& CrossOriginPreflightResultCache::shared() return cache; } -void CrossOriginPreflightResultCache::appendEntry(const String& origin, const KURL& url, CrossOriginPreflightResultCacheItem* preflightResult) +void CrossOriginPreflightResultCache::appendEntry(const String& origin, const KURL& url, PassOwnPtr<CrossOriginPreflightResultCacheItem> preflightResult) { ASSERT(isMainThread()); - m_preflightHashMap.set(std::make_pair(origin, url), preflightResult); + CrossOriginPreflightResultCacheItem* resultPtr = preflightResult.leakPtr(); + pair<CrossOriginPreflightResultHashMap::iterator, bool> addResult = m_preflightHashMap.add(make_pair(origin, url), resultPtr); + if (!addResult.second) { + // FIXME: We need to delete the old value before replacing with the new one. + addResult.first->second = resultPtr; + } } bool CrossOriginPreflightResultCache::canSkipPreflight(const String& origin, const KURL& url, bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) diff --git a/WebCore/loader/CrossOriginPreflightResultCache.h b/WebCore/loader/CrossOriginPreflightResultCache.h index faf55e5..1016aed 100644 --- a/WebCore/loader/CrossOriginPreflightResultCache.h +++ b/WebCore/loader/CrossOriginPreflightResultCache.h @@ -24,10 +24,14 @@ * */ +#ifndef CrossOriginPreflightResultCache_h +#define CrossOriginPreflightResultCache_h + #include "KURLHash.h" -#include "StringHash.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/text/StringHash.h> namespace WebCore { @@ -42,9 +46,9 @@ namespace WebCore { { } - bool parse(const ResourceResponse&); - bool allowsCrossOriginMethod(const String&) const; - bool allowsCrossOriginHeaders(const HTTPHeaderMap&) const; + bool parse(const ResourceResponse&, String& errorDescription); + bool allowsCrossOriginMethod(const String&, String& errorDescription) const; + bool allowsCrossOriginHeaders(const HTTPHeaderMap&, String& errorDescription) const; bool allowsRequest(bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) const; private: @@ -63,7 +67,7 @@ namespace WebCore { public: static CrossOriginPreflightResultCache& shared(); - void appendEntry(const String& origin, const KURL&, CrossOriginPreflightResultCacheItem*); + void appendEntry(const String& origin, const KURL&, PassOwnPtr<CrossOriginPreflightResultCacheItem>); bool canSkipPreflight(const String& origin, const KURL&, bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders); void empty(); @@ -77,3 +81,5 @@ namespace WebCore { }; } // namespace WebCore + +#endif diff --git a/WebCore/loader/MediaDocument.h b/WebCore/loader/DocumentLoadTiming.h index aa751ab..2d4b0fa 100644 --- a/WebCore/loader/MediaDocument.h +++ b/WebCore/loader/DocumentLoadTiming.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008,2009 Apple Inc. All Rights Reserved. + * Copyright (C) 2010 Google, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE 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 @@ -23,39 +23,36 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MediaDocument_h -#define MediaDocument_h - -#if ENABLE(VIDEO) - -#include "HTMLDocument.h" +#ifndef DocumentLoadTiming_h +#define DocumentLoadTiming_h namespace WebCore { -class MediaDocument : public HTMLDocument { -public: - static PassRefPtr<MediaDocument> create(Frame* frame) +struct DocumentLoadTiming { + DocumentLoadTiming() + : navigationStart(0.0) + , unloadEventEnd(0.0) + , redirectStart(0.0) + , redirectEnd(0.0) + , redirectCount(0) + , fetchStart(0.0) + , responseEnd(0.0) + , loadEventStart(0.0) + , loadEventEnd(0.0) { - return adoptRef(new MediaDocument(frame)); } - virtual ~MediaDocument(); - - void mediaElementSawUnsupportedTracks(); - -private: - MediaDocument(Frame*); - - virtual bool isMediaDocument() const { return true; } - virtual Tokenizer* createTokenizer(); - virtual void defaultEventHandler(Event*); - - void replaceMediaElementTimerFired(Timer<MediaDocument>*); - - Timer<MediaDocument> m_replaceMediaElementTimer; + double navigationStart; + double unloadEventEnd; + double redirectStart; + double redirectEnd; + short redirectCount; + double fetchStart; + double responseEnd; + double loadEventStart; + double loadEventEnd; }; - + } #endif -#endif diff --git a/WebCore/loader/DocumentLoader.cpp b/WebCore/loader/DocumentLoader.cpp index dca416e..0836805 100644 --- a/WebCore/loader/DocumentLoader.cpp +++ b/WebCore/loader/DocumentLoader.cpp @@ -37,11 +37,13 @@ #include "SubstituteResource.h" #endif #include "CachedPage.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "Document.h" +#include "DocumentParser.h" #include "Event.h" #include "Frame.h" #include "FrameLoader.h" +#include "FrameLoaderClient.h" #include "FrameTree.h" #include "HistoryItem.h" #include "Logging.h" @@ -50,9 +52,9 @@ #include "PlatformString.h" #include "Settings.h" #include "SharedBuffer.h" -#include "XMLTokenizer.h" #include <wtf/Assertions.h> +#include <wtf/text/CString.h> #include <wtf/unicode/Unicode.h> namespace WebCore { @@ -86,11 +88,12 @@ DocumentLoader::DocumentLoader(const ResourceRequest& req, const SubstituteData& , m_gotFirstByte(false) , m_primaryLoadComplete(false) , m_isClientRedirect(false) + , m_wasOnloadHandled(false) , m_stopRecordingResponses(false) , m_substituteResourceDeliveryTimer(this, &DocumentLoader::substituteResourceDeliveryTimerFired) , m_didCreateGlobalHistoryEntry(false) #if ENABLE(OFFLINE_WEB_APPLICATIONS) - , m_applicationCacheHost(new ApplicationCacheHost(this)) + , m_applicationCacheHost(adoptPtr(new ApplicationCacheHost(this))) #endif { } @@ -221,6 +224,11 @@ void DocumentLoader::stopLoading(DatabasePolicy databasePolicy) // Always cancel multipart loaders cancelAll(m_multipartSubresourceLoaders); + // Appcache uses ResourceHandle directly, DocumentLoader doesn't count these loads. +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + m_applicationCacheHost->stopLoadingInFrame(m_frame); +#endif + if (!loading) return; @@ -259,7 +267,7 @@ void DocumentLoader::commitIfReady() { if (m_gotFirstByte && !m_committed) { m_committed = true; - frameLoader()->commitProvisionalLoad(0); + frameLoader()->commitProvisionalLoad(); } } @@ -269,7 +277,7 @@ void DocumentLoader::finishedLoading() commitIfReady(); if (FrameLoader* loader = frameLoader()) { loader->finishedLoadingDocument(this); - loader->end(); + loader->writer()->end(); } } @@ -280,8 +288,29 @@ void DocumentLoader::commitLoad(const char* data, int length) RefPtr<DocumentLoader> protect(this); commitIfReady(); - if (FrameLoader* frameLoader = DocumentLoader::frameLoader()) - frameLoader->committedLoad(this, data, length); + FrameLoader* frameLoader = DocumentLoader::frameLoader(); + if (!frameLoader) + return; +#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size + if (ArchiveFactory::isArchiveMimeType(response().mimeType())) + return; +#endif + frameLoader->client()->committedLoad(this, data, length); +} + +void DocumentLoader::commitData(const char* bytes, int length) +{ + // Set the text encoding. This is safe to call multiple times. + bool userChosen = true; + String encoding = overrideEncoding(); + if (encoding.isNull()) { + userChosen = false; + encoding = response().textEncodingName(); + } + // FIXME: DocumentWriter should be owned by DocumentLoader. + m_frame->loader()->writer()->setEncoding(encoding, userChosen); + ASSERT(m_frame->document()->parsing()); + m_frame->loader()->writer()->addData(bytes, length); } bool DocumentLoader::doesProgressiveLoad(const String& MIMEType) const @@ -311,7 +340,7 @@ void DocumentLoader::setupForReplaceByMIMEType(const String& newMIMEType) } frameLoader()->finishedLoadingDocument(this); - m_frame->loader()->end(); + m_frame->loader()->writer()->end(); frameLoader()->setReplacing(); m_gotFirstByte = false; @@ -397,10 +426,10 @@ bool DocumentLoader::isLoadingInAPISense() const if (!m_subresourceLoaders.isEmpty()) return true; Document* doc = m_frame->document(); - if (doc->docLoader()->requestCount()) + if (doc->cachedResourceLoader()->requestCount()) return true; - if (Tokenizer* tok = doc->tokenizer()) - if (tok->processingData()) + if (DocumentParser* parser = doc->parser()) + if (parser->processingData()) return true; } return frameLoader()->subframeIsLoading(); @@ -410,7 +439,7 @@ bool DocumentLoader::isLoadingInAPISense() const void DocumentLoader::addAllArchiveResources(Archive* archive) { if (!m_archiveResourceCollection) - m_archiveResourceCollection.set(new ArchiveResourceCollection); + m_archiveResourceCollection = adoptPtr(new ArchiveResourceCollection); ASSERT(archive); if (!archive) @@ -424,7 +453,7 @@ void DocumentLoader::addAllArchiveResources(Archive* archive) void DocumentLoader::addArchiveResource(PassRefPtr<ArchiveResource> resource) { if (!m_archiveResourceCollection) - m_archiveResourceCollection.set(new ArchiveResourceCollection); + m_archiveResourceCollection = adoptPtr(new ArchiveResourceCollection); ASSERT(resource); if (!resource) @@ -471,7 +500,7 @@ PassRefPtr<ArchiveResource> DocumentLoader::mainResource() const if (!mainResourceBuffer) mainResourceBuffer = SharedBuffer::create(); - return ArchiveResource::create(mainResourceBuffer, r.url(), r.mimeType(), r.textEncodingName(), frame()->tree()->name()); + return ArchiveResource::create(mainResourceBuffer, r.url(), r.mimeType(), r.textEncodingName(), frame()->tree()->uniqueName()); } PassRefPtr<ArchiveResource> DocumentLoader::subresource(const KURL& url) const @@ -479,7 +508,7 @@ PassRefPtr<ArchiveResource> DocumentLoader::subresource(const KURL& url) const if (!isCommitted()) return 0; - CachedResource* resource = m_frame->document()->docLoader()->cachedResource(url); + CachedResource* resource = m_frame->document()->cachedResourceLoader()->cachedResource(url); if (!resource || !resource->isLoaded()) return archiveResourceForURL(url); @@ -502,9 +531,9 @@ void DocumentLoader::getSubresources(Vector<PassRefPtr<ArchiveResource> >& subre Document* document = m_frame->document(); - const DocLoader::DocumentResourceMap& allResources = document->docLoader()->allCachedResources(); - DocLoader::DocumentResourceMap::const_iterator end = allResources.end(); - for (DocLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) { + const CachedResourceLoader::DocumentResourceMap& allResources = document->cachedResourceLoader()->allCachedResources(); + CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end(); + for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) { RefPtr<ArchiveResource> subresource = this->subresource(KURL(ParsedURLString, it->second->url())); if (subresource) subresources.append(subresource.release()); @@ -546,7 +575,7 @@ void DocumentLoader::substituteResourceDeliveryTimerFired(Timer<DocumentLoader>* loader->didReceiveResponse(resource->response()); loader->didReceiveData(data->data(), data->size(), data->size(), true); - loader->didFinishLoading(); + loader->didFinishLoading(0); } else { // A null resource means that we should fail the load. // FIXME: Maybe we should use another error here - something like "not in cache". @@ -617,6 +646,17 @@ void DocumentLoader::setTitle(const String& title) } } +void DocumentLoader::setIconURL(const String& iconURL) +{ + if (iconURL.isEmpty()) + return; + + if (m_pageIconURL != iconURL) { + m_pageIconURL = iconURL; + frameLoader()->didChangeIcons(this); + } +} + KURL DocumentLoader::urlForHistory() const { // Return the URL to be used for history and B/F list. @@ -759,6 +799,27 @@ void DocumentLoader::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loa frame->loader()->checkLoadComplete(); } +void DocumentLoader::transferLoadingResourcesFromPage(Page* oldPage) +{ + ASSERT(oldPage != m_frame->page()); + + FrameLoaderClient* frameLoaderClient = frameLoader()->client(); + const ResourceRequest& request = originalRequest(); + if (isLoadingMainResource()) { + frameLoaderClient->transferLoadingResourceFromPage( + m_mainResourceLoader->identifier(), this, request, oldPage); + } + + if (isLoadingSubresources()) { + ResourceLoaderSet::const_iterator it = m_subresourceLoaders.begin(); + ResourceLoaderSet::const_iterator end = m_subresourceLoaders.end(); + for (; it != end; ++it) { + frameLoaderClient->transferLoadingResourceFromPage( + (*it)->identifier(), this, request, oldPage); + } + } +} + void DocumentLoader::iconLoadDecisionAvailable() { if (m_frame) diff --git a/WebCore/loader/DocumentLoader.h b/WebCore/loader/DocumentLoader.h index 440cfc4..2328160 100644 --- a/WebCore/loader/DocumentLoader.h +++ b/WebCore/loader/DocumentLoader.h @@ -29,6 +29,7 @@ #ifndef DocumentLoader_h #define DocumentLoader_h +#include "DocumentLoadTiming.h" #include "NavigationAction.h" #include "ResourceError.h" #include "ResourceRequest.h" @@ -47,6 +48,7 @@ namespace WebCore { class Frame; class FrameLoader; class MainResourceLoader; + class Page; class ResourceLoader; class SchedulePair; class SharedBuffer; @@ -108,9 +110,12 @@ namespace WebCore { void prepareForLoadStart(); bool isClientRedirect() const { return m_isClientRedirect; } void setIsClientRedirect(bool isClientRedirect) { m_isClientRedirect = isClientRedirect; } + void handledOnloadEvents() { m_wasOnloadHandled = true; } + bool wasOnloadHandled() { return m_wasOnloadHandled; } bool isLoadingInAPISense() const; void setPrimaryLoadComplete(bool); void setTitle(const String&); + void setIconURL(const String&); const String& overrideEncoding() const { return m_overrideEncoding; } #if PLATFORM(MAC) @@ -154,6 +159,7 @@ namespace WebCore { void stopRecordingResponses(); const String& title() const { return m_pageTitle; } + const String& iconURL() const { return m_pageIconURL; } KURL urlForHistory() const; bool urlForHistoryReflectsFailure() const; @@ -194,15 +200,28 @@ namespace WebCore { void removePlugInStreamLoader(ResourceLoader*); void subresourceLoaderFinishedLoadingOnePart(ResourceLoader*); - + + void transferLoadingResourcesFromPage(Page*); + void setDeferMainResourceDataLoad(bool defer) { m_deferMainResourceDataLoad = defer; } bool deferMainResourceDataLoad() const { return m_deferMainResourceDataLoad; } - void didTellClientAboutLoad(const String& url) { m_resourcesClientKnowsAbout.add(url); } + void didTellClientAboutLoad(const String& url) + { + if (!url.isEmpty()) + m_resourcesClientKnowsAbout.add(url); + } bool haveToldClientAboutLoad(const String& url) { return m_resourcesClientKnowsAbout.contains(url); } void recordMemoryCacheLoadForFutureClientNotification(const String& url); void takeMemoryCacheLoadsForClientNotification(Vector<String>& loads); + DocumentLoadTiming* timing() { return &m_documentLoadTiming; } + void resetTiming() { m_documentLoadTiming = DocumentLoadTiming(); } + + // The WebKit layer calls this function when it's ready for the data to + // actually be added to the document. + void commitData(const char* bytes, int length); + #if ENABLE(OFFLINE_WEB_APPLICATIONS) ApplicationCacheHost* applicationCacheHost() const { return m_applicationCacheHost.get(); } #endif @@ -259,8 +278,10 @@ namespace WebCore { bool m_gotFirstByte; bool m_primaryLoadComplete; bool m_isClientRedirect; + bool m_wasOnloadHandled; String m_pageTitle; + String m_pageIconURL; String m_overrideEncoding; @@ -293,6 +314,8 @@ namespace WebCore { String m_clientRedirectSourceForHistory; bool m_didCreateGlobalHistoryEntry; + DocumentLoadTiming m_documentLoadTiming; + #if ENABLE(OFFLINE_WEB_APPLICATIONS) friend class ApplicationCacheHost; // for substitute resource delivery OwnPtr<ApplicationCacheHost> m_applicationCacheHost; diff --git a/WebCore/loader/DocumentThreadableLoader.cpp b/WebCore/loader/DocumentThreadableLoader.cpp index d0f6c04..a792144 100644 --- a/WebCore/loader/DocumentThreadableLoader.cpp +++ b/WebCore/loader/DocumentThreadableLoader.cpp @@ -37,10 +37,12 @@ #include "Document.h" #include "Frame.h" #include "FrameLoader.h" +#include "ResourceHandle.h" #include "ResourceRequest.h" #include "SecurityOrigin.h" #include "SubresourceLoader.h" #include "ThreadableLoaderClient.h" +#include <wtf/UnusedParam.h> namespace WebCore { @@ -75,22 +77,25 @@ DocumentThreadableLoader::DocumentThreadableLoader(Document* document, Threadabl } if (m_options.crossOriginRequestPolicy == DenyCrossOriginRequests) { - m_client->didFail(ResourceError()); + m_client->didFail(ResourceError(errorDomainWebKitInternal, 0, request.url().string(), "Cross origin requests are not supported.")); return; } ASSERT(m_options.crossOriginRequestPolicy == UseAccessControl); - if (!m_options.forcePreflight && isSimpleCrossOriginAccessRequest(request.httpMethod(), request.httpHeaderFields())) - makeSimpleCrossOriginAccessRequest(request); + OwnPtr<ResourceRequest> crossOriginRequest = adoptPtr(new ResourceRequest(request)); + crossOriginRequest->removeCredentials(); + crossOriginRequest->setAllowCookies(m_options.allowCredentials); + + if (!m_options.forcePreflight && isSimpleCrossOriginAccessRequest(crossOriginRequest->httpMethod(), crossOriginRequest->httpHeaderFields())) + makeSimpleCrossOriginAccessRequest(*crossOriginRequest); else { - m_actualRequest.set(new ResourceRequest(request)); - m_actualRequest->setAllowCookies(m_options.allowCredentials); + m_actualRequest = crossOriginRequest.release(); - if (CrossOriginPreflightResultCache::shared().canSkipPreflight(document->securityOrigin()->toString(), request.url(), m_options.allowCredentials, request.httpMethod(), request.httpHeaderFields())) + if (CrossOriginPreflightResultCache::shared().canSkipPreflight(document->securityOrigin()->toString(), m_actualRequest->url(), m_options.allowCredentials, m_actualRequest->httpMethod(), m_actualRequest->httpHeaderFields())) preflightSuccess(); else - makeCrossOriginAccessRequestWithPreflight(request); + makeCrossOriginAccessRequestWithPreflight(*m_actualRequest); } } @@ -100,14 +105,12 @@ void DocumentThreadableLoader::makeSimpleCrossOriginAccessRequest(const Resource // Cross-origin requests are only defined for HTTP. We would catch this when checking response headers later, but there is no reason to send a request that's guaranteed to be denied. if (!request.url().protocolInHTTPFamily()) { - m_client->didFail(ResourceError()); + m_client->didFail(ResourceError(errorDomainWebKitInternal, 0, request.url().string(), "Cross origin requests are only supported for HTTP.")); return; } // Make a copy of the passed request so that we can modify some details. ResourceRequest crossOriginRequest(request); - crossOriginRequest.removeCredentials(); - crossOriginRequest.setAllowCookies(m_options.allowCredentials); crossOriginRequest.setHTTPOrigin(m_document->securityOrigin()->toString()); loadRequest(crossOriginRequest, DoSecurityCheck); @@ -185,25 +188,26 @@ void DocumentThreadableLoader::didReceiveResponse(SubresourceLoader* loader, con ASSERT(m_client); ASSERT_UNUSED(loader, loader == m_loader); + String accessControlErrorDescription; if (m_actualRequest) { - if (!passesAccessControlCheck(response, m_options.allowCredentials, m_document->securityOrigin())) { - preflightFailure(); + if (!passesAccessControlCheck(response, m_options.allowCredentials, m_document->securityOrigin(), accessControlErrorDescription)) { + preflightFailure(response.url(), accessControlErrorDescription); return; } - OwnPtr<CrossOriginPreflightResultCacheItem> preflightResult(new CrossOriginPreflightResultCacheItem(m_options.allowCredentials)); - if (!preflightResult->parse(response) - || !preflightResult->allowsCrossOriginMethod(m_actualRequest->httpMethod()) - || !preflightResult->allowsCrossOriginHeaders(m_actualRequest->httpHeaderFields())) { - preflightFailure(); + OwnPtr<CrossOriginPreflightResultCacheItem> preflightResult = adoptPtr(new CrossOriginPreflightResultCacheItem(m_options.allowCredentials)); + if (!preflightResult->parse(response, accessControlErrorDescription) + || !preflightResult->allowsCrossOriginMethod(m_actualRequest->httpMethod(), accessControlErrorDescription) + || !preflightResult->allowsCrossOriginHeaders(m_actualRequest->httpHeaderFields(), accessControlErrorDescription)) { + preflightFailure(response.url(), accessControlErrorDescription); return; } CrossOriginPreflightResultCache::shared().appendEntry(m_document->securityOrigin()->toString(), m_actualRequest->url(), preflightResult.release()); } else { if (!m_sameOriginRequest && m_options.crossOriginRequestPolicy == UseAccessControl) { - if (!passesAccessControlCheck(response, m_options.allowCredentials, m_document->securityOrigin())) { - m_client->didFail(ResourceError()); + if (!passesAccessControlCheck(response, m_options.allowCredentials, m_document->securityOrigin(), accessControlErrorDescription)) { + m_client->didFail(ResourceError(errorDomainWebKitInternal, 0, response.url().string(), accessControlErrorDescription)); return; } } @@ -217,6 +221,10 @@ void DocumentThreadableLoader::didReceiveData(SubresourceLoader* loader, const c ASSERT(m_client); ASSERT_UNUSED(loader, loader == m_loader); + // Ignore response body of preflight requests. + if (m_actualRequest) + return; + m_client->didReceiveData(data, lengthReceived); } @@ -258,14 +266,20 @@ bool DocumentThreadableLoader::getShouldUseCredentialStorage(SubresourceLoader* return false; // Only FrameLoaderClient can ultimately permit credential use. } -void DocumentThreadableLoader::didReceiveAuthenticationChallenge(SubresourceLoader* loader, const AuthenticationChallenge&) +void DocumentThreadableLoader::didReceiveAuthenticationChallenge(SubresourceLoader* loader, const AuthenticationChallenge& challenge) { ASSERT(loader == m_loader); // Users are not prompted for credentials for cross-origin requests. if (!m_sameOriginRequest) { +#if PLATFORM(MAC) || USE(CFNETWORK) || USE(CURL) + loader->handle()->receivedRequestToContinueWithoutCredential(challenge); +#else + // These platforms don't provide a way to continue without credentials, cancel the load altogether. + UNUSED_PARAM(challenge); RefPtr<DocumentThreadableLoader> protect(this); m_client->didFail(loader->blockedError()); cancel(); +#endif } } @@ -285,14 +299,19 @@ void DocumentThreadableLoader::preflightSuccess() loadRequest(*actualRequest, SkipSecurityCheck); } -void DocumentThreadableLoader::preflightFailure() +void DocumentThreadableLoader::preflightFailure(const String& url, const String& errorDescription) { m_actualRequest = 0; // Prevent didFinishLoading() from bypassing access check. - m_client->didFail(ResourceError()); + m_client->didFail(ResourceError(errorDomainWebKitInternal, 0, url, errorDescription)); } void DocumentThreadableLoader::loadRequest(const ResourceRequest& request, SecurityCheckPolicy securityCheck) { + // Any credential should have been removed from the cross-site requests. + const KURL& requestURL = request.url(); + ASSERT(m_sameOriginRequest || requestURL.user().isEmpty()); + ASSERT(m_sameOriginRequest || requestURL.pass().isEmpty()); + if (m_async) { // Don't sniff content or send load callbacks for the preflight request. bool sendLoadCallbacks = m_options.sendLoadCallbacks && !m_actualRequest; @@ -316,15 +335,15 @@ void DocumentThreadableLoader::loadRequest(const ResourceRequest& request, Secur // 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) { + if (!error.isNull() && !requestURL.isLocalFile() && response.httpStatusCode() <= 0) { m_client->didFail(error); return; } // FIXME: FrameLoader::loadSynchronously() does not tell us whether a redirect happened or not, so we guess by comparing the // request and response URLs. This isn't a perfect test though, since a server can serve a redirect to the same URL that was - // requested. - if (request.url() != response.url() && !isAllowedRedirect(response.url())) { + // requested. Also comparing the request and response URLs as strings will fail if the requestURL still has its credentials. + if (requestURL != response.url() && !isAllowedRedirect(response.url())) { m_client->didFailRedirectCheck(); return; } diff --git a/WebCore/loader/DocumentThreadableLoader.h b/WebCore/loader/DocumentThreadableLoader.h index 48d1551..ebf3a25 100644 --- a/WebCore/loader/DocumentThreadableLoader.h +++ b/WebCore/loader/DocumentThreadableLoader.h @@ -34,6 +34,7 @@ #include "FrameLoaderTypes.h" #include "SubresourceLoaderClient.h" #include "ThreadableLoader.h" +#include <wtf/Forward.h> #include <wtf/OwnPtr.h> #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> @@ -84,7 +85,7 @@ namespace WebCore { void makeSimpleCrossOriginAccessRequest(const ResourceRequest& request); void makeCrossOriginAccessRequestWithPreflight(const ResourceRequest& request); void preflightSuccess(); - void preflightFailure(); + void preflightFailure(const String& url, const String& errorDescription); void loadRequest(const ResourceRequest&, SecurityCheckPolicy); bool isAllowedRedirect(const KURL&); diff --git a/WebCore/loader/DocumentWriter.cpp b/WebCore/loader/DocumentWriter.cpp new file mode 100644 index 0000000..5b03cd7 --- /dev/null +++ b/WebCore/loader/DocumentWriter.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2010. Adam Barth. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DocumentWriter.h" + +#include "DOMImplementation.h" +#include "DOMWindow.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoaderClient.h" +#include "FrameLoaderStateMachine.h" +#include "FrameView.h" +#include "PlaceholderDocument.h" +#include "PluginDocument.h" +#include "RawDataDocumentParser.h" +#include "ScriptableDocumentParser.h" +#include "SecurityOrigin.h" +#include "SegmentedString.h" +#include "Settings.h" +#include "SinkDocument.h" +#include "TextResourceDecoder.h" + + +namespace WebCore { + +static inline bool canReferToParentFrameEncoding(const Frame* frame, const Frame* parentFrame) +{ + return parentFrame && parentFrame->document()->securityOrigin()->canAccess(frame->document()->securityOrigin()); +} + +DocumentWriter::DocumentWriter(Frame* frame) + : m_frame(frame) + , m_receivedData(false) + , m_encodingWasChosenByUser(false) +{ +} + +// This is only called by ScriptController::executeIfJavaScriptURL +// and always contains the result of evaluating a javascript: url. +// This is the <iframe src="javascript:'html'"> case. +void DocumentWriter::replaceDocument(const String& source) +{ + m_frame->loader()->stopAllLoaders(); + begin(m_frame->loader()->url(), true, m_frame->document()->securityOrigin()); + + if (!source.isNull()) { + if (!m_receivedData) { + m_receivedData = true; + m_frame->document()->setCompatibilityMode(Document::NoQuirksMode); + } + + // FIXME: This should call DocumentParser::appendBytes instead of append + // to support RawDataDocumentParsers. + if (DocumentParser* parser = m_frame->document()->parser()) + parser->append(source); + } + + end(); +} + +void DocumentWriter::clear() +{ + m_decoder = 0; + m_receivedData = false; + if (!m_encodingWasChosenByUser) + m_encoding = String(); +} + +void DocumentWriter::begin() +{ + begin(KURL()); +} + +PassRefPtr<Document> DocumentWriter::createDocument(const KURL& url) +{ + if (!m_frame->loader()->stateMachine()->isDisplayingInitialEmptyDocument() && m_frame->loader()->client()->shouldUsePluginDocument(m_mimeType)) + return PluginDocument::create(m_frame, url); + if (!m_frame->loader()->client()->hasHTMLView()) + return PlaceholderDocument::create(m_frame, url); + return DOMImplementation::createDocument(m_mimeType, m_frame, url, m_frame->inViewSourceMode()); +} + +void DocumentWriter::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) +{ + // We need to take a reference to the security origin because |clear| + // might destroy the document that owns it. + RefPtr<SecurityOrigin> forcedSecurityOrigin = origin; + + // Create a new document before clearing the frame, because it may need to + // inherit an aliased security context. + RefPtr<Document> document = createDocument(url); + + // If the new document is for a Plugin but we're supposed to be sandboxed from Plugins, + // then replace the document with one whose parser will ignore the incoming data (bug 39323) + if (document->isPluginDocument() && m_frame->loader()->isSandboxed(SandboxPlugins)) + document = SinkDocument::create(m_frame, url); + + bool resetScripting = !(m_frame->loader()->stateMachine()->isDisplayingInitialEmptyDocument() && m_frame->document()->securityOrigin()->isSecureTransitionTo(url)); + m_frame->loader()->clear(resetScripting, resetScripting); + if (resetScripting) + m_frame->script()->updatePlatformScriptObjects(); + + m_frame->loader()->setURL(url); + m_frame->setDocument(document); + + if (m_decoder) + document->setDecoder(m_decoder.get()); + if (forcedSecurityOrigin) + document->setSecurityOrigin(forcedSecurityOrigin.get()); + + m_frame->domWindow()->setURL(document->url()); + m_frame->domWindow()->setSecurityOrigin(document->securityOrigin()); + + m_frame->loader()->didBeginDocument(dispatch); + + document->implicitOpen(); + + if (m_frame->view() && m_frame->loader()->client()->hasHTMLView()) + m_frame->view()->setContentsSize(IntSize()); +} + +TextResourceDecoder* DocumentWriter::createDecoderIfNeeded() +{ + if (!m_decoder) { + if (Settings* settings = m_frame->settings()) { + m_decoder = TextResourceDecoder::create(m_mimeType, + settings->defaultTextEncodingName(), + settings->usesEncodingDetector()); + Frame* parentFrame = m_frame->tree()->parent(); + // 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_mimeType, 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); + } + m_frame->document()->setDecoder(m_decoder.get()); + } + return m_decoder.get(); +} + +void DocumentWriter::reportDataReceived() +{ + ASSERT(m_decoder); + if (!m_receivedData) { + m_receivedData = true; + if (m_decoder->encoding().usesVisualOrdering()) + m_frame->document()->setVisuallyOrdered(); + m_frame->document()->recalcStyle(Node::Force); + } +} + +void DocumentWriter::addData(const char* str, int len, bool flush) +{ + if (len == -1) + len = strlen(str); + + DocumentParser* parser = m_frame->document()->parser(); + if (parser) + parser->appendBytes(this, str, len, flush); +} + +void DocumentWriter::end() +{ + m_frame->loader()->didEndDocument(); + endIfNotLoadingMainResource(); +} + +void DocumentWriter::endIfNotLoadingMainResource() +{ + if (m_frame->loader()->isLoadingMainResource() || !m_frame->page() || !m_frame->document()) + return; + + // http://bugs.webkit.org/show_bug.cgi?id=10854 + // The frame's last ref may be removed and it can be deleted by checkCompleted(), + // so we'll add a protective refcount + RefPtr<Frame> protector(m_frame); + + // make sure nothing's left in there + addData(0, 0, true); + m_frame->document()->finishParsing(); +} + +String DocumentWriter::encoding() const +{ + if (m_encodingWasChosenByUser && !m_encoding.isEmpty()) + return m_encoding; + if (m_decoder && m_decoder->encoding().isValid()) + return m_decoder->encoding().name(); + Settings* settings = m_frame->settings(); + return settings ? settings->defaultTextEncodingName() : String(); +} + +void DocumentWriter::setEncoding(const String& name, bool userChosen) +{ + m_frame->loader()->willSetEncoding(); + m_encoding = name; + m_encodingWasChosenByUser = userChosen; +} + +void DocumentWriter::setDecoder(TextResourceDecoder* decoder) +{ + m_decoder = decoder; +} + +String DocumentWriter::deprecatedFrameEncoding() const +{ + return m_frame->loader()->url().isEmpty() ? m_encoding : encoding(); +} + +void DocumentWriter::setDocumentWasLoadedAsPartOfNavigation() +{ + m_frame->document()->parser()->setDocumentWasLoadedAsPartOfNavigation(); +} + +} // namespace WebCore diff --git a/WebCore/loader/DocumentWriter.h b/WebCore/loader/DocumentWriter.h new file mode 100644 index 0000000..5fb3dc1 --- /dev/null +++ b/WebCore/loader/DocumentWriter.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010. Adam Barth. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Adam Barth. ("Adam Barth") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DocumentWriter_h +#define DocumentWriter_h + +#include "KURL.h" +#include "PlatformString.h" + +namespace WebCore { + +class Document; +class Frame; +class SecurityOrigin; +class TextResourceDecoder; + +class DocumentWriter : public Noncopyable { +public: + DocumentWriter(Frame*); + + // This is only called by ScriptController::executeIfJavaScriptURL + // and always contains the result of evaluating a javascript: url. + void replaceDocument(const String&); + + void begin(); + void begin(const KURL&, bool dispatchWindowObjectAvailable = true, SecurityOrigin* forcedSecurityOrigin = 0); + void addData(const char* string, int length = -1, bool flush = false); + void end(); + void endIfNotLoadingMainResource(); + void clear(); + + String encoding() const; + void setEncoding(const String& encoding, bool userChosen); + + // FIXME: It's really unforunate to need to expose this piece of state. + // I suspect a better design is to disentangle user-provided encodings, + // default encodings, and the decoding we're currently using. + String deprecatedFrameEncoding() const; + + const String& mimeType() const { return m_mimeType; } + void setMIMEType(const String& type) { m_mimeType = type; } + + void setDecoder(TextResourceDecoder*); + + // Exposed for DoucmentParser::appendBytes + TextResourceDecoder* createDecoderIfNeeded(); + void reportDataReceived(); + + void setDocumentWasLoadedAsPartOfNavigation(); + +private: + PassRefPtr<Document> createDocument(const KURL&); + + Frame* m_frame; + + bool m_receivedData; + String m_mimeType; + + bool m_encodingWasChosenByUser; + String m_encoding; + RefPtr<TextResourceDecoder> m_decoder; +}; + +} // namespace WebCore + +#endif // DocumentWriter_h diff --git a/WebCore/loader/EmptyClients.h b/WebCore/loader/EmptyClients.h index 5b24bd3..3a5e0e9 100644 --- a/WebCore/loader/EmptyClients.h +++ b/WebCore/loader/EmptyClients.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2006 Eric Seidel (eric@webkit.org) - * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,20 +29,23 @@ #define EmptyClients_h #include "ChromeClient.h" -#include "ContextMenuClient.h" #include "Console.h" +#include "ContextMenuClient.h" +#include "DeviceMotionClient.h" +#include "DeviceOrientationClient.h" #include "DocumentLoader.h" #include "DragClient.h" #include "EditCommand.h" #include "EditorClient.h" #include "FloatRect.h" #include "FocusDirection.h" -#include "FormState.h" #include "FrameLoaderClient.h" +#include "FrameNetworkingContext.h" #include "InspectorClient.h" #include "PluginHalterClient.h" +#include "PopupMenu.h" #include "ResourceError.h" -#include "SharedBuffer.h" +#include "SearchPopupMenu.h" /* This file holds empty Client stubs for use by WebCore. @@ -58,6 +62,27 @@ namespace WebCore { +class SharedGraphicsContext3D; + +class EmptyPopupMenu : public PopupMenu { +public: + virtual void show(const IntRect&, FrameView*, int) {} + virtual void hide() {} + virtual void updateFromElement() {} + virtual void disconnectClient() {} +}; + +class EmptySearchPopupMenu : public SearchPopupMenu { +public: + virtual PopupMenu* popupMenu() { return m_popup.get(); } + virtual void saveRecentSearches(const AtomicString&, const Vector<String>&) {} + virtual void loadRecentSearches(const AtomicString&, Vector<String>&) {} + virtual bool enabled() { return false; } + +private: + RefPtr<EmptyPopupMenu> m_popup; +}; + class EmptyChromeClient : public ChromeClient { public: virtual ~EmptyChromeClient() { } @@ -70,19 +95,20 @@ public: virtual float scaleFactor() { return 1.f; } -#ifdef ANDROID_USER_GESTURE - virtual void focus(bool userGesture) { } -#else - virtual void focus() { } +#if ENABLE(ANDROID_INSTALLABLE_WEB_APPS) + virtual void webAppCanBeInstalled() { } #endif + + virtual void focus() { } virtual void unfocus() { } virtual bool canTakeFocus(FocusDirection) { return false; } virtual void takeFocus(FocusDirection) { } virtual void focusedNodeChanged(Node*) { } + virtual void focusedFrameChanged(Frame*) { } - virtual Page* createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures&) { return 0; } + virtual Page* createWindow(Frame*, const FrameLoadRequest&, const WindowFeatures&, const NavigationAction&) { return 0; } virtual void show() { } virtual bool canRunModal() { return false; } @@ -114,17 +140,28 @@ public: virtual bool runJavaScriptPrompt(Frame*, const String&, const String&, String&) { return false; } virtual bool shouldInterruptJavaScript() { return false; } + virtual bool selectItemWritingDirectionIsNatural() { return false; } + virtual PassRefPtr<PopupMenu> createPopupMenu(PopupMenuClient*) const { return adoptRef(new EmptyPopupMenu()); } + virtual PassRefPtr<SearchPopupMenu> createSearchPopupMenu(PopupMenuClient*) const { return adoptRef(new EmptySearchPopupMenu()); } + +#if ENABLE(CONTEXT_MENUS) + virtual void showContextMenu() { } +#endif + virtual void setStatusbarText(const String&) { } virtual bool tabsToLinks() const { return false; } virtual IntRect windowResizerRect() const { return IntRect(); } - virtual void addToDirtyRegion(const IntRect&) { } - virtual void scrollBackingStore(int, int, const IntRect&, const IntRect&) { } - virtual void updateBackingStore() { } - virtual void repaint(const IntRect&, bool, bool, bool) { } + virtual void invalidateWindow(const IntRect&, bool) { } + virtual void invalidateContentsAndWindow(const IntRect&, bool) { } + virtual void invalidateContentsForSlowScroll(const IntRect&, bool) {}; virtual void scroll(const IntSize&, const IntRect&, const IntRect&) { } +#if ENABLE(TILED_BACKING_STORE) + virtual void delegatedScrollRequested(const IntSize&) { } +#endif + virtual IntPoint screenToWindow(const IntPoint& p) const { return p; } virtual IntRect windowToScreen(const IntRect& r) const { return r; } virtual PlatformPageClient platformPageClient() const { return 0; } @@ -143,6 +180,7 @@ public: #if ENABLE(OFFLINE_WEB_APPLICATIONS) virtual void reachedMaxAppCacheSize(int64_t) { } + virtual void reachedApplicationCacheOriginQuota(SecurityOrigin*) { } #endif #if ENABLE(NOTIFICATIONS) @@ -150,6 +188,7 @@ public: #endif virtual void runOpenPanel(Frame*, PassRefPtr<FileChooser>) { } + virtual void chooseIconForFiles(const Vector<String>&, FileChooser*) { } virtual void formStateDidChange(const Node*) { } @@ -158,19 +197,22 @@ public: virtual PassOwnPtr<HTMLParserQuirks> createHTMLParserQuirks() { return 0; } - virtual bool setCursor(PlatformCursorHandle) { return false; } + virtual void setCursor(const Cursor&) { } virtual void scrollRectIntoView(const IntRect&, const ScrollView*) const {} virtual void requestGeolocationPermissionForFrame(Frame*, Geolocation*) {} - virtual void cancelGeolocationPermissionRequestForFrame(Frame*) {} + virtual void cancelGeolocationPermissionRequestForFrame(Frame*, Geolocation*) {} #if USE(ACCELERATED_COMPOSITING) - virtual void attachRootGraphicsLayer(Frame*, GraphicsLayer*) {}; - virtual void setNeedsOneShotDrawingSynchronization() {}; - virtual void scheduleCompositingLayerSync() {}; + virtual void attachRootGraphicsLayer(Frame*, GraphicsLayer*) {} + virtual void setNeedsOneShotDrawingSynchronization() {} + virtual void scheduleCompositingLayerSync() {} #endif +#if PLATFORM(WIN) + virtual void setLastSetCursorToCurrentCursor() { } +#endif #if ENABLE(TOUCH_EVENTS) virtual void needTouchEvents(bool) { } #endif @@ -199,12 +241,14 @@ public: virtual void dispatchWillSendRequest(DocumentLoader*, unsigned long, ResourceRequest&, const ResourceResponse&) { } virtual void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long, const AuthenticationChallenge&) { } virtual void dispatchDidCancelAuthenticationChallenge(DocumentLoader*, unsigned long, const AuthenticationChallenge&) { } +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + virtual bool canAuthenticateAgainstProtectionSpace(DocumentLoader*, unsigned long, const ProtectionSpace&) { return false; } +#endif virtual void dispatchDidReceiveResponse(DocumentLoader*, unsigned long, const ResourceResponse&) { } virtual void dispatchDidReceiveContentLength(DocumentLoader*, unsigned long, int) { } virtual void dispatchDidFinishLoading(DocumentLoader*, unsigned long) { } virtual void dispatchDidFailLoading(DocumentLoader*, unsigned long, const ResourceError&) { } virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int) { return false; } - virtual void dispatchDidLoadResourceByXMLHttpRequest(unsigned long, const ScriptString&) { } virtual void dispatchDidHandleOnloadEvents() { } virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() { } @@ -218,6 +262,7 @@ public: virtual void dispatchDidReceiveIcon() { } virtual void dispatchDidStartProvisionalLoad() { } virtual void dispatchDidReceiveTitle(const String&) { } + virtual void dispatchDidChangeIcons() { } virtual void dispatchDidCommitLoad() { } virtual void dispatchDidFailProvisionalLoad(const ResourceError&) { } virtual void dispatchDidFailLoad(const ResourceError&) { } @@ -226,7 +271,7 @@ public: virtual void dispatchDidFirstLayout() { } virtual void dispatchDidFirstVisuallyNonEmptyLayout() { } - virtual Frame* dispatchCreatePage() { return 0; } + virtual Frame* dispatchCreatePage(const NavigationAction&) { return 0; } virtual void dispatchShow() { } virtual void dispatchDecidePolicyForMIMEType(FramePolicyFunction, const String&, const ResourceRequest&) { } @@ -236,6 +281,7 @@ public: virtual void dispatchUnableToImplementPolicy(const ResourceError&) { } + virtual void dispatchWillSendSubmitEvent(HTMLFormElement*) { } virtual void dispatchWillSubmitForm(FramePolicyFunction, PassRefPtr<FormState>) { } virtual void dispatchDidLoadMainResource(DocumentLoader*) { } @@ -271,6 +317,7 @@ public: virtual bool canHandleRequest(const ResourceRequest&) const { return false; } virtual bool canShowMIMEType(const String&) const { return false; } + virtual bool canShowMIMETypeAsHTML(const String&) const { return false; } virtual bool representationExistsForURLScheme(const String&) const { return false; } virtual String generatedMIMETypeForURLScheme(const String&) const { return ""; } @@ -290,6 +337,8 @@ public: virtual void transitionToCommittedFromCachedFrame(CachedFrame*) { } virtual void transitionToCommittedForNewPage() { } + virtual void dispatchDidBecomeFrameset(bool) { } + virtual void updateGlobalHistory() { } virtual void updateGlobalHistoryRedirectLinks() { } virtual bool shouldGoToHistoryItem(HistoryItem*) const { return false; } @@ -301,8 +350,15 @@ public: virtual void didDisplayInsecureContent() { } virtual void didRunInsecureContent(SecurityOrigin*) { } virtual PassRefPtr<Frame> createFrame(const KURL&, const String&, HTMLFrameOwnerElement*, const String&, bool, int, int) { return 0; } + virtual void didTransferChildFrameToNewDocument(Page*) { } + virtual void transferLoadingResourceFromPage(unsigned long, DocumentLoader*, const ResourceRequest&, Page*) { } virtual PassRefPtr<Widget> createPlugin(const IntSize&, HTMLPlugInElement*, const KURL&, const Vector<String>&, const Vector<String>&, const String&, bool) { return 0; } virtual PassRefPtr<Widget> createJavaAppletWidget(const IntSize&, HTMLAppletElement*, const KURL&, const Vector<String>&, const Vector<String>&) { return 0; } +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + virtual PassRefPtr<Widget> createMediaPlayerProxyPlugin(const IntSize&, HTMLMediaElement*, const KURL&, const Vector<String>&, const Vector<String>&, const String&) { return 0; } + virtual void hideMediaPlayerProxyPlugin(Widget*) { } + virtual void showMediaPlayerProxyPlugin(Widget*) { } +#endif virtual ObjectContentType objectContentType(const KURL&, const String&) { return ObjectContentType(); } virtual String overrideMediaType() const { return String(); } @@ -312,12 +368,17 @@ public: virtual void documentElementAvailable() { } virtual void didPerformFirstNavigation() const { } - virtual void registerForIconNotification(bool) { } - #if USE(V8) virtual void didCreateScriptContextForFrame() { } virtual void didDestroyScriptContextForFrame() { } virtual void didCreateIsolatedScriptContext() { } + virtual bool allowScriptExtension(const String& extensionName, int extensionGroup) { return false; } +#endif + + virtual void registerForIconNotification(bool) { } + +#ifdef ANDROID_APPLE_TOUCH_ICON + virtual void dispatchDidReceiveTouchIconURL(const String& url, bool precomposed) { } #endif #if PLATFORM(MAC) @@ -327,6 +388,7 @@ public: virtual bool shouldCacheResponse(DocumentLoader*, unsigned long, const ResourceResponse&, const unsigned char*, unsigned long long) { return true; } #endif + virtual PassRefPtr<FrameNetworkingContext> createNetworkingContext() { return PassRefPtr<FrameNetworkingContext>(); } }; class EmptyEditorClient : public EditorClient, public Noncopyable { @@ -393,6 +455,8 @@ public: virtual void markedTextAbandoned(Frame*) { } virtual NSString* userVisibleString(NSURL*) { return 0; } + virtual DocumentFragment* documentFragmentFromAttributedString(NSAttributedString*, Vector<RefPtr<ArchiveResource> >&) { return 0; }; + virtual void setInsertionPasteboard(NSPasteboard*) { }; #ifdef BUILDING_ON_TIGER virtual NSArray* pasteboardTypesForSelection(Frame*) { return 0; } #endif @@ -423,11 +487,17 @@ public: #if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) virtual void checkTextOfParagraph(const UChar*, int, uint64_t, Vector<TextCheckingResult>&) { }; #endif +#if SUPPORT_AUTOCORRECTION_PANEL + virtual void showCorrectionPanel(CorrectionPanelInfo::PanelType, const FloatRect&, const String&, const String&, Editor*) { } + virtual void dismissCorrectionPanel(CorrectionWasRejectedOrNot) { } + virtual bool isShowingCorrectionPanel() { return false; } +#endif virtual void updateSpellingUIWithGrammarString(const String&, const GrammarDetail&) { } virtual void updateSpellingUIWithMisspelledWord(const String&) { } virtual void showSpellingUI(bool) { } virtual bool spellingUIIsShowing() { return false; } virtual void getGuessesForWord(const String&, Vector<String>&) { } + virtual void willSetInputMethodState() { } virtual void setInputMethodState(bool) { } @@ -475,32 +545,35 @@ public: virtual ~EmptyInspectorClient() { } virtual void inspectorDestroyed() { } - - virtual Page* createPage() { return 0; }; - - virtual String localizedStringsURL() { return String(); } - - virtual String hiddenPanels() { return String(); } - - virtual void showWindow() { } - virtual void closeWindow() { } - - virtual void attachWindow() { } - virtual void detachWindow() { } - - virtual void setAttachedWindowHeight(unsigned) { } + + virtual void openInspectorFrontend(InspectorController*) { } virtual void highlight(Node*) { } virtual void hideHighlight() { } - virtual void inspectedURLChanged(const String&) { } virtual void populateSetting(const String&, String*) { } virtual void storeSetting(const String&, const String&) { } + virtual bool sendMessageToFrontend(const String&) { return false; } +}; - virtual void inspectorWindowObjectCleared() { } +class EmptyDeviceMotionClient : public DeviceMotionClient { +public: + virtual void setController(DeviceMotionController*) { } + virtual void startUpdating() { } + virtual void stopUpdating() { } + virtual DeviceMotionData* currentDeviceMotion() const { return 0; } + virtual void deviceMotionControllerDestroyed() { } +}; + +class EmptyDeviceOrientationClient : public DeviceOrientationClient { +public: + virtual void setController(DeviceOrientationController*) { } + virtual void startUpdating() { } + virtual void stopUpdating() { } + virtual DeviceOrientation* lastOrientation() const { return 0; } + virtual void deviceOrientationControllerDestroyed() { } }; } #endif // EmptyClients_h - diff --git a/WebCore/loader/FTPDirectoryDocument.cpp b/WebCore/loader/FTPDirectoryDocument.cpp deleted file mode 100644 index 62173f5..0000000 --- a/WebCore/loader/FTPDirectoryDocument.cpp +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * 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, - * 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(FTPDIR) -#include "FTPDirectoryDocument.h" - -#include "CharacterNames.h" -#include "CString.h" -#include "HTMLNames.h" -#include "HTMLTableElement.h" -#include "HTMLTokenizer.h" -#include "LocalizedStrings.h" -#include "Logging.h" -#include "FTPDirectoryParser.h" -#include "SegmentedString.h" -#include "Settings.h" -#include "SharedBuffer.h" -#include "Text.h" - -#include <wtf/CurrentTime.h> -#include <wtf/StdLibExtras.h> - -using namespace std; - -namespace WebCore { - -using namespace HTMLNames; - -class FTPDirectoryTokenizer : public HTMLTokenizer { -public: - FTPDirectoryTokenizer(HTMLDocument*); - - virtual void write(const SegmentedString&, bool appendData); - virtual void finish(); - - virtual bool isWaitingForScripts() const { return false; } - - inline void checkBuffer(int len = 10) - { - if ((m_dest - m_buffer) > m_size - len) { - // Enlarge buffer - int newSize = max(m_size * 2, m_size + len); - int oldOffset = m_dest - m_buffer; - m_buffer = static_cast<UChar*>(fastRealloc(m_buffer, newSize * sizeof(UChar))); - m_dest = m_buffer + oldOffset; - m_size = newSize; - } - } - -private: - // The tokenizer will attempt to load the document template specified via the preference - // Failing that, it will fall back and create the basic document which will have a minimal - // table for presenting the FTP directory in a useful manner - bool loadDocumentTemplate(); - void createBasicDocument(); - - void parseAndAppendOneLine(const String&); - void appendEntry(const String& name, const String& size, const String& date, bool isDirectory); - PassRefPtr<Element> createTDForFilename(const String&); - - Document* m_doc; - RefPtr<HTMLTableElement> m_tableElement; - - bool m_skipLF; - bool m_parsedTemplate; - - int m_size; - UChar* m_buffer; - UChar* m_dest; - String m_carryOver; - - ListState m_listState; -}; - -FTPDirectoryTokenizer::FTPDirectoryTokenizer(HTMLDocument* doc) - : HTMLTokenizer(doc, false) - , m_doc(doc) - , m_skipLF(false) - , m_parsedTemplate(false) - , m_size(254) - , m_buffer(static_cast<UChar*>(fastMalloc(sizeof(UChar) * m_size))) - , m_dest(m_buffer) -{ -} - -void FTPDirectoryTokenizer::appendEntry(const String& filename, const String& size, const String& date, bool isDirectory) -{ - ExceptionCode ec; - - RefPtr<Element> rowElement = m_tableElement->insertRow(-1, ec); - rowElement->setAttribute("class", "ftpDirectoryEntryRow", ec); - - RefPtr<Element> element = m_doc->createElement(tdTag, false); - element->appendChild(Text::create(m_doc, String(&noBreakSpace, 1)), ec); - if (isDirectory) - element->setAttribute("class", "ftpDirectoryIcon ftpDirectoryTypeDirectory", ec); - else - element->setAttribute("class", "ftpDirectoryIcon ftpDirectoryTypeFile", ec); - rowElement->appendChild(element, ec); - - element = createTDForFilename(filename); - element->setAttribute("class", "ftpDirectoryFileName", ec); - rowElement->appendChild(element, ec); - - element = m_doc->createElement(tdTag, false); - element->appendChild(Text::create(m_doc, date), ec); - element->setAttribute("class", "ftpDirectoryFileDate", ec); - rowElement->appendChild(element, ec); - - element = m_doc->createElement(tdTag, false); - element->appendChild(Text::create(m_doc, size), ec); - element->setAttribute("class", "ftpDirectoryFileSize", ec); - rowElement->appendChild(element, ec); -} - -PassRefPtr<Element> FTPDirectoryTokenizer::createTDForFilename(const String& filename) -{ - ExceptionCode ec; - - String fullURL = m_doc->baseURL().string(); - if (fullURL[fullURL.length() - 1] == '/') - fullURL.append(filename); - else - fullURL.append("/" + filename); - - RefPtr<Element> anchorElement = m_doc->createElement(aTag, false); - anchorElement->setAttribute("href", fullURL, ec); - anchorElement->appendChild(Text::create(m_doc, filename), ec); - - RefPtr<Element> tdElement = m_doc->createElement(tdTag, false); - tdElement->appendChild(anchorElement, ec); - - return tdElement.release(); -} - -static String processFilesizeString(const String& size, bool isDirectory) -{ - if (isDirectory) - return "--"; - - bool valid; - int64_t bytes = size.toUInt64(&valid); - if (!valid) - return unknownFileSizeText(); - - if (bytes < 1000000) - return String::format("%.2f KB", static_cast<float>(bytes)/1000); - - if (bytes < 1000000000) - return String::format("%.2f MB", static_cast<float>(bytes)/1000000); - - return String::format("%.2f GB", static_cast<float>(bytes)/1000000000); -} - -static bool wasLastDayOfMonth(int year, int month, int day) -{ - static int lastDays[] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - if (month < 0 || month > 11) - return false; - - if (month == 2) { - if (year % 4 == 0 && (year % 100 || year % 400 == 0)) { - if (day == 29) - return true; - return false; - } - - if (day == 28) - return true; - return false; - } - - return lastDays[month] == day; -} - -static String processFileDateString(const FTPTime& fileTime) -{ - // FIXME: Need to localize this string? - - String timeOfDay; - - if (!(fileTime.tm_hour == 0 && fileTime.tm_min == 0 && fileTime.tm_sec == 0)) { - int hour = fileTime.tm_hour; - ASSERT(hour >= 0 && hour < 24); - - if (hour < 12) { - if (hour == 0) - hour = 12; - timeOfDay = String::format(", %i:%02i AM", hour, fileTime.tm_min); - } else { - hour = hour - 12; - if (hour == 0) - hour = 12; - timeOfDay = String::format(", %i:%02i PM", hour, fileTime.tm_min); - } - } - - // If it was today or yesterday, lets just do that - but we have to compare to the current time - struct tm now; - time_t now_t = time(NULL); - getLocalTime(&now_t, &now); - - // localtime does "year = current year - 1900", compensate for that for readability and comparison purposes - now.tm_year += 1900; - - if (fileTime.tm_year == now.tm_year) { - if (fileTime.tm_mon == now.tm_mon) { - if (fileTime.tm_mday == now.tm_mday) - return "Today" + timeOfDay; - if (fileTime.tm_mday == now.tm_mday - 1) - return "Yesterday" + timeOfDay; - } - - 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; - } - - if (fileTime.tm_year == now.tm_year - 1 && fileTime.tm_mon == 12 && fileTime.tm_mday == 31 && now.tm_mon == 1 && now.tm_mday == 1) - return "Yesterday" + timeOfDay; - - static const char* months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???" }; - - int month = fileTime.tm_mon; - if (month < 0 || month > 11) - month = 12; - - String dateString; - - if (fileTime.tm_year > -1) - dateString = String::format("%s %i, %i", months[month], fileTime.tm_mday, fileTime.tm_year); - else - dateString = String::format("%s %i, %i", months[month], fileTime.tm_mday, now.tm_year); - - return dateString + timeOfDay; -} - -void FTPDirectoryTokenizer::parseAndAppendOneLine(const String& inputLine) -{ - ListResult result; - CString latin1Input = inputLine.latin1(); - - 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) - return; - - String filename(result.filename, result.filenameLength); - if (result.type == FTPDirectoryEntry) { - filename.append("/"); - - // We have no interest in linking to "current directory" - if (filename == "./") - return; - } - - LOG(FTP, "Appending entry - %s, %s", filename.ascii().data(), result.fileSize.ascii().data()); - - appendEntry(filename, processFilesizeString(result.fileSize, result.type == FTPDirectoryEntry), processFileDateString(result.modifiedTime), result.type == FTPDirectoryEntry); -} - -static inline PassRefPtr<SharedBuffer> createTemplateDocumentData(Settings* settings) -{ - RefPtr<SharedBuffer> buffer = 0; - if (settings) - buffer = SharedBuffer::createWithContentsOfFile(settings->ftpDirectoryTemplatePath()); - if (buffer) - LOG(FTP, "Loaded FTPDirectoryTemplate of length %i\n", buffer->size()); - return buffer.release(); -} - -bool FTPDirectoryTokenizer::loadDocumentTemplate() -{ - DEFINE_STATIC_LOCAL(RefPtr<SharedBuffer>, templateDocumentData, (createTemplateDocumentData(m_doc->settings()))); - // FIXME: Instead of storing the data, we'd rather actually parse the template data into the template Document once, - // store that document, then "copy" it whenever we get an FTP directory listing. There are complexities with this - // approach that make it worth putting this off. - - if (!templateDocumentData) { - LOG_ERROR("Could not load templateData"); - return false; - } - - // Tokenize the template as an HTML document synchronously - setForceSynchronous(true); - HTMLTokenizer::write(String(templateDocumentData->data(), templateDocumentData->size()), true); - setForceSynchronous(false); - - RefPtr<Element> tableElement = m_doc->getElementById("ftpDirectoryTable"); - if (!tableElement) - LOG_ERROR("Unable to find element by id \"ftpDirectoryTable\" in the template document."); - else if (!tableElement->hasTagName(tableTag)) - LOG_ERROR("Element of id \"ftpDirectoryTable\" is not a table element"); - else - m_tableElement = static_cast<HTMLTableElement*>(tableElement.get()); - - // Bail if we found the table element - if (m_tableElement) - return true; - - // Otherwise create one manually - 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 - // If that fails for some reason, cram it on the end of the document as a last - // ditch effort - if (Element* body = m_doc->body()) - body->appendChild(m_tableElement, ec); - else - m_doc->appendChild(m_tableElement, ec); - - return true; -} - -void FTPDirectoryTokenizer::createBasicDocument() -{ - LOG(FTP, "Creating a basic FTP document structure as no template was loaded"); - - // FIXME: Make this "basic document" more acceptable - - - RefPtr<Element> bodyElement = m_doc->createElement(bodyTag, false); - - ExceptionCode ec; - m_doc->appendChild(bodyElement, 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); -} - -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 - if (!m_tableElement) { - if (!loadDocumentTemplate()) - createBasicDocument(); - ASSERT(m_tableElement); - } - - bool foundNewLine = false; - - m_dest = m_buffer; - SegmentedString str = s; - while (!str.isEmpty()) { - UChar c = *str; - - if (c == '\r') { - *m_dest++ = '\n'; - foundNewLine = true; - // possibly skip an LF in the case of an CRLF sequence - m_skipLF = true; - } else if (c == '\n') { - if (!m_skipLF) - *m_dest++ = c; - else - m_skipLF = false; - } else { - *m_dest++ = c; - m_skipLF = false; - } - - str.advance(); - - // Maybe enlarge the buffer - checkBuffer(); - } - - if (!foundNewLine) { - m_dest = m_buffer; - return; - } - - UChar* start = m_buffer; - UChar* cursor = start; - - while (cursor < m_dest) { - if (*cursor == '\n') { - m_carryOver.append(String(start, cursor - start)); - LOG(FTP, "%s", m_carryOver.ascii().data()); - parseAndAppendOneLine(m_carryOver); - m_carryOver = String(); - - start = ++cursor; - } else - cursor++; - } - - // Copy the partial line we have left to the carryover buffer - if (cursor - start > 1) - m_carryOver.append(String(start, cursor - start - 1)); -} - -void FTPDirectoryTokenizer::finish() -{ - // Possible the last line in the listing had no newline, so try to parse it now - if (!m_carryOver.isEmpty()) { - parseAndAppendOneLine(m_carryOver); - m_carryOver = String(); - } - - m_tableElement = 0; - fastFree(m_buffer); - - HTMLTokenizer::finish(); -} - -FTPDirectoryDocument::FTPDirectoryDocument(Frame* frame) - : HTMLDocument(frame) -{ -#ifndef NDEBUG - LogFTP.state = WTFLogChannelOn; -#endif -} - -Tokenizer* FTPDirectoryDocument::createTokenizer() -{ - return new FTPDirectoryTokenizer(this); -} - -} - -#endif // ENABLE(FTPDIR) diff --git a/WebCore/loader/FTPDirectoryParser.cpp b/WebCore/loader/FTPDirectoryParser.cpp index 142f2a3..f6a74de 100644 --- a/WebCore/loader/FTPDirectoryParser.cpp +++ b/WebCore/loader/FTPDirectoryParser.cpp @@ -188,9 +188,13 @@ FTPEntryType parseOneFTPLine(const char* line, ListState& state, ListResult& res if (pos < linelen && line[pos] == ',') { unsigned long long seconds = 0; +#if OS(WINDOWS) + sscanf(p + 1, "%I64u", &seconds); +#else sscanf(p + 1, "%llu", &seconds); +#endif time_t t = static_cast<time_t>(seconds); - + // FIXME: This code has the year 2038 bug gmtime_r(&t, &result.modifiedTime); result.modifiedTime.tm_year += 1900; diff --git a/WebCore/loader/FormSubmission.cpp b/WebCore/loader/FormSubmission.cpp new file mode 100644 index 0000000..f3f19d2 --- /dev/null +++ b/WebCore/loader/FormSubmission.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FormSubmission.h" + +#include "DOMFormData.h" +#include "Document.h" +#include "Event.h" +#include "FormData.h" +#include "FormDataBuilder.h" +#include "FormState.h" +#include "Frame.h" +#include "FrameLoadRequest.h" +#include "FrameLoader.h" +#include "HTMLFormControlElement.h" +#include "HTMLFormElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "TextEncoding.h" +#include <wtf/CurrentTime.h> +#include <wtf/RandomNumber.h> + +namespace WebCore { + +using namespace HTMLNames; + +static int64_t generateFormDataIdentifier() +{ + // Initialize to the current time to reduce the likelihood of generating + // identifiers that overlap with those from past/future browser sessions. + static int64_t nextIdentifier = static_cast<int64_t>(currentTime() * 1000000.0); + return ++nextIdentifier; +} + +static void appendMailtoPostFormDataToURL(KURL& url, const FormData& data, const String& encodingType) +{ + String body = data.flattenToString(); + + if (equalIgnoringCase(encodingType, "text/plain")) { + // Convention seems to be to decode, and s/&/\r\n/. Also, spaces are encoded as %20. + body = decodeURLEscapeSequences(body.replace('&', "\r\n").replace('+', ' ') + "\r\n"); + } + + Vector<char> bodyData; + bodyData.append("body=", 5); + FormDataBuilder::encodeStringAsFormData(bodyData, body.utf8()); + body = String(bodyData.data(), bodyData.size()).replace('+', "%20"); + + String query = url.query(); + if (!query.isEmpty()) + query.append('&'); + query.append(body); + url.setQuery(query); +} + +void FormSubmission::Attributes::parseAction(const String& action) +{ + // FIXME: Can we parse into a KURL? + m_action = stripLeadingAndTrailingHTMLSpaces(action); +} + +void FormSubmission::Attributes::parseEncodingType(const String& type) +{ + if (type.contains("multipart", false) || type.contains("form-data", false)) { + m_encodingType = "multipart/form-data"; + m_isMultiPartForm = true; + } else if (type.contains("text", false) || type.contains("plain", false)) { + m_encodingType = "text/plain"; + m_isMultiPartForm = false; + } else { + m_encodingType = "application/x-www-form-urlencoded"; + m_isMultiPartForm = false; + } +} + +void FormSubmission::Attributes::parseMethodType(const String& type) +{ + if (equalIgnoringCase(type, "post")) + m_method = FormSubmission::PostMethod; + else if (equalIgnoringCase(type, "get")) + m_method = FormSubmission::GetMethod; +} + +inline FormSubmission::FormSubmission(Method method, const KURL& action, const String& target, const String& contentType, PassRefPtr<FormState> state, PassRefPtr<FormData> data, const String& boundary, bool lockHistory, PassRefPtr<Event> event) + : m_method(method) + , m_action(action) + , m_target(target) + , m_contentType(contentType) + , m_formState(state) + , m_formData(data) + , m_boundary(boundary) + , m_lockHistory(lockHistory) + , m_event(event) +{ +} + +PassRefPtr<FormSubmission> FormSubmission::create(HTMLFormElement* form, const Attributes& attributes, PassRefPtr<Event> event, bool lockHistory, FormSubmissionTrigger trigger) +{ + ASSERT(form); + Document* document = form->document(); + KURL actionURL = document->completeURL(attributes.action().isEmpty() ? document->url().string() : attributes.action()); + bool isMailtoForm = actionURL.protocolIs("mailto"); + bool isMultiPartForm = false; + String encodingType = attributes.encodingType(); + + if (attributes.method() == PostMethod) { + isMultiPartForm = attributes.isMultiPartForm(); + if (isMultiPartForm && isMailtoForm) { + encodingType = "application/x-www-form-urlencoded"; + isMultiPartForm = false; + } + } + + TextEncoding dataEncoding = isMailtoForm ? UTF8Encoding() : FormDataBuilder::encodingFromAcceptCharset(attributes.acceptCharset(), document); + RefPtr<DOMFormData> domFormData = DOMFormData::create(dataEncoding.encodingForFormSubmission()); + Vector<pair<String, String> > formValues; + + for (unsigned i = 0; i < form->associatedElements().size(); ++i) { + HTMLFormControlElement* control = form->associatedElements()[i]; + if (!control->disabled()) + control->appendFormData(*domFormData, isMultiPartForm); + if (control->hasLocalName(inputTag)) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(control); + if (input->isTextField()) { + formValues.append(pair<String, String>(input->name(), input->value())); + if (input->isSearchField()) + input->addSearchResult(); + } + } + } + + RefPtr<FormData> formData; + String boundary; + + if (isMultiPartForm) { + formData = FormData::createMultiPart(*(static_cast<FormDataList*>(domFormData.get())), domFormData->encoding(), document); + boundary = formData->boundary().data(); + } else { + formData = FormData::create(*(static_cast<FormDataList*>(domFormData.get())), domFormData->encoding()); + if (attributes.method() == PostMethod && isMailtoForm) { + // Convert the form data into a string that we put into the URL. + appendMailtoPostFormDataToURL(actionURL, *formData, encodingType); + formData = FormData::create(); + } + } + + formData->setIdentifier(generateFormDataIdentifier()); + String targetOrBaseTarget = attributes.target().isEmpty() ? document->baseTarget() : attributes.target(); + RefPtr<FormState> formState = FormState::create(form, formValues, document->frame(), trigger); + return adoptRef(new FormSubmission(attributes.method(), actionURL, targetOrBaseTarget, encodingType, formState.release(), formData.release(), boundary, lockHistory, event)); +} + +KURL FormSubmission::requestURL() const +{ + if (m_method == FormSubmission::PostMethod) + return m_action; + + KURL requestURL(m_action); + requestURL.setQuery(m_formData->flattenToString()); + return requestURL; +} + +void FormSubmission::populateFrameLoadRequest(FrameLoadRequest& frameRequest) +{ + if (!m_target.isEmpty()) + frameRequest.setFrameName(m_target); + + if (!m_referrer.isEmpty()) + frameRequest.resourceRequest().setHTTPReferrer(m_referrer); + + if (m_method == FormSubmission::PostMethod) { + frameRequest.resourceRequest().setHTTPMethod("POST"); + frameRequest.resourceRequest().setHTTPBody(m_formData); + + // construct some user headers if necessary + if (m_contentType.isNull() || m_contentType == "application/x-www-form-urlencoded") + frameRequest.resourceRequest().setHTTPContentType(m_contentType); + else // contentType must be "multipart/form-data" + frameRequest.resourceRequest().setHTTPContentType(m_contentType + "; boundary=" + m_boundary); + } + + frameRequest.resourceRequest().setURL(requestURL()); + FrameLoader::addHTTPOriginIfNeeded(frameRequest.resourceRequest(), m_origin); +} + +} diff --git a/WebCore/loader/FormSubmission.h b/WebCore/loader/FormSubmission.h new file mode 100644 index 0000000..b935882 --- /dev/null +++ b/WebCore/loader/FormSubmission.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2010 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 FormSubmission_h +#define FormSubmission_h + +#include "FormState.h" +#include "KURL.h" + +namespace WebCore { + +class Document; +class Event; +class FormData; +struct FrameLoadRequest; +class HTMLFormElement; +class TextEncoding; + +class FormSubmission : public RefCounted<FormSubmission> { +public: + enum Method { GetMethod, PostMethod }; + + class Attributes : public Noncopyable { + public: + Attributes() + : m_method(GetMethod) + , m_isMultiPartForm(false) + , m_encodingType("application/x-www-form-urlencoded") + { + } + + Method method() const { return m_method; } + void parseMethodType(const String&); + + const String& action() const { return m_action; } + void parseAction(const String&); + + const String& target() const { return m_target; } + void setTarget(const String& target) { m_target = target; } + + const String& encodingType() const { return m_encodingType; } + void parseEncodingType(const String&); + bool isMultiPartForm() const { return m_isMultiPartForm; } + + const String& acceptCharset() const { return m_acceptCharset; } + void setAcceptCharset(const String& value) { m_acceptCharset = value; } + + private: + Method m_method; + bool m_isMultiPartForm; + + String m_action; + String m_target; + String m_encodingType; + String m_acceptCharset; + }; + + static PassRefPtr<FormSubmission> create(HTMLFormElement*, const Attributes&, PassRefPtr<Event> event, bool lockHistory, FormSubmissionTrigger); + + void populateFrameLoadRequest(FrameLoadRequest&); + + KURL requestURL() const; + + Method method() const { return m_method; } + const KURL& action() const { return m_action; } + const String& target() const { return m_target; } + void clearTarget() { m_target = String(); } + const String& contentType() const { return m_contentType; } + FormState* state() const { return m_formState.get(); } + FormData* data() const { return m_formData.get(); } + const String boundary() const { return m_boundary; } + bool lockHistory() const { return m_lockHistory; } + Event* event() const { return m_event.get(); } + + const String& referrer() const { return m_referrer; } + void setReferrer(const String& referrer) { m_referrer = referrer; } + const String& origin() const { return m_origin; } + void setOrigin(const String& origin) { m_origin = origin; } + +private: + FormSubmission(Method, const KURL& action, const String& target, const String& contentType, PassRefPtr<FormState>, PassRefPtr<FormData>, const String& boundary, bool lockHistory, PassRefPtr<Event>); + + // FIXME: Hold an instance of Attributes instead of individual members. + Method m_method; + KURL m_action; + String m_target; + String m_contentType; + RefPtr<FormState> m_formState; + RefPtr<FormData> m_formData; + String m_boundary; + bool m_lockHistory; + RefPtr<Event> m_event; + String m_referrer; + String m_origin; +}; + +} + +#endif // FormSubmission_h diff --git a/WebCore/loader/FrameLoader.cpp b/WebCore/loader/FrameLoader.cpp index 514f98a..8277338 100644 --- a/WebCore/loader/FrameLoader.cpp +++ b/WebCore/loader/FrameLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2008 Alp Toker <alp@atoker.com> @@ -38,15 +38,16 @@ #include "Archive.h" #include "ArchiveFactory.h" #endif -#include "BackForwardList.h" -#include "CString.h" -#include "Cache.h" +#include "BackForwardController.h" +#include "BeforeUnloadEvent.h" +#include "MemoryCache.h" #include "CachedPage.h" +#include "CachedResourceLoader.h" #include "Chrome.h" #include "DOMImplementation.h" #include "DOMWindow.h" -#include "DocLoader.h" #include "Document.h" +#include "DocumentLoadTiming.h" #include "DocumentLoader.h" #include "Editor.h" #include "EditorClient.h" @@ -55,15 +56,15 @@ #include "EventNames.h" #include "FloatRect.h" #include "FormState.h" +#include "FormSubmission.h" #include "Frame.h" #include "FrameLoadRequest.h" #include "FrameLoaderClient.h" +#include "FrameNetworkingContext.h" #include "FrameTree.h" #include "FrameView.h" #include "HTMLAnchorElement.h" -#include "HTMLAppletElement.h" #include "HTMLFormElement.h" -#include "HTMLFrameElement.h" #include "HTMLNames.h" #include "HTMLObjectElement.h" #include "HTTPParsers.h" @@ -78,36 +79,34 @@ #include "PageCache.h" #include "PageGroup.h" #include "PageTransitionEvent.h" -#include "PlaceholderDocument.h" #include "PluginData.h" #include "PluginDatabase.h" #include "PluginDocument.h" #include "ProgressTracker.h" -#include "RenderPart.h" -#include "RenderView.h" -#include "RenderWidget.h" #include "ResourceHandle.h" #include "ResourceRequest.h" +#include "SchemeRegistry.h" #include "ScriptController.h" #include "ScriptSourceCode.h" -#include "ScriptString.h" -#include "ScriptValue.h" #include "SecurityOrigin.h" #include "SegmentedString.h" +#include "SerializedScriptValue.h" #include "Settings.h" - -#if ENABLE(SHARED_WORKERS) -#include "SharedWorkerRepository.h" -#endif - #include "TextResourceDecoder.h" #include "WindowFeatures.h" -#include "XMLHttpRequest.h" -#include "XMLTokenizer.h" -#include "XSSAuditor.h" +#include "XMLDocumentParser.h" #include <wtf/CurrentTime.h> #include <wtf/StdLibExtras.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "HTMLMediaElement.h" +#endif + +#if ENABLE(SHARED_WORKERS) +#include "SharedWorkerRepository.h" +#endif #if ENABLE(SVG) #include "SVGDocument.h" @@ -126,16 +125,18 @@ namespace WebCore { +using namespace HTMLNames; + #if ENABLE(SVG) using namespace SVGNames; #endif -using namespace HTMLNames; #if ENABLE(XHTMLMP) static const char defaultAcceptHeader[] = "application/xml,application/vnd.wap.xhtml+xml,application/xhtml+xml;profile='http://www.wapforum.org/xhtml',text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; #else static const char defaultAcceptHeader[] = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; #endif + static double storedTimeOfLastCompletedLoad; bool isBackForwardLoadType(FrameLoadType type) @@ -163,12 +164,19 @@ static int numRequests(Document* document) if (!document) return 0; - return document->docLoader()->requestCount(); + return document->cachedResourceLoader()->requestCount(); } -static inline bool canReferToParentFrameEncoding(const Frame* frame, const Frame* parentFrame) +// This is not in the FrameLoader class to emphasize that it does not depend on +// private FrameLoader data, and to avoid increasing the number of public functions +// with access to private data. Since only this .cpp file needs it, making it +// non-member lets us exclude it from the header file, thus keeping FrameLoader.h's +// API simpler. +// +// FIXME: isDocumentSandboxed should eventually replace isSandboxed. +static bool isDocumentSandboxed(Frame* frame, SandboxFlags mask) { - return parentFrame && parentFrame->document()->securityOrigin()->canAccess(frame->document()->securityOrigin()); + return frame->document() && frame->document()->securityOrigin()->isSandboxed(mask); } FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client) @@ -177,34 +185,30 @@ FrameLoader::FrameLoader(Frame* frame, FrameLoaderClient* client) , m_policyChecker(frame) , m_history(frame) , m_notifer(frame) + , m_writer(frame) + , m_subframeLoader(frame) , m_state(FrameStateCommittedPage) , m_loadType(FrameLoadTypeStandard) , m_delegateIsHandlingProvisionalLoadError(false) - , m_firstLayoutDone(false) , m_quickRedirectComing(false) , m_sentRedirectNotification(false) , m_inStopAllLoaders(false) , m_isExecutingJavaScriptFormAction(false) , m_didCallImplicitClose(false) , m_wasUnloadEventEmitted(false) - , m_unloadEventBeingDispatched(false) + , m_pageDismissalEventBeingDispatched(false) , m_isComplete(false) , m_isLoadingMainResource(false) , m_needsClear(false) - , m_receivedData(false) - , m_encodingWasChosenByUser(false) - , m_containsPlugIns(false) , m_checkTimer(this, &FrameLoader::checkTimerFired) , m_shouldCallCheckCompleted(false) , m_shouldCallCheckLoadComplete(false) , m_opener(0) - , m_creatingInitialEmptyDocument(false) - , m_isDisplayingInitialEmptyDocument(false) - , m_committedFirstRealDocumentLoad(false) , m_didPerformFirstNavigation(false) , m_loadingFromCachedPage(false) , m_suppressOpenerInNewFrame(false) , m_sandboxFlags(SandboxAll) + , m_forcedSandboxFlags(SandboxNone) #ifndef NDEBUG , m_didDispatchDidCommitLoad(false) #endif @@ -220,26 +224,31 @@ FrameLoader::~FrameLoader() (*it)->loader()->m_opener = 0; m_client->frameLoaderDestroyed(); + + if (m_networkingContext) + m_networkingContext->invalidate(); } void FrameLoader::init() { + // Propagate sandbox attributes to this Frameloader and its descendants. + // This needs to be done early, so that an initial document gets correct sandbox flags in its SecurityOrigin. + updateSandboxFlags(); + // this somewhat odd set of steps is needed to give the frame an initial empty document - m_isDisplayingInitialEmptyDocument = false; - m_creatingInitialEmptyDocument = true; + m_stateMachine.advanceTo(FrameLoaderStateMachine::CreatingInitialEmptyDocument); setPolicyDocumentLoader(m_client->createDocumentLoader(ResourceRequest(KURL(ParsedURLString, "")), SubstituteData()).get()); setProvisionalDocumentLoader(m_policyDocumentLoader.get()); setState(FrameStateProvisional); m_provisionalDocumentLoader->setResponse(ResourceResponse(KURL(), "text/html", 0, String(), String())); m_provisionalDocumentLoader->finishedLoading(); - begin(KURL(), false); - end(); + writer()->begin(KURL(), false); + writer()->end(); m_frame->document()->cancelParsing(); - m_creatingInitialEmptyDocument = false; + m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocument); m_didCallImplicitClose = true; - // Propagate sandbox attributes to this Frameloader and its descendants. - updateSandboxFlags(); + m_networkingContext = m_client->createNetworkingContext(); } void FrameLoader::setDefersLoading(bool defers) @@ -252,101 +261,37 @@ void FrameLoader::setDefersLoading(bool defers) m_policyDocumentLoader->setDefersLoading(defers); if (!defers) { - m_frame->redirectScheduler()->startTimer(); + m_frame->navigationScheduler()->startTimer(); startCheckCompleteTimer(); } } -Frame* FrameLoader::createWindow(FrameLoader* frameLoaderForFrameLookup, const FrameLoadRequest& request, const WindowFeatures& features, bool& created) -{ - ASSERT(!features.dialog || request.frameName().isEmpty()); - - if (!request.frameName().isEmpty() && request.frameName() != "_blank") { - Frame* frame = frameLoaderForFrameLookup->frame()->tree()->find(request.frameName()); - if (frame && shouldAllowNavigation(frame)) { - if (!request.resourceRequest().url().isEmpty()) - frame->loader()->loadFrameRequest(request, false, false, 0, 0, SendReferrer); - if (Page* page = frame->page()) -#ifdef ANDROID_USER_GESTURE - page->chrome()->focus(isProcessingUserGesture()); -#else - page->chrome()->focus(); -#endif - created = false; - return frame; - } - } - - // FIXME: Setting the referrer should be the caller's responsibility. - FrameLoadRequest requestWithReferrer = request; - requestWithReferrer.resourceRequest().setHTTPReferrer(m_outgoingReferrer); - addHTTPOriginIfNeeded(requestWithReferrer.resourceRequest(), outgoingOrigin()); - - Page* oldPage = m_frame->page(); - if (!oldPage) - return 0; - - Page* page = oldPage->chrome()->createWindow(m_frame, requestWithReferrer, features); - if (!page) - return 0; - - Frame* frame = page->mainFrame(); - if (request.frameName() != "_blank") - frame->tree()->setName(request.frameName()); - - page->chrome()->setToolbarsVisible(features.toolBarVisible || features.locationBarVisible); - page->chrome()->setStatusbarVisible(features.statusBarVisible); - page->chrome()->setScrollbarsVisible(features.scrollbarsVisible); - page->chrome()->setMenubarVisible(features.menuBarVisible); - page->chrome()->setResizable(features.resizable); - - // 'x' and 'y' specify the location of the window, while 'width' and 'height' - // specify the size of the page. We can only resize the window, so - // adjust for the difference between the window size and the page size. - - FloatRect windowRect = page->chrome()->windowRect(); - FloatSize pageSize = page->chrome()->pageRect().size(); - if (features.xSet) - windowRect.setX(features.x); - if (features.ySet) - windowRect.setY(features.y); - if (features.widthSet) - windowRect.setWidth(features.width + (windowRect.width() - pageSize.width())); - if (features.heightSet) - windowRect.setHeight(features.height + (windowRect.height() - pageSize.height())); - page->chrome()->setWindowRect(windowRect); - - page->chrome()->show(); - - created = true; - return frame; -} - bool FrameLoader::canHandleRequest(const ResourceRequest& request) { return m_client->canHandleRequest(request); } -void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool userGesture, bool refresh) +void FrameLoader::changeLocation(const KURL& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool refresh) { RefPtr<Frame> protect(m_frame); ResourceRequest request(url, referrer, refresh ? ReloadIgnoringCacheData : UseProtocolCachePolicy); -#ifdef ANDROID_USER_GESTURE - request.setUserGesture(userGesture); -#endif - if (m_frame->script()->executeIfJavaScriptURL(request.url(), userGesture)) - return; + urlSelected(request, "_self", 0, lockHistory, lockBackForwardList, SendReferrer, ReplaceDocumentIfJavaScriptURL); +} - urlSelected(request, "_self", 0, lockHistory, lockBackForwardList, userGesture, SendReferrer); +void FrameLoader::urlSelected(const KURL& url, const String& passedTarget, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, ReferrerPolicy referrerPolicy) +{ + urlSelected(ResourceRequest(url), passedTarget, triggeringEvent, lockHistory, lockBackForwardList, referrerPolicy, DoNotReplaceDocumentIfJavaScriptURL); } -void FrameLoader::urlSelected(const ResourceRequest& request, const String& passedTarget, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, bool userGesture, ReferrerPolicy referrerPolicy) +// The shouldReplaceDocumentIfJavaScriptURL parameter will go away when the FIXME to eliminate the +// corresponding parameter from ScriptController::executeIfJavaScriptURL() is addressed. +void FrameLoader::urlSelected(const ResourceRequest& request, const String& passedTarget, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, ReferrerPolicy referrerPolicy, ShouldReplaceDocumentIfJavaScriptURL shouldReplaceDocumentIfJavaScriptURL) { ASSERT(!m_suppressOpenerInNewFrame); - if (m_frame->script()->executeIfJavaScriptURL(request.url(), userGesture, false)) + if (m_frame->script()->executeIfJavaScriptURL(request.url(), shouldReplaceDocumentIfJavaScriptURL)) return; String target = passedTarget; @@ -366,125 +311,42 @@ void FrameLoader::urlSelected(const ResourceRequest& request, const String& pass m_suppressOpenerInNewFrame = false; } -bool FrameLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName) +void FrameLoader::submitForm(PassRefPtr<FormSubmission> submission) { - // Support for <frame src="javascript:string"> - KURL scriptURL; - KURL url; - if (protocolIsJavaScript(urlString)) { - scriptURL = completeURL(urlString); // completeURL() encodes the URL. - url = blankURL(); - } else - url = completeURL(urlString); + ASSERT(submission->method() == FormSubmission::PostMethod || submission->method() == FormSubmission::GetMethod); - Frame* frame = ownerElement->contentFrame(); - if (frame) - frame->redirectScheduler()->scheduleLocationChange(url.string(), m_outgoingReferrer, true, true, isProcessingUserGesture()); - else - frame = loadSubframe(ownerElement, url, frameName, m_outgoingReferrer); - - if (!frame) - return false; - - if (!scriptURL.isEmpty()) - frame->script()->executeIfJavaScriptURL(scriptURL); - - return true; -} - -Frame* FrameLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer) -{ - bool allowsScrolling = true; - int marginWidth = -1; - int marginHeight = -1; - if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) { - HTMLFrameElementBase* o = static_cast<HTMLFrameElementBase*>(ownerElement); - allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff; - marginWidth = o->getMarginWidth(); - marginHeight = o->getMarginHeight(); - } - - if (!SecurityOrigin::canLoad(url, referrer, 0)) { - FrameLoader::reportLocalLoadFailed(m_frame, url.string()); - return 0; - } - - bool hideReferrer = SecurityOrigin::shouldHideReferrer(url, referrer); - RefPtr<Frame> frame = m_client->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, allowsScrolling, marginWidth, marginHeight); - - if (!frame) { - checkCallImplicitClose(); - return 0; - } - - // All new frames will have m_isComplete set to true at this point due to synchronously loading - // an empty document in FrameLoader::init(). But many frames will now be starting an - // asynchronous load of url, so we set m_isComplete to false and then check if the load is - // actually completed below. (Note that we set m_isComplete to false even for synchronous - // loads, so that checkCompleted() below won't bail early.) - // FIXME: Can we remove this entirely? m_isComplete normally gets set to false when a load is committed. - frame->loader()->m_isComplete = false; - - RenderObject* renderer = ownerElement->renderer(); - FrameView* view = frame->view(); - if (renderer && renderer->isWidget() && view) - toRenderWidget(renderer)->setWidget(view); - - checkCallImplicitClose(); - - // Some loads are performed synchronously (e.g., about:blank and loads - // cancelled by returning a null ResourceRequest from requestFromDelegate). - // In these cases, the synchronous load would have finished - // before we could connect the signals, so make sure to send the - // completed() signal for the child by hand and mark the load as being - // complete. - // FIXME: In this case the Frame will have finished loading before - // it's being added to the child list. It would be a good idea to - // create the child first, then invoke the loader separately. - if (frame->loader()->state() == FrameStateComplete) - frame->loader()->checkCompleted(); - - return frame.get(); -} - -void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<FormData> formData, - const String& target, const String& contentType, const String& boundary, - bool lockHistory, PassRefPtr<Event> event, PassRefPtr<FormState> formState) -{ - ASSERT(action); - ASSERT(strcmp(action, "GET") == 0 || strcmp(action, "POST") == 0); - ASSERT(formData); - ASSERT(formState); - ASSERT(formState->sourceFrame() == m_frame); + // FIXME: Find a good spot for these. + ASSERT(submission->data()); + ASSERT(submission->state()); + ASSERT(submission->state()->sourceFrame() == m_frame); if (!m_frame->page()) return; - KURL u = completeURL(url.isNull() ? "" : url); - if (u.isEmpty()) + if (submission->action().isEmpty()) return; - if (isDocumentSandboxed(SandboxForms)) + if (isDocumentSandboxed(m_frame, SandboxForms)) return; - if (protocolIsJavaScript(u)) { + if (protocolIsJavaScript(submission->action())) { m_isExecutingJavaScriptFormAction = true; - m_frame->script()->executeIfJavaScriptURL(u, false, false); + m_frame->script()->executeIfJavaScriptURL(submission->action(), DoNotReplaceDocumentIfJavaScriptURL); m_isExecutingJavaScriptFormAction = false; return; } - FrameLoadRequest frameRequest; -#ifdef ANDROID_USER_GESTURE - frameRequest.resourceRequest().setUserGesture(isProcessingUserGesture()); -#endif - - String targetOrBaseTarget = target.isEmpty() ? m_frame->document()->baseTarget() : target; - Frame* targetFrame = findFrameForNavigation(targetOrBaseTarget); + Frame* targetFrame = m_frame->tree()->find(submission->target()); + if (!shouldAllowNavigation(targetFrame)) + return; if (!targetFrame) { + if (!DOMWindow::allowPopUp(m_frame) && !isProcessingUserGesture()) + return; + targetFrame = m_frame; - frameRequest.setFrameName(targetOrBaseTarget); - } + } else + submission->clearTarget(); + if (!targetFrame->page()) return; @@ -501,39 +363,22 @@ void FrameLoader::submitForm(const char* action, const String& url, PassRefPtr<F // needed any more now that we reset m_submittedFormURL on each mouse or key down event. if (m_frame->tree()->isDescendantOf(targetFrame)) { - if (m_submittedFormURL == u) + if (m_submittedFormURL == submission->action()) return; - m_submittedFormURL = u; + m_submittedFormURL = submission->action(); } - formData->generateFiles(m_frame->page()->chrome()->client()); - - if (!m_outgoingReferrer.isEmpty()) - frameRequest.resourceRequest().setHTTPReferrer(m_outgoingReferrer); - - if (strcmp(action, "GET") == 0) - u.setQuery(formData->flattenToString()); - else { - frameRequest.resourceRequest().setHTTPMethod("POST"); - frameRequest.resourceRequest().setHTTPBody(formData); - - // construct some user headers if necessary - if (contentType.isNull() || contentType == "application/x-www-form-urlencoded") - frameRequest.resourceRequest().setHTTPContentType(contentType); - else // contentType must be "multipart/form-data" - frameRequest.resourceRequest().setHTTPContentType(contentType + "; boundary=" + boundary); - } - - frameRequest.resourceRequest().setURL(u); - addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin()); + submission->data()->generateFiles(m_frame->document()); + submission->setReferrer(m_outgoingReferrer); + submission->setOrigin(outgoingOrigin()); - targetFrame->redirectScheduler()->scheduleFormSubmission(frameRequest, lockHistory, event, formState); + targetFrame->navigationScheduler()->scheduleFormSubmission(submission); } void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy, DatabasePolicy databasePolicy) { - if (m_frame->document() && m_frame->document()->tokenizer()) - m_frame->document()->tokenizer()->stopParsing(); + if (m_frame->document() && m_frame->document()->parser()) + m_frame->document()->parser()->stopParsing(); if (unloadEventPolicy != UnloadEventPolicyNone) { if (m_frame->document()) { @@ -541,14 +386,23 @@ void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy, DatabasePolic Node* currentFocusedNode = m_frame->document()->focusedNode(); if (currentFocusedNode) currentFocusedNode->aboutToUnload(); - m_unloadEventBeingDispatched = true; + m_pageDismissalEventBeingDispatched = true; if (m_frame->domWindow()) { if (unloadEventPolicy == UnloadEventPolicyUnloadAndPageHide) m_frame->domWindow()->dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, m_frame->document()->inPageCache()), m_frame->document()); - if (!m_frame->document()->inPageCache()) + if (!m_frame->document()->inPageCache()) { m_frame->domWindow()->dispatchEvent(Event::create(eventNames().unloadEvent, false, false), m_frame->domWindow()->document()); + + if (m_provisionalDocumentLoader) { + DocumentLoadTiming* timing = m_provisionalDocumentLoader->timing(); + ASSERT(timing->navigationStart); + ASSERT(!timing->unloadEventEnd); + timing->unloadEventEnd = currentTime(); + ASSERT(timing->unloadEventEnd >= timing->navigationStart); + } + } } - m_unloadEventBeingDispatched = false; + m_pageDismissalEventBeingDispatched = false; if (m_frame->document()) m_frame->document()->updateStyleIfNeeded(); m_wasUnloadEventEmitted = true; @@ -558,7 +412,7 @@ void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy, DatabasePolic // Dispatching the unload event could have made m_frame->document() null. if (m_frame->document() && !m_frame->document()->inPageCache()) { // Don't remove event listeners from a transitional empty document (see bug 28716 for more information). - bool keepEventListeners = m_isDisplayingInitialEmptyDocument && m_provisionalDocumentLoader + bool keepEventListeners = m_stateMachine.isDisplayingInitialEmptyDocument() && m_provisionalDocumentLoader && m_frame->document()->securityOrigin()->isSecureTransitionTo(m_provisionalDocumentLoader->url()); if (!keepEventListeners) @@ -578,8 +432,12 @@ void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy, DatabasePolic m_workingURL = KURL(); if (Document* doc = m_frame->document()) { - if (DocLoader* docLoader = doc->docLoader()) - cache()->loader()->cancelRequests(docLoader); + // FIXME: HTML5 doesn't tell us to set the state to complete when aborting, but we do anyway to match legacy behavior. + // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10537 + doc->setReadyState(Document::Complete); + + if (CachedResourceLoader* cachedResourceLoader = doc->cachedResourceLoader()) + cache()->loader()->cancelRequests(cachedResourceLoader); #if ENABLE(DATABASE) if (databasePolicy == DatabasePolicyStop) @@ -589,11 +447,8 @@ void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy, DatabasePolic #endif } - // tell all subframes to stop as well - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - child->loader()->stopLoading(unloadEventPolicy); - - m_frame->redirectScheduler()->cancel(); + // FIXME: This will cancel redirection timer, which really needs to be restarted when restoring the frame from b/f cache. + m_frame->navigationScheduler()->cancel(); } void FrameLoader::stop() @@ -602,8 +457,8 @@ 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()->tokenizer()) - m_frame->document()->tokenizer()->stopParsing(); + if (m_frame->document()->parser()) + m_frame->document()->parser()->stopParsing(); m_frame->document()->finishParsing(); if (m_iconLoader) @@ -648,13 +503,13 @@ KURL FrameLoader::iconURL() bool FrameLoader::didOpenURL(const KURL& url) { - if (m_frame->redirectScheduler()->redirectScheduledDuringLoad()) { + if (m_frame->navigationScheduler()->redirectScheduledDuringLoad()) { // A redirect was scheduled before the document was created. // This can happen when one frame changes another frame's location. return false; } - m_frame->redirectScheduler()->cancel(); + m_frame->navigationScheduler()->cancel(); m_frame->editor()->clearLastEditCommand(); m_isComplete = false; @@ -664,9 +519,11 @@ bool FrameLoader::didOpenURL(const KURL& url) // If we are still in the process of initializing an empty document then // its frame is not in a consistent state for rendering, so avoid setJSStatusBarText // since it may cause clients to attempt to render the frame. - if (!m_creatingInitialEmptyDocument) { - m_frame->setJSStatusBarText(String()); - m_frame->setJSDefaultStatusBarText(String()); + if (!m_stateMachine.creatingInitialEmptyDocument()) { + if (DOMWindow* window = m_frame->existingDOMWindow()) { + window->setStatus(String()); + window->setDefaultStatus(String()); + } } m_URL = url; if (m_URL.protocolInHTTPFamily() && !m_URL.host().isEmpty() && m_URL.path().isEmpty()) @@ -684,13 +541,14 @@ void FrameLoader::didExplicitOpen() m_didCallImplicitClose = false; // Calling document.open counts as committing the first real document load. - m_committedFirstRealDocumentLoad = true; + if (!m_stateMachine.committedFirstRealDocumentLoad()) + m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit); // Prevent window.open(url) -- eg window.open("about:blank") -- from blowing away results // from a subsequent window.document.open / window.document.write call. // Canceling redirection here works for all cases because document.open // implicitly precedes document.write. - m_frame->redirectScheduler()->cancel(); + m_frame->navigationScheduler()->cancel(); if (m_frame->document()->url() != blankURL()) m_URL = m_frame->document()->url(); } @@ -698,7 +556,7 @@ void FrameLoader::didExplicitOpen() void FrameLoader::cancelAndClear() { - m_frame->redirectScheduler()->cancel(); + m_frame->navigationScheduler()->cancel(); if (!m_isComplete) closeURL(); @@ -707,14 +565,6 @@ void FrameLoader::cancelAndClear() m_frame->script()->updatePlatformScriptObjects(); } -void FrameLoader::replaceDocument(const String& html) -{ - stopAllLoaders(); - begin(m_URL, true, m_frame->document()->securityOrigin()); - write(html); - end(); -} - void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects, bool clearFrameView) { m_frame->editor()->clear(); @@ -737,7 +587,7 @@ void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects, boo // Do this after detaching the document so that the unload event works. if (clearWindowProperties) { m_frame->clearDOMWindow(); - m_frame->script()->clearWindowShell(); + m_frame->script()->clearWindowShell(m_frame->document()->inPageCache()); } m_frame->selection()->clear(); @@ -745,34 +595,30 @@ void FrameLoader::clear(bool clearWindowProperties, bool clearScriptObjects, boo if (clearFrameView && m_frame->view()) m_frame->view()->clear(); - m_frame->setSelectionGranularity(CharacterGranularity); - // Do not drop the document before the ScriptController and view are cleared // as some destructors might still try to access the document. m_frame->setDocument(0); - m_decoder = 0; + writer()->clear(); - m_containsPlugIns = false; + m_subframeLoader.clear(); if (clearScriptObjects) m_frame->script()->clearScriptObjects(); - m_frame->redirectScheduler()->clear(); + m_frame->navigationScheduler()->clear(); m_checkTimer.stop(); m_shouldCallCheckCompleted = false; m_shouldCallCheckLoadComplete = false; - m_receivedData = false; - m_isDisplayingInitialEmptyDocument = false; - - if (!m_encodingWasChosenByUser) - m_encoding = String(); + if (m_stateMachine.isDisplayingInitialEmptyDocument() && m_stateMachine.committedFirstRealDocumentLoad()) + m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad); } void FrameLoader::receivedFirstData() { - begin(m_workingURL, false); + writer()->begin(m_workingURL, false); + writer()->setDocumentWasLoadedAsPartOfNavigation(); dispatchDidCommitLoad(); dispatchDidClearWindowObjectsInAllWorlds(); @@ -800,196 +646,55 @@ void FrameLoader::receivedFirstData() else url = m_frame->document()->completeURL(url).string(); - m_frame->redirectScheduler()->scheduleRedirect(delay, url); -} - -const String& FrameLoader::responseMIMEType() const -{ - return m_responseMIMEType; + m_frame->navigationScheduler()->scheduleRedirect(delay, url); } -void FrameLoader::setResponseMIMEType(const String& type) +void FrameLoader::setURL(const KURL& url) { - m_responseMIMEType = type; -} - -void FrameLoader::begin() -{ - begin(KURL()); -} - -void FrameLoader::begin(const KURL& url, bool dispatch, SecurityOrigin* origin) -{ - // We need to take a reference to the security origin because |clear| - // might destroy the document that owns it. - RefPtr<SecurityOrigin> forcedSecurityOrigin = origin; - - 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 if (!m_client->hasHTMLView()) - document = PlaceholderDocument::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(); - - m_needsClear = true; - m_isComplete = false; - m_didCallImplicitClose = false; - m_isLoadingMainResource = true; - m_isDisplayingInitialEmptyDocument = m_creatingInitialEmptyDocument; - KURL ref(url); ref.setUser(String()); ref.setPass(String()); ref.removeFragmentIdentifier(); m_outgoingReferrer = ref.string(); m_URL = url; +} - document->setURL(m_URL); - m_frame->setDocument(document); +void FrameLoader::didBeginDocument(bool dispatch) +{ + m_needsClear = true; + m_isComplete = false; + m_didCallImplicitClose = false; + m_isLoadingMainResource = true; + m_frame->document()->setReadyState(Document::Loading); if (m_pendingStateObject) { - document->statePopped(m_pendingStateObject.get()); + m_frame->document()->statePopped(m_pendingStateObject.get()); m_pendingStateObject.clear(); } - - if (m_decoder) - document->setDecoder(m_decoder.get()); - if (forcedSecurityOrigin) - document->setSecurityOrigin(forcedSecurityOrigin.get()); - - m_frame->domWindow()->setURL(document->url()); - m_frame->domWindow()->setSecurityOrigin(document->securityOrigin()); if (dispatch) dispatchDidClearWindowObjectsInAllWorlds(); - + updateFirstPartyForCookies(); - Settings* settings = document->settings(); - document->docLoader()->setAutoLoadImages(settings && settings->loadsImagesAutomatically()); + Settings* settings = m_frame->document()->settings(); + m_frame->document()->cachedResourceLoader()->setAutoLoadImages(settings && settings->loadsImagesAutomatically()); #ifdef ANDROID_BLOCK_NETWORK_IMAGE - document->docLoader()->setBlockNetworkImage(settings && settings->blockNetworkImage()); + m_frame->document()->cachedResourceLoader()->setBlockNetworkImage(settings && settings->blockNetworkImage()); #endif if (m_documentLoader) { String dnsPrefetchControl = m_documentLoader->response().httpHeaderField("X-DNS-Prefetch-Control"); if (!dnsPrefetchControl.isEmpty()) - document->parseDNSPrefetchControlHeader(dnsPrefetchControl); + m_frame->document()->parseDNSPrefetchControlHeader(dnsPrefetchControl); } history()->restoreDocumentState(); - - document->implicitOpen(); - - if (m_frame->view() && m_client->hasHTMLView()) - m_frame->view()->setContentsSize(IntSize()); -} - -void FrameLoader::write(const char* str, int len, bool flush) -{ - if (len == 0 && !flush) - return; - - if (len == -1) - len = strlen(str); - - Tokenizer* tokenizer = m_frame->document()->tokenizer(); - if (tokenizer && tokenizer->wantsRawData()) { - if (len > 0) - tokenizer->writeRawData(str, len); - return; - } - - if (!m_decoder) { - if (Settings* settings = m_frame->settings()) { - m_decoder = TextResourceDecoder::create(m_responseMIMEType, - settings->defaultTextEncodingName(), - settings->usesEncodingDetector()); - Frame* parentFrame = m_frame->tree()->parent(); - // 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); - } - m_frame->document()->setDecoder(m_decoder.get()); - } - - String decoded = m_decoder->decode(str, len); - if (flush) - decoded += m_decoder->flush(); - if (decoded.isEmpty()) - return; - - if (!m_receivedData) { - m_receivedData = true; - if (m_decoder->encoding().usesVisualOrdering()) - m_frame->document()->setVisuallyOrdered(); - m_frame->document()->recalcStyle(Node::Force); - } - - if (tokenizer) { - ASSERT(!tokenizer->wantsRawData()); - tokenizer->write(decoded, true); - } } -void FrameLoader::write(const String& str) -{ - if (str.isNull()) - return; - - if (!m_receivedData) { - m_receivedData = true; - m_frame->document()->setParseMode(Document::Strict); - } - - if (Tokenizer* tokenizer = m_frame->document()->tokenizer()) - tokenizer->write(str, true); -} - -void FrameLoader::end() +void FrameLoader::didEndDocument() { m_isLoadingMainResource = false; - endIfNotLoadingMainResource(); -} - -void FrameLoader::endIfNotLoadingMainResource() -{ - if (m_isLoadingMainResource || !m_frame->page() || !m_frame->document()) - return; - - // http://bugs.webkit.org/show_bug.cgi?id=10854 - // The frame's last ref may be removed and it can be deleted by checkCompleted(), - // so we'll add a protective refcount - RefPtr<Frame> protector(m_frame); - - // make sure nothing's left in there - write(0, 0, true); - m_frame->document()->finishParsing(); } void FrameLoader::iconLoadDecisionAvailable() @@ -1059,7 +764,7 @@ void FrameLoader::startIconLoader() // This is either a reload or the icon database said "yes, load the icon", so kick off the load! if (!m_iconLoader) - m_iconLoader.set(IconLoader::create(m_frame).release()); + m_iconLoader = IconLoader::create(m_frame); m_iconLoader->startLoading(); } @@ -1074,7 +779,7 @@ void FrameLoader::commitIconURLToIconDatabase(const KURL& icon) void FrameLoader::finishedParsing() { - if (m_creatingInitialEmptyDocument) + if (m_stateMachine.creatingInitialEmptyDocument()) return; m_frame->injectUserScripts(InjectAtDocumentEnd); @@ -1143,13 +848,18 @@ void FrameLoader::checkCompleted() if (numRequests(m_frame->document())) return; + // Still waiting for elements that don't go through a FrameLoader? + if (m_frame->document()->isDelayingLoadEvent()) + return; + // OK, completed. m_isComplete = true; + m_frame->document()->setReadyState(Document::Complete); RefPtr<Frame> protect(m_frame); checkCallImplicitClose(); // if we didn't do it before - m_frame->redirectScheduler()->startTimer(); + m_frame->navigationScheduler()->startTimer(); completed(); if (m_frame->page()) @@ -1191,7 +901,7 @@ void FrameLoader::scheduleCheckLoadComplete() void FrameLoader::checkCallImplicitClose() { - if (m_didCallImplicitClose || m_frame->document()->parsing()) + if (m_didCallImplicitClose || m_frame->document()->parsing() || m_frame->document()->isDelayingLoadEvent()) return; if (!allChildrenAreComplete()) @@ -1227,7 +937,7 @@ void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, // 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->childItemWithTarget(childFrame->tree()->name()); + HistoryItem* childItem = parentItem->childItemWithTarget(childFrame->tree()->uniqueName()); if (childItem) { // Use the original URL to ensure we get all the side-effects, such as // onLoad handlers, of any redirects that happened. An example of where @@ -1239,17 +949,13 @@ void FrameLoader::loadURLIntoChildFrame(const KURL& url, const String& referer, } #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size - RefPtr<Archive> subframeArchive = activeDocumentLoader()->popArchiveForSubframe(childFrame->tree()->name()); + RefPtr<Archive> subframeArchive = activeDocumentLoader()->popArchiveForSubframe(childFrame->tree()->uniqueName()); if (subframeArchive) childFrame->loader()->loadArchive(subframeArchive.release()); else #endif -#ifdef ANDROID_USER_GESTURE - childFrame->loader()->loadURL(workingURL, referer, String(), false, childLoadType, 0, 0, false); -#else childFrame->loader()->loadURL(workingURL, referer, String(), false, childLoadType, 0, 0); -#endif } #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size @@ -1275,74 +981,6 @@ void FrameLoader::loadArchive(PassRefPtr<Archive> prpArchive) } #endif -String FrameLoader::encoding() const -{ - if (m_encodingWasChosenByUser && !m_encoding.isEmpty()) - return m_encoding; - if (m_decoder && m_decoder->encoding().isValid()) - return m_decoder->encoding().name(); - Settings* settings = m_frame->settings(); - return settings ? settings->defaultTextEncodingName() : String(); -} - -bool FrameLoader::requestObject(RenderPart* renderer, const String& url, const AtomicString& frameName, - const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues) -{ - if (url.isEmpty() && mimeType.isEmpty()) - return false; - - if (!m_frame->script()->xssAuditor()->canLoadObject(url)) { - // It is unsafe to honor the request for this object. - return false; - } - - KURL completedURL; - if (!url.isEmpty()) - completedURL = completeURL(url); - - bool useFallback; - if (shouldUsePlugin(completedURL, mimeType, renderer->hasFallbackContent(), useFallback)) { - Settings* settings = m_frame->settings(); - if (!m_client->allowPlugins(settings && settings->arePluginsEnabled()) - || (!settings->isJavaEnabled() && MIMETypeRegistry::isJavaAppletMIMEType(mimeType))) - return false; - if (isDocumentSandboxed(SandboxPlugins)) - return false; - return loadPlugin(renderer, completedURL, mimeType, paramNames, paramValues, useFallback); - } - - ASSERT(renderer->node()->hasTagName(objectTag) || renderer->node()->hasTagName(embedTag)); - HTMLPlugInElement* element = static_cast<HTMLPlugInElement*>(renderer->node()); - - // If the plug-in element already contains a subframe, requestFrame will re-use it. Otherwise, - // it will create a new frame and set it as the RenderPart's widget, causing what was previously - // in the widget to be torn down. - return requestFrame(element, completedURL, frameName); -} - -bool FrameLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback) -{ - if (m_client->shouldUsePluginDocument(mimeType)) { - useFallback = false; - return true; - } - - // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that - // can handle TIFF (which QuickTime can also handle) they probably intended to override QT. - if (m_frame->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) { - const PluginData* pluginData = m_frame->page()->pluginData(); - String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String(); - if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false)) - return true; - } - - ObjectContentType objectType = m_client->objectContentType(url, mimeType); - // If an object's content can't be handled and it has no fallback, let - // it be handled as a plugin to show the broken plugin icon. - useFallback = objectType == ObjectContentNone && hasFallback; - return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin; -} - ObjectContentType FrameLoader::defaultObjectContentType(const KURL& url, const String& mimeTypeIn) { String mimeType = mimeTypeIn; @@ -1356,7 +994,7 @@ ObjectContentType FrameLoader::defaultObjectContentType(const KURL& url, const S if (MIMETypeRegistry::isSupportedImageMIMEType(mimeType)) return WebCore::ObjectContentImage; -#if !PLATFORM(MAC) && !PLATFORM(CHROMIUM) // Mac has no PluginDatabase, nor does Chromium +#if !PLATFORM(MAC) && !PLATFORM(CHROMIUM) && !PLATFORM(EFL) // Mac has no PluginDatabase, nor does Chromium or EFL if (PluginDatabase::installedPlugins()->isMIMETypeRegistered(mimeType)) return WebCore::ObjectContentNetscapePlugin; #endif @@ -1367,50 +1005,6 @@ ObjectContentType FrameLoader::defaultObjectContentType(const KURL& url, const S return WebCore::ObjectContentNone; } -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) -{ - RefPtr<Widget> widget; - - if (renderer && !useFallback) { - HTMLPlugInElement* element = toPlugInElement(renderer->node()); - - if (!SecurityOrigin::canLoad(url, String(), frame()->document())) { - FrameLoader::reportLocalLoadFailed(m_frame, url.string()); - return false; - } - - checkIfRunInsecureContent(m_frame->document()->securityOrigin(), url); - - widget = m_client->createPlugin(IntSize(renderer->contentWidth(), renderer->contentHeight()), - element, url, paramNames, paramValues, mimeType, - m_frame->document()->isPluginDocument() && !m_containsPlugIns); - if (widget) { - renderer->setWidget(widget); - m_containsPlugIns = true; - } - } - - return widget != 0; -} - String FrameLoader::outgoingReferrer() const { return m_outgoingReferrer; @@ -1426,7 +1020,7 @@ bool FrameLoader::isMixedContent(SecurityOrigin* context, const KURL& url) if (context->protocol() != "https") return false; // We only care about HTTPS security origins. - if (!url.isValid() || url.protocolIs("https") || url.protocolIs("about") || url.protocolIs("data")) + if (!url.isValid() || SchemeRegistry::shouldTreatURLSchemeAsSecure(url.protocol())) return false; // Loading these protocols is secure. return true; @@ -1437,8 +1031,7 @@ void FrameLoader::checkIfDisplayInsecureContent(SecurityOrigin* context, const K if (!isMixedContent(context, url)) return; - String message = String::format("The page at %s displayed insecure content from %s.\n", - m_URL.string().utf8().data(), url.string().utf8().data()); + String message = makeString("The page at ", m_URL.string(), " displayed insecure content from ", url.string(), ".\n"); m_frame->domWindow()->console()->addMessage(HTMLMessageSource, LogMessageType, WarningMessageLevel, message, 1, String()); m_client->didDisplayInsecureContent(); @@ -1449,8 +1042,7 @@ void FrameLoader::checkIfRunInsecureContent(SecurityOrigin* context, const KURL& if (!isMixedContent(context, url)) return; - String message = String::format("The page at %s ran insecure content from %s.\n", - m_URL.string().utf8().data(), url.string().utf8().data()); + String message = makeString("The page at ", m_URL.string(), " ran insecure content from ", url.string(), ".\n"); m_frame->domWindow()->console()->addMessage(HTMLMessageSource, LogMessageType, WarningMessageLevel, message, 1, String()); m_client->didRunInsecureContent(context); @@ -1475,6 +1067,7 @@ void FrameLoader::setOpener(Frame* opener) } } +// FIXME: This does not belong in FrameLoader! void FrameLoader::handleFallbackContent() { HTMLFrameOwnerElement* owner = m_frame->ownerElement(); @@ -1489,17 +1082,18 @@ void FrameLoader::provisionalLoadStarted() if (!m_frame->tree()->parent()) android::TimeCounter::reset(); #endif - m_firstLayoutDone = false; - m_frame->redirectScheduler()->cancel(true); + if (m_stateMachine.firstLayoutDone()) + m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad); + m_frame->navigationScheduler()->cancel(true); m_client->provisionalLoadStarted(); } bool FrameLoader::isProcessingUserGesture() { Frame* frame = m_frame->tree()->top(); - if (!frame->script()->canExecuteScripts()) + if (!frame->script()->canExecuteScripts(NotAboutToExecuteScript)) return true; // If JavaScript is disabled, a user gesture must have initiated the navigation. - return frame->script()->processingUserGesture(mainThreadNormalWorld()); // FIXME: Use pageIsProcessingUserGesture. + return ScriptController::processingUserGesture(); // FIXME: Use pageIsProcessingUserGesture. } void FrameLoader::resetMultipleFormSubmissionProtection() @@ -1507,20 +1101,10 @@ void FrameLoader::resetMultipleFormSubmissionProtection() m_submittedFormURL = KURL(); } -void FrameLoader::setEncoding(const String& name, bool userChosen) +void FrameLoader::willSetEncoding() { if (!m_workingURL.isEmpty()) receivedFirstData(); - m_encoding = name; - m_encodingWasChosenByUser = userChosen; -} - -void FrameLoader::addData(const char* bytes, int length) -{ - ASSERT(m_workingURL.isEmpty()); - ASSERT(m_frame->document()); - ASSERT(m_frame->document()->parsing()); - write(bytes, length); } #if ENABLE(WML) @@ -1534,208 +1118,6 @@ static inline bool frameContainsWMLContent(Frame* frame) } #endif -bool FrameLoader::canCachePageContainingThisFrame() -{ - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) { - if (!child->loader()->canCachePageContainingThisFrame()) - return false; - } - - return m_documentLoader - && m_documentLoader->mainDocumentError().isNull() - // FIXME: If we ever change this so that frames with plug-ins will be cached, - // we need to make sure that we don't cache frames that have outstanding NPObjects - // (objects created by the plug-in). Since there is no way to pause/resume a Netscape plug-in, - // they would need to be destroyed and then recreated, and there is no way that we can recreate - // the right NPObjects. See <rdar://problem/5197041> for more information. - && !m_containsPlugIns - && !m_URL.protocolIs("https") - && (!m_frame->domWindow() || !m_frame->domWindow()->hasEventListeners(eventNames().unloadEvent)) -#if ENABLE(DATABASE) - && !m_frame->document()->hasOpenDatabases() -#endif -#if ENABLE(SHARED_WORKERS) - && !SharedWorkerRepository::hasSharedWorkers(m_frame->document()) -#endif - && !m_frame->document()->usingGeolocation() - && history()->currentItem() - && !m_quickRedirectComing - && !m_documentLoader->isLoadingInAPISense() - && !m_documentLoader->isStopping() - && m_frame->document()->canSuspendActiveDOMObjects() -#if ENABLE(OFFLINE_WEB_APPLICATIONS) - // FIXME: We should investigating caching frames that have an associated - // application cache. <rdar://problem/5917899> tracks that work. - && m_documentLoader->applicationCacheHost()->canCacheInPageCache() -#endif -#if ENABLE(WML) - && !frameContainsWMLContent(m_frame) -#endif - && m_client->canCachePage() - ; -} - -bool FrameLoader::canCachePage() -{ -#ifndef NDEBUG - logCanCachePageDecision(); -#endif - - // Cache the page, if possible. - // Don't write to the cache if in the middle of a redirect, since we will want to - // store the final page we end up on. - // No point writing to the cache on a reload or loadSame, since we will just write - // over it again when we leave that page. - // FIXME: <rdar://problem/4886592> - We should work out the complexities of caching pages with frames as they - // are the most interesting pages on the web, and often those that would benefit the most from caching! - FrameLoadType loadType = this->loadType(); - - return !m_frame->tree()->parent() - && canCachePageContainingThisFrame() - && m_frame->page() - && m_frame->page()->backForwardList()->enabled() - && m_frame->page()->backForwardList()->capacity() > 0 - && m_frame->page()->settings()->usesPageCache() - && loadType != FrameLoadTypeReload - && loadType != FrameLoadTypeReloadFromOrigin - && loadType != FrameLoadTypeSame - ; -} - -#ifndef NDEBUG -static String& pageCacheLogPrefix(int indentLevel) -{ - static int previousIndent = -1; - DEFINE_STATIC_LOCAL(String, prefix, ()); - - if (indentLevel != previousIndent) { - previousIndent = indentLevel; - prefix.truncate(0); - for (int i = 0; i < previousIndent; ++i) - prefix += " "; - } - - return prefix; -} - -static void pageCacheLog(const String& prefix, const String& message) -{ - LOG(PageCache, "%s%s", prefix.utf8().data(), message.utf8().data()); -} - -#define PCLOG(...) pageCacheLog(pageCacheLogPrefix(indentLevel), String::format(__VA_ARGS__)) - -void FrameLoader::logCanCachePageDecision() -{ - // Only bother logging for main frames that have actually loaded and have content. - if (m_creatingInitialEmptyDocument) - return; - KURL currentURL = m_documentLoader ? m_documentLoader->url() : KURL(); - if (currentURL.isEmpty()) - return; - - int indentLevel = 0; - PCLOG("--------\n Determining if page can be cached:"); - - bool cannotCache = !logCanCacheFrameDecision(1); - - FrameLoadType loadType = this->loadType(); - do { - if (m_frame->tree()->parent()) - { PCLOG(" -Frame has a parent frame"); cannotCache = true; } - if (!m_frame->page()) { - PCLOG(" -There is no Page object"); - cannotCache = true; - break; - } - if (!m_frame->page()->backForwardList()->enabled()) - { PCLOG(" -The back/forward list is disabled"); cannotCache = true; } - if (!(m_frame->page()->backForwardList()->capacity() > 0)) - { PCLOG(" -The back/forward list has a 0 capacity"); cannotCache = true; } - if (!m_frame->page()->settings()->usesPageCache()) - { PCLOG(" -Page settings says b/f cache disabled"); cannotCache = true; } - if (loadType == FrameLoadTypeReload) - { PCLOG(" -Load type is: Reload"); cannotCache = true; } - if (loadType == FrameLoadTypeReloadFromOrigin) - { PCLOG(" -Load type is: Reload from origin"); cannotCache = true; } - if (loadType == FrameLoadTypeSame) - { PCLOG(" -Load type is: Same"); cannotCache = true; } - } while (false); - - PCLOG(cannotCache ? " Page CANNOT be cached\n--------" : " Page CAN be cached\n--------"); -} - -bool FrameLoader::logCanCacheFrameDecision(int indentLevel) -{ - // Only bother logging for frames that have actually loaded and have content. - if (m_creatingInitialEmptyDocument) - return false; - KURL currentURL = m_documentLoader ? m_documentLoader->url() : KURL(); - if (currentURL.isEmpty()) - return false; - - PCLOG("+---"); - KURL newURL = m_provisionalDocumentLoader ? m_provisionalDocumentLoader->url() : KURL(); - if (!newURL.isEmpty()) - PCLOG(" Determining if frame can be cached navigating from (%s) to (%s):", currentURL.string().utf8().data(), newURL.string().utf8().data()); - else - PCLOG(" Determining if subframe with URL (%s) can be cached:", currentURL.string().utf8().data()); - - bool cannotCache = false; - - do { - if (!m_documentLoader) { - PCLOG(" -There is no DocumentLoader object"); - cannotCache = true; - break; - } - if (!m_documentLoader->mainDocumentError().isNull()) - { PCLOG(" -Main document has an error"); cannotCache = true; } - if (m_containsPlugIns) - { PCLOG(" -Frame contains plugins"); cannotCache = true; } - if (m_URL.protocolIs("https")) - { PCLOG(" -Frame is HTTPS"); cannotCache = true; } - if (m_frame->domWindow() && m_frame->domWindow()->hasEventListeners(eventNames().unloadEvent)) - { PCLOG(" -Frame has an unload event listener"); cannotCache = true; } -#if ENABLE(DATABASE) - if (m_frame->document()->hasOpenDatabases()) - { PCLOG(" -Frame has open database handles"); cannotCache = true; } -#endif -#if ENABLE(SHARED_WORKERS) - if (SharedWorkerRepository::hasSharedWorkers(m_frame->document())) - { PCLOG(" -Frame has associated SharedWorkers"); cannotCache = true; } -#endif - if (m_frame->document()->usingGeolocation()) - { PCLOG(" -Frame uses Geolocation"); cannotCache = true; } - if (!history()->currentItem()) - { PCLOG(" -No current history item"); cannotCache = true; } - if (m_quickRedirectComing) - { PCLOG(" -Quick redirect is coming"); cannotCache = true; } - if (m_documentLoader->isLoadingInAPISense()) - { PCLOG(" -DocumentLoader is still loading in API sense"); cannotCache = true; } - if (m_documentLoader->isStopping()) - { PCLOG(" -DocumentLoader is in the middle of stopping"); cannotCache = true; } - if (!m_frame->document()->canSuspendActiveDOMObjects()) - { PCLOG(" -The document cannot suspect its active DOM Objects"); cannotCache = true; } -#if ENABLE(OFFLINE_WEB_APPLICATIONS) - if (!m_documentLoader->applicationCacheHost()->canCacheInPageCache()) - { PCLOG(" -The DocumentLoader uses an application cache"); cannotCache = true; } -#endif - if (!m_client->canCachePage()) - { PCLOG(" -The client says this frame cannot be cached"); cannotCache = true; } - } while (false); - - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - if (!child->loader()->logCanCacheFrameDecision(indentLevel + 1)) - cannotCache = true; - - PCLOG(cannotCache ? " Frame CANNOT be cached" : " Frame CAN be cached"); - PCLOG("+---"); - - return !cannotCache; -} -#endif - void FrameLoader::updateFirstPartyForCookies() { if (m_frame->tree()->parent()) @@ -1776,7 +1158,10 @@ void FrameLoader::loadInSameDocument(const KURL& url, SerializedScriptValue* sta history()->updateBackForwardListForFragmentScroll(); } + String oldURL; bool hashChange = equalIgnoringFragmentIdentifier(url, m_URL) && url.fragmentIdentifier() != m_URL.fragmentIdentifier(); + oldURL = m_URL; + m_URL = url; history()->updateForSameDocumentNavigation(); @@ -1787,11 +1172,11 @@ void FrameLoader::loadInSameDocument(const KURL& url, SerializedScriptValue* sta // It's important to model this as a load that starts and immediately finishes. // Otherwise, the parent frame may think we never finished loading. started(); - - if (hashChange) { - if (FrameView* view = m_frame->view()) - view->scrollToFragment(m_URL); - } + + // We need to scroll to the fragment whether or not a hash change occurred, since + // the user might have scrolled since the previous navigation. + if (FrameView* view = m_frame->view()) + view->scrollToFragment(m_URL); m_isComplete = false; checkCompleted(); @@ -1803,13 +1188,13 @@ void FrameLoader::loadInSameDocument(const KURL& url, SerializedScriptValue* sta checkLoadComplete(); } - if (stateObject) { - m_frame->document()->statePopped(stateObject); - m_client->dispatchDidPopStateWithinPage(); - } + m_client->dispatchDidNavigateWithinPage(); + + m_frame->document()->statePopped(stateObject ? stateObject : SerializedScriptValue::nullValue()); + m_client->dispatchDidPopStateWithinPage(); if (hashChange) { - m_frame->document()->dispatchWindowEvent(Event::create(eventNames().hashchangeEvent, false, false)); + m_frame->document()->enqueueHashchangeEvent(oldURL, url); m_client->dispatchDidChangeLocationWithinPage(); } @@ -1827,7 +1212,7 @@ void FrameLoader::completed() RefPtr<Frame> protect(m_frame); for (Frame* descendant = m_frame->tree()->traverseNext(m_frame); descendant; descendant = descendant->tree()->traverseNext(m_frame)) - descendant->redirectScheduler()->startTimer(); + descendant->navigationScheduler()->startTimer(); if (Frame* parent = m_frame->tree()->parent()) parent->loader()->checkCompleted(); @@ -1842,11 +1227,6 @@ void FrameLoader::started() frame->loader()->m_isComplete = false; } -bool FrameLoader::containsPlugins() const -{ - return m_containsPlugIns; -} - void FrameLoader::prepareForLoadStart() { if (Page* page = m_frame->page()) @@ -1895,9 +1275,10 @@ void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHis referrer = m_outgoingReferrer; ASSERT(frame()->document()); - if (SecurityOrigin::shouldTreatURLAsLocal(url.string()) && !isFeedWithNestedProtocolInHTTPFamily(url)) { - if (!SecurityOrigin::canLoad(url, String(), frame()->document()) && !SecurityOrigin::canLoad(url, referrer, 0)) { - FrameLoader::reportLocalLoadFailed(m_frame, url.string()); + // FIXME: Should we move the isFeedWithNestedProtocolInHTTPFamily logic inside SecurityOrigin::canDisplay? + if (!isFeedWithNestedProtocolInHTTPFamily(url)) { + if (!frame()->document()->securityOrigin()->canDisplay(url) && !SecurityOrigin::deprecatedCanDisplay(referrer, url)) { + reportLocalLoadFailed(m_frame, url.string()); return; } } @@ -1913,17 +1294,10 @@ void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHis else loadType = FrameLoadTypeStandard; -#ifdef ANDROID_USER_GESTURE - if (request.resourceRequest().httpMethod() == "POST") - loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.get(), request.resourceRequest().getUserGesture()); - else - loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.get(), request.resourceRequest().getUserGesture()); -#else if (request.resourceRequest().httpMethod() == "POST") loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.get()); else loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.get()); -#endif // FIXME: It's possible this targetFrame will not be the same frame that was targeted by the actual // load if frame names have changed. @@ -1931,29 +1305,17 @@ void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHis Frame* targetFrame = sourceFrame->loader()->findFrameForNavigation(request.frameName()); if (targetFrame && targetFrame != sourceFrame) { if (Page* page = targetFrame->page()) -#ifdef ANDROID_USER_GESTURE - page->chrome()->focus(request.resourceRequest().getUserGesture()); -#else page->chrome()->focus(); -#endif } } -#ifdef ANDROID_USER_GESTURE -void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType, - PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState, bool userGesture) -#else void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType, PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState) -#endif { RefPtr<FormState> formState = prpFormState; bool isFormSubmission = formState; ResourceRequest request(newURL); -#ifdef ANDROID_USER_GESTURE - request.setUserGesture(userGesture); -#endif if (!referrer.isEmpty()) { request.setHTTPReferrer(referrer); RefPtr<SecurityOrigin> referrerOrigin = SecurityOrigin::createFromString(referrer); @@ -1968,15 +1330,11 @@ void FrameLoader::loadURL(const KURL& newURL, const String& referrer, const Stri // The search for a target frame is done earlier in the case of form submission. Frame* targetFrame = isFormSubmission ? 0 : findFrameForNavigation(frameName); if (targetFrame && targetFrame != m_frame) { -#ifdef ANDROID_USER_GESTURE - targetFrame->loader()->loadURL(newURL, referrer, String(), lockHistory, newLoadType, event, formState.release(), userGesture); -#else targetFrame->loader()->loadURL(newURL, referrer, String(), lockHistory, newLoadType, event, formState.release()); -#endif return; } - if (m_unloadEventBeingDispatched) + if (m_pageDismissalEventBeingDispatched) return; NavigationAction action(newURL, newLoadType, isFormSubmission, event); @@ -2095,6 +1453,9 @@ void FrameLoader::load(DocumentLoader* newDocumentLoader) void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState) { + // Retain because dispatchBeforeLoadEvent may release the last reference to it. + RefPtr<Frame> protect(m_frame); + ASSERT(m_client->hasWebView()); // Unfortunately the view must be non-nil, this is ultimately due @@ -2102,7 +1463,7 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t ASSERT(m_frame->view()); - if (m_unloadEventBeingDispatched) + if (m_pageDismissalEventBeingDispatched) return; policyChecker()->setLoadType(type); @@ -2154,11 +1515,6 @@ const ResourceRequest& FrameLoader::initialRequest() const return activeDocumentLoader()->originalRequest(); } -void FrameLoader::receivedData(const char* data, int length) -{ - activeDocumentLoader()->receivedData(data, length); -} - bool FrameLoader::willLoadMediaElementURL(KURL& url) { ResourceRequest request(url); @@ -2264,6 +1620,7 @@ static bool canAccessAncestor(const SecurityOrigin* activeSecurityOrigin, Frame* if (!targetFrame) return false; + const bool isLocalActiveOrigin = activeSecurityOrigin->isLocal(); for (Frame* ancestorFrame = targetFrame; ancestorFrame; ancestorFrame = ancestorFrame->tree()->parent()) { Document* ancestorDocument = ancestorFrame->document(); if (!ancestorDocument) @@ -2272,6 +1629,10 @@ static bool canAccessAncestor(const SecurityOrigin* activeSecurityOrigin, Frame* const SecurityOrigin* ancestorSecurityOrigin = ancestorDocument->securityOrigin(); if (activeSecurityOrigin->canAccess(ancestorSecurityOrigin)) return true; + + // Allow file URL descendant navigation even when allowFileAccessFromFileURLs is false. + if (isLocalActiveOrigin && ancestorSecurityOrigin->isLocal()) + return true; } return false; @@ -2295,16 +1656,16 @@ bool FrameLoader::shouldAllowNavigation(Frame* targetFrame) const if (m_frame == targetFrame) return true; - // A sandboxed frame can only navigate itself and its descendants. - if (isDocumentSandboxed(SandboxNavigation) && !targetFrame->tree()->isDescendantOf(m_frame)) - return false; - // Let a frame navigate the top-level window that contains it. This is // important to allow because it lets a site "frame-bust" (escape from a // frame created by another web site). - if (targetFrame == m_frame->tree()->top()) + if (!isDocumentSandboxed(m_frame, SandboxTopNavigation) && targetFrame == m_frame->tree()->top()) return true; + // A sandboxed frame can only navigate itself and its descendants. + if (isDocumentSandboxed(m_frame, SandboxNavigation) && !targetFrame->tree()->isDescendantOf(m_frame)) + return false; + // Let a frame navigate its opener if the opener is a top-level window. if (!targetFrame->tree()->parent() && m_frame->loader()->opener() == targetFrame) return true; @@ -2325,8 +1686,8 @@ bool FrameLoader::shouldAllowNavigation(Frame* targetFrame) const if (settings && !settings->privateBrowsingEnabled()) { Document* targetDocument = targetFrame->document(); // FIXME: this error message should contain more specifics of why the navigation change is not allowed. - String message = String::format("Unsafe JavaScript attempt to initiate a navigation change for frame with URL %s from frame with URL %s.\n", - targetDocument->url().string().utf8().data(), activeDocument->url().string().utf8().data()); + String message = makeString("Unsafe JavaScript attempt to initiate a navigation change for frame with URL ", + targetDocument->url().string(), " from frame with URL ", activeDocument->url().string(), ".\n"); // FIXME: should we print to the console of the activeFrame as well? targetFrame->domWindow()->console()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String()); @@ -2344,7 +1705,7 @@ void FrameLoader::stopLoadingSubframes() void FrameLoader::stopAllLoaders(DatabasePolicy databasePolicy) { ASSERT(!m_frame->document() || !m_frame->document()->inPageCache()); - if (m_unloadEventBeingDispatched) + if (m_pageDismissalEventBeingDispatched) return; // If this method is called from within this method, infinite recursion can occur (3442218). Avoid this. @@ -2400,7 +1761,14 @@ bool FrameLoader::isLoading() const bool FrameLoader::frameHasLoaded() const { - return m_committedFirstRealDocumentLoad || (m_provisionalDocumentLoader && !m_creatingInitialEmptyDocument); + return m_stateMachine.committedFirstRealDocumentLoad() || (m_provisionalDocumentLoader && !m_stateMachine.creatingInitialEmptyDocument()); +} + +void FrameLoader::transferLoadingResourcesFromPage(Page* oldPage) +{ + ASSERT(oldPage != m_frame->page()); + if (isLoading()) + activeDocumentLoader()->transferLoadingResourcesFromPage(oldPage); } void FrameLoader::setDocumentLoader(DocumentLoader* loader) @@ -2478,22 +1846,24 @@ void FrameLoader::markLoadComplete() setState(FrameStateComplete); } -void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) +void FrameLoader::commitProvisionalLoad() { - RefPtr<CachedPage> cachedPage = prpCachedPage; + RefPtr<CachedPage> cachedPage = m_loadingFromCachedPage ? pageCache()->get(history()->provisionalItem()) : 0; RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader; - LOG(PageCache, "WebCoreLoading %s: About to commit provisional load from previous URL '%s' to new URL '%s'", m_frame->tree()->name().string().utf8().data(), m_URL.string().utf8().data(), + LOG(PageCache, "WebCoreLoading %s: About to commit provisional load from previous URL '%s' to new URL '%s'", m_frame->tree()->uniqueName().string().utf8().data(), m_URL.string().utf8().data(), pdl ? pdl->url().string().utf8().data() : "<no provisional DocumentLoader>"); // 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. - cachePageForHistoryItem(history()->currentItem()); + HistoryItem* item = history()->currentItem(); + if (!m_frame->tree()->parent() && PageCache::canCache(m_frame->page()) && !item->isInPageCache()) + pageCache()->add(item, m_frame->page()); if (m_loadType != FrameLoadTypeReplace) closeOldDataSources(); - if (!cachedPage && !m_creatingInitialEmptyDocument) + if (!cachedPage && !m_stateMachine.creatingInitialEmptyDocument()) m_client->makeRepresentation(pdl.get()); transitionToCommitted(cachedPage); @@ -2505,9 +1875,19 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) if (m_sentRedirectNotification) clientRedirectCancelledOrFinished(false); - if (cachedPage && cachedPage->document()) - open(*cachedPage); - else { + if (cachedPage && cachedPage->document()) { + prepareForCachedPageRestore(); + cachedPage->restore(m_frame->page()); + + dispatchDidCommitLoad(); + + // If we have a title let the WebView know about it. + String title = m_documentLoader->title(); + if (!title.isNull()) + m_client->dispatchDidReceiveTitle(title); + + checkCompleted(); + } else { KURL url = pdl->substituteData().responseURL(); if (url.isEmpty()) url = pdl->url(); @@ -2519,7 +1899,7 @@ void FrameLoader::commitProvisionalLoad(PassRefPtr<CachedPage> prpCachedPage) didOpenURL(url); } - LOG(Loading, "WebCoreLoading %s: Finished committing provisional load to URL %s", m_frame->tree()->name().string().utf8().data(), m_URL.string().utf8().data()); + LOG(Loading, "WebCoreLoading %s: Finished committing provisional load to URL %s", m_frame->tree()->uniqueName().string().utf8().data(), m_URL.string().utf8().data()); if (m_loadType == FrameLoadTypeStandard && m_documentLoader->isClientRedirect()) history()->updateForClientRedirect(); @@ -2586,30 +1966,34 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) // Handle adding the URL to the back/forward list. DocumentLoader* dl = m_documentLoader.get(); - String ptitle = dl->title(); switch (m_loadType) { case FrameLoadTypeForward: case FrameLoadTypeBack: case FrameLoadTypeBackWMLDeckNotAccessible: case FrameLoadTypeIndexedBackForward: - if (Page* page = m_frame->page()) { - if (page->backForwardList()) { - history()->updateForBackForwardNavigation(); - - if (history()->currentItem()) - m_pendingStateObject = history()->currentItem()->stateObject(); - - // Create a document view for this document, or used the cached view. - if (cachedPage) { - DocumentLoader* cachedDocumentLoader = cachedPage->documentLoader(); - ASSERT(cachedDocumentLoader); - cachedDocumentLoader->setFrame(m_frame); - m_client->transitionToCommittedFromCachedFrame(cachedPage->cachedMainFrame()); - - } else - m_client->transitionToCommittedForNewPage(); - } + if (m_frame->page()) { + // If the first load within a frame is a navigation within a back/forward list that was attached + // without any of the items being loaded then we need to update the history in a similar manner as + // for a standard load with the exception of updating the back/forward list (<rdar://problem/8091103>). + if (!m_stateMachine.committedFirstRealDocumentLoad()) + history()->updateForStandardLoad(HistoryController::UpdateAllExceptBackForwardList); + + history()->updateForBackForwardNavigation(); + + // For cached pages, CachedFrame::restore will take care of firing the popstate event with the history item's state object + if (history()->currentItem() && !cachedPage) + m_pendingStateObject = history()->currentItem()->stateObject(); + + // Create a document view for this document, or used the cached view. + if (cachedPage) { + DocumentLoader* cachedDocumentLoader = cachedPage->documentLoader(); + ASSERT(cachedDocumentLoader); + cachedDocumentLoader->setFrame(m_frame); + m_client->transitionToCommittedFromCachedFrame(cachedPage->cachedMainFrame()); + + } else + m_client->transitionToCommittedForNewPage(); } break; @@ -2643,26 +2027,19 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage) ASSERT_NOT_REACHED(); } - m_responseMIMEType = dl->responseMIMEType(); + writer()->setMIMEType(dl->responseMIMEType()); // Tell the client we've committed this URL. ASSERT(m_frame->view()); - if (m_creatingInitialEmptyDocument) + if (m_stateMachine.creatingInitialEmptyDocument()) return; - - m_committedFirstRealDocumentLoad = true; + + if (!m_stateMachine.committedFirstRealDocumentLoad()) + m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit); if (!m_client->hasHTMLView()) receivedFirstData(); - else if (cachedPage) { - // For non-cached HTML pages, these methods are called in receivedFirstData(). - dispatchDidCommitLoad(); - - // If we have a title let the WebView know about it. - if (!ptitle.isNull()) - m_client->dispatchDidReceiveTitle(ptitle); - } } void FrameLoader::clientRedirectCancelledOrFinished(bool cancelWithLoadInProgress) @@ -2690,7 +2067,7 @@ void FrameLoader::clientRedirected(const KURL& url, double seconds, double fireD // load as part of the original navigation. If we don't have a document loader, we have // no "original" load on which to base a redirect, so we treat the redirect as a normal load. // Loads triggered by JavaScript form submissions never count as quick redirects. - m_quickRedirectComing = lockBackForwardList && m_documentLoader && !m_isExecutingJavaScriptFormAction; + m_quickRedirectComing = (lockBackForwardList || history()->currentItemShouldBeReplaced()) && m_documentLoader && !m_isExecutingJavaScriptFormAction; } bool FrameLoader::shouldReload(const KURL& currentURL, const KURL& destinationURL) @@ -2723,26 +2100,24 @@ void FrameLoader::closeOldDataSources() m_client->setMainFrameDocumentReady(false); // stop giving out the actual DOMDocument to observers } -void FrameLoader::open(CachedPage& cachedPage) +void FrameLoader::prepareForCachedPageRestore() { ASSERT(!m_frame->tree()->parent()); ASSERT(m_frame->page()); ASSERT(m_frame->page()->mainFrame() == m_frame); - m_frame->redirectScheduler()->cancel(); + m_frame->navigationScheduler()->cancel(); // We still have to close the previous part page. closeURL(); // Delete old status bar messages (if it _was_ activated on last URL). - if (m_frame->script()->canExecuteScripts()) { - m_frame->setJSStatusBarText(String()); - m_frame->setJSDefaultStatusBarText(String()); + if (m_frame->script()->canExecuteScripts(NotAboutToExecuteScript)) { + if (DOMWindow* window = m_frame->existingDOMWindow()) { + window->setStatus(String()); + window->setDefaultStatus(String()); + } } - - cachedPage.restore(m_frame->page()); - - checkCompleted(); } void FrameLoader::open(CachedFrameBase& cachedFrame) @@ -2788,7 +2163,7 @@ void FrameLoader::open(CachedFrameBase& cachedFrame) m_frame->domWindow()->setURL(document->url()); m_frame->domWindow()->setSecurityOrigin(document->securityOrigin()); - m_decoder = document->decoder(); + writer()->setDecoder(document->decoder()); updateFirstPartyForCookies(); @@ -2812,6 +2187,10 @@ void FrameLoader::finishedLoading() dl->setPrimaryLoadComplete(true); m_client->dispatchDidLoadMainResource(dl.get()); checkLoadComplete(); + + DOMWindow* window = m_frame->existingDOMWindow(); + if (window && window->printDeferred()) + window->print(); } bool FrameLoader::isHostedByObjectElement() const @@ -2850,7 +2229,7 @@ void FrameLoader::finishedLoadingDocument(DocumentLoader* loader) { // FIXME: Platforms shouldn't differ here! #if PLATFORM(WIN) || PLATFORM(CHROMIUM) || defined(ANDROID) - if (m_creatingInitialEmptyDocument) + if (m_stateMachine.creatingInitialEmptyDocument()) return; #endif @@ -2876,23 +2255,23 @@ void FrameLoader::finishedLoadingDocument(DocumentLoader* loader) if (!archive) return; - loader->addAllArchiveResources(archive.get()); + // FIXME: The remainder of this function should be in DocumentLoader. + loader->addAllArchiveResources(archive.get()); + ArchiveResource* mainResource = archive->mainResource(); loader->setParsedArchiveData(mainResource->data()); - m_responseMIMEType = mainResource->mimeType(); + writer()->setMIMEType(mainResource->mimeType()); closeURL(); didOpenURL(mainResource->url()); + ASSERT(m_frame->document()); String userChosenEncoding = documentLoader()->overrideEncoding(); bool encodingIsUserChosen = !userChosenEncoding.isNull(); - setEncoding(encodingIsUserChosen ? userChosenEncoding : mainResource->textEncoding(), encodingIsUserChosen); - - ASSERT(m_frame->document()); - - addData(mainResource->data()->data(), mainResource->data()->size()); + writer()->setEncoding(encodingIsUserChosen ? userChosenEncoding : mainResource->textEncoding(), encodingIsUserChosen); + writer()->addData(mainResource->data()->data(), mainResource->data()->size()); #else m_client->finishedLoading(loader); #endif // ARCHIVE @@ -2924,6 +2303,9 @@ bool FrameLoader::subframeIsLoading() const documentLoader = childLoader->provisionalDocumentLoader(); if (documentLoader && documentLoader->isLoadingInAPISense()) return true; + documentLoader = childLoader->policyDocumentLoader(); + if (documentLoader) + return true; } return false; } @@ -3021,7 +2403,7 @@ void FrameLoader::checkLoadCompleteForThisFrame() } if (shouldReset && item) if (Page* page = m_frame->page()) { - page->backForwardList()->goToItem(item.get()); + page->backForward()->setCurrentItem(item.get()); Settings* settings = m_frame->settings(); page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : item.get()); } @@ -3042,11 +2424,12 @@ void FrameLoader::checkLoadCompleteForThisFrame() m_client->forceLayoutForNonHTML(); // If the user had a scroll point, scroll to it, overriding the anchor point if any. - if (Page* page = m_frame->page()) - if ((isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload || m_loadType == FrameLoadTypeReloadFromOrigin) && page->backForwardList()) + if (m_frame->page()) { + if (isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload || m_loadType == FrameLoadTypeReloadFromOrigin) history()->restoreScrollPositionAndViewState(); + } - if (m_creatingInitialEmptyDocument || !m_committedFirstRealDocumentLoad) + if (m_stateMachine.creatingInitialEmptyDocument() || !m_stateMachine.committedFirstRealDocumentLoad()) return; const ResourceError& error = dl->mainDocumentError(); @@ -3103,17 +2486,20 @@ void FrameLoader::continueLoadAfterWillSubmitForm() notifier()->assignIdentifierToInitialRequest(identifier, m_provisionalDocumentLoader.get(), m_provisionalDocumentLoader->originalRequest()); } + ASSERT(!m_provisionalDocumentLoader->timing()->navigationStart); + m_provisionalDocumentLoader->timing()->navigationStart = currentTime(); + if (!m_provisionalDocumentLoader->startLoadingMainResource(identifier)) m_provisionalDocumentLoader->updateLoading(); } void FrameLoader::didFirstLayout() { - if (Page* page = m_frame->page()) - if (isBackForwardLoadType(m_loadType) && page->backForwardList()) - history()->restoreScrollPositionAndViewState(); + if (m_frame->page() && isBackForwardLoadType(m_loadType)) + history()->restoreScrollPositionAndViewState(); - m_firstLayoutDone = true; + if (m_stateMachine.committedFirstRealDocumentLoad() && !m_stateMachine.isDisplayingInitialEmptyDocument() && !m_stateMachine.firstLayoutDone()) + m_stateMachine.advanceTo(FrameLoaderStateMachine::FirstLayoutDone); m_client->dispatchDidFirstLayout(); } @@ -3132,13 +2518,8 @@ void FrameLoader::frameLoadCompleted() // After a canceled provisional load, firstLayoutDone is false. // Reset it to true if we're displaying a page. - if (m_documentLoader) - m_firstLayoutDone = true; -} - -bool FrameLoader::firstLayoutDone() const -{ - return m_firstLayoutDone; + if (m_documentLoader && m_stateMachine.committedFirstRealDocumentLoad() && !m_stateMachine.isDisplayingInitialEmptyDocument() && !m_stateMachine.firstLayoutDone()) + m_stateMachine.advanceTo(FrameLoaderStateMachine::FirstLayoutDone); } void FrameLoader::detachChildren() @@ -3158,6 +2539,7 @@ void FrameLoader::closeAndRemoveChild(Frame* child) child->setView(0); if (child->ownerElement() && child->page()) child->page()->decrementFrameCount(); + // FIXME: The page isn't being destroyed, so it's not right to call a function named pageDestroyed(). child->pageDestroyed(); m_frame->tree()->removeChild(child); @@ -3206,18 +2588,16 @@ String FrameLoader::userAgent(const KURL& url) const return m_client->userAgent(url); } -void FrameLoader::tokenizerProcessedData() -{ - checkCompleted(); -} - void FrameLoader::handledOnloadEvents() { m_client->dispatchDidHandleOnloadEvents(); + + if (documentLoader()) { + documentLoader()->handledOnloadEvents(); #if ENABLE(OFFLINE_WEB_APPLICATIONS) - if (documentLoader()) documentLoader()->applicationCacheHost()->stopDeferringEvents(); #endif + } } void FrameLoader::frameDetached() @@ -3232,9 +2612,12 @@ void FrameLoader::detachFromParent() RefPtr<Frame> protect(m_frame); closeURL(); - stopAllLoaders(); history()->saveScrollPositionAndViewStateToItem(history()->currentItem()); detachChildren(); + // stopAllLoaders() needs to be called after detachChildren(), because detachedChildren() + // will trigger the unload event handlers of any child frames, and those event + // handlers might start a new subresource load in this frame. + stopAllLoaders(); #if ENABLE(INSPECTOR) if (Page* page = m_frame->page()) @@ -3248,6 +2631,7 @@ void FrameLoader::detachFromParent() parent->loader()->scheduleCheckCompleted(); } else { m_frame->setView(0); + // FIXME: The page isn't being destroyed, so it's not right to call a function named pageDestroyed(). m_frame->pageDestroyed(); } } @@ -3286,15 +2670,33 @@ void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadTyp applyUserAgent(request); - if (loadType == FrameLoadTypeReload) { - request.setCachePolicy(ReloadIgnoringCacheData); - request.setHTTPHeaderField("Cache-Control", "max-age=0"); - } else if (loadType == FrameLoadTypeReloadFromOrigin) { + // If we inherit cache policy from a main resource, we use the DocumentLoader's + // original request cache policy for two reasons: + // 1. For POST requests, we mutate the cache policy for the main resource, + // but we do not want this to apply to subresources + // 2. Delegates that modify the cache policy using willSendRequest: should + // not affect any other resources. Such changes need to be done + // per request. + if (!mainResource) { + if (request.isConditional()) + request.setCachePolicy(ReloadIgnoringCacheData); + else if (documentLoader()->isLoadingInAPISense()) + request.setCachePolicy(documentLoader()->originalRequest().cachePolicy()); + else + request.setCachePolicy(UseProtocolCachePolicy); + } else if (loadType == FrameLoadTypeReload || loadType == FrameLoadTypeReloadFromOrigin || request.isConditional()) request.setCachePolicy(ReloadIgnoringCacheData); - request.setHTTPHeaderField("Cache-Control", "no-cache"); - request.setHTTPHeaderField("Pragma", "no-cache"); - } else if (isBackForwardLoadType(loadType) && !request.url().protocolIs("https")) + else if (isBackForwardLoadType(loadType) && m_stateMachine.committedFirstRealDocumentLoad() && !request.url().protocolIs("https")) request.setCachePolicy(ReturnCacheDataElseLoad); + + if (request.cachePolicy() == ReloadIgnoringCacheData) { + if (loadType == FrameLoadTypeReload) + request.setHTTPHeaderField("Cache-Control", "max-age=0"); + else if (loadType == FrameLoadTypeReloadFromOrigin) { + request.setHTTPHeaderField("Cache-Control", "no-cache"); + request.setHTTPHeaderField("Pragma", "no-cache"); + } + } if (mainResource) request.setHTTPAccept(defaultAcceptHeader); @@ -3305,7 +2707,7 @@ void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadTyp // Always try UTF-8. If that fails, try frame encoding (if any) and then the default. // For a newly opened frame with an empty URL, encoding() should not be used, because this methods asks decoder, which uses ISO-8859-1. Settings* settings = m_frame->settings(); - request.setResponseContentDispositionEncodingFallbackArray("UTF-8", m_URL.isEmpty() ? m_encoding : encoding(), settings ? settings->defaultTextEncodingName() : String()); + request.setResponseContentDispositionEncodingFallbackArray("UTF-8", writer()->deprecatedFrameEncoding(), settings ? settings->defaultTextEncodingName() : String()); } void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, String origin) @@ -3334,20 +2736,7 @@ void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, String origin) request.setHTTPOrigin(origin); } -void FrameLoader::committedLoad(DocumentLoader* loader, const char* data, int length) -{ -#if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size - if (ArchiveFactory::isArchiveMimeType(loader->response().mimeType())) - return; -#endif - m_client->committedLoad(loader, data, length); -} - -#ifdef ANDROID_USER_GESTURE -void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType loadType, PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState, bool userGesture) -#else void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType loadType, PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState) -#endif { RefPtr<FormState> formState = prpFormState; @@ -3367,9 +2756,6 @@ void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String String origin = inRequest.httpOrigin(); ResourceRequest workingResourceRequest(url); -#ifdef ANDROID_USER_GESTURE - workingResourceRequest.setUserGesture(userGesture); -#endif if (!referrer.isEmpty()) workingResourceRequest.setHTTPReferrer(referrer); @@ -3400,17 +2786,6 @@ unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& requ ResourceRequest initialRequest = request; initialRequest.setTimeoutInterval(10); - // Use the original request's cache policy for two reasons: - // 1. For POST requests, we mutate the cache policy for the main resource, - // but we do not want this to apply to subresources - // 2. Delegates that modify the cache policy using willSendRequest: should - // not affect any other resources. Such changes need to be done - // per request. - if (initialRequest.isConditional()) - initialRequest.setCachePolicy(ReloadIgnoringCacheData); - else - initialRequest.setCachePolicy(originalRequest().cachePolicy()); - if (!referrer.isEmpty()) initialRequest.setHTTPReferrer(referrer); addHTTPOriginIfNeeded(initialRequest, outgoingOrigin()); @@ -3418,6 +2793,8 @@ unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& requ if (Page* page = m_frame->page()) initialRequest.setFirstPartyForCookies(page->mainFrame()->loader()->documentLoader()->request().url()); initialRequest.setHTTPUserAgent(client()->userAgent(request.url())); + + addExtraFieldsToSubresourceRequest(initialRequest); unsigned long identifier = 0; ResourceRequest newRequest(initialRequest); @@ -3429,7 +2806,7 @@ unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& requ #if ENABLE(OFFLINE_WEB_APPLICATIONS) if (!documentLoader()->applicationCacheHost()->maybeLoadSynchronously(newRequest, error, response, data)) { #endif - ResourceHandle::loadResourceSynchronously(newRequest, storedCredentials, error, response, data, m_frame); + ResourceHandle::loadResourceSynchronously(networkingContext(), newRequest, storedCredentials, error, response, data); #if ENABLE(OFFLINE_WEB_APPLICATIONS) documentLoader()->applicationCacheHost()->maybeLoadFallbackSynchronously(newRequest, error, response, data); } @@ -3526,6 +2903,36 @@ void FrameLoader::callContinueLoadAfterNavigationPolicy(void* argument, loader->continueLoadAfterNavigationPolicy(request, formState, shouldContinue); } +bool FrameLoader::shouldClose() +{ + Page* page = m_frame->page(); + Chrome* chrome = page ? page->chrome() : 0; + if (!chrome || !chrome->canRunBeforeUnloadConfirmPanel()) + return true; + + DOMWindow* domWindow = m_frame->existingDOMWindow(); + if (!domWindow) + return true; + + RefPtr<Document> document = m_frame->document(); + HTMLElement* body = document->body(); + if (!body) + return true; + + RefPtr<BeforeUnloadEvent> beforeUnloadEvent = BeforeUnloadEvent::create(); + m_pageDismissalEventBeingDispatched = true; + domWindow->dispatchEvent(beforeUnloadEvent.get(), domWindow->document()); + m_pageDismissalEventBeingDispatched = false; + + if (!beforeUnloadEvent->defaultPrevented()) + document->defaultEventHandler(beforeUnloadEvent.get()); + if (beforeUnloadEvent->result().isNull()) + return true; + + String text = document->displayStringModifiedByEncoding(beforeUnloadEvent->result()); + return chrome->runBeforeUnloadConfirmPanel(text, m_frame); +} + void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue) { // If we loaded an alternate page to replace an unreachableURL, we'll get in here with a @@ -3540,7 +2947,7 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass // is the user responding Cancel to the form repost nag sheet. // 2) User responded Cancel to an alert popped up by the before unload event handler. // The "before unload" event handler runs only for the main frame. - bool canContinue = shouldContinue && (!isLoadingMainFrame() || m_frame->shouldClose()); + bool canContinue = shouldContinue && (!isLoadingMainFrame() || shouldClose()); if (!canContinue) { // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we @@ -3557,7 +2964,7 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass if (Page* page = m_frame->page()) { Frame* mainFrame = page->mainFrame(); if (HistoryItem* resetItem = mainFrame->loader()->history()->currentItem()) { - page->backForwardList()->goToItem(resetItem); + page->backForward()->setCurrentItem(resetItem); Settings* settings = m_frame->settings(); page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : resetItem); } @@ -3576,7 +2983,7 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass #if ENABLE(JAVASCRIPT_DEBUGGER) && ENABLE(INSPECTOR) && USE(JSC) if (Page* page = m_frame->page()) { if (page->mainFrame() == m_frame) - page->inspectorController()->resumeDebugger(); + page->inspectorController()->resume(); } #endif @@ -3586,8 +2993,10 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass setPolicyDocumentLoader(0); - if (isBackForwardLoadType(type) && loadProvisionalItemFromCachedPage()) + if (isBackForwardLoadType(type) && history()->provisionalItem()->isInPageCache()) { + loadProvisionalItemFromCachedPage(); return; + } if (formState) m_client->dispatchWillSubmitForm(&PolicyChecker::continueLoadAfterWillSubmitForm, formState); @@ -3596,20 +3005,20 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass } void FrameLoader::callContinueLoadAfterNewWindowPolicy(void* argument, - const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, bool shouldContinue) + const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& action, bool shouldContinue) { FrameLoader* loader = static_cast<FrameLoader*>(argument); - loader->continueLoadAfterNewWindowPolicy(request, formState, frameName, shouldContinue); + loader->continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue); } void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& request, - PassRefPtr<FormState> formState, const String& frameName, bool shouldContinue) + PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& action, bool shouldContinue) { if (!shouldContinue) return; RefPtr<Frame> frame = m_frame; - RefPtr<Frame> mainFrame = m_client->dispatchCreatePage(); + RefPtr<Frame> mainFrame = m_client->dispatchCreatePage(action); if (!mainFrame) return; @@ -3650,14 +3059,13 @@ void FrameLoader::loadedResourceFromMemoryCache(const CachedResource* resource) if (!page) return; -#if ENABLE(INSPECTOR) - page->inspectorController()->didLoadResourceFromMemoryCache(m_documentLoader.get(), resource); -#endif - if (!resource->sendResourceLoadCallbacks() || m_documentLoader->haveToldClientAboutLoad(resource->url())) return; if (!page->areMemoryCacheClientCallsEnabled()) { +#if ENABLE(INSPECTOR) + page->inspectorController()->didLoadResourceFromMemoryCache(m_documentLoader.get(), resource); +#endif m_documentLoader->recordMemoryCacheLoadForFutureClientNotification(resource->url()); m_documentLoader->didTellClientAboutLoad(resource->url()); return; @@ -3665,6 +3073,9 @@ void FrameLoader::loadedResourceFromMemoryCache(const CachedResource* resource) ResourceRequest request(resource->url()); if (m_client->dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, resource->response(), resource->encodedSize())) { +#if ENABLE(INSPECTOR) + page->inspectorController()->didLoadResourceFromMemoryCache(m_documentLoader.get(), resource); +#endif m_documentLoader->didTellClientAboutLoad(resource->url()); return; } @@ -3672,6 +3083,9 @@ void FrameLoader::loadedResourceFromMemoryCache(const CachedResource* resource) unsigned long identifier; ResourceError error; requestFromDelegate(request, identifier, error); +#if ENABLE(INSPECTOR) + page->inspectorController()->markResourceAsCached(identifier); +#endif notifier()->sendRemainingDelegateMessages(m_documentLoader.get(), identifier, resource->response(), resource->encodedSize(), error); } @@ -3700,48 +3114,22 @@ bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, con return false; } -bool FrameLoader::loadProvisionalItemFromCachedPage() +void FrameLoader::loadProvisionalItemFromCachedPage() { - RefPtr<CachedPage> cachedPage = pageCache()->get(history()->provisionalItem()); - if (!cachedPage || !cachedPage->document()) - return false; + DocumentLoader* provisionalLoader = provisionalDocumentLoader(); + LOG(PageCache, "WebCorePageCache: Loading provisional DocumentLoader %p with URL '%s' from CachedPage", provisionalDocumentLoader(), provisionalDocumentLoader()->url().string().utf8().data()); - DocumentLoader *provisionalLoader = provisionalDocumentLoader(); - LOG(PageCache, "WebCorePageCache: FrameLoader %p loading provisional DocumentLoader %p with URL '%s' from CachedPage %p", this, provisionalLoader, provisionalLoader->url().string().utf8().data(), cachedPage.get()); - provisionalLoader->prepareForLoadStart(); m_loadingFromCachedPage = true; - - provisionalLoader->setCommitted(true); - commitProvisionalLoad(cachedPage); - - return true; -} - -void FrameLoader::cachePageForHistoryItem(HistoryItem* item) -{ - if (!canCachePage() || item->isInPageCache()) - return; - - pageHidden(); - if (Page* page = m_frame->page()) { - RefPtr<CachedPage> cachedPage = CachedPage::create(page); - pageCache()->add(item, cachedPage.release()); - } -} - -void FrameLoader::pageHidden() -{ - m_unloadEventBeingDispatched = true; - if (m_frame->domWindow()) - m_frame->domWindow()->dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, true), m_frame->document()); - m_unloadEventBeingDispatched = false; + // Should have timing data from previous time(s) the page was shown. + ASSERT(provisionalLoader->timing()->navigationStart); + provisionalLoader->resetTiming(); + provisionalLoader->timing()->navigationStart = currentTime(); - // Send pagehide event for subframes as well - for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) - child->loader()->pageHidden(); + provisionalLoader->setCommitted(true); + commitProvisionalLoad(); } bool FrameLoader::shouldTreatURLAsSameAsCurrent(const KURL& url) const @@ -3757,7 +3145,7 @@ void FrameLoader::checkDidPerformFirstNavigation() if (!page) return; - if (!m_didPerformFirstNavigation && page->backForwardList()->entries().size() == 1) { + if (!m_didPerformFirstNavigation && page->backForward()->currentItem() && !page->backForward()->backItem() && !page->backForward()->forwardItem()) { m_didPerformFirstNavigation = true; m_client->didPerformFirstNavigation(); } @@ -3767,7 +3155,7 @@ Frame* FrameLoader::findFrameForNavigation(const AtomicString& name) { Frame* frame = m_frame->tree()->find(name); if (!shouldAllowNavigation(frame)) - return 0; + return 0; return frame; } @@ -3797,21 +3185,10 @@ void FrameLoader::navigateToDifferentDocument(HistoryItem* item, FrameLoadType l { // Remember this item so we can traverse any child items as child frames load history()->setProvisionalItem(item); - - // Check if we'll be using the page cache. We only use the page cache - // if one exists and it is less than _backForwardCacheExpirationInterval - // seconds old. If the cache is expired it gets flushed here. - if (RefPtr<CachedPage> cachedPage = pageCache()->get(item)) { - // FIXME: 1800 should not be hardcoded, it should come from - // WebKitBackForwardCacheExpirationIntervalKey in WebKit. - // Or we should remove WebKitBackForwardCacheExpirationIntervalKey. - if (currentTime() - cachedPage->timeStamp() <= 1800) { - loadWithDocumentLoader(cachedPage->documentLoader(), loadType, 0); - return; - } - - LOG(PageCache, "Not restoring page for %s from back/forward cache because cache entry has expired", history()->provisionalItem()->url().string().ascii().data()); - pageCache()->remove(item); + + if (CachedPage* cachedPage = pageCache()->get(item)) { + loadWithDocumentLoader(cachedPage->documentLoader(), loadType, 0); + return; } KURL itemURL = item->url(); @@ -3830,7 +3207,7 @@ void FrameLoader::navigateToDifferentDocument(HistoryItem* item, FrameLoadType l // If this was a repost that failed the page cache, we might try to repost the form. NavigationAction action; if (formData) { - formData->generateFiles(m_frame->page()->chrome()->client()); + formData->generateFiles(m_frame->document()); request.setHTTPMethod("POST"); request.setHTTPBody(formData); @@ -3867,7 +3244,9 @@ void FrameLoader::navigateToDifferentDocument(HistoryItem* item, FrameLoadType l case FrameLoadTypeBackWMLDeckNotAccessible: case FrameLoadTypeForward: case FrameLoadTypeIndexedBackForward: - if (!itemURL.protocolIs("https")) + // If the first load within a frame is a navigation within a back/forward list that was attached + // without any of the items being loaded then we should use the default caching policy (<rdar://problem/8131355>). + if (m_stateMachine.committedFirstRealDocumentLoad() && !itemURL.protocolIs("https")) request.setCachePolicy(ReturnCacheDataElseLoad); break; case FrameLoadTypeStandard: @@ -3890,13 +3269,8 @@ void FrameLoader::navigateToDifferentDocument(HistoryItem* item, FrameLoadType l // Loads content into this frame, as specified by history item void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType) { - // We do same-document navigation in the following cases: - // - The HistoryItem has a history state object - // - Navigating to an anchor within the page, with no form data stored on the target item or the current history entry, - // and the URLs in the frame tree match the history item for fragment scrolling. HistoryItem* currentItem = history()->currentItem(); - bool sameDocumentNavigation = (!item->formData() && !(currentItem && currentItem->formData()) && history()->urlsMatchItem(item)) - || (currentItem && item->documentSequenceNumber() == currentItem->documentSequenceNumber()); + bool sameDocumentNavigation = currentItem && item->shouldDoSameDocumentNavigationTo(currentItem); #if ENABLE(WML) // All WML decks should go through the real load mechanism, not the scroll-to-anchor code @@ -3961,11 +3335,23 @@ bool FrameLoader::shouldUseCredentialStorage(ResourceLoader* loader) return m_client->shouldUseCredentialStorage(loader->documentLoader(), loader->identifier()); } +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) +bool FrameLoader::canAuthenticateAgainstProtectionSpace(ResourceLoader* loader, const ProtectionSpace& protectionSpace) +{ + return m_client->canAuthenticateAgainstProtectionSpace(loader->documentLoader(), loader->identifier(), protectionSpace); +} +#endif + void FrameLoader::setTitle(const String& title) { documentLoader()->setTitle(title); } +void FrameLoader::setIconURL(const String& iconURL) +{ + documentLoader()->setIconURL(iconURL); +} + KURL FrameLoader::originalRequestURL() const { return activeDocumentLoader()->originalRequest().url(); @@ -3973,7 +3359,7 @@ KURL FrameLoader::originalRequestURL() const String FrameLoader::referrer() const { - return documentLoader()->request().httpReferrer(); + return m_documentLoader ? m_documentLoader->request().httpReferrer() : ""; } void FrameLoader::dispatchDocumentElementAvailable() @@ -3984,7 +3370,7 @@ void FrameLoader::dispatchDocumentElementAvailable() void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() { - if (!m_frame->script()->canExecuteScripts()) + if (!m_frame->script()->canExecuteScripts(NotAboutToExecuteScript)) return; Vector<DOMWrapperWorld*> worlds; @@ -3995,7 +3381,7 @@ void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds() void FrameLoader::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld* world) { - if (!m_frame->script()->canExecuteScripts() || !m_frame->script()->existingWindowShell(world)) + if (!m_frame->script()->canExecuteScripts(NotAboutToExecuteScript) || !m_frame->script()->existingWindowShell(world)) return; m_client->dispatchDidClearWindowObjectInWorld(world); @@ -4007,15 +3393,13 @@ void FrameLoader::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld* world) if (Page* page = m_frame->page()) { if (InspectorController* inspector = page->inspectorController()) inspector->inspectedWindowScriptObjectCleared(m_frame); - if (InspectorController* inspector = page->parentInspectorController()) - inspector->windowScriptObjectAvailable(); } #endif } void FrameLoader::updateSandboxFlags() { - SandboxFlags flags = SandboxNone; + SandboxFlags flags = m_forcedSandboxFlags; if (Frame* parentFrame = m_frame->tree()->parent()) flags |= parentFrame->loader()->sandboxFlags(); if (HTMLFrameOwnerElement* ownerElement = m_frame->ownerElement()) @@ -4030,47 +3414,6 @@ void FrameLoader::updateSandboxFlags() child->loader()->updateSandboxFlags(); } -bool FrameLoader::isDocumentSandboxed(SandboxFlags mask) const -{ - return m_frame->document() && m_frame->document()->securityOrigin()->isSandboxed(mask); -} - -PassRefPtr<Widget> FrameLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement* element, const HashMap<String, String>& args) -{ - String baseURLString; - String codeBaseURLString; - Vector<String> paramNames; - Vector<String> paramValues; - HashMap<String, String>::const_iterator end = args.end(); - for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) { - if (equalIgnoringCase(it->first, "baseurl")) - baseURLString = it->second; - else if (equalIgnoringCase(it->first, "codebase")) - codeBaseURLString = it->second; - paramNames.append(it->first); - paramValues.append(it->second); - } - - if (!codeBaseURLString.isEmpty()) { - KURL codeBaseURL = completeURL(codeBaseURLString); - if (!SecurityOrigin::canLoad(codeBaseURL, String(), element->document())) { - FrameLoader::reportLocalLoadFailed(m_frame, codeBaseURL.string()); - return 0; - } - } - - if (baseURLString.isEmpty()) - baseURLString = m_frame->document()->baseURL().string(); - KURL baseURL = completeURL(baseURLString); - - RefPtr<Widget> widget = m_client->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues); - if (!widget) - return 0; - - m_containsPlugIns = true; - return widget; -} - void FrameLoader::didChangeTitle(DocumentLoader* loader) { m_client->didChangeTitle(loader); @@ -4085,9 +3428,15 @@ void FrameLoader::didChangeTitle(DocumentLoader* loader) } } +void FrameLoader::didChangeIcons(DocumentLoader* loader) +{ + if (loader == m_documentLoader) + m_client->dispatchDidChangeIcons(); +} + void FrameLoader::dispatchDidCommitLoad() { - if (m_creatingInitialEmptyDocument) + if (m_stateMachine.creatingInitialEmptyDocument()) return; #ifndef NDEBUG @@ -4129,9 +3478,80 @@ void FrameLoader::tellClientAboutPastMemoryCacheLoads() } } +NetworkingContext* FrameLoader::networkingContext() const +{ + return m_networkingContext.get(); +} + bool FrameLoaderClient::hasHTMLView() const { return true; } +Frame* createWindow(Frame* openerFrame, Frame* lookupFrame, const FrameLoadRequest& request, const WindowFeatures& features, bool& created) +{ + ASSERT(!features.dialog || request.frameName().isEmpty()); + + if (!request.frameName().isEmpty() && request.frameName() != "_blank") { + Frame* frame = lookupFrame->tree()->find(request.frameName()); + if (frame && openerFrame->loader()->shouldAllowNavigation(frame)) { + if (!request.resourceRequest().url().isEmpty()) + frame->loader()->loadFrameRequest(request, false, false, 0, 0, SendReferrer); + if (Page* page = frame->page()) + page->chrome()->focus(); + created = false; + return frame; + } + } + + // Sandboxed frames cannot open new auxiliary browsing contexts. + if (isDocumentSandboxed(openerFrame, SandboxNavigation)) + return 0; + + // FIXME: Setting the referrer should be the caller's responsibility. + FrameLoadRequest requestWithReferrer = request; + requestWithReferrer.resourceRequest().setHTTPReferrer(openerFrame->loader()->outgoingReferrer()); + FrameLoader::addHTTPOriginIfNeeded(requestWithReferrer.resourceRequest(), openerFrame->loader()->outgoingOrigin()); + + Page* oldPage = openerFrame->page(); + if (!oldPage) + return 0; + + NavigationAction action; + Page* page = oldPage->chrome()->createWindow(openerFrame, requestWithReferrer, features, action); + if (!page) + return 0; + + Frame* frame = page->mainFrame(); + if (request.frameName() != "_blank") + frame->tree()->setName(request.frameName()); + + page->chrome()->setToolbarsVisible(features.toolBarVisible || features.locationBarVisible); + page->chrome()->setStatusbarVisible(features.statusBarVisible); + page->chrome()->setScrollbarsVisible(features.scrollbarsVisible); + page->chrome()->setMenubarVisible(features.menuBarVisible); + page->chrome()->setResizable(features.resizable); + + // 'x' and 'y' specify the location of the window, while 'width' and 'height' + // specify the size of the page. We can only resize the window, so + // adjust for the difference between the window size and the page size. + + FloatRect windowRect = page->chrome()->windowRect(); + FloatSize pageSize = page->chrome()->pageRect().size(); + if (features.xSet) + windowRect.setX(features.x); + if (features.ySet) + windowRect.setY(features.y); + if (features.widthSet) + windowRect.setWidth(features.width + (windowRect.width() - pageSize.width())); + if (features.heightSet) + windowRect.setHeight(features.height + (windowRect.height() - pageSize.height())); + page->chrome()->setWindowRect(windowRect); + + page->chrome()->show(); + + created = true; + return frame; +} + } // namespace WebCore diff --git a/WebCore/loader/FrameLoader.h b/WebCore/loader/FrameLoader.h index abe3b3a..b07ed27 100644 --- a/WebCore/loader/FrameLoader.h +++ b/WebCore/loader/FrameLoader.h @@ -32,13 +32,16 @@ #define FrameLoader_h #include "CachePolicy.h" +#include "DocumentWriter.h" +#include "FrameLoaderStateMachine.h" #include "FrameLoaderTypes.h" #include "HistoryController.h" +#include "NavigationScheduler.h" #include "PolicyCallback.h" #include "PolicyChecker.h" -#include "RedirectScheduler.h" #include "ResourceLoadNotifier.h" #include "ResourceRequest.h" +#include "SubframeLoader.h" #include "ThreadableLoader.h" #include "Timer.h" #include <wtf/Forward.h> @@ -58,28 +61,27 @@ class DocumentLoader; class Event; class FormData; class FormState; +class FormSubmission; class Frame; class FrameLoaderClient; +class FrameNetworkingContext; class HistoryItem; -class HTMLAppletElement; class HTMLFormElement; -class HTMLFrameOwnerElement; class IconLoader; -class IntSize; class NavigationAction; -class RenderPart; +class NetworkingContext; +class Page; +class ProtectionSpace; class ResourceError; class ResourceLoader; class ResourceResponse; class ScriptSourceCode; -class ScriptString; class ScriptValue; class SecurityOrigin; class SerializedScriptValue; class SharedBuffer; class SubstituteData; class TextResourceDecoder; -class Widget; struct FrameLoadRequest; struct WindowFeatures; @@ -98,6 +100,8 @@ public: PolicyChecker* policyChecker() const { return &m_policyChecker; } HistoryController* history() const { return &m_history; } ResourceLoadNotifier* notifier() const { return &m_notifer; } + DocumentWriter* writer() const { return &m_writer; } + SubframeLoader* subframeLoader() const { return &m_subframeLoader; } // FIXME: This is not cool, people. There are too many different functions that all start loads. // We should aim to consolidate these into a smaller set of functions, and try to reuse more of @@ -122,9 +126,6 @@ public: static void reportLocalLoadFailed(Frame*, const String& url); - // Called by createWindow in JSDOMWindowBase.cpp, e.g. to fulfill a modal dialog creation - Frame* createWindow(FrameLoader* frameLoaderForFrameLookup, const FrameLoadRequest&, const WindowFeatures&, bool& created); - unsigned long loadResourceSynchronously(const ResourceRequest&, StoredCredentials, ResourceError&, ResourceResponse&, Vector<char>& data); bool canHandleRequest(const ResourceRequest&); @@ -136,6 +137,7 @@ public: bool isLoadingMainResource() const { return m_isLoadingMainResource; } bool isLoading() const; bool frameHasLoaded() const; + void transferLoadingResourcesFromPage(Page*); int numPendingOrLoadingRequests(bool recurse) const; String referrer() const; @@ -150,10 +152,12 @@ public: static double timeOfLastCompletedLoad(); bool shouldUseCredentialStorage(ResourceLoader*); +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + bool canAuthenticateAgainstProtectionSpace(ResourceLoader* loader, const ProtectionSpace& protectionSpace); +#endif const ResourceRequest& originalRequest() const; const ResourceRequest& initialRequest() const; void receivedMainResourceError(const ResourceError&, bool isComplete); - void receivedData(const char*, int); bool willLoadMediaElementURL(KURL&); @@ -179,7 +183,6 @@ public: void didReceiveServerRedirectForProvisionalLoadForFrame(); void finishedLoadingDocument(DocumentLoader*); - void committedLoad(DocumentLoader*, const char*, int); bool isReplacing() const; void setReplacing(); void revertToProvisional(DocumentLoader*); @@ -188,12 +191,13 @@ public: bool subframeIsLoading() const; void willChangeTitle(DocumentLoader*); void didChangeTitle(DocumentLoader*); + void didChangeIcons(DocumentLoader*); FrameLoadType loadType() const; + CachePolicy subresourceCachePolicy() const; void didFirstLayout(); - bool firstLayoutDone() const; void didFirstVisuallyNonEmptyLayout(); @@ -213,13 +217,10 @@ public: void setDefersLoading(bool); - void changeLocation(const KURL&, const String& referrer, bool lockHistory = true, bool lockBackForwardList = true, bool userGesture = false, bool refresh = false); - void urlSelected(const ResourceRequest&, const String& target, PassRefPtr<Event>, bool lockHistory, bool lockBackForwardList, bool userGesture, ReferrerPolicy); - bool requestFrame(HTMLFrameOwnerElement*, const String& url, const AtomicString& frameName); + void changeLocation(const KURL&, const String& referrer, bool lockHistory = true, bool lockBackForwardList = true, bool refresh = false); + void urlSelected(const KURL&, const String& target, PassRefPtr<Event>, bool lockHistory, bool lockBackForwardList, ReferrerPolicy); - void submitForm(const char* action, const String& url, - PassRefPtr<FormData>, const String& target, const String& contentType, const String& boundary, - bool lockHistory, PassRefPtr<Event>, PassRefPtr<FormState>); + void submitForm(PassRefPtr<FormSubmission>); void stop(); void stopLoading(UnloadEventPolicy, DatabasePolicy = DatabasePolicyStop); @@ -227,31 +228,19 @@ public: void didExplicitOpen(); + // Callbacks from DocumentWriter + void didBeginDocument(bool dispatchWindowObjectAvailable); + void didEndDocument(); + void willSetEncoding(); + KURL iconURL(); void commitIconURLToIconDatabase(const KURL&); KURL baseURL() const; - void replaceDocument(const String&); - - void begin(); - void begin(const KURL&, bool dispatchWindowObjectAvailable = true, SecurityOrigin* forcedSecurityOrigin = 0); - - void write(const char* string, int length = -1, bool flush = false); - void write(const String&); - void end(); - void endIfNotLoadingMainResource(); - - void setEncoding(const String& encoding, bool userChosen); - String encoding() const; - - void tokenizerProcessedData(); - void handledOnloadEvents(); String userAgent(const KURL&) const; - PassRefPtr<Widget> createJavaAppletWidget(const IntSize&, HTMLAppletElement*, const HashMap<String, String>& args); - void dispatchDidClearWindowObjectInWorld(DOMWrapperWorld*); void dispatchDidClearWindowObjectsInAllWorlds(); void dispatchDocumentElementAvailable(); @@ -260,6 +249,9 @@ public: bool isSandboxed(SandboxFlags mask) const { return m_sandboxFlags & mask; } SandboxFlags sandboxFlags() const { return m_sandboxFlags; } + // The following sandbox flags will be forced, regardless of changes to + // the sandbox attribute of any parent frames. + void setForcedSandboxFlags(SandboxFlags flags) { m_forcedSandboxFlags = flags; m_sandboxFlags |= flags; } // Mixed content related functions. static bool isMixedContent(SecurityOrigin* context, const KURL&); @@ -273,18 +265,14 @@ public: void resetMultipleFormSubmissionProtection(); - void addData(const char* bytes, int length); - void checkCallImplicitClose(); void frameDetached(); const KURL& url() const { return m_URL; } - void setResponseMIMEType(const String&); - const String& responseMIMEType() const; - - bool containsPlugins() const; + // setURL is a low-level setter and does not trigger loading. + void setURL(const KURL&); void loadDone(); void finishedParsing(); @@ -294,21 +282,17 @@ public: bool isComplete() const; - bool requestObject(RenderPart* frame, const String& url, const AtomicString& frameName, - const String& serviceType, const Vector<String>& paramNames, const Vector<String>& paramValues); - KURL completeURL(const String& url); void cancelAndClear(); void setTitle(const String&); + void setIconURL(const String&); - void commitProvisionalLoad(PassRefPtr<CachedPage>); + void commitProvisionalLoad(); bool isLoadingFromCachedPage() const { return m_loadingFromCachedPage; } - bool committingFirstRealLoad() const { return !m_creatingInitialEmptyDocument && !m_committedFirstRealDocumentLoad; } - bool committedFirstRealDocumentLoad() const { return m_committedFirstRealDocumentLoad; } - bool creatingInitialEmptyDocument() const { return m_creatingInitialEmptyDocument; } + FrameLoaderStateMachine* stateMachine() const { return &m_stateMachine; } void iconLoadDecisionAvailable(); @@ -340,6 +324,18 @@ public: static ObjectContentType defaultObjectContentType(const KURL& url, const String& mimeType); + void clear(bool clearWindowProperties = true, bool clearScriptObjects = true, bool clearFrameView = true); + + bool quickRedirectComing() const { return m_quickRedirectComing; } + + bool shouldClose(); + + void started(); + + bool pageDismissalEventBeingDispatched() const { return m_pageDismissalEventBeingDispatched; } + + NetworkingContext* networkingContext() const; + private: bool canCachePageContainingThisFrame(); #ifndef NDEBUG @@ -348,19 +344,11 @@ private: #endif void checkTimerFired(Timer<FrameLoader>*); - - void started(); - - bool shouldUsePlugin(const KURL&, const String& mimeType, bool hasFallback, bool& useFallback); - bool loadPlugin(RenderPart*, const KURL&, const String& mimeType, - const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback); void navigateWithinDocument(HistoryItem*); void navigateToDifferentDocument(HistoryItem*, FrameLoadType); - bool loadProvisionalItemFromCachedPage(); - void cachePageForHistoryItem(HistoryItem*); - void pageHidden(); + void loadProvisionalItemFromCachedPage(); void receivedFirstData(); @@ -382,11 +370,11 @@ private: void setLoadType(FrameLoadType); static void callContinueLoadAfterNavigationPolicy(void*, const ResourceRequest&, PassRefPtr<FormState>, bool shouldContinue); - static void callContinueLoadAfterNewWindowPolicy(void*, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, bool shouldContinue); + static void callContinueLoadAfterNewWindowPolicy(void*, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, const NavigationAction&, bool shouldContinue); static void callContinueFragmentScrollAfterNavigationPolicy(void*, const ResourceRequest&, PassRefPtr<FormState>, bool shouldContinue); void continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState>, bool shouldContinue); - void continueLoadAfterNewWindowPolicy(const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, bool shouldContinue); + void continueLoadAfterNewWindowPolicy(const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, const NavigationAction&, bool shouldContinue); void continueFragmentScrollAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue); bool shouldScrollToAnchor(bool isFormSubmission, FrameLoadType, const KURL&); @@ -400,33 +388,26 @@ private: void setState(FrameState); void closeOldDataSources(); - void open(CachedPage&); + void prepareForCachedPageRestore(); void updateHistoryAfterClientRedirect(); - void clear(bool clearWindowProperties = true, bool clearScriptObjects = true, bool clearFrameView = true); - bool shouldReloadToHandleUnreachableURL(DocumentLoader*); void dispatchDidCommitLoad(); + void urlSelected(const ResourceRequest&, const String& target, PassRefPtr<Event>, bool lockHistory, bool lockBackForwardList, ReferrerPolicy, ShouldReplaceDocumentIfJavaScriptURL); + void loadWithDocumentLoader(DocumentLoader*, FrameLoadType, PassRefPtr<FormState>); // Calls continueLoadAfterNavigationPolicy void load(DocumentLoader*); // Calls loadWithDocumentLoader void loadWithNavigationAction(const ResourceRequest&, const NavigationAction&, // Calls loadWithDocumentLoader bool lockHistory, FrameLoadType, PassRefPtr<FormState>); -#ifdef ANDROID_USER_GESTURE - void loadPostRequest(const ResourceRequest&, const String& referrer, // Called by loadFrameRequest, calls loadWithNavigationAction - const String& frameName, bool lockHistory, FrameLoadType, PassRefPtr<Event>, PassRefPtr<FormState>, bool); - void loadURL(const KURL&, const String& referrer, const String& frameName, // Called by loadFrameRequest, calls loadWithNavigationAction or dispatches to navigation policy delegate - bool lockHistory, FrameLoadType, PassRefPtr<Event>, PassRefPtr<FormState>, bool); -#else void loadPostRequest(const ResourceRequest&, const String& referrer, // Called by loadFrameRequest, calls loadWithNavigationAction const String& frameName, bool lockHistory, FrameLoadType, PassRefPtr<Event>, PassRefPtr<FormState>); void loadURL(const KURL&, const String& referrer, const String& frameName, // Called by loadFrameRequest, calls loadWithNavigationAction or dispatches to navigation policy delegate bool lockHistory, FrameLoadType, PassRefPtr<Event>, PassRefPtr<FormState>); -#endif bool shouldReload(const KURL& currentURL, const KURL& destinationURL); @@ -437,8 +418,6 @@ private: void detachChildren(); void closeAndRemoveChild(Frame*); - Frame* loadSubframe(HTMLFrameOwnerElement*, const KURL&, const String& name, const String& referrer); - void loadInSameDocument(const KURL&, SerializedScriptValue* stateObject, bool isNewNavigation); void provisionalLoadStarted(); @@ -456,8 +435,6 @@ private: bool shouldTreatURLAsSameAsCurrent(const KURL&) const; void updateSandboxFlags(); - // FIXME: isDocumentSandboxed should eventually replace isSandboxed. - bool isDocumentSandboxed(SandboxFlags) const; Frame* m_frame; FrameLoaderClient* m_client; @@ -465,6 +442,9 @@ private: mutable PolicyChecker m_policyChecker; mutable HistoryController m_history; mutable ResourceLoadNotifier m_notifer; + mutable DocumentWriter m_writer; + mutable SubframeLoader m_subframeLoader; + mutable FrameLoaderStateMachine m_stateMachine; FrameState m_state; FrameLoadType m_loadType; @@ -479,7 +459,6 @@ private: bool m_delegateIsHandlingProvisionalLoadError; - bool m_firstLayoutDone; bool m_quickRedirectComing; bool m_sentRedirectNotification; bool m_inStopAllLoaders; @@ -488,11 +467,9 @@ private: bool m_isExecutingJavaScriptFormAction; - String m_responseMIMEType; - bool m_didCallImplicitClose; bool m_wasUnloadEventEmitted; - bool m_unloadEventBeingDispatched; + bool m_pageDismissalEventBeingDispatched; bool m_isComplete; bool m_isLoadingMainResource; @@ -504,16 +481,7 @@ private: OwnPtr<IconLoader> m_iconLoader; bool m_mayLoadIconLater; - bool m_cancellingWithLoadInProgress; - bool m_needsClear; - bool m_receivedData; - - bool m_encodingWasChosenByUser; - String m_encoding; - RefPtr<TextResourceDecoder> m_decoder; - - bool m_containsPlugIns; KURL m_submittedFormURL; @@ -524,21 +492,29 @@ private: Frame* m_opener; HashSet<Frame*> m_openedFrames; - bool m_creatingInitialEmptyDocument; - bool m_isDisplayingInitialEmptyDocument; - bool m_committedFirstRealDocumentLoad; - bool m_didPerformFirstNavigation; bool m_loadingFromCachedPage; bool m_suppressOpenerInNewFrame; SandboxFlags m_sandboxFlags; + SandboxFlags m_forcedSandboxFlags; #ifndef NDEBUG bool m_didDispatchDidCommitLoad; #endif + + RefPtr<FrameNetworkingContext> m_networkingContext; }; +// This function is called by createWindow() in JSDOMWindowBase.cpp, for example, for +// modal dialog creation. The lookupFrame is for looking up the frame name in case +// the frame name references a frame different from the openerFrame, e.g. when it is +// "_self" or "_parent". +// +// FIXME: Consider making this function part of an appropriate class (not FrameLoader) +// and moving it to a more appropriate location. +Frame* createWindow(Frame* openerFrame, Frame* lookupFrame, const FrameLoadRequest&, const WindowFeatures&, bool& created); + } // namespace WebCore #endif // FrameLoader_h diff --git a/WebCore/loader/FrameLoaderClient.h b/WebCore/loader/FrameLoaderClient.h index 2668958..7348293 100644 --- a/WebCore/loader/FrameLoaderClient.h +++ b/WebCore/loader/FrameLoaderClient.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,7 +32,6 @@ #include "FrameLoaderTypes.h" #include "ScrollTypes.h" #include <wtf/Forward.h> -#include <wtf/Platform.h> #include <wtf/Vector.h> typedef class _jobject* jobject; @@ -53,13 +52,20 @@ namespace WebCore { class FormState; class Frame; class FrameLoader; + class FrameNetworkingContext; class HistoryItem; class HTMLAppletElement; + class HTMLFormElement; class HTMLFrameOwnerElement; +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + class HTMLMediaElement; +#endif class HTMLPlugInElement; class IntSize; class KURL; class NavigationAction; + class Page; + class ProtectionSpace; class PluginView; class PolicyChecker; class ResourceError; @@ -67,11 +73,9 @@ namespace WebCore { class ResourceLoader; class ResourceRequest; class ResourceResponse; - class ScriptString; class SecurityOrigin; class SharedBuffer; class SubstituteData; - class String; class Widget; typedef void (PolicyChecker::*FramePolicyFunction)(PolicyAction); @@ -105,17 +109,20 @@ namespace WebCore { virtual bool shouldUseCredentialStorage(DocumentLoader*, unsigned long identifier) = 0; virtual void dispatchDidReceiveAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) = 0; virtual void dispatchDidCancelAuthenticationChallenge(DocumentLoader*, unsigned long identifier, const AuthenticationChallenge&) = 0; +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + virtual bool canAuthenticateAgainstProtectionSpace(DocumentLoader*, unsigned long identifier, const ProtectionSpace&) = 0; +#endif virtual void dispatchDidReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&) = 0; virtual void dispatchDidReceiveContentLength(DocumentLoader*, unsigned long identifier, int lengthReceived) = 0; virtual void dispatchDidFinishLoading(DocumentLoader*, unsigned long identifier) = 0; virtual void dispatchDidFailLoading(DocumentLoader*, unsigned long identifier, const ResourceError&) = 0; virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int length) = 0; - virtual void dispatchDidLoadResourceByXMLHttpRequest(unsigned long identifier, const ScriptString&) = 0; virtual void dispatchDidHandleOnloadEvents() = 0; virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() = 0; virtual void dispatchDidCancelClientRedirect() = 0; virtual void dispatchWillPerformClientRedirect(const KURL&, double interval, double fireDate) = 0; + virtual void dispatchDidNavigateWithinPage() { } virtual void dispatchDidChangeLocationWithinPage() = 0; virtual void dispatchDidPushStateWithinPage() = 0; virtual void dispatchDidReplaceStateWithinPage() = 0; @@ -124,6 +131,7 @@ namespace WebCore { virtual void dispatchDidReceiveIcon() = 0; virtual void dispatchDidStartProvisionalLoad() = 0; virtual void dispatchDidReceiveTitle(const String& title) = 0; + virtual void dispatchDidChangeIcons() = 0; virtual void dispatchDidCommitLoad() = 0; virtual void dispatchDidFailProvisionalLoad(const ResourceError&) = 0; virtual void dispatchDidFailLoad(const ResourceError&) = 0; @@ -132,7 +140,7 @@ namespace WebCore { virtual void dispatchDidFirstLayout() = 0; virtual void dispatchDidFirstVisuallyNonEmptyLayout() = 0; - virtual Frame* dispatchCreatePage() = 0; + virtual Frame* dispatchCreatePage(const NavigationAction&) = 0; virtual void dispatchShow() = 0; virtual void dispatchDecidePolicyForMIMEType(FramePolicyFunction, const String& MIMEType, const ResourceRequest&) = 0; @@ -142,6 +150,7 @@ namespace WebCore { virtual void dispatchUnableToImplementPolicy(const ResourceError&) = 0; + virtual void dispatchWillSendSubmitEvent(HTMLFormElement*) = 0; virtual void dispatchWillSubmitForm(FramePolicyFunction, PassRefPtr<FormState>) = 0; virtual void dispatchDidLoadMainResource(DocumentLoader*) = 0; @@ -195,6 +204,7 @@ namespace WebCore { virtual bool canHandleRequest(const ResourceRequest&) const = 0; virtual bool canShowMIMEType(const String& MIMEType) const = 0; + virtual bool canShowMIMETypeAsHTML(const String& MIMEType) const = 0; virtual bool representationExistsForURLScheme(const String& URLScheme) const = 0; virtual String generatedMIMETypeForURLScheme(const String& URLScheme) const = 0; @@ -214,17 +224,26 @@ namespace WebCore { virtual void transitionToCommittedFromCachedFrame(CachedFrame*) = 0; virtual void transitionToCommittedForNewPage() = 0; + virtual void dispatchDidBecomeFrameset(bool) = 0; // Can change due to navigation or DOM modification. + virtual bool canCachePage() const = 0; virtual void download(ResourceHandle*, const ResourceRequest&, const ResourceRequest&, const ResourceResponse&) = 0; virtual PassRefPtr<Frame> createFrame(const KURL& url, const String& name, HTMLFrameOwnerElement* ownerElement, const String& referrer, bool allowsScrolling, int marginWidth, int marginHeight) = 0; + virtual void didTransferChildFrameToNewDocument(Page* oldPage) = 0; + virtual void transferLoadingResourceFromPage(unsigned long identifier, DocumentLoader*, const ResourceRequest&, Page* oldPage) = 0; virtual PassRefPtr<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 PassRefPtr<Widget> createJavaAppletWidget(const IntSize&, HTMLAppletElement*, const KURL& baseURL, const Vector<String>& paramNames, const Vector<String>& paramValues) = 0; virtual void dispatchDidFailToStartPlugin(const PluginView*) const { } +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + virtual PassRefPtr<Widget> createMediaPlayerProxyPlugin(const IntSize&, HTMLMediaElement*, const KURL&, const Vector<String>&, const Vector<String>&, const String&) = 0; + virtual void hideMediaPlayerProxyPlugin(Widget*) = 0; + virtual void showMediaPlayerProxyPlugin(Widget*) = 0; +#endif virtual ObjectContentType objectContentType(const KURL& url, const String& mimeType) = 0; virtual String overrideMediaType() const = 0; @@ -237,6 +256,7 @@ namespace WebCore { virtual void didCreateScriptContextForFrame() = 0; virtual void didDestroyScriptContextForFrame() = 0; virtual void didCreateIsolatedScriptContext() = 0; + virtual bool allowScriptExtension(const String& extensionName, int extensionGroup) = 0; #endif virtual void registerForIconNotification(bool listen = true) = 0; @@ -245,7 +265,7 @@ namespace WebCore { #endif #if PLATFORM(MAC) -#if ENABLE(MAC_JAVA_BRIDGE) +#if ENABLE(JAVA_BRIDGE) virtual jobject javaApplet(NSView*) { return 0; } #endif virtual NSCachedURLResponse* willCacheResponse(DocumentLoader*, unsigned long identifier, NSCachedURLResponse*) const = 0; @@ -262,6 +282,17 @@ namespace WebCore { virtual bool allowJavaScript(bool enabledPerSettings) { return enabledPerSettings; } virtual bool allowPlugins(bool enabledPerSettings) { return enabledPerSettings; } virtual bool allowImages(bool enabledPerSettings) { return enabledPerSettings; } + + // This callback notifies the client that the frame was about to run + // JavaScript but did not because allowJavaScript returned false. We + // have a separate callback here because there are a number of places + // that need to know if JavaScript is enabled but are not necessarily + // preparing to execute script. + virtual void didNotAllowScript() { } + // This callback is similar, but for plugins. + virtual void didNotAllowPlugins() { } + + virtual PassRefPtr<FrameNetworkingContext> createNetworkingContext() = 0; }; } // namespace WebCore diff --git a/WebCore/loader/FrameLoaderStateMachine.cpp b/WebCore/loader/FrameLoaderStateMachine.cpp new file mode 100644 index 0000000..790b144 --- /dev/null +++ b/WebCore/loader/FrameLoaderStateMachine.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010 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: + * + * 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 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 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 "FrameLoaderStateMachine.h" + +#include <wtf/Assertions.h> + +namespace WebCore { + + +FrameLoaderStateMachine::FrameLoaderStateMachine() + : m_state(Uninitialized) +{ +} + +bool FrameLoaderStateMachine::committingFirstRealLoad() const +{ + return m_state == DisplayingInitialEmptyDocument; +} + +bool FrameLoaderStateMachine::committedFirstRealDocumentLoad() const +{ + return m_state >= DisplayingInitialEmptyDocumentPostCommit; +} + +bool FrameLoaderStateMachine::creatingInitialEmptyDocument() const +{ + return m_state == CreatingInitialEmptyDocument; +} + +bool FrameLoaderStateMachine::isDisplayingInitialEmptyDocument() const +{ + return m_state == DisplayingInitialEmptyDocument || m_state == DisplayingInitialEmptyDocumentPostCommit; +} + +bool FrameLoaderStateMachine::firstLayoutDone() const +{ + return m_state == FirstLayoutDone; +} + +void FrameLoaderStateMachine::advanceTo(State state) +{ + ASSERT(State(m_state + 1) == state || (firstLayoutDone() && state == CommittedFirstRealLoad)); + m_state = state; +} + +} // namespace WebCore diff --git a/WebCore/loader/FrameLoaderStateMachine.h b/WebCore/loader/FrameLoaderStateMachine.h new file mode 100644 index 0000000..c3408c2 --- /dev/null +++ b/WebCore/loader/FrameLoaderStateMachine.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2010 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: + * + * 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 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 APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FrameLoaderStateMachine_h +#define FrameLoaderStateMachine_h + +#include <wtf/Noncopyable.h> + +namespace WebCore { + +// Encapsulates a state machine for FrameLoader. Note that this is different from FrameState, +// which stores the state of the current load that FrameLoader is executing. +class FrameLoaderStateMachine : public Noncopyable { +public: + FrameLoaderStateMachine(); + + // Once a load has been committed, the state may + // alternate between CommittedFirstRealLoad and FirstLayoutDone. + // Otherwise, the states only go down the list. + enum State { + Uninitialized, + CreatingInitialEmptyDocument, + DisplayingInitialEmptyDocument, + DisplayingInitialEmptyDocumentPostCommit, + CommittedFirstRealLoad, + FirstLayoutDone + }; + + bool committingFirstRealLoad() const; + bool committedFirstRealDocumentLoad() const; + bool creatingInitialEmptyDocument() const; + bool isDisplayingInitialEmptyDocument() const; + bool firstLayoutDone() const; + void advanceTo(State); + +private: + State m_state; +}; + +} // namespace WebCore + +#endif // FrameLoaderStateMachine_h diff --git a/WebCore/loader/FrameLoaderTypes.h b/WebCore/loader/FrameLoaderTypes.h index 8288bce..016de19 100644 --- a/WebCore/loader/FrameLoaderTypes.h +++ b/WebCore/loader/FrameLoaderTypes.h @@ -92,7 +92,7 @@ namespace WebCore { SendReferrer, NoReferrer }; - + enum SandboxFlag { SandboxNone = 0, SandboxNavigation = 1, @@ -100,14 +100,29 @@ namespace WebCore { SandboxOrigin = 1 << 2, SandboxForms = 1 << 3, SandboxScripts = 1 << 4, + SandboxTopNavigation = 1 << 5, SandboxAll = -1 // Mask with all bits set to 1. }; - + enum SecurityCheckPolicy { SkipSecurityCheck, DoSecurityCheck }; + // Passed to FrameLoader::urlSelected() and ScriptController::executeIfJavaScriptURL() + // to control whether, in the case of a JavaScript URL, executeIfJavaScriptURL() should + // replace the document. It is a FIXME to eliminate this extra parameter from + // executeIfJavaScriptURL(), in which case this enum can go away. + enum ShouldReplaceDocumentIfJavaScriptURL { + ReplaceDocumentIfJavaScriptURL, + DoNotReplaceDocumentIfJavaScriptURL + }; + + enum ReasonForCallingAllowPlugins { + AboutToInstantiatePlugin, + NotAboutToInstantiatePlugin + }; + typedef int SandboxFlags; } diff --git a/WebCore/loader/FrameNetworkingContext.h b/WebCore/loader/FrameNetworkingContext.h new file mode 100644 index 0000000..dff1144 --- /dev/null +++ b/WebCore/loader/FrameNetworkingContext.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef FrameNetworkingContext_h +#define FrameNetworkingContext_h + +#include "Frame.h" +#include "NetworkingContext.h" + +namespace WebCore { + +class FrameNetworkingContext : public NetworkingContext { +public: + void invalidate() + { + m_frame = 0; + } + +protected: + FrameNetworkingContext(Frame* frame) + : m_frame(frame) + { + } + + Frame* frame() const { return m_frame; } + +private: + virtual bool isValid() const { return m_frame; } + + Frame* m_frame; +}; + +} + +#endif // FrameNetworkingContext_h diff --git a/WebCore/loader/HistoryController.cpp b/WebCore/loader/HistoryController.cpp index 43c9979..07bece7 100644 --- a/WebCore/loader/HistoryController.cpp +++ b/WebCore/loader/HistoryController.cpp @@ -31,13 +31,13 @@ #include "config.h" #include "HistoryController.h" -#include "BackForwardList.h" +#include "BackForwardController.h" #include "CachedPage.h" -#include "CString.h" #include "DocumentLoader.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" +#include "FrameLoaderStateMachine.h" #include "FrameTree.h" #include "FrameView.h" #include "HistoryItem.h" @@ -46,9 +46,24 @@ #include "PageCache.h" #include "PageGroup.h" #include "Settings.h" +#include <wtf/text/CString.h> + +#if USE(PLATFORM_STRATEGIES) +#include "PlatformStrategies.h" +#include "VisitedLinkStrategy.h" +#endif namespace WebCore { +static inline void addVisitedLink(Page* page, const KURL& url) +{ +#if USE(PLATFORM_STRATEGIES) + platformStrategies()->visitedLinkStrategy()->addVisitedLink(page, visitedLinkHash(url.string().characters(), url.string().length())); +#else + page->group().addVisitedLink(url); +#endif +} + HistoryController::HistoryController(Frame* frame) : m_frame(frame) { @@ -81,7 +96,7 @@ void HistoryController::saveScrollPositionAndViewStateToItem(HistoryItem* item) */ void HistoryController::restoreScrollPositionAndViewState() { - if (!m_frame->loader()->committedFirstRealDocumentLoad()) + if (!m_frame->loader()->stateMachine()->committedFirstRealDocumentLoad()) return; ASSERT(m_currentItem); @@ -106,17 +121,13 @@ void HistoryController::restoreScrollPositionAndViewState() void HistoryController::updateBackForwardListForFragmentScroll() { updateBackForwardListClippedAtTarget(false); - - // Since the document isn't changed as a result of a fragment scroll, we should - // preserve the DocumentSequenceNumber of the previous item. - m_currentItem->setDocumentSequenceNumber(m_previousItem->documentSequenceNumber()); } void HistoryController::saveDocumentState() { // FIXME: Reading this bit of FrameLoader state here is unfortunate. I need to study // this more to see if we can remove this dependency. - if (m_frame->loader()->creatingInitialEmptyDocument()) + if (m_frame->loader()->stateMachine()->creatingInitialEmptyDocument()) return; // For a standard page load, we will have a previous item set, which will be used to @@ -138,7 +149,7 @@ void HistoryController::saveDocumentState() ASSERT(document); if (item->isCurrentDocument(document)) { - LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->name().string().utf8().data(), item); + LOG(Loading, "WebCoreLoading %s: saving form state to %p", m_frame->tree()->uniqueName().string().utf8().data(), item); item->setDocumentState(document->formElementsState()); } } @@ -177,7 +188,7 @@ void HistoryController::restoreDocumentState() if (!itemToRestore) return; - LOG(Loading, "WebCoreLoading %s: restoring form state from %p", m_frame->tree()->name().string().utf8().data(), itemToRestore); + LOG(Loading, "WebCoreLoading %s: restoring form state from %p", m_frame->tree()->uniqueName().string().utf8().data(), itemToRestore); doc->setStateForNewFormElements(itemToRestore->documentState()); } @@ -219,33 +230,13 @@ void HistoryController::goToItem(HistoryItem* targetItem, FrameLoadType type) // Set the BF cursor before commit, which lets the user quickly click back/forward again. // - plus, it only makes sense for the top level of the operation through the frametree, // as opposed to happening for some/one of the page commits that might happen soon - BackForwardList* bfList = page->backForwardList(); - HistoryItem* currentItem = bfList->currentItem(); - bfList->goToItem(targetItem); + HistoryItem* currentItem = page->backForward()->currentItem(); + page->backForward()->setCurrentItem(targetItem); Settings* settings = m_frame->settings(); page->setGlobalHistoryItem((!settings || settings->privateBrowsingEnabled()) ? 0 : targetItem); recursiveGoToItem(targetItem, currentItem, type); } -// Walk the frame tree and ensure that the URLs match the URLs in the item. -bool HistoryController::urlsMatchItem(HistoryItem* item) const -{ - const KURL& currentURL = m_frame->loader()->documentLoader()->url(); - if (!equalIgnoringFragmentIdentifier(currentURL, item->url())) - return false; - - const HistoryItemVector& childItems = item->children(); - - unsigned size = childItems.size(); - for (unsigned i = 0; i < size; ++i) { - Frame* childFrame = m_frame->tree()->child(childItems[i]->target()); - if (childFrame && !childFrame->loader()->history()->urlsMatchItem(childItems[i].get())) - return false; - } - - return true; -} - void HistoryController::updateForBackForwardNavigation() { #if !LOG_DISABLED @@ -282,7 +273,7 @@ void HistoryController::updateForReload() // 2) Global history: Handled by the client. // 3) Visited links: Handled by the PageGroup. -void HistoryController::updateForStandardLoad() +void HistoryController::updateForStandardLoad(HistoryUpdateType updateType) { LOG(History, "WebCoreHistory: Updating History for Standard Load in frame %s", m_frame->loader()->documentLoader()->url().string().ascii().data()); @@ -294,7 +285,8 @@ void HistoryController::updateForStandardLoad() if (!frameLoader->documentLoader()->isClientRedirect()) { if (!historyURL.isEmpty()) { - updateBackForwardListClippedAtTarget(true); + if (updateType != UpdateAllExceptBackForwardList) + updateBackForwardListClippedAtTarget(true); if (!needPrivacy) { frameLoader->client()->updateGlobalHistory(); frameLoader->documentLoader()->setDidCreateGlobalHistoryEntry(true); @@ -302,7 +294,7 @@ void HistoryController::updateForStandardLoad() frameLoader->client()->updateGlobalHistoryRedirectLinks(); } if (Page* page = m_frame->page()) - page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForwardList()->currentItem()); + page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForward()->currentItem()); } } else if (frameLoader->documentLoader()->unreachableURL().isEmpty() && m_currentItem) { m_currentItem->setURL(frameLoader->documentLoader()->url()); @@ -311,7 +303,7 @@ void HistoryController::updateForStandardLoad() if (!historyURL.isEmpty() && !needPrivacy) { if (Page* page = m_frame->page()) - page->group().addVisitedLink(historyURL); + addVisitedLink(page, historyURL); if (!frameLoader->documentLoader()->didCreateGlobalHistoryEntry() && frameLoader->documentLoader()->unreachableURL().isEmpty() && !frameLoader->url().isEmpty()) frameLoader->client()->updateGlobalHistoryRedirectLinks(); @@ -340,7 +332,7 @@ void HistoryController::updateForRedirectWithLockedBackForwardList() m_frame->loader()->client()->updateGlobalHistoryRedirectLinks(); } if (Page* page = m_frame->page()) - page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForwardList()->currentItem()); + page->setGlobalHistoryItem(needPrivacy ? 0 : page->backForward()->currentItem()); } } if (m_currentItem) { @@ -355,7 +347,7 @@ void HistoryController::updateForRedirectWithLockedBackForwardList() if (!historyURL.isEmpty() && !needPrivacy) { if (Page* page = m_frame->page()) - page->group().addVisitedLink(historyURL); + addVisitedLink(page, historyURL); if (!m_frame->loader()->documentLoader()->didCreateGlobalHistoryEntry() && m_frame->loader()->documentLoader()->unreachableURL().isEmpty() && !m_frame->loader()->url().isEmpty()) m_frame->loader()->client()->updateGlobalHistoryRedirectLinks(); @@ -382,7 +374,7 @@ void HistoryController::updateForClientRedirect() if (!historyURL.isEmpty() && !needPrivacy) { if (Page* page = m_frame->page()) - page->group().addVisitedLink(historyURL); + addVisitedLink(page, historyURL); } } @@ -420,7 +412,7 @@ void HistoryController::updateForSameDocumentNavigation() if (!page) return; - page->group().addVisitedLink(m_frame->loader()->url()); + addVisitedLink(page, m_frame->loader()->url()); } void HistoryController::updateForFrameLoadCompleted() @@ -441,6 +433,15 @@ void HistoryController::setCurrentItemTitle(const String& title) m_currentItem->setTitle(title); } +bool HistoryController::currentItemShouldBeReplaced() const +{ + // From the HTML5 spec for location.assign(): + // "If the browsing context's session history contains only one Document, + // and that was the about:blank Document created when the browsing context + // was created, then the navigation must be done with replacement enabled." + return m_currentItem && !m_previousItem && equalIgnoringCase(m_currentItem->urlString(), blankURL()); +} + void HistoryController::setProvisionalItem(HistoryItem* item) { m_provisionalItem = item; @@ -448,9 +449,9 @@ void HistoryController::setProvisionalItem(HistoryItem* item) PassRefPtr<HistoryItem> HistoryController::createItem(bool useOriginal) { - DocumentLoader* docLoader = m_frame->loader()->documentLoader(); + DocumentLoader* documentLoader = m_frame->loader()->documentLoader(); - KURL unreachableURL = docLoader ? docLoader->unreachableURL() : KURL(); + KURL unreachableURL = documentLoader ? documentLoader->unreachableURL() : KURL(); KURL url; KURL originalURL; @@ -459,11 +460,11 @@ PassRefPtr<HistoryItem> HistoryController::createItem(bool useOriginal) url = unreachableURL; originalURL = unreachableURL; } else { - originalURL = docLoader ? docLoader->originalURL() : KURL(); + originalURL = documentLoader ? documentLoader->originalURL() : KURL(); if (useOriginal) url = originalURL; - else if (docLoader) - url = docLoader->requestURL(); + else if (documentLoader) + url = documentLoader->requestURL(); } LOG(History, "WebCoreHistory: Creating item for %s", url.string().ascii().data()); @@ -479,21 +480,21 @@ PassRefPtr<HistoryItem> HistoryController::createItem(bool useOriginal) originalURL = blankURL(); Frame* parentFrame = m_frame->tree()->parent(); - String parent = parentFrame ? parentFrame->tree()->name() : ""; - String title = docLoader ? docLoader->title() : ""; + String parent = parentFrame ? parentFrame->tree()->uniqueName() : ""; + String title = documentLoader ? documentLoader->title() : ""; - RefPtr<HistoryItem> item = HistoryItem::create(url, m_frame->tree()->name(), parent, title); + RefPtr<HistoryItem> item = HistoryItem::create(url, m_frame->tree()->uniqueName(), parent, title); item->setOriginalURLString(originalURL.string()); - if (!unreachableURL.isEmpty() || !docLoader || docLoader->response().httpStatusCode() >= 400) + if (!unreachableURL.isEmpty() || !documentLoader || documentLoader->response().httpStatusCode() >= 400) item->setLastVisitWasFailure(true); // Save form state if this is a POST - if (docLoader) { + if (documentLoader) { if (useOriginal) - item->setFormInfoFromRequest(docLoader->originalRequest()); + item->setFormInfoFromRequest(documentLoader->originalRequest()); else - item->setFormInfoFromRequest(docLoader->request()); + item->setFormInfoFromRequest(documentLoader->request()); } // Set the item for which we will save document state @@ -508,9 +509,21 @@ PassRefPtr<HistoryItem> HistoryController::createItemTree(Frame* targetFrame, bo RefPtr<HistoryItem> bfItem = createItem(m_frame->tree()->parent() ? true : false); if (m_previousItem) saveScrollPositionAndViewStateToItem(m_previousItem.get()); - if (!(clipAtTarget && m_frame == targetFrame)) { + + if (!clipAtTarget || m_frame != targetFrame) { // save frame state for items that aren't loading (khtml doesn't save those) saveDocumentState(); + + // clipAtTarget is false for navigations within the same document, so + // we should copy the documentSequenceNumber over to the newly create + // item. Non-target items are just clones, and they should therefore + // preserve the same itemSequenceNumber. + if (m_previousItem) { + if (m_frame != targetFrame) + bfItem->setItemSequenceNumber(m_previousItem->itemSequenceNumber()); + bfItem->setDocumentSequenceNumber(m_previousItem->documentSequenceNumber()); + } + for (Frame* child = m_frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) { FrameLoader* childLoader = child->loader(); bool hasChildLoaded = childLoader->frameHasLoaded(); @@ -523,6 +536,7 @@ PassRefPtr<HistoryItem> HistoryController::createItemTree(Frame* targetFrame, bo bfItem->addChildItem(childLoader->history()->createItemTree(targetFrame, clipAtTarget)); } } + // FIXME: Eliminate the isTargetItem flag in favor of itemSequenceNumber. if (m_frame == targetFrame) bfItem->setIsTargetItem(true); return bfItem; @@ -537,21 +551,16 @@ void HistoryController::recursiveGoToItem(HistoryItem* item, HistoryItem* fromIt ASSERT(item); ASSERT(fromItem); - KURL itemURL = item->url(); - KURL currentURL; - if (m_frame->loader()->documentLoader()) - currentURL = m_frame->loader()->documentLoader()->url(); - - // Always reload the target frame of the item we're going to. This ensures that we will - // do -some- load for the transition, which means a proper notification will be posted - // to the app. - // The exact URL has to match, including fragment. We want to go through the _load - // method, even if to do a within-page navigation. - // The current frame tree and the frame tree snapshot in the item have to match. - if (!item->isTargetItem() && - itemURL == currentURL && - ((m_frame->tree()->name().isEmpty() && item->target().isEmpty()) || m_frame->tree()->name() == item->target()) && - childFramesMatchItem(item)) + // If the item we're going to is a clone of the item we're at, then do + // not load it again, and continue history traversal to its children. + // The current frame tree and the frame tree snapshot in the item have + // to match. + // Note: If item and fromItem are the same, then we need to create a new + // document. + if (item != fromItem + && item->itemSequenceNumber() == fromItem->itemSequenceNumber() + && currentFramesMatchItem(item) + && fromItem->hasSameFrames(item)) { // This content is good, so leave it alone and look for children that need reloading // Save form state (works from currentItem, since prevItem is nil) @@ -576,7 +585,7 @@ void HistoryController::recursiveGoToItem(HistoryItem* item, HistoryItem* fromIt for (int i = 0; i < size; ++i) { String childFrameName = childItems[i]->target(); HistoryItem* fromChildItem = fromItem->childItemWithTarget(childFrameName); - ASSERT(fromChildItem || fromItem->isTargetItem()); + ASSERT(fromChildItem); Frame* childFrame = m_frame->tree()->child(childFrameName); ASSERT(childFrame); childFrame->loader()->history()->recursiveGoToItem(childItems[i].get(), fromChildItem, type); @@ -586,10 +595,12 @@ void HistoryController::recursiveGoToItem(HistoryItem* item, HistoryItem* fromIt } } -// helper method that determines whether the subframes described by the item's subitems -// match our own current frameset -bool HistoryController::childFramesMatchItem(HistoryItem* item) const +// Helper method that determines whether the current frame tree matches given history item's. +bool HistoryController::currentFramesMatchItem(HistoryItem* item) const { + if ((!m_frame->tree()->uniqueName().isEmpty() || !item->target().isEmpty()) && m_frame->tree()->uniqueName() != item->target()) + return false; + const HistoryItemVector& childItems = item->children(); if (childItems.size() != m_frame->tree()->childCount()) return false; @@ -600,7 +611,6 @@ bool HistoryController::childFramesMatchItem(HistoryItem* item) const return false; } - // Found matches for all item targets return true; } @@ -624,43 +634,40 @@ void HistoryController::updateBackForwardListClippedAtTarget(bool doClip) frameLoader->checkDidPerformFirstNavigation(); - RefPtr<HistoryItem> item = frameLoader->history()->createItemTree(m_frame, doClip); - LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", item.get(), m_frame->loader()->documentLoader()->url().string().ascii().data()); - page->backForwardList()->addItem(item); + RefPtr<HistoryItem> topItem = frameLoader->history()->createItemTree(m_frame, doClip); + LOG(BackForward, "WebCoreBackForward - Adding backforward item %p for frame %s", topItem.get(), m_frame->loader()->documentLoader()->url().string().ascii().data()); + page->backForward()->addItem(topItem.release()); } void HistoryController::pushState(PassRefPtr<SerializedScriptValue> stateObject, const String& title, const String& urlString) { + if (!m_currentItem) + return; + Page* page = m_frame->page(); ASSERT(page); // Get a HistoryItem tree for the current frame tree. - RefPtr<HistoryItem> item = createItemTree(m_frame, false); - ASSERT(item->isTargetItem()); - - // Override data in the target item to reflect the pushState() arguments. - item->setTitle(title); - item->setStateObject(stateObject); - item->setURLString(urlString); - - // Since the document isn't changed as a result of a pushState call, we - // should preserve the DocumentSequenceNumber of the previous item. - item->setDocumentSequenceNumber(m_previousItem->documentSequenceNumber()); + RefPtr<HistoryItem> topItem = page->mainFrame()->loader()->history()->createItemTree(m_frame, false); - page->backForwardList()->pushStateItem(item.release()); + // Override data in the current item (created by createItemTree) to reflect + // the pushState() arguments. + m_currentItem->setTitle(title); + m_currentItem->setStateObject(stateObject); + m_currentItem->setURLString(urlString); + + page->backForward()->addItem(topItem.release()); } void HistoryController::replaceState(PassRefPtr<SerializedScriptValue> stateObject, const String& title, const String& urlString) { - Page* page = m_frame->page(); - ASSERT(page); - HistoryItem* current = page->backForwardList()->currentItem(); - ASSERT(current); + if (!m_currentItem) + return; if (!urlString.isEmpty()) - current->setURLString(urlString); - current->setTitle(title); - current->setStateObject(stateObject); + m_currentItem->setURLString(urlString); + m_currentItem->setTitle(title); + m_currentItem->setStateObject(stateObject); } } // namespace WebCore diff --git a/WebCore/loader/HistoryController.h b/WebCore/loader/HistoryController.h index 7c4a1ac..1002dbc 100644 --- a/WebCore/loader/HistoryController.h +++ b/WebCore/loader/HistoryController.h @@ -43,6 +43,8 @@ class SerializedScriptValue; class HistoryController : public Noncopyable { public: + enum HistoryUpdateType { UpdateAll, UpdateAllExceptBackForwardList }; + HistoryController(Frame*); ~HistoryController(); @@ -58,11 +60,10 @@ public: void invalidateCurrentItemCachedPage(); void goToItem(HistoryItem*, FrameLoadType); - bool urlsMatchItem(HistoryItem*) const; void updateForBackForwardNavigation(); void updateForReload(); - void updateForStandardLoad(); + void updateForStandardLoad(HistoryUpdateType updateType = UpdateAll); void updateForRedirectWithLockedBackForwardList(); void updateForClientRedirect(); void updateForCommit(); @@ -72,6 +73,9 @@ public: HistoryItem* currentItem() const { return m_currentItem.get(); } void setCurrentItem(HistoryItem*); void setCurrentItemTitle(const String&); + bool currentItemShouldBeReplaced() const; + + HistoryItem* previousItem() const { return m_previousItem.get(); } HistoryItem* provisionalItem() const { return m_provisionalItem.get(); } void setProvisionalItem(HistoryItem*); @@ -84,7 +88,7 @@ private: PassRefPtr<HistoryItem> createItemTree(Frame* targetFrame, bool clipAtTarget); void recursiveGoToItem(HistoryItem*, HistoryItem*, FrameLoadType); - bool childFramesMatchItem(HistoryItem*) const; + bool currentFramesMatchItem(HistoryItem*) const; void updateBackForwardListClippedAtTarget(bool doClip); Frame* m_frame; diff --git a/WebCore/loader/ImageDocument.cpp b/WebCore/loader/ImageDocument.cpp deleted file mode 100644 index 2f564cc..0000000 --- a/WebCore/loader/ImageDocument.cpp +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * 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, - * 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 "ImageDocument.h" - -#include "CSSStyleDeclaration.h" -#include "CachedImage.h" -#include "DocumentLoader.h" -#include "Element.h" -#include "EventListener.h" -#include "EventNames.h" -#include "Frame.h" -#include "FrameLoader.h" -#include "FrameLoaderClient.h" -#include "FrameView.h" -#include "HTMLImageElement.h" -#include "HTMLNames.h" -#include "LocalizedStrings.h" -#include "MouseEvent.h" -#include "NotImplemented.h" -#include "Page.h" -#include "SegmentedString.h" -#include "Settings.h" -#include "Text.h" -#include "XMLTokenizer.h" - -using std::min; - -namespace WebCore { - -using namespace HTMLNames; - -class ImageEventListener : public EventListener { -public: - static PassRefPtr<ImageEventListener> create(ImageDocument* document) { return adoptRef(new ImageEventListener(document)); } - static const ImageEventListener* cast(const EventListener* listener) - { - return listener->type() == ImageEventListenerType - ? static_cast<const ImageEventListener*>(listener) - : 0; - } - - virtual bool operator==(const EventListener& other); - -private: - ImageEventListener(ImageDocument* document) - : EventListener(ImageEventListenerType) - , m_doc(document) - { - } - - virtual void handleEvent(ScriptExecutionContext*, Event*); - - ImageDocument* m_doc; -}; - -class ImageTokenizer : public Tokenizer { -public: - ImageTokenizer(ImageDocument* doc) : m_doc(doc) {} - - virtual void write(const SegmentedString&, bool appendData); - virtual void finish(); - virtual bool isWaitingForScripts() const; - - virtual bool wantsRawData() const { return true; } - virtual bool writeRawData(const char* data, int len); - -private: - ImageDocument* m_doc; -}; - -class ImageDocumentElement : public HTMLImageElement { -public: - ImageDocumentElement(ImageDocument* doc) - : HTMLImageElement(imgTag, doc) - , m_imageDocument(doc) - { - } - - virtual ~ImageDocumentElement(); - virtual void willMoveToNewOwnerDocument(); - -private: - ImageDocument* m_imageDocument; -}; - -// -------- - -void ImageTokenizer::write(const SegmentedString&, bool) -{ - // <https://bugs.webkit.org/show_bug.cgi?id=25397>: JS code can always call document.write, we need to handle it. - notImplemented(); -} - -bool ImageTokenizer::writeRawData(const char*, int) -{ - Frame* frame = m_doc->frame(); - Settings* settings = frame->settings(); - if (!frame->loader()->client()->allowImages(!settings || settings->areImagesEnabled())) - return false; - - CachedImage* cachedImage = m_doc->cachedImage(); - cachedImage->data(frame->loader()->documentLoader()->mainResourceData(), false); - - m_doc->imageChanged(); - - return false; -} - -void ImageTokenizer::finish() -{ - if (!m_parserStopped && m_doc->imageElement()) { - CachedImage* cachedImage = m_doc->cachedImage(); - RefPtr<SharedBuffer> data = m_doc->frame()->loader()->documentLoader()->mainResourceData(); - - // If this is a multipart image, make a copy of the current part, since the resource data - // will be overwritten by the next part. - if (m_doc->frame()->loader()->documentLoader()->isLoadingMultipartContent()) - data = data->copy(); - - cachedImage->data(data.release(), true); - cachedImage->finish(); - - cachedImage->setResponse(m_doc->frame()->loader()->documentLoader()->response()); - - IntSize size = cachedImage->imageSize(m_doc->frame()->pageZoomFactor()); - if (size.width()) { - // Compute the title, we use the decoded filename of the resource, falling - // back on the (decoded) hostname if there is no path. - String fileName = decodeURLEscapeSequences(m_doc->url().lastPathComponent()); - if (fileName.isEmpty()) - fileName = m_doc->url().host(); - m_doc->setTitle(imageTitle(fileName, size)); - } - - m_doc->imageChanged(); - } - - m_doc->finishedParsing(); -} - -bool ImageTokenizer::isWaitingForScripts() const -{ - // An image document is never waiting for scripts - return false; -} - -// -------- - -ImageDocument::ImageDocument(Frame* frame) - : HTMLDocument(frame) - , m_imageElement(0) - , m_imageSizeIsKnown(false) - , m_didShrinkImage(false) - , m_shouldShrinkImage(shouldShrinkToFit()) -{ - setParseMode(Compat); -} - -Tokenizer* ImageDocument::createTokenizer() -{ - return new ImageTokenizer(this); -} - -void ImageDocument::createDocumentStructure() -{ - ExceptionCode ec; - - RefPtr<Element> rootElement = Document::createElement(htmlTag, false); - appendChild(rootElement, ec); - - RefPtr<Element> body = Document::createElement(bodyTag, false); - body->setAttribute(styleAttr, "margin: 0px;"); - - rootElement->appendChild(body, ec); - - RefPtr<ImageDocumentElement> imageElement = new ImageDocumentElement(this); - - imageElement->setAttribute(styleAttr, "-webkit-user-select: none"); - imageElement->setLoadManually(true); - imageElement->setSrc(url().string()); - - body->appendChild(imageElement, ec); - - if (shouldShrinkToFit()) { - // Add event listeners - RefPtr<EventListener> listener = ImageEventListener::create(this); - if (DOMWindow* domWindow = this->domWindow()) - domWindow->addEventListener("resize", listener, false); - imageElement->addEventListener("click", listener.release(), false); - } - - m_imageElement = imageElement.get(); -} - -float ImageDocument::scale() const -{ - if (!m_imageElement) - return 1.0f; - - IntSize imageSize = m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()); - IntSize windowSize = IntSize(frame()->view()->width(), frame()->view()->height()); - - float widthScale = (float)windowSize.width() / imageSize.width(); - float heightScale = (float)windowSize.height() / imageSize.height(); - - return min(widthScale, heightScale); -} - -void ImageDocument::resizeImageToFit() -{ - if (!m_imageElement) - return; - - IntSize imageSize = m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()); - - float scale = this->scale(); - m_imageElement->setWidth(static_cast<int>(imageSize.width() * scale)); - m_imageElement->setHeight(static_cast<int>(imageSize.height() * scale)); - - ExceptionCode ec; - m_imageElement->style()->setProperty("cursor", "-webkit-zoom-in", ec); -} - -void ImageDocument::imageClicked(int x, int y) -{ - if (!m_imageSizeIsKnown || imageFitsInWindow()) - return; - - m_shouldShrinkImage = !m_shouldShrinkImage; - - if (m_shouldShrinkImage) - windowSizeChanged(); - else { - restoreImageSize(); - - updateLayout(); - - float scale = this->scale(); - - int scrollX = static_cast<int>(x / scale - (float)frame()->view()->width() / 2); - int scrollY = static_cast<int>(y / scale - (float)frame()->view()->height() / 2); - - frame()->view()->setScrollPosition(IntPoint(scrollX, scrollY)); - } -} - -void ImageDocument::imageChanged() -{ - ASSERT(m_imageElement); - - if (m_imageSizeIsKnown) - return; - - if (m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()).isEmpty()) - return; - - m_imageSizeIsKnown = true; - - if (shouldShrinkToFit()) { - // Force resizing of the image - windowSizeChanged(); - } -} - -void ImageDocument::restoreImageSize() -{ - if (!m_imageElement || !m_imageSizeIsKnown) - return; - - m_imageElement->setWidth(m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()).width()); - m_imageElement->setHeight(m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()).height()); - - ExceptionCode ec; - if (imageFitsInWindow()) - m_imageElement->style()->removeProperty("cursor", ec); - else - m_imageElement->style()->setProperty("cursor", "-webkit-zoom-out", ec); - - m_didShrinkImage = false; -} - -bool ImageDocument::imageFitsInWindow() const -{ - if (!m_imageElement) - return true; - - IntSize imageSize = m_imageElement->cachedImage()->imageSize(frame()->pageZoomFactor()); - IntSize windowSize = IntSize(frame()->view()->width(), frame()->view()->height()); - - return imageSize.width() <= windowSize.width() && imageSize.height() <= windowSize.height(); -} - -void ImageDocument::windowSizeChanged() -{ - if (!m_imageElement || !m_imageSizeIsKnown) - return; - - bool fitsInWindow = imageFitsInWindow(); - - // If the image has been explicitly zoomed in, restore the cursor if the image fits - // and set it to a zoom out cursor if the image doesn't fit - if (!m_shouldShrinkImage) { - ExceptionCode ec; - - if (fitsInWindow) - m_imageElement->style()->removeProperty("cursor", ec); - else - m_imageElement->style()->setProperty("cursor", "-webkit-zoom-out", ec); - return; - } - - if (m_didShrinkImage) { - // If the window has been resized so that the image fits, restore the image size - // otherwise update the restored image size. - if (fitsInWindow) - restoreImageSize(); - else - resizeImageToFit(); - } else { - // If the image isn't resized but needs to be, then resize it. - if (!fitsInWindow) { - resizeImageToFit(); - m_didShrinkImage = true; - } - } -} - -CachedImage* ImageDocument::cachedImage() -{ - if (!m_imageElement) - createDocumentStructure(); - - return m_imageElement->cachedImage(); -} - -bool ImageDocument::shouldShrinkToFit() const -{ - return frame()->page()->settings()->shrinksStandaloneImagesToFit() && - frame()->page()->mainFrame() == frame(); -} - -// -------- - -void ImageEventListener::handleEvent(ScriptExecutionContext*, Event* event) -{ - if (event->type() == eventNames().resizeEvent) - m_doc->windowSizeChanged(); - else if (event->type() == eventNames().clickEvent) { - MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); - m_doc->imageClicked(mouseEvent->x(), mouseEvent->y()); - } -} - -bool ImageEventListener::operator==(const EventListener& listener) -{ - if (const ImageEventListener* imageEventListener = ImageEventListener::cast(&listener)) - return m_doc == imageEventListener->m_doc; - return false; -} - -// -------- - -ImageDocumentElement::~ImageDocumentElement() -{ - if (m_imageDocument) - m_imageDocument->disconnectImageElement(); -} - -void ImageDocumentElement::willMoveToNewOwnerDocument() -{ - if (m_imageDocument) { - m_imageDocument->disconnectImageElement(); - m_imageDocument = 0; - } - HTMLImageElement::willMoveToNewOwnerDocument(); -} - -} diff --git a/WebCore/loader/ImageDocument.h b/WebCore/loader/ImageDocument.h deleted file mode 100644 index 080b250..0000000 --- a/WebCore/loader/ImageDocument.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 - * 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, - * 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 ImageDocument_h -#define ImageDocument_h - -#include "HTMLDocument.h" - -namespace WebCore { - -class ImageDocumentElement; - -class ImageDocument : public HTMLDocument { -public: - static PassRefPtr<ImageDocument> create(Frame* frame) - { - return adoptRef(new ImageDocument(frame)); - } - - CachedImage* cachedImage(); - ImageDocumentElement* imageElement() const { return m_imageElement; } - void disconnectImageElement() { m_imageElement = 0; } - - void windowSizeChanged(); - void imageChanged(); - void imageClicked(int x, int y); - -private: - ImageDocument(Frame*); - - virtual Tokenizer* createTokenizer(); - virtual bool isImageDocument() const { return true; } - - void createDocumentStructure(); - void resizeImageToFit(); - void restoreImageSize(); - bool imageFitsInWindow() const; - bool shouldShrinkToFit() const; - float scale() const; - - ImageDocumentElement* m_imageElement; - - // Whether enough of the image has been loaded to determine its size - bool m_imageSizeIsKnown; - - // Whether the image is shrunk to fit or not - bool m_didShrinkImage; - - // Whether the image should be shrunk or not - bool m_shouldShrinkImage; -}; - -} - -#endif // ImageDocument_h diff --git a/WebCore/loader/ImageLoader.cpp b/WebCore/loader/ImageLoader.cpp index c61d133..94a21a4 100644 --- a/WebCore/loader/ImageLoader.cpp +++ b/WebCore/loader/ImageLoader.cpp @@ -22,13 +22,21 @@ #include "config.h" #include "ImageLoader.h" -#include "CSSHelper.h" #include "CachedImage.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "Document.h" #include "Element.h" +#include "HTMLNames.h" +#include "HTMLObjectElement.h" #include "RenderImage.h" +#if ENABLE(SVG) +#include "RenderSVGImage.h" +#endif +#if ENABLE(VIDEO) +#include "RenderVideo.h" +#endif + #if !ASSERT_DISABLED // ImageLoader objects are allocated as members of other objects, so generic pointer check would always fail. namespace WTF { @@ -128,11 +136,8 @@ void ImageLoader::setImage(CachedImage* newImage) oldImage->removeClient(this); } - if (RenderObject* renderer = m_element->renderer()) { - if (!renderer->isImage()) - return; - toRenderImage(renderer)->resetAnimation(); - } + if (RenderImageResource* imageResource = renderImageResource()) + imageResource->resetAnimation(); } void ImageLoader::updateFromElement() @@ -155,13 +160,15 @@ void ImageLoader::updateFromElement() CachedImage* newImage = 0; if (!(attr.isNull() || (attr.isEmpty() && document->baseURI().isLocalFile()))) { if (m_loadManually) { - document->docLoader()->setAutoLoadImages(false); + bool autoLoadOtherImages = document->cachedResourceLoader()->autoLoadImages(); + document->cachedResourceLoader()->setAutoLoadImages(false); newImage = new CachedImage(sourceURI(attr)); newImage->setLoading(true); - newImage->setDocLoader(document->docLoader()); - document->docLoader()->m_documentResources.set(newImage->url(), newImage); + newImage->setCachedResourceLoader(document->cachedResourceLoader()); + document->cachedResourceLoader()->m_documentResources.set(newImage->url(), newImage); + document->cachedResourceLoader()->setAutoLoadImages(autoLoadOtherImages); } else - newImage = document->docLoader()->requestImage(sourceURI(attr)); + newImage = document->cachedResourceLoader()->requestImage(sourceURI(attr)); // If we do not have an image here, it means that a cross-site // violation occurred. @@ -191,11 +198,8 @@ void ImageLoader::updateFromElement() oldImage->removeClient(this); } - if (RenderObject* renderer = m_element->renderer()) { - if (!renderer->isImage()) - return; - toRenderImage(renderer)->resetAnimation(); - } + if (RenderImageResource* imageResource = renderImageResource()) + imageResource->resetAnimation(); } void ImageLoader::updateFromElementIgnoringPreviousError() @@ -219,20 +223,42 @@ void ImageLoader::notifyFinished(CachedResource*) loadEventSender().dispatchEventSoon(this); } +RenderImageResource* ImageLoader::renderImageResource() +{ + RenderObject* renderer = m_element->renderer(); + + if (!renderer) + return 0; + + if (renderer->isImage()) + return toRenderImage(renderer)->imageResource(); + +#if ENABLE(SVG) + if (renderer->isSVGImage()) + return toRenderSVGImage(renderer)->imageResource(); +#endif + +#if ENABLE(VIDEO) + if (renderer->isVideo()) + return toRenderVideo(renderer)->imageResource(); +#endif + + return 0; +} + void ImageLoader::updateRenderer() { - if (RenderObject* renderer = m_element->renderer()) { - if (!renderer->isImage() && !renderer->isVideo()) - return; - RenderImage* imageRenderer = toRenderImage(renderer); - - // Only update the renderer if it doesn't have an image or if what we have - // is a complete image. This prevents flickering in the case where a dynamic - // change is happening between two images. - CachedImage* cachedImage = imageRenderer->cachedImage(); - if (m_image != cachedImage && (m_imageComplete || !imageRenderer->cachedImage())) - imageRenderer->setCachedImage(m_image.get()); - } + RenderImageResource* imageResource = renderImageResource(); + + if (!imageResource) + return; + + // Only update the renderer if it doesn't have an image or if what we have + // is a complete image. This prevents flickering in the case where a dynamic + // change is happening between two images. + CachedImage* cachedImage = imageResource->cachedImage(); + if (m_image != cachedImage && (m_imageComplete || !cachedImage)) + imageResource->setCachedImage(m_image.get()); } void ImageLoader::dispatchPendingBeforeLoadEvent() @@ -253,6 +279,9 @@ void ImageLoader::dispatchPendingBeforeLoadEvent() m_image = 0; } loadEventSender().cancelEvent(this); + + if (m_element->hasTagName(HTMLNames::objectTag)) + static_cast<HTMLObjectElement*>(m_element)->renderFallbackContent(); } void ImageLoader::dispatchPendingLoadEvent() @@ -277,6 +306,11 @@ void ImageLoader::dispatchPendingLoadEvents() loadEventSender().dispatchPendingEvents(); } +void ImageLoader::elementWillMoveToNewOwnerDocument() +{ + setImage(0); +} + ImageEventSender::ImageEventSender(const AtomicString& eventType) : m_eventType(eventType) , m_timer(this, &ImageEventSender::timerFired) diff --git a/WebCore/loader/ImageLoader.h b/WebCore/loader/ImageLoader.h index 44fe98e..9bf7624 100644 --- a/WebCore/loader/ImageLoader.h +++ b/WebCore/loader/ImageLoader.h @@ -23,14 +23,15 @@ #ifndef ImageLoader_h #define ImageLoader_h -#include "AtomicString.h" #include "CachedResourceClient.h" #include "CachedResourceHandle.h" +#include <wtf/text/AtomicString.h> namespace WebCore { class Element; class ImageLoadEventSender; +class RenderImageResource; class ImageLoader : public CachedResourceClient { public: @@ -45,6 +46,8 @@ public: // doesn't change; starts new load unconditionally (matches Firefox and Opera behavior). void updateFromElementIgnoringPreviousError(); + void elementWillMoveToNewOwnerDocument(); + Element* element() const { return m_element; } bool imageComplete() const { return m_imageComplete; } @@ -70,6 +73,7 @@ private: void dispatchPendingBeforeLoadEvent(); void dispatchPendingLoadEvent(); + RenderImageResource* renderImageResource(); void updateRenderer(); Element* m_element; diff --git a/WebCore/loader/MainResourceLoader.cpp b/WebCore/loader/MainResourceLoader.cpp index 3e75880..7e5eb90 100644 --- a/WebCore/loader/MainResourceLoader.cpp +++ b/WebCore/loader/MainResourceLoader.cpp @@ -31,6 +31,7 @@ #include "MainResourceLoader.h" #include "ApplicationCacheHost.h" +#include "DocumentLoadTiming.h" #include "DocumentLoader.h" #include "FormState.h" #include "Frame.h" @@ -43,7 +44,9 @@ #endif #include "ResourceError.h" #include "ResourceHandle.h" +#include "SchemeRegistry.h" #include "Settings.h" +#include <wtf/CurrentTime.h> // FIXME: More that is in common with SubresourceLoader should move up into ResourceLoader. @@ -120,10 +123,17 @@ void MainResourceLoader::callContinueAfterNavigationPolicy(void* argument, const static_cast<MainResourceLoader*>(argument)->continueAfterNavigationPolicy(request, shouldContinue); } -void MainResourceLoader::continueAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue) +void MainResourceLoader::continueAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue) { if (!shouldContinue) stopLoadingForPolicyChange(); + else if (m_substituteData.isValid()) { + // A redirect resulted in loading substitute data. + ASSERT(documentLoader()->timing()->redirectCount); + handle()->cancel(); + handleDataLoadSoon(request); + } + deref(); // balances ref in willSendRequest } @@ -143,7 +153,7 @@ bool MainResourceLoader::isPostOrRedirectAfterPost(const ResourceRequest& newReq void MainResourceLoader::addData(const char* data, int length, bool allAtOnce) { ResourceLoader::addData(data, length, allAtOnce); - frameLoader()->receivedData(data, length); + documentLoader()->receivedData(data, length); } void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse) @@ -157,7 +167,17 @@ void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const Reso // The additional processing can do anything including possibly removing the last // reference to this object; one example of this is 3266216. RefPtr<MainResourceLoader> protect(this); - + + ASSERT(documentLoader()->timing()->fetchStart); + if (!redirectResponse.isNull()) { + DocumentLoadTiming* documentLoadTiming = documentLoader()->timing(); + documentLoadTiming->redirectCount++; + if (!documentLoadTiming->redirectStart) + documentLoadTiming->redirectStart = documentLoadTiming->fetchStart; + documentLoadTiming->redirectEnd = currentTime(); + documentLoadTiming->fetchStart = documentLoadTiming->redirectEnd; + } + // Update cookie policy base URL as URL changes, except for subframes, which use the // URL of the main frame which doesn't change when we redirect. if (frameLoader()->isLoadingMainFrame()) @@ -179,6 +199,14 @@ void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const Reso if (top != m_frame) frameLoader()->checkIfDisplayInsecureContent(top->document()->securityOrigin(), newRequest.url()); +#if ENABLE(OFFLINE_WEB_APPLICATIONS) + if (!redirectResponse.isNull()) { + // We checked application cache for initial URL, now we need to check it for redirected one. + ASSERT(!m_substituteData.isValid()); + documentLoader()->applicationCacheHost()->maybeLoadMainResourceForRedirect(newRequest, m_substituteData); + } +#endif + // FIXME: Ideally we'd stop the I/O until we hear back from the navigation policy delegate // listener. But there's no way to do that in practice. So instead we cancel later if the // listener tells us to. In practice that means the navigation policy needs to be decided @@ -194,7 +222,7 @@ static bool shouldLoadAsEmptyDocument(const KURL& url) #if PLATFORM(TORCHMOBILE) return url.isEmpty() || (url.protocolIs("about") && equalIgnoringRef(url, blankURL())); #else - return url.isEmpty() || url.protocolIs("about"); + return url.isEmpty() || SchemeRegistry::shouldLoadURLSchemeAsEmptyDocument(url.protocol()); #endif } @@ -223,7 +251,7 @@ void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy, receivedError(cannotShowURLError()); return; } - frameLoader()->client()->download(m_handle.get(), request(), m_handle.get()->request(), r); + frameLoader()->client()->download(m_handle.get(), request(), m_handle.get()->firstRequest(), r); // It might have gone missing if (frameLoader()) receivedError(interruptionForPolicyChangeError()); @@ -262,9 +290,9 @@ void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy, if (m_substituteData.content()->size()) didReceiveData(m_substituteData.content()->data(), m_substituteData.content()->size(), m_substituteData.content()->size(), true); if (frameLoader() && !frameLoader()->isStopping()) - didFinishLoading(); + didFinishLoading(0); } else if (shouldLoadAsEmptyDocument(url) || frameLoader()->representationExistsForURLScheme(url.protocol())) - didFinishLoading(); + didFinishLoading(0); } } @@ -285,15 +313,15 @@ void MainResourceLoader::continueAfterContentPolicy(PolicyAction policy) #if PLATFORM(QT) void MainResourceLoader::substituteMIMETypeFromPluginDatabase(const ResourceResponse& r) { - if (!m_frame->settings()->arePluginsEnabled()) + if (!m_frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin)) return; String filename = r.url().lastPathComponent(); if (filename.endsWith("/")) return; - int extensionPos = filename.reverseFind('.'); - if (extensionPos == -1) + size_t extensionPos = filename.reverseFind('.'); + if (extensionPos == notFound) return; String extension = filename.substring(extensionPos + 1); @@ -402,10 +430,12 @@ void MainResourceLoader::didReceiveData(const char* data, int length, long long // reference to this object; one example of this is 3266216. RefPtr<MainResourceLoader> protect(this); + m_timeOfLastDataReceived = currentTime(); + ResourceLoader::didReceiveData(data, length, lengthReceived, allAtOnce); } -void MainResourceLoader::didFinishLoading() +void MainResourceLoader::didFinishLoading(double finishTime) { // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. // See <rdar://problem/6304600> for more details. @@ -421,8 +451,10 @@ void MainResourceLoader::didFinishLoading() RefPtr<DocumentLoader> dl = documentLoader(); #endif + ASSERT(!documentLoader()->timing()->responseEnd); + documentLoader()->timing()->responseEnd = finishTime ? finishTime : m_timeOfLastDataReceived; frameLoader()->finishedLoading(); - ResourceLoader::didFinishLoading(); + ResourceLoader::didFinishLoading(finishTime); #if ENABLE(OFFLINE_WEB_APPLICATIONS) dl->applicationCacheHost()->finishedLoadingMainResource(); @@ -464,6 +496,10 @@ void MainResourceLoader::handleDataLoadNow(MainResourceLoaderTimer*) KURL url = m_substituteData.responseURL(); if (url.isEmpty()) url = m_initialRequest.url(); + + // Clear the initial request here so that subsequent entries into the + // loader will not think there's still a deferred load left to do. + m_initialRequest = ResourceRequest(); ResourceResponse response(url, m_substituteData.mimeType(), m_substituteData.content()->size(), m_substituteData.textEncoding(), ""); didReceiveResponse(response); @@ -479,7 +515,7 @@ void MainResourceLoader::startDataLoadTimer() #endif } -void MainResourceLoader::handleDataLoadSoon(ResourceRequest& r) +void MainResourceLoader::handleDataLoadSoon(const ResourceRequest& r) { m_initialRequest = r; @@ -517,7 +553,7 @@ bool MainResourceLoader::loadNow(ResourceRequest& r) else if (shouldLoadEmpty || frameLoader()->representationExistsForURLScheme(url.protocol())) handleEmptyLoad(url, !shouldLoadEmpty); else - m_handle = ResourceHandle::create(r, this, m_frame.get(), false, true, true); + m_handle = ResourceHandle::create(m_frame->loader()->networkingContext(), r, this, false, true); return false; } @@ -528,6 +564,9 @@ bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& su m_substituteData = substituteData; + ASSERT(documentLoader()->timing()->navigationStart); + ASSERT(!documentLoader()->timing()->fetchStart); + documentLoader()->timing()->fetchStart = currentTime(); ResourceRequest request(r); #if ENABLE(OFFLINE_WEB_APPLICATIONS) diff --git a/WebCore/loader/MainResourceLoader.h b/WebCore/loader/MainResourceLoader.h index eaaf2e8..1620f7a 100644 --- a/WebCore/loader/MainResourceLoader.h +++ b/WebCore/loader/MainResourceLoader.h @@ -26,6 +26,9 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef MainResourceLoader_h +#define MainResourceLoader_h + #include "FrameLoaderTypes.h" #include "ResourceLoader.h" #include "SubstituteData.h" @@ -55,7 +58,7 @@ namespace WebCore { virtual void willSendRequest(ResourceRequest&, const ResourceResponse& redirectResponse); virtual void didReceiveResponse(const ResourceResponse&); virtual void didReceiveData(const char*, int, long long lengthReceived, bool allAtOnce); - virtual void didFinishLoading(); + virtual void didFinishLoading(double finishTime); virtual void didFail(const ResourceError&); #if HAVE(RUNLOOP_TIMER) @@ -76,7 +79,7 @@ namespace WebCore { bool loadNow(ResourceRequest&); void handleEmptyLoad(const KURL&, bool forURLScheme); - void handleDataLoadSoon(ResourceRequest& r); + void handleDataLoadSoon(const ResourceRequest& r); void startDataLoadTimer(); void handleDataLoad(ResourceRequest&); @@ -104,6 +107,9 @@ namespace WebCore { bool m_loadingMultipartContent; bool m_waitingForContentPolicy; + double m_timeOfLastDataReceived; }; } + +#endif diff --git a/WebCore/loader/MediaDocument.cpp b/WebCore/loader/MediaDocument.cpp deleted file mode 100644 index a2d6276..0000000 --- a/WebCore/loader/MediaDocument.cpp +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" - -#if ENABLE(VIDEO) -#include "MediaDocument.h" - -#include "DocumentLoader.h" -#include "Element.h" -#include "Event.h" -#include "EventNames.h" -#include "Frame.h" -#include "FrameLoader.h" -#include "FrameLoaderClient.h" -#include "HTMLEmbedElement.h" -#include "HTMLNames.h" -#include "HTMLVideoElement.h" -#include "KeyboardEvent.h" -#include "MainResourceLoader.h" -#include "NodeList.h" -#include "Page.h" -#include "SegmentedString.h" -#include "Settings.h" -#include "Text.h" -#include "XMLTokenizer.h" - -namespace WebCore { - -using namespace HTMLNames; - -class MediaTokenizer : public Tokenizer { -public: - MediaTokenizer(Document* doc) : m_doc(doc), m_mediaElement(0) {} - -private: - virtual void write(const SegmentedString&, bool appendData); - virtual void stopParsing(); - virtual void finish(); - virtual bool isWaitingForScripts() const; - - virtual bool wantsRawData() const { return true; } - virtual bool writeRawData(const char* data, int len); - - void createDocumentStructure(); - - Document* m_doc; - HTMLMediaElement* m_mediaElement; -}; - -void MediaTokenizer::write(const SegmentedString&, bool) -{ - ASSERT_NOT_REACHED(); -} - -void MediaTokenizer::createDocumentStructure() -{ - ExceptionCode ec; - RefPtr<Element> rootElement = m_doc->createElement(htmlTag, false); - m_doc->appendChild(rootElement, 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->createElement(videoTag, false); - - m_mediaElement = static_cast<HTMLVideoElement*>(mediaElement.get()); - m_mediaElement->setAttribute(controlsAttr, ""); - m_mediaElement->setAttribute(autoplayAttr, ""); - m_mediaElement->setAttribute(styleAttr, "margin: auto; position: absolute; top: 0; right: 0; bottom: 0; left: 0;"); - - m_mediaElement->setAttribute(nameAttr, "media"); - m_mediaElement->setSrc(m_doc->url()); - - body->appendChild(mediaElement, ec); - - Frame* frame = m_doc->frame(); - if (!frame) - return; - - frame->loader()->activeDocumentLoader()->mainResourceLoader()->setShouldBufferData(false); -} - -bool MediaTokenizer::writeRawData(const char*, int) -{ - ASSERT(!m_mediaElement); - if (m_mediaElement) - return false; - - createDocumentStructure(); - finish(); - return false; -} - -void MediaTokenizer::stopParsing() -{ - Tokenizer::stopParsing(); -} - -void MediaTokenizer::finish() -{ - if (!m_parserStopped) - m_doc->finishedParsing(); -} - -bool MediaTokenizer::isWaitingForScripts() const -{ - // A media document is never waiting for scripts - return false; -} - -MediaDocument::MediaDocument(Frame* frame) - : HTMLDocument(frame) - , m_replaceMediaElementTimer(this, &MediaDocument::replaceMediaElementTimerFired) -{ - setParseMode(Compat); -} - -MediaDocument::~MediaDocument() -{ - ASSERT(!m_replaceMediaElementTimer.isActive()); -} - -Tokenizer* MediaDocument::createTokenizer() -{ - return new MediaTokenizer(this); -} - -void MediaDocument::defaultEventHandler(Event* event) -{ - // Match the default Quicktime plugin behavior to allow - // clicking and double-clicking to pause and play the media. - Node* targetNode = event->target()->toNode(); - if (targetNode && targetNode->hasTagName(videoTag)) { - HTMLVideoElement* video = static_cast<HTMLVideoElement*>(targetNode); - if (event->type() == eventNames().clickEvent) { - if (!video->canPlay()) { - video->pause(event->fromUserGesture()); - event->setDefaultHandled(); - } - } else if (event->type() == eventNames().dblclickEvent) { - if (video->canPlay()) { - video->play(event->fromUserGesture()); - event->setDefaultHandled(); - } - } - } - - if (event->type() == eventNames().keydownEvent && event->isKeyboardEvent()) { - HTMLVideoElement* video = 0; - if (targetNode) { - if (targetNode->hasTagName(videoTag)) - video = static_cast<HTMLVideoElement*>(targetNode); - else { - RefPtr<NodeList> nodeList = targetNode->getElementsByTagName("video"); - if (nodeList.get()->length() > 0) - video = static_cast<HTMLVideoElement*>(nodeList.get()->item(0)); - } - } - if (video) { - KeyboardEvent* keyboardEvent = static_cast<KeyboardEvent*>(event); - if (keyboardEvent->keyIdentifier() == "U+0020") { // space - if (video->paused()) { - if (video->canPlay()) - video->play(event->fromUserGesture()); - } else - video->pause(event->fromUserGesture()); - event->setDefaultHandled(); - } - } - } -} - -void MediaDocument::mediaElementSawUnsupportedTracks() -{ - // The HTMLMediaElement was told it has something that the underlying - // MediaPlayer cannot handle so we should switch from <video> to <embed> - // and let the plugin handle this. Don't do it immediately as this - // function may be called directly from a media engine callback, and - // replaceChild will destroy the element, media player, and media engine. - m_replaceMediaElementTimer.startOneShot(0); -} - -void MediaDocument::replaceMediaElementTimerFired(Timer<MediaDocument>*) -{ - HTMLElement* htmlBody = body(); - if (!htmlBody) - return; - - // Set body margin width and height to 0 as that is what a PluginDocument uses. - htmlBody->setAttribute(marginwidthAttr, "0"); - htmlBody->setAttribute(marginheightAttr, "0"); - - RefPtr<NodeList> nodeList = htmlBody->getElementsByTagName("video"); - - if (nodeList.get()->length() > 0) { - HTMLVideoElement* videoElement = static_cast<HTMLVideoElement*>(nodeList.get()->item(0)); - - RefPtr<Element> element = Document::createElement(embedTag, false); - HTMLEmbedElement* embedElement = static_cast<HTMLEmbedElement*>(element.get()); - - embedElement->setAttribute(widthAttr, "100%"); - embedElement->setAttribute(heightAttr, "100%"); - embedElement->setAttribute(nameAttr, "plugin"); - embedElement->setAttribute(srcAttr, url().string()); - embedElement->setAttribute(typeAttr, frame()->loader()->responseMIMEType()); - - ExceptionCode ec; - videoElement->parent()->replaceChild(embedElement, videoElement, ec); - } -} - -} -#endif diff --git a/WebCore/loader/NavigationScheduler.cpp b/WebCore/loader/NavigationScheduler.cpp new file mode 100644 index 0000000..28fda9a --- /dev/null +++ b/WebCore/loader/NavigationScheduler.cpp @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2009 Adam Barth. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "NavigationScheduler.h" + +#include "BackForwardController.h" +#include "DOMWindow.h" +#include "DocumentLoader.h" +#include "Event.h" +#include "FormState.h" +#include "FormSubmission.h" +#include "Frame.h" +#include "FrameLoadRequest.h" +#include "FrameLoader.h" +#include "FrameLoaderStateMachine.h" +#include "HTMLFormElement.h" +#include "HTMLFrameOwnerElement.h" +#include "HistoryItem.h" +#include "Page.h" +#include "UserGestureIndicator.h" +#include <wtf/CurrentTime.h> + +namespace WebCore { + +class ScheduledNavigation : public Noncopyable { +public: + ScheduledNavigation(double delay, bool lockHistory, bool lockBackForwardList, bool wasDuringLoad, bool isLocationChange) + : m_delay(delay) + , m_lockHistory(lockHistory) + , m_lockBackForwardList(lockBackForwardList) + , m_wasDuringLoad(wasDuringLoad) + , m_isLocationChange(isLocationChange) + , m_wasUserGesture(ScriptController::processingUserGesture()) + { + } + virtual ~ScheduledNavigation() { } + + virtual void fire(Frame*) = 0; + + virtual bool shouldStartTimer(Frame*) { return true; } + virtual void didStartTimer(Frame*, Timer<NavigationScheduler>*) { } + virtual void didStopTimer(Frame*, bool /* newLoadInProgress */) { } + + double delay() const { return m_delay; } + bool lockHistory() const { return m_lockHistory; } + bool lockBackForwardList() const { return m_lockBackForwardList; } + bool wasDuringLoad() const { return m_wasDuringLoad; } + bool isLocationChange() const { return m_isLocationChange; } + bool wasUserGesture() const { return m_wasUserGesture; } + +private: + double m_delay; + bool m_lockHistory; + bool m_lockBackForwardList; + bool m_wasDuringLoad; + bool m_isLocationChange; + bool m_wasUserGesture; +}; + +class ScheduledURLNavigation : public ScheduledNavigation { +public: + ScheduledURLNavigation(double delay, const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool duringLoad, bool isLocationChange) + : ScheduledNavigation(delay, lockHistory, lockBackForwardList, duringLoad, isLocationChange) + , m_url(url) + , m_referrer(referrer) + , m_haveToldClient(false) + { + } + + virtual void fire(Frame* frame) + { + UserGestureIndicator gestureIndicator(wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture); + frame->loader()->changeLocation(KURL(ParsedURLString, m_url), m_referrer, lockHistory(), lockBackForwardList(), false); + } + + virtual void didStartTimer(Frame* frame, Timer<NavigationScheduler>* timer) + { + if (m_haveToldClient) + return; + m_haveToldClient = true; + frame->loader()->clientRedirected(KURL(ParsedURLString, m_url), delay(), currentTime() + timer->nextFireInterval(), lockBackForwardList()); + } + + virtual void didStopTimer(Frame* frame, bool newLoadInProgress) + { + if (!m_haveToldClient) + return; + frame->loader()->clientRedirectCancelledOrFinished(newLoadInProgress); + } + + String url() const { return m_url; } + String referrer() const { return m_referrer; } + +private: + String m_url; + String m_referrer; + bool m_haveToldClient; +}; + +class ScheduledRedirect : public ScheduledURLNavigation { +public: + ScheduledRedirect(double delay, const String& url, bool lockHistory, bool lockBackForwardList) + : ScheduledURLNavigation(delay, url, String(), lockHistory, lockBackForwardList, false, false) + { + } + + virtual bool shouldStartTimer(Frame* frame) { return frame->loader()->allAncestorsAreComplete(); } +}; + +class ScheduledLocationChange : public ScheduledURLNavigation { +public: + ScheduledLocationChange(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool duringLoad) + : ScheduledURLNavigation(0.0, url, referrer, lockHistory, lockBackForwardList, duringLoad, true) { } +}; + +class ScheduledRefresh : public ScheduledURLNavigation { +public: + ScheduledRefresh(const String& url, const String& referrer) + : ScheduledURLNavigation(0.0, url, referrer, true, true, false, true) + { + } + + virtual void fire(Frame* frame) + { + UserGestureIndicator gestureIndicator(wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture); + frame->loader()->changeLocation(KURL(ParsedURLString, url()), referrer(), lockHistory(), lockBackForwardList(), true); + } +}; + +class ScheduledHistoryNavigation : public ScheduledNavigation { +public: + explicit ScheduledHistoryNavigation(int historySteps) + : ScheduledNavigation(0, false, false, false, true) + , m_historySteps(historySteps) + { + } + + virtual void fire(Frame* frame) + { + UserGestureIndicator gestureIndicator(wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture); + + FrameLoader* loader = frame->loader(); + if (!m_historySteps) { + // Special case for go(0) from a frame -> reload only the frame + // To follow Firefox and IE's behavior, history reload can only navigate the self frame. + loader->urlSelected(loader->url(), "_self", 0, lockHistory(), lockBackForwardList(), SendReferrer); + return; + } + // go(i!=0) from a frame navigates into the history of the frame only, + // in both IE and NS (but not in Mozilla). We can't easily do that. + frame->page()->backForward()->goBackOrForward(m_historySteps); + } + +private: + int m_historySteps; +}; + +class ScheduledFormSubmission : public ScheduledNavigation { +public: + ScheduledFormSubmission(PassRefPtr<FormSubmission> submission, bool lockBackForwardList, bool duringLoad) + : ScheduledNavigation(0, submission->lockHistory(), lockBackForwardList, duringLoad, true) + , m_submission(submission) + , m_haveToldClient(false) + { + ASSERT(m_submission->state()); + } + + virtual void fire(Frame* frame) + { + UserGestureIndicator gestureIndicator(wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture); + + // The submitForm function will find a target frame before using the redirection timer. + // Now that the timer has fired, we need to repeat the security check which normally is done when + // selecting a target, in case conditions have changed. Other code paths avoid this by targeting + // without leaving a time window. If we fail the check just silently drop the form submission. + if (!m_submission->state()->sourceFrame()->loader()->shouldAllowNavigation(frame)) + return; + FrameLoadRequest frameRequest; + m_submission->populateFrameLoadRequest(frameRequest); + frame->loader()->loadFrameRequest(frameRequest, lockHistory(), lockBackForwardList(), m_submission->event(), m_submission->state(), SendReferrer); + } + + virtual void didStartTimer(Frame* frame, Timer<NavigationScheduler>* timer) + { + if (m_haveToldClient) + return; + m_haveToldClient = true; + frame->loader()->clientRedirected(m_submission->requestURL(), delay(), currentTime() + timer->nextFireInterval(), lockBackForwardList()); + } + + virtual void didStopTimer(Frame* frame, bool newLoadInProgress) + { + if (!m_haveToldClient) + return; + frame->loader()->clientRedirectCancelledOrFinished(newLoadInProgress); + } + +private: + RefPtr<FormSubmission> m_submission; + bool m_haveToldClient; +}; + +NavigationScheduler::NavigationScheduler(Frame* frame) + : m_frame(frame) + , m_timer(this, &NavigationScheduler::timerFired) +{ +} + +NavigationScheduler::~NavigationScheduler() +{ +} + +bool NavigationScheduler::redirectScheduledDuringLoad() +{ + return m_redirect && m_redirect->wasDuringLoad(); +} + +bool NavigationScheduler::locationChangePending() +{ + return m_redirect && m_redirect->isLocationChange(); +} + +void NavigationScheduler::clear() +{ + m_timer.stop(); + m_redirect.clear(); +} + +void NavigationScheduler::scheduleRedirect(double delay, const String& url) +{ + if (!m_frame->page()) + return; + if (delay < 0 || delay > INT_MAX / 1000) + return; + if (url.isEmpty()) + return; + + // We want a new back/forward list item if the refresh timeout is > 1 second. + if (!m_redirect || delay <= m_redirect->delay()) + schedule(adoptPtr(new ScheduledRedirect(delay, url, true, delay <= 1))); +} + +bool NavigationScheduler::mustLockBackForwardList(Frame* targetFrame) +{ + // Non-user navigation before the page has finished firing onload should not create a new back/forward item. + // See https://webkit.org/b/42861 for the original motivation for this. + if (!ScriptController::processingUserGesture() && targetFrame->loader()->documentLoader() && !targetFrame->loader()->documentLoader()->wasOnloadHandled()) + return true; + + // Navigation of a subframe during loading of an ancestor frame does not create a new back/forward item. + // The definition of "during load" is any time before all handlers for the load event have been run. + // See https://bugs.webkit.org/show_bug.cgi?id=14957 for the original motivation for this. + for (Frame* ancestor = targetFrame->tree()->parent(); ancestor; ancestor = ancestor->tree()->parent()) { + Document* document = ancestor->document(); + if (!ancestor->loader()->isComplete() || (document && document->processingLoadEvent())) + return true; + } + return false; +} + +void NavigationScheduler::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList) +{ + if (!m_frame->page()) + return; + if (url.isEmpty()) + return; + + lockBackForwardList = lockBackForwardList || mustLockBackForwardList(m_frame); + + FrameLoader* loader = m_frame->loader(); + + // 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(ParsedURLString, url); + if (parsedURL.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(loader->url(), parsedURL)) { + loader->changeLocation(loader->completeURL(url), referrer, lockHistory, lockBackForwardList); + return; + } + + // Handle a location change of a page with no document as a special case. + // This may happen when a frame changes the location of another frame. + bool duringLoad = !loader->stateMachine()->committedFirstRealDocumentLoad(); + + schedule(adoptPtr(new ScheduledLocationChange(url, referrer, lockHistory, lockBackForwardList, duringLoad))); +} + +void NavigationScheduler::scheduleFormSubmission(PassRefPtr<FormSubmission> submission) +{ + ASSERT(m_frame->page()); + + // FIXME: Do we need special handling for form submissions where the URL is the same + // as the current one except for the fragment part? See scheduleLocationChange above. + + // Handle a location change of a page with no document as a special case. + // This may happen when a frame changes the location of another frame. + bool duringLoad = !m_frame->loader()->stateMachine()->committedFirstRealDocumentLoad(); + + // If this is a child frame and the form submission was triggered by a script, lock the back/forward list + // to match IE and Opera. + // See https://bugs.webkit.org/show_bug.cgi?id=32383 for the original motivation for this. + bool lockBackForwardList = mustLockBackForwardList(m_frame) + || (submission->state()->formSubmissionTrigger() == SubmittedByJavaScript + && m_frame->tree()->parent() && !ScriptController::processingUserGesture()); + + schedule(adoptPtr(new ScheduledFormSubmission(submission, lockBackForwardList, duringLoad))); +} + +void NavigationScheduler::scheduleRefresh() +{ + if (!m_frame->page()) + return; + const KURL& url = m_frame->loader()->url(); + if (url.isEmpty()) + return; + + schedule(adoptPtr(new ScheduledRefresh(url.string(), m_frame->loader()->outgoingReferrer()))); +} + +void NavigationScheduler::scheduleHistoryNavigation(int steps) +{ + if (!m_frame->page()) + return; + + // Invalid history navigations (such as history.forward() during a new load) have the side effect of cancelling any scheduled + // redirects. We also avoid the possibility of cancelling the current load by avoiding the scheduled redirection altogether. + BackForwardController* backForward = m_frame->page()->backForward(); + if (steps > backForward->forwardCount() || -steps > backForward->backCount()) { + cancel(); + return; + } + + // In all other cases, schedule the history traversal to occur asynchronously. + schedule(adoptPtr(new ScheduledHistoryNavigation(steps))); +} + +void NavigationScheduler::timerFired(Timer<NavigationScheduler>*) +{ + if (!m_frame->page()) + return; + if (m_frame->page()->defersLoading()) + return; + + OwnPtr<ScheduledNavigation> redirect(m_redirect.release()); + redirect->fire(m_frame); +} + +void NavigationScheduler::schedule(PassOwnPtr<ScheduledNavigation> redirect) +{ + ASSERT(m_frame->page()); + + // If a redirect was scheduled during a load, then stop the current load. + // Otherwise when the current load transitions from a provisional to a + // committed state, pending redirects may be cancelled. + if (redirect->wasDuringLoad()) { + if (DocumentLoader* provisionalDocumentLoader = m_frame->loader()->provisionalDocumentLoader()) + provisionalDocumentLoader->stopLoading(); + m_frame->loader()->stopLoading(UnloadEventPolicyUnloadAndPageHide); + } + + cancel(); + m_redirect = redirect; + + if (!m_frame->loader()->isComplete() && m_redirect->isLocationChange()) + m_frame->loader()->completed(); + + startTimer(); +} + +void NavigationScheduler::startTimer() +{ + if (!m_redirect) + return; + + ASSERT(m_frame->page()); + if (m_timer.isActive()) + return; + if (!m_redirect->shouldStartTimer(m_frame)) + return; + + m_timer.startOneShot(m_redirect->delay()); + m_redirect->didStartTimer(m_frame, &m_timer); +} + +void NavigationScheduler::cancel(bool newLoadInProgress) +{ + m_timer.stop(); + + OwnPtr<ScheduledNavigation> redirect(m_redirect.release()); + if (redirect) + redirect->didStopTimer(m_frame, newLoadInProgress); +} + +} // namespace WebCore diff --git a/WebCore/loader/RedirectScheduler.h b/WebCore/loader/NavigationScheduler.h index 005a173..3bf5010 100644 --- a/WebCore/loader/RedirectScheduler.h +++ b/WebCore/loader/NavigationScheduler.h @@ -33,6 +33,7 @@ #include "Event.h" #include "Timer.h" +#include <wtf/Forward.h> #include <wtf/OwnPtr.h> #include <wtf/PassOwnPtr.h> #include <wtf/PassRefPtr.h> @@ -40,24 +41,24 @@ namespace WebCore { class FormState; +class FormSubmission; class Frame; -class String; struct FrameLoadRequest; -struct ScheduledRedirection; +class ScheduledNavigation; -class RedirectScheduler : public Noncopyable { +class NavigationScheduler : public Noncopyable { public: - RedirectScheduler(Frame*); - ~RedirectScheduler(); + NavigationScheduler(Frame*); + ~NavigationScheduler(); bool redirectScheduledDuringLoad(); bool locationChangePending(); void scheduleRedirect(double delay, const String& url); - void scheduleLocationChange(const String& url, const String& referrer, bool lockHistory = true, bool lockBackForwardList = true, bool userGesture = false); - void scheduleFormSubmission(const FrameLoadRequest&, bool lockHistory, PassRefPtr<Event>, PassRefPtr<FormState>); - void scheduleRefresh(bool userGesture = false); + void scheduleLocationChange(const String& url, const String& referrer, bool lockHistory = true, bool lockBackForwardList = true); + void scheduleFormSubmission(PassRefPtr<FormSubmission>); + void scheduleRefresh(); void scheduleHistoryNavigation(int steps); void startTimer(); @@ -66,16 +67,16 @@ public: void clear(); private: - void timerFired(Timer<RedirectScheduler>*); - void schedule(PassOwnPtr<ScheduledRedirection>); + void timerFired(Timer<NavigationScheduler>*); + void schedule(PassOwnPtr<ScheduledNavigation>); static bool mustLockBackForwardList(Frame* targetFrame); Frame* m_frame; - Timer<RedirectScheduler> m_timer; - OwnPtr<ScheduledRedirection> m_scheduledRedirection; + Timer<NavigationScheduler> m_timer; + OwnPtr<ScheduledNavigation> m_redirect; }; } // namespace WebCore -#endif // FrameLoader_h +#endif // RedirectScheduler_h diff --git a/WebCore/loader/NetscapePlugInStreamLoader.cpp b/WebCore/loader/NetscapePlugInStreamLoader.cpp index 9d0e81b..1225652 100644 --- a/WebCore/loader/NetscapePlugInStreamLoader.cpp +++ b/WebCore/loader/NetscapePlugInStreamLoader.cpp @@ -95,13 +95,13 @@ void NetscapePlugInStreamLoader::didReceiveData(const char* data, int length, lo ResourceLoader::didReceiveData(data, length, lengthReceived, allAtOnce); } -void NetscapePlugInStreamLoader::didFinishLoading() +void NetscapePlugInStreamLoader::didFinishLoading(double finishTime) { RefPtr<NetscapePlugInStreamLoader> protect(this); m_documentLoader->removePlugInStreamLoader(this); m_client->didFinishLoading(this); - ResourceLoader::didFinishLoading(); + ResourceLoader::didFinishLoading(finishTime); } void NetscapePlugInStreamLoader::didFail(const ResourceError& error) diff --git a/WebCore/loader/NetscapePlugInStreamLoader.h b/WebCore/loader/NetscapePlugInStreamLoader.h index 092c6fc..c8c4cb6 100644 --- a/WebCore/loader/NetscapePlugInStreamLoader.h +++ b/WebCore/loader/NetscapePlugInStreamLoader.h @@ -55,7 +55,7 @@ namespace WebCore { private: virtual void didReceiveResponse(const ResourceResponse&); virtual void didReceiveData(const char*, int, long long lengthReceived, bool allAtOnce); - virtual void didFinishLoading(); + virtual void didFinishLoading(double finishTime); virtual void didFail(const ResourceError&); virtual void releaseResources(); diff --git a/WebCore/loader/PingLoader.cpp b/WebCore/loader/PingLoader.cpp new file mode 100644 index 0000000..d687b42 --- /dev/null +++ b/WebCore/loader/PingLoader.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "PingLoader.h" + +#include "FormData.h" +#include "Frame.h" +#include "ResourceHandle.h" +#include "SecurityOrigin.h" +#include <wtf/OwnPtr.h> +#include <wtf/UnusedParam.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +void PingLoader::loadImage(Frame* frame, const KURL& url) +{ + if (!frame->document()->securityOrigin()->canDisplay(url)) { + FrameLoader::reportLocalLoadFailed(frame, url); + return; + } + + ResourceRequest request(url); + request.setTargetType(ResourceRequest::TargetIsImage); + request.setHTTPHeaderField("Cache-Control", "max-age=0"); + if (!SecurityOrigin::shouldHideReferrer(request.url(), frame->loader()->outgoingReferrer())) + request.setHTTPReferrer(frame->loader()->outgoingReferrer()); + frame->loader()->addExtraFieldsToSubresourceRequest(request); + OwnPtr<PingLoader> pingLoader = adoptPtr(new PingLoader(frame, request)); + + // Leak the ping loader, since it will kill itself as soon as it receives a response. + PingLoader* leakedPingLoader = pingLoader.leakPtr(); + UNUSED_PARAM(leakedPingLoader); +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing +void PingLoader::sendPing(Frame* frame, const KURL& pingURL, const KURL& destinationURL) +{ + ResourceRequest request(pingURL); + request.setTargetType(ResourceRequest::TargetIsSubresource); + request.setHTTPMethod("POST"); + request.setHTTPContentType("text/ping"); + request.setHTTPBody(FormData::create("PING")); + request.setHTTPHeaderField("Cache-Control", "max-age=0"); + frame->loader()->addExtraFieldsToSubresourceRequest(request); + + SecurityOrigin* sourceOrigin = frame->document()->securityOrigin(); + RefPtr<SecurityOrigin> pingOrigin = SecurityOrigin::create(pingURL); + FrameLoader::addHTTPOriginIfNeeded(request, sourceOrigin->toString()); + request.setHTTPHeaderField("Ping-To", destinationURL); + if (sourceOrigin->isSameSchemeHostPort(pingOrigin.get())) + request.setHTTPHeaderField("Ping-From", frame->document()->url()); + else if (!SecurityOrigin::shouldHideReferrer(pingURL, frame->loader()->outgoingReferrer())) + request.setHTTPReferrer(frame->loader()->outgoingReferrer()); + OwnPtr<PingLoader> pingLoader = adoptPtr(new PingLoader(frame, request)); + + // Leak the ping loader, since it will kill itself as soon as it receives a response. + PingLoader* leakedPingLoader = pingLoader.leakPtr(); + UNUSED_PARAM(leakedPingLoader); +} + +PingLoader::PingLoader(Frame* frame, const ResourceRequest& request) + : m_timeout(this, &PingLoader::timeout) +{ + m_handle = ResourceHandle::create(frame->loader()->networkingContext(), request, this, false, false); + + // If the server never responds, FrameLoader won't be able to cancel this load and + // we'll sit here waiting forever. Set a very generous timeout, just in case. + m_timeout.startOneShot(60000); +} + +PingLoader::~PingLoader() +{ + m_handle->cancel(); +} + +} diff --git a/WebCore/loader/PingLoader.h b/WebCore/loader/PingLoader.h new file mode 100644 index 0000000..eb43166 --- /dev/null +++ b/WebCore/loader/PingLoader.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 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 PingLoader_h +#define PingLoader_h + +#include "ResourceHandleClient.h" +#include "Timer.h" +#include <wtf/Noncopyable.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class Frame; +class KURL; +class ResourceError; +class ResourceHandle; +class ResourceResponse; + +// This class triggers asynchronous loads independent of Frame staying alive (i.e., auditing pingbacks). +// Since nothing depends on resources loaded through this class, we just want +// to allow the load to live long enough to ensure the message was actually sent. +// Therefore, as soon as a callback is received from the ResourceHandle, this class +// will cancel the load and delete itself. +class PingLoader : private ResourceHandleClient, public Noncopyable { +public: + static void loadImage(Frame*, const KURL& url); + static void sendPing(Frame*, const KURL& pingURL, const KURL& destinationURL); + + ~PingLoader(); + +private: + PingLoader(Frame*, const ResourceRequest&); + + void didReceiveResponse(ResourceHandle*, const ResourceResponse&) { delete this; } + void didReceiveData(ResourceHandle*, const char*, int) { delete this; } + void didFinishLoading(ResourceHandle*, double) { delete this; } + void didFail(ResourceHandle*, const ResourceError&) { delete this; } + void timeout(Timer<PingLoader>*) { delete this; } + + RefPtr<ResourceHandle> m_handle; + Timer<PingLoader> m_timeout; +}; + +} + +#endif diff --git a/WebCore/loader/PlaceholderDocument.cpp b/WebCore/loader/PlaceholderDocument.cpp index 81222b3..93a26db 100644 --- a/WebCore/loader/PlaceholderDocument.cpp +++ b/WebCore/loader/PlaceholderDocument.cpp @@ -26,9 +26,6 @@ #include "config.h" #include "PlaceholderDocument.h" -#include "CSSStyleSelector.h" -#include "StyleSheetList.h" - namespace WebCore { void PlaceholderDocument::attach() diff --git a/WebCore/loader/PlaceholderDocument.h b/WebCore/loader/PlaceholderDocument.h index 5b76a9c..3d40a6e 100644 --- a/WebCore/loader/PlaceholderDocument.h +++ b/WebCore/loader/PlaceholderDocument.h @@ -32,15 +32,15 @@ namespace WebCore { class PlaceholderDocument : public Document { public: - static PassRefPtr<PlaceholderDocument> create(Frame* frame) + static PassRefPtr<PlaceholderDocument> create(Frame* frame, const KURL& url) { - return adoptRef(new PlaceholderDocument(frame)); + return adoptRef(new PlaceholderDocument(frame, url)); } virtual void attach(); private: - PlaceholderDocument(Frame* frame) : Document(frame, false, false) { } + PlaceholderDocument(Frame* frame, const KURL& url) : Document(frame, url, false, false) { } }; } // namespace WebCore diff --git a/WebCore/loader/PluginDocument.cpp b/WebCore/loader/PluginDocument.cpp deleted file mode 100644 index 788691f..0000000 --- a/WebCore/loader/PluginDocument.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * 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, - * 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 "PluginDocument.h" - -#include "DocumentLoader.h" -#include "Element.h" -#include "Frame.h" -#include "FrameLoader.h" -#include "FrameLoaderClient.h" -#include "HTMLEmbedElement.h" -#include "HTMLNames.h" -#include "MainResourceLoader.h" -#include "Page.h" -#include "RenderWidget.h" -#include "SegmentedString.h" -#include "Settings.h" -#include "Text.h" -#include "XMLTokenizer.h" - -namespace WebCore { - -using namespace HTMLNames; - -class PluginTokenizer : public Tokenizer { -public: - PluginTokenizer(Document* doc) : m_doc(doc), m_embedElement(0) {} - -private: - virtual void write(const SegmentedString&, bool appendData); - virtual void stopParsing(); - virtual void finish(); - virtual bool isWaitingForScripts() const; - - virtual bool wantsRawData() const { return true; } - virtual bool writeRawData(const char* data, int len); - - void createDocumentStructure(); - - Document* m_doc; - HTMLEmbedElement* m_embedElement; -}; - -void PluginTokenizer::write(const SegmentedString&, bool) -{ - ASSERT_NOT_REACHED(); -} - -void PluginTokenizer::createDocumentStructure() -{ - ExceptionCode ec; - RefPtr<Element> rootElement = m_doc->createElement(htmlTag, false); - m_doc->appendChild(rootElement, 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->createElement(embedTag, false); - - m_embedElement = static_cast<HTMLEmbedElement*>(embedElement.get()); - m_embedElement->setAttribute(widthAttr, "100%"); - m_embedElement->setAttribute(heightAttr, "100%"); - - m_embedElement->setAttribute(nameAttr, "plugin"); - m_embedElement->setAttribute(srcAttr, m_doc->url().string()); - m_embedElement->setAttribute(typeAttr, m_doc->frame()->loader()->responseMIMEType()); - - body->appendChild(embedElement, ec); -} - -bool PluginTokenizer::writeRawData(const char*, int) -{ - ASSERT(!m_embedElement); - if (m_embedElement) - return false; - - createDocumentStructure(); - - if (Frame* frame = m_doc->frame()) { - Settings* settings = frame->settings(); - if (settings && settings->arePluginsEnabled()) { - m_doc->updateLayout(); - - if (RenderWidget* renderer = toRenderWidget(m_embedElement->renderer())) { - frame->loader()->client()->redirectDataToPlugin(renderer->widget()); - frame->loader()->activeDocumentLoader()->mainResourceLoader()->setShouldBufferData(false); - } - - finish(); - } - } - - return false; -} - -void PluginTokenizer::stopParsing() -{ - Tokenizer::stopParsing(); -} - -void PluginTokenizer::finish() -{ - if (!m_parserStopped) - m_doc->finishedParsing(); -} - -bool PluginTokenizer::isWaitingForScripts() const -{ - // A plugin document is never waiting for scripts - return false; -} - -PluginDocument::PluginDocument(Frame* frame) - : HTMLDocument(frame) -{ - setParseMode(Compat); -} - -Tokenizer* PluginDocument::createTokenizer() -{ - return new PluginTokenizer(this); -} - -} diff --git a/WebCore/loader/PluginDocument.h b/WebCore/loader/PluginDocument.h deleted file mode 100644 index 1d5c964..0000000 --- a/WebCore/loader/PluginDocument.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2006, 2008, 2009Apple 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, - * 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 PluginDocument_h -#define PluginDocument_h - -#include "HTMLDocument.h" - -namespace WebCore { - -class PluginDocument : public HTMLDocument { -public: - static PassRefPtr<PluginDocument> create(Frame* frame) - { - return adoptRef(new PluginDocument(frame)); - } - -private: - PluginDocument(Frame*); - - virtual bool isPluginDocument() const { return true; } - virtual Tokenizer* createTokenizer(); -}; - -} - -#endif // PluginDocument_h diff --git a/WebCore/loader/PolicyCallback.cpp b/WebCore/loader/PolicyCallback.cpp index 14799cf..4ec2c84 100644 --- a/WebCore/loader/PolicyCallback.cpp +++ b/WebCore/loader/PolicyCallback.cpp @@ -71,11 +71,12 @@ void PolicyCallback::set(const ResourceRequest& request, PassRefPtr<FormState> f } void PolicyCallback::set(const ResourceRequest& request, PassRefPtr<FormState> formState, - const String& frameName, NewWindowPolicyDecisionFunction function, void* argument) + const String& frameName, const NavigationAction& navigationAction, NewWindowPolicyDecisionFunction function, void* argument) { m_request = request; m_formState = formState; m_frameName = frameName; + m_navigationAction = navigationAction; m_navigationFunction = 0; m_newWindowFunction = function; @@ -100,7 +101,7 @@ void PolicyCallback::call(bool shouldContinue) if (m_navigationFunction) m_navigationFunction(m_argument, m_request, m_formState.get(), shouldContinue); if (m_newWindowFunction) - m_newWindowFunction(m_argument, m_request, m_formState.get(), m_frameName, shouldContinue); + m_newWindowFunction(m_argument, m_request, m_formState.get(), m_frameName, m_navigationAction, shouldContinue); ASSERT(!m_contentFunction); } @@ -125,7 +126,7 @@ void PolicyCallback::cancel() if (m_navigationFunction) m_navigationFunction(m_argument, m_request, m_formState.get(), false); if (m_newWindowFunction) - m_newWindowFunction(m_argument, m_request, m_formState.get(), m_frameName, false); + m_newWindowFunction(m_argument, m_request, m_formState.get(), m_frameName, m_navigationAction, false); if (m_contentFunction) m_contentFunction(m_argument, PolicyIgnore); } diff --git a/WebCore/loader/PolicyCallback.h b/WebCore/loader/PolicyCallback.h index 757fff8..415a3e3 100644 --- a/WebCore/loader/PolicyCallback.h +++ b/WebCore/loader/PolicyCallback.h @@ -31,9 +31,10 @@ #define PolicyCallback_h #include "FrameLoaderTypes.h" -#include "PlatformString.h" +#include "NavigationAction.h" #include "ResourceRequest.h" #include <wtf/RefPtr.h> +#include <wtf/text/WTFString.h> namespace WebCore { @@ -42,7 +43,7 @@ class FormState; typedef void (*NavigationPolicyDecisionFunction)(void* argument, const ResourceRequest&, PassRefPtr<FormState>, bool shouldContinue); typedef void (*NewWindowPolicyDecisionFunction)(void* argument, - const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, bool shouldContinue); + const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, const NavigationAction&, bool shouldContinue); typedef void (*ContentPolicyDecisionFunction)(void* argument, PolicyAction); class PolicyCallback { @@ -53,7 +54,7 @@ public: void clear(); void set(const ResourceRequest&, PassRefPtr<FormState>, NavigationPolicyDecisionFunction, void* argument); - void set(const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, + void set(const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, const NavigationAction&, NewWindowPolicyDecisionFunction, void* argument); void set(ContentPolicyDecisionFunction, void* argument); @@ -68,6 +69,7 @@ private: ResourceRequest m_request; RefPtr<FormState> m_formState; String m_frameName; + NavigationAction m_navigationAction; NavigationPolicyDecisionFunction m_navigationFunction; NewWindowPolicyDecisionFunction m_newWindowFunction; diff --git a/WebCore/loader/PolicyChecker.cpp b/WebCore/loader/PolicyChecker.cpp index 196ab4f..2680386 100644 --- a/WebCore/loader/PolicyChecker.cpp +++ b/WebCore/loader/PolicyChecker.cpp @@ -92,7 +92,7 @@ void PolicyChecker::checkNavigationPolicy(const ResourceRequest& request, Docume void PolicyChecker::checkNewWindowPolicy(const NavigationAction& action, NewWindowPolicyDecisionFunction function, const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, void* argument) { - m_callback.set(request, formState, frameName, function, argument); + m_callback.set(request, formState, frameName, action, function, argument); m_frame->loader()->client()->dispatchDecidePolicyForNewWindowAction(&PolicyChecker::continueAfterNewWindowPolicy, action, request, formState, frameName); } diff --git a/WebCore/loader/ProgressTracker.cpp b/WebCore/loader/ProgressTracker.cpp index 0c9f2fb..6bc2055 100644 --- a/WebCore/loader/ProgressTracker.cpp +++ b/WebCore/loader/ProgressTracker.cpp @@ -29,8 +29,11 @@ #include "DocumentLoader.h" #include "Frame.h" #include "FrameLoader.h" +#include "FrameLoaderStateMachine.h" #include "FrameLoaderClient.h" +#include "Logging.h" #include "ResourceResponse.h" +#include <wtf/text/CString.h> #include <wtf/CurrentTime.h> using std::min; @@ -56,9 +59,10 @@ struct ProgressItem : Noncopyable { long long estimatedLength; }; +unsigned long ProgressTracker::s_uniqueIdentifier = 0; + ProgressTracker::ProgressTracker() - : m_uniqueIdentifier(0) - , m_totalPageAndResourceBytesToLoad(0) + : m_totalPageAndResourceBytesToLoad(0) , m_totalBytesReceived(0) , m_lastNotifiedProgressValue(0) , m_lastNotifiedProgressTime(0) @@ -97,7 +101,7 @@ void ProgressTracker::reset() void ProgressTracker::progressStarted(Frame* frame) { - // LOG (Progress, "frame %p(%@), _private->numProgressTrackedFrames %d, _private->originatingProgressFrame %p", frame, [frame name], _private->numProgressTrackedFrames, _private->originatingProgressFrame); + LOG(Progress, "Progress started (%p) - frame %p(\"%s\"), value %f, tracked frames %d, originating frame %p", this, frame, frame->tree()->uniqueName().string().utf8().data(), m_progressValue, m_numProgressTrackedFrames, m_originatingProgressFrame.get()); frame->loader()->client()->willChangeEstimatedProgress(); @@ -115,7 +119,7 @@ void ProgressTracker::progressStarted(Frame* frame) void ProgressTracker::progressCompleted(Frame* frame) { - // LOG (Progress, "frame %p(%@), _private->numProgressTrackedFrames %d, _private->originatingProgressFrame %p", frame, [frame name], _private->numProgressTrackedFrames, _private->originatingProgressFrame); + LOG(Progress, "Progress completed (%p) - frame %p(\"%s\"), value %f, tracked frames %d, originating frame %p", this, frame, frame->tree()->uniqueName().string().utf8().data(), m_progressValue, m_numProgressTrackedFrames, m_originatingProgressFrame.get()); if (m_numProgressTrackedFrames <= 0) return; @@ -132,7 +136,7 @@ void ProgressTracker::progressCompleted(Frame* frame) void ProgressTracker::finalProgressComplete() { - // LOG (Progress, ""); + LOG(Progress, "Final progress complete (%p)", this); RefPtr<Frame> frame = m_originatingProgressFrame.release(); @@ -151,7 +155,7 @@ void ProgressTracker::finalProgressComplete() void ProgressTracker::incrementProgress(unsigned long identifier, const ResourceResponse& response) { - // LOG (Progress, "_private->numProgressTrackedFrames %d, _private->originatingProgressFrame %p", _private->numProgressTrackedFrames, _private->originatingProgressFrame); + LOG(Progress, "Progress incremented (%p) - value %f, tracked frames %d, originating frame %p", this, m_progressValue, m_numProgressTrackedFrames, m_originatingProgressFrame.get()); if (m_numProgressTrackedFrames <= 0) return; @@ -166,7 +170,7 @@ void ProgressTracker::incrementProgress(unsigned long identifier, const Resource item->bytesReceived = 0; item->estimatedLength = estimatedLength; } else - m_progressItems.set(identifier, new ProgressItem(estimatedLength)); + m_progressItems.set(identifier, adoptPtr(new ProgressItem(estimatedLength)).leakPtr()); } void ProgressTracker::incrementProgress(unsigned long identifier, const char*, int length) @@ -202,7 +206,7 @@ void ProgressTracker::incrementProgress(unsigned long identifier, const char*, i // For documents that use WebCore's layout system, treat first layout as the half-way point. // FIXME: The hasHTMLView function is a sort of roundabout way of asking "do you use WebCore's layout system". bool useClampedMaxProgress = frame->loader()->client()->hasHTMLView() - && !frame->loader()->firstLayoutDone(); + && !frame->loader()->stateMachine()->firstLayoutDone(); double maxProgressValue = useClampedMaxProgress ? 0.5 : finalProgressValue; increment = (maxProgressValue - m_progressValue) * percentOfRemainingBytes; m_progressValue += increment; @@ -214,7 +218,7 @@ void ProgressTracker::incrementProgress(unsigned long identifier, const char*, i double now = currentTime(); double notifiedProgressTimeDelta = now - m_lastNotifiedProgressTime; - // LOG (Progress, "_private->progressValue %g, _private->numProgressTrackedFrames %d", _private->progressValue, _private->numProgressTrackedFrames); + LOG(Progress, "Progress incremented (%p) - value %f, tracked frames %d", this, m_progressValue, m_numProgressTrackedFrames); double notificationProgressDelta = m_progressValue - m_lastNotifiedProgressValue; if ((notificationProgressDelta >= m_progressNotificationInterval || notifiedProgressTimeDelta >= m_progressNotificationTimeInterval) && @@ -252,7 +256,7 @@ void ProgressTracker::completeProgress(unsigned long identifier) unsigned long ProgressTracker::createUniqueIdentifier() { - return ++m_uniqueIdentifier; + return ++s_uniqueIdentifier; } diff --git a/WebCore/loader/ProgressTracker.h b/WebCore/loader/ProgressTracker.h index 744e101..5d5b6b2 100644 --- a/WebCore/loader/ProgressTracker.h +++ b/WebCore/loader/ProgressTracker.h @@ -41,7 +41,7 @@ public: ProgressTracker(); ~ProgressTracker(); - unsigned long createUniqueIdentifier(); + static unsigned long createUniqueIdentifier(); double estimatedProgress() const; @@ -59,7 +59,7 @@ private: void reset(); void finalProgressComplete(); - unsigned long m_uniqueIdentifier; + static unsigned long s_uniqueIdentifier; long long m_totalPageAndResourceBytesToLoad; long long m_totalBytesReceived; diff --git a/WebCore/loader/RedirectScheduler.cpp b/WebCore/loader/RedirectScheduler.cpp deleted file mode 100644 index 4b44422..0000000 --- a/WebCore/loader/RedirectScheduler.cpp +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) - * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) - * Copyright (C) 2009 Adam Barth. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "RedirectScheduler.h" - -#include "BackForwardList.h" -#include "DocumentLoader.h" -#include "Event.h" -#include "FormState.h" -#include "Frame.h" -#include "FrameLoadRequest.h" -#include "FrameLoader.h" -#include "HistoryItem.h" -#include "HTMLFormElement.h" -#include "HTMLFrameOwnerElement.h" -#include "Page.h" -#include <wtf/CurrentTime.h> - -namespace WebCore { - -struct ScheduledRedirection : Noncopyable { - enum Type { redirection, locationChange, historyNavigation, formSubmission }; - - const Type type; - const double delay; - const String url; - const String referrer; - const FrameLoadRequest frameRequest; - const RefPtr<Event> event; - const RefPtr<FormState> formState; - const int historySteps; - const bool lockHistory; - const bool lockBackForwardList; - const bool wasUserGesture; - const bool wasRefresh; - const bool wasDuringLoad; - bool toldClient; - - ScheduledRedirection(double delay, const String& url, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh) - : type(redirection) - , delay(delay) - , url(url) - , historySteps(0) - , lockHistory(lockHistory) - , lockBackForwardList(lockBackForwardList) - , wasUserGesture(wasUserGesture) - , wasRefresh(refresh) - , wasDuringLoad(false) - , toldClient(false) - { - ASSERT(!url.isEmpty()); - } - - ScheduledRedirection(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool wasUserGesture, bool refresh, bool duringLoad) - : type(locationChange) - , delay(0) - , url(url) - , referrer(referrer) - , historySteps(0) - , lockHistory(lockHistory) - , lockBackForwardList(lockBackForwardList) - , wasUserGesture(wasUserGesture) - , wasRefresh(refresh) - , wasDuringLoad(duringLoad) - , toldClient(false) - { - ASSERT(!url.isEmpty()); - } - - explicit ScheduledRedirection(int historyNavigationSteps) - : type(historyNavigation) - , delay(0) - , historySteps(historyNavigationSteps) - , lockHistory(false) - , lockBackForwardList(false) - , wasUserGesture(false) - , wasRefresh(false) - , wasDuringLoad(false) - , toldClient(false) - { - } - - ScheduledRedirection(const FrameLoadRequest& frameRequest, - bool lockHistory, bool lockBackForwardList, PassRefPtr<Event> event, PassRefPtr<FormState> formState, - bool duringLoad) - : type(formSubmission) - , delay(0) - , frameRequest(frameRequest) - , event(event) - , formState(formState) - , historySteps(0) - , lockHistory(lockHistory) - , lockBackForwardList(lockBackForwardList) - , wasUserGesture(false) - , wasRefresh(false) - , wasDuringLoad(duringLoad) - , toldClient(false) - { - ASSERT(!frameRequest.isEmpty()); - ASSERT(this->formState); - } -}; - -RedirectScheduler::RedirectScheduler(Frame* frame) - : m_frame(frame) - , m_timer(this, &RedirectScheduler::timerFired) -{ -} - -RedirectScheduler::~RedirectScheduler() -{ -} - -bool RedirectScheduler::redirectScheduledDuringLoad() -{ - return m_scheduledRedirection && m_scheduledRedirection->wasDuringLoad; -} - -void RedirectScheduler::clear() -{ - m_timer.stop(); - m_scheduledRedirection.clear(); -} - -void RedirectScheduler::scheduleRedirect(double delay, const String& url) -{ - if (delay < 0 || delay > INT_MAX / 1000) - return; - - 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) - schedule(new ScheduledRedirection(delay, url, true, delay <= 1, false, false)); -} - -bool RedirectScheduler::mustLockBackForwardList(Frame* targetFrame) -{ - // Navigation of a subframe during loading of an ancestor frame does not create a new back/forward item. - // The definition of "during load" is any time before all handlers for the load event have been run. - // See https://bugs.webkit.org/show_bug.cgi?id=14957 for the original motivation for this. - - for (Frame* ancestor = targetFrame->tree()->parent(); ancestor; ancestor = ancestor->tree()->parent()) { - Document* document = ancestor->document(); - if (!ancestor->loader()->isComplete() || (document && document->processingLoadEvent())) - return true; - } - return false; -} - -void RedirectScheduler::scheduleLocationChange(const String& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool wasUserGesture) -{ - if (!m_frame->page()) - return; - - if (url.isEmpty()) - return; - - lockBackForwardList = lockBackForwardList || mustLockBackForwardList(m_frame); - - FrameLoader* loader = m_frame->loader(); - - // 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(ParsedURLString, url); - if (parsedURL.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(loader->url(), parsedURL)) { - loader->changeLocation(loader->completeURL(url), referrer, lockHistory, lockBackForwardList, wasUserGesture); - return; - } - - // Handle a location change of a page with no document as a special case. - // This may happen when a frame changes the location of another frame. - bool duringLoad = !loader->committedFirstRealDocumentLoad(); - - schedule(new ScheduledRedirection(url, referrer, lockHistory, lockBackForwardList, wasUserGesture, false, duringLoad)); -} - -void RedirectScheduler::scheduleFormSubmission(const FrameLoadRequest& frameRequest, - bool lockHistory, PassRefPtr<Event> event, PassRefPtr<FormState> formState) -{ - ASSERT(m_frame->page()); - ASSERT(!frameRequest.isEmpty()); - - // FIXME: Do we need special handling for form submissions where the URL is the same - // as the current one except for the fragment part? See scheduleLocationChange above. - - // Handle a location change of a page with no document as a special case. - // This may happen when a frame changes the location of another frame. - bool duringLoad = !m_frame->loader()->committedFirstRealDocumentLoad(); - - // If this is a child frame and the form submission was triggered by a script, lock the back/forward list - // to match IE and Opera. - // See https://bugs.webkit.org/show_bug.cgi?id=32383 for the original motivation for this. - - bool lockBackForwardList = mustLockBackForwardList(m_frame) || (formState->formSubmissionTrigger() == SubmittedByJavaScript && m_frame->tree()->parent()); - - schedule(new ScheduledRedirection(frameRequest, lockHistory, lockBackForwardList, event, formState, duringLoad)); -} - -void RedirectScheduler::scheduleRefresh(bool wasUserGesture) -{ - if (!m_frame->page()) - return; - - const KURL& url = m_frame->loader()->url(); - - if (url.isEmpty()) - return; - - schedule(new ScheduledRedirection(url.string(), m_frame->loader()->outgoingReferrer(), true, true, wasUserGesture, true, false)); -} - -bool RedirectScheduler::locationChangePending() -{ - if (!m_scheduledRedirection) - return false; - - switch (m_scheduledRedirection->type) { - case ScheduledRedirection::redirection: - return false; - case ScheduledRedirection::historyNavigation: - case ScheduledRedirection::locationChange: - case ScheduledRedirection::formSubmission: - return true; - } - ASSERT_NOT_REACHED(); - return false; -} - -void RedirectScheduler::scheduleHistoryNavigation(int steps) -{ - if (!m_frame->page()) - return; - - // Invalid history navigations (such as history.forward() during a new load) have the side effect of cancelling any scheduled - // redirects. We also avoid the possibility of cancelling the current load by avoiding the scheduled redirection altogether. - HistoryItem* specifiedEntry = m_frame->page()->backForwardList()->itemAtIndex(steps); - if (!specifiedEntry) { - cancel(); - return; - } - -#if !ENABLE(HISTORY_ALWAYS_ASYNC) - // If the specified entry and the current entry have the same document, this is either a state object traversal or a fragment - // traversal (or both) and should be performed synchronously. - HistoryItem* currentEntry = m_frame->loader()->history()->currentItem(); - if (currentEntry != specifiedEntry && currentEntry->documentSequenceNumber() == specifiedEntry->documentSequenceNumber()) { - m_frame->loader()->history()->goToItem(specifiedEntry, FrameLoadTypeIndexedBackForward); - return; - } -#endif - - // In all other cases, schedule the history traversal to occur asynchronously. - schedule(new ScheduledRedirection(steps)); -} - -void RedirectScheduler::timerFired(Timer<RedirectScheduler>*) -{ - if (!m_frame->page()) - return; - - if (m_frame->page()->defersLoading()) - return; - - OwnPtr<ScheduledRedirection> redirection(m_scheduledRedirection.release()); - FrameLoader* loader = m_frame->loader(); - - switch (redirection->type) { - case ScheduledRedirection::redirection: - case ScheduledRedirection::locationChange: - loader->changeLocation(KURL(ParsedURLString, redirection->url), redirection->referrer, - redirection->lockHistory, redirection->lockBackForwardList, redirection->wasUserGesture, redirection->wasRefresh); - return; - case ScheduledRedirection::historyNavigation: - if (redirection->historySteps == 0) { - // Special case for go(0) from a frame -> reload only the frame - loader->urlSelected(loader->url(), "", 0, redirection->lockHistory, redirection->lockBackForwardList, redirection->wasUserGesture, SendReferrer); - return; - } - // go(i!=0) from a frame navigates into the history of the frame only, - // in both IE and NS (but not in Mozilla). We can't easily do that. - m_frame->page()->goBackOrForward(redirection->historySteps); - return; - case ScheduledRedirection::formSubmission: - // The submitForm function will find a target frame before using the redirection timer. - // Now that the timer has fired, we need to repeat the security check which normally is done when - // selecting a target, in case conditions have changed. Other code paths avoid this by targeting - // without leaving a time window. If we fail the check just silently drop the form submission. - if (!redirection->formState->sourceFrame()->loader()->shouldAllowNavigation(m_frame)) - return; - loader->loadFrameRequest(redirection->frameRequest, redirection->lockHistory, redirection->lockBackForwardList, - redirection->event, redirection->formState, SendReferrer); - return; - } - - ASSERT_NOT_REACHED(); -} - -void RedirectScheduler::schedule(PassOwnPtr<ScheduledRedirection> redirection) -{ - ASSERT(m_frame->page()); - FrameLoader* loader = m_frame->loader(); - - // If a redirect was scheduled during a load, then stop the current load. - // Otherwise when the current load transitions from a provisional to a - // committed state, pending redirects may be cancelled. - if (redirection->wasDuringLoad) { - if (DocumentLoader* provisionalDocumentLoader = loader->provisionalDocumentLoader()) - provisionalDocumentLoader->stopLoading(); - loader->stopLoading(UnloadEventPolicyUnloadAndPageHide); - } - - cancel(); - m_scheduledRedirection = redirection; - if (!loader->isComplete() && m_scheduledRedirection->type != ScheduledRedirection::redirection) - loader->completed(); - startTimer(); -} - -void RedirectScheduler::startTimer() -{ - if (!m_scheduledRedirection) - return; - - ASSERT(m_frame->page()); - - FrameLoader* loader = m_frame->loader(); - - if (m_timer.isActive()) - return; - - if (m_scheduledRedirection->type == ScheduledRedirection::redirection && !loader->allAncestorsAreComplete()) - return; - - m_timer.startOneShot(m_scheduledRedirection->delay); - - switch (m_scheduledRedirection->type) { - case ScheduledRedirection::locationChange: - case ScheduledRedirection::redirection: - if (m_scheduledRedirection->toldClient) - return; - m_scheduledRedirection->toldClient = true; - loader->clientRedirected(KURL(ParsedURLString, m_scheduledRedirection->url), - m_scheduledRedirection->delay, - currentTime() + m_timer.nextFireInterval(), - m_scheduledRedirection->lockBackForwardList); - return; - case ScheduledRedirection::formSubmission: - // FIXME: It would make sense to report form submissions as client redirects too. - // But we didn't do that in the past when form submission used a separate delay - // mechanism, so doing it will be a behavior change. - return; - case ScheduledRedirection::historyNavigation: - // Don't report history navigations. - return; - } - ASSERT_NOT_REACHED(); -} - -void RedirectScheduler::cancel(bool newLoadInProgress) -{ - m_timer.stop(); - - OwnPtr<ScheduledRedirection> redirection(m_scheduledRedirection.release()); - if (redirection && redirection->toldClient) - m_frame->loader()->clientRedirectCancelledOrFinished(newLoadInProgress); -} - -} // namespace WebCore - diff --git a/WebCore/loader/Request.cpp b/WebCore/loader/Request.cpp index 630a4bb..6ad6f9c 100644 --- a/WebCore/loader/Request.cpp +++ b/WebCore/loader/Request.cpp @@ -28,9 +28,9 @@ namespace WebCore { -Request::Request(DocLoader* docLoader, CachedResource* object, bool incremental, SecurityCheckPolicy shouldDoSecurityCheck, bool sendResourceLoadCallbacks) +Request::Request(CachedResourceLoader* cachedResourceLoader, CachedResource* object, bool incremental, SecurityCheckPolicy shouldDoSecurityCheck, bool sendResourceLoadCallbacks) : m_object(object) - , m_docLoader(docLoader) + , m_cachedResourceLoader(cachedResourceLoader) , m_incremental(incremental) , m_multipart(false) , m_shouldDoSecurityCheck(shouldDoSecurityCheck) diff --git a/WebCore/loader/Request.h b/WebCore/loader/Request.h index 468f8ff..b6de312 100644 --- a/WebCore/loader/Request.h +++ b/WebCore/loader/Request.h @@ -29,30 +29,28 @@ namespace WebCore { class CachedResource; - class DocLoader; + class CachedResourceLoader; class Request : public Noncopyable { public: - Request(DocLoader*, CachedResource*, bool incremental, SecurityCheckPolicy, bool sendResourceLoadCallbacks); + Request(CachedResourceLoader*, CachedResource*, bool incremental, SecurityCheckPolicy, bool sendResourceLoadCallbacks); ~Request(); - Vector<char>& buffer() { return m_buffer; } - CachedResource* cachedResource() { return m_object; } - DocLoader* docLoader() { return m_docLoader; } + CachedResource* cachedResource() const { return m_object; } + CachedResourceLoader* cachedResourceLoader() const { return m_cachedResourceLoader; } - bool isIncremental() { return m_incremental; } + bool isIncremental() const { return m_incremental; } void setIsIncremental(bool b = true) { m_incremental = b; } - bool isMultipart() { return m_multipart; } + bool isMultipart() const { return m_multipart; } void setIsMultipart(bool b = true) { m_multipart = b; } SecurityCheckPolicy shouldDoSecurityCheck() const { return m_shouldDoSecurityCheck; } bool sendResourceLoadCallbacks() const { return m_sendResourceLoadCallbacks; } private: - Vector<char> m_buffer; CachedResource* m_object; - DocLoader* m_docLoader; + CachedResourceLoader* m_cachedResourceLoader; bool m_incremental; bool m_multipart; SecurityCheckPolicy m_shouldDoSecurityCheck; diff --git a/WebCore/loader/ResourceLoadNotifier.cpp b/WebCore/loader/ResourceLoadNotifier.cpp index 9280434..b32b737 100644 --- a/WebCore/loader/ResourceLoadNotifier.cpp +++ b/WebCore/loader/ResourceLoadNotifier.cpp @@ -82,11 +82,11 @@ void ResourceLoadNotifier::didReceiveData(ResourceLoader* loader, const char* da dispatchDidReceiveContentLength(loader->documentLoader(), loader->identifier(), lengthReceived); } -void ResourceLoadNotifier::didFinishLoad(ResourceLoader* loader) +void ResourceLoadNotifier::didFinishLoad(ResourceLoader* loader, double finishTime) { if (Page* page = m_frame->page()) page->progress()->completeProgress(loader->identifier()); - dispatchDidFinishLoading(loader->documentLoader(), loader->identifier()); + dispatchDidFinishLoading(loader->documentLoader(), loader->identifier(), finishTime); } void ResourceLoadNotifier::didFailToLoad(ResourceLoader* loader, const ResourceError& error) @@ -103,11 +103,6 @@ void ResourceLoadNotifier::didFailToLoad(ResourceLoader* loader, const ResourceE #endif } -void ResourceLoadNotifier::didLoadResourceByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString) -{ - m_frame->loader()->client()->dispatchDidLoadResourceByXMLHttpRequest(identifier, sourceString); -} - void ResourceLoadNotifier::assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader* loader, const ResourceRequest& request) { m_frame->loader()->client()->assignIdentifierToInitialRequest(identifier, loader, request); @@ -141,7 +136,7 @@ void ResourceLoadNotifier::dispatchDidReceiveResponse(DocumentLoader* loader, un #if ENABLE(INSPECTOR) if (Page* page = m_frame->page()) - page->inspectorController()->didReceiveResponse(identifier, r); + page->inspectorController()->didReceiveResponse(identifier, loader, r); #endif } @@ -155,13 +150,13 @@ void ResourceLoadNotifier::dispatchDidReceiveContentLength(DocumentLoader* loade #endif } -void ResourceLoadNotifier::dispatchDidFinishLoading(DocumentLoader* loader, unsigned long identifier) +void ResourceLoadNotifier::dispatchDidFinishLoading(DocumentLoader* loader, unsigned long identifier, double finishTime) { m_frame->loader()->client()->dispatchDidFinishLoading(loader, identifier); #if ENABLE(INSPECTOR) if (Page* page = m_frame->page()) - page->inspectorController()->didFinishLoading(identifier); + page->inspectorController()->didFinishLoading(identifier, finishTime); #endif } @@ -174,7 +169,7 @@ void ResourceLoadNotifier::sendRemainingDelegateMessages(DocumentLoader* loader, dispatchDidReceiveContentLength(loader, identifier, length); if (error.isNull()) - dispatchDidFinishLoading(loader, identifier); + dispatchDidFinishLoading(loader, identifier, 0); else m_frame->loader()->client()->dispatchDidFailLoading(loader, identifier, error); } diff --git a/WebCore/loader/ResourceLoadNotifier.h b/WebCore/loader/ResourceLoadNotifier.h index 23e4246..93fcccc 100644 --- a/WebCore/loader/ResourceLoadNotifier.h +++ b/WebCore/loader/ResourceLoadNotifier.h @@ -40,7 +40,6 @@ class Frame; class ResourceError; class ResourceLoader; class ResourceResponse; -class ScriptString; class ResourceRequest; class ResourceLoadNotifier : public Noncopyable { @@ -53,15 +52,14 @@ public: void willSendRequest(ResourceLoader*, ResourceRequest&, const ResourceResponse& redirectResponse); void didReceiveResponse(ResourceLoader*, const ResourceResponse&); void didReceiveData(ResourceLoader*, const char*, int, int lengthReceived); - void didFinishLoad(ResourceLoader*); + void didFinishLoad(ResourceLoader*, double finishTime); void didFailToLoad(ResourceLoader*, const ResourceError&); - void didLoadResourceByXMLHttpRequest(unsigned long identifier, const ScriptString& sourceString); void assignIdentifierToInitialRequest(unsigned long identifier, DocumentLoader*, const ResourceRequest&); void dispatchWillSendRequest(DocumentLoader*, unsigned long identifier, ResourceRequest&, const ResourceResponse& redirectResponse); void dispatchDidReceiveResponse(DocumentLoader*, unsigned long identifier, const ResourceResponse&); void dispatchDidReceiveContentLength(DocumentLoader*, unsigned long identifier, int length); - void dispatchDidFinishLoading(DocumentLoader*, unsigned long identifier); + void dispatchDidFinishLoading(DocumentLoader*, unsigned long identifier, double finishTime); void sendRemainingDelegateMessages(DocumentLoader*, unsigned long identifier, const ResourceResponse&, int length, const ResourceError&); diff --git a/WebCore/loader/ResourceLoader.cpp b/WebCore/loader/ResourceLoader.cpp index d14afc8..eab5acd 100644 --- a/WebCore/loader/ResourceLoader.cpp +++ b/WebCore/loader/ResourceLoader.cpp @@ -32,8 +32,11 @@ #include "ApplicationCacheHost.h" #include "DocumentLoader.h" +#include "FileStreamProxy.h" #include "Frame.h" #include "FrameLoader.h" +#include "FrameLoaderClient.h" +#include "InspectorInstrumentation.h" #include "Page.h" #include "ProgressTracker.h" #include "ResourceHandle.h" @@ -111,6 +114,17 @@ bool ResourceLoader::load(const ResourceRequest& r) ASSERT(!m_documentLoader->isSubstituteLoadPending(this)); ResourceRequest clientRequest(r); + + // https://bugs.webkit.org/show_bug.cgi?id=26391 + // The various plug-in implementations call directly to ResourceLoader::load() instead of piping requests + // through FrameLoader. As a result, they miss the FrameLoader::addExtraFieldsToRequest() step which sets + // up the 1st party for cookies URL. Until plug-in implementations can be reigned in to pipe through that + // method, we need to make sure there is always a 1st party for cookies set. + if (clientRequest.firstPartyForCookies().isNull()) { + if (Document* document = m_frame->document()) + clientRequest.setFirstPartyForCookies(document->firstPartyForCookies()); + } + willSendRequest(clientRequest, ResourceResponse()); if (clientRequest.isNull()) { didFail(frameLoader()->cancelledError(r)); @@ -132,7 +146,7 @@ bool ResourceLoader::load(const ResourceRequest& r) return true; } - m_handle = ResourceHandle::create(clientRequest, this, m_frame.get(), m_defersLoading, m_shouldContentSniff, true); + m_handle = ResourceHandle::create(m_frame->loader()->networkingContext(), clientRequest, this, m_defersLoading, m_shouldContentSniff); return true; } @@ -273,7 +287,7 @@ void ResourceLoader::willStopBufferingData(const char* data, int length) m_resourceData = SharedBuffer::create(data, length); } -void ResourceLoader::didFinishLoading() +void ResourceLoader::didFinishLoading(double finishTime) { // If load has been cancelled after finishing (which could happen with a // JavaScript that changes the window location), do nothing. @@ -281,11 +295,11 @@ void ResourceLoader::didFinishLoading() return; ASSERT(!m_reachedTerminalState); - didFinishLoadingOnePart(); + didFinishLoadingOnePart(finishTime); releaseResources(); } -void ResourceLoader::didFinishLoadingOnePart() +void ResourceLoader::didFinishLoadingOnePart(double finishTime) { if (m_cancelled) return; @@ -295,7 +309,7 @@ void ResourceLoader::didFinishLoadingOnePart() return; m_calledDidFinishLoad = true; if (m_sendResourceLoadCallbacks) - frameLoader()->notifier()->didFinishLoad(this); + frameLoader()->notifier()->didFinishLoad(this, finishTime); } void ResourceLoader::didFail(const ResourceError& error) @@ -401,17 +415,21 @@ void ResourceLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForResponse(this, response)) return; #endif + InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceResponse(m_frame.get(), identifier(), response); didReceiveResponse(response); + InspectorInstrumentation::didReceiveResourceResponse(cookie); } void ResourceLoader::didReceiveData(ResourceHandle*, const char* data, int length, int lengthReceived) { + InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceData(m_frame.get(), identifier()); didReceiveData(data, length, lengthReceived, false); + InspectorInstrumentation::didReceiveResourceData(cookie); } -void ResourceLoader::didFinishLoading(ResourceHandle*) +void ResourceLoader::didFinishLoading(ResourceHandle*, double finishTime) { - didFinishLoading(); + didFinishLoading(finishTime); } void ResourceLoader::didFail(ResourceHandle*, const ResourceError& error) @@ -455,6 +473,14 @@ void ResourceLoader::didCancelAuthenticationChallenge(const AuthenticationChalle frameLoader()->notifier()->didCancelAuthenticationChallenge(this, challenge); } +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) +bool ResourceLoader::canAuthenticateAgainstProtectionSpace(const ProtectionSpace& protectionSpace) +{ + RefPtr<ResourceLoader> protector(this); + return frameLoader()->canAuthenticateAgainstProtectionSpace(this, protectionSpace); +} +#endif + void ResourceLoader::receivedCancellation(const AuthenticationChallenge&) { cancel(); @@ -462,9 +488,24 @@ void ResourceLoader::receivedCancellation(const AuthenticationChallenge&) void ResourceLoader::willCacheResponse(ResourceHandle*, CacheStoragePolicy& policy) { + // <rdar://problem/7249553> - There are reports of crashes with this method being called + // with a null m_frame->settings(), which can only happen if the frame doesn't have a page. + // Sadly we have no reproducible cases of this. + // We think that any frame without a page shouldn't have any loads happening in it, yet + // there is at least one code path where that is not true. + ASSERT(m_frame->settings()); + // When in private browsing mode, prevent caching to disk - if (policy == StorageAllowed && m_frame->settings()->privateBrowsingEnabled()) + if (policy == StorageAllowed && m_frame->settings() && m_frame->settings()->privateBrowsingEnabled()) policy = StorageAllowedInMemoryOnly; } +#if ENABLE(BLOB) +AsyncFileStream* ResourceLoader::createAsyncFileStream(FileStreamClient* client) +{ + // It is OK to simply return a pointer since FileStreamProxy::create adds an extra ref. + return FileStreamProxy::create(m_frame->document()->scriptExecutionContext(), client).get(); +} +#endif + } diff --git a/WebCore/loader/ResourceLoader.h b/WebCore/loader/ResourceLoader.h index 3178eb4..29afbc1 100644 --- a/WebCore/loader/ResourceLoader.h +++ b/WebCore/loader/ResourceLoader.h @@ -44,6 +44,7 @@ namespace WebCore { class DocumentLoader; class Frame; class FrameLoader; + class ProtectionSpace; class ResourceHandle; class SharedBuffer; @@ -83,13 +84,17 @@ namespace WebCore { virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent); virtual void didReceiveResponse(const ResourceResponse&); virtual void didReceiveData(const char*, int, long long lengthReceived, bool allAtOnce); + virtual void didReceiveCachedMetadata(const char*, int) { } void willStopBufferingData(const char*, int); - virtual void didFinishLoading(); + virtual void didFinishLoading(double finishTime); virtual void didFail(const ResourceError&); virtual bool shouldUseCredentialStorage(); virtual void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); void didCancelAuthenticationChallenge(const AuthenticationChallenge&); +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + virtual bool canAuthenticateAgainstProtectionSpace(const ProtectionSpace&); +#endif virtual void receivedCancellation(const AuthenticationChallenge&); // ResourceHandleClient @@ -97,7 +102,8 @@ namespace WebCore { virtual void didSendData(ResourceHandle*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent); virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); - virtual void didFinishLoading(ResourceHandle*); + virtual void didReceiveCachedMetadata(ResourceHandle*, const char* data, int length) { didReceiveCachedMetadata(data, length); } + virtual void didFinishLoading(ResourceHandle*, double finishTime); virtual void didFail(ResourceHandle*, const ResourceError&); virtual void wasBlocked(ResourceHandle*); virtual void cannotShowURL(ResourceHandle*); @@ -105,6 +111,9 @@ namespace WebCore { virtual bool shouldUseCredentialStorage(ResourceHandle*) { return shouldUseCredentialStorage(); } virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) { didReceiveAuthenticationChallenge(challenge); } virtual void didCancelAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) { didCancelAuthenticationChallenge(challenge); } +#if USE(PROTECTION_SPACE_AUTH_CALLBACK) + virtual bool canAuthenticateAgainstProtectionSpace(ResourceHandle*, const ProtectionSpace& protectionSpace) { return canAuthenticateAgainstProtectionSpace(protectionSpace); } +#endif virtual void receivedCancellation(ResourceHandle*, const AuthenticationChallenge& challenge) { receivedCancellation(challenge); } virtual void willCacheResponse(ResourceHandle*, CacheStoragePolicy&); #if PLATFORM(MAC) @@ -113,6 +122,9 @@ namespace WebCore { #if USE(CFNETWORK) virtual bool shouldCacheResponse(ResourceHandle*, CFCachedURLResponseRef); #endif +#if ENABLE(BLOB) + virtual AsyncFileStream* createAsyncFileStream(FileStreamClient*); +#endif ResourceHandle* handle() const { return m_handle.get(); } bool sendResourceLoadCallbacks() const { return m_sendResourceLoadCallbacks; } @@ -127,7 +139,7 @@ namespace WebCore { #endif virtual void didCancel(const ResourceError&); - void didFinishLoadingOnePart(); + void didFinishLoadingOnePart(double finishTime); const ResourceRequest& request() const { return m_request; } bool reachedTerminalState() const { return m_reachedTerminalState; } diff --git a/WebCore/loader/FTPDirectoryDocument.h b/WebCore/loader/SinkDocument.cpp index b208c4e..47535dc 100644 --- a/WebCore/loader/FTPDirectoryDocument.h +++ b/WebCore/loader/SinkDocument.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -16,33 +16,47 @@ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -#ifndef FTPDirectoryDocument_h -#define FTPDirectoryDocument_h -#include "HTMLDocument.h" +#include "config.h" +#include "SinkDocument.h" + +#include "RawDataDocumentParser.h" namespace WebCore { - -class DOMImplementation; - -class FTPDirectoryDocument : public HTMLDocument { + +class SinkDocumentParser : public RawDataDocumentParser { public: - static PassRefPtr<FTPDirectoryDocument> create(Frame* frame) + static PassRefPtr<SinkDocumentParser> create(SinkDocument* document) { - return adoptRef(new FTPDirectoryDocument(frame)); + return adoptRef(new SinkDocumentParser(document)); } - + private: - FTPDirectoryDocument(Frame*); - virtual Tokenizer* createTokenizer(); + SinkDocumentParser(SinkDocument* document) + : RawDataDocumentParser(document) + { + } + + // Ignore all data. + virtual void appendBytes(DocumentWriter*, const char*, int, bool) { } }; - -} // namespace WebCore -#endif // FTPDirectoryDocument_h +SinkDocument::SinkDocument(Frame* frame, const KURL& url) + : HTMLDocument(frame, url) +{ + setCompatibilityMode(QuirksMode); + lockCompatibilityMode(); +} + +PassRefPtr<DocumentParser> SinkDocument::createParser() +{ + return SinkDocumentParser::create(this); +} + +} // namespace WebCore diff --git a/WebCore/loader/TextDocument.h b/WebCore/loader/SinkDocument.h index 53e3074..50152ff 100644 --- a/WebCore/loader/TextDocument.h +++ b/WebCore/loader/SinkDocument.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -16,36 +16,34 @@ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TextDocument_h -#define TextDocument_h +#ifndef SinkDocument_h +#define SinkDocument_h #include "HTMLDocument.h" namespace WebCore { -class HTMLViewSourceDocument; - -class TextDocument : public HTMLDocument { +class SinkDocument : public HTMLDocument { public: - static PassRefPtr<TextDocument> create(Frame* frame) + static PassRefPtr<SinkDocument> create(Frame* frame, const KURL& url) { - return adoptRef(new TextDocument(frame)); + return adoptRef(new SinkDocument(frame, url)); } private: - TextDocument(Frame*); + SinkDocument(Frame*, const KURL&); - virtual Tokenizer* createTokenizer(); + virtual PassRefPtr<DocumentParser> createParser(); }; -Tokenizer* createTextTokenizer(HTMLViewSourceDocument*); -} +}; // namespace WebCore -#endif // TextDocument_h +#endif // SinkDocument_h diff --git a/WebCore/loader/SubframeLoader.cpp b/WebCore/loader/SubframeLoader.cpp new file mode 100644 index 0000000..d486de0 --- /dev/null +++ b/WebCore/loader/SubframeLoader.cpp @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2008 Alp Toker <alp@atoker.com> + * Copyright (C) Research In Motion Limited 2009. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SubframeLoader.h" + +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "HTMLAppletElement.h" +#include "HTMLFrameElementBase.h" +#include "HTMLNames.h" +#include "HTMLPlugInImageElement.h" +#include "MIMETypeRegistry.h" +#include "Page.h" +#include "PluginData.h" +#include "PluginDocument.h" +#include "RenderEmbeddedObject.h" +#include "RenderView.h" +#include "Settings.h" +#include "XSSAuditor.h" + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "HTMLMediaElement.h" +#include "RenderVideo.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +SubframeLoader::SubframeLoader(Frame* frame) + : m_containsPlugins(false) + , m_frame(frame) +{ +} + +void SubframeLoader::clear() +{ + m_containsPlugins = false; +} + +bool SubframeLoader::requestFrame(HTMLFrameOwnerElement* ownerElement, const String& urlString, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList) +{ + // Support for <frame src="javascript:string"> + KURL scriptURL; + KURL url; + if (protocolIsJavaScript(urlString)) { + scriptURL = completeURL(urlString); // completeURL() encodes the URL. + url = blankURL(); + } else + url = completeURL(urlString); + + Frame* frame = loadOrRedirectSubframe(ownerElement, url, frameName, lockHistory, lockBackForwardList); + if (!frame) + return false; + + if (!scriptURL.isEmpty()) + frame->script()->executeIfJavaScriptURL(scriptURL); + + return true; +} + +bool SubframeLoader::resourceWillUsePlugin(const String& url, const String& mimeType) +{ + KURL completedURL; + if (!url.isEmpty()) + completedURL = completeURL(url); + bool useFallback; + return shouldUsePlugin(completedURL, mimeType, false, useFallback); +} + +bool SubframeLoader::requestObject(HTMLPlugInImageElement* ownerElement, const String& url, const AtomicString& frameName, + const String& mimeType, const Vector<String>& paramNames, const Vector<String>& paramValues) +{ + if (url.isEmpty() && mimeType.isEmpty()) + return false; + + if (!m_frame->script()->xssAuditor()->canLoadObject(url)) { + // It is unsafe to honor the request for this object. + return false; + } + + // FIXME: None of this code should use renderers! + RenderEmbeddedObject* renderer = ownerElement->renderEmbeddedObject(); + ASSERT(renderer); + if (!renderer) + return false; + + KURL completedURL; + if (!url.isEmpty()) + completedURL = completeURL(url); + + bool useFallback; + if (shouldUsePlugin(completedURL, mimeType, renderer->hasFallbackContent(), useFallback)) { + Settings* settings = m_frame->settings(); + if ((!allowPlugins(AboutToInstantiatePlugin) + // Application plugins are plugins implemented by the user agent, for example Qt plugins, + // as opposed to third-party code such as flash. The user agent decides whether or not they are + // permitted, rather than WebKit. + && !MIMETypeRegistry::isApplicationPluginMIMEType(mimeType)) + || (!settings->isJavaEnabled() && MIMETypeRegistry::isJavaAppletMIMEType(mimeType))) + return false; + if (m_frame->document() && m_frame->document()->securityOrigin()->isSandboxed(SandboxPlugins)) + return false; + + ASSERT(ownerElement->hasTagName(objectTag) || ownerElement->hasTagName(embedTag)); + HTMLPlugInImageElement* pluginElement = static_cast<HTMLPlugInImageElement*>(ownerElement); + + return loadPlugin(pluginElement, completedURL, mimeType, paramNames, paramValues, useFallback); + } + + // If the plug-in element already contains a subframe, loadOrRedirectSubframe will re-use it. Otherwise, + // it will create a new frame and set it as the RenderPart's widget, causing what was previously + // in the widget to be torn down. + return loadOrRedirectSubframe(ownerElement, completedURL, frameName, true, true); +} + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +PassRefPtr<Widget> SubframeLoader::loadMediaPlayerProxyPlugin(Node* node, const KURL& url, + const Vector<String>& paramNames, const Vector<String>& paramValues) +{ + ASSERT(node->hasTagName(videoTag) || node->hasTagName(audioTag)); + + if (!m_frame->script()->xssAuditor()->canLoadObject(url.string())) + return 0; + + KURL completedURL; + if (!url.isEmpty()) + completedURL = completeURL(url); + + if (!m_frame->document()->securityOrigin()->canDisplay(completedURL)) { + FrameLoader::reportLocalLoadFailed(m_frame, completedURL.string()); + return 0; + } + + HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(node); + RenderPart* renderer = toRenderPart(node->renderer()); + IntSize size; + + if (renderer) + size = IntSize(renderer->contentWidth(), renderer->contentHeight()); + else if (mediaElement->isVideo()) + size = RenderVideo::defaultSize(); + + m_frame->loader()->checkIfRunInsecureContent(m_frame->document()->securityOrigin(), completedURL); + + RefPtr<Widget> widget = m_frame->loader()->client()->createMediaPlayerProxyPlugin(size, mediaElement, completedURL, + paramNames, paramValues, "application/x-media-element-proxy-plugin"); + + if (widget && renderer) { + renderer->setWidget(widget); + renderer->node()->setNeedsStyleRecalc(SyntheticStyleChange); + } + m_containsPlugins = true; + + return widget ? widget.release() : 0; +} + +void FrameLoader::hideMediaPlayerProxyPlugin(Widget* widget) +{ + m_client->hideMediaPlayerProxyPlugin(widget); +} + +void FrameLoader::showMediaPlayerProxyPlugin(Widget* widget) +{ + m_client->showMediaPlayerProxyPlugin(widget); +} + +#endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO) + +PassRefPtr<Widget> SubframeLoader::createJavaAppletWidget(const IntSize& size, HTMLAppletElement* element, const HashMap<String, String>& args) +{ + String baseURLString; + String codeBaseURLString; + Vector<String> paramNames; + Vector<String> paramValues; + HashMap<String, String>::const_iterator end = args.end(); + for (HashMap<String, String>::const_iterator it = args.begin(); it != end; ++it) { + if (equalIgnoringCase(it->first, "baseurl")) + baseURLString = it->second; + else if (equalIgnoringCase(it->first, "codebase")) + codeBaseURLString = it->second; + paramNames.append(it->first); + paramValues.append(it->second); + } + + if (!codeBaseURLString.isEmpty()) { + KURL codeBaseURL = completeURL(codeBaseURLString); + if (!element->document()->securityOrigin()->canDisplay(codeBaseURL)) { + FrameLoader::reportLocalLoadFailed(m_frame, codeBaseURL.string()); + return 0; + } + } + + if (baseURLString.isEmpty()) + baseURLString = m_frame->document()->baseURL().string(); + KURL baseURL = completeURL(baseURLString); + + RefPtr<Widget> widget; + if (allowPlugins(AboutToInstantiatePlugin)) + widget = m_frame->loader()->client()->createJavaAppletWidget(size, element, baseURL, paramNames, paramValues); + if (!widget) + return 0; + + m_containsPlugins = true; + return widget; +} + +Frame* SubframeLoader::loadOrRedirectSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList) +{ + Frame* frame = ownerElement->contentFrame(); + if (frame) + frame->navigationScheduler()->scheduleLocationChange(url.string(), m_frame->loader()->outgoingReferrer(), lockHistory, lockBackForwardList); + else + frame = loadSubframe(ownerElement, url, frameName, m_frame->loader()->outgoingReferrer()); + return frame; +} + +Frame* SubframeLoader::loadSubframe(HTMLFrameOwnerElement* ownerElement, const KURL& url, const String& name, const String& referrer) +{ + bool allowsScrolling = true; + int marginWidth = -1; + int marginHeight = -1; + if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) { + HTMLFrameElementBase* o = static_cast<HTMLFrameElementBase*>(ownerElement); + allowsScrolling = o->scrollingMode() != ScrollbarAlwaysOff; + marginWidth = o->getMarginWidth(); + marginHeight = o->getMarginHeight(); + } + + if (!ownerElement->document()->securityOrigin()->canDisplay(url)) { + FrameLoader::reportLocalLoadFailed(m_frame, url.string()); + return 0; + } + + bool hideReferrer = SecurityOrigin::shouldHideReferrer(url, referrer); + RefPtr<Frame> frame = m_frame->loader()->client()->createFrame(url, name, ownerElement, hideReferrer ? String() : referrer, allowsScrolling, marginWidth, marginHeight); + + if (!frame) { + m_frame->loader()->checkCallImplicitClose(); + return 0; + } + + // All new frames will have m_isComplete set to true at this point due to synchronously loading + // an empty document in FrameLoader::init(). But many frames will now be starting an + // asynchronous load of url, so we set m_isComplete to false and then check if the load is + // actually completed below. (Note that we set m_isComplete to false even for synchronous + // loads, so that checkCompleted() below won't bail early.) + // FIXME: Can we remove this entirely? m_isComplete normally gets set to false when a load is committed. + frame->loader()->started(); + + RenderObject* renderer = ownerElement->renderer(); + FrameView* view = frame->view(); + if (renderer && renderer->isWidget() && view) + toRenderWidget(renderer)->setWidget(view); + + m_frame->loader()->checkCallImplicitClose(); + + // Some loads are performed synchronously (e.g., about:blank and loads + // cancelled by returning a null ResourceRequest from requestFromDelegate). + // In these cases, the synchronous load would have finished + // before we could connect the signals, so make sure to send the + // completed() signal for the child by hand and mark the load as being + // complete. + // FIXME: In this case the Frame will have finished loading before + // it's being added to the child list. It would be a good idea to + // create the child first, then invoke the loader separately. + if (frame->loader()->state() == FrameStateComplete && !frame->loader()->policyDocumentLoader()) + frame->loader()->checkCompleted(); + + return frame.get(); +} + +bool SubframeLoader::allowPlugins(ReasonForCallingAllowPlugins reason) +{ + Settings* settings = m_frame->settings(); + bool allowed = m_frame->loader()->client()->allowPlugins(settings && settings->arePluginsEnabled()); + if (!allowed && reason == AboutToInstantiatePlugin) + m_frame->loader()->client()->didNotAllowPlugins(); + return allowed; +} + +bool SubframeLoader::shouldUsePlugin(const KURL& url, const String& mimeType, bool hasFallback, bool& useFallback) +{ + if (m_frame->loader()->client()->shouldUsePluginDocument(mimeType)) { + useFallback = false; + return true; + } + + // Allow other plug-ins to win over QuickTime because if the user has installed a plug-in that + // can handle TIFF (which QuickTime can also handle) they probably intended to override QT. + if (m_frame->page() && (mimeType == "image/tiff" || mimeType == "image/tif" || mimeType == "image/x-tiff")) { + const PluginData* pluginData = m_frame->page()->pluginData(); + String pluginName = pluginData ? pluginData->pluginNameForMimeType(mimeType) : String(); + if (!pluginName.isEmpty() && !pluginName.contains("QuickTime", false)) + return true; + } + + ObjectContentType objectType = m_frame->loader()->client()->objectContentType(url, mimeType); + // If an object's content can't be handled and it has no fallback, let + // it be handled as a plugin to show the broken plugin icon. + useFallback = objectType == ObjectContentNone && hasFallback; + return objectType == ObjectContentNone || objectType == ObjectContentNetscapePlugin || objectType == ObjectContentOtherPlugin; +} + +Document* SubframeLoader::document() const +{ + return m_frame->document(); +} + +bool SubframeLoader::loadPlugin(HTMLPlugInImageElement* pluginElement, const KURL& url, const String& mimeType, + const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback) +{ + RenderEmbeddedObject* renderer = pluginElement->renderEmbeddedObject(); + + // FIXME: This code should not depend on renderer! + if (!renderer || useFallback) + return false; + + if (!document()->securityOrigin()->canDisplay(url)) { + FrameLoader::reportLocalLoadFailed(m_frame, url.string()); + return false; + } + + FrameLoader* frameLoader = m_frame->loader(); + frameLoader->checkIfRunInsecureContent(document()->securityOrigin(), url); + + IntSize contentSize(renderer->contentWidth(), renderer->contentHeight()); + bool loadManually = document()->isPluginDocument() && !m_containsPlugins && toPluginDocument(document())->shouldLoadPluginManually(); + RefPtr<Widget> widget = frameLoader->client()->createPlugin(contentSize, + pluginElement, url, paramNames, paramValues, mimeType, loadManually); + + if (!widget) { + renderer->setShowsMissingPluginIndicator(); + return false; + } + + renderer->setWidget(widget); + m_containsPlugins = true; + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) || ENABLE(3D_PLUGIN) + pluginElement->setNeedsStyleRecalc(SyntheticStyleChange); +#endif + return true; +} + +KURL SubframeLoader::completeURL(const String& url) const +{ + ASSERT(m_frame->document()); + return m_frame->document()->completeURL(url); +} + +} // namespace WebCore diff --git a/WebCore/loader/SubframeLoader.h b/WebCore/loader/SubframeLoader.h new file mode 100644 index 0000000..a573045 --- /dev/null +++ b/WebCore/loader/SubframeLoader.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) Research In Motion Limited 2009. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SubframeLoader_h +#define SubframeLoader_h + +#include "FrameLoaderTypes.h" +#include "PlatformString.h" +#include <wtf/Forward.h> +#include <wtf/HashMap.h> +#include <wtf/Noncopyable.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Document; +class Frame; +class FrameLoaderClient; +class HTMLAppletElement; +class HTMLFrameOwnerElement; +class HTMLPlugInImageElement; +class IntSize; +class KURL; +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +class Node; +#endif +class Widget; + +// This is a slight misnomer. It handles the higher level logic of loading both subframes and plugins. +class SubframeLoader : public Noncopyable { +public: + SubframeLoader(Frame*); + + void clear(); + + bool requestFrame(HTMLFrameOwnerElement*, const String& url, const AtomicString& frameName, bool lockHistory = true, bool lockBackForwardList = true); + bool requestObject(HTMLPlugInImageElement*, const String& url, const AtomicString& frameName, + const String& serviceType, const Vector<String>& paramNames, const Vector<String>& paramValues); + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + // FIXME: This should take Element* instead of Node*, or better yet the + // specific type of Element which this code depends on. + PassRefPtr<Widget> loadMediaPlayerProxyPlugin(Node*, const KURL&, const Vector<String>& paramNames, const Vector<String>& paramValues); +#endif + + PassRefPtr<Widget> createJavaAppletWidget(const IntSize&, HTMLAppletElement*, const HashMap<String, String>& args); + + bool allowPlugins(ReasonForCallingAllowPlugins); + + bool containsPlugins() const { return m_containsPlugins; } + + bool resourceWillUsePlugin(const String& url, const String& mimeType); + +private: + Frame* loadOrRedirectSubframe(HTMLFrameOwnerElement*, const KURL&, const AtomicString& frameName, bool lockHistory, bool lockBackForwardList); + Frame* loadSubframe(HTMLFrameOwnerElement*, const KURL&, const String& name, const String& referrer); + bool loadPlugin(HTMLPlugInImageElement*, const KURL&, const String& mimeType, + const Vector<String>& paramNames, const Vector<String>& paramValues, bool useFallback); + + bool shouldUsePlugin(const KURL&, const String& mimeType, bool hasFallback, bool& useFallback); + + Document* document() const; + + bool m_containsPlugins; + Frame* m_frame; + + KURL completeURL(const String&) const; +}; + +} // namespace WebCore + +#endif // SubframeLoader_h diff --git a/WebCore/loader/SubresourceLoader.cpp b/WebCore/loader/SubresourceLoader.cpp index ebb943a..5377382 100644 --- a/WebCore/loader/SubresourceLoader.cpp +++ b/WebCore/loader/SubresourceLoader.cpp @@ -51,7 +51,6 @@ SubresourceLoader::SubresourceLoader(Frame* frame, SubresourceLoaderClient* clie #ifndef NDEBUG subresourceLoaderCounter.increment(); #endif - m_documentLoader->addSubresourceLoader(this); } SubresourceLoader::~SubresourceLoader() @@ -67,42 +66,26 @@ PassRefPtr<SubresourceLoader> SubresourceLoader::create(Frame* frame, Subresourc return 0; FrameLoader* fl = frame->loader(); - if (securityCheck == DoSecurityCheck && (fl->state() == FrameStateProvisional || fl->activeDocumentLoader()->isStopping())) + if (securityCheck == DoSecurityCheck && (fl->state() == FrameStateProvisional || !fl->activeDocumentLoader() || fl->activeDocumentLoader()->isStopping())) return 0; ResourceRequest newRequest = request; - if (securityCheck == DoSecurityCheck - && SecurityOrigin::restrictAccessToLocal() - && !SecurityOrigin::canLoad(request.url(), String(), frame->document())) { + if (securityCheck == DoSecurityCheck && !frame->document()->securityOrigin()->canDisplay(request.url())) { FrameLoader::reportLocalLoadFailed(frame, request.url().string()); return 0; } - + if (SecurityOrigin::shouldHideReferrer(request.url(), fl->outgoingReferrer())) newRequest.clearHTTPReferrer(); -#ifdef ANDROID_FIX - else if (request.httpReferrer().isEmpty()) -#else else if (!request.httpReferrer()) -#endif newRequest.setHTTPReferrer(fl->outgoingReferrer()); FrameLoader::addHTTPOriginIfNeeded(newRequest, fl->outgoingOrigin()); - // Use the original request's cache policy for two reasons: - // 1. For POST requests, we mutate the cache policy for the main resource, - // but we do not want this to apply to subresources - // 2. Delegates that modify the cache policy using willSendRequest: should - // not affect any other resources. Such changes need to be done - // per request. - if (newRequest.isConditional()) - newRequest.setCachePolicy(ReloadIgnoringCacheData); - else - newRequest.setCachePolicy(fl->originalRequest().cachePolicy()); - fl->addExtraFieldsToSubresourceRequest(newRequest); RefPtr<SubresourceLoader> subloader(adoptRef(new SubresourceLoader(frame, client, sendResourceLoadCallbacks, shouldContentSniff))); + subloader->documentLoader()->addSubresourceLoader(subloader.get()); if (!subloader->load(newRequest)) return 0; @@ -157,7 +140,7 @@ void SubresourceLoader::didReceiveResponse(const ResourceResponse& r) // After the first multipart section is complete, signal to delegates that this load is "finished" m_documentLoader->subresourceLoaderFinishedLoadingOnePart(this); - didFinishLoadingOnePart(); + didFinishLoadingOnePart(0); } } @@ -175,7 +158,17 @@ void SubresourceLoader::didReceiveData(const char* data, int length, long long l m_client->didReceiveData(this, data, length); } -void SubresourceLoader::didFinishLoading() +void SubresourceLoader::didReceiveCachedMetadata(const char* data, int length) +{ + // Reference the object in this method since the additional processing can do + // anything including removing the last reference to this object; one example of this is 3266216. + RefPtr<SubresourceLoader> protect(this); + + if (m_client) + m_client->didReceiveCachedMetadata(this, data, length); +} + +void SubresourceLoader::didFinishLoading(double finishTime) { if (cancelled()) return; @@ -192,7 +185,7 @@ void SubresourceLoader::didFinishLoading() if (cancelled()) return; m_documentLoader->removeSubresourceLoader(this); - ResourceLoader::didFinishLoading(); + ResourceLoader::didFinishLoading(finishTime); } void SubresourceLoader::didFail(const ResourceError& error) @@ -253,14 +246,20 @@ void SubresourceLoader::didReceiveAuthenticationChallenge(const AuthenticationCh { RefPtr<SubresourceLoader> protect(this); + ASSERT(handle()->hasAuthenticationChallenge()); + if (m_client) m_client->didReceiveAuthenticationChallenge(this, challenge); // The SubResourceLoaderClient may have cancelled this ResourceLoader in response to the challenge. - // If that's the case, don't call didReceiveAuthenticationChallenge + // If that's the case, don't call didReceiveAuthenticationChallenge. if (reachedTerminalState()) return; - + + // It may have also handled authentication on its own. + if (!handle()->hasAuthenticationChallenge()) + return; + ResourceLoader::didReceiveAuthenticationChallenge(challenge); } diff --git a/WebCore/loader/SubresourceLoader.h b/WebCore/loader/SubresourceLoader.h index 907d917..cb7ed81 100644 --- a/WebCore/loader/SubresourceLoader.h +++ b/WebCore/loader/SubresourceLoader.h @@ -51,7 +51,8 @@ namespace WebCore { virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent); virtual void didReceiveResponse(const ResourceResponse&); virtual void didReceiveData(const char*, int, long long lengthReceived, bool allAtOnce); - virtual void didFinishLoading(); + virtual void didReceiveCachedMetadata(const char*, int); + virtual void didFinishLoading(double finishTime); virtual void didFail(const ResourceError&); virtual bool shouldUseCredentialStorage(); virtual void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); diff --git a/WebCore/loader/SubresourceLoaderClient.h b/WebCore/loader/SubresourceLoaderClient.h index 76fde47..e18abe3 100644 --- a/WebCore/loader/SubresourceLoaderClient.h +++ b/WebCore/loader/SubresourceLoaderClient.h @@ -47,6 +47,7 @@ public: virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&) { } virtual void didReceiveData(SubresourceLoader*, const char*, int /*lengthReceived*/) { } + virtual void didReceiveCachedMetadata(SubresourceLoader*, const char*, int /*lengthReceived*/) { } virtual void didFinishLoading(SubresourceLoader*) { } virtual void didFail(SubresourceLoader*, const ResourceError&) { } diff --git a/WebCore/loader/TextDocument.cpp b/WebCore/loader/TextDocument.cpp deleted file mode 100644 index a3d7061..0000000 --- a/WebCore/loader/TextDocument.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * 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, - * 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 "TextDocument.h" - -#include "Element.h" -#include "HTMLNames.h" -#include "HTMLViewSourceDocument.h" -#include "SegmentedString.h" -#include "Text.h" -#include "XMLTokenizer.h" - -using namespace std; - -namespace WebCore { - -using namespace HTMLNames; - -class TextTokenizer : public Tokenizer { -public: - TextTokenizer(Document*); - virtual ~TextTokenizer(); - TextTokenizer(HTMLViewSourceDocument*); - - virtual void write(const SegmentedString&, bool appendData); - virtual void finish(); - virtual bool isWaitingForScripts() const; - - inline void checkBuffer(int len = 10) - { - if ((m_dest - m_buffer) > m_size - len) { - // Enlarge buffer - int newSize = std::max(m_size * 2, m_size + len); - int oldOffset = m_dest - m_buffer; - m_buffer = static_cast<UChar*>(fastRealloc(m_buffer, newSize * sizeof(UChar))); - m_dest = m_buffer + oldOffset; - m_size = newSize; - } - } - -private: - Document* m_doc; - Element* m_preElement; - - bool m_skipLF; - - int m_size; - UChar* m_buffer; - UChar* m_dest; -}; - -TextTokenizer::TextTokenizer(Document* doc) - : m_doc(doc) - , m_preElement(0) - , m_skipLF(false) -{ - // Allocate buffer - m_size = 254; - m_buffer = static_cast<UChar*>(fastMalloc(sizeof(UChar) * m_size)); - m_dest = m_buffer; -} - -TextTokenizer::TextTokenizer(HTMLViewSourceDocument* doc) - : Tokenizer(true) - , m_doc(doc) - , m_preElement(0) - , m_skipLF(false) -{ - // Allocate buffer - 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); -} - -void TextTokenizer::write(const SegmentedString& s, bool) -{ - ExceptionCode ec; - - m_dest = m_buffer; - - SegmentedString str = s; - while (!str.isEmpty()) { - UChar c = *str; - - if (c == '\r') { - *m_dest++ = '\n'; - - // possibly skip an LF in the case of an CRLF sequence - m_skipLF = true; - } else if (c == '\n') { - if (!m_skipLF) - *m_dest++ = c; - else - m_skipLF = false; - } else { - *m_dest++ = c; - m_skipLF = false; - } - - str.advance(); - - // Maybe enlarge the buffer - checkBuffer(); - } - - if (!m_preElement && !inViewSourceMode()) { - RefPtr<Element> rootElement = m_doc->createElement(htmlTag, false); - m_doc->appendChild(rootElement, ec); - - RefPtr<Element> body = m_doc->createElement(bodyTag, false); - rootElement->appendChild(body, 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); - - m_preElement = preElement.get(); - } - - String string = String(m_buffer, m_dest - m_buffer); - if (inViewSourceMode()) { - static_cast<HTMLViewSourceDocument*>(m_doc)->addViewSourceText(string); - return; - } - - unsigned charsLeft = string.length(); - while (charsLeft) { - // split large text to nodes of manageable size - RefPtr<Text> text = Text::createWithLengthLimit(m_doc, string, charsLeft); - m_preElement->appendChild(text, ec); - } -} - -void TextTokenizer::finish() -{ - if (!m_preElement) - write(SegmentedString(), true); // Create document structure for an empty text document. - m_preElement = 0; - fastFree(m_buffer); - m_buffer = 0; - m_dest = 0; - - m_doc->finishedParsing(); -} - -bool TextTokenizer::isWaitingForScripts() const -{ - // A text document is never waiting for scripts - return false; -} - -TextDocument::TextDocument(Frame* frame) - : HTMLDocument(frame) -{ -} - -Tokenizer* TextDocument::createTokenizer() -{ - return new TextTokenizer(this); -} - -Tokenizer* createTextTokenizer(HTMLViewSourceDocument* document) -{ - return new TextTokenizer(document); -} - -} diff --git a/WebCore/loader/TextResourceDecoder.cpp b/WebCore/loader/TextResourceDecoder.cpp index 6ddd604..4002b38 100644 --- a/WebCore/loader/TextResourceDecoder.cpp +++ b/WebCore/loader/TextResourceDecoder.cpp @@ -488,7 +488,7 @@ bool TextResourceDecoder::checkForCSSCharset(const char* data, size_t len, bool& if (pos == dataEnd) return false; - int encodingNameLength = pos - dataStart + 1; + int encodingNameLength = pos - dataStart; ++pos; if (!skipWhitespace(pos, dataEnd)) @@ -568,7 +568,7 @@ bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool if (xmlDeclarationEnd == pEnd) return false; // No need for +1, because we have an extra "?" to lose at the end of XML declaration. - int len; + int len = 0; int pos = findXMLEncoding(ptr, xmlDeclarationEnd - ptr, len); if (pos != -1) setEncoding(findTextEncoding(ptr + pos, len), EncodingFromXMLHeader); @@ -812,7 +812,7 @@ String TextResourceDecoder::decode(const char* data, size_t len) ASSERT(m_encoding.isValid()); if (!m_codec) - m_codec.set(newTextCodec(m_encoding).release()); + m_codec = newTextCodec(m_encoding); if (m_buffer.isEmpty()) return m_codec->decode(data + lengthOfBOM, len - lengthOfBOM, false, m_contentType == XML, m_sawError); @@ -842,7 +842,7 @@ String TextResourceDecoder::flush() } if (!m_codec) - m_codec.set(newTextCodec(m_encoding).release()); + m_codec = newTextCodec(m_encoding); String result = m_codec->decode(m_buffer.data(), m_buffer.size(), true, m_contentType == XML && !m_useLenientXMLDecoding, m_sawError); m_buffer.clear(); diff --git a/WebCore/loader/ThreadableLoaderClient.h b/WebCore/loader/ThreadableLoaderClient.h index b8a6584..bcf68be 100644 --- a/WebCore/loader/ThreadableLoaderClient.h +++ b/WebCore/loader/ThreadableLoaderClient.h @@ -31,6 +31,8 @@ #ifndef ThreadableLoaderClient_h #define ThreadableLoaderClient_h +#include <wtf/Noncopyable.h> + namespace WebCore { class ResourceError; diff --git a/WebCore/loader/WorkerThreadableLoader.cpp b/WebCore/loader/WorkerThreadableLoader.cpp index 6837ca1..4d18c28 100644 --- a/WebCore/loader/WorkerThreadableLoader.cpp +++ b/WebCore/loader/WorkerThreadableLoader.cpp @@ -34,7 +34,7 @@ #include "WorkerThreadableLoader.h" -#include "GenericWorkerTask.h" +#include "CrossThreadTask.h" #include "ResourceError.h" #include "ResourceRequest.h" #include "ResourceResponse.h" @@ -42,7 +42,6 @@ #include "WorkerContext.h" #include "WorkerLoaderProxy.h" #include "WorkerThread.h" -#include <memory> #include <wtf/OwnPtr.h> #include <wtf/Threading.h> #include <wtf/Vector.h> @@ -101,7 +100,7 @@ WorkerThreadableLoader::MainThreadBridge::~MainThreadBridge() { } -void WorkerThreadableLoader::MainThreadBridge::mainThreadCreateLoader(ScriptExecutionContext* context, MainThreadBridge* thisPtr, auto_ptr<CrossThreadResourceRequestData> requestData, ThreadableLoaderOptions options) +void WorkerThreadableLoader::MainThreadBridge::mainThreadCreateLoader(ScriptExecutionContext* context, MainThreadBridge* thisPtr, PassOwnPtr<CrossThreadResourceRequestData> requestData, ThreadableLoaderOptions options) { ASSERT(isMainThread()); ASSERT(context->isDocument()); @@ -174,7 +173,7 @@ void WorkerThreadableLoader::MainThreadBridge::didSendData(unsigned long long by m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidSendData, m_workerClientWrapper, bytesSent, totalBytesToBeSent), m_taskMode); } -static void workerContextDidReceiveResponse(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, auto_ptr<CrossThreadResourceResponseData> responseData) +static void workerContextDidReceiveResponse(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, PassOwnPtr<CrossThreadResourceResponseData> responseData) { ASSERT_UNUSED(context, context->isWorkerContext()); OwnPtr<ResourceResponse> response(ResourceResponse::adopt(responseData)); @@ -186,7 +185,7 @@ void WorkerThreadableLoader::MainThreadBridge::didReceiveResponse(const Resource m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidReceiveResponse, m_workerClientWrapper, response), m_taskMode); } -static void workerContextDidReceiveData(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, auto_ptr<Vector<char> > vectorData) +static void workerContextDidReceiveData(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, PassOwnPtr<Vector<char> > vectorData) { ASSERT_UNUSED(context, context->isWorkerContext()); workerClientWrapper->didReceiveData(vectorData->data(), vectorData->size()); @@ -194,9 +193,9 @@ static void workerContextDidReceiveData(ScriptExecutionContext* context, RefPtr< 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. + OwnPtr<Vector<char> > vector = adoptPtr(new Vector<char>(lengthReceived)); // needs to be an OwnPtr for usage with createCallbackTask. memcpy(vector->data(), data, lengthReceived); - m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidReceiveData, m_workerClientWrapper, vector), m_taskMode); + m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidReceiveData, m_workerClientWrapper, vector.release()), m_taskMode); } static void workerContextDidFinishLoading(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, unsigned long identifier) @@ -232,7 +231,7 @@ void WorkerThreadableLoader::MainThreadBridge::didFailRedirectCheck() m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidFailRedirectCheck, m_workerClientWrapper), m_taskMode); } -static void workerContextDidReceiveAuthenticationCancellation(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, auto_ptr<CrossThreadResourceResponseData> responseData) +static void workerContextDidReceiveAuthenticationCancellation(ScriptExecutionContext* context, RefPtr<ThreadableLoaderClientWrapper> workerClientWrapper, PassOwnPtr<CrossThreadResourceResponseData> responseData) { ASSERT_UNUSED(context, context->isWorkerContext()); OwnPtr<ResourceResponse> response(ResourceResponse::adopt(responseData)); diff --git a/WebCore/loader/WorkerThreadableLoader.h b/WebCore/loader/WorkerThreadableLoader.h index 09f8f85..81da2e0 100644 --- a/WebCore/loader/WorkerThreadableLoader.h +++ b/WebCore/loader/WorkerThreadableLoader.h @@ -38,7 +38,7 @@ #include "ThreadableLoaderClient.h" #include "ThreadableLoaderClientWrapper.h" -#include <memory> +#include <wtf/PassOwnPtr.h> #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> @@ -109,7 +109,7 @@ namespace WebCore { static void mainThreadDestroy(ScriptExecutionContext*, MainThreadBridge*); ~MainThreadBridge(); - static void mainThreadCreateLoader(ScriptExecutionContext*, MainThreadBridge*, std::auto_ptr<CrossThreadResourceRequestData>, ThreadableLoaderOptions); + static void mainThreadCreateLoader(ScriptExecutionContext*, MainThreadBridge*, PassOwnPtr<CrossThreadResourceRequestData>, ThreadableLoaderOptions); static void mainThreadCancel(ScriptExecutionContext*, MainThreadBridge*); virtual void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent); virtual void didReceiveResponse(const ResourceResponse&); diff --git a/WebCore/loader/appcache/ApplicationCache.cpp b/WebCore/loader/appcache/ApplicationCache.cpp index c0cd3ea..2a93765 100644 --- a/WebCore/loader/appcache/ApplicationCache.cpp +++ b/WebCore/loader/appcache/ApplicationCache.cpp @@ -32,6 +32,7 @@ #include "ApplicationCacheResource.h" #include "ApplicationCacheStorage.h" #include "ResourceRequest.h" +#include <wtf/text/CString.h> #include <stdio.h> namespace WebCore { @@ -130,7 +131,7 @@ ApplicationCacheResource* ApplicationCache::resourceForRequest(const ResourceReq { // We only care about HTTP/HTTPS GET requests. if (!requestIsHTTPOrHTTPSGet(request)) - return false; + return 0; KURL url(request.url()); if (url.hasFragmentIdentifier()) diff --git a/WebCore/loader/appcache/ApplicationCache.h b/WebCore/loader/appcache/ApplicationCache.h index 08e2dd3..f073499 100644 --- a/WebCore/loader/appcache/ApplicationCache.h +++ b/WebCore/loader/appcache/ApplicationCache.h @@ -29,11 +29,11 @@ #if ENABLE(OFFLINE_WEB_APPLICATIONS) #include "PlatformString.h" -#include "StringHash.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> +#include <wtf/text/StringHash.h> namespace WebCore { @@ -41,7 +41,6 @@ class ApplicationCacheGroup; class ApplicationCacheResource; class DocumentLoader; class KURL; - class ResourceRequest; typedef Vector<std::pair<KURL, KURL> > FallbackURLVector; diff --git a/WebCore/loader/appcache/ApplicationCacheGroup.cpp b/WebCore/loader/appcache/ApplicationCacheGroup.cpp index c8a485a..8d5d5c0 100644 --- a/WebCore/loader/appcache/ApplicationCacheGroup.cpp +++ b/WebCore/loader/appcache/ApplicationCacheGroup.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,24 +39,40 @@ #include "DOMWindow.h" #include "Frame.h" #include "FrameLoader.h" +#include "FrameLoaderClient.h" #include "MainResourceLoader.h" #include "ManifestParser.h" #include "Page.h" +#include "SecurityOrigin.h" #include "Settings.h" #include <wtf/HashMap.h> +#if ENABLE(INSPECTOR) +#include "InspectorApplicationCacheAgent.h" +#include "InspectorController.h" +#include "ProgressTracker.h" +#else +#include <wtf/UnusedParam.h> +#endif + namespace WebCore { ApplicationCacheGroup::ApplicationCacheGroup(const KURL& manifestURL, bool isCopy) : m_manifestURL(manifestURL) + , m_origin(SecurityOrigin::create(manifestURL)) , m_updateStatus(Idle) , m_downloadingPendingMasterResourceLoadersCount(0) + , m_progressTotal(0) + , m_progressDone(0) , m_frame(0) , m_storageID(0) , m_isObsolete(false) , m_completionType(None) , m_isCopy(isCopy) , m_calledReachedMaxAppCacheSize(false) + , m_loadedSize(0) + , m_availableSpaceInQuota(ApplicationCacheStorage::unknownQuota()) + , m_originQuotaReached(false) { } @@ -143,14 +159,17 @@ void ApplicationCacheGroup::selectCache(Frame* frame, const KURL& passedManifest if (mainResourceCache) { if (manifestURL == mainResourceCache->group()->m_manifestURL) { + // The cache may have gotten obsoleted after we've loaded from it, but before we parsed the document and saw cache manifest. + if (mainResourceCache->group()->isObsolete()) + return; mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext); } else { // The main resource was loaded from cache, so the cache must have an entry for it. Mark it as foreign. - KURL documentURL(documentLoader->url()); - if (documentURL.hasFragmentIdentifier()) - documentURL.removeFragmentIdentifier(); - ApplicationCacheResource* resource = mainResourceCache->resourceForURL(documentURL); + KURL resourceURL(documentLoader->responseURL()); + if (resourceURL.hasFragmentIdentifier()) + resourceURL.removeFragmentIdentifier(); + ApplicationCacheResource* resource = mainResourceCache->resourceForURL(resourceURL); bool inStorage = resource->storageID(); resource->addType(ApplicationCacheResource::Foreign); if (inStorage) @@ -159,7 +178,7 @@ void ApplicationCacheGroup::selectCache(Frame* frame, const KURL& passedManifest // Restart the current navigation from the top of the navigation algorithm, undoing any changes that were made // as part of the initial load. // The navigation will not result in the same resource being loaded, because "foreign" entries are never picked during navigation. - frame->redirectScheduler()->scheduleLocationChange(documentLoader->url(), frame->loader()->referrer(), true); + frame->navigationScheduler()->scheduleLocationChange(documentLoader->url(), frame->loader()->referrer(), true); } return; @@ -368,12 +387,38 @@ void ApplicationCacheGroup::cacheDestroyed(ApplicationCache* cache) } } +void ApplicationCacheGroup::stopLoadingInFrame(Frame* frame) +{ + if (frame != m_frame) + return; + + stopLoading(); +} + +#if ENABLE(INSPECTOR) +static void inspectorUpdateApplicationCacheStatus(Frame* frame) +{ + if (!frame) + return; + + if (Page *page = frame->page()) { + if (InspectorApplicationCacheAgent* applicationCacheAgent = page->inspectorController()->applicationCacheAgent()) { + ApplicationCacheHost::Status status = frame->loader()->documentLoader()->applicationCacheHost()->status(); + applicationCacheAgent->updateApplicationCacheStatus(status); + } + } +} +#endif + void ApplicationCacheGroup::setNewestCache(PassRefPtr<ApplicationCache> newestCache) { m_newestCache = newestCache; m_caches.add(m_newestCache.get()); m_newestCache->setGroup(this); +#if ENABLE(INSPECTOR) + inspectorUpdateApplicationCacheStatus(m_frame); +#endif } void ApplicationCacheGroup::makeObsolete() @@ -384,6 +429,9 @@ void ApplicationCacheGroup::makeObsolete() m_isObsolete = true; cacheStorage().cacheGroupMadeObsolete(this); ASSERT(!m_storageID); +#if ENABLE(INSPECTOR) + inspectorUpdateApplicationCacheStatus(m_frame); +#endif } void ApplicationCacheGroup::update(Frame* frame, ApplicationCacheUpdateOption updateOption) @@ -410,7 +458,7 @@ void ApplicationCacheGroup::update(Frame* frame, ApplicationCacheUpdateOption up ASSERT(!m_frame); m_frame = frame; - m_updateStatus = Checking; + setUpdateStatus(Checking); postListenerTask(ApplicationCacheHost::CHECKING_EVENT, m_associatedDocumentLoaders); if (!m_newestCache) { @@ -442,12 +490,43 @@ PassRefPtr<ResourceHandle> ApplicationCacheGroup::createResourceHandle(const KUR request.setHTTPHeaderField("If-None-Match", eTag); } } - - return ResourceHandle::create(request, this, m_frame, false, true, false); + + RefPtr<ResourceHandle> handle = ResourceHandle::create(m_frame->loader()->networkingContext(), request, this, false, true); +#if ENABLE(INSPECTOR) + // Because willSendRequest only gets called during redirects, we initialize + // the identifier and the first willSendRequest here. + m_currentResourceIdentifier = m_frame->page()->progress()->createUniqueIdentifier(); + if (Page* page = m_frame->page()) { + InspectorController* inspectorController = page->inspectorController(); + inspectorController->identifierForInitialRequest(m_currentResourceIdentifier, m_frame->loader()->documentLoader(), handle->firstRequest()); + ResourceResponse redirectResponse = ResourceResponse(); + inspectorController->willSendRequest(m_currentResourceIdentifier, request, redirectResponse); + } +#endif + return handle; +} + +#if ENABLE(INSPECTOR) +void ApplicationCacheGroup::willSendRequest(ResourceHandle*, ResourceRequest& request, const ResourceResponse& redirectResponse) +{ + // This only gets called by ResourceHandleMac if there is a redirect. + if (Page* page = m_frame->page()) + page->inspectorController()->willSendRequest(m_currentResourceIdentifier, request, redirectResponse); } +#endif void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response) { +#if ENABLE(INSPECTOR) + if (Page* page = m_frame->page()) { + if (handle == m_manifestHandle) { + if (InspectorApplicationCacheAgent* applicationCacheAgent = page->inspectorController()->applicationCacheAgent()) + applicationCacheAgent->didReceiveManifestResponse(m_currentResourceIdentifier, response); + } else + page->inspectorController()->didReceiveResponse(m_currentResourceIdentifier, m_frame->loader()->documentLoader(), response); + } +#endif + if (handle == m_manifestHandle) { didReceiveManifestResponse(response); return; @@ -455,7 +534,7 @@ void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const Res ASSERT(handle == m_currentHandle); - KURL url(handle->request().url()); + KURL url(handle->firstRequest().url()); if (url.hasFragmentIdentifier()) url.removeFragmentIdentifier(); @@ -472,7 +551,7 @@ void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const Res ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(url); if (newestCachedResource) { m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data())); - m_pendingEntries.remove(m_currentHandle->request().url()); + m_pendingEntries.remove(m_currentHandle->firstRequest().url()); m_currentHandle->cancel(); m_currentHandle = 0; // Load the next resource, if any. @@ -482,7 +561,7 @@ void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const Res // 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->firstRequest().url()) { if ((type & ApplicationCacheResource::Explicit) || (type & ApplicationCacheResource::Fallback)) { // Note that cacheUpdateFailed() can cause the cache group to be deleted. cacheUpdateFailed(); @@ -497,10 +576,10 @@ 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* newestCachedResource = m_newestCache->resourceForURL(handle->request().url()); + ApplicationCacheResource* newestCachedResource = m_newestCache->resourceForURL(handle->firstRequest().url()); ASSERT(newestCachedResource); m_cacheBeingUpdated->addResource(ApplicationCacheResource::create(url, newestCachedResource->response(), type, newestCachedResource->data())); - m_pendingEntries.remove(m_currentHandle->request().url()); + m_pendingEntries.remove(m_currentHandle->firstRequest().url()); m_currentHandle->cancel(); m_currentHandle = 0; // Load the next resource, if any. @@ -512,8 +591,15 @@ void ApplicationCacheGroup::didReceiveResponse(ResourceHandle* handle, const Res m_currentResource = ApplicationCacheResource::create(url, response, type); } -void ApplicationCacheGroup::didReceiveData(ResourceHandle* handle, const char* data, int length, int) +void ApplicationCacheGroup::didReceiveData(ResourceHandle* handle, const char* data, int length, int lengthReceived) { +#if ENABLE(INSPECTOR) + if (Page* page = m_frame->page()) + page->inspectorController()->didReceiveContentLength(m_currentResourceIdentifier, lengthReceived); +#else + UNUSED_PARAM(lengthReceived); +#endif + if (handle == m_manifestHandle) { didReceiveManifestData(data, length); return; @@ -523,19 +609,42 @@ void ApplicationCacheGroup::didReceiveData(ResourceHandle* handle, const char* d ASSERT(m_currentResource); m_currentResource->data()->append(data, length); + + m_loadedSize += length; } -void ApplicationCacheGroup::didFinishLoading(ResourceHandle* handle) +void ApplicationCacheGroup::didFinishLoading(ResourceHandle* handle, double finishTime) { +#if ENABLE(INSPECTOR) + if (Page* page = m_frame->page()) + page->inspectorController()->didFinishLoading(m_currentResourceIdentifier, finishTime); +#endif + if (handle == m_manifestHandle) { didFinishLoadingManifest(); return; } - + + // After finishing the loading of any resource, we check if it will + // fit in our last known quota limit. + if (m_availableSpaceInQuota == ApplicationCacheStorage::unknownQuota()) { + // Failed to determine what is left in the quota. Fallback to allowing anything. + if (!cacheStorage().remainingSizeForOriginExcludingCache(m_origin.get(), m_newestCache.get(), m_availableSpaceInQuota)) + m_availableSpaceInQuota = ApplicationCacheStorage::noQuota(); + } + + // Check each resource, as it loads, to see if it would fit in our + // idea of the available quota space. + if (m_availableSpaceInQuota < m_loadedSize) { + m_currentResource = 0; + cacheUpdateFailedDueToOriginQuota(); + return; + } + ASSERT(m_currentHandle == handle); - ASSERT(m_pendingEntries.contains(handle->request().url())); + ASSERT(m_pendingEntries.contains(handle->firstRequest().url())); - m_pendingEntries.remove(handle->request().url()); + m_pendingEntries.remove(handle->firstRequest().url()); ASSERT(m_cacheBeingUpdated); @@ -546,15 +655,22 @@ void ApplicationCacheGroup::didFinishLoading(ResourceHandle* handle) startLoadingEntry(); } -void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError&) +void ApplicationCacheGroup::didFail(ResourceHandle* handle, const ResourceError& error) { +#if ENABLE(INSPECTOR) + if (Page* page = m_frame->page()) + page->inspectorController()->didFailLoading(m_currentResourceIdentifier, error); +#else + UNUSED_PARAM(error); +#endif + if (handle == m_manifestHandle) { cacheUpdateFailed(); return; } - unsigned type = m_currentResource ? m_currentResource->type() : m_pendingEntries.get(handle->request().url()); - KURL url(handle->request().url()); + unsigned type = m_currentResource ? m_currentResource->type() : m_pendingEntries.get(handle->firstRequest().url()); + KURL url(handle->firstRequest().url()); if (url.hasFragmentIdentifier()) url.removeFragmentIdentifier(); @@ -590,13 +706,12 @@ void ApplicationCacheGroup::didReceiveManifestResponse(const ResourceResponse& r if (response.httpStatusCode() == 304) return; - if (response.httpStatusCode() / 100 != 2 || response.url() != m_manifestHandle->request().url() || !equalIgnoringCase(response.mimeType(), "text/cache-manifest")) { + if (response.httpStatusCode() / 100 != 2 || response.url() != m_manifestHandle->firstRequest().url() || !equalIgnoringCase(response.mimeType(), "text/cache-manifest")) { cacheUpdateFailed(); return; } - m_manifestResource = ApplicationCacheResource::create(m_manifestHandle->request().url(), response, - ApplicationCacheResource::Manifest); + m_manifestResource = ApplicationCacheResource::create(m_manifestHandle->firstRequest().url(), response, ApplicationCacheResource::Manifest); } void ApplicationCacheGroup::didReceiveManifestData(const char* data, int length) @@ -648,7 +763,7 @@ void ApplicationCacheGroup::didFinishLoadingManifest() associateDocumentLoaderWithCache(*iter, m_cacheBeingUpdated.get()); // We have the manifest, now download the resources. - m_updateStatus = Downloading; + setUpdateStatus(Downloading); postListenerTask(ApplicationCacheHost::DOWNLOADING_EVENT, m_associatedDocumentLoaders); @@ -674,7 +789,10 @@ void ApplicationCacheGroup::didFinishLoadingManifest() m_cacheBeingUpdated->setOnlineWhitelist(manifest.onlineWhitelistedURLs); m_cacheBeingUpdated->setFallbackURLs(manifest.fallbackURLs); m_cacheBeingUpdated->setAllowsAllNetworkRequests(manifest.allowAllNetworkRequests); - + + m_progressTotal = m_pendingEntries.size(); + m_progressDone = 0; + startLoadingEntry(); } @@ -687,6 +805,13 @@ void ApplicationCacheGroup::didReachMaxAppCacheSize() checkIfLoadIsComplete(); } +void ApplicationCacheGroup::didReachOriginQuota(PassRefPtr<Frame> frame) +{ + // Inform the client the origin quota has been reached, + // they may decide to increase the quota. + frame->page()->chrome()->client()->reachedApplicationCacheOriginQuota(m_origin.get()); +} + void ApplicationCacheGroup::cacheUpdateFailed() { stopLoading(); @@ -696,6 +821,16 @@ void ApplicationCacheGroup::cacheUpdateFailed() m_completionType = Failure; deliverDelayedMainResources(); } + +void ApplicationCacheGroup::cacheUpdateFailedDueToOriginQuota() +{ + if (!m_originQuotaReached) { + m_originQuotaReached = true; + scheduleReachedOriginQuotaCallback(); + } + + cacheUpdateFailed(); +} void ApplicationCacheGroup::manifestNotFound() { @@ -719,7 +854,7 @@ void ApplicationCacheGroup::manifestNotFound() } m_downloadingPendingMasterResourceLoadersCount = 0; - m_updateStatus = Idle; + setUpdateStatus(Idle); m_frame = 0; if (m_caches.isEmpty()) { @@ -775,17 +910,32 @@ void ApplicationCacheGroup::checkIfLoadIsComplete() ASSERT(cacheStorage().isMaximumSizeReached() && m_calledReachedMaxAppCacheSize); } + ApplicationCacheStorage::FailureReason failureReason; RefPtr<ApplicationCache> oldNewestCache = (m_newestCache == m_cacheBeingUpdated) ? 0 : m_newestCache; - setNewestCache(m_cacheBeingUpdated.release()); - if (cacheStorage().storeNewestCache(this)) { + if (cacheStorage().storeNewestCache(this, oldNewestCache.get(), failureReason)) { // New cache stored, now remove the old cache. if (oldNewestCache) cacheStorage().remove(oldNewestCache.get()); - // Fire the success events. + + // Fire the final progress event. + ASSERT(m_progressDone == m_progressTotal); + postListenerTask(ApplicationCacheHost::PROGRESS_EVENT, m_progressTotal, m_progressDone, m_associatedDocumentLoaders); + + // Fire the success event. postListenerTask(isUpgradeAttempt ? ApplicationCacheHost::UPDATEREADY_EVENT : ApplicationCacheHost::CACHED_EVENT, m_associatedDocumentLoaders); + // It is clear that the origin quota was not reached, so clear the flag if it was set. + m_originQuotaReached = false; } else { - if (cacheStorage().isMaximumSizeReached() && !m_calledReachedMaxAppCacheSize) { + if (failureReason == ApplicationCacheStorage::OriginQuotaReached) { + // We ran out of space for this origin. Roll back to previous state. + if (oldNewestCache) + setNewestCache(oldNewestCache.release()); + cacheUpdateFailedDueToOriginQuota(); + return; + } + + if (failureReason == ApplicationCacheStorage::TotalQuotaReached && !m_calledReachedMaxAppCacheSize) { // We ran out of space. All the changes in the cache storage have // been rolled back. We roll back to the previous state in here, // as well, call the chrome client asynchronously and retry to @@ -799,30 +949,30 @@ void ApplicationCacheGroup::checkIfLoadIsComplete() } scheduleReachedMaxAppCacheSizeCallback(); return; + } + + // Run the "cache failure steps" + // Fire the error events to all pending master entries, as well any other cache hosts + // currently associated with a cache in this group. + postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_associatedDocumentLoaders); + // Disassociate the pending master entries from the failed new cache. Note that + // all other loaders in the m_associatedDocumentLoaders are still associated with + // some other cache in this group. They are not associated with the failed new cache. + + // Need to copy loaders, because the cache group may be destroyed at the end of iteration. + Vector<DocumentLoader*> loaders; + copyToVector(m_pendingMasterResourceLoaders, loaders); + size_t count = loaders.size(); + for (size_t i = 0; i != count; ++i) + disassociateDocumentLoader(loaders[i]); // This can delete this group. + + // Reinstate the oldNewestCache, if there was one. + if (oldNewestCache) { + // This will discard the failed new cache. + setNewestCache(oldNewestCache.release()); } else { - // Run the "cache failure steps" - // Fire the error events to all pending master entries, as well any other cache hosts - // currently associated with a cache in this group. - postListenerTask(ApplicationCacheHost::ERROR_EVENT, m_associatedDocumentLoaders); - // Disassociate the pending master entries from the failed new cache. Note that - // all other loaders in the m_associatedDocumentLoaders are still associated with - // some other cache in this group. They are not associated with the failed new cache. - - // Need to copy loaders, because the cache group may be destroyed at the end of iteration. - Vector<DocumentLoader*> loaders; - copyToVector(m_pendingMasterResourceLoaders, loaders); - size_t count = loaders.size(); - for (size_t i = 0; i != count; ++i) - disassociateDocumentLoader(loaders[i]); // This can delete this group. - - // Reinstate the oldNewestCache, if there was one. - if (oldNewestCache) { - // This will discard the failed new cache. - setNewestCache(oldNewestCache.release()); - } else { - // We must have been deleted by the last call to disassociateDocumentLoader(). - return; - } + // We must have been deleted by the last call to disassociateDocumentLoader(). + return; } } break; @@ -832,8 +982,10 @@ void ApplicationCacheGroup::checkIfLoadIsComplete() // Empty cache group's list of pending master entries. m_pendingMasterResourceLoaders.clear(); m_completionType = None; - m_updateStatus = Idle; + setUpdateStatus(Idle); m_frame = 0; + m_loadedSize = 0; + m_availableSpaceInQuota = ApplicationCacheStorage::unknownQuota(); m_calledReachedMaxAppCacheSize = false; } @@ -849,7 +1001,8 @@ void ApplicationCacheGroup::startLoadingEntry() EntryMap::const_iterator it = m_pendingEntries.begin(); - postListenerTask(ApplicationCacheHost::PROGRESS_EVENT, m_associatedDocumentLoaders); + postListenerTask(ApplicationCacheHost::PROGRESS_EVENT, m_progressTotal, m_progressDone, m_associatedDocumentLoaders); + m_progressDone++; ASSERT(!m_currentHandle); @@ -946,11 +1099,17 @@ void ApplicationCacheGroup::scheduleReachedMaxAppCacheSizeCallback() // The timer will delete itself once it fires. } +void ApplicationCacheGroup::scheduleReachedOriginQuotaCallback() +{ + // FIXME: it might be nice to run this asynchronously, because there is no return value to wait for. + didReachOriginQuota(m_frame); +} + class CallCacheListenerTask : public ScriptExecutionContext::Task { public: - static PassOwnPtr<CallCacheListenerTask> create(PassRefPtr<DocumentLoader> loader, ApplicationCacheHost::EventID eventID) + static PassOwnPtr<CallCacheListenerTask> create(PassRefPtr<DocumentLoader> loader, ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone) { - return new CallCacheListenerTask(loader, eventID); + return adoptPtr(new CallCacheListenerTask(loader, eventID, progressTotal, progressDone)); } virtual void performTask(ScriptExecutionContext* context) @@ -963,28 +1122,32 @@ public: ASSERT(frame->loader()->documentLoader() == m_documentLoader.get()); - m_documentLoader->applicationCacheHost()->notifyDOMApplicationCache(m_eventID); + m_documentLoader->applicationCacheHost()->notifyDOMApplicationCache(m_eventID, m_progressTotal, m_progressDone); } private: - CallCacheListenerTask(PassRefPtr<DocumentLoader> loader, ApplicationCacheHost::EventID eventID) + CallCacheListenerTask(PassRefPtr<DocumentLoader> loader, ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone) : m_documentLoader(loader) , m_eventID(eventID) + , m_progressTotal(progressTotal) + , m_progressDone(progressDone) { } RefPtr<DocumentLoader> m_documentLoader; ApplicationCacheHost::EventID m_eventID; + int m_progressTotal; + int m_progressDone; }; -void ApplicationCacheGroup::postListenerTask(ApplicationCacheHost::EventID eventID, const HashSet<DocumentLoader*>& loaderSet) +void ApplicationCacheGroup::postListenerTask(ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone, const HashSet<DocumentLoader*>& loaderSet) { HashSet<DocumentLoader*>::const_iterator loaderSetEnd = loaderSet.end(); for (HashSet<DocumentLoader*>::const_iterator iter = loaderSet.begin(); iter != loaderSetEnd; ++iter) - postListenerTask(eventID, *iter); + postListenerTask(eventID, progressTotal, progressDone, *iter); } -void ApplicationCacheGroup::postListenerTask(ApplicationCacheHost::EventID eventID, DocumentLoader* loader) +void ApplicationCacheGroup::postListenerTask(ApplicationCacheHost::EventID eventID, int progressTotal, int progressDone, DocumentLoader* loader) { Frame* frame = loader->frame(); if (!frame) @@ -992,7 +1155,15 @@ void ApplicationCacheGroup::postListenerTask(ApplicationCacheHost::EventID event ASSERT(frame->loader()->documentLoader() == loader); - frame->document()->postTask(CallCacheListenerTask::create(loader, eventID)); + frame->document()->postTask(CallCacheListenerTask::create(loader, eventID, progressTotal, progressDone)); +} + +void ApplicationCacheGroup::setUpdateStatus(UpdateStatus status) +{ + m_updateStatus = status; +#if ENABLE(INSPECTOR) + inspectorUpdateApplicationCacheStatus(m_frame); +#endif } void ApplicationCacheGroup::clearStorageID() diff --git a/WebCore/loader/appcache/ApplicationCacheGroup.h b/WebCore/loader/appcache/ApplicationCacheGroup.h index 8df52cc..29d0749 100644 --- a/WebCore/loader/appcache/ApplicationCacheGroup.h +++ b/WebCore/loader/appcache/ApplicationCacheGroup.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,10 +28,6 @@ #if ENABLE(OFFLINE_WEB_APPLICATIONS) -#include <wtf/Noncopyable.h> -#include <wtf/HashMap.h> -#include <wtf/HashSet.h> - #include "DOMApplicationCache.h" #include "KURL.h" #include "PlatformString.h" @@ -39,6 +35,10 @@ #include "ResourceHandleClient.h" #include "SharedBuffer.h" +#include <wtf/Noncopyable.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> + namespace WebCore { class ApplicationCache; @@ -46,6 +46,7 @@ class ApplicationCacheResource; class Document; class DocumentLoader; class Frame; +class SecurityOrigin; enum ApplicationCacheUpdateOption { ApplicationCacheUpdateWithBrowsingContext, @@ -66,17 +67,21 @@ public: static void selectCacheWithoutManifestURL(Frame*); const KURL& manifestURL() const { return m_manifestURL; } + const SecurityOrigin* origin() const { return m_origin.get(); } UpdateStatus updateStatus() const { return m_updateStatus; } + void setUpdateStatus(UpdateStatus status); void setStorageID(unsigned storageID) { m_storageID = storageID; } unsigned storageID() const { return m_storageID; } void clearStorageID(); - void update(Frame*, ApplicationCacheUpdateOption); // FIXME: Frame should not bee needed when updating witout browsing context. + void update(Frame*, ApplicationCacheUpdateOption); // FIXME: Frame should not be needed when updating without browsing context. void cacheDestroyed(ApplicationCache*); bool cacheIsBeingUpdated(const ApplicationCache* cache) const { return cache == m_cacheBeingUpdated; } + void stopLoadingInFrame(Frame*); + ApplicationCache* newestCache() const { return m_newestCache.get(); } void setNewestCache(PassRefPtr<ApplicationCache>); @@ -91,9 +96,13 @@ public: bool isCopy() const { return m_isCopy; } private: - static void postListenerTask(ApplicationCacheHost::EventID, const HashSet<DocumentLoader*>&); - static void postListenerTask(ApplicationCacheHost::EventID, DocumentLoader*); + static void postListenerTask(ApplicationCacheHost::EventID id, const HashSet<DocumentLoader*>& set) { postListenerTask(id, 0, 0, set); } + static void postListenerTask(ApplicationCacheHost::EventID id, DocumentLoader* loader) { postListenerTask(id, 0, 0, loader); } + static void postListenerTask(ApplicationCacheHost::EventID, int progressTotal, int progressDone, const HashSet<DocumentLoader*>&); + static void postListenerTask(ApplicationCacheHost::EventID, int progressTotal, int progressDone, DocumentLoader*); + void scheduleReachedMaxAppCacheSizeCallback(); + void scheduleReachedOriginQuotaCallback(); PassRefPtr<ResourceHandle> createResourceHandle(const KURL&, ApplicationCacheResource* newestCachedResource); @@ -101,20 +110,25 @@ private: // the existing client callback cannot be used, so assume that any client that enables application cache also wants it to use credential storage. virtual bool shouldUseCredentialStorage(ResourceHandle*) { return true; } +#if ENABLE(INSPECTOR) + virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse&); +#endif virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); - virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); - virtual void didFinishLoading(ResourceHandle*); + virtual void didReceiveData(ResourceHandle*, const char*, int length, int lengthReceived); + virtual void didFinishLoading(ResourceHandle*, double finishTime); virtual void didFail(ResourceHandle*, const ResourceError&); void didReceiveManifestResponse(const ResourceResponse&); void didReceiveManifestData(const char*, int); void didFinishLoadingManifest(); void didReachMaxAppCacheSize(); + void didReachOriginQuota(PassRefPtr<Frame> frame); void startLoadingEntry(); void deliverDelayedMainResources(); void checkIfLoadIsComplete(); void cacheUpdateFailed(); + void cacheUpdateFailedDueToOriginQuota(); void manifestNotFound(); void addEntry(const String&, unsigned type); @@ -124,6 +138,7 @@ private: void stopLoading(); KURL m_manifestURL; + RefPtr<SecurityOrigin> m_origin; UpdateStatus m_updateStatus; // This is the newest complete cache in the group. @@ -147,6 +162,10 @@ private: // The URLs and types of pending cache entries. typedef HashMap<String, unsigned> EntryMap; EntryMap m_pendingEntries; + + // The total number of items to be processed to update the cache group and the number that have been done. + int m_progressTotal; + int m_progressDone; // Frame used for fetching resources when updating. // FIXME: An update started by a particular frame should not stop if it is destroyed, but there are other frames associated with the same cache group. @@ -175,11 +194,20 @@ private: RefPtr<ResourceHandle> m_currentHandle; RefPtr<ApplicationCacheResource> m_currentResource; - + +#if ENABLE(INSPECTOR) + unsigned long m_currentResourceIdentifier; +#endif + RefPtr<ApplicationCacheResource> m_manifestResource; RefPtr<ResourceHandle> m_manifestHandle; + int64_t m_loadedSize; + int64_t m_availableSpaceInQuota; + bool m_originQuotaReached; + friend class ChromeClientCallbackTimer; + friend class OriginQuotaReachedCallbackTimer; }; } // namespace WebCore diff --git a/WebCore/loader/appcache/ApplicationCacheHost.cpp b/WebCore/loader/appcache/ApplicationCacheHost.cpp index fc98746..d5707cf 100644 --- a/WebCore/loader/appcache/ApplicationCacheHost.cpp +++ b/WebCore/loader/appcache/ApplicationCacheHost.cpp @@ -37,6 +37,7 @@ #include "FrameLoader.h" #include "FrameLoaderClient.h" #include "MainResourceLoader.h" +#include "ProgressEvent.h" #include "ResourceLoader.h" #include "ResourceRequest.h" #include "Settings.h" @@ -54,6 +55,8 @@ ApplicationCacheHost::ApplicationCacheHost(DocumentLoader* documentLoader) ApplicationCacheHost::~ApplicationCacheHost() { + ASSERT(!m_applicationCache || !m_candidateApplicationCacheGroup || m_applicationCache->group() == m_candidateApplicationCacheGroup); + if (m_applicationCache) m_applicationCache->group()->disassociateDocumentLoader(m_documentLoader); else if (m_candidateApplicationCacheGroup) @@ -88,6 +91,12 @@ void ApplicationCacheHost::maybeLoadMainResource(ResourceRequest& request, Subst } } +void ApplicationCacheHost::maybeLoadMainResourceForRedirect(ResourceRequest& request, SubstituteData& substituteData) +{ + ASSERT(status() == UNCACHED); + maybeLoadMainResource(request, substituteData); +} + bool ApplicationCacheHost::maybeLoadFallbackForMainResponse(const ResourceRequest& request, const ResourceResponse& r) { if (r.httpStatusCode() / 100 == 4 || r.httpStatusCode() / 100 == 5) { @@ -125,7 +134,10 @@ void ApplicationCacheHost::failedLoadingMainResource() { ApplicationCacheGroup* group = m_candidateApplicationCacheGroup; if (!group && m_applicationCache) { - ASSERT(!mainResourceApplicationCache()); // If the main resource were loaded from a cache, it wouldn't fail. + if (mainResourceApplicationCache()) { + // Even when the main resource is being loaded from an application cache, loading can fail if aborted. + return; + } group = m_applicationCache->group(); } @@ -228,35 +240,83 @@ void ApplicationCacheHost::setDOMApplicationCache(DOMApplicationCache* domApplic m_domApplicationCache = domApplicationCache; } -void ApplicationCacheHost::notifyDOMApplicationCache(EventID id) +void ApplicationCacheHost::notifyDOMApplicationCache(EventID id, int total, int done) { if (m_defersEvents) { - // Events are deferred until document.onload has fired. - m_deferredEvents.append(id); + // Event dispatching is deferred until document.onload has fired. + m_deferredEvents.append(DeferredEvent(id, total, done)); return; } - if (m_domApplicationCache) { - ExceptionCode ec = 0; - m_domApplicationCache->dispatchEvent(Event::create(DOMApplicationCache::toEventType(id), false, false), ec); - ASSERT(!ec); - } + dispatchDOMEvent(id, total, done); +} + +void ApplicationCacheHost::stopLoadingInFrame(Frame* frame) +{ + ASSERT(!m_applicationCache || !m_candidateApplicationCacheGroup || m_applicationCache->group() == m_candidateApplicationCacheGroup); + + if (m_candidateApplicationCacheGroup) + m_candidateApplicationCacheGroup->stopLoadingInFrame(frame); + else if (m_applicationCache) + m_applicationCache->group()->stopLoadingInFrame(frame); } void ApplicationCacheHost::stopDeferringEvents() { RefPtr<DocumentLoader> protect(documentLoader()); for (unsigned i = 0; i < m_deferredEvents.size(); ++i) { - EventID id = m_deferredEvents[i]; - if (m_domApplicationCache) { - ExceptionCode ec = 0; - m_domApplicationCache->dispatchEvent(Event::create(DOMApplicationCache::toEventType(id), false, false), ec); - ASSERT(!ec); - } + const DeferredEvent& deferred = m_deferredEvents[i]; + dispatchDOMEvent(deferred.eventID, deferred.progressTotal, deferred.progressDone); } m_deferredEvents.clear(); m_defersEvents = false; } +#if ENABLE(INSPECTOR) +void ApplicationCacheHost::fillResourceList(ResourceInfoList* resources) +{ + ApplicationCache* cache = applicationCache(); + if (!cache || !cache->isComplete()) + return; + + ApplicationCache::ResourceMap::const_iterator end = cache->end(); + for (ApplicationCache::ResourceMap::const_iterator it = cache->begin(); it != end; ++it) { + RefPtr<ApplicationCacheResource> resource = it->second; + unsigned type = resource->type(); + bool isMaster = type & ApplicationCacheResource::Master; + bool isManifest = type & ApplicationCacheResource::Manifest; + bool isExplicit = type & ApplicationCacheResource::Explicit; + bool isForeign = type & ApplicationCacheResource::Foreign; + bool isFallback = type & ApplicationCacheResource::Fallback; + resources->append(ResourceInfo(resource->url(), isMaster, isManifest, isFallback, isForeign, isExplicit, resource->estimatedSizeInStorage())); + } +} + +ApplicationCacheHost::CacheInfo ApplicationCacheHost::applicationCacheInfo() +{ + ApplicationCache* cache = applicationCache(); + if (!cache || !cache->isComplete()) + return CacheInfo(KURL(), 0, 0, 0); + + // FIXME: Add "Creation Time" and "Update Time" to Application Caches. + return CacheInfo(cache->manifestResource()->url(), 0, 0, cache->estimatedSizeInStorage()); +} +#endif + +void ApplicationCacheHost::dispatchDOMEvent(EventID id, int total, int done) +{ + if (m_domApplicationCache) { + const AtomicString& eventType = DOMApplicationCache::toEventType(id); + ExceptionCode ec = 0; + RefPtr<Event> event; + if (id == PROGRESS_EVENT) + event = ProgressEvent::create(eventType, true, done, total); + else + event = Event::create(eventType, false, false); + m_domApplicationCache->dispatchEvent(event, ec); + ASSERT(!ec); + } +} + void ApplicationCacheHost::setCandidateApplicationCacheGroup(ApplicationCacheGroup* group) { ASSERT(!m_applicationCache); diff --git a/WebCore/loader/appcache/ApplicationCacheHost.h b/WebCore/loader/appcache/ApplicationCacheHost.h index 52d4d40..8ac5357 100644 --- a/WebCore/loader/appcache/ApplicationCacheHost.h +++ b/WebCore/loader/appcache/ApplicationCacheHost.h @@ -33,6 +33,7 @@ #if ENABLE(OFFLINE_WEB_APPLICATIONS) +#include "KURL.h" #include <wtf/Deque.h> #include <wtf/OwnPtr.h> #include <wtf/PassRefPtr.h> @@ -40,10 +41,9 @@ #include <wtf/Vector.h> namespace WebCore { - class DOMApplicationCache; class DocumentLoader; - class KURL; + class Frame; class ResourceLoader; class ResourceError; class ResourceRequest; @@ -81,6 +81,40 @@ namespace WebCore { OBSOLETE_EVENT // Must remain the last value, this is used to size arrays. }; +#if ENABLE(INSPECTOR) + struct CacheInfo { + CacheInfo(const KURL& manifest, double creationTime, double updateTime, long long size) + : m_manifest(manifest) + , m_creationTime(creationTime) + , m_updateTime(updateTime) + , m_size(size) { } + KURL m_manifest; + double m_creationTime; + double m_updateTime; + long long m_size; + }; + + struct ResourceInfo { + ResourceInfo(const KURL& resource, bool isMaster, bool isManifest, bool isFallback, bool isForeign, bool isExplicit, long long size) + : m_resource(resource) + , m_isMaster(isMaster) + , m_isManifest(isManifest) + , m_isFallback(isFallback) + , m_isForeign(isForeign) + , m_isExplicit(isExplicit) + , m_size(size) { } + KURL m_resource; + bool m_isMaster; + bool m_isManifest; + bool m_isFallback; + bool m_isForeign; + bool m_isExplicit; + long long m_size; + }; + + typedef Vector<ResourceInfo> ResourceInfoList; +#endif + ApplicationCacheHost(DocumentLoader*); ~ApplicationCacheHost(); @@ -88,6 +122,7 @@ namespace WebCore { void selectCacheWithManifest(const KURL& manifestURL); void maybeLoadMainResource(ResourceRequest&, SubstituteData&); + void maybeLoadMainResourceForRedirect(ResourceRequest&, SubstituteData&); bool maybeLoadFallbackForMainResponse(const ResourceRequest&, const ResourceResponse&); bool maybeLoadFallbackForMainError(const ResourceRequest&, const ResourceError&); void mainResourceDataReceived(const char* data, int length, long long lengthReceived, bool allAtOnce); @@ -108,19 +143,35 @@ namespace WebCore { bool update(); bool swapCache(); - void setDOMApplicationCache(DOMApplicationCache* domApplicationCache); - void notifyDOMApplicationCache(EventID id); + void setDOMApplicationCache(DOMApplicationCache*); + void notifyDOMApplicationCache(EventID, int progressTotal, int progressDone); + + void stopLoadingInFrame(Frame*); void stopDeferringEvents(); // Also raises the events that have been queued up. +#if ENABLE(INSPECTOR) + void fillResourceList(ResourceInfoList*); + CacheInfo applicationCacheInfo(); +#endif + private: bool isApplicationCacheEnabled(); - DocumentLoader* documentLoader() { return m_documentLoader; } + DocumentLoader* documentLoader() const { return m_documentLoader; } + + struct DeferredEvent { + EventID eventID; + int progressTotal; + int progressDone; + DeferredEvent(EventID id, int total, int done) : eventID(id), progressTotal(total), progressDone(done) { } + }; DOMApplicationCache* m_domApplicationCache; DocumentLoader* m_documentLoader; bool m_defersEvents; // Events are deferred until after document onload. - Vector<EventID> m_deferredEvents; + Vector<DeferredEvent> m_deferredEvents; + + void dispatchDOMEvent(EventID, int progressTotal, int progressDone); #if PLATFORM(CHROMIUM) friend class ApplicationCacheHostInternal; diff --git a/WebCore/loader/appcache/ApplicationCacheStorage.cpp b/WebCore/loader/appcache/ApplicationCacheStorage.cpp index 1e97d78..7b20775 100644 --- a/WebCore/loader/appcache/ApplicationCacheStorage.cpp +++ b/WebCore/loader/appcache/ApplicationCacheStorage.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,14 +29,15 @@ #if ENABLE(OFFLINE_WEB_APPLICATIONS) #include "ApplicationCache.h" -#include "ApplicationCacheHost.h" #include "ApplicationCacheGroup.h" +#include "ApplicationCacheHost.h" #include "ApplicationCacheResource.h" -#include "CString.h" #include "FileSystem.h" #include "KURL.h" #include "SQLiteStatement.h" #include "SQLiteTransaction.h" +#include "SecurityOrigin.h" +#include <wtf/text/CString.h> #include <wtf/StdLibExtras.h> #include <wtf/StringExtras.h> @@ -171,8 +172,7 @@ void ApplicationCacheStorage::loadManifestHostHashes() if (statement.prepare() != SQLResultOk) return; - int result; - while ((result = statement.step()) == SQLResultRow) + while (statement.step() == SQLResultRow) m_cacheHostSet.add(static_cast<unsigned>(statement.getColumnInt64(0))); } @@ -415,6 +415,126 @@ int64_t ApplicationCacheStorage::spaceNeeded(int64_t cacheToSave) return spaceNeeded; } +void ApplicationCacheStorage::setDefaultOriginQuota(int64_t quota) +{ + m_defaultOriginQuota = quota; +} + +bool ApplicationCacheStorage::quotaForOrigin(const SecurityOrigin* origin, int64_t& quota) +{ + // If an Origin record doesn't exist, then the COUNT will be 0 and quota will be 0. + // Using the count to determine if a record existed or not is a safe way to determine + // if a quota of 0 is real, from the record, or from null. + SQLiteStatement statement(m_database, "SELECT COUNT(quota), quota FROM Origins WHERE origin=?"); + if (statement.prepare() != SQLResultOk) + return false; + + statement.bindText(1, origin->databaseIdentifier()); + int result = statement.step(); + + // Return the quota, or if it was null the default. + if (result == SQLResultRow) { + bool wasNoRecord = statement.getColumnInt64(0) == 0; + quota = wasNoRecord ? m_defaultOriginQuota : statement.getColumnInt64(1); + return true; + } + + LOG_ERROR("Could not get the quota of an origin, error \"%s\"", m_database.lastErrorMsg()); + return false; +} + +bool ApplicationCacheStorage::usageForOrigin(const SecurityOrigin* origin, int64_t& usage) +{ + // If an Origins record doesn't exist, then the SUM will be null, + // which will become 0, as expected, when converting to a number. + SQLiteStatement statement(m_database, "SELECT SUM(Caches.size)" + " FROM CacheGroups" + " INNER JOIN Origins ON CacheGroups.origin = Origins.origin" + " INNER JOIN Caches ON CacheGroups.id = Caches.cacheGroup" + " WHERE Origins.origin=?"); + if (statement.prepare() != SQLResultOk) + return false; + + statement.bindText(1, origin->databaseIdentifier()); + int result = statement.step(); + + if (result == SQLResultRow) { + usage = statement.getColumnInt64(0); + return true; + } + + LOG_ERROR("Could not get the quota of an origin, error \"%s\"", m_database.lastErrorMsg()); + return false; +} + +bool ApplicationCacheStorage::remainingSizeForOriginExcludingCache(const SecurityOrigin* origin, ApplicationCache* cache, int64_t& remainingSize) +{ + openDatabase(false); + if (!m_database.isOpen()) + return false; + + // Remaining size = total origin quota - size of all caches with origin excluding the provided cache. + // Keep track of the number of caches so we can tell if the result was a calculation or not. + const char* query; + int64_t excludingCacheIdentifier = cache ? cache->storageID() : 0; + if (excludingCacheIdentifier != 0) { + query = "SELECT COUNT(Caches.size), Origins.quota - SUM(Caches.size)" + " FROM CacheGroups" + " INNER JOIN Origins ON CacheGroups.origin = Origins.origin" + " INNER JOIN Caches ON CacheGroups.id = Caches.cacheGroup" + " WHERE Origins.origin=?" + " AND Caches.id!=?"; + } else { + query = "SELECT COUNT(Caches.size), Origins.quota - SUM(Caches.size)" + " FROM CacheGroups" + " INNER JOIN Origins ON CacheGroups.origin = Origins.origin" + " INNER JOIN Caches ON CacheGroups.id = Caches.cacheGroup" + " WHERE Origins.origin=?"; + } + + SQLiteStatement statement(m_database, query); + if (statement.prepare() != SQLResultOk) + return false; + + statement.bindText(1, origin->databaseIdentifier()); + if (excludingCacheIdentifier != 0) + statement.bindInt64(2, excludingCacheIdentifier); + int result = statement.step(); + + // If the count was 0 that then we have to query the origin table directly + // for its quota. Otherwise we can use the calculated value. + if (result == SQLResultRow) { + int64_t numberOfCaches = statement.getColumnInt64(0); + if (numberOfCaches == 0) + quotaForOrigin(origin, remainingSize); + else + remainingSize = statement.getColumnInt64(1); + return true; + } + + LOG_ERROR("Could not get the remaining size of an origin's quota, error \"%s\"", m_database.lastErrorMsg()); + return false; +} + +bool ApplicationCacheStorage::storeUpdatedQuotaForOrigin(const SecurityOrigin* origin, int64_t quota) +{ + openDatabase(true); + if (!m_database.isOpen()) + return false; + + if (!ensureOriginRecord(origin)) + return false; + + SQLiteStatement updateStatement(m_database, "UPDATE Origins SET quota=? WHERE origin=?"); + if (updateStatement.prepare() != SQLResultOk) + return false; + + updateStatement.bindInt64(1, quota); + updateStatement.bindText(2, origin->databaseIdentifier()); + + return executeStatement(updateStatement); +} + bool ApplicationCacheStorage::executeSQLCommand(const String& sql) { ASSERT(m_database.isOpen()); @@ -427,7 +547,10 @@ bool ApplicationCacheStorage::executeSQLCommand(const String& sql) return result; } -static const int schemaVersion = 5; +// Update the schemaVersion when the schema of any the Application Cache +// SQLite tables changes. This allows the database to be rebuilt when +// a new, incompatible change has been introduced to the database schema. +static const int schemaVersion = 6; void ApplicationCacheStorage::verifySchemaVersion() { @@ -476,7 +599,7 @@ void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist) // Create tables executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheGroups (id INTEGER PRIMARY KEY AUTOINCREMENT, " - "manifestHostHash INTEGER NOT NULL ON CONFLICT FAIL, manifestURL TEXT UNIQUE ON CONFLICT FAIL, newestCache INTEGER)"); + "manifestHostHash INTEGER NOT NULL ON CONFLICT FAIL, manifestURL TEXT UNIQUE ON CONFLICT FAIL, newestCache INTEGER, origin TEXT)"); executeSQLCommand("CREATE TABLE IF NOT EXISTS Caches (id INTEGER PRIMARY KEY AUTOINCREMENT, cacheGroup INTEGER, size INTEGER)"); executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheWhitelistURLs (url TEXT NOT NULL ON CONFLICT FAIL, cache INTEGER NOT NULL ON CONFLICT FAIL)"); executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheAllowsAllNetworkRequests (wildcard INTEGER NOT NULL ON CONFLICT FAIL, cache INTEGER NOT NULL ON CONFLICT FAIL)"); @@ -486,6 +609,7 @@ void ApplicationCacheStorage::openDatabase(bool createIfDoesNotExist) executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResources (id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT NOT NULL ON CONFLICT FAIL, " "statusCode INTEGER NOT NULL, responseURL TEXT NOT NULL, mimeType TEXT, textEncodingName TEXT, headers TEXT, data INTEGER NOT NULL ON CONFLICT FAIL)"); executeSQLCommand("CREATE TABLE IF NOT EXISTS CacheResourceData (id INTEGER PRIMARY KEY AUTOINCREMENT, data BLOB)"); + executeSQLCommand("CREATE TABLE IF NOT EXISTS Origins (origin TEXT UNIQUE ON CONFLICT IGNORE, quota INTEGER NOT NULL ON CONFLICT FAIL)"); // When a cache is deleted, all its entries and its whitelist should be deleted. executeSQLCommand("CREATE TRIGGER IF NOT EXISTS CacheDeleted AFTER DELETE ON Caches" @@ -524,17 +648,23 @@ bool ApplicationCacheStorage::store(ApplicationCacheGroup* group, GroupStorageID ASSERT(group->storageID() == 0); ASSERT(journal); - SQLiteStatement statement(m_database, "INSERT INTO CacheGroups (manifestHostHash, manifestURL) VALUES (?, ?)"); + SQLiteStatement statement(m_database, "INSERT INTO CacheGroups (manifestHostHash, manifestURL, origin) VALUES (?, ?, ?)"); if (statement.prepare() != SQLResultOk) return false; statement.bindInt64(1, urlHostHash(group->manifestURL())); statement.bindText(2, group->manifestURL()); + statement.bindText(3, group->origin()->databaseIdentifier()); if (!executeStatement(statement)) return false; - group->setStorageID(static_cast<unsigned>(m_database.lastInsertRowID())); + unsigned groupStorageID = static_cast<unsigned>(m_database.lastInsertRowID()); + + if (!ensureOriginRecord(group->origin())) + return false; + + group->setStorageID(groupStorageID); journal->add(group, 0); return true; } @@ -626,7 +756,11 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, unsigned ASSERT(!resource->storageID()); openDatabase(true); - + + // openDatabase(true) could still fail, for example when cacheStorage is full or no longer available. + if (!m_database.isOpen()) + return false; + // First, insert the data SQLiteStatement dataStatement(m_database, "INSERT INTO CacheResourceData (data) VALUES (?)"); if (dataStatement.prepare() != SQLResultOk) @@ -712,6 +846,9 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, Applicat ASSERT(cache->storageID()); openDatabase(true); + + if (!m_database.isOpen()) + return false; m_isMaximumSizeReached = false; m_database.setMaximumSize(m_maximumSize); @@ -739,10 +876,27 @@ bool ApplicationCacheStorage::store(ApplicationCacheResource* resource, Applicat return true; } -bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group) +bool ApplicationCacheStorage::ensureOriginRecord(const SecurityOrigin* origin) +{ + SQLiteStatement insertOriginStatement(m_database, "INSERT INTO Origins (origin, quota) VALUES (?, ?)"); + if (insertOriginStatement.prepare() != SQLResultOk) + return false; + + insertOriginStatement.bindText(1, origin->databaseIdentifier()); + insertOriginStatement.bindInt64(2, m_defaultOriginQuota); + if (!executeStatement(insertOriginStatement)) + return false; + + return true; +} + +bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group, ApplicationCache* oldCache, FailureReason& failureReason) { openDatabase(true); + if (!m_database.isOpen()) + return false; + m_isMaximumSizeReached = false; m_database.setMaximumSize(m_maximumSize); @@ -750,11 +904,21 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group) storeCacheTransaction.begin(); + // Check if this would reach the per-origin quota. + int64_t remainingSpaceInOrigin; + if (remainingSizeForOriginExcludingCache(group->origin(), oldCache, remainingSpaceInOrigin)) { + if (remainingSpaceInOrigin < group->newestCache()->estimatedSizeInStorage()) { + failureReason = OriginQuotaReached; + return false; + } + } + GroupStorageIDJournal groupStorageIDJournal; if (!group->storageID()) { // Store the group if (!store(group, &groupStorageIDJournal)) { checkForMaxSizeReached(); + failureReason = isMaximumSizeReached() ? TotalQuotaReached : DiskOrOperationFailure; return false; } } @@ -771,20 +935,25 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group) // Store the newest cache if (!store(group->newestCache(), &resourceStorageIDJournal)) { checkForMaxSizeReached(); + failureReason = isMaximumSizeReached() ? TotalQuotaReached : DiskOrOperationFailure; return false; } // Update the newest cache in the group. SQLiteStatement statement(m_database, "UPDATE CacheGroups SET newestCache=? WHERE id=?"); - if (statement.prepare() != SQLResultOk) + if (statement.prepare() != SQLResultOk) { + failureReason = DiskOrOperationFailure; return false; + } statement.bindInt64(1, group->newestCache()->storageID()); statement.bindInt64(2, group->storageID()); - if (!executeStatement(statement)) + if (!executeStatement(statement)) { + failureReason = DiskOrOperationFailure; return false; + } groupStorageIDJournal.commit(); resourceStorageIDJournal.commit(); @@ -792,10 +961,17 @@ bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group) return true; } +bool ApplicationCacheStorage::storeNewestCache(ApplicationCacheGroup* group) +{ + // Ignore the reason for failing, just attempt the store. + FailureReason ignoredFailureReason; + return storeNewestCache(group, 0, ignoredFailureReason); +} + static inline void parseHeader(const UChar* header, size_t headerLength, ResourceResponse& response) { - int pos = find(header, headerLength, ':'); - ASSERT(pos != -1); + size_t pos = find(header, headerLength, ':'); + ASSERT(pos != notFound); AtomicString headerName = AtomicString(header, pos); String headerValue = String(header + pos + 1, headerLength - pos - 1); @@ -805,9 +981,9 @@ static inline void parseHeader(const UChar* header, size_t headerLength, Resourc static inline void parseHeaders(const String& headers, ResourceResponse& response) { - int startPos = 0; - int endPos; - while ((endPos = headers.find('\n', startPos)) != -1) { + unsigned startPos = 0; + size_t endPos; + while ((endPos = headers.find('\n', startPos)) != notFound) { ASSERT(startPos != endPos); parseHeader(headers.characters() + startPos, endPos - startPos, response); @@ -815,7 +991,7 @@ static inline void parseHeaders(const String& headers, ResourceResponse& respons startPos = endPos + 1; } - if (startPos != static_cast<int>(headers.length())) + if (startPos != headers.length()) parseHeader(headers.characters(), headers.length(), response); } @@ -955,9 +1131,10 @@ void ApplicationCacheStorage::empty() if (!m_database.isOpen()) return; - // Clear cache groups, caches and cache resources. + // Clear cache groups, caches, cache resources, and origins. executeSQLCommand("DELETE FROM CacheGroups"); executeSQLCommand("DELETE FROM Caches"); + executeSQLCommand("DELETE FROM Origins"); // Clear the storage IDs for the caches in memory. // The caches will still work, but cached resources will not be saved to disk @@ -990,7 +1167,7 @@ bool ApplicationCacheStorage::storeCopyOfCache(const String& cacheDirectory, App } // Now create a new cache group. - OwnPtr<ApplicationCacheGroup> groupCopy(new ApplicationCacheGroup(cache->group()->manifestURL(), true)); + OwnPtr<ApplicationCacheGroup> groupCopy(adoptPtr(new ApplicationCacheGroup(cache->group()->manifestURL(), true))); groupCopy->setNewestCache(cacheCopy); @@ -1111,8 +1288,9 @@ void ApplicationCacheStorage::checkForMaxSizeReached() } ApplicationCacheStorage::ApplicationCacheStorage() - : m_maximumSize(INT_MAX) + : m_maximumSize(ApplicationCacheStorage::noQuota()) , m_isMaximumSizeReached(false) + , m_defaultOriginQuota(ApplicationCacheStorage::noQuota()) { } diff --git a/WebCore/loader/appcache/ApplicationCacheStorage.h b/WebCore/loader/appcache/ApplicationCacheStorage.h index aaa5c9c..7db34e6 100644 --- a/WebCore/loader/appcache/ApplicationCacheStorage.h +++ b/WebCore/loader/appcache/ApplicationCacheStorage.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2010 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,22 +30,28 @@ #include "PlatformString.h" #include "SQLiteDatabase.h" -#include "StringHash.h" - #include <wtf/HashCountedSet.h> +#include <wtf/text/StringHash.h> namespace WebCore { class ApplicationCache; -class ApplicationCacheHost; class ApplicationCacheGroup; +class ApplicationCacheHost; class ApplicationCacheResource; class KURL; template <class T> class StorageIDJournal; +class SecurityOrigin; class ApplicationCacheStorage : public Noncopyable { public: + enum FailureReason { + OriginQuotaReached, + TotalQuotaReached, + DiskOrOperationFailure + }; + void setCacheDirectory(const String&); const String& cacheDirectory() const; @@ -54,6 +60,13 @@ public: bool isMaximumSizeReached() const; int64_t spaceNeeded(int64_t cacheToSave); + int64_t defaultOriginQuota() const { return m_defaultOriginQuota; } + void setDefaultOriginQuota(int64_t quota); + bool usageForOrigin(const SecurityOrigin*, int64_t& usage); + bool quotaForOrigin(const SecurityOrigin*, int64_t& quota); + bool remainingSizeForOriginExcludingCache(const SecurityOrigin*, ApplicationCache*, int64_t& remainingSize); + bool storeUpdatedQuotaForOrigin(const SecurityOrigin*, int64_t quota); + ApplicationCacheGroup* cacheGroupForURL(const KURL&); // Cache to load a main resource from. ApplicationCacheGroup* fallbackCacheGroupForURL(const KURL&); // Cache that has a fallback entry to load a main resource from if normal loading fails. @@ -61,6 +74,7 @@ public: void cacheGroupDestroyed(ApplicationCacheGroup*); void cacheGroupMadeObsolete(ApplicationCacheGroup*); + bool storeNewestCache(ApplicationCacheGroup*, ApplicationCache* oldCache, FailureReason& failureReason); bool storeNewestCache(ApplicationCacheGroup*); // Updates the cache group, but doesn't remove old cache. bool store(ApplicationCacheResource*, ApplicationCache*); bool storeUpdatedType(ApplicationCacheResource*, ApplicationCache*); @@ -76,6 +90,9 @@ public: bool cacheGroupSize(const String& manifestURL, int64_t* size); bool deleteCacheGroup(const String& manifestURL); void vacuumDatabaseFile(); + + static int64_t unknownQuota() { return -1; } + static int64_t noQuota() { return std::numeric_limits<int64_t>::max(); } private: ApplicationCacheStorage(); PassRefPtr<ApplicationCache> loadCache(unsigned storageID); @@ -88,6 +105,8 @@ private: bool store(ApplicationCache*, ResourceStorageIDJournal*); bool store(ApplicationCacheResource*, unsigned cacheStorageID); + bool ensureOriginRecord(const SecurityOrigin*); + void loadManifestHostHashes(); void verifySchemaVersion(); @@ -105,6 +124,8 @@ private: int64_t m_maximumSize; bool m_isMaximumSizeReached; + int64_t m_defaultOriginQuota; + SQLiteDatabase m_database; // In order to quickly determine if a given resource exists in an application cache, diff --git a/WebCore/loader/appcache/DOMApplicationCache.h b/WebCore/loader/appcache/DOMApplicationCache.h index 077cae0..2a806fa 100644 --- a/WebCore/loader/appcache/DOMApplicationCache.h +++ b/WebCore/loader/appcache/DOMApplicationCache.h @@ -29,27 +29,27 @@ #if ENABLE(OFFLINE_WEB_APPLICATIONS) #include "ApplicationCacheHost.h" -#include "AtomicStringHash.h" #include "EventListener.h" #include "EventNames.h" #include "EventTarget.h" +#include <wtf/Forward.h> #include <wtf/HashMap.h> #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/Vector.h> +#include <wtf/text/AtomicStringHash.h> namespace WebCore { -class AtomicStringImpl; class Frame; class KURL; -class String; class DOMApplicationCache : public RefCounted<DOMApplicationCache>, public EventTarget { public: static PassRefPtr<DOMApplicationCache> create(Frame* frame) { return adoptRef(new DOMApplicationCache(frame)); } ~DOMApplicationCache() { ASSERT(!m_frame); } + Frame* frame() const { return m_frame; } void disconnectFrame(); unsigned short status() const; diff --git a/WebCore/loader/appcache/DOMApplicationCache.idl b/WebCore/loader/appcache/DOMApplicationCache.idl index 9c3a359..9113ffa 100644 --- a/WebCore/loader/appcache/DOMApplicationCache.idl +++ b/WebCore/loader/appcache/DOMApplicationCache.idl @@ -28,7 +28,8 @@ module offline { interface [ Conditional=OFFLINE_WEB_APPLICATIONS, EventTarget, - OmitConstructor + OmitConstructor, + DontCheckEnums ] DOMApplicationCache { // update status const unsigned short UNCACHED = 0; @@ -55,12 +56,12 @@ module offline { attribute EventListener onobsolete; // EventTarget interface - [Custom] void addEventListener(in DOMString type, - in EventListener listener, - in boolean useCapture); - [Custom] void removeEventListener(in DOMString type, - in EventListener listener, - in boolean useCapture); + void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); boolean dispatchEvent(in Event evt) raises(EventException); }; diff --git a/WebCore/loader/appcache/ManifestParser.cpp b/WebCore/loader/appcache/ManifestParser.cpp index b001bff..f58a55d 100644 --- a/WebCore/loader/appcache/ManifestParser.cpp +++ b/WebCore/loader/appcache/ManifestParser.cpp @@ -127,6 +127,9 @@ bool parseManifest(const KURL& manifestURL, const char* data, int length, Manife if (!equalIgnoringCase(url.protocol(), manifestURL.protocol())) continue; + if (mode == Explicit && manifestURL.protocolIs("https") && !protocolHostAndPortAreEqual(manifestURL, url)) + continue; + if (mode == Explicit) manifest.explicitURLs.add(url.string()); else diff --git a/WebCore/loader/archive/ArchiveFactory.cpp b/WebCore/loader/archive/ArchiveFactory.cpp index d09b064..5d10415 100644 --- a/WebCore/loader/archive/ArchiveFactory.cpp +++ b/WebCore/loader/archive/ArchiveFactory.cpp @@ -34,6 +34,8 @@ #if PLATFORM(CF) && !PLATFORM(QT) #include "LegacyWebArchive.h" +#elif PLATFORM(ANDROID) +#include "WebArchiveAndroid.h" #endif #include <wtf/HashMap.h> @@ -62,6 +64,8 @@ static ArchiveMIMETypesMap& archiveMIMETypes() #if PLATFORM(CF) && !PLATFORM(QT) mimeTypes.set("application/x-webarchive", archiveFactoryCreate<LegacyWebArchive>); +#elif PLATFORM(ANDROID) + mimeTypes.set("application/x-webarchive-xml", archiveFactoryCreate<WebArchiveAndroid>); #endif initialized = true; diff --git a/WebCore/loader/archive/ArchiveFactory.h b/WebCore/loader/archive/ArchiveFactory.h index bf1d5c6..c3b9464 100644 --- a/WebCore/loader/archive/ArchiveFactory.h +++ b/WebCore/loader/archive/ArchiveFactory.h @@ -31,12 +31,12 @@ #include "Archive.h" +#include <wtf/Forward.h> #include <wtf/PassRefPtr.h> namespace WebCore { class SharedBuffer; -class String; class ArchiveFactory { public: diff --git a/WebCore/loader/archive/ArchiveResource.cpp b/WebCore/loader/archive/ArchiveResource.cpp index 691f66a..7dedc93 100644 --- a/WebCore/loader/archive/ArchiveResource.cpp +++ b/WebCore/loader/archive/ArchiveResource.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2010 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,31 +33,8 @@ namespace WebCore { -PassRefPtr<ArchiveResource> ArchiveResource::create(PassRefPtr<SharedBuffer> data, const KURL& url, const ResourceResponse& response) -{ - return data ? adoptRef(new ArchiveResource(data, url, response)) : 0; -} - -PassRefPtr<ArchiveResource> ArchiveResource::create(PassRefPtr<SharedBuffer> data, const KURL& url, const String& mimeType, const String& textEncoding, const String& frameName) -{ - return data ? adoptRef(new ArchiveResource(data, url, mimeType, textEncoding, frameName)) : 0; -} - -PassRefPtr<ArchiveResource> ArchiveResource::create(PassRefPtr<SharedBuffer> data, const KURL& url, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse& resourceResponse) -{ - return data ? adoptRef(new ArchiveResource(data, url, mimeType, textEncoding, frameName, resourceResponse)) : 0; -} - -ArchiveResource::ArchiveResource(PassRefPtr<SharedBuffer> data, const KURL& url, const ResourceResponse& response) +inline ArchiveResource::ArchiveResource(PassRefPtr<SharedBuffer> data, const KURL& url, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse& response) : SubstituteResource(url, response, data) - , m_mimeType(response.mimeType()) - , m_textEncoding(response.textEncodingName()) - , m_shouldIgnoreWhenUnarchiving(false) -{ -} - -ArchiveResource::ArchiveResource(PassRefPtr<SharedBuffer> data, const KURL& url, const String& mimeType, const String& textEncoding, const String& frameName) - : SubstituteResource(url, ResourceResponse(url, mimeType, data ? data->size() : 0, textEncoding, String()), data) , m_mimeType(mimeType) , m_textEncoding(textEncoding) , m_frameName(frameName) @@ -65,13 +42,21 @@ ArchiveResource::ArchiveResource(PassRefPtr<SharedBuffer> data, const KURL& url, { } -ArchiveResource::ArchiveResource(PassRefPtr<SharedBuffer> data, const KURL& url, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse& response) - : SubstituteResource(url, response.isNull() ? ResourceResponse(url, mimeType, data ? data->size() : 0, textEncoding, String()) : response, data) - , m_mimeType(mimeType) - , m_textEncoding(textEncoding) - , m_frameName(frameName) - , m_shouldIgnoreWhenUnarchiving(false) +PassRefPtr<ArchiveResource> ArchiveResource::create(PassRefPtr<SharedBuffer> data, const KURL& url, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse& response) +{ + if (!data) + return 0; + if (response.isNull()) { + unsigned dataSize = data->size(); + return adoptRef(new ArchiveResource(data, url, mimeType, textEncoding, frameName, + ResourceResponse(url, mimeType, dataSize, textEncoding, String()))); + } + return adoptRef(new ArchiveResource(data, url, mimeType, textEncoding, frameName, response)); +} + +PassRefPtr<ArchiveResource> ArchiveResource::create(PassRefPtr<SharedBuffer> data, const KURL& url, const ResourceResponse& response) { + return create(data, url, response.mimeType(), response.textEncodingName(), String(), response); } } diff --git a/WebCore/loader/archive/ArchiveResource.h b/WebCore/loader/archive/ArchiveResource.h index d975e04..97d6e32 100644 --- a/WebCore/loader/archive/ArchiveResource.h +++ b/WebCore/loader/archive/ArchiveResource.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2010 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,32 +31,29 @@ #include "SubstituteResource.h" -#include "PlatformString.h" - namespace WebCore { class ArchiveResource : public SubstituteResource { public: static PassRefPtr<ArchiveResource> create(PassRefPtr<SharedBuffer>, const KURL&, const ResourceResponse&); - static PassRefPtr<ArchiveResource> create(PassRefPtr<SharedBuffer>, const KURL&, const String& mimeType, const String& textEncoding, const String& frameName); - static PassRefPtr<ArchiveResource> create(PassRefPtr<SharedBuffer>, const KURL&, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse&); - + static PassRefPtr<ArchiveResource> create(PassRefPtr<SharedBuffer>, const KURL&, + const String& mimeType, const String& textEncoding, const String& frameName, + const ResourceResponse& = ResourceResponse()); + const String& mimeType() const { return m_mimeType; } const String& textEncoding() const { return m_textEncoding; } const String& frameName() const { return m_frameName; } - + void ignoreWhenUnarchiving() { m_shouldIgnoreWhenUnarchiving = true; } bool shouldIgnoreWhenUnarchiving() const { return m_shouldIgnoreWhenUnarchiving; } private: - ArchiveResource(PassRefPtr<SharedBuffer>, const KURL&, const ResourceResponse&); - ArchiveResource(PassRefPtr<SharedBuffer>, const KURL&, const String& mimeType, const String& textEncoding, const String& frameName); ArchiveResource(PassRefPtr<SharedBuffer>, const KURL&, const String& mimeType, const String& textEncoding, const String& frameName, const ResourceResponse&); - + String m_mimeType; String m_textEncoding; String m_frameName; - + bool m_shouldIgnoreWhenUnarchiving; }; diff --git a/WebCore/loader/archive/android/WebArchiveAndroid.cpp b/WebCore/loader/archive/android/WebArchiveAndroid.cpp new file mode 100644 index 0000000..49cfe9d --- /dev/null +++ b/WebCore/loader/archive/android/WebArchiveAndroid.cpp @@ -0,0 +1,469 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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. + */ + +#define LOG_TAG "webarchive" + +#include "config.h" +#include "WebArchiveAndroid.h" + +#include "Base64.h" +#include <libxml/encoding.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xmlstring.h> +#include <libxml/xmlwriter.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +static const xmlChar* const archiveTag = BAD_CAST "Archive"; +static const xmlChar* const archiveResourceTag = BAD_CAST "ArchiveResource"; +static const xmlChar* const mainResourceTag = BAD_CAST "mainResource"; +static const xmlChar* const subresourcesTag = BAD_CAST "subresources"; +static const xmlChar* const subframesTag = BAD_CAST "subframes"; +static const xmlChar* const urlFieldTag = BAD_CAST "url"; +static const xmlChar* const mimeFieldTag = BAD_CAST "mimeType"; +static const xmlChar* const encodingFieldTag = BAD_CAST "textEncoding"; +static const xmlChar* const frameFieldTag = BAD_CAST "frameName"; +static const xmlChar* const dataFieldTag = BAD_CAST "data"; + +PassRefPtr<WebArchiveAndroid> WebArchiveAndroid::create(PassRefPtr<ArchiveResource> mainResource, + Vector<PassRefPtr<ArchiveResource> >& subresources, + Vector<PassRefPtr<Archive> >& subframeArchives) +{ + if (mainResource) + return adoptRef(new WebArchiveAndroid(mainResource, subresources, subframeArchives)); + return 0; +} + +PassRefPtr<WebArchiveAndroid> WebArchiveAndroid::create(Frame* frame) +{ + PassRefPtr<ArchiveResource> mainResource = frame->loader()->documentLoader()->mainResource(); + Vector<PassRefPtr<ArchiveResource> > subresources; + Vector<PassRefPtr<Archive> > subframes; + int children = frame->tree()->childCount(); + + frame->loader()->documentLoader()->getSubresources(subresources); + + for (int child = 0; child < children; child++) + subframes.append(create(frame->tree()->child(child))); + + return create(mainResource, subresources, subframes); +} + +WebArchiveAndroid::WebArchiveAndroid(PassRefPtr<ArchiveResource> mainResource, + Vector<PassRefPtr<ArchiveResource> >& subresources, + Vector<PassRefPtr<Archive> >& subframeArchives) +{ + setMainResource(mainResource); + + for (Vector<PassRefPtr<ArchiveResource> >::iterator subresourcesIterator = subresources.begin(); + subresourcesIterator != subresources.end(); + subresourcesIterator++) { + addSubresource(*subresourcesIterator); + } + + for (Vector<PassRefPtr<Archive> >::iterator subframesIterator = subframeArchives.begin(); + subframesIterator != subframeArchives.end(); + subframesIterator++) { + addSubframeArchive(*subframesIterator); + } +} + +static bool loadArchiveResourceField(xmlNodePtr resourceNode, const xmlChar* fieldName, Vector<char>* outputData) +{ + if (!outputData) + return false; + + outputData->clear(); + + const char* base64Data = 0; + + for (xmlNodePtr fieldNode = resourceNode->xmlChildrenNode; + fieldNode; + fieldNode = fieldNode->next) { + if (xmlStrEqual(fieldNode->name, fieldName)) { + base64Data = (const char*)xmlNodeGetContent(fieldNode->xmlChildrenNode); + if (!base64Data) { + /* Empty fields seem to break if they aren't null terminated. */ + outputData->append('\0'); + return true; + } + break; + } + } + if (!base64Data) { + LOGD("loadWebArchive: Failed to load field."); + return false; + } + + const int base64Size = xmlStrlen(BAD_CAST base64Data); + + const int result = base64Decode(base64Data, base64Size, *outputData); + if (!result) { + LOGD("loadWebArchive: Failed to decode field."); + return false; + } + + return true; +} + +static PassRefPtr<SharedBuffer> loadArchiveResourceFieldBuffer(xmlNodePtr resourceNode, const xmlChar* fieldName) +{ + Vector<char> fieldData; + + if (loadArchiveResourceField(resourceNode, fieldName, &fieldData)) + return SharedBuffer::create(fieldData.data(), fieldData.size()); + + return 0; +} + +static String loadArchiveResourceFieldString(xmlNodePtr resourceNode, const xmlChar* fieldName) +{ + Vector<char> fieldData; + + if (loadArchiveResourceField(resourceNode, fieldName, &fieldData)) + return String::fromUTF8(fieldData.data(), fieldData.size()); + + return String(); +} + +static KURL loadArchiveResourceFieldURL(xmlNodePtr resourceNode, const xmlChar* fieldName) +{ + Vector<char> fieldData; + + if (loadArchiveResourceField(resourceNode, fieldName, &fieldData)) + return KURL(ParsedURLString, String::fromUTF8(fieldData.data(), fieldData.size())); + + return KURL(); +} + +static PassRefPtr<ArchiveResource> loadArchiveResource(xmlNodePtr resourceNode) +{ + if (!xmlStrEqual(resourceNode->name, archiveResourceTag)) { + LOGD("loadWebArchive: Malformed resource."); + return 0; + } + + KURL url = loadArchiveResourceFieldURL(resourceNode, urlFieldTag); + if (url.isNull()) { + LOGD("loadWebArchive: Failed to load resource."); + return 0; + } + + String mimeType = loadArchiveResourceFieldString(resourceNode, mimeFieldTag); + if (mimeType.isNull()) { + LOGD("loadWebArchive: Failed to load resource."); + return 0; + } + + String textEncoding = loadArchiveResourceFieldString(resourceNode, encodingFieldTag); + if (textEncoding.isNull()) { + LOGD("loadWebArchive: Failed to load resource."); + return 0; + } + + String frameName = loadArchiveResourceFieldString(resourceNode, frameFieldTag); + if (frameName.isNull()) { + LOGD("loadWebArchive: Failed to load resource."); + return 0; + } + + PassRefPtr<SharedBuffer> data = loadArchiveResourceFieldBuffer(resourceNode, dataFieldTag); + if (!data) { + LOGD("loadWebArchive: Failed to load resource."); + return 0; + } + + return ArchiveResource::create(data, url, mimeType, textEncoding, frameName); +} + +static PassRefPtr<WebArchiveAndroid> loadArchive(xmlNodePtr archiveNode) +{ + xmlNodePtr resourceNode = 0; + + PassRefPtr<ArchiveResource> mainResource; + Vector<PassRefPtr<ArchiveResource> > subresources; + Vector<PassRefPtr<Archive> > subframes; + + if (!xmlStrEqual(archiveNode->name, archiveTag)) { + LOGD("loadWebArchive: Malformed archive."); + return 0; + } + + for (resourceNode = archiveNode->xmlChildrenNode; + resourceNode; + resourceNode = resourceNode->next) { + if (xmlStrEqual(resourceNode->name, mainResourceTag)) { + resourceNode = resourceNode->xmlChildrenNode; + if (!resourceNode) + break; + mainResource = loadArchiveResource(resourceNode); + break; + } + } + if (!mainResource) { + LOGD("saveWebArchive: Failed to load main resource."); + return 0; + } + + for (resourceNode = archiveNode->xmlChildrenNode; + resourceNode; + resourceNode = resourceNode->next) { + if (xmlStrEqual(resourceNode->name, subresourcesTag)) { + for (resourceNode = resourceNode->xmlChildrenNode; + resourceNode; + resourceNode = resourceNode->next) { + PassRefPtr<ArchiveResource> subresource = loadArchiveResource(resourceNode); + if (!subresource) { + LOGD("saveWebArchive: Failed to load subresource."); + break; + } + subresources.append(subresource); + } + break; + } + } + + for (resourceNode = archiveNode->xmlChildrenNode; + resourceNode; + resourceNode = resourceNode->next) { + if (xmlStrEqual(resourceNode->name, subframesTag)) { + for (resourceNode = resourceNode->xmlChildrenNode; + resourceNode; + resourceNode = resourceNode->next) { + PassRefPtr<WebArchiveAndroid> subframe = loadArchive(resourceNode); + if (!subframe) { + LOGD("saveWebArchive: Failed to load subframe."); + break; + } + subframes.append(subframe); + } + break; + } + } + + return WebArchiveAndroid::create(mainResource, subresources, subframes); +} + +static PassRefPtr<WebArchiveAndroid> createArchiveForError() +{ + /* When an archive cannot be loaded, we return an empty archive instead. */ + PassRefPtr<ArchiveResource> mainResource = ArchiveResource::create( + SharedBuffer::create(), KURL(ParsedURLString, String::fromUTF8("file:///dummy")), + String::fromUTF8("text/plain"), String(""), String("")); + Vector<PassRefPtr<ArchiveResource> > subresources; + Vector<PassRefPtr<Archive> > subframes; + + return WebArchiveAndroid::create(mainResource, subresources, subframes); +} + +PassRefPtr<WebArchiveAndroid> WebArchiveAndroid::create(SharedBuffer* buffer) +{ + const char* const noBaseUrl = ""; + const char* const defaultEncoding = 0; + const int noParserOptions = 0; + + xmlDocPtr doc = xmlReadMemory(buffer->data(), buffer->size(), noBaseUrl, defaultEncoding, noParserOptions); + if (!doc) { + LOGD("loadWebArchive: Failed to parse document."); + return createArchiveForError(); + } + + xmlNodePtr root = xmlDocGetRootElement(doc); + if (!root) { + LOGD("loadWebArchive: Empty document."); + xmlFreeDoc(doc); + return createArchiveForError(); + } + + RefPtr<WebArchiveAndroid> archive = loadArchive(root); + if (!archive) { + LOGD("loadWebArchive: Failed to load archive."); + xmlFreeDoc(doc); + return createArchiveForError(); + } + + xmlFreeDoc(doc); + return archive.release(); +} + +static bool saveArchiveResourceField(xmlTextWriterPtr writer, const xmlChar* tag, const char* data, int size) +{ + int result = xmlTextWriterStartElement(writer, tag); + if (result < 0) { + LOGD("saveWebArchive: Failed to start element."); + return false; + } + + if (size > 0) { + Vector<char> base64Data; + base64Encode(data, size, base64Data, false); + if (base64Data.isEmpty()) { + LOGD("saveWebArchive: Failed to base64 encode data."); + return false; + } + + result = xmlTextWriterWriteRawLen(writer, BAD_CAST base64Data.data(), base64Data.size()); + if (result < 0) { + LOGD("saveWebArchive: Failed to write data."); + return false; + } + } + + result = xmlTextWriterEndElement(writer); + if (result < 0) { + LOGD("saveWebArchive: Failed to end element."); + return false; + } + + return true; +} + +static bool saveArchiveResourceField(xmlTextWriterPtr writer, const xmlChar* tag, SharedBuffer* buffer) +{ + return saveArchiveResourceField(writer, tag, buffer->data(), buffer->size()); +} + +static bool saveArchiveResourceField(xmlTextWriterPtr writer, const xmlChar* tag, const String& string) +{ + CString utf8String = string.utf8(); + + return saveArchiveResourceField(writer, tag, utf8String.data(), utf8String.length()); +} + +static bool saveArchiveResource(xmlTextWriterPtr writer, PassRefPtr<ArchiveResource> resource) +{ + int result = xmlTextWriterStartElement(writer, archiveResourceTag); + if (result < 0) { + LOGD("saveWebArchive: Failed to start element."); + return false; + } + + if (!saveArchiveResourceField(writer, urlFieldTag, resource->url().string()) + || !saveArchiveResourceField(writer, mimeFieldTag, resource->mimeType()) + || !saveArchiveResourceField(writer, encodingFieldTag, resource->textEncoding()) + || !saveArchiveResourceField(writer, frameFieldTag, resource->frameName()) + || !saveArchiveResourceField(writer, dataFieldTag, resource->data())) + return false; + + result = xmlTextWriterEndElement(writer); + if (result < 0) { + LOGD("saveWebArchive: Failed to end element."); + return false; + } + + return true; +} + +static bool saveArchive(xmlTextWriterPtr writer, PassRefPtr<Archive> archive) +{ + int result = xmlTextWriterStartElement(writer, archiveTag); + if (result < 0) { + LOGD("saveWebArchive: Failed to start element."); + return false; + } + + result = xmlTextWriterStartElement(writer, mainResourceTag); + if (result < 0) { + LOGD("saveWebArchive: Failed to start element."); + return false; + } + + if (!saveArchiveResource(writer, archive->mainResource())) + return false; + + result = xmlTextWriterEndElement(writer); + if (result < 0) { + LOGD("saveWebArchive: Failed to end element."); + return false; + } + + result = xmlTextWriterStartElement(writer, subresourcesTag); + if (result < 0) { + LOGD("saveWebArchive: Failed to start element."); + return false; + } + + for (Vector<const RefPtr<ArchiveResource> >::iterator subresource = archive->subresources().begin(); + subresource != archive->subresources().end(); + subresource++) { + if (!saveArchiveResource(writer, *subresource)) + return false; + } + + result = xmlTextWriterEndElement(writer); + if (result < 0) { + LOGD("saveWebArchive: Failed to end element."); + return false; + } + + result = xmlTextWriterStartElement(writer, subframesTag); + if (result < 0) { + LOGD("saveWebArchive: Failed to start element."); + return false; + } + + for (Vector<const RefPtr<Archive> >::iterator subframe = archive->subframeArchives().begin(); + subframe != archive->subframeArchives().end(); + subframe++) { + if (!saveArchive(writer, *subframe)) + return false; + } + + result = xmlTextWriterEndElement(writer); + if (result < 0) { + LOGD("saveWebArchive: Failed to end element."); + return true; + } + + return true; +} + +bool WebArchiveAndroid::saveWebArchive(xmlTextWriterPtr writer) +{ + const char* const defaultXmlVersion = 0; + const char* const defaultEncoding = 0; + const char* const defaultStandalone = 0; + + int result = xmlTextWriterStartDocument(writer, defaultXmlVersion, defaultEncoding, defaultStandalone); + if (result < 0) { + LOGD("saveWebArchive: Failed to start document."); + return false; + } + + if (!saveArchive(writer, this)) + return false; + + result = xmlTextWriterEndDocument(writer); + if (result< 0) { + LOGD("saveWebArchive: Failed to end document."); + return false; + } + + return true; +} + +} diff --git a/WebCore/loader/archive/android/WebArchiveAndroid.h b/WebCore/loader/archive/android/WebArchiveAndroid.h new file mode 100644 index 0000000..1bbf952 --- /dev/null +++ b/WebCore/loader/archive/android/WebArchiveAndroid.h @@ -0,0 +1,55 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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 WebArchiveAndroid_h +#define WebArchiveAndroid_h + +#include "Archive.h" +#include "DocumentLoader.h" +#include "Frame.h" +#include <libxml/xmlwriter.h> + +namespace WebCore { + +class WebArchiveAndroid : public Archive { +public: + static PassRefPtr<WebArchiveAndroid> create(PassRefPtr<ArchiveResource> mainResource, + Vector<PassRefPtr<ArchiveResource> >& subresources, + Vector<PassRefPtr<Archive> >& subframeArchives); + + static PassRefPtr<WebArchiveAndroid> create(Frame* frame); + static PassRefPtr<WebArchiveAndroid> create(SharedBuffer* buffer); + + bool saveWebArchive(xmlTextWriterPtr writer); + +private: + WebArchiveAndroid(PassRefPtr<ArchiveResource> mainResource, + Vector<PassRefPtr<ArchiveResource> >& subresources, + Vector<PassRefPtr<Archive> >& subframeArchives); +}; + +} + +#endif // WEBARCHIVEANDROID_H diff --git a/WebCore/loader/archive/cf/LegacyWebArchive.cpp b/WebCore/loader/archive/cf/LegacyWebArchive.cpp index 3141e98..ddd564e 100644 --- a/WebCore/loader/archive/cf/LegacyWebArchive.cpp +++ b/WebCore/loader/archive/cf/LegacyWebArchive.cpp @@ -29,8 +29,7 @@ #include "config.h" #include "LegacyWebArchive.h" -#include "CString.h" -#include "Cache.h" +#include "MemoryCache.h" #include "Document.h" #include "DocumentLoader.h" #include "Frame.h" @@ -47,6 +46,8 @@ #include "Range.h" #include "SelectionController.h" #include "SharedBuffer.h" +#include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> #include <wtf/ListHashSet.h> #include <wtf/RetainPtr.h> @@ -198,7 +199,7 @@ PassRefPtr<ArchiveResource> LegacyWebArchive::createResource(CFDictionaryRef dic } CFStringRef mimeType = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceMIMETypeKey)); - if (mimeType && CFGetTypeID(mimeType) != CFStringGetTypeID()) { + if (!mimeType || CFGetTypeID(mimeType) != CFStringGetTypeID()) { LOG(Archives, "LegacyWebArchive - MIME type is not of type CFString, cannot create invalid resource"); return 0; } @@ -233,7 +234,7 @@ PassRefPtr<ArchiveResource> LegacyWebArchive::createResource(CFDictionaryRef dic response = createResourceResponseFromPropertyListData(resourceResponseData, resourceResponseVersion); } - return ArchiveResource::create(SharedBuffer::create(CFDataGetBytePtr(resourceData), CFDataGetLength(resourceData)), KURL(ParsedURLString, url), mimeType, textEncoding, frameName, response); + return ArchiveResource::create(SharedBuffer::wrapCFData(resourceData), KURL(KURL(), url), mimeType, textEncoding, frameName, response); } PassRefPtr<LegacyWebArchive> LegacyWebArchive::create() @@ -491,7 +492,7 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString if (responseURL.isNull()) responseURL = KURL(ParsedURLString, ""); - PassRefPtr<ArchiveResource> mainResource = ArchiveResource::create(utf8Buffer(markupString), responseURL, response.mimeType(), "UTF-8", frame->tree()->name()); + PassRefPtr<ArchiveResource> mainResource = ArchiveResource::create(utf8Buffer(markupString), responseURL, response.mimeType(), "UTF-8", frame->tree()->uniqueName()); Vector<PassRefPtr<LegacyWebArchive> > subframeArchives; Vector<PassRefPtr<ArchiveResource> > subresources; @@ -508,7 +509,7 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString if (subframeArchive) subframeArchives.append(subframeArchive); else - LOG_ERROR("Unabled to archive subframe %s", childFrame->tree()->name().string().utf8().data()); + LOG_ERROR("Unabled to archive subframe %s", childFrame->tree()->uniqueName().string().utf8().data()); } else { ListHashSet<KURL> subresourceURLs; node->getSubresourceURLs(subresourceURLs); @@ -573,8 +574,8 @@ PassRefPtr<LegacyWebArchive> LegacyWebArchive::createFromSelection(Frame* frame) // Wrap the frameset document in an iframe so it can be pasted into // another document (which will have a body or frameset of its own). - String iframeMarkup = String::format("<iframe frameborder=\"no\" marginwidth=\"0\" marginheight=\"0\" width=\"98%%\" height=\"98%%\" src=\"%s\"></iframe>", - frame->loader()->documentLoader()->response().url().string().utf8().data()); + String iframeMarkup = makeString("<iframe frameborder=\"no\" marginwidth=\"0\" marginheight=\"0\" width=\"98%%\" height=\"98%%\" src=\"", + frame->loader()->documentLoader()->response().url().string(), "\"></iframe>"); RefPtr<ArchiveResource> iframeResource = ArchiveResource::create(utf8Buffer(iframeMarkup), blankURL(), "text/html", "UTF-8", String()); Vector<PassRefPtr<ArchiveResource> > subresources; diff --git a/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm b/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm index c474bba..6a35753 100644 --- a/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm +++ b/WebCore/loader/archive/cf/LegacyWebArchiveMac.mm @@ -31,7 +31,7 @@ namespace WebCore { -static const NSString *LegacyWebArchiveResourceResponseKey = @"WebResourceResponse"; +static NSString * const LegacyWebArchiveResourceResponseKey = @"WebResourceResponse"; // 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. diff --git a/WebCore/loader/CachePolicy.h b/WebCore/loader/cache/CachePolicy.h index 2639caa..2639caa 100644 --- a/WebCore/loader/CachePolicy.h +++ b/WebCore/loader/cache/CachePolicy.h diff --git a/WebCore/loader/CachedCSSStyleSheet.cpp b/WebCore/loader/cache/CachedCSSStyleSheet.cpp index b2e03b9..f0016d1 100644 --- a/WebCore/loader/CachedCSSStyleSheet.cpp +++ b/WebCore/loader/cache/CachedCSSStyleSheet.cpp @@ -27,10 +27,12 @@ #include "config.h" #include "CachedCSSStyleSheet.h" +#include "MemoryCache.h" #include "CachedResourceClient.h" #include "CachedResourceClientWalker.h" #include "HTTPParsers.h" #include "TextResourceDecoder.h" +#include "SharedBuffer.h" #include "loader.h" #include <wtf/Vector.h> @@ -51,13 +53,13 @@ CachedCSSStyleSheet::~CachedCSSStyleSheet() void CachedCSSStyleSheet::didAddClient(CachedResourceClient *c) { - if (!m_loading) + if (!isLoading()) c->setCSSStyleSheet(m_url, m_response.url(), m_decoder->encoding().name(), this); } void CachedCSSStyleSheet::allClientsRemoved() { - if (isSafeToMakePurgeable()) + if (!MemoryCache::shouldMakeResourcePurgeableOnEviction() && isSafeToMakePurgeable()) makePurgeable(true); } @@ -99,7 +101,7 @@ void CachedCSSStyleSheet::data(PassRefPtr<SharedBuffer> data, bool allDataReceiv m_decodedSheetText = m_decoder->decode(m_data->data(), m_data->size()); m_decodedSheetText += m_decoder->flush(); } - m_loading = false; + setLoading(false); checkNotify(); // Clear the decoded text as it is unlikely to be needed immediately again and is cheap to regenerate. m_decodedSheetText = String(); @@ -107,7 +109,7 @@ void CachedCSSStyleSheet::data(PassRefPtr<SharedBuffer> data, bool allDataReceiv void CachedCSSStyleSheet::checkNotify() { - if (m_loading) + if (isLoading()) return; CachedResourceClientWalker w(m_clients); @@ -117,8 +119,8 @@ void CachedCSSStyleSheet::checkNotify() void CachedCSSStyleSheet::error() { - m_loading = false; - m_errorOccurred = true; + setLoading(false); + setErrorOccurred(true); checkNotify(); } diff --git a/WebCore/loader/CachedCSSStyleSheet.h b/WebCore/loader/cache/CachedCSSStyleSheet.h index 908c4c0..abcdb85 100644 --- a/WebCore/loader/CachedCSSStyleSheet.h +++ b/WebCore/loader/cache/CachedCSSStyleSheet.h @@ -32,7 +32,7 @@ namespace WebCore { - class DocLoader; + class CachedResourceLoader; class TextResourceDecoder; class CachedCSSStyleSheet : public CachedResource { @@ -51,12 +51,11 @@ namespace WebCore { virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived); virtual void error(); - virtual bool schedule() const { return true; } - void checkNotify(); private: bool canUseSheet(bool enforceMIMEType, bool* hasValidMIMEType) const; + virtual PurgePriority purgePriority() const { return PurgeLast; } protected: RefPtr<TextResourceDecoder> m_decoder; diff --git a/WebCore/loader/CachedFont.cpp b/WebCore/loader/cache/CachedFont.cpp index 3d9eeb9..6297ad1 100644 --- a/WebCore/loader/CachedFont.cpp +++ b/WebCore/loader/cache/CachedFont.cpp @@ -27,22 +27,23 @@ #include "config.h" #include "CachedFont.h" -#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && (OS(WINDOWS) || OS(LINUX))) || PLATFORM(HAIKU) || OS(WINCE) || OS(ANDROID) +#if PLATFORM(CG) || PLATFORM(QT) || PLATFORM(GTK) || (PLATFORM(CHROMIUM) && (OS(WINDOWS) || OS(LINUX) || OS(FREEBSD))) || PLATFORM(HAIKU) || OS(WINCE) || PLATFORM(ANDROID) || PLATFORM(BREWMP) #define STORE_FONT_CUSTOM_PLATFORM_DATA #endif -#include "Cache.h" +#include "MemoryCache.h" #include "CachedResourceClient.h" #include "CachedResourceClientWalker.h" -#include "DOMImplementation.h" #include "FontPlatformData.h" -#ifdef STORE_FONT_CUSTOM_PLATFORM_DATA -#include "FontCustomPlatformData.h" -#endif +#include "SharedBuffer.h" #include "TextResourceDecoder.h" #include "loader.h" #include <wtf/Vector.h> +#ifdef STORE_FONT_CUSTOM_PLATFORM_DATA +#include "FontCustomPlatformData.h" +#endif + #if ENABLE(SVG_FONTS) #include "HTMLNames.h" #include "NodeList.h" @@ -70,15 +71,15 @@ CachedFont::~CachedFont() #endif } -void CachedFont::load(DocLoader*) +void CachedFont::load(CachedResourceLoader*) { // Don't load the file yet. Wait for an access before triggering the load. - m_loading = true; + setLoading(true); } void CachedFont::didAddClient(CachedResourceClient* c) { - if (!m_loading) + if (!isLoading()) c->fontLoaded(this); } @@ -89,11 +90,11 @@ void CachedFont::data(PassRefPtr<SharedBuffer> data, bool allDataReceived) m_data = data; setEncodedSize(m_data.get() ? m_data->size() : 0); - m_loading = false; + setLoading(false); checkNotify(); } -void CachedFont::beginLoadIfNeeded(DocLoader* dl) +void CachedFont::beginLoadIfNeeded(CachedResourceLoader* dl) { if (!m_loadInitiated) { m_loadInitiated = true; @@ -107,16 +108,16 @@ bool CachedFont::ensureCustomFontData() #if ENABLE(SVG_FONTS) ASSERT(!m_isSVGFont); #endif - if (!m_fontData && !m_errorOccurred && !m_loading && m_data) { + if (!m_fontData && !errorOccurred() && !isLoading() && m_data) { m_fontData = createFontCustomPlatformData(m_data.get()); if (!m_fontData) - m_errorOccurred = true; + setErrorOccurred(true); } #endif return m_fontData; } -FontPlatformData CachedFont::platformDataFromCustomData(float size, bool bold, bool italic, FontRenderingMode renderingMode) +FontPlatformData CachedFont::platformDataFromCustomData(float size, bool bold, bool italic, FontOrientation orientation, FontRenderingMode renderingMode) { #if ENABLE(SVG_FONTS) if (m_externalSVGDocument) @@ -124,7 +125,7 @@ FontPlatformData CachedFont::platformDataFromCustomData(float size, bool bold, b #endif #ifdef STORE_FONT_CUSTOM_PLATFORM_DATA ASSERT(m_fontData); - return m_fontData->fontPlatformData(static_cast<int>(size), bold, italic, renderingMode); + return m_fontData->fontPlatformData(static_cast<int>(size), bold, italic, orientation, renderingMode); #else return FontPlatformData(); #endif @@ -134,8 +135,8 @@ FontPlatformData CachedFont::platformDataFromCustomData(float size, bool bold, b bool CachedFont::ensureSVGFontData() { ASSERT(m_isSVGFont); - if (!m_externalSVGDocument && !m_errorOccurred && !m_loading && m_data) { - m_externalSVGDocument = SVGDocument::create(0); + if (!m_externalSVGDocument && !errorOccurred() && !isLoading() && m_data) { + m_externalSVGDocument = SVGDocument::create(0, KURL()); m_externalSVGDocument->open(); RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create("application/xml"); @@ -156,20 +157,28 @@ bool CachedFont::ensureSVGFontData() SVGFontElement* CachedFont::getSVGFontById(const String& fontName) const { ASSERT(m_isSVGFont); - RefPtr<NodeList> list = m_externalSVGDocument->getElementsByTagName(SVGNames::fontTag.localName()); + RefPtr<NodeList> list = m_externalSVGDocument->getElementsByTagNameNS(SVGNames::fontTag.namespaceURI(), SVGNames::fontTag.localName()); if (!list) return 0; - unsigned fonts = list->length(); - for (unsigned i = 0; i < fonts; ++i) { - Node* node = list->item(i); - ASSERT(node); + unsigned listLength = list->length(); + if (!listLength) + return 0; + +#ifndef NDEBUG + for (unsigned i = 0; i < listLength; ++i) { + ASSERT(list->item(i)); + ASSERT(list->item(i)->hasTagName(SVGNames::fontTag)); + } +#endif - if (static_cast<Element*>(node)->getAttribute(static_cast<Element*>(node)->idAttributeName()) != fontName) - continue; + if (fontName.isEmpty()) + return static_cast<SVGFontElement*>(list->item(0)); - ASSERT(node->hasTagName(SVGNames::fontTag)); - return static_cast<SVGFontElement*>(node); + for (unsigned i = 0; i < listLength; ++i) { + SVGFontElement* element = static_cast<SVGFontElement*>(list->item(i)); + if (element->getIdAttribute() == fontName) + return element; } return 0; @@ -188,7 +197,7 @@ void CachedFont::allClientsRemoved() void CachedFont::checkNotify() { - if (m_loading) + if (isLoading()) return; CachedResourceClientWalker w(m_clients); @@ -199,8 +208,8 @@ void CachedFont::checkNotify() void CachedFont::error() { - m_loading = false; - m_errorOccurred = true; + setLoading(false); + setErrorOccurred(true); checkNotify(); } diff --git a/WebCore/loader/CachedFont.h b/WebCore/loader/cache/CachedFont.h index 05b28f3..e1a34e8 100644 --- a/WebCore/loader/CachedFont.h +++ b/WebCore/loader/cache/CachedFont.h @@ -27,6 +27,7 @@ #define CachedFont_h #include "CachedResource.h" +#include "FontOrientation.h" #include "FontRenderingMode.h" #include <wtf/Vector.h> @@ -37,8 +38,8 @@ namespace WebCore { -class DocLoader; -class Cache; +class CachedResourceLoader; +class MemoryCache; class FontPlatformData; class SVGFontElement; @@ -49,7 +50,7 @@ public: CachedFont(const String& url); virtual ~CachedFont(); - virtual void load(DocLoader* docLoader); + virtual void load(CachedResourceLoader* cachedResourceLoader); virtual void didAddClient(CachedResourceClient*); virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived); @@ -57,14 +58,12 @@ public: virtual void allClientsRemoved(); - virtual bool schedule() const { return true; } - void checkNotify(); - void beginLoadIfNeeded(DocLoader* dl); + void beginLoadIfNeeded(CachedResourceLoader* dl); bool ensureCustomFontData(); - FontPlatformData platformDataFromCustomData(float size, bool bold, bool italic, FontRenderingMode = NormalRenderingMode); + FontPlatformData platformDataFromCustomData(float size, bool bold, bool italic, FontOrientation = Horizontal, FontRenderingMode = NormalRenderingMode); #if ENABLE(SVG_FONTS) bool isSVGFont() const { return m_isSVGFont; } @@ -82,7 +81,7 @@ private: RefPtr<SVGDocument> m_externalSVGDocument; #endif - friend class Cache; + friend class MemoryCache; }; } diff --git a/WebCore/loader/CachedImage.cpp b/WebCore/loader/cache/CachedImage.cpp index dd93041..ce1c9a3 100644 --- a/WebCore/loader/CachedImage.cpp +++ b/WebCore/loader/cache/CachedImage.cpp @@ -25,15 +25,16 @@ #include "CachedImage.h" #include "BitmapImage.h" -#include "Cache.h" +#include "MemoryCache.h" #include "CachedResourceClient.h" #include "CachedResourceClientWalker.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "Frame.h" #include "FrameLoaderTypes.h" #include "FrameView.h" #include "Request.h" #include "Settings.h" +#include "SharedBuffer.h" #include <wtf/CurrentTime.h> #include <wtf/StdLibExtras.h> #include <wtf/Vector.h> @@ -56,7 +57,7 @@ CachedImage::CachedImage(const String& url) , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired) , m_httpStatusCodeErrorOccurred(false) { - m_status = Unknown; + setStatus(Unknown); } CachedImage::CachedImage(Image* image) @@ -65,8 +66,8 @@ CachedImage::CachedImage(Image* image) , m_decodedDataDeletionTimer(this, &CachedImage::decodedDataDeletionTimerFired) , m_httpStatusCodeErrorOccurred(false) { - m_status = Cached; - m_loading = false; + setStatus(Cached); + setLoading(false); } CachedImage::~CachedImage() @@ -79,16 +80,16 @@ void CachedImage::decodedDataDeletionTimerFired(Timer<CachedImage>*) destroyDecodedData(); } -void CachedImage::load(DocLoader* docLoader) +void CachedImage::load(CachedResourceLoader* cachedResourceLoader) { #ifdef ANDROID_BLOCK_NETWORK_IMAGE - if (!docLoader || (docLoader->autoLoadImages() && !docLoader->shouldBlockNetworkImage(m_url))) + if (!cachedResourceLoader || (cachedResourceLoader->autoLoadImages() && !cachedResourceLoader->shouldBlockNetworkImage(m_url))) #else - if (!docLoader || docLoader->autoLoadImages()) + if (!cachedResourceLoader || cachedResourceLoader->autoLoadImages()) #endif - CachedResource::load(docLoader, true, DoSecurityCheck, true); + CachedResource::load(cachedResourceLoader, true, DoSecurityCheck, true); else - m_loading = false; + setLoading(false); } void CachedImage::didAddClient(CachedResourceClient* c) @@ -96,7 +97,7 @@ void CachedImage::didAddClient(CachedResourceClient* c) if (m_decodedDataDeletionTimer.isActive()) m_decodedDataDeletionTimer.stop(); - if (m_data && !m_image && !m_errorOccurred) { + if (m_data && !m_image && !errorOccurred()) { createImage(); m_image->setData(m_data, true); } @@ -104,13 +105,13 @@ void CachedImage::didAddClient(CachedResourceClient* c) if (m_image && !m_image->isNull()) c->imageChanged(this); - if (!m_loading) + if (!isLoading()) c->notifyFinished(this); } void CachedImage::allClientsRemoved() { - if (m_image && !m_errorOccurred) + if (m_image && !errorOccurred()) m_image->resetAnimation(); if (double interval = cache()->deadDecodedDataDeletionInterval()) m_decodedDataDeletionTimer.startOneShot(interval); @@ -132,7 +133,7 @@ Image* CachedImage::image() const { ASSERT(!isPurgeable()); - if (m_errorOccurred) + if (errorOccurred()) return brokenImage(); if (m_image) @@ -240,7 +241,7 @@ inline void CachedImage::createImage() // Create the image if it doesn't yet exist. if (m_image) return; -#if PLATFORM(CG) +#if PLATFORM(CG) && !USE(WEBKIT_IMAGE_DECODERS) if (m_response.mimeType() == "application/pdf") { m_image = PDFDocumentImage::create(); return; @@ -260,7 +261,7 @@ inline void CachedImage::createImage() size_t CachedImage::maximumDecodedImageSize() { - Frame* frame = m_request ? m_request->docLoader()->frame() : 0; + Frame* frame = m_request ? m_request->cachedResourceLoader()->frame() : 0; if (!frame) return 0; Settings* settings = frame->settings(); @@ -304,7 +305,7 @@ void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived) } if (allDataReceived) { - m_loading = false; + setLoading(false); checkNotify(); } } @@ -312,16 +313,16 @@ void CachedImage::data(PassRefPtr<SharedBuffer> data, bool allDataReceived) void CachedImage::error() { clear(); - m_errorOccurred = true; + setErrorOccurred(true); m_data.clear(); notifyObservers(); - m_loading = false; + setLoading(false); checkNotify(); } void CachedImage::checkNotify() { - if (m_loading) + if (isLoading()) return; CachedResourceClientWalker w(m_clients); @@ -332,13 +333,14 @@ void CachedImage::checkNotify() void CachedImage::destroyDecodedData() { bool canDeleteImage = !m_image || (m_image->hasOneRef() && m_image->isBitmapImage()); - if (isSafeToMakePurgeable() && canDeleteImage && !m_loading) { + if (isSafeToMakePurgeable() && canDeleteImage && !isLoading()) { // Image refs the data buffer so we should not make it purgeable while the image is alive. // Invoking addClient() will reconstruct the image object. m_image = 0; setDecodedSize(0); - makePurgeable(true); - } else if (m_image && !m_errorOccurred) + if (!MemoryCache::shouldMakeResourcePurgeableOnEviction()) + makePurgeable(true); + } else if (m_image && !errorOccurred()) m_image->destroyDecodedData(); } diff --git a/WebCore/loader/CachedImage.h b/WebCore/loader/cache/CachedImage.h index 2aa35ac..313f3f3 100644 --- a/WebCore/loader/CachedImage.h +++ b/WebCore/loader/cache/CachedImage.h @@ -25,25 +25,24 @@ #include "CachedResource.h" #include "ImageObserver.h" -#include "Image.h" #include "IntRect.h" #include "Timer.h" #include <wtf/Vector.h> namespace WebCore { -class DocLoader; -class Cache; +class CachedResourceLoader; +class MemoryCache; class CachedImage : public CachedResource, public ImageObserver { - friend class Cache; + friend class MemoryCache; public: CachedImage(const String& url); CachedImage(Image*); virtual ~CachedImage(); - virtual void load(DocLoader* docLoader); + virtual void load(CachedResourceLoader* cachedResourceLoader); Image* image() const; @@ -71,15 +70,13 @@ public: virtual void httpStatusCodeError() { m_httpStatusCodeErrorOccurred = true; } bool httpStatusCodeErrorOccurred() const { return m_httpStatusCodeErrorOccurred; } - virtual bool schedule() const { return true; } - void checkNotify(); virtual bool isImage() const { return true; } void clear(); - bool stillNeedsLoad() const { return !m_errorOccurred && m_status == Unknown && m_loading == false; } + bool stillNeedsLoad() const { return !errorOccurred() && status() == Unknown && !isLoading(); } void load(); // ImageObserver @@ -96,6 +93,7 @@ private: // If not null, changeRect is the changed part of the image. void notifyObservers(const IntRect* changeRect = 0); void decodedDataDeletionTimerFired(Timer<CachedImage>*); + virtual PurgePriority purgePriority() const { return PurgeFirst; } RefPtr<Image> m_image; Timer<CachedImage> m_decodedDataDeletionTimer; diff --git a/WebCore/loader/CachedResource.cpp b/WebCore/loader/cache/CachedResource.cpp index 640d1f7..c440ec9 100644 --- a/WebCore/loader/CachedResource.cpp +++ b/WebCore/loader/cache/CachedResource.cpp @@ -24,14 +24,20 @@ #include "config.h" #include "CachedResource.h" -#include "Cache.h" +#include "MemoryCache.h" +#include "CachedMetadata.h" +#include "CachedResourceClient.h" +#include "CachedResourceClientWalker.h" #include "CachedResourceHandle.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "Frame.h" -#include "FrameLoader.h" +#include "FrameLoaderClient.h" #include "KURL.h" +#include "Logging.h" #include "PurgeableBuffer.h" #include "Request.h" +#include "ResourceHandle.h" +#include "SharedBuffer.h" #include <wtf/CurrentTime.h> #include <wtf/MathExtras.h> #include <wtf/RefCountedLeakCounter.h> @@ -48,43 +54,38 @@ static RefCountedLeakCounter cachedResourceLeakCounter("CachedResource"); CachedResource::CachedResource(const String& url, Type type) : m_url(url) + , m_request(0) , m_responseTimestamp(currentTime()) , m_lastDecodedAccessTime(0) - , m_sendResourceLoadCallbacks(true) + , m_encodedSize(0) + , m_decodedSize(0) + , m_accessCount(0) + , m_handleCount(0) , m_preloadCount(0) , m_preloadResult(PreloadNotReferenced) + , m_inLiveDecodedResourcesList(false) , m_requestedFromNetworkingLayer(false) + , m_sendResourceLoadCallbacks(true) + , m_errorOccurred(false) , m_inCache(false) , m_loading(false) - , m_docLoader(0) - , m_handleCount(0) + , m_type(type) + , m_status(Pending) +#ifndef NDEBUG + , m_deleted(false) + , m_lruIndex(0) +#endif + , m_nextInAllResourcesList(0) + , m_prevInAllResourcesList(0) + , m_nextInLiveResourcesList(0) + , m_prevInLiveResourcesList(0) + , m_cachedResourceLoader(0) , m_resourceToRevalidate(0) , m_proxyResource(0) { #ifndef NDEBUG cachedResourceLeakCounter.increment(); #endif - - m_type = type; - m_status = Pending; - m_encodedSize = 0; - m_decodedSize = 0; - m_request = 0; - - m_accessCount = 0; - m_inLiveDecodedResourcesList = false; - - m_nextInAllResourcesList = 0; - m_prevInAllResourcesList = 0; - - m_nextInLiveResourcesList = 0; - m_prevInLiveResourcesList = 0; - -#ifndef NDEBUG - m_deleted = false; - m_lruIndex = 0; -#endif - m_errorOccurred = false; } CachedResource::~CachedResource() @@ -99,17 +100,27 @@ CachedResource::~CachedResource() cachedResourceLeakCounter.decrement(); #endif - if (m_docLoader) - m_docLoader->removeCachedResource(this); + if (m_cachedResourceLoader) + m_cachedResourceLoader->removeCachedResource(this); } - -void CachedResource::load(DocLoader* docLoader, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks) + +void CachedResource::load(CachedResourceLoader* cachedResourceLoader, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks) { m_sendResourceLoadCallbacks = sendResourceLoadCallbacks; - cache()->loader()->load(docLoader, this, incremental, securityCheck, sendResourceLoadCallbacks); + cache()->loader()->load(cachedResourceLoader, this, incremental, securityCheck, sendResourceLoadCallbacks); m_loading = true; } +void CachedResource::data(PassRefPtr<SharedBuffer>, bool allDataReceived) +{ + if (!allDataReceived) + return; + + CachedResourceClientWalker w(m_clients); + while (CachedResourceClient* c = w.next()) + c->notifyFinished(this); +} + void CachedResource::finish() { m_status = Cached; @@ -163,6 +174,34 @@ void CachedResource::setResponse(const ResourceResponse& response) m_responseTimestamp = currentTime(); } +void CachedResource::setSerializedCachedMetadata(const char* data, size_t size) +{ + // We only expect to receive cached metadata from the platform once. + // If this triggers, it indicates an efficiency problem which is most + // likely unexpected in code designed to improve performance. + ASSERT(!m_cachedMetadata); + + m_cachedMetadata = CachedMetadata::deserialize(data, size); +} + +void CachedResource::setCachedMetadata(unsigned dataTypeID, const char* data, size_t size) +{ + // Currently, only one type of cached metadata per resource is supported. + // If the need arises for multiple types of metadata per resource this could + // be enhanced to store types of metadata in a map. + ASSERT(!m_cachedMetadata); + + m_cachedMetadata = CachedMetadata::create(dataTypeID, data, size); + ResourceHandle::cacheMetadata(m_response, m_cachedMetadata->serialize()); +} + +CachedMetadata* CachedResource::cachedMetadata(unsigned dataTypeID) const +{ + if (!m_cachedMetadata || m_cachedMetadata->dataTypeID() != dataTypeID) + return 0; + return m_cachedMetadata.get(); +} + void CachedResource::setRequest(Request* request) { if (request && !m_request) @@ -178,6 +217,12 @@ void CachedResource::addClient(CachedResourceClient* client) didAddClient(client); } +void CachedResource::didAddClient(CachedResourceClient* c) +{ + if (!isLoading()) + c->notifyFinished(this); +} + void CachedResource::addClientToSet(CachedResourceClient* client) { ASSERT(!isPurgeable()); @@ -306,6 +351,8 @@ void CachedResource::setResourceToRevalidate(CachedResource* resource) ASSERT(m_handlesToRevalidate.isEmpty()); ASSERT(resource->type() == type()); + LOG(ResourceLoading, "CachedResource %p setResourceToRevalidate %p", this, resource); + // The following assert should be investigated whenever it occurs. Although it should never fire, it currently does in rare circumstances. // https://bugs.webkit.org/show_bug.cgi?id=28604. // So the code needs to be robust to this assert failing thus the "if (m_resourceToRevalidate->m_proxyResource == this)" in CachedResource::clearResourceToRevalidate. @@ -334,6 +381,8 @@ void CachedResource::switchClientsToRevalidatedResource() ASSERT(m_resourceToRevalidate->inCache()); ASSERT(!inCache()); + LOG(ResourceLoading, "CachedResource %p switchClientsToRevalidatedResource %p", this, m_resourceToRevalidate); + HashSet<CachedResourceHandleBase*>::iterator end = m_handlesToRevalidate.end(); for (HashSet<CachedResourceHandleBase*>::iterator it = m_handlesToRevalidate.begin(); it != end; ++it) { CachedResourceHandleBase* handle = *it; @@ -383,7 +432,26 @@ void CachedResource::updateResponseAfterRevalidation(const ResourceResponse& val m_response.setHTTPHeaderField(it->first, it->second); } } - + +void CachedResource::registerHandle(CachedResourceHandleBase* h) +{ + ++m_handleCount; + if (m_resourceToRevalidate) + m_handlesToRevalidate.add(h); +} + +void CachedResource::unregisterHandle(CachedResourceHandleBase* h) +{ + ASSERT(m_handleCount > 0); + --m_handleCount; + + if (m_resourceToRevalidate) + m_handlesToRevalidate.remove(h); + + if (!m_handleCount) + deleteIfPossible(); +} + bool CachedResource::canUseCacheValidator() const { if (m_loading || m_errorOccurred) @@ -399,19 +467,33 @@ bool CachedResource::canUseCacheValidator() const bool CachedResource::mustRevalidate(CachePolicy cachePolicy) const { - if (m_errorOccurred) + if (m_errorOccurred) { + LOG(ResourceLoading, "CachedResource %p mustRevalidate because of m_errorOccurred\n", this); return true; + } if (m_loading) return false; - if (m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()) + if (m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()) { + LOG(ResourceLoading, "CachedResource %p mustRevalidate because of m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()\n", this); return true; + } + + if (cachePolicy == CachePolicyCache) { + if (m_response.cacheControlContainsMustRevalidate() && isExpired()) { + LOG(ResourceLoading, "CachedResource %p mustRevalidate because of cachePolicy == CachePolicyCache and m_response.cacheControlContainsMustRevalidate() && isExpired()\n", this); + return true; + } + return false; + } - if (cachePolicy == CachePolicyCache) - return m_response.cacheControlContainsMustRevalidate() && isExpired(); + if (isExpired()) { + LOG(ResourceLoading, "CachedResource %p mustRevalidate because of isExpired()\n", this); + return true; + } - return isExpired(); + return false; } bool CachedResource::isSafeToMakePurgeable() const @@ -435,17 +517,13 @@ bool CachedResource::makePurgeable(bool purgeable) if (!m_data->hasOneRef()) return false; - // Purgeable buffers are allocated in multiples of the page size (4KB in common CPUs) so it does not make sense for very small buffers. - const size_t purgeableThreshold = 4 * 4096; - if (m_data->size() < purgeableThreshold) - return false; - if (m_data->hasPurgeableBuffer()) { - m_purgeableData.set(m_data->releasePurgeableBuffer()); + m_purgeableData = m_data->releasePurgeableBuffer(); } else { - m_purgeableData.set(PurgeableBuffer::create(m_data->data(), m_data->size())); + m_purgeableData = PurgeableBuffer::create(m_data->data(), m_data->size()); if (!m_purgeableData) return false; + m_purgeableData->setPurgePriority(purgePriority()); } m_purgeableData->makePurgeable(true); diff --git a/WebCore/loader/CachedResource.h b/WebCore/loader/cache/CachedResource.h index 0f46a62..ba02459 100644 --- a/WebCore/loader/CachedResource.h +++ b/WebCore/loader/cache/CachedResource.h @@ -26,8 +26,8 @@ #include "CachePolicy.h" #include "FrameLoaderTypes.h" #include "PlatformString.h" +#include "PurgePriority.h" #include "ResourceResponse.h" -#include "SharedBuffer.h" #include <wtf/HashCountedSet.h> #include <wtf/HashSet.h> #include <wtf/OwnPtr.h> @@ -36,19 +36,21 @@ namespace WebCore { -class Cache; +class MemoryCache; +class CachedMetadata; class CachedResourceClient; class CachedResourceHandleBase; -class DocLoader; +class CachedResourceLoader; +class Frame; class InspectorResource; -class Request; class PurgeableBuffer; +class Request; // A resource that is held in the cache. Classes who want to use this object should derive // from CachedResourceClient, to get the function calls in case the requested data has arrived. // This class also does the actual communication with the loader to obtain the resource from the network. class CachedResource : public Noncopyable { - friend class Cache; + friend class MemoryCache; friend class InspectorResource; public: @@ -60,15 +62,13 @@ public: #if ENABLE(XSLT) , XSLStyleSheet #endif -#if ENABLE(XBL) - , XBL +#if ENABLE(LINK_PREFETCH) + , LinkPrefetch #endif }; enum Status { - NotCached, // this URL is not cached Unknown, // let cache decide what to do with it - New, // inserting new item Pending, // only partially loaded Cached // regular case }; @@ -76,17 +76,17 @@ public: CachedResource(const String& url, Type); virtual ~CachedResource(); - virtual void load(DocLoader* docLoader) { load(docLoader, false, DoSecurityCheck, true); } - void load(DocLoader*, bool incremental, SecurityCheckPolicy, bool sendResourceLoadCallbacks); + virtual void load(CachedResourceLoader* cachedResourceLoader) { load(cachedResourceLoader, false, DoSecurityCheck, true); } + void load(CachedResourceLoader*, bool incremental, SecurityCheckPolicy, bool sendResourceLoadCallbacks); virtual void setEncoding(const String&) { } virtual String encoding() const { return String(); } - virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived) = 0; - virtual void error() = 0; + virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived); + virtual void error() { } virtual void httpStatusCodeError() { error(); } // Images keep loading in spite of HTTP errors (for legacy compat with <img>, etc.). const String &url() const { return m_url; } - Type type() const { return m_type; } + Type type() const { return static_cast<Type>(m_type); } void addClient(CachedResourceClient*); void removeClient(CachedResourceClient*); @@ -99,25 +99,36 @@ public: PreloadReferencedWhileLoading, PreloadReferencedWhileComplete }; - PreloadResult preloadResult() const { return m_preloadResult; } + PreloadResult preloadResult() const { return static_cast<PreloadResult>(m_preloadResult); } void setRequestedFromNetworkingLayer() { m_requestedFromNetworkingLayer = true; } - virtual void didAddClient(CachedResourceClient*) = 0; + virtual void didAddClient(CachedResourceClient*); virtual void allClientsRemoved() { } unsigned count() const { return m_clients.size(); } - Status status() const { return m_status; } + Status status() const { return static_cast<Status>(m_status); } + void setStatus(Status status) { m_status = status; } unsigned size() const { return encodedSize() + decodedSize() + overheadSize(); } unsigned encodedSize() const { return m_encodedSize; } unsigned decodedSize() const { return m_decodedSize; } unsigned overheadSize() const; - bool isLoaded() const { return !m_loading; } + bool isLoaded() const { return !m_loading; } // FIXME. Method name is inaccurate. Loading might not have started yet. + + bool isLoading() const { return m_loading; } void setLoading(bool b) { m_loading = b; } virtual bool isImage() const { return false; } + bool isPrefetch() const + { +#if ENABLE(LINK_PREFETCH) + return type() == LinkPrefetch; +#else + return false; +#endif + } unsigned accessCount() const { return m_accessCount; } void increaseAccessCount() { m_accessCount++; } @@ -143,30 +154,41 @@ public: void setResponse(const ResourceResponse&); const ResourceResponse& response() const { return m_response; } + // Sets the serialized metadata retrieved from the platform's cache. + void setSerializedCachedMetadata(const char*, size_t); + + // Caches the given metadata in association with this resource and suggests + // that the platform persist it. The dataTypeID is a pseudo-randomly chosen + // identifier that is used to distinguish data generated by the caller. + void setCachedMetadata(unsigned dataTypeID, const char*, size_t); + + // Returns cached metadata of the given type associated with this resource. + CachedMetadata* cachedMetadata(unsigned dataTypeID) const; + bool canDelete() const { return !hasClients() && !m_request && !m_preloadCount && !m_handleCount && !m_resourceToRevalidate && !m_proxyResource; } bool isExpired() const; - virtual bool schedule() const { return false; } - // List of acceptable MIME types separated by ",". // A MIME type may contain a wildcard, e.g. "text/*". String accept() const { return m_accept; } void setAccept(const String& accept) { m_accept = accept; } bool errorOccurred() const { return m_errorOccurred; } + void setErrorOccurred(bool b) { m_errorOccurred = b; } + bool sendResourceLoadCallbacks() const { return m_sendResourceLoadCallbacks; } virtual void destroyDecodedData() { } - void setDocLoader(DocLoader* docLoader) { m_docLoader = docLoader; } + void setCachedResourceLoader(CachedResourceLoader* cachedResourceLoader) { m_cachedResourceLoader = cachedResourceLoader; } bool isPreloaded() const { return m_preloadCount; } void increasePreloadCount() { ++m_preloadCount; } void decreasePreloadCount() { ASSERT(m_preloadCount); --m_preloadCount; } - void registerHandle(CachedResourceHandleBase* h) { ++m_handleCount; if (m_resourceToRevalidate) m_handlesToRevalidate.add(h); } - void unregisterHandle(CachedResourceHandleBase* h) { ASSERT(m_handleCount > 0); --m_handleCount; if (m_resourceToRevalidate) m_handlesToRevalidate.remove(h); if (!m_handleCount) deleteIfPossible(); } + void registerHandle(CachedResourceHandleBase* h); + void unregisterHandle(CachedResourceHandleBase* h); bool canUseCacheValidator() const; bool mustRevalidate(CachePolicy) const; @@ -200,53 +222,55 @@ protected: RefPtr<SharedBuffer> m_data; OwnPtr<PurgeableBuffer> m_purgeableData; - Type m_type; - Status m_status; - - bool m_errorOccurred; - private: void addClientToSet(CachedResourceClient*); - // These are called by the friendly Cache only + // These are called by the friendly MemoryCache only void setResourceToRevalidate(CachedResource*); void switchClientsToRevalidatedResource(); void clearResourceToRevalidate(); void updateResponseAfterRevalidation(const ResourceResponse& validatingResponse); + virtual PurgePriority purgePriority() const { return PurgeDefault; } double currentAge() const; double freshnessLifetime() const; + RefPtr<CachedMetadata> m_cachedMetadata; + + double m_lastDecodedAccessTime; // Used as a "thrash guard" in the cache + unsigned m_encodedSize; unsigned m_decodedSize; unsigned m_accessCount; - unsigned m_inLiveDecodedResourcesList; - double m_lastDecodedAccessTime; // Used as a "thrash guard" in the cache - - bool m_sendResourceLoadCallbacks; - + unsigned m_handleCount; unsigned m_preloadCount; - PreloadResult m_preloadResult; - bool m_requestedFromNetworkingLayer; -protected: - bool m_inCache; - bool m_loading; + unsigned m_preloadResult : 2; // PreloadResult + + bool m_inLiveDecodedResourcesList : 1; + bool m_requestedFromNetworkingLayer : 1; + bool m_sendResourceLoadCallbacks : 1; + + bool m_errorOccurred : 1; + bool m_inCache : 1; + bool m_loading : 1; + + unsigned m_type : 3; // Type + unsigned m_status : 2; // Status + #ifndef NDEBUG bool m_deleted; unsigned m_lruIndex; #endif -private: CachedResource* m_nextInAllResourcesList; CachedResource* m_prevInAllResourcesList; CachedResource* m_nextInLiveResourcesList; CachedResource* m_prevInLiveResourcesList; - DocLoader* m_docLoader; // only non-0 for resources that are not in the cache + CachedResourceLoader* m_cachedResourceLoader; // only non-0 for resources that are not in the cache - unsigned m_handleCount; // If this field is non-null we are using the resource as a proxy for checking whether an existing resource is still up to date // using HTTP If-Modified-Since/If-None-Match headers. If the response is 304 all clients of this resource are moved // to to be clients of m_resourceToRevalidate and the resource is deleted. If not, the field is zeroed and this diff --git a/WebCore/loader/CachedResourceClient.h b/WebCore/loader/cache/CachedResourceClient.h index be3f87e..275d331 100644 --- a/WebCore/loader/CachedResourceClient.h +++ b/WebCore/loader/cache/CachedResourceClient.h @@ -26,12 +26,7 @@ #define CachedResourceClient_h #include <wtf/FastAllocBase.h> - -#if ENABLE(XBL) -namespace XBL { - class XBLDocument; -} -#endif +#include <wtf/Forward.h> namespace WebCore { @@ -39,7 +34,6 @@ namespace WebCore { class CachedFont; class CachedResource; class CachedImage; - class String; class Image; class IntRect; class KURL; @@ -68,13 +62,7 @@ namespace WebCore { virtual void setCSSStyleSheet(const String& /* href */, const KURL& /* baseURL */, const String& /* charset */, const CachedCSSStyleSheet*) { } virtual void setXSLStyleSheet(const String& /* href */, const KURL& /* baseURL */, const String& /* sheet */) { } - virtual void fontLoaded(CachedFont*) {}; - -#if ENABLE(XBL) - virtual void setXBLDocument(const String& /*URL*/, XBL::XBLDocument*) { } -#endif - virtual void notifyFinished(CachedResource*) { } }; diff --git a/WebCore/loader/CachedResourceClientWalker.cpp b/WebCore/loader/cache/CachedResourceClientWalker.cpp index 142a2a1..142a2a1 100644 --- a/WebCore/loader/CachedResourceClientWalker.cpp +++ b/WebCore/loader/cache/CachedResourceClientWalker.cpp diff --git a/WebCore/loader/CachedResourceClientWalker.h b/WebCore/loader/cache/CachedResourceClientWalker.h index d079584..d079584 100644 --- a/WebCore/loader/CachedResourceClientWalker.h +++ b/WebCore/loader/cache/CachedResourceClientWalker.h diff --git a/WebCore/loader/CachedResourceHandle.cpp b/WebCore/loader/cache/CachedResourceHandle.cpp index 871292c..871292c 100644 --- a/WebCore/loader/CachedResourceHandle.cpp +++ b/WebCore/loader/cache/CachedResourceHandle.cpp diff --git a/WebCore/loader/CachedResourceHandle.h b/WebCore/loader/cache/CachedResourceHandle.h index 7d485bf..7d485bf 100644 --- a/WebCore/loader/CachedResourceHandle.h +++ b/WebCore/loader/cache/CachedResourceHandle.h diff --git a/WebCore/loader/DocLoader.cpp b/WebCore/loader/cache/CachedResourceLoader.cpp index 8c0a1b2..3cca206 100644 --- a/WebCore/loader/DocLoader.cpp +++ b/WebCore/loader/cache/CachedResourceLoader.cpp @@ -25,33 +25,34 @@ */ #include "config.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" -#include "Cache.h" #include "CachedCSSStyleSheet.h" #include "CachedFont.h" #include "CachedImage.h" #include "CachedScript.h" #include "CachedXSLStyleSheet.h" #include "Console.h" -#include "CString.h" -#include "Document.h" #include "DOMWindow.h" -#include "HTMLElement.h" +#include "Document.h" #include "Frame.h" #include "FrameLoader.h" #include "FrameLoaderClient.h" -#include "loader.h" +#include "HTMLElement.h" +#include "MemoryCache.h" +#include "PingLoader.h" #include "SecurityOrigin.h" #include "Settings.h" +#include "loader.h" +#include <wtf/text/StringConcatenate.h> #define PRELOAD_DEBUG 0 namespace WebCore { -DocLoader::DocLoader(Document* doc) +CachedResourceLoader::CachedResourceLoader(Document* document) : m_cache(cache()) - , m_doc(doc) + , m_document(document) , m_requestCount(0) #ifdef ANDROID_BLOCK_NETWORK_IMAGE , m_blockNetworkImage(false) @@ -60,10 +61,10 @@ DocLoader::DocLoader(Document* doc) , m_loadInProgress(false) , m_allowStaleResources(false) { - m_cache->addDocLoader(this); + m_cache->addCachedResourceLoader(this); } -DocLoader::~DocLoader() +CachedResourceLoader::~CachedResourceLoader() { if (m_requestCount) m_cache->loader()->cancelRequests(this); @@ -71,19 +72,19 @@ DocLoader::~DocLoader() clearPreloads(); DocumentResourceMap::iterator end = m_documentResources.end(); for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) - it->second->setDocLoader(0); - m_cache->removeDocLoader(this); + it->second->setCachedResourceLoader(0); + m_cache->removeCachedResourceLoader(this); - // Make sure no requests still point to this DocLoader + // Make sure no requests still point to this CachedResourceLoader ASSERT(m_requestCount == 0); } -Frame* DocLoader::frame() const +Frame* CachedResourceLoader::frame() const { - return m_doc->frame(); + return m_document->frame(); } -void DocLoader::checkForReload(const KURL& fullURL) +void CachedResourceLoader::checkForReload(const KURL& fullURL) { if (m_allowStaleResources) return; // Don't reload resources while pasting @@ -122,12 +123,19 @@ void DocLoader::checkForReload(const KURL& fullURL) m_reloadedURLs.add(fullURL.string()); } -CachedImage* DocLoader::requestImage(const String& url) +CachedImage* CachedResourceLoader::requestImage(const String& url) { if (Frame* f = frame()) { Settings* settings = f->settings(); if (!f->loader()->client()->allowImages(!settings || settings->areImagesEnabled())) return 0; + + if (f->loader()->pageDismissalEventBeingDispatched()) { + KURL completeURL = m_document->completeURL(url); + if (completeURL.isValid() && canRequest(CachedResource::ImageResource, completeURL)) + PingLoader::loadImage(f, completeURL); + return 0; + } } CachedImage* resource = static_cast<CachedImage*>(requestResource(CachedResource::ImageResource, url, String())); if (autoLoadImages() && resource && resource->stillNeedsLoad()) { @@ -142,41 +150,42 @@ CachedImage* DocLoader::requestImage(const String& url) return resource; } -CachedFont* DocLoader::requestFont(const String& url) +CachedFont* CachedResourceLoader::requestFont(const String& url) { return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, url, String())); } -CachedCSSStyleSheet* DocLoader::requestCSSStyleSheet(const String& url, const String& charset) +CachedCSSStyleSheet* CachedResourceLoader::requestCSSStyleSheet(const String& url, const String& charset) { return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, url, charset)); } -CachedCSSStyleSheet* DocLoader::requestUserCSSStyleSheet(const String& url, const String& charset) +CachedCSSStyleSheet* CachedResourceLoader::requestUserCSSStyleSheet(const String& url, const String& charset) { return cache()->requestUserCSSStyleSheet(this, url, charset); } -CachedScript* DocLoader::requestScript(const String& url, const String& charset) +CachedScript* CachedResourceLoader::requestScript(const String& url, const String& charset) { return static_cast<CachedScript*>(requestResource(CachedResource::Script, url, charset)); } #if ENABLE(XSLT) -CachedXSLStyleSheet* DocLoader::requestXSLStyleSheet(const String& url) +CachedXSLStyleSheet* CachedResourceLoader::requestXSLStyleSheet(const String& url) { return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, url, String())); } #endif -#if ENABLE(XBL) -CachedXBLDocument* DocLoader::requestXBLDocument(const String& url) +#if ENABLE(LINK_PREFETCH) +CachedResource* CachedResourceLoader::requestLinkPrefetch(const String& url) { - return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XBL, url, String())); + ASSERT(frame()); + return requestResource(CachedResource::LinkPrefetch, url, String()); } #endif -bool DocLoader::canRequest(CachedResource::Type type, const KURL& url) +bool CachedResourceLoader::canRequest(CachedResource::Type type, const KURL& url) { // Some types of resources can be loaded only from the same origin. Other // types of resources, like Images, Scripts, and CSS, can be loaded from @@ -186,17 +195,15 @@ bool DocLoader::canRequest(CachedResource::Type type, const KURL& url) case CachedResource::CSSStyleSheet: case CachedResource::Script: case CachedResource::FontResource: +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkPrefetch: +#endif // These types of resources can be loaded from any origin. // FIXME: Are we sure about CachedResource::FontResource? break; #if ENABLE(XSLT) case CachedResource::XSLStyleSheet: -#endif -#if ENABLE(XBL) - case CachedResource::XBL: -#endif -#if ENABLE(XSLT) || ENABLE(XBL) - if (!m_doc->securityOrigin()->canRequest(url)) { + if (!m_document->securityOrigin()->canRequest(url)) { printAccessDeniedMessage(url); return false; } @@ -219,12 +226,9 @@ bool DocLoader::canRequest(CachedResource::Type type, const KURL& url) #if ENABLE(XSLT) case CachedResource::XSLStyleSheet: #endif -#if ENABLE(XBL) - case CachedResource::XBL: -#endif // These resource can inject script into the current document. if (Frame* f = frame()) - f->loader()->checkIfRunInsecureContent(m_doc->securityOrigin(), url); + f->loader()->checkIfRunInsecureContent(m_document->securityOrigin(), url); break; case CachedResource::ImageResource: case CachedResource::CSSStyleSheet: @@ -236,6 +240,11 @@ bool DocLoader::canRequest(CachedResource::Type type, const KURL& url) } break; } +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkPrefetch: + // Prefetch cannot affect the current document. + break; +#endif default: ASSERT_NOT_REACHED(); break; @@ -244,9 +253,9 @@ bool DocLoader::canRequest(CachedResource::Type type, const KURL& url) return true; } -CachedResource* DocLoader::requestResource(CachedResource::Type type, const String& url, const String& charset, bool isPreload) +CachedResource* CachedResourceLoader::requestResource(CachedResource::Type type, const String& url, const String& charset, bool isPreload) { - KURL fullURL = m_doc->completeURL(url); + KURL fullURL = m_document->completeURL(url); if (!fullURL.isValid() || !canRequest(type, fullURL)) return 0; @@ -255,7 +264,7 @@ CachedResource* DocLoader::requestResource(CachedResource::Type type, const Stri DocumentResourceMap::iterator it = m_documentResources.find(fullURL.string()); if (it != m_documentResources.end()) { - it->second->setDocLoader(0); + it->second->setCachedResourceLoader(0); m_documentResources.remove(it); } } @@ -275,7 +284,7 @@ CachedResource* DocLoader::requestResource(CachedResource::Type type, const Stri return resource; } -void DocLoader::printAccessDeniedMessage(const KURL& url) const +void CachedResourceLoader::printAccessDeniedMessage(const KURL& url) const { if (url.isNull()) return; @@ -287,19 +296,15 @@ void DocLoader::printAccessDeniedMessage(const KURL& url) const if (!settings || settings->privateBrowsingEnabled()) return; - String message = m_doc->url().isNull() ? - String::format("Unsafe attempt to load URL %s.", - url.string().utf8().data()) : - String::format("Unsafe attempt to load URL %s from frame with URL %s. " - "Domains, protocols and ports must match.\n", - url.string().utf8().data(), - m_doc->url().string().utf8().data()); + String message = m_document->url().isNull() ? + makeString("Unsafe attempt to load URL ", url.string(), '.') : + makeString("Unsafe attempt to load URL ", url.string(), " from frame with URL ", m_document->url().string(), ". Domains, protocols and ports must match.\n"); // FIXME: provide a real line number and source URL. frame()->domWindow()->console()->addMessage(OtherMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String()); } -void DocLoader::setAutoLoadImages(bool enable) +void CachedResourceLoader::setAutoLoadImages(bool enable) { if (enable == m_autoLoadImages) return; @@ -326,19 +331,19 @@ void DocLoader::setAutoLoadImages(bool enable) } #ifdef ANDROID_BLOCK_NETWORK_IMAGE -bool DocLoader::shouldBlockNetworkImage(const String& url) const +bool CachedResourceLoader::shouldBlockNetworkImage(const String& url) const { if (!m_blockNetworkImage) return false; - KURL kurl = m_doc->completeURL(url); + KURL kurl = m_document->completeURL(url); if (kurl.protocolIs("http") || kurl.protocolIs("https")) return true; return false; } -void DocLoader::setBlockNetworkImage(bool block) +void CachedResourceLoader::setBlockNetworkImage(bool block) { if (block == m_blockNetworkImage) return; @@ -360,12 +365,12 @@ void DocLoader::setBlockNetworkImage(bool block) } #endif -CachePolicy DocLoader::cachePolicy() const +CachePolicy CachedResourceLoader::cachePolicy() const { return frame() ? frame()->loader()->subresourceCachePolicy() : CachePolicyVerify; } -void DocLoader::removeCachedResource(CachedResource* resource) const +void CachedResourceLoader::removeCachedResource(CachedResource* resource) const { #ifndef NDEBUG DocumentResourceMap::iterator it = m_documentResources.find(resource->url()); @@ -375,54 +380,50 @@ void DocLoader::removeCachedResource(CachedResource* resource) const m_documentResources.remove(resource->url()); } -void DocLoader::setLoadInProgress(bool load) +void CachedResourceLoader::setLoadInProgress(bool load) { m_loadInProgress = load; if (!load && frame()) frame()->loader()->loadDone(); } -void DocLoader::checkCacheObjectStatus(CachedResource* resource) +void CachedResourceLoader::checkCacheObjectStatus(CachedResource* resource) { // Return from the function for objects that we didn't load from the cache or if we don't have a frame. - if (!resource || !frame()) + if (!resource || !frame() || resource->status() != CachedResource::Cached) return; - switch (resource->status()) { - case CachedResource::Cached: - break; - case CachedResource::NotCached: - case CachedResource::Unknown: - case CachedResource::New: - case CachedResource::Pending: - return; - } - // FIXME: If the WebKit client changes or cancels the request, WebCore does not respect this and continues the load. frame()->loader()->loadedResourceFromMemoryCache(resource); } -void DocLoader::incrementRequestCount() +void CachedResourceLoader::incrementRequestCount(const CachedResource* res) { + if (res->isPrefetch()) + return; + ++m_requestCount; } -void DocLoader::decrementRequestCount() +void CachedResourceLoader::decrementRequestCount(const CachedResource* res) { + if (res->isPrefetch()) + return; + --m_requestCount; ASSERT(m_requestCount > -1); } -int DocLoader::requestCount() +int CachedResourceLoader::requestCount() { if (loadInProgress()) return m_requestCount + 1; return m_requestCount; } -void DocLoader::preload(CachedResource::Type type, const String& url, const String& charset, bool referencedFromBody) +void CachedResourceLoader::preload(CachedResource::Type type, const String& url, const String& charset, bool referencedFromBody) { - bool hasRendering = m_doc->body() && m_doc->body()->renderer(); + bool hasRendering = m_document->body() && m_document->body()->renderer(); if (!hasRendering && (referencedFromBody || type == CachedResource::ImageResource)) { // Don't preload images or body resources before we have something to draw. This prevents // preloads from body delaying first display when bandwidth is limited. @@ -433,43 +434,50 @@ void DocLoader::preload(CachedResource::Type type, const String& url, const Stri requestPreload(type, url, charset); } -void DocLoader::checkForPendingPreloads() +void CachedResourceLoader::checkForPendingPreloads() { unsigned count = m_pendingPreloads.size(); - if (!count || !m_doc->body() || !m_doc->body()->renderer()) + if (!count || !m_document->body() || !m_document->body()->renderer()) return; for (unsigned i = 0; i < count; ++i) { PendingPreload& preload = m_pendingPreloads[i]; // Don't request preload if the resource already loaded normally (this will result in double load if the page is being reloaded with cached results ignored). - if (!cachedResource(m_doc->completeURL(preload.m_url))) + if (!cachedResource(m_document->completeURL(preload.m_url))) requestPreload(preload.m_type, preload.m_url, preload.m_charset); } m_pendingPreloads.clear(); } -void DocLoader::requestPreload(CachedResource::Type type, const String& url, const String& charset) +void CachedResourceLoader::requestPreload(CachedResource::Type type, const String& url, const String& charset) { String encoding; if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet) - encoding = charset.isEmpty() ? m_doc->frame()->loader()->encoding() : charset; + encoding = charset.isEmpty() ? m_document->frame()->loader()->writer()->encoding() : charset; CachedResource* resource = requestResource(type, url, encoding, true); - if (!resource || m_preloads.contains(resource)) + if (!resource || (m_preloads && m_preloads->contains(resource))) return; resource->increasePreloadCount(); - m_preloads.add(resource); + + if (!m_preloads) + m_preloads = adoptPtr(new ListHashSet<CachedResource*>); + m_preloads->add(resource); + #if PRELOAD_DEBUG printf("PRELOADING %s\n", resource->url().latin1().data()); #endif } -void DocLoader::clearPreloads() +void CachedResourceLoader::clearPreloads() { #if PRELOAD_DEBUG printPreloadStats(); #endif - ListHashSet<CachedResource*>::iterator end = m_preloads.end(); - for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) { + if (!m_preloads) + return; + + ListHashSet<CachedResource*>::iterator end = m_preloads->end(); + for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) { CachedResource* res = *it; res->decreasePreloadCount(); if (res->canDelete() && !res->inCache()) @@ -480,13 +488,13 @@ void DocLoader::clearPreloads() m_preloads.clear(); } -void DocLoader::clearPendingPreloads() +void CachedResourceLoader::clearPendingPreloads() { m_pendingPreloads.clear(); } #if PRELOAD_DEBUG -void DocLoader::printPreloadStats() +void CachedResourceLoader::printPreloadStats() { unsigned scripts = 0; unsigned scriptMisses = 0; diff --git a/WebCore/loader/DocLoader.h b/WebCore/loader/cache/CachedResourceLoader.h index 06d8a47..eaed52e 100644 --- a/WebCore/loader/DocLoader.h +++ b/WebCore/loader/cache/CachedResourceLoader.h @@ -23,16 +23,16 @@ pages from the web. It has a memory cache for these objects. */ -#ifndef DocLoader_h -#define DocLoader_h +#ifndef CachedResourceLoader_h +#define CachedResourceLoader_h #include "CachedResource.h" #include "CachedResourceHandle.h" #include "CachePolicy.h" -#include "StringHash.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/ListHashSet.h> +#include <wtf/text/StringHash.h> namespace WebCore { @@ -46,15 +46,14 @@ class Frame; class ImageLoader; class KURL; -// The DocLoader manages the loading of scripts/images/stylesheets for a single document. -class DocLoader : public Noncopyable -{ -friend class Cache; +// The CachedResourceLoader manages the loading of scripts/images/stylesheets for a single document. +class CachedResourceLoader : public Noncopyable { +friend class MemoryCache; friend class ImageLoader; public: - DocLoader(Document*); - ~DocLoader(); + CachedResourceLoader(Document*); + ~CachedResourceLoader(); CachedImage* requestImage(const String& url); CachedCSSStyleSheet* requestCSSStyleSheet(const String& url, const String& charset); @@ -65,8 +64,8 @@ public: #if ENABLE(XSLT) CachedXSLStyleSheet* requestXSLStyleSheet(const String& url); #endif -#if ENABLE(XBL) - CachedXBLDocument* requestXBLDocument(const String &url); +#if ENABLE(LINK_PREFETCH) + CachedResource* requestLinkPrefetch(const String &url); #endif // Logs an access denied message to the console for the specified URL. @@ -89,7 +88,7 @@ public: CachePolicy cachePolicy() const; Frame* frame() const; // Can be NULL - Document* doc() const { return m_doc; } + Document* document() const { return m_document; } void removeCachedResource(CachedResource*) const; @@ -98,8 +97,8 @@ public: void setAllowStaleResources(bool allowStaleResources) { m_allowStaleResources = allowStaleResources; } - void incrementRequestCount(); - void decrementRequestCount(); + void incrementRequestCount(const CachedResource*); + void decrementRequestCount(const CachedResource*); int requestCount(); void clearPreloads(); @@ -116,14 +115,14 @@ private: void checkCacheObjectStatus(CachedResource*); bool canRequest(CachedResource::Type, const KURL&); - Cache* m_cache; + MemoryCache* m_cache; HashSet<String> m_reloadedURLs; mutable DocumentResourceMap m_documentResources; - Document* m_doc; + Document* m_document; int m_requestCount; - ListHashSet<CachedResource*> m_preloads; + OwnPtr<ListHashSet<CachedResource*> > m_preloads; struct PendingPreload { CachedResource::Type m_type; String m_url; diff --git a/WebCore/loader/CachedScript.cpp b/WebCore/loader/cache/CachedScript.cpp index 31483d6..50a8c17 100644 --- a/WebCore/loader/CachedScript.cpp +++ b/WebCore/loader/cache/CachedScript.cpp @@ -27,8 +27,10 @@ #include "config.h" #include "CachedScript.h" +#include "MemoryCache.h" #include "CachedResourceClient.h" #include "CachedResourceClientWalker.h" +#include "SharedBuffer.h" #include "TextResourceDecoder.h" #include <wtf/Vector.h> @@ -49,12 +51,6 @@ CachedScript::~CachedScript() { } -void CachedScript::didAddClient(CachedResourceClient* c) -{ - if (!m_loading) - c->notifyFinished(this); -} - void CachedScript::allClientsRemoved() { m_decodedDataDeletionTimer.startOneShot(0); @@ -79,7 +75,6 @@ const String& CachedScript::script() m_script += m_decoder->flush(); setDecodedSize(m_script.length() * sizeof(UChar)); } - m_decodedDataDeletionTimer.startOneShot(0); return m_script; } @@ -91,13 +86,13 @@ void CachedScript::data(PassRefPtr<SharedBuffer> data, bool allDataReceived) m_data = data; setEncodedSize(m_data.get() ? m_data->size() : 0); - m_loading = false; + setLoading(false); checkNotify(); } void CachedScript::checkNotify() { - if (m_loading) + if (isLoading()) return; CachedResourceClientWalker w(m_clients); @@ -107,8 +102,8 @@ void CachedScript::checkNotify() void CachedScript::error() { - m_loading = false; - m_errorOccurred = true; + setLoading(false); + setErrorOccurred(true); checkNotify(); } @@ -116,7 +111,7 @@ void CachedScript::destroyDecodedData() { m_script = String(); setDecodedSize(0); - if (isSafeToMakePurgeable()) + if (!MemoryCache::shouldMakeResourcePurgeableOnEviction() && isSafeToMakePurgeable()) makePurgeable(true); } diff --git a/WebCore/loader/CachedScript.h b/WebCore/loader/cache/CachedScript.h index 13afa89..7311f9b 100644 --- a/WebCore/loader/CachedScript.h +++ b/WebCore/loader/cache/CachedScript.h @@ -31,7 +31,7 @@ namespace WebCore { - class DocLoader; + class CachedResourceLoader; class TextResourceDecoder; class CachedScript : public CachedResource { @@ -41,7 +41,6 @@ namespace WebCore { const String& script(); - virtual void didAddClient(CachedResourceClient*); virtual void allClientsRemoved(); virtual void setEncoding(const String&); @@ -49,14 +48,13 @@ namespace WebCore { virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived); virtual void error(); - virtual bool schedule() const { return false; } - void checkNotify(); virtual void destroyDecodedData(); private: void decodedDataDeletionTimerFired(Timer<CachedScript>*); + virtual PurgePriority purgePriority() const { return PurgeLast; } String m_script; RefPtr<TextResourceDecoder> m_decoder; diff --git a/WebCore/loader/CachedXSLStyleSheet.cpp b/WebCore/loader/cache/CachedXSLStyleSheet.cpp index 59c3907..5b30e30 100644 --- a/WebCore/loader/CachedXSLStyleSheet.cpp +++ b/WebCore/loader/cache/CachedXSLStyleSheet.cpp @@ -29,6 +29,7 @@ #include "CachedResourceClient.h" #include "CachedResourceClientWalker.h" +#include "SharedBuffer.h" #include "TextResourceDecoder.h" #include <wtf/Vector.h> @@ -47,7 +48,7 @@ CachedXSLStyleSheet::CachedXSLStyleSheet(const String &url) void CachedXSLStyleSheet::didAddClient(CachedResourceClient* c) { - if (!m_loading) + if (!isLoading()) c->setXSLStyleSheet(m_url, m_response.url(), m_sheet); } @@ -72,13 +73,13 @@ void CachedXSLStyleSheet::data(PassRefPtr<SharedBuffer> data, bool allDataReceiv m_sheet = String(m_decoder->decode(m_data->data(), encodedSize())); m_sheet += m_decoder->flush(); } - m_loading = false; + setLoading(false); checkNotify(); } void CachedXSLStyleSheet::checkNotify() { - if (m_loading) + if (isLoading()) return; CachedResourceClientWalker w(m_clients); @@ -88,8 +89,8 @@ void CachedXSLStyleSheet::checkNotify() void CachedXSLStyleSheet::error() { - m_loading = false; - m_errorOccurred = true; + setLoading(false); + setErrorOccurred(true); checkNotify(); } diff --git a/WebCore/loader/CachedXSLStyleSheet.h b/WebCore/loader/cache/CachedXSLStyleSheet.h index b6b0585..8587b0b 100644 --- a/WebCore/loader/CachedXSLStyleSheet.h +++ b/WebCore/loader/cache/CachedXSLStyleSheet.h @@ -31,7 +31,7 @@ namespace WebCore { - class DocLoader; + class CachedResourceLoader; class TextResourceDecoder; #if ENABLE(XSLT) @@ -48,8 +48,6 @@ namespace WebCore { virtual void data(PassRefPtr<SharedBuffer> data, bool allDataReceived); virtual void error(); - virtual bool schedule() const { return true; } - void checkNotify(); protected: diff --git a/WebCore/loader/Cache.cpp b/WebCore/loader/cache/MemoryCache.cpp index fdd9b25..25af774 100644 --- a/WebCore/loader/Cache.cpp +++ b/WebCore/loader/cache/MemoryCache.cpp @@ -21,19 +21,20 @@ */ #include "config.h" -#include "Cache.h" +#include "MemoryCache.h" #include "CachedCSSStyleSheet.h" #include "CachedFont.h" #include "CachedImage.h" #include "CachedScript.h" #include "CachedXSLStyleSheet.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "Document.h" #include "FrameLoader.h" #include "FrameLoaderTypes.h" #include "FrameView.h" #include "Image.h" +#include "Logging.h" #include "ResourceHandle.h" #include "SecurityOrigin.h" #include <stdio.h> @@ -48,13 +49,13 @@ static const double cMinDelayBeforeLiveDecodedPrune = 1; // Seconds. static const float cTargetPrunePercentage = .95f; // Percentage of capacity toward which we prune, to avoid immediately pruning again. static const double cDefaultDecodedDataDeletionInterval = 0; -Cache* cache() +MemoryCache* cache() { - static Cache* staticCache = new Cache; + static MemoryCache* staticCache = new MemoryCache; return staticCache; } -Cache::Cache() +MemoryCache::MemoryCache() : m_disabled(false) , m_pruneEnabled(true) , m_inPruneDeadResources(false) @@ -82,9 +83,9 @@ static CachedResource* createResource(CachedResource::Type type, const KURL& url case CachedResource::XSLStyleSheet: return new CachedXSLStyleSheet(url.string()); #endif -#if ENABLE(XBL) - case CachedResource::XBLStyleSheet: - return new CachedXBLDocument(url.string()); +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkPrefetch: + return new CachedResource(url.string(), CachedResource::LinkPrefetch); #endif default: break; @@ -93,27 +94,32 @@ static CachedResource* createResource(CachedResource::Type type, const KURL& url return 0; } -CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Type type, const KURL& url, const String& charset, bool requestIsPreload) +CachedResource* MemoryCache::requestResource(CachedResourceLoader* cachedResourceLoader, CachedResource::Type type, const KURL& url, const String& charset, bool requestIsPreload) { + LOG(ResourceLoading, "MemoryCache::requestResource '%s', charset '%s', preload=%u", url.string().latin1().data(), charset.latin1().data(), requestIsPreload); + // FIXME: Do we really need to special-case an empty URL? // Would it be better to just go on with the cache code and let it fail later? if (url.isEmpty()) return 0; - + // Look up the resource in our map. CachedResource* resource = resourceForURL(url.string()); - - if (resource && requestIsPreload && !resource->isPreloaded()) + + if (resource && requestIsPreload && !resource->isPreloaded()) { + LOG(ResourceLoading, "MemoryCache::requestResource already has a preload request for this request, and it hasn't been preloaded yet"); return 0; - - if (SecurityOrigin::restrictAccessToLocal() && !SecurityOrigin::canLoad(url, String(), docLoader->doc())) { - Document* doc = docLoader->doc(); - if (doc && !requestIsPreload) - FrameLoader::reportLocalLoadFailed(doc->frame(), url.string()); + } + + if (!cachedResourceLoader->document()->securityOrigin()->canDisplay(url)) { + LOG(ResourceLoading, "...URL was not allowed by SecurityOrigin"); + if (!requestIsPreload) + FrameLoader::reportLocalLoadFailed(cachedResourceLoader->document()->frame(), url.string()); return 0; } if (!resource) { + LOG(ResourceLoading, "CachedResource for '%s' wasn't found in cache. Creating it", url.string().latin1().data()); // The resource does not exist. Create it. resource = createResource(type, url, charset); ASSERT(resource); @@ -122,7 +128,7 @@ CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Typ // FIXME: CachedResource should just use normal refcounting instead. resource->setInCache(true); - resource->load(docLoader); + resource->load(cachedResourceLoader); if (resource->errorOccurred()) { // We don't support immediate loads, but we do support immediate failure. @@ -138,22 +144,26 @@ CachedResource* Cache::requestResource(DocLoader* docLoader, CachedResource::Typ else { // Kick the resource out of the cache, because the cache is disabled. resource->setInCache(false); - resource->setDocLoader(docLoader); + resource->setCachedResourceLoader(cachedResourceLoader); } } - if (resource->type() != type) + if (resource->type() != type) { + LOG(ResourceLoading, "MemoryCache::requestResource cannot use cached resource for '%s' due to type mismatch", url.string().latin1().data()); return 0; + } if (!disabled()) { // This will move the resource to the front of its LRU list and increase its access count. resourceAccessed(resource); } + LOG(ResourceLoading, "MemoryCache::requestResource for '%s' returning resource %p\n", url.string().latin1().data(), resource); + return resource; } -CachedCSSStyleSheet* Cache::requestUserCSSStyleSheet(DocLoader* docLoader, const String& url, const String& charset) +CachedCSSStyleSheet* MemoryCache::requestUserCSSStyleSheet(CachedResourceLoader* cachedResourceLoader, const String& url, const String& charset) { CachedCSSStyleSheet* userSheet; if (CachedResource* existing = resourceForURL(url)) { @@ -167,7 +177,7 @@ CachedCSSStyleSheet* Cache::requestUserCSSStyleSheet(DocLoader* docLoader, const // FIXME: CachedResource should just use normal refcounting instead. userSheet->setInCache(true); // Don't load incrementally, skip load checks, don't send resource load callbacks. - userSheet->load(docLoader, false, SkipSecurityCheck, false); + userSheet->load(cachedResourceLoader, false, SkipSecurityCheck, false); if (!disabled()) m_resources.set(url, userSheet); else @@ -182,7 +192,7 @@ CachedCSSStyleSheet* Cache::requestUserCSSStyleSheet(DocLoader* docLoader, const return userSheet; } -void Cache::revalidateResource(CachedResource* resource, DocLoader* docLoader) +void MemoryCache::revalidateResource(CachedResource* resource, CachedResourceLoader* cachedResourceLoader) { ASSERT(resource); ASSERT(resource->inCache()); @@ -196,15 +206,16 @@ void Cache::revalidateResource(CachedResource* resource, DocLoader* docLoader) } const String& url = resource->url(); CachedResource* newResource = createResource(resource->type(), KURL(ParsedURLString, url), resource->encoding()); + LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource, resource); newResource->setResourceToRevalidate(resource); evict(resource); m_resources.set(url, newResource); newResource->setInCache(true); resourceAccessed(newResource); - newResource->load(docLoader); + newResource->load(cachedResourceLoader); } -void Cache::revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse& response) +void MemoryCache::revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse& response) { CachedResource* resource = revalidatingResource->resourceToRevalidate(); ASSERT(resource); @@ -230,24 +241,29 @@ void Cache::revalidationSucceeded(CachedResource* revalidatingResource, const Re revalidatingResource->clearResourceToRevalidate(); } -void Cache::revalidationFailed(CachedResource* revalidatingResource) +void MemoryCache::revalidationFailed(CachedResource* revalidatingResource) { + LOG(ResourceLoading, "Revalidation failed for %p", revalidatingResource); ASSERT(revalidatingResource->resourceToRevalidate()); revalidatingResource->clearResourceToRevalidate(); } -CachedResource* Cache::resourceForURL(const String& url) +CachedResource* MemoryCache::resourceForURL(const String& url) { CachedResource* resource = m_resources.get(url); + bool wasPurgeable = MemoryCache::shouldMakeResourcePurgeableOnEviction() && resource && resource->isPurgeable(); if (resource && !resource->makePurgeable(false)) { ASSERT(!resource->hasClients()); evict(resource); return 0; } + // Add the size back since we had subtracted it when we marked the memory as purgeable. + if (wasPurgeable) + adjustSize(resource->hasClients(), resource->size()); return resource; } -unsigned Cache::deadCapacity() const +unsigned MemoryCache::deadCapacity() const { // Dead resource capacity is whatever space is not occupied by live resources, bounded by an independent minimum and maximum. unsigned capacity = m_capacity - min(m_liveSize, m_capacity); // Start with available capacity. @@ -256,13 +272,13 @@ unsigned Cache::deadCapacity() const return capacity; } -unsigned Cache::liveCapacity() const +unsigned MemoryCache::liveCapacity() const { // Live resource capacity is whatever is left over after calculating dead resource capacity. return m_capacity - deadCapacity(); } -void Cache::pruneLiveResources() +void MemoryCache::pruneLiveResources() { if (!m_pruneEnabled) return; @@ -306,7 +322,7 @@ void Cache::pruneLiveResources() } } -void Cache::pruneDeadResources() +void MemoryCache::pruneDeadResources() { if (!m_pruneEnabled) return; @@ -364,7 +380,9 @@ void Cache::pruneDeadResources() while (current) { CachedResource* prev = current->m_prevInAllResourcesList; if (!current->hasClients() && !current->isPreloaded() && !current->isCacheValidator()) { - evict(current); + if (!makeResourcePurgeable(current)) + evict(current); + // If evict() caused pruneDeadResources() to be re-entered, bail out. This can happen when removing an // SVG CachedImage that has subresources. if (!m_inPruneDeadResources) @@ -388,7 +406,7 @@ void Cache::pruneDeadResources() m_inPruneDeadResources = false; } -void Cache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes) +void MemoryCache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned totalBytes) { ASSERT(minDeadBytes <= maxDeadBytes); ASSERT(maxDeadBytes <= totalBytes); @@ -398,8 +416,31 @@ void Cache::setCapacities(unsigned minDeadBytes, unsigned maxDeadBytes, unsigned prune(); } -void Cache::evict(CachedResource* resource) +bool MemoryCache::makeResourcePurgeable(CachedResource* resource) { + if (!MemoryCache::shouldMakeResourcePurgeableOnEviction()) + return false; + + if (!resource->inCache()) + return false; + + if (resource->isPurgeable()) + return true; + + if (!resource->isSafeToMakePurgeable()) + return false; + + if (!resource->makePurgeable(true)) + return false; + + adjustSize(resource->hasClients(), -static_cast<int>(resource->size())); + + return true; +} + +void MemoryCache::evict(CachedResource* resource) +{ + LOG(ResourceLoading, "Evicting resource %p for '%s' from cache", resource, resource->url().latin1().data()); // The resource may have already been removed by someone other than our caller, // who needed a fresh copy for a reload. See <http://bugs.webkit.org/show_bug.cgi?id=12479#c6>. if (resource->inCache()) { @@ -411,10 +452,11 @@ void Cache::evict(CachedResource* resource) removeFromLRUList(resource); removeFromLiveDecodedResourcesList(resource); - // Subtract from our size totals. - int delta = -static_cast<int>(resource->size()); - if (delta) - adjustSize(resource->hasClients(), delta); + // If the resource was purged, it means we had already decremented the size when we made the + // resource purgeable in makeResourcePurgeable(). So adjust the size if we are evicting a + // resource that was not marked as purgeable. + if (!MemoryCache::shouldMakeResourcePurgeableOnEviction() || !resource->isPurgeable()) + adjustSize(resource->hasClients(), -static_cast<int>(resource->size())); } else ASSERT(m_resources.get(resource->url()) != resource); @@ -422,14 +464,14 @@ void Cache::evict(CachedResource* resource) delete resource; } -void Cache::addDocLoader(DocLoader* docLoader) +void MemoryCache::addCachedResourceLoader(CachedResourceLoader* cachedResourceLoader) { - m_docLoaders.add(docLoader); + m_cachedResourceLoaders.add(cachedResourceLoader); } -void Cache::removeDocLoader(DocLoader* docLoader) +void MemoryCache::removeCachedResourceLoader(CachedResourceLoader* cachedResourceLoader) { - m_docLoaders.remove(docLoader); + m_cachedResourceLoaders.remove(cachedResourceLoader); } static inline unsigned fastLog2(unsigned i) @@ -450,7 +492,7 @@ static inline unsigned fastLog2(unsigned i) return log2; } -Cache::LRUList* Cache::lruListFor(CachedResource* resource) +MemoryCache::LRUList* MemoryCache::lruListFor(CachedResource* resource) { unsigned accessCount = max(resource->accessCount(), 1U); unsigned queueIndex = fastLog2(resource->size() / accessCount); @@ -462,7 +504,7 @@ Cache::LRUList* Cache::lruListFor(CachedResource* resource) return &m_allResources[queueIndex]; } -void Cache::removeFromLRUList(CachedResource* resource) +void MemoryCache::removeFromLRUList(CachedResource* resource) { // If we've never been accessed, then we're brand new and not in any list. if (resource->accessCount() == 0) @@ -509,7 +551,7 @@ void Cache::removeFromLRUList(CachedResource* resource) list->m_head = next; } -void Cache::insertInLRUList(CachedResource* resource) +void MemoryCache::insertInLRUList(CachedResource* resource) { // Make sure we aren't in some list already. ASSERT(!resource->m_nextInAllResourcesList && !resource->m_prevInAllResourcesList); @@ -541,7 +583,7 @@ void Cache::insertInLRUList(CachedResource* resource) } -void Cache::resourceAccessed(CachedResource* resource) +void MemoryCache::resourceAccessed(CachedResource* resource) { ASSERT(resource->inCache()); @@ -560,7 +602,7 @@ void Cache::resourceAccessed(CachedResource* resource) insertInLRUList(resource); } -void Cache::removeFromLiveDecodedResourcesList(CachedResource* resource) +void MemoryCache::removeFromLiveDecodedResourcesList(CachedResource* resource) { // If we've never been accessed, then we're brand new and not in any list. if (!resource->m_inLiveDecodedResourcesList) @@ -599,7 +641,7 @@ void Cache::removeFromLiveDecodedResourcesList(CachedResource* resource) m_liveDecodedResources.m_head = next; } -void Cache::insertInLiveDecodedResourcesList(CachedResource* resource) +void MemoryCache::insertInLiveDecodedResourcesList(CachedResource* resource) { // Make sure we aren't in the list already. ASSERT(!resource->m_nextInLiveResourcesList && !resource->m_prevInLiveResourcesList && !resource->m_inLiveDecodedResourcesList); @@ -627,19 +669,19 @@ void Cache::insertInLiveDecodedResourcesList(CachedResource* resource) } -void Cache::addToLiveResourcesSize(CachedResource* resource) +void MemoryCache::addToLiveResourcesSize(CachedResource* resource) { m_liveSize += resource->size(); m_deadSize -= resource->size(); } -void Cache::removeFromLiveResourcesSize(CachedResource* resource) +void MemoryCache::removeFromLiveResourcesSize(CachedResource* resource) { m_liveSize -= resource->size(); m_deadSize += resource->size(); } -void Cache::adjustSize(bool live, int delta) +void MemoryCache::adjustSize(bool live, int delta) { if (live) { ASSERT(delta >= 0 || ((int)m_liveSize + delta >= 0)); @@ -650,7 +692,7 @@ void Cache::adjustSize(bool live, int delta) } } -void Cache::TypeStatistic::addResource(CachedResource* o) +void MemoryCache::TypeStatistic::addResource(CachedResource* o) { bool purged = o->wasPurged(); bool purgeable = o->isPurgeable() && !purged; @@ -663,7 +705,7 @@ void Cache::TypeStatistic::addResource(CachedResource* o) purgedSize += purged ? pageSize : 0; } -Cache::Statistics Cache::getStatistics() +MemoryCache::Statistics MemoryCache::getStatistics() { Statistics stats; CachedResourceMap::iterator e = m_resources.end(); @@ -687,11 +729,6 @@ Cache::Statistics Cache::getStatistics() case CachedResource::FontResource: stats.fonts.addResource(resource); break; -#if ENABLE(XBL) - case CachedResource::XBL: - stats.xblDocs.addResource(resource) - break; -#endif default: break; } @@ -699,7 +736,7 @@ Cache::Statistics Cache::getStatistics() return stats; } -void Cache::setDisabled(bool disabled) +void MemoryCache::setDisabled(bool disabled) { m_disabled = disabled; if (!m_disabled) @@ -714,24 +751,24 @@ void Cache::setDisabled(bool disabled) } #ifndef NDEBUG -void Cache::dumpStats() +void MemoryCache::dumpStats() { Statistics s = getStatistics(); - printf("%-11s %-11s %-11s %-11s %-11s %-11s %-11s\n", "", "Count", "Size", "LiveSize", "DecodedSize", "PurgeableSize", "PurgedSize"); - printf("%-11s %-11s %-11s %-11s %-11s %-11s %-11s\n", "-----------", "-----------", "-----------", "-----------", "-----------", "-----------", "-----------"); - printf("%-11s %11d %11d %11d %11d %11d %11d\n", "Images", s.images.count, s.images.size, s.images.liveSize, s.images.decodedSize, s.images.purgeableSize, s.images.purgedSize); - printf("%-11s %11d %11d %11d %11d %11d %11d\n", "CSS", s.cssStyleSheets.count, s.cssStyleSheets.size, s.cssStyleSheets.liveSize, s.cssStyleSheets.decodedSize, s.cssStyleSheets.purgeableSize, s.cssStyleSheets.purgedSize); + printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "", "Count", "Size", "LiveSize", "DecodedSize", "PurgeableSize", "PurgedSize"); + printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------"); + printf("%-13s %13d %13d %13d %13d %13d %13d\n", "Images", s.images.count, s.images.size, s.images.liveSize, s.images.decodedSize, s.images.purgeableSize, s.images.purgedSize); + printf("%-13s %13d %13d %13d %13d %13d %13d\n", "CSS", s.cssStyleSheets.count, s.cssStyleSheets.size, s.cssStyleSheets.liveSize, s.cssStyleSheets.decodedSize, s.cssStyleSheets.purgeableSize, s.cssStyleSheets.purgedSize); #if ENABLE(XSLT) - printf("%-11s %11d %11d %11d %11d %11d %11d\n", "XSL", s.xslStyleSheets.count, s.xslStyleSheets.size, s.xslStyleSheets.liveSize, s.xslStyleSheets.decodedSize, s.xslStyleSheets.purgeableSize, s.xslStyleSheets.purgedSize); + printf("%-13s %13d %13d %13d %13d %13d %13d\n", "XSL", s.xslStyleSheets.count, s.xslStyleSheets.size, s.xslStyleSheets.liveSize, s.xslStyleSheets.decodedSize, s.xslStyleSheets.purgeableSize, s.xslStyleSheets.purgedSize); #endif - printf("%-11s %11d %11d %11d %11d %11d %11d\n", "JavaScript", s.scripts.count, s.scripts.size, s.scripts.liveSize, s.scripts.decodedSize, s.scripts.purgeableSize, s.scripts.purgedSize); - printf("%-11s %11d %11d %11d %11d %11d %11d\n", "Fonts", s.fonts.count, s.fonts.size, s.fonts.liveSize, s.fonts.decodedSize, s.fonts.purgeableSize, s.fonts.purgedSize); - printf("%-11s %-11s %-11s %-11s %-11s %-11s %-11s\n\n", "-----------", "-----------", "-----------", "-----------", "-----------", "-----------", "-----------"); + printf("%-13s %13d %13d %13d %13d %13d %13d\n", "JavaScript", s.scripts.count, s.scripts.size, s.scripts.liveSize, s.scripts.decodedSize, s.scripts.purgeableSize, s.scripts.purgedSize); + printf("%-13s %13d %13d %13d %13d %13d %13d\n", "Fonts", s.fonts.count, s.fonts.size, s.fonts.liveSize, s.fonts.decodedSize, s.fonts.purgeableSize, s.fonts.purgedSize); + printf("%-13s %-13s %-13s %-13s %-13s %-13s %-13s\n\n", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------", "-------------"); } -void Cache::dumpLRULists(bool includeLive) const +void MemoryCache::dumpLRULists(bool includeLive) const { - printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced):\n"); + printf("LRU-SP lists in eviction order (Kilobytes decoded, Kilobytes encoded, Access count, Referenced, isPurgeable, wasPurged):\n"); int size = m_allResources.size(); for (int i = size - 1; i >= 0; i--) { @@ -740,7 +777,8 @@ void Cache::dumpLRULists(bool includeLive) const while (current) { CachedResource* prev = current->m_prevInAllResourcesList; if (includeLive || !current->hasClients()) - printf("(%.1fK, %.1fK, %uA, %dR); ", current->decodedSize() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, current->accessCount(), current->hasClients()); + printf("(%.1fK, %.1fK, %uA, %dR, %d, %d); ", current->decodedSize() / 1024.0f, (current->encodedSize() + current->overheadSize()) / 1024.0f, current->accessCount(), current->hasClients(), current->isPurgeable(), current->wasPurged()); + current = prev; } } diff --git a/WebCore/loader/Cache.h b/WebCore/loader/cache/MemoryCache.h index 0a5b74d..a40f85e 100644 --- a/WebCore/loader/Cache.h +++ b/WebCore/loader/cache/MemoryCache.h @@ -28,18 +28,18 @@ #include "CachePolicy.h" #include "CachedResource.h" #include "PlatformString.h" -#include "StringHash.h" #include "loader.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/Noncopyable.h> #include <wtf/Vector.h> +#include <wtf/text/StringHash.h> namespace WebCore { class CachedCSSStyleSheet; class CachedResource; -class DocLoader; +class CachedResourceLoader; class KURL; // This cache holds subresources used by Web pages: images, scripts, stylesheets, etc. @@ -55,9 +55,25 @@ class KURL; // -------|-----+++++++++++++++| // -------|-----+++++++++++++++|+++++ -class Cache : public Noncopyable { +// The behavior of the cache changes in the following way if shouldMakeResourcePurgeableOnEviction +// returns true. +// +// 1. Dead resources in the cache are kept in non-purgeable memory. +// 2. When we prune dead resources, instead of freeing them, we mark their memory as purgeable and +// keep the resources until the kernel reclaims the purgeable memory. +// +// By leaving the in-cache dead resources in dirty resident memory, we decrease the likelihood of +// the kernel claiming that memory and forcing us to refetch the resource (for example when a user +// presses back). +// +// And by having an unbounded number of resource objects using purgeable memory, we can use as much +// memory as is available on the machine. The trade-off here is that the CachedResource object (and +// its member variables) are allocated in non-purgeable TC-malloc'd memory so we would see slightly +// more memory use due to this. + +class MemoryCache : public Noncopyable { public: - friend Cache* cache(); + friend MemoryCache* cache(); typedef HashMap<String, CachedResource*> CachedResourceMap; @@ -85,9 +101,6 @@ public: #if ENABLE(XSLT) TypeStatistic xslStyleSheets; #endif -#if ENABLE(XBL) - TypeStatistic xblDocs; -#endif TypeStatistic fonts; }; @@ -96,11 +109,11 @@ public: // Request resources from the cache. A load will be initiated and a cache object created if the object is not // found in the cache. - CachedResource* requestResource(DocLoader*, CachedResource::Type, const KURL& url, const String& charset, bool isPreload = false); + CachedResource* requestResource(CachedResourceLoader*, CachedResource::Type, const KURL& url, const String& charset, bool isPreload = false); - CachedCSSStyleSheet* requestUserCSSStyleSheet(DocLoader*, const String& url, const String& charset); + CachedCSSStyleSheet* requestUserCSSStyleSheet(CachedResourceLoader*, const String& url, const String& charset); - void revalidateResource(CachedResource*, DocLoader*); + void revalidateResource(CachedResource*, CachedResourceLoader*); void revalidationSucceeded(CachedResource* revalidatingResource, const ResourceResponse&); void revalidationFailed(CachedResource* revalidatingResource); @@ -132,8 +145,8 @@ public: // Remove an existing cache entry from both the resource map and from the LRU list. void remove(CachedResource* resource) { evict(resource); } - void addDocLoader(DocLoader*); - void removeDocLoader(DocLoader*); + void addCachedResourceLoader(CachedResourceLoader*); + void removeCachedResourceLoader(CachedResourceLoader*); CachedResource* resourceForURL(const String&); @@ -151,6 +164,8 @@ public: void addToLiveResourcesSize(CachedResource*); void removeFromLiveResourcesSize(CachedResource*); + static bool shouldMakeResourcePurgeableOnEviction(); + // Function to collect cache statistics for the caches window in the Safari Debug menu. Statistics getStatistics(); @@ -160,8 +175,8 @@ public: #endif private: - Cache(); - ~Cache(); // Not implemented to make sure nobody accidentally calls delete -- WebCore does not delete singletons. + MemoryCache(); + ~MemoryCache(); // Not implemented to make sure nobody accidentally calls delete -- WebCore does not delete singletons. LRUList* lruListFor(CachedResource*); void resourceAccessed(CachedResource*); @@ -176,10 +191,11 @@ private: void pruneDeadResources(); // Flush decoded and encoded data from resources not referenced by Web pages. void pruneLiveResources(); // Flush decoded data from resources still referenced by Web pages. + bool makeResourcePurgeable(CachedResource*); void evict(CachedResource*); // Member variables. - HashSet<DocLoader*> m_docLoaders; + HashSet<CachedResourceLoader*> m_cachedResourceLoaders; Loader m_loader; bool m_disabled; // Whether or not the cache is enabled. @@ -207,8 +223,17 @@ private: HashMap<String, CachedResource*> m_resources; }; +inline bool MemoryCache::shouldMakeResourcePurgeableOnEviction() +{ +#if PLATFORM(IOS) + return true; +#else + return false; +#endif +} + // Function to obtain the global cache. -Cache* cache(); +MemoryCache* cache(); } diff --git a/WebCore/loader/icon/IconDatabase.cpp b/WebCore/loader/icon/IconDatabase.cpp index 5a9bfaa..6040037 100644 --- a/WebCore/loader/icon/IconDatabase.cpp +++ b/WebCore/loader/icon/IconDatabase.cpp @@ -43,6 +43,7 @@ #include <wtf/CurrentTime.h> #include <wtf/MainThread.h> #include <wtf/StdLibExtras.h> +#include <wtf/text/CString.h> // For methods that are meant to support API from the main thread - should not be called internally #define ASSERT_NOT_SYNC_THREAD() ASSERT(!m_syncThreadRunning || !IS_ICON_SYNC_THREAD()) @@ -768,6 +769,7 @@ IconDatabase::IconDatabase() , m_threadTerminationRequested(false) , m_removeIconsRequested(false) , m_iconURLImportComplete(false) + , m_disabledSuddenTerminationForSyncThread(false) , m_initialPruningComplete(false) , m_client(defaultClient()) , m_imported(false) @@ -806,13 +808,17 @@ void IconDatabase::notifyPendingLoadDecisions() void IconDatabase::wakeSyncThread() { - // The following is balanced by the call to enableSuddenTermination in the - // syncThreadMainLoop function. - // FIXME: It would be better to only disable sudden termination if we have - // something to write, not just if we have something to read. - disableSuddenTermination(); - MutexLocker locker(m_syncLock); + + if (!m_disabledSuddenTerminationForSyncThread) { + m_disabledSuddenTerminationForSyncThread = true; + // The following is balanced by the call to enableSuddenTermination in the + // syncThreadMainLoop function. + // FIXME: It would be better to only disable sudden termination if we have + // something to write, not just if we have something to read. + disableSuddenTermination(); + } + m_syncCondition.signal(); } @@ -1315,7 +1321,7 @@ void IconDatabase::performURLImport() } } - LOG(IconDatabase, "Notifying %zu interested page URLs that their icon URL is known due to the import", urlsToNotify.size()); + LOG(IconDatabase, "Notifying %lu interested page URLs that their icon URL is known due to the import", static_cast<unsigned long>(urlsToNotify.size())); // Now that we don't hold any locks, perform the actual notifications for (unsigned i = 0; i < urlsToNotify.size(); ++i) { LOG(IconDatabase, "Notifying icon info known for pageURL %s", urlsToNotify[i].ascii().data()); @@ -1411,7 +1417,9 @@ void* IconDatabase::syncThreadMainLoop() // The following is balanced by the call to disableSuddenTermination in the // wakeSyncThread function. Any time we wait on the condition, we also have // to enableSuddenTermation, after doing the next batch of work. + ASSERT(m_disabledSuddenTerminationForSyncThread); enableSuddenTermination(); + m_disabledSuddenTerminationForSyncThread = false; } m_syncCondition.wait(m_syncLock); @@ -1428,7 +1436,9 @@ void* IconDatabase::syncThreadMainLoop() // The following is balanced by the call to disableSuddenTermination in the // wakeSyncThread function. Any time we wait on the condition, we also have // to enableSuddenTermation, after doing the next batch of work. + ASSERT(m_disabledSuddenTerminationForSyncThread); enableSuddenTermination(); + m_disabledSuddenTerminationForSyncThread = false; } return 0; @@ -1639,11 +1649,19 @@ void IconDatabase::pruneUnretainedIcons() SQLiteStatement pageDeleteSQL(m_syncDB, "DELETE FROM PageURL WHERE rowid = (?);"); pageDeleteSQL.prepare(); for (size_t i = 0; i < numToDelete; ++i) { - LOG(IconDatabase, "Pruning page with rowid %lli from disk", pageIDsToDelete[i]); +#if OS(WINDOWS) + LOG(IconDatabase, "Pruning page with rowid %I64i from disk", static_cast<long long>(pageIDsToDelete[i])); +#else + LOG(IconDatabase, "Pruning page with rowid %lli from disk", static_cast<long long>(pageIDsToDelete[i])); +#endif pageDeleteSQL.bindInt64(1, pageIDsToDelete[i]); int result = pageDeleteSQL.step(); if (result != SQLResultDone) - LOG_ERROR("Unabled to delete page with id %lli from disk", pageIDsToDelete[i]); +#if OS(WINDOWS) + LOG_ERROR("Unabled to delete page with id %I64i from disk", static_cast<long long>(pageIDsToDelete[i])); +#else + LOG_ERROR("Unabled to delete page with id %lli from disk", static_cast<long long>(pageIDsToDelete[i])); +#endif pageDeleteSQL.reset(); // If the thread was asked to terminate, we should commit what pruning we've done so far, figuring we can @@ -1829,7 +1847,7 @@ inline void readySQLiteStatement(OwnPtr<SQLiteStatement>& statement, SQLiteDatab statement.set(0); } if (!statement) { - statement.set(new SQLiteStatement(db, str)); + statement = adoptPtr(new SQLiteStatement(db, str)); if (statement->prepare() != SQLResultOk) LOG_ERROR("Preparing statement %s failed", str.ascii().data()); } diff --git a/WebCore/loader/icon/IconDatabase.h b/WebCore/loader/icon/IconDatabase.h index 44ef22a..6146aa6 100644 --- a/WebCore/loader/icon/IconDatabase.h +++ b/WebCore/loader/icon/IconDatabase.h @@ -27,12 +27,12 @@ #ifndef IconDatabase_h #define IconDatabase_h -#include "StringHash.h" #include "Timer.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/Noncopyable.h> #include <wtf/OwnPtr.h> +#include <wtf/text/StringHash.h> #if ENABLE(ICONDATABASE) #include "SQLiteDatabase.h" @@ -144,11 +144,12 @@ private: String m_databaseDirectory; // Holding m_syncLock is required when accessing m_completeDatabasePath String m_completeDatabasePath; - + bool m_threadTerminationRequested; bool m_removeIconsRequested; bool m_iconURLImportComplete; - + bool m_disabledSuddenTerminationForSyncThread; + Mutex m_urlAndIconLock; // Holding m_urlAndIconLock is required when accessing any of the following data structures or the objects they contain HashMap<String, IconRecord*> m_iconURLToRecordMap; diff --git a/WebCore/loader/icon/IconDatabaseClient.h b/WebCore/loader/icon/IconDatabaseClient.h index 1811214..c210d7d 100644 --- a/WebCore/loader/icon/IconDatabaseClient.h +++ b/WebCore/loader/icon/IconDatabaseClient.h @@ -29,6 +29,7 @@ #ifndef IconDatabaseClient_h #define IconDatabaseClient_h +#include <wtf/Forward.h> #include <wtf/Noncopyable.h> // All of these client methods will be called from a non-main thread @@ -36,8 +37,6 @@ namespace WebCore { -class String; - class IconDatabaseClient : public Noncopyable { public: virtual ~IconDatabaseClient() { } diff --git a/WebCore/loader/icon/IconFetcher.cpp b/WebCore/loader/icon/IconFetcher.cpp deleted file mode 100644 index d1aa2f3..0000000 --- a/WebCore/loader/icon/IconFetcher.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "IconFetcher.h" - -#include "Frame.h" -#include "HTMLHeadElement.h" -#include "HTMLLinkElement.h" -#include "HTMLNames.h" -#include "ResourceHandle.h" -#include "ResourceRequest.h" - -namespace WebCore { - -using namespace HTMLNames; - -struct IconLinkEntry { -public: - enum IconType { - Unknown, - ICNS, - ICO, - }; - - IconLinkEntry(IconType type, const KURL& url) - : m_type(type) - , m_url(url) - { - } - - IconType type() const { return m_type; } - const KURL& url() const { return m_url; } - - SharedBuffer* buffer() - { - if (!m_buffer) - m_buffer = SharedBuffer::create(); - - return m_buffer.get(); - } - -private: - RefPtr<SharedBuffer> m_buffer; - IconType m_type; - KURL m_url; -}; - -#if PLATFORM(MAC) -static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::ICNS; -#elif PLATFORM(WIN) -static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::ICO; -#else -static const IconLinkEntry::IconType NativeIconType = IconLinkEntry::Unknown; -#endif - -static void parseIconLink(HTMLLinkElement* link, Vector<IconLinkEntry>& entries) -{ - // FIXME: Parse the size attribute too. - - IconLinkEntry::IconType type = IconLinkEntry::Unknown; - const KURL& url = link->href(); - - // Try to determine the file type. - String path = url.path(); - - int pos = path.reverseFind('.'); - if (pos >= 0) { - String extension = path.substring(pos + 1); - if (equalIgnoringCase(extension, "icns")) - type = IconLinkEntry::ICNS; - else if (equalIgnoringCase(extension, "ico")) - type = IconLinkEntry::ICO; - } - - entries.append(IconLinkEntry(type, url)); -} - -PassRefPtr<IconFetcher> IconFetcher::create(Frame* frame, IconFetcherClient* client) -{ - Document* document = frame->document(); - - HTMLHeadElement* head = document->head(); - if (!head) - return 0; - - Vector<IconLinkEntry> entries; - - for (Node* n = head; n; n = n->traverseNextNode()) { - if (!n->hasTagName(linkTag)) - continue; - - HTMLLinkElement* link = static_cast<HTMLLinkElement*>(n); - if (!link->isIcon()) - continue; - - parseIconLink(link, entries); - } - - if (entries.isEmpty()) - return 0; - - // Check if any of the entries have the same type as the native icon type. - - // FIXME: This should be way more sophisticated, and handle conversion - // of multisize formats for example. - for (unsigned i = 0; i < entries.size(); i++) { - const IconLinkEntry& entry = entries[i]; - if (entry.type() == NativeIconType) { - RefPtr<IconFetcher> iconFetcher = adoptRef(new IconFetcher(frame, client)); - - iconFetcher->m_entries.append(entry); - iconFetcher->loadEntry(); - - return iconFetcher.release(); - } - } - - return 0; -} - -IconFetcher::IconFetcher(Frame* frame, IconFetcherClient* client) - : m_frame(frame) - , m_client(client) - , m_currentEntry(0) -{ -} - -IconFetcher::~IconFetcher() -{ - cancel(); -} - -void IconFetcher::cancel() -{ - if (m_handle) - m_handle->cancel(); -} - -PassRefPtr<SharedBuffer> IconFetcher::createIcon() -{ - ASSERT(!m_entries.isEmpty()); - - // For now, just return the data of the first entry. - return m_entries.first().buffer(); -} - -void IconFetcher::loadEntry() -{ - ASSERT(m_currentEntry < m_entries.size()); - ASSERT(!m_handle); - - m_handle = ResourceHandle::create(m_entries[m_currentEntry].url(), this, m_frame, false, false); -} - -void IconFetcher::loadFailed() -{ - m_handle = 0; - - m_client->finishedFetchingIcon(0); -} - -void IconFetcher::didReceiveResponse(ResourceHandle* handle, const ResourceResponse& response) -{ - ASSERT_UNUSED(handle, m_handle == handle); - - int statusCode = response.httpStatusCode() / 100; - if (statusCode == 4 || statusCode == 5) { - loadFailed(); - return; - } -} - -void IconFetcher::didReceiveData(ResourceHandle* handle, const char* data, int length, int) -{ - ASSERT_UNUSED(handle, m_handle == handle); - - m_entries[m_currentEntry].buffer()->append(data, length); -} - -void IconFetcher::didFinishLoading(ResourceHandle* handle) -{ - ASSERT_UNUSED(handle, m_handle == handle); - - if (m_currentEntry == m_entries.size() - 1) { - // We finished loading, create the icon - RefPtr<SharedBuffer> iconData = createIcon(); - - m_client->finishedFetchingIcon(iconData.release()); - return; - } - - // Load the next entry - m_currentEntry++; - - loadEntry(); -} - -void IconFetcher::didFail(ResourceHandle* handle, const ResourceError&) -{ - ASSERT_UNUSED(handle, m_handle == handle); - - loadFailed(); -} - -} // namespace WebCore diff --git a/WebCore/loader/icon/IconFetcher.h b/WebCore/loader/icon/IconFetcher.h deleted file mode 100644 index 5327693..0000000 --- a/WebCore/loader/icon/IconFetcher.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef IconFetcher_h -#define IconFetcher_h - -#include <wtf/RefCounted.h> -#include <wtf/Forward.h> -#include <wtf/Vector.h> - -#include "ResourceHandleClient.h" - -namespace WebCore { - -class Frame; -struct IconLinkEntry; -class ResourceHandle; -class SharedBuffer; - -class IconFetcherClient { -public: - virtual void finishedFetchingIcon(PassRefPtr<SharedBuffer> iconData) = 0; - - virtual ~IconFetcherClient() { } -}; - -class IconFetcher : public RefCounted<IconFetcher>, ResourceHandleClient { -public: - static PassRefPtr<IconFetcher> create(Frame*, IconFetcherClient*); - ~IconFetcher(); - - void cancel(); - -private: - IconFetcher(Frame*, IconFetcherClient*); - void loadEntry(); - void loadFailed(); - - PassRefPtr<SharedBuffer> createIcon(); - - virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); - virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); - virtual void didFinishLoading(ResourceHandle*); - virtual void didFail(ResourceHandle*, const ResourceError&); - - Frame* m_frame; - IconFetcherClient* m_client; - - unsigned m_currentEntry; - RefPtr<ResourceHandle> m_handle; - Vector<IconLinkEntry> m_entries; -}; - -} // namespace WebCore - -#endif // IconFetcher_h diff --git a/WebCore/loader/icon/IconLoader.cpp b/WebCore/loader/icon/IconLoader.cpp index 5dd000e..adfa04b 100644 --- a/WebCore/loader/icon/IconLoader.cpp +++ b/WebCore/loader/icon/IconLoader.cpp @@ -38,6 +38,7 @@ #include "SharedBuffer.h" #include "SubresourceLoader.h" #include <wtf/UnusedParam.h> +#include <wtf/text/CString.h> using namespace std; @@ -49,9 +50,9 @@ IconLoader::IconLoader(Frame* frame) { } -auto_ptr<IconLoader> IconLoader::create(Frame* frame) +PassOwnPtr<IconLoader> IconLoader::create(Frame* frame) { - return auto_ptr<IconLoader>(new IconLoader(frame)); + return adoptPtr(new IconLoader(frame)); } IconLoader::~IconLoader() @@ -91,7 +92,7 @@ void IconLoader::didReceiveResponse(SubresourceLoader* resourceLoader, const Res if (status && (status < 200 || status > 299)) { ResourceHandle* handle = resourceLoader->handle(); - finishLoading(handle ? handle->request().url() : KURL(), 0); + finishLoading(handle ? handle->firstRequest().url() : KURL(), 0); } } @@ -115,7 +116,7 @@ void IconLoader::didFail(SubresourceLoader* resourceLoader, const ResourceError& if (m_loadIsInProgress) { ASSERT(resourceLoader == m_resourceLoader); ResourceHandle* handle = resourceLoader->handle(); - finishLoading(handle ? handle->request().url() : KURL(), 0); + finishLoading(handle ? handle->firstRequest().url() : KURL(), 0); } } @@ -137,7 +138,7 @@ void IconLoader::didFinishLoading(SubresourceLoader* resourceLoader) if (m_loadIsInProgress) { ASSERT(resourceLoader == m_resourceLoader); ResourceHandle* handle = resourceLoader->handle(); - finishLoading(handle ? handle->request().url() : KURL(), m_resourceLoader->resourceData()); + finishLoading(handle ? handle->firstRequest().url() : KURL(), m_resourceLoader->resourceData()); } } diff --git a/WebCore/loader/icon/IconLoader.h b/WebCore/loader/icon/IconLoader.h index 7b96ed8..1ebac48 100644 --- a/WebCore/loader/icon/IconLoader.h +++ b/WebCore/loader/icon/IconLoader.h @@ -27,7 +27,6 @@ #define IconLoader_h #include "SubresourceLoaderClient.h" -#include <memory> #include <wtf/Forward.h> #include <wtf/Noncopyable.h> #include <wtf/RefPtr.h> @@ -40,7 +39,7 @@ class SharedBuffer; class IconLoader : private SubresourceLoaderClient, public Noncopyable { public: - static std::auto_ptr<IconLoader> create(Frame*); + static PassOwnPtr<IconLoader> create(Frame*); ~IconLoader(); void startLoading(); diff --git a/WebCore/loader/icon/IconRecord.cpp b/WebCore/loader/icon/IconRecord.cpp index ffea318..7e90d8e 100644 --- a/WebCore/loader/icon/IconRecord.cpp +++ b/WebCore/loader/icon/IconRecord.cpp @@ -34,6 +34,7 @@ #include "Logging.h" #include "SQLiteStatement.h" #include "SQLiteTransaction.h" +#include <wtf/text/CString.h> #include <limits.h> diff --git a/WebCore/loader/icon/IconRecord.h b/WebCore/loader/icon/IconRecord.h index aaea787..f1fe12f 100644 --- a/WebCore/loader/icon/IconRecord.h +++ b/WebCore/loader/icon/IconRecord.h @@ -33,10 +33,10 @@ #include <wtf/RefCounted.h> #include "SharedBuffer.h" +#include "PlatformString.h" #include <wtf/HashSet.h> #include <wtf/OwnPtr.h> -#include "PlatformString.h" -#include "StringHash.h" +#include <wtf/text/StringHash.h> namespace WebCore { diff --git a/WebCore/loader/icon/wince/IconDatabaseWince.cpp b/WebCore/loader/icon/wince/IconDatabaseWinCE.cpp index e6d686c..54a36e5 100644 --- a/WebCore/loader/icon/wince/IconDatabaseWince.cpp +++ b/WebCore/loader/icon/wince/IconDatabaseWinCE.cpp @@ -22,12 +22,12 @@ #include "IconDatabase.h" #include "AutodrainedPool.h" -#include "CString.h" #include "DocumentLoader.h" #include "FileSystem.h" #include "IconDatabaseClient.h" #include "IconRecord.h" #include "Image.h" +#include <wtf/text/CString.h> namespace WebCore { diff --git a/WebCore/loader/loader.cpp b/WebCore/loader/loader.cpp index 4d2b474..bd27312 100644 --- a/WebCore/loader/loader.cpp +++ b/WebCore/loader/loader.cpp @@ -24,25 +24,26 @@ #include "config.h" #include "loader.h" -#include "Cache.h" +#include "MemoryCache.h" #include "CachedImage.h" #include "CachedResource.h" -#include "CString.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" +#include "InspectorInstrumentation.h" #include "Frame.h" #include "FrameLoader.h" #include "HTMLDocument.h" +#include "Logging.h" #include "Request.h" #include "ResourceHandle.h" #include "ResourceRequest.h" #include "ResourceResponse.h" #include "SecurityOrigin.h" +#include "SharedBuffer.h" #include "SubresourceLoader.h" #include <wtf/Assertions.h> #include <wtf/Vector.h> #define REQUEST_MANAGEMENT_ENABLED 1 -#define REQUEST_DEBUG 0 namespace WebCore { @@ -78,9 +79,6 @@ static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource #if ENABLE(XSLT) case CachedResource::XSLStyleSheet: #endif -#if ENABLE(XBL) - case CachedResource::XBL: -#endif return ResourceRequest::TargetIsStyleSheet; case CachedResource::Script: return ResourceRequest::TargetIsScript; @@ -88,7 +86,12 @@ static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource return ResourceRequest::TargetIsFontResource; case CachedResource::ImageResource: return ResourceRequest::TargetIsImage; +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkPrefetch: + return ResourceRequest::TargetIsPrefetch; +#endif } + ASSERT_NOT_REACHED(); return ResourceRequest::TargetIsSubresource; } @@ -100,15 +103,16 @@ Loader::Priority Loader::determinePriority(const CachedResource* resource) const #if ENABLE(XSLT) case CachedResource::XSLStyleSheet: #endif -#if ENABLE(XBL) - case CachedResource::XBL: -#endif return High; case CachedResource::Script: case CachedResource::FontResource: return Medium; case CachedResource::ImageResource: return Low; +#if ENABLE(LINK_PREFETCH) + case CachedResource::LinkPrefetch: + return VeryLow; +#endif } ASSERT_NOT_REACHED(); return Low; @@ -117,10 +121,12 @@ Loader::Priority Loader::determinePriority(const CachedResource* resource) const #endif } -void Loader::load(DocLoader* docLoader, CachedResource* resource, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks) +void Loader::load(CachedResourceLoader* cachedResourceLoader, CachedResource* resource, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks) { - ASSERT(docLoader); - Request* request = new Request(docLoader, resource, incremental, securityCheck, sendResourceLoadCallbacks); + LOG(ResourceLoading, "Loader::load resource %p '%s'", resource, resource->url().latin1().data()); + + ASSERT(cachedResourceLoader); + Request* request = new Request(cachedResourceLoader, resource, incremental, securityCheck, sendResourceLoadCallbacks); RefPtr<Host> host; KURL url(ParsedURLString, resource->url()); @@ -138,30 +144,34 @@ void Loader::load(DocLoader* docLoader, CachedResource* resource, bool increment bool hadRequests = host->hasRequests(); Priority priority = determinePriority(resource); host->addRequest(request, priority); - docLoader->incrementRequestCount(); + cachedResourceLoader->incrementRequestCount(request->cachedResource()); - if (priority > Low || !url.protocolInHTTPFamily() || !hadRequests) { + if (priority > Low || !url.protocolInHTTPFamily() || (priority == Low && !hadRequests)) { // Try to request important resources immediately host->servePendingRequests(priority); } else { // Handle asynchronously so early low priority requests don't get scheduled before later high priority ones + InspectorInstrumentation::didScheduleResourceRequest(cachedResourceLoader->document(), resource->url()); scheduleServePendingRequests(); } } void Loader::scheduleServePendingRequests() { + LOG(ResourceLoading, "Loader::scheduleServePendingRequests, m_requestTimer.isActive()=%u", m_requestTimer.isActive()); if (!m_requestTimer.isActive()) m_requestTimer.startOneShot(0); } void Loader::requestTimerFired(Timer<Loader>*) { + LOG(ResourceLoading, "Loader::requestTimerFired\n"); servePendingRequests(); } void Loader::servePendingRequests(Priority minimumPriority) { + LOG(ResourceLoading, "Loader::servePendingRequests. m_isSuspendingPendingRequests=%d", m_isSuspendingPendingRequests); if (m_isSuspendingPendingRequests) return; @@ -232,12 +242,12 @@ void Loader::nonCacheRequestComplete(const KURL& url) host->nonCacheRequestComplete(); } -void Loader::cancelRequests(DocLoader* docLoader) +void Loader::cancelRequests(CachedResourceLoader* cachedResourceLoader) { - docLoader->clearPendingPreloads(); + cachedResourceLoader->clearPendingPreloads(); if (m_nonHTTPProtocolHost->hasRequests()) - m_nonHTTPProtocolHost->cancelRequests(docLoader); + m_nonHTTPProtocolHost->cancelRequests(cachedResourceLoader); Vector<Host*> hostsToCancel; m_hosts.checkConsistency(); @@ -249,12 +259,12 @@ void Loader::cancelRequests(DocLoader* docLoader) for (unsigned n = 0; n < hostsToCancel.size(); ++n) { Host* host = hostsToCancel[n]; if (host->hasRequests()) - host->cancelRequests(docLoader); + host->cancelRequests(cachedResourceLoader); } scheduleServePendingRequests(); - ASSERT(docLoader->requestCount() == (docLoader->loadInProgress() ? 1 : 0)); + ASSERT(cachedResourceLoader->requestCount() == (cachedResourceLoader->loadInProgress() ? 1 : 0)); } Loader::Host::Host(const AtomicString& name, unsigned maxRequestsInFlight) @@ -286,6 +296,8 @@ void Loader::Host::nonCacheRequestComplete() { --m_nonCachedRequestsInFlight; ASSERT(m_nonCachedRequestsInFlight >= 0); + + cache()->loader()->scheduleServePendingRequests(); } bool Loader::Host::hasRequests() const @@ -301,8 +313,11 @@ bool Loader::Host::hasRequests() const void Loader::Host::servePendingRequests(Loader::Priority minimumPriority) { - if (cache()->loader()->isSuspendingPendingRequests()) + LOG(ResourceLoading, "Host::servePendingRequests '%s'", m_name.string().latin1().data()); + if (cache()->loader()->isSuspendingPendingRequests()) { + LOG(ResourceLoading, "...isSuspendingPendingRequests"); return; + } bool serveMore = true; for (int priority = High; priority >= minimumPriority && serveMore; --priority) @@ -313,16 +328,15 @@ void Loader::Host::servePendingRequests(RequestQueue& requestsPending, bool& ser { while (!requestsPending.isEmpty()) { Request* request = requestsPending.first(); - DocLoader* docLoader = request->docLoader(); + CachedResourceLoader* cachedResourceLoader = request->cachedResourceLoader(); bool resourceIsCacheValidator = request->cachedResource()->isCacheValidator(); // For named hosts - which are only http(s) hosts - we should always enforce the connection limit. // For non-named hosts - everything but http(s) - we should only enforce the limit if the document isn't done parsing // and we don't know all stylesheets yet. - bool shouldLimitRequests = !m_name.isNull() || docLoader->doc()->parsing() || !docLoader->doc()->haveStylesheetsLoaded(); + bool shouldLimitRequests = !m_name.isNull() || cachedResourceLoader->document()->parsing() || !cachedResourceLoader->document()->haveStylesheetsLoaded(); if (shouldLimitRequests && m_requestsLoading.size() + m_nonCachedRequestsInFlight >= m_maxRequestsInFlight) { serveLowerPriority = false; - cache()->loader()->scheduleServePendingRequests(); return; } requestsPending.removeFirst(); @@ -342,8 +356,8 @@ void Loader::Host::servePendingRequests(RequestQueue& requestsPending, bool& ser const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified"); const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag"); if (!lastModified.isEmpty() || !eTag.isEmpty()) { - ASSERT(docLoader->cachePolicy() != CachePolicyReload); - if (docLoader->cachePolicy() == CachePolicyRevalidate) + ASSERT(cachedResourceLoader->cachePolicy() != CachePolicyReload); + if (cachedResourceLoader->cachePolicy() == CachePolicyRevalidate) resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0"); if (!lastModified.isEmpty()) resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified); @@ -352,19 +366,27 @@ void Loader::Host::servePendingRequests(RequestQueue& requestsPending, bool& ser } } - RefPtr<SubresourceLoader> loader = SubresourceLoader::create(docLoader->doc()->frame(), +#if ENABLE(LINK_PREFETCH) + if (request->cachedResource()->type() == CachedResource::LinkPrefetch) + resourceRequest.setHTTPHeaderField("Purpose", "prefetch"); +#endif + + RefPtr<SubresourceLoader> loader = SubresourceLoader::create(cachedResourceLoader->document()->frame(), this, resourceRequest, request->shouldDoSecurityCheck(), request->sendResourceLoadCallbacks()); if (loader) { m_requestsLoading.add(loader.release(), request); request->cachedResource()->setRequestedFromNetworkingLayer(); -#if REQUEST_DEBUG - printf("HOST %s COUNT %d LOADING %s\n", resourceRequest.url().host().latin1().data(), m_requestsLoading.size(), request->cachedResource()->url().latin1().data()); -#endif - } else { - docLoader->decrementRequestCount(); - docLoader->setLoadInProgress(true); - request->cachedResource()->error(); - docLoader->setLoadInProgress(false); + LOG(ResourceLoading, "Host '%s' loading '%s'. Current count %d", m_name.string().latin1().data(), request->cachedResource()->url().latin1().data(), m_requestsLoading.size()); + } else { + // FIXME: What if resources in other frames were waiting for this revalidation? + LOG(ResourceLoading, "Host '%s' cannot start loading '%s'", m_name.string().latin1().data(), request->cachedResource()->url().latin1().data()); + CachedResource* resource = request->cachedResource(); + cachedResourceLoader->decrementRequestCount(resource); + cachedResourceLoader->setLoadInProgress(true); + if (resource->resourceToRevalidate()) + cache()->revalidationFailed(resource); + resource->error(); + cachedResourceLoader->setLoadInProgress(false); delete request; } } @@ -380,34 +402,32 @@ void Loader::Host::didFinishLoading(SubresourceLoader* loader) Request* request = i->second; m_requestsLoading.remove(i); - DocLoader* docLoader = request->docLoader(); + CachedResourceLoader* cachedResourceLoader = request->cachedResourceLoader(); // Prevent the document from being destroyed before we are done with - // the docLoader that it will delete when the document gets deleted. - RefPtr<Document> protector(docLoader->doc()); + // the cachedResourceLoader that it will delete when the document gets deleted. + RefPtr<Document> protector(cachedResourceLoader->document()); if (!request->isMultipart()) - docLoader->decrementRequestCount(); + cachedResourceLoader->decrementRequestCount(request->cachedResource()); CachedResource* resource = request->cachedResource(); ASSERT(!resource->resourceToRevalidate()); + LOG(ResourceLoading, "Host '%s' received %s. Current count %d\n", m_name.string().latin1().data(), resource->url().latin1().data(), m_requestsLoading.size()); + // If we got a 4xx response, we're pretending to have received a network // error, so we can't send the successful data() and finish() callbacks. if (!resource->errorOccurred()) { - docLoader->setLoadInProgress(true); + cachedResourceLoader->setLoadInProgress(true); resource->data(loader->resourceData(), true); resource->finish(); } delete request; - docLoader->setLoadInProgress(false); + cachedResourceLoader->setLoadInProgress(false); - docLoader->checkForPendingPreloads(); + cachedResourceLoader->checkForPendingPreloads(); -#if REQUEST_DEBUG - KURL u(ParsedURLString, resource->url()); - printf("HOST %s COUNT %d RECEIVED %s\n", u.host().latin1().data(), m_requestsLoading.size(), resource->url().latin1().data()); -#endif servePendingRequests(); } @@ -428,30 +448,32 @@ void Loader::Host::didFail(SubresourceLoader* loader, bool cancelled) Request* request = i->second; m_requestsLoading.remove(i); - DocLoader* docLoader = request->docLoader(); + CachedResourceLoader* cachedResourceLoader = request->cachedResourceLoader(); // Prevent the document from being destroyed before we are done with - // the docLoader that it will delete when the document gets deleted. - RefPtr<Document> protector(docLoader->doc()); + // the cachedResourceLoader that it will delete when the document gets deleted. + RefPtr<Document> protector(cachedResourceLoader->document()); if (!request->isMultipart()) - docLoader->decrementRequestCount(); + cachedResourceLoader->decrementRequestCount(request->cachedResource()); CachedResource* resource = request->cachedResource(); - + + LOG(ResourceLoading, "Host '%s' failed to load %s (cancelled=%d). Current count %d\n", m_name.string().latin1().data(), resource->url().latin1().data(), cancelled, m_requestsLoading.size()); + if (resource->resourceToRevalidate()) cache()->revalidationFailed(resource); if (!cancelled) { - docLoader->setLoadInProgress(true); + cachedResourceLoader->setLoadInProgress(true); resource->error(); } - docLoader->setLoadInProgress(false); + cachedResourceLoader->setLoadInProgress(false); if (cancelled || !resource->isPreloaded()) cache()->remove(resource); delete request; - docLoader->checkForPendingPreloads(); + cachedResourceLoader->checkForPendingPreloads(); servePendingRequests(); } @@ -478,13 +500,13 @@ void Loader::Host::didReceiveResponse(SubresourceLoader* loader, const ResourceR // 304 Not modified / Use local copy m_requestsLoading.remove(loader); loader->clearClient(); - request->docLoader()->decrementRequestCount(); + request->cachedResourceLoader()->decrementRequestCount(request->cachedResource()); // Existing resource is ok, just use it updating the expiration time. cache()->revalidationSucceeded(resource, response); - if (request->docLoader()->frame()) - request->docLoader()->frame()->loader()->checkCompleted(); + if (request->cachedResourceLoader()->frame()) + request->cachedResourceLoader()->frame()->loader()->checkCompleted(); delete request; @@ -504,13 +526,13 @@ void Loader::Host::didReceiveResponse(SubresourceLoader* loader, const ResourceR if (request->isMultipart()) { ASSERT(resource->isImage()); static_cast<CachedImage*>(resource)->clear(); - if (request->docLoader()->frame()) - request->docLoader()->frame()->loader()->checkCompleted(); + if (request->cachedResourceLoader()->frame()) + request->cachedResourceLoader()->frame()->loader()->checkCompleted(); } else if (response.isMultipart()) { request->setIsMultipart(true); - // We don't count multiParts in a DocLoader's request count - request->docLoader()->decrementRequestCount(); + // We don't count multiParts in a CachedResourceLoader's request count + request->cachedResourceLoader()->decrementRequestCount(request->cachedResource()); // If we get a multipart response, we must have a handle ASSERT(loader->handle()); @@ -533,9 +555,7 @@ void Loader::Host::didReceiveData(SubresourceLoader* loader, const char* data, i if (resource->errorOccurred()) return; - if (resource->response().httpStatusCode() / 100 == 4) { - // Treat a 4xx response like a network error for all resources but images (which will ignore the error and continue to load for - // legacy compatibility). + if (resource->response().httpStatusCode() >= 400) { resource->httpStatusCodeError(); return; } @@ -550,33 +570,47 @@ void Loader::Host::didReceiveData(SubresourceLoader* loader, const char* data, i resource->data(loader->resourceData(), false); } -void Loader::Host::cancelPendingRequests(RequestQueue& requestsPending, DocLoader* docLoader) +void Loader::Host::didReceiveCachedMetadata(SubresourceLoader* loader, const char* data, int size) +{ + RefPtr<Host> protector(this); + + Request* request = m_requestsLoading.get(loader); + if (!request) + return; + + CachedResource* resource = request->cachedResource(); + ASSERT(!resource->isCacheValidator()); + + resource->setSerializedCachedMetadata(data, size); +} + +void Loader::Host::cancelPendingRequests(RequestQueue& requestsPending, CachedResourceLoader* cachedResourceLoader) { RequestQueue remaining; RequestQueue::iterator end = requestsPending.end(); for (RequestQueue::iterator it = requestsPending.begin(); it != end; ++it) { Request* request = *it; - if (request->docLoader() == docLoader) { + if (request->cachedResourceLoader() == cachedResourceLoader) { cache()->remove(request->cachedResource()); + cachedResourceLoader->decrementRequestCount(request->cachedResource()); delete request; - docLoader->decrementRequestCount(); } else remaining.append(request); } requestsPending.swap(remaining); } -void Loader::Host::cancelRequests(DocLoader* docLoader) +void Loader::Host::cancelRequests(CachedResourceLoader* cachedResourceLoader) { for (unsigned p = 0; p <= High; p++) - cancelPendingRequests(m_requestsPending[p], docLoader); + cancelPendingRequests(m_requestsPending[p], cachedResourceLoader); Vector<SubresourceLoader*, 256> loadersToCancel; RequestMap::iterator end = m_requestsLoading.end(); for (RequestMap::iterator i = m_requestsLoading.begin(); i != end; ++i) { Request* r = i->second; - if (r->docLoader() == docLoader) + if (r->cachedResourceLoader() == cachedResourceLoader) loadersToCancel.append(i->first.get()); } diff --git a/WebCore/loader/loader.h b/WebCore/loader/loader.h index a9de032..4d353e0 100644 --- a/WebCore/loader/loader.h +++ b/WebCore/loader/loader.h @@ -22,8 +22,6 @@ #ifndef loader_h #define loader_h -#include "AtomicString.h" -#include "AtomicStringImpl.h" #include "FrameLoaderTypes.h" #include "PlatformString.h" #include "SubresourceLoaderClient.h" @@ -31,11 +29,13 @@ #include <wtf/Deque.h> #include <wtf/HashMap.h> #include <wtf/Noncopyable.h> +#include <wtf/text/AtomicString.h> +#include <wtf/text/AtomicStringImpl.h> namespace WebCore { class CachedResource; - class DocLoader; + class CachedResourceLoader; class KURL; class Request; @@ -44,12 +44,12 @@ namespace WebCore { Loader(); ~Loader(); - void load(DocLoader*, CachedResource*, bool incremental = true, SecurityCheckPolicy = DoSecurityCheck, bool sendResourceLoadCallbacks = true); + void load(CachedResourceLoader*, CachedResource*, bool incremental = true, SecurityCheckPolicy = DoSecurityCheck, bool sendResourceLoadCallbacks = true); - void cancelRequests(DocLoader*); + void cancelRequests(CachedResourceLoader*); - enum Priority { Low, Medium, High }; - void servePendingRequests(Priority minimumPriority = Low); + enum Priority { VeryLow, Low, Medium, High }; + void servePendingRequests(Priority minimumPriority = VeryLow); bool isSuspendingPendingRequests() { return m_isSuspendingPendingRequests; } void suspendPendingRequests(); @@ -76,8 +76,8 @@ namespace WebCore { void addRequest(Request*, Priority); void nonCacheRequestInFlight(); void nonCacheRequestComplete(); - void servePendingRequests(Priority minimumPriority = Low); - void cancelRequests(DocLoader*); + void servePendingRequests(Priority minimumPriority = VeryLow); + void cancelRequests(CachedResourceLoader*); bool hasRequests() const; bool processingResource() const { return m_numResourcesProcessing != 0 || m_nonCachedRequestsInFlight !=0; } @@ -87,13 +87,14 @@ namespace WebCore { virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&); virtual void didReceiveData(SubresourceLoader*, const char*, int); + virtual void didReceiveCachedMetadata(SubresourceLoader*, const char*, int); virtual void didFinishLoading(SubresourceLoader*); virtual void didFail(SubresourceLoader*, const ResourceError&); typedef Deque<Request*> RequestQueue; void servePendingRequests(RequestQueue& requestsPending, bool& serveLowerPriority); void didFail(SubresourceLoader*, bool cancelled = false); - void cancelPendingRequests(RequestQueue& requestsPending, DocLoader*); + void cancelPendingRequests(RequestQueue& requestsPending, CachedResourceLoader*); RequestQueue m_requestsPending[High + 1]; typedef HashMap<RefPtr<SubresourceLoader>, Request*> RequestMap; |